@arabold/docs-mcp-server 2.0.4 → 2.1.0
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 +57 -2
- package/db/migrations/012-add-source-content-type.sql +11 -0
- package/dist/assets/main.css +1 -1
- package/dist/assets/main.js +673 -630
- package/dist/assets/main.js.map +1 -1
- package/dist/index.js +7 -18952
- package/dist/index.js.map +1 -1
- package/dist/logger-CLtABTNb.js +99 -0
- package/dist/logger-CLtABTNb.js.map +1 -0
- package/dist/main-CmXZnHFM.js +18171 -0
- package/dist/main-CmXZnHFM.js.map +1 -0
- package/dist/utils-XwXdQlZ_.js +1062 -0
- package/dist/utils-XwXdQlZ_.js.map +1 -0
- package/package.json +48 -45
- package/public/assets/main.css +1 -1
- package/public/assets/main.js +673 -630
- package/public/assets/main.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main-CmXZnHFM.js","sources":["../src/store/errors.ts","../src/store/embeddings/FixedDimensionEmbeddings.ts","../src/store/embeddings/EmbeddingFactory.ts","../src/utils/config.ts","../src/cli/output.ts","../src/cli/commands/config.ts","../src/auth/middleware.ts","../src/auth/ProxyAuthManager.ts","../src/events/RemoteEventProxy.ts","../src/tools/errors.ts","../src/mcp/utils.ts","../src/mcp/mcpServer.ts","../src/utils/errors.ts","../src/utils/mimeTypeUtils.ts","../src/scraper/fetcher/FingerprintGenerator.ts","../src/scraper/fetcher/types.ts","../src/scraper/fetcher/BrowserFetcher.ts","../src/scraper/fetcher/FileFetcher.ts","../src/pipeline/errors.ts","../src/scraper/fetcher/HttpFetcher.ts","../src/scraper/fetcher/AutoDetectFetcher.ts","../src/tools/CancelJobTool.ts","../src/tools/ClearCompletedJobsTool.ts","../src/utils/dom.ts","../src/utils/string.ts","../src/utils/url.ts","../src/splitter/GreedySplitter.ts","../src/splitter/errors.ts","../src/splitter/splitters/CodeContentSplitter.ts","../src/splitter/splitters/TextContentSplitter.ts","../src/splitter/splitters/ListContentSplitter.ts","../src/splitter/splitters/TableContentSplitter.ts","../src/splitter/SemanticMarkdownSplitter.ts","../src/scraper/pipelines/BasePipeline.ts","../src/scraper/pipelines/DocumentPipeline.ts","../src/scraper/middleware/HtmlCheerioParserMiddleware.ts","../src/scraper/middleware/HtmlLinkExtractorMiddleware.ts","../src/scraper/middleware/HtmlMetadataExtractorMiddleware.ts","../src/scraper/middleware/HtmlNormalizationMiddleware.ts","../src/scraper/types.ts","../src/scraper/utils/SimpleMemoryCache.ts","../src/scraper/middleware/HtmlPlaywrightMiddleware.ts","../src/scraper/middleware/HtmlSanitizerMiddleware.ts","../src/scraper/middleware/HtmlToMarkdownMiddleware.ts","../src/scraper/utils/charset.ts","../src/scraper/utils/buffer.ts","../src/scraper/pipelines/HtmlPipeline.ts","../src/splitter/TextDocumentSplitter.ts","../src/splitter/JsonDocumentSplitter.ts","../src/scraper/pipelines/JsonPipeline.ts","../src/scraper/middleware/MarkdownLinkExtractorMiddleware.ts","../src/scraper/middleware/MarkdownMetadataExtractorMiddleware.ts","../src/scraper/pipelines/MarkdownPipeline.ts","../src/splitter/treesitter/parsers/types.ts","../src/splitter/treesitter/parsers/PythonParser.ts","../src/splitter/treesitter/parsers/TypeScriptParser.ts","../src/splitter/treesitter/LanguageParserRegistry.ts","../src/splitter/treesitter/TreesitterSourceCodeSplitter.ts","../src/scraper/pipelines/SourceCodePipeline.ts","../src/scraper/pipelines/TextPipeline.ts","../src/scraper/pipelines/PipelineFactory.ts","../src/tools/FetchUrlTool.ts","../src/tools/FindVersionTool.ts","../src/tools/GetJobInfoTool.ts","../src/tools/ListJobsTool.ts","../src/tools/ListLibrariesTool.ts","../src/tools/RefreshVersionTool.ts","../src/tools/RemoveTool.ts","../src/tools/ScrapeTool.ts","../src/store/DocumentManagementClient.ts","../src/utils/version.ts","../src/store/assembly/strategies/HierarchicalAssemblyStrategy.ts","../src/store/assembly/strategies/MarkdownAssemblyStrategy.ts","../src/store/assembly/ContentAssemblyStrategyFactory.ts","../src/store/DocumentRetrieverService.ts","../src/store/applyMigrations.ts","../src/store/types.ts","../src/store/DocumentStore.ts","../src/store/DocumentManagementService.ts","../src/store/index.ts","../src/tools/SearchTool.ts","../src/mcp/tools.ts","../src/services/mcpService.ts","../src/events/trpc/router.ts","../src/pipeline/trpc/router.ts","../src/store/trpc/router.ts","../src/services/trpcService.ts","../src/web/routes/events.ts","../src/web/components/PrimaryButton.tsx","../src/web/components/AddJobButton.tsx","../src/web/components/Toast.tsx","../src/web/components/Layout.tsx","../src/web/routes/index.tsx","../src/web/routes/jobs/cancel.tsx","../src/web/routes/jobs/clear-completed.tsx","../src/web/components/VersionBadge.tsx","../src/web/components/StatusBadge.tsx","../src/web/components/ProgressBar.tsx","../src/web/components/LoadingSpinner.tsx","../src/web/components/JobItem.tsx","../src/web/components/JobList.tsx","../src/web/routes/jobs/list.tsx","../src/web/components/AddVersionButton.tsx","../src/web/components/Alert.tsx","../src/web/components/Tooltip.tsx","../src/web/components/ScrapeFormContent.tsx","../src/web/components/ScrapeForm.tsx","../src/scraper/utils/defaultPatterns.ts","../src/web/routes/jobs/new.tsx","../src/web/components/VersionDetailsRow.tsx","../src/web/components/LibraryDetailCard.tsx","../src/web/components/LibrarySearchCard.tsx","../src/web/components/SearchResultItem.tsx","../src/web/components/SearchResultList.tsx","../src/web/components/SearchResultSkeletonItem.tsx","../src/web/routes/libraries/detail.tsx","../src/web/components/LibraryItem.tsx","../src/web/components/LibraryList.tsx","../src/web/routes/libraries/list.tsx","../src/web/components/AnalyticsCards.tsx","../src/web/routes/stats.tsx","../src/services/webService.ts","../src/services/workerService.ts","../src/utils/banner.ts","../src/app/AppServer.ts","../src/app/index.ts","../src/mcp/startStdioServer.ts","../src/pipeline/PipelineClient.ts","../src/scraper/utils/patternMatcher.ts","../src/scraper/utils/scope.ts","../src/scraper/strategies/BaseScraperStrategy.ts","../src/scraper/strategies/GitHubRepoProcessor.ts","../src/scraper/strategies/GitHubWikiProcessor.ts","../src/scraper/strategies/github-auth.ts","../src/scraper/strategies/GitHubScraperStrategy.ts","../src/utils/archive/TarAdapter.ts","../src/utils/archive/ZipAdapter.ts","../src/utils/archive/ArchiveFactory.ts","../src/scraper/strategies/LocalFileStrategy.ts","../src/scraper/strategies/WebScraperStrategy.ts","../src/scraper/strategies/NpmScraperStrategy.ts","../src/scraper/strategies/PyPiScraperStrategy.ts","../src/scraper/ScraperRegistry.ts","../src/scraper/ScraperService.ts","../src/pipeline/PipelineWorker.ts","../src/pipeline/PipelineManager.ts","../src/pipeline/PipelineFactory.ts","../src/cli/services.ts","../src/cli/commands/default.ts","../src/cli/commands/fetchUrl.ts","../src/cli/commands/findVersion.ts","../src/cli/commands/list.ts","../src/cli/commands/mcp.ts","../src/cli/commands/refresh.ts","../src/cli/commands/remove.ts","../src/cli/commands/scrape.ts","../src/cli/commands/search.ts","../src/cli/commands/web.ts","../src/cli/commands/worker.ts","../src/cli/index.ts","../src/cli/main.ts"],"sourcesContent":["/**\n * Base error class for all store-related errors.\n * Provides consistent error handling with optional cause tracking.\n */\nexport class StoreError extends Error {\n constructor(\n message: string,\n public readonly cause?: unknown,\n ) {\n super(cause ? `${message} caused by ${cause}` : message);\n this.name = this.constructor.name;\n\n const causeError =\n cause instanceof Error ? cause : cause ? new Error(String(cause)) : undefined;\n if (causeError?.stack) {\n this.stack = causeError.stack;\n }\n }\n}\n\n/**\n * Error thrown when a requested library cannot be found in the store.\n * Includes suggestions for similar library names if available.\n */\nexport class LibraryNotFoundInStoreError extends StoreError {\n constructor(\n public readonly library: string,\n public readonly similarLibraries: string[] = [],\n ) {\n let text = `Library ${library} not found in store.`;\n if (similarLibraries.length > 0) {\n text += ` Did you mean: ${similarLibraries.join(\", \")}?`;\n }\n super(text);\n }\n}\n\n/**\n * Error thrown when a specific version of a library cannot be found in the store.\n * Includes the list of available versions for better context.\n */\nexport class VersionNotFoundInStoreError extends StoreError {\n constructor(\n public readonly library: string,\n public readonly version: string,\n public readonly availableVersions: string[],\n ) {\n const versionText = version ? `Version ${version}` : \"Version\";\n let text = `${versionText} for library ${library} not found in store.`;\n if (availableVersions.length > 0) {\n text += ` Available versions: ${availableVersions.join(\", \")}`;\n }\n super(text);\n }\n}\n\n/**\n * Error thrown when an embedding model's vector dimension exceeds the database's fixed dimension.\n * This occurs when trying to use a model that produces vectors larger than the database can store.\n */\nexport class DimensionError extends StoreError {\n constructor(\n public readonly modelName: string,\n public readonly modelDimension: number,\n public readonly dbDimension: number,\n ) {\n super(\n `Model \"${modelName}\" produces ${modelDimension}-dimensional vectors, ` +\n `which exceeds the database's fixed dimension of ${dbDimension}. ` +\n `Please use a model with dimension ≤ ${dbDimension}.`,\n );\n }\n}\n\n/**\n * Error thrown when there's a problem with database connectivity or operations.\n */\nexport class ConnectionError extends StoreError {}\n\n/**\n * Error thrown when attempting to retrieve a document that doesn't exist.\n */\nexport class DocumentNotFoundError extends StoreError {\n constructor(public readonly id: string) {\n super(`Document ${id} not found`);\n }\n}\n\n/**\n * Error thrown when required credentials for an embedding provider are missing.\n * This allows the system to gracefully degrade to FTS-only search when vectorization is unavailable.\n */\nexport class MissingCredentialsError extends StoreError {\n constructor(\n public readonly provider: string,\n missingCredentials: string[],\n ) {\n super(\n `Missing credentials for ${provider} embedding provider. ` +\n `Required: ${missingCredentials.join(\", \")}`,\n );\n }\n}\n","import { Embeddings } from \"@langchain/core/embeddings\";\nimport { DimensionError } from \"../errors\";\n\n/**\n * Wrapper around an Embeddings implementation that ensures vectors have a fixed dimension.\n * - If a vector's dimension is greater than the target and truncation is allowed,\n * the vector is truncated (e.g., for models that support MRL - Matryoshka\n * Representation Learning).\n * - If a vector's dimension is greater than the target and truncation is not\n * allowed, a DimensionError is thrown.\n * - If a vector's dimension is less than the target, it is padded with zeros.\n */\nexport class FixedDimensionEmbeddings extends Embeddings {\n private provider: string;\n private model: string;\n\n constructor(\n private readonly embeddings: Embeddings,\n private readonly targetDimension: number,\n providerAndModel: string,\n private readonly allowTruncate: boolean = false,\n ) {\n super({});\n // Parse provider and model from string (e.g., \"gemini:embedding-001\" or just \"text-embedding-3-small\")\n const [providerOrModel, modelName] = providerAndModel.split(\":\");\n this.provider = modelName ? providerOrModel : \"openai\"; // Default to openai if no provider specified\n this.model = modelName || providerOrModel;\n }\n\n /**\n * Normalize a vector to the target dimension by truncating (for MRL models) or padding.\n * @throws {DimensionError} If vector is too large and provider doesn't support MRL\n */\n private normalizeVector(vector: number[]): number[] {\n const dimension = vector.length;\n\n if (dimension > this.targetDimension) {\n // If truncation is allowed (e.g., for MRL models like Gemini), truncate the vector\n if (this.allowTruncate) {\n return vector.slice(0, this.targetDimension);\n }\n // Otherwise, throw an error\n throw new DimensionError(\n `${this.provider}:${this.model}`,\n dimension,\n this.targetDimension,\n );\n }\n\n if (dimension < this.targetDimension) {\n // Pad with zeros to reach target dimension\n return [...vector, ...new Array(this.targetDimension - dimension).fill(0)];\n }\n\n return vector;\n }\n\n async embedQuery(text: string): Promise<number[]> {\n const vector = await this.embeddings.embedQuery(text);\n return this.normalizeVector(vector);\n }\n\n async embedDocuments(documents: string[]): Promise<number[][]> {\n const vectors = await this.embeddings.embedDocuments(documents);\n return vectors.map((vector) => this.normalizeVector(vector));\n }\n}\n","import { BedrockEmbeddings } from \"@langchain/aws\";\nimport type { Embeddings } from \"@langchain/core/embeddings\";\nimport { GoogleGenerativeAIEmbeddings } from \"@langchain/google-genai\";\nimport { VertexAIEmbeddings } from \"@langchain/google-vertexai\";\nimport {\n AzureOpenAIEmbeddings,\n type ClientOptions,\n OpenAIEmbeddings,\n type OpenAIEmbeddingsParams,\n} from \"@langchain/openai\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { MissingCredentialsError } from \"../errors\";\nimport { FixedDimensionEmbeddings } from \"./FixedDimensionEmbeddings\";\n\n/**\n * Supported embedding model providers. Each provider requires specific environment\n * variables to be set for API access.\n */\nexport type EmbeddingProvider =\n | \"openai\"\n | \"vertex\"\n | \"gemini\"\n | \"aws\"\n | \"microsoft\"\n | \"sagemaker\";\n\n/**\n * Error thrown when an invalid or unsupported embedding provider is specified.\n */\nexport class UnsupportedProviderError extends Error {\n constructor(provider: string) {\n super(\n `❌ Unsupported embedding provider: ${provider}\\n` +\n \" Supported providers: openai, vertex, gemini, aws, microsoft, sagemaker\\n\" +\n \" See README.md for configuration options or run with --help for more details.\",\n );\n this.name = \"UnsupportedProviderError\";\n }\n}\n\n/**\n * Error thrown when there's an issue with the model configuration or missing environment variables.\n */\nexport class ModelConfigurationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ModelConfigurationError\";\n }\n}\n\n/**\n * Checks if credentials are available for a specific embedding provider.\n * Returns true if any credentials are found, false if no credentials are provided.\n * Does not validate if the credentials are correct, only if they are present.\n *\n * @param provider The embedding provider to check\n * @returns true if credentials are available, false if no credentials found\n */\nexport function areCredentialsAvailable(provider: EmbeddingProvider): boolean {\n switch (provider) {\n case \"openai\":\n return !!process.env.OPENAI_API_KEY;\n\n case \"vertex\":\n return !!process.env.GOOGLE_APPLICATION_CREDENTIALS;\n\n case \"gemini\":\n return !!process.env.GOOGLE_API_KEY;\n\n case \"aws\": {\n const region = process.env.BEDROCK_AWS_REGION || process.env.AWS_REGION;\n return (\n !!region &&\n (!!process.env.AWS_PROFILE ||\n (!!process.env.AWS_ACCESS_KEY_ID && !!process.env.AWS_SECRET_ACCESS_KEY))\n );\n }\n\n case \"microsoft\":\n return !!(\n process.env.AZURE_OPENAI_API_KEY &&\n process.env.AZURE_OPENAI_API_INSTANCE_NAME &&\n process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME &&\n process.env.AZURE_OPENAI_API_VERSION\n );\n\n case \"sagemaker\": {\n const region = process.env.AWS_REGION;\n return (\n !!region &&\n (!!process.env.AWS_PROFILE ||\n (!!process.env.AWS_ACCESS_KEY_ID && !!process.env.AWS_SECRET_ACCESS_KEY))\n );\n }\n\n default:\n return false;\n }\n}\n\n/**\n * Creates an embedding model instance based on the specified provider and model name.\n * The provider and model name should be specified in the format \"provider:model_name\"\n * (e.g., \"google:text-embedding-004\"). If no provider is specified (i.e., just \"model_name\"),\n * OpenAI is used as the default provider.\n *\n * Environment variables required per provider:\n * - OpenAI: OPENAI_API_KEY (and optionally OPENAI_API_BASE, OPENAI_ORG_ID)\n * - Google Cloud Vertex AI: GOOGLE_APPLICATION_CREDENTIALS (path to service account JSON)\n * - Google GenAI (Gemini): GOOGLE_API_KEY\n * - AWS: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION (or BEDROCK_AWS_REGION)\n * - Microsoft: AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_INSTANCE_NAME, AZURE_OPENAI_API_DEPLOYMENT_NAME, AZURE_OPENAI_API_VERSION\n *\n * @param providerAndModel - The provider and model name in the format \"provider:model_name\"\n * or just \"model_name\" for OpenAI models.\n * @returns A configured instance of the appropriate Embeddings implementation.\n * @throws {UnsupportedProviderError} If an unsupported provider is specified.\n * @throws {ModelConfigurationError} If there's an issue with the model configuration.\n */\nexport function createEmbeddingModel(\n providerAndModel: string,\n runtime?: {\n requestTimeoutMs?: number;\n vectorDimension?: number;\n config?: AppConfig[\"embeddings\"];\n },\n): Embeddings {\n const config = runtime?.config;\n const requestTimeoutMs = runtime?.requestTimeoutMs ?? config?.requestTimeoutMs;\n const vectorDimension = runtime?.vectorDimension ?? config?.vectorDimension;\n if (vectorDimension === undefined) {\n throw new ModelConfigurationError(\n \"Embedding vector dimension is required; set DOCS_MCP_EMBEDDINGS_VECTOR_DIMENSION or embeddings.vectorDimension in config.\",\n );\n }\n // Parse provider and model name\n const [providerOrModel, ...modelNameParts] = providerAndModel.split(\":\");\n const modelName = modelNameParts.join(\":\");\n const provider = modelName ? (providerOrModel as EmbeddingProvider) : \"openai\";\n const model = modelName || providerOrModel;\n\n // Default configuration for each provider\n const baseConfig = { stripNewLines: true };\n\n switch (provider) {\n case \"openai\": {\n if (!process.env.OPENAI_API_KEY) {\n throw new MissingCredentialsError(\"openai\", [\"OPENAI_API_KEY\"]);\n }\n const config: Partial<OpenAIEmbeddingsParams> & { configuration?: ClientOptions } =\n {\n ...baseConfig,\n modelName: model,\n batchSize: 512, // OpenAI supports large batches\n timeout: requestTimeoutMs,\n };\n // Add custom base URL if specified\n const baseURL = process.env.OPENAI_API_BASE;\n config.configuration = baseURL\n ? { baseURL, timeout: requestTimeoutMs }\n : { timeout: requestTimeoutMs };\n return new OpenAIEmbeddings(config);\n }\n\n case \"vertex\": {\n if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {\n throw new MissingCredentialsError(\"vertex\", [\"GOOGLE_APPLICATION_CREDENTIALS\"]);\n }\n return new VertexAIEmbeddings({\n ...baseConfig,\n model: model, // e.g., \"text-embedding-004\"\n });\n }\n\n case \"gemini\": {\n if (!process.env.GOOGLE_API_KEY) {\n throw new MissingCredentialsError(\"gemini\", [\"GOOGLE_API_KEY\"]);\n }\n // Create base embeddings and wrap with FixedDimensionEmbeddings since Gemini\n // supports MRL (Matryoshka Representation Learning) for safe truncation\n const baseEmbeddings = new GoogleGenerativeAIEmbeddings({\n ...baseConfig,\n apiKey: process.env.GOOGLE_API_KEY,\n model: model, // e.g., \"gemini-embedding-exp-03-07\"\n });\n return new FixedDimensionEmbeddings(\n baseEmbeddings,\n vectorDimension,\n providerAndModel,\n true,\n );\n }\n\n case \"aws\": {\n // For AWS, model should be the full Bedrock model ID\n const region = process.env.BEDROCK_AWS_REGION || process.env.AWS_REGION;\n const missingCredentials: string[] = [];\n\n if (!region) {\n missingCredentials.push(\"BEDROCK_AWS_REGION or AWS_REGION\");\n }\n\n // Allow using AWS_PROFILE for credentials if set\n if (\n !process.env.AWS_PROFILE &&\n !process.env.AWS_ACCESS_KEY_ID &&\n !process.env.AWS_SECRET_ACCESS_KEY\n ) {\n missingCredentials.push(\n \"AWS_PROFILE or (AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY)\",\n );\n }\n\n if (missingCredentials.length > 0) {\n throw new MissingCredentialsError(\"aws\", missingCredentials);\n }\n\n // Only pass explicit credentials if present, otherwise let SDK resolve via profile/other means\n const credentials =\n process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY\n ? {\n accessKeyId: process.env.AWS_ACCESS_KEY_ID,\n secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,\n sessionToken: process.env.AWS_SESSION_TOKEN,\n }\n : undefined;\n\n return new BedrockEmbeddings({\n ...baseConfig,\n model: model, // e.g., \"amazon.titan-embed-text-v1\"\n region,\n ...(credentials ? { credentials } : {}),\n });\n }\n\n case \"microsoft\": {\n // For Azure, model name corresponds to the deployment name\n const missingCredentials: string[] = [];\n\n if (!process.env.AZURE_OPENAI_API_KEY) {\n missingCredentials.push(\"AZURE_OPENAI_API_KEY\");\n }\n if (!process.env.AZURE_OPENAI_API_INSTANCE_NAME) {\n missingCredentials.push(\"AZURE_OPENAI_API_INSTANCE_NAME\");\n }\n if (!process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME) {\n missingCredentials.push(\"AZURE_OPENAI_API_DEPLOYMENT_NAME\");\n }\n if (!process.env.AZURE_OPENAI_API_VERSION) {\n missingCredentials.push(\"AZURE_OPENAI_API_VERSION\");\n }\n\n if (missingCredentials.length > 0) {\n throw new MissingCredentialsError(\"microsoft\", missingCredentials);\n }\n\n return new AzureOpenAIEmbeddings({\n ...baseConfig,\n azureOpenAIApiKey: process.env.AZURE_OPENAI_API_KEY,\n azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_API_INSTANCE_NAME,\n azureOpenAIApiDeploymentName: process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME,\n azureOpenAIApiVersion: process.env.AZURE_OPENAI_API_VERSION,\n deploymentName: model,\n });\n }\n\n default:\n throw new UnsupportedProviderError(provider);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport envPaths from \"env-paths\";\nimport yaml from \"yaml\";\nimport { z } from \"zod\";\nimport { normalizeEnvValue } from \"./env\";\nimport { logger } from \"./logger\";\n\n/**\n * Custom zod schema for boolean values that properly handles string representations.\n * Unlike z.coerce.boolean() which treats any non-empty string as true,\n * this schema correctly parses \"false\", \"0\", \"no\", \"off\" as false.\n */\nconst envBoolean = z\n .union([z.boolean(), z.string()])\n .transform((val) => {\n if (typeof val === \"boolean\") return val;\n const lower = val.toLowerCase().trim();\n return (\n lower !== \"false\" &&\n lower !== \"0\" &&\n lower !== \"no\" &&\n lower !== \"off\" &&\n lower !== \"\"\n );\n })\n .pipe(z.boolean());\n\n// --- Default Global Configuration ---\n\nexport const DEFAULT_CONFIG = {\n app: {\n storePath: \"\",\n telemetryEnabled: true,\n readOnly: false,\n embeddingModel: \"text-embedding-3-small\",\n },\n server: {\n protocol: \"auto\",\n host: \"127.0.0.1\",\n ports: {\n default: 6280,\n worker: 8080,\n mcp: 6280,\n web: 6281,\n },\n heartbeatMs: 30_000,\n },\n auth: {\n enabled: false,\n issuerUrl: \"\",\n audience: \"\",\n },\n scraper: {\n maxPages: 1000,\n maxDepth: 3,\n maxConcurrency: 3,\n pageTimeoutMs: 5000,\n browserTimeoutMs: 30_000,\n fetcher: {\n maxRetries: 6,\n baseDelayMs: 1000,\n maxCacheItems: 200,\n maxCacheItemSizeBytes: 500 * 1024,\n },\n document: {\n maxSize: 10 * 1024 * 1024, // 10MB max size for PDF/Office documents\n },\n },\n splitter: {\n minChunkSize: 500,\n preferredChunkSize: 1500,\n maxChunkSize: 5000,\n treeSitterSizeLimit: 30_000,\n json: {\n maxNestingDepth: 5,\n maxChunks: 1000,\n },\n },\n embeddings: {\n batchSize: 100,\n batchChars: 50_000,\n requestTimeoutMs: 30_000,\n initTimeoutMs: 30_000,\n vectorDimension: 1536,\n },\n db: {\n migrationMaxRetries: 5,\n migrationRetryDelayMs: 300,\n },\n search: {\n overfetchFactor: 2,\n weightVec: 1,\n weightFts: 1,\n vectorMultiplier: 10,\n },\n sandbox: {\n defaultTimeoutMs: 5000,\n },\n assembly: {\n maxParentChainDepth: 10,\n childLimit: 3,\n precedingSiblingsLimit: 1,\n subsequentSiblingsLimit: 2,\n maxChunkDistance: 3,\n },\n} as const;\n\n// --- Configuration Schema (Nested) ---\n\nexport const AppConfigSchema = z.object({\n app: z\n .object({\n storePath: z.string().default(DEFAULT_CONFIG.app.storePath),\n telemetryEnabled: envBoolean.default(DEFAULT_CONFIG.app.telemetryEnabled),\n readOnly: envBoolean.default(DEFAULT_CONFIG.app.readOnly),\n embeddingModel: z.string().default(DEFAULT_CONFIG.app.embeddingModel),\n })\n .default(DEFAULT_CONFIG.app),\n server: z\n .object({\n protocol: z.string().default(DEFAULT_CONFIG.server.protocol),\n host: z.string().default(DEFAULT_CONFIG.server.host),\n ports: z\n .object({\n default: z.coerce.number().int().default(DEFAULT_CONFIG.server.ports.default),\n worker: z.coerce.number().int().default(DEFAULT_CONFIG.server.ports.worker),\n mcp: z.coerce.number().int().default(DEFAULT_CONFIG.server.ports.mcp),\n web: z.coerce.number().int().default(DEFAULT_CONFIG.server.ports.web),\n })\n .default(DEFAULT_CONFIG.server.ports),\n heartbeatMs: z.coerce.number().int().default(DEFAULT_CONFIG.server.heartbeatMs),\n })\n .default(DEFAULT_CONFIG.server),\n auth: z\n .object({\n enabled: envBoolean.default(DEFAULT_CONFIG.auth.enabled),\n issuerUrl: z.string().default(DEFAULT_CONFIG.auth.issuerUrl),\n audience: z.string().default(DEFAULT_CONFIG.auth.audience),\n })\n .default(DEFAULT_CONFIG.auth),\n scraper: z\n .object({\n maxPages: z.coerce.number().int().default(DEFAULT_CONFIG.scraper.maxPages),\n maxDepth: z.coerce.number().int().default(DEFAULT_CONFIG.scraper.maxDepth),\n maxConcurrency: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.maxConcurrency),\n pageTimeoutMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.pageTimeoutMs),\n browserTimeoutMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.browserTimeoutMs),\n fetcher: z\n .object({\n maxRetries: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.fetcher.maxRetries),\n baseDelayMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.fetcher.baseDelayMs),\n maxCacheItems: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.fetcher.maxCacheItems),\n maxCacheItemSizeBytes: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.fetcher.maxCacheItemSizeBytes),\n })\n .default(DEFAULT_CONFIG.scraper.fetcher),\n document: z\n .object({\n maxSize: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.scraper.document.maxSize),\n })\n .default(DEFAULT_CONFIG.scraper.document),\n })\n .default(DEFAULT_CONFIG.scraper),\n splitter: z\n .object({\n minChunkSize: z.coerce.number().int().default(DEFAULT_CONFIG.splitter.minChunkSize),\n preferredChunkSize: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.splitter.preferredChunkSize),\n maxChunkSize: z.coerce.number().int().default(DEFAULT_CONFIG.splitter.maxChunkSize),\n treeSitterSizeLimit: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.splitter.treeSitterSizeLimit),\n json: z\n .object({\n maxNestingDepth: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.splitter.json.maxNestingDepth),\n maxChunks: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.splitter.json.maxChunks),\n })\n .default(DEFAULT_CONFIG.splitter.json),\n })\n .default(DEFAULT_CONFIG.splitter),\n embeddings: z\n .object({\n batchSize: z.coerce.number().int().default(DEFAULT_CONFIG.embeddings.batchSize),\n batchChars: z.coerce.number().int().default(DEFAULT_CONFIG.embeddings.batchChars),\n requestTimeoutMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.embeddings.requestTimeoutMs),\n initTimeoutMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.embeddings.initTimeoutMs),\n vectorDimension: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.embeddings.vectorDimension),\n })\n .default(DEFAULT_CONFIG.embeddings),\n db: z\n .object({\n migrationMaxRetries: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.db.migrationMaxRetries),\n migrationRetryDelayMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.db.migrationRetryDelayMs),\n })\n .default(DEFAULT_CONFIG.db),\n search: z\n .object({\n overfetchFactor: z.coerce.number().default(DEFAULT_CONFIG.search.overfetchFactor),\n weightVec: z.coerce.number().default(DEFAULT_CONFIG.search.weightVec),\n weightFts: z.coerce.number().default(DEFAULT_CONFIG.search.weightFts),\n vectorMultiplier: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.search.vectorMultiplier),\n })\n .default(DEFAULT_CONFIG.search),\n sandbox: z\n .object({\n defaultTimeoutMs: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.sandbox.defaultTimeoutMs),\n })\n .default(DEFAULT_CONFIG.sandbox),\n assembly: z\n .object({\n maxParentChainDepth: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.assembly.maxParentChainDepth),\n childLimit: z.coerce.number().int().default(DEFAULT_CONFIG.assembly.childLimit),\n precedingSiblingsLimit: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.assembly.precedingSiblingsLimit),\n subsequentSiblingsLimit: z.coerce\n .number()\n .int()\n .default(DEFAULT_CONFIG.assembly.subsequentSiblingsLimit),\n maxChunkDistance: z.coerce\n .number()\n .int()\n .min(0)\n .default(DEFAULT_CONFIG.assembly.maxChunkDistance),\n })\n .default(DEFAULT_CONFIG.assembly),\n});\n\nexport type AppConfig = z.infer<typeof AppConfigSchema>;\n\n// Get defaults from the schema\nexport const defaults = AppConfigSchema.parse({});\n\n// --- Mapping Configuration ---\n// Maps flat env vars and CLI args to the nested config structure\n\ninterface ConfigMapping {\n path: string[]; // Path in AppConfig\n env?: string[]; // Environment variables\n cli?: string; // CLI argument name (yargs)\n}\n\nconst configMappings: ConfigMapping[] = [\n { path: [\"server\", \"protocol\"], env: [\"DOCS_MCP_PROTOCOL\"], cli: \"protocol\" },\n { path: [\"app\", \"storePath\"], env: [\"DOCS_MCP_STORE_PATH\"], cli: \"storePath\" },\n { path: [\"app\", \"telemetryEnabled\"], env: [\"DOCS_MCP_TELEMETRY\"] }, // Handled via --no-telemetry in CLI usually\n { path: [\"app\", \"readOnly\"], env: [\"DOCS_MCP_READ_ONLY\"], cli: \"readOnly\" },\n // Ports - Special handling for shared env vars is done in mapping logic\n {\n path: [\"server\", \"ports\", \"default\"],\n env: [\"DOCS_MCP_PORT\", \"PORT\"],\n cli: \"port\",\n },\n {\n path: [\"server\", \"ports\", \"worker\"],\n env: [\"DOCS_MCP_PORT\", \"PORT\"],\n cli: \"port\",\n },\n {\n path: [\"server\", \"ports\", \"mcp\"],\n env: [\"DOCS_MCP_PORT\", \"PORT\"],\n cli: \"port\",\n },\n {\n path: [\"server\", \"ports\", \"web\"],\n env: [\"DOCS_MCP_WEB_PORT\", \"DOCS_MCP_PORT\", \"PORT\"],\n cli: \"port\",\n },\n { path: [\"server\", \"host\"], env: [\"DOCS_MCP_HOST\", \"HOST\"], cli: \"host\" },\n {\n path: [\"app\", \"embeddingModel\"],\n env: [\"DOCS_MCP_EMBEDDING_MODEL\"],\n cli: \"embeddingModel\",\n },\n { path: [\"auth\", \"enabled\"], env: [\"DOCS_MCP_AUTH_ENABLED\"], cli: \"authEnabled\" },\n {\n path: [\"auth\", \"issuerUrl\"],\n env: [\"DOCS_MCP_AUTH_ISSUER_URL\"],\n cli: \"authIssuerUrl\",\n },\n {\n path: [\"auth\", \"audience\"],\n env: [\"DOCS_MCP_AUTH_AUDIENCE\"],\n cli: \"authAudience\",\n },\n // Add other mappings as needed for CLI/Env overrides\n];\n\n// --- Loader Logic ---\n\nexport interface LoadConfigOptions {\n configPath?: string; // Explicit config path\n searchDir?: string; // Search directory (store path)\n}\n\n// System-specific paths\nconst systemPaths = envPaths(\"docs-mcp-server\", { suffix: \"\" });\n\nexport function loadConfig(\n cliArgs: Record<string, unknown> = {},\n options: LoadConfigOptions = {},\n): AppConfig {\n // 1. Determine Config File Path & Mode\n // Priority: CLI > Options > Env > Default System Path\n const explicitPath =\n (cliArgs.config as string) || options.configPath || process.env.DOCS_MCP_CONFIG;\n\n let configPath: string;\n let isReadOnlyConfig = false;\n\n if (explicitPath) {\n configPath = explicitPath;\n isReadOnlyConfig = true; // User provided specific config, do not overwrite\n } else {\n // Default: strict system config path\n configPath = path.join(systemPaths.config, \"config.yaml\");\n isReadOnlyConfig = false; // Auto-update default config\n }\n\n logger.debug(`Using config file: ${configPath}`);\n\n // 2. Load Config File (if exists) or use empty object\n const fileConfig = loadConfigFile(configPath) || {};\n\n // 3. Merge Defaults < File\n const baseConfig = deepMerge(defaults, fileConfig) as ConfigObject;\n\n // 4. Write back to file (Auto-Update) - ONLY if using default path\n if (!isReadOnlyConfig) {\n try {\n saveConfigFile(configPath, baseConfig);\n } catch (error) {\n logger.warn(`Failed to save config file to ${configPath}: ${error}`);\n }\n }\n\n // 5. Map Env Vars and CLI Args\n const envConfig = mapEnvToConfig();\n const cliConfig = mapCliToConfig(cliArgs);\n\n // 6. Merge: Base < Env < CLI\n const mergedInput = deepMerge(\n baseConfig,\n deepMerge(envConfig, cliConfig),\n ) as ConfigObject;\n\n // Special handling for embedding model fallback\n if (!getAtPath(mergedInput, [\"app\", \"embeddingModel\"]) && process.env.OPENAI_API_KEY) {\n setAtPath(mergedInput, [\"app\", \"embeddingModel\"], \"text-embedding-3-small\");\n }\n\n return AppConfigSchema.parse(mergedInput);\n}\n\nfunction loadConfigFile(filePath: string): Record<string, unknown> | null {\n if (!fs.existsSync(filePath)) return null;\n\n try {\n const content = fs.readFileSync(filePath, \"utf8\");\n if (filePath.endsWith(\".json\")) {\n return JSON.parse(content);\n }\n return yaml.parse(content) || {};\n } catch (error) {\n logger.warn(`Failed to parse config file ${filePath}: ${error}`);\n return null;\n }\n}\n\nfunction saveConfigFile(filePath: string, config: Record<string, unknown>): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n let content: string;\n if (filePath.endsWith(\".json\")) {\n content = JSON.stringify(config, null, 2);\n } else {\n // Default to YAML\n content = yaml.stringify(config);\n }\n\n logger.debug(`Updating config file at ${filePath}`);\n fs.writeFileSync(filePath, content, \"utf8\");\n}\n\nfunction mapEnvToConfig(): Record<string, unknown> {\n const config: Record<string, unknown> = {};\n\n // 1. Apply explicit mappings first (for aliases like PORT, HOST)\n for (const mapping of configMappings) {\n if (mapping.env) {\n for (const envVar of mapping.env) {\n if (process.env[envVar] !== undefined) {\n setAtPath(\n config,\n mapping.path,\n normalizeEnvValue(process.env[envVar] as string),\n );\n break; // First match wins\n }\n }\n }\n }\n\n // 2. Apply auto-generated env vars (takes precedence over explicit mappings)\n for (const pathArr of ALL_CONFIG_LEAF_PATHS) {\n const envVar = pathToEnvVar(pathArr);\n if (process.env[envVar] !== undefined) {\n setAtPath(config, pathArr, normalizeEnvValue(process.env[envVar] as string));\n }\n }\n\n return config;\n}\n\nfunction mapCliToConfig(args: Record<string, unknown>): Record<string, unknown> {\n const config: Record<string, unknown> = {};\n for (const mapping of configMappings) {\n if (mapping.cli && args[mapping.cli] !== undefined) {\n setAtPath(config, mapping.path, args[mapping.cli]);\n }\n }\n return config;\n}\n\n// --- Helpers ---\n\n// Helper type for nested objects\ntype ConfigObject = Record<string, unknown>;\n\n/**\n * Convert camelCase to UPPER_SNAKE_CASE\n * Example: \"maxSize\" → \"MAX_SIZE\", \"maxNestingDepth\" → \"MAX_NESTING_DEPTH\"\n */\nexport function camelToUpperSnake(str: string): string {\n return str.replace(/([a-z])([A-Z])/g, \"$1_$2\").toUpperCase();\n}\n\n/**\n * Convert config path to environment variable name\n * Example: [\"scraper\", \"document\", \"maxSize\"] → \"DOCS_MCP_SCRAPER_DOCUMENT_MAX_SIZE\"\n */\nexport function pathToEnvVar(pathArr: string[]): string {\n return `DOCS_MCP_${pathArr.map(camelToUpperSnake).join(\"_\")}`;\n}\n\n/**\n * Recursively collect all leaf paths from a config object\n */\nexport function collectLeafPaths(obj: object, prefix: string[] = []): string[][] {\n const paths: string[][] = [];\n for (const [key, value] of Object.entries(obj)) {\n const currentPath = [...prefix, key];\n if (value !== null && typeof value === \"object\" && !Array.isArray(value)) {\n paths.push(...collectLeafPaths(value, currentPath));\n } else {\n paths.push(currentPath);\n }\n }\n return paths;\n}\n\n// Cache leaf paths at module init since DEFAULT_CONFIG is constant\nconst ALL_CONFIG_LEAF_PATHS = collectLeafPaths(DEFAULT_CONFIG);\n\nfunction setAtPath(obj: ConfigObject, pathArr: string[], value: unknown) {\n let current = obj;\n for (let i = 0; i < pathArr.length - 1; i++) {\n const key = pathArr[i];\n if (\n current[key] === undefined ||\n typeof current[key] !== \"object\" ||\n current[key] === null\n ) {\n current[key] = {};\n }\n current = current[key] as ConfigObject;\n }\n current[pathArr[pathArr.length - 1]] = value;\n}\n\nfunction getAtPath(obj: ConfigObject, pathArr: string[]): unknown {\n let current: unknown = obj;\n for (const key of pathArr) {\n if (typeof current !== \"object\" || current === null) return undefined;\n current = (current as ConfigObject)[key];\n }\n return current;\n}\n\nfunction deepMerge(target: unknown, source: unknown): unknown {\n if (typeof target !== \"object\" || target === null) return source;\n if (typeof source !== \"object\" || source === null) return target;\n\n const t = target as ConfigObject;\n const s = source as ConfigObject;\n const output = { ...t };\n\n for (const key of Object.keys(s)) {\n const sValue = s[key];\n const tValue = t[key];\n\n if (\n typeof sValue === \"object\" &&\n sValue !== null &&\n typeof tValue === \"object\" &&\n tValue !== null &&\n key in t\n ) {\n output[key] = deepMerge(tValue, sValue);\n } else {\n output[key] = sValue;\n }\n }\n return output;\n}\n\n// --- CLI Helper Functions ---\n\n/**\n * Check if a config path is valid by verifying it exists in DEFAULT_CONFIG\n */\nexport function isValidConfigPath(path: string): boolean {\n const pathArr = path.split(\".\");\n return getAtPath(DEFAULT_CONFIG as ConfigObject, pathArr) !== undefined;\n}\n\n/**\n * Get a config value by dot-separated path\n */\nexport function getConfigValue(config: AppConfig, path: string): unknown {\n const pathArr = path.split(\".\");\n return getAtPath(config as unknown as ConfigObject, pathArr);\n}\n\n/**\n * Parse a string value to the appropriate type (number, boolean, or string)\n */\nexport function parseConfigValue(value: string): unknown {\n // Try number first\n const num = Number(value);\n if (!Number.isNaN(num) && value.trim() !== \"\") {\n return num;\n }\n\n // Try boolean\n const lower = value.toLowerCase();\n if (lower === \"true\") return true;\n if (lower === \"false\") return false;\n\n // Default to string\n return value;\n}\n\n/**\n * Set a config value and persist to file.\n * Returns the path to the config file that was updated.\n * Validates the updated config against the schema before saving.\n */\nexport function setConfigValue(path: string, value: string): string {\n const configPath = getDefaultConfigPath();\n const fileConfig = loadConfigFile(configPath) || {};\n const pathArr = path.split(\".\");\n const parsedValue = parseConfigValue(value);\n\n // Apply change to a copy so we can validate before persisting\n const updatedConfig = JSON.parse(JSON.stringify(fileConfig));\n setAtPath(updatedConfig, pathArr, parsedValue);\n\n // Validate against schema before saving\n try {\n AppConfigSchema.parse(updatedConfig);\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n throw new Error(`Invalid config value for \"${path}\": ${errorMsg}`);\n }\n\n saveConfigFile(configPath, updatedConfig);\n\n return configPath;\n}\n\n/**\n * Get the default system config path\n */\nexport function getDefaultConfigPath(): string {\n return path.join(systemPaths.config, \"config.yaml\");\n}\n","import { encode as encodeToToon } from \"@toon-format/toon\";\nimport yaml from \"yaml\";\nimport type { Argv } from \"yargs\";\nimport { getLogLevelFromEnv, LogLevel, logger, setLogLevel } from \"../utils/logger\";\n\nexport type OutputFormat = \"json\" | \"yaml\" | \"toon\";\n\nexport interface HasOutputOption {\n [key: string]: unknown;\n output?: unknown;\n}\n\ntype PrimitiveValue = boolean | null | number | string;\n\nexport function isInteractiveOutput(): boolean {\n return !!process.stdout.isTTY && !!process.stderr.isTTY;\n}\n\nexport function resolveOutputFormat(argv: HasOutputOption): OutputFormat {\n const requestedFormat = argv.output as OutputFormat | undefined;\n if (requestedFormat) {\n return requestedFormat;\n }\n\n return \"json\";\n}\n\nexport function formatStructuredOutput(value: unknown, format: OutputFormat): string {\n if (format === \"yaml\") {\n return yaml.stringify(value).trimEnd();\n }\n\n if (format === \"toon\") {\n return encodeToToon(value).trimEnd();\n }\n\n return JSON.stringify(value, null, 2);\n}\n\nexport function renderStructuredOutput(value: unknown, argv: HasOutputOption): void {\n const format = resolveOutputFormat(argv);\n process.stdout.write(`${formatStructuredOutput(value, format)}\\n`);\n}\n\nexport function renderTextOutput(value: string): void {\n process.stdout.write(value.endsWith(\"\\n\") ? value : `${value}\\n`);\n}\n\nexport function renderScalarOutput(value: PrimitiveValue, argv: HasOutputOption): void {\n const hasExplicitFormat = typeof argv.output === \"string\";\n if (!hasExplicitFormat) {\n renderTextOutput(String(value));\n return;\n }\n\n renderStructuredOutput(value, argv);\n}\n\nexport function applyGlobalCliOutputMode(options: {\n verbose?: boolean;\n quiet?: boolean;\n}): void {\n const interactive = isInteractiveOutput();\n const envLogLevel = getLogLevelFromEnv();\n\n if (options.verbose) {\n setLogLevel(LogLevel.DEBUG);\n } else if (options.quiet) {\n setLogLevel(LogLevel.ERROR);\n } else if (envLogLevel !== null && envLogLevel !== undefined) {\n setLogLevel(envLogLevel);\n } else if (interactive) {\n setLogLevel(LogLevel.INFO);\n } else {\n setLogLevel(LogLevel.ERROR);\n }\n}\n\nexport function registerGlobalOutputOptions<T>(yargs: Argv<T>): Argv<T> {\n return yargs.option(\"output\", {\n type: \"string\",\n choices: [\"json\", \"yaml\", \"toon\"],\n description: \"Structured output format for commands that return data\",\n });\n}\n\nexport function reportCliError(message: string): void {\n logger.error(message);\n}\n\nexport function reportCliWarning(message: string): void {\n logger.warn(message);\n}\n\nexport function reportCliInfo(message: string): void {\n logger.info(message);\n}\n","import type { Argv } from \"yargs\";\nimport {\n getConfigValue,\n isValidConfigPath,\n loadConfig,\n parseConfigValue,\n setConfigValue,\n} from \"../../utils/config\";\nimport {\n renderScalarOutput,\n renderStructuredOutput,\n renderTextOutput,\n reportCliError,\n} from \"../output\";\n\nfunction validateConfigPath(path: string): boolean {\n return isValidConfigPath(path);\n}\n\nexport function createConfigCommand(cli: Argv) {\n cli.command(\n \"config [action] [path] [value]\",\n \"View or modify configuration\",\n (yargs) =>\n yargs\n .positional(\"action\", {\n type: \"string\",\n })\n .positional(\"path\", {\n type: \"string\",\n })\n .positional(\"value\", {\n type: \"string\",\n }),\n (argv) => {\n const action = argv.action as string | undefined;\n\n if (!action) {\n const config = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string,\n });\n renderStructuredOutput(config, argv);\n return;\n }\n\n if (action !== \"get\" && action !== \"set\") {\n reportCliError(`Error: Unknown config action '${action}'. Use 'get' or 'set'.`);\n process.exitCode = 1;\n return;\n }\n\n if (action === \"get\") {\n const path = argv.path as string | undefined;\n if (!path || !validateConfigPath(path)) {\n reportCliError(`Error: Invalid config path '${path ?? \"\"}'`);\n reportCliError(\"Use 'docs-mcp-server config' to see all available paths.\");\n process.exitCode = 1;\n return;\n }\n\n const config = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string,\n });\n\n const value = getConfigValue(config, path);\n if (\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\"\n ) {\n renderScalarOutput(value, argv);\n return;\n }\n\n renderStructuredOutput(value, argv);\n return;\n }\n\n const path = argv.path as string | undefined;\n const value = argv.value as string | undefined;\n\n if (!path || !value) {\n reportCliError(\"Error: config set requires both <path> and <value>.\");\n process.exitCode = 1;\n return;\n }\n\n if (argv.config) {\n reportCliError(\n \"Error: Cannot modify configuration when using explicit --config file.\",\n );\n reportCliError(\"Remove the --config flag to modify the default configuration.\");\n process.exitCode = 1;\n return;\n }\n\n if (!validateConfigPath(path)) {\n reportCliError(`Error: Invalid config path '${path}'`);\n reportCliError(\"Use 'docs-mcp-server config' to see all available paths.\");\n process.exitCode = 1;\n return;\n }\n\n const config = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string,\n });\n const currentValue = getConfigValue(config, path);\n if (\n currentValue !== undefined &&\n currentValue !== null &&\n typeof currentValue === \"object\" &&\n !Array.isArray(currentValue)\n ) {\n reportCliError(\n `Error: Config path '${path}' refers to an object. Use a more specific leaf path to set a scalar value.`,\n );\n reportCliError(\n \"Hint: Run 'docs-mcp-server config' to inspect the current structure.\",\n );\n process.exitCode = 1;\n return;\n }\n\n try {\n const savedPath = setConfigValue(path, value);\n const parsedValue = parseConfigValue(value);\n renderTextOutput(`Updated ${path} = ${JSON.stringify(parsedValue)}`);\n renderTextOutput(`Saved to: ${savedPath}`);\n } catch (error) {\n reportCliError(\n `Error: Failed to save configuration: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n process.exitCode = 1;\n }\n },\n );\n}\n","/**\n * Fastify middleware for OAuth2/OIDC authentication using ProxyAuthManager.\n * Provides binary authentication (authenticated vs not authenticated) for MCP endpoints.\n */\n\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\nimport { logger } from \"../utils/logger\";\nimport type { ProxyAuthManager } from \"./ProxyAuthManager\";\nimport type { AuthContext } from \"./types\";\n\n// Type for Fastify request with auth context\ntype AuthenticatedRequest = FastifyRequest & { auth: AuthContext };\n\n/**\n * Create authentication middleware that validates Bearer tokens using ProxyAuthManager.\n */\nexport function createAuthMiddleware(authManager: ProxyAuthManager) {\n return async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const authContext = await authManager.createAuthContext(\n request.headers.authorization || \"\",\n request,\n );\n\n // Always set auth context on request\n (request as AuthenticatedRequest).auth = authContext;\n\n // Check if authentication is enabled by looking at the config\n const isAuthEnabled = authManager.authConfig.enabled;\n\n if (!isAuthEnabled) {\n // Auth is disabled - allow all requests through\n logger.debug(\"Authentication disabled, allowing request\");\n return;\n }\n\n // Auth is enabled - validate authentication\n if (!authContext.authenticated) {\n const hasAuthHeader = !!request.headers.authorization;\n\n if (hasAuthHeader) {\n // Auth is enabled but token is invalid\n logger.debug(\"Token validation failed\");\n reply\n .status(401)\n .header(\n \"WWW-Authenticate\",\n 'Bearer realm=\"MCP Server\", error=\"invalid_token\"',\n )\n .send({\n error: \"invalid_token\",\n error_description: \"The access token is invalid\",\n });\n return;\n } else {\n // Auth is enabled but no authorization header provided\n logger.debug(\"Missing authorization header\");\n reply.status(401).header(\"WWW-Authenticate\", 'Bearer realm=\"MCP Server\"').send({\n error: \"unauthorized\",\n error_description: \"Authorization header required\",\n });\n return;\n }\n }\n\n // Authentication successful\n logger.debug(\n `Authentication successful for subject: ${authContext.subject || \"anonymous\"}`,\n );\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Authentication failed\";\n logger.debug(`Authentication error: ${message}`);\n\n reply\n .status(401)\n .header(\"WWW-Authenticate\", 'Bearer realm=\"MCP Server\", error=\"invalid_token\"')\n .send({\n error: \"invalid_token\",\n error_description: \"Token validation failed\",\n });\n }\n };\n}\n","/**\n * Simplified MCP Authentication Manager using the MCP SDK's ProxyOAuthServerProvider.\n * This provides OAuth2 proxy functionality for Fastify, leveraging the SDK's auth logic\n * while maintaining compatibility with the existing Fastify-based architecture.\n * Uses standard OAuth identity scopes with binary authentication (authenticated vs not).\n * Supports hybrid token validation: JWT tokens using JWKS, opaque tokens using userinfo endpoint.\n */\n\nimport { ProxyOAuthServerProvider } from \"@modelcontextprotocol/sdk/server/auth/providers/proxyProvider.js\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport { createRemoteJWKSet, jwtVerify } from \"jose\";\nimport { logger } from \"../utils/logger\";\nimport type { AuthConfig, AuthContext } from \"./types\";\n\nexport class ProxyAuthManager {\n private proxyProvider: ProxyOAuthServerProvider | null = null;\n private discoveredEndpoints: {\n authorizationUrl: string;\n tokenUrl: string;\n revocationUrl?: string;\n registrationUrl?: string;\n jwksUri?: string;\n userinfoUrl?: string;\n } | null = null;\n private jwks: ReturnType<typeof createRemoteJWKSet> | null = null;\n\n constructor(private config: AuthConfig) {}\n\n /**\n * Get the authentication configuration\n */\n get authConfig(): AuthConfig {\n return this.config;\n }\n\n /**\n * Initialize the proxy auth manager with the configured OAuth provider.\n */\n async initialize(): Promise<void> {\n if (!this.config.enabled) {\n logger.debug(\"Authentication disabled, skipping proxy auth manager initialization\");\n return;\n }\n\n if (!this.config.issuerUrl || !this.config.audience) {\n throw new Error(\"Issuer URL and Audience are required when auth is enabled\");\n }\n\n try {\n logger.info(\"🔐 Initializing OAuth2 proxy authentication...\");\n\n // Discover and cache the OAuth endpoints from the provider\n this.discoveredEndpoints = await this.discoverEndpoints();\n\n // Set up JWKS for JWT token validation if available\n if (this.discoveredEndpoints.jwksUri) {\n this.jwks = createRemoteJWKSet(new URL(this.discoveredEndpoints.jwksUri));\n logger.debug(`JWKS configured from: ${this.discoveredEndpoints.jwksUri}`);\n }\n\n // Log validation capabilities\n const capabilities = [];\n if (this.discoveredEndpoints.jwksUri) capabilities.push(\"JWT validation via JWKS\");\n if (this.discoveredEndpoints.userinfoUrl)\n capabilities.push(\"opaque token validation via userinfo\");\n logger.debug(`Token validation capabilities: ${capabilities.join(\", \")}`);\n\n if (capabilities.length === 0) {\n logger.warn(\n \"⚠️ No token validation mechanisms available - authentication may fail\",\n );\n }\n\n // Create the proxy provider\n this.proxyProvider = new ProxyOAuthServerProvider({\n endpoints: {\n authorizationUrl: this.discoveredEndpoints.authorizationUrl,\n tokenUrl: this.discoveredEndpoints.tokenUrl,\n revocationUrl: this.discoveredEndpoints.revocationUrl,\n registrationUrl: this.discoveredEndpoints.registrationUrl,\n },\n verifyAccessToken: this.verifyAccessToken.bind(this),\n getClient: this.getClient.bind(this),\n });\n\n logger.info(\"✅ OAuth2 proxy authentication initialized successfully\");\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown error\";\n logger.error(`❌ Failed to initialize OAuth2 proxy authentication: ${message}`);\n throw new Error(`Proxy authentication initialization failed: ${message}`);\n }\n }\n\n /**\n * Register OAuth2 endpoints on the Fastify server.\n * This manually implements the necessary OAuth2 endpoints using the proxy provider.\n */\n registerRoutes(server: FastifyInstance, baseUrl: URL): void {\n if (!this.proxyProvider) {\n throw new Error(\"Proxy provider not initialized\");\n }\n\n // OAuth2 Authorization Server Metadata (RFC 8414)\n server.get(\"/.well-known/oauth-authorization-server\", async (_request, reply) => {\n const metadata = {\n issuer: baseUrl.origin,\n authorization_endpoint: `${baseUrl.origin}/oauth/authorize`,\n token_endpoint: `${baseUrl.origin}/oauth/token`,\n revocation_endpoint: `${baseUrl.origin}/oauth/revoke`,\n registration_endpoint: `${baseUrl.origin}/oauth/register`,\n scopes_supported: [\"profile\", \"email\"],\n response_types_supported: [\"code\"],\n grant_types_supported: [\"authorization_code\", \"refresh_token\"],\n token_endpoint_auth_methods_supported: [\n \"client_secret_basic\",\n \"client_secret_post\",\n \"none\",\n ],\n code_challenge_methods_supported: [\"S256\"],\n };\n\n reply.type(\"application/json\").send(metadata);\n });\n\n // OAuth2 Protected Resource Metadata (RFC 9728)\n server.get(\"/.well-known/oauth-protected-resource\", async (request, reply) => {\n const baseUrl = `${request.protocol}://${request.headers.host}`;\n const metadata = {\n resource: `${baseUrl}/sse`,\n authorization_servers: [this.config.issuerUrl],\n scopes_supported: [\"profile\", \"email\"],\n bearer_methods_supported: [\"header\"],\n resource_name: \"Documentation MCP Server\",\n resource_documentation: \"https://github.com/arabold/docs-mcp-server#readme\",\n // Enhanced metadata for better discoverability\n resource_server_metadata_url: `${baseUrl}/.well-known/oauth-protected-resource`,\n authorization_server_metadata_url: `${this.config.issuerUrl}/.well-known/openid-configuration`,\n jwks_uri: `${this.config.issuerUrl}/.well-known/jwks.json`,\n // Supported MCP transports\n mcp_transports: [\n {\n transport: \"sse\",\n endpoint: `${baseUrl}/sse`,\n description: \"Server-Sent Events transport\",\n },\n {\n transport: \"http\",\n endpoint: `${baseUrl}/mcp`,\n description: \"Streaming HTTP transport\",\n },\n ],\n };\n\n reply.type(\"application/json\").send(metadata);\n });\n\n // OAuth2 Authorization endpoint\n server.get(\"/oauth/authorize\", async (request, reply) => {\n // In a proxy setup, redirect to the upstream authorization server\n const endpoints = await this.discoverEndpoints();\n const params = new URLSearchParams(request.query as Record<string, string>);\n\n // Add resource parameter (RFC 8707) for token binding\n if (!params.has(\"resource\")) {\n const resourceUrl = `${request.protocol}://${request.headers.host}/sse`;\n params.set(\"resource\", resourceUrl);\n }\n\n const redirectUrl = `${endpoints.authorizationUrl}?${params.toString()}`;\n reply.redirect(redirectUrl);\n });\n\n // OAuth2 Token endpoint\n server.post(\"/oauth/token\", async (request, reply) => {\n // Proxy token requests to the upstream server\n const endpoints = await this.discoverEndpoints();\n\n // Prepare token request body, preserving resource parameter if present\n const tokenBody = new URLSearchParams(request.body as Record<string, string>);\n\n // Add resource parameter if not already present (for backward compatibility)\n if (!tokenBody.has(\"resource\")) {\n const resourceUrl = `${request.protocol}://${request.headers.host}/sse`;\n tokenBody.set(\"resource\", resourceUrl);\n }\n\n const response = await fetch(endpoints.tokenUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: tokenBody.toString(),\n });\n\n const data = await response.json();\n reply.status(response.status).type(\"application/json\").send(data);\n });\n\n // OAuth2 Token Revocation endpoint\n server.post(\"/oauth/revoke\", async (request, reply) => {\n const endpoints = await this.discoverEndpoints();\n\n if (endpoints.revocationUrl) {\n const response = await fetch(endpoints.revocationUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\",\n },\n body: new URLSearchParams(request.body as Record<string, string>).toString(),\n });\n\n reply.status(response.status).send();\n } else {\n reply.status(404).send({ error: \"Revocation not supported\" });\n }\n });\n\n // OAuth2 Dynamic Client Registration endpoint\n server.post(\"/oauth/register\", async (request, reply) => {\n const endpoints = await this.discoverEndpoints();\n\n if (endpoints.registrationUrl) {\n const response = await fetch(endpoints.registrationUrl, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(request.body),\n });\n\n const data = await response.json();\n reply.status(response.status).type(\"application/json\").send(data);\n } else {\n reply.status(404).send({ error: \"Dynamic client registration not supported\" });\n }\n });\n\n logger.debug(\"OAuth2 endpoints registered on Fastify server\");\n }\n\n /**\n * Discover OAuth endpoints from the OAuth2 authorization server.\n * Uses OAuth2 discovery (RFC 8414) with OIDC discovery fallback.\n * Supports both JWT and opaque token validation methods.\n */\n private async discoverEndpoints() {\n // Try OAuth2 authorization server discovery first (RFC 8414)\n const oauthDiscoveryUrl = `${this.config.issuerUrl}/.well-known/oauth-authorization-server`;\n\n try {\n const oauthResponse = await fetch(oauthDiscoveryUrl);\n if (oauthResponse.ok) {\n const config = await oauthResponse.json();\n logger.debug(\n `Successfully discovered OAuth2 endpoints from: ${oauthDiscoveryUrl}`,\n );\n\n // Try to get userinfo endpoint from OIDC discovery as fallback for opaque tokens\n const userinfoEndpoint = await this.discoverUserinfoEndpoint();\n if (userinfoEndpoint) {\n config.userinfo_endpoint = userinfoEndpoint;\n }\n\n return this.buildEndpointsFromConfig(config);\n }\n } catch (error) {\n logger.debug(`OAuth2 discovery failed: ${error}, trying OIDC discovery`);\n }\n\n // Fallback to OIDC discovery\n const oidcDiscoveryUrl = `${this.config.issuerUrl}/.well-known/openid-configuration`;\n const oidcResponse = await fetch(oidcDiscoveryUrl);\n if (!oidcResponse.ok) {\n throw new Error(\n `Failed to fetch configuration from both ${oauthDiscoveryUrl} and ${oidcDiscoveryUrl}`,\n );\n }\n\n const config = await oidcResponse.json();\n logger.debug(`Successfully discovered OIDC endpoints from: ${oidcDiscoveryUrl}`);\n return this.buildEndpointsFromConfig(config);\n }\n\n /**\n * Try to discover userinfo endpoint for opaque token validation\n */\n private async discoverUserinfoEndpoint(): Promise<string | null> {\n try {\n const oidcDiscoveryUrl = `${this.config.issuerUrl}/.well-known/openid-configuration`;\n const response = await fetch(oidcDiscoveryUrl);\n if (response.ok) {\n const config = await response.json();\n return config.userinfo_endpoint || null;\n }\n } catch (error) {\n logger.debug(`Failed to fetch userinfo endpoint: ${error}`);\n }\n return null;\n }\n\n /**\n * Build endpoint configuration from discovery response.\n */\n private buildEndpointsFromConfig(config: Record<string, unknown>) {\n return {\n authorizationUrl: config.authorization_endpoint as string,\n tokenUrl: config.token_endpoint as string,\n revocationUrl: config.revocation_endpoint as string | undefined,\n registrationUrl: config.registration_endpoint as string | undefined,\n jwksUri: config.jwks_uri as string | undefined,\n userinfoUrl: config.userinfo_endpoint as string | undefined,\n };\n }\n\n /**\n * Get supported resource URLs for this MCP server instance.\n * This enables self-discovering resource validation per MCP Authorization spec.\n */\n private getSupportedResources(request: FastifyRequest): string[] {\n const baseUrl = `${request.protocol}://${request.headers.host}`;\n\n return [\n `${baseUrl}/sse`, // SSE transport\n `${baseUrl}/mcp`, // Streaming HTTP transport\n `${baseUrl}`, // Server root\n ];\n }\n\n /**\n * Verify an access token using hybrid validation approach.\n * First tries JWT validation with JWKS, falls back to userinfo endpoint for opaque tokens.\n * This provides universal compatibility with all OAuth2 providers and token formats.\n */\n private async verifyAccessToken(token: string, request?: FastifyRequest) {\n logger.debug(`Attempting to verify token: ${token.substring(0, 20)}...`);\n\n // Strategy 1: Try JWT validation first (more efficient for JWT tokens)\n if (this.jwks) {\n try {\n logger.debug(\"Attempting JWT validation with JWKS...\");\n const { payload } = await jwtVerify(token, this.jwks, {\n issuer: this.config.issuerUrl,\n audience: this.config.audience,\n });\n\n logger.debug(\n `JWT validation successful. Subject: ${payload.sub}, Audience: ${payload.aud}`,\n );\n\n if (!payload.sub) {\n throw new Error(\"JWT payload missing subject claim\");\n }\n\n return {\n token,\n clientId: payload.sub,\n scopes: [\"*\"], // Full access for all authenticated users\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n logger.debug(\n `JWT validation failed: ${errorMessage}, trying userinfo fallback...`,\n );\n // Continue to userinfo fallback\n }\n }\n\n // Strategy 2: Fallback to userinfo endpoint validation (works for opaque tokens)\n if (this.discoveredEndpoints?.userinfoUrl) {\n try {\n logger.debug(\"Attempting userinfo endpoint validation...\");\n const response = await fetch(this.discoveredEndpoints.userinfoUrl, {\n method: \"GET\",\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: \"application/json\",\n },\n });\n\n if (!response.ok) {\n throw new Error(\n `Userinfo request failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const userinfo = await response.json();\n logger.debug(\n `Token validation successful. User: ${userinfo.sub}, Email: ${userinfo.email}`,\n );\n\n if (!userinfo.sub) {\n throw new Error(\"Userinfo response missing subject\");\n }\n\n // Optional: Resource validation if MCP Authorization spec requires it\n if (request) {\n const supportedResources = this.getSupportedResources(request);\n logger.debug(`Supported resources: ${JSON.stringify(supportedResources)}`);\n // For now, we allow access if the token is valid - binary authentication\n }\n\n return {\n token,\n clientId: userinfo.sub,\n scopes: [\"*\"], // Full access for all authenticated users\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n logger.debug(`Userinfo validation failed: ${errorMessage}`);\n // Continue to final error\n }\n }\n\n // Both validation strategies failed\n logger.debug(\"All token validation strategies exhausted\");\n throw new Error(\"Invalid access token\");\n }\n\n /**\n * Get client information for the given client ID.\n * This is called by the proxy provider for client validation.\n */\n private async getClient(clientId: string) {\n // For now, return a basic client configuration\n // In a real implementation, you might look this up from a database\n return {\n client_id: clientId,\n redirect_uris: [`${this.config.audience}/callback`],\n // Add other client metadata as needed\n };\n }\n\n /**\n * Create an authentication context from a token (for compatibility with existing middleware).\n * Uses binary authentication - valid token grants full access.\n */\n async createAuthContext(\n authorization: string,\n request?: FastifyRequest,\n ): Promise<AuthContext> {\n if (!this.config.enabled) {\n return {\n authenticated: false,\n scopes: new Set(),\n };\n }\n\n try {\n logger.debug(\n `Processing authorization header: ${authorization.substring(0, 20)}...`,\n );\n\n const match = authorization.match(/^Bearer\\s+(.+)$/i);\n if (!match) {\n logger.debug(\"Authorization header does not match Bearer token pattern\");\n throw new Error(\"Invalid authorization header format\");\n }\n\n const token = match[1];\n logger.debug(`Extracted token: ${token.substring(0, 20)}...`);\n\n const authInfo = await this.verifyAccessToken(token, request);\n\n logger.debug(`Authentication successful for client: ${authInfo.clientId}`);\n\n // Binary authentication: valid token = full access\n return {\n authenticated: true,\n scopes: new Set([\"*\"]), // Full access for authenticated users\n subject: authInfo.clientId,\n };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : \"Unknown error\";\n logger.debug(`Authentication failed: ${errorMessage}`);\n return {\n authenticated: false,\n scopes: new Set(),\n };\n }\n }\n}\n","/**\n * Remote event proxy that subscribes to events from a remote tRPC worker\n * and re-emits them to the local EventBusService.\n *\n * This enables the web UI to receive events from remote workers transparently,\n * without needing to know about the remote worker's location or configuration.\n */\n\nimport {\n createTRPCClient,\n createWSClient,\n httpBatchLink,\n splitLink,\n wsLink,\n} from \"@trpc/client\";\nimport superjson from \"superjson\";\nimport { logger } from \"../utils/logger\";\nimport type { EventBusService } from \"./EventBusService\";\nimport type { EventType } from \"./types\";\n\n/**\n * Manages the connection to a remote worker and forwards its events locally.\n */\nexport class RemoteEventProxy {\n private trpcClient: ReturnType<typeof createTRPCClient> | null = null;\n private wsClient: ReturnType<typeof createWSClient> | null = null;\n private subscription: { unsubscribe: () => void } | null = null;\n private isConnected = false;\n\n constructor(\n private readonly remoteWorkerUrl: string,\n private readonly localEventBus: EventBusService,\n ) {}\n\n /**\n * Start subscribing to remote events and forwarding them locally.\n */\n async connect(): Promise<void> {\n if (this.isConnected) {\n logger.warn(\"Remote event proxy already connected\");\n return;\n }\n\n logger.debug(`Connecting to remote worker at ${this.remoteWorkerUrl}`);\n\n try {\n // Extract base URL without the /api path for WebSocket connection\n // The tRPC WebSocket adapter handles the /api routing internally\n const url = new URL(this.remoteWorkerUrl);\n const baseUrl = `${url.protocol}//${url.host}`;\n const wsUrl = baseUrl.replace(/^http/, \"ws\");\n\n // Create WebSocket client for subscriptions\n this.wsClient = createWSClient({\n url: wsUrl,\n });\n\n // Create tRPC client with split link:\n // - Subscriptions use WebSocket\n // - Queries and mutations use HTTP\n this.trpcClient = createTRPCClient({\n links: [\n splitLink({\n condition: (op) => op.type === \"subscription\",\n true: wsLink({ client: this.wsClient, transformer: superjson }),\n false: httpBatchLink({ url: this.remoteWorkerUrl, transformer: superjson }),\n }),\n ],\n });\n\n // Subscribe to all events from the remote worker\n // biome-ignore lint/suspicious/noExplicitAny: tRPC client type is generic\n this.subscription = (this.trpcClient as any).events.subscribe.subscribe(\n {}, // Subscribe to all event types\n {\n onData: (data: { type: EventType; payload: unknown }) => {\n logger.debug(`Received remote event: ${data.type}`);\n // Re-emit the event on the local event bus\n this.localEventBus.emit(data.type, data.payload as never);\n },\n onError: (error: Error) => {\n logger.error(`❌ Remote event subscription error: ${error}`);\n this.isConnected = false;\n this.scheduleReconnect();\n },\n onStarted: () => {\n logger.debug(\"Remote event subscription started\");\n this.isConnected = true;\n },\n onComplete: () => {\n logger.debug(\"Remote event subscription completed\");\n this.isConnected = false;\n },\n },\n );\n } catch (error) {\n logger.error(`❌ Failed to connect to remote worker: ${error}`);\n this.scheduleReconnect();\n }\n }\n\n /**\n * Disconnect from the remote worker and stop forwarding events.\n */\n disconnect(): void {\n if (this.subscription) {\n this.subscription.unsubscribe();\n this.subscription = null;\n }\n\n // Close WebSocket connection\n if (this.wsClient) {\n this.wsClient.close();\n this.wsClient = null;\n }\n\n this.isConnected = false;\n logger.info(\"🚫 Disconnected from remote worker\");\n }\n\n /**\n * Check if the proxy is currently connected to the remote worker.\n */\n isActive(): boolean {\n return this.isConnected;\n }\n\n /**\n * Schedule a reconnection attempt after a delay.\n */\n private scheduleReconnect(): void {\n logger.info(\"🔄 Scheduling reconnect to remote worker in 5 seconds...\");\n setTimeout(() => {\n if (!this.isConnected) {\n this.connect();\n }\n }, 5000);\n }\n}\n","export class ToolError extends Error {\n constructor(\n message: string,\n public readonly toolName: string,\n ) {\n super(message);\n this.name = this.constructor.name;\n }\n}\n\n/**\n * Error thrown when tool input validation fails.\n * This indicates a client-side issue with the request parameters.\n */\nexport class ValidationError extends ToolError {}\n","import type { CallToolResult } from \"@modelcontextprotocol/sdk/types.js\";\n\n/**\n * Creates a success response object in the format expected by the MCP server.\n * @param text The text content of the response.\n * @returns The response object.\n */\nexport function createResponse(text: string): CallToolResult {\n return {\n content: [\n {\n type: \"text\",\n text,\n },\n ],\n isError: false,\n };\n}\n\n/**\n * Creates an error response object in the format expected by the MCP server.\n * @param text The error message.\n * @returns The response object.\n */\nexport function createError(errorOrText: unknown): CallToolResult {\n const text = errorOrText instanceof Error ? errorOrText.message : String(errorOrText);\n return {\n content: [\n {\n type: \"text\",\n text,\n },\n ],\n isError: true,\n };\n}\n","import { McpServer, ResourceTemplate } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod/v3\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport { TelemetryEvent, telemetry } from \"../telemetry\";\nimport type { JobInfo } from \"../tools\";\nimport { ToolError } from \"../tools/errors\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport type { McpServerTools } from \"./tools\";\nimport { createError, createResponse } from \"./utils\";\n\n/**\n * Creates and configures an instance of the MCP server with registered tools and resources.\n * @param tools The shared tool instances to use for server operations.\n * @param config The application configuration.\n * @returns A configured McpServer instance.\n */\nexport function createMcpServerInstance(\n tools: McpServerTools,\n config: AppConfig,\n): McpServer {\n const readOnly = config.app.readOnly;\n const server = new McpServer(\n {\n name: \"docs-mcp-server\",\n version: \"0.1.0\",\n },\n {\n capabilities: {\n tools: {},\n resources: {},\n },\n },\n );\n\n // --- Tool Definitions ---\n\n // Only register write/job tools if not in read-only mode\n if (!readOnly) {\n // Scrape docs tool - suppress deep inference issues\n server.tool(\n \"scrape_docs\",\n \"Scrape and index documentation from a URL for a library. Use this tool to index a new library or a new version.\",\n {\n url: z.string().url().describe(\"Documentation root URL to scrape.\"),\n library: z.string().trim().describe(\"Library name.\"),\n version: z.string().trim().optional().describe(\"Library version (optional).\"),\n maxPages: z\n .number()\n .optional()\n .default(config.scraper.maxPages)\n .describe(\n `Maximum number of pages to scrape (default: ${config.scraper.maxPages}).`,\n ),\n maxDepth: z\n .number()\n .optional()\n .default(config.scraper.maxDepth)\n .describe(`Maximum navigation depth (default: ${config.scraper.maxDepth}).`),\n scope: z\n .enum([\"subpages\", \"hostname\", \"domain\"])\n .optional()\n .default(\"subpages\")\n .describe(\"Crawling boundary: 'subpages', 'hostname', or 'domain'.\"),\n followRedirects: z\n .boolean()\n .optional()\n .default(true)\n .describe(\"Follow HTTP redirects (3xx responses).\"),\n },\n {\n title: \"Scrape New Library Documentation\",\n destructiveHint: true, // replaces existing docs\n openWorldHint: true, // requires internet access\n },\n async ({ url, library, version, maxPages, maxDepth, scope, followRedirects }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"scrape_docs\",\n context: \"mcp_server\",\n library,\n version,\n url: new URL(url).hostname, // Privacy-safe URL tracking\n maxPages,\n maxDepth,\n scope,\n });\n\n try {\n // Execute scrape tool without waiting and without progress callback\n const result = await tools.scrape.execute({\n url,\n library,\n version,\n waitForCompletion: false, // Don't wait for completion\n // onProgress: undefined, // Explicitly undefined or omitted\n options: {\n maxPages,\n maxDepth,\n scope,\n followRedirects,\n },\n });\n\n // Check the type of result\n if (\"jobId\" in result) {\n // If we got a jobId back, report that\n return createResponse(`🚀 Scraping job started with ID: ${result.jobId}.`);\n }\n // This case shouldn't happen if waitForCompletion is false, but handle defensively\n return createResponse(\n `Scraping finished immediately (unexpectedly) with ${result.pagesScraped} pages.`,\n );\n } catch (error) {\n // Handle errors during job *enqueueing* or initial setup\n return createError(error);\n }\n },\n );\n\n // Refresh version tool - suppress deep inference issues\n server.tool(\n \"refresh_version\",\n \"Re-scrape a previously indexed library version, updating only changed pages.\",\n {\n library: z.string().trim().describe(\"Library name.\"),\n version: z\n .string()\n .trim()\n .optional()\n .describe(\"Library version (optional, refreshes latest if omitted).\"),\n },\n {\n title: \"Refresh Library Version\",\n destructiveHint: false, // Only updates changed content\n openWorldHint: true, // requires internet access\n },\n async ({ library, version }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"refresh_version\",\n context: \"mcp_server\",\n library,\n version,\n });\n\n try {\n // Execute refresh tool without waiting\n const result = await tools.refresh.execute({\n library,\n version,\n waitForCompletion: false, // Don't wait for completion\n });\n\n // Check the type of result\n if (\"jobId\" in result) {\n // If we got a jobId back, report that\n return createResponse(`🔄 Refresh job started with ID: ${result.jobId}.`);\n }\n // This case shouldn't happen if waitForCompletion is false, but handle defensively\n return createResponse(\n `Refresh finished immediately (unexpectedly) with ${result.pagesRefreshed} pages.`,\n );\n } catch (error) {\n // Handle errors during job enqueueing or initial setup\n return createError(error);\n }\n },\n );\n }\n\n // Search docs tool\n server.tool(\n \"search_docs\",\n \"Search up-to-date documentation for a library or package. Examples:\\n\\n\" +\n '- {library: \"react\", query: \"hooks lifecycle\"} -> matches latest version of React\\n' +\n '- {library: \"react\", version: \"18.0.0\", query: \"hooks lifecycle\"} -> matches React 18.0.0 or earlier\\n' +\n '- {library: \"typescript\", version: \"5.x\", query: \"ReturnType example\"} -> any TypeScript 5.x.x version\\n' +\n '- {library: \"typescript\", version: \"5.2.x\", query: \"ReturnType example\"} -> any TypeScript 5.2.x version',\n {\n library: z.string().trim().describe(\"Library name.\"),\n version: z\n .string()\n .trim()\n .optional()\n .describe(\"Library version (exact or X-Range, optional).\"),\n query: z.string().trim().describe(\"Documentation search query.\"),\n limit: z.number().optional().default(5).describe(\"Maximum number of results.\"),\n },\n {\n title: \"Search Library Documentation\",\n readOnlyHint: true,\n destructiveHint: false,\n },\n async ({ library, version, query, limit }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"search_docs\",\n context: \"mcp_server\",\n library,\n version,\n query: query.substring(0, 100), // Truncate query for privacy\n limit,\n });\n\n try {\n const result = await tools.search.execute({\n library,\n version,\n query,\n limit,\n exactMatch: false, // Always false for MCP interface\n });\n\n const formattedResults = result.results.map(\n (r: { url: string; content: string }, i: number) => `\n------------------------------------------------------------\nResult ${i + 1}: ${r.url}\n\n${r.content}\\n`,\n );\n\n if (formattedResults.length === 0) {\n return createResponse(\n `No results found for '${query}' in ${library}. Try to use a different or more general query.`,\n );\n }\n return createResponse(formattedResults.join(\"\"));\n } catch (error) {\n return createError(error);\n }\n },\n );\n\n // List libraries tool\n server.tool(\n \"list_libraries\",\n \"List all indexed libraries.\",\n {\n // no params\n },\n {\n title: \"List Libraries\",\n readOnlyHint: true,\n destructiveHint: false,\n },\n async () => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"list_libraries\",\n context: \"mcp_server\",\n });\n\n try {\n const result = await tools.listLibraries.execute();\n if (result.libraries.length === 0) {\n return createResponse(\"No libraries indexed yet.\");\n }\n\n return createResponse(\n `Indexed libraries:\\n\\n${result.libraries.map((lib: { name: string }) => `- ${lib.name}`).join(\"\\n\")}`,\n );\n } catch (error) {\n return createError(error);\n }\n },\n );\n\n // Find version tool\n server.tool(\n \"find_version\",\n \"Find the best matching version for a library. Use to identify available or closest versions.\",\n {\n library: z.string().trim().describe(\"Library name.\"),\n targetVersion: z\n .string()\n .trim()\n .optional()\n .describe(\"Version pattern to match (exact or X-Range, optional).\"),\n },\n {\n title: \"Find Library Version\",\n readOnlyHint: true,\n destructiveHint: false,\n },\n async ({ library, targetVersion }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"find_version\",\n context: \"mcp_server\",\n library,\n targetVersion,\n });\n\n try {\n const result = await tools.findVersion.execute({\n library,\n targetVersion,\n });\n\n // Tool now returns a structured object with message\n return createResponse(result.message);\n } catch (error) {\n return createError(error);\n }\n },\n );\n\n // Job and write tools - only available when not in read-only mode\n if (!readOnly) {\n // List jobs tool - suppress deep inference issues\n server.tool(\n \"list_jobs\",\n \"List all indexing jobs. Optionally filter by status.\",\n {\n status: z\n .enum([\"queued\", \"running\", \"completed\", \"failed\", \"cancelling\", \"cancelled\"])\n .optional()\n .describe(\"Filter jobs by status (optional).\"),\n },\n {\n title: \"List Indexing Jobs\",\n readOnlyHint: true,\n destructiveHint: false,\n },\n async ({ status }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"list_jobs\",\n context: \"mcp_server\",\n status,\n });\n\n try {\n const result = await tools.listJobs.execute({\n status: status as PipelineJobStatus | undefined,\n });\n // Format the simplified job list for display\n const formattedJobs = result.jobs\n .map(\n (job: JobInfo) =>\n `- ID: ${job.id}\\n Status: ${job.status}\\n Library: ${job.library}\\n Version: ${job.version}\\n Created: ${job.createdAt}${job.startedAt ? `\\n Started: ${job.startedAt}` : \"\"}${job.finishedAt ? `\\n Finished: ${job.finishedAt}` : \"\"}${job.error ? `\\n Error: ${job.error}` : \"\"}`,\n )\n .join(\"\\n\\n\");\n return createResponse(\n result.jobs.length > 0\n ? `Current Jobs:\\n\\n${formattedJobs}`\n : \"No jobs found.\",\n );\n } catch (error) {\n return createError(error);\n }\n },\n );\n\n // Get job info tool\n server.tool(\n \"get_job_info\",\n \"Get details for a specific indexing job. Use the 'list_jobs' tool to find the job ID.\",\n {\n jobId: z.string().uuid().describe(\"Job ID to query.\"),\n },\n {\n title: \"Get Indexing Job Info\",\n readOnlyHint: true,\n destructiveHint: false,\n },\n async ({ jobId }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"get_job_info\",\n context: \"mcp_server\",\n jobId,\n });\n\n try {\n const result = await tools.getJobInfo.execute({ jobId });\n // Tool now guarantees result.job is always present on success\n const job = result.job;\n const formattedJob = `- ID: ${job.id}\\n Status: ${job.status}\\n Library: ${job.library}@${job.version}\\n Created: ${job.createdAt}${job.startedAt ? `\\n Started: ${job.startedAt}` : \"\"}${job.finishedAt ? `\\n Finished: ${job.finishedAt}` : \"\"}${job.error ? `\\n Error: ${job.error}` : \"\"}`;\n return createResponse(`Job Info:\\n\\n${formattedJob}`);\n } catch (error) {\n // Tool now throws error when job not found\n return createError(error);\n }\n },\n );\n\n // Cancel job tool\n server.tool(\n \"cancel_job\",\n \"Cancel a queued or running indexing job. Use the 'list_jobs' tool to find the job ID.\",\n {\n jobId: z.string().uuid().describe(\"Job ID to cancel.\"),\n },\n {\n title: \"Cancel Indexing Job\",\n destructiveHint: true,\n },\n async ({ jobId }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"cancel_job\",\n context: \"mcp_server\",\n jobId,\n });\n\n try {\n const result = await tools.cancelJob.execute({ jobId });\n // Tool now always returns success data or throws error\n return createResponse(result.message);\n } catch (error) {\n // Catch any errors thrown by the tool (job not found, cancellation failed, etc.)\n return createError(error);\n }\n },\n );\n\n // Remove docs tool\n server.tool(\n \"remove_docs\",\n \"Remove indexed documentation for a library version. Use only if explicitly instructed.\",\n {\n library: z.string().trim().describe(\"Library name.\"),\n version: z\n .string()\n .trim()\n .optional()\n .describe(\"Library version (optional, removes latest if omitted).\"),\n },\n {\n title: \"Remove Library Documentation\",\n destructiveHint: true,\n },\n async ({ library, version }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"remove_docs\",\n context: \"mcp_server\",\n library,\n version,\n });\n\n try {\n // Execute the remove tool logic\n const result = await tools.remove.execute({ library, version });\n // Use the message from the tool's successful execution\n return createResponse(result.message);\n } catch (error) {\n // Catch errors thrown by the RemoveTool's execute method\n return createError(error);\n }\n },\n );\n }\n\n // Fetch URL tool\n server.tool(\n \"fetch_url\",\n \"Fetch a single URL and convert its content to Markdown. Use this tool to read the content of any web page.\",\n {\n url: z.string().url().describe(\"URL to fetch and convert to Markdown.\"),\n followRedirects: z\n .boolean()\n .optional()\n .default(true)\n .describe(\"Follow HTTP redirects (3xx responses).\"),\n },\n {\n title: \"Fetch URL\",\n readOnlyHint: true,\n destructiveHint: false,\n openWorldHint: true, // requires internet access\n },\n async ({ url, followRedirects }) => {\n // Track MCP tool usage\n telemetry.track(TelemetryEvent.TOOL_USED, {\n tool: \"fetch_url\",\n context: \"mcp_server\",\n url: new URL(url).hostname, // Privacy-safe URL tracking\n followRedirects,\n });\n\n try {\n const result = await tools.fetchUrl.execute({ url, followRedirects });\n return createResponse(result);\n } catch (error) {\n return createError(error);\n }\n },\n );\n\n server.resource(\n \"libraries\",\n \"docs://libraries\",\n {\n description: \"List all indexed libraries\",\n },\n async (uri: URL) => {\n const result = await tools.listLibraries.execute();\n\n return {\n contents: result.libraries.map((lib: { name: string }) => ({\n uri: new URL(lib.name, uri).href,\n text: lib.name,\n })),\n };\n },\n );\n\n server.resource(\n \"versions\",\n new ResourceTemplate(\"docs://libraries/{library}/versions\", {\n list: undefined,\n }),\n {\n description: \"List all indexed versions for a library\",\n },\n async (uri: URL, { library }) => {\n const result = await tools.listLibraries.execute();\n\n const lib = result.libraries.find((l: { name: string }) => l.name === library);\n if (!lib) {\n return { contents: [] };\n }\n\n return {\n contents: lib.versions.map((v: { version: string }) => ({\n uri: new URL(v.version, uri).href,\n text: v.version,\n })),\n };\n },\n );\n\n // Job-related resources - only available when not in read-only mode\n if (!readOnly) {\n /**\n * Resource handler for listing pipeline jobs.\n * Supports filtering by status via a query parameter (e.g., ?status=running).\n * URI: docs://jobs[?status=<status>]\n */\n server.resource(\n \"jobs\",\n \"docs://jobs\",\n {\n description: \"List indexing jobs, optionally filtering by status.\",\n mimeType: \"application/json\",\n },\n async (uri: URL) => {\n const statusParam = uri.searchParams.get(\"status\");\n let statusFilter: PipelineJobStatus | undefined;\n\n // Validate status parameter if provided\n if (statusParam) {\n const validation = z.nativeEnum(PipelineJobStatus).safeParse(statusParam);\n if (validation.success) {\n statusFilter = validation.data;\n } else {\n // Handle invalid status - perhaps return an error or ignore?\n // For simplicity, let's ignore invalid status for now and return all jobs.\n // Alternatively, could throw an McpError or return specific error content.\n logger.warn(`⚠️ Invalid status parameter received: ${statusParam}`);\n }\n }\n\n // Fetch simplified jobs using the ListJobsTool\n const result = await tools.listJobs.execute({ status: statusFilter });\n\n return {\n contents: result.jobs.map((job) => ({\n uri: new URL(job.id, uri).href,\n mimeType: \"application/json\",\n text: JSON.stringify({\n id: job.id,\n library: job.library,\n version: job.version,\n status: job.status,\n error: job.error || undefined,\n }),\n })),\n };\n },\n );\n\n /**\n * Resource handler for retrieving a specific pipeline job by its ID.\n * URI Template: docs://jobs/{jobId}\n */\n server.resource(\n \"job\", // A distinct name for this specific resource type\n new ResourceTemplate(\"docs://jobs/{jobId}\", { list: undefined }),\n {\n description: \"Get details for a specific indexing job by ID.\",\n mimeType: \"application/json\",\n },\n async (uri: URL, { jobId }) => {\n // Validate jobId format if necessary (basic check)\n if (typeof jobId !== \"string\" || jobId.length === 0) {\n // Handle invalid jobId format - return empty or error\n logger.warn(`⚠️ Invalid jobId received in URI: ${jobId}`);\n return { contents: [] }; // Return empty content for invalid ID format\n }\n\n try {\n // Fetch the simplified job info using GetJobInfoTool\n const result = await tools.getJobInfo.execute({ jobId });\n\n // Tool now guarantees result.job is always present on success\n return {\n contents: [\n {\n uri: uri.href,\n mimeType: \"application/json\",\n text: JSON.stringify({\n id: result.job.id,\n library: result.job.library,\n version: result.job.version,\n status: result.job.status,\n error: result.job.error || undefined,\n }),\n },\n ],\n };\n } catch (error) {\n if (error instanceof ToolError) {\n // Expected error (job not found, etc.)\n logger.warn(`⚠️ Job not found for resource request: ${jobId}`);\n } else {\n // Unexpected error\n logger.error(\n `❌ Unexpected error in job resource handler: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n return { contents: [] };\n }\n },\n );\n }\n\n return server;\n}\n","class ScraperError extends Error {\n constructor(\n message: string,\n public readonly isRetryable: boolean = false,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = this.constructor.name;\n if (cause?.stack) {\n this.stack = `${this.stack}\\nCaused by: ${cause.stack}`;\n }\n }\n}\n\nclass InvalidUrlError extends ScraperError {\n constructor(url: string, cause?: Error) {\n super(`Invalid URL: ${url}`, false, cause);\n }\n}\n\nclass RedirectError extends ScraperError {\n constructor(\n public readonly originalUrl: string,\n public readonly redirectUrl: string,\n public readonly statusCode: number,\n ) {\n super(\n `Redirect detected from ${originalUrl} to ${redirectUrl} (status: ${statusCode})`,\n false,\n );\n }\n}\n\nclass ChallengeError extends ScraperError {\n constructor(\n public readonly url: string,\n public readonly statusCode: number,\n public readonly challengeType: string,\n ) {\n super(\n `Challenge detected for ${url} (status: ${statusCode}, type: ${challengeType})`,\n false,\n );\n }\n}\n\nexport { ChallengeError, InvalidUrlError, RedirectError, ScraperError };\n","import mime from \"mime\";\n\n/**\n * Represents a parsed Content-Type header.\n */\nexport interface ParsedContentType {\n mimeType: string;\n charset?: string;\n}\n\n/**\n * Enhanced MIME type detection and utility functions.\n * Combines standard MIME type operations with enhanced source code detection.\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: helpers are static\nexport class MimeTypeUtils {\n /**\n * Parses a Content-Type header string into its MIME type and charset.\n * @param contentTypeHeader The Content-Type header string (e.g., \"text/html; charset=utf-8\").\n * @returns A ParsedContentType object, or a default if parsing fails.\n */\n public static parseContentType(contentTypeHeader?: string | null): ParsedContentType {\n if (!contentTypeHeader) {\n return { mimeType: \"application/octet-stream\" };\n }\n const parts = contentTypeHeader.split(\";\").map((part) => part.trim());\n const mimeType = parts[0].toLowerCase();\n let charset: string | undefined;\n\n for (let i = 1; i < parts.length; i++) {\n const param = parts[i];\n if (param.toLowerCase().startsWith(\"charset=\")) {\n charset = param.substring(\"charset=\".length).toLowerCase();\n break;\n }\n }\n return { mimeType, charset };\n }\n\n /**\n * Checks if a MIME type represents HTML content.\n */\n public static isHtml(mimeType: string): boolean {\n return mimeType === \"text/html\" || mimeType === \"application/xhtml+xml\";\n }\n\n /**\n * Checks if a MIME type represents Markdown content.\n */\n public static isMarkdown(mimeType: string): boolean {\n return (\n mimeType === \"text/markdown\" ||\n mimeType === \"text/x-markdown\" ||\n mimeType === \"text/mdx\" ||\n mimeType === \"text/x-gfm\"\n );\n }\n\n /**\n * Checks if a MIME type represents plain text content.\n * This includes basic text/* types but excludes structured formats like JSON, XML, etc.\n */\n public static isText(mimeType: string): boolean {\n if (!mimeType) {\n return false;\n }\n\n const normalizedMimeType = mimeType.toLowerCase();\n\n // Accept basic text/* types, but exclude structured formats that have specific pipelines\n if (normalizedMimeType.startsWith(\"text/\")) {\n // Exclude structured text formats that should go to specific pipelines\n if (\n MimeTypeUtils.isJson(normalizedMimeType) ||\n MimeTypeUtils.isMarkdown(normalizedMimeType)\n ) {\n return false;\n }\n return true;\n }\n\n return false;\n }\n\n /**\n * Checks if a MIME type represents content that is safe for text processing.\n * This includes all text/* types and specific application types that are text-based.\n * Used by TextPipeline as a fallback for content that other pipelines don't handle.\n */\n public static isSafeForTextProcessing(mimeType: string): boolean {\n if (!mimeType) {\n return false;\n }\n\n const normalizedMimeType = mimeType.toLowerCase();\n\n // Accept all text/* types\n if (normalizedMimeType.startsWith(\"text/\")) {\n return true;\n }\n\n // Accept JSON content (when not handled by JsonPipeline)\n if (MimeTypeUtils.isJson(normalizedMimeType)) {\n return true;\n }\n\n // Accept source code types (when not handled by SourceCodePipeline)\n if (MimeTypeUtils.isSourceCode(normalizedMimeType)) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Checks if a MIME type represents JSON content.\n */\n public static isJson(mimeType: string): boolean {\n return (\n mimeType === \"application/json\" ||\n mimeType === \"text/json\" ||\n mimeType === \"text/x-json\"\n );\n }\n\n /**\n * Checks if a MIME type represents PDF content.\n */\n public static isPdf(mimeType: string): boolean {\n return mimeType === \"application/pdf\";\n }\n\n /**\n * Checks if a MIME type represents a modern Office document (DOCX, XLSX, PPTX).\n */\n public static isOfficeDocument(mimeType: string): boolean {\n return (\n mimeType ===\n \"application/vnd.openxmlformats-officedocument.wordprocessingml.document\" ||\n mimeType === \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" ||\n mimeType ===\n \"application/vnd.openxmlformats-officedocument.presentationml.presentation\"\n );\n }\n\n /**\n * Checks if a MIME type represents a legacy Office document (DOC, XLS, PPT).\n */\n public static isLegacyOfficeDocument(mimeType: string): boolean {\n return (\n mimeType === \"application/msword\" ||\n mimeType === \"application/vnd.ms-excel\" ||\n mimeType === \"application/vnd.ms-powerpoint\"\n );\n }\n\n /**\n * Checks if a MIME type represents an OpenDocument format (ODT, ODS, ODP).\n */\n public static isOpenDocument(mimeType: string): boolean {\n return (\n mimeType === \"application/vnd.oasis.opendocument.text\" ||\n mimeType === \"application/vnd.oasis.opendocument.spreadsheet\" ||\n mimeType === \"application/vnd.oasis.opendocument.presentation\"\n );\n }\n\n /**\n * Checks if a MIME type represents RTF content.\n */\n public static isRtf(mimeType: string): boolean {\n return mimeType === \"application/rtf\" || mimeType === \"text/rtf\";\n }\n\n /**\n * Checks if a MIME type represents an eBook format (EPUB, FB2).\n */\n public static isEbook(mimeType: string): boolean {\n return (\n mimeType === \"application/epub+zip\" || mimeType === \"application/x-fictionbook+xml\"\n );\n }\n\n /**\n * Checks if a MIME type represents a Jupyter Notebook.\n */\n public static isJupyterNotebook(mimeType: string): boolean {\n return mimeType === \"application/x-ipynb+json\";\n }\n\n /**\n * Checks if a MIME type represents a document that can be processed\n * by the DocumentPipeline (PDF, Office docs, OpenDocument, RTF, eBooks,\n * Jupyter notebooks).\n */\n public static isSupportedDocument(mimeType: string): boolean {\n return (\n MimeTypeUtils.isPdf(mimeType) ||\n MimeTypeUtils.isOfficeDocument(mimeType) ||\n MimeTypeUtils.isLegacyOfficeDocument(mimeType) ||\n MimeTypeUtils.isOpenDocument(mimeType) ||\n MimeTypeUtils.isRtf(mimeType) ||\n MimeTypeUtils.isEbook(mimeType) ||\n MimeTypeUtils.isJupyterNotebook(mimeType)\n );\n }\n\n /**\n * Checks if a MIME type represents source code that should be wrapped in code blocks.\n */\n public static isSourceCode(mimeType: string): boolean {\n return MimeTypeUtils.extractLanguageFromMimeType(mimeType) !== \"\";\n }\n\n /**\n * Checks if content appears to be binary based on the presence of null bytes.\n * This is a reliable heuristic since text files should not contain null bytes.\n * @param content The content to check (string or Buffer)\n * @returns true if the content appears to be binary\n */\n public static isBinary(content: string | Buffer): boolean {\n if (typeof content === \"string\") {\n return content.includes(\"\\0\");\n }\n\n // For Buffer, check for null bytes directly\n return content.includes(0);\n }\n\n /**\n * Detects MIME type from file path or URL, with special handling for common source code\n * extensions that the mime package doesn't handle well or gets wrong.\n *\n * Query parameters and hash fragments are stripped before extension detection, so URLs\n * like `https://cdn.example.com/report.pdf?token=abc#page=1` are handled correctly.\n *\n * @param filePath - The file path or URL to detect MIME type for\n * @returns The detected MIME type or null if unknown\n */\n public static detectMimeTypeFromPath(filePath: string): string | null {\n // Strip query parameters and hash fragments that may be present in URLs\n // (e.g., \"report.pdf?token=abc\" or \"doc.html#section\")\n const cleanPath = filePath.split(\"?\")[0].split(\"#\")[0];\n const extension = cleanPath.toLowerCase().split(\".\").pop();\n\n // Handle common source code extensions that mime package gets wrong or doesn't know.\n // See openspec/changes/refactor-mime-type-detection/design.md for full documentation.\n const customMimeTypes: Record<string, string> = {\n // JavaScript/TypeScript family\n ts: \"text/x-typescript\",\n tsx: \"text/x-tsx\",\n mts: \"text/x-typescript\", // TypeScript ES modules\n cts: \"text/x-typescript\", // TypeScript CommonJS modules\n js: \"text/javascript\",\n jsx: \"text/x-jsx\",\n cjs: \"text/javascript\", // CommonJS modules\n mjs: \"text/javascript\", // ES modules\n\n // Python family\n py: \"text/x-python\",\n pyw: \"text/x-python\",\n pyi: \"text/x-python\",\n pyx: \"text/x-cython\", // Cython\n pxd: \"text/x-cython\", // Cython\n\n // Systems languages\n go: \"text/x-go\",\n rs: \"text/x-rust\",\n c: \"text/x-csrc\",\n h: \"text/x-chdr\",\n cpp: \"text/x-c++src\",\n cxx: \"text/x-c++src\",\n cc: \"text/x-c++src\",\n hpp: \"text/x-c++hdr\",\n hxx: \"text/x-c++hdr\",\n zig: \"text/x-zig\",\n nim: \"text/x-nim\",\n v: \"text/x-v\",\n cr: \"text/x-crystal\",\n\n // JVM languages\n kt: \"text/x-kotlin\",\n kts: \"text/x-kotlin\", // Kotlin script\n scala: \"text/x-scala\",\n groovy: \"text/x-groovy\",\n gradle: \"text/x-gradle\",\n\n // Apple/Mobile\n swift: \"text/x-swift\",\n dart: \"text/x-dart\",\n\n // Scripting languages\n rb: \"text/x-ruby\",\n rake: \"text/x-ruby\", // Rakefile\n php: \"text/x-php\",\n lua: \"text/x-lua\",\n pl: \"text/x-perl\",\n pm: \"text/x-perl\",\n r: \"text/x-r\", // Also handles .R since extension is lowercased\n\n // Functional languages\n hs: \"text/x-haskell\",\n lhs: \"text/x-haskell\", // Literate Haskell\n elm: \"text/x-elm\",\n erl: \"text/x-erlang\",\n ex: \"text/x-elixir\",\n exs: \"text/x-elixir\",\n clj: \"text/x-clojure\",\n cljs: \"text/x-clojure\",\n cljc: \"text/x-clojure\",\n jl: \"text/x-julia\",\n\n // .NET\n cs: \"text/x-csharp\",\n\n // Web3/Smart contracts\n sol: \"text/x-solidity\",\n move: \"text/x-move\",\n cairo: \"text/x-cairo\",\n\n // Modern web frameworks\n vue: \"text/x-vue\",\n svelte: \"text/x-svelte\",\n astro: \"text/x-astro\",\n\n // Shell scripting\n sh: \"text/x-shellscript\",\n bash: \"text/x-shellscript\",\n zsh: \"text/x-shellscript\",\n fish: \"text/x-shellscript\",\n ps1: \"text/x-powershell\",\n\n // Documentation formats\n markdown: \"text/markdown\",\n mdx: \"text/mdx\",\n gfm: \"text/x-gfm\",\n mkd: \"text/markdown\",\n mkdn: \"text/markdown\",\n mkdown: \"text/markdown\",\n mdown: \"text/markdown\",\n mdwn: \"text/markdown\",\n ronn: \"text/markdown\",\n rst: \"text/x-rst\", // reStructuredText\n adoc: \"text/x-asciidoc\",\n asciidoc: \"text/x-asciidoc\",\n textile: \"text/x-textile\",\n org: \"text/x-org\", // Org-mode\n pod: \"text/x-pod\", // Perl documentation\n rdoc: \"text/x-rdoc\", // Ruby documentation\n wiki: \"text/x-wiki\",\n rmd: \"text/x-rmarkdown\", // R Markdown\n\n // Configuration files\n toml: \"text/x-toml\",\n ini: \"text/x-ini\",\n cfg: \"text/x-ini\",\n conf: \"text/x-conf\",\n properties: \"text/x-properties\",\n env: \"text/x-dotenv\",\n\n // Build systems\n dockerfile: \"text/x-dockerfile\",\n containerfile: \"text/x-dockerfile\",\n makefile: \"text/x-makefile\",\n cmake: \"text/x-cmake\",\n bazel: \"text/x-bazel\",\n bzl: \"text/x-bazel\",\n buck: \"text/x-buck\",\n\n // Infrastructure as Code\n tf: \"text/x-terraform\",\n tfvars: \"text/x-terraform\",\n hcl: \"text/x-hcl\",\n\n // Data/Query languages\n sql: \"text/x-sql\",\n graphql: \"text/x-graphql\",\n gql: \"text/x-graphql\",\n\n // Schema/API definitions\n proto: \"text/x-proto\",\n prisma: \"text/x-prisma\",\n thrift: \"text/x-thrift\",\n avro: \"text/x-avro\",\n\n // TeX/LaTeX\n tex: \"text/x-tex\",\n latex: \"text/x-latex\",\n\n // Document formats (ensure correct detection for DocumentPipeline)\n doc: \"application/msword\",\n xls: \"application/vnd.ms-excel\",\n ppt: \"application/vnd.ms-powerpoint\",\n odt: \"application/vnd.oasis.opendocument.text\",\n ods: \"application/vnd.oasis.opendocument.spreadsheet\",\n odp: \"application/vnd.oasis.opendocument.presentation\",\n rtf: \"application/rtf\",\n epub: \"application/epub+zip\",\n fb2: \"application/x-fictionbook+xml\",\n };\n\n if (extension && customMimeTypes[extension]) {\n return customMimeTypes[extension];\n }\n\n // Fall back to the mime package for other types\n const detectedType = mime.getType(cleanPath);\n\n // Normalize problematic MIME types that the mime package gets wrong\n return MimeTypeUtils.normalizeMimeType(detectedType);\n }\n\n /**\n * Normalizes MIME types that are incorrectly detected by the mime package.\n * This handles cases like 'application/node' for .cjs files.\n *\n * @param mimeType - The MIME type to normalize\n * @returns The normalized MIME type\n */\n public static normalizeMimeType(mimeType: string | null): string | null {\n if (!mimeType) {\n return null;\n }\n\n // Map problematic MIME types to correct ones.\n // These are defense-in-depth for external MIME types (e.g., HTTP Content-Type headers).\n // Extensions are checked first in customMimeTypes, so these mostly apply to external sources.\n const mimeTypeNormalization: Record<string, string> = {\n \"application/node\": \"text/javascript\", // .cjs files\n \"video/mp2t\": \"text/x-typescript\", // .ts/.mts files (MPEG-2 transport stream conflict)\n \"application/rls-services+xml\": \"text/x-rust\", // .rs files\n \"application/vnd.lotus-organizer\": \"text/x-org\", // .org files (Lotus Organizer conflict)\n \"application/vnd.dart\": \"text/x-dart\", // .dart files\n \"application/x-perl\": \"text/x-perl\", // .pl/.pm files\n \"application/x-tex\": \"text/x-tex\", // .tex files\n \"application/x-latex\": \"text/x-latex\", // .latex files\n \"application/toml\": \"text/x-toml\", // .toml files\n };\n\n return mimeTypeNormalization[mimeType] || mimeType;\n }\n\n /**\n * Extracts the programming language identifier from a MIME type for code block formatting.\n *\n * @param mimeType - The MIME type to extract language from\n * @returns The language identifier (e.g., \"typescript\", \"python\") or empty string if unknown\n */\n public static extractLanguageFromMimeType(mimeType: string): string {\n const mimeToLanguage: Record<string, string> = {\n // JavaScript/TypeScript\n \"text/x-typescript\": \"typescript\",\n \"text/typescript\": \"typescript\",\n \"application/typescript\": \"typescript\",\n \"text/x-tsx\": \"tsx\",\n \"text/javascript\": \"javascript\",\n \"application/javascript\": \"javascript\",\n \"application/x-javascript\": \"javascript\",\n \"text/x-jsx\": \"jsx\",\n\n // Python\n \"text/x-python\": \"python\",\n \"text/x-cython\": \"cython\",\n\n // Systems languages\n \"text/x-c\": \"c\",\n \"text/x-csrc\": \"c\",\n \"text/x-chdr\": \"c\",\n \"text/x-c++\": \"cpp\",\n \"text/x-c++src\": \"cpp\",\n \"text/x-c++hdr\": \"cpp\",\n \"text/x-go\": \"go\",\n \"text/x-rust\": \"rust\",\n \"text/x-zig\": \"zig\",\n \"text/x-nim\": \"nim\",\n \"text/x-v\": \"v\",\n \"text/x-crystal\": \"crystal\",\n\n // JVM languages\n \"text/x-java\": \"java\",\n \"text/x-kotlin\": \"kotlin\",\n \"text/x-scala\": \"scala\",\n \"text/x-groovy\": \"groovy\",\n \"text/x-gradle\": \"groovy\",\n\n // Apple/Mobile\n \"text/x-swift\": \"swift\",\n \"text/x-dart\": \"dart\",\n\n // .NET\n \"text/x-csharp\": \"csharp\",\n\n // Scripting languages\n \"text/x-ruby\": \"ruby\",\n \"text/x-php\": \"php\",\n \"text/x-lua\": \"lua\",\n \"text/x-perl\": \"perl\",\n \"text/x-r\": \"r\",\n\n // Functional languages\n \"text/x-haskell\": \"haskell\",\n \"text/x-elm\": \"elm\",\n \"text/x-erlang\": \"erlang\",\n \"text/x-elixir\": \"elixir\",\n \"text/x-clojure\": \"clojure\",\n \"text/x-julia\": \"julia\",\n\n // Web3/Smart contracts\n \"text/x-solidity\": \"solidity\",\n \"text/x-move\": \"move\",\n \"text/x-cairo\": \"cairo\",\n\n // Modern web frameworks\n \"text/x-vue\": \"vue\",\n \"text/x-svelte\": \"svelte\",\n \"text/x-astro\": \"astro\",\n\n // Stylesheets\n \"text/css\": \"css\",\n \"text/x-scss\": \"scss\",\n \"text/x-sass\": \"sass\",\n \"text/less\": \"less\",\n\n // Shell\n \"text/x-sh\": \"bash\",\n \"text/x-shellscript\": \"bash\",\n \"application/x-sh\": \"bash\",\n \"text/x-powershell\": \"powershell\",\n\n // Documentation formats\n \"text/x-rst\": \"rst\",\n \"text/x-asciidoc\": \"asciidoc\",\n \"text/x-textile\": \"textile\",\n \"text/x-org\": \"org\",\n \"text/x-pod\": \"pod\",\n \"text/x-rdoc\": \"rdoc\",\n \"text/x-wiki\": \"wiki\",\n \"text/x-rmarkdown\": \"rmarkdown\",\n\n // Configuration files\n \"text/x-toml\": \"toml\",\n \"text/x-ini\": \"ini\",\n \"text/x-conf\": \"conf\",\n \"text/x-properties\": \"properties\",\n \"text/x-dotenv\": \"dotenv\",\n\n // Build systems\n \"text/x-dockerfile\": \"dockerfile\",\n \"text/x-makefile\": \"makefile\",\n \"text/x-cmake\": \"cmake\",\n \"text/x-bazel\": \"bazel\",\n \"text/x-buck\": \"buck\",\n\n // Infrastructure as Code\n \"text/x-terraform\": \"hcl\",\n \"text/x-hcl\": \"hcl\",\n\n // Data formats\n \"text/x-yaml\": \"yaml\",\n \"text/yaml\": \"yaml\",\n \"application/x-yaml\": \"yaml\",\n \"application/yaml\": \"yaml\",\n \"text/x-json\": \"json\",\n \"application/json\": \"json\",\n \"text/x-xml\": \"xml\",\n \"text/xml\": \"xml\",\n \"application/xml\": \"xml\",\n \"text/x-sql\": \"sql\",\n \"text/x-graphql\": \"graphql\",\n\n // Schema/API definitions\n \"text/x-proto\": \"protobuf\",\n \"text/x-prisma\": \"prisma\",\n \"text/x-thrift\": \"thrift\",\n \"text/x-avro\": \"avro\",\n\n // TeX/LaTeX\n \"text/x-tex\": \"tex\",\n \"text/x-latex\": \"latex\",\n };\n\n return mimeToLanguage[mimeType] || \"\";\n }\n}\n","import { HeaderGenerator, type HeaderGeneratorOptions } from \"header-generator\";\n\n/**\n * Generates realistic browser-like HTTP headers to help avoid bot detection.\n * Uses the `header-generator` library for header generation.\n */\nexport class FingerprintGenerator {\n private headerGenerator: HeaderGenerator;\n\n /**\n * Creates an instance of FingerprintGenerator.\n * @param options Optional configuration for the header generator.\n */\n constructor(options?: Partial<HeaderGeneratorOptions>) {\n // Default options for a broad range of realistic headers\n const defaultOptions: Partial<HeaderGeneratorOptions> = {\n browsers: [{ name: \"chrome\", minVersion: 100 }, \"firefox\", \"safari\"],\n devices: [\"desktop\", \"mobile\"],\n operatingSystems: [\"windows\", \"linux\", \"macos\", \"android\", \"ios\"],\n locales: [\"en-US\", \"en\"],\n httpVersion: \"2\",\n };\n\n this.headerGenerator = new HeaderGenerator({\n ...defaultOptions,\n ...options,\n });\n }\n\n /**\n * Generates a set of realistic HTTP headers.\n * @returns A set of realistic HTTP headers.\n */\n generateHeaders(): Record<string, string> {\n return this.headerGenerator.getHeaders();\n }\n}\n","/**\n * Semantic status of a fetch operation, abstracting HTTP status codes\n * into meaningful states for content processing.\n */\nexport enum FetchStatus {\n /**\n * Content was successfully fetched (HTTP 200 or new file).\n * The content field will contain the fetched data.\n */\n SUCCESS = \"success\",\n\n /**\n * Content has not been modified since the last fetch (HTTP 304).\n * The content field will be empty. Occurs when etag is provided\n * in FetchOptions and matches the server's current ETag.\n */\n NOT_MODIFIED = \"not_modified\",\n\n /**\n * The resource was not found (HTTP 404 or file doesn't exist).\n * The content field will be empty. In refresh operations,\n * this indicates the page should be removed from the index.\n */\n NOT_FOUND = \"not_found\",\n}\n\n/**\n * Raw content fetched from a source before processing.\n * Includes metadata about the content for proper processing.\n */\nexport interface RawContent {\n /** Raw content as string or buffer */\n content: string | Buffer;\n /**\n * MIME type of the content (e.g., \"text/html\", \"application/json\").\n * Does not include parameters like charset.\n */\n mimeType: string;\n /**\n * Character set of the content (e.g., \"utf-8\"), extracted from Content-Type header.\n */\n charset?: string;\n /**\n * Content encoding (e.g., \"gzip\", \"deflate\"), from Content-Encoding header.\n */\n encoding?: string;\n /** Original source location */\n source: string;\n /**\n * ETag value for caching purposes.\n * For HTTP sources, this comes from the ETag header.\n * For local files, this is a hash of the last modified date.\n */\n etag?: string;\n /**\n * Last modified timestamp in ISO8601 format.\n * For HTTP sources, this comes from the Last-Modified header.\n * For local files, this is the file modification time.\n */\n lastModified?: string;\n /**\n * Semantic status of the fetch operation.\n * Abstracts HTTP status codes into meaningful states:\n * - SUCCESS: Content was fetched successfully\n * - NOT_MODIFIED: Content unchanged since last fetch (conditional request)\n * - NOT_FOUND: Resource doesn't exist (should be removed from index)\n */\n status: FetchStatus;\n}\n\n/**\n * Options for configuring content fetching behavior\n */\nexport interface FetchOptions {\n /** Maximum retry attempts for failed fetches */\n maxRetries?: number;\n /** Base delay between retries in milliseconds */\n retryDelay?: number;\n /** Additional headers for HTTP requests */\n headers?: Record<string, string>;\n /** Timeout in milliseconds */\n timeout?: number;\n /** AbortSignal for cancellation */\n signal?: AbortSignal;\n /** Whether to follow HTTP redirects (3xx responses) */\n followRedirects?: boolean;\n /**\n * ETag value for conditional requests.\n * When provided, the fetcher will include an If-None-Match header\n * and may return a 304 Not Modified response if content hasn't changed.\n */\n etag?: string | null;\n}\n\n/**\n * Interface for fetching content from different sources\n */\nexport interface ContentFetcher {\n /**\n * Check if this fetcher can handle the given source\n */\n canFetch(source: string): boolean;\n\n /**\n * Fetch content from the source\n */\n fetch(source: string, options?: FetchOptions): Promise<RawContent>;\n}\n","import { type Browser, chromium, type Page } from \"playwright\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { ScraperError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { FingerprintGenerator } from \"./FingerprintGenerator\";\nimport {\n type ContentFetcher,\n type FetchOptions,\n FetchStatus,\n type RawContent,\n} from \"./types\";\n\n/**\n * Fetches content using a headless browser (Playwright).\n * This fetcher can handle JavaScript-heavy pages and bypass anti-scraping measures.\n */\nexport class BrowserFetcher implements ContentFetcher {\n private browser: Browser | null = null;\n private page: Page | null = null;\n private fingerprintGenerator: FingerprintGenerator;\n private readonly defaultTimeoutMs: number;\n\n constructor(scraperConfig: AppConfig[\"scraper\"]) {\n this.defaultTimeoutMs = scraperConfig.browserTimeoutMs;\n this.fingerprintGenerator = new FingerprintGenerator();\n }\n\n canFetch(source: string): boolean {\n return source.startsWith(\"http://\") || source.startsWith(\"https://\");\n }\n\n async fetch(source: string, options?: FetchOptions): Promise<RawContent> {\n try {\n await this.ensureBrowserReady();\n\n if (!this.page) {\n throw new ScraperError(\"Failed to create browser page\", false);\n }\n\n // Set custom headers if provided\n if (options?.headers) {\n await this.page.setExtraHTTPHeaders(options.headers);\n }\n\n // Set timeout\n const timeout = options?.timeout || this.defaultTimeoutMs;\n\n // Navigate to the page and wait for it to load\n logger.debug(`Navigating to ${source} with browser...`);\n const response = await this.page.goto(source, {\n waitUntil: \"networkidle\",\n timeout,\n });\n\n if (!response) {\n throw new ScraperError(`Failed to navigate to ${source}`, false);\n }\n\n // Check if we should follow redirects\n if (\n options?.followRedirects === false &&\n response.status() >= 300 &&\n response.status() < 400\n ) {\n const location = response.headers().location;\n if (location) {\n throw new ScraperError(`Redirect blocked: ${source} -> ${location}`, false);\n }\n }\n\n // Get the final URL after any redirects\n const finalUrl = this.page.url();\n\n // Get the page content\n const content = await this.page.content();\n const contentBuffer = Buffer.from(content, \"utf-8\");\n\n // Determine content type\n const contentType = response.headers()[\"content-type\"] || \"text/html\";\n const { mimeType, charset } = MimeTypeUtils.parseContentType(contentType);\n\n // Extract ETag header for caching\n const etag = response.headers().etag;\n\n return {\n content: contentBuffer,\n mimeType,\n charset,\n encoding: undefined, // Browser handles encoding automatically\n source: finalUrl,\n etag,\n status: FetchStatus.SUCCESS,\n } satisfies RawContent;\n } catch (error) {\n if (options?.signal?.aborted) {\n throw new ScraperError(\"Browser fetch cancelled\", false);\n }\n\n logger.error(`❌ Browser fetch failed for ${source}: ${error}`);\n throw new ScraperError(\n `Browser fetch failed for ${source}: ${error instanceof Error ? error.message : String(error)}`,\n false,\n error instanceof Error ? error : undefined,\n );\n }\n }\n\n public static async launchBrowser(): Promise<Browser> {\n return chromium.launch({\n headless: true,\n executablePath: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || undefined,\n args: [\"--no-sandbox\"],\n });\n }\n\n private async ensureBrowserReady(): Promise<void> {\n if (!this.browser) {\n logger.debug(\"Launching browser...\");\n this.browser = await BrowserFetcher.launchBrowser();\n }\n\n if (!this.page) {\n this.page = await this.browser.newPage();\n\n // Generate and set realistic browser headers\n const dynamicHeaders = this.fingerprintGenerator.generateHeaders();\n await this.page.setExtraHTTPHeaders(dynamicHeaders);\n\n // Set viewport\n await this.page.setViewportSize({ width: 1920, height: 1080 });\n }\n }\n\n /**\n * Close the browser and clean up resources.\n * Always attempts cleanup even if browser is disconnected to reap zombie processes.\n */\n async close(): Promise<void> {\n // Close page first\n if (this.page) {\n try {\n await this.page.close();\n } catch (error) {\n logger.warn(`⚠️ Error closing browser page: ${error}`);\n } finally {\n this.page = null;\n }\n }\n\n // Then close browser\n if (this.browser) {\n try {\n await this.browser.close();\n } catch (error) {\n logger.warn(`⚠️ Error closing browser: ${error}`);\n } finally {\n this.browser = null;\n }\n }\n\n logger.debug(\"Browser closed successfully\");\n }\n}\n","import crypto from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport { ScraperError } from \"../../utils/errors\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport {\n type ContentFetcher,\n type FetchOptions,\n FetchStatus,\n type RawContent,\n} from \"./types\";\n\n/**\n * Fetches content from local file system.\n */\nexport class FileFetcher implements ContentFetcher {\n canFetch(source: string): boolean {\n return source.startsWith(\"file://\");\n }\n\n /**\n * Fetches the content of a file given a file:// URL, decoding percent-encoded paths as needed.\n * Uses enhanced MIME type detection for better source code file recognition.\n * Supports conditional fetching via ETag comparison for efficient refresh operations.\n */\n async fetch(source: string, options?: FetchOptions): Promise<RawContent> {\n // Remove the file:// protocol prefix and handle both file:// and file:/// formats\n let filePath = source.replace(/^file:\\/\\/\\/?/, \"\");\n\n // Decode percent-encoded characters\n filePath = decodeURIComponent(filePath);\n\n // Ensure absolute path on Unix-like systems (if not already absolute)\n if (!filePath.startsWith(\"/\") && process.platform !== \"win32\") {\n filePath = `/${filePath}`;\n }\n\n try {\n const stats = await fs.stat(filePath);\n\n // Generate current ETag from last modified time\n const currentEtag = crypto\n .createHash(\"md5\")\n .update(stats.mtime.toISOString())\n .digest(\"hex\");\n\n // Check if file has been modified (ETag comparison)\n if (options?.etag && options.etag === currentEtag) {\n // File hasn't changed - return NOT_MODIFIED status\n return {\n content: Buffer.from(\"\"),\n mimeType: \"text/plain\",\n source,\n etag: currentEtag,\n lastModified: stats.mtime.toISOString(),\n status: FetchStatus.NOT_MODIFIED,\n };\n }\n\n // File is new or has been modified - read the content\n const content = await fs.readFile(filePath);\n\n // Use enhanced MIME type detection that properly handles source code files\n const detectedMimeType = MimeTypeUtils.detectMimeTypeFromPath(filePath);\n const mimeType = detectedMimeType || \"application/octet-stream\";\n\n return {\n content,\n mimeType,\n source,\n etag: currentEtag,\n lastModified: stats.mtime.toISOString(),\n status: FetchStatus.SUCCESS,\n // Don't assume charset for text files - let the pipeline detect it\n };\n } catch (error: unknown) {\n // Check for file not found error\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n return {\n content: Buffer.from(\"\"),\n mimeType: \"text/plain\",\n source,\n status: FetchStatus.NOT_FOUND,\n };\n }\n // For all other errors, throw a ScraperError\n throw new ScraperError(\n `Failed to read file ${filePath}: ${\n (error as { message?: string }).message ?? \"Unknown error\"\n }`,\n false,\n error instanceof Error ? error : undefined,\n );\n }\n }\n}\n","export class PipelineError extends Error {\n constructor(\n message: string,\n public readonly cause?: Error,\n ) {\n super(message);\n this.name = this.constructor.name;\n if (cause?.stack) {\n this.stack = `${this.stack}\\nCaused by: ${cause.stack}`;\n }\n }\n}\n\nexport class PipelineStateError extends PipelineError {}\n\n/**\n * Error indicating that an operation was cancelled.\n */\nexport class CancellationError extends PipelineError {\n constructor(message = \"Operation cancelled\") {\n super(message);\n }\n}\n","import axios, { type AxiosError, type AxiosRequestConfig } from \"axios\";\nimport { CancellationError } from \"../../pipeline/errors\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { ChallengeError, RedirectError, ScraperError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { FingerprintGenerator } from \"./FingerprintGenerator\";\nimport {\n type ContentFetcher,\n type FetchOptions,\n FetchStatus,\n type RawContent,\n} from \"./types\";\n\n/**\n * Fetches content from remote sources using HTTP/HTTPS.\n */\nexport class HttpFetcher implements ContentFetcher {\n private readonly maxRetriesDefault: number;\n private readonly baseDelayDefaultMs: number;\n private readonly retryableStatusCodes = [\n 408, // Request Timeout\n 429, // Too Many Requests\n 500, // Internal Server Error\n 502, // Bad Gateway\n 503, // Service Unavailable\n 504, // Gateway Timeout\n 525, // SSL Handshake Failed (Cloudflare specific)\n ];\n\n private readonly nonRetryableErrorCodes = [\n \"ENOTFOUND\", // DNS resolution failed - domain doesn't exist\n \"ECONNREFUSED\", // Connection refused - service not running\n \"ENOENT\", // No such file or directory\n \"EACCES\", // Permission denied\n \"EINVAL\", // Invalid argument\n \"EMFILE\", // Too many open files\n \"ENFILE\", // File table overflow\n \"EPERM\", // Operation not permitted\n ];\n\n private fingerprintGenerator: FingerprintGenerator;\n\n constructor(scraperConfig: AppConfig[\"scraper\"]) {\n this.maxRetriesDefault = scraperConfig.fetcher.maxRetries;\n this.baseDelayDefaultMs = scraperConfig.fetcher.baseDelayMs;\n this.fingerprintGenerator = new FingerprintGenerator();\n }\n\n canFetch(source: string): boolean {\n return source.startsWith(\"http://\") || source.startsWith(\"https://\");\n }\n\n private async delay(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n\n async fetch(source: string, options?: FetchOptions): Promise<RawContent> {\n const maxRetries = options?.maxRetries ?? this.maxRetriesDefault;\n const baseDelay = options?.retryDelay ?? this.baseDelayDefaultMs;\n // Default to following redirects if not specified\n const followRedirects = options?.followRedirects ?? true;\n\n const result = await this.performFetch(\n source,\n options,\n maxRetries,\n baseDelay,\n followRedirects,\n );\n\n return result;\n }\n\n private async performFetch(\n source: string,\n options: FetchOptions | undefined,\n maxRetries: number = this.maxRetriesDefault,\n baseDelay: number = this.baseDelayDefaultMs,\n followRedirects: boolean = true,\n ): Promise<RawContent> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const fingerprint = this.fingerprintGenerator.generateHeaders();\n const headers: Record<string, string> = {\n ...fingerprint,\n ...options?.headers, // User-provided headers override generated ones\n };\n\n // Add If-None-Match header for conditional requests if ETag is provided\n if (options?.etag) {\n headers[\"If-None-Match\"] = options.etag;\n logger.debug(\n `Conditional request for ${source} with If-None-Match: ${options.etag}`,\n );\n }\n\n const config: AxiosRequestConfig = {\n responseType: \"arraybuffer\",\n headers: {\n ...headers,\n // Override Accept-Encoding to exclude zstd which Axios doesn't handle automatically\n // This prevents servers from sending zstd-compressed content that would appear as binary garbage\n \"Accept-Encoding\": \"gzip, deflate, br\",\n },\n timeout: options?.timeout,\n signal: options?.signal, // Pass signal to axios\n // Axios follows redirects by default, we need to explicitly disable it if needed\n maxRedirects: followRedirects ? 5 : 0,\n decompress: true,\n // Allow 304 responses to be handled as successful responses\n validateStatus: (status) => {\n return (status >= 200 && status < 300) || status === 304;\n },\n };\n\n const response = await axios.get(source, config);\n\n // Handle 304 Not Modified responses for conditional requests\n if (response.status === 304) {\n logger.debug(`HTTP 304 Not Modified for ${source}`);\n return {\n content: Buffer.from(\"\"),\n mimeType: \"text/plain\",\n source: source,\n status: FetchStatus.NOT_MODIFIED,\n } satisfies RawContent;\n }\n\n const contentTypeHeader = response.headers[\"content-type\"];\n const { mimeType, charset } = MimeTypeUtils.parseContentType(contentTypeHeader);\n const contentEncoding = response.headers[\"content-encoding\"];\n\n // Convert ArrayBuffer to Buffer properly\n let content: Buffer;\n if (response.data instanceof ArrayBuffer) {\n content = Buffer.from(response.data);\n } else if (Buffer.isBuffer(response.data)) {\n content = response.data;\n } else if (typeof response.data === \"string\") {\n content = Buffer.from(response.data, \"utf-8\");\n } else {\n // Fallback for other data types\n content = Buffer.from(response.data);\n }\n\n // Determine the final effective URL after redirects (if any)\n const finalUrl =\n // Node follow-redirects style\n response.request?.res?.responseUrl ||\n // Some adapters may expose directly\n response.request?.responseUrl ||\n // Fallback to axios recorded config URL\n response.config?.url ||\n source;\n\n // Extract ETag header for caching\n const etag = response.headers.etag || response.headers.ETag;\n if (etag) {\n logger.debug(`Received ETag for ${source}: ${etag}`);\n }\n\n // Extract Last-Modified header for caching\n const lastModified = response.headers[\"last-modified\"];\n const lastModifiedISO = lastModified\n ? new Date(lastModified).toISOString()\n : undefined;\n\n return {\n content,\n mimeType,\n charset,\n encoding: contentEncoding,\n source: finalUrl,\n etag,\n lastModified: lastModifiedISO,\n status: FetchStatus.SUCCESS,\n } satisfies RawContent;\n } catch (error: unknown) {\n const axiosError = error as AxiosError;\n const status = axiosError.response?.status;\n const code = axiosError.code;\n\n // Handle abort/cancel: do not retry, throw CancellationError\n if (options?.signal?.aborted || code === \"ERR_CANCELED\") {\n // Throw with isError = false to indicate cancellation is not an error\n throw new CancellationError(\"HTTP fetch cancelled\");\n }\n\n // Handle 404 Not Found - return special status for refresh operations\n if (status === 404) {\n logger.debug(`Resource not found (404): ${source}`);\n return {\n content: Buffer.from(\"\"),\n mimeType: \"text/plain\",\n source: source,\n status: FetchStatus.NOT_FOUND,\n } satisfies RawContent;\n }\n\n // Handle redirect errors (status codes 301, 302, 303, 307, 308)\n if (!followRedirects && status && status >= 300 && status < 400) {\n const location = axiosError.response?.headers?.location;\n if (location) {\n throw new RedirectError(source, location, status);\n }\n }\n\n // Detect Cloudflare challenges\n if (status === 403) {\n const cfMitigated = axiosError.response?.headers?.[\"cf-mitigated\"];\n const server = axiosError.response?.headers?.server;\n let responseBody = \"\";\n\n // Safely convert response data to string\n if (axiosError.response?.data) {\n try {\n if (typeof axiosError.response.data === \"string\") {\n responseBody = axiosError.response.data;\n } else if (Buffer.isBuffer(axiosError.response.data)) {\n responseBody = axiosError.response.data.toString(\"utf-8\");\n } else if (axiosError.response.data instanceof ArrayBuffer) {\n responseBody = Buffer.from(axiosError.response.data).toString(\"utf-8\");\n }\n } catch {\n // Ignore conversion errors\n }\n }\n\n // Check for various Cloudflare challenge indicators\n const isCloudflareChallenge =\n cfMitigated === \"challenge\" ||\n server === \"cloudflare\" ||\n responseBody.includes(\"Enable JavaScript and cookies to continue\") ||\n responseBody.includes(\"Just a moment...\") ||\n responseBody.includes(\"cf_chl_opt\");\n\n if (isCloudflareChallenge) {\n throw new ChallengeError(source, status, \"cloudflare\");\n }\n }\n\n if (\n attempt < maxRetries &&\n (status === undefined || this.retryableStatusCodes.includes(status)) &&\n !this.nonRetryableErrorCodes.includes(code ?? \"\")\n ) {\n const delay = baseDelay * 2 ** attempt;\n logger.warn(\n `⚠️ Attempt ${attempt + 1}/${\n maxRetries + 1\n } failed for ${source} (Status: ${status}, Code: ${code}). Retrying in ${delay}ms...`,\n );\n await this.delay(delay);\n continue;\n }\n\n // Not a 5xx error or max retries reached\n throw new ScraperError(\n `Failed to fetch ${source} after ${\n attempt + 1\n } attempts: ${axiosError.message ?? \"Unknown error\"}`,\n true,\n error instanceof Error ? error : undefined,\n );\n }\n }\n throw new ScraperError(\n `Failed to fetch ${source} after ${maxRetries + 1} attempts`,\n true,\n );\n }\n}\n","/**\n * Fetcher that routes URLs to HTTP, browser, or file implementations and\n * retries with a browser when an HTTP challenge is detected. Requires the\n * resolved scraper configuration to align with entrypoint-provided settings.\n */\n\nimport type { AppConfig } from \"../../utils/config\";\nimport { ChallengeError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { BrowserFetcher } from \"./BrowserFetcher\";\nimport { FileFetcher } from \"./FileFetcher\";\nimport { HttpFetcher } from \"./HttpFetcher\";\nimport type { ContentFetcher, FetchOptions, RawContent } from \"./types\";\n\n/**\n * AutoDetectFetcher automatically selects the appropriate fetcher based on URL type\n * and handles fallbacks for challenge detection.\n *\n * This eliminates the need for consumers to manage multiple fetcher instances\n * and implement fallback logic themselves.\n */\nexport class AutoDetectFetcher implements ContentFetcher {\n private readonly httpFetcher: HttpFetcher;\n private readonly browserFetcher: BrowserFetcher;\n private readonly fileFetcher = new FileFetcher();\n\n constructor(scraperConfig: AppConfig[\"scraper\"]) {\n this.httpFetcher = new HttpFetcher(scraperConfig);\n this.browserFetcher = new BrowserFetcher(scraperConfig);\n }\n\n /**\n * Check if this fetcher can handle the given source.\n * Returns true for any URL that any of the underlying fetchers can handle.\n */\n canFetch(source: string): boolean {\n return (\n this.httpFetcher.canFetch(source) ||\n this.browserFetcher.canFetch(source) ||\n this.fileFetcher.canFetch(source)\n );\n }\n\n /**\n * Fetch content from the source, automatically selecting the appropriate fetcher\n * and handling fallbacks when challenges are detected.\n */\n async fetch(source: string, options?: FetchOptions): Promise<RawContent> {\n // For file:// URLs, use FileFetcher directly\n if (this.fileFetcher.canFetch(source)) {\n logger.debug(`Using FileFetcher for: ${source}`);\n return this.fileFetcher.fetch(source, options);\n }\n\n // For HTTP(S) URLs, try HttpFetcher first, fallback to BrowserFetcher on challenge\n if (this.httpFetcher.canFetch(source)) {\n try {\n logger.debug(`Using HttpFetcher for: ${source}`);\n return await this.httpFetcher.fetch(source, options);\n } catch (error) {\n if (error instanceof ChallengeError) {\n logger.info(\n `🔄 Challenge detected for ${source}, falling back to browser fetcher...`,\n );\n return this.browserFetcher.fetch(source, options);\n }\n throw error;\n }\n }\n\n // If we get here, no fetcher can handle this URL\n throw new Error(`No suitable fetcher found for URL: ${source}`);\n }\n\n /**\n * Close all underlying fetchers to prevent resource leaks.\n */\n async close(): Promise<void> {\n await Promise.allSettled([\n this.browserFetcher.close(),\n // HttpFetcher and FileFetcher don't need explicit cleanup\n ]);\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError, ValidationError } from \"./errors\";\n\n/**\n * Input parameters for the CancelJobTool.\n */\nexport interface CancelJobInput {\n /** The ID of the job to cancel. */\n jobId: string;\n}\n\n/**\n * Output result for the CancelJobTool.\n */\nexport interface CancelJobResult {\n /** A message indicating the outcome of the cancellation attempt. */\n message: string;\n /** The final status of the job after cancellation attempt. */\n finalStatus: string;\n}\n\n/**\n * Tool for attempting to cancel a pipeline job.\n */\nexport class CancelJobTool {\n private pipeline: IPipeline;\n\n /**\n * Creates an instance of CancelJobTool.\n * @param pipeline The pipeline instance.\n */\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\n }\n\n /**\n * Executes the tool to attempt cancellation of a specific job.\n * @param input - The input parameters, containing the jobId.\n * @returns A promise that resolves with the outcome message.\n * @throws {ValidationError} If the jobId is invalid.\n * @throws {ToolError} If the job is not found or cancellation fails.\n */\n async execute(input: CancelJobInput): Promise<CancelJobResult> {\n // Validate input\n if (!input.jobId || typeof input.jobId !== \"string\" || input.jobId.trim() === \"\") {\n throw new ValidationError(\n \"Job ID is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n try {\n // Retrieve the job first to check its status before attempting cancellation\n const job = await this.pipeline.getJob(input.jobId);\n\n if (!job) {\n logger.warn(`❓ [CancelJobTool] Job not found: ${input.jobId}`);\n throw new ToolError(\n `Job with ID ${input.jobId} not found.`,\n this.constructor.name,\n );\n }\n\n // Check if the job is already in a final state\n if (\n job.status === PipelineJobStatus.COMPLETED || // Use enum member\n job.status === PipelineJobStatus.FAILED || // Use enum member\n job.status === PipelineJobStatus.CANCELLED // Use enum member\n ) {\n logger.debug(`Job ${input.jobId} is already in a final state: ${job.status}.`);\n return {\n message: `Job ${input.jobId} is already ${job.status}. No action taken.`,\n finalStatus: job.status,\n };\n }\n\n // Attempt cancellation\n await this.pipeline.cancelJob(input.jobId);\n\n // Re-fetch the job to confirm status change (or check status directly if cancelJob returned it)\n // PipelineManager.cancelJob doesn't return status, so re-fetch is needed for confirmation.\n const updatedJob = await this.pipeline.getJob(input.jobId);\n const finalStatus = updatedJob?.status ?? \"UNKNOWN (job disappeared?)\";\n\n logger.debug(\n `Cancellation requested for job ${input.jobId}. Current status: ${finalStatus}`,\n );\n return {\n message: `Cancellation requested for job ${input.jobId}. Current status: ${finalStatus}.`,\n finalStatus,\n };\n } catch (error) {\n logger.error(`❌ Error cancelling job ${input.jobId}: ${error}`);\n throw new ToolError(\n `Failed to cancel job ${input.jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n this.constructor.name,\n );\n }\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError } from \"./errors\";\n\n/**\n * Input parameters for the ClearCompletedJobsTool.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: No input parameters needed for this tool\nexport interface ClearCompletedJobsInput {\n // No input parameters needed for this tool\n}\n\n/**\n * Output result for the ClearCompletedJobsTool.\n */\nexport interface ClearCompletedJobsResult {\n /** A message indicating the outcome of the clear operation. */\n message: string;\n /** The number of jobs that were cleared. */\n clearedCount: number;\n}\n\n/**\n * Tool for clearing all completed, cancelled, and failed jobs from the pipeline.\n * This helps keep the job queue clean by removing jobs that are no longer active.\n */\nexport class ClearCompletedJobsTool {\n private pipeline: IPipeline;\n\n /**\n * Creates an instance of ClearCompletedJobsTool.\n * @param pipeline The pipeline instance.\n */\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\n }\n\n /**\n * Executes the tool to clear all completed jobs from the pipeline.\n * @param input - The input parameters (currently unused).\n * @returns A promise that resolves with the outcome of the clear operation.\n * @throws {ToolError} If the clear operation fails.\n */\n async execute(_input: ClearCompletedJobsInput): Promise<ClearCompletedJobsResult> {\n try {\n const clearedCount = await this.pipeline.clearCompletedJobs();\n\n const message =\n clearedCount > 0\n ? `Successfully cleared ${clearedCount} completed job${clearedCount === 1 ? \"\" : \"s\"} from the queue.`\n : \"No completed jobs to clear.\";\n\n logger.debug(message);\n\n return {\n message,\n clearedCount,\n };\n } catch (error) {\n const errorMessage = `Failed to clear completed jobs: ${\n error instanceof Error ? error.message : String(error)\n }`;\n\n logger.error(`❌ ${errorMessage}`);\n\n throw new ToolError(errorMessage, this.constructor.name);\n }\n }\n}\n","import type { ConstructorOptions } from \"jsdom\";\nimport { JSDOM, VirtualConsole } from \"jsdom\";\n\n/**\n * Creates a JSDOM instance with a pre-configured virtual console to suppress console noise.\n * This utility simplifies the setup of JSDOM by providing a standard configuration.\n *\n * @param html - The HTML content to parse.\n * @param options - Optional JSDOM configuration options. These will be merged with the default virtual console setup.\n * @returns A JSDOM instance.\n */\nexport function createJSDOM(html: string, options?: ConstructorOptions): JSDOM {\n const virtualConsole = new VirtualConsole();\n // Suppress console output from JSDOM by default\n virtualConsole.on(\"error\", () => {});\n virtualConsole.on(\"warn\", () => {});\n virtualConsole.on(\"info\", () => {});\n virtualConsole.on(\"debug\", () => {});\n virtualConsole.on(\"log\", () => {}); // Also suppress regular logs\n\n const defaultOptions: ConstructorOptions = {\n virtualConsole,\n };\n\n // Merge provided options with defaults, letting provided options override\n const finalOptions: ConstructorOptions = { ...defaultOptions, ...options };\n\n return new JSDOM(html, finalOptions);\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 psl from \"psl\";\nimport { InvalidUrlError } from \"./errors\";\n\ninterface UrlNormalizerOptions {\n ignoreCase?: boolean;\n removeHash?: boolean;\n removeTrailingSlash?: boolean;\n removeQuery?: boolean;\n removeIndex?: boolean;\n}\n\nconst defaultNormalizerOptions: UrlNormalizerOptions = {\n ignoreCase: true,\n removeHash: true,\n removeTrailingSlash: true,\n removeQuery: false,\n removeIndex: true,\n};\n\nexport function normalizeUrl(\n url: string,\n options: UrlNormalizerOptions = defaultNormalizerOptions,\n): string {\n try {\n const parsedUrl = new URL(url);\n const finalOptions = { ...defaultNormalizerOptions, ...options };\n\n // Clone the URL to modify it safely\n const normalized = new URL(url);\n\n // Reset search and hash for the normalized base\n normalized.search = \"\";\n normalized.hash = \"\";\n\n // Remove index files first, before handling trailing slashes\n if (finalOptions.removeIndex) {\n normalized.pathname = normalized.pathname.replace(\n /\\/index\\.(html|htm|asp|php|jsp)$/i,\n \"/\",\n );\n }\n\n // Handle trailing slash\n if (finalOptions.removeTrailingSlash && normalized.pathname.length > 1) {\n normalized.pathname = normalized.pathname.replace(/\\/+$/, \"\");\n }\n\n // Keep original parts we want to preserve\n const preservedHash = !finalOptions.removeHash ? parsedUrl.hash : \"\";\n const preservedSearch = !finalOptions.removeQuery ? parsedUrl.search : \"\";\n\n // Construct final URL string\n // Use href to get the full string, but we need to re-assemble if we want query/hash specific control\n // Easier: use the modified normalized object\n if (!finalOptions.removeQuery) {\n normalized.search = preservedSearch;\n }\n if (!finalOptions.removeHash) {\n normalized.hash = preservedHash;\n }\n\n let result = normalized.href;\n\n // Apply case normalization if configured\n if (finalOptions.ignoreCase) {\n result = result.toLowerCase();\n }\n\n return result;\n } catch {\n return url; // Return original URL if parsing fails\n }\n}\n\n/**\n * Validates if a string is a valid URL\n * @throws {InvalidUrlError} If the URL is invalid\n */\nexport function validateUrl(url: string): void {\n try {\n new URL(url);\n } catch (error) {\n throw new InvalidUrlError(url, error instanceof Error ? error : undefined);\n }\n}\n\n/**\n * Extracts the primary/registrable domain from a hostname using the public suffix list.\n * This properly handles complex TLDs like .co.uk, .com.au, etc.\n *\n * Examples:\n * - docs.python.org -> python.org\n * - api.github.com -> github.com\n * - example.co.uk -> example.co.uk\n * - user.github.io -> user.github.io (special case for GitHub Pages)\n * - localhost -> localhost\n * - 192.168.1.1 -> 192.168.1.1 (IP addresses returned as-is)\n */\nexport function extractPrimaryDomain(hostname: string): string {\n // Handle IP addresses - return as-is\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(hostname) || /^[0-9a-fA-F:]+$/.test(hostname)) {\n return hostname;\n }\n\n // Handle localhost and other single-part hostnames\n if (!hostname.includes(\".\")) {\n return hostname;\n }\n\n // Use public suffix list for accurate domain extraction\n const domain = psl.get(hostname.toLowerCase());\n return domain || hostname; // Fallback to original hostname if psl fails\n}\n\nexport type { UrlNormalizerOptions };\n","import { logger } from \"../utils\";\nimport type { Chunk, 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 private maxChunkSize: 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 maxChunkSize: number,\n ) {\n this.baseSplitter = baseSplitter;\n this.minChunkSize = minChunkSize;\n this.preferredChunkSize = preferredChunkSize;\n this.maxChunkSize = maxChunkSize;\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, contentType?: string): Promise<Chunk[]> {\n const initialChunks = await this.baseSplitter.splitText(markdown, contentType);\n const concatenatedChunks: Chunk[] = [];\n let currentChunk: Chunk | null = null;\n\n for (const nextChunk of initialChunks) {\n // Warn if a chunk from the base splitter already exceeds max size\n if (nextChunk.content.length > this.maxChunkSize) {\n logger.warn(\n `⚠ Chunk from base splitter exceeds max size: ${nextChunk.content.length} > ${this.maxChunkSize}`,\n );\n }\n\n if (currentChunk) {\n // Account for the newline separator that may be added when merging (see merge below)\n const separatorSize = currentChunk.content.endsWith(\"\\n\") ? 0 : 1;\n const combinedSize =\n currentChunk.content.length + separatorSize + nextChunk.content.length;\n\n // HARD LIMIT: Never exceed max chunk size\n if (combinedSize > this.maxChunkSize) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n\n // STRUCTURE > SIZE: Respect major section boundaries (H1/H2) when current chunk\n // is large enough. This prevents headings from being merged with unrelated preceding\n // content while still allowing tiny chunks to be merged to avoid orphans.\n if (\n currentChunk.content.length >= this.minChunkSize &&\n this.startsNewMajorSection(nextChunk) &&\n !this.isSameSection(currentChunk, nextChunk)\n ) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n\n // If combining would exceed preferred size AND we're already at min size, split\n // UNLESS the next chunk is very small (< min size), in which case merge it anyway\n if (\n combinedSize > this.preferredChunkSize &&\n currentChunk.content.length >= this.minChunkSize &&\n nextChunk.content.length >= this.minChunkSize\n ) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n\n // Merge the chunks\n currentChunk.content += `${currentChunk.content.endsWith(\"\\n\") ? \"\" : \"\\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: Chunk): Chunk {\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: Chunk): boolean {\n return chunk.section.level === 1 || chunk.section.level === 2;\n }\n\n /**\n * Checks if two chunks belong to the same section by comparing their paths.\n * Returns true if the paths are identical or if one is a parent of the other.\n */\n private isSameSection(chunk1: Chunk, chunk2: Chunk): boolean {\n const path1 = chunk1.section.path;\n const path2 = chunk2.section.path;\n\n // Exact match\n if (path1.length === path2.length && path1.every((part, i) => part === path2[i])) {\n return true;\n }\n\n // Parent-child relationship (one path includes the other)\n return this.isPathIncluded(path1, path2) || this.isPathIncluded(path2, path1);\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(currentChunk: Chunk, nextChunk: Chunk): Chunk[\"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 * 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 { 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 { RecursiveCharacterTextSplitter } from \"@langchain/textsplitters\";\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 * Always preserves formatting - trimming should be done by higher-level splitters if needed.\n */\n async split(content: string): Promise<string[]> {\n if (content.length <= this.options.chunkSize) {\n return [content];\n }\n\n // Check for unsplittable content (e.g., a single word longer than chunkSize)\n const words = content.split(/\\s+/);\n const longestWord = words.reduce((max: string, word: string) =>\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(content);\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(content);\n if (this.areChunksValid(lineChunks)) {\n return this.mergeChunks(lineChunks, \"\"); // No separator needed - newlines are preserved in chunks\n }\n\n // Finally, fall back to word-based splitting using LangChain\n const wordChunks = await this.splitByWords(content);\n return this.mergeChunks(wordChunks, \" \"); // Word chunks still need space separator\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 * Preserves all formatting and whitespace including the paragraph separators\n */\n private splitByParagraphs(text: string): string[] {\n const chunks: string[] = [];\n let startPos = 0;\n\n // Find all paragraph boundaries\n const paragraphRegex = /\\n\\s*\\n/g;\n let match = paragraphRegex.exec(text);\n\n while (match !== null) {\n // Include the paragraph separator in the current chunk\n const endPos = match.index + match[0].length;\n const chunk = text.slice(startPos, endPos);\n if (chunk.length > 2) {\n chunks.push(chunk);\n }\n startPos = endPos;\n match = paragraphRegex.exec(text);\n }\n\n // Add the remaining text\n if (startPos < text.length) {\n const remainingChunk = text.slice(startPos);\n if (remainingChunk.length > 2) {\n chunks.push(remainingChunk);\n }\n }\n\n return chunks.filter(Boolean);\n }\n\n /**\n * Splits text into chunks by line boundaries\n * Preserves all formatting and whitespace, including newlines at the end of each line\n */\n private splitByLines(text: string): string[] {\n const chunks: string[] = [];\n let startPos = 0;\n\n // Find all line boundaries\n for (let i = 0; i < text.length; i++) {\n if (text[i] === \"\\n\") {\n // Include the newline in the current chunk\n const chunk = text.slice(startPos, i + 1);\n chunks.push(chunk);\n startPos = i + 1;\n }\n }\n\n // Add the remaining text (if any) without a trailing newline\n if (startPos < text.length) {\n chunks.push(text.slice(startPos));\n }\n\n return chunks;\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 { TextContentSplitter } from \"./TextContentSplitter\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\nexport class ListContentSplitter implements ContentSplitter {\n private textSplitter: TextContentSplitter;\n\n constructor(private options: ContentSplitterOptions) {\n this.textSplitter = new TextContentSplitter(options);\n }\n\n async split(content: string): Promise<string[]> {\n if (content.length <= this.options.chunkSize) {\n return [content];\n }\n\n // Split by list items\n // This regex looks for lines starting with -, *, or digits followed by dot, at the start of line or after newline\n // It captures the delimiter to keep it\n const listItems = this.splitByListItems(content);\n\n // Merge items that fit together\n const mergedChunks = this.mergeChunks(listItems);\n\n // Check if any chunk is still too large\n const finalChunks: string[] = [];\n for (const chunk of mergedChunks) {\n if (chunk.length > this.options.chunkSize) {\n // Fallback to text splitting for huge items\n const subChunks = await this.textSplitter.split(chunk);\n finalChunks.push(...subChunks);\n } else {\n finalChunks.push(chunk);\n }\n }\n\n return finalChunks;\n }\n\n private splitByListItems(content: string): string[] {\n const lines = content.split(\"\\n\");\n const items: string[] = [];\n let currentItem = \"\";\n\n // Regex for list item start: optional whitespace, then -, *, or 1., then space\n const listItemRegex = /^\\s*([-*]|\\d+\\.)\\s/;\n\n for (const line of lines) {\n if (listItemRegex.test(line)) {\n // Start of new item\n if (currentItem) {\n items.push(currentItem);\n }\n currentItem = line;\n } else {\n // Continuation of current item (or content before first item)\n if (currentItem) {\n currentItem += `\\n${line}`;\n } else {\n // Content before first list item? Should rarely happen if we passed a list\n // But valid markdown can have text before list in same block if not carefully separated\n currentItem = line;\n }\n }\n }\n\n if (currentItem) {\n items.push(currentItem);\n }\n\n return items;\n }\n\n private mergeChunks(chunks: string[]): string[] {\n const mergedChunks: string[] = [];\n let currentChunk = \"\";\n\n for (const chunk of chunks) {\n if (!currentChunk) {\n currentChunk = chunk;\n continue;\n }\n\n // Check if adding this chunk exceeds size\n // We add a newline separator since we split by lines originally\n if (currentChunk.length + 1 + chunk.length <= this.options.chunkSize) {\n currentChunk += `\\n${chunk}`;\n } else {\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","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/textsplitters\";\nimport matter from \"gray-matter\";\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 { ListContentSplitter } from \"./splitters/ListContentSplitter\";\nimport { TableContentSplitter } from \"./splitters/TableContentSplitter\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { Chunk, 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 public listSplitter: ListContentSplitter;\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 this.listSplitter = new ListContentSplitter({\n chunkSize: this.preferredChunkSize, // Lists prefer to stay together, so use preferred size\n });\n }\n\n /**\n * Main entry point for splitting markdown content\n */\n async splitText(markdown: string, _contentType?: string): Promise<Chunk[]> {\n // Note: JSON content is now handled by dedicated JsonDocumentSplitter in JsonPipeline\n // This splitter focuses on markdown, HTML, and plain text content\n\n let contentToProcess = markdown;\n let frontmatterChunk: Chunk | null = null;\n\n try {\n // Check for frontmatter\n const file = matter(markdown);\n if (Object.keys(file.data).length > 0) {\n // Reconstruct the frontmatter block\n // file.matter contains the raw content between the delimiters\n const rawFrontmatter = `---\\n${file.matter}\\n---`;\n\n frontmatterChunk = {\n types: [\"frontmatter\"],\n content: rawFrontmatter,\n section: {\n level: 0,\n path: [],\n },\n };\n\n contentToProcess = file.content;\n }\n } catch (err) {\n // Log warning but continue with original content if parsing fails\n logger.warn(\n `Failed to parse frontmatter in splitter: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n // For markdown, HTML, or plain text, process normally\n const html = await this.markdownToHtml(contentToProcess);\n const dom = await this.parseHtml(html);\n const sections = await this.splitIntoSections(dom);\n const chunks = await this.splitSectionContent(sections);\n\n if (frontmatterChunk) {\n chunks.unshift(frontmatterChunk);\n }\n\n return chunks;\n }\n\n /**\n * Step 1: Split document into sections based on H1-H6 headings,\n * as well as code blocks, tables, lists, blockquotes, and media.\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 this.addSectionFromElement(element, \"table\", currentSection, sections);\n } else if (element.tagName === \"UL\" || element.tagName === \"OL\") {\n this.addSectionFromElement(element, \"list\", currentSection, sections);\n } else if (element.tagName === \"BLOCKQUOTE\") {\n this.addSectionFromElement(element, \"blockquote\", currentSection, sections);\n } else if (element.tagName === \"IMG\") {\n this.addSectionFromElement(element, \"media\", currentSection, sections);\n } else if (\n element.tagName === \"P\" &&\n element.children.length === 1 &&\n element.children[0].tagName === \"IMG\" &&\n (!element.textContent || element.textContent.trim() === \"\")\n ) {\n // Handle images wrapped in paragraphs\n this.addSectionFromElement(\n element.children[0],\n \"media\",\n currentSection,\n sections,\n );\n } else if (element.tagName === \"HR\") {\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 * Helper to create a new section from a specific DOM element type\n */\n private addSectionFromElement(\n element: Element,\n type: SectionContentType,\n currentSection: DocumentSection,\n sections: DocumentSection[],\n ): void {\n const markdown = fullTrim(this.turndownService.turndown(element.outerHTML));\n const newSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type,\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(newSection);\n }\n\n /**\n * Step 2: Split section content into smaller chunks\n */\n private async splitSectionContent(sections: DocumentSection[]): Promise<Chunk[]> {\n const chunks: Chunk[] = [];\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 case \"blockquote\":\n case \"media\": {\n // Trim markdown content before splitting\n splitContent = await this.textSplitter.split(fullTrim(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 case \"list\": {\n splitContent = await this.listSplitter.split(content.text);\n break;\n }\n default: {\n // Fallback for any unknown type\n splitContent = await this.textSplitter.split(fullTrim(content.text));\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): Chunk => ({\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 { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport type { ContentPipeline, PipelineResult } from \"./types\";\n\n/**\n * Base class for content processing pipelines.\n * Provides common functionality for executing middleware stacks.\n */\nexport class BasePipeline implements ContentPipeline {\n /**\n * Determines if this pipeline can process content with the given MIME type.\n * Must be implemented by derived classes.\n */\n public canProcess(_mimeType: string, _content?: Buffer): boolean {\n throw new Error(\"Method not implemented.\");\n }\n\n /**\n * Processes the raw content through the pipeline.\n * Must be implemented by derived classes.\n */\n public async process(\n _rawContent: RawContent,\n _options: ScraperOptions,\n _fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n throw new Error(\"Method not implemented.\");\n }\n\n /**\n * Cleanup resources used by this pipeline.\n * Default implementation does nothing - override in derived classes as needed.\n */\n public async close(): Promise<void> {\n // No-op by default\n }\n\n /**\n * Executes a middleware stack on the given context.\n * This is a utility method used by derived pipeline classes.\n *\n * @param middleware - The middleware stack to execute\n * @param context - The context to process\n */\n protected async executeMiddlewareStack(\n middleware: ContentProcessorMiddleware[],\n context: MiddlewareContext,\n ): Promise<void> {\n let index = -1;\n const dispatch = async (i: number): Promise<void> => {\n if (i <= index) throw new Error(\"next() called multiple times\");\n index = i;\n const mw = middleware[i];\n if (!mw) return;\n await mw.process(context, dispatch.bind(null, i + 1));\n };\n\n try {\n await dispatch(0);\n } catch (error) {\n context.errors.push(error instanceof Error ? error : new Error(String(error)));\n }\n }\n}\n","/**\n * DocumentPipeline - Processes binary document formats using Kreuzberg for text extraction,\n * then splits semantically.\n *\n * Supported formats:\n * - PDF (.pdf)\n * - Modern Office: Word (.docx), Excel (.xlsx), PowerPoint (.pptx)\n * - Legacy Office: Word (.doc), Excel (.xls), PowerPoint (.ppt)\n * - OpenDocument: Text (.odt), Spreadsheet (.ods), Presentation (.odp)\n * - Rich Text Format (.rtf)\n * - eBooks: EPUB (.epub), FictionBook (.fb2)\n * - Jupyter Notebook (.ipynb)\n *\n * The pipeline requests Markdown output from Kreuzberg (`@kreuzberg/node`) via\n * `outputFormat: \"markdown\"`, aligning with the project's Markdown-first processing\n * pipeline. For spreadsheet-type documents where `result.content` is flat text,\n * the pipeline prefers pre-rendered Markdown from `tables[].markdown` which includes\n * sheet names as headings and properly formatted Markdown tables.\n *\n * Documents exceeding the configured maximum size are skipped with a warning.\n */\n\nimport { extractBytes } from \"@kreuzberg/node\";\nimport { GreedySplitter } from \"../../splitter/GreedySplitter\";\nimport { SemanticMarkdownSplitter } from \"../../splitter/SemanticMarkdownSplitter\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { RawContent } from \"../fetcher/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\nexport class DocumentPipeline extends BasePipeline {\n private readonly splitter: GreedySplitter;\n private readonly maxSize: number;\n\n constructor(config: AppConfig) {\n super();\n this.maxSize = config.scraper.document.maxSize;\n\n const semanticSplitter = new SemanticMarkdownSplitter(\n config.splitter.preferredChunkSize,\n config.splitter.maxChunkSize,\n );\n this.splitter = new GreedySplitter(\n semanticSplitter,\n config.splitter.minChunkSize,\n config.splitter.preferredChunkSize,\n config.splitter.maxChunkSize,\n );\n }\n\n canProcess(mimeType: string): boolean {\n return MimeTypeUtils.isSupportedDocument(mimeType);\n }\n\n async process(\n rawContent: RawContent,\n _options: ScraperOptions,\n ): Promise<PipelineResult> {\n const buffer = Buffer.isBuffer(rawContent.content)\n ? rawContent.content\n : Buffer.from(rawContent.content);\n\n // Check size limit\n if (buffer.length > this.maxSize) {\n logger.warn(\n `Document exceeds size limit (${buffer.length} > ${this.maxSize}): ${rawContent.source}`,\n );\n return {\n title: null,\n contentType: rawContent.mimeType,\n textContent: null,\n links: [],\n errors: [new Error(`Document exceeds maximum size of ${this.maxSize} bytes`)],\n chunks: [],\n };\n }\n\n // Resolve the actual MIME type when the server sends a generic type.\n // This is common with S3, CDNs, and other file storage services that\n // serve documents as application/octet-stream.\n const mimeType = this.resolveMimeType(rawContent.mimeType, rawContent.source);\n if (!mimeType) {\n logger.warn(\n `Could not determine document type for ${rawContent.source} (MIME type: ${rawContent.mimeType})`,\n );\n return {\n title: null,\n contentType: rawContent.mimeType,\n textContent: null,\n links: [],\n errors: [new Error(\"Could not determine document type\")],\n chunks: [],\n };\n }\n\n try {\n const result = await extractBytes(buffer, mimeType, {\n outputFormat: \"markdown\",\n });\n\n const content = this.extractContent(result, mimeType);\n\n if (!content) {\n logger.warn(`No content extracted from document: ${rawContent.source}`);\n return {\n title: null,\n contentType: rawContent.mimeType,\n textContent: null,\n links: [],\n errors: [],\n chunks: [],\n };\n }\n\n // Use title from Kreuzberg metadata, fall back to filename\n const title = result.metadata?.title || this.extractFilename(rawContent.source);\n\n // Split the content (Kreuzberg output is Markdown)\n const chunks = await this.splitter.splitText(content, \"text/markdown\");\n\n return {\n title,\n contentType: \"text/markdown\", // Output is always markdown\n textContent: content,\n links: [], // Documents don't have extractable links\n errors: [],\n chunks,\n };\n } catch (error) {\n // Log a safe error message to avoid potential binary data in the logs\n const errorName = error instanceof Error ? error.name : \"UnknownError\";\n const safeMessage = `Failed to convert document: ${errorName}`;\n\n logger.warn(`${safeMessage} for ${rawContent.source}`);\n\n return {\n title: null,\n contentType: rawContent.mimeType,\n textContent: null,\n links: [],\n errors: [new Error(safeMessage)],\n chunks: [],\n };\n }\n }\n\n /**\n * Selects the best content representation from a Kreuzberg extraction result.\n * For spreadsheet-type documents, prefer pre-rendered Markdown from `tables[].markdown`.\n * For other documents, prefer `result.content` and only fall back to tables when needed.\n */\n private extractContent(\n result: Awaited<ReturnType<typeof extractBytes>>,\n mimeType: string,\n ): string | null {\n const tableContent = result.tables.map((t) => t.markdown).join(\"\\n\\n\");\n const hasTableContent = tableContent.trim().length > 0;\n const content = result.content ?? \"\";\n const hasContent = content.trim().length > 0;\n\n if (this.isSpreadsheetMimeType(mimeType)) {\n if (hasTableContent) {\n return tableContent;\n }\n return hasContent ? content : null;\n }\n\n if (hasContent) {\n return content;\n }\n\n if (hasTableContent) {\n return tableContent;\n }\n\n return null;\n }\n\n private isSpreadsheetMimeType(mimeType: string): boolean {\n return (\n mimeType === \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" ||\n mimeType === \"application/vnd.ms-excel\" ||\n mimeType === \"application/vnd.oasis.opendocument.spreadsheet\"\n );\n }\n\n /**\n * Resolves the effective MIME type for document processing.\n * When the provided MIME type is generic (`application/octet-stream`), attempts to\n * detect the actual type from the source URL's file extension. Returns `null` if\n * the resolved type is not a supported document format.\n */\n private resolveMimeType(mimeType: string, source: string): string | null {\n if (mimeType !== \"application/octet-stream\") {\n return mimeType;\n }\n\n // detectMimeTypeFromPath handles query params and hash fragments internally\n const detected = MimeTypeUtils.detectMimeTypeFromPath(source);\n if (detected && MimeTypeUtils.isSupportedDocument(detected)) {\n return detected;\n }\n\n return null;\n }\n\n private extractFilename(source: string): string | null {\n try {\n const url = new URL(source);\n const pathname = url.pathname;\n const lastSlash = pathname.lastIndexOf(\"/\");\n return pathname.substring(lastSlash + 1) || null;\n } catch {\n const lastSlash = source.lastIndexOf(\"/\");\n return source.substring(lastSlash + 1) || null;\n }\n }\n}\n","import * as cheerio from \"cheerio\";\nimport { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to parse HTML string/buffer content into a Cheerio object.\n * It populates the `context.dom` property.\n * Assumes the input HTML in `context.content` is the final version to be parsed\n * (e.g., after potential rendering by Playwright or modification by JS execution).\n */\nexport class HtmlCheerioParserMiddleware implements ContentProcessorMiddleware {\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n try {\n logger.debug(`Parsing HTML content with Cheerio from ${context.source}`);\n // Load the HTML string using Cheerio\n const $ = cheerio.load(context.content);\n\n // Add the Cheerio API object to the context\n context.dom = $;\n\n // Proceed to the next middleware\n await next();\n } catch (error) {\n logger.error(\n `❌ Failed to parse HTML with Cheerio for ${context.source}: ${error}`,\n );\n context.errors.push(\n error instanceof Error\n ? error\n : new Error(`Cheerio HTML parsing failed: ${String(error)}`),\n );\n // Do not proceed further down the pipeline if parsing fails\n return;\n }\n }\n}\n","import { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to extract links (href attributes from <a> tags) from HTML content using Cheerio.\n * It expects the Cheerio API object to be available in `context.dom`.\n * This should run *after* parsing but *before* conversion to Markdown.\n */\nexport class HtmlLinkExtractorMiddleware implements ContentProcessorMiddleware {\n /**\n * Processes the context to extract links from the sanitized HTML body.\n * @param context The current middleware context.\n * @param next Function to call the next middleware.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // Check if we have a Cheerio object from a previous step\n const $ = context.dom;\n if (!$) {\n logger.warn(\n `⏭️ Skipping ${this.constructor.name}: context.dom is missing. Ensure HtmlCheerioParserMiddleware runs before this.`,\n );\n await next();\n return;\n }\n\n try {\n // Determine effective document base (first <base href> if present)\n let docBase = context.source;\n try {\n const baseEl = $(\"base[href]\").first();\n const rawBase = baseEl.attr(\"href\");\n if (rawBase && rawBase.trim() !== \"\") {\n try {\n const trimmed = rawBase.trim();\n // Attempt to resolve candidate base against the document source.\n const candidate = new URL(trimmed, context.source);\n // Heuristic validation:\n // Accept if:\n // - Starts with a valid scheme (e.g. https:, data:, etc.) OR\n // - Protocol-relative (//...) OR\n // - Root-relative (/...) OR\n // - Dot-relative (./ or ../...) OR\n // - Otherwise a plain relative path segment WITHOUT a suspicious leading colon\n //\n // Reject (fallback to original page URL) if the first character is a colon or\n // there is a colon before any slash that does NOT form a valid scheme.\n const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(trimmed);\n const protocolRelative = trimmed.startsWith(\"//\");\n const firstSlash = trimmed.indexOf(\"/\");\n const firstColon = trimmed.indexOf(\":\");\n const colonBeforeSlash =\n firstColon !== -1 && (firstSlash === -1 || firstColon < firstSlash);\n const suspiciousColon = colonBeforeSlash && !hasScheme && !protocolRelative;\n if (suspiciousColon || trimmed.startsWith(\":\")) {\n logger.debug(\n `Ignoring suspicious <base href> value (colon misuse): ${rawBase}`,\n );\n } else {\n // Accept candidate (valid scheme, protocol/root/dot relative, or plain relative segment)\n docBase = candidate.href;\n }\n } catch {\n logger.debug(`Ignoring invalid <base href> value: ${rawBase}`);\n }\n }\n } catch {\n // Ignore base parsing errors\n }\n\n const linkElements = $(\"a[href]\");\n logger.debug(\n `Found ${linkElements.length} potential links in ${context.source} (base=${docBase})`,\n );\n\n const extractedLinks: string[] = [];\n linkElements.each((_index, element) => {\n const href = $(element).attr(\"href\");\n if (href && href.trim() !== \"\") {\n try {\n const urlObj = new URL(href, docBase);\n if (![\"http:\", \"https:\", \"file:\"].includes(urlObj.protocol)) {\n logger.debug(`Ignoring link with invalid protocol: ${href}`);\n return;\n }\n extractedLinks.push(urlObj.href);\n } catch (_e) {\n logger.debug(`Ignoring invalid URL syntax: ${href}`);\n }\n }\n });\n\n context.links = [...new Set(extractedLinks)];\n logger.debug(\n `Extracted ${context.links.length} unique, valid links from ${context.source}`,\n );\n } catch (error) {\n logger.error(`❌ Error extracting links from ${context.source}: ${error}`);\n context.errors.push(\n new Error(\n `Failed to extract links from HTML: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n }\n\n await next();\n }\n}\n","import { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to extract the title from HTML content using Cheerio.\n * Assumes context.dom (Cheerio API object) is populated by a preceding middleware\n * (e.g., HtmlCheerioParserMiddleware).\n */\nexport class HtmlMetadataExtractorMiddleware implements ContentProcessorMiddleware {\n /**\n * Processes the context to extract the HTML title.\n * @param context The current processing context.\n * @param next Function to call the next middleware.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // Check if Cheerio DOM exists from previous middleware\n const $ = context.dom;\n if (!$) {\n logger.warn(\n `⏭️ Skipping ${this.constructor.name}: context.dom is missing. Ensure HtmlCheerioParserMiddleware runs before this.`,\n );\n await next();\n return;\n }\n\n // Only process if we have a Cheerio object (implicitly means it's HTML)\n try {\n // Extract title (using title tag, fallback to h1 if title is empty/missing)\n let title = $(\"title\").first().text().trim();\n\n if (!title) {\n // Fallback to the first H1 if title is empty\n title = $(\"h1\").first().text().trim();\n }\n\n // Default to \"Untitled\" if both are empty\n title = title || \"Untitled\";\n\n // Basic cleanup (replace multiple spaces with single space)\n title = title.replace(/\\s+/g, \" \").trim();\n\n context.title = title;\n logger.debug(`Extracted title: \"${title}\" from ${context.source}`);\n } catch (error) {\n logger.error(`❌ Error extracting metadata from ${context.source}: ${error}`);\n context.errors.push(\n new Error(\n `Failed to extract metadata from HTML: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n // Optionally decide whether to stop the pipeline here\n }\n\n // Call the next middleware in the chain\n await next();\n\n // No cleanup needed for Cheerio\n }\n}\n","import type * as cheerio from \"cheerio\";\nimport type { AnyNode } from \"domhandler\";\nimport { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware that normalizes URLs and links in HTML content after DOM parsing.\n *\n * This middleware performs the following transformations:\n * - Converts relative image URLs to absolute URLs\n * - Removes tracking/ad images (1x1 pixels, analytics beacons)\n * - Converts relative link URLs to absolute URLs\n * - Removes anchor links (#...) but preserves their text content\n * - Removes non-HTTP links (javascript:, mailto:, etc.) but preserves their text content\n *\n * This ensures that indexed documents contain functional absolute URLs and removes\n * non-functional links while preserving contextually valuable text content.\n */\nexport class HtmlNormalizationMiddleware implements ContentProcessorMiddleware {\n private readonly presentationalAnchorWrapperTags = new Set([\"div\", \"p\"]);\n\n // Known tracking/analytics domains and patterns to filter out\n private readonly trackingPatterns = [\n \"adroll.com\",\n \"doubleclick.net\",\n \"google-analytics.com\",\n \"googletagmanager.com\",\n \"analytics.twitter.com\",\n \"twitter.com/1/i/adsct\",\n \"t.co/1/i/adsct\",\n \"bat.bing.com\",\n \"pixel.rubiconproject.com\",\n \"casalemedia.com\",\n \"tremorhub.com\",\n \"rlcdn.com\",\n \"facebook.com/tr\",\n \"linkedin.com/px\",\n \"quantserve.com\",\n \"scorecardresearch.com\",\n \"hotjar.com\",\n \"mouseflow.com\",\n \"crazyegg.com\",\n \"clarity.ms\",\n ];\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n if (!context.dom) {\n logger.debug(\n `Skipping HTML normalization for ${context.source} - no DOM available`,\n );\n await next();\n return;\n }\n\n try {\n logger.debug(`Normalizing HTML URLs and links for ${context.source}`);\n\n const $ = context.dom;\n const baseUrl = context.source;\n\n // Normalize image URLs\n this.normalizeImageUrls($, baseUrl);\n\n // Normalize and clean links\n this.normalizeLinks($, baseUrl);\n\n // Simplify purely presentational wrappers inside links before markdown conversion\n this.simplifyAnchorContent($);\n\n logger.debug(`Successfully normalized HTML content for ${context.source}`);\n } catch (error) {\n logger.error(`❌ Failed to normalize HTML for ${context.source}: ${error}`);\n context.errors.push(\n error instanceof Error\n ? error\n : new Error(`HTML normalization failed: ${String(error)}`),\n );\n }\n\n await next();\n }\n\n /**\n * Checks if an image should be kept based on its source URL.\n * Filters out tracking pixels and analytics beacons.\n */\n private shouldKeepImage(src: string): boolean {\n const srcLower = src.toLowerCase();\n return !this.trackingPatterns.some((pattern) => srcLower.includes(pattern));\n }\n\n /**\n * Normalizes image URLs by converting relative URLs to absolute URLs.\n * Removes tracking/analytics images.\n * Preserves data URIs (inline images).\n */\n private normalizeImageUrls($: cheerio.CheerioAPI, baseUrl: string): void {\n $(\"img\").each((_index, element) => {\n const $img = $(element);\n const src = $img.attr(\"src\");\n\n if (!src) {\n // Remove images without src\n $img.remove();\n return;\n }\n\n // Keep data URIs (inline images) as-is\n if (src.startsWith(\"data:\")) {\n return;\n }\n\n // Check if this is a tracking image\n if (!this.shouldKeepImage(src)) {\n $img.remove();\n return;\n }\n\n try {\n // If it's already an absolute URL, leave it unchanged\n new URL(src);\n } catch {\n // It's a relative URL, convert to absolute\n try {\n const absoluteUrl = new URL(src, baseUrl).href;\n $img.attr(\"src\", absoluteUrl);\n } catch (error) {\n logger.debug(`Failed to resolve relative image URL: ${src} - ${error}`);\n // Remove images we can't resolve\n $img.remove();\n }\n }\n });\n }\n\n /**\n * Normalizes links by:\n * - Converting relative URLs to absolute URLs\n * - Unwrapping anchor links (preserving text content)\n * - Unwrapping non-HTTP links (preserving text content)\n */\n private normalizeLinks($: cheerio.CheerioAPI, baseUrl: string): void {\n $(\"a\").each((_index, element) => {\n const $link = $(element);\n const href = $link.attr(\"href\");\n\n if (!href) {\n // Links without href - unwrap them (preserve content, remove tag)\n this.unwrapElement($, $link);\n return;\n }\n\n // Handle anchor links (starting with #)\n if (href.startsWith(\"#\")) {\n this.unwrapElement($, $link);\n return;\n }\n\n // Check if it's already an absolute URL\n try {\n const url = new URL(href);\n\n // Handle non-HTTP protocols (javascript:, mailto:, etc.)\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n this.unwrapElement($, $link);\n return;\n }\n\n // It's already a valid HTTP/HTTPS absolute URL, leave it unchanged\n } catch {\n // It's a relative URL, convert to absolute\n try {\n const absoluteUrl = new URL(href, baseUrl).href;\n $link.attr(\"href\", absoluteUrl);\n } catch (error) {\n logger.debug(`Failed to resolve relative link URL: ${href} - ${error}`);\n // If we can't resolve it, unwrap it to preserve the text content\n this.unwrapElement($, $link);\n }\n }\n });\n }\n\n /**\n * Removes single presentational block wrappers inside anchors so Turndown does not\n * emit multi-line link labels for layout-only structures.\n */\n private simplifyAnchorContent($: cheerio.CheerioAPI): void {\n $(\"a\").each((_index, element) => {\n const $link = $(element);\n let simplified = true;\n\n while (simplified) {\n simplified = false;\n\n const significantChildren = $link\n .contents()\n .toArray()\n .filter((node) => node.type !== \"text\" || (node.data || \"\").trim().length > 0);\n\n if (significantChildren.length !== 1) {\n continue;\n }\n\n const wrapperNode = significantChildren[0];\n if (wrapperNode.type !== \"tag\") {\n continue;\n }\n\n const tagName = wrapperNode.tagName.toLowerCase();\n if (!this.presentationalAnchorWrapperTags.has(tagName)) {\n continue;\n }\n\n const $wrapper = $(wrapperNode);\n if (this.hasProtectedDescendants($wrapper)) {\n continue;\n }\n\n this.insertSpacesBetweenSiblingTextFragments($, $wrapper);\n $wrapper.replaceWith($wrapper.contents());\n simplified = true;\n }\n });\n }\n\n private hasProtectedDescendants($element: cheerio.Cheerio<AnyNode>): boolean {\n return $element.find(\"img, pre, table, ul, ol, li, blockquote\").length > 0;\n }\n\n private insertSpacesBetweenSiblingTextFragments(\n $: cheerio.CheerioAPI,\n $element: cheerio.Cheerio<AnyNode>,\n ): void {\n const children = $element.contents().toArray();\n\n for (let index = 1; index < children.length; index++) {\n const previousNode = children[index - 1];\n const currentNode = children[index];\n\n if (\n !this.nodeHasVisibleText($, previousNode) ||\n !this.nodeHasVisibleText($, currentNode)\n ) {\n continue;\n }\n\n if (this.hasWhitespaceBoundary($, previousNode, currentNode)) {\n continue;\n }\n\n $(currentNode).before(\" \");\n }\n }\n\n private nodeHasVisibleText($: cheerio.CheerioAPI, node: AnyNode): boolean {\n if (node.type === \"text\") {\n return (node.data || \"\").trim().length > 0;\n }\n\n return $(node).text().trim().length > 0;\n }\n\n private hasWhitespaceBoundary(\n $: cheerio.CheerioAPI,\n previousNode: AnyNode,\n currentNode: AnyNode,\n ): boolean {\n const previousText = this.getNodeText($, previousNode);\n const currentText = this.getNodeText($, currentNode);\n\n return /\\s$/.test(previousText) || /^\\s/.test(currentText);\n }\n\n private getNodeText($: cheerio.CheerioAPI, node: AnyNode): string {\n if (node.type === \"text\") {\n return node.data || \"\";\n }\n\n return $(node).text();\n }\n\n /**\n * Unwraps an element by replacing it with its HTML content.\n * This preserves the inner HTML (including nested elements) while removing the wrapping tag.\n */\n private unwrapElement(\n _$: cheerio.CheerioAPI,\n $element: cheerio.Cheerio<AnyNode>,\n ): void {\n const htmlContent = $element.html() || $element.text();\n $element.replaceWith(htmlContent);\n }\n}\n","import type { Chunk } from \"../splitter/types\";\nimport type { ProgressCallback } from \"../types\";\n\n/**\n * Represents an item in the scraping queue\n */\nexport type QueueItem = {\n url: string;\n depth: number;\n pageId?: number; // Database page ID for efficient deletion during refresh\n etag?: string | null; // Last known ETag for conditional requests during refresh\n};\n\n/**\n * Enum defining the available HTML processing strategies.\n */\nexport enum ScrapeMode {\n Fetch = \"fetch\",\n Playwright = \"playwright\",\n Auto = \"auto\",\n}\n\n/**\n * Strategy interface for implementing different scraping behaviors\n */\nexport interface ScraperStrategy {\n canHandle(url: string): boolean;\n scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal, // Add optional signal\n ): Promise<void>;\n\n /**\n * Cleanup resources used by this strategy (e.g., pipeline browser instances).\n * Should be called when the strategy is no longer needed.\n */\n cleanup?(): Promise<void>;\n}\n\n/**\n * Internal runtime options for configuring the scraping process.\n *\n * This is the comprehensive configuration object used by ScraperService, PipelineWorker,\n * and scraper strategies. It includes both:\n * - User-facing options (provided via tools like scrape_docs)\n * - System-managed options (set internally by PipelineManager)\n *\n * Note: User-facing tools should NOT expose all these options directly. Instead,\n * PipelineManager is responsible for translating user input into this complete\n * runtime configuration.\n */\nexport interface ScraperOptions {\n url: string;\n library: string;\n version: string;\n maxPages?: number;\n maxDepth?: number;\n /**\n * Defines the allowed crawling boundary relative to the starting URL\n * - 'subpages': Only crawl URLs on the same hostname and within the same starting path (default)\n * - 'hostname': Crawl any URL on the same exact hostname, regardless of path\n * - 'domain': Crawl any URL on the same top-level domain, including subdomains\n */\n scope?: \"subpages\" | \"hostname\" | \"domain\";\n /**\n * Controls whether HTTP redirects (3xx responses) should be followed\n * - When true: Redirects are followed automatically (default)\n * - When false: A RedirectError is thrown when a 3xx response is received\n */\n followRedirects?: boolean;\n maxConcurrency?: number;\n ignoreErrors?: boolean;\n /** CSS selectors for elements to exclude during HTML processing */\n excludeSelectors?: string[];\n /**\n * Determines the HTML processing strategy.\n * - 'fetch': Use a simple DOM parser (faster, less JS support).\n * - 'playwright': Use a headless browser (slower, full JS support).\n * - 'auto': Automatically select the best strategy (currently defaults to 'playwright').\n * @default ScrapeMode.Auto\n */\n scrapeMode?: ScrapeMode;\n /** Optional AbortSignal for cancellation */\n signal?: AbortSignal;\n /**\n * Patterns for including URLs during scraping. If not set, all are included by default.\n */\n includePatterns?: string[];\n /**\n * Patterns for excluding URLs during scraping. Exclude takes precedence over include.\n */\n excludePatterns?: string[];\n /**\n * Custom HTTP headers to send with each HTTP request (e.g., for authentication).\n * Keys are header names, values are header values.\n */\n headers?: Record<string, string>;\n /**\n * Pre-populated queue of pages to visit.\n * When provided:\n * - Disables link discovery and crawling\n * - Processes only the provided URLs\n * - Uses provided metadata (pageId, etag) for optimization\n */\n initialQueue?: QueueItem[];\n /**\n * Indicates whether this is a refresh operation (re-indexing existing version).\n * When true:\n * - Skips initial removeAllDocuments call to preserve existing data\n * - Uses ETags for conditional requests\n * - Only updates changed/deleted pages\n * @default false\n */\n isRefresh?: boolean;\n /**\n * If true, clears existing documents for the library version before scraping.\n * If false, appends to the existing documents.\n * @default true\n */\n clean?: boolean;\n}\n\n/**\n * Result of scraping a single page.\n */\nexport interface ScrapeResult {\n /** The URL of the page that was scraped */\n url: string;\n /** Page title */\n title: string;\n /** Original MIME type of the fetched resource before pipeline processing */\n sourceContentType: string;\n /** MIME type of the stored content after pipeline processing */\n contentType: string;\n /** The final processed content, typically as a string (e.g., Markdown). Used primarily for debugging */\n textContent: string;\n /** Extracted links from the content. */\n links: string[];\n /** Any non-critical errors encountered during processing. */\n errors: Error[];\n /** Pre-split chunks from pipeline processing */\n chunks: Chunk[];\n /** ETag from HTTP response for caching */\n etag?: string | null;\n /** Last-Modified from HTTP response for caching */\n lastModified?: string | null;\n}\n\n/**\n * Progress information during scraping\n */\nexport interface ScraperProgressEvent {\n /** Number of pages successfully scraped so far */\n pagesScraped: number;\n /**\n * Maximum number of pages to scrape (from maxPages option).\n * May be undefined if no limit is set.\n */\n totalPages: number;\n /**\n * Total number of URLs discovered during crawling.\n * This may be higher than totalPages if maxPages limit is reached.\n */\n totalDiscovered: number;\n /** Current URL being processed */\n currentUrl: string;\n /** Current depth in the crawl tree */\n depth: number;\n /** Maximum depth allowed (from maxDepth option) */\n maxDepth: number;\n /** The result of scraping the current page, if available. This may be null if the page has been deleted or if an error occurred. */\n result: ScrapeResult | null;\n /** Database page ID (for refresh operations or tracking) */\n pageId?: number;\n /** Indicates this page was deleted (404 during refresh or broken link) */\n deleted?: boolean;\n}\n","/**\n * A simple in-memory LRU (Least Recently Used) cache.\n * When the cache reaches maxSize, the oldest (least recently used) entry is evicted.\n *\n * This implementation uses a Map to maintain insertion order, where:\n * - get() moves the accessed key to the end (marks as recently used)\n * - set() adds to the end (most recent)\n * - When full, the first key (oldest) is deleted\n */\nexport class SimpleMemoryCache<K, V> {\n private cache: Map<K, V>;\n private readonly maxSize: number;\n\n constructor(maxSize: number) {\n if (maxSize <= 0) {\n throw new Error(\"maxSize must be positive\");\n }\n this.cache = new Map();\n this.maxSize = maxSize;\n }\n\n /**\n * Retrieve a value from the cache.\n * Marks the key as recently used (moves to end of Map).\n */\n get(key: K): V | undefined {\n const value = this.cache.get(key);\n if (value !== undefined) {\n // Move to end (mark as recently used)\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n return value;\n }\n\n /**\n * Store a value in the cache.\n * If cache is full, evicts the oldest entry first.\n */\n set(key: K, value: V): void {\n // If key exists, delete it first so we can re-add at end\n if (this.cache.has(key)) {\n this.cache.delete(key);\n } else if (this.cache.size >= this.maxSize) {\n // Cache is full and this is a new key - evict oldest (first key)\n const oldestKey = this.cache.keys().next().value;\n if (oldestKey !== undefined) {\n this.cache.delete(oldestKey);\n }\n }\n\n this.cache.set(key, value);\n }\n\n /**\n * Check if a key exists in the cache.\n * Marks the key as recently used (moves to end of Map) to maintain LRU semantics.\n */\n has(key: K): boolean {\n const exists = this.cache.has(key);\n if (exists) {\n // Move to end (mark as recently used) to maintain LRU semantics\n const value = this.cache.get(key);\n if (value !== undefined) {\n this.cache.delete(key);\n this.cache.set(key, value);\n }\n }\n return exists;\n }\n\n /**\n * Get current cache size.\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n}\n","import type { Browser, BrowserContext, ElementHandle, Frame, Page } from \"playwright\";\nimport { type AppConfig, defaults } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { BrowserFetcher } from \"../fetcher\";\nimport { ScrapeMode } from \"../types\";\nimport { SimpleMemoryCache } from \"../utils/SimpleMemoryCache\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Shadow DOM mapping structure for non-invasive extraction\n */\ninterface ShadowMapping {\n shadowContent: string;\n hostTagName: string;\n hostClasses: string;\n hostId: string;\n hostOuterHTML: string;\n elementIndex: number;\n parentTagName: string;\n positionTop: number;\n positionLeft: number;\n}\n\n/**\n * Cached resource structure containing body and content type\n */\ninterface CachedResource {\n body: string;\n contentType: string;\n}\n\n/**\n * Middleware to process HTML content using Playwright for rendering dynamic content,\n * *if* the scrapeMode option requires it ('playwright' or 'auto').\n * It updates `context.content` with the rendered HTML if Playwright runs.\n * Subsequent middleware (e.g., HtmlCheerioParserMiddleware) should handle parsing this content.\n *\n * This middleware also supports URLs with embedded credentials (user:password@host) and ensures\n * credentials are used for all same-origin resource requests (not just the main page) via HTTP Basic Auth.\n *\n * Additionally, all custom headers from context.options?.headers are forwarded to Playwright requests.\n */\nexport class HtmlPlaywrightMiddleware implements ContentProcessorMiddleware {\n private browser: Browser | null = null;\n private readonly config: AppConfig[\"scraper\"];\n\n // Static LRU cache for all fetched resources, shared across instances\n private static readonly resourceCache = new SimpleMemoryCache<string, CachedResource>(\n defaults.scraper.fetcher.maxCacheItems,\n );\n\n constructor(config: AppConfig[\"scraper\"]) {\n this.config = config;\n }\n\n /**\n * Initializes the Playwright browser instance.\n * Consider making this more robust (e.g., lazy initialization, singleton).\n */\n private async ensureBrowser(): Promise<Browser> {\n if (!this.browser || !this.browser.isConnected()) {\n logger.debug(\"Launching new Playwright browser instance (Chromium)\");\n this.browser = await BrowserFetcher.launchBrowser();\n this.browser.on(\"disconnected\", () => {\n logger.debug(\"Playwright browser instance disconnected.\");\n this.browser = null;\n });\n }\n return this.browser;\n }\n\n /**\n * Closes the Playwright browser instance if it exists.\n * Should be called during application shutdown.\n * Attempts to close even if the browser is disconnected to ensure proper cleanup of zombie processes.\n */\n async closeBrowser(): Promise<void> {\n if (this.browser) {\n try {\n logger.debug(\"Closing Playwright browser instance...\");\n // Always attempt to close, even if disconnected, to reap zombie processes\n await this.browser.close();\n } catch (error) {\n // Log error but don't throw - cleanup should be non-fatal\n logger.warn(`⚠️ Error closing Playwright browser: ${error}`);\n } finally {\n // Always set to null to allow fresh browser on next request\n this.browser = null;\n }\n }\n }\n\n /**\n * Injects the shadow DOM extractor script into the page.\n * This script performs non-invasive extraction that preserves document structure.\n * The extraction function is called just-in-time when content is actually needed, ensuring we capture\n * the final state of all shadow DOMs after page loading is complete.\n * Returns an array of shadow mappings directly (empty array = no shadow DOMs found).\n */\n private async injectShadowDOMExtractor(page: Page): Promise<void> {\n await page.addInitScript(`\n window.shadowExtractor = {\n extract() {\n // Extract shadow DOM mappings\n const shadowMappings = [];\n \n function createShadowMapping(root, depth = 0) {\n if (depth > 15) return;\n \n // Use TreeWalker to traverse in document order\n const walker = document.createTreeWalker(\n root,\n NodeFilter.SHOW_ELEMENT,\n null,\n false\n );\n \n let currentNode = walker.nextNode();\n while (currentNode) {\n const element = currentNode;\n if (element.shadowRoot) {\n try {\n // Extract shadow DOM content without modifying anything\n const shadowChildren = Array.from(element.shadowRoot.children);\n const shadowHTML = shadowChildren.map(child => child.outerHTML).join('\\\\n');\n \n if (shadowHTML.trim()) {\n // Get position info for precise insertion later\n const rect = element.getBoundingClientRect();\n const elementIndex = Array.from(element.parentNode?.children || []).indexOf(element);\n \n shadowMappings.push({\n shadowContent: shadowHTML,\n hostTagName: element.tagName,\n hostClasses: element.className || '',\n hostId: element.id || '',\n hostOuterHTML: element.outerHTML,\n elementIndex: elementIndex,\n parentTagName: element.parentNode?.tagName || '',\n positionTop: rect.top,\n positionLeft: rect.left\n });\n }\n \n // Recursively process nested shadow DOMs\n createShadowMapping(element.shadowRoot, depth + 1);\n \n } catch (error) {\n console.debug('Shadow DOM access error:', error);\n }\n }\n currentNode = walker.nextNode();\n }\n }\n \n createShadowMapping(document);\n \n return shadowMappings;\n }\n };\n \n `);\n }\n\n /**\n * Extracts content using either shadow DOM non-invasive extraction or standard page.content() method.\n * Returns the extracted content and the method used.\n *\n * Performs just-in-time shadow DOM extraction after all page loading is complete.\n */\n private async extractContentWithShadowDOMSupport(page: Page): Promise<{\n content: string;\n method: string;\n }> {\n // Force fresh extraction right now (when everything is loaded)\n const [shadowMappings, originalPageContent] = await Promise.all([\n page.evaluate(() => {\n // Extract fresh data right now - just-in-time extraction\n // biome-ignore lint/suspicious/noExplicitAny: no type for injected script\n return (window as any).shadowExtractor?.extract() || [];\n }),\n page.content(),\n ]);\n\n if (shadowMappings.length === 0) {\n // No shadow DOMs - use standard page.content()\n logger.debug(\"No shadow DOMs detected - using page.content()\");\n return { content: originalPageContent, method: \"page.content()\" };\n } else {\n // Shadow DOMs found - combine content outside the browser (non-invasive)\n logger.debug(\n `Shadow DOMs detected - found ${shadowMappings.length} shadow host(s)`,\n );\n logger.debug(\"Combining content outside browser (non-invasive)\");\n\n // Combine original content with shadow content outside the browser\n const finalContent = this.combineContentSafely(originalPageContent, shadowMappings);\n return { content: finalContent, method: \"non-invasive shadow DOM extraction\" };\n }\n }\n\n /**\n * Waits for common loading indicators (spinners, loaders) that are currently visible to disappear from the page or frame.\n * Only waits for selectors that are present and visible at the time of check.\n *\n * @param pageOrFrame The Playwright page or frame instance to operate on.\n */\n private async waitForLoadingToComplete(\n pageOrFrame:\n | Page\n | { waitForSelector: Page[\"waitForSelector\"]; isVisible: Page[\"isVisible\"] },\n ): Promise<void> {\n const commonLoadingSelectors = [\n '[class*=\"loading\"]',\n '[class*=\"spinner\"]',\n '[class*=\"loader\"]',\n '[id*=\"loading\"]',\n '[class*=\"preload\"]',\n \"#loading\",\n '[aria-label*=\"loading\" i]',\n '[aria-label*=\"spinner\" i]',\n ];\n\n // Wait for all visible loading indicators in parallel\n const waitPromises: Promise<unknown>[] = [];\n for (const selector of commonLoadingSelectors) {\n try {\n // Use page.isVisible to check if any matching element is visible (legacy API, but works for any visible match)\n const isVisible = await pageOrFrame.isVisible(selector).catch(() => false);\n if (isVisible) {\n waitPromises.push(\n pageOrFrame\n .waitForSelector(selector, {\n state: \"hidden\",\n timeout: this.config.pageTimeoutMs,\n })\n .catch(() => {}),\n );\n }\n } catch {\n // Ignore errors (e.g., selector not found or timeout)\n }\n }\n if (waitPromises.length > 0) {\n await Promise.all(waitPromises);\n }\n }\n\n /**\n * Waits for all iframes on the page to load their content.\n * For each iframe, waits for the body to appear and loading indicators to disappear.\n *\n * @param page The Playwright page instance to operate on.\n */\n private async waitForIframesToLoad(page: Page): Promise<void> {\n try {\n // Get all iframe elements\n const iframes = await page.$$(\"iframe\");\n if (iframes.length === 0) {\n return;\n }\n\n logger.debug(`Found ${iframes.length} iframe(s) on ${page.url()}`);\n\n // Wait for all iframes to load in parallel\n const iframePromises = iframes.map((iframe, index) =>\n this.processIframe(page, iframe, index),\n );\n\n await Promise.all(iframePromises);\n logger.debug(`Finished waiting for all iframes to load`);\n } catch (error) {\n logger.debug(`Error during iframe loading for ${page.url()}: ${error}`);\n }\n }\n\n /**\n * Processes a single iframe: validates, extracts content, and replaces in main page.\n *\n * @param page The main page containing the iframe\n * @param iframe The iframe element handle\n * @param index The iframe index for logging/identification\n */\n private async processIframe(\n page: Page,\n iframe: ElementHandle,\n index: number,\n ): Promise<void> {\n try {\n const src = await iframe.getAttribute(\"src\");\n if (this.shouldSkipIframeSrc(src)) {\n logger.debug(`Skipping iframe ${index + 1} - no valid src (${src})`);\n return;\n }\n\n logger.debug(`Waiting for iframe ${index + 1} to load: ${src}`);\n\n // Get the frame content\n const frame = await iframe.contentFrame();\n if (!frame) {\n logger.debug(`Could not access content frame for iframe ${index + 1}`);\n return;\n }\n\n // Wait for the iframe body to load - if this times out, skip the rest of processing\n try {\n await frame.waitForSelector(\"body\", {\n timeout: this.config.pageTimeoutMs,\n });\n } catch {\n logger.debug(\n `Timeout waiting for body in iframe ${index + 1} - skipping content extraction`,\n );\n return;\n }\n\n // Wait for loading indicators in the iframe to complete (with timeout protection)\n try {\n await this.waitForLoadingToComplete(frame);\n } catch {\n logger.debug(\n `Timeout waiting for loading indicators in iframe ${index + 1} - proceeding anyway`,\n );\n }\n\n // Extract and replace iframe content (with timeout protection)\n let content: string | null = null;\n try {\n content = await this.extractIframeContent(frame);\n } catch (error) {\n logger.debug(`Error extracting content from iframe ${index + 1}: ${error}`);\n return;\n }\n\n if (content && content.trim().length > 0) {\n await this.replaceIframeWithContent(page, index, content);\n logger.debug(\n `Successfully extracted and replaced content for iframe ${index + 1}: ${src}`,\n );\n } else {\n logger.debug(`Iframe ${index + 1} body content is empty: ${src}`);\n }\n\n logger.debug(`Successfully loaded iframe ${index + 1}: ${src}`);\n } catch (error) {\n logger.debug(`Error processing iframe ${index + 1}: ${error}`);\n }\n }\n\n /**\n * Determines if an iframe src should be skipped during processing.\n *\n * @param src The iframe src attribute value\n * @returns true if the iframe should be skipped\n */\n private shouldSkipIframeSrc(src: string | null): boolean {\n return (\n !src ||\n src.startsWith(\"data:\") ||\n src.startsWith(\"javascript:\") ||\n src === \"about:blank\"\n );\n }\n\n /**\n * Extracts the body innerHTML from an iframe.\n *\n * @param frame The iframe's content frame\n * @returns The extracted HTML content or null if extraction fails\n */\n private async extractIframeContent(frame: Frame): Promise<string | null> {\n try {\n return await frame.$eval(\"body\", (el: HTMLElement) => el.innerHTML);\n } catch (error) {\n logger.debug(`Error extracting iframe content: ${error}`);\n return null;\n }\n }\n\n /**\n * Replaces an iframe element with its extracted content in the main page.\n *\n * @param page The main page containing the iframe\n * @param index The iframe index (0-based)\n * @param content The extracted content to replace the iframe with\n */\n private async replaceIframeWithContent(\n page: Page,\n index: number,\n content: string,\n ): Promise<void> {\n await page.evaluate(\n (args: [number, string]) => {\n const [iframeIndex, bodyContent] = args;\n const iframe = document.querySelectorAll(\"iframe\")[iframeIndex];\n if (iframe && bodyContent) {\n // Create a replacement div with the iframe content\n const replacement = document.createElement(\"div\");\n replacement.innerHTML = bodyContent;\n\n // Replace the iframe with the extracted content\n iframe.parentNode?.replaceChild(replacement, iframe);\n }\n },\n [index, content] as [number, string],\n );\n }\n\n /**\n * Waits for and processes framesets on the page by extracting content from each frame\n * and replacing the frameset with merged content.\n *\n * @param page The Playwright page instance to operate on.\n */\n private async waitForFramesetsToLoad(page: Page): Promise<void> {\n try {\n // Check if the page contains framesets\n const framesets = await page.$$(\"frameset\");\n if (framesets.length === 0) {\n return;\n }\n\n logger.debug(`Found ${framesets.length} frameset(s) on ${page.url()}`);\n\n // Extract all frame URLs from the frameset structure\n const frameUrls = await this.extractFrameUrls(page);\n if (frameUrls.length === 0) {\n logger.debug(\"No frame URLs found in framesets\");\n return;\n }\n\n logger.debug(`Found ${frameUrls.length} frame(s) to process`);\n\n // Fetch content from each frame\n const frameContents: Array<{ url: string; content: string; name?: string }> = [];\n for (const frameInfo of frameUrls) {\n try {\n const content = await this.fetchFrameContent(page, frameInfo.src);\n if (content && content.trim().length > 0) {\n frameContents.push({\n url: frameInfo.src,\n content,\n name: frameInfo.name,\n });\n logger.debug(`Successfully fetched content from frame: ${frameInfo.src}`);\n } else {\n logger.debug(`Frame content is empty: ${frameInfo.src}`);\n }\n } catch (error) {\n logger.debug(`Error fetching frame content from ${frameInfo.src}: ${error}`);\n }\n }\n\n // Merge frame contents and replace frameset\n if (frameContents.length > 0) {\n await this.mergeFrameContents(page, frameContents);\n logger.debug(\n `Successfully merged ${frameContents.length} frame(s) into main page`,\n );\n }\n\n logger.debug(`Finished processing framesets`);\n } catch (error) {\n logger.debug(`Error during frameset processing for ${page.url()}: ${error}`);\n }\n }\n\n /**\n * Extracts frame URLs from all framesets on the page in document order.\n *\n * @param page The Playwright page instance to operate on.\n * @returns Array of frame information objects with src and optional name.\n */\n private async extractFrameUrls(\n page: Page,\n ): Promise<Array<{ src: string; name?: string }>> {\n try {\n return await page.evaluate(() => {\n const frames: Array<{ src: string; name?: string }> = [];\n const frameElements = document.querySelectorAll(\"frame\");\n\n for (const frame of frameElements) {\n const src = frame.getAttribute(\"src\");\n if (src?.trim() && !src.startsWith(\"javascript:\") && src !== \"about:blank\") {\n const name = frame.getAttribute(\"name\") || undefined;\n frames.push({ src: src.trim(), name });\n }\n }\n\n return frames;\n });\n } catch (error) {\n logger.debug(`Error extracting frame URLs: ${error}`);\n return [];\n }\n }\n\n /**\n * Sets up caching route interception for a Playwright page.\n * This handles:\n * - Aborting non-essential resources (images, fonts, media)\n * - Caching GET requests to speed up subsequent loads\n * - Forwarding custom headers and credentials for same-origin requests\n *\n * @param page The Playwright page to set up routing for\n * @param customHeaders Custom headers to forward with requests\n * @param credentials Optional credentials for same-origin requests\n * @param origin The origin for same-origin credential checking\n */\n /**\n * Checks if an error is a Playwright \"Route is already handled\" error.\n * This specific error occurs when multiple handlers attempt to handle the same route.\n */\n private isRouteAlreadyHandledError(error: unknown): boolean {\n if (error instanceof Error) {\n return error.message.includes(\"Route is already handled\");\n }\n return false;\n }\n\n private async setupCachingRouteInterception(\n page: Page,\n customHeaders: Record<string, string> = {},\n credentials?: { username: string; password: string },\n origin?: string,\n ): Promise<void> {\n await page.route(\"**/*\", async (route) => {\n const reqUrl = route.request().url();\n const reqOrigin = (() => {\n try {\n return new URL(reqUrl).origin;\n } catch {\n return null;\n }\n })();\n const resourceType = route.request().resourceType();\n\n // Abort non-essential resources\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n try {\n return await route.abort();\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (abort): ${reqUrl}`);\n return;\n }\n // Re-throw other errors (page closed, invalid state, etc.)\n throw error;\n }\n }\n\n // Cache all GET requests to speed up subsequent page loads\n if (route.request().method() === \"GET\") {\n // Check cache first\n const cached = HtmlPlaywrightMiddleware.resourceCache.get(reqUrl);\n if (cached !== undefined) {\n logger.debug(`✓ Cache hit for ${resourceType}: ${reqUrl}`);\n try {\n return await route.fulfill({\n status: 200,\n contentType: cached.contentType,\n body: cached.body,\n });\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (fulfill cached): ${reqUrl}`);\n return;\n }\n // Re-throw other errors (bad response/options, closed page, etc.)\n throw error;\n }\n }\n\n // Cache miss - fetch and potentially cache the response\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials,\n origin,\n reqOrigin ?? undefined,\n );\n\n try {\n const response = await route.fetch({ headers });\n const body = await response.text();\n\n // Only cache if content is small enough and response was successful (2xx status)\n if (response.status() >= 200 && response.status() < 300 && body.length > 0) {\n const contentSizeBytes = Buffer.byteLength(body, \"utf8\");\n if (contentSizeBytes <= this.config.fetcher.maxCacheItemSizeBytes) {\n const contentType =\n response.headers()[\"content-type\"] || \"application/octet-stream\";\n HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType });\n logger.debug(\n `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`,\n );\n } else {\n logger.debug(\n `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${this.config.fetcher.maxCacheItemSizeBytes} bytes limit)`,\n );\n }\n }\n\n try {\n return await route.fulfill({ response });\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (fulfill): ${reqUrl}`);\n return;\n }\n // Re-throw other errors (bad response/options, closed page, etc.)\n throw error;\n }\n } catch (error) {\n // Handle network errors (DNS, connection refused, timeout, etc.)\n // Treat these as failed resource requests - abort gracefully\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(\n `Network error fetching ${resourceType} ${reqUrl}: ${errorMessage}`,\n );\n try {\n return await route.abort(\"failed\");\n } catch (abortError) {\n if (this.isRouteAlreadyHandledError(abortError)) {\n logger.debug(`Route already handled (abort after error): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw abortError;\n }\n }\n }\n\n // Non-GET requests: just forward with headers\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials,\n origin,\n reqOrigin ?? undefined,\n );\n\n try {\n return await route.continue({ headers });\n } catch (error) {\n // If route was already handled, return silently\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (continue): ${reqUrl}`);\n return;\n }\n\n // For other errors (network issues, closed page, etc.), try to abort\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(`Error continuing ${resourceType} ${reqUrl}: ${errorMessage}`);\n try {\n return await route.abort(\"failed\");\n } catch (abortError) {\n if (this.isRouteAlreadyHandledError(abortError)) {\n logger.debug(`Route already handled (abort after continue error): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw abortError;\n }\n }\n });\n }\n\n /**\n * Fetches content from a frame URL by navigating to it in a new page.\n * Uses LRU cache to avoid re-fetching identical frames across multiple pages.\n *\n * @param parentPage The parent page (used to resolve relative URLs and share context)\n * @param frameUrl The URL of the frame to fetch content from\n * @returns The HTML content of the frame\n */\n private async fetchFrameContent(parentPage: Page, frameUrl: string): Promise<string> {\n // Resolve relative URLs against the parent page URL\n const resolvedUrl = new URL(frameUrl, parentPage.url()).href;\n\n // Check cache first\n const cached = HtmlPlaywrightMiddleware.resourceCache.get(resolvedUrl);\n if (cached !== undefined) {\n logger.debug(`✓ Cache hit for frame: ${resolvedUrl}`);\n return cached.body;\n }\n\n logger.debug(`Cache miss for frame: ${resolvedUrl}`);\n\n let framePage: Page | null = null;\n try {\n // Create a new page in the same browser context for consistency\n framePage = await parentPage.context().newPage();\n\n // Set up the same caching route interception as the parent page\n await this.setupCachingRouteInterception(framePage);\n\n logger.debug(`Fetching frame content from: ${resolvedUrl}`);\n\n // Navigate to the frame URL\n await framePage.goto(resolvedUrl, {\n waitUntil: \"load\",\n timeout: this.config.pageTimeoutMs,\n });\n await framePage.waitForSelector(\"body\", {\n timeout: this.config.pageTimeoutMs,\n });\n\n // Wait for loading indicators to complete\n await this.waitForLoadingToComplete(framePage);\n\n // Extract the body content (not full HTML to avoid conflicts)\n const bodyContent = await framePage.$eval(\n \"body\",\n (el: HTMLElement) => el.innerHTML,\n );\n\n const content = bodyContent || \"\";\n\n // Only cache if content is small enough (avoid caching large content pages)\n const contentSizeBytes = Buffer.byteLength(content, \"utf8\");\n if (contentSizeBytes <= this.config.fetcher.maxCacheItemSizeBytes) {\n // Frame content is always HTML\n HtmlPlaywrightMiddleware.resourceCache.set(resolvedUrl, {\n body: content,\n contentType: \"text/html\",\n });\n logger.debug(\n `Cached frame content: ${resolvedUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`,\n );\n } else {\n logger.debug(\n `Frame content too large to cache: ${resolvedUrl} (${contentSizeBytes} bytes > ${this.config.fetcher.maxCacheItemSizeBytes} bytes limit)`,\n );\n }\n\n logger.debug(`Successfully fetched frame content from: ${resolvedUrl}`);\n return content;\n } catch (error) {\n logger.debug(`Error fetching frame content from ${frameUrl}: ${error}`);\n return \"\";\n } finally {\n if (framePage) {\n await framePage.unroute(\"**/*\");\n await framePage.close();\n }\n }\n }\n\n /**\n * Merges frame contents and replaces the frameset structure with the merged content.\n *\n * @param page The main page containing the frameset\n * @param frameContents Array of frame content objects with URL, content, and optional name\n */\n private async mergeFrameContents(\n page: Page,\n frameContents: Array<{ url: string; content: string; name?: string }>,\n ): Promise<void> {\n try {\n // Build merged content sequentially, preserving frameset definition order\n const mergedContent = frameContents\n .map((frame, index) => {\n const frameName = frame.name ? ` (${frame.name})` : \"\";\n const frameHeader = `<!-- Frame ${index + 1}${frameName}: ${frame.url} -->`;\n return `${frameHeader}\\n<div data-frame-url=\"${frame.url}\" data-frame-name=\"${frame.name || \"\"}\">\\n${frame.content}\\n</div>`;\n })\n .join(\"\\n\\n\");\n\n // Replace the entire frameset structure with merged content\n await page.evaluate((mergedHtml: string) => {\n // Find all framesets and replace them with the merged content\n const framesets = document.querySelectorAll(\"frameset\");\n if (framesets.length > 0) {\n // Create a body element with the merged content\n const body = document.createElement(\"body\");\n body.innerHTML = mergedHtml;\n\n // Replace the first frameset with our body element\n // (typically there's only one root frameset)\n const firstFrameset = framesets[0];\n if (firstFrameset.parentNode) {\n firstFrameset.parentNode.replaceChild(body, firstFrameset);\n }\n\n // Remove any remaining framesets\n for (let i = 1; i < framesets.length; i++) {\n const frameset = framesets[i];\n if (frameset.parentNode) {\n frameset.parentNode.removeChild(frameset);\n }\n }\n }\n }, mergedContent);\n\n logger.debug(\"Successfully replaced frameset with merged content\");\n } catch (error) {\n logger.debug(`Error merging frame contents: ${error}`);\n }\n }\n\n /**\n * Processes the context using Playwright, rendering dynamic content and propagating credentials for all same-origin requests.\n *\n * - Parses credentials from the URL (if present).\n * - Uses browser.newContext({ httpCredentials }) for HTTP Basic Auth on the main page and subresources.\n * - Injects Authorization header for all same-origin requests if credentials are present and not already set.\n * - Forwards all custom headers from context.options?.headers to Playwright requests.\n * - Waits for common loading indicators to disappear before extracting HTML.\n *\n * @param context The middleware context containing the HTML and source URL.\n * @param next The next middleware function in the pipeline.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // Check if we have a MIME type from the raw content and if it's suitable for HTML processing\n const contentType = context.options?.headers?.[\"content-type\"] || context.contentType;\n\n // Safety check: If we detect this is definitely not HTML content, skip Playwright\n if (\n contentType &&\n typeof contentType === \"string\" &&\n !MimeTypeUtils.isHtml(contentType)\n ) {\n logger.debug(\n `Skipping Playwright rendering for ${context.source} - content type '${contentType}' is not HTML`,\n );\n await next();\n return;\n }\n\n // Determine if Playwright should run based on scrapeMode\n const scrapeMode = context.options?.scrapeMode ?? ScrapeMode.Auto;\n const shouldRunPlaywright =\n scrapeMode === ScrapeMode.Playwright || scrapeMode === ScrapeMode.Auto;\n\n if (!shouldRunPlaywright) {\n // Handle gracefully although the middleware shouldn't even be in the pipeline\n logger.debug(\n `Skipping Playwright rendering for ${context.source} as scrapeMode is '${scrapeMode}'.`,\n );\n await next();\n return;\n }\n\n logger.debug(\n `Running Playwright rendering for ${context.source} (scrapeMode: '${scrapeMode}')`,\n );\n\n let page: Page | null = null;\n let browserContext: BrowserContext | null = null;\n let renderedHtml: string | null = null;\n\n // Extract credentials and origin using helper\n const { credentials, origin } = extractCredentialsAndOrigin(context.source);\n\n // Extract custom headers (Record<string, string>)\n const customHeaders: Record<string, string> = context.options?.headers ?? {};\n\n try {\n const browser = await this.ensureBrowser();\n\n // Always create a browser context (with or without credentials)\n if (credentials) {\n browserContext = await browser.newContext({ httpCredentials: credentials });\n } else {\n browserContext = await browser.newContext();\n }\n page = await browserContext.newPage();\n\n logger.debug(`Playwright: Processing ${context.source}`);\n\n // Inject shadow DOM extractor script early\n await this.injectShadowDOMExtractor(page);\n\n // Set up route interception with special handling for the initial page load\n await page.route(\"**/*\", async (route) => {\n const reqUrl = route.request().url();\n\n // Serve the initial HTML for the main page (bypass cache and fetch)\n if (reqUrl === context.source) {\n try {\n return await route.fulfill({\n status: 200,\n contentType: \"text/html; charset=utf-8\",\n body: context.content,\n });\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (initial page): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw error;\n }\n }\n\n // For all other requests, use the standard caching logic\n // We need to manually handle the interception since we can't delegate to another route\n const reqOrigin = (() => {\n try {\n return new URL(reqUrl).origin;\n } catch {\n return null;\n }\n })();\n const resourceType = route.request().resourceType();\n\n // Abort non-essential resources\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n try {\n return await route.abort();\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (abort): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw error;\n }\n }\n\n // Cache all GET requests to speed up subsequent page loads\n if (route.request().method() === \"GET\") {\n // Check cache first\n const cached = HtmlPlaywrightMiddleware.resourceCache.get(reqUrl);\n if (cached !== undefined) {\n logger.debug(`✓ Cache hit for ${resourceType}: ${reqUrl}`);\n try {\n return await route.fulfill({\n status: 200,\n contentType: cached.contentType,\n body: cached.body,\n });\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (fulfill cached): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw error;\n }\n }\n\n // Cache miss - fetch and potentially cache the response\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials ?? undefined,\n origin ?? undefined,\n reqOrigin ?? undefined,\n );\n\n try {\n const response = await route.fetch({ headers });\n const body = await response.text();\n\n // Only cache if content is small enough and response was successful (2xx status)\n if (response.status() >= 200 && response.status() < 300 && body.length > 0) {\n const contentSizeBytes = Buffer.byteLength(body, \"utf8\");\n const maxCacheItemSizeBytes =\n this.config?.fetcher?.maxCacheItemSizeBytes ??\n defaults.scraper.fetcher.maxCacheItemSizeBytes;\n if (contentSizeBytes <= maxCacheItemSizeBytes) {\n const contentType =\n response.headers()[\"content-type\"] || \"application/octet-stream\";\n HtmlPlaywrightMiddleware.resourceCache.set(reqUrl, { body, contentType });\n logger.debug(\n `Cached ${resourceType}: ${reqUrl} (${contentSizeBytes} bytes, cache size: ${HtmlPlaywrightMiddleware.resourceCache.size})`,\n );\n } else {\n logger.debug(\n `Resource too large to cache: ${reqUrl} (${contentSizeBytes} bytes > ${maxCacheItemSizeBytes} bytes limit)`,\n );\n }\n }\n\n try {\n return await route.fulfill({ response });\n } catch (error) {\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (fulfill): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw error;\n }\n } catch (error) {\n // Handle network errors (DNS, connection refused, timeout, etc.)\n // Treat these as failed resource requests - abort gracefully\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(\n `Network error fetching ${resourceType} ${reqUrl}: ${errorMessage}`,\n );\n try {\n return await route.abort(\"failed\");\n } catch (abortError) {\n if (this.isRouteAlreadyHandledError(abortError)) {\n logger.debug(`Route already handled (abort after error): ${reqUrl}`);\n return;\n }\n // Re-throw other errors\n throw abortError;\n }\n }\n }\n\n // Non-GET requests: just forward with headers\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials ?? undefined,\n origin ?? undefined,\n reqOrigin ?? undefined,\n );\n\n try {\n return await route.continue({ headers });\n } catch (error) {\n // If route was already handled, return silently\n if (this.isRouteAlreadyHandledError(error)) {\n logger.debug(`Route already handled (continue): ${reqUrl}`);\n return;\n }\n\n // For other errors (network issues, closed page, etc.), try to abort\n const errorMessage = error instanceof Error ? error.message : String(error);\n logger.debug(`Error continuing ${resourceType} ${reqUrl}: ${errorMessage}`);\n try {\n return await route.abort(\"failed\");\n } catch (abortError) {\n if (this.isRouteAlreadyHandledError(abortError)) {\n logger.debug(\n `Route already handled (abort after continue error): ${reqUrl}`,\n );\n return;\n }\n // Re-throw other errors\n throw abortError;\n }\n }\n });\n\n // Load initial HTML content\n await page.goto(context.source, { waitUntil: \"load\" });\n\n // Wait for either body (normal HTML) or frameset (frameset documents) to appear\n const pageTimeoutMs = this.config.pageTimeoutMs ?? defaults.scraper.pageTimeoutMs;\n await page.waitForSelector(\"body, frameset\", {\n timeout: pageTimeoutMs,\n });\n\n // Wait for network idle to let dynamic content initialize\n try {\n await page.waitForLoadState(\"networkidle\", {\n timeout: pageTimeoutMs,\n });\n } catch {\n logger.debug(\"Network idle timeout, proceeding anyway\");\n }\n\n await this.waitForLoadingToComplete(page);\n await this.waitForIframesToLoad(page);\n await this.waitForFramesetsToLoad(page);\n\n // Extract content using shadow DOM-aware method\n const { content, method } = await this.extractContentWithShadowDOMSupport(page);\n renderedHtml = content;\n logger.debug(\n `Playwright: Successfully rendered content for ${context.source} using ${method}`,\n );\n } catch (error) {\n logger.error(`❌ Playwright failed to render ${context.source}: ${error}`);\n context.errors.push(\n error instanceof Error\n ? error\n : new Error(`Playwright rendering failed: ${String(error)}`),\n );\n } finally {\n // Ensure page/context are closed even if subsequent steps fail\n if (page) {\n await page.unroute(\"**/*\");\n await page.close();\n }\n if (browserContext) {\n await browserContext.close();\n }\n }\n\n if (renderedHtml !== null) {\n context.content = renderedHtml;\n logger.debug(\n `Playwright middleware updated content for ${context.source}. Proceeding.`,\n );\n } else {\n logger.warn(\n `⚠️ Playwright rendering resulted in null content for ${context.source}. Proceeding without content update.`,\n );\n }\n\n await next();\n }\n\n /**\n * Safely combines original page content with shadow DOM content outside the browser context.\n * This avoids triggering any anti-scraping detection mechanisms.\n */\n private combineContentSafely(\n originalContent: string,\n shadowMappings: ShadowMapping[],\n ): string {\n let combinedContent = originalContent;\n\n // Add shadow content at the end of the body to avoid breaking the document structure\n const bodyCloseIndex = combinedContent.lastIndexOf(\"</body>\");\n if (bodyCloseIndex !== -1) {\n let shadowContentHTML = \"\\n<!-- SHADOW DOM CONTENT EXTRACTED SAFELY -->\\n\";\n\n // Sort by content length (largest first) to prioritize important content\n const sortedMappings = shadowMappings.sort(\n (a, b) => b.shadowContent.length - a.shadowContent.length,\n );\n\n sortedMappings.forEach((mapping) => {\n shadowContentHTML += `\\n<!-- SHADOW CONTENT: ${mapping.hostTagName} (${mapping.shadowContent.length} chars) -->\\n`;\n shadowContentHTML += mapping.shadowContent;\n shadowContentHTML += `\\n<!-- END SHADOW CONTENT: ${mapping.hostTagName} -->\\n`;\n });\n\n shadowContentHTML += \"\\n<!-- END ALL SHADOW DOM CONTENT -->\\n\";\n\n // Insert before closing body tag\n combinedContent =\n combinedContent.slice(0, bodyCloseIndex) +\n shadowContentHTML +\n combinedContent.slice(bodyCloseIndex);\n }\n\n return combinedContent;\n }\n}\n\n/**\n * Extracts credentials and origin from a URL string.\n * Returns { credentials, origin } where credentials is null if not present.\n */\nexport function extractCredentialsAndOrigin(urlString: string): {\n credentials: { username: string; password: string } | null;\n origin: string | null;\n} {\n try {\n const url = new URL(urlString);\n const origin = url.origin;\n if (url.username && url.password) {\n return {\n credentials: { username: url.username, password: url.password },\n origin,\n };\n }\n return { credentials: null, origin };\n } catch {\n return { credentials: null, origin: null };\n }\n}\n\n/**\n * Merges Playwright request headers, custom headers, and credentials.\n * - Custom headers are merged in unless already present (except Authorization, see below).\n * - If credentials are present and the request is same-origin, injects Authorization if not already set.\n */\nexport function mergePlaywrightHeaders(\n requestHeaders: Record<string, string>,\n customHeaders: Record<string, string>,\n credentials?: { username: string; password: string },\n origin?: string,\n reqOrigin?: string,\n): Record<string, string> {\n let headers = { ...requestHeaders };\n for (const [key, value] of Object.entries(customHeaders)) {\n if (key.toLowerCase() === \"authorization\" && headers.authorization) continue;\n headers[key] = value;\n }\n if (credentials && origin && reqOrigin === origin && !headers.authorization) {\n const basic = Buffer.from(`${credentials.username}:${credentials.password}`).toString(\n \"base64\",\n );\n headers = {\n ...headers,\n Authorization: `Basic ${basic}`,\n };\n }\n return headers;\n}\n","import { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Options for HtmlSanitizerMiddleware.\n */\nexport interface HtmlSanitizerOptions {\n /** CSS selectors for elements to remove *in addition* to the defaults. */\n excludeSelectors?: string[];\n}\n\n/**\n * Middleware to remove unwanted elements from parsed HTML content using Cheerio.\n * It expects the Cheerio API object (`context.dom`) to be populated by a preceding middleware\n * (e.g., HtmlCheerioParserMiddleware).\n * It modifies the `context.dom` object in place.\n */\nexport class HtmlSanitizerMiddleware implements ContentProcessorMiddleware {\n // Default selectors to remove\n private readonly defaultSelectorsToRemove = [\n \"aside\",\n \"nav\",\n \"footer\",\n \"script\",\n \"style\",\n \"noscript\",\n \"svg\",\n \"link\",\n \"meta\",\n \"iframe\",\n \"header\",\n \"button\",\n \"input\",\n \"textarea\",\n \"select\",\n // \"form\", // Keep commented\n \".ads\",\n \".advertisement\",\n \".banner\",\n \".cookie-banner\",\n \".cookie-consent\",\n \".hidden\",\n \".hide\",\n \".mobile-menu\",\n \".mobile-nav\",\n \".modal\",\n \".nav-bar\",\n \".overlay\",\n \".popup\",\n \".promo\",\n \".mw-editsection\",\n \".search-bar\",\n \".search-form\",\n \".side-bar\",\n \".sidebar\",\n \".social-share\",\n \".sticky\",\n \".table-of-contents\",\n \".toc\",\n \"#ads\",\n \"#banner\",\n \"#cookieBanner\",\n \"#mobile-menu\",\n \"#mobile-nav\",\n \"#modal\",\n \"#nav\",\n \"#overlay\",\n \"#popup\",\n \"#sidebar\",\n \"#socialMediaBox\",\n \"#stickyHeader\",\n \"#ad-container\",\n \".ad-container\",\n \".login-form\",\n \".signup-form\",\n \".tooltip\",\n \".dropdown-menu\",\n // \".alert\", // Keep commented\n \".breadcrumb\",\n \".pagination\",\n // '[role=\"alert\"]', // Keep commented\n '[role=\"banner\"]',\n '[role=\"complementary\"]',\n '[role=\"dialog\"]',\n '[role=\"alertdialog\"]',\n '[role=\"navigation\"]',\n '[role=\"search\"]',\n '[role=\"region\"][aria-label*=\"skip\" i]',\n '[aria-modal=\"true\"]',\n \".noprint\",\n ];\n\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // Check if Cheerio DOM exists\n const $ = context.dom;\n if (!$) {\n logger.warn(\n `⏭️ Skipping ${this.constructor.name}: context.dom is missing. Ensure HtmlCheerioParserMiddleware runs before this.`,\n );\n await next();\n return;\n }\n\n try {\n // Capture the body content before sanitization for safety net\n const bodyBeforeSanitization = $(\"body\").html() || \"\";\n const textLengthBefore = $(\"body\").text().trim().length;\n\n // Remove unwanted elements using Cheerio\n const selectorsToRemove = [\n ...(context.options.excludeSelectors || []), // Use options from the context\n ...this.defaultSelectorsToRemove,\n ];\n logger.debug(\n `Removing elements matching ${selectorsToRemove.length} selectors for ${context.source}`,\n );\n let removedCount = 0;\n for (const selector of selectorsToRemove) {\n try {\n const elements = $(selector); // Use Cheerio selector\n // Filter out html and body tags to prevent removing them or their entire content\n const filteredElements = elements.filter(function () {\n const tagName = $(this).prop(\"tagName\")?.toLowerCase();\n return tagName !== \"html\" && tagName !== \"body\";\n });\n const count = filteredElements.length;\n if (count > 0) {\n filteredElements.remove(); // Use Cheerio remove\n removedCount += count;\n }\n } catch (selectorError) {\n // Log invalid selectors but continue with others\n // Cheerio is generally more tolerant of invalid selectors than querySelectorAll\n logger.warn(\n `⚠️ Potentially invalid selector \"${selector}\" during element removal: ${selectorError}`,\n );\n context.errors.push(\n new Error(`Invalid selector \"${selector}\": ${selectorError}`),\n );\n }\n }\n logger.debug(`Removed ${removedCount} elements for ${context.source}`);\n\n // Safety net: Check if sanitization removed all content\n const textLengthAfter = $(\"body\").text().trim().length;\n if (textLengthBefore > 0 && textLengthAfter === 0) {\n logger.warn(\n `⚠️ Sanitization removed all content from ${context.source}. Reverting to pre-sanitization state.`,\n );\n // Restore the body content\n $(\"body\").html(bodyBeforeSanitization);\n }\n\n // The context.dom object ($) has been modified in place.\n } catch (error) {\n logger.error(\n `❌ Error during HTML element removal for ${context.source}: ${error}`,\n );\n context.errors.push(\n error instanceof Error\n ? error\n : new Error(`HTML element removal failed: ${String(error)}`),\n );\n // Decide if pipeline should stop? For now, continue.\n }\n\n // Proceed to the next middleware\n await next();\n }\n}\n","// @ts-expect-error\nimport { gfm } from \"@joplin/turndown-plugin-gfm\";\nimport TurndownService from \"turndown\";\nimport { logger } from \"../../utils/logger\";\nimport { fullTrim } from \"../../utils/string\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to convert the final processed HTML content (from Cheerio object in context.dom)\n * into Markdown using Turndown, applying custom rules.\n */\nexport class HtmlToMarkdownMiddleware implements ContentProcessorMiddleware {\n private turndownService: TurndownService;\n\n constructor() {\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 this.turndownService.use(gfm);\n\n this.addCustomRules();\n }\n\n private addCustomRules(): void {\n // Preserve code blocks and syntax (replicated from HtmlProcessor)\n this.turndownService.addRule(\"pre\", {\n filter: [\"pre\"],\n replacement: (_content: string, node: Node) => {\n const element = node as unknown as HTMLElement;\n let language = element.getAttribute(\"data-language\") || \"\";\n if (!language) {\n // Try to infer the language from the class name\n // This is a common pattern in syntax highlighters\n const highlightElement =\n element.closest(\n '[class*=\"highlight-source-\"], [class*=\"highlight-\"], [class*=\"language-\"]',\n ) ||\n element.querySelector(\n '[class*=\"highlight-source-\"], [class*=\"highlight-\"], [class*=\"language-\"]',\n );\n if (highlightElement) {\n const className = highlightElement.className;\n const match = className.match(\n /(?:highlight-source-|highlight-|language-)(\\w+)/,\n );\n if (match) language = match[1];\n }\n }\n\n const brElements = Array.from(element.querySelectorAll(\"br\"));\n for (const br of brElements) {\n br.replaceWith(\"\\n\");\n }\n const text = element.textContent || \"\";\n\n return `\\n\\`\\`\\`${language}\\n${text.replace(/^\\n+|\\n+$/g, \"\")}\\n\\`\\`\\`\\n`;\n },\n });\n this.turndownService.addRule(\"anchor\", {\n filter: [\"a\"],\n replacement: (content: string, node: Node) => {\n const element = node as HTMLElement;\n const href = element.getAttribute(\"href\");\n const normalizedContent = this.normalizeLinkContent(content);\n\n if (!normalizedContent || normalizedContent === \"#\") {\n return \"\"; // Remove if content is # or empty\n }\n if (!href) {\n return normalizedContent; // Preserve content if href is missing or empty\n }\n return `[${normalizedContent}](${href})`; // Standard link conversion\n },\n });\n }\n\n private normalizeLinkContent(content: string): string {\n return fullTrim(content).replace(/[ \\t]*[\\r\\n]+[ \\t]*/g, \" \");\n }\n\n /**\n * Processes the context to convert the sanitized HTML body node to Markdown.\n * @param context The current processing context.\n * @param next Function to call the next middleware.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // Check if we have a Cheerio object from a previous step\n const $ = context.dom;\n if (!$) {\n logger.warn(\n `⏭️ Skipping ${this.constructor.name}: context.dom is missing. Ensure HtmlCheerioParserMiddleware ran correctly.`,\n );\n await next();\n return;\n }\n\n // Only process if we have a Cheerio object (implicitly means it's HTML)\n try {\n logger.debug(`Converting HTML content to Markdown for ${context.source}`);\n // Provide Turndown with the HTML string content from the Cheerio object's body,\n // or the whole document if body is empty/unavailable.\n const htmlToConvert = $(\"body\").html() || $.html();\n const markdown = this.turndownService.turndown(htmlToConvert).trim();\n\n if (!markdown) {\n // If conversion results in empty markdown, log a warning but treat as valid empty markdown\n const warnMsg = `HTML to Markdown conversion resulted in empty content for ${context.source}.`;\n logger.warn(`⚠️ ${warnMsg}`);\n context.content = \"\";\n } else {\n // Conversion successful and produced non-empty markdown\n context.content = markdown;\n logger.debug(`Successfully converted HTML to Markdown for ${context.source}`);\n }\n\n // Update contentType to reflect the converted format\n context.contentType = \"text/markdown\";\n } catch (error) {\n logger.error(\n `❌ Error converting HTML to Markdown for ${context.source}: ${error}`,\n );\n context.errors.push(\n new Error(\n `Failed to convert HTML to Markdown: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n // Decide if pipeline should stop? For now, continue.\n }\n\n // Call the next middleware in the chain regardless of whether conversion happened\n await next();\n\n // No need to close/free Cheerio object explicitly\n // context.dom = undefined; // Optionally clear the dom property if no longer needed downstream\n }\n}\n","/**\n * Utility functions for enhanced charset detection and handling.\n */\n\nimport { logger } from \"../../utils/logger\";\n\n/**\n * Detects charset from HTML meta tags.\n * Looks for both <meta charset=\"...\"> and <meta http-equiv=\"Content-Type\" content=\"text/html; charset=...\">\n */\nexport function detectCharsetFromHtml(htmlContent: string): string | undefined {\n // Match <meta charset=\"...\"> (HTML5 style)\n const charsetMatch = htmlContent.match(\n /<meta\\s+charset\\s*=\\s*[\"']?([^\"'>\\s]+)[\"']?[^>]*>/i,\n );\n if (charsetMatch) {\n return charsetMatch[1].toLowerCase();\n }\n\n // Match <meta http-equiv=\"Content-Type\" content=\"...charset=...\"> (HTML4 style)\n const httpEquivMatch = htmlContent.match(\n /<meta\\s+http-equiv\\s*=\\s*[\"']?content-type[\"']?\\s+content\\s*=\\s*[\"']?[^\"'>]*charset=([^\"'>\\s;]+)/i,\n );\n if (httpEquivMatch) {\n return httpEquivMatch[1].toLowerCase();\n }\n\n return undefined;\n}\n\n/**\n * Determines the best charset to use for decoding content.\n * Prioritizes HTML meta tags over HTTP headers for HTML content.\n */\nexport function resolveCharset(\n httpCharset: string | undefined,\n htmlContent: string | Buffer,\n mimeType: string,\n): string {\n // For non-HTML content, use HTTP charset or default to UTF-8\n if (!mimeType.includes(\"html\")) {\n return httpCharset || \"utf-8\";\n }\n\n // For HTML content, try to detect charset from meta tags\n let htmlString: string;\n try {\n // Try to decode as UTF-8 first to look for meta tags\n htmlString =\n typeof htmlContent === \"string\" ? htmlContent : htmlContent.toString(\"utf-8\");\n } catch {\n // If UTF-8 fails, use the HTTP charset or fall back to latin1 for meta tag detection\n htmlString =\n typeof htmlContent === \"string\"\n ? htmlContent\n : htmlContent.toString((httpCharset || \"latin1\") as BufferEncoding);\n }\n\n // Look for charset in HTML meta tags (usually in first 1024 bytes)\n const headContent = htmlString.substring(0, 1024);\n const metaCharset = detectCharsetFromHtml(headContent);\n\n if (metaCharset) {\n logger.debug(`Detected charset from HTML meta tag: ${metaCharset}`);\n return metaCharset;\n }\n\n if (httpCharset) {\n logger.debug(`Using charset from HTTP header: ${httpCharset}`);\n return httpCharset;\n }\n\n logger.debug(\"No charset detected, defaulting to UTF-8\");\n return \"utf-8\";\n}\n\n/**\n * Common charset aliases and their canonical names.\n * Helps handle cases where servers use non-standard charset names.\n */\nexport const CHARSET_ALIASES: Record<string, string> = {\n \"iso-8859-1\": \"latin1\",\n \"iso_8859-1\": \"latin1\",\n \"latin-1\": \"latin1\",\n \"windows-1252\": \"cp1252\",\n \"cp-1252\": \"cp1252\",\n \"ms-ansi\": \"cp1252\",\n utf8: \"utf-8\",\n unicode: \"utf-8\",\n \"us-ascii\": \"ascii\",\n ascii: \"ascii\",\n};\n\n/**\n * Normalizes charset name to handle common aliases.\n */\nexport function normalizeCharset(charset: string): string {\n const normalized = charset.toLowerCase().trim();\n return CHARSET_ALIASES[normalized] || normalized;\n}\n","import iconv from \"iconv-lite\";\nimport { normalizeCharset } from \"./charset\";\n\n/**\n * Decodes a Buffer or string to a JavaScript string using the specified charset.\n * The charset should be the encoding as reported by the source (e.g., HTTP header).\n * The result is always a valid JS string (Unicode/UTF-16).\n *\n * If the charset is missing or unsupported, falls back to UTF-8.\n *\n * @param content The content to decode (Buffer or string)\n * @param charset The source encoding (e.g., 'utf-8', 'iso-8859-1', 'utf-16le', etc.)\n * @returns The decoded string\n */\nexport function convertToString(content: string | Buffer, charset?: string): string {\n if (typeof content === \"string\") return content;\n\n const normalizedCharset = charset ? normalizeCharset(charset) : \"utf-8\";\n\n try {\n return iconv.decode(content, normalizedCharset);\n } catch {\n // Fallback to utf-8 if decoding fails\n try {\n return iconv.decode(content, \"utf-8\");\n } catch {\n // Last resort: decode as latin1 which can handle any byte sequence\n return iconv.decode(content, \"latin1\");\n }\n }\n}\n","import { GreedySplitter } from \"../../splitter/GreedySplitter\";\nimport { SemanticMarkdownSplitter } from \"../../splitter/SemanticMarkdownSplitter\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport { HtmlCheerioParserMiddleware } from \"../middleware/HtmlCheerioParserMiddleware\";\nimport { HtmlLinkExtractorMiddleware } from \"../middleware/HtmlLinkExtractorMiddleware\";\nimport { HtmlMetadataExtractorMiddleware } from \"../middleware/HtmlMetadataExtractorMiddleware\";\nimport { HtmlNormalizationMiddleware } from \"../middleware/HtmlNormalizationMiddleware\";\nimport { HtmlPlaywrightMiddleware } from \"../middleware/HtmlPlaywrightMiddleware\";\nimport { HtmlSanitizerMiddleware } from \"../middleware/HtmlSanitizerMiddleware\";\nimport { HtmlToMarkdownMiddleware } from \"../middleware/HtmlToMarkdownMiddleware\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { convertToString } from \"../utils/buffer\";\nimport { resolveCharset } from \"../utils/charset\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\n/**\n * HtmlPipeline - Processes HTML content into Markdown chunks.\n * Uses Playwright for rendering if needed and Cheerio for semantic extraction.\n */\nexport class HtmlPipeline extends BasePipeline {\n private readonly playwrightMiddleware: HtmlPlaywrightMiddleware;\n private readonly standardMiddleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(config: AppConfig) {\n super();\n\n const preferredChunkSize = config.splitter.preferredChunkSize;\n const maxChunkSize = config.splitter.maxChunkSize;\n const minChunkSize = config.splitter.minChunkSize;\n\n this.playwrightMiddleware = new HtmlPlaywrightMiddleware(config.scraper);\n this.standardMiddleware = [\n new HtmlCheerioParserMiddleware(),\n new HtmlMetadataExtractorMiddleware(),\n new HtmlLinkExtractorMiddleware(),\n new HtmlSanitizerMiddleware(),\n new HtmlNormalizationMiddleware(),\n new HtmlToMarkdownMiddleware(),\n ];\n\n // Create the two-phase splitting: semantic + size optimization\n const semanticSplitter = new SemanticMarkdownSplitter(\n preferredChunkSize,\n maxChunkSize,\n );\n this.greedySplitter = new GreedySplitter(\n semanticSplitter,\n minChunkSize,\n preferredChunkSize,\n maxChunkSize,\n );\n }\n\n canProcess(mimeType: string): boolean {\n return MimeTypeUtils.isHtml(mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n // Use enhanced charset detection that considers HTML meta tags\n const resolvedCharset = resolveCharset(\n rawContent.charset,\n rawContent.content,\n rawContent.mimeType,\n );\n const contentString = convertToString(rawContent.content, resolvedCharset);\n\n const context: MiddlewareContext = {\n content: contentString,\n contentType: rawContent.mimeType || \"text/html\",\n source: rawContent.source,\n // metadata: {},\n links: [],\n errors: [],\n options,\n fetcher,\n };\n\n // Build middleware stack dynamically based on scrapeMode\n let middleware: ContentProcessorMiddleware[] = [...this.standardMiddleware];\n if (options.scrapeMode === \"playwright\" || options.scrapeMode === \"auto\") {\n middleware = [this.playwrightMiddleware, ...middleware];\n }\n\n // Execute the middleware stack using the base class method\n await this.executeMiddlewareStack(middleware, context);\n\n // Split the content using SemanticMarkdownSplitter (HTML is converted to markdown by middleware)\n const chunks = await this.greedySplitter.splitText(\n typeof context.content === \"string\" ? context.content : \"\",\n );\n\n return {\n title: context.title,\n contentType: context.contentType,\n textContent: context.content,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n\n /**\n * Cleanup resources used by this pipeline, specifically the Playwright browser instance.\n * Errors during cleanup are logged but not propagated to ensure graceful shutdown.\n */\n public async close(): Promise<void> {\n await super.close(); // Call base class close (no-op by default)\n try {\n await this.playwrightMiddleware.closeBrowser();\n } catch (error) {\n // Log error but don't throw - cleanup should be best-effort\n // The closeBrowser method already handles errors internally, but\n // this provides an additional safety layer for unexpected failures\n logger.warn(`⚠️ Error during browser cleanup: ${error}`);\n }\n }\n}\n","/**\n * TextDocumentSplitter - Simple text-based document splitter\n *\n * This splitter provides basic text splitting functionality for plain text files.\n * It uses the TextContentSplitter for hierarchical text splitting with no semantic organization.\n * This is a fallback splitter for any text document that cannot be handled by HTML, Markdown,\n * or Source Code document splitters.\n */\n\nimport { MinimumChunkSizeError } from \"./errors\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { Chunk, DocumentSplitter, SplitterConfig } from \"./types\";\n\n/**\n * Simple document splitter for plain text files.\n * Uses TextContentSplitter for hierarchical text splitting with no semantic organization.\n * This is a fallback splitter for any text document that cannot be handled by HTML,\n * Markdown, or Source Code document splitters.\n */\nexport class TextDocumentSplitter implements DocumentSplitter {\n private config: SplitterConfig;\n private textSplitter: TextContentSplitter;\n\n constructor(config: SplitterConfig) {\n this.config = config;\n\n this.textSplitter = new TextContentSplitter({\n chunkSize: this.config.maxChunkSize,\n });\n }\n\n async splitText(content: string): Promise<Chunk[]> {\n if (!content.trim()) {\n return [];\n }\n\n try {\n // Split the text content into chunks\n const chunks = await this.textSplitter.split(content);\n\n // Convert string chunks to ContentChunk objects\n return chunks.map((chunk) => ({\n types: [\"text\"] as const,\n content: chunk,\n section: {\n level: 0,\n path: [],\n },\n }));\n } catch (error) {\n // If splitting fails due to minimum chunk size error (e.g., a very long word/token),\n // forcefully split the content by character count to ensure we never return chunks\n // that exceed the maximum size. This is a last resort to handle unsplittable content\n // like very long strings without spaces or newlines.\n\n // For MinimumChunkSizeError or other text splitting errors, forcefully split by character count\n if (!(error instanceof MinimumChunkSizeError) && error instanceof Error) {\n // Log unexpected errors but still proceed with forceful splitting to avoid data loss\n console.warn(\n `Unexpected text splitting error: ${error.message}. Forcing character-based split.`,\n );\n }\n\n const chunks: Chunk[] = [];\n let offset = 0;\n while (offset < content.length) {\n const chunkContent = content.substring(offset, offset + this.config.maxChunkSize);\n chunks.push({\n types: [\"text\"] as const,\n content: chunkContent,\n section: {\n level: 0,\n path: [],\n },\n });\n offset += this.config.maxChunkSize;\n }\n return chunks;\n }\n }\n}\n","/**\n * JsonDocumentSplitter - Concatenation-friendly JSON document splitting.\n *\n * Creates minimal, concatenable chunks that form valid JSON when combined.\n * Each chunk is a building block: opening braces, individual properties with proper commas,\n * nested structures, and closing braces. Designed to work with GreedySplitter for optimization.\n *\n * Algorithm:\n * 1. Create opening structure chunks (braces/brackets)\n * 2. Create individual property/element chunks with proper punctuation\n * 3. Process nested structures recursively up to maxDepth\n * 4. Maintain proper indentation and hierarchical paths\n * 5. Let GreedySplitter handle size optimization\n * 6. Fall back to text-based chunking if maxChunks limit is exceeded or maxDepth is reached\n */\n\nimport { defaults } from \"../utils/config\";\nimport { TextDocumentSplitter } from \"./TextDocumentSplitter\";\nimport type { Chunk, DocumentSplitter, SplitterConfig } from \"./types\";\n\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\n/**\n * Options for the JsonDocumentSplitter\n */\nexport interface JsonDocumentSplitterOptions {\n /**\n * Maximum depth to traverse in the JSON object.\n * Defaults to a reasonable limit to prevent stack overflow.\n */\n maxDepth?: number;\n\n /**\n * Maximum number of chunks to generate.\n * If exceeded, falls back to text splitting.\n */\n maxChunks?: number;\n\n preserveFormatting?: boolean;\n\n /** Maximum size for individual chunks */\n maxChunkSize?: number;\n}\n\n/**\n * JsonDocumentSplitter handles splitting of JSON content.\n * It attempts to preserve the structure of the JSON object while splitting it into chunks.\n */\nexport class JsonDocumentSplitter implements DocumentSplitter {\n private preserveFormatting: boolean;\n private maxDepth: number;\n private maxChunks: number;\n private maxChunkSize: number;\n private textFallbackSplitter: TextDocumentSplitter;\n\n constructor(config: SplitterConfig, options: JsonDocumentSplitterOptions = {}) {\n this.preserveFormatting = options.preserveFormatting ?? true;\n this.maxDepth =\n options.maxDepth ??\n config.json?.maxNestingDepth ??\n options.maxDepth ??\n config.json?.maxNestingDepth ??\n defaults.splitter.json.maxNestingDepth;\n this.maxChunks =\n options.maxChunks ?? config.json?.maxChunks ?? defaults.splitter.json.maxChunks;\n this.maxChunkSize = options.maxChunkSize ?? config.maxChunkSize;\n\n const textSplitterConfig = { ...config };\n if (options.maxChunkSize) {\n textSplitterConfig.maxChunkSize = options.maxChunkSize;\n }\n this.textFallbackSplitter = new TextDocumentSplitter(textSplitterConfig);\n }\n\n async splitText(content: string, _contentType?: string): Promise<Chunk[]> {\n try {\n const parsed: JsonValue = JSON.parse(content);\n const chunks: Chunk[] = [];\n\n // Process the JSON structure recursively, starting with root path\n await this.processValue(parsed, [\"root\"], 1, 0, chunks, true);\n\n // Check if we exceeded the maximum number of chunks\n if (chunks.length > this.maxChunks) {\n // Fall back to text-based chunking\n return this.textFallbackSplitter.splitText(content);\n }\n\n return chunks;\n } catch {\n // If JSON parsing fails, create a single chunk with the raw content\n return [\n {\n types: [\"code\"],\n content: content.trim(),\n section: {\n level: 1,\n path: [\"invalid-json\"],\n },\n },\n ];\n }\n }\n\n private async processValue(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastItem: boolean,\n ): Promise<void> {\n // Check if we've exceeded the maximum depth\n if (level > this.maxDepth) {\n // Switch to simple text-based representation for deep nesting\n await this.processValueAsText(value, path, level, indentLevel, chunks, isLastItem);\n return;\n }\n\n if (Array.isArray(value)) {\n await this.processArray(value, path, level, indentLevel, chunks, isLastItem);\n } else if (value !== null && typeof value === \"object\") {\n await this.processObject(value, path, level, indentLevel, chunks, isLastItem);\n } else {\n await this.processPrimitive(value, path, level, indentLevel, chunks, isLastItem);\n }\n }\n\n private async processArray(\n array: JsonValue[],\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastItem: boolean,\n ): Promise<void> {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n\n // Opening bracket chunk\n chunks.push({\n types: [\"code\"],\n content: `${indent}[`,\n section: { level, path: [...path] },\n });\n\n // Process each array element\n for (let index = 0; index < array.length; index++) {\n const item = array[index];\n const isLast = index === array.length - 1;\n const itemPath = [...path, `[${index}]`];\n await this.processValue(item, itemPath, level + 1, indentLevel + 1, chunks, isLast);\n }\n\n // Closing bracket chunk\n chunks.push({\n types: [\"code\"],\n content: `${indent}]${comma}`,\n section: { level, path: [...path] },\n });\n }\n\n private async processObject(\n obj: Record<string, JsonValue>,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastItem: boolean,\n ): Promise<void> {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n const entries = Object.entries(obj);\n\n // Opening brace chunk\n chunks.push({\n types: [\"code\"],\n content: `${indent}{`,\n section: { level, path: [...path] },\n });\n\n // Process each property\n for (let index = 0; index < entries.length; index++) {\n const [key, value] = entries[index];\n const isLast = index === entries.length - 1;\n const propertyPath = [...path, key];\n await this.processProperty(\n key,\n value,\n propertyPath,\n level + 1,\n indentLevel + 1,\n chunks,\n isLast,\n );\n }\n\n // Closing brace chunk\n chunks.push({\n types: [\"code\"],\n content: `${indent}}${comma}`,\n section: { level, path: [...path] },\n });\n }\n\n private async processProperty(\n key: string,\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastProperty: boolean,\n ): Promise<void> {\n const indent = this.getIndent(indentLevel);\n\n if (typeof value === \"object\" && value !== null) {\n // For complex values (objects/arrays), create a property opening chunk\n chunks.push({\n types: [\"code\"],\n content: `${indent}\"${key}\": `,\n section: { level, path },\n });\n\n // Process the complex value (it handles its own comma)\n await this.processValue(value, path, level, indentLevel, chunks, isLastProperty);\n } else {\n // For primitive values, create a complete property chunk and ensure it respects max chunk size\n const comma = isLastProperty ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n const fullContent = `${indent}\"${key}\": ${formattedValue}${comma}`;\n\n if (fullContent.length > this.maxChunkSize) {\n // Use text splitter for oversized primitive values while keeping property context\n const textChunks = await this.textFallbackSplitter.splitText(formattedValue);\n\n // Emit property prefix once, then split value across chunks\n chunks.push({\n types: [\"code\"],\n content: `${indent}\"${key}\": `,\n section: { level, path },\n });\n\n textChunks.forEach((textChunk, index) => {\n const isLastChunk = index === textChunks.length - 1;\n const content = `${textChunk.content}${isLastChunk ? comma : \"\"}`;\n chunks.push({\n types: [\"code\"],\n content,\n section: { level, path },\n });\n });\n } else {\n chunks.push({\n types: [\"code\"],\n content: fullContent,\n section: { level, path },\n });\n }\n }\n }\n\n private async processPrimitive(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastItem: boolean,\n ): Promise<void> {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n\n const fullContent = `${indent}${formattedValue}${comma}`;\n\n if (fullContent.length > this.maxChunkSize) {\n // Use text splitter for oversized primitive values in arrays\n const textChunks = await this.textFallbackSplitter.splitText(formattedValue);\n\n textChunks.forEach((textChunk, index) => {\n const isFirstChunk = index === 0;\n const isLastChunk = index === textChunks.length - 1;\n const valueContent = isFirstChunk\n ? `${indent}${textChunk.content}`\n : textChunk.content;\n const content = `${valueContent}${isLastChunk ? comma : \"\"}`;\n chunks.push({\n types: [\"code\"],\n content,\n section: { level, path: [...path] },\n });\n });\n } else {\n chunks.push({\n types: [\"code\"],\n content: fullContent,\n section: { level, path },\n });\n }\n }\n\n private getIndent(level: number): string {\n return this.preserveFormatting ? \" \".repeat(level) : \"\";\n }\n\n /**\n * Process a value that has exceeded the maximum depth limit by serializing it as text.\n * This prevents excessive chunking of deeply nested structures.\n * If the serialized value is too large, splits it using the text fallback splitter.\n */\n private async processValueAsText(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: Chunk[],\n isLastItem: boolean,\n ): Promise<void> {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n\n // Serialize the entire value\n let serialized: string;\n if (this.preserveFormatting) {\n // Use a more efficient approach for indented serialization\n const lines = JSON.stringify(value, null, 2).split(\"\\n\");\n serialized = lines\n .map((line, idx) => (idx === 0 ? line : `${indent}${line}`))\n .join(\"\\n\");\n } else {\n serialized = JSON.stringify(value);\n }\n\n const fullContent = `${indent}${serialized}${comma}`;\n\n // Check if the FINAL formatted content (with indent and comma) exceeds the limit.\n // If so, we split just the serialized content (without structural formatting) because\n // the resulting chunks are treated as searchable text blocks, not structural JSON elements.\n if (fullContent.length > this.maxChunkSize) {\n // Use text splitter to break down the large serialized JSON\n // Note: When content is this large, we prioritize searchability over perfect JSON structure.\n // The chunks contain the actual data that users can search, with proper metadata (level, path)\n // to indicate where in the JSON structure this content originated from.\n const textChunks = await this.textFallbackSplitter.splitText(serialized);\n\n // Add each text chunk with the current path information\n for (const textChunk of textChunks) {\n chunks.push({\n types: [\"code\"],\n content: textChunk.content,\n section: { level, path: [...path] },\n });\n }\n } else {\n // Content is small enough, add as single chunk\n chunks.push({\n types: [\"code\"],\n content: fullContent,\n section: { level, path: [...path] },\n });\n }\n }\n}\n","import { JsonDocumentSplitter } from \"../../splitter/JsonDocumentSplitter\";\nimport type { DocumentSplitter } from \"../../splitter/types\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { convertToString } from \"../utils/buffer\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\n/**\n * Pipeline for processing JSON content with semantic, hierarchical splitting.\n * Uses JsonDocumentSplitter to produce structurally faithful chunks (preserving {level, path})\n * without greedy size-based merging. Greedy merging is intentionally omitted to avoid collapsing\n * distinct structural nodes that are required for precise hierarchical reassembly.\n */\nexport class JsonPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: DocumentSplitter;\n\n constructor(config: AppConfig) {\n super();\n this.middleware = [];\n // Structure-preserving splitter only (no greedy size merging)\n this.splitter = new JsonDocumentSplitter(config.splitter, {\n preserveFormatting: true,\n });\n }\n\n canProcess(mimeType: string): boolean {\n if (!mimeType) return false;\n return MimeTypeUtils.isJson(mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n // Validate JSON structure\n let parsedJson: unknown;\n let isValidJson = true;\n try {\n parsedJson = JSON.parse(contentString);\n } catch (_error) {\n isValidJson = false;\n }\n\n // For invalid JSON, return as-is for fallback text processing\n if (!isValidJson) {\n // Still split invalid JSON content for consistency\n const fallbackChunks = await this.splitter.splitText(contentString);\n return {\n textContent: contentString,\n // metadata: {\n // isValidJson: false,\n // },\n links: [],\n errors: [],\n chunks: fallbackChunks,\n };\n }\n\n const metadata = this.extractMetadata(parsedJson);\n const context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\n title: metadata.title,\n contentType: rawContent.mimeType || \"application/json\",\n // metadata: {\n // ...this.extractMetadata(parsedJson),\n // isValidJson,\n // jsonStructure: this.analyzeJsonStructure(parsedJson),\n // },\n links: [], // JSON files typically don't contain links\n errors: [],\n options,\n fetcher,\n };\n\n // Execute the middleware stack (minimal for JSON)\n await this.executeMiddlewareStack(this.middleware, context);\n\n // Split the content using JsonContentSplitter\n const chunks = await this.splitter.splitText(context.content);\n\n return {\n title: context.title,\n contentType: context.contentType,\n textContent: context.content,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n\n /**\n * Extracts metadata from JSON content only when meaningful values exist\n */\n private extractMetadata(parsedJson: unknown): { title?: string; description?: string } {\n const metadata: { title?: string; description?: string } = {};\n\n if (typeof parsedJson === \"object\" && parsedJson !== null) {\n const obj = parsedJson as Record<string, unknown>;\n\n // Look for common title fields - only use if they exist and are strings\n const titleFields = [\"title\", \"name\", \"displayName\", \"label\"];\n for (const field of titleFields) {\n if (field in obj && typeof obj[field] === \"string\" && obj[field]) {\n metadata.title = obj[field] as string;\n break;\n }\n }\n\n // Look for common description fields - only use if they exist and are strings\n const descFields = [\"description\", \"summary\", \"about\", \"info\"];\n for (const field of descFields) {\n if (field in obj && typeof obj[field] === \"string\" && obj[field]) {\n metadata.description = obj[field] as string;\n break;\n }\n }\n }\n\n return metadata;\n }\n\n /**\n * Calculates the maximum nesting depth of a JSON structure\n */\n private calculateDepth(obj: unknown, currentDepth = 1): number {\n if (Array.isArray(obj)) {\n let maxDepth = currentDepth;\n for (const item of obj) {\n if (typeof item === \"object\" && item !== null) {\n maxDepth = Math.max(maxDepth, this.calculateDepth(item, currentDepth + 1));\n }\n }\n return maxDepth;\n } else if (typeof obj === \"object\" && obj !== null) {\n let maxDepth = currentDepth;\n for (const value of Object.values(obj)) {\n if (typeof value === \"object\" && value !== null) {\n maxDepth = Math.max(maxDepth, this.calculateDepth(value, currentDepth + 1));\n }\n }\n return maxDepth;\n }\n\n return currentDepth;\n }\n}\n","import type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Placeholder middleware for extracting links from Markdown content.\n * Currently, it does not implement link extraction, matching the\n * original MarkdownProcessor's TODO status.\n */\nexport class MarkdownLinkExtractorMiddleware implements ContentProcessorMiddleware {\n /**\n * Processes the context. Currently a no-op regarding link extraction.\n * @param context The current processing context.\n * @param next Function to call the next middleware.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n // TODO: Implement Markdown link extraction (e.g., using regex or a Markdown parser)\n // For now, ensure context.links exists, defaulting to empty array if not set.\n if (!Array.isArray(context.links)) {\n context.links = [];\n }\n // No links are added here yet.\n\n await next();\n }\n}\n","import matter from \"gray-matter\";\nimport { logger } from \"../../utils/logger\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to extract the title from Markdown content.\n * Prioritizes YAML frontmatter 'title' field, falls back to first H1 heading.\n */\nexport class MarkdownMetadataExtractorMiddleware implements ContentProcessorMiddleware {\n /**\n * Processes the context to extract the title from Markdown.\n * @param context The current processing context.\n * @param next Function to call the next middleware.\n */\n async process(context: MiddlewareContext, next: () => Promise<void>): Promise<void> {\n try {\n let title = \"Untitled\";\n let frontmatterTitle: string | undefined;\n\n // 1. Try to extract title from YAML frontmatter\n try {\n const file = matter(context.content);\n if (file.data && file.data.title !== undefined && file.data.title !== null) {\n // Convert to string to handle numeric titles (e.g. title: 2024)\n frontmatterTitle = String(file.data.title).trim();\n }\n } catch (err) {\n // Log warning but continue - don't crash the pipeline for bad frontmatter\n logger.warn(\n `Failed to parse markdown frontmatter: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (frontmatterTitle && frontmatterTitle.length > 0) {\n title = frontmatterTitle;\n } else {\n // 2. Fallback: Extract first H1 heading\n const match = context.content.match(/^#\\s+(.*)$/m);\n if (match?.[1]) {\n title = match[1].trim();\n }\n }\n\n context.title = title;\n } catch (error) {\n context.errors.push(\n new Error(\n `Failed to extract metadata from Markdown: ${error instanceof Error ? error.message : String(error)}`,\n ),\n );\n }\n\n await next();\n }\n}\n","import { GreedySplitter } from \"../../splitter/GreedySplitter\";\nimport { SemanticMarkdownSplitter } from \"../../splitter/SemanticMarkdownSplitter\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport { MarkdownLinkExtractorMiddleware } from \"../middleware/MarkdownLinkExtractorMiddleware\";\nimport { MarkdownMetadataExtractorMiddleware } from \"../middleware/MarkdownMetadataExtractorMiddleware\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { convertToString } from \"../utils/buffer\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\n/**\n * MarkdownPipeline - Processes Markdown content using middleware and semantic splitting.\n */\nexport class MarkdownPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(config: AppConfig) {\n super();\n\n const preferredChunkSize = config.splitter.preferredChunkSize;\n const maxChunkSize = config.splitter.maxChunkSize;\n const minChunkSize = config.splitter.minChunkSize;\n\n this.middleware = [\n new MarkdownMetadataExtractorMiddleware(),\n new MarkdownLinkExtractorMiddleware(),\n ];\n\n // Create the two-phase splitting: semantic + size optimization\n const semanticSplitter = new SemanticMarkdownSplitter(\n preferredChunkSize,\n maxChunkSize,\n );\n this.greedySplitter = new GreedySplitter(\n semanticSplitter,\n minChunkSize,\n preferredChunkSize,\n maxChunkSize,\n );\n }\n\n canProcess(mimeType: string): boolean {\n if (!mimeType) return false;\n return MimeTypeUtils.isMarkdown(mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n contentType: rawContent.mimeType || \"text/markdown\",\n content: contentString,\n source: rawContent.source,\n links: [],\n errors: [],\n options,\n fetcher,\n };\n\n // Execute the middleware stack using the base class method\n await this.executeMiddlewareStack(this.middleware, context);\n\n // Split the content using SemanticMarkdownSplitter\n const chunks = await this.greedySplitter.splitText(\n typeof context.content === \"string\" ? context.content : \"\",\n rawContent.mimeType,\n );\n\n return {\n title: context.title,\n contentType: context.contentType,\n textContent: typeof context.content === \"string\" ? context.content : \"\",\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n}\n","/**\n * Type definitions for tree-sitter based parsers\n */\n\nimport type { SyntaxNode, Tree } from \"tree-sitter\";\n\n/**\n * Universal tree-sitter parser size limit.\n * Set to 30,000 characters to be safely under the observed 32,767 limit.\n */\n\nexport enum StructuralNodeType {\n // Function declarations\n FUNCTION_DECLARATION = \"function_declaration\",\n ARROW_FUNCTION = \"arrow_function\",\n METHOD_DEFINITION = \"method_definition\",\n CONSTRUCTOR = \"constructor\",\n\n // Class and object structures\n CLASS_DECLARATION = \"class_declaration\",\n OBJECT_EXPRESSION = \"object_expression\",\n\n // TypeScript specific\n INTERFACE_DECLARATION = \"interface_declaration\",\n TYPE_ALIAS_DECLARATION = \"type_alias_declaration\",\n NAMESPACE_DECLARATION = \"namespace_declaration\",\n ENUM_DECLARATION = \"enum_declaration\",\n\n // JSX specific\n JSX_ELEMENT = \"jsx_element\",\n JSX_FRAGMENT = \"jsx_fragment\",\n JSX_EXPRESSION = \"jsx_expression\",\n\n // Module level\n VARIABLE_DECLARATION = \"variable_declaration\", // for const/let functions\n EXPORT_STATEMENT = \"export_statement\",\n IMPORT_STATEMENT = \"import_statement\",\n\n // Control structures (for content sections)\n IF_STATEMENT = \"if_statement\",\n FOR_STATEMENT = \"for_statement\",\n WHILE_STATEMENT = \"while_statement\",\n SWITCH_STATEMENT = \"switch_statement\",\n}\n\nexport interface ParseResult {\n tree: Tree;\n hasErrors: boolean;\n errorNodes: SyntaxNode[];\n}\n\nexport interface StructuralNode {\n type: StructuralNodeType;\n name: string;\n startLine: number;\n endLine: number;\n startByte: number;\n endByte: number;\n children: StructuralNode[];\n text: string;\n indentLevel: number;\n modifiers: string[];\n documentation?: string[]; // JSDoc/TSDoc and preceding comments\n}\n\nexport interface LineRange {\n startLine: number;\n endLine: number;\n}\n\n/**\n * Simplified boundary interface for focused chunk generation\n * This is what we actually need for semantic splitting\n */\nexport interface CodeBoundary {\n /** Simple boundary type for context */\n type: \"function\" | \"class\" | \"interface\" | \"enum\" | \"module\" | \"other\";\n /** Classification for downstream chunk typing */\n boundaryType: \"structural\" | \"content\";\n /** Optional simple name for debugging/context */\n name?: string;\n /** Start position in the source (1-indexed line) */\n startLine: number;\n /** End position in the source (1-indexed line) */\n endLine: number;\n /** Start byte offset in the source */\n startByte: number;\n /** End byte offset in the source */\n endByte: number;\n /** Parent boundary for building hierarchical paths */\n parent?: CodeBoundary;\n /** Hierarchical path built from parent chain */\n path?: string[];\n /** Level in the hierarchy (calculated from path length) */\n level?: number;\n}\n\n/**\n * Result of parsing source code for boundaries\n */\nexport interface BoundaryParseResult {\n /** List of code boundaries found */\n boundaries: CodeBoundary[];\n /** Whether parsing had errors (but may still have partial results) */\n hasErrors: boolean;\n}\n\nexport interface LanguageParser {\n readonly name: string;\n readonly fileExtensions: string[];\n readonly mimeTypes: string[];\n\n parse(source: string): ParseResult;\n extractStructuralNodes(tree: Tree, source?: string): StructuralNode[];\n getNodeText(node: SyntaxNode, source: string): string;\n getNodeLines(node: SyntaxNode, source: string): LineRange;\n\n /** NEW: Simplified boundary extraction for focused chunking */\n extractBoundaries(tree: Tree, source: string): CodeBoundary[];\n}\n","/**\n * Python parser for tree-sitter based source code splitting.\n *\n * Goals:\n * - Semantic parsing of Python source code (.py files)\n * - Direct boundary extraction aligned with canonical ruleset\n * - Handle Python-specific docstrings (inside function/class bodies)\n * - Support for functions, classes, methods, and nested structures\n */\n\nimport Parser, { type SyntaxNode, type Tree } from \"tree-sitter\";\nimport Python from \"tree-sitter-python\";\nimport { defaults } from \"../../../utils/config\";\nimport type { CodeBoundary, LanguageParser, ParseResult, StructuralNode } from \"./types\";\nimport { StructuralNodeType } from \"./types\";\n\n/**\n * Sets of node types we care about for Python.\n */\nconst STRUCTURAL_DECL_TYPES = new Set([\n \"class_definition\",\n \"import_statement\",\n \"import_from_statement\",\n]);\n\n// Executable / content declarations we also emit\nconst CONTENT_DECL_TYPES = new Set([\"function_definition\", \"async_function_definition\"]);\n\n/**\n * Decide if node type is boundary-worthy (before suppression rules).\n */\nfunction isCandidateBoundary(node: SyntaxNode): boolean {\n return STRUCTURAL_DECL_TYPES.has(node.type) || CONTENT_DECL_TYPES.has(node.type);\n}\n\n/**\n * Determine if a function-like node is a local helper (nested inside another function body),\n * in which case we suppress emission (canonical ruleset).\n */\nfunction isLocalHelper(node: SyntaxNode): boolean {\n const functionLike = new Set([\"function_definition\", \"async_function_definition\"]);\n\n let ancestor = node.parent;\n while (ancestor) {\n if (functionLike.has(ancestor.type)) {\n // Current node is nested inside a function body -> local helper\n return true;\n }\n // Stop climbing at structural containers where function declarations are allowed as direct members\n if (ancestor.type === \"class_definition\" || ancestor.type === \"module\") {\n break;\n }\n ancestor = ancestor.parent;\n }\n return false;\n}\n\n/**\n * Extract Python docstring from function or class body.\n * Python docstrings are string literals as the first statement in the body.\n */\nfunction findDocumentationStart(\n node: SyntaxNode,\n source: string,\n): { startLine: number; startByte: number } {\n let startByte = node.startIndex;\n let startLine = node.startPosition.row + 1;\n\n // For Python, look for docstring inside the function/class body\n if (\n node.type === \"function_definition\" ||\n node.type === \"async_function_definition\" ||\n node.type === \"class_definition\"\n ) {\n // Find the body (block) of the function/class\n const body = node.childForFieldName(\"body\");\n if (body && body.type === \"block\") {\n const firstChild = body.children.find((child) => child.type !== \"newline\");\n\n // Check if first statement is a string (docstring)\n if (firstChild && firstChild.type === \"expression_statement\") {\n const expr = firstChild.childForFieldName(\"value\") || firstChild.children[0];\n if (expr && expr.type === \"string\") {\n // We keep the function/class signature as the start, but we'll include\n // the docstring in the content by not adjusting the boundaries here.\n // The docstring will be naturally included in the function body.\n }\n }\n }\n }\n\n // Look for preceding comments (Python uses # comments, not JSDoc)\n const parent = node.parent;\n if (!parent) {\n return { startLine, startByte };\n }\n\n const siblings = parent.children;\n const idx = siblings.indexOf(node);\n if (idx === -1) {\n return { startLine, startByte };\n }\n\n // Walk upward collecting contiguous comment block\n let sawComment = false;\n for (let i = idx - 1; i >= 0; i--) {\n const s = siblings[i];\n const text = source.slice(s.startIndex, s.endIndex);\n\n if (s.type === \"comment\") {\n sawComment = true;\n startByte = s.startIndex;\n startLine = s.startPosition.row + 1;\n continue;\n }\n\n if (/^\\s*$/.test(text)) {\n if (sawComment) {\n startByte = s.startIndex;\n startLine = s.startPosition.row + 1;\n }\n continue;\n }\n\n // Hit non-comment code: stop\n break;\n }\n\n return { startLine, startByte };\n}\n\n/**\n * Name extraction for Python nodes.\n */\nfunction extractName(node: SyntaxNode): string {\n switch (node.type) {\n case \"function_definition\":\n case \"async_function_definition\":\n case \"class_definition\": {\n const nameNode = node.childForFieldName(\"name\");\n return nameNode?.text || `<anonymous_${node.type}>`;\n }\n case \"import_statement\": {\n // Extract imported module names\n const names: string[] = [];\n const dotted_names = node.children.filter(\n (c) => c.type === \"dotted_name\" || c.type === \"identifier\",\n );\n for (const name of dotted_names) {\n names.push(name.text);\n }\n return names.length > 0 ? `import ${names.join(\", \")}` : \"import\";\n }\n case \"import_from_statement\": {\n const moduleNode = node.childForFieldName(\"module_name\");\n const moduleName = moduleNode?.text || \"?\";\n return `from ${moduleName}`;\n }\n default:\n return node.type;\n }\n}\n\n/**\n * Boundary classification mapping for Python.\n */\nfunction classifyBoundaryKind(node: SyntaxNode): {\n boundaryType: \"structural\" | \"content\";\n simple: CodeBoundary[\"type\"];\n} {\n if (node.type === \"class_definition\") {\n return { boundaryType: \"structural\", simple: \"class\" };\n }\n if (node.type === \"import_statement\" || node.type === \"import_from_statement\") {\n return { boundaryType: \"structural\", simple: \"module\" };\n }\n if (node.type === \"function_definition\" || node.type === \"async_function_definition\") {\n return { boundaryType: \"content\", simple: \"function\" };\n }\n return { boundaryType: \"content\", simple: \"other\" };\n}\n\nexport class PythonParser implements LanguageParser {\n readonly name = \"python\";\n readonly fileExtensions = [\".py\", \".pyi\", \".pyw\"];\n readonly mimeTypes = [\n \"text/python\",\n \"text/x-python\",\n \"application/python\",\n \"application/x-python\",\n ];\n\n constructor(\n private readonly treeSitterSizeLimit: number = defaults.splitter.treeSitterSizeLimit,\n ) {}\n\n private createParser(): Parser {\n const parser = new Parser();\n parser.setLanguage(Python as unknown);\n return parser;\n }\n\n parse(source: string): ParseResult {\n // Validate input\n if (typeof source !== \"string\") {\n throw new Error(`PythonParser expected string input, got ${typeof source}`);\n }\n\n if (source == null) {\n throw new Error(\"PythonParser received null or undefined source\");\n }\n\n // Handle tree-sitter size limit\n const limit = this.treeSitterSizeLimit;\n if (source.length > limit) {\n // For files exceeding the limit, we truncate at a reasonable boundary and return a limited parse\n // Try to find a good truncation point (end of line)\n let truncatedSource = source.slice(0, limit);\n const lastNewline = truncatedSource.lastIndexOf(\"\\n\");\n if (lastNewline > limit * 0.9) {\n // If we can find a newline in the last 10% of the limit, use that\n truncatedSource = source.slice(0, lastNewline + 1);\n }\n\n try {\n const parser = this.createParser();\n const tree = parser.parse(truncatedSource);\n const errorNodes: SyntaxNode[] = [];\n this.collectErrorNodes(tree.rootNode, errorNodes);\n\n return {\n tree,\n hasErrors: true, // Mark as having errors due to truncation\n errorNodes,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse truncated Python file (${truncatedSource.length} chars): ${(error as Error).message}`,\n );\n }\n }\n\n // Normal parsing for files within the size limit\n try {\n const parser = this.createParser();\n const tree = parser.parse(source);\n const errorNodes: SyntaxNode[] = [];\n this.collectErrorNodes(tree.rootNode, errorNodes);\n\n return {\n tree,\n hasErrors: errorNodes.length > 0,\n errorNodes,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse Python file (${source.length} chars): ${(error as Error).message}`,\n );\n }\n }\n\n private collectErrorNodes(node: SyntaxNode, acc: SyntaxNode[]): void {\n if (node.hasError && node.type === \"ERROR\") {\n acc.push(node);\n }\n for (const c of node.children) {\n this.collectErrorNodes(c, acc);\n }\n }\n\n getNodeText(node: SyntaxNode, source: string): string {\n return source.slice(node.startIndex, node.endIndex);\n }\n\n getNodeLines(node: SyntaxNode, _source: string) {\n return {\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n };\n }\n\n /**\n * Legacy structural node extraction (used by existing tests).\n * Produces a flat list (no parent/child linking beyond simple push).\n */\n extractStructuralNodes(tree: Tree, source?: string): StructuralNode[] {\n const src = source ?? tree.rootNode.text;\n const out: StructuralNode[] = [];\n const structuralTypes = new Set<string>([\n ...STRUCTURAL_DECL_TYPES,\n ...CONTENT_DECL_TYPES,\n ]);\n\n const visit = (node: SyntaxNode): void => {\n if (structuralTypes.has(node.type)) {\n if (this.shouldSkipStructuralNode(node)) {\n for (const child of node.children) visit(child);\n return;\n }\n\n const name = extractName(node);\n const { startLine, startByte } = findDocumentationStart(node, src);\n const endLine = node.endPosition.row + 1;\n const structuralNode: StructuralNode = {\n type: this.classifyStructuralNode(node),\n name,\n startLine,\n endLine,\n startByte,\n endByte: node.endIndex,\n children: [],\n text: this.getNodeText(node, src),\n indentLevel: 0,\n modifiers: [],\n documentation: undefined,\n };\n out.push(structuralNode);\n // Continue into children (we keep nested declarations; suppression handled separately)\n for (const child of node.children) visit(child);\n return;\n }\n for (const child of node.children) visit(child);\n };\n\n visit(tree.rootNode);\n return this.deduplicate(out);\n }\n\n /**\n * Boundary extraction: produces CodeBoundary[] directly from AST.\n */\n extractBoundaries(tree: Tree, source: string): CodeBoundary[] {\n if (!source.trim()) return [];\n const boundaries: CodeBoundary[] = [];\n\n const walk = (node: SyntaxNode): void => {\n if (isCandidateBoundary(node)) {\n if (this.shouldSkipStructuralNode(node)) {\n for (const c of node.children) walk(c);\n return;\n }\n\n // Local helper suppression\n if (\n (node.type === \"function_definition\" ||\n node.type === \"async_function_definition\") &&\n isLocalHelper(node)\n ) {\n // Do not emit boundary for local helper\n for (const c of node.children) walk(c);\n return;\n }\n\n const name = extractName(node);\n const docInfo = findDocumentationStart(node, source);\n const classification = classifyBoundaryKind(node);\n\n boundaries.push({\n type: classification.simple,\n boundaryType: classification.boundaryType,\n name,\n startLine: docInfo.startLine,\n endLine: node.endPosition.row + 1,\n startByte: docInfo.startByte,\n endByte: node.endIndex,\n });\n\n // Traverse children (we allow nested boundaries where rules permit)\n for (const c of node.children) walk(c);\n return;\n }\n\n for (const c of node.children) walk(c);\n };\n\n walk(tree.rootNode);\n\n // Deduplicate by start/end/name triple\n const seen = new Set<string>();\n return boundaries.filter((b) => {\n const key = `${b.startByte}:${b.endByte}:${b.name}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n }\n\n /**\n * Determine if a structural node should be skipped in favor of children.\n */\n private shouldSkipStructuralNode(_node: SyntaxNode): boolean {\n // Currently no wrapper types to skip in Python\n return false;\n }\n\n private classifyStructuralNode(node: SyntaxNode): StructuralNodeType {\n switch (node.type) {\n case \"function_definition\":\n case \"async_function_definition\":\n return StructuralNodeType.FUNCTION_DECLARATION;\n case \"class_definition\":\n return StructuralNodeType.CLASS_DECLARATION;\n case \"import_statement\":\n case \"import_from_statement\":\n return StructuralNodeType.IMPORT_STATEMENT;\n default:\n return StructuralNodeType.VARIABLE_DECLARATION;\n }\n }\n\n private deduplicate(nodes: StructuralNode[]): StructuralNode[] {\n const seen = new Set<string>();\n const out: StructuralNode[] = [];\n for (const n of nodes) {\n const key = `${n.startByte}:${n.endByte}:${n.type}:${n.name}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(n);\n }\n out.sort((a, b) => a.startByte - b.startByte);\n return out;\n }\n}\n","/**\n * Simplified standalone TypeScript (and JavaScript) parser.\n *\n * Goals:\n * - Single, unified parser for .ts/.tsx/.js/.jsx (+ mjs/cjs/mts/cts)\n * - Direct boundary extraction aligned with canonical ruleset\n * - Minimal, predictable logic (no complex multi-phase traversals)\n * - Retain legacy extractStructuralNodes() for existing tests\n *\n * This replaces the previous inheritance-heavy BaseLanguageParser design.\n */\n\nimport Parser, { type SyntaxNode, type Tree } from \"tree-sitter\";\nimport TypeScript from \"tree-sitter-typescript\";\nimport { defaults } from \"../../../utils/config\";\nimport type { CodeBoundary, LanguageParser, ParseResult, StructuralNode } from \"./types\";\nimport { StructuralNodeType } from \"./types\";\n\n/**\n * Helper: language selection (TS vs TSX).\n */\nfunction detectTSX(source: string): boolean {\n // Heuristic: JSX-like tags or React usage\n return /<[A-Za-z][A-Za-z0-9]*\\s|<\\/[A-Za-z]|React\\./.test(source);\n}\n\n/**\n * Sets of node types we care about.\n */\nconst STRUCTURAL_DECL_TYPES = new Set([\n \"class_declaration\",\n \"abstract_class_declaration\",\n \"interface_declaration\",\n \"type_alias_declaration\",\n \"enum_declaration\",\n \"module_declaration\",\n \"namespace_declaration\",\n \"internal_module\",\n \"import_statement\",\n \"export_statement\",\n]);\n\n// Executable / content declarations we also emit\nconst CONTENT_DECL_TYPES = new Set([\n \"function_declaration\",\n \"method_definition\",\n \"method_signature\",\n \"abstract_method_signature\",\n \"constructor\",\n \"arrow_function\",\n // Only emit arrow-function variables via their variable_declarator to avoid duplicates\n \"variable_declarator\",\n]);\n\n/**\n * Tokens considered modifiers (merged into name for static methods if needed).\n */\nconst MODIFIER_TOKENS = new Set([\n \"export\",\n \"default\",\n \"public\",\n \"private\",\n \"protected\",\n \"readonly\",\n \"abstract\",\n \"async\",\n \"static\",\n]);\n\n/**\n * Decide if node type is boundary-worthy (before suppression rules).\n */\nfunction isCandidateBoundary(node: SyntaxNode): boolean {\n return STRUCTURAL_DECL_TYPES.has(node.type) || CONTENT_DECL_TYPES.has(node.type);\n}\n\n/**\n * Determine if a function-like node is a local helper (nested inside another function/method body),\n * in which case we suppress emission (canonical ruleset).\n */\nfunction isLocalHelper(node: SyntaxNode): boolean {\n const functionLike = new Set([\n \"function_declaration\",\n \"arrow_function\",\n \"method_definition\",\n \"method_signature\",\n \"abstract_method_signature\",\n \"constructor\",\n ]);\n\n let ancestor = node.parent;\n while (ancestor) {\n if (functionLike.has(ancestor.type)) {\n // If ancestor is a class method/constructor and current node is method/constructor,\n // we DO allow emission (method inside class) — only suppress deeper nested function-like inside method bodies.\n if (\n ancestor.type === \"method_definition\" ||\n ancestor.type === \"constructor\" ||\n ancestor.type === \"function_declaration\" ||\n ancestor.type === \"arrow_function\"\n ) {\n // Current node is nested inside a function/method body -> local helper\n return true;\n }\n }\n // Stop climbing at structural containers where function declarations are allowed as direct members\n if (\n ancestor.type === \"class_declaration\" ||\n ancestor.type === \"abstract_class_declaration\" ||\n ancestor.type === \"namespace_declaration\" ||\n ancestor.type === \"module_declaration\" ||\n ancestor.type === \"internal_module\" ||\n ancestor.type === \"interface_declaration\" ||\n ancestor.type === \"enum_declaration\"\n ) {\n break;\n }\n ancestor = ancestor.parent;\n }\n return false;\n}\n\n/**\n * Extract contiguous documentation (comments + blank lines) preceding a node,\n * crossing transparent export wrappers.\n */\nfunction findDocumentationStart(\n node: SyntaxNode,\n source: string,\n): { startLine: number; startByte: number } {\n // If wrapped in export_statement, shift focus to wrapper for doc scan\n let target: SyntaxNode = node;\n if (node.parent && node.parent.type === \"export_statement\") {\n target = node.parent;\n }\n\n // Walk backwards among siblings collecting comments/whitespace.\n const parent = target.parent;\n if (!parent) {\n return {\n startLine: target.startPosition.row + 1,\n startByte: target.startIndex,\n };\n }\n\n const siblings = parent.children;\n const idx = siblings.indexOf(target);\n if (idx === -1) {\n return {\n startLine: target.startPosition.row + 1,\n startByte: target.startIndex,\n };\n }\n\n let startByte = target.startIndex;\n let startLine = target.startPosition.row + 1;\n\n // Walk upward collecting contiguous doc comment block.\n // IMPORTANT: Only expand over whitespace that appears AFTER we've seen at least one comment.\n // This prevents pulling in a blank line above the doc block (tests expect the comment line).\n let sawComment = false;\n for (let i = idx - 1; i >= 0; i--) {\n const s = siblings[i];\n const text = source.slice(s.startIndex, s.endIndex);\n\n if (s.type === \"comment\") {\n sawComment = true;\n startByte = s.startIndex;\n startLine = s.startPosition.row + 1;\n continue;\n }\n\n if (/^\\s*$/.test(text)) {\n if (sawComment) {\n startByte = s.startIndex;\n startLine = s.startPosition.row + 1;\n }\n // If we haven't yet seen a comment, ignore leading blank lines (do not shift start)\n continue;\n }\n // Hit non-comment code: stop.\n break;\n }\n\n // Inline doc on same line as declaration (/** ... */ declaration ...)\n // If we didn't already capture a preceding comment node but the same line\n // contains a JSDoc opening before the declaration start, shift start to that line start.\n const lineStartIdx = source.lastIndexOf(\"\\n\", target.startIndex - 1) + 1;\n if (lineStartIdx >= 0) {\n const prefix = source.slice(lineStartIdx, target.startIndex);\n if (prefix.includes(\"/**\")) {\n startLine = target.startPosition.row + 1;\n startByte = lineStartIdx;\n }\n }\n\n return { startLine, startByte };\n}\n\n/**\n * Name extraction similar to previous implementation but reduced.\n */\nfunction extractName(node: SyntaxNode): string {\n switch (node.type) {\n case \"function_declaration\":\n case \"class_declaration\":\n case \"abstract_class_declaration\":\n case \"interface_declaration\":\n case \"type_alias_declaration\":\n case \"enum_declaration\":\n case \"namespace_declaration\":\n case \"module_declaration\":\n case \"internal_module\": {\n const nameNode = node.childForFieldName(\"name\");\n return nameNode?.text || `<anonymous_${node.type}>`;\n }\n case \"method_definition\":\n case \"method_signature\":\n case \"abstract_method_signature\": {\n const nameNode = node.childForFieldName(\"name\");\n const isStatic = node.children.some((c) => c.type === \"static\");\n const base = nameNode?.text || \"method\";\n return isStatic ? `static ${base}` : base;\n }\n case \"constructor\":\n return \"constructor\";\n case \"arrow_function\": {\n let parent = node.parent;\n while (parent) {\n if (parent.type === \"variable_declarator\") {\n const nameNode = parent.childForFieldName(\"name\");\n return nameNode?.text || \"<anonymous_arrow>\";\n }\n parent = parent.parent;\n }\n return \"<anonymous_arrow>\";\n }\n case \"variable_declaration\":\n case \"lexical_declaration\": {\n // If it declares arrow functions, collect their names\n const declarators = node.children.filter((c) => c.type === \"variable_declarator\");\n const names: string[] = [];\n for (const d of declarators) {\n const value = d.childForFieldName(\"value\");\n if (value?.type === \"arrow_function\") {\n const nameNode = d.childForFieldName(\"name\");\n if (nameNode) names.push(nameNode.text);\n }\n }\n if (names.length > 0) return names.join(\", \");\n return \"<variable_declaration>\";\n }\n case \"variable_declarator\": {\n const nameNode = node.childForFieldName(\"name\");\n return nameNode?.text || \"<variable>\";\n }\n case \"import_statement\":\n case \"export_statement\":\n return node.text.split(\"\\n\")[0].trim();\n default:\n return node.type;\n }\n}\n\n/**\n * Modifier extraction (scan children + backward tokens).\n */\nfunction extractModifiers(node: SyntaxNode): string[] {\n const mods = new Set<string>();\n for (const c of node.children) {\n if (MODIFIER_TOKENS.has(c.type)) mods.add(c.type);\n }\n // Look at preceding siblings for leading modifiers (export/default)\n let prev = node.previousSibling;\n while (prev) {\n if (prev.type === \"comment\") {\n prev = prev.previousSibling;\n continue;\n }\n if (/^\\s*$/.test(prev.text)) {\n prev = prev.previousSibling;\n continue;\n }\n if (MODIFIER_TOKENS.has(prev.type)) {\n mods.add(prev.type);\n prev = prev.previousSibling;\n continue;\n }\n break;\n }\n return Array.from(mods);\n}\n\n/**\n * Boundary classification mapping.\n */\nfunction classifyBoundaryKind(node: SyntaxNode): {\n boundaryType: \"structural\" | \"content\";\n simple: CodeBoundary[\"type\"];\n} {\n if (node.type === \"class_declaration\" || node.type === \"abstract_class_declaration\") {\n return { boundaryType: \"structural\", simple: \"class\" };\n }\n if (node.type === \"interface_declaration\" || node.type === \"type_alias_declaration\") {\n return { boundaryType: \"structural\", simple: \"interface\" };\n }\n if (node.type === \"enum_declaration\") {\n return { boundaryType: \"structural\", simple: \"enum\" };\n }\n if (\n node.type === \"namespace_declaration\" ||\n node.type === \"module_declaration\" ||\n node.type === \"internal_module\" ||\n node.type === \"export_statement\" ||\n node.type === \"import_statement\"\n ) {\n return { boundaryType: \"structural\", simple: \"module\" };\n }\n if (\n node.type === \"function_declaration\" ||\n node.type === \"method_definition\" ||\n node.type === \"method_signature\" ||\n node.type === \"abstract_method_signature\" ||\n node.type === \"constructor\" ||\n node.type === \"arrow_function\" ||\n node.type === \"variable_declaration\" ||\n node.type === \"lexical_declaration\" ||\n node.type === \"variable_declarator\"\n ) {\n return { boundaryType: \"content\", simple: \"function\" };\n }\n return { boundaryType: \"content\", simple: \"other\" };\n}\n\nexport class TypeScriptParser implements LanguageParser {\n // Unified extensions: TS + JS\n readonly fileExtensions = [\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n ];\n\n readonly mimeTypes = [\n // text/x-* variants (output by MimeTypeUtils.detectMimeTypeFromPath)\n \"text/x-typescript\",\n \"text/x-tsx\",\n \"text/x-jsx\",\n // Standard variants\n \"text/typescript\",\n \"application/typescript\",\n \"text/tsx\",\n \"application/tsx\",\n \"text/javascript\",\n \"application/javascript\",\n \"text/jsx\",\n \"application/jsx\",\n ];\n\n readonly name = \"typescript\";\n\n constructor(\n private readonly treeSitterSizeLimit: number = defaults.splitter.treeSitterSizeLimit,\n ) {}\n\n private createParser(source: string): Parser {\n const p = new Parser();\n const lang = detectTSX(source)\n ? (TypeScript.tsx as unknown)\n : (TypeScript.typescript as unknown);\n p.setLanguage(lang);\n return p;\n }\n\n parse(source: string): ParseResult {\n // Handle tree-sitter size limit\n const limit = this.treeSitterSizeLimit;\n if (source.length > limit) {\n // For files exceeding the limit, we truncate at a reasonable boundary and return a limited parse\n // Try to find a good truncation point (end of line)\n let truncatedSource = source.slice(0, limit);\n const lastNewline = truncatedSource.lastIndexOf(\"\\n\");\n if (lastNewline > limit * 0.9) {\n // If we can find a newline in the last 10% of the limit, use that\n truncatedSource = source.slice(0, lastNewline + 1);\n }\n\n try {\n const parser = this.createParser(truncatedSource);\n const tree = parser.parse(truncatedSource);\n const errorNodes: SyntaxNode[] = [];\n this.collectErrorNodes(tree.rootNode, errorNodes);\n\n return {\n tree,\n hasErrors: true, // Mark as having errors due to truncation\n errorNodes,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse truncated TypeScript file (${truncatedSource.length} chars): ${(error as Error).message}`,\n );\n }\n }\n\n // Normal parsing for files within the size limit\n try {\n const parser = this.createParser(source);\n const tree = parser.parse(source);\n const errorNodes: SyntaxNode[] = [];\n this.collectErrorNodes(tree.rootNode, errorNodes);\n\n return {\n tree,\n hasErrors: errorNodes.length > 0,\n errorNodes,\n };\n } catch (error) {\n throw new Error(\n `Failed to parse TypeScript file (${source.length} chars): ${(error as Error).message}`,\n );\n }\n }\n\n private collectErrorNodes(node: SyntaxNode, acc: SyntaxNode[]): void {\n if (node.hasError && node.type === \"ERROR\") {\n acc.push(node);\n }\n for (const c of node.children) {\n this.collectErrorNodes(c, acc);\n }\n }\n\n getNodeText(node: SyntaxNode, source: string): string {\n return source.slice(node.startIndex, node.endIndex);\n }\n\n getNodeLines(node: SyntaxNode, _source: string) {\n return {\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n };\n }\n\n /**\n * Legacy structural node extraction (used by existing tests).\n * Produces a flat list (no parent/child linking beyond simple push).\n */\n extractStructuralNodes(tree: Tree, source?: string): StructuralNode[] {\n const src = source ?? tree.rootNode.text;\n const out: StructuralNode[] = [];\n const structuralTypes = new Set<string>([\n ...STRUCTURAL_DECL_TYPES,\n ...CONTENT_DECL_TYPES,\n ]);\n\n const visit = (node: SyntaxNode): void => {\n if (structuralTypes.has(node.type)) {\n if (this.shouldSkipStructuralNode(node)) {\n for (const child of node.children) visit(child);\n return;\n }\n\n const name = extractName(node);\n const modifiers = extractModifiers(node);\n const { startLine, startByte } = findDocumentationStart(node, src);\n const endLine = node.endPosition.row + 1;\n const structuralNode: StructuralNode = {\n type: this.classifyStructuralNode(node),\n name,\n startLine,\n endLine,\n startByte,\n endByte: node.endIndex,\n children: [],\n text: this.getNodeText(node, src),\n indentLevel: 0,\n modifiers,\n documentation: undefined,\n };\n out.push(structuralNode);\n // Continue into children (we keep nested declarations; suppression handled separately)\n for (const child of node.children) visit(child);\n return;\n }\n for (const child of node.children) visit(child);\n };\n\n visit(tree.rootNode);\n return this.deduplicate(out);\n }\n\n /**\n * Boundary extraction: produces CodeBoundary[] directly from AST.\n */\n extractBoundaries(tree: Tree, source: string): CodeBoundary[] {\n if (!source.trim()) return [];\n const boundaries: CodeBoundary[] = [];\n\n const walk = (node: SyntaxNode): void => {\n if (isCandidateBoundary(node)) {\n // Skip wrapper export_statement in favor of its meaningful child(ren)\n if (node.type === \"export_statement\") {\n for (const c of node.children) walk(c);\n return;\n }\n\n if (this.shouldSkipStructuralNode(node)) {\n for (const c of node.children) walk(c);\n return;\n }\n\n // Local helper suppression\n if (\n (node.type === \"function_declaration\" ||\n node.type === \"arrow_function\" ||\n node.type === \"method_definition\" ||\n node.type === \"constructor\") &&\n isLocalHelper(node)\n ) {\n // Do not emit boundary for local helper\n for (const c of node.children) walk(c);\n return;\n }\n\n // Suppress arrow_function boundary when parent is variable_declarator (avoid duplicate with variable_declarator)\n if (\n node.type === \"arrow_function\" &&\n node.parent?.type === \"variable_declarator\"\n ) {\n for (const c of node.children) walk(c);\n return;\n }\n\n // Variable declarators: emit if not within function body (local helpers)\n if (node.type === \"variable_declarator\") {\n if (this.isWithinFunctionLikeBody(node)) {\n for (const c of node.children) walk(c);\n return;\n }\n }\n\n const name = extractName(node);\n const docInfo = findDocumentationStart(node, source);\n const classification = classifyBoundaryKind(node);\n\n boundaries.push({\n type: classification.simple,\n boundaryType: classification.boundaryType,\n name,\n startLine: docInfo.startLine,\n endLine: node.endPosition.row + 1,\n startByte: docInfo.startByte,\n endByte: node.endIndex,\n });\n\n // Traverse children (we allow nested boundaries where rules permit)\n for (const c of node.children) walk(c);\n return;\n }\n\n for (const c of node.children) walk(c);\n };\n\n walk(tree.rootNode);\n\n // Deduplicate by start/end/name triple\n const seen = new Set<string>();\n return boundaries.filter((b) => {\n const key = `${b.startByte}:${b.endByte}:${b.name}`;\n if (seen.has(key)) return false;\n seen.add(key);\n return true;\n });\n }\n\n /**\n * Determine if a structural node should be skipped in favor of children (transparent wrapper logic).\n * (Reduced compared to previous logic: currently only handles export wrappers already inline.)\n */\n private shouldSkipStructuralNode(_node: SyntaxNode): boolean {\n // Placeholder for future rules (e.g., more wrapper types). Currently we perform\n // explicit export_statement handling inside traversal so always false here.\n return false;\n }\n\n /**\n * Detect whether a node (e.g., variable_declarator with arrow function) is inside\n * a function/method/constructor body (local helper) before hitting a higher-level\n * structural container (class/namespace/module). Used to suppress local helpers.\n */\n private isWithinFunctionLikeBody(node: SyntaxNode): boolean {\n let ancestor = node.parent;\n while (ancestor) {\n if (\n ancestor.type === \"function_declaration\" ||\n ancestor.type === \"arrow_function\" ||\n ancestor.type === \"method_definition\" ||\n ancestor.type === \"constructor\"\n ) {\n return true;\n }\n if (\n ancestor.type === \"class_declaration\" ||\n ancestor.type === \"abstract_class_declaration\" ||\n ancestor.type === \"namespace_declaration\" ||\n ancestor.type === \"module_declaration\" ||\n ancestor.type === \"internal_module\" ||\n ancestor.type === \"interface_declaration\" ||\n ancestor.type === \"enum_declaration\"\n ) {\n return false;\n }\n ancestor = ancestor.parent;\n }\n return false;\n }\n\n private classifyStructuralNode(node: SyntaxNode): StructuralNodeType {\n switch (node.type) {\n case \"function_declaration\":\n return StructuralNodeType.FUNCTION_DECLARATION;\n case \"arrow_function\":\n return StructuralNodeType.ARROW_FUNCTION;\n case \"method_definition\":\n case \"method_signature\":\n case \"abstract_method_signature\":\n return StructuralNodeType.METHOD_DEFINITION;\n case \"constructor\":\n return StructuralNodeType.CONSTRUCTOR;\n case \"class_declaration\":\n case \"abstract_class_declaration\":\n return StructuralNodeType.CLASS_DECLARATION;\n case \"interface_declaration\":\n return StructuralNodeType.INTERFACE_DECLARATION;\n case \"type_alias_declaration\":\n return StructuralNodeType.TYPE_ALIAS_DECLARATION;\n case \"enum_declaration\":\n return StructuralNodeType.ENUM_DECLARATION;\n case \"namespace_declaration\":\n case \"module_declaration\":\n case \"internal_module\":\n return StructuralNodeType.NAMESPACE_DECLARATION;\n case \"import_statement\":\n return StructuralNodeType.IMPORT_STATEMENT;\n case \"export_statement\":\n return StructuralNodeType.EXPORT_STATEMENT;\n case \"variable_declaration\":\n case \"lexical_declaration\":\n case \"variable_declarator\":\n return StructuralNodeType.VARIABLE_DECLARATION;\n default:\n return StructuralNodeType.VARIABLE_DECLARATION;\n }\n }\n\n private deduplicate(nodes: StructuralNode[]): StructuralNode[] {\n const seen = new Set<string>();\n const out: StructuralNode[] = [];\n for (const n of nodes) {\n const key = `${n.startByte}:${n.endByte}:${n.type}:${n.name}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(n);\n }\n out.sort((a, b) => a.startByte - b.startByte);\n return out;\n }\n}\n","/**\n * LanguageParserRegistry - Registry for tree-sitter language parsers\n *\n * Manages available language parsers and provides parser selection\n * based on file extensions and MIME types.\n */\n\nimport { defaults } from \"../../utils/config\";\nimport { PythonParser } from \"./parsers/PythonParser\";\nimport { TypeScriptParser } from \"./parsers/TypeScriptParser\";\nimport type { LanguageParser } from \"./parsers/types\";\n\nexport class LanguageParserRegistry {\n private parsers = new Map<string, LanguageParser>();\n private extensionMap = new Map<string, string>();\n private mimeTypeMap = new Map<string, string>();\n private readonly treeSitterSizeLimit: number;\n\n constructor(treeSitterSizeLimit: number = defaults.splitter.treeSitterSizeLimit) {\n this.treeSitterSizeLimit = treeSitterSizeLimit;\n this.initializeParsers();\n }\n\n /**\n * Get a parser by language name\n */\n getParser(language: string): LanguageParser | undefined {\n return this.parsers.get(language);\n }\n\n /**\n * Get a parser by file extension\n */\n getParserByExtension(extension: string): LanguageParser | undefined {\n const language = this.extensionMap.get(extension.toLowerCase());\n return language ? this.parsers.get(language) : undefined;\n }\n\n /**\n * Get a parser by MIME type\n */\n getParserByMimeType(mimeType: string): LanguageParser | undefined {\n const language = this.mimeTypeMap.get(mimeType.toLowerCase());\n return language ? this.parsers.get(language) : undefined;\n }\n\n /**\n * Check if a language is supported\n */\n isLanguageSupported(language: string): boolean {\n return this.parsers.has(language);\n }\n\n /**\n * Check if a file extension is supported\n */\n isExtensionSupported(extension: string): boolean {\n return this.extensionMap.has(extension.toLowerCase());\n }\n\n /**\n * Check if a MIME type is supported\n */\n isMimeTypeSupported(mimeType: string): boolean {\n return this.mimeTypeMap.has(mimeType.toLowerCase());\n }\n\n /**\n * Get all supported languages\n */\n getSupportedLanguages(): string[] {\n return Array.from(this.parsers.keys());\n }\n\n /**\n * Get all supported file extensions\n */\n getSupportedExtensions(): string[] {\n return Array.from(this.extensionMap.keys());\n }\n\n /**\n * Get all supported MIME types\n */\n getSupportedMimeTypes(): string[] {\n return Array.from(this.mimeTypeMap.keys());\n }\n\n /**\n * Register a new parser\n */\n registerParser(parser: LanguageParser): void {\n this.parsers.set(parser.name, parser);\n\n // Register file extensions\n for (const extension of parser.fileExtensions) {\n this.extensionMap.set(extension.toLowerCase(), parser.name);\n }\n\n // Register MIME types\n for (const mimeType of parser.mimeTypes) {\n this.mimeTypeMap.set(mimeType.toLowerCase(), parser.name);\n }\n }\n\n private initializeParsers(): void {\n const limit = this.treeSitterSizeLimit;\n\n // Unified TypeScript parser handles the full TS/JS family.\n const unified = new TypeScriptParser(limit);\n this.registerParser(unified); // registers under 'typescript' with all extensions & MIME types\n\n // Create a bound alias object with name 'javascript' so tests expecting parser.name === 'javascript' pass.\n // We DO NOT call registerParser() again (would overwrite extension mappings); instead we:\n // 1. Provide a proxy parser entry named 'javascript'\n // 2. Remap JS-specific extensions & MIME types to that alias\n const jsAlias: LanguageParser = {\n ...unified,\n name: \"javascript\",\n // Bind methods to the original instance to retain internal behavior.\n parse: unified.parse.bind(unified),\n extractStructuralNodes: unified.extractStructuralNodes.bind(unified),\n getNodeText: unified.getNodeText.bind(unified),\n getNodeLines: unified.getNodeLines.bind(unified),\n extractBoundaries: unified.extractBoundaries.bind(unified),\n // Narrow advertised extensions/mime types for the alias (informational only).\n fileExtensions: [\".js\", \".jsx\", \".mjs\", \".cjs\"],\n mimeTypes: [\n \"text/x-jsx\", // Output by MimeTypeUtils.detectMimeTypeFromPath\n \"text/javascript\",\n \"application/javascript\",\n \"text/jsx\",\n \"application/jsx\",\n ],\n };\n this.parsers.set(\"javascript\", jsAlias);\n\n // Remap JS-related extensions & MIME types to point to the 'javascript' alias so lookups yield alias name.\n const jsExts = [\".js\", \".jsx\", \".mjs\", \".cjs\"];\n for (const ext of jsExts) {\n this.extensionMap.set(ext.toLowerCase(), \"javascript\");\n }\n const jsMimes = [\n \"text/x-jsx\", // Output by MimeTypeUtils.detectMimeTypeFromPath\n \"text/javascript\",\n \"application/javascript\",\n \"text/jsx\",\n \"application/jsx\",\n ];\n for (const mt of jsMimes) {\n this.mimeTypeMap.set(mt.toLowerCase(), \"javascript\");\n }\n\n // Register Python parser\n const pythonParser = new PythonParser(limit);\n this.registerParser(pythonParser);\n }\n}\n","import { defaults } from \"../../utils/config\";\nimport { TextContentSplitter } from \"../splitters/TextContentSplitter\";\nimport type { Chunk, DocumentSplitter, SplitterConfig } from \"../types\";\nimport { LanguageParserRegistry } from \"./LanguageParserRegistry\";\nimport type { CodeBoundary, LanguageParser } from \"./parsers/types\";\n\n/**\n * Tree-sitter based source code splitter that provides semantic parsing\n * while maintaining compatibility with existing chunk patterns\n */\nexport class TreesitterSourceCodeSplitter implements DocumentSplitter {\n private readonly textContentSplitter: TextContentSplitter;\n private readonly registry: LanguageParserRegistry;\n private readonly maxChunkSize: number;\n\n constructor(config: SplitterConfig) {\n this.maxChunkSize = config.maxChunkSize;\n const treeSitterSizeLimit =\n config.treeSitterSizeLimit ?? defaults.splitter.treeSitterSizeLimit;\n\n // Initialize registry and text content splitter\n this.registry = new LanguageParserRegistry(treeSitterSizeLimit);\n this.textContentSplitter = new TextContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n }\n\n async splitText(content: string, contentType?: string): Promise<Chunk[]> {\n if (!content.trim()) {\n return [];\n }\n\n // Try to get a parser for this content type\n const parser = this.getParserForContent(contentType);\n if (!parser) {\n // Fall back to TextContentSplitter for unsupported languages\n return this.fallbackToTextSplitter(content);\n }\n\n try {\n // Parse the source code\n const parseResult = parser.parse(content);\n\n if (parseResult.hasErrors) {\n console.warn(\n `Tree-sitter parsing had errors for ${contentType}, but continuing with partial results`,\n );\n }\n\n // Extract simplified boundaries for chunking\n const boundaries = parser.extractBoundaries(parseResult.tree, content);\n\n if (boundaries.length === 0) {\n // No semantic boundaries found, fall back to text splitter\n return this.fallbackToTextSplitter(content);\n }\n\n // Build hierarchical relationships between boundaries\n const hierarchicalBoundaries = this.buildBoundaryHierarchy(boundaries);\n\n // Direct conversion to chunks (parser is responsible for suppressing unwanted nested boundaries)\n return await this.boundariesToChunks(hierarchicalBoundaries, content, contentType);\n } catch (error) {\n // Graceful fallback to TextContentSplitter on any parsing error\n console.warn(\n \"TreesitterSourceCodeSplitter failed, falling back to TextContentSplitter:\",\n error,\n );\n return this.fallbackToTextSplitter(content);\n }\n }\n\n /**\n * Helper method to fall back to TextContentSplitter and convert results to ContentChunk[]\n */\n private async fallbackToTextSplitter(content: string): Promise<Chunk[]> {\n return this.splitContentIntoChunks(content, [], 0);\n }\n\n /**\n * Get the appropriate parser for the given content type\n */\n private getParserForContent(contentType?: string): LanguageParser | undefined {\n if (!contentType) {\n return undefined;\n }\n\n // Try to find parser by MIME type first\n let parser = this.registry.getParserByMimeType(contentType);\n if (parser) {\n return parser;\n }\n\n // Try to extract file extension from content type\n const extensionMatch = contentType.match(/\\.([a-zA-Z]+)$/);\n if (extensionMatch) {\n const extension = `.${extensionMatch[1]}`;\n parser = this.registry.getParserByExtension(extension);\n if (parser) {\n return parser;\n }\n }\n\n // Check for common patterns in content type\n if (contentType.includes(\"javascript\") || contentType.includes(\"typescript\")) {\n return this.registry.getParser(\"typescript\");\n }\n if (contentType.includes(\"jsx\") || contentType.includes(\"tsx\")) {\n // Unified TypeScript parser also handles JSX/TSX\n return this.registry.getParser(\"typescript\");\n }\n\n return undefined;\n }\n\n /**\n * Check if the content type is supported\n */\n isSupportedContentType(contentType?: string): boolean {\n return this.getParserForContent(contentType) !== undefined;\n }\n\n /**\n * Get the list of supported languages\n */\n getSupportedLanguages(): string[] {\n return this.registry.getSupportedLanguages();\n }\n\n /**\n * Get the list of supported file extensions\n */\n getSupportedExtensions(): string[] {\n return this.registry.getSupportedExtensions();\n }\n\n /**\n * Get the list of supported MIME types\n */\n getSupportedMimeTypes(): string[] {\n return this.registry.getSupportedMimeTypes();\n }\n\n /**\n * Helper method to split content using TextContentSplitter only if needed\n * and create ContentChunks with the specified hierarchical path and level\n */\n private async splitContentIntoChunks(\n content: string,\n path: string[],\n level: number,\n ): Promise<Chunk[]> {\n // Preserve whitespace-only content if it fits within chunk size (for perfect reconstruction)\n // Only skip if content is completely empty\n if (content.length === 0) {\n return [];\n }\n\n // Only apply TextContentSplitter if content exceeds max chunk size\n if (content.length <= this.maxChunkSize) {\n // Content is small enough, return as single chunk preserving original formatting\n return [\n {\n types: [\"code\"] as const,\n content,\n section: {\n level,\n path,\n },\n },\n ];\n }\n\n // Content is too large, use TextContentSplitter to break it down\n const textChunks = await this.textContentSplitter.split(content);\n\n // Convert text chunks to ContentChunks with semantic context\n return textChunks.map((textChunk) => ({\n types: [\"code\"] as const,\n content: textChunk,\n section: {\n level,\n path,\n // Make sure to preserve parent hierarchy for large content chunks too\n },\n }));\n }\n\n /**\n * Convert boundaries to chunks.\n * Algorithm:\n * - Collect line breakpoints: file start, each boundary start, each boundary end+1, file end+1\n * - Create linear segments between breakpoints (each line appears exactly once)\n * - Determine containing (innermost) boundary for each segment for path/level\n * - First segment belonging to a structural boundary => structural chunk; subsequent segments demoted to content\n * - Universal max size enforcement: any segment > maxChunkSize is further split via TextContentSplitter\n * - No heuristic de-noising or whitespace merging; reconstruction is guaranteed by preserving order + exact text\n */\n private async boundariesToChunks(\n boundaries: CodeBoundary[],\n content: string,\n _contentType?: string,\n ): Promise<Chunk[]> {\n const lines = content.split(\"\\n\");\n const totalLines = lines.length;\n\n if (boundaries.length === 0) {\n // No boundaries found, use TextContentSplitter on entire content\n // Note: splitContentIntoChunks will preserve content if small enough\n return this.splitContentIntoChunks(content, [], 0);\n }\n\n // NOTE: Removed previous adjustment that forcibly shifted the first boundary\n // to line 1 when only whitespace preceded it. That logic rewrote startLine\n // and broke documentation merging tests (expected doc start line).\n // We preserve original boundary start lines now.\n\n // Step 1: Collect all boundary points (start and end+1 for exclusive ranges)\n const boundaryPoints = new Set<number>();\n boundaryPoints.add(1); // Always start from line 1\n boundaryPoints.add(totalLines + 1); // Always end after last line\n\n for (const boundary of boundaries) {\n boundaryPoints.add(boundary.startLine);\n boundaryPoints.add(boundary.endLine + 1); // +1 for exclusive end\n }\n\n // Step 2: Sort points to create segments\n const sortedPoints = Array.from(boundaryPoints).sort((a, b) => a - b);\n\n // Step 3: Create segments between consecutive points (collect first, don't process yet)\n interface TextSegment {\n startLine: number;\n endLine: number;\n content: string;\n containingBoundary?: CodeBoundary;\n }\n\n const segments: TextSegment[] = [];\n\n for (let i = 0; i < sortedPoints.length - 1; i++) {\n const startLine = sortedPoints[i];\n const endLine = sortedPoints[i + 1] - 1; // Convert back to inclusive end\n\n // Skip empty segments\n if (startLine > endLine || startLine > totalLines) {\n continue;\n }\n\n // Extract content for this segment\n const segmentLines = lines.slice(startLine - 1, Math.min(endLine, totalLines)); // Convert to 0-indexed and clamp\n let segmentContent = segmentLines.join(\"\\n\");\n\n // Add trailing newline for all segments except the last one (for perfect reconstruction)\n if (endLine < totalLines) {\n segmentContent += \"\\n\";\n }\n\n if (segmentContent.length === 0) {\n continue; // Skip empty segments\n }\n\n // Determine which boundary this segment belongs to (innermost containing boundary)\n const containingBoundary = this.findContainingBoundary(\n startLine,\n endLine,\n boundaries,\n );\n\n segments.push({\n startLine,\n endLine,\n content: segmentContent,\n containingBoundary,\n });\n }\n\n // Step 4: Convert segments directly to chunks (whitespace retained verbatim)\n const chunks: Chunk[] = [];\n\n // Ensure only ONE structural chunk is emitted per structural boundary.\n const structuralBoundaryFirstChunk = new Set<CodeBoundary>();\n\n // Accumulate whitespace-only segments and prepend to next non-whitespace segment\n // to avoid emitting standalone whitespace chunks (tests assert no empty-trim chunks)\n let pendingWhitespace = \"\";\n\n for (const segment of segments) {\n if (segment.content.trim() === \"\") {\n pendingWhitespace += segment.content;\n continue;\n }\n // Assign path and level based on containing boundary\n let path: string[];\n let level: number;\n\n const boundary = segment.containingBoundary;\n\n if (boundary) {\n // Use the boundary's hierarchical path and level\n path = boundary.path || [boundary.name || \"unnamed\"];\n level = boundary.level || path.length;\n } else {\n // No containing boundary, this is global code\n path = [];\n level = 0;\n }\n\n // Determine initial structural classification\n let isStructural = boundary?.boundaryType === \"structural\";\n\n if (isStructural && boundary) {\n if (structuralBoundaryFirstChunk.has(boundary)) {\n // Demote subsequent segments of the same structural boundary\n isStructural = false;\n } else {\n structuralBoundaryFirstChunk.add(boundary);\n }\n }\n\n // If segment is too large, delegate to TextContentSplitter\n const segmentChunks = await this.splitContentIntoChunks(\n segment.content,\n path,\n level,\n );\n\n // Overwrite types based on structural vs content classification (always include \"code\" for backward compatibility)\n for (const c of segmentChunks) {\n // Prepend any accumulated whitespace to the FIRST chunk emitted for this segment\n if (pendingWhitespace) {\n c.content = pendingWhitespace + c.content;\n pendingWhitespace = \"\";\n }\n c.types = isStructural ? [\"code\", \"structural\"] : [\"code\"];\n }\n\n chunks.push(...segmentChunks);\n }\n\n // If file ended with whitespace-only content, append it to last chunk (preserve reconstructability)\n if (pendingWhitespace && chunks.length > 0) {\n chunks[chunks.length - 1].content += pendingWhitespace;\n }\n\n return chunks;\n }\n\n /**\n * Build hierarchical relationships between boundaries based on containment\n */\n private buildBoundaryHierarchy(boundaries: CodeBoundary[]): CodeBoundary[] {\n // Create a copy of boundaries to avoid mutating the original\n const hierarchicalBoundaries = boundaries.map((b) => ({ ...b }));\n\n // Build parent-child relationships\n for (let i = 0; i < hierarchicalBoundaries.length; i++) {\n const boundary = hierarchicalBoundaries[i];\n let parent: CodeBoundary | undefined;\n let smallestRange = Infinity;\n\n // Find the smallest containing parent\n for (let j = 0; j < hierarchicalBoundaries.length; j++) {\n if (i === j) continue;\n const candidate = hierarchicalBoundaries[j];\n\n // Check if candidate contains boundary\n if (\n candidate.startLine <= boundary.startLine &&\n candidate.endLine >= boundary.endLine &&\n candidate.startByte <= boundary.startByte &&\n candidate.endByte >= boundary.endByte\n ) {\n const range = candidate.endLine - candidate.startLine;\n\n // Keep the smallest containing boundary (innermost parent)\n if (range < smallestRange) {\n smallestRange = range;\n parent = candidate;\n }\n }\n }\n\n if (parent) {\n boundary.parent = parent;\n }\n\n // Build hierarchical path\n boundary.path = this.buildBoundaryPath(boundary);\n boundary.level = boundary.path.length;\n }\n\n return hierarchicalBoundaries;\n }\n\n /**\n * Build hierarchical path for a boundary by walking up the parent chain\n */\n private buildBoundaryPath(boundary: CodeBoundary): string[] {\n const path: string[] = [];\n let current: CodeBoundary | undefined = boundary;\n\n // Walk up the parent chain\n while (current) {\n if (current.name) {\n path.unshift(current.name); // Add to beginning to build path from root\n }\n current = current.parent;\n }\n\n return path;\n }\n\n /**\n * Find the innermost boundary that contains the given line range\n */\n private findContainingBoundary(\n startLine: number,\n endLine: number,\n boundaries: CodeBoundary[],\n ): CodeBoundary | undefined {\n let innermost: CodeBoundary | undefined;\n let smallestRange = Infinity;\n\n for (const boundary of boundaries) {\n // Check if boundary contains the segment\n if (boundary.startLine <= startLine && boundary.endLine >= endLine) {\n const range = boundary.endLine - boundary.startLine;\n\n // Keep the smallest containing boundary (innermost)\n if (range < smallestRange) {\n smallestRange = range;\n innermost = boundary;\n }\n }\n }\n\n return innermost;\n }\n}\n","import { TreesitterSourceCodeSplitter } from \"../../splitter/treesitter/TreesitterSourceCodeSplitter\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { convertToString } from \"../utils/buffer\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\n/**\n * Pipeline for processing source code content with semantic, structure-aware splitting.\n * Uses TreesitterSourceCodeSplitter for language-aware hierarchical chunking that preserves\n * {level, path} integrity for reassembly. No greedy size-based merging is applied because it\n * would blur structural boundaries and degrade hierarchical reconstruction quality.\n */\nexport class SourceCodePipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: TreesitterSourceCodeSplitter;\n\n constructor(config: AppConfig) {\n super();\n\n // Source code processing uses minimal middleware since we preserve raw structure\n this.middleware = [];\n\n // Semantic, structure-preserving splitter only (no greedy size merging to keep hierarchy intact)\n this.splitter = new TreesitterSourceCodeSplitter(config.splitter);\n }\n\n canProcess(mimeType: string): boolean {\n if (!mimeType) return false;\n return MimeTypeUtils.isSourceCode(mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n contentType: rawContent.mimeType || \"text/plain\",\n content: contentString,\n source: rawContent.source,\n // metadata: {\n // language: rawContent.mimeType\n // ? MimeTypeUtils.extractLanguageFromMimeType(rawContent.mimeType)\n // : \"text\",\n // isSourceCode: true,\n // },\n links: [], // Source code files typically don't contain web links\n errors: [],\n options,\n fetcher,\n };\n\n // Execute the middleware stack (minimal for source code)\n await this.executeMiddlewareStack(this.middleware, context);\n\n // Split the content using CodeContentSplitter\n const chunks = await this.splitter.splitText(context.content, rawContent.mimeType);\n\n return {\n title: context.title,\n contentType: context.contentType,\n textContent: context.content,\n // metadata: context.metadata,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n}\n","import { GreedySplitter } from \"../../splitter/GreedySplitter\";\nimport { TextDocumentSplitter } from \"../../splitter/TextDocumentSplitter\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport { convertToString } from \"../utils/buffer\";\nimport { BasePipeline } from \"./BasePipeline\";\nimport type { PipelineResult } from \"./types\";\n\n/**\n * TextPipeline - Processes plain text content with size optimization.\n */\nexport class TextPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: GreedySplitter;\n\n constructor(config: AppConfig) {\n super();\n\n const preferredChunkSize = config.splitter.preferredChunkSize;\n const maxChunkSize = config.splitter.maxChunkSize;\n const minChunkSize = config.splitter.minChunkSize;\n\n // Text processing uses minimal middleware for maximum compatibility\n this.middleware = [];\n\n // Create the two-phase splitting: basic text splitting + size optimization\n const textSplitter = new TextDocumentSplitter(config.splitter);\n this.splitter = new GreedySplitter(\n textSplitter,\n minChunkSize,\n preferredChunkSize,\n maxChunkSize,\n );\n }\n\n canProcess(mimeType: string, content?: string | Buffer): boolean {\n // This pipeline serves as a fallback for text content, but should not process binary files\n\n // First check: MIME type filtering - use utility method for safe types\n if (!MimeTypeUtils.isSafeForTextProcessing(mimeType)) {\n return false;\n }\n\n // Second check: binary detection via null bytes (if content is provided)\n if (content && MimeTypeUtils.isBinary(content)) {\n return false;\n }\n\n // If we get here, it's a safe MIME type and doesn't appear binary\n return true;\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<PipelineResult> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n title: \"\", // Title extraction can be added in middleware if needed\n contentType: rawContent.mimeType || \"text/plain\",\n content: contentString,\n source: rawContent.source,\n links: [], // Generic text content typically doesn't contain structured links\n errors: [],\n options,\n fetcher,\n };\n\n // Execute the middleware stack (minimal for generic text)\n await this.executeMiddlewareStack(this.middleware, context);\n\n // Split the content using TextDocumentSplitter with size optimization\n const chunks = await this.splitter.splitText(context.content, rawContent.mimeType);\n\n return {\n title: context.title,\n contentType: context.contentType,\n textContent: context.content,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n}\n","/**\n * PipelineFactory - Centralized factory for creating content processing pipelines.\n *\n * This factory ensures consistency across scraper strategies by providing standardized\n * pipeline configurations. It eliminates code duplication and makes it easy to maintain\n * and extend pipeline configurations globally.\n */\n\nimport type { AppConfig } from \"../../utils/config\";\nimport { DocumentPipeline } from \"./DocumentPipeline\";\nimport { HtmlPipeline } from \"./HtmlPipeline\";\nimport { JsonPipeline } from \"./JsonPipeline\";\nimport { MarkdownPipeline } from \"./MarkdownPipeline\";\nimport { SourceCodePipeline } from \"./SourceCodePipeline\";\nimport { TextPipeline } from \"./TextPipeline\";\nimport type { ContentPipeline } from \"./types\";\n\n/**\n * Factory class for creating content processing pipelines.\n * Provides standardized pipeline configurations used across different scraper strategies.\n */\n// biome-ignore lint/complexity/noStaticOnlyClass: Factory pattern with static methods is appropriate here\nexport class PipelineFactory {\n /**\n * Creates the standard set of content pipelines used by all scraper strategies.\n * Includes HTML, Markdown, JSON, source code, document, and text processing capabilities.\n * Each pipeline now handles both preprocessing and content-specific splitting.\n * TextPipeline is placed last as the universal fallback for unknown content types.\n *\n * @returns Array of content pipelines in processing order\n */\n public static createStandardPipelines(appConfig: AppConfig): ContentPipeline[] {\n return [\n new JsonPipeline(appConfig),\n new SourceCodePipeline(appConfig),\n new DocumentPipeline(appConfig), // PDF, Office docs, OpenDocument, RTF, eBooks, Jupyter notebooks\n new HtmlPipeline(appConfig),\n new MarkdownPipeline(appConfig),\n new TextPipeline(appConfig), // Universal fallback - must be last\n ];\n }\n}\n","import type { AutoDetectFetcher, RawContent } from \"../scraper/fetcher\";\nimport { PipelineFactory } from \"../scraper/pipelines/PipelineFactory\";\nimport type { ContentPipeline, PipelineResult } from \"../scraper/pipelines/types\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport { convertToString } from \"../scraper/utils/buffer\";\nimport { resolveCharset } from \"../scraper/utils/charset\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError, ValidationError } from \"./errors\";\n\nexport interface FetchUrlToolOptions {\n /**\n * The URL to fetch and convert to markdown.\n * Must be a valid HTTP/HTTPS URL or file:// URL.\n */\n url: string;\n\n /**\n * Whether to follow HTTP redirects.\n * @default true\n */\n followRedirects?: boolean;\n\n /**\n * Determines the HTML processing strategy.\n * - 'fetch': Use a simple DOM parser (faster, less JS support).\n * - 'playwright': Use a headless browser (slower, full JS support).\n * - 'auto': Automatically select the best strategy (currently defaults to 'playwright').\n * @default ScrapeMode.Auto\n */\n scrapeMode?: ScrapeMode;\n\n /**\n * Custom HTTP headers to send with the request (e.g., for authentication).\n * Keys are header names, values are header values.\n */\n headers?: Record<string, string>;\n}\n\n/**\n * Tool for fetching a single URL and converting its content to Markdown.\n * Unlike scrape_docs, this tool only processes one page without crawling\n * or storing the content.\n *\n * Supports both HTTP/HTTPS URLs and local file URLs (file://).\n */\nexport class FetchUrlTool {\n /**\n * AutoDetectFetcher handles all URL types and fallback logic automatically.\n */\n private readonly fetcher: AutoDetectFetcher;\n /**\n * Collection of pipelines that will be tried in order for processing content.\n * The first pipeline that can process the content type will be used.\n * Currently includes HtmlPipeline, MarkdownPipeline, and TextPipeline (as fallback).\n */\n private readonly pipelines: ContentPipeline[];\n\n constructor(fetcher: AutoDetectFetcher, config: AppConfig) {\n this.fetcher = fetcher;\n // Use the central factory to ensure consistent pipeline configuration across the system\n this.pipelines = PipelineFactory.createStandardPipelines(config);\n }\n\n /**\n * Fetches content from a URL and converts it to Markdown.\n * Supports both HTTP/HTTPS URLs and local file URLs (file://).\n * @returns The processed Markdown content\n * @throws {ToolError} If fetching or processing fails\n */\n async execute(options: FetchUrlToolOptions): Promise<string> {\n const { url, scrapeMode = ScrapeMode.Auto, headers } = options;\n\n if (!this.fetcher.canFetch(url)) {\n throw new ValidationError(\n `Invalid URL: ${url}. Must be an HTTP/HTTPS URL or a file:// URL.`,\n this.constructor.name,\n );\n }\n\n try {\n logger.info(`📡 Fetching ${url}...`);\n\n const fetchOptions = {\n followRedirects: options.followRedirects ?? true,\n maxRetries: 3,\n headers, // propagate custom headers\n };\n\n // AutoDetectFetcher handles all fallback logic automatically\n const rawContent: RawContent = await this.fetcher.fetch(url, fetchOptions);\n\n logger.info(\"🔄 Processing content...\");\n\n let processed: Awaited<PipelineResult> | undefined;\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent.mimeType, rawContent.content)) {\n processed = await pipeline.process(\n rawContent,\n {\n url,\n library: \"\",\n version: \"\",\n maxDepth: 0,\n maxPages: 1,\n maxConcurrency: 1,\n scope: \"subpages\",\n followRedirects: options.followRedirects ?? true,\n excludeSelectors: undefined,\n ignoreErrors: false,\n scrapeMode,\n headers, // propagate custom headers\n },\n this.fetcher,\n );\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for ${url}. Returning raw content.`,\n );\n // Use proper charset detection for unsupported content types\n const resolvedCharset = resolveCharset(\n rawContent.charset,\n rawContent.content,\n rawContent.mimeType,\n );\n const contentString = convertToString(rawContent.content, resolvedCharset);\n return contentString;\n }\n\n for (const err of processed.errors ?? []) {\n logger.warn(`⚠️ Processing error for ${url}: ${err.message}`);\n }\n\n if (typeof processed.textContent !== \"string\" || !processed.textContent.trim()) {\n throw new ToolError(\n `Processing resulted in empty content for ${url}`,\n this.constructor.name,\n );\n }\n\n logger.info(`✅ Successfully processed ${url}`);\n return processed.textContent;\n } catch (error) {\n // Preserve ToolError as it already has user-friendly messages, wrap others\n if (error instanceof ToolError) {\n throw error;\n }\n\n throw new ToolError(\n `Unable to fetch or process the URL \"${url}\". Please verify the URL is correct and accessible.`,\n this.constructor.name,\n );\n } finally {\n // Cleanup all pipelines and fetcher to prevent resource leaks (e.g., browser instances)\n await Promise.allSettled([\n ...this.pipelines.map((pipeline) => pipeline.close()),\n this.fetcher.close(),\n ]);\n }\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { ValidationError } from \"./errors\";\n\nexport interface FindVersionToolOptions {\n library: string;\n targetVersion?: string;\n}\n\nexport interface FindVersionToolResult {\n bestMatch: string | null;\n hasUnversioned: boolean;\n message: string;\n}\n\n/**\n * Tool for finding the best matching version of a library in the store.\n * Supports exact version matches and X-Range patterns (e.g., '5.x', '5.2.x').\n */\nexport class FindVersionTool {\n private docService: IDocumentManagement;\n\n constructor(docService: IDocumentManagement) {\n this.docService = docService;\n }\n\n /**\n * Executes the tool to find the best matching version and checks for unversioned docs.\n * @returns A structured object with the best match, unversioned status, and descriptive message.\n * @throws {ValidationError} If the library parameter is invalid.\n * @throws {VersionNotFoundInStoreError} If no matching versions or unversioned docs are found.\n */\n async execute(options: FindVersionToolOptions): Promise<FindVersionToolResult> {\n const { library, targetVersion } = options;\n\n // Validate input\n if (!library || typeof library !== \"string\" || library.trim() === \"\") {\n throw new ValidationError(\n \"Library name is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n\n // Let VersionNotFoundInStoreError bubble up instead of catching it\n const { bestMatch, hasUnversioned } = await this.docService.findBestVersion(\n library,\n targetVersion,\n );\n\n let message = \"\";\n if (bestMatch) {\n message = `Best match: ${bestMatch}.`;\n if (hasUnversioned) {\n message += \" Unversioned docs also available.\";\n }\n } else if (hasUnversioned) {\n message = `No matching version found for ${libraryAndVersion}, but unversioned docs exist.`;\n } else {\n // This case should ideally be caught by VersionNotFoundInStoreError,\n // but added for completeness.\n message = `No matching version or unversioned documents found for ${libraryAndVersion}.`;\n }\n\n return {\n bestMatch,\n hasUnversioned,\n message,\n };\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJobStatus } from \"../pipeline/types\";\nimport type { VersionStatus } from \"../store/types\";\nimport { ToolError, ValidationError } from \"./errors\";\n\n/**\n * Input parameters for the GetJobInfoTool.\n */\nexport interface GetJobInfoInput {\n /** The ID of the job to retrieve info for. */\n jobId: string;\n}\n\n/**\n * Simplified information about a pipeline job for external use.\n */\nexport interface JobInfo {\n id: string;\n library: string;\n version: string | null;\n status: PipelineJobStatus; // Pipeline status (for compatibility)\n dbStatus?: VersionStatus; // Database status (enhanced)\n createdAt: string;\n startedAt: string | null;\n finishedAt: string | null;\n error: string | null;\n // Progress information from database\n progress?: {\n pages: number;\n totalPages: number;\n totalDiscovered: number;\n };\n // Additional database fields\n updatedAt?: string;\n errorMessage?: string; // Database error message\n}\n\n/**\n * Response structure for the GetJobInfoTool.\n */\nexport interface GetJobInfoToolResponse {\n job: JobInfo;\n}\n\n/**\n * Tool for retrieving simplified information about a specific pipeline job.\n */\nexport class GetJobInfoTool {\n private pipeline: IPipeline;\n\n /**\n * Creates an instance of GetJobInfoTool.\n * @param pipeline The pipeline instance.\n */\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\n }\n\n /**\n * Executes the tool to retrieve simplified info for a specific job using enhanced PipelineJob interface.\n * @param input - The input parameters, containing the jobId.\n * @returns A promise that resolves with the simplified job info.\n * @throws {ValidationError} If the jobId is invalid.\n * @throws {ToolError} If the job is not found.\n */\n async execute(input: GetJobInfoInput): Promise<GetJobInfoToolResponse> {\n // Validate input\n if (!input.jobId || typeof input.jobId !== \"string\" || input.jobId.trim() === \"\") {\n throw new ValidationError(\n \"Job ID is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n const job = await this.pipeline.getJob(input.jobId);\n\n if (!job) {\n throw new ToolError(`Job with ID ${input.jobId} not found.`, this.constructor.name);\n }\n\n // Transform the job into a simplified object using enhanced PipelineJob interface\n const jobInfo: JobInfo = {\n id: job.id,\n library: job.library,\n version: job.version,\n status: job.status,\n dbStatus: job.versionStatus,\n createdAt: job.createdAt.toISOString(),\n startedAt: job.startedAt?.toISOString() ?? null,\n finishedAt: job.finishedAt?.toISOString() ?? null,\n error: job.error?.message ?? null,\n progress:\n job.progressMaxPages && job.progressMaxPages > 0\n ? {\n pages: job.progressPages || 0,\n totalPages: job.progressMaxPages,\n totalDiscovered: job.progress?.totalDiscovered || job.progressMaxPages,\n }\n : undefined,\n updatedAt: job.updatedAt?.toISOString(),\n errorMessage: job.errorMessage ?? undefined,\n };\n\n return { job: jobInfo };\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJob, PipelineJobStatus } from \"../pipeline/types\";\nimport type { JobInfo } from \"./GetJobInfoTool\"; // Import JobInfo\n\n/**\n * Input parameters for the ListJobsTool.\n */\nexport interface ListJobsInput {\n /** Optional status to filter jobs by. */\n status?: PipelineJobStatus;\n}\n\n/**\n * Response structure for the ListJobsTool.\n */\nexport interface ListJobsToolResponse {\n jobs: JobInfo[];\n}\n\n/**\n * Tool for listing pipeline jobs managed by the pipeline.\n * Allows filtering jobs by their status.\n */\nexport class ListJobsTool {\n private pipeline: IPipeline;\n\n /**\n * Creates an instance of ListJobsTool.\n * @param pipeline The pipeline instance.\n */\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\n }\n\n /**\n * Executes the tool to retrieve a list of pipeline jobs using single source of truth.\n * @param input - The input parameters, optionally including a status filter.\n * @returns A promise that resolves with the list of simplified job objects.\n */\n async execute(input: ListJobsInput): Promise<ListJobsToolResponse> {\n const jobs = await this.pipeline.getJobs(input.status);\n\n // Transform jobs into simplified objects using enhanced PipelineJob interface\n const simplifiedJobs: JobInfo[] = jobs.map((job: PipelineJob): JobInfo => {\n return {\n id: job.id,\n library: job.library,\n version: job.version,\n status: job.status,\n dbStatus: job.versionStatus,\n createdAt: job.createdAt.toISOString(),\n startedAt: job.startedAt?.toISOString() ?? null,\n finishedAt: job.finishedAt?.toISOString() ?? null,\n error: job.error?.message ?? null,\n progress:\n job.progressMaxPages && job.progressMaxPages > 0\n ? {\n pages: job.progressPages || 0,\n totalPages: job.progressMaxPages,\n totalDiscovered: job.progress?.totalDiscovered || job.progressMaxPages,\n }\n : undefined,\n updatedAt: job.updatedAt?.toISOString(),\n errorMessage: job.errorMessage ?? undefined,\n };\n });\n\n return { jobs: simplifiedJobs };\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { VersionStatus, VersionSummary } from \"../store/types\";\n\n// Define the structure for the tool's output, using the detailed version info\nexport interface LibraryInfo {\n name: string;\n versions: Array<{\n version: string;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n status: VersionStatus;\n // Progress is omitted for COMPLETED versions to reduce noise\n progress?: { pages: number; maxPages: number };\n sourceUrl?: string | null;\n }>;\n}\n\nexport interface ListLibrariesResult {\n libraries: LibraryInfo[];\n}\n\n/**\n * Tool for listing all available libraries and their indexed versions in the store.\n */\nexport class ListLibrariesTool {\n private docService: IDocumentManagement;\n\n constructor(docService: IDocumentManagement) {\n this.docService = docService;\n }\n\n async execute(_options?: Record<string, never>): Promise<ListLibrariesResult> {\n // docService.listLibraries() now returns the detailed structure directly\n const rawLibraries = await this.docService.listLibraries();\n\n // The structure returned by listLibraries already matches LibraryInfo[]\n // No complex mapping is needed here anymore, just ensure the names match\n const libraries: LibraryInfo[] = rawLibraries.map(({ library, versions }) => ({\n name: library,\n versions: versions.map((v: VersionSummary) => ({\n version: v.ref.version,\n documentCount: v.counts.documents,\n uniqueUrlCount: v.counts.uniqueUrls,\n indexedAt: v.indexedAt,\n status: v.status,\n ...(v.progress ? { progress: v.progress } : undefined),\n sourceUrl: v.sourceUrl,\n })),\n }));\n\n return { libraries };\n }\n}\n","import * as semver from \"semver\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { logger } from \"../utils/logger\";\nimport { ValidationError } from \"./errors\";\n\nexport interface RefreshVersionToolOptions {\n library: string;\n version?: string | null; // Make version optional\n /** If false, returns jobId immediately without waiting. Defaults to true. */\n waitForCompletion?: boolean;\n}\n\nexport interface RefreshResult {\n /** Indicates the number of pages refreshed if waitForCompletion was true and the job succeeded. May be 0 or inaccurate if job failed or waitForCompletion was false. */\n pagesRefreshed: number;\n}\n\n/** Return type for RefreshVersionTool.execute */\nexport type RefreshExecuteResult = RefreshResult | { jobId: string };\n\n/**\n * Tool for refreshing an existing library version by re-scraping all pages\n * and using ETag comparison to skip unchanged content.\n */\nexport class RefreshVersionTool {\n private pipeline: IPipeline;\n\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\n }\n\n async execute(options: RefreshVersionToolOptions): Promise<RefreshExecuteResult> {\n const { library, version, waitForCompletion = true } = options;\n\n let internalVersion: string;\n const partialVersionRegex = /^\\d+(\\.\\d+)?$/; // Matches '1' or '1.2'\n\n if (version === null || version === undefined) {\n internalVersion = \"\";\n } else {\n const validFullVersion = semver.valid(version);\n if (validFullVersion) {\n internalVersion = validFullVersion;\n } else if (partialVersionRegex.test(version)) {\n const coercedVersion = semver.coerce(version);\n if (coercedVersion) {\n internalVersion = coercedVersion.version;\n } else {\n throw new ValidationError(\n `Invalid version format for refreshing: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n \"RefreshVersionTool\",\n );\n }\n } else {\n throw new ValidationError(\n `Invalid version format for refreshing: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n \"RefreshVersionTool\",\n );\n }\n }\n\n internalVersion = internalVersion.toLowerCase();\n\n // Use the injected pipeline instance\n const pipeline = this.pipeline;\n\n // Normalize pipeline version argument: use null for unversioned to be explicit cross-platform\n const refreshVersion: string | null = internalVersion === \"\" ? null : internalVersion;\n\n // Enqueue the refresh job using the injected pipeline\n const jobId = await pipeline.enqueueRefreshJob(library, refreshVersion);\n\n // Conditionally wait for completion\n if (waitForCompletion) {\n try {\n await pipeline.waitForJobCompletion(jobId);\n // Fetch final job state to get status and potentially final page count\n const finalJob = await pipeline.getJob(jobId);\n const finalPagesRefreshed = finalJob?.progress?.pagesScraped ?? 0; // Get count from final job state\n logger.debug(\n `Refresh job ${jobId} finished with status ${finalJob?.status}. Pages refreshed: ${finalPagesRefreshed}`,\n );\n return {\n pagesRefreshed: finalPagesRefreshed,\n };\n } catch (error) {\n logger.error(`❌ Refresh job ${jobId} failed or was cancelled: ${error}`);\n throw error; // Re-throw so the caller knows it failed\n }\n }\n\n // If not waiting, return the job ID immediately\n return { jobId };\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError, ValidationError } from \"./errors\";\n\n/**\n * Represents the arguments for the remove_docs tool.\n * The MCP server should validate the input against RemoveToolInputSchema before calling execute.\n */\nexport interface RemoveToolArgs {\n library: string;\n version?: string;\n}\n\n/**\n * Tool to remove indexed documentation for a specific library version.\n * This class provides the core logic, intended to be called by the McpServer.\n */\nexport class RemoveTool {\n constructor(\n private readonly documentManagementService: IDocumentManagement,\n private readonly pipeline: IPipeline,\n ) {}\n\n /**\n * Executes the tool to remove the specified library version completely.\n * Aborts any QUEUED/RUNNING job for the same library+version before deleting.\n * Removes all documents, the version record, and the library if no other versions exist.\n */\n async execute(args: RemoveToolArgs): Promise<{ message: string }> {\n const { library, version } = args;\n\n // Validate input\n if (!library || typeof library !== \"string\" || library.trim() === \"\") {\n throw new ValidationError(\n \"Library name is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n logger.info(`🗑️ Removing library: ${library}${version ? `@${version}` : \"\"}`);\n\n try {\n // Validate that the library exists before attempting removal\n await this.documentManagementService.validateLibraryExists(library);\n\n // Abort any QUEUED or RUNNING job for this library+version\n const allJobs = await this.pipeline.getJobs();\n const jobs = allJobs.filter(\n (job) =>\n job.library === library &&\n job.version === (version ?? \"\") &&\n (job.status === PipelineJobStatus.QUEUED ||\n job.status === PipelineJobStatus.RUNNING),\n );\n\n for (const job of jobs) {\n logger.info(\n `🚫 Aborting job for ${library}@${version ?? \"\"} before deletion: ${job.id}`,\n );\n await this.pipeline.cancelJob(job.id);\n // Wait for job to finish cancelling if running\n await this.pipeline.waitForJobCompletion(job.id);\n }\n\n // Core logic: Call the document management service to remove the version completely\n await this.documentManagementService.removeVersion(library, version);\n\n const message = `Successfully removed ${library}${version ? `@${version}` : \"\"}.`;\n logger.info(`✅ ${message}`);\n // Return a simple success object, the McpServer will format the final response\n return { message };\n } catch (error) {\n // If it's already a ToolError or other known error types, re-throw as is\n if (error instanceof ToolError) {\n throw error;\n }\n\n const errorMessage = `Failed to remove ${library}${version ? `@${version}` : \"\"}: ${error instanceof Error ? error.message : String(error)}`;\n logger.error(`❌ Error removing library: ${errorMessage}`);\n // Re-throw the error for the McpServer to handle and format\n throw new ToolError(errorMessage, this.constructor.name);\n }\n }\n}\n","import * as semver from \"semver\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { ValidationError } from \"./errors\";\n\nexport interface ScrapeToolOptions {\n library: string;\n version?: string | null; // Make version optional\n url: string;\n options?: {\n maxPages?: number;\n maxDepth?: number;\n /**\n * Defines the allowed crawling boundary relative to the starting URL\n * - 'subpages': Only crawl URLs on the same hostname and within the same starting path (default)\n * - 'hostname': Crawl any URL on the same hostname, regardless of path\n * - 'domain': Crawl any URL on the same top-level domain, including subdomains\n */\n scope?: \"subpages\" | \"hostname\" | \"domain\";\n /**\n * Controls whether HTTP redirects (3xx responses) should be followed\n * - When true: Redirects are followed automatically (default)\n * - When false: A RedirectError is thrown when a 3xx response is received\n */\n followRedirects?: boolean;\n maxConcurrency?: number; // Note: Concurrency is now set when PipelineManager is created\n ignoreErrors?: boolean;\n /**\n * Determines the HTML processing strategy.\n * - 'fetch': Use a simple DOM parser (faster, less JS support).\n * - 'playwright': Use a headless browser (slower, full JS support).\n * - 'auto': Automatically select the best strategy (currently defaults to 'playwright').\n * @default ScrapeMode.Auto\n */\n scrapeMode?: ScrapeMode;\n /**\n * Patterns for including URLs during scraping. If not set, all are included by default.\n * Regex patterns must be wrapped in slashes, e.g. /pattern/.\n */\n includePatterns?: string[];\n /**\n * Patterns for excluding URLs during scraping. Exclude takes precedence over include.\n * If not specified, default patterns exclude common files (CHANGELOG.md, LICENSE, etc.)\n * and folders (archive, deprecated, i18n locales, etc.).\n * Regex patterns must be wrapped in slashes, e.g. /pattern/.\n */\n excludePatterns?: string[];\n /**\n * Custom HTTP headers to send with each request (e.g., for authentication).\n * Keys are header names, values are header values.\n */\n headers?: Record<string, string>;\n /**\n * If true, clears existing documents for the library version before scraping.\n * If false, appends to the existing documents.\n * @default true\n */\n clean?: boolean;\n };\n /** If false, returns jobId immediately without waiting. Defaults to true. */\n waitForCompletion?: boolean;\n}\n\nexport interface ScrapeResult {\n /** Indicates the number of pages scraped if waitForCompletion was true and the job succeeded. May be 0 or inaccurate if job failed or waitForCompletion was false. */\n pagesScraped: number;\n}\n\n/** Return type for ScrapeTool.execute */\nexport type ScrapeExecuteResult = ScrapeResult | { jobId: string };\n\n/**\n * Tool for enqueuing documentation scraping jobs via the pipeline.\n */\nexport class ScrapeTool {\n private pipeline: IPipeline;\n private readonly scraperConfig: AppConfig[\"scraper\"];\n\n constructor(pipeline: IPipeline, config: AppConfig[\"scraper\"]) {\n this.pipeline = pipeline;\n this.scraperConfig = config;\n }\n\n async execute(options: ScrapeToolOptions): Promise<ScrapeExecuteResult> {\n const {\n library,\n version,\n url,\n options: scraperOptions,\n waitForCompletion = true,\n } = options;\n\n // Store initialization and manager start should happen externally\n\n let internalVersion: string;\n const partialVersionRegex = /^\\d+(\\.\\d+)?$/; // Matches '1' or '1.2'\n\n if (version === null || version === undefined) {\n internalVersion = \"\";\n } else {\n const validFullVersion = semver.valid(version);\n if (validFullVersion) {\n internalVersion = validFullVersion;\n } else if (partialVersionRegex.test(version)) {\n const coercedVersion = semver.coerce(version);\n if (coercedVersion) {\n internalVersion = coercedVersion.version;\n } else {\n throw new ValidationError(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n \"ScrapeTool\",\n );\n }\n } else {\n throw new ValidationError(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n \"ScrapeTool\",\n );\n }\n }\n\n internalVersion = internalVersion.toLowerCase();\n\n // Use the injected pipeline instance\n const pipeline = this.pipeline;\n\n // Remove internal progress tracking and callbacks\n // let pagesScraped = 0;\n // let lastReportedPages = 0;\n // const reportProgress = ...\n // pipeline.setCallbacks(...)\n\n // Normalize pipeline version argument: use null for unversioned to be explicit cross-platform\n const enqueueVersion: string | null = internalVersion === \"\" ? null : internalVersion;\n\n // Enqueue the job using the injected pipeline\n const jobId = await pipeline.enqueueScrapeJob(library, enqueueVersion, {\n url: url,\n library: library,\n version: internalVersion,\n scope: scraperOptions?.scope ?? \"subpages\",\n followRedirects: scraperOptions?.followRedirects ?? true,\n maxPages: scraperOptions?.maxPages ?? this.scraperConfig.maxPages,\n maxDepth: scraperOptions?.maxDepth ?? this.scraperConfig.maxDepth,\n maxConcurrency: scraperOptions?.maxConcurrency ?? this.scraperConfig.maxConcurrency,\n ignoreErrors: scraperOptions?.ignoreErrors ?? true,\n scrapeMode: scraperOptions?.scrapeMode ?? ScrapeMode.Auto, // Pass scrapeMode enum\n includePatterns: scraperOptions?.includePatterns,\n excludePatterns: scraperOptions?.excludePatterns,\n headers: scraperOptions?.headers, // <-- propagate headers\n clean: scraperOptions?.clean, // <-- propagate clean option\n });\n\n // Conditionally wait for completion\n if (waitForCompletion) {\n try {\n await pipeline.waitForJobCompletion(jobId);\n // Fetch final job state to get status and potentially final page count\n const finalJob = await pipeline.getJob(jobId);\n const finalPagesScraped = finalJob?.progress?.pagesScraped ?? 0; // Get count from final job state\n logger.debug(\n `Job ${jobId} finished with status ${finalJob?.status}. Pages scraped: ${finalPagesScraped}`,\n );\n return {\n pagesScraped: finalPagesScraped,\n };\n } catch (error) {\n logger.error(`❌ Job ${jobId} failed or was cancelled: ${error}`);\n throw error; // Re-throw so the caller knows it failed\n }\n // No finally block needed to stop pipeline, as it's managed externally\n }\n\n // If not waiting, return the job ID immediately\n return { jobId };\n }\n}\n","/**\n * tRPC client for the document management API.\n * Implements IDocumentManagement and delegates to /api data router.\n */\nimport { createTRPCProxyClient, httpBatchLink } from \"@trpc/client\";\nimport superjson from \"superjson\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { logger } from \"../utils/logger\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport type { IDocumentManagement } from \"./trpc/interfaces\";\nimport type { DataRouter } from \"./trpc/router\";\nimport type {\n DbVersionWithLibrary,\n FindVersionResult,\n LibrarySummary,\n StoredScraperOptions,\n StoreSearchResult,\n VersionStatus,\n} from \"./types\";\n\nexport class DocumentManagementClient implements IDocumentManagement {\n private readonly baseUrl: string;\n private readonly client: ReturnType<typeof createTRPCProxyClient<DataRouter>>;\n\n constructor(serverUrl: string) {\n this.baseUrl = serverUrl.replace(/\\/$/, \"\");\n this.client = createTRPCProxyClient<DataRouter>({\n links: [\n httpBatchLink({\n url: this.baseUrl,\n transformer: superjson,\n }),\n ],\n });\n logger.debug(`DocumentManagementClient (tRPC) created for: ${this.baseUrl}`);\n }\n\n async initialize(): Promise<void> {\n // Connectivity check using ping procedure\n try {\n await this.client.ping.query();\n } catch (error) {\n logger.debug(\n `Failed to connect to DocumentManagement server at ${this.baseUrl}: ${error}`,\n );\n throw new Error(\n `Failed to connect to server at ${this.baseUrl}.\\n\\nPlease verify the server URL includes the correct port (default 8080) and ends with '/api' (e.g., 'http://localhost:8080/api').`,\n );\n }\n }\n\n async shutdown(): Promise<void> {\n // no-op for HTTP client\n }\n\n async listLibraries(): Promise<LibrarySummary[]> {\n return this.client.listLibraries.query();\n }\n\n async validateLibraryExists(library: string): Promise<void> {\n await this.client.validateLibraryExists.mutate({ library });\n }\n\n async findBestVersion(\n library: string,\n targetVersion?: string,\n ): Promise<FindVersionResult> {\n return this.client.findBestVersion.query({ library, targetVersion });\n }\n\n async searchStore(\n library: string,\n version: string | null | undefined,\n query: string,\n limit?: number,\n ): Promise<StoreSearchResult[]> {\n return this.client.search.query({ library, version: version ?? null, query, limit });\n }\n\n async removeVersion(library: string, version?: string | null): Promise<void> {\n await this.client.removeVersion.mutate({ library, version });\n }\n\n async removeAllDocuments(library: string, version?: string | null): Promise<void> {\n await this.client.removeAllDocuments.mutate({ library, version: version ?? null });\n }\n\n async getVersionsByStatus(statuses: VersionStatus[]): Promise<DbVersionWithLibrary[]> {\n return this.client.getVersionsByStatus.query({\n statuses: statuses as unknown as string[],\n });\n }\n\n async findVersionsBySourceUrl(url: string): Promise<DbVersionWithLibrary[]> {\n return this.client.findVersionsBySourceUrl.query({ url });\n }\n\n async getScraperOptions(versionId: number): Promise<StoredScraperOptions | null> {\n return this.client.getScraperOptions.query({ versionId });\n }\n\n async updateVersionStatus(\n versionId: number,\n status: VersionStatus,\n errorMessage?: string,\n ): Promise<void> {\n await this.client.updateVersionStatus.mutate({ versionId, status, errorMessage });\n }\n\n async updateVersionProgress(\n versionId: number,\n pages: number,\n maxPages: number,\n ): Promise<void> {\n await this.client.updateVersionProgress.mutate({ versionId, pages, maxPages });\n }\n\n async storeScraperOptions(versionId: number, options: ScraperOptions): Promise<void> {\n await this.client.storeScraperOptions.mutate({ versionId, options });\n }\n\n getActiveEmbeddingConfig(): EmbeddingModelConfig | null {\n // For remote client, embedding config is not available locally.\n // The remote server's embedding status cannot be synchronously queried.\n // Return null to indicate embeddings status is unknown/unavailable.\n return null;\n }\n}\n","/**\n * Version comparison utilities for consistent sorting across the application.\n * Provides semver-aware comparison with support for unversioned (latest) entries.\n */\n\nimport semver from \"semver\";\n\n/**\n * Compares two version strings for sorting in descending order (latest first).\n *\n * Rules:\n * - Unversioned entries (empty string, null, undefined) are considered \"latest\" and sort first.\n * - Valid semver versions are compared using semver rules.\n * - Invalid semver versions are compared as strings (case-insensitive).\n *\n * @param a First version string (may be empty, null, or undefined for unversioned)\n * @param b Second version string (may be empty, null, or undefined for unversioned)\n * @returns Negative if a should come before b, positive if b should come before a, 0 if equal\n */\nexport function compareVersionsDescending(\n a: string | null | undefined,\n b: string | null | undefined,\n): number {\n const aIsUnversioned = a === \"\" || a === null || a === undefined;\n const bIsUnversioned = b === \"\" || b === null || b === undefined;\n\n // Unversioned entries come first (are \"latest\")\n if (aIsUnversioned && bIsUnversioned) return 0;\n if (aIsUnversioned) return -1;\n if (bIsUnversioned) return 1;\n\n // Both have versions - try semver comparison\n // First try exact semver validation (preserves prerelease tags)\n // Then fall back to coercion for loose versions like \"v1.0.0\"\n const aSemver = semver.valid(a) ?? semver.valid(semver.coerce(a));\n const bSemver = semver.valid(b) ?? semver.valid(semver.coerce(b));\n\n if (aSemver && bSemver) {\n // Both are valid semver - compare descending (higher version first)\n return semver.rcompare(aSemver, bSemver);\n }\n\n // Fallback to string comparison (case-insensitive, descending)\n const aLower = (a as string).toLowerCase();\n const bLower = (b as string).toLowerCase();\n return bLower.localeCompare(aLower);\n}\n\n/**\n * Sorts an array of version strings in descending order (latest first).\n * Unversioned entries (empty string) are placed at the beginning.\n *\n * @param versions Array of version strings\n * @returns New array sorted in descending order\n */\nexport function sortVersionsDescending(versions: string[]): string[] {\n return [...versions].sort(compareVersionsDescending);\n}\n","import type { AppConfig } from \"../../../utils/config\";\nimport { logger } from \"../../../utils/logger\";\nimport { MimeTypeUtils } from \"../../../utils/mimeTypeUtils\";\nimport type { DocumentStore } from \"../../DocumentStore\";\nimport type { DbPageChunk } from \"../../types\";\nimport type { ContentAssemblyStrategy } from \"../types\";\n\n/**\n * Assembly strategy for structured content (source code, JSON, config files).\n *\n * Uses selective subtree reassembly: for single matches, walks up the complete parent\n * hierarchy to the root. For multiple matches within the same document, finds the common\n * ancestor and reconstructs only the relevant subtrees, avoiding inclusion of excessive\n * unrelated content. Simple concatenation leverages splitter concatenation guarantees.\n */\nexport class HierarchicalAssemblyStrategy implements ContentAssemblyStrategy {\n constructor(private config: AppConfig) {}\n\n /**\n * Determines if this strategy can handle the given content type.\n * Handles structured content like source code, JSON, configuration files.\n */\n canHandle(mimeType?: string): boolean {\n if (!mimeType) {\n return false;\n }\n // Source code content\n if (MimeTypeUtils.isSourceCode(mimeType)) {\n return true;\n }\n\n // JSON content\n if (MimeTypeUtils.isJson(mimeType)) {\n return true;\n }\n\n // Could add more structured content detection here if needed\n // (e.g., YAML, TOML, XML configuration files)\n\n return false;\n }\n /**\n * Selects chunks using selective subtree reassembly for multiple matches within documents.\n * For single matches: uses existing parent chain logic.\n * For multiple matches in same document: finds common ancestor and reconstructs minimal subtree.\n */\n async selectChunks(\n library: string,\n version: string,\n initialChunks: DbPageChunk[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk[]> {\n if (initialChunks.length === 0) {\n return [];\n }\n\n try {\n // Group chunks by document URL\n const chunksByDocument = new Map<string, DbPageChunk[]>();\n for (const chunk of initialChunks) {\n const url = chunk.url;\n if (!chunksByDocument.has(url)) {\n chunksByDocument.set(url, []);\n }\n chunksByDocument.get(url)?.push(chunk);\n }\n\n const allChunkIds = new Set<string>();\n\n // Process each document group\n for (const [_url, documentChunks] of Array.from(chunksByDocument.entries())) {\n if (documentChunks.length === 1) {\n // Single match: reconstruct complete structural subtree containing the match\n const matched = documentChunks[0];\n\n // Find nearest structural ancestor (class / interface / enum / namespace, etc.)\n const structuralAncestor =\n (await this.findStructuralAncestor(\n library,\n version,\n matched,\n documentStore,\n )) ?? matched;\n\n // If no structural ancestor was found (e.g. we matched a deeply nested anonymous or inner function),\n // attempt to promote to the top-level container represented by the first path element.\n // Example: path ['applyMigrations','overallTransaction','overallTransaction','<anonymous_arrow>']\n // We want to reconstruct the whole top-level function 'applyMigrations', not just the arrow body.\n let promotedAncestor = structuralAncestor;\n try {\n const path = (matched.metadata.path as string[]) || [];\n if (promotedAncestor === matched && path.length > 0) {\n const topLevelPath = [path[0]];\n const containerIds = await this.findContainerChunks(\n library,\n version,\n matched,\n topLevelPath,\n documentStore,\n );\n if (containerIds.length > 0) {\n const topChunks = await documentStore.findChunksByIds(library, version, [\n containerIds[0],\n ]);\n if (topChunks.length > 0) {\n promotedAncestor = topChunks[0];\n }\n }\n }\n } catch (e) {\n logger.warn(\n `Top-level function promotion failed for chunk ${matched.id}: ${e}`,\n );\n }\n\n // IMPORTANT: Always include the original matched chunk first\n allChunkIds.add(matched.id);\n\n // Use promoted ancestor (may still be the original matched chunk if promotion not applicable)\n const ancestorParentChain = await this.walkToRoot(\n library,\n version,\n promotedAncestor,\n documentStore,\n );\n for (const id of ancestorParentChain) {\n allChunkIds.add(id);\n }\n\n // Add full subtree of the structural ancestor (ensures full class / container reconstruction)\n const subtreeIds = await this.findSubtreeChunks(\n library,\n version,\n promotedAncestor,\n documentStore,\n );\n for (const id of subtreeIds) {\n allChunkIds.add(id);\n }\n } else {\n // Multiple matches: use selective subtree reassembly\n // IMPORTANT: Always include all original matched chunks first\n for (const matched of documentChunks) {\n allChunkIds.add(matched.id);\n }\n\n const subtreeIds = await this.selectSubtreeChunks(\n library,\n version,\n documentChunks,\n documentStore,\n );\n for (const id of subtreeIds) {\n allChunkIds.add(id);\n }\n }\n }\n\n // Fetch all chunks in proper sort order\n const chunkIds = Array.from(allChunkIds);\n const chunks = await documentStore.findChunksByIds(library, version, chunkIds);\n\n return chunks;\n } catch (error) {\n // Fallback to simpler selection if parent chain walking fails\n logger.warn(\n `Hierarchical parent chain walking failed, falling back to basic selection: ${error}`,\n );\n return this.fallbackSelection(library, version, initialChunks, documentStore);\n }\n }\n\n /**\n * Assembles chunks using simple concatenation.\n * Relies on splitter concatenation guarantees - chunks are designed to join seamlessly.\n */\n assembleContent(chunks: DbPageChunk[], debug = false): string {\n if (debug) {\n return chunks\n .map(\n (chunk) =>\n `=== #${chunk.id} ${chunk.metadata.path?.join(\"/\")} [${chunk.metadata.level}] ===\\n` +\n chunk.content,\n )\n .join(\"\");\n }\n // Production/default: simple concatenation leveraging splitter guarantees.\n return chunks.map((chunk) => chunk.content).join(\"\");\n }\n\n /**\n * Walks up the parent hierarchy from a chunk to collect the complete parent chain.\n * Includes the chunk itself and every parent until reaching the root.\n * Protected against circular references and infinite loops.\n *\n * Handles hierarchical gaps by attempting to find ancestors at progressively shorter\n * path lengths when direct parent lookup fails (e.g., when intermediate chunks\n * have been merged or are missing).\n */\n private async walkToRoot(\n library: string,\n version: string,\n chunk: DbPageChunk,\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const chainIds: string[] = [];\n const visited = new Set<string>();\n let currentChunk: DbPageChunk | null = chunk;\n const { maxParentChainDepth } = this.config.assembly; // Safety limit to prevent runaway loops\n let depth = 0;\n\n // Walk up parent chain until we reach the root\n while (currentChunk && depth < maxParentChainDepth) {\n const currentId = currentChunk.id;\n\n // Check for circular references\n if (visited.has(currentId)) {\n logger.warn(`Circular reference detected in parent chain for chunk ${currentId}`);\n break;\n }\n\n visited.add(currentId);\n chainIds.push(currentId);\n depth++;\n\n // Try normal parent lookup first\n let parentChunk = await documentStore.findParentChunk(library, version, currentId);\n\n // If no direct parent found, try gap-aware ancestor search\n if (!parentChunk) {\n parentChunk = await this.findAncestorWithGaps(\n library,\n version,\n currentChunk.url,\n currentChunk.metadata.path ?? [],\n documentStore,\n );\n }\n\n currentChunk = parentChunk;\n }\n\n if (depth >= maxParentChainDepth) {\n logger.warn(\n `Maximum parent chain depth (${maxParentChainDepth}) reached for chunk ${chunk.id}`,\n );\n }\n\n return chainIds;\n }\n\n /**\n * Attempts to find ancestors when there are gaps in the hierarchy.\n * Tries progressively shorter path prefixes to find existing ancestor chunks.\n */\n private async findAncestorWithGaps(\n library: string,\n version: string,\n url: string,\n path: string[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk | null> {\n if (path.length <= 1) {\n return null; // Already at or near root\n }\n\n // Try progressively shorter path prefixes to find existing ancestors\n // Start from immediate parent and work backwards to root\n for (let pathLength = path.length - 1; pathLength > 0; pathLength--) {\n const ancestorPath = path.slice(0, pathLength);\n\n try {\n // Search for chunks that have this exact path in the same document\n const potentialAncestors = await this.findChunksByPathPrefix(\n library,\n version,\n url,\n ancestorPath,\n documentStore,\n );\n\n if (potentialAncestors.length > 0) {\n // Return the first matching ancestor found\n return potentialAncestors[0];\n }\n } catch (error) {\n logger.debug(\n `Failed to find ancestor with path ${ancestorPath.join(\"/\")}: ${error}`,\n );\n // Continue trying shorter paths\n }\n }\n\n return null; // No ancestors found\n }\n\n /**\n * Finds chunks that have an exact path match or are prefixes of the given path.\n * This is a more flexible version of findChunksByPath that can handle gaps.\n */\n private async findChunksByPathPrefix(\n library: string,\n version: string,\n url: string,\n targetPath: string[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk[]> {\n try {\n // Get all chunks from the same document URL\n const allChunks = await documentStore.findChunksByUrl(library, version, url);\n\n if (allChunks.length === 0) {\n return [];\n }\n\n const matchingChunks = allChunks.filter((chunk) => {\n const chunkPath = (chunk.metadata.path as string[]) || [];\n const chunkUrl = chunk.url;\n\n // Must be in the same document\n if (chunkUrl !== url) return false;\n\n // Path must match exactly\n if (chunkPath.length !== targetPath.length) return false;\n\n // All path elements must match\n return chunkPath.every((part, index) => part === targetPath[index]);\n });\n\n return matchingChunks;\n } catch (error) {\n logger.warn(`Error in findChunksByPathPrefix: ${error}`);\n return [];\n }\n }\n\n /**\n * Finds the nearest structural ancestor (types includes \"structural\") for a chunk.\n * If none exists (e.g. the matched chunk itself is structural or at top), returns null.\n */\n private async findStructuralAncestor(\n library: string,\n version: string,\n chunk: DbPageChunk,\n documentStore: DocumentStore,\n ): Promise<DbPageChunk | null> {\n let current: DbPageChunk | null = chunk;\n\n // If current is structural already, return it\n const isStructural = (c: DbPageChunk | null) =>\n !!c && Array.isArray(c.metadata?.types) && c.metadata.types.includes(\"structural\");\n\n if (isStructural(current)) {\n return current;\n }\n\n // Walk up until we find a structural ancestor\n while (true) {\n const parent = await documentStore.findParentChunk(library, version, current.id);\n if (!parent) {\n return null;\n }\n if (isStructural(parent)) {\n return parent;\n }\n current = parent;\n }\n }\n\n /**\n * Selects chunks for selective subtree reassembly when multiple matches exist in the same document.\n * Finds the common ancestor and reconstructs only the relevant subtrees.\n */\n private async selectSubtreeChunks(\n library: string,\n version: string,\n documentChunks: DbPageChunk[],\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const chunkIds = new Set<string>();\n\n // Find common ancestor path\n const commonAncestorPath = this.findCommonAncestorPath(documentChunks);\n\n if (commonAncestorPath.length === 0) {\n // No common ancestor found, fall back to individual parent chains\n logger.warn(\n \"No common ancestor found for multiple matches, using individual parent chains\",\n );\n for (const chunk of documentChunks) {\n const parentChain = await this.walkToRoot(library, version, chunk, documentStore);\n for (const id of parentChain) {\n chunkIds.add(id);\n }\n }\n return Array.from(chunkIds);\n }\n\n // Find container chunks (opening/closing) for the common ancestor\n const containerIds = await this.findContainerChunks(\n library,\n version,\n documentChunks[0], // Use first chunk to get document URL\n commonAncestorPath,\n documentStore,\n );\n for (const id of containerIds) {\n chunkIds.add(id);\n }\n\n // For each matched chunk, include its full subtree\n for (const chunk of documentChunks) {\n const subtreeIds = await this.findSubtreeChunks(\n library,\n version,\n chunk,\n documentStore,\n );\n for (const id of subtreeIds) {\n chunkIds.add(id);\n }\n }\n\n return Array.from(chunkIds);\n }\n\n /**\n * Finds the common ancestor path from a list of chunks by finding the longest common prefix.\n */\n private findCommonAncestorPath(chunks: DbPageChunk[]): string[] {\n if (chunks.length === 0) return [];\n if (chunks.length === 1) return (chunks[0].metadata.path as string[]) ?? [];\n\n const paths = chunks.map((chunk) => (chunk.metadata.path as string[]) ?? []);\n\n if (paths.length === 0) return [];\n\n // Find the longest common prefix\n const minLength = Math.min(...paths.map((path) => path.length));\n const commonPrefix: string[] = [];\n\n for (let i = 0; i < minLength; i++) {\n const currentElement = paths[0][i];\n if (paths.every((path) => path[i] === currentElement)) {\n commonPrefix.push(currentElement);\n } else {\n break;\n }\n }\n\n return commonPrefix;\n }\n\n /**\n * Finds the container chunks (opening/closing) for a given ancestor path.\n */\n private async findContainerChunks(\n library: string,\n version: string,\n referenceChunk: DbPageChunk,\n ancestorPath: string[],\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const containerIds: string[] = [];\n\n // Try to find the opening chunk for this ancestor path\n try {\n // Query for chunks with the exact ancestor path\n const ancestorChunks = await this.findChunksByExactPath(\n library,\n version,\n referenceChunk.url,\n ancestorPath,\n documentStore,\n );\n\n for (const chunk of ancestorChunks) {\n containerIds.push(chunk.id);\n }\n } catch (error) {\n logger.warn(\n `Failed to find container chunks for path ${ancestorPath.join(\"/\")}: ${error}`,\n );\n }\n\n return containerIds;\n }\n\n /**\n * Finds all chunks with an exact path match within a specific document.\n * More efficient than searching across all chunks by first filtering by URL.\n */\n private async findChunksByExactPath(\n library: string,\n version: string,\n url: string,\n path: string[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk[]> {\n try {\n // For root path, return empty - no specific chunks to find\n if (path.length === 0) {\n logger.debug(\"Root path requested - no chunks found\");\n return [];\n }\n\n // First, get all chunks from the specific document URL (much more efficient)\n const allChunks = await documentStore.findChunksByUrl(library, version, url);\n\n if (allChunks.length === 0) {\n return [];\n }\n\n // Filter in memory for chunks with exact path match\n const matchingChunks = allChunks.filter((chunk) => {\n const chunkPath = (chunk.metadata.path as string[]) ?? [];\n\n // Path must match exactly\n if (chunkPath.length !== path.length) return false;\n\n // All path elements must match\n return chunkPath.every((part, index) => part === path[index]);\n });\n\n logger.debug(\n `Found ${matchingChunks.length} chunks for exact path: ${path.join(\"/\")}`,\n );\n return matchingChunks;\n } catch (error) {\n logger.warn(`Error finding chunks for exact path ${path.join(\"/\")}: ${error}`);\n return [];\n }\n }\n\n /**\n * Finds all chunks in the subtree rooted at the given chunk.\n */\n private async findSubtreeChunks(\n library: string,\n version: string,\n rootChunk: DbPageChunk,\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const subtreeIds: string[] = [];\n const visited = new Set<string>();\n const queue: DbPageChunk[] = [rootChunk];\n\n while (queue.length > 0) {\n // biome-ignore lint/style/noNonNullAssertion: this is safe due to the while condition\n const currentChunk = queue.shift()!;\n const currentId = currentChunk.id;\n\n if (visited.has(currentId)) continue;\n visited.add(currentId);\n subtreeIds.push(currentId);\n\n // Add all children to the queue\n try {\n const children = await documentStore.findChildChunks(\n library,\n version,\n currentId,\n 1000,\n ); // Large limit\n queue.push(...children);\n } catch (error) {\n logger.warn(`Failed to find children for chunk ${currentId}: ${error}`);\n }\n }\n\n return subtreeIds;\n }\n\n /**\n * Fallback selection method when parent chain walking fails.\n * Uses a simplified approach similar to MarkdownAssemblyStrategy but more conservative.\n */\n private async fallbackSelection(\n library: string,\n version: string,\n initialChunks: DbPageChunk[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk[]> {\n const chunkIds = new Set<string>();\n\n // Just include the initial chunks and their immediate parents/children\n for (const chunk of initialChunks) {\n const id = chunk.id;\n chunkIds.add(id);\n\n // Add parent for context\n const parent = await documentStore.findParentChunk(library, version, id);\n if (parent) {\n chunkIds.add(parent.id);\n }\n\n // Add direct children (limited)\n try {\n const children = await documentStore.findChildChunks(library, version, id, 3);\n for (const child of children) {\n chunkIds.add(child.id as string);\n }\n } catch (error) {\n logger.warn(`Failed to find children for chunk ${id}: ${error}`);\n }\n }\n\n const chunks = await documentStore.findChunksByIds(\n library,\n version,\n Array.from(chunkIds),\n );\n\n return chunks;\n }\n}\n","import type { AppConfig } from \"../../../utils/config\";\nimport { MimeTypeUtils } from \"../../../utils/mimeTypeUtils\";\nimport type { DocumentStore } from \"../../DocumentStore\";\nimport type { DbPageChunk } from \"../../types\";\nimport type { ContentAssemblyStrategy } from \"../types\";\n\n/**\n * Assembly strategy that preserves the current behavior for markdown and text content.\n *\n * Uses broad context expansion (parents, siblings, children) and simple \"\\n\\n\" joining.\n * This strategy is optimized for prose content where broader context enhances understanding.\n */\nexport class MarkdownAssemblyStrategy implements ContentAssemblyStrategy {\n constructor(private config: AppConfig) {}\n\n /**\n * Determines if this strategy can handle the given content type.\n * Handles markdown, HTML, plain text, and serves as fallback for unknown types.\n */\n canHandle(mimeType?: string): boolean {\n // Handle undefined/unknown MIME types as fallback\n if (!mimeType) {\n return true;\n }\n\n // First, check if it's a structured type that should be handled by HierarchicalAssemblyStrategy\n if (MimeTypeUtils.isSourceCode(mimeType) || MimeTypeUtils.isJson(mimeType)) {\n return false;\n }\n\n // Handle markdown content\n if (MimeTypeUtils.isMarkdown(mimeType)) {\n return true;\n }\n\n // Handle HTML content\n if (MimeTypeUtils.isHtml(mimeType)) {\n return true;\n }\n\n // Handle plain text content\n if (MimeTypeUtils.isText(mimeType)) {\n return true;\n }\n\n // Accept as fallback for truly unknown types\n return true;\n }\n\n /**\n * Selects chunks using the current context expansion logic.\n * This replicates the existing behavior from DocumentRetrieverService.getRelatedChunkIds().\n */\n async selectChunks(\n library: string,\n version: string,\n initialChunks: DbPageChunk[],\n documentStore: DocumentStore,\n ): Promise<DbPageChunk[]> {\n const allChunkIds = new Set<string>();\n\n // Process all initial chunks in parallel to gather related chunk IDs\n const relatedIdsPromises = initialChunks.map((doc) =>\n this.getRelatedChunkIds(library, version, doc, documentStore),\n );\n\n const relatedIdsResults = await Promise.all(relatedIdsPromises);\n\n // Add all related IDs to the set (automatically deduplicates)\n for (const relatedIds of relatedIdsResults) {\n for (const id of relatedIds) {\n allChunkIds.add(id);\n }\n }\n\n // Fetch all chunks and return them in sort_order\n const chunkIds = Array.from(allChunkIds);\n const chunks = await documentStore.findChunksByIds(library, version, chunkIds);\n\n return chunks; // Already sorted by sort_order in findChunksByIds\n }\n\n /**\n * Assembles chunks using simple \"\\n\\n\" joining (current behavior).\n */\n assembleContent(chunks: DbPageChunk[]): string {\n return chunks.map((chunk) => chunk.content).join(\"\\n\\n\");\n }\n\n /**\n * Collects related chunk IDs for a single chunk using current context expansion logic.\n * This is a direct port of the logic from DocumentRetrieverService.getRelatedChunkIds().\n */\n private async getRelatedChunkIds(\n library: string,\n version: string,\n doc: DbPageChunk,\n documentStore: DocumentStore,\n ): Promise<Set<string>> {\n const id = doc.id;\n const relatedIds = new Set<string>();\n const { childLimit, precedingSiblingsLimit, subsequentSiblingsLimit } =\n this.config.assembly;\n\n // Add the original chunk\n relatedIds.add(id);\n\n // Parent\n const parent = await documentStore.findParentChunk(library, version, id);\n if (parent) {\n relatedIds.add(parent.id);\n }\n\n // Preceding Siblings\n const precedingSiblings = await documentStore.findPrecedingSiblingChunks(\n library,\n version,\n id,\n precedingSiblingsLimit,\n );\n for (const sib of precedingSiblings) {\n relatedIds.add(sib.id);\n }\n\n // Child Chunks\n const childChunks = await documentStore.findChildChunks(\n library,\n version,\n id,\n childLimit,\n );\n for (const child of childChunks) {\n relatedIds.add(child.id);\n }\n\n // Subsequent Siblings\n const subsequentSiblings = await documentStore.findSubsequentSiblingChunks(\n library,\n version,\n id,\n subsequentSiblingsLimit,\n );\n for (const sib of subsequentSiblings) {\n relatedIds.add(sib.id);\n }\n\n return relatedIds;\n }\n}\n","import type { AppConfig } from \"../../utils/config\";\nimport { HierarchicalAssemblyStrategy } from \"./strategies/HierarchicalAssemblyStrategy\";\nimport { MarkdownAssemblyStrategy } from \"./strategies/MarkdownAssemblyStrategy\";\nimport type { ContentAssemblyStrategy } from \"./types\";\n\n/**\n * Creates the appropriate assembly strategy based on content MIME type.\n *\n * @param mimeType The MIME type of the content (optional)\n * @param config Application configuration\n * @returns The appropriate strategy instance\n */\nexport function createContentAssemblyStrategy(\n mimeType: string | null | undefined,\n config: AppConfig,\n): ContentAssemblyStrategy {\n // Default to MarkdownAssemblyStrategy for unknown or missing MIME types\n if (!mimeType) {\n return new MarkdownAssemblyStrategy(config); // Markdown strategy doesn't need config currently\n }\n\n // Try each strategy to see which one can handle the content type\n const strategies = [\n new HierarchicalAssemblyStrategy(config),\n new MarkdownAssemblyStrategy(config),\n ];\n\n for (const strategy of strategies) {\n if (strategy.canHandle(mimeType)) {\n return strategy;\n }\n }\n\n // Default fallback to MarkdownAssemblyStrategy\n return new MarkdownAssemblyStrategy(config);\n}\n","import type { AppConfig } from \"../utils/config\";\nimport { createContentAssemblyStrategy } from \"./assembly/ContentAssemblyStrategyFactory\";\nimport type { DocumentStore } from \"./DocumentStore\";\nimport type { DbChunkRank, DbPageChunk, StoreSearchResult } from \"./types\";\n\nexport class DocumentRetrieverService {\n private documentStore: DocumentStore;\n private config: AppConfig;\n\n constructor(documentStore: DocumentStore, config: AppConfig) {\n this.documentStore = documentStore;\n this.config = config;\n }\n\n /**\n * Searches for documents and expands the context around the matches using content-type-aware strategies.\n * @param library The library name.\n * @param version The library version.\n * @param query The search query.\n * @param limit The optional limit for the initial search results.\n * @returns An array of search results with content assembled according to content type.\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 if (initialResults.length === 0) {\n return [];\n }\n\n // Group initial results by URL\n const resultsByUrl = this.groupResultsByUrl(initialResults);\n\n // Process each URL group with appropriate strategy\n const results: StoreSearchResult[] = [];\n for (const [url, urlResults] of resultsByUrl.entries()) {\n // Cluster chunks based on distance\n const clusters = this.clusterChunksByDistance(urlResults);\n\n // Process each cluster as a separate result\n for (const cluster of clusters) {\n const result = await this.processUrlGroup(\n library,\n normalizedVersion,\n url,\n cluster,\n );\n results.push(result);\n }\n }\n\n // Sort all results by score descending\n // This ensures that if a highly relevant chunk was split from a less relevant one,\n // the highly relevant one appears first in the final list.\n results.sort((a, b) => (b.score ?? 0) - (a.score ?? 0));\n\n return results;\n }\n\n /**\n * Groups search results by URL.\n */\n private groupResultsByUrl(\n results: (DbPageChunk & DbChunkRank)[],\n ): Map<string, (DbPageChunk & DbChunkRank)[]> {\n const resultsByUrl = new Map<string, (DbPageChunk & DbChunkRank)[]>();\n\n for (const result of results) {\n const url = result.url;\n if (!resultsByUrl.has(url)) {\n resultsByUrl.set(url, []);\n }\n const urlResults = resultsByUrl.get(url);\n if (urlResults) {\n urlResults.push(result);\n }\n }\n\n return resultsByUrl;\n }\n\n /**\n * Processes a group of search results from the same URL using appropriate strategy.\n */\n private async processUrlGroup(\n library: string,\n version: string,\n url: string,\n initialChunks: (DbPageChunk & DbChunkRank)[],\n ): Promise<StoreSearchResult> {\n // Extract processed and source MIME types from page-level fields.\n // Convert null to undefined for consistency.\n const mimeType =\n initialChunks.length > 0 ? (initialChunks[0].content_type ?? undefined) : undefined;\n const sourceMimeType =\n initialChunks.length > 0\n ? (initialChunks[0].source_content_type ?? undefined)\n : undefined;\n\n // Find the maximum score from the initial results\n const maxScore = Math.max(...initialChunks.map((chunk) => chunk.score));\n\n // Create appropriate assembly strategy based on content type\n const strategy = createContentAssemblyStrategy(mimeType, this.config);\n\n // Use strategy to select and assemble chunks\n const selectedChunks = await strategy.selectChunks(\n library,\n version,\n initialChunks,\n this.documentStore,\n );\n\n const content = strategy.assembleContent(selectedChunks);\n\n return {\n url,\n content,\n score: maxScore,\n mimeType,\n sourceMimeType,\n };\n }\n\n /**\n * Clusters chunks based on their sort_order distance.\n * Chunks within maxChunkDistance of each other are grouped together.\n *\n * @param chunks The list of chunks to cluster (must be from the same URL).\n * @returns An array of chunk clusters, where each cluster is an array of chunks.\n */\n private clusterChunksByDistance(\n chunks: (DbPageChunk & DbChunkRank)[],\n ): (DbPageChunk & DbChunkRank)[][] {\n if (chunks.length === 0) return [];\n if (chunks.length === 1) return [chunks];\n\n // Sort chunks by sort_order, then by id for deterministic stability\n const sortedChunks = [...chunks].sort((a, b) => {\n const diff = a.sort_order - b.sort_order;\n if (diff !== 0) return diff;\n return a.id.localeCompare(b.id);\n });\n\n const clusters: (DbPageChunk & DbChunkRank)[][] = [];\n let currentCluster: (DbPageChunk & DbChunkRank)[] = [sortedChunks[0]];\n // Ensure maxChunkDistance is non-negative\n const maxChunkDistance = Math.max(0, this.config.assembly.maxChunkDistance);\n\n for (let i = 1; i < sortedChunks.length; i++) {\n const currentChunk = sortedChunks[i];\n const previousChunk = sortedChunks[i - 1];\n\n // Check distance between current and previous chunk\n const distance = currentChunk.sort_order - previousChunk.sort_order;\n\n if (distance <= maxChunkDistance) {\n // Close enough - add to current cluster\n currentCluster.push(currentChunk);\n } else {\n // Too far - start new cluster\n clusters.push(currentCluster);\n currentCluster = [currentChunk];\n }\n }\n\n // Add the last cluster\n clusters.push(currentCluster);\n\n return clusters;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { logger } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport { StoreError } from \"./errors\";\n\n// Construct the absolute path to the migrations directory using the project root\nconst MIGRATIONS_DIR = path.join(getProjectRoot(), \"db\", \"migrations\");\nconst MIGRATIONS_TABLE = \"_schema_migrations\";\n\n/**\n * Ensures the migration tracking table exists in the database.\n * @param db The database instance.\n */\nfunction ensureMigrationsTable(db: Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS ${MIGRATIONS_TABLE} (\n id TEXT PRIMARY KEY,\n applied_at DATETIME DEFAULT CURRENT_TIMESTAMP\n );\n `);\n}\n\n/**\n * Retrieves the set of already applied migration IDs (filenames) from the tracking table.\n * @param db The database instance.\n * @returns A Set containing the IDs of applied migrations.\n */\nfunction getAppliedMigrations(db: Database): Set<string> {\n const stmt = db.prepare(`SELECT id FROM ${MIGRATIONS_TABLE}`);\n const rows = stmt.all() as Array<{ id: string }>;\n return new Set(rows.map((row) => row.id));\n}\n\n/**\n * Applies pending database migrations found in the migrations directory.\n * Migrations are expected to be .sql files with sequential prefixes (e.g., 001-, 002-).\n * It tracks applied migrations in the _schema_migrations table.\n *\n * @param db The better-sqlite3 database instance.\n * @param options Optional runtime configuration.\n * @throws {StoreError} If any migration fails.\n */\nexport async function applyMigrations(\n db: Database,\n options?: {\n maxRetries?: number;\n retryDelayMs?: number;\n },\n): Promise<void> {\n const maxRetries = options?.maxRetries ?? 5;\n const retryDelayMs = options?.retryDelayMs ?? 300;\n // Apply performance optimizations for large dataset migrations\n try {\n db.pragma(\"journal_mode = OFF\");\n db.pragma(\"synchronous = OFF\");\n db.pragma(\"mmap_size = 268435456\"); // 256MB memory mapping\n db.pragma(\"cache_size = -64000\"); // 64MB cache (default is ~2MB)\n db.pragma(\"temp_store = MEMORY\"); // Store temporary data in memory\n logger.debug(\"Applied performance optimizations for migration\");\n } catch (_error) {\n logger.warn(\"⚠️ Could not apply all performance optimizations for migration\");\n }\n\n const overallTransaction = db.transaction(() => {\n logger.debug(\"Checking database migrations...\");\n ensureMigrationsTable(db);\n const appliedMigrations = getAppliedMigrations(db);\n\n if (!fs.existsSync(MIGRATIONS_DIR)) {\n throw new StoreError(\"Migrations directory not found\");\n }\n\n const migrationFiles = fs\n .readdirSync(MIGRATIONS_DIR)\n .filter((file) => file.endsWith(\".sql\"))\n .sort(); // Sort alphabetically, relying on naming convention (001-, 002-)\n\n const pendingMigrations = migrationFiles.filter(\n (filename) => !appliedMigrations.has(filename),\n );\n\n if (pendingMigrations.length > 0) {\n logger.info(`🔄 Applying ${pendingMigrations.length} database migration(s)...`);\n }\n\n let appliedCount = 0;\n for (const filename of pendingMigrations) {\n logger.debug(`Applying migration: ${filename}`);\n const filePath = path.join(MIGRATIONS_DIR, filename);\n const sql = fs.readFileSync(filePath, \"utf8\");\n\n // Execute migration and record it directly within the overall transaction\n try {\n db.exec(sql);\n const insertStmt = db.prepare(`INSERT INTO ${MIGRATIONS_TABLE} (id) VALUES (?)`);\n insertStmt.run(filename);\n logger.debug(`Applied migration: ${filename}`);\n appliedCount++;\n } catch (error) {\n logger.error(`❌ Failed to apply migration: ${filename} - ${error}`);\n // Re-throw to ensure the overall transaction rolls back\n throw new StoreError(`Migration failed: ${filename}`, error);\n }\n }\n\n if (appliedCount > 0) {\n logger.info(`✅ Successfully applied ${appliedCount} migration(s)`);\n } else {\n logger.debug(\"Database schema is up to date\");\n }\n\n // Return the count of applied migrations so we know if VACUUM is needed\n return appliedCount;\n });\n\n let retries = 0;\n let appliedMigrationsCount = 0;\n\n while (true) {\n try {\n // Start a single IMMEDIATE transaction for the entire migration process\n appliedMigrationsCount = overallTransaction.immediate(); // Execute the encompassing transaction\n logger.debug(\"Database migrations completed successfully\");\n\n // Only run VACUUM if migrations were actually applied\n if (appliedMigrationsCount > 0) {\n try {\n logger.debug(\n `Running VACUUM after applying ${appliedMigrationsCount} migration(s)...`,\n );\n db.exec(\"VACUUM\");\n logger.debug(\"Database vacuum completed successfully\");\n } catch (error) {\n logger.warn(`⚠️ Could not vacuum database after migrations: ${error}`);\n // Don't fail the migration process if vacuum fails\n }\n } else {\n logger.debug(\"Skipping VACUUM - no migrations were applied\");\n }\n\n break; // Success\n } catch (error) {\n // biome-ignore lint/suspicious/noExplicitAny: error can be any\n if ((error as any)?.code === \"SQLITE_BUSY\" && retries < maxRetries) {\n retries++;\n logger.warn(\n `⚠️ Migrations busy (SQLITE_BUSY), retrying attempt ${retries}/${maxRetries} in ${retryDelayMs}ms...`,\n );\n await new Promise((resolve) => setTimeout(resolve, retryDelayMs));\n } else {\n // biome-ignore lint/suspicious/noExplicitAny: error can be any\n if ((error as any)?.code === \"SQLITE_BUSY\") {\n logger.error(\n `❌ Migrations still busy after ${maxRetries} retries. Giving up: ${error}`,\n );\n }\n // Ensure StoreError is thrown for consistent handling\n if (error instanceof StoreError) {\n throw error;\n }\n throw new StoreError(\"Failed during migration process\", error);\n }\n }\n }\n\n // Configure production-ready settings after migrations\n try {\n // Enable WAL mode for better concurrency (allows readers while writing)\n db.pragma(\"journal_mode = WAL\");\n\n // Configure WAL autocheckpoint to prevent unbounded growth\n db.pragma(\"wal_autocheckpoint = 1000\"); // Checkpoint every 1000 pages (~4MB)\n\n // Set busy timeout for better handling of concurrent access\n db.pragma(\"busy_timeout = 30000\"); // 30 seconds\n\n // Enable foreign key constraints for data integrity\n db.pragma(\"foreign_keys = ON\");\n\n // Set synchronous to NORMAL for good balance of safety and performance\n db.pragma(\"synchronous = NORMAL\");\n\n logger.debug(\n \"Applied production database configuration (WAL mode, autocheckpoint, foreign keys, busy timeout)\",\n );\n } catch (_error) {\n logger.warn(\"⚠️ Could not apply all production database settings\");\n }\n}\n","import type { ScrapeMode } from \"../scraper/types\";\n\n/**\n * Database page record type matching the pages table schema\n */\nexport interface DbPage {\n id: number;\n version_id: number;\n url: string;\n title: string | null;\n etag: string | null;\n last_modified: string | null;\n source_content_type: string | null;\n content_type: string | null;\n depth: number | null;\n created_at: string;\n updated_at: string;\n}\n\n/**\n * Chunk-level metadata stored with each document chunk.\n * Contains hierarchical information about the chunk's position within the page.\n */\nexport interface DbChunkMetadata {\n level?: number; // Hierarchical level in document\n path?: string[]; // Hierarchical path in document\n // TODO: Check if `types` is properly used\n types?: string[]; // Types of content in this chunk (e.g., \"text\", \"code\", \"table\")\n // TODO: Enable additional metadata fields again once we have a clear schema for what metadata we want to store with each chunk.\n // Allow for additional chunk-specific metadata\n // [key: string]: unknown;\n}\n\n/**\n * Database document record type matching the documents table schema\n */\nexport interface DbChunk {\n id: string;\n page_id: number; // Foreign key to pages table\n content: string;\n metadata: DbChunkMetadata; // Chunk-specific metadata (level, path, etc.)\n sort_order: number;\n embedding: Buffer | null; // Binary blob for embeddings\n created_at: string;\n score: number | null; // Added during search queries\n}\n\n/**\n * Represents the result of a JOIN between the documents and pages tables.\n * It includes all fields from a document chunk plus the relevant page-level metadata.\n */\nexport interface DbPageChunk extends DbChunk {\n url: string;\n title?: string | null;\n source_content_type?: string | null;\n content_type?: string | null;\n}\n\n/**\n * Represents the ranking information for a search result, including both\n * vector and full-text search ranks.\n */\nexport interface DbChunkRank {\n score: number;\n vec_rank?: number;\n fts_rank?: number;\n}\n\n/**\n * Utility type for handling SQLite query results that may be undefined\n */\nexport type DbQueryResult<T> = T | undefined;\n\n/**\n * Search result type returned by the DocumentRetrieverService\n */\nexport interface StoreSearchResult {\n url: string;\n content: string;\n score: number | null;\n mimeType?: string | null;\n sourceMimeType?: string | null;\n}\n\n/**\n * Represents the possible states of a version's indexing status.\n * These statuses are stored in the database and persist across server restarts.\n */\nexport enum VersionStatus {\n NOT_INDEXED = \"not_indexed\", // Version created but never indexed\n QUEUED = \"queued\", // Waiting in pipeline queue\n RUNNING = \"running\", // Currently being indexed\n COMPLETED = \"completed\", // Successfully indexed\n FAILED = \"failed\", // Indexing failed\n CANCELLED = \"cancelled\", // Indexing was cancelled\n UPDATING = \"updating\", // Re-indexing existing version\n}\n\n/**\n * Scraper options stored with each version for reproducible indexing.\n * Excludes runtime-only fields like signal, library, version, and url.\n */\nexport interface VersionScraperOptions {\n // Core scraping parameters\n maxPages?: number;\n maxDepth?: number;\n scope?: \"subpages\" | \"hostname\" | \"domain\";\n followRedirects?: boolean;\n maxConcurrency?: number;\n ignoreErrors?: boolean;\n\n // Content filtering\n excludeSelectors?: string[];\n includePatterns?: string[];\n excludePatterns?: string[];\n\n // Processing options\n scrapeMode?: ScrapeMode;\n headers?: Record<string, string>;\n}\n\n/**\n * Unified return type for retrieving stored scraping configuration for a version.\n * Includes the original source URL and the parsed scraper options used during indexing.\n */\nexport interface StoredScraperOptions {\n sourceUrl: string;\n options: VersionScraperOptions;\n}\n\n/**\n * Alias for the unified scraping configuration returned by the service.\n * Prefer ScraperConfig in new code; StoredScraperOptions remains for backward-compat.\n */\nexport type ScraperConfig = StoredScraperOptions;\n\n/**\n * Canonical reference to a library version in the domain layer.\n * Version uses empty string for unversioned content.\n */\nexport interface VersionRef {\n library: string;\n version: string; // empty string for unversioned\n}\n\n/** Normalize a VersionRef (lowercase, trim; empty string for unversioned). */\nexport function normalizeVersionRef(ref: VersionRef): VersionRef {\n return {\n library: ref.library.trim().toLowerCase(),\n version: (ref.version ?? \"\").trim().toLowerCase(),\n };\n}\n\n/**\n * Summary of a specific version for API/UI consumption.\n * Aggregates status, progress and document statistics.\n */\nexport interface VersionSummary {\n id: number;\n ref: VersionRef;\n status: VersionStatus;\n /**\n * Progress information while a version is being indexed.\n * Omitted once status is COMPLETED to reduce noise.\n */\n progress?: { pages: number; maxPages: number };\n counts: { documents: number; uniqueUrls: number };\n indexedAt: string | null; // ISO 8601\n sourceUrl?: string | null;\n}\n\n/**\n * Summary of a library and its versions for API/UI consumption.\n */\nexport interface LibrarySummary {\n library: string;\n versions: VersionSummary[];\n}\n\n/**\n * Database version record type matching the versions table schema.\n * Uses snake_case naming to match database column names.\n */\nexport interface DbVersion {\n id: number;\n library_id: number;\n name: string | null; // NULL for unversioned content\n created_at: string;\n\n // Status tracking fields (added in migration 005)\n status: VersionStatus;\n progress_pages: number;\n progress_max_pages: number;\n error_message: string | null;\n started_at: string | null; // When the indexing job started\n updated_at: string;\n\n // Scraper options fields (added in migration 006)\n source_url: string | null; // Original scraping URL\n scraper_options: string | null; // JSON string of VersionScraperOptions\n}\n\n/**\n * Version record with library name included from JOIN query.\n * Used when we need both version data and the associated library name.\n */\nexport interface DbVersionWithLibrary extends DbVersion {\n library_name: string;\n}\n\n/**\n * Helper function to convert NULL version name to empty string for API compatibility.\n * Database stores NULL for unversioned content, but APIs expect empty string.\n */\nexport function normalizeVersionName(name: string | null): string {\n return name ?? \"\";\n}\n\n/**\n * Helper function for version name normalization prior to storage.\n * Policy:\n * - Empty string represents the unversioned variant (stored as '').\n * - Names are lower-cased at call sites (see resolveLibraryAndVersionIds) to enforce\n * case-insensitive uniqueness; this function only preserves the empty-string rule.\n */\nexport function denormalizeVersionName(name: string): string {\n // Store unversioned as empty string to leverage UNIQUE(library_id, name)\n return name === \"\" ? \"\" : name;\n}\n\n/**\n * Result type for findBestVersion, indicating the best semver match\n * and whether unversioned documents exist.\n */\nexport interface FindVersionResult {\n bestMatch: string | null;\n hasUnversioned: boolean;\n}\n\n/**\n * Gets a human-readable description of a version status.\n */\nexport function getStatusDescription(status: VersionStatus): string {\n const descriptions: Record<VersionStatus, string> = {\n [VersionStatus.NOT_INDEXED]: \"Version created but not yet indexed\",\n [VersionStatus.QUEUED]: \"Waiting in queue for indexing\",\n [VersionStatus.RUNNING]: \"Currently being indexed\",\n [VersionStatus.COMPLETED]: \"Successfully indexed\",\n [VersionStatus.FAILED]: \"Indexing failed\",\n [VersionStatus.CANCELLED]: \"Indexing was cancelled\",\n [VersionStatus.UPDATING]: \"Re-indexing in progress\",\n };\n\n return descriptions[status] || \"Unknown status\";\n}\n\n/**\n * Checks if a status represents a final state (job completed).\n */\nexport function isFinalStatus(status: VersionStatus): boolean {\n return [\n VersionStatus.COMPLETED,\n VersionStatus.FAILED,\n VersionStatus.CANCELLED,\n ].includes(status);\n}\n\n/**\n * Checks if a status represents an active state (job in progress).\n */\nexport function isActiveStatus(status: VersionStatus): boolean {\n return [VersionStatus.QUEUED, VersionStatus.RUNNING, VersionStatus.UPDATING].includes(\n status,\n );\n}\n\n/**\n * Library version row returned by queryLibraryVersions.\n * Aggregates version metadata with document counts and indexing status.\n */\nexport interface DbLibraryVersion {\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;\n}\n","import type { Embeddings } from \"@langchain/core/embeddings\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport type { ScrapeResult, ScraperOptions } from \"../scraper/types\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { compareVersionsDescending } from \"../utils/version\";\nimport { applyMigrations } from \"./applyMigrations\";\nimport { EmbeddingConfig, type EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport {\n areCredentialsAvailable,\n createEmbeddingModel,\n ModelConfigurationError,\n UnsupportedProviderError,\n} from \"./embeddings/EmbeddingFactory\";\nimport { ConnectionError, DimensionError, StoreError } from \"./errors\";\nimport type { DbChunkMetadata, DbChunkRank, StoredScraperOptions } from \"./types\";\nimport {\n type DbChunk,\n type DbLibraryVersion,\n type DbPage,\n type DbPageChunk,\n type DbQueryResult,\n type DbVersion,\n type DbVersionWithLibrary,\n denormalizeVersionName,\n normalizeVersionName,\n type VersionScraperOptions,\n type VersionStatus,\n} from \"./types\";\n\ninterface RawSearchResult extends DbChunk {\n // Page fields joined from pages table\n url?: string;\n title?: string | null;\n source_content_type?: string | null;\n content_type?: string | null;\n // Search scoring fields\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 config: AppConfig;\n\n private readonly db: DatabaseType;\n private embeddings!: Embeddings;\n private readonly dbDimension: number;\n private readonly searchWeightVec: number;\n private readonly searchWeightFts: number;\n private readonly searchOverfetchFactor: number;\n private readonly vectorSearchMultiplier: number;\n private readonly splitterMaxChunkSize: number;\n private readonly embeddingBatchSize: number;\n private readonly embeddingBatchChars: number;\n private readonly embeddingInitTimeoutMs: number;\n private modelDimension!: number;\n private readonly embeddingConfig?: EmbeddingModelConfig | null;\n private isVectorSearchEnabled: boolean = false;\n\n /**\n * Returns the active embedding configuration if vector search is enabled,\n * or null if embeddings are disabled (no config provided or credentials unavailable).\n */\n getActiveEmbeddingConfig(): EmbeddingModelConfig | null {\n if (!this.isVectorSearchEnabled || !this.embeddingConfig) {\n return null;\n }\n return this.embeddingConfig;\n }\n\n private statements!: {\n getById: Database.Statement<[bigint]>;\n // Updated for new schema - documents table now uses page_id\n insertDocument: Database.Statement<[number, string, string, number]>;\n // Updated for new schema - embeddings stored directly in documents table\n insertEmbedding: Database.Statement<[string, bigint]>;\n // New statement for pages table\n insertPage: Database.Statement<\n [\n number,\n string,\n string,\n string | null,\n string | null,\n string | null,\n string | null,\n number | null,\n ]\n >;\n getPageId: Database.Statement<[number, string]>;\n deleteDocuments: Database.Statement<[string, string]>;\n deleteDocumentsByPageId: Database.Statement<[number]>;\n deletePage: Database.Statement<[number]>;\n deletePages: Database.Statement<[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 getLibraryById: Database.Statement<[number]>;\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 // Version and library deletion statements\n deleteVersionById: Database.Statement<[number]>;\n deleteLibraryById: Database.Statement<[number]>;\n countVersionsByLibraryId: Database.Statement<[number]>;\n getVersionId: Database.Statement<[string, string]>;\n getPagesByVersionId: Database.Statement<[number]>;\n };\n\n /**\n * Calculates Reciprocal Rank Fusion score for a result with configurable weights\n */\n private calculateRRF(vecRank?: number, ftsRank?: number, k = 60): number {\n let rrf = 0;\n if (vecRank !== undefined) {\n rrf += this.searchWeightVec / (k + vecRank);\n }\n if (ftsRank !== undefined) {\n rrf += this.searchWeightFts / (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, appConfig: AppConfig) {\n if (!dbPath) {\n throw new StoreError(\"Missing required database path\");\n }\n this.config = appConfig;\n this.dbDimension = this.config.embeddings.vectorDimension;\n this.searchWeightVec = this.config.search.weightVec;\n this.searchWeightFts = this.config.search.weightFts;\n this.searchOverfetchFactor = this.config.search.overfetchFactor;\n this.vectorSearchMultiplier = this.config.search.vectorMultiplier;\n this.splitterMaxChunkSize = this.config.splitter.maxChunkSize;\n this.embeddingBatchSize = this.config.embeddings.batchSize;\n this.embeddingBatchChars = this.config.embeddings.batchChars;\n this.embeddingInitTimeoutMs = this.config.embeddings.initTimeoutMs;\n\n // Only establish database connection in constructor\n this.db = new Database(dbPath);\n\n // Store embedding config for later initialization\n this.embeddingConfig = this.resolveEmbeddingConfig(appConfig.app.embeddingModel);\n }\n\n private resolveEmbeddingConfig(modelSpec: string): EmbeddingModelConfig | null {\n const resolvedSpec = modelSpec;\n if (!resolvedSpec) {\n logger.debug(\"No embedding model specified. Embeddings are disabled.\");\n return null;\n }\n\n try {\n logger.debug(`Resolving embedding configuration for model: ${resolvedSpec}`);\n return EmbeddingConfig.parseEmbeddingConfig(resolvedSpec);\n } catch (error) {\n logger.debug(`Failed to resolve embedding configuration: ${error}`);\n return null;\n }\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]>(\n `SELECT d.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type \n FROM documents d\n JOIN pages p ON d.page_id = p.id\n WHERE d.id = ?`,\n ),\n // Updated for new schema\n insertDocument: this.db.prepare<[number, string, string, number]>(\n \"INSERT INTO documents (page_id, content, metadata, sort_order) VALUES (?, ?, ?, ?)\",\n ),\n insertEmbedding: this.db.prepare<[string, bigint]>(\n \"UPDATE documents SET embedding = ? WHERE id = ?\",\n ),\n insertPage: this.db.prepare<\n [\n number,\n string,\n string,\n string | null,\n string | null,\n string | null,\n string | null,\n number | null,\n ]\n >(\n \"INSERT INTO pages (version_id, url, title, etag, last_modified, source_content_type, content_type, depth) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(version_id, url) DO UPDATE SET title = excluded.title, source_content_type = excluded.source_content_type, content_type = excluded.content_type, etag = excluded.etag, last_modified = excluded.last_modified, depth = excluded.depth\",\n ),\n getPageId: this.db.prepare<[number, string]>(\n \"SELECT id FROM pages WHERE version_id = ? AND url = ?\",\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 getLibraryById: this.db.prepare<[number]>(\"SELECT * FROM libraries WHERE id = ?\"),\n // New version-related statements\n insertVersion: this.db.prepare<[number, string]>(\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]>(\n \"SELECT id FROM versions WHERE library_id = ? AND name = ?\",\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 deleteDocuments: this.db.prepare<[string, string]>(\n `DELETE FROM documents \n WHERE page_id IN (\n SELECT p.id FROM pages p\n JOIN versions v ON p.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ? AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocumentsByPageId: this.db.prepare<[number]>(\n \"DELETE FROM documents WHERE page_id = ?\",\n ),\n deletePage: this.db.prepare<[number]>(\"DELETE FROM pages WHERE id = ?\"),\n deletePages: this.db.prepare<[string, string]>(\n `DELETE FROM pages \n WHERE version_id IN (\n SELECT v.id FROM versions v\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ? AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n getDocumentBySort: this.db.prepare<[string, string]>(\n `SELECT d.id\n FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 pages p ON d.page_id = p.id\n JOIN versions v ON p.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(p.created_at) as indexedAt,\n COUNT(d.id) as documentCount,\n COUNT(DISTINCT p.url) as uniqueUrlCount\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n LEFT JOIN pages p ON p.version_id = v.id\n LEFT JOIN documents d ON d.page_id = p.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.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.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.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.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.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.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.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.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 // Version and library deletion statements\n deleteVersionById: this.db.prepare<[number]>(\"DELETE FROM versions WHERE id = ?\"),\n deleteLibraryById: this.db.prepare<[number]>(\"DELETE FROM libraries WHERE id = ?\"),\n countVersionsByLibraryId: this.db.prepare<[number]>(\n \"SELECT COUNT(*) as count FROM versions WHERE library_id = ?\",\n ),\n getVersionId: this.db.prepare<[string, string]>(\n `SELECT v.id, v.library_id FROM versions v\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ? AND COALESCE(v.name, '') = COALESCE(?, '')`,\n ),\n getPagesByVersionId: this.db.prepare<[number]>(\n \"SELECT * FROM pages WHERE version_id = ?\",\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 * Initialize the embeddings client using the provided config.\n * If no embedding config is provided (null or undefined), embeddings will not be initialized.\n * This allows DocumentStore to be used without embeddings for FTS-only operations.\n *\n * Environment variables per provider:\n * - openai: OPENAI_API_KEY (and optionally OPENAI_API_BASE, OPENAI_ORG_ID)\n * - vertex: GOOGLE_APPLICATION_CREDENTIALS (path to service account JSON)\n * - gemini: GOOGLE_API_KEY\n * - aws: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION\n * - microsoft: Azure OpenAI credentials (AZURE_OPENAI_API_*)\n */\n private async initializeEmbeddings(): Promise<void> {\n // If embedding config is explicitly null or undefined, skip embedding initialization\n if (this.embeddingConfig === null || this.embeddingConfig === undefined) {\n logger.debug(\n \"Embedding initialization skipped (no config provided - FTS-only mode)\",\n );\n return;\n }\n\n const config = this.embeddingConfig;\n\n // Check if credentials are available for the provider\n if (!areCredentialsAvailable(config.provider)) {\n logger.warn(\n `⚠️ No credentials found for ${config.provider} embedding provider. Vector search is disabled.\\n` +\n ` Only full-text search will be available. To enable vector search, please configure the required\\n` +\n ` environment variables for ${config.provider} or choose a different provider.\\n` +\n ` See README.md for configuration options or run with --help for more details.`,\n );\n return; // Skip initialization, keep isVectorSearchEnabled = false\n }\n\n // Create embedding model\n try {\n this.embeddings = createEmbeddingModel(config.modelSpec, {\n requestTimeoutMs: this.config.embeddings.requestTimeoutMs,\n vectorDimension: this.dbDimension,\n });\n\n // Use known dimensions if available, otherwise detect via test query\n if (config.dimensions !== null) {\n this.modelDimension = config.dimensions;\n } else {\n // Fallback: determine the model's actual dimension by embedding a test string\n // Use a timeout to fail fast if the embedding service is unreachable\n const testPromise = this.embeddings.embedQuery(\"test\");\n let timeoutId: NodeJS.Timeout | undefined;\n const timeoutPromise = new Promise<never>((_, reject) => {\n timeoutId = setTimeout(() => {\n reject(\n new Error(\n `Embedding service connection timed out after ${this.embeddingInitTimeoutMs / 1000} seconds`,\n ),\n );\n }, this.embeddingInitTimeoutMs);\n });\n\n try {\n const testVector = await Promise.race([testPromise, timeoutPromise]);\n this.modelDimension = testVector.length;\n } finally {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n }\n\n // Cache the discovered dimensions for future use\n EmbeddingConfig.setKnownModelDimensions(config.model, this.modelDimension);\n }\n\n if (this.modelDimension > this.dbDimension) {\n throw new DimensionError(config.modelSpec, this.modelDimension, this.dbDimension);\n }\n\n // If we reach here, embeddings are successfully initialized\n this.isVectorSearchEnabled = true;\n logger.debug(\n `Embeddings initialized: ${config.provider}:${config.model} (${this.modelDimension}d)`,\n );\n } catch (error) {\n // Handle model-related errors with helpful messages\n if (error instanceof Error) {\n if (\n error.message.includes(\"does not exist\") ||\n error.message.includes(\"MODEL_NOT_FOUND\")\n ) {\n throw new ModelConfigurationError(\n `Invalid embedding model: ${config.model}\\n` +\n ` The model \"${config.model}\" is not available or you don't have access to it.\\n` +\n \" See README.md for supported models or run with --help for more details.\",\n );\n }\n if (\n error.message.includes(\"API key\") ||\n error.message.includes(\"401\") ||\n error.message.includes(\"authentication\")\n ) {\n throw new ModelConfigurationError(\n `Authentication failed for ${config.provider} embedding provider\\n` +\n \" Please check your API key configuration.\\n\" +\n \" See README.md for configuration options or run with --help for more details.\",\n );\n }\n // Handle network-related errors (timeout, connection refused, etc.)\n if (\n error.message.includes(\"timed out\") ||\n error.message.includes(\"ECONNREFUSED\") ||\n error.message.includes(\"ENOTFOUND\") ||\n error.message.includes(\"ETIMEDOUT\") ||\n error.message.includes(\"ECONNRESET\") ||\n error.message.includes(\"network\") ||\n error.message.includes(\"fetch failed\")\n ) {\n throw new ModelConfigurationError(\n `Failed to connect to ${config.provider} embedding service\\n` +\n ` ${error.message}\\n` +\n ` Please check that the embedding service is running and accessible.\\n` +\n ` If using a local model (e.g., Ollama), ensure the service is started.`,\n );\n }\n }\n // Re-throw other embedding errors (like DimensionError) as-is\n throw error;\n }\n }\n\n /**\n * Generates a safe FTS query by tokenizing the input and escaping for FTS5.\n *\n * Strategy:\n * - Quotes toggle between \"phrase mode\" and \"word mode\" (simple state machine)\n * - Text inside quotes becomes a single phrase token\n * - Text outside quotes is split by whitespace into word tokens\n * - All tokens are escaped (double quotes -> \"\") and wrapped in quotes for safety\n *\n * This prevents FTS5 syntax errors while supporting intuitive phrase searches.\n *\n * Query construction:\n * - Exact match of full input: `(\"escaped full query\")`\n * - Individual terms: `(\"term1\" AND \"term2\" AND \"phrase\")`\n * - Combined: `(\"full query\") OR (\"term1\" AND \"term2\")`\n *\n * Examples:\n * - `foo bar` -> `(\"foo bar\") OR (\"foo\" AND \"bar\")`\n * - `\"hello world\"` -> `(\"hello world\")`\n * - `test \"exact phrase\" word` -> `(\"test exact phrase word\") OR (\"test\" AND \"exact phrase\" AND \"word\")`\n */\n private escapeFtsQuery(query: string): string {\n // Tokenize the query using a simple quote-toggle state machine\n const tokens: string[] = [];\n let currentToken = \"\";\n let inQuote = false;\n\n for (let i = 0; i < query.length; i++) {\n const char = query[i];\n\n if (char === '\"') {\n // Toggle quote mode\n if (inQuote) {\n // Closing quote: save the current phrase token\n if (currentToken.length > 0) {\n tokens.push(currentToken);\n currentToken = \"\";\n }\n inQuote = false;\n } else {\n // Opening quote: save any accumulated word token first\n if (currentToken.length > 0) {\n tokens.push(currentToken);\n currentToken = \"\";\n }\n inQuote = true;\n }\n } else if (char === \" \" && !inQuote) {\n // Whitespace outside quotes: token separator\n if (currentToken.length > 0) {\n tokens.push(currentToken);\n currentToken = \"\";\n }\n } else {\n // Regular character: accumulate\n currentToken += char;\n }\n }\n\n // Save any remaining token\n if (currentToken.length > 0) {\n tokens.push(currentToken);\n }\n\n // Handle empty query or only whitespace\n if (tokens.length === 0) {\n return '\"\"';\n }\n\n // Escape and quote each token for FTS5 safety\n const escapedTokens = tokens.map((token) => {\n const escaped = token.replace(/\"/g, '\"\"');\n return `\"${escaped}\"`;\n });\n\n // If single token, just return it\n if (escapedTokens.length === 1) {\n return escapedTokens[0];\n }\n\n // Build query: (Exact match of semantic content) OR (Terms ORed together)\n // Join tokens with space to match the semantic phrase, not the syntactic quotes\n const exactMatch = `\"${tokens.join(\" \").replace(/\"/g, '\"\"')}\"`;\n const termsQuery = escapedTokens.join(\" OR \");\n\n return `${exactMatch} OR ${termsQuery}`;\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 await applyMigrations(this.db, {\n maxRetries: this.config.db.migrationMaxRetries,\n retryDelayMs: this.config.db.migrationRetryDelayMs,\n });\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, ModelConfigurationError, and UnsupportedProviderError directly\n if (\n error instanceof StoreError ||\n error instanceof ModelConfigurationError ||\n error instanceof UnsupportedProviderError\n ) {\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 version_id.\n * Creates library and version records if they don't exist.\n */\n async resolveVersionId(library: string, version: string): Promise<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,\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 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 * Retrieves a version by its ID.\n * @param versionId The version ID to retrieve\n * @returns The version record, or null if not found\n */\n async getVersionById(versionId: number): Promise<DbVersion | null> {\n try {\n const row = this.statements.getVersionById.get(versionId) as DbVersion | undefined;\n return row || null;\n } catch (error) {\n throw new StoreError(`Failed to get version by ID: ${error}`);\n }\n }\n\n /**\n * Retrieves a library by its ID.\n * @param libraryId The library ID to retrieve\n * @returns The library record, or null if not found\n */\n async getLibraryById(libraryId: number): Promise<{ id: number; name: string } | null> {\n try {\n const row = this.statements.getLibraryById.get(libraryId) as\n | { id: number; name: string }\n | undefined;\n return row || null;\n } catch (error) {\n throw new StoreError(`Failed to get library by ID: ${error}`);\n }\n }\n\n /**\n * Retrieves a library by its name.\n * @param name The library name to retrieve\n * @returns The library record, or null if not found\n */\n async getLibrary(name: string): Promise<{ id: number; name: string } | null> {\n try {\n const normalizedName = name.toLowerCase();\n const row = this.statements.getLibraryIdByName.get(normalizedName) as\n | { id: number }\n | undefined;\n if (!row) {\n return null;\n }\n return { id: row.id, name: normalizedName };\n } catch (error) {\n throw new StoreError(`Failed to get library by name: ${error}`);\n }\n }\n\n /**\n * Deletes a library by its ID.\n * This should only be called when the library has no remaining versions.\n * @param libraryId The library ID to delete\n */\n async deleteLibrary(libraryId: number): Promise<void> {\n try {\n this.statements.deleteLibraryById.run(libraryId);\n } catch (error) {\n throw new StoreError(`Failed to delete library: ${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 // Extract source URL and exclude runtime-only fields using destructuring\n const {\n url: source_url,\n library: _library,\n version: _version,\n signal: _signal,\n initialQueue: _initialQueue,\n isRefresh: _isRefresh,\n ...scraper_options\n } = 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 const rows = this.statements.queryLibraryVersions.all() as DbLibraryVersion[];\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: descending (latest first), unversioned is \"latest\"\n for (const versions of libraryMap.values()) {\n versions.sort((a, b) => compareVersionsDescending(a.version, b.version));\n }\n\n return libraryMap;\n } catch (error) {\n throw new ConnectionError(\"Failed to query library versions\", error);\n }\n }\n\n /**\n * Helper method to detect if an error is related to input size limits.\n * Checks for common error messages from various embedding providers.\n */\n private isInputSizeError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n\n const message = error.message.toLowerCase();\n return (\n message.includes(\"maximum context length\") ||\n message.includes(\"too long\") ||\n message.includes(\"token limit\") ||\n message.includes(\"input is too large\") ||\n message.includes(\"exceeds\") ||\n (message.includes(\"max\") && message.includes(\"token\"))\n );\n }\n\n /**\n * Creates embeddings for an array of texts with automatic retry logic for size-related errors.\n * If a batch fails due to size limits:\n * - Batches with multiple texts are split in half and retried recursively\n * - Single texts that are too large are truncated and retried once\n *\n * @param texts Array of texts to embed\n * @param isRetry Internal flag to prevent duplicate warning logs\n * @returns Array of embedding vectors\n */\n private async embedDocumentsWithRetry(\n texts: string[],\n isRetry = false,\n ): Promise<number[][]> {\n if (texts.length === 0) {\n return [];\n }\n\n try {\n // Try to embed the batch normally\n return await this.embeddings.embedDocuments(texts);\n } catch (error) {\n // Check if this is a size-related error\n if (this.isInputSizeError(error)) {\n if (texts.length > 1) {\n // Split batch in half and retry each half recursively\n const midpoint = Math.floor(texts.length / 2);\n const firstHalf = texts.slice(0, midpoint);\n const secondHalf = texts.slice(midpoint);\n\n // Only log if this is not already a retry\n if (!isRetry) {\n logger.warn(\n `⚠️ Batch of ${texts.length} texts exceeded size limit, splitting into ${firstHalf.length} + ${secondHalf.length}`,\n );\n }\n\n const [firstEmbeddings, secondEmbeddings] = await Promise.all([\n this.embedDocumentsWithRetry(firstHalf, true),\n this.embedDocumentsWithRetry(secondHalf, true),\n ]);\n\n return [...firstEmbeddings, ...secondEmbeddings];\n } else {\n // Single text that's too large - split in half and retry\n const text = texts[0];\n const midpoint = Math.floor(text.length / 2);\n const firstHalf = text.substring(0, midpoint);\n\n // Only log once for the original text\n if (!isRetry) {\n logger.warn(\n `⚠️ Single text exceeded embedding size limit (${text.length} chars).`,\n );\n }\n\n try {\n // Recursively retry with first half only (mark as retry to prevent duplicate logs)\n // This preserves the beginning of the text which typically contains the most important context\n const embedding = await this.embedDocumentsWithRetry([firstHalf], true);\n return embedding;\n } catch (retryError) {\n // If even split text fails, log error and throw\n logger.error(\n `❌ Failed to embed even after splitting. Original length: ${text.length}`,\n );\n throw retryError;\n }\n }\n }\n\n // Not a size error, re-throw\n throw error;\n }\n }\n\n /**\n * Stores documents with library and version metadata, generating embeddings\n * for vector similarity search. Uses the new pages table to normalize page-level\n * metadata and avoid duplication across document chunks.\n */\n async addDocuments(\n library: string,\n version: string,\n depth: number,\n result: ScrapeResult,\n ): Promise<void> {\n try {\n const { title, url, chunks } = result;\n if (chunks.length === 0) {\n return;\n }\n\n // Generate embeddings in batch only if vector search is enabled\n let paddedEmbeddings: number[][] = [];\n\n if (this.isVectorSearchEnabled) {\n const texts = chunks.map((chunk) => {\n const header = `<title>${title}</title>\\n<url>${url}</url>\\n<path>${(chunk.section.path || []).join(\" / \")}</path>\\n`;\n return `${header}${chunk.content}`;\n });\n\n // Validate chunk body sizes before creating embeddings.\n // Note: We compare the chunk body (without the metadata header) against maxChunkSize,\n // because the splitter's size budget applies to the content body only. The metadata\n // header (title, URL, path) is expected overhead added after splitting.\n for (let i = 0; i < chunks.length; i++) {\n const bodySize = chunks[i].content.length;\n if (bodySize > this.splitterMaxChunkSize) {\n logger.warn(\n `⚠️ Chunk ${i + 1}/${chunks.length} body exceeds max size: ${bodySize} > ${this.splitterMaxChunkSize} chars (URL: ${url})`,\n );\n }\n }\n\n // Batch embedding creation to avoid token limit errors\n const maxBatchChars = this.embeddingBatchChars;\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.embedDocumentsWithRetry(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 >= this.embeddingBatchSize) {\n batchCount++;\n logger.debug(\n `Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embedDocumentsWithRetry(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.embedDocumentsWithRetry(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n }\n paddedEmbeddings = rawEmbeddings.map((vector) => this.padVector(vector));\n }\n\n // Resolve library and version IDs (creates them if they don't exist)\n const versionId = await this.resolveVersionId(library, version);\n\n // Delete existing documents for this page to prevent conflicts\n // First check if the page exists and get its ID\n const existingPage = this.statements.getPageId.get(versionId, url) as\n | { id: number }\n | undefined;\n\n if (existingPage) {\n const result = this.statements.deleteDocumentsByPageId.run(existingPage.id);\n if (result.changes > 0) {\n logger.debug(`Deleted ${result.changes} existing documents for URL: ${url}`);\n }\n }\n\n // Insert documents in a transaction\n const transaction = this.db.transaction(() => {\n // Extract content type from metadata if available\n const sourceContentType = result.sourceContentType || result.contentType || null;\n const contentType = result.contentType || result.sourceContentType || null;\n\n // Extract etag from document metadata if available\n const etag = result.etag || null;\n\n // Extract lastModified from document metadata if available\n const lastModified = result.lastModified || null;\n\n // Insert or update page record\n this.statements.insertPage.run(\n versionId,\n url,\n title || \"\",\n etag,\n lastModified,\n sourceContentType,\n contentType,\n depth,\n );\n\n // Query for the page ID since we can't use RETURNING\n const existingPage = this.statements.getPageId.get(versionId, url) as\n | { id: number }\n | undefined;\n if (!existingPage) {\n throw new StoreError(`Failed to get page ID for URL: ${url}`);\n }\n const pageId = existingPage.id;\n\n // Then insert document chunks linked to their pages\n let docIndex = 0;\n for (let i = 0; i < chunks.length; i++) {\n const chunk = chunks[i];\n\n // Insert document chunk\n const result = this.statements.insertDocument.run(\n pageId,\n chunk.content,\n JSON.stringify({\n types: chunk.types,\n level: chunk.section.level,\n path: chunk.section.path,\n } satisfies DbChunkMetadata),\n i, // sort_order within this page\n );\n const rowId = result.lastInsertRowid;\n\n // Insert into vector table only if vector search is enabled\n if (this.isVectorSearchEnabled && paddedEmbeddings.length > 0) {\n this.statements.insertEmbedding.run(\n JSON.stringify(paddedEmbeddings[docIndex]),\n BigInt(rowId),\n );\n }\n\n docIndex++;\n }\n });\n\n transaction();\n } catch (error) {\n throw new ConnectionError(\"Failed to add documents to store\", error);\n }\n }\n\n /**\n * Removes documents and pages matching specified library and version.\n * This consolidated method deletes both documents and their associated pages.\n * @returns Number of documents deleted\n */\n async deletePages(library: string, version: string): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n\n // First delete documents\n const result = this.statements.deleteDocuments.run(\n library.toLowerCase(),\n normalizedVersion,\n );\n\n // Then delete the pages (after documents are gone, due to foreign key constraints)\n this.statements.deletePages.run(library.toLowerCase(), normalizedVersion);\n\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents\", error);\n }\n }\n\n /**\n * Deletes a page and all its associated document chunks.\n * Performs manual deletion in the correct order to satisfy foreign key constraints:\n * 1. Delete document chunks (page_id references pages.id)\n * 2. Delete page record\n *\n * This method is used during refresh operations when a page returns 404 Not Found.\n */\n async deletePage(pageId: number): Promise<void> {\n try {\n // Delete documents first (due to foreign key constraint)\n const docResult = this.statements.deleteDocumentsByPageId.run(pageId);\n logger.debug(`Deleted ${docResult.changes} document(s) for page ID ${pageId}`);\n\n // Then delete the page record\n const pageResult = this.statements.deletePage.run(pageId);\n if (pageResult.changes > 0) {\n logger.debug(`Deleted page record for page ID ${pageId}`);\n }\n } catch (error) {\n throw new ConnectionError(`Failed to delete page ${pageId}`, error);\n }\n }\n\n /**\n * Retrieves all pages for a specific version ID with their metadata.\n * Used for refresh operations to get existing pages with their ETags and depths.\n * @returns Array of page records\n */\n async getPagesByVersionId(versionId: number): Promise<DbPage[]> {\n try {\n const result = this.statements.getPagesByVersionId.all(versionId) as DbPage[];\n return result;\n } catch (error) {\n throw new ConnectionError(\"Failed to get pages by version ID\", error);\n }\n }\n\n /**\n * Completely removes a library version and all associated documents.\n * Optionally removes the library if no other versions remain.\n * @param library Library name\n * @param version Version string (empty string for unversioned)\n * @param removeLibraryIfEmpty Whether to remove the library if no versions remain\n * @returns Object with counts of deleted documents, version deletion status, and library deletion status\n */\n async removeVersion(\n library: string,\n version: string,\n removeLibraryIfEmpty = true,\n ): Promise<{\n documentsDeleted: number;\n versionDeleted: boolean;\n libraryDeleted: boolean;\n }> {\n try {\n const normalizedLibrary = library.toLowerCase();\n const normalizedVersion = version.toLowerCase();\n\n // First, get the version ID and library ID\n const versionResult = this.statements.getVersionId.get(\n normalizedLibrary,\n normalizedVersion,\n ) as { id: number; library_id: number } | undefined;\n\n if (!versionResult) {\n // Version doesn't exist, return zero counts\n return { documentsDeleted: 0, versionDeleted: false, libraryDeleted: false };\n }\n\n const { id: versionId, library_id: libraryId } = versionResult;\n\n // Delete in order to respect foreign key constraints:\n // 1. documents (page_id → pages.id)\n // 2. pages (version_id → versions.id)\n // 3. versions (library_id → libraries.id)\n // 4. libraries (if empty)\n\n // Delete all documents for this version\n const documentsDeleted = await this.deletePages(library, version);\n\n // Delete all pages for this version (must be done after documents, before version)\n this.statements.deletePages.run(normalizedLibrary, normalizedVersion);\n\n // Delete the version record\n const versionDeleteResult = this.statements.deleteVersionById.run(versionId);\n const versionDeleted = versionDeleteResult.changes > 0;\n\n let libraryDeleted = false;\n\n // Check if we should remove the library\n if (removeLibraryIfEmpty && versionDeleted) {\n // Count remaining versions for this library\n const countResult = this.statements.countVersionsByLibraryId.get(libraryId) as\n | { count: number }\n | undefined;\n const remainingVersions = countResult?.count ?? 0;\n\n if (remainingVersions === 0) {\n // No versions left, delete the library\n const libraryDeleteResult = this.statements.deleteLibraryById.run(libraryId);\n libraryDeleted = libraryDeleteResult.changes > 0;\n }\n }\n\n return { documentsDeleted, versionDeleted, libraryDeleted };\n } catch (error) {\n throw new ConnectionError(\"Failed to remove version\", error);\n }\n }\n\n /**\n * Parses the metadata field from a JSON string to an object.\n * This is necessary because better-sqlite3's json() function returns a string, not an object.\n */\n private parseMetadata<M extends {}, T extends { metadata: M }>(row: T): T {\n if (row.metadata && typeof row.metadata === \"string\") {\n try {\n row.metadata = JSON.parse(row.metadata);\n } catch (error) {\n logger.warn(`Failed to parse metadata JSON: ${error}`);\n row.metadata = {} as M;\n }\n }\n return row;\n }\n\n /**\n * Parses metadata for an array of rows.\n */\n private parseMetadataArray<M extends {}, T extends { metadata: M }>(rows: T[]): T[] {\n return rows.map((row) => this.parseMetadata(row));\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<DbPageChunk | null> {\n try {\n const row = this.statements.getById.get(BigInt(id)) as DbQueryResult<DbPageChunk>;\n if (!row) {\n return null;\n }\n\n return this.parseMetadata(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 when vector search is enabled,\n * or falls back to full-text search only when vector search is disabled.\n * Uses Reciprocal Rank Fusion for hybrid search or simple FTS ranking for fallback mode.\n */\n async findByContent(\n library: string,\n version: string,\n query: string,\n limit: number,\n ): Promise<(DbPageChunk & DbChunkRank)[]> {\n try {\n // Return empty array for empty or whitespace-only queries\n if (!query || typeof query !== \"string\" || query.trim().length === 0) {\n return [];\n }\n\n const ftsQuery = this.escapeFtsQuery(query);\n const normalizedVersion = version.toLowerCase();\n\n if (this.isVectorSearchEnabled) {\n // Hybrid search: vector + full-text search with RRF ranking\n const rawEmbedding = await this.embeddings.embedQuery(query);\n const embedding = this.padVector(rawEmbedding);\n\n // Apply overfetch factor to both vector and FTS searches for better recall\n const overfetchLimit = Math.max(1, limit * this.searchOverfetchFactor);\n\n // Use a multiplier to cast a wider net in vector search before final ranking\n const vectorSearchK = overfetchLimit * this.vectorSearchMultiplier;\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 documents d ON dv.rowid = d.id\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.url as url,\n p.title as title,\n p.source_content_type as source_content_type,\n p.content_type as content_type,\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 JOIN pages p ON d.page_id = p.id\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 AND NOT EXISTS (\n SELECT 1 FROM json_each(json_extract(d.metadata, '$.types')) je\n WHERE je.value = 'structural'\n )\n `);\n\n const rawResults = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n JSON.stringify(embedding),\n vectorSearchK,\n library.toLowerCase(),\n normalizedVersion,\n ftsQuery,\n overfetchLimit,\n ) as RawSearchResult[];\n\n // Apply RRF ranking with configurable weights\n const rankedResults = this.assignRanks(rawResults);\n\n // Sort by RRF score and take top results (truncate to original limit)\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 const result: DbPageChunk = {\n ...row,\n url: row.url || \"\", // Ensure url is never undefined\n title: row.title || null,\n source_content_type: row.source_content_type || null,\n content_type: row.content_type || null,\n };\n // Add search scores as additional properties (not in metadata)\n return Object.assign(result, {\n score: row.rrf_score,\n vec_rank: row.vec_rank,\n fts_rank: row.fts_rank,\n });\n });\n } else {\n // Fallback: full-text search only\n const stmt = this.db.prepare(`\n SELECT\n d.id,\n d.content,\n d.metadata,\n p.url as url,\n p.title as title,\n p.source_content_type as source_content_type,\n p.content_type as content_type,\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 pages p ON d.page_id = p.id\n JOIN versions v ON p.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 AND NOT EXISTS (\n SELECT 1 FROM json_each(json_extract(d.metadata, '$.types')) je\n WHERE je.value = 'structural'\n )\n ORDER BY fts_score\n LIMIT ?\n `);\n\n const rawResults = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n ftsQuery,\n limit,\n ) as (RawSearchResult & { fts_score: number })[];\n\n // Assign FTS ranks based on order (best score = rank 1)\n return rawResults.map((row, index) => {\n const result: DbPageChunk = {\n ...row,\n url: row.url || \"\", // Ensure url is never undefined\n title: row.title || null,\n source_content_type: row.source_content_type || null,\n content_type: row.content_type || null,\n };\n // Add search scores as additional properties (not in metadata)\n return Object.assign(result, {\n score: -row.fts_score, // Convert BM25 score to positive value for consistency\n fts_rank: index + 1, // Assign rank based on order (1-based)\n });\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<DbPageChunk[]> {\n try {\n const parent = await this.getById(id);\n if (!parent) {\n return [];\n }\n\n const parentPath = parent.metadata.path ?? [];\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getChildChunks.all(\n library.toLowerCase(),\n normalizedVersion,\n parent.url,\n parentPath.length + 1,\n JSON.stringify(parentPath),\n BigInt(id),\n limit,\n ) as Array<DbPageChunk>;\n\n return this.parseMetadataArray(result);\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<DbPageChunk[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getPrecedingSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n reference.url,\n BigInt(id),\n JSON.stringify(reference.metadata.path),\n limit,\n ) as Array<DbPageChunk>;\n\n return this.parseMetadataArray(result).reverse();\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<DbPageChunk[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getSubsequentSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n reference.url,\n BigInt(id),\n JSON.stringify(reference.metadata.path),\n limit,\n ) as Array<DbPageChunk>;\n\n return this.parseMetadataArray(result);\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 * Returns null if no parent is found or if there's a database error.\n * Database errors are logged but not thrown to maintain consistent behavior.\n */\n async findParentChunk(\n library: string,\n version: string,\n id: string,\n ): Promise<DbPageChunk | null> {\n try {\n const child = await this.getById(id);\n if (!child) {\n return null;\n }\n\n const path = child.metadata.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 child.url,\n JSON.stringify(parentPath),\n BigInt(id),\n ) as DbQueryResult<DbPageChunk>;\n\n if (!result) {\n return null;\n }\n\n return this.parseMetadata(result);\n } catch (error) {\n logger.warn(`Failed to find parent chunk for ID ${id}: ${error}`);\n return null;\n }\n }\n\n /**\n * Fetches multiple documents by their IDs in a single call.\n * Returns an array of DbPageChunk objects, sorted by their sort_order.\n */\n async findChunksByIds(\n library: string,\n version: string,\n ids: string[],\n ): Promise<DbPageChunk[]> {\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.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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.id IN (${placeholders}) \n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n ...ids,\n ) as DbPageChunk[];\n return this.parseMetadataArray(rows);\n } catch (error) {\n throw new ConnectionError(\"Failed to fetch documents by IDs\", error);\n }\n }\n\n /**\n * Fetches all document chunks for a specific URL within a library and version.\n * Returns DbPageChunk objects sorted by their sort_order for proper reassembly.\n */\n async findChunksByUrl(\n library: string,\n version: string,\n url: string,\n ): Promise<DbPageChunk[]> {\n try {\n const normalizedVersion = version.toLowerCase();\n const stmt = this.db.prepare(\n `SELECT d.id, d.page_id, d.content, json(d.metadata) as metadata, d.sort_order, d.embedding, d.created_at, p.url, p.title, p.source_content_type, p.content_type FROM documents d\n JOIN pages p ON d.page_id = p.id\n JOIN versions v ON p.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 p.url = ?\n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n url,\n ) as DbPageChunk[];\n return this.parseMetadataArray(rows);\n } catch (error) {\n throw new ConnectionError(`Failed to fetch documents by URL ${url}`, error);\n }\n }\n}\n","import path from \"node:path\";\nimport Fuse from \"fuse.js\";\nimport semver from \"semver\";\nimport type { EventBusService } from \"../events\";\nimport { EventType } from \"../events\";\nimport { PipelineFactory } from \"../scraper/pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../scraper/pipelines/types\";\nimport type { ScrapeResult, ScraperOptions } from \"../scraper/types\";\nimport type { Chunk } from \"../splitter/types\";\nimport { telemetry } from \"../telemetry\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { sortVersionsDescending } from \"../utils/version\";\nimport { DocumentRetrieverService } from \"./DocumentRetrieverService\";\nimport { DocumentStore } from \"./DocumentStore\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport {\n LibraryNotFoundInStoreError,\n StoreError,\n VersionNotFoundInStoreError,\n} 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 * Uses content-type-specific pipelines for processing and splitting content.\n */\nexport class DocumentManagementService {\n private readonly appConfig: AppConfig;\n private readonly store: DocumentStore;\n private readonly documentRetriever: DocumentRetrieverService;\n private readonly pipelines: ContentPipeline[];\n private readonly eventBus: EventBusService;\n\n constructor(eventBus: EventBusService, appConfig: AppConfig) {\n this.appConfig = appConfig;\n this.eventBus = eventBus;\n const storePath = this.appConfig.app.storePath;\n if (!storePath) {\n throw new Error(\"storePath is required when not using a remote server\");\n }\n // Handle special :memory: case for in-memory databases (primarily for testing)\n const dbPath =\n storePath === \":memory:\" ? \":memory:\" : path.join(storePath, \"documents.db\");\n\n logger.debug(`Using database path: ${dbPath}`);\n\n // Directory creation is handled by the centralized path resolution\n\n this.store = new DocumentStore(dbPath, this.appConfig);\n this.documentRetriever = new DocumentRetrieverService(this.store, this.appConfig);\n\n // Initialize content pipelines for different content types including universal TextPipeline fallback\n this.pipelines = PipelineFactory.createStandardPipelines(this.appConfig);\n }\n\n /**\n * Returns the active embedding configuration if vector search is enabled,\n * or null if embeddings are disabled.\n */\n getActiveEmbeddingConfig(): EmbeddingModelConfig | null {\n return this.store.getActiveEmbeddingConfig();\n }\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 /**\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 and cleans up pipeline resources.\n */\n\n async shutdown(): Promise<void> {\n logger.debug(\"Shutting down store manager\");\n\n // Cleanup all pipelines to prevent resource leaks (e.g., browser instances)\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n\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.\n * Checks if the library record exists in the database, regardless of whether it has versions or documents.\n * Throws LibraryNotFoundInStoreError with suggestions if the library is not found.\n * @param library The name of the library to validate.\n * @throws {LibraryNotFoundInStoreError} If the library does not exist.\n */\n async validateLibraryExists(library: string): Promise<void> {\n logger.info(`🔎 Validating existence of library: ${library}`);\n\n // Check if the library exists in the libraries table\n const libraryRecord = await this.store.getLibrary(library);\n\n if (!libraryRecord) {\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 threshold: 0.7, // Adjust threshold for desired fuzziness (0=exact, 1=match anything)\n });\n const results = fuse.search(library.toLowerCase());\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 LibraryNotFoundInStoreError(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 * Sorted in descending order (latest first).\n */\n async listVersions(library: string): Promise<string[]> {\n const versions = await this.store.queryUniqueVersions(library);\n const validVersions = versions.filter((v) => semver.valid(v));\n return sortVersionsDescending(validVersions);\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 // The next line should usually throw\n await this.validateLibraryExists(library);\n // Fallback, should not reach here\n throw new LibraryNotFoundInStoreError(library, []);\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 (!semver.valid(targetVersion) && !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 const availableVersions = libraryDetails.map((v) => v.version);\n throw new VersionNotFoundInStoreError(\n library,\n targetVersion ?? \"\",\n availableVersions,\n );\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 || \"latest\"} store`,\n );\n const count = await this.store.deletePages(library, normalizedVersion);\n logger.info(`🗑️ Deleted ${count} documents`);\n\n // Emit library change event\n this.eventBus.emit(EventType.LIBRARY_CHANGE, undefined);\n }\n\n /**\n * Deletes a page and all its associated document chunks.\n * This is used during refresh operations when a page returns 404 Not Found.\n */\n async deletePage(pageId: number): Promise<void> {\n logger.debug(`Deleting page ID: ${pageId}`);\n await this.store.deletePage(pageId);\n\n // Emit library change event\n this.eventBus.emit(EventType.LIBRARY_CHANGE, undefined);\n }\n\n /**\n * Retrieves all pages for a specific version ID with their metadata.\n * Used for refresh operations to get existing pages with their ETags and depths.\n */\n async getPagesByVersionId(\n versionId: number,\n ): Promise<\n Array<{ id: number; url: string; etag: string | null; depth: number | null }>\n > {\n return this.store.getPagesByVersionId(versionId);\n }\n\n /**\n * Completely removes a library version and all associated documents.\n * Also removes the library if no other versions remain.\n * If the specified version doesn't exist but the library exists with no versions, removes the library.\n * @param library Library name\n * @param version Version string (null/undefined for unversioned)\n */\n async removeVersion(library: string, version?: string | null): Promise<void> {\n const normalizedVersion = this.normalizeVersion(version);\n logger.debug(`Removing version: ${library}@${normalizedVersion || \"latest\"}`);\n\n const result = await this.store.removeVersion(library, normalizedVersion, true);\n\n logger.info(`🗑️ Removed ${result.documentsDeleted} documents`);\n\n if (result.versionDeleted && result.libraryDeleted) {\n logger.info(`🗑️ Completely removed library ${library} (was last version)`);\n } else if (result.versionDeleted) {\n logger.info(`🗑️ Removed version ${library}@${normalizedVersion || \"latest\"}`);\n } else {\n // Version not found - check if library exists but is empty (has no versions)\n logger.warn(`⚠️ Version ${library}@${normalizedVersion || \"latest\"} not found`);\n\n const libraryRecord = await this.store.getLibrary(library);\n if (libraryRecord) {\n // Library exists - check if it has any versions\n const versions = await this.store.queryUniqueVersions(library);\n if (versions.length === 0) {\n // Library exists but has no versions - delete the library itself\n logger.info(`🗑️ Library ${library} has no versions, removing library record`);\n await this.store.deleteLibrary(libraryRecord.id);\n logger.info(`🗑️ Completely removed library ${library} (had no versions)`);\n }\n }\n }\n\n // Emit library change event\n this.eventBus.emit(EventType.LIBRARY_CHANGE, undefined);\n }\n\n /**\n * Adds pre-processed content directly to the store.\n * This method is used when content has already been processed by a pipeline,\n * avoiding redundant processing. Used primarily by the scraping pipeline.\n *\n * @param library Library name\n * @param version Version string (null/undefined for unversioned)\n * @param processed Pre-processed content with chunks already created\n * @param pageId Optional page ID for refresh operations\n */\n async addScrapeResult(\n library: string,\n version: string | null | undefined,\n depth: number,\n result: ScrapeResult,\n ): Promise<void> {\n const processingStart = performance.now();\n const normalizedVersion = this.normalizeVersion(version);\n const { url, title, chunks, contentType } = result;\n if (!url) {\n throw new StoreError(\"Processed content metadata must include a valid URL\");\n }\n\n logger.info(`📚 Adding processed content: ${title || url}`);\n\n if (chunks.length === 0) {\n logger.warn(`⚠️ No chunks in processed content for ${url}. Skipping.`);\n return;\n }\n\n try {\n logger.info(`✂️ Storing ${chunks.length} pre-split chunks`);\n\n // Add split documents to store\n await this.store.addDocuments(library, normalizedVersion, depth, result);\n\n // Emit library change event after adding documents\n this.eventBus.emit(EventType.LIBRARY_CHANGE, undefined);\n } catch (error) {\n // Track processing failures with native error tracking\n const processingTime = performance.now() - processingStart;\n\n if (error instanceof Error) {\n telemetry.captureException(error, {\n mimeType: contentType,\n contentSizeBytes: chunks.reduce(\n (sum: number, chunk: Chunk) => sum + chunk.content.length,\n 0,\n ),\n processingTimeMs: Math.round(processingTime),\n library,\n libraryVersion: normalizedVersion || null,\n context: \"processed_content_storage\",\n component: DocumentManagementService.constructor.name,\n });\n }\n\n throw error;\n }\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.resolveVersionId(\n normalizedLibrary,\n normalizedVersion,\n );\n\n return versionId;\n }\n\n /**\n * Retrieves a version by its ID from the database.\n */\n async getVersionById(versionId: number) {\n return this.store.getVersionById(versionId);\n }\n\n /**\n * Retrieves a library by its ID from the database.\n */\n async getLibraryById(libraryId: number) {\n return this.store.getLibraryById(libraryId);\n }\n}\n","import type { EventBusService } from \"../events\";\nimport type { AppConfig } from \"../utils/config\";\nimport { DocumentManagementClient } from \"./DocumentManagementClient\";\nimport { DocumentManagementService } from \"./DocumentManagementService\";\nimport type { IDocumentManagement } from \"./trpc/interfaces\";\n\nexport * from \"./DocumentManagementClient\";\nexport * from \"./DocumentManagementService\";\nexport * from \"./DocumentStore\";\nexport * from \"./errors\";\nexport * from \"./trpc/interfaces\";\n\n/** Factory to create a document management implementation */\nexport async function createDocumentManagement(options: {\n eventBus: EventBusService;\n serverUrl?: string;\n appConfig: AppConfig;\n}) {\n if (options.serverUrl) {\n const client = new DocumentManagementClient(options.serverUrl);\n await client.initialize();\n return client as IDocumentManagement;\n }\n\n const storePath = options.appConfig.app.storePath;\n if (!storePath) {\n throw new Error(\"storePath is required when not using a remote server\");\n }\n\n const service = new DocumentManagementService(options.eventBus, options.appConfig);\n await service.initialize();\n return service as IDocumentManagement;\n}\n\n/**\n * Creates and initializes a local DocumentManagementService instance.\n * Use this only when constructing an in-process PipelineManager (worker path).\n */\nexport async function createLocalDocumentManagement(\n eventBus: EventBusService,\n appConfig: AppConfig,\n) {\n const storePath = appConfig.app.storePath;\n if (!storePath) {\n throw new Error(\"storePath is required when not using a remote server\");\n }\n\n const service = new DocumentManagementService(eventBus, appConfig);\n await service.initialize();\n return service;\n}\n","import { VersionNotFoundInStoreError } from \"../store\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { StoreSearchResult } from \"../store/types\";\nimport { logger } from \"../utils/logger\";\nimport { ValidationError } from \"./errors\";\n\nexport interface SearchToolOptions {\n library: string;\n version?: string;\n query: string;\n limit?: number;\n exactMatch?: boolean;\n}\n\nexport interface SearchToolResultError {\n message: string;\n availableVersions?: Array<{\n version: string;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>;\n suggestions?: string[]; // Specific to LibraryNotFoundInStoreError\n}\n\nexport interface SearchToolResult {\n results: StoreSearchResult[];\n}\n\n/**\n * Tool for searching indexed documentation.\n * Supports exact version matches and version range patterns.\n * Returns available versions when requested version is not found.\n */\nexport class SearchTool {\n private docService: IDocumentManagement;\n\n constructor(docService: IDocumentManagement) {\n this.docService = docService;\n }\n\n async execute(options: SearchToolOptions): Promise<SearchToolResult> {\n const { library, version, query, limit = 5, exactMatch = false } = options;\n\n // Validate required inputs\n if (!library || typeof library !== \"string\" || library.trim() === \"\") {\n throw new ValidationError(\n \"Library name is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n if (!query || typeof query !== \"string\" || query.trim() === \"\") {\n throw new ValidationError(\n \"Query is required and must be a non-empty string.\",\n this.constructor.name,\n );\n }\n\n if (limit !== undefined && (typeof limit !== \"number\" || limit < 1 || limit > 100)) {\n throw new ValidationError(\n \"Limit must be a number between 1 and 100.\",\n this.constructor.name,\n );\n }\n\n // When exactMatch is true, version must be specified and not 'latest'\n if (exactMatch && (!version || version === \"latest\")) {\n // Get available *detailed* versions for error message\n await this.docService.validateLibraryExists(library);\n // Fetch detailed versions using listLibraries and find the specific library\n const allLibraries = await this.docService.listLibraries();\n const libraryInfo = allLibraries.find((lib) => lib.library === library);\n const availableVersions = libraryInfo\n ? libraryInfo.versions.map((v) => v.ref.version)\n : [];\n throw new VersionNotFoundInStoreError(\n library,\n version ?? \"latest\",\n availableVersions,\n );\n }\n\n // Default to 'latest' only when exactMatch is false\n const resolvedVersion = version || \"latest\";\n\n logger.info(\n `🔍 Searching ${library}@${resolvedVersion} for: ${query}${exactMatch ? \" (exact match)\" : \"\"}`,\n );\n\n try {\n // 1. Validate library exists first\n await this.docService.validateLibraryExists(library);\n\n // 2. Proceed with version finding and searching\n let versionToSearch: string | null | undefined = resolvedVersion;\n\n if (!exactMatch) {\n // If not exact match, find the best version (which might be null)\n const versionResult = await this.docService.findBestVersion(library, version);\n // Use the bestMatch from the result, which could be null\n versionToSearch = versionResult.bestMatch;\n\n // If findBestVersion returned null (no matching semver) AND unversioned docs exist,\n // should we search unversioned? The current logic passes null to searchStore,\n // which gets normalized to \"\" (unversioned). This seems reasonable.\n // If findBestVersion threw VersionNotFoundInStoreError, it's caught below.\n }\n // If exactMatch is true, versionToSearch remains the originally provided version.\n\n // Note: versionToSearch can be string | null | undefined here.\n // searchStore handles null/undefined by normalizing to \"\".\n const results = await this.docService.searchStore(\n library,\n versionToSearch,\n query,\n limit,\n );\n logger.info(`✅ Found ${results.length} matching results`);\n\n return { results };\n } catch (error) {\n logger.error(\n `❌ Search failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n throw error;\n }\n }\n}\n","/**\n * Helper utilities for constructing MCP tools with shared dependencies. Tools\n * are created with the resolved configuration supplied by the entrypoint to\n * avoid internal config loading.\n */\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { AutoDetectFetcher } from \"../scraper/fetcher\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport {\n CancelJobTool,\n FetchUrlTool,\n FindVersionTool,\n GetJobInfoTool,\n ListJobsTool,\n ListLibrariesTool,\n RefreshVersionTool,\n RemoveTool,\n ScrapeTool,\n SearchTool,\n} from \"../tools\";\nimport type { AppConfig } from \"../utils/config\";\n\n/**\n * Interface for the shared tool instances.\n */\nexport interface McpServerTools {\n listLibraries: ListLibrariesTool;\n findVersion: FindVersionTool;\n scrape: ScrapeTool;\n refresh: RefreshVersionTool;\n search: SearchTool;\n listJobs: ListJobsTool;\n getJobInfo: GetJobInfoTool;\n cancelJob: CancelJobTool;\n remove: RemoveTool;\n fetchUrl: FetchUrlTool;\n}\n\n/**\n * Initializes and returns the shared tool instances.\n * This should be called after initializeServices has completed.\n * @param docService The initialized DocumentManagementService instance.\n * @param pipeline The initialized pipeline instance.\n * @param config The resolved configuration provided by the entrypoint.\n * @returns An object containing all instantiated tool instances.\n */\nexport async function initializeTools(\n docService: IDocumentManagement,\n pipeline: IPipeline,\n config: AppConfig,\n): Promise<McpServerTools> {\n const tools: McpServerTools = {\n listLibraries: new ListLibrariesTool(docService),\n findVersion: new FindVersionTool(docService),\n scrape: new ScrapeTool(pipeline, config.scraper),\n refresh: new RefreshVersionTool(pipeline),\n search: new SearchTool(docService),\n listJobs: new ListJobsTool(pipeline),\n getJobInfo: new GetJobInfoTool(pipeline),\n cancelJob: new CancelJobTool(pipeline),\n // clearCompletedJobs: new ClearCompletedJobsTool(pipeline),\n remove: new RemoveTool(docService, pipeline),\n fetchUrl: new FetchUrlTool(new AutoDetectFetcher(config.scraper), config),\n };\n\n return tools;\n}\n","/**\n * MCP service that registers MCP protocol routes for AI tool integration.\n * Provides modular server composition for MCP endpoints.\n */\n\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { SSEServerTransport } from \"@modelcontextprotocol/sdk/server/sse.js\";\nimport { StreamableHTTPServerTransport } from \"@modelcontextprotocol/sdk/server/streamableHttp.js\";\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport type { ProxyAuthManager } from \"../auth\";\nimport { createAuthMiddleware } from \"../auth/middleware\";\nimport { createMcpServerInstance } from \"../mcp/mcpServer\";\nimport { initializeTools } from \"../mcp/tools\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { telemetry } from \"../telemetry\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\n\n/**\n * Register MCP protocol routes on a Fastify server instance.\n * This includes SSE endpoints for persistent connections and HTTP endpoints for stateless requests.\n *\n * @param server The Fastify server instance\n * @param docService The document management service\n * @param pipeline The pipeline instance\n * @param config The resolved configuration from the entrypoint\n * @param authManager Optional authentication manager\n * @returns The McpServer instance for cleanup\n */\nexport async function registerMcpService(\n server: FastifyInstance,\n docService: IDocumentManagement,\n pipeline: IPipeline,\n config: AppConfig,\n authManager?: ProxyAuthManager,\n): Promise<McpServer> {\n // Initialize MCP server and tools\n const mcpTools = await initializeTools(docService, pipeline, config);\n const mcpServer = createMcpServerInstance(mcpTools, config);\n\n // Setup auth middleware if auth manager is provided\n const authMiddleware = authManager ? createAuthMiddleware(authManager) : null;\n\n // Track SSE transports for cleanup\n const sseTransports: Record<string, SSEServerTransport> = {};\n\n // Track SSE server instances for cleanup\n const sseServers: Record<string, McpServer> = {};\n\n // Track heartbeat intervals for cleanup\n const heartbeatIntervals: Record<string, NodeJS.Timeout> = {};\n\n // SSE endpoint for MCP connections\n server.route({\n method: \"GET\",\n url: \"/sse\",\n preHandler: authMiddleware ? [authMiddleware] : undefined,\n handler: async (_request: FastifyRequest, reply: FastifyReply) => {\n try {\n // Handle SSE connection using raw response\n const transport = new SSEServerTransport(\"/messages\", reply.raw);\n sseTransports[transport.sessionId] = transport;\n\n const sessionServer = createMcpServerInstance(mcpTools, config);\n sseServers[transport.sessionId] = sessionServer;\n\n // Log client connection (simple connection tracking without sessions)\n if (telemetry.isEnabled()) {\n logger.info(`🔗 MCP client connected: ${transport.sessionId}`);\n }\n\n // Start heartbeat to keep connection alive and prevent client timeouts\n // SSE comments (lines starting with ':') are ignored by clients but keep the connection active\n const heartbeatInterval = setInterval(() => {\n try {\n reply.raw.write(\": heartbeat\\n\\n\");\n } catch {\n // Connection likely closed, cleanup will happen in close handler\n clearInterval(heartbeatInterval);\n delete heartbeatIntervals[transport.sessionId];\n }\n }, config.server.heartbeatMs);\n heartbeatIntervals[transport.sessionId] = heartbeatInterval;\n\n // Cleanup function to handle both close and error scenarios\n const cleanupConnection = () => {\n const interval = heartbeatIntervals[transport.sessionId];\n if (interval) {\n clearInterval(interval);\n delete heartbeatIntervals[transport.sessionId];\n }\n\n const serverToClose = sseServers[transport.sessionId];\n if (serverToClose) {\n delete sseServers[transport.sessionId];\n void serverToClose.close().catch((error) => {\n logger.error(`❌ Failed to close SSE server instance: ${error}`);\n });\n }\n\n delete sseTransports[transport.sessionId];\n transport.close();\n\n // Log client disconnection\n if (telemetry.isEnabled()) {\n logger.info(`🔗 MCP client disconnected: ${transport.sessionId}`);\n }\n };\n\n reply.raw.on(\"close\", cleanupConnection);\n\n // Handle stream errors (e.g., client disconnects abruptly)\n reply.raw.on(\"error\", (error) => {\n logger.debug(`SSE connection error: ${error}`);\n cleanupConnection();\n });\n\n await sessionServer.connect(transport);\n } catch (error) {\n logger.error(`❌ Error in SSE endpoint: ${error}`);\n reply.code(500).send({\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n });\n\n // SSE message handling endpoint\n server.route({\n method: \"POST\",\n url: \"/messages\",\n handler: async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n const url = new URL(request.url, `http://${request.headers.host}`);\n const sessionId = url.searchParams.get(\"sessionId\");\n const transport = sessionId ? sseTransports[sessionId] : undefined;\n\n if (transport) {\n await transport.handlePostMessage(request.raw, reply.raw, request.body);\n } else {\n reply.code(400).send({ error: \"No transport found for sessionId\" });\n }\n } catch (error) {\n logger.error(`❌ Error in messages endpoint: ${error}`);\n reply.code(500).send({\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n });\n\n // Streamable HTTP endpoint for stateless MCP requests\n server.route({\n method: \"POST\",\n url: \"/mcp\",\n preHandler: authMiddleware ? [authMiddleware] : undefined,\n handler: async (request: FastifyRequest, reply: FastifyReply) => {\n try {\n // In stateless mode, create a new instance of server and transport for each request\n const requestServer = createMcpServerInstance(mcpTools, config);\n const requestTransport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n });\n\n const cleanupRequest = () => {\n logger.debug(\"Streamable HTTP request closed\");\n requestTransport.close();\n requestServer.close(); // Close the per-request server instance\n };\n\n reply.raw.on(\"close\", cleanupRequest);\n reply.raw.on(\"error\", (error) => {\n logger.debug(`Streamable HTTP connection error: ${error}`);\n cleanupRequest();\n });\n\n await requestServer.connect(requestTransport);\n await requestTransport.handleRequest(request.raw, reply.raw, request.body);\n } catch (error) {\n logger.error(`❌ Error in MCP endpoint: ${error}`);\n reply.code(500).send({\n error: error instanceof Error ? error.message : String(error),\n });\n }\n },\n });\n\n server.route({\n method: \"GET\",\n url: \"/mcp\",\n preHandler: authMiddleware ? [authMiddleware] : undefined,\n handler: async (_request: FastifyRequest, reply: FastifyReply) => {\n reply.code(405).header(\"Allow\", \"POST\").send();\n },\n });\n\n // Store reference to SSE transports on the server instance for cleanup\n (\n mcpServer as unknown as {\n _sseTransports: Record<string, SSEServerTransport>;\n _sseServers: Record<string, McpServer>;\n _heartbeatIntervals: Record<string, NodeJS.Timeout>;\n }\n )._sseTransports = sseTransports;\n (\n mcpServer as unknown as {\n _sseServers: Record<string, McpServer>;\n }\n )._sseServers = sseServers;\n (\n mcpServer as unknown as {\n _heartbeatIntervals: Record<string, NodeJS.Timeout>;\n }\n )._heartbeatIntervals = heartbeatIntervals;\n\n return mcpServer;\n}\n\n/**\n * Clean up MCP service resources including SSE transports.\n */\nexport async function cleanupMcpService(mcpServer: McpServer): Promise<void> {\n try {\n // Clear all heartbeat intervals\n const heartbeatIntervals = (\n mcpServer as unknown as {\n _heartbeatIntervals: Record<string, NodeJS.Timeout>;\n }\n )._heartbeatIntervals;\n if (heartbeatIntervals) {\n for (const interval of Object.values(heartbeatIntervals)) {\n clearInterval(interval);\n }\n }\n\n // Close all SSE transports\n const sseTransports = (\n mcpServer as unknown as {\n _sseTransports: Record<string, SSEServerTransport>;\n }\n )._sseTransports;\n if (sseTransports) {\n for (const transport of Object.values(sseTransports)) {\n await transport.close();\n }\n }\n\n const sseServers = (\n mcpServer as unknown as {\n _sseServers: Record<string, McpServer>;\n }\n )._sseServers;\n if (sseServers) {\n for (const server of Object.values(sseServers)) {\n await server.close();\n }\n }\n\n // Close MCP server\n await mcpServer.close();\n logger.debug(\"MCP service cleaned up\");\n } catch (error) {\n logger.error(`❌ Failed to cleanup MCP service: ${error}`);\n throw error;\n }\n}\n","/**\n * tRPC router for event subscriptions.\n * Allows remote processes to subscribe to application events via tRPC.\n */\n\nimport { initTRPC } from \"@trpc/server\";\nimport { observable } from \"@trpc/server/observable\";\nimport superjson from \"superjson\";\nimport { z } from \"zod\";\nimport type { EventBusService } from \"../EventBusService\";\nimport { EventType } from \"../types\";\n\n// Context carries the event bus instance\nexport interface EventsTrpcContext {\n eventBus: EventBusService;\n}\n\nconst t = initTRPC.context<EventsTrpcContext>().create({\n transformer: superjson,\n});\n\n/**\n * Factory to create an events router from any t instance whose context contains `eventBus`.\n */\nexport function createEventsRouter(trpc: unknown) {\n const tt = trpc as typeof t;\n\n return tt.router({\n /**\n * Subscribe to all application events.\n * Clients receive a stream of events as they occur.\n */\n subscribe: tt.procedure\n .input(\n z\n .object({\n events: z.array(z.nativeEnum(EventType)).optional(),\n })\n .optional(),\n )\n .subscription(({ ctx, input }) => {\n // Determine which events to subscribe to (default: all)\n const eventTypes = input?.events ?? Object.values(EventType);\n\n return observable<{\n type: EventType;\n payload: unknown;\n }>((emit) => {\n const unsubscribers: Array<() => void> = [];\n\n // Subscribe to each requested event type\n for (const eventType of eventTypes) {\n const unsubscribe = ctx.eventBus.on(eventType, (payload) => {\n // EventBus already emits public types (PipelineJob), no conversion needed\n emit.next({\n type: eventType,\n payload,\n });\n });\n\n unsubscribers.push(unsubscribe);\n }\n\n // Cleanup function called when client disconnects\n return () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n });\n }),\n });\n}\n\n// Default router for standalone usage\nexport const eventsRouter = createEventsRouter(t);\nexport type EventsRouter = typeof eventsRouter;\n","/**\n * tRPC router exposing pipeline procedures for external workers.\n * Provides a minimal RPC surface to replace legacy REST endpoints.\n *\n * This module now exports a factory to build the router from a provided t instance,\n * allowing us to compose multiple routers under a single /api endpoint.\n */\n\nimport { initTRPC } from \"@trpc/server\";\nimport superjson from \"superjson\";\nimport { z } from \"zod\";\nimport type { ScraperOptions } from \"../../scraper/types\";\nimport { PipelineJobStatus } from \"../types\";\nimport type { IPipeline } from \"./interfaces\";\n\n// Context carries the pipeline instance\nexport interface PipelineTrpcContext {\n pipeline: IPipeline;\n}\n\nconst t = initTRPC.context<PipelineTrpcContext>().create({\n transformer: superjson,\n});\n\n// Schemas\nconst nonEmptyTrimmed = z\n .string()\n .transform((s) => s.trim())\n .refine((s) => s.length > 0, \"must not be empty\");\n\nconst optionalTrimmed = z.preprocess(\n (v) => (typeof v === \"string\" ? v.trim() : v),\n z.string().min(1).optional().nullable(),\n);\n\nconst enqueueScrapeInput = z.object({\n library: nonEmptyTrimmed,\n version: optionalTrimmed,\n options: z.custom<ScraperOptions>(),\n});\n\nconst enqueueRefreshInput = z.object({\n library: nonEmptyTrimmed,\n version: optionalTrimmed,\n});\n\nconst jobIdInput = z.object({ id: z.string().min(1) });\n\nconst getJobsInput = z.object({\n status: z.nativeEnum(PipelineJobStatus).optional(),\n});\n\n// Factory to create a pipeline router from any t instance whose context contains `pipeline`\nexport function createPipelineRouter(trpc: unknown) {\n const tt = trpc as typeof t;\n return tt.router({\n ping: tt.procedure.query(async () => ({ status: \"ok\", ts: Date.now() })),\n\n enqueueScrapeJob: tt.procedure\n .input(enqueueScrapeInput)\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof enqueueScrapeInput>;\n }) => {\n const jobId = await ctx.pipeline.enqueueScrapeJob(\n input.library,\n input.version ?? null,\n input.options,\n );\n\n return { jobId };\n },\n ),\n\n enqueueRefreshJob: tt.procedure\n .input(enqueueRefreshInput)\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof enqueueRefreshInput>;\n }) => {\n const jobId = await ctx.pipeline.enqueueRefreshJob(\n input.library,\n input.version ?? null,\n );\n\n return { jobId };\n },\n ),\n\n getJob: tt.procedure\n .input(jobIdInput)\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof jobIdInput>;\n }) => {\n return ctx.pipeline.getJob(input.id);\n },\n ),\n\n getJobs: tt.procedure\n .input(getJobsInput.optional())\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof getJobsInput> | undefined;\n }) => {\n const jobs = await ctx.pipeline.getJobs(input?.status);\n return { jobs };\n },\n ),\n\n cancelJob: tt.procedure\n .input(jobIdInput)\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof jobIdInput>;\n }) => {\n await ctx.pipeline.cancelJob(input.id);\n return { success: true } as const;\n },\n ),\n\n clearCompletedJobs: tt.procedure.mutation(\n async ({ ctx }: { ctx: PipelineTrpcContext }) => {\n const count = await ctx.pipeline.clearCompletedJobs();\n return { count };\n },\n ),\n });\n}\n\n// Default router for standalone usage (keeps existing imports working)\nexport const pipelineRouter = createPipelineRouter(t);\n\nexport type PipelineRouter = typeof pipelineRouter;\n","/**\n * tRPC router exposing document data store operations via the worker API.\n * Only procedures actually used externally are included to keep surface minimal.\n */\nimport { initTRPC } from \"@trpc/server\";\nimport superjson from \"superjson\";\nimport { z } from \"zod\";\nimport type {\n DbVersionWithLibrary,\n FindVersionResult,\n StoreSearchResult,\n VersionStatus,\n} from \"../types\";\nimport type { IDocumentManagement } from \"./interfaces\";\n\n// Context carries the document management API\nexport interface DataTrpcContext {\n docService: IDocumentManagement;\n}\n\nconst t = initTRPC.context<DataTrpcContext>().create({\n transformer: superjson,\n});\n\n// Common schemas\nconst nonEmpty = z\n .string()\n .min(1)\n .transform((s) => s.trim());\nconst optionalVersion = z\n .string()\n .optional()\n .nullable()\n .transform((v) => (typeof v === \"string\" ? v.trim() : v));\n\nexport function createDataRouter(trpc: unknown) {\n const tt = trpc as typeof t;\n return tt.router({\n ping: tt.procedure.query(async () => ({ status: \"ok\", ts: Date.now() })),\n\n listLibraries: tt.procedure.query(async ({ ctx }: { ctx: DataTrpcContext }) => {\n return await ctx.docService.listLibraries(); // LibrarySummary[]\n }),\n\n findBestVersion: tt.procedure\n .input(z.object({ library: nonEmpty, targetVersion: z.string().optional() }))\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { library: string; targetVersion?: string };\n }) => {\n const result = await ctx.docService.findBestVersion(\n input.library,\n input.targetVersion,\n );\n return result as FindVersionResult;\n },\n ),\n\n validateLibraryExists: tt.procedure\n .input(z.object({ library: nonEmpty }))\n .mutation(\n async ({ ctx, input }: { ctx: DataTrpcContext; input: { library: string } }) => {\n await ctx.docService.validateLibraryExists(input.library);\n return { ok: true } as const;\n },\n ),\n\n search: tt.procedure\n .input(\n z.object({\n library: nonEmpty,\n version: optionalVersion,\n query: nonEmpty,\n limit: z.number().int().positive().max(50).optional(),\n }),\n )\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: {\n library: string;\n version: string | null | undefined;\n query: string;\n limit?: number;\n };\n }) => {\n const results = await ctx.docService.searchStore(\n input.library,\n input.version ?? null,\n input.query,\n input.limit ?? 5,\n );\n\n return results as StoreSearchResult[];\n },\n ),\n\n removeVersion: tt.procedure\n .input(z.object({ library: nonEmpty, version: optionalVersion }))\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { library: string; version: string | null | undefined };\n }) => {\n await ctx.docService.removeVersion(input.library, input.version ?? null);\n return { ok: true } as const;\n },\n ),\n\n removeAllDocuments: tt.procedure\n .input(z.object({ library: nonEmpty, version: optionalVersion }))\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { library: string; version: string | null | undefined };\n }) => {\n await ctx.docService.removeAllDocuments(input.library, input.version ?? null);\n return { ok: true } as const;\n },\n ),\n\n // Status and version helpers\n\n getVersionsByStatus: tt.procedure\n .input(z.object({ statuses: z.array(z.string()) }))\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { statuses: string[] };\n }) => {\n // Cast trusting caller to pass valid VersionStatus strings\n const statuses = input.statuses as unknown as VersionStatus[];\n return (await ctx.docService.getVersionsByStatus(\n statuses,\n )) as DbVersionWithLibrary[];\n },\n ),\n\n findVersionsBySourceUrl: tt.procedure\n .input(z.object({ url: nonEmpty }))\n .query(async ({ ctx, input }: { ctx: DataTrpcContext; input: { url: string } }) => {\n return (await ctx.docService.findVersionsBySourceUrl(\n input.url,\n )) as DbVersionWithLibrary[];\n }),\n\n getScraperOptions: tt.procedure\n .input(z.object({ versionId: z.number().int().positive() }))\n .query(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { versionId: number };\n }) => {\n return await ctx.docService.getScraperOptions(input.versionId);\n },\n ),\n\n updateVersionStatus: tt.procedure\n .input(\n z.object({\n versionId: z.number().int().positive(),\n status: z.string(),\n errorMessage: z.string().optional().nullable(),\n }),\n )\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { versionId: number; status: string; errorMessage?: string | null };\n }) => {\n await ctx.docService.updateVersionStatus(\n input.versionId,\n input.status as VersionStatus,\n input.errorMessage ?? undefined,\n );\n return { ok: true } as const;\n },\n ),\n\n updateVersionProgress: tt.procedure\n .input(\n z.object({\n versionId: z.number().int().positive(),\n pages: z.number().int().nonnegative(),\n maxPages: z.number().int().positive(),\n }),\n )\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { versionId: number; pages: number; maxPages: number };\n }) => {\n await ctx.docService.updateVersionProgress(\n input.versionId,\n input.pages,\n input.maxPages,\n );\n return { ok: true } as const;\n },\n ),\n\n storeScraperOptions: tt.procedure\n .input(\n z.object({\n versionId: z.number().int().positive(),\n options: z.unknown(),\n }),\n )\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: DataTrpcContext;\n input: { versionId: number; options: unknown };\n }) => {\n // options conforms to ScraperOptions at the caller; keep as unknown here\n await ctx.docService.storeScraperOptions(\n input.versionId,\n input.options as unknown as Parameters<\n IDocumentManagement[\"storeScraperOptions\"]\n >[1],\n );\n return { ok: true } as const;\n },\n ),\n });\n}\n\n// Default router for standalone usage\nexport const dataRouter = createDataRouter(t);\nexport type DataRouter = typeof dataRouter;\n","/**\n * Fastify service to register unified tRPC API at /api.\n * Merges pipeline, data store, and events routers under a single endpoint.\n * Also provides WebSocket support for subscriptions.\n */\n\nimport { initTRPC } from \"@trpc/server\";\nimport { fastifyTRPCPlugin } from \"@trpc/server/adapters/fastify\";\nimport { applyWSSHandler } from \"@trpc/server/adapters/ws\";\nimport type { FastifyInstance } from \"fastify\";\nimport superjson from \"superjson\";\nimport type { WebSocketServer } from \"ws\";\nimport type { EventBusService } from \"../events\";\nimport { createEventsRouter, type EventsTrpcContext } from \"../events/trpc/router\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { createPipelineRouter, type PipelineTrpcContext } from \"../pipeline/trpc/router\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { createDataRouter, type DataTrpcContext } from \"../store/trpc/router\";\n\ntype UnifiedContext = PipelineTrpcContext & DataTrpcContext & EventsTrpcContext;\n\nexport async function registerTrpcService(\n server: FastifyInstance,\n pipeline: IPipeline,\n docService: IDocumentManagement,\n eventBus: EventBusService,\n): Promise<void> {\n const t = initTRPC.context<UnifiedContext>().create({\n transformer: superjson,\n });\n\n // Define a single root-level health check to avoid duplicate keys from feature routers\n const healthRouter = t.router({\n ping: t.procedure.query(async () => ({ status: \"ok\", ts: Date.now() })),\n });\n\n const router = t.router({\n ...healthRouter._def.procedures,\n ...createPipelineRouter(t)._def.procedures,\n ...createDataRouter(t)._def.procedures,\n events: createEventsRouter(t),\n });\n\n await server.register(fastifyTRPCPlugin, {\n prefix: \"/api\",\n trpcOptions: {\n router,\n createContext: async (): Promise<UnifiedContext> => ({\n pipeline,\n docService,\n eventBus,\n }),\n },\n });\n}\n\n/**\n * Apply WebSocket handler to a WebSocketServer for tRPC subscriptions.\n */\nexport function applyTrpcWebSocketHandler(\n wss: WebSocketServer,\n pipeline: IPipeline,\n docService: IDocumentManagement,\n eventBus: EventBusService,\n) {\n const t = initTRPC.context<UnifiedContext>().create({\n transformer: superjson,\n });\n\n const healthRouter = t.router({\n ping: t.procedure.query(async () => ({ status: \"ok\", ts: Date.now() })),\n });\n\n const router = t.router({\n ...healthRouter._def.procedures,\n ...createPipelineRouter(t)._def.procedures,\n ...createDataRouter(t)._def.procedures,\n events: createEventsRouter(t),\n });\n\n const handler = applyWSSHandler({\n wss,\n router,\n createContext: (): UnifiedContext => ({\n pipeline,\n docService,\n eventBus,\n }),\n });\n\n return handler;\n}\n","/**\n * SSE (Server-Sent Events) endpoint for real-time updates.\n * Clients connect to this endpoint to receive live updates about jobs and libraries.\n */\n\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport type { EventBusService } from \"../../events/EventBusService\";\nimport {\n type EventPayloads,\n EventType,\n ServerEventName,\n type SseEventPayloads,\n} from \"../../events/types\";\nimport type { PipelineJob } from \"../../pipeline/types\";\nimport type { ScraperProgressEvent } from \"../../scraper/types\";\nimport { logger } from \"../../utils/logger\";\n\n/**\n * Convert internal event payload to SSE payload format.\n */\nfunction convertToSsePayload(\n eventType: EventType,\n payload: EventPayloads[EventType],\n): SseEventPayloads[keyof SseEventPayloads] {\n switch (eventType) {\n case EventType.JOB_STATUS_CHANGE: {\n const job = payload as PipelineJob;\n return {\n id: job.id,\n library: job.library,\n version: job.version,\n status: job.status,\n error: job.error,\n createdAt: job.createdAt.toISOString(),\n startedAt: job.startedAt?.toISOString() ?? null,\n finishedAt: job.finishedAt?.toISOString() ?? null,\n sourceUrl: job.sourceUrl,\n } satisfies SseEventPayloads[\"job-status-change\"];\n }\n\n case EventType.JOB_PROGRESS: {\n const { job, progress } = payload as {\n job: PipelineJob;\n progress: ScraperProgressEvent;\n };\n return {\n id: job.id,\n library: job.library,\n version: job.version,\n progress: {\n pagesScraped: progress.pagesScraped,\n totalPages: progress.totalPages,\n totalDiscovered: progress.totalDiscovered,\n currentUrl: progress.currentUrl,\n depth: progress.depth,\n maxDepth: progress.maxDepth,\n },\n } satisfies SseEventPayloads[\"job-progress\"];\n }\n\n case EventType.LIBRARY_CHANGE: {\n return {} satisfies SseEventPayloads[\"library-change\"];\n }\n\n case EventType.JOB_LIST_CHANGE: {\n return {} satisfies SseEventPayloads[\"job-list-change\"];\n }\n\n default: {\n // TypeScript ensures this is unreachable if all cases are handled\n const _exhaustive: never = eventType;\n throw new Error(`Unhandled event type: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * Send an SSE message to the client.\n */\nfunction sendSseMessage(reply: FastifyReply, eventName: string, data: unknown): boolean {\n try {\n const message = `event: ${eventName}\\ndata: ${JSON.stringify(data)}\\n\\n`;\n reply.raw.write(message);\n return true;\n } catch (error) {\n logger.error(`❌ Failed to send SSE event: ${error}`);\n return false;\n }\n}\n\n/**\n * Registers the SSE events route.\n * @param server - The Fastify instance.\n * @param eventBus - The central event bus service instance.\n */\nexport function registerEventsRoute(\n server: FastifyInstance,\n eventBus: EventBusService,\n): void {\n server.get(\"/web/events\", async (request: FastifyRequest, reply: FastifyReply) => {\n // Set headers for SSE\n reply.raw.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache, no-transform\",\n Connection: \"keep-alive\",\n \"X-Accel-Buffering\": \"no\", // Disable buffering in nginx\n });\n\n // Send initial connection message\n reply.raw.write(\"data: connected\\n\\n\");\n logger.debug(\"SSE client connected\");\n\n // Subscribe to all event types using a generic handler\n const allEventTypes = [\n EventType.JOB_STATUS_CHANGE,\n EventType.JOB_PROGRESS,\n EventType.LIBRARY_CHANGE,\n EventType.JOB_LIST_CHANGE,\n ] as const;\n\n const unsubscribers: (() => void)[] = [];\n\n for (const eventType of allEventTypes) {\n const unsubscribe = eventBus.on(eventType, (payload) => {\n try {\n const eventName = ServerEventName[eventType];\n const ssePayload = convertToSsePayload(eventType, payload);\n logger.debug(\n `SSE forwarding event: ${eventName} ${JSON.stringify(ssePayload)}`,\n );\n sendSseMessage(reply, eventName, ssePayload);\n } catch (error) {\n logger.error(`❌ Failed to convert/send SSE event ${eventType}: ${error}`);\n }\n });\n\n unsubscribers.push(unsubscribe);\n logger.debug(`SSE listener registered for: ${ServerEventName[eventType]}`);\n }\n\n // Cleanup function to unsubscribe from all events\n const cleanup = () => {\n for (const unsubscribe of unsubscribers) {\n unsubscribe();\n }\n };\n\n // Send periodic heartbeat to keep connection alive\n const heartbeatInterval = setInterval(() => {\n try {\n reply.raw.write(\": heartbeat\\n\\n\");\n } catch (_error) {\n logger.debug(\"Failed to send heartbeat, client likely disconnected\");\n clearInterval(heartbeatInterval);\n }\n }, 30_000); // Every 30 seconds\n\n // Clean up when client disconnects\n request.raw.on(\"close\", () => {\n logger.debug(\"SSE client disconnected\");\n cleanup();\n clearInterval(heartbeatInterval);\n });\n\n // Handle errors\n request.raw.on(\"error\", (error) => {\n // This may happen when the client disconnects abruptly, the page is reloaded, etc.\n logger.debug(`SSE connection error: ${error}`);\n cleanup();\n clearInterval(heartbeatInterval);\n });\n });\n}\n","import type { PropsWithChildren } from \"@kitajs/html\";\n\n/**\n * Props for the PrimaryButton component.\n */\ninterface PrimaryButtonProps extends PropsWithChildren {\n type?: \"button\" | \"submit\" | \"reset\";\n class?: string;\n disabled?: boolean;\n [key: string]: unknown;\n}\n\n/**\n * A reusable primary button component with consistent styling.\n * Supports additional HTML attributes via spread.\n */\nconst PrimaryButton = ({\n children,\n type = \"button\",\n class: className = \"\",\n disabled = false,\n ...rest\n}: PrimaryButtonProps) => {\n const baseClasses =\n \"w-full flex justify-center py-1.5 px-3 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors duration-150\";\n const disabledClasses = disabled ? \"opacity-50 cursor-not-allowed\" : \"\";\n const combinedClasses =\n `${baseClasses} ${disabledClasses} ${className}`.trim();\n\n return (\n <button type={type} class={combinedClasses} disabled={disabled} {...rest}>\n {children}\n </button>\n );\n};\n\nexport default PrimaryButton;\n","import PrimaryButton from \"./PrimaryButton\";\n\n/**\n * A reusable button component for adding a new documentation job.\n * Encapsulates both styling and HTMX behavior.\n */\nconst AddJobButton = () => {\n return (\n <PrimaryButton\n hx-get=\"/web/jobs/new\"\n hx-target=\"#addJobForm\"\n hx-swap=\"innerHTML\"\n >\n Add New Documentation\n </PrimaryButton>\n );\n};\n\nexport default AddJobButton;\n","/**\n * Toast notification component using Flowbite styling and Alpine.js for state management.\n * Displays floating notifications for success, error, warning, and info messages.\n */\nconst Toast = () => {\n return (\n <div\n x-data\n x-show=\"$store.toast.visible\"\n x-transition:enter=\"transition ease-out duration-300\"\n x-transition:enter-start=\"opacity-0 transform translate-y-2\"\n x-transition:enter-end=\"opacity-100 transform translate-y-0\"\n x-transition:leave=\"transition ease-in duration-200\"\n x-transition:leave-start=\"opacity-100\"\n x-transition:leave-end=\"opacity-0\"\n class=\"fixed top-5 right-5 z-50\"\n style=\"display: none;\"\n >\n <div\n class=\"flex items-center w-full max-w-xs p-4 text-gray-500 bg-white rounded-lg shadow dark:text-gray-400 dark:bg-gray-800\"\n role=\"alert\"\n >\n {/* Icon based on type */}\n <div\n class=\"inline-flex items-center justify-center shrink-0 w-8 h-8 rounded-lg\"\n x-bind:class=\"{\n 'text-green-500 bg-green-100 dark:bg-green-800 dark:text-green-200': $store.toast.type === 'success',\n 'text-red-500 bg-red-100 dark:bg-red-800 dark:text-red-200': $store.toast.type === 'error',\n 'text-orange-500 bg-orange-100 dark:bg-orange-700 dark:text-orange-200': $store.toast.type === 'warning',\n 'text-blue-500 bg-blue-100 dark:bg-blue-800 dark:text-blue-200': $store.toast.type === 'info'\n }\"\n >\n {/* Success icon */}\n <svg\n x-show=\"$store.toast.type === 'success'\"\n class=\"w-5 h-5\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 8.207-4 4a1 1 0 0 1-1.414 0l-2-2a1 1 0 0 1 1.414-1.414L9 10.586l3.293-3.293a1 1 0 0 1 1.414 1.414Z\" />\n </svg>\n {/* Error icon */}\n <svg\n x-show=\"$store.toast.type === 'error'\"\n class=\"w-5 h-5\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm3.707 11.793a1 1 0 1 1-1.414 1.414L10 11.414l-2.293 2.293a1 1 0 0 1-1.414-1.414L8.586 10 6.293 7.707a1 1 0 0 1 1.414-1.414L10 8.586l2.293-2.293a1 1 0 0 1 1.414 1.414L11.414 10l2.293 2.293Z\" />\n </svg>\n {/* Warning icon */}\n <svg\n x-show=\"$store.toast.type === 'warning'\"\n class=\"w-5 h-5\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM10 15a1 1 0 1 1 0-2 1 1 0 0 1 0 2Zm1-4a1 1 0 0 1-2 0V6a1 1 0 0 1 2 0v5Z\" />\n </svg>\n {/* Info icon */}\n <svg\n x-show=\"$store.toast.type === 'info'\"\n class=\"w-5 h-5\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z\" />\n </svg>\n </div>\n {/* Message */}\n <div\n class=\"ml-3 text-sm font-normal\"\n x-text=\"$store.toast.message\"\n ></div>\n {/* Close button */}\n <button\n type=\"button\"\n class=\"ml-auto -mx-1.5 -my-1.5 bg-white text-gray-400 hover:text-gray-900 rounded-lg focus:ring-2 focus:ring-gray-300 p-1.5 hover:bg-gray-100 inline-flex items-center justify-center h-8 w-8 dark:text-gray-500 dark:hover:text-white dark:bg-gray-800 dark:hover:bg-gray-700\"\n x-on:click=\"$store.toast.hide()\"\n aria-label=\"Close\"\n >\n <span class=\"sr-only\">Close</span>\n <svg\n class=\"w-3 h-3\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 14 14\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6\"\n />\n </svg>\n </button>\n </div>\n </div>\n );\n};\n\nexport default Toast;\n","/**\n * Defines the shared HTML skeleton for all web pages, including the global\n * header with version badge and the hook for client-side update notifications.\n * The component resolves the current version from props or build-time injection\n * and renders placeholders that AlpineJS hydrates at runtime.\n */\nimport type { PropsWithChildren } from \"@kitajs/html\";\nimport Toast from \"./Toast\";\n\n/**\n * Props for the Layout component.\n */\ninterface LayoutProps extends PropsWithChildren {\n title: string;\n /** Optional version string to display next to the title. */\n version?: string;\n /** Event client configuration for real-time updates */\n eventClientConfig?: {\n useRemoteWorker: boolean;\n trpcUrl?: string;\n };\n}\n\n/**\n * Base HTML layout component for all pages.\n * Includes common head elements, header, and scripts.\n * @param props - Component props including title, version, children, and eventClientConfig.\n */\nconst Layout = ({\n title,\n version,\n children,\n eventClientConfig,\n}: LayoutProps) => {\n // Use provided version prop, or fall back to build-time injected version\n const versionString = version || __APP_VERSION__;\n const versionInitializer = `versionUpdate({ currentVersion: ${\n versionString ? `'${versionString}'` : \"null\"\n } })`;\n\n return (\n <html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title safe>{title}</title>\n\n {/* Favicons */}\n <link\n rel=\"apple-touch-icon\"\n sizes=\"57x57\"\n href=\"/apple-icon-57x57.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"60x60\"\n href=\"/apple-icon-60x60.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"72x72\"\n href=\"/apple-icon-72x72.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"76x76\"\n href=\"/apple-icon-76x76.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"114x114\"\n href=\"/apple-icon-114x114.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"120x120\"\n href=\"/apple-icon-120x120.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"144x144\"\n href=\"/apple-icon-144x144.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"152x152\"\n href=\"/apple-icon-152x152.png\"\n />\n <link\n rel=\"apple-touch-icon\"\n sizes=\"180x180\"\n href=\"/apple-icon-180x180.png\"\n />\n <link\n rel=\"icon\"\n type=\"image/png\"\n sizes=\"192x192\"\n href=\"/android-icon-192x192.png\"\n />\n <link\n rel=\"icon\"\n type=\"image/png\"\n sizes=\"32x32\"\n href=\"/favicon-32x32.png\"\n />\n <link\n rel=\"icon\"\n type=\"image/png\"\n sizes=\"96x96\"\n href=\"/favicon-96x96.png\"\n />\n <link\n rel=\"icon\"\n type=\"image/png\"\n sizes=\"16x16\"\n href=\"/favicon-16x16.png\"\n />\n <link rel=\"shortcut icon\" href=\"/favicon.ico\" />\n <link rel=\"manifest\" href=\"/manifest.json\" />\n <meta name=\"msapplication-TileColor\" content=\"#ffffff\" />\n <meta name=\"msapplication-TileImage\" content=\"/ms-icon-144x144.png\" />\n <meta name=\"theme-color\" content=\"#ffffff\" />\n {/* Bundled CSS (includes Tailwind and Flowbite) */}\n <link rel=\"stylesheet\" href=\"/assets/main.css\" />\n {/* Add style for htmx-indicator behavior (needed globally) */}\n <style>\n {`\n .htmx-indicator {\n display: none;\n }\n .htmx-request .htmx-indicator {\n display: block;\n }\n .htmx-request.htmx-indicator {\n display: block;\n }\n /* Default: Hide skeleton, show results container */\n #searchResultsContainer .search-skeleton { display: none; }\n #searchResultsContainer .search-results { display: block; } /* Or as needed */\n\n /* Request in progress: Show skeleton, hide results */\n #searchResultsContainer.htmx-request .search-skeleton { display: block; } /* Or flex etc. */\n #searchResultsContainer.htmx-request .search-results { display: none; }\n\n /* Keep button spinner logic */\n form .htmx-indicator .spinner { display: flex; }\n form .htmx-indicator .search-text { display: none; }\n form .spinner { display: none; }\n `}\n </style>\n </head>\n <body class=\"bg-gray-50 dark:bg-gray-900\" hx-ext=\"morph\">\n {/* Toast notification component */}\n <Toast />\n\n {/* Full-width header with grounded.tools branding */}\n <header\n class=\"bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700\"\n x-data={versionInitializer}\n x-init=\"queueCheck()\"\n >\n <div class=\"container max-w-2xl mx-auto px-4 py-4\">\n {/* Large screens: single row layout */}\n <div class=\"hidden sm:flex items-center justify-between\">\n <div class=\"flex items-center gap-3\">\n <a\n href=\"https://grounded.tools\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-xl font-medium text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand\"\n >\n <span class=\"text-primary-600 dark:text-primary-300\">\n grounded\n </span>\n <span class=\"text-accent-500\">.</span>\n <span class=\"text-gray-900 dark:text-gray-100\">tools</span>\n </a>\n <span class=\"text-gray-400 dark:text-gray-400\">|</span>\n <a\n href=\"/\"\n class=\"text-lg font-semibold text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand\"\n >\n Docs MCP Server\n </a>\n {versionString ? (\n <span\n safe\n class=\"text-sm font-normal text-gray-500 dark:text-slate-400\"\n title={`Version ${versionString}`}\n >\n v{versionString}\n </span>\n ) : null}\n </div>\n <div>\n <span\n x-show=\"hasUpdate\"\n x-cloak\n class=\"inline-flex items-center gap-2 rounded-full bg-amber-100 dark:bg-amber-500/20 px-3 py-1.5 text-sm font-medium text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span class=\"flex h-4 w-4 items-center justify-center rounded-full bg-amber-500 text-amber-800 dark:text-amber-900 text-xs font-bold\">\n !\n </span>\n <a\n x-bind:href=\"latestReleaseUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"hover:text-amber-800 dark:hover:text-amber-200 transition-colors\"\n >\n <span class=\"mr-1\">Update available</span>\n </a>\n </span>\n </div>\n </div>\n\n {/* Small screens: stacked layout */}\n <div class=\"sm:hidden space-y-2\">\n {/* Row 1: grounded.tools branding */}\n <div class=\"flex justify-center\">\n <a\n href=\"https://grounded.tools\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"text-xl font-medium text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand\"\n >\n <span class=\"text-primary-600 dark:text-primary-300\">\n grounded\n </span>\n <span class=\"text-accent-500\">.</span>\n <span class=\"text-gray-900 dark:text-gray-100\">tools</span>\n </a>\n </div>\n\n {/* Row 2: Docs MCP Server + Version */}\n <div class=\"flex items-center justify-center gap-2\">\n <a\n href=\"/\"\n class=\"text-lg font-semibold text-gray-900 dark:text-white hover:text-primary-500 dark:hover:text-primary-400 transition-colors font-brand\"\n >\n Docs MCP Server\n </a>\n {versionString ? (\n <span\n safe\n class=\"text-sm font-normal text-gray-500 dark:text-slate-400\"\n title={`Version ${versionString}`}\n >\n v{versionString}\n </span>\n ) : null}\n </div>\n\n {/* Row 3: Update notification */}\n <div class=\"flex justify-center\">\n <span\n x-show=\"hasUpdate\"\n x-cloak\n class=\"inline-flex items-center gap-2 rounded-full bg-amber-100 dark:bg-amber-500/20 px-3 py-1.5 text-sm font-medium text-amber-700 dark:text-amber-300 border border-amber-200 dark:border-amber-500/30\"\n role=\"status\"\n aria-live=\"polite\"\n >\n <span class=\"flex h-4 w-4 items-center justify-center rounded-full bg-amber-500 text-amber-800 dark:text-amber-900 text-xs font-bold\">\n !\n </span>\n <a\n x-bind:href=\"latestReleaseUrl\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"hover:text-amber-800 dark:hover:text-amber-200 transition-colors\"\n >\n <span class=\"mr-1\">Update available</span>\n </a>\n </span>\n </div>\n </div>\n </div>\n </header>\n\n <div class=\"container max-w-2xl mx-auto px-4 py-6\">\n <main>{children}</main>\n </div>\n\n {/* Event client configuration */}\n <script>\n {`window.__EVENT_CLIENT_CONFIG__ = ${JSON.stringify(eventClientConfig)};`}\n </script>\n\n {/* Bundled JS (includes Flowbite, HTMX, AlpineJS, and initialization) */}\n <script type=\"module\" src=\"/assets/main.js\"></script>\n </body>\n </html>\n );\n};\n\nexport default Layout;\n","import type { FastifyInstance } from \"fastify\";\nimport AddJobButton from \"../components/AddJobButton\";\nimport Layout from \"../components/Layout\";\n\n/**\n * Registers the root route that serves the main HTML page.\n * @param server - The Fastify instance.\n * @param externalWorkerUrl - Optional URL for external worker service.\n */\nexport function registerIndexRoute(\n server: FastifyInstance,\n externalWorkerUrl?: string\n) {\n server.get(\"/\", async (_, reply) => {\n reply.type(\"text/html\");\n\n // Determine if we're using a remote worker\n const useRemoteWorker = Boolean(externalWorkerUrl);\n const trpcUrl = externalWorkerUrl ? `${externalWorkerUrl}/api` : undefined;\n\n // Use the Layout component and define the main content within it\n return (\n \"<!DOCTYPE html>\" +\n (\n <Layout\n title=\"MCP Docs\"\n eventClientConfig={{\n useRemoteWorker,\n trpcUrl,\n }}\n >\n {/* Analytics Section */}\n <div\n id=\"analytics-stats\"\n hx-get=\"/web/stats\"\n hx-trigger=\"load, library-change from:body\"\n hx-swap=\"morph:innerHTML\"\n >\n <div class=\"grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4 animate-pulse\">\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20\" />\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20\" />\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600 h-20\" />\n </div>\n </div>\n {/* Job Queue Section */}\n <section class=\"mb-4 p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600\">\n <div class=\"flex items-center justify-between mb-2\">\n <h2 class=\"text-xl font-semibold text-gray-900 dark:text-white\">\n Job Queue\n </h2>\n <button\n id=\"clear-completed-btn\"\n type=\"button\"\n class=\"text-xs px-3 py-1.5 text-gray-400 bg-gray-50 border border-gray-200 rounded-lg cursor-not-allowed focus:ring-4 focus:outline-none transition-colors duration-150 dark:bg-gray-700 dark:text-gray-500 dark:border-gray-600\"\n title=\"Clear all completed, cancelled, and failed jobs\"\n hx-post=\"/web/jobs/clear-completed\"\n hx-trigger=\"click\"\n hx-on=\"htmx:afterRequest: document.dispatchEvent(new Event('job-list-refresh'))\"\n hx-swap=\"none\"\n disabled\n >\n Clear Completed Jobs\n </button>\n </div>\n {/* Container for the job list, loaded via HTMX and updated via SSE */}\n <div\n id=\"job-queue\"\n hx-get=\"/web/jobs\"\n hx-trigger=\"load, job-status-change from:body, job-progress from:body, job-list-change from:body, job-list-refresh from:body\"\n hx-swap=\"morph:innerHTML\"\n >\n {/* Initial loading state */}\n <div class=\"animate-pulse\">\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4\" />\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5\" />\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5\" />\n </div>\n </div>\n </section>\n {/* Add New Job Section */}\n <section class=\"mb-8\">\n {/* Button to reveal the scrape form, loaded via HTMX */}\n <div id=\"addJobForm\">\n <AddJobButton />\n </div>\n </section>\n {/* Indexed Documentation Section */}\n <div>\n <h2 class=\"text-xl font-semibold mb-2 text-gray-900 dark:text-white\">\n Indexed Documentation\n </h2>\n <div\n id=\"indexed-docs\"\n hx-get=\"/web/libraries\"\n hx-trigger=\"load, library-change from:body\"\n hx-swap=\"morph:innerHTML\"\n >\n <div class=\"animate-pulse\">\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-48 mb-4\" />\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5\" />\n <div class=\"h-[0.8em] bg-gray-200 rounded-full dark:bg-gray-700 w-full mb-2.5\" />\n </div>\n </div>\n </div>\n </Layout>\n )\n );\n });\n}\n","import type { FastifyInstance } from \"fastify\";\nimport type { CancelJobTool } from \"../../../tools/CancelJobTool\";\nimport { ToolError } from \"../../../tools/errors\";\n\n/**\n * Registers the API route for cancelling jobs.\n * @param server - The Fastify instance.\n * @param cancelJobTool - The tool instance for cancelling jobs.\n */\nexport function registerCancelJobRoute(\n server: FastifyInstance,\n cancelJobTool: CancelJobTool\n) {\n // POST /web/jobs/:jobId/cancel - Cancel a job by ID\n server.post<{ Params: { jobId: string } }>(\n \"/web/jobs/:jobId/cancel\",\n async (request, reply) => {\n const { jobId } = request.params;\n try {\n await cancelJobTool.execute({ jobId });\n return { success: true, message: \"Job cancelled successfully\" };\n } catch (error) {\n if (error instanceof ToolError) {\n reply.status(400);\n return { success: false, message: error.message };\n } else {\n reply.status(500);\n return { success: false, message: \"Internal server error\" };\n }\n }\n }\n );\n}\n","import type { FastifyInstance } from \"fastify\";\nimport type { ClearCompletedJobsTool } from \"../../../tools/ClearCompletedJobsTool\";\nimport { ToolError } from \"../../../tools/errors\";\n\n/**\n * Registers the API route for clearing completed jobs.\n * @param server - The Fastify instance.\n * @param clearCompletedJobsTool - The tool instance for clearing completed jobs.\n */\nexport function registerClearCompletedJobsRoute(\n server: FastifyInstance,\n clearCompletedJobsTool: ClearCompletedJobsTool\n) {\n // POST /web/jobs/clear-completed - Clear all completed jobs\n server.post(\"/web/jobs/clear-completed\", async (_, reply) => {\n try {\n await clearCompletedJobsTool.execute({});\n\n reply.type(\"application/json\");\n return {\n success: true,\n message: \"Completed jobs cleared successfully\",\n };\n } catch (error) {\n if (error instanceof ToolError) {\n reply.code(400);\n return {\n success: false,\n message: error.message,\n };\n } else {\n reply.code(500);\n return {\n success: false,\n message: `Internal server error: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n }\n });\n}\n","interface VersionBadgeProps {\n version: string | null;\n}\n\nconst VersionBadge = ({ version }: VersionBadgeProps) => {\n if (!version) {\n return null; // Don't render if no version is provided\n }\n\n return (\n <span class=\"bg-primary-100 text-primary-800 text-xs font-medium me-2 px-1.5 py-0.5 rounded dark:bg-primary-900 dark:text-primary-300\">\n <span safe>{version}</span>\n </span>\n );\n};\n\nexport default VersionBadge;\n","/**\n * StatusBadge component displays version status with appropriate styling.\n * Uses database VersionStatus and helper functions for proper display.\n */\n\nimport { VersionStatus, getStatusDescription } from \"../../store/types\";\n\ninterface StatusBadgeProps {\n status: VersionStatus;\n showDescription?: boolean;\n}\n\n/**\n * Get CSS classes for status badge based on status type.\n */\nfunction getStatusClasses(status: VersionStatus): string {\n const baseClasses = \"px-1.5 py-0.5 text-xs font-medium rounded\";\n\n switch (status) {\n case VersionStatus.COMPLETED:\n return `${baseClasses} bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300`;\n case VersionStatus.RUNNING:\n case VersionStatus.UPDATING:\n return `${baseClasses} bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300`;\n case VersionStatus.QUEUED:\n return `${baseClasses} bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-300`;\n case VersionStatus.FAILED:\n return `${baseClasses} bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300`;\n case VersionStatus.CANCELLED:\n return `${baseClasses} bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300`;\n case VersionStatus.NOT_INDEXED:\n default:\n return `${baseClasses} bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400`;\n }\n}\n\nconst StatusBadge = ({ status, showDescription = true }: StatusBadgeProps) => (\n <span class={getStatusClasses(status)}>\n {showDescription ? getStatusDescription(status) : status}\n </span>\n);\n\nexport default StatusBadge;\n","/**\n * ProgressBar component displays indexing progress.\n * Shows pages processed out of total discovered pages with visual progress bar.\n * The progress reflects actual queue-based progress: processed vs. discovered pages.\n */\n\ninterface ProgressBarProps {\n progress: {\n pages: number;\n totalPages: number; // Effective total pages (limited by maxPages config)\n totalDiscovered: number; // Total pages actually discovered\n };\n showText?: boolean;\n}\n\nconst ProgressBar = ({ progress, showText = true }: ProgressBarProps) => {\n // Handle the initial case where we only know about 1 page (starting URL)\n // and haven't discovered any additional pages yet.\n const isIndeterminate = progress.totalDiscovered === 1;\n\n const percentage =\n progress.totalPages > 0\n ? Math.round((progress.pages / progress.totalPages) * 100)\n : 0;\n\n // Create the progress text\n const getProgressText = () => {\n if (isIndeterminate) {\n return \"Discovering pages...\";\n }\n\n const baseText = `${progress.pages}/${progress.totalPages} pages (${percentage}%)`;\n\n // If we discovered more pages than the limit, show the total discovered\n if (progress.totalDiscovered > progress.totalPages) {\n return `${baseText} • ${progress.totalDiscovered} total`;\n }\n\n return baseText;\n };\n\n return (\n <div class=\"w-full\">\n {showText && (\n <div class=\"flex justify-between text-xs text-gray-600 dark:text-gray-400 mb-1\">\n <span>Progress</span>\n <span safe>{getProgressText()}</span>\n </div>\n )}\n <div class=\"w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700\">\n {isIndeterminate ? (\n // Indeterminate progress bar with animation\n <div\n class=\"bg-blue-600 h-2 rounded-full animate-pulse\"\n style=\"width: 30%\"\n ></div>\n ) : (\n <div\n class=\"bg-blue-600 h-2 rounded-full transition-all duration-300\"\n style={`width: ${percentage}%`}\n ></div>\n )}\n </div>\n </div>\n );\n};\n\nexport default ProgressBar;\n","interface LoadingSpinnerProps {\n /** Additional Tailwind classes for color and size customization. Defaults to \"text-white\" */\n class?: string;\n}\n\n/**\n * Renders an SVG loading spinner icon.\n * Used for indicating loading states in buttons or other elements.\n */\nconst LoadingSpinner = ({\n class: className = \"text-white\",\n}: LoadingSpinnerProps) => (\n <svg\n class={`animate-spin h-4 w-4 ${className}`}\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n ></path>\n </svg>\n);\n\nexport default LoadingSpinner;\n","import type { JobInfo } from \"../../tools/GetJobInfoTool\";\nimport { PipelineJobStatus } from \"../../pipeline/types\";\nimport { VersionStatus, isActiveStatus } from \"../../store/types\";\nimport VersionBadge from \"./VersionBadge\";\nimport StatusBadge from \"./StatusBadge\";\nimport ProgressBar from \"./ProgressBar\";\nimport LoadingSpinner from \"./LoadingSpinner\";\n\n/**\n * Props for the JobItem component.\n */\ninterface JobItemProps {\n job: JobInfo;\n}\n\n/**\n * Renders a single job item with its details and status.\n * @param props - Component props including the job information.\n */\nconst JobItem = ({ job }: JobItemProps) => {\n // Use database status if available, fallback to pipeline status\n const displayStatus = job.dbStatus || job.status;\n const isActiveJob = job.dbStatus\n ? isActiveStatus(job.dbStatus)\n : job.status === PipelineJobStatus.QUEUED ||\n job.status === PipelineJobStatus.RUNNING;\n\n // Define state-specific button classes for Alpine toggling\n const defaultStateClasses =\n \"border border-gray-300 bg-white text-red-600 hover:bg-red-50 focus:ring-4 focus:outline-none focus:ring-red-100 dark:border-gray-600 dark:bg-gray-800 dark:text-red-400 dark:hover:bg-gray-700 dark:focus:ring-red-900\";\n const confirmingStateClasses =\n \"bg-red-600 text-white border-red-600 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-700 dark:border-red-700 dark:focus:ring-red-800\";\n\n return (\n <div\n id={`job-item-${job.id}`}\n class=\"block p-3 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600\"\n data-job-id={job.id}\n x-data=\"{ jobId: $el.dataset.jobId, confirming: $el.dataset.confirming === 'true', isStopping: false }\"\n >\n <div class=\"flex items-start justify-between\">\n <div class=\"flex-1\">\n <p class=\"text-sm font-medium text-gray-900 dark:text-white\">\n <span safe>{job.library}</span>{\" \"}\n <VersionBadge version={job.version} />\n </p>\n\n {/* Timestamps */}\n <div class=\"text-xs text-gray-500 dark:text-gray-400 mt-1\">\n {job.startedAt ? (\n <div>\n Last Indexed:{\" \"}\n <span safe>{new Date(job.startedAt).toLocaleString()}</span>\n </div>\n ) : null}\n </div>\n\n {/* Progress bar for active jobs */}\n {job.progress && job.progress.totalPages > 0 && isActiveJob ? (\n <div class=\"mt-2\">\n <ProgressBar progress={job.progress} />\n </div>\n ) : null}\n\n {/* Error message display */}\n {job.errorMessage || job.error ? (\n <div class=\"mt-2 p-2 bg-red-50 dark:bg-red-900/20 border border-red-200 dark:border-red-800 rounded text-xs\">\n <div class=\"font-medium text-red-800 dark:text-red-300 mb-1\">\n Error:\n </div>\n <div safe class=\"text-red-700 dark:text-red-400\">\n {job.errorMessage || job.error}\n </div>\n </div>\n ) : null}\n </div>\n\n <div class=\"flex flex-col items-end gap-2 ml-4\">\n {/* Status badge */}\n <div class=\"flex items-center gap-2\">\n {job.dbStatus ? (\n <StatusBadge status={job.dbStatus} />\n ) : (\n <span\n class={`px-1.5 py-0.5 text-xs font-medium rounded ${\n job.status === PipelineJobStatus.COMPLETED\n ? \"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300\"\n : job.error\n ? \"bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300\"\n : \"bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300\"\n }`}\n >\n {job.status}\n </span>\n )}\n\n {/* Stop button for active jobs */}\n {isActiveJob && (\n <button\n type=\"button\"\n class=\"font-medium rounded-lg text-xs p-1 text-center inline-flex items-center transition-colors duration-150 ease-in-out\"\n title=\"Stop this job\"\n x-bind:class={`confirming ? '${confirmingStateClasses}' : '${defaultStateClasses}'`}\n x-on:click=\"\n if (confirming) {\n isStopping = true;\n window.confirmationManager.clear($root.id);\n fetch('/web/jobs/' + jobId + '/cancel', {\n method: 'POST',\n headers: { 'Accept': 'application/json' },\n })\n .then(r => r.json())\n .then(() => {\n confirming = false;\n isStopping = false;\n document.dispatchEvent(new CustomEvent('job-list-refresh'));\n })\n .catch(() => { isStopping = false; });\n } else {\n confirming = true;\n isStopping = false;\n window.confirmationManager.start($root.id);\n }\n \"\n x-bind:disabled=\"isStopping\"\n >\n <span x-show=\"!confirming && !isStopping\">\n {/* Red Stop Icon */}\n <svg\n class=\"w-4 h-4\"\n aria-hidden=\"true\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <rect x=\"5\" y=\"5\" width=\"10\" height=\"10\" rx=\"2\" />\n </svg>\n <span class=\"sr-only\">Stop job</span>\n </span>\n <span x-show=\"confirming && !isStopping\" class=\"px-2\">\n Cancel?\n </span>\n <span x-show=\"isStopping\">\n <LoadingSpinner />\n <span class=\"sr-only\">Stopping...</span>\n </span>\n </button>\n )}\n </div>\n {job.error ? (\n // Keep the error badge for clarity if an error occurred\n <span class=\"bg-red-100 text-red-800 text-xs font-medium px-1.5 py-0.5 rounded dark:bg-red-900 dark:text-red-300\">\n Error\n </span>\n ) : null}\n </div>\n </div>\n </div>\n );\n};\n\nexport default JobItem;\n","import type { JobInfo } from \"../../tools/GetJobInfoTool\";\nimport JobItem from \"./JobItem\";\n\n/**\n * Props for the JobList component.\n */\ninterface JobListProps {\n jobs: JobInfo[];\n}\n\n/**\n * Renders a list of JobItem components or a message if the list is empty.\n * Also renders an out-of-band swap for the \"Clear Completed Jobs\" button state.\n * @param props - Component props including the array of jobs.\n */\nconst JobList = ({ jobs }: JobListProps) => {\n const hasJobs = jobs.length > 0;\n\n return (\n <>\n <div id=\"job-list\" class=\"space-y-2 animate-[fadeSlideIn_0.2s_ease-out]\">\n {hasJobs ? (\n jobs.map((job) => <JobItem job={job} />)\n ) : (\n <p class=\"text-center text-gray-500 dark:text-gray-400\">\n No pending jobs.\n </p>\n )}\n </div>\n {/* Out-of-band swap for the Clear Completed Jobs button */}\n <button\n id=\"clear-completed-btn\"\n hx-swap-oob=\"true\"\n type=\"button\"\n class={`text-xs px-3 py-1.5 rounded-lg focus:ring-4 focus:outline-none transition-colors duration-150 ${\n hasJobs\n ? \"text-gray-700 bg-gray-100 border border-gray-300 hover:bg-gray-200 focus:ring-gray-100 dark:bg-gray-600 dark:text-gray-300 dark:border-gray-500 dark:hover:bg-gray-700 dark:focus:ring-gray-700\"\n : \"text-gray-400 bg-gray-50 border border-gray-200 cursor-not-allowed dark:bg-gray-700 dark:text-gray-500 dark:border-gray-600\"\n }`}\n title=\"Clear all completed, cancelled, and failed jobs\"\n hx-post=\"/web/jobs/clear-completed\"\n hx-trigger=\"click\"\n hx-on=\"htmx:afterRequest: document.dispatchEvent(new Event('job-list-refresh'))\"\n hx-swap=\"none\"\n disabled={!hasJobs}\n >\n Clear Completed Jobs\n </button>\n </>\n );\n};\n\nexport default JobList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListJobsTool } from \"../../../tools/ListJobsTool\";\nimport JobList from \"../../components/JobList\"; // Import the extracted component\n\n/**\n * Registers the API route for listing jobs.\n * @param server - The Fastify instance.\n * @param listJobsTool - The tool instance for listing jobs.\n */\nexport function registerJobListRoutes(\n server: FastifyInstance,\n listJobsTool: ListJobsTool\n) {\n // GET /web/jobs - List current jobs (only the list)\n server.get(\"/web/jobs\", async () => {\n const result = await listJobsTool.execute({});\n return <JobList jobs={result.jobs} />;\n });\n}\n","import PrimaryButton from \"./PrimaryButton\";\n\n/**\n * Props for the AddVersionButton component.\n */\ninterface AddVersionButtonProps {\n libraryName: string;\n}\n\n/**\n * A reusable button component for adding a new version to a library.\n * Encapsulates both styling and HTMX behavior.\n */\nconst AddVersionButton = ({ libraryName }: AddVersionButtonProps) => {\n return (\n <PrimaryButton\n hx-get={`/web/libraries/${encodeURIComponent(libraryName)}/add-version-form`}\n hx-target=\"#add-version-form-container\"\n hx-swap=\"innerHTML\"\n >\n Add New Version\n </PrimaryButton>\n );\n};\n\nexport default AddVersionButton;\n","import type { PropsWithChildren } from \"@kitajs/html\";\n\n/**\n * Defines the possible types for the Alert component.\n */\ntype AlertType = \"success\" | \"error\" | \"warning\" | \"info\";\n\n/**\n * Props for the Alert component.\n */\ninterface AlertProps extends PropsWithChildren {\n type: AlertType;\n title?: string;\n message: string | JSX.Element; // Allow JSX for messages\n}\n\n/**\n * Reusable Alert component using Flowbite styling.\n * Displays messages with appropriate colors and icons based on the type.\n * @param props - Component props including type, title (optional), and message.\n */\nconst Alert = ({ type, title, message }: AlertProps) => {\n let iconSvg: JSX.Element;\n let colorClasses: string;\n let defaultTitle: string;\n\n switch (type) {\n case \"success\":\n defaultTitle = \"Success:\";\n colorClasses =\n \"text-green-800 border-green-300 bg-green-50 dark:bg-gray-800 dark:text-green-400 dark:border-green-800\";\n iconSvg = (\n <svg\n class=\"shrink-0 inline w-4 h-4 me-3\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Zm9.5 9.5A9.5 9.5 0 0 1 10 19a9.46 9.46 0 0 1-1.671-.14c-.165-.05-.3-.19-.42-.335l-.165-.165c-.19-.2-.3-.425-.3-.655A4.2 4.2 0 0 1 4.5 10a4.25 4.25 0 0 1 7.462-2.882l1.217 1.217a3.175 3.175 0 0 0 4.5.01l.106-.106a.934.934 0 0 0 .1-.36ZM10 11a1 1 0 1 0 0 2 1 1 0 0 0 0-2Z\" />\n </svg>\n );\n break;\n case \"error\":\n defaultTitle = \"Error:\";\n colorClasses =\n \"text-red-800 border-red-300 bg-red-50 dark:bg-gray-800 dark:text-red-400 dark:border-red-800\";\n iconSvg = (\n <svg\n class=\"shrink-0 inline w-4 h-4 me-3\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3h-1a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z\" />\n </svg>\n );\n break;\n case \"warning\":\n defaultTitle = \"Warning:\";\n colorClasses =\n \"text-yellow-800 border-yellow-300 bg-yellow-50 dark:bg-gray-800 dark:text-yellow-300 dark:border-yellow-800\";\n iconSvg = (\n <svg\n class=\"shrink-0 inline w-4 h-4 me-3\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3h-1a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z\" />\n </svg>\n );\n break;\n case \"info\":\n default: // Default to info style\n defaultTitle = \"Info:\";\n colorClasses =\n \"text-blue-800 border-blue-300 bg-blue-50 dark:bg-gray-800 dark:text-blue-400 dark:border-blue-800\";\n iconSvg = (\n <svg\n class=\"shrink-0 inline w-4 h-4 me-3\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"currentColor\"\n viewBox=\"0 0 20 20\"\n >\n <path d=\"M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z\" />\n </svg>\n );\n break;\n }\n\n const displayTitle = title ?? defaultTitle;\n\n return (\n <div\n class={`flex items-center p-4 mb-4 text-sm border rounded-lg ${colorClasses}`}\n role=\"alert\"\n >\n {iconSvg}\n <span class=\"sr-only\">Info</span>\n <div>\n {displayTitle ? (\n <span class=\"font-medium\" safe>\n {displayTitle}\n </span>\n ) : null}{\" \"}\n {message}\n </div>\n </div>\n );\n};\n\nexport default Alert;\n","import type { PropsWithChildren } from \"@kitajs/html\";\n\n/**\n * Props for the Tooltip component.\n */\ninterface TooltipProps extends PropsWithChildren {\n text: string | Promise<string> | Element;\n position?: \"top\" | \"right\" | \"bottom\" | \"left\";\n}\n\n/**\n * Reusable Tooltip component using Alpine.js for state management.\n * Displays a help icon that shows a tooltip on hover/focus.\n *\n * @param props - Component props including text and optional position.\n */\nconst Tooltip = ({ text, position = \"top\" }: TooltipProps) => {\n // Map position to Tailwind classes\n const positionClasses = {\n top: \"bottom-full left-1/2 transform -translate-x-1/2 -translate-y-1 mb-1\",\n right: \"left-full top-1/2 transform -translate-y-1/2 translate-x-1 ml-1\",\n bottom: \"top-full left-1/2 transform -translate-x-1/2 translate-y-1 mt-1\",\n left: \"right-full top-1/2 transform -translate-y-1/2 -translate-x-1 mr-1\",\n };\n\n return (\n <div\n class=\"relative ml-1.5 flex items-center\"\n x-data=\"{ isVisible: false }\"\n >\n <button\n type=\"button\"\n class=\"text-gray-400 hover:text-gray-500 dark:text-gray-500 dark:hover:text-gray-400 focus:outline-none flex items-center\"\n aria-label=\"Help\"\n x-on:mouseenter=\"isVisible = true\"\n x-on:mouseleave=\"isVisible = false\"\n x-on:focus=\"isVisible = true\"\n x-on:blur=\"isVisible = false\"\n tabindex=\"0\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke-width=\"1.5\"\n stroke=\"currentColor\"\n class=\"w-4 h-4\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n d=\"M9.879 7.519c1.171-1.025 3.071-1.025 4.242 0 1.172 1.025 1.172 2.687 0 3.712-.203.179-.43.326-.67.442-.745.361-1.45.999-1.45 1.827v.75M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9 5.25h.008v.008H12v-.008z\"\n />\n </svg>\n </button>\n <div\n x-show=\"isVisible\"\n x-cloak\n class={`absolute z-10 w-64 p-2 text-sm text-gray-500 bg-white border border-gray-200 rounded-lg shadow-sm dark:bg-gray-800 dark:border-gray-600 dark:text-gray-400 ${positionClasses[position]}`}\n >\n {text as \"safe\"}\n </div>\n </div>\n );\n};\n\nexport default Tooltip;\n","import type { AppConfig } from \"../../utils/config\";\nimport { ScrapeMode } from \"../../scraper/types\";\nimport Alert from \"./Alert\";\nimport Tooltip from \"./Tooltip\";\n\n/**\n * Initial values for pre-filling the scrape form.\n * Used when adding a new version to an existing library.\n */\nexport interface ScrapeFormInitialValues {\n library?: string;\n url?: string;\n maxPages?: number;\n maxDepth?: number;\n scope?: string;\n includePatterns?: string;\n excludePatterns?: string;\n scrapeMode?: string;\n headers?: Array<{ name: string; value: string }>;\n followRedirects?: boolean;\n ignoreErrors?: boolean;\n}\n\ninterface ScrapeFormContentProps {\n defaultExcludePatterns?: string[];\n /** Initial values for pre-filling the form (used in add-version mode) */\n initialValues?: ScrapeFormInitialValues;\n /** Mode of the form: 'new' for new library, 'add-version' for adding version to existing library */\n mode?: \"new\" | \"add-version\";\n /** Application configuration for scraper defaults */\n scraperConfig?: AppConfig[\"scraper\"];\n}\n\n/**\n * Renders the form fields for queuing a new scrape job.\n * Includes basic fields (URL, Library, Version) and advanced options.\n * Supports pre-filling values when adding a new version to an existing library.\n */\nconst ScrapeFormContent = ({\n defaultExcludePatterns,\n initialValues,\n mode = \"new\",\n scraperConfig,\n}: ScrapeFormContentProps) => {\n const isAddVersionMode = mode === \"add-version\";\n\n // Use initial values or defaults\n const urlValue = initialValues?.url || \"\";\n const libraryValue = initialValues?.library || \"\";\n const maxPagesValue = initialValues?.maxPages?.toString() || \"\";\n const maxDepthValue = initialValues?.maxDepth?.toString() || \"\";\n const scopeValue = initialValues?.scope || \"subpages\";\n const includePatternsValue = initialValues?.includePatterns || \"\";\n const scrapeModeValue = initialValues?.scrapeMode || ScrapeMode.Auto;\n const followRedirectsValue = initialValues?.followRedirects ?? true;\n const ignoreErrorsValue = initialValues?.ignoreErrors ?? true;\n\n // Format exclude patterns - use initial values if provided, otherwise use defaults\n const excludePatternsText =\n initialValues?.excludePatterns !== undefined\n ? initialValues.excludePatterns\n : defaultExcludePatterns?.join(\"\\n\") || \"\";\n\n // Serialize headers for Alpine.js initialization\n const headersJson = JSON.stringify(initialValues?.headers || []);\n\n // Determine the close button action based on mode\n const closeButtonAttrs = isAddVersionMode\n ? {\n \"hx-get\": `/web/libraries/${encodeURIComponent(libraryValue)}/add-version-button`,\n \"hx-target\": \"#add-version-form-container\",\n \"hx-swap\": \"innerHTML\",\n }\n : {\n \"hx-get\": \"/web/jobs/new-button\",\n \"hx-target\": \"#addJobForm\",\n \"hx-swap\": \"innerHTML\",\n };\n\n // Determine the form target based on mode\n const formTarget = isAddVersionMode\n ? \"#add-version-form-container\"\n : \"#addJobForm\";\n\n const title = isAddVersionMode ? \"Add New Version\" : \"Add New Documentation\";\n\n return (\n <div class=\"mt-4 p-4 bg-white dark:bg-gray-800 rounded-lg shadow border border-gray-300 dark:border-gray-600 relative animate-[fadeSlideIn_0.2s_ease-out]\">\n {/* Close button */}\n <button\n type=\"button\"\n {...closeButtonAttrs}\n class=\"absolute top-3 right-3 p-1 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300 rounded-full hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors duration-150\"\n title=\"Close\"\n >\n <svg\n class=\"w-5 h-5\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n xmlns=\"http://www.w3.org/2000/svg\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M6 18L18 6M6 6l12 12\"\n />\n </svg>\n </button>\n <h3 class=\"text-xl font-semibold text-gray-900 dark:text-white mb-2 pr-8\">\n {title}\n </h3>\n <form\n hx-post=\"/web/jobs/scrape\"\n hx-target={formTarget}\n hx-swap=\"innerHTML\"\n class=\"space-y-2\"\n data-initial-url={urlValue}\n data-initial-headers={headersJson}\n x-data=\"{\n url: '',\n hasPath: false,\n headers: [],\n checkUrlPath() {\n try {\n const url = new URL(this.url);\n this.hasPath = url.pathname !== '/' && url.pathname !== '';\n } catch (e) {\n this.hasPath = false;\n }\n }\n }\"\n x-init=\"\n url = $el.dataset.initialUrl || '';\n headers = JSON.parse($el.dataset.initialHeaders || '[]');\n checkUrlPath();\n \"\n >\n {/* Hidden field to tell backend which button to return on success */}\n <input type=\"hidden\" name=\"formMode\" value={mode} />\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"url\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n URL\n </label>\n <Tooltip\n text={\n <div>\n <p>Enter the URL of the documentation you want to scrape.</p>\n <p class=\"mt-2\">\n For local files/folders, you must use the{\" \"}\n <code>file://</code> prefix and ensure the path is\n accessible to the server.\n </p>\n <p class=\"mt-2\">\n If running in Docker, <b>mount the folder</b> (see README\n for details).\n </p>\n </div>\n }\n />\n </div>\n <input\n type=\"url\"\n name=\"url\"\n id=\"url\"\n required\n x-model=\"url\"\n x-on:input=\"checkUrlPath\"\n x-on:paste=\"$nextTick(() => checkUrlPath())\"\n placeholder=\"https://docs.example.com/library/\"\n class=\"mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n <div\n x-show=\"hasPath && !(url.startsWith('file://'))\"\n x-cloak\n x-transition:enter=\"transition ease-out duration-300\"\n x-transition:enter-start=\"opacity-0 transform -translate-y-2\"\n x-transition:enter-end=\"opacity-100 transform translate-y-0\"\n class=\"mt-2\"\n >\n <Alert\n type=\"info\"\n message=\"By default, only subpages under the given URL will be scraped. To scrape the whole website, adjust the 'Scope' option in Advanced Options.\"\n />\n </div>\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"library\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Library Name\n </label>\n <Tooltip text=\"The name of the library you're documenting. This will be used when searching.\" />\n </div>\n {isAddVersionMode ? (\n <>\n <input type=\"hidden\" name=\"library\" value={libraryValue} />\n <div class=\"mt-0.5 px-2 py-1 text-sm text-gray-700 dark:text-gray-300 bg-gray-100 dark:bg-gray-700 rounded-md border border-gray-300 dark:border-gray-600\">\n <span safe>{libraryValue}</span>\n </div>\n </>\n ) : (\n <input\n type=\"text\"\n name=\"library\"\n id=\"library\"\n required\n value={libraryValue}\n placeholder=\"e.g. react, vue, express\"\n class=\"mt-0.5 block w-full px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n )}\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"version\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Version (optional)\n </label>\n <Tooltip text=\"Specify the version of the library documentation you're indexing (e.g. 2.0.0). Leave empty or enter 'latest' to index without a specific version. This allows for version-specific searches.\" />\n </div>\n <input\n type=\"text\"\n name=\"version\"\n id=\"version\"\n placeholder=\"e.g. 2.0.0 or leave empty for latest\"\n class=\"mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n </div>\n\n {/* Advanced Options with slide animation */}\n <div\n class=\"bg-gray-50 dark:bg-gray-900 p-2 rounded-md\"\n data-should-open={\n isAddVersionMode &&\n (maxPagesValue ||\n maxDepthValue ||\n scopeValue !== \"subpages\" ||\n includePatternsValue ||\n excludePatternsText ||\n scrapeModeValue !== ScrapeMode.Auto)\n ? \"true\"\n : \"false\"\n }\n x-data=\"{ open: false }\"\n x-init=\"open = $el.dataset.shouldOpen === 'true'\"\n >\n <button\n type=\"button\"\n class=\"w-full flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 transition-colors\"\n x-on:click=\"open = !open\"\n >\n <svg\n class=\"w-4 h-4 transform transition-transform duration-200\"\n x-bind:class=\"{ 'rotate-90': open }\"\n fill=\"none\"\n stroke=\"currentColor\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M9 5l7 7-7 7\"\n />\n </svg>\n <span>Advanced Options</span>\n </button>\n <div x-show=\"open\" x-cloak x-collapse class=\"mt-2 space-y-2\">\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"maxPages\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Max Pages\n </label>\n <Tooltip\n text={`The maximum number of pages to scrape. Default is ${scraperConfig?.maxPages ?? 1000}. Setting this too high may result in longer processing times.`}\n />\n </div>\n <input\n type=\"number\"\n name=\"maxPages\"\n id=\"maxPages\"\n min=\"1\"\n placeholder={scraperConfig?.maxPages?.toString() || \"1000\"}\n value={maxPagesValue}\n class=\"mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"maxDepth\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Max Depth\n </label>\n <Tooltip\n text={`How many links deep the scraper should follow. Default is ${scraperConfig?.maxDepth || 3}. Higher values capture more content but increase processing time.`}\n />\n </div>\n <input\n type=\"number\"\n name=\"maxDepth\"\n id=\"maxDepth\"\n min=\"0\"\n placeholder={scraperConfig?.maxDepth?.toString() || \"3\"}\n value={maxDepthValue}\n class=\"mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"scope\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Scope\n </label>\n <Tooltip\n text={\n <div>\n Controls which pages are scraped:\n <ul class=\"list-disc pl-5\">\n <li>\n 'Subpages' only scrapes under the given URL path,\n </li>\n <li>\n 'Hostname' scrapes all content on the same host (e.g.,\n all of docs.example.com),\n </li>\n <li>\n 'Domain' scrapes all content on the domain and its\n subdomains (e.g., all of example.com).\n </li>\n </ul>\n </div>\n }\n />\n </div>\n <select\n name=\"scope\"\n id=\"scope\"\n class=\"mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option value=\"subpages\" selected={scopeValue === \"subpages\"}>\n Subpages (Default)\n </option>\n <option value=\"hostname\" selected={scopeValue === \"hostname\"}>\n Hostname\n </option>\n <option value=\"domain\" selected={scopeValue === \"domain\"}>\n Domain\n </option>\n </select>\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"includePatterns\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Include Patterns\n </label>\n <Tooltip text=\"Glob or regex patterns for URLs to include. One per line or comma-separated. Regex patterns must be wrapped in slashes, e.g. /pattern/.\" />\n </div>\n <textarea\n name=\"includePatterns\"\n id=\"includePatterns\"\n rows=\"2\"\n placeholder=\"e.g. docs/* or /api\\/v1.*/\"\n class=\"mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n safe\n >\n {includePatternsValue}\n </textarea>\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"excludePatterns\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Exclude Patterns\n </label>\n <Tooltip text=\"Glob or regex patterns for URLs to exclude. One per line or comma-separated. Exclude takes precedence over include. Regex patterns must be wrapped in slashes, e.g. /pattern/. Edit or clear this field to customize exclusions.\" />\n </div>\n <textarea\n name=\"excludePatterns\"\n id=\"excludePatterns\"\n rows=\"5\"\n safe\n class=\"mt-0.5 block w-full max-w-sm px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono text-xs\"\n >\n {excludePatternsText}\n </textarea>\n <p class=\"mt-1 text-xs text-gray-500 dark:text-gray-400\">\n {isAddVersionMode\n ? \"Patterns from previous version. Edit as needed.\"\n : \"Default patterns are pre-filled. Edit to customize or clear to exclude nothing.\"}\n </p>\n </div>\n <div>\n <div class=\"flex items-center\">\n <label\n for=\"scrapeMode\"\n class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\"\n >\n Scrape Mode\n </label>\n <Tooltip\n text={\n <div>\n <ul class=\"list-disc pl-5\">\n <li>'Auto' automatically selects the best method,</li>\n <li>\n 'Fetch' uses simple HTTP requests (faster but may miss\n dynamic content),\n </li>\n <li>\n 'Playwright' uses a headless browser (slower but\n better for JS-heavy sites).\n </li>\n </ul>\n </div>\n }\n />\n </div>\n <select\n name=\"scrapeMode\"\n id=\"scrapeMode\"\n class=\"mt-0.5 block w-full max-w-sm pl-2 pr-10 py-1 text-base border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-primary-500 focus:border-primary-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option\n value={ScrapeMode.Auto}\n selected={scrapeModeValue === ScrapeMode.Auto}\n >\n Auto (Default)\n </option>\n <option\n value={ScrapeMode.Fetch}\n selected={scrapeModeValue === ScrapeMode.Fetch}\n >\n Fetch\n </option>\n <option\n value={ScrapeMode.Playwright}\n selected={scrapeModeValue === ScrapeMode.Playwright}\n >\n Playwright\n </option>\n </select>\n </div>\n <div>\n <div class=\"flex items-center mb-1\">\n <label class=\"block text-sm font-medium text-gray-700 dark:text-gray-300\">\n Custom HTTP Headers\n </label>\n <Tooltip text=\"Add custom HTTP headers (e.g., for authentication). These will be sent with every HTTP request.\" />\n </div>\n <div>\n {/* AlpineJS dynamic header rows */}\n <template x-for=\"(header, idx) in headers\">\n <div class=\"flex space-x-2 mb-1\">\n <input\n type=\"text\"\n class=\"w-1/3 px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-xs\"\n placeholder=\"Header Name\"\n x-model=\"header.name\"\n required\n />\n <span class=\"text-gray-500\">:</span>\n <input\n type=\"text\"\n class=\"w-1/2 px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white text-xs\"\n placeholder=\"Header Value\"\n x-model=\"header.value\"\n required\n />\n <button\n type=\"button\"\n class=\"text-red-500 hover:text-red-700 text-xs\"\n x-on:click=\"headers.splice(idx, 1)\"\n >\n Remove\n </button>\n <input\n type=\"hidden\"\n name=\"header[]\"\n x-bind:value=\"header.name && header.value ? header.name + ':' + header.value : ''\"\n />\n </div>\n </template>\n <button\n type=\"button\"\n class=\"mt-1 px-2 py-0.5 bg-primary-100 dark:bg-primary-900 text-primary-700 dark:text-primary-200 rounded text-xs\"\n x-on:click=\"headers.push({ name: '', value: '' })\"\n >\n + Add Header\n </button>\n </div>\n </div>\n <div class=\"flex items-center\">\n <input\n id=\"followRedirects\"\n name=\"followRedirects\"\n type=\"checkbox\"\n checked={followRedirectsValue}\n class=\"h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700\"\n />\n <label\n for=\"followRedirects\"\n class=\"ml-1 block text-sm text-gray-900 dark:text-gray-300\"\n >\n Follow Redirects\n </label>\n </div>\n <div class=\"flex items-center\">\n <input\n id=\"ignoreErrors\"\n name=\"ignoreErrors\"\n type=\"checkbox\"\n checked={ignoreErrorsValue}\n class=\"h-4 w-4 text-primary-600 focus:ring-primary-500 border-gray-300 dark:border-gray-600 rounded bg-white dark:bg-gray-700\"\n />\n <label\n for=\"ignoreErrors\"\n class=\"ml-1 block text-sm text-gray-900 dark:text-gray-300\"\n >\n Ignore Errors During Scraping\n </label>\n </div>\n </div>\n </div>\n\n <div>\n <button\n type=\"submit\"\n class=\"w-full flex justify-center py-1.5 px-3 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-primary-600 hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500\"\n >\n Start Indexing\n </button>\n </div>\n </form>\n {/* Target div for HTMX response - now only used for success messages */}\n <div id=\"job-response\" class=\"mt-2 text-sm\"></div>\n </div>\n );\n};\n\nexport default ScrapeFormContent;\n","import type { AppConfig } from \"../../utils/config\";\nimport ScrapeFormContent from \"./ScrapeFormContent\";\n\ninterface ScrapeFormProps {\n defaultExcludePatterns?: string[];\n scraperConfig?: AppConfig[\"scraper\"];\n}\n\n/**\n * Wrapper component for the ScrapeFormContent.\n * Provides a container div, often used as a target for HTMX OOB swaps.\n */\nconst ScrapeForm = ({\n defaultExcludePatterns,\n scraperConfig,\n}: ScrapeFormProps) => (\n <div id=\"scrape-form-container\" class=\"animate-[fadeSlideIn_0.2s_ease-out]\">\n <ScrapeFormContent\n defaultExcludePatterns={defaultExcludePatterns}\n scraperConfig={scraperConfig}\n />\n </div>\n);\n\nexport default ScrapeForm;\n","/**\n * Default exclusion patterns for documentation scraping.\n * These patterns are always applied unless user explicitly provides their own exclude patterns.\n * Patterns use glob/regex syntax supported by the pattern matcher.\n */\n\n/**\n * Default file exclusion patterns - files commonly found in documentation that should be excluded.\n * These patterns match files anywhere in the path structure.\n */\nexport const DEFAULT_FILE_EXCLUSIONS = [\n // CHANGELOG files (case variations)\n \"**/CHANGELOG.md\",\n \"**/changelog.md\",\n \"**/CHANGELOG.mdx\",\n \"**/changelog.mdx\",\n\n // LICENSE files (case variations)\n \"**/LICENSE\",\n \"**/LICENSE.md\",\n \"**/license.md\",\n\n // CODE_OF_CONDUCT files (case variations)\n \"**/CODE_OF_CONDUCT.md\",\n \"**/code_of_conduct.md\",\n\n // Test files\n \"**/*.test.*\",\n \"**/*.spec.*\",\n \"**/*_test.py\",\n \"**/*_test.go\",\n\n // Package manager lock files\n \"**/*.lock\",\n \"**/package-lock.json\",\n \"**/yarn.lock\",\n \"**/pnpm-lock.yaml\",\n \"**/go.sum\",\n\n // Build artifacts\n \"**/*.min.js\",\n \"**/*.min.css\",\n \"**/*.map\",\n \"**/*.d.ts\",\n\n // IDE/System files\n \"**/.DS_Store\",\n \"**/Thumbs.db\",\n \"**/*.swp\",\n \"**/*.swo\",\n\n // Internal config files (using regex pattern)\n \"/.*\\\\.(ini|cfg|conf|log|pid)$/\",\n];\n\n/**\n * Default folder/path exclusion patterns - directories commonly found in documentation that should be excluded.\n */\nexport const DEFAULT_FOLDER_EXCLUSIONS = [\n // Archive and deprecated content (matches anywhere in path)\n \"**/archive/**\",\n \"**/archived/**\",\n \"**/deprecated/**\",\n \"**/legacy/**\",\n \"**/old/**\",\n \"**/outdated/**\",\n \"**/previous/**\",\n \"**/superseded/**\",\n\n // Specific paths that don't follow the general pattern\n \"docs/old/**\",\n\n // Test directories\n \"**/test/**\",\n \"**/tests/**\",\n \"**/__tests__/**\",\n \"**/spec/**\",\n\n // Build output directories\n \"**/dist/**\",\n \"**/build/**\",\n \"**/out/**\",\n \"**/target/**\",\n \"**/.next/**\",\n \"**/.nuxt/**\",\n\n // IDE directories\n \"**/.vscode/**\",\n \"**/.idea/**\",\n\n // Internationalization folders - non-English locales\n \"**/i18n/ar*/**\",\n \"**/i18n/de*/**\",\n \"**/i18n/es*/**\",\n \"**/i18n/fr*/**\",\n \"**/i18n/hi*/**\",\n \"**/i18n/it*/**\",\n \"**/i18n/ja*/**\",\n \"**/i18n/ko*/**\",\n \"**/i18n/nl*/**\",\n \"**/i18n/pl*/**\",\n \"**/i18n/pt*/**\",\n \"**/i18n/ru*/**\",\n \"**/i18n/sv*/**\",\n \"**/i18n/th*/**\",\n \"**/i18n/tr*/**\",\n \"**/i18n/vi*/**\",\n \"**/i18n/zh*/**\",\n\n // Common locale folder patterns\n \"**/zh-cn/**\",\n \"**/zh-hk/**\",\n \"**/zh-mo/**\",\n \"**/zh-sg/**\",\n \"**/zh-tw/**\",\n];\n\n/**\n * Combined default exclusion patterns (files + folders).\n * These are applied when no user-provided exclude patterns are specified.\n */\nexport const DEFAULT_EXCLUSION_PATTERNS = [\n ...DEFAULT_FILE_EXCLUSIONS,\n ...DEFAULT_FOLDER_EXCLUSIONS,\n];\n\n/**\n * Get effective exclusion patterns by merging defaults with user patterns.\n * If user provides patterns, use only theirs (allowing override).\n * If user provides no patterns, use defaults.\n */\nexport function getEffectiveExclusionPatterns(userPatterns?: string[]): string[] {\n // If user explicitly provides patterns (even empty array), respect their choice\n if (userPatterns !== undefined) {\n return userPatterns;\n }\n\n // Otherwise, use default patterns\n return DEFAULT_EXCLUSION_PATTERNS;\n}\n","import type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport type { ScrapeTool } from \"../../../tools/ScrapeTool\";\nimport { ScrapeMode } from \"../../../scraper/types\";\nimport type { AppConfig } from \"../../../utils/config\";\nimport { logger } from \"../../../utils/logger\";\nimport AddJobButton from \"../../components/AddJobButton\";\nimport AddVersionButton from \"../../components/AddVersionButton\";\nimport Alert from \"../../components/Alert\";\nimport ScrapeForm from \"../../components/ScrapeForm\";\nimport { DEFAULT_EXCLUSION_PATTERNS } from \"../../../scraper/utils/defaultPatterns\";\nimport { ValidationError } from \"../../../tools/errors\";\n\n/**\n * Registers the API routes for creating new jobs.\n * @param server - The Fastify instance.\n * @param scrapeTool - The tool instance for scraping documents.\n */\nexport function registerNewJobRoutes(\n server: FastifyInstance,\n scrapeTool: ScrapeTool,\n scraperConfig: AppConfig[\"scraper\"]\n) {\n // GET /web/jobs/new - Return the form component wrapped in its container\n server.get(\"/web/jobs/new\", async () => {\n // Return the wrapper component which includes the container div\n return (\n <ScrapeForm\n defaultExcludePatterns={DEFAULT_EXCLUSION_PATTERNS}\n scraperConfig={scraperConfig}\n />\n );\n });\n\n // GET /web/jobs/new-button - Return just the button to collapse the form\n server.get(\"/web/jobs/new-button\", async () => {\n return <AddJobButton />;\n });\n\n // POST /web/jobs/scrape - Queue a new scrape job\n server.post(\n \"/web/jobs/scrape\",\n async (\n request: FastifyRequest<{\n Body: {\n url: string;\n library: string;\n version?: string;\n formMode?: \"new\" | \"add-version\"; // Hidden field indicating form context\n maxPages?: string;\n maxDepth?: string;\n scope?: \"subpages\" | \"hostname\" | \"domain\";\n scrapeMode?: ScrapeMode;\n followRedirects?: \"on\" | undefined; // Checkbox value is 'on' if checked\n ignoreErrors?: \"on\" | undefined;\n includePatterns?: string;\n excludePatterns?: string;\n \"header[]\"?: string[] | string; // Added header field for custom headers\n };\n }>,\n reply\n ) => {\n const body = request.body;\n reply.type(\"text/html\"); // Set content type for all responses from this handler\n try {\n // Basic validation\n if (!body.url || !body.library) {\n reply.status(400);\n // Use Alert component for validation error\n return (\n <Alert\n type=\"error\"\n title=\"Validation Error:\"\n message=\"URL and Library Name are required.\"\n />\n );\n }\n\n // Parse includePatterns and excludePatterns from textarea input\n function parsePatterns(input?: string): string[] | undefined {\n if (input === undefined) return undefined;\n if (input.trim() === \"\") return [];\n return input\n .split(/\\n|,/)\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n // Parse custom headers from repeated header[] fields (format: name:value)\n function parseHeaders(\n input?: string[] | string\n ): Record<string, string> | undefined {\n if (!input) return undefined;\n const arr = Array.isArray(input) ? input : [input];\n const headers: Record<string, string> = {};\n for (const entry of arr) {\n const idx = entry.indexOf(\":\");\n if (idx > 0) {\n const name = entry.slice(0, idx).trim();\n const value = entry.slice(idx + 1).trim();\n if (name) headers[name] = value;\n }\n }\n return Object.keys(headers).length > 0 ? headers : undefined;\n }\n\n // Normalize version: treat \"latest\", empty string, or whitespace-only as null (latest)\n const normalizedVersion =\n !body.version ||\n body.version.trim() === \"\" ||\n body.version.trim().toLowerCase() === \"latest\"\n ? null\n : body.version.trim();\n\n // Prepare options for ScrapeTool\n const scrapeOptions = {\n url: body.url,\n library: body.library,\n version: normalizedVersion,\n waitForCompletion: false, // Don't wait in UI\n options: {\n maxPages: body.maxPages\n ? Number.parseInt(body.maxPages, 10)\n : undefined,\n maxDepth: body.maxDepth\n ? Number.parseInt(body.maxDepth, 10)\n : undefined,\n scope: body.scope,\n scrapeMode: body.scrapeMode,\n // Checkboxes send 'on' when checked, otherwise undefined\n followRedirects: body.followRedirects === \"on\",\n ignoreErrors: body.ignoreErrors === \"on\",\n includePatterns: parsePatterns(body.includePatterns),\n excludePatterns: parsePatterns(body.excludePatterns),\n headers: parseHeaders(body[\"header[]\"]), // <-- propagate custom headers from web UI\n },\n };\n\n // Execute the scrape tool\n const result = await scrapeTool.execute(scrapeOptions);\n\n if (\"jobId\" in result) {\n // Success: Collapse form back to button and show toast via HX-Trigger\n const versionDisplay = normalizedVersion || \"latest\";\n reply.header(\n \"HX-Trigger\",\n JSON.stringify({\n toast: {\n message: `Indexing started for ${body.library}@${versionDisplay}`,\n type: \"success\",\n },\n })\n );\n // Return the appropriate button based on the form mode\n if (body.formMode === \"add-version\") {\n return <AddVersionButton libraryName={body.library} />;\n }\n return <AddJobButton />;\n }\n\n // This case shouldn't happen with waitForCompletion: false, but handle defensively\n // Use Alert component for unexpected success\n return (\n <Alert type=\"warning\" message=\"Job finished unexpectedly quickly.\" />\n );\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n logger.error(`❌ Scrape job submission failed: ${error}`);\n\n // Use appropriate HTTP status code based on error type\n if (error instanceof ValidationError) {\n reply.status(400); // Bad Request for validation errors\n } else {\n reply.status(500); // Internal Server Error for other errors\n }\n\n // Return the error message directly - it's already user-friendly\n return (\n <Alert\n type=\"error\"\n title=\"Error:\"\n message={<span safe>{errorMessage}</span>}\n />\n );\n }\n }\n );\n}\n","import { type VersionSummary, isActiveStatus } from \"../../store/types\";\nimport VersionBadge from \"./VersionBadge\";\nimport LoadingSpinner from \"./LoadingSpinner\";\n\n/**\n * Props for the VersionDetailsRow component.\n */\ninterface VersionDetailsRowProps {\n version: VersionSummary;\n libraryName: string;\n showDelete?: boolean;\n showRefresh?: boolean;\n}\n\n/**\n * Renders details for a single library version in a row format.\n * Includes version, stats, and optional delete/refresh buttons.\n * @param props - Component props including version, libraryName, showDelete, and showRefresh flags.\n */\nconst VersionDetailsRow = ({\n version,\n libraryName,\n showDelete = true,\n showRefresh = false,\n}: VersionDetailsRowProps) => {\n // Format the indexed date nicely, handle null case\n const indexedDate = version.indexedAt\n ? new Date(version.indexedAt).toLocaleDateString()\n : \"N/A\";\n // Display 'Latest' if version string is empty\n const versionLabel = version.ref.version || \"Latest\";\n // Use empty string for latest version in param and rowId\n const versionParam = version.ref.version || \"\";\n\n // Sanitize both libraryName and versionParam for valid CSS selector\n const sanitizedLibraryName = libraryName.replace(/[^a-zA-Z0-9-_]/g, \"-\");\n const sanitizedVersionParam = versionParam.replace(/[^a-zA-Z0-9-_]/g, \"-\");\n const rowId = `row-${sanitizedLibraryName}-${sanitizedVersionParam}`;\n\n // Determine initial isRefreshing based on version status\n const initialIsRefreshing = isActiveStatus(version.status);\n\n // Define state-specific button classes for Alpine toggling\n const defaultStateClasses =\n \"text-red-700 border border-red-700 hover:bg-red-700 hover:text-white focus:ring-4 focus:outline-none focus:ring-red-300 dark:border-red-500 dark:text-red-500 dark:hover:text-white dark:focus:ring-red-800 dark:hover:bg-red-500\";\n const confirmingStateClasses =\n \"bg-red-600 text-white border-red-600 focus:ring-4 focus:outline-none focus:ring-red-300 dark:bg-red-700 dark:border-red-700 dark:focus:ring-red-800\";\n\n return (\n // Use flexbox for layout, add border between rows\n <div\n id={rowId}\n class=\"flex justify-between items-center py-1 border-b border-gray-200 dark:border-gray-600 last:border-b-0\"\n data-library-name={libraryName}\n data-version-param={versionParam}\n data-is-refreshing={initialIsRefreshing ? \"true\" : \"false\"}\n x-data=\"{ \n library: $el.dataset.libraryName, \n version: $el.dataset.versionParam, \n confirming: $el.dataset.confirming === 'true', \n isDeleting: false,\n isRefreshing: $el.dataset.isRefreshing === 'true',\n setRefreshing(val) {\n this.isRefreshing = !!val;\n this.$el.dataset.isRefreshing = val ? 'true' : 'false';\n },\n init() {\n const rowId = this.$el.id;\n const myLibrary = this.library;\n const myVersion = this.version;\n \n document.body.addEventListener('job-status-change', (e) => {\n const job = e.detail;\n const jobVersion = job.version || '';\n if (job.library === myLibrary && jobVersion === myVersion) {\n const newValue = ['queued', 'running'].includes(job.status);\n const el = document.getElementById(rowId);\n if (el) {\n el.dispatchEvent(new CustomEvent('set-refreshing', { detail: newValue, bubbles: true }));\n }\n }\n });\n }\n }\"\n x-on:set-refreshing=\"setRefreshing($event.detail)\"\n >\n {/* Version Label */}\n <span\n class=\"text-sm text-gray-900 dark:text-white w-1/4 truncate\"\n title={versionLabel}\n >\n {version.ref.version ? (\n <VersionBadge version={version.ref.version} />\n ) : (\n <span class=\"text-gray-600 dark:text-gray-400\">Latest</span>\n )}\n </span>\n\n {/* Stats Group */}\n <div class=\"flex space-x-2 text-sm text-gray-600 dark:text-gray-400 w-3/4 justify-end items-center\">\n <span title=\"Number of unique pages indexed\">\n Pages:{\" \"}\n <span class=\"font-semibold\" safe>\n {version.counts.uniqueUrls.toLocaleString()}\n </span>\n </span>\n <span title=\"Number of indexed snippets\">\n Chunks:{\" \"}\n <span class=\"font-semibold\" safe>\n {version.counts.documents.toLocaleString()}\n </span>\n </span>\n <span title=\"Date last indexed\">\n Last Update:{\" \"}\n <span class=\"font-semibold\" safe>\n {indexedDate}\n </span>\n </span>\n </div>\n\n {/* Action buttons container */}\n <div class=\"flex items-center ml-2 space-x-1\">\n {/* Refresh Button - Icon shown inline based on state */}\n {showRefresh && (\n <>\n {/* Icon button - shown when NOT refreshing */}\n <template x-if=\"!isRefreshing\">\n <button\n type=\"button\"\n class=\"font-medium rounded-lg text-sm p-1 w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out text-gray-500 border border-gray-300 hover:bg-gray-100 hover:text-gray-700 focus:ring-4 focus:outline-none focus:ring-gray-200 dark:border-gray-600 dark:text-gray-400 dark:hover:text-white dark:focus:ring-gray-700 dark:hover:bg-gray-600\"\n title=\"Refresh this version (re-scrape changed pages)\"\n x-on:click=\"\n isRefreshing = true;\n $root.dataset.isRefreshing = 'true';\n $el.dispatchEvent(new CustomEvent('trigger-refresh', { bubbles: true }));\n \"\n hx-post={`/web/libraries/${encodeURIComponent(libraryName)}/versions/${encodeURIComponent(versionParam)}/refresh`}\n hx-swap=\"none\"\n hx-trigger=\"trigger-refresh\"\n >\n <svg\n class=\"w-4 h-4\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15\"\n />\n </svg>\n <span class=\"sr-only\">Refresh version</span>\n </button>\n </template>\n {/* Spinner button - shown when refreshing */}\n <template x-if=\"isRefreshing\">\n <button\n type=\"button\"\n class=\"font-medium rounded-lg text-sm p-1 w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out text-gray-500 border border-gray-300 dark:border-gray-600 dark:text-gray-400\"\n title=\"Refresh in progress...\"\n disabled\n >\n <LoadingSpinner class=\"text-gray-500 dark:text-gray-400\" />\n <span class=\"sr-only\">Refreshing...</span>\n </button>\n </template>\n </>\n )}\n\n {/**\n * Conditionally renders a delete button for the version row.\n * The button has three states:\n * 1. Default: Displays a trash icon.\n * 2. Confirming: Displays a confirmation text with an accessible label.\n * 3. Deleting: Displays a spinner icon indicating the deletion process.\n * The button uses AlpineJS for state management and htmx for server interaction.\n */}\n {showDelete && (\n <button\n type=\"button\"\n class=\"font-medium rounded-lg text-sm p-1 min-w-6 h-6 text-center inline-flex items-center justify-center transition-colors duration-150 ease-in-out\"\n title=\"Remove this version\"\n x-bind:class={`confirming ? '${confirmingStateClasses}' : '${defaultStateClasses}'`}\n x-bind:disabled=\"isDeleting\"\n x-on:click=\"\n if (confirming) {\n isDeleting = true;\n window.confirmationManager.clear($root.id);\n $el.dispatchEvent(new CustomEvent('confirmed-delete', { bubbles: true }));\n } else {\n confirming = true;\n isDeleting = false;\n window.confirmationManager.start($root.id);\n }\n \"\n hx-delete={`/web/libraries/${encodeURIComponent(libraryName)}/versions/${encodeURIComponent(versionParam)}`}\n hx-target={`#${rowId}`}\n hx-swap=\"outerHTML\"\n hx-trigger=\"confirmed-delete\"\n >\n {/* Default State: Trash Icon */}\n <span x-show=\"!confirming && !isDeleting\">\n <svg\n class=\"w-4 h-4\"\n aria-hidden=\"true\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 18 20\"\n >\n <path\n stroke=\"currentColor\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n stroke-width=\"2\"\n d=\"M1 5h16M7 8v8m4-8v8M7 1h4a1 1 0 0 1 1 1v3H6V2a1 1 0 0 1-1-1ZM3 5h12v13a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1V5Z\"\n />\n </svg>\n <span class=\"sr-only\">Remove version</span>\n </span>\n\n {/* Confirming State: Text */}\n <span x-show=\"confirming && !isDeleting\" class=\"mx-1\">\n Confirm?<span class=\"sr-only\">Confirm delete</span>\n </span>\n\n {/* Deleting State: Spinner Icon */}\n <span x-show=\"isDeleting\">\n <LoadingSpinner />\n <span class=\"sr-only\">Loading...</span>\n </span>\n </button>\n )}\n </div>\n </div>\n );\n};\n\nexport default VersionDetailsRow;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport AddVersionButton from \"./AddVersionButton\";\nimport VersionDetailsRow from \"./VersionDetailsRow\";\n\n/**\n * Props for the LibraryDetailCard component.\n */\ninterface LibraryDetailCardProps {\n library: LibraryInfo;\n}\n\n/**\n * Renders a card displaying library details and its versions.\n * Includes Delete and Refresh buttons for each version, and an \"Add New Version\" button.\n * @param props - Component props including the library information.\n */\nconst LibraryDetailCard = ({ library }: LibraryDetailCardProps) => {\n // Versions are already sorted descending (latest first) from the API\n const versions = library.versions || [];\n const latestVersion = versions[0];\n return (\n <div class=\"block p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600 mb-4\">\n <div class=\"flex justify-between items-start mb-1\">\n <div class=\"min-w-0\">\n <h3 class=\"text-lg font-medium text-gray-900 dark:text-white\">\n <span safe>{library.name}</span>\n </h3>\n {latestVersion?.sourceUrl ? (\n <div class=\"text-sm text-gray-500 dark:text-gray-400 truncate\">\n <a\n href={latestVersion.sourceUrl}\n target=\"_blank\"\n class=\"hover:underline\"\n title={latestVersion.sourceUrl}\n safe\n >\n {latestVersion.sourceUrl}\n </a>\n </div>\n ) : null}\n </div>\n </div>\n {/* Container for version rows - auto-refreshes on library-change */}\n <div\n class=\"mt-2\"\n id=\"version-list\"\n hx-get={`/web/libraries/${encodeURIComponent(library.name)}/versions-list`}\n hx-trigger=\"library-change from:body\"\n hx-swap=\"morph:innerHTML\"\n >\n {versions.length > 0 ? (\n versions.map((v) => {\n const adapted: VersionSummary = {\n id: -1,\n ref: { library: library.name, version: v.version },\n status: v.status,\n progress: v.progress,\n counts: {\n documents: v.documentCount,\n uniqueUrls: v.uniqueUrlCount,\n },\n indexedAt: v.indexedAt,\n sourceUrl: v.sourceUrl ?? undefined,\n };\n return (\n <VersionDetailsRow\n libraryName={library.name}\n version={adapted}\n showDelete={true}\n showRefresh={true}\n />\n );\n })\n ) : (\n <p class=\"text-sm text-gray-500 dark:text-gray-400 italic\">\n No versions indexed.\n </p>\n )}\n </div>\n {/* Add New Version Section */}\n <div id=\"add-version-form-container\" class=\"mt-4\">\n <AddVersionButton libraryName={library.name} />\n </div>\n </div>\n );\n};\n\nexport default LibraryDetailCard;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport LoadingSpinner from \"./LoadingSpinner\"; // Import spinner\n\n/**\n * Props for the LibrarySearchCard component.\n */\ninterface LibrarySearchCardProps {\n library: LibraryInfo;\n}\n\n/**\n * Renders the search form card for a specific library.\n * Includes a version dropdown and query input.\n * @param props - Component props including the library information.\n */\nconst LibrarySearchCard = ({ library }: LibrarySearchCardProps) => {\n return (\n <div class=\"block p-4 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600 mb-4\">\n <h2 class=\"text-xl font-semibold mb-2 text-gray-900 dark:text-white\" safe>\n Search {library.name} Documentation\n </h2>\n <form\n hx-get={`/web/libraries/${encodeURIComponent(library.name)}/search`}\n hx-target=\"#searchResultsContainer .search-results\"\n hx-swap=\"innerHTML\"\n hx-indicator=\"#searchResultsContainer\"\n class=\"flex space-x-2\"\n >\n <select\n name=\"version\"\n class=\"w-40 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"\n >\n <option value=\"\">Latest</option> {/* Default to latest */}\n {library.versions.map((version) => (\n <option value={version.version || \"latest\"} safe>\n {version.version || \"Latest\"}\n </option>\n ))}\n </select>\n <input\n type=\"text\"\n name=\"query\"\n placeholder=\"Search query...\"\n required\n class=\"flex-grow bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500\"\n />\n <button\n type=\"submit\"\n class=\"text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-blue-800 relative\"\n >\n <span class=\"search-text\">Search</span>\n {/* Spinner for HTMX loading - shown via htmx-indicator class on parent */}\n <span class=\"spinner absolute inset-0 flex items-center justify-center\">\n <LoadingSpinner />\n </span>\n </button>\n </form>\n {/* Add style for htmx-indicator behavior on button */}\n {/* Styles moved to Layout.tsx */}\n </div>\n );\n};\n\nexport default LibrarySearchCard;\n","import { unified } from \"unified\"; // Import unified\nimport remarkParse from \"remark-parse\"; // Import unified plugins\nimport remarkGfm from \"remark-gfm\";\nimport remarkHtml from \"remark-html\";\nimport DOMPurify from \"dompurify\"; // Import DOMPurify\nimport type { StoreSearchResult } from \"../../store/types\";\nimport { createJSDOM } from \"../../utils/dom\"; // Import JSDOM helper\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { escapeHtml } from \"@kitajs/html\";\n\n/**\n * Props for the SearchResultItem component.\n */\ninterface SearchResultItemProps {\n result: StoreSearchResult;\n}\n\n/**\n * Renders a single search result item.\n * Converts markdown content to HTML using unified for markdown MIME types.\n * For other content types, renders as preformatted text.\n * @param props - Component props including the search result data.\n */\nconst SearchResultItem = async ({ result }: SearchResultItemProps) => {\n const isMarkdown = result.mimeType\n ? MimeTypeUtils.isMarkdown(result.mimeType) ||\n MimeTypeUtils.isSupportedDocument(result.mimeType)\n : true; // Default to true if mimeType is undefined (backward compatibility)\n\n // Create JSDOM instance and initialize DOMPurify (used for both markdown and non-markdown content)\n const jsdom = createJSDOM(\"\");\n const purifier = DOMPurify(jsdom.window);\n\n let contentElement: JSX.Element;\n\n if (isMarkdown) {\n // Use unified pipeline to convert markdown to HTML\n const processor = unified().use(remarkParse).use(remarkGfm).use(remarkHtml);\n const file = await processor.process(result.content);\n const rawHtml = String(file);\n\n // Sanitize the HTML content\n const safeHtml = purifier.sanitize(rawHtml);\n contentElement = (\n <div class=\"format dark:format-invert max-w-none\">{safeHtml}</div>\n );\n } else {\n // For non-markdown content, sanitize and render as preformatted text\n const safeContent = escapeHtml(result.content);\n contentElement = (\n <div class=\"format dark:format-invert max-w-none\">\n <pre>\n <code>{safeContent}</code>\n </pre>\n </div>\n );\n }\n\n return (\n <div class=\"block px-4 py-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600 mb-2\">\n <div class=\"text-sm text-gray-600 dark:text-gray-400 mb-1 flex items-center gap-2 min-w-0\">\n <a\n href={result.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"underline underline-offset-4 flex-1 truncate\"\n title={result.url}\n safe\n >\n {result.url}\n </a>\n {result.sourceMimeType || result.mimeType ? (\n <span class=\"text-xs opacity-75 font-mono\" safe>\n {result.sourceMimeType || result.mimeType}\n </span>\n ) : null}\n </div>\n {/* Render the content based on MIME type */}\n {contentElement}\n </div>\n );\n};\n\nexport default SearchResultItem;\n","import type { StoreSearchResult } from \"../../store/types\";\nimport SearchResultItem from \"./SearchResultItem\";\n\n/**\n * Props for the SearchResultList component.\n */\ninterface SearchResultListProps {\n results: StoreSearchResult[];\n}\n\n/**\n * Renders the list of search results using SearchResultItem.\n * Displays a message if no results are found.\n * @param props - Component props including the array of search results.\n */\nconst SearchResultList = ({ results }: SearchResultListProps) => {\n if (results.length === 0) {\n return (\n <p class=\"text-gray-500 dark:text-gray-400 italic\">No results found.</p>\n );\n }\n return (\n <div class=\"space-y-2\">\n {results.map((result) => (\n <SearchResultItem result={result} />\n ))}\n </div>\n );\n};\n\nexport default SearchResultList;\n","/**\n * Renders a skeleton placeholder for a search result item.\n * Used to indicate loading state while search results are being fetched.\n */\nconst SearchResultSkeletonItem = () => (\n <div class=\"block px-4 py-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm mb-2 animate-pulse\">\n <div class=\"h-[0.8em] bg-gray-200 dark:bg-gray-700 rounded w-3/4 mb-2\"></div>\n <div class=\"h-[0.8em] bg-gray-200 dark:bg-gray-700 rounded w-full mb-2\"></div>\n <div class=\"h-[0.8em] bg-gray-200 dark:bg-gray-700 rounded w-5/6\"></div>\n </div>\n);\n\nexport default SearchResultSkeletonItem;\n","import type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport type { ListLibrariesTool } from \"../../../tools/ListLibrariesTool\";\nimport type { ScrapeTool } from \"../../../tools/ScrapeTool\";\nimport type { SearchTool } from \"../../../tools/SearchTool\";\nimport type { IDocumentManagement } from \"../../../store/trpc/interfaces\";\nimport { logger } from \"../../../utils/logger\";\nimport AddVersionButton from \"../../components/AddVersionButton\";\nimport Alert from \"../../components/Alert\";\nimport Layout from \"../../components/Layout\";\nimport LibraryDetailCard from \"../../components/LibraryDetailCard\";\nimport VersionDetailsRow from \"../../components/VersionDetailsRow\";\nimport LibrarySearchCard from \"../../components/LibrarySearchCard\";\nimport SearchResultList from \"../../components/SearchResultList\";\nimport SearchResultSkeletonItem from \"../../components/SearchResultSkeletonItem\";\nimport ScrapeFormContent from \"../../components/ScrapeFormContent\";\n\n/**\n * Registers the route for displaying library details.\n * @param server - The Fastify instance.\n * @param listLibrariesTool - The tool instance for listing libraries.\n * @param searchTool - The tool instance for searching documentation.\n * @param scrapeTool - The tool instance for scraping documentation.\n * @param docService - The document management service for getting scraper options.\n */\nexport function registerLibraryDetailRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n searchTool: SearchTool,\n scrapeTool: ScrapeTool,\n docService: IDocumentManagement\n) {\n // Route for the library detail page\n server.get(\n \"/libraries/:libraryName\",\n async (\n request: FastifyRequest<{ Params: { libraryName: string } }>,\n reply: FastifyReply\n ) => {\n const { libraryName } = request.params;\n try {\n // Fetch all libraries and find the requested one\n const result = await listLibrariesTool.execute();\n const libraryInfo = result.libraries.find(\n (lib) => lib.name.toLowerCase() === libraryName.toLowerCase()\n );\n\n if (!libraryInfo) {\n reply.status(404).send(\"Library not found\");\n return;\n }\n\n reply.type(\"text/html; charset=utf-8\");\n // Use the Layout component\n return (\n \"<!DOCTYPE html>\" +\n (\n <Layout title={`MCP Docs - ${libraryInfo.name}`}>\n {/* Library Detail Card */}\n <LibraryDetailCard library={libraryInfo} />\n\n {/* Library Search Card */}\n <LibrarySearchCard library={libraryInfo} />\n\n {/* Search Results Container */}\n <div id=\"searchResultsContainer\">\n {/* Skeleton loader - Initially present */}\n <div class=\"search-skeleton space-y-2\">\n <SearchResultSkeletonItem />\n <SearchResultSkeletonItem />\n <SearchResultSkeletonItem />\n </div>\n {/* Search results will be loaded here via HTMX */}\n <div class=\"search-results\">\n {/* Initially empty, HTMX will swap content here */}\n </div>\n </div>\n </Layout>\n )\n );\n } catch (error) {\n server.log.error(\n error,\n `Failed to load library details for ${libraryName}`\n );\n reply.status(500).send(\"Internal Server Error\");\n }\n }\n );\n\n // API route for searching a specific library\n server.get(\n \"/web/libraries/:libraryName/search\",\n async (\n request: FastifyRequest<{\n Params: { libraryName: string };\n Querystring: { query: string; version?: string };\n }>,\n reply: FastifyReply\n ) => {\n const { libraryName } = request.params;\n const { query, version } = request.query;\n\n if (!query) {\n reply.status(400).send(\"Search query is required.\");\n return;\n }\n\n // Map \"latest\" string to undefined for the tool\n const versionParam = version === \"latest\" ? undefined : version;\n\n try {\n const searchResult = await searchTool.execute({\n library: libraryName,\n query,\n version: versionParam,\n limit: 10, // Limit search results\n });\n\n // Return only the results list or error message\n reply.type(\"text/html; charset=utf-8\");\n return <SearchResultList results={searchResult.results} />;\n } catch (error) {\n server.log.error(error, `Failed to search library ${libraryName}`);\n // Return error message using Alert component\n reply.type(\"text/html; charset=utf-8\");\n const errorMessage =\n error instanceof Error\n ? error.message\n : \"An unexpected error occurred during the search.\";\n return <Alert type=\"error\" message={errorMessage} />;\n }\n }\n );\n\n // Note: DELETE and REFRESH routes for versions are defined in list.tsx\n\n // GET route for versions list fragment (for HTMX partial refresh)\n server.get<{ Params: { libraryName: string } }>(\n \"/web/libraries/:libraryName/versions-list\",\n async (request, reply) => {\n const { libraryName } = request.params;\n try {\n const result = await listLibrariesTool.execute();\n const libraryInfo = result.libraries.find(\n (lib) => lib.name.toLowerCase() === libraryName.toLowerCase()\n );\n\n if (!libraryInfo) {\n reply.status(404).send(\"Library not found\");\n return;\n }\n\n // Versions are already sorted descending (latest first) from the API\n const versions = libraryInfo.versions || [];\n\n reply.type(\"text/html; charset=utf-8\");\n if (versions.length === 0) {\n return (\n <p class=\"text-sm text-gray-500 dark:text-gray-400 italic\">\n No versions indexed.\n </p>\n );\n }\n return (\n <>\n {versions.map((v) => {\n const adapted = {\n id: -1,\n ref: { library: libraryInfo.name, version: v.version },\n status: v.status,\n progress: v.progress,\n counts: {\n documents: v.documentCount,\n uniqueUrls: v.uniqueUrlCount,\n },\n indexedAt: v.indexedAt,\n sourceUrl: v.sourceUrl ?? undefined,\n };\n return (\n <VersionDetailsRow\n libraryName={libraryInfo.name}\n version={adapted}\n showDelete={true}\n showRefresh={true}\n />\n );\n })}\n </>\n );\n } catch (error) {\n logger.error(`Failed to fetch versions for ${libraryName}: ${error}`);\n reply.status(500).send(\"Internal Server Error\");\n }\n }\n );\n\n // GET route for add-version button (closes the form and shows the button again)\n server.get<{ Params: { libraryName: string } }>(\n \"/web/libraries/:libraryName/add-version-button\",\n async (request, reply) => {\n const { libraryName } = request.params;\n reply.type(\"text/html; charset=utf-8\");\n return <AddVersionButton libraryName={libraryName} />;\n }\n );\n\n // GET route for add-version form (pre-filled with latest version's config)\n server.get<{ Params: { libraryName: string } }>(\n \"/web/libraries/:libraryName/add-version-form\",\n async (request, reply) => {\n const { libraryName } = request.params;\n try {\n // Fetch library info to get the latest version\n const result = await listLibrariesTool.execute();\n const libraryInfo = result.libraries.find(\n (lib) => lib.name.toLowerCase() === libraryName.toLowerCase()\n );\n\n if (!libraryInfo) {\n reply.status(404).send(\"Library not found\");\n return;\n }\n\n // Get the latest version (versions are sorted descending, first is latest)\n const versions = libraryInfo.versions || [];\n const latestVersion = versions[0];\n\n let initialValues: {\n library: string;\n url?: string;\n maxPages?: number;\n maxDepth?: number;\n scope?: string;\n includePatterns?: string;\n excludePatterns?: string;\n scrapeMode?: string;\n headers?: Array<{ name: string; value: string }>;\n followRedirects?: boolean;\n ignoreErrors?: boolean;\n } = {\n library: libraryName,\n };\n\n // If there's a latest version, fetch its scraper options\n if (latestVersion) {\n const summaries = await docService.listLibraries();\n const libSummary = summaries.find(\n (s) => s.library.toLowerCase() === libraryName.toLowerCase()\n );\n if (libSummary) {\n const versionSummary = libSummary.versions.find(\n (v) =>\n v.ref.version === (latestVersion.version || \"\") ||\n (!latestVersion.version && v.ref.version === \"\")\n );\n if (versionSummary) {\n const scraperConfig = await docService.getScraperOptions(\n versionSummary.id\n );\n if (scraperConfig) {\n const opts = scraperConfig.options;\n initialValues = {\n library: libraryName,\n url: scraperConfig.sourceUrl,\n maxPages: opts.maxPages,\n maxDepth: opts.maxDepth,\n scope: opts.scope,\n includePatterns: opts.includePatterns?.join(\"\\n\"),\n excludePatterns: opts.excludePatterns?.join(\"\\n\"),\n scrapeMode: opts.scrapeMode,\n headers: opts.headers\n ? Object.entries(opts.headers).map(([name, value]) => ({\n name,\n value,\n }))\n : undefined,\n followRedirects: opts.followRedirects,\n ignoreErrors: opts.ignoreErrors,\n };\n }\n }\n }\n }\n\n reply.type(\"text/html; charset=utf-8\");\n return (\n <ScrapeFormContent initialValues={initialValues} mode=\"add-version\" />\n );\n } catch (error) {\n logger.error(\n `Failed to load add-version form for ${libraryName}: ${error}`\n );\n reply.type(\"text/html; charset=utf-8\");\n return (\n <Alert type=\"error\" message=\"Failed to load the add version form.\" />\n );\n }\n }\n );\n}\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport VersionDetailsRow from \"./VersionDetailsRow\";\n\n/**\n * Props for the LibraryItem component.\n */\ninterface LibraryItemProps {\n library: LibraryInfo;\n}\n\n/**\n * Renders a card for a single library, listing its versions with details.\n * Uses VersionDetailsRow to display each version.\n * @param props - Component props including the library information.\n */\nconst LibraryItem = ({ library }: LibraryItemProps) => {\n // Versions are already sorted descending (latest first) from the API\n const versions = library.versions || [];\n const latestVersion = versions[0];\n return (\n // Use Flowbite Card structure with updated padding and border, and white background\n <div\n id={`library-item-${library.name}`}\n class=\"block px-4 py-2 bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-300 dark:border-gray-600\"\n >\n <h3 class=\"text-lg font-medium text-gray-900 dark:text-white\">\n <a\n href={`/libraries/${encodeURIComponent(library.name)}`}\n class=\"hover:underline\"\n >\n <span safe>{library.name}</span>\n </a>\n </h3>\n {latestVersion?.sourceUrl ? (\n <div class=\"text-sm text-gray-500 dark:text-gray-400 overflow-hidden h-5 @container\">\n <a\n href={latestVersion.sourceUrl}\n target=\"_blank\"\n class=\"inline-block whitespace-nowrap hover:underline hover:animate-[scrollText_2s_ease-in-out_forwards]\"\n title={latestVersion.sourceUrl}\n safe\n >\n {latestVersion.sourceUrl}\n </a>\n </div>\n ) : null}\n {/* Container for version rows */}\n <div class=\"mt-2\">\n {versions.length > 0 ? (\n versions.map((v) => {\n // Adapt simplified tool version shape to VersionSummary expected by VersionDetailsRow\n const adapted: VersionSummary = {\n id: -1,\n ref: { library: library.name, version: v.version },\n status: v.status,\n progress: v.progress,\n counts: {\n documents: v.documentCount,\n uniqueUrls: v.uniqueUrlCount,\n },\n indexedAt: v.indexedAt,\n sourceUrl: v.sourceUrl ?? undefined,\n };\n return (\n <VersionDetailsRow libraryName={library.name} version={adapted} />\n );\n })\n ) : (\n // Display message if no versions are indexed\n <p class=\"text-sm text-gray-500 dark:text-gray-400 italic\">\n No versions indexed.\n </p>\n )}\n </div>\n </div>\n );\n};\n\nexport default LibraryItem;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport Alert from \"./Alert\";\nimport LibraryItem from \"./LibraryItem\";\n\n/**\n * Props for the LibraryList component.\n */\ninterface LibraryListProps {\n libraries: LibraryInfo[];\n}\n\n/**\n * Renders a list of LibraryItem components.\n * @param props - Component props including the array of libraries.\n */\nconst LibraryList = ({ libraries }: LibraryListProps) => {\n if (libraries.length === 0) {\n return (\n <Alert\n type=\"info\"\n title=\"Welcome!\"\n message={\n <>\n To get started, click{\" \"}\n <span class=\"font-semibold\">Add New Documentation</span> above and\n enter the URL of a documentation site to index. For more\n information, check the{\" \"}\n <a\n href=\"https://grounded.tools\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"font-medium underline hover:no-underline\"\n >\n official website\n </a>\n .\n </>\n }\n />\n );\n }\n\n return (\n <div\n id=\"library-list\"\n class=\"space-y-2 animate-[fadeSlideIn_0.2s_ease-out]\"\n >\n {libraries.map((library) => (\n <LibraryItem library={library} />\n ))}\n </div>\n );\n};\n\nexport default LibraryList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListLibrariesTool } from \"../../../tools/ListLibrariesTool\";\nimport type { RefreshVersionTool } from \"../../../tools/RefreshVersionTool\";\nimport { RemoveTool } from \"../../../tools\";\nimport { logger } from \"../../../utils/logger\";\nimport LibraryList from \"../../components/LibraryList\";\n\n/**\n * Registers the API routes for library management.\n * @param server - The Fastify instance.\n * @param listLibrariesTool - The tool instance for listing libraries.\n * @param removeTool - The tool instance for removing library versions.\n * @param refreshVersionTool - The tool instance for refreshing library versions.\n */\nexport function registerLibrariesRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n removeTool: RemoveTool,\n refreshVersionTool: RefreshVersionTool\n) {\n server.get(\"/web/libraries\", async (_request, reply) => {\n // Add reply\n try {\n const result = await listLibrariesTool.execute();\n // Set content type to HTML for JSX rendering\n reply.type(\"text/html; charset=utf-8\");\n // Render the component directly\n return <LibraryList libraries={result.libraries} />;\n } catch (error) {\n logger.error(`Failed to list libraries: ${error}`);\n reply.status(500).send(\"Internal Server Error\"); // Handle errors\n }\n });\n\n // Add DELETE route for removing versions\n server.delete<{ Params: { libraryName: string; versionParam: string } }>(\n \"/web/libraries/:libraryName/versions/:versionParam\",\n async (request, reply) => {\n const { libraryName, versionParam } = request.params;\n const version = versionParam === \"latest\" ? undefined : versionParam;\n try {\n await removeTool.execute({ library: libraryName, version });\n\n // Check if the library still exists after deletion\n const result = await listLibrariesTool.execute();\n const libraryStillExists = result.libraries.some(\n (lib) => lib.name.toLowerCase() === libraryName.toLowerCase()\n );\n\n if (!libraryStillExists) {\n // Library was deleted (last version removed) - redirect to main list\n reply.header(\"HX-Redirect\", \"/\");\n }\n\n reply.status(204).send(); // No Content on success\n } catch (error: any) {\n logger.error(\n `Failed to remove ${libraryName}@${versionParam}: ${error}`\n );\n // Check for specific errors if needed, e.g., NotFoundError\n reply\n .status(500)\n .send({ message: error.message || \"Failed to remove version.\" });\n }\n }\n );\n\n // POST route for refreshing a version\n server.post<{ Params: { libraryName: string; versionParam: string } }>(\n \"/web/libraries/:libraryName/versions/:versionParam/refresh\",\n async (request, reply) => {\n const { libraryName, versionParam } = request.params;\n const version =\n versionParam === \"latest\" || versionParam === \"\"\n ? undefined\n : versionParam;\n try {\n // Start refresh without waiting for completion\n await refreshVersionTool.execute({\n library: libraryName,\n version,\n waitForCompletion: false,\n });\n // Show toast notification via HX-Trigger\n const versionDisplay = version || \"latest\";\n reply.header(\n \"HX-Trigger\",\n JSON.stringify({\n toast: {\n message: `Refresh started for ${libraryName}@${versionDisplay}`,\n type: \"success\",\n },\n })\n );\n // Return empty response - the button will reset via Alpine.js\n reply.status(204).send();\n } catch (error: unknown) {\n logger.error(\n `Failed to refresh ${libraryName}@${versionParam}: ${error}`\n );\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to refresh version.\";\n // Show error toast via HX-Trigger\n reply.header(\n \"HX-Trigger\",\n JSON.stringify({\n toast: {\n message: errorMessage,\n type: \"error\",\n },\n })\n );\n reply.status(500).send();\n }\n }\n );\n}\n","interface AnalyticsCardsProps {\n totalChunks: number;\n activeLibraries: number;\n activeVersions: number;\n indexedPages: number;\n}\n\n/**\n * Formats a number for display, using K, M, B suffixes for large numbers.\n */\nfunction formatNumber(num: number): string {\n if (num >= 1_000_000_000) {\n return `${(num / 1_000_000_000).toFixed(1)}B`;\n }\n if (num >= 1_000_000) {\n return `${(num / 1_000_000).toFixed(1)}M`;\n }\n if (num >= 1_000) {\n return `${(num / 1_000).toFixed(1)}K`;\n }\n return num.toString();\n}\n\n/**\n * Displays three analytics cards: Total Chunks, Active Libraries, and Indexed Pages.\n */\nconst AnalyticsCards = ({\n totalChunks,\n activeLibraries,\n activeVersions,\n indexedPages,\n}: AnalyticsCardsProps) => (\n <div class=\"grid grid-cols-1 sm:grid-cols-3 gap-4 mb-4 animate-[fadeSlideIn_0.2s_ease-out]\">\n {/* Knowledge Base Card */}\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600\">\n <div class=\"flex items-center\">\n <div>\n <p class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">\n Total Knowledge Base\n </p>\n <p class=\"text-xl font-semibold text-gray-900 dark:text-white\" safe>\n {formatNumber(totalChunks)} Chunks\n </p>\n </div>\n </div>\n </div>\n\n {/* Active Libraries Card */}\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600\">\n <div class=\"flex items-center\">\n <div>\n <p class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">\n Libraries / Versions\n </p>\n <p class=\"text-xl font-semibold text-gray-900 dark:text-white\">\n {activeLibraries}\n {\" / \"}\n {activeVersions}\n </p>\n </div>\n </div>\n </div>\n\n {/* Indexed Pages Card */}\n <div class=\"p-4 bg-white rounded-lg shadow dark:bg-gray-800 border border-gray-300 dark:border-gray-600\">\n <div class=\"flex items-center\">\n <div>\n <p class=\"text-sm font-medium text-gray-500 dark:text-gray-400\">\n Indexed Pages\n </p>\n <p class=\"text-xl font-semibold text-gray-900 dark:text-white\" safe>\n {formatNumber(indexedPages)}\n </p>\n </div>\n </div>\n </div>\n </div>\n);\n\nexport default AnalyticsCards;\n","import type { FastifyInstance } from \"fastify\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { logger } from \"../../utils/logger\";\nimport AnalyticsCards from \"../components/AnalyticsCards\";\n\n/**\n * Registers the API route for analytics stats.\n * @param server - The Fastify instance.\n * @param docService - The document management service for querying library data.\n */\nexport function registerStatsRoute(\n server: FastifyInstance,\n docService: IDocumentManagement\n) {\n server.get(\"/web/stats\", async (_request, reply) => {\n try {\n const libraries = await docService.listLibraries();\n\n let totalChunks = 0;\n let indexedPages = 0;\n let activeVersions = 0;\n\n for (const lib of libraries) {\n activeVersions += lib.versions.length;\n for (const version of lib.versions) {\n totalChunks += version.counts.documents;\n indexedPages += version.counts.uniqueUrls;\n }\n }\n\n const activeLibraries = libraries.length;\n\n reply.type(\"text/html; charset=utf-8\");\n return (\n <AnalyticsCards\n totalChunks={totalChunks}\n activeLibraries={activeLibraries}\n activeVersions={activeVersions}\n indexedPages={indexedPages}\n />\n );\n } catch (error) {\n logger.error(`Failed to fetch stats: ${error}`);\n reply.status(500).send(\"Internal Server Error\");\n }\n });\n}\n","/**\n * Web service that registers all web interface routes for human interaction.\n * Extracted from src/web/web.ts to enable modular server composition.\n */\n\nimport type { FastifyInstance } from \"fastify\";\nimport type { EventBusService } from \"../events/EventBusService\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { SearchTool } from \"../tools\";\nimport { CancelJobTool } from \"../tools/CancelJobTool\";\nimport { ClearCompletedJobsTool } from \"../tools/ClearCompletedJobsTool\";\nimport { ListJobsTool } from \"../tools/ListJobsTool\";\nimport { ListLibrariesTool } from \"../tools/ListLibrariesTool\";\nimport { RefreshVersionTool } from \"../tools/RefreshVersionTool\";\nimport { RemoveTool } from \"../tools/RemoveTool\";\nimport { ScrapeTool } from \"../tools/ScrapeTool\";\nimport type { AppConfig } from \"../utils/config\";\nimport { registerEventsRoute } from \"../web/routes/events\";\nimport { registerIndexRoute } from \"../web/routes/index\";\nimport { registerCancelJobRoute } from \"../web/routes/jobs/cancel\";\nimport { registerClearCompletedJobsRoute } from \"../web/routes/jobs/clear-completed\";\nimport { registerJobListRoutes } from \"../web/routes/jobs/list\";\nimport { registerNewJobRoutes } from \"../web/routes/jobs/new\";\nimport { registerLibraryDetailRoutes } from \"../web/routes/libraries/detail\";\nimport { registerLibrariesRoutes } from \"../web/routes/libraries/list\";\nimport { registerStatsRoute } from \"../web/routes/stats\";\n\n/**\n * Register web interface routes on a Fastify server instance.\n * This includes all human-facing UI routes.\n * Note: Static file serving and form body parsing are handled by AppServer.\n */\nexport async function registerWebService(\n server: FastifyInstance,\n docService: IDocumentManagement,\n pipeline: IPipeline,\n eventBus: EventBusService,\n appConfig: AppConfig,\n externalWorkerUrl?: string,\n): Promise<void> {\n // Note: Web interface uses direct event tracking without session management\n // This approach provides meaningful analytics without the complexity of per-request sessions\n // Future enhancements could add browser-based session correlation if needed\n\n // Instantiate tools for web routes\n const listLibrariesTool = new ListLibrariesTool(docService);\n const listJobsTool = new ListJobsTool(pipeline);\n const scrapeTool = new ScrapeTool(pipeline, appConfig.scraper);\n const removeTool = new RemoveTool(docService, pipeline);\n const refreshVersionTool = new RefreshVersionTool(pipeline);\n const searchTool = new SearchTool(docService);\n const cancelJobTool = new CancelJobTool(pipeline);\n const clearCompletedJobsTool = new ClearCompletedJobsTool(pipeline);\n\n // Register all web routes\n registerIndexRoute(server, externalWorkerUrl);\n registerLibrariesRoutes(server, listLibrariesTool, removeTool, refreshVersionTool);\n registerLibraryDetailRoutes(\n server,\n listLibrariesTool,\n searchTool,\n scrapeTool,\n docService,\n );\n registerJobListRoutes(server, listJobsTool);\n registerNewJobRoutes(server, scrapeTool, appConfig.scraper);\n registerCancelJobRoute(server, cancelJobTool);\n registerClearCompletedJobsRoute(server, clearCompletedJobsTool);\n registerEventsRoute(server, eventBus);\n registerStatsRoute(server, docService);\n}\n","/**\n * Worker service that enables the embedded pipeline worker functionality.\n * This service starts the pipeline and configures it for background job processing.\n */\n\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { logger } from \"../utils/logger\";\n\n/**\n * Register worker service to enable embedded pipeline processing.\n * This starts the pipeline and configures callbacks for job processing.\n *\n * Note: This function is now deprecated in favor of configuring callbacks\n * in AppServer.setupPipelineEventBridge(). Keeping telemetry/logging here\n * would overwrite the event bus callbacks, breaking SSE notifications.\n * Telemetry and logging should be integrated into the event bus listeners instead.\n */\nexport async function registerWorkerService(pipeline: IPipeline): Promise<void> {\n // DO NOT call pipeline.setCallbacks() here - it would overwrite the callbacks\n // set by AppServer.setupPipelineEventBridge() that emit to the event bus.\n // All callbacks (including telemetry/logging) should be configured in one place.\n\n // Start the pipeline for job processing\n await pipeline.start();\n logger.debug(\"Worker service started\");\n}\n\n/**\n * Stop the worker service and cleanup resources.\n */\nexport async function stopWorkerService(pipeline: IPipeline): Promise<void> {\n await pipeline.stop();\n logger.debug(\"Worker service stopped\");\n}\n","/**\n * ASCII art banner for console startup display.\n * Uses ANSI colors: gray (dim) top line, bright white bottom line.\n */\nexport const BANNER = [\n \"\\x1b[90m █▀▀ █▀█ █▀█ █ █ █▄ █ █▀▄ █▀▀ █▀▄ █▀▄ █▀█ █▀▀ █▀▀\\x1b[0m\",\n \"\\x1b[97m █▄█ █▀▄ █▄█ █▄█ █ ▀█ █▄▀ ██▄ █▄▀ █▄▀ █▄█ █▄▄ ▄▄█\\x1b[0m\",\n].join(\"\\n\");\n\n/**\n * Prints the startup banner to console.\n * Only call this for HTTP server startup, not stdio mode.\n */\nexport function printBanner(): void {\n console.log(); // Blank line before banner\n console.log(BANNER);\n console.log(); // Blank line after banner\n}\n","/**\n * Central application server that can be configured to run different combinations of services.\n * This replaces the separate server implementations with a single, modular approach.\n */\n\nimport path from \"node:path\";\nimport formBody from \"@fastify/formbody\";\nimport fastifyStatic from \"@fastify/static\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport Fastify, { type FastifyError, type FastifyInstance } from \"fastify\";\nimport { WebSocketServer } from \"ws\";\nimport { ProxyAuthManager } from \"../auth\";\nimport type { EventBusService } from \"../events\";\nimport { RemoteEventProxy } from \"../events/RemoteEventProxy\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { cleanupMcpService, registerMcpService } from \"../services/mcpService\";\nimport { applyTrpcWebSocketHandler, registerTrpcService } from \"../services/trpcService\";\nimport { registerWebService } from \"../services/webService\";\nimport { registerWorkerService, stopWorkerService } from \"../services/workerService\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { TelemetryEvent, telemetry } from \"../telemetry\";\nimport { shouldEnableTelemetry } from \"../telemetry/TelemetryConfig\";\nimport { printBanner } from \"../utils/banner\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport type { AppServerConfig } from \"./AppServerConfig\";\n\n/**\n * Central application server that provides modular service composition.\n */\nexport class AppServer {\n private server: FastifyInstance;\n private mcpServer: McpServer | null = null;\n private authManager: ProxyAuthManager | null = null;\n private serverConfig: AppServerConfig;\n private readonly appConfig: AppConfig;\n private remoteEventProxy: RemoteEventProxy | null = null;\n private wss: WebSocketServer | null = null;\n\n constructor(\n private docService: IDocumentManagement,\n private pipeline: IPipeline,\n private eventBus: EventBusService,\n serverConfig: AppServerConfig,\n appConfig: AppConfig,\n ) {\n this.serverConfig = serverConfig;\n this.appConfig = appConfig;\n this.server = Fastify({\n logger: false, // Use our own logger\n });\n }\n\n /**\n * Validate the server configuration for invalid service combinations.\n */\n private validateConfig(): void {\n // Web interface needs either worker or external worker URL\n if (this.serverConfig.enableWebInterface) {\n if (!this.serverConfig.enableWorker && !this.serverConfig.externalWorkerUrl) {\n throw new Error(\n \"Web interface requires either embedded worker (enableWorker: true) or external worker (externalWorkerUrl)\",\n );\n }\n }\n\n // MCP server needs pipeline access (worker or external)\n if (this.serverConfig.enableMcpServer) {\n if (!this.serverConfig.enableWorker && !this.serverConfig.externalWorkerUrl) {\n throw new Error(\n \"MCP server requires either embedded worker (enableWorker: true) or external worker (externalWorkerUrl)\",\n );\n }\n }\n }\n\n /**\n * Start the application server with the configured services.\n */\n async start(): Promise<FastifyInstance> {\n this.validateConfig();\n\n // Get embedding configuration from the document service (source of truth)\n const embeddingConfig = this.docService.getActiveEmbeddingConfig();\n\n // Initialize telemetry if enabled\n if (this.appConfig.app.telemetryEnabled && shouldEnableTelemetry()) {\n try {\n // Set global application context that will be included in all events\n if (telemetry.isEnabled()) {\n telemetry.setGlobalContext({\n appVersion: __APP_VERSION__,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appServicesEnabled: this.getActiveServicesList(),\n appAuthEnabled: Boolean(this.appConfig.auth.enabled),\n appReadOnly: Boolean(this.appConfig.app.readOnly),\n // Add embedding configuration to global context\n ...(embeddingConfig && {\n aiEmbeddingProvider: embeddingConfig.provider,\n aiEmbeddingModel: embeddingConfig.model,\n aiEmbeddingDimensions: embeddingConfig.dimensions,\n }),\n });\n\n // Track app start at the very beginning\n telemetry.track(TelemetryEvent.APP_STARTED, {\n services: this.getActiveServicesList(),\n port: this.serverConfig.port,\n externalWorker: Boolean(this.serverConfig.externalWorkerUrl),\n // Include startup context when available\n ...(this.serverConfig.startupContext?.cliCommand && {\n cliCommand: this.serverConfig.startupContext.cliCommand,\n }),\n ...(this.serverConfig.startupContext?.mcpProtocol && {\n mcpProtocol: this.serverConfig.startupContext.mcpProtocol,\n }),\n ...(this.serverConfig.startupContext?.mcpTransport && {\n mcpTransport: this.serverConfig.startupContext.mcpTransport,\n }),\n });\n }\n } catch (error) {\n logger.debug(`Failed to initialize telemetry: ${error}`);\n }\n }\n\n await this.setupServer();\n\n try {\n const address = await this.server.listen({\n port: this.serverConfig.port,\n host: this.appConfig.server.host,\n });\n\n // Setup WebSocket server for tRPC subscriptions if API server is enabled\n if (this.serverConfig.enableApiServer) {\n this.setupWebSocketServer();\n }\n\n // Connect to remote worker after server is fully started\n if (this.remoteEventProxy) {\n // Don't await - let it connect in the background\n this.remoteEventProxy.connect();\n }\n\n this.logStartupInfo(address);\n return this.server;\n } catch (error) {\n logger.error(`❌ Failed to start AppServer: ${error}`);\n await this.server.close();\n throw error;\n }\n }\n\n /**\n * Stop the application server and cleanup all services.\n */\n async stop(): Promise<void> {\n try {\n // Disconnect remote event proxy if connected\n if (this.remoteEventProxy) {\n this.remoteEventProxy.disconnect();\n }\n\n // Stop worker service if enabled\n if (this.serverConfig.enableWorker) {\n await stopWorkerService(this.pipeline);\n }\n\n // Cleanup MCP service if enabled\n if (this.mcpServer) {\n await cleanupMcpService(this.mcpServer);\n }\n\n // Close WebSocket server if it exists\n if (this.wss) {\n // Forcibly close all active client connections before closing the server\n for (const client of this.wss.clients) {\n client.terminate();\n }\n\n await new Promise<void>((resolve, reject) => {\n this.wss?.close((err) => {\n if (err) {\n logger.error(`❌ Failed to close WebSocket server: ${err}`);\n reject(err);\n } else {\n logger.debug(\"WebSocket server closed\");\n resolve();\n }\n });\n });\n }\n\n // Track app shutdown\n if (telemetry.isEnabled()) {\n telemetry.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: true,\n });\n }\n\n // Shutdown telemetry service (this will flush remaining events)\n await telemetry.shutdown();\n\n // Force close all connections to ensure immediate shutdown\n if (this.server.server) {\n this.server.server.closeAllConnections();\n }\n\n // Close Fastify server\n await this.server.close();\n logger.info(\"🛑 AppServer stopped\");\n } catch (error) {\n logger.error(`❌ Failed to stop AppServer gracefully: ${error}`);\n\n // Track ungraceful shutdown\n if (telemetry.isEnabled()) {\n telemetry.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: false,\n error: error instanceof Error ? error.constructor.name : \"UnknownError\",\n });\n await telemetry.shutdown();\n }\n\n throw error;\n }\n }\n\n /**\n * Setup global error handling for telemetry\n */\n private setupErrorHandling(): void {\n // Only add listeners if they haven't been added yet (prevent duplicate listeners in tests)\n if (!process.listenerCount(\"unhandledRejection\")) {\n // Catch unhandled promise rejections\n process.on(\"unhandledRejection\", (reason) => {\n logger.error(`Unhandled Promise Rejection: ${reason}`);\n if (telemetry.isEnabled()) {\n // Create an Error object from the rejection reason for better tracking\n const error = reason instanceof Error ? reason : new Error(String(reason));\n telemetry.captureException(error, {\n error_category: \"system\",\n component: AppServer.constructor.name,\n context: \"process_unhandled_rejection\",\n });\n }\n });\n }\n\n if (!process.listenerCount(\"uncaughtException\")) {\n // Catch uncaught exceptions\n process.on(\"uncaughtException\", (error) => {\n logger.error(`Uncaught Exception: ${error.message}`);\n if (telemetry.isEnabled()) {\n telemetry.captureException(error, {\n error_category: \"system\",\n component: AppServer.constructor.name,\n context: \"process_uncaught_exception\",\n });\n }\n // Don't exit immediately, let the app attempt graceful shutdown\n });\n }\n\n // Setup Fastify error handler (if method exists - for testing compatibility)\n if (typeof this.server.setErrorHandler === \"function\") {\n this.server.setErrorHandler<FastifyError>(async (error, request, reply) => {\n if (telemetry.isEnabled()) {\n telemetry.captureException(error, {\n errorCategory: \"http\",\n component: \"FastifyServer\",\n statusCode: error.statusCode || 500,\n method: request.method,\n route: request.routeOptions?.url || request.url,\n context: \"http_request_error\",\n });\n }\n\n logger.error(`HTTP Error on ${request.method} ${request.url}: ${error.message}`);\n\n // Send appropriate error response\n const statusCode = error.statusCode || 500;\n reply.status(statusCode).send({\n error: \"Internal Server Error\",\n statusCode,\n message: statusCode < 500 ? error.message : \"An unexpected error occurred\",\n });\n });\n }\n }\n\n /**\n * Get list of currently active services for telemetry\n */\n private getActiveServicesList(): string[] {\n const services: string[] = [];\n if (this.serverConfig.enableMcpServer) services.push(\"mcp\");\n if (this.serverConfig.enableWebInterface) services.push(\"web\");\n if (this.serverConfig.enableApiServer) services.push(\"api\");\n if (this.serverConfig.enableWorker) services.push(\"worker\");\n return services;\n }\n\n /**\n * Setup the server with plugins and conditionally enabled services.\n */\n private async setupServer(): Promise<void> {\n // Setup global error handling for telemetry\n this.setupErrorHandling();\n\n // Setup remote event proxy if using an external worker\n this.setupRemoteEventProxy();\n\n // Initialize authentication if enabled\n if (this.appConfig.auth.enabled) {\n await this.initializeAuth();\n }\n\n // Register core Fastify plugins\n await this.server.register(formBody);\n\n // Add request logging middleware for OAuth debugging\n if (this.appConfig.auth.enabled) {\n this.server.addHook(\"onRequest\", async (request) => {\n if (\n request.url.includes(\"/oauth\") ||\n request.url.includes(\"/auth\") ||\n request.url.includes(\"/register\")\n ) {\n logger.debug(\n `${request.method} ${request.url} - Headers: ${JSON.stringify(request.headers)}`,\n );\n }\n });\n }\n\n // Add protected resource metadata endpoint for RFC9728 compliance\n if (this.appConfig.auth.enabled && this.authManager) {\n await this.setupAuthMetadataEndpoint();\n }\n\n // Conditionally enable services based on configuration\n if (this.serverConfig.enableWebInterface) {\n await this.enableWebInterface();\n }\n\n if (this.serverConfig.enableMcpServer) {\n await this.enableMcpServer();\n }\n\n if (this.serverConfig.enableApiServer) {\n await this.enableTrpcApi();\n }\n\n if (this.serverConfig.enableWorker) {\n await this.enableWorker();\n }\n\n // Setup static file serving as fallback (must be last)\n if (this.serverConfig.enableWebInterface) {\n await this.setupStaticFiles();\n }\n }\n\n /**\n * Initialize remote event proxy if using an external worker.\n * The proxy is created here but connection is deferred until after server starts.\n */\n private setupRemoteEventProxy(): void {\n // If using an external worker, create remote event proxy (connection happens later)\n if (this.serverConfig.externalWorkerUrl) {\n this.remoteEventProxy = new RemoteEventProxy(\n this.serverConfig.externalWorkerUrl,\n this.eventBus,\n );\n logger.debug(\n \"Remote event proxy created for external worker (connection deferred)\",\n );\n }\n }\n\n /**\n * Enable web interface service.\n */\n private async enableWebInterface(): Promise<void> {\n await registerWebService(\n this.server,\n this.docService,\n this.pipeline,\n this.eventBus,\n this.appConfig,\n this.serverConfig.externalWorkerUrl,\n );\n\n logger.debug(\"Web interface service enabled\");\n }\n\n /**\n * Enable MCP server service.\n */\n private async enableMcpServer(): Promise<void> {\n this.mcpServer = await registerMcpService(\n this.server,\n this.docService,\n this.pipeline,\n this.appConfig,\n this.authManager || undefined,\n );\n logger.debug(\"MCP server service enabled\");\n }\n\n /**\n * Enable Pipeline RPC (tRPC) service.\n */\n private async enableTrpcApi(): Promise<void> {\n await registerTrpcService(this.server, this.pipeline, this.docService, this.eventBus);\n logger.debug(\"API server (tRPC) enabled\");\n }\n\n /**\n * Setup WebSocket server for tRPC subscriptions.\n * This is called after the HTTP server is listening.\n */\n private setupWebSocketServer(): void {\n // Ensure the underlying HTTP server is available\n if (!this.server.server) {\n throw new Error(\n \"Cannot setup WebSocket server: HTTP server not available. \" +\n \"This method must be called after server.listen() completes.\",\n );\n }\n\n // Create WebSocket server attached to the HTTP server\n this.wss = new WebSocketServer({\n noServer: true,\n });\n\n // Handle HTTP upgrade requests for WebSocket connections\n this.server.server.on(\"upgrade\", (request, socket, head) => {\n // Let the WebSocket server handle all upgrade requests.\n // tRPC's WebSocket handler manages routing internally after connection is established.\n this.wss?.handleUpgrade(request, socket, head, (ws) => {\n this.wss?.emit(\"connection\", ws, request);\n });\n });\n\n // Apply tRPC WebSocket handler to enable subscriptions\n applyTrpcWebSocketHandler(this.wss, this.pipeline, this.docService, this.eventBus);\n\n logger.debug(\"WebSocket server initialized for tRPC subscriptions\");\n }\n\n /**\n * Enable worker service.\n */\n private async enableWorker(): Promise<void> {\n await registerWorkerService(this.pipeline);\n logger.debug(\"Worker service enabled\");\n }\n\n /**\n * Setup static file serving with root prefix as fallback.\n */\n private async setupStaticFiles(): Promise<void> {\n await this.server.register(fastifyStatic, {\n root: path.join(getProjectRoot(), \"public\"),\n prefix: \"/\",\n index: false,\n });\n }\n\n /**\n * Initialize OAuth2/OIDC authentication manager.\n */\n private async initializeAuth(): Promise<void> {\n if (!this.appConfig.auth.enabled) {\n return;\n }\n\n if (!this.appConfig.auth.issuerUrl || !this.appConfig.auth.audience) {\n throw new Error(\n \"Authentication is enabled but auth.issuerUrl or auth.audience is not configured.\",\n );\n }\n\n this.authManager = new ProxyAuthManager({\n enabled: true,\n issuerUrl: this.appConfig.auth.issuerUrl,\n audience: this.appConfig.auth.audience,\n scopes: [\"openid\", \"profile\"],\n });\n await this.authManager.initialize();\n logger.debug(\"Proxy auth manager initialized\");\n }\n\n /**\n * Setup OAuth2 endpoints using ProxyAuthManager.\n */\n private async setupAuthMetadataEndpoint(): Promise<void> {\n if (!this.authManager) {\n return;\n }\n\n // ProxyAuthManager handles all OAuth2 endpoints automatically\n const baseUrl = new URL(`http://localhost:${this.serverConfig.port}`);\n this.authManager.registerRoutes(this.server, baseUrl);\n\n logger.debug(\"OAuth2 proxy endpoints registered\");\n }\n\n /**\n * Log startup information showing which services are enabled.\n */\n private logStartupInfo(address: string): void {\n // Print the ASCII art banner if enabled\n if (this.serverConfig.showLogo !== false) {\n printBanner();\n }\n\n // Determine the service mode\n const isWorkerOnly =\n this.serverConfig.enableWorker &&\n !this.serverConfig.enableWebInterface &&\n !this.serverConfig.enableMcpServer;\n const isWebOnly =\n this.serverConfig.enableWebInterface &&\n !this.serverConfig.enableWorker &&\n !this.serverConfig.enableMcpServer;\n const isMcpOnly =\n this.serverConfig.enableMcpServer &&\n !this.serverConfig.enableWebInterface &&\n !this.serverConfig.enableWorker;\n\n // Determine the main service name\n if (isWorkerOnly) {\n logger.info(`🚀 Worker available at ${address}`);\n } else if (isWebOnly) {\n logger.info(`🚀 Web interface available at ${address}`);\n } else if (isMcpOnly) {\n logger.info(`🚀 MCP server available at ${address}`);\n } else {\n logger.info(`🚀 Grounded Docs available at ${address}`);\n }\n\n const isCombined = !isWorkerOnly && !isWebOnly && !isMcpOnly;\n\n const enabledServices: string[] = [];\n\n // Web interface: only show if combined mode\n if (this.serverConfig.enableWebInterface && isCombined) {\n enabledServices.push(`Web interface: ${address}`);\n }\n\n // MCP endpoints: always show if enabled\n if (this.serverConfig.enableMcpServer) {\n enabledServices.push(`MCP endpoints: ${address}/mcp, ${address}/sse`);\n }\n\n // Worker: only show external worker URL (internal is implied)\n if (!this.serverConfig.enableWorker && this.serverConfig.externalWorkerUrl) {\n enabledServices.push(`Worker: ${this.serverConfig.externalWorkerUrl}`);\n }\n\n // Embeddings: only show if worker is enabled\n if (this.serverConfig.enableWorker) {\n const embeddingConfig = this.docService.getActiveEmbeddingConfig();\n if (embeddingConfig) {\n enabledServices.push(\n `Embeddings: ${embeddingConfig.provider}:${embeddingConfig.model}`,\n );\n } else {\n enabledServices.push(`Embeddings: disabled (full text search only)`);\n }\n }\n\n for (const service of enabledServices) {\n logger.info(` • ${service}`);\n }\n }\n}\n","/**\n * App module exports for the central application server architecture.\n */\n\n/**\n * Application server module exports.\n * Provides AppServer and configuration types, plus convenience functions for CLI integration.\n */\n\nexport { AppServer } from \"./AppServer\";\nexport type { AppServerConfig } from \"./AppServerConfig\";\n\nimport type { EventBusService } from \"../events\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { AppConfig } from \"../utils/config\";\nimport { AppServer } from \"./AppServer\";\nimport type { AppServerConfig } from \"./AppServerConfig\";\n\n/**\n * Start an AppServer with the given configuration.\n * Convenience function for CLI integration.\n */\nexport async function startAppServer(\n docService: IDocumentManagement,\n pipeline: IPipeline,\n eventBus: EventBusService,\n serverConfig: AppServerConfig,\n appConfig: AppConfig,\n): Promise<AppServer> {\n const appServer = new AppServer(\n docService,\n pipeline,\n eventBus,\n serverConfig,\n appConfig,\n );\n await appServer.start();\n return appServer;\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { createMcpServerInstance } from \"./mcpServer\";\nimport type { McpServerTools } from \"./tools\";\n\n/**\n * Starts the MCP server using the Stdio transport.\n * @param tools The shared tool instances.\n * @param config The application configuration.\n * @returns The created McpServer instance.\n */\nexport async function startStdioServer(\n tools: McpServerTools,\n config: AppConfig,\n): Promise<McpServer> {\n // Create a server instance using the factory and shared tools\n const server = createMcpServerInstance(tools, config);\n\n // Start server with Stdio transport\n const transport = new StdioServerTransport();\n await server.connect(transport);\n logger.info(\"🤖 MCP server listening on stdio\");\n\n // Return the server instance\n return server;\n}\n","/**\n * tRPC client implementation of the Pipeline interface.\n * Delegates all pipeline operations to an external worker via tRPC router.\n * Uses WebSocket link for subscriptions and HTTP for queries/mutations.\n */\n\nimport {\n createTRPCProxyClient,\n createWSClient,\n httpBatchLink,\n splitLink,\n wsLink,\n} from \"@trpc/client\";\nimport superjson from \"superjson\";\nimport type { EventBusService } from \"../events/EventBusService\";\nimport { EventType } from \"../events/types\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { logger } from \"../utils/logger\";\nimport type { IPipeline } from \"./trpc/interfaces\";\nimport type { PipelineRouter } from \"./trpc/router\";\nimport type { PipelineJob, PipelineJobStatus, PipelineManagerCallbacks } from \"./types\";\n\n/**\n * HTTP client that implements the IPipeline interface by delegating to external worker.\n */\nexport class PipelineClient implements IPipeline {\n private readonly baseUrl: string;\n private readonly wsUrl: string;\n private readonly client: ReturnType<typeof createTRPCProxyClient<PipelineRouter>>;\n private readonly wsClient: ReturnType<typeof createWSClient>;\n private readonly eventBus: EventBusService;\n\n constructor(serverUrl: string, eventBus: EventBusService) {\n this.baseUrl = serverUrl.replace(/\\/$/, \"\");\n this.eventBus = eventBus;\n\n // Extract base URL without the /api path for WebSocket connection\n // The tRPC WebSocket adapter handles the /api routing internally\n const url = new URL(this.baseUrl);\n const baseWsUrl = `${url.protocol}//${url.host}`;\n this.wsUrl = baseWsUrl.replace(/^http/, \"ws\");\n\n // Create WebSocket client for subscriptions\n this.wsClient = createWSClient({\n url: this.wsUrl,\n });\n\n // Create tRPC client with split link:\n // - Subscriptions use WebSocket\n // - Queries and mutations use HTTP\n this.client = createTRPCProxyClient<PipelineRouter>({\n links: [\n splitLink({\n condition: (op) => op.type === \"subscription\",\n true: wsLink({ client: this.wsClient, transformer: superjson }),\n false: httpBatchLink({ url: this.baseUrl, transformer: superjson }),\n }),\n ],\n });\n\n logger.debug(\n `PipelineClient (tRPC) created for: ${this.baseUrl} (ws: ${this.wsUrl})`,\n );\n }\n\n async start(): Promise<void> {\n // Check connectivity via ping procedure\n try {\n await this.client.ping.query();\n logger.debug(\"PipelineClient connected to external worker via tRPC\");\n } catch (error) {\n throw new Error(\n `Failed to connect to external worker at ${this.baseUrl}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async stop(): Promise<void> {\n // Close WebSocket connection\n this.wsClient.close();\n\n logger.debug(\"PipelineClient stopped\");\n }\n\n async enqueueScrapeJob(\n library: string,\n version: string | undefined | null,\n options: ScraperOptions,\n ): Promise<string> {\n try {\n const normalizedVersion =\n typeof version === \"string\" && version.trim().length === 0\n ? null\n : (version ?? null);\n const result = await this.client.enqueueScrapeJob.mutate({\n library,\n version: normalizedVersion,\n options,\n });\n logger.debug(`Job ${result.jobId} enqueued successfully`);\n return result.jobId;\n } catch (error) {\n throw new Error(\n `Failed to enqueue job: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async enqueueRefreshJob(\n library: string,\n version: string | undefined | null,\n ): Promise<string> {\n try {\n const normalizedVersion =\n typeof version === \"string\" && version.trim().length === 0\n ? null\n : (version ?? null);\n const result = await this.client.enqueueRefreshJob.mutate({\n library,\n version: normalizedVersion,\n });\n logger.debug(`Refresh job ${result.jobId} enqueued successfully`);\n return result.jobId;\n } catch (error) {\n throw new Error(\n `Failed to enqueue refresh job: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async getJob(jobId: string): Promise<PipelineJob | undefined> {\n try {\n // superjson automatically deserializes Date objects\n return await this.client.getJob.query({ id: jobId });\n } catch (error) {\n throw new Error(\n `Failed to get job ${jobId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n async getJobs(status?: PipelineJobStatus): Promise<PipelineJob[]> {\n try {\n // superjson automatically deserializes Date objects\n const result = await this.client.getJobs.query({ status });\n return result.jobs || [];\n } catch (error) {\n logger.error(`❌ Failed to get jobs from external worker: ${error}`);\n throw error;\n }\n }\n\n async cancelJob(jobId: string): Promise<void> {\n try {\n await this.client.cancelJob.mutate({ id: jobId });\n logger.debug(`Job cancelled via external worker: ${jobId}`);\n } catch (error) {\n logger.error(`❌ Failed to cancel job ${jobId} via external worker: ${error}`);\n throw error;\n }\n }\n\n async clearCompletedJobs(): Promise<number> {\n try {\n const result = await this.client.clearCompletedJobs.mutate();\n logger.debug(`Cleared ${result.count} completed jobs via external worker`);\n return result.count || 0;\n } catch (error) {\n logger.error(`❌ Failed to clear completed jobs via external worker: ${error}`);\n throw error;\n }\n }\n\n async waitForJobCompletion(jobId: string): Promise<void> {\n return new Promise((resolve, reject) => {\n // Listen for job status changes on the event bus\n // RemoteEventProxy bridges remote worker events to this local bus\n const unsubscribe = this.eventBus.on(\n EventType.JOB_STATUS_CHANGE,\n (job: PipelineJob) => {\n // Filter for the specific job we're waiting for\n if (job.id !== jobId) {\n return;\n }\n\n // Check if job reached a terminal state\n if (\n job.status === \"completed\" ||\n job.status === \"failed\" ||\n job.status === \"cancelled\"\n ) {\n unsubscribe();\n\n if (job.status === \"failed\" && job.error) {\n reject(new Error(job.error.message));\n } else {\n resolve();\n }\n }\n },\n );\n });\n }\n\n setCallbacks(_callbacks: PipelineManagerCallbacks): void {\n // For external pipeline, callbacks are not used since all updates come via event bus\n logger.debug(\"PipelineClient.setCallbacks called - no-op for external worker\");\n }\n}\n","import { minimatch } from \"minimatch\";\nimport { getEffectiveExclusionPatterns } from \"./defaultPatterns\";\n\n/**\n * Utility functions for pattern matching (glob and regex) for URL filtering.\n * Supports auto-detection and conversion of glob patterns to RegExp.\n *\n * Default exclusion patterns are applied when no user-provided exclude patterns are specified.\n * This includes common documentation files (CHANGELOG.md, LICENSE, etc.) and folders\n * (archive directories, non-English locales, etc.).\n *\n * Patterns starting and ending with '/' are treated as regex, otherwise as glob (minimatch syntax).\n * Glob wildcards supported: '*' (any chars except '/'), '**' (any chars, including '/').\n *\n * @module patternMatcher\n */\n\n/**\n * Detects if a pattern is a regex (starts and ends with '/')\n */\nexport function isRegexPattern(pattern: string): boolean {\n return pattern.length > 2 && pattern.startsWith(\"/\") && pattern.endsWith(\"/\");\n}\n\n/**\n * Converts a pattern string to a RegExp instance (auto-detects glob/regex).\n * For globs, uses minimatch's internal conversion.\n */\nexport function patternToRegExp(pattern: string): RegExp {\n if (isRegexPattern(pattern)) {\n return new RegExp(pattern.slice(1, -1));\n }\n // For globs, minimatch.makeRe returns a RegExp\n const re = minimatch.makeRe(pattern, { dot: true });\n if (!re) throw new Error(`Invalid glob pattern: ${pattern}`);\n return re;\n}\n\n/**\n * Checks if a given path matches any pattern in the list.\n * For globs, uses minimatch. For regex, uses RegExp.\n */\nexport function matchesAnyPattern(path: string, patterns?: string[]): boolean {\n if (!patterns || patterns.length === 0) return false;\n // Always match from a leading slash for path-based globs\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n return patterns.some((pattern) => {\n if (isRegexPattern(pattern)) {\n return patternToRegExp(pattern).test(normalizedPath);\n }\n // For glob patterns:\n // - If pattern starts with '/', strip leading slash from BOTH pattern and path for minimatch\n // - Otherwise, strip leading slash only from path\n const pathForMatch = normalizedPath.replace(/^\\//, \"\");\n const patternForMatch = pattern.startsWith(\"/\") ? pattern.slice(1) : pattern;\n return minimatch(pathForMatch, patternForMatch, { dot: true });\n });\n}\n\n/**\n * Extracts the path and query from a URL string (no domain).\n */\nexport function extractPathAndQuery(url: string): string {\n try {\n const u = new URL(url);\n return u.pathname + (u.search || \"\");\n } catch {\n return url; // fallback: return as-is\n }\n}\n\n/**\n * Determines if a URL should be included based on include/exclude patterns.\n * Exclude patterns take precedence. If no include patterns, all are included by default.\n *\n * If no user exclude patterns are provided, default exclusion patterns are automatically applied.\n * These defaults exclude common documentation files (CHANGELOG.md, LICENSE, etc.) and folders\n * (archives, non-English locales, etc.).\n *\n * Patterns are matched against both the full URL and the pathname for maximum flexibility:\n * - Full URL: `https://example.com/docs/v3/**` matches `https://example.com/docs/v3/guide`\n * - Pathname: `/docs/v3/**` matches `https://example.com/docs/v3/guide`\n */\nexport function shouldIncludeUrl(\n url: string,\n includePatterns?: string[],\n excludePatterns?: string[],\n): boolean {\n // Extract pathname for path-based pattern matching\n const path = extractPathAndQuery(url);\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\n\n // For file:// URLs, also match against the basename (strip leading slash from pattern for basename matching)\n let basename: string | undefined;\n if (url.startsWith(\"file://\")) {\n try {\n const u = new URL(url);\n basename = u.pathname ? u.pathname.split(\"/\").pop() : undefined;\n } catch {}\n }\n // Helper to strip leading slash from patterns for basename matching\n const stripSlash = (patterns?: string[]) =>\n patterns?.map((p) => (p.startsWith(\"/\") ? p.slice(1) : p));\n\n // Get effective exclusion patterns (merges defaults with user patterns)\n const effectiveExcludePatterns = getEffectiveExclusionPatterns(excludePatterns);\n\n // Exclude patterns take precedence\n // Match against BOTH full URL and pathname for flexibility\n if (\n matchesAnyPattern(url, effectiveExcludePatterns) ||\n matchesAnyPattern(normalizedPath, effectiveExcludePatterns) ||\n (basename && matchesAnyPattern(basename, stripSlash(effectiveExcludePatterns)))\n )\n return false;\n if (!includePatterns || includePatterns.length === 0) return true;\n // Match against BOTH full URL and pathname for flexibility\n return (\n matchesAnyPattern(url, includePatterns) ||\n matchesAnyPattern(normalizedPath, includePatterns) ||\n (basename ? matchesAnyPattern(basename, stripSlash(includePatterns)) : false)\n );\n}\n","// Utility for scope filtering, extracted from WebScraperStrategy\nimport type { URL } from \"node:url\";\nimport { extractPrimaryDomain } from \"../../utils/url\";\n\n/**\n * Compute the effective base directory for scope=subpages.\n * Rules:\n * - If path ends with '/', treat as directory (keep as-is)\n * - Else if last segment contains a dot, treat as file and use its parent directory\n * - Else treat the path as a directory name (append '/')\n */\nexport function computeBaseDirectory(pathname: string): string {\n if (pathname === \"\") return \"/\";\n if (pathname.endsWith(\"/\")) return pathname;\n const lastSegment = pathname.split(\"/\").at(-1) || \"\";\n const looksLikeFile = lastSegment.includes(\".\");\n if (looksLikeFile) {\n return pathname.replace(/\\/[^/]*$/, \"/\");\n }\n return `${pathname}/`;\n}\n\n/**\n * Returns true if the targetUrl is in scope of the baseUrl for the given scope.\n * - \"subpages\": same hostname, and target path starts with the parent directory of the base path\n * - \"hostname\": same hostname\n * - \"domain\": same top-level domain (e.g. example.com)\n */\nexport function isInScope(\n baseUrl: URL,\n targetUrl: URL,\n scope: \"subpages\" | \"hostname\" | \"domain\",\n): boolean {\n if (baseUrl.protocol !== targetUrl.protocol) return false;\n\n switch (scope) {\n case \"subpages\": {\n if (baseUrl.hostname !== targetUrl.hostname) return false;\n const baseDir = computeBaseDirectory(baseUrl.pathname);\n return targetUrl.pathname.startsWith(baseDir);\n }\n case \"hostname\":\n return baseUrl.hostname === targetUrl.hostname;\n case \"domain\": {\n return (\n extractPrimaryDomain(baseUrl.hostname) ===\n extractPrimaryDomain(targetUrl.hostname)\n );\n }\n default:\n return false;\n }\n}\n","import { URL } from \"node:url\";\nimport { CancellationError } from \"../../pipeline/errors\";\nimport type { ProgressCallback } from \"../../types\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { normalizeUrl, type UrlNormalizerOptions } from \"../../utils/url\";\nimport { FetchStatus } from \"../fetcher/types\";\nimport type { PipelineResult } from \"../pipelines/types\";\nimport type {\n QueueItem,\n ScrapeResult,\n ScraperOptions,\n ScraperProgressEvent,\n ScraperStrategy,\n} from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { isInScope } from \"../utils/scope\";\n\nexport interface BaseScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n}\n\n/**\n * Result of processing a single queue item.\n * - processed: The processed content (when available)\n * - links: Discovered links for crawling (may exist without content, e.g., directories)\n * - status: The fetch status (SUCCESS, NOT_MODIFIED, NOT_FOUND)\n */\nexport interface ProcessItemResult {\n /** The URL of the content */\n url: string;\n /** The title of the page or document, extracted during processing */\n title?: string | null;\n /** Original MIME type of the fetched resource, if known */\n sourceContentType?: string | null;\n /** MIME type of the stored content after pipeline processing, if known */\n contentType?: string | null;\n /** The ETag header value from the HTTP response, if available, used for caching and change detection. */\n etag?: string | null;\n /** The Last-Modified header value, if available, used for caching and change detection. */\n lastModified?: string | null;\n /** The pipeline-processed content, including title, text content, links, errors, and chunks. This may be null if the content was not successfully processed (e.g., 404 or 304). */\n content?: PipelineResult;\n /** Extracted links from the content. This may be an empty array if no links were found or if the content was not processed. */\n links?: string[];\n /** Any non-critical errors encountered during processing. This may be an empty array if no errors were encountered or if the content was not processed. */\n status: FetchStatus;\n}\n\nexport abstract class BaseScraperStrategy implements ScraperStrategy {\n /**\n * Set of normalized URLs that have been marked for processing.\n *\n * IMPORTANT: URLs are added to this set BEFORE they are actually processed, not after.\n * This prevents the same URL from being queued multiple times when discovered from different sources.\n *\n * Usage flow:\n * 1. Initial queue setup: Root URL and initialQueue items are added to visited\n * 2. During processing: When a page returns links, each link is checked against visited\n * 3. In processBatch deduplication: Only links NOT in visited are added to the queue AND to visited\n *\n * This approach ensures:\n * - No URL is processed more than once\n * - No URL appears in the queue multiple times\n * - Efficient deduplication across concurrent processing\n */\n protected visited = new Set<string>();\n protected pageCount = 0;\n protected totalDiscovered = 0; // Track total URLs discovered (unlimited)\n protected effectiveTotal = 0; // Track effective total (limited by maxPages)\n protected canonicalBaseUrl?: URL; // Final URL after initial redirect (depth 0)\n\n abstract canHandle(url: string): boolean;\n\n protected options: BaseScraperStrategyOptions;\n protected config: AppConfig;\n\n constructor(config: AppConfig, options: BaseScraperStrategyOptions = {}) {\n this.config = config;\n this.options = options;\n }\n\n /**\n * Determines if a URL should be processed based on scope and include/exclude patterns in ScraperOptions.\n * Scope is checked first, then patterns.\n */\n protected shouldProcessUrl(url: string, options: ScraperOptions): boolean {\n if (options.scope) {\n try {\n const base = this.canonicalBaseUrl ?? new URL(options.url);\n const target = new URL(url);\n if (!isInScope(base, target, options.scope)) return false;\n } catch {\n return false;\n }\n }\n return shouldIncludeUrl(url, options.includePatterns, options.excludePatterns);\n }\n\n /**\n * Process a single item from the queue.\n *\n * @returns Processed content, links, and metadata\n */\n protected abstract processItem(\n item: QueueItem,\n options: ScraperOptions,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult>;\n\n protected async processBatch(\n batch: QueueItem[],\n baseUrl: URL,\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal, // Add signal\n ): Promise<QueueItem[]> {\n const maxPages = options.maxPages ?? this.config.scraper.maxPages;\n const results = await Promise.all(\n batch.map(async (item) => {\n // Check signal before processing each item in the batch\n if (signal?.aborted) {\n throw new CancellationError(\"Scraping cancelled during batch processing\");\n }\n // Resolve default for maxDepth check\n const maxDepth = options.maxDepth ?? this.config.scraper.maxDepth;\n if (item.depth > maxDepth) {\n return [];\n }\n\n try {\n // Pass signal to processItem\n const result = await this.processItem(item, options, signal);\n\n // Only count items that represent tracked pages or have actual content\n // - Refresh operations (have pageId): Always count (they're tracked in DB)\n // - New files with content: Count (they're being indexed)\n // - Directory discovery (no pageId, no content): Don't count\n const shouldCount = item.pageId !== undefined || result.content !== undefined;\n\n let currentPageCount = this.pageCount;\n if (shouldCount) {\n currentPageCount = ++this.pageCount;\n\n // Log progress for all counted items\n logger.info(\n `🌐 Scraping page ${currentPageCount}/${this.effectiveTotal} (depth ${item.depth}/${maxDepth}): ${item.url}`,\n );\n }\n\n if (result.status === FetchStatus.NOT_MODIFIED) {\n // File/page hasn't changed, skip processing but count as processed\n logger.debug(`Page unchanged (304): ${item.url}`);\n if (shouldCount) {\n await progressCallback({\n pagesScraped: currentPageCount,\n totalPages: this.effectiveTotal,\n totalDiscovered: this.totalDiscovered,\n currentUrl: item.url,\n depth: item.depth,\n maxDepth: maxDepth,\n result: null,\n pageId: item.pageId,\n });\n }\n return [];\n }\n\n if (result.status === FetchStatus.NOT_FOUND) {\n // File/page was deleted, count as processed\n logger.debug(`Page deleted (404): ${item.url}`);\n if (shouldCount) {\n await progressCallback({\n pagesScraped: currentPageCount,\n totalPages: this.effectiveTotal,\n totalDiscovered: this.totalDiscovered,\n currentUrl: item.url,\n depth: item.depth,\n maxDepth: maxDepth,\n result: null,\n pageId: item.pageId,\n deleted: true,\n });\n }\n return [];\n }\n\n if (result.status !== FetchStatus.SUCCESS) {\n logger.error(`❌ Unknown fetch status: ${result.status}`);\n return [];\n }\n\n // Handle successful processing - report result with content\n // Use the final URL from the result (which may differ due to redirects)\n const finalUrl = result.url || item.url;\n\n if (result.content) {\n await progressCallback({\n pagesScraped: currentPageCount,\n totalPages: this.effectiveTotal,\n totalDiscovered: this.totalDiscovered,\n currentUrl: finalUrl,\n depth: item.depth,\n maxDepth: maxDepth,\n result: {\n url: finalUrl,\n title: result.content.title?.trim() || result.title?.trim() || \"\",\n sourceContentType: result.sourceContentType || result.contentType || \"\",\n contentType: result.contentType || \"\",\n textContent: result.content.textContent || \"\",\n links: result.content.links || [],\n errors: result.content.errors || [],\n chunks: result.content.chunks || [],\n etag: result.etag || null,\n lastModified: result.lastModified || null,\n } satisfies ScrapeResult,\n pageId: item.pageId,\n });\n }\n\n // Extract discovered links - use the final URL as the base for resolving relative links\n const nextItems = result.links || [];\n const linkBaseUrl = finalUrl ? new URL(finalUrl) : baseUrl;\n\n return nextItems\n .map((value) => {\n try {\n const targetUrl = new URL(value, linkBaseUrl);\n // Filter using shouldProcessUrl\n if (!this.shouldProcessUrl(targetUrl.href, options)) {\n return null;\n }\n return {\n url: targetUrl.href,\n depth: item.depth + 1,\n } satisfies QueueItem;\n } catch (_error) {\n // Invalid URL or path\n logger.warn(`❌ Invalid URL: ${value}`);\n }\n return null;\n })\n .filter((item) => item !== null);\n } catch (error) {\n // Never ignore errors for the root URL (depth 0) - if it fails, the job should fail\n // There's no point in \"successfully\" completing with 0 documents\n if (item.depth === 0) {\n throw error;\n }\n if (options.ignoreErrors) {\n logger.error(`❌ Failed to process ${item.url}: ${error}`);\n return [];\n }\n throw error;\n }\n }),\n );\n\n // After all concurrent processing is done, deduplicate the results\n const allLinks = results.flat();\n const uniqueLinks: QueueItem[] = [];\n\n // Now perform deduplication once, after all parallel processing is complete\n for (const item of allLinks) {\n const normalizedUrl = normalizeUrl(item.url, this.options.urlNormalizerOptions);\n if (!this.visited.has(normalizedUrl)) {\n this.visited.add(normalizedUrl);\n uniqueLinks.push(item);\n\n // Always increment the unlimited counter\n this.totalDiscovered++;\n\n // Only increment effective total if we haven't exceeded maxPages\n if (this.effectiveTotal < maxPages) {\n this.effectiveTotal++;\n }\n }\n }\n\n return uniqueLinks;\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal, // Add signal\n ): Promise<void> {\n this.visited.clear();\n this.pageCount = 0;\n\n // Check if this is a refresh operation with pre-populated queue\n const initialQueue = options.initialQueue || [];\n const isRefreshMode = initialQueue.length > 0;\n\n // Set up base URL and queue\n this.canonicalBaseUrl = new URL(options.url);\n let baseUrl = this.canonicalBaseUrl;\n\n // Initialize queue: Start with root URL or use items from initialQueue (refresh mode)\n // The root URL is always processed (depth 0), but if it's in initialQueue, use that\n // version to preserve etag/pageId for conditional fetching\n const queue: QueueItem[] = [];\n const normalizedRootUrl = normalizeUrl(\n options.url,\n this.options.urlNormalizerOptions,\n );\n\n if (isRefreshMode) {\n logger.debug(\n `Starting refresh mode with ${initialQueue.length} pre-populated pages`,\n );\n\n // Add all items from initialQueue, using visited set to deduplicate\n for (const item of initialQueue) {\n const normalizedUrl = normalizeUrl(item.url, this.options.urlNormalizerOptions);\n if (!this.visited.has(normalizedUrl)) {\n this.visited.add(normalizedUrl);\n queue.push(item);\n }\n }\n }\n\n // If root URL wasn't in initialQueue, add it now at depth 0\n if (!this.visited.has(normalizedRootUrl)) {\n this.visited.add(normalizedRootUrl);\n queue.unshift({ url: options.url, depth: 0 } satisfies QueueItem);\n }\n\n // Initialize counters based on actual queue length after population\n this.totalDiscovered = queue.length;\n this.effectiveTotal = queue.length;\n\n // Resolve optional values to defaults using temporary config lookup\n // (We'll replace this with proper config merging later)\n const maxPages = options.maxPages ?? this.config.scraper.maxPages;\n const maxConcurrency = options.maxConcurrency ?? this.config.scraper.maxConcurrency;\n\n // Unified processing loop for both normal and refresh modes\n while (queue.length > 0 && this.pageCount < maxPages) {\n // Check for cancellation at the start of each loop iteration\n if (signal?.aborted) {\n logger.debug(`${isRefreshMode ? \"Refresh\" : \"Scraping\"} cancelled by signal.`);\n throw new CancellationError(\n `${isRefreshMode ? \"Refresh\" : \"Scraping\"} cancelled by signal`,\n );\n }\n\n const remainingPages = maxPages - this.pageCount;\n if (remainingPages <= 0) {\n break;\n }\n\n const batchSize = Math.min(maxConcurrency, remainingPages, queue.length);\n const batch = queue.splice(0, batchSize);\n\n // Always use latest canonical base (may have been updated after first fetch)\n baseUrl = this.canonicalBaseUrl ?? baseUrl;\n const newUrls = await this.processBatch(\n batch,\n baseUrl,\n options,\n progressCallback,\n signal,\n );\n\n queue.push(...newUrls);\n }\n }\n\n /**\n * Cleanup resources used by this strategy.\n * Default implementation does nothing - override in derived classes as needed.\n */\n async cleanup(): Promise<void> {\n // No-op by default\n }\n}\n","import type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { HttpFetcher } from \"../fetcher\";\nimport { FetchStatus, type RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, PipelineResult } from \"../pipelines/types\";\nimport type { QueueItem } from \"../types\";\nimport { ScrapeMode, type ScraperOptions } from \"../types\";\nimport type { ProcessItemResult } from \"./BaseScraperStrategy\";\n\nexport interface GitHubRepoInfo {\n owner: string;\n repo: string;\n branch?: string;\n subPath?: string;\n}\n\nexport interface GitHubTreeItem {\n path: string;\n type: \"blob\" | \"tree\";\n sha: string;\n size?: number;\n url: string;\n}\n\nexport interface GitHubTreeResponse {\n sha: string;\n url: string;\n tree: GitHubTreeItem[];\n truncated: boolean;\n}\n\n/**\n * GitHubRepoProcessor handles processing individual files from GitHub repositories.\n * It processes HTTPS blob URLs (https://github.com/owner/repo/blob/branch/filepath).\n *\n * This processor is stateless and contains the core logic from GitHubRepoScraperStrategy.\n */\nexport class GitHubRepoProcessor {\n private readonly httpFetcher: HttpFetcher;\n private readonly pipelines: ContentPipeline[];\n\n constructor(config: AppConfig) {\n this.httpFetcher = new HttpFetcher(config.scraper);\n this.pipelines = PipelineFactory.createStandardPipelines(config);\n }\n\n /**\n * Parses an HTTPS blob URL to extract repository information.\n * Format: https://github.com/owner/repo/blob/branch/filepath\n */\n parseHttpsBlobUrl(url: string): GitHubRepoInfo & { filePath: string } {\n const parsedUrl = new URL(url);\n const segments = parsedUrl.pathname.split(\"/\").filter(Boolean);\n\n // Expected format: /owner/repo/blob/branch/filepath\n if (segments.length < 5 || segments[2] !== \"blob\") {\n throw new Error(\n `Invalid GitHub blob URL format. Expected: https://github.com/owner/repo/blob/branch/filepath. Got: ${url}`,\n );\n }\n\n const owner = segments[0];\n const repo = segments[1];\n const branch = segments[3];\n const filePath = segments.slice(4).join(\"/\");\n\n return { owner, repo, branch, filePath };\n }\n\n /**\n * Fetches the raw content of a file from GitHub.\n */\n private async fetchFileContent(\n repoInfo: GitHubRepoInfo,\n filePath: string,\n etag?: string | null,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<RawContent> {\n const { owner, repo, branch } = repoInfo;\n const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`;\n\n const rawContent = await this.httpFetcher.fetch(rawUrl, { signal, etag, headers });\n\n // Override GitHub's generic 'text/plain' or 'application/octet-stream' MIME type with file extension-based detection\n const detectedMimeType = MimeTypeUtils.detectMimeTypeFromPath(filePath);\n if (\n detectedMimeType &&\n (rawContent.mimeType === \"text/plain\" ||\n rawContent.mimeType === \"application/octet-stream\")\n ) {\n return {\n ...rawContent,\n mimeType: detectedMimeType,\n };\n }\n\n return rawContent;\n }\n\n /**\n * Processes a single GitHub repository file from an HTTPS blob URL.\n */\n async process(\n item: QueueItem,\n options: ScraperOptions,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n // Parse the HTTPS blob URL to extract repository info and file path\n const repoInfo = this.parseHttpsBlobUrl(item.url);\n const { owner, repo, branch, filePath } = repoInfo;\n\n // Fetch the file content from raw.githubusercontent.com\n const rawContent = await this.fetchFileContent(\n { owner, repo, branch },\n filePath,\n item.etag,\n headers,\n signal,\n );\n\n // Return the status directly - BaseScraperStrategy handles NOT_MODIFIED and NOT_FOUND\n if (rawContent.status !== FetchStatus.SUCCESS) {\n return { url: item.url, links: [], status: rawContent.status };\n }\n\n // Process content through appropriate pipeline\n let processed: PipelineResult | undefined;\n\n for (const pipeline of this.pipelines) {\n const contentBuffer = Buffer.isBuffer(rawContent.content)\n ? rawContent.content\n : Buffer.from(rawContent.content);\n if (pipeline.canProcess(rawContent.mimeType || \"text/plain\", contentBuffer)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${filePath})`,\n );\n\n // Force 'fetch' mode for GitHub to avoid unnecessary Playwright usage on raw content.\n // GitHub raw files (e.g., HTML files) don't have their dependencies available at the\n // raw.githubusercontent.com domain, so rendering them in a browser would be broken\n // and provide no additional value over direct HTML parsing with Cheerio.\n const gitHubOptions = { ...options, scrapeMode: ScrapeMode.Fetch };\n\n processed = await pipeline.process(rawContent, gitHubOptions, this.httpFetcher);\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for file ${filePath}. Skipping processing.`,\n );\n return { url: item.url, links: [], status: FetchStatus.SUCCESS };\n }\n\n for (const err of processed.errors ?? []) {\n logger.warn(`⚠️ Processing error for ${filePath}: ${err.message}`);\n }\n\n // Create document with GitHub-specific metadata\n const githubUrl = `https://github.com/${owner}/${repo}/blob/${branch}/${filePath}`;\n\n // Use filename as fallback if title is empty or not a string\n const filename = filePath.split(\"/\").pop() || \"Untitled\";\n\n return {\n url: githubUrl,\n title: processed.title?.trim() || filename || \"Untitled\",\n etag: rawContent.etag,\n lastModified: rawContent.lastModified,\n sourceContentType: rawContent.mimeType,\n contentType: processed.contentType || rawContent.mimeType,\n content: processed,\n links: [], // Always return empty links array for individual files\n status: FetchStatus.SUCCESS,\n };\n }\n\n /**\n * Cleanup resources used by this processor.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n }\n}\n","import type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { HttpFetcher } from \"../fetcher\";\nimport { FetchStatus } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, PipelineResult } from \"../pipelines/types\";\nimport type { QueueItem } from \"../types\";\nimport { ScrapeMode, type ScraperOptions } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport type { ProcessItemResult } from \"./BaseScraperStrategy\";\n\ninterface GitHubWikiInfo {\n owner: string;\n repo: string;\n}\n\n/**\n * GitHubWikiProcessor handles scraping GitHub wiki pages using standard web scraping techniques.\n * GitHub wikis are separate from the main repository and are hosted at /wiki/ URLs.\n *\n * Features:\n * - Scrapes all wiki pages by following links within the wiki\n * - Uses web scraping approach since wikis are not available via the Git tree API\n * - Processes wiki content as HTML/Markdown pages\n * - Stays within the wiki scope to avoid crawling the entire repository\n *\n * This processor is stateless and contains the core logic from GitHubWikiScraperStrategy.\n */\nexport class GitHubWikiProcessor {\n private readonly httpFetcher: HttpFetcher;\n private readonly pipelines: ContentPipeline[];\n\n constructor(config: AppConfig) {\n this.httpFetcher = new HttpFetcher(config.scraper);\n this.pipelines = PipelineFactory.createStandardPipelines(config);\n }\n\n /**\n * Parses a GitHub wiki URL to extract repository information.\n */\n parseGitHubWikiUrl(url: string): GitHubWikiInfo {\n const parsedUrl = new URL(url);\n // Extract /<org>/<repo> from github.com/<org>/<repo>/wiki/...\n const match = parsedUrl.pathname.match(/^\\/([^/]+)\\/([^/]+)\\/wiki/);\n if (!match) {\n throw new Error(`Invalid GitHub wiki URL: ${url}`);\n }\n\n const [, owner, repo] = match;\n return { owner, repo };\n }\n\n /**\n * Determines if a URL should be processed within the wiki scope.\n */\n shouldProcessUrl(url: string, options: ScraperOptions): boolean {\n try {\n const parsedUrl = new URL(url);\n\n // Get the expected repository info from the base URL\n const baseWikiInfo = this.parseGitHubWikiUrl(options.url);\n const expectedWikiPath = `/${baseWikiInfo.owner}/${baseWikiInfo.repo}/wiki`;\n\n // Check if the URL is within the same wiki\n if (!parsedUrl.pathname.startsWith(expectedWikiPath)) {\n return false;\n }\n\n // Apply include/exclude patterns to the wiki page path\n const wikiPagePath = parsedUrl.pathname\n .replace(expectedWikiPath, \"\")\n .replace(/^\\//, \"\");\n return shouldIncludeUrl(\n wikiPagePath || \"Home\",\n options.includePatterns,\n options.excludePatterns,\n );\n } catch {\n return false;\n }\n }\n\n /**\n * Processes a single GitHub wiki page.\n */\n async process(\n item: QueueItem,\n options: ScraperOptions,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n const currentUrl = item.url;\n\n try {\n // Fetch the wiki page content with ETag for conditional requests\n const rawContent = await this.httpFetcher.fetch(currentUrl, {\n signal,\n etag: item.etag,\n headers,\n });\n\n // Return the status directly - BaseScraperStrategy handles NOT_MODIFIED and NOT_FOUND\n if (rawContent.status !== FetchStatus.SUCCESS) {\n return { url: currentUrl, links: [], status: rawContent.status };\n }\n\n // Process content through appropriate pipeline\n let processed: PipelineResult | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent.mimeType, rawContent.content)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${currentUrl})`,\n );\n\n // Use fetch mode for consistent behavior\n const wikiOptions = { ...options, scrapeMode: ScrapeMode.Fetch };\n\n processed = await pipeline.process(rawContent, wikiOptions, this.httpFetcher);\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for wiki page ${currentUrl}. Skipping processing.`,\n );\n return { url: currentUrl, links: [], status: FetchStatus.SUCCESS };\n }\n\n for (const err of processed.errors ?? []) {\n logger.warn(`⚠️ Processing error for ${currentUrl}: ${err.message}`);\n }\n\n // Extract wiki page title from URL\n const parsedUrl = new URL(currentUrl);\n const wikiInfo = this.parseGitHubWikiUrl(currentUrl);\n const wikiPagePath = parsedUrl.pathname\n .replace(`/${wikiInfo.owner}/${wikiInfo.repo}/wiki`, \"\")\n .replace(/^\\//, \"\");\n const pageTitle = wikiPagePath || \"Home\";\n\n // Extract links from the processed content\n const links = processed.links || [];\n\n // Filter links to only include other wiki pages and ensure they're absolute URLs\n const wikiLinks = links\n .filter((link) => {\n // Skip obviously invalid links\n if (\n !link ||\n link.trim() === \"\" ||\n link === \"invalid-url\" ||\n link === \"not-a-url-at-all\"\n ) {\n return false;\n }\n return true;\n })\n .map((link) => {\n try {\n // Convert relative links to absolute URLs\n return new URL(link, currentUrl).href;\n } catch {\n return null;\n }\n })\n .filter((link): link is string => link !== null)\n .filter((link) => {\n try {\n const linkUrl = new URL(link);\n // Only include links that are within the same wiki\n return (\n linkUrl.hostname === parsedUrl.hostname &&\n linkUrl.pathname.startsWith(`/${wikiInfo.owner}/${wikiInfo.repo}/wiki`)\n );\n } catch {\n return false;\n }\n });\n\n return {\n url: currentUrl,\n title: pageTitle,\n etag: rawContent.etag,\n lastModified: rawContent.lastModified,\n contentType: rawContent.mimeType,\n content: processed,\n links: wikiLinks,\n status: FetchStatus.SUCCESS,\n };\n } catch (error) {\n logger.warn(`⚠️ Failed to process wiki page ${currentUrl}: ${error}`);\n return { url: currentUrl, links: [], status: FetchStatus.SUCCESS };\n }\n }\n\n /**\n * Cleanup resources used by this processor.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n }\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { logger } from \"../../utils/logger\";\n\nconst execAsync = promisify(exec);\n\n/**\n * Resolves GitHub authentication headers using a cascading fallback mechanism.\n *\n * Resolution order (first match wins):\n * 1. Explicit `Authorization` header in input - user has full control\n * 2. `GITHUB_TOKEN` environment variable - standard GitHub Actions / CI convention\n * 3. `GH_TOKEN` environment variable - alternative used by some CI systems\n * 4. `gh auth token` CLI - local development convenience\n *\n * @param explicitHeaders - Headers provided by the user in scraper options\n * @returns Headers object with Authorization if available, or the original headers\n */\nexport async function resolveGitHubAuth(\n explicitHeaders?: Record<string, string>,\n): Promise<Record<string, string>> {\n // 1. Check for explicit Authorization header (case-insensitive check)\n if (explicitHeaders) {\n const hasAuthHeader = Object.keys(explicitHeaders).some(\n (key) => key.toLowerCase() === \"authorization\",\n );\n if (hasAuthHeader) {\n return explicitHeaders;\n }\n }\n\n // 2. Check GITHUB_TOKEN environment variable\n const githubToken = process.env.GITHUB_TOKEN;\n if (githubToken) {\n logger.debug(\"Using GitHub token from GITHUB_TOKEN environment variable\");\n return {\n ...explicitHeaders,\n Authorization: `Bearer ${githubToken}`,\n };\n }\n\n // 3. Check GH_TOKEN environment variable (fallback)\n const ghToken = process.env.GH_TOKEN;\n if (ghToken) {\n logger.debug(\"Using GitHub token from GH_TOKEN environment variable\");\n return {\n ...explicitHeaders,\n Authorization: `Bearer ${ghToken}`,\n };\n }\n\n // 4. Try gh CLI as last resort\n try {\n const { stdout } = await execAsync(\"gh auth token\", { timeout: 5000 });\n const cliToken = stdout.trim();\n if (cliToken) {\n logger.debug(\"Using GitHub token from local gh CLI\");\n return {\n ...explicitHeaders,\n Authorization: `Bearer ${cliToken}`,\n };\n }\n } catch {\n // gh CLI not installed, not authenticated, or command failed - silently continue\n }\n\n // No authentication available - return original headers (or empty object)\n return explicitHeaders ?? {};\n}\n","import type { ProgressCallback } from \"../../types\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { ScraperError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { HttpFetcher } from \"../fetcher\";\nimport { FetchStatus } from \"../fetcher/types\";\nimport type { QueueItem, ScraperOptions, ScraperProgressEvent } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { BaseScraperStrategy, type ProcessItemResult } from \"./BaseScraperStrategy\";\nimport type {\n GitHubRepoInfo,\n GitHubTreeItem,\n GitHubTreeResponse,\n} from \"./GitHubRepoProcessor\";\nimport { GitHubRepoProcessor } from \"./GitHubRepoProcessor\";\nimport { GitHubWikiProcessor } from \"./GitHubWikiProcessor\";\nimport { resolveGitHubAuth } from \"./github-auth\";\n\n/** Text-based file extensions recognized for GitHub repository scraping. */\nconst TEXT_EXTENSIONS: ReadonlySet<string> = new Set([\n // Markup\n \".md\",\n \".markdown\",\n \".mdx\",\n \".gfm\",\n \".mkd\",\n \".mkdn\",\n \".mkdown\",\n \".mdown\",\n \".mdwn\",\n \".ronn\",\n \".txt\",\n \".rst\",\n \".adoc\",\n \".asciidoc\",\n \".textile\",\n \".org\",\n \".pod\",\n \".rdoc\",\n \".wiki\",\n \".rmd\",\n // Web\n \".html\",\n \".htm\",\n \".xml\",\n \".xhtml\",\n // Stylesheets\n \".css\",\n \".scss\",\n \".sass\",\n \".less\",\n // JavaScript/TypeScript\n \".js\",\n \".jsx\",\n \".mjs\",\n \".cjs\",\n \".ts\",\n \".tsx\",\n \".mts\",\n \".cts\",\n // Python\n \".py\",\n \".pyw\",\n \".pyi\",\n \".pyx\",\n \".pxd\",\n // JVM\n \".java\",\n \".kt\",\n \".kts\",\n \".scala\",\n \".groovy\",\n \".gradle\",\n // .NET\n \".cs\",\n // Systems\n \".c\",\n \".cpp\",\n \".cc\",\n \".cxx\",\n \".h\",\n \".hpp\",\n \".hxx\",\n \".go\",\n \".rs\",\n \".zig\",\n \".nim\",\n \".v\",\n \".cr\",\n // Apple/Mobile\n \".swift\",\n \".dart\",\n \".m\",\n \".mm\",\n // Scripting\n \".rb\",\n \".rake\",\n \".php\",\n \".lua\",\n \".pl\",\n \".pm\",\n \".r\",\n // Functional\n \".hs\",\n \".lhs\",\n \".elm\",\n \".erl\",\n \".ex\",\n \".exs\",\n \".clj\",\n \".cljs\",\n \".cljc\",\n \".jl\",\n // Web3\n \".sol\",\n \".move\",\n \".cairo\",\n // Web Frameworks\n \".vue\",\n \".svelte\",\n \".astro\",\n // Shell\n \".sh\",\n \".bash\",\n \".zsh\",\n \".fish\",\n \".ps1\",\n \".bat\",\n \".cmd\",\n // Data\n \".json\",\n \".yaml\",\n \".yml\",\n \".csv\",\n \".tsv\",\n \".sql\",\n \".graphql\",\n \".gql\",\n // Config\n \".toml\",\n \".ini\",\n \".cfg\",\n \".conf\",\n \".properties\",\n \".env\",\n \".gitignore\",\n \".dockerignore\",\n \".gitattributes\",\n \".editorconfig\",\n // Build Systems\n \".pom\",\n \".sbt\",\n \".maven\",\n \".cmake\",\n \".make\",\n \".dockerfile\",\n \".containerfile\",\n \".makefile\",\n \".bazel\",\n \".bzl\",\n \".buck\",\n // IaC\n \".tf\",\n \".tfvars\",\n \".hcl\",\n // Package managers\n \".mod\",\n \".sum\",\n // Schema/API\n \".proto\",\n \".prisma\",\n \".thrift\",\n \".avro\",\n // TeX\n \".tex\",\n \".latex\",\n // Other\n \".log\",\n]);\n\n/** Document extensions supported by DocumentPipeline. */\nconst DOCUMENT_EXTENSIONS: ReadonlySet<string> = new Set([\n \".pdf\",\n \".docx\",\n \".xlsx\",\n \".pptx\",\n \".ipynb\",\n \".doc\",\n \".xls\",\n \".ppt\",\n \".odt\",\n \".ods\",\n \".odp\",\n \".rtf\",\n \".epub\",\n \".fb2\",\n]);\n\n/** Well-known filenames (case-insensitive) that are typically text-based. */\nconst COMMON_TEXT_FILES: readonly string[] = [\n \"readme\",\n \"license\",\n \"changelog\",\n \"contributing\",\n \"authors\",\n \"maintainers\",\n \"dockerfile\",\n \"makefile\",\n \"rakefile\",\n \"gemfile\",\n \"podfile\",\n \"cartfile\",\n \"brewfile\",\n \"procfile\",\n \"vagrantfile\",\n \"gulpfile\",\n \"gruntfile\",\n \".prettierrc\",\n \".eslintrc\",\n \".babelrc\",\n \".nvmrc\",\n \".npmrc\",\n];\n\n/**\n * GitHubScraperStrategy is a discovery strategy that orchestrates the scraping of both\n * GitHub repository code and wiki pages. When given a GitHub repository URL, it will:\n *\n * 1. Attempt to scrape the repository's wiki pages using GitHubWikiProcessor (prioritized)\n * 2. Discover all repository files using the GitHub Tree API\n * 3. Create HTTPS blob URLs for each file, which are stored in the database\n * 4. Process blob URLs directly with GitHubRepoProcessor\n *\n * This provides comprehensive documentation coverage by including both wiki documentation\n * and source code in a single scraping job, with wikis prioritized as they typically\n * contain higher-quality curated documentation.\n *\n * Features:\n * - Handles base GitHub repository URLs (e.g., https://github.com/owner/repo)\n * - Handles branch-specific URLs (e.g., https://github.com/owner/repo/tree/branch)\n * - Handles single file URLs (e.g., https://github.com/owner/repo/blob/branch/path)\n * - Discovers all files efficiently using GitHub's Tree API\n * - Generates and processes user-friendly HTTPS blob URLs throughout\n * - Prioritizes wiki content over repository files for better documentation quality\n * - Respects maxPages limit across both scraping phases to prevent exceeding quotas\n * - Automatically discovers and scrapes both wiki and code content\n * - Graceful handling when wikis don't exist or are inaccessible\n */\nexport class GitHubScraperStrategy extends BaseScraperStrategy {\n private readonly httpFetcher: HttpFetcher;\n private readonly wikiProcessor: GitHubWikiProcessor;\n private readonly repoProcessor: GitHubRepoProcessor;\n private resolvedAuthHeaders?: Record<string, string>;\n private resolvedAuthKey?: string;\n\n constructor(config: AppConfig) {\n super(config);\n this.httpFetcher = new HttpFetcher(config.scraper);\n this.wikiProcessor = new GitHubWikiProcessor(config);\n this.repoProcessor = new GitHubRepoProcessor(config);\n }\n\n canHandle(url: string): boolean {\n // Handle legacy github-file:// protocol URLs (no longer supported)\n // These will be processed and marked as NOT_FOUND to trigger cleanup\n if (url.startsWith(\"github-file://\")) {\n return true;\n }\n\n try {\n const parsedUrl = new URL(url);\n const { hostname, pathname } = parsedUrl;\n\n // Handle GitHub repository URLs\n if (![\"github.com\", \"www.github.com\"].includes(hostname)) {\n return false;\n }\n\n // Handle base repository URLs (owner/repo)\n const baseMatch = pathname.match(/^\\/([^/]+)\\/([^/]+)\\/?$/);\n if (baseMatch) {\n return true;\n }\n\n // Handle tree URLs (owner/repo/tree/branch/...)\n const treeMatch = pathname.match(/^\\/([^/]+)\\/([^/]+)\\/tree\\//);\n if (treeMatch) {\n return true;\n }\n\n // Handle blob URLs (owner/repo/blob/branch/...)\n const blobMatch = pathname.match(/^\\/([^/]+)\\/([^/]+)\\/blob\\//);\n if (blobMatch) {\n return true;\n }\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Parses a GitHub URL to extract repository information.\n */\n private parseGitHubUrl(\n url: string,\n ): GitHubRepoInfo & { isBlob?: boolean; filePath?: string } {\n const parsedUrl = new URL(url);\n // Extract /<org>/<repo> from github.com/<org>/<repo>/...\n const match = parsedUrl.pathname.match(/^\\/([^/]+)\\/([^/]+)/);\n if (!match) {\n throw new Error(`Invalid GitHub repository URL: ${url}`);\n }\n\n const [, owner, repo] = match;\n\n // Extract branch and optional subpath from URLs like /tree/<branch>/<subPath>\n const segments = parsedUrl.pathname.split(\"/\").filter(Boolean);\n\n // Handle /blob/ URLs for single file indexing\n if (segments.length >= 4 && segments[2] === \"blob\") {\n const branch = segments[3];\n const filePath = segments.length > 4 ? segments.slice(4).join(\"/\") : undefined;\n return { owner, repo, branch, filePath, isBlob: true };\n }\n\n // Handle /tree/ URLs with branch and optional subpath\n if (segments.length >= 4 && segments[2] === \"tree\") {\n const branch = segments[3];\n const subPath = segments.length > 4 ? segments.slice(4).join(\"/\") : undefined;\n return { owner, repo, branch, subPath };\n }\n\n // Base repository URL\n return { owner, repo };\n }\n\n private buildAuthCacheKey(explicitHeaders?: Record<string, string>): string {\n const normalizedHeaders = explicitHeaders\n ? Object.keys(explicitHeaders)\n .sort()\n .map((key) => [key, explicitHeaders[key]])\n : [];\n const envKey = `${process.env.GITHUB_TOKEN ?? \"\"}|${process.env.GH_TOKEN ?? \"\"}`;\n return JSON.stringify({ headers: normalizedHeaders, env: envKey });\n }\n\n private async getResolvedAuthHeaders(\n explicitHeaders?: Record<string, string>,\n ): Promise<Record<string, string>> {\n const cacheKey = this.buildAuthCacheKey(explicitHeaders);\n if (this.resolvedAuthHeaders && this.resolvedAuthKey === cacheKey) {\n return this.resolvedAuthHeaders;\n }\n\n const resolved = await resolveGitHubAuth(explicitHeaders);\n this.resolvedAuthHeaders = resolved;\n this.resolvedAuthKey = cacheKey;\n return resolved;\n }\n\n /**\n * Fetches the repository tree structure from GitHub API.\n */\n private async fetchRepositoryTree(\n repoInfo: GitHubRepoInfo,\n headers?: Record<string, string>,\n signal?: AbortSignal,\n ): Promise<{ tree: GitHubTreeResponse; resolvedBranch: string }> {\n const { owner, repo, branch } = repoInfo;\n\n // If no branch specified, fetch the default branch first\n let targetBranch = branch;\n if (!targetBranch) {\n const repoUrl = `https://api.github.com/repos/${owner}/${repo}`;\n logger.debug(`Fetching repository info: ${repoUrl}`);\n\n let repoContent: Awaited<ReturnType<typeof this.httpFetcher.fetch>>;\n try {\n repoContent = await this.httpFetcher.fetch(repoUrl, { signal, headers });\n } catch (error) {\n // Convert HTTP auth errors to user-friendly messages\n if (error instanceof ScraperError) {\n if (error.message.includes(\"401\")) {\n throw new ScraperError(\n `GitHub authentication failed for \"${owner}/${repo}\". Your token is invalid or expired. Please check your GITHUB_TOKEN or GH_TOKEN environment variable.`,\n false,\n error,\n );\n }\n if (error.message.includes(\"403\")) {\n throw new ScraperError(\n `GitHub access denied for \"${owner}/${repo}\". Your token may lack the required permissions, or you may be rate-limited. Please check your GITHUB_TOKEN or GH_TOKEN.`,\n false,\n error,\n );\n }\n }\n throw error;\n }\n\n // Check for NOT_FOUND status before parsing - repo is inaccessible (private or doesn't exist)\n if (repoContent.status === FetchStatus.NOT_FOUND) {\n throw new ScraperError(\n `Repository \"${owner}/${repo}\" not found or not accessible. For private repositories, set the GITHUB_TOKEN environment variable.`,\n false,\n );\n }\n\n // Try to parse the response to get the default branch\n try {\n const content =\n typeof repoContent.content === \"string\"\n ? repoContent.content\n : repoContent.content.toString(\"utf-8\");\n const repoData = JSON.parse(content) as { default_branch?: string };\n const defaultBranch =\n typeof repoData.default_branch === \"string\"\n ? repoData.default_branch.trim()\n : \"\";\n if (!defaultBranch) {\n logger.warn(\n `⚠️ Repository info missing default_branch for ${owner}/${repo}, using 'main'`,\n );\n targetBranch = \"main\";\n } else {\n targetBranch = defaultBranch;\n logger.debug(`Using default branch: ${targetBranch}`);\n }\n } catch (parseError) {\n // Only fall back to \"main\" for JSON parse errors (e.g., unexpected API response format)\n logger.warn(`⚠️ Could not parse repository info, using 'main': ${parseError}`);\n targetBranch = \"main\";\n }\n }\n\n const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${targetBranch}?recursive=1`;\n logger.debug(`Fetching repository tree: ${treeUrl}`);\n\n let rawContent: Awaited<ReturnType<typeof this.httpFetcher.fetch>>;\n try {\n rawContent = await this.httpFetcher.fetch(treeUrl, { signal, headers });\n } catch (error) {\n // Convert HTTP auth errors to user-friendly messages\n if (error instanceof ScraperError) {\n if (error.message.includes(\"401\")) {\n throw new ScraperError(\n `GitHub authentication failed for \"${owner}/${repo}\". Your token is invalid or expired. Please check your GITHUB_TOKEN or GH_TOKEN environment variable.`,\n false,\n error,\n );\n }\n if (error.message.includes(\"403\")) {\n throw new ScraperError(\n `GitHub access denied for \"${owner}/${repo}\". Your token may lack the required permissions, or you may be rate-limited. Please check your GITHUB_TOKEN or GH_TOKEN.`,\n false,\n error,\n );\n }\n }\n throw error;\n }\n\n // Check for NOT_FOUND status before parsing - this indicates repo is inaccessible\n if (rawContent.status === FetchStatus.NOT_FOUND) {\n throw new ScraperError(\n `Repository \"${owner}/${repo}\" not found or not accessible. For private repositories, set the GITHUB_TOKEN environment variable.`,\n false,\n );\n }\n\n const content =\n typeof rawContent.content === \"string\"\n ? rawContent.content\n : rawContent.content.toString(\"utf-8\");\n\n // Parse JSON with proper error handling\n let treeData: GitHubTreeResponse;\n try {\n treeData = JSON.parse(content) as GitHubTreeResponse;\n } catch (parseError) {\n throw new ScraperError(\n `Failed to parse GitHub API response for \"${owner}/${repo}\". The repository may be inaccessible or the API returned an unexpected response.`,\n false,\n parseError instanceof Error ? parseError : undefined,\n );\n }\n\n if (treeData.truncated) {\n logger.warn(\n `⚠️ Repository tree was truncated for ${owner}/${repo}. Some files may be missing.`,\n );\n }\n\n return { tree: treeData, resolvedBranch: targetBranch };\n }\n\n /**\n * Determines if a file should be processed based on its path and type.\n */\n private shouldProcessFile(item: GitHubTreeItem, options: ScraperOptions): boolean {\n if (item.type !== \"blob\") {\n return false;\n }\n\n const filePath = item.path;\n const pathLower = filePath.toLowerCase();\n\n // Extract extension for Set-based lookup\n const lastDot = pathLower.lastIndexOf(\".\");\n const ext = lastDot !== -1 ? pathLower.slice(lastDot) : \"\";\n\n const hasTextExtension = ext !== \"\" && TEXT_EXTENSIONS.has(ext);\n const hasDocumentExtension = ext !== \"\" && DOCUMENT_EXTENSIONS.has(ext);\n const hasCompoundExtension =\n pathLower.includes(\".env.\") ||\n pathLower.endsWith(\".env\") ||\n pathLower.includes(\".config.\") ||\n pathLower.includes(\".lock\");\n\n const fileName = filePath.split(\"/\").pop() || \"\";\n const fileNameLower = fileName.toLowerCase();\n\n const isCommonTextFile = COMMON_TEXT_FILES.some(\n (name) => fileNameLower === name || fileNameLower.startsWith(`${name}.`),\n );\n\n // If file passes known checks, include it\n if (\n hasTextExtension ||\n hasDocumentExtension ||\n hasCompoundExtension ||\n isCommonTextFile\n ) {\n return shouldIncludeUrl(filePath, options.includePatterns, options.excludePatterns);\n }\n\n // Fallback: check if unknown extension has text/* MIME type using MimeTypeUtils\n const mimeType = MimeTypeUtils.detectMimeTypeFromPath(filePath);\n if (mimeType?.startsWith(\"text/\")) {\n logger.debug(`Including file with text MIME type: ${filePath} (${mimeType})`);\n return shouldIncludeUrl(filePath, options.includePatterns, options.excludePatterns);\n }\n\n // Not a text file\n return false;\n }\n\n /**\n * Checks if a path is within the specified subpath.\n */\n private isWithinSubPath(path: string, subPath?: string): boolean {\n if (!subPath) {\n return true;\n }\n\n const trimmedSubPath = subPath.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (trimmedSubPath.length === 0) {\n return true;\n }\n\n const normalizedPath = path.replace(/^\\/+/, \"\").replace(/\\/+$/, \"\");\n if (normalizedPath === trimmedSubPath) {\n return true;\n }\n\n return normalizedPath.startsWith(`${trimmedSubPath}/`);\n }\n\n async processItem(\n item: QueueItem,\n options: ScraperOptions,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n // Handle legacy github-file:// URLs - treat as deleted/not found\n if (item.url.startsWith(\"github-file://\")) {\n logger.info(\n `🗑️ Legacy github-file:// URL detected, marking as deleted: ${item.url}`,\n );\n return {\n url: item.url,\n links: [],\n status: FetchStatus.NOT_FOUND,\n };\n }\n\n const headers = await this.getResolvedAuthHeaders(options.headers);\n\n // Delegate to wiki processor for wiki URLs\n // Use precise pattern matching: /owner/repo/wiki or /owner/repo/wiki/\n try {\n const parsedUrl = new URL(item.url);\n if (/^\\/[^/]+\\/[^/]+\\/wiki($|\\/)/.test(parsedUrl.pathname)) {\n return await this.wikiProcessor.process(item, options, headers, signal);\n }\n } catch {\n // If URL parsing fails, fall through to other handlers\n }\n\n // For the main repository URL (depth 0), perform discovery\n // This includes blob URLs at depth 0, which should return themselves as discovered links\n if (item.depth === 0) {\n const repoInfo = this.parseGitHubUrl(options.url);\n const { owner, repo } = repoInfo;\n\n logger.debug(`Discovering GitHub repository ${owner}/${repo}`);\n\n const discoveredLinks: string[] = [];\n\n // Handle single file (blob) URLs - strict scoping: index ONLY the file\n if (\"isBlob\" in repoInfo && repoInfo.isBlob && repoInfo.filePath) {\n const { branch = \"main\", filePath } = repoInfo;\n logger.debug(\n `Single file URL detected: ${owner}/${repo}/${filePath} - indexing file only`,\n );\n\n // Generate HTTPS blob URL for storage\n discoveredLinks.push(\n `https://github.com/${owner}/${repo}/blob/${branch}/${filePath}`,\n );\n\n return {\n url: item.url,\n links: discoveredLinks,\n status: FetchStatus.SUCCESS,\n };\n }\n\n // Discover wiki URL for full repo scrapes (will be processed by GitHubWikiScraperStrategy)\n const wikiUrl = `${options.url.replace(/\\/$/, \"\")}/wiki`;\n discoveredLinks.push(wikiUrl);\n logger.debug(`Discovered wiki URL: ${wikiUrl}`);\n\n // 3. Discover all files in the repository\n const { tree, resolvedBranch } = await this.fetchRepositoryTree(\n repoInfo,\n headers,\n signal,\n );\n\n const fileItems = tree.tree\n .filter((treeItem) => this.isWithinSubPath(treeItem.path, repoInfo.subPath))\n .filter((treeItem) => this.shouldProcessFile(treeItem, options));\n\n logger.debug(\n `Discovered ${fileItems.length} processable files in repository (branch: ${resolvedBranch})`,\n );\n\n // Create HTTPS blob URLs for storage in database\n // These are user-friendly, clickable URLs that work outside the system\n const fileUrls = fileItems.map(\n (treeItem) =>\n `https://github.com/${owner}/${repo}/blob/${resolvedBranch}/${treeItem.path}`,\n );\n\n discoveredLinks.push(...fileUrls);\n\n logger.debug(\n `Discovery complete: ${fileUrls.length} repo file(s) + 1 wiki URL = ${discoveredLinks.length} total URLs`,\n );\n\n return { url: item.url, links: discoveredLinks, status: FetchStatus.SUCCESS };\n }\n\n // Handle HTTPS blob URLs at depth > 0 (from database during refresh or discovered files)\n // Process blob URLs directly - fetch content and return empty links\n // Use precise pattern matching: /owner/repo/blob/branch/path\n try {\n const parsedUrl = new URL(item.url);\n if (/^\\/[^/]+\\/[^/]+\\/blob\\//.test(parsedUrl.pathname)) {\n logger.debug(`Processing HTTPS blob URL at depth ${item.depth}: ${item.url}`);\n return await this.repoProcessor.process(item, options, headers, signal);\n }\n } catch (error) {\n logger.warn(`⚠️ Failed to parse blob URL ${item.url}: ${error}`);\n return { url: item.url, links: [], status: FetchStatus.SUCCESS };\n }\n\n // For any other URLs at non-zero depth, return empty (shouldn't happen in practice)\n logger.debug(`No further processing for URL at depth ${item.depth}: ${item.url}`);\n return { url: item.url, links: [], status: FetchStatus.SUCCESS };\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal,\n ): Promise<void> {\n const url = new URL(options.url);\n if (!url.hostname.includes(\"github.com\")) {\n throw new Error(\"URL must be a GitHub URL\");\n }\n\n await this.getResolvedAuthHeaders(options.headers);\n\n // Use the base class implementation which handles initialQueue properly\n // The processItem method will discover all wiki and repo file URLs\n // The base scraper will automatically deduplicate URLs from initialQueue\n try {\n await super.scrape(options, progressCallback, signal);\n } finally {\n this.resolvedAuthHeaders = undefined;\n this.resolvedAuthKey = undefined;\n }\n }\n\n async cleanup(): Promise<void> {\n await Promise.all([this.wikiProcessor.cleanup(), this.repoProcessor.cleanup()]);\n }\n}\n","import fs from \"node:fs\";\nimport { Readable } from \"node:stream\";\nimport * as tar from \"tar\";\nimport type { ArchiveAdapter, ArchiveEntry } from \"./types\";\n\nexport class TarAdapter implements ArchiveAdapter {\n private filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n async *listEntries(): AsyncGenerator<ArchiveEntry> {\n const fileStream = fs.createReadStream(this.filePath);\n const parseStream = new tar.Parser();\n\n fileStream.pipe(parseStream);\n\n const entryStream = new Readable({ objectMode: true, read() {} });\n\n parseStream.on(\"entry\", (entry: tar.ReadEntry) => {\n const isDir = entry.type === \"Directory\";\n const path = entry.path;\n const size = entry.size;\n\n entryStream.push({\n path,\n type: isDir ? \"directory\" : \"file\",\n size,\n } as ArchiveEntry);\n\n // Important: resume to skip data so we get next entry\n entry.resume();\n });\n\n parseStream.on(\"end\", () => entryStream.push(null));\n parseStream.on(\"error\", (err: Error) => entryStream.destroy(err));\n fileStream.on(\"error\", (err: Error) => entryStream.destroy(err));\n\n try {\n for await (const entry of entryStream) {\n yield entry as ArchiveEntry;\n }\n } finally {\n fileStream.destroy();\n }\n }\n\n async getContent(path: string): Promise<Buffer> {\n const stream = await this.getStream(path);\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.from(chunk));\n }\n return Buffer.concat(chunks);\n }\n\n async getStream(path: string): Promise<Readable> {\n return new Promise((resolve, reject) => {\n const fileStream = fs.createReadStream(this.filePath);\n const parseStream = new tar.Parser();\n let found = false;\n\n parseStream.on(\"entry\", (entry: tar.ReadEntry) => {\n if (found) {\n entry.resume();\n return;\n }\n\n // Normalize paths? Tar paths often relative.\n // Check for exact match or ./ match\n if (\n entry.path === path ||\n entry.path === `./${path}` ||\n entry.path === path.replace(/^\\//, \"\")\n ) {\n found = true;\n // We return the entry as a Readable stream\n resolve(entry as unknown as Readable);\n } else {\n entry.resume();\n }\n });\n\n parseStream.on(\"end\", () => {\n if (!found) reject(new Error(`File not found in tar: ${path}`));\n });\n\n parseStream.on(\"error\", (err: Error) => {\n fileStream.destroy();\n reject(err);\n });\n fileStream.on(\"error\", (err: Error) => {\n // parseStream.destroy() might not exist on all versions or types,\n // but fileStream is the source.\n // @ts-expect-error\n if (typeof parseStream.destroy === \"function\") parseStream.destroy();\n reject(err);\n });\n\n fileStream.pipe(parseStream);\n });\n }\n\n async close(): Promise<void> {\n return Promise.resolve();\n }\n}\n","import { Readable } from \"node:stream\";\nimport yauzl from \"yauzl\";\nimport type { ArchiveAdapter, ArchiveEntry } from \"./types\";\n\nexport class ZipAdapter implements ArchiveAdapter {\n private zipfile: yauzl.ZipFile | null = null;\n private filePath: string;\n\n constructor(filePath: string) {\n this.filePath = filePath;\n }\n\n private async getZipFile(): Promise<yauzl.ZipFile> {\n if (this.zipfile) return this.zipfile;\n\n return new Promise((resolve, reject) => {\n yauzl.open(\n this.filePath,\n { lazyEntries: true, autoClose: false },\n (err, zipfile) => {\n if (err) return reject(err);\n if (!zipfile) return reject(new Error(\"Failed to open zip file\"));\n this.zipfile = zipfile;\n resolve(zipfile);\n },\n );\n });\n }\n\n async *listEntries(): AsyncGenerator<ArchiveEntry> {\n // Ensure we start fresh\n if (this.zipfile) {\n this.zipfile.close();\n this.zipfile = null;\n }\n const z = await this.getZipFile();\n\n const entryStream = new Readable({\n objectMode: true,\n read() {\n z.readEntry();\n },\n });\n\n z.on(\"entry\", (entry: yauzl.Entry) => {\n const isDir = entry.fileName.endsWith(\"/\");\n entryStream.push({\n path: entry.fileName,\n type: isDir ? \"directory\" : \"file\",\n size: entry.uncompressedSize,\n } as ArchiveEntry);\n });\n\n z.on(\"end\", () => {\n entryStream.push(null);\n });\n\n z.on(\"error\", (err) => {\n entryStream.destroy(err);\n });\n\n for await (const entry of entryStream) {\n yield entry as ArchiveEntry;\n }\n }\n\n async getContent(path: string): Promise<Buffer> {\n const stream = await this.getStream(path);\n const chunks: Buffer[] = [];\n for await (const chunk of stream) {\n chunks.push(Buffer.from(chunk));\n }\n return Buffer.concat(chunks);\n }\n\n async getStream(path: string): Promise<Readable> {\n // Let's implement a cache of entries on first load.\n if (!this.entriesCache) {\n await this.loadEntries();\n }\n\n const entry = this.entriesCache?.get(path);\n if (!entry) {\n throw new Error(`File not found in zip: ${path}`);\n }\n\n const z = await this.getZipFile();\n return new Promise((resolve, reject) => {\n z.openReadStream(entry, (err, readStream) => {\n if (err) return reject(err);\n if (!readStream) return reject(new Error(\"Failed to create read stream\"));\n resolve(readStream);\n });\n });\n }\n\n private entriesCache: Map<string, yauzl.Entry> | null = null;\n\n private async loadEntries(): Promise<void> {\n if (this.entriesCache) return;\n this.entriesCache = new Map();\n\n // Ensure clean state\n if (this.zipfile) {\n this.zipfile.close();\n this.zipfile = null;\n }\n const z = await this.getZipFile();\n\n return new Promise((resolve, reject) => {\n z.on(\"entry\", (entry: yauzl.Entry) => {\n this.entriesCache?.set(entry.fileName, entry);\n z.readEntry();\n });\n z.on(\"end\", () => resolve());\n z.on(\"error\", (err) => reject(err));\n z.readEntry();\n });\n }\n\n async close(): Promise<void> {\n if (this.zipfile) {\n this.zipfile.close();\n this.zipfile = null;\n }\n this.entriesCache = null;\n }\n}\n","import path from \"node:path\";\nimport { TarAdapter } from \"./TarAdapter\";\nimport type { ArchiveAdapter } from \"./types\";\nimport { ZipAdapter } from \"./ZipAdapter\";\n\n/**\n * Returns an appropriate archive adapter for the given file path based on its\n * extension, or `null` if the file is not a recognized archive format.\n *\n * Detection relies exclusively on file extensions (`.zip`, `.tar`, `.gz`,\n * `.tgz`). Magic byte inspection is intentionally omitted because many\n * document formats (DOCX, XLSX, PPTX, EPUB, ODT, ODS, ODP) are internally\n * ZIP archives sharing the same `PK` signature. Treating them as archives\n * would prevent proper document extraction via `DocumentPipeline`.\n */\nexport async function getArchiveAdapter(\n filePath: string,\n): Promise<ArchiveAdapter | null> {\n const ext = path.extname(filePath).toLowerCase();\n\n if (ext === \".zip\") {\n return new ZipAdapter(filePath);\n }\n if (ext === \".tar\" || ext === \".gz\" || ext === \".tgz\") {\n return new TarAdapter(filePath);\n }\n\n return null;\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { type ArchiveAdapter, getArchiveAdapter } from \"../../utils/archive\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { FileFetcher } from \"../fetcher\";\nimport { FetchStatus, type RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, PipelineResult } from \"../pipelines/types\";\nimport type { QueueItem, ScraperOptions } from \"../types\";\nimport { BaseScraperStrategy, type ProcessItemResult } from \"./BaseScraperStrategy\";\n\n/**\n * LocalFileStrategy handles crawling and scraping of local files and folders using file:// URLs.\n *\n * All files with a MIME type of `text/*` are processed. This includes HTML, Markdown, plain text, and source code files such as `.js`, `.ts`, `.tsx`, `.css`, etc. Binary files, PDFs, images, and other non-text formats are ignored.\n *\n * Supports include/exclude filters and percent-encoded paths.\n */\nexport class LocalFileStrategy extends BaseScraperStrategy {\n private readonly fileFetcher = new FileFetcher();\n private readonly pipelines: ContentPipeline[];\n\n constructor(config: AppConfig) {\n super(config);\n this.pipelines = PipelineFactory.createStandardPipelines(config);\n }\n\n canHandle(url: string): boolean {\n return url.startsWith(\"file://\");\n }\n\n async processItem(\n item: QueueItem,\n options: ScraperOptions,\n _signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n // Parse the file URL properly to handle both file:// and file:/// formats\n let filePath = item.url.replace(/^file:\\/\\/\\/?/, \"\");\n filePath = decodeURIComponent(filePath);\n\n // Ensure absolute path on Unix-like systems (if not already absolute)\n if (!filePath.startsWith(\"/\") && process.platform !== \"win32\") {\n filePath = `/${filePath}`;\n }\n\n let stats: Awaited<ReturnType<typeof fs.stat>> | null = null;\n let archivePath: string | null = null;\n let innerPath: string | null = null;\n let archiveAdapter: ArchiveAdapter | null = null;\n\n try {\n try {\n stats = await fs.stat(filePath);\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\" || code === \"ENOTDIR\") {\n // File not found or path component is not a directory (maybe archive traversal)\n // Check if it's a virtual path inside an archive\n const resolved = await this.resolveVirtualPath(filePath);\n if (resolved.archive && resolved.inner && resolved.adapter) {\n archivePath = resolved.archive;\n innerPath = resolved.inner;\n archiveAdapter = resolved.adapter;\n } else {\n logger.info(`✓ File deleted or not available: ${filePath}`);\n return {\n url: item.url,\n links: [],\n status: FetchStatus.NOT_FOUND,\n };\n }\n } else {\n throw error;\n }\n }\n\n // Handle physical directory\n if (stats?.isDirectory()) {\n const contents = await fs.readdir(filePath);\n // Only return links that pass shouldProcessUrl\n const links = contents\n .map((name) => {\n // Construct valid file URL using URL class to ensure proper encoding and structure\n const url = new URL(\n `file://${path.join(filePath, name).replace(/\\\\/g, \"/\")}`,\n );\n // Ensure we always have file:/// format (empty host)\n if (url.hostname !== \"\") {\n url.pathname = `/${url.hostname}${url.pathname}`;\n url.hostname = \"\";\n }\n return url.href;\n })\n .filter((url) => {\n const allowed = this.shouldProcessUrl(url, options);\n if (!allowed) {\n logger.debug(`Skipping out-of-scope link: ${url}`);\n }\n return allowed;\n });\n\n logger.debug(\n `Found ${links.length} files in ${filePath} (from ${contents.length} entries)`,\n );\n return { url: item.url, links, status: FetchStatus.SUCCESS };\n }\n\n // Check if the file itself is an archive (Root Archive)\n if (stats?.isFile()) {\n const adapter = await getArchiveAdapter(filePath);\n if (adapter) {\n logger.info(`📦 Detected archive file: ${filePath}`);\n try {\n const links: string[] = [];\n for await (const entry of adapter.listEntries()) {\n // Validate entry path to prevent Zip Slip\n if (entry.path.includes(\"..\")) {\n logger.warn(`⚠️ Skipping unsafe archive entry path: ${entry.path}`);\n continue;\n }\n\n // Create virtual URL: file:///path/to/archive.zip/entry/path\n // Ensure entry path doesn't start with / to avoid double slash issues\n const entryPath = entry.path.replace(/^\\//, \"\");\n\n // Normalize windows separators if any in entry path (rare in standard zips but possible in display)\n const fullVirtualPath = path.join(filePath, entryPath).replace(/\\\\/g, \"/\");\n const virtualUrl = new URL(`file://${fullVirtualPath}`);\n if (virtualUrl.hostname !== \"\") {\n virtualUrl.pathname = `/${virtualUrl.hostname}${virtualUrl.pathname}`;\n virtualUrl.hostname = \"\";\n }\n\n if (this.shouldProcessUrl(virtualUrl.href, options)) {\n links.push(virtualUrl.href);\n }\n }\n logger.debug(`Found ${links.length} entries in archive ${filePath}`);\n return { url: item.url, links, status: FetchStatus.SUCCESS };\n } catch (err) {\n logger.error(`❌ Failed to list archive ${filePath}: ${err}`);\n // Treat as binary file or fail?\n // If listing fails, maybe just fall through to standard processing (which will likely ignore it)\n } finally {\n await adapter.close();\n }\n }\n }\n\n // Handle Virtual Archive Path (inner file)\n if (archivePath && innerPath && archiveAdapter) {\n // Validate inner path for Zip Slip\n if (innerPath.includes(\"..\")) {\n logger.warn(`⚠️ Detected unsafe virtual path traversal: ${innerPath}`);\n return {\n url: item.url,\n links: [],\n status: FetchStatus.NOT_FOUND,\n };\n }\n return await this.processArchiveEntry(\n item,\n archivePath,\n innerPath,\n archiveAdapter,\n options,\n );\n }\n\n const rawContent: RawContent = await this.fileFetcher.fetch(item.url, {\n etag: item.etag,\n });\n\n // Handle NOT_MODIFIED status (file hasn't changed)\n if (rawContent.status === FetchStatus.NOT_MODIFIED) {\n logger.debug(`✓ File unchanged: ${filePath}`);\n return { url: rawContent.source, links: [], status: FetchStatus.NOT_MODIFIED };\n }\n\n return await this.processContent(item.url, filePath, rawContent, options);\n } finally {\n if (archiveAdapter) {\n await archiveAdapter.close();\n }\n }\n }\n\n /**\n * Resolves a path that might be inside an archive.\n * Returns the archive path and the inner path if found.\n */\n private async resolveVirtualPath(fullPath: string): Promise<{\n archive: string | null;\n inner: string | null;\n adapter: ArchiveAdapter | null;\n }> {\n let currentPath = fullPath;\n while (\n currentPath !== \"/\" &&\n currentPath !== \".\" &&\n path.dirname(currentPath) !== currentPath\n ) {\n const dirname = path.dirname(currentPath);\n\n try {\n const stats = await fs.stat(currentPath);\n if (stats.isFile()) {\n // Found a file part of the path. Check if it is an archive.\n const adapter = await getArchiveAdapter(currentPath);\n if (adapter) {\n // We return the OPEN adapter to avoid reopening it\n const inner = fullPath\n .substring(currentPath.length)\n .replace(/^\\/+/, \"\")\n .replace(/^\\\\+/, \"\");\n return { archive: currentPath, inner, adapter };\n }\n }\n // If it exists and is not an archive (or is a dir), then the path is just wrong/missing\n // because we started from a full path that didn't exist (ENOENT), and walked up.\n // If we hit a real directory or real file that isn't an archive, we stop.\n return { archive: null, inner: null, adapter: null };\n } catch (_e) {\n // Path segment doesn't exist, go up\n currentPath = dirname;\n }\n }\n return { archive: null, inner: null, adapter: null };\n }\n\n private async processArchiveEntry(\n item: QueueItem,\n archivePath: string,\n innerPath: string,\n adapter: ArchiveAdapter,\n options: ScraperOptions,\n ): Promise<ProcessItemResult> {\n logger.debug(`Reading archive entry: ${innerPath} inside ${archivePath}`);\n\n try {\n const contentBuffer = await adapter.getContent(innerPath);\n\n // Detect mime type based on inner filename using MimeTypeUtils for consistent detection\n const mimeType =\n MimeTypeUtils.detectMimeTypeFromPath(innerPath) || \"application/octet-stream\";\n\n const rawContent: RawContent = {\n source: item.url,\n content: contentBuffer,\n mimeType,\n status: FetchStatus.SUCCESS,\n lastModified: new Date().toISOString(), // Archive entries don't easily give mod time in generic way, defaulting\n etag: undefined, // Could hash content?\n };\n\n return this.processContent(\n item.url,\n `${archivePath}/${innerPath}`,\n rawContent,\n options,\n );\n } catch (err) {\n logger.warn(\n `⚠️ Failed to read archive entry \"${innerPath}\" from archive \"${archivePath}\": ${err}`,\n );\n console.error(`DEBUG ERROR: ${err}`); // Force output to console\n return {\n url: item.url,\n links: [],\n status: FetchStatus.NOT_FOUND,\n };\n }\n }\n\n private async processContent(\n _url: string,\n displayPath: string,\n rawContent: RawContent,\n options: ScraperOptions,\n ): Promise<ProcessItemResult> {\n let processed: PipelineResult | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent.mimeType, rawContent.content)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${displayPath})`,\n );\n processed = await pipeline.process(rawContent, options, this.fileFetcher);\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for file ${displayPath}. Skipping processing.`,\n );\n return { url: rawContent.source, links: [], status: FetchStatus.SUCCESS };\n }\n\n for (const err of processed.errors ?? []) {\n logger.warn(`⚠️ Processing error for ${displayPath}: ${err.message}`);\n }\n\n // Use filename as fallback if title is empty or not a string\n const filename = path.basename(displayPath);\n const title = processed.title?.trim() || filename || null;\n\n // For local files, we don't follow links (no crawling within file content)\n // Return empty links array\n return {\n url: rawContent.source,\n title: title,\n etag: rawContent.etag,\n lastModified: rawContent.lastModified,\n sourceContentType: rawContent.mimeType,\n contentType: processed.contentType || rawContent.mimeType,\n content: processed,\n links: [],\n status: FetchStatus.SUCCESS,\n };\n }\n\n /**\n * Cleanup resources used by this strategy, specifically the pipeline browser instances.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n }\n}\n","/**\n * Web scraper strategy that normalizes URLs, fetches content with automatic\n * fetcher selection, and routes content through pipelines. Requires resolved\n * configuration from the entrypoint to avoid implicit config loading.\n */\nimport fsPromises from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport type { AppConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport type { UrlNormalizerOptions } from \"../../utils/url\";\nimport { AutoDetectFetcher } from \"../fetcher\";\nimport { FetchStatus, type RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, PipelineResult } from \"../pipelines/types\";\nimport type { QueueItem, ScraperOptions } from \"../types\";\nimport { BaseScraperStrategy, type ProcessItemResult } from \"./BaseScraperStrategy\";\nimport { LocalFileStrategy } from \"./LocalFileStrategy\";\n\nexport interface WebScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n shouldFollowLink?: (baseUrl: URL, targetUrl: URL) => boolean;\n}\n\nexport class WebScraperStrategy extends BaseScraperStrategy {\n private readonly fetcher: AutoDetectFetcher;\n private readonly shouldFollowLinkFn?: (baseUrl: URL, targetUrl: URL) => boolean;\n private readonly pipelines: ContentPipeline[];\n private readonly localFileStrategy: LocalFileStrategy;\n private tempFiles: string[] = [];\n\n constructor(config: AppConfig, options: WebScraperStrategyOptions = {}) {\n super(config, { urlNormalizerOptions: options.urlNormalizerOptions });\n this.shouldFollowLinkFn = options.shouldFollowLink;\n this.fetcher = new AutoDetectFetcher(config.scraper);\n this.pipelines = PipelineFactory.createStandardPipelines(config);\n this.localFileStrategy = new LocalFileStrategy(config);\n }\n\n canHandle(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n return parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\";\n } catch {\n return false;\n }\n }\n\n // Removed custom isInScope logic; using shared scope utility for consistent behavior\n\n /**\n * Processes a single queue item by fetching its content and processing it through pipelines.\n * @param item - The queue item to process.\n * @param options - Scraper options including headers for HTTP requests.\n * @param _progressCallback - Optional progress callback (not used here).\n * @param signal - Optional abort signal for request cancellation.\n * @returns An object containing the processed document and extracted links.\n */\n protected override async processItem(\n item: QueueItem,\n options: ScraperOptions,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n const { url } = item;\n\n try {\n // Log when processing with ETag for conditional requests\n if (item.etag) {\n logger.debug(`Processing ${url} with stored ETag: ${item.etag}`);\n }\n\n // Check for Archive Root URL (only if depth 0)\n if (item.depth === 0) {\n const isArchive = /\\.(zip|tar|gz|tgz)$/i.test(new URL(url).pathname);\n if (isArchive) {\n return this.processRootArchive(item, options, signal);\n }\n }\n\n // Define fetch options, passing signal, followRedirects, headers, and etag\n const fetchOptions = {\n signal,\n followRedirects: options.followRedirects,\n headers: options.headers, // Forward custom headers\n etag: item.etag, // Pass ETag for conditional requests\n };\n\n // Use AutoDetectFetcher which handles fallbacks automatically\n const rawContent: RawContent = await this.fetcher.fetch(url, fetchOptions);\n\n logger.debug(\n `Fetch result for ${url}: status=${rawContent.status}, etag=${rawContent.etag || \"none\"}`,\n );\n\n // Return the status directly - BaseScraperStrategy handles NOT_MODIFIED and NOT_FOUND\n // Use the final URL from rawContent.source (which may differ due to redirects)\n if (rawContent.status !== FetchStatus.SUCCESS) {\n logger.debug(`Skipping pipeline for ${url} due to status: ${rawContent.status}`);\n return { url: rawContent.source, links: [], status: rawContent.status };\n }\n\n // --- Start Pipeline Processing ---\n let processed: PipelineResult | undefined;\n for (const pipeline of this.pipelines) {\n const contentBuffer = Buffer.isBuffer(rawContent.content)\n ? rawContent.content\n : Buffer.from(rawContent.content);\n if (pipeline.canProcess(rawContent.mimeType || \"text/plain\", contentBuffer)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${url})`,\n );\n processed = await pipeline.process(rawContent, options, this.fetcher);\n break;\n }\n }\n\n if (!processed) {\n // If content type is unsupported (e.g. binary/archive encountered during crawl), we just skip\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for URL ${url}. Skipping processing.`,\n );\n return { url: rawContent.source, links: [], status: FetchStatus.SUCCESS };\n }\n\n // Log errors from pipeline\n for (const err of processed.errors ?? []) {\n logger.warn(`⚠️ Processing error for ${url}: ${err.message}`);\n }\n\n // Check if content processing resulted in usable content\n if (!processed.textContent || !processed.textContent.trim()) {\n logger.warn(\n `⚠️ No processable content found for ${url} after pipeline execution.`,\n );\n return {\n url: rawContent.source,\n links: processed.links,\n status: FetchStatus.SUCCESS,\n };\n }\n\n // Update canonical base URL from the first page's final URL (after redirects)\n if (item.depth === 0) {\n this.canonicalBaseUrl = new URL(rawContent.source);\n }\n\n const filteredLinks =\n processed.links?.filter((link) => {\n try {\n const targetUrl = new URL(link);\n\n // Check for archive links during crawl - ignore them\n if (/\\.(zip|tar|gz|tgz)$/i.test(targetUrl.pathname)) {\n return false;\n }\n\n // Use the base class's shouldProcessUrl which handles scope + include/exclude patterns\n if (!this.shouldProcessUrl(targetUrl.href, options)) {\n return false;\n }\n // Apply optional custom filter function if provided\n if (this.shouldFollowLinkFn) {\n const baseUrl = this.canonicalBaseUrl ?? new URL(options.url);\n return this.shouldFollowLinkFn(baseUrl, targetUrl);\n }\n return true;\n } catch {\n return false;\n }\n }) ?? [];\n\n return {\n url: rawContent.source,\n etag: rawContent.etag,\n lastModified: rawContent.lastModified,\n sourceContentType: rawContent.mimeType,\n contentType: processed.contentType || rawContent.mimeType,\n content: processed,\n links: filteredLinks,\n status: FetchStatus.SUCCESS,\n };\n } catch (error) {\n // Log fetch errors or pipeline execution errors (if run throws)\n logger.error(`❌ Failed processing page ${url}: ${error}`);\n throw error;\n }\n }\n\n private async processRootArchive(\n item: QueueItem,\n options: ScraperOptions,\n signal?: AbortSignal,\n ): Promise<ProcessItemResult> {\n logger.info(`📦 Downloading root archive: ${item.url}`);\n\n // We need to stream the download to a temp file\n // Since fetcher.fetch returns a buffer (usually), we might want to bypass it or use it if small enough?\n // But archives can be huge. fetcher.fetch currently loads into memory.\n // For now, let's assume we can use fetcher but warn about memory, OR implement stream download here.\n // Our fetcher abstraction returns RawContent with Buffer.\n // If we want to stream, we might need to access the underlying axios/fetch stream.\n // `AutoDetectFetcher` doesn't expose stream easily.\n // Ideally we refactor fetcher to support streams, but that's out of scope.\n // So we will use fetcher and write buffer to temp file.\n // LIMITATION: Large archives will hit memory limits.\n\n const rawContent = await this.fetcher.fetch(item.url, {\n signal,\n headers: options.headers,\n });\n\n if (rawContent.status !== FetchStatus.SUCCESS) {\n return { url: rawContent.source, links: [], status: rawContent.status };\n }\n\n const buffer = Buffer.isBuffer(rawContent.content)\n ? rawContent.content\n : Buffer.from(rawContent.content);\n\n const tempDir = os.tmpdir();\n const tempFile = path.join(\n tempDir,\n `scraper-${Date.now()}-${path.basename(new URL(item.url).pathname)}`,\n );\n\n // Track file immediately so we can clean it up if write fails or later\n this.tempFiles.push(tempFile);\n\n await fsPromises.writeFile(tempFile, buffer);\n\n // Delegate to LocalFileStrategy\n const localUrl = `file://${tempFile}`;\n const localItem = { ...item, url: localUrl };\n\n const result = await this.localFileStrategy.processItem(localItem, options, signal);\n\n // We need to fix up the links to point back to something meaningful?\n // If we process a zip, we get file:///tmp/.../file.txt\n // These links are only useful if we continue to treat them as local files for this session.\n // But `WebScraper` expects http links usually?\n // Actually, if we return file:// links, the queue might try to fetch them.\n // `WebScraperStrategy` handles http/https. `LocalFileStrategy` handles file://.\n // If we return file:// links, the scraper will need to route them to `LocalFileStrategy`.\n // The `ScraperService` uses `ScraperRegistry` to pick strategy.\n // So file:// links will work!\n\n return {\n ...result,\n url: item.url, // Keep original URL as the source of this item\n // links are file://...\n };\n }\n\n /**\n * Cleanup resources used by this strategy, specifically the pipeline browser instances and fetcher.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled([\n ...this.pipelines.map((pipeline) => pipeline.close()),\n this.localFileStrategy.cleanup(),\n this.fetcher.close(),\n ...this.tempFiles.map((file) => fsPromises.unlink(file).catch(() => {})),\n ]);\n }\n}\n","import type { ProgressCallback } from \"../../types\";\nimport type { AppConfig } from \"../../utils/config\";\nimport type { ScraperOptions, ScraperProgressEvent, ScraperStrategy } from \"../types\";\nimport { WebScraperStrategy } from \"./WebScraperStrategy\";\n\nexport class NpmScraperStrategy implements ScraperStrategy {\n private defaultStrategy: WebScraperStrategy;\n\n canHandle(url: string): boolean {\n const { hostname } = new URL(url);\n return [\"npmjs.org\", \"npmjs.com\", \"www.npmjs.com\"].includes(hostname);\n }\n\n constructor(config: AppConfig) {\n this.defaultStrategy = new WebScraperStrategy(config, {\n urlNormalizerOptions: {\n ignoreCase: true,\n removeHash: true,\n removeTrailingSlash: true,\n removeQuery: true, // Enable removeQuery for NPM packages\n },\n });\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal,\n ): Promise<void> {\n // Use default strategy with our configuration, passing the signal\n await this.defaultStrategy.scrape(options, progressCallback, signal);\n }\n\n /**\n * Cleanup resources used by this strategy.\n */\n async cleanup(): Promise<void> {\n await this.defaultStrategy.cleanup();\n }\n}\n","import type { ProgressCallback } from \"../../types\";\nimport type { AppConfig } from \"../../utils/config\";\nimport type { ScraperOptions, ScraperProgressEvent, ScraperStrategy } from \"../types\";\nimport { WebScraperStrategy } from \"./WebScraperStrategy\";\n\nexport class PyPiScraperStrategy implements ScraperStrategy {\n private defaultStrategy: WebScraperStrategy;\n\n canHandle(url: string): boolean {\n const { hostname } = new URL(url);\n return [\"pypi.org\", \"www.pypi.org\"].includes(hostname);\n }\n\n constructor(config: AppConfig) {\n this.defaultStrategy = new WebScraperStrategy(config, {\n urlNormalizerOptions: {\n ignoreCase: true,\n removeHash: true,\n removeTrailingSlash: true,\n removeQuery: true, // Enable removeQuery for PyPI packages\n },\n });\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal,\n ): Promise<void> {\n // Use default strategy with our configuration, passing the signal\n await this.defaultStrategy.scrape(options, progressCallback, signal);\n }\n\n /**\n * Cleanup resources used by this strategy.\n */\n async cleanup(): Promise<void> {\n await this.defaultStrategy.cleanup();\n }\n}\n","import type { AppConfig } from \"../utils/config\";\nimport { ScraperError } from \"../utils/errors\";\nimport { logger } from \"../utils/logger\";\nimport { validateUrl } from \"../utils/url\";\nimport { GitHubScraperStrategy } from \"./strategies/GitHubScraperStrategy\";\nimport { LocalFileStrategy } from \"./strategies/LocalFileStrategy\";\nimport { NpmScraperStrategy } from \"./strategies/NpmScraperStrategy\";\nimport { PyPiScraperStrategy } from \"./strategies/PyPiScraperStrategy\";\nimport { WebScraperStrategy } from \"./strategies/WebScraperStrategy\";\nimport type { ScraperStrategy } from \"./types\";\n\n/**\n * Factory for creating scraper strategy instances.\n * Each call to getStrategy() returns a fresh instance to ensure\n * parallel scrape jobs have completely isolated state.\n */\nexport class ScraperRegistry {\n private config: AppConfig;\n\n constructor(config: AppConfig) {\n this.config = config;\n }\n\n /**\n * Creates and returns a fresh strategy instance for the given URL.\n * Each call returns a new instance to ensure state isolation between parallel scrapes.\n */\n getStrategy(url: string): ScraperStrategy {\n if (!url.startsWith(\"github-file://\")) {\n validateUrl(url);\n }\n\n // Check each strategy type without instantiating heavy objects.\n // Order matters: more specific strategies should come before generic ones.\n\n if (isLocalFileUrl(url)) {\n logger.debug(`Using strategy \"LocalFileStrategy\" for URL: ${url}`);\n return new LocalFileStrategy(this.config);\n }\n\n if (isNpmUrl(url)) {\n logger.debug(`Using strategy \"NpmScraperStrategy\" for URL: ${url}`);\n return new NpmScraperStrategy(this.config);\n }\n\n if (isPyPiUrl(url)) {\n logger.debug(`Using strategy \"PyPiScraperStrategy\" for URL: ${url}`);\n return new PyPiScraperStrategy(this.config);\n }\n\n if (isGitHubUrl(url)) {\n logger.debug(`Using strategy \"GitHubScraperStrategy\" for URL: ${url}`);\n return new GitHubScraperStrategy(this.config);\n }\n\n if (isWebUrl(url)) {\n logger.debug(`Using strategy \"WebScraperStrategy\" for URL: ${url}`);\n return new WebScraperStrategy(this.config, {});\n }\n\n throw new ScraperError(`No strategy found for URL: ${url}`);\n }\n}\n\nfunction isLocalFileUrl(url: string): boolean {\n return url.startsWith(\"file://\");\n}\n\nfunction isNpmUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return [\"npmjs.org\", \"npmjs.com\", \"www.npmjs.com\"].includes(hostname);\n } catch {\n return false;\n }\n}\n\nfunction isPyPiUrl(url: string): boolean {\n try {\n const { hostname } = new URL(url);\n return [\"pypi.org\", \"www.pypi.org\"].includes(hostname);\n } catch {\n return false;\n }\n}\n\nfunction isGitHubUrl(url: string): boolean {\n if (url.startsWith(\"github-file://\")) {\n return true;\n }\n\n try {\n const parsedUrl = new URL(url);\n const { hostname, pathname } = parsedUrl;\n\n if (![\"github.com\", \"www.github.com\"].includes(hostname)) {\n return false;\n }\n\n if (pathname.match(/^\\/[^/]+\\/[^/]+\\/?$/)) {\n return true;\n }\n\n if (pathname.match(/^\\/[^/]+\\/[^/]+\\/tree\\//)) {\n return true;\n }\n\n if (pathname.match(/^\\/[^/]+\\/[^/]+\\/blob\\//)) {\n return true;\n }\n\n return false;\n } catch {\n return false;\n }\n}\n\nfunction isWebUrl(url: string): boolean {\n try {\n const parsedUrl = new URL(url);\n return parsedUrl.protocol === \"http:\" || parsedUrl.protocol === \"https:\";\n } catch {\n return false;\n }\n}\n","import type { ProgressCallback } from \"../types\";\nimport { ScraperError } from \"../utils/errors\";\nimport { logger } from \"../utils/logger\";\nimport type { ScraperRegistry } from \"./ScraperRegistry\";\nimport type { ScraperOptions, ScraperProgressEvent } from \"./types\";\n\n/**\n * Orchestrates document scraping operations using registered scraping strategies.\n * Automatically selects appropriate strategy based on URL patterns.\n * Each scrape operation uses a fresh strategy instance for state isolation.\n */\nexport class ScraperService {\n private registry: ScraperRegistry;\n\n constructor(registry: ScraperRegistry) {\n this.registry = registry;\n }\n\n /**\n * Scrapes content from the provided URL using the appropriate strategy.\n * Reports progress via callback and handles errors.\n * Cleans up strategy resources after scrape completes (success or failure).\n */\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgressEvent>,\n signal?: AbortSignal,\n ): Promise<void> {\n // Get a fresh strategy instance for this scrape (factory pattern)\n const strategy = this.registry.getStrategy(options.url);\n\n let scrapeError: Error | null = null;\n let cleanupErrorToThrow: Error | null = null;\n try {\n // Pass the signal down to the strategy\n await strategy.scrape(options, progressCallback, signal);\n } catch (error) {\n scrapeError =\n error instanceof Error\n ? error\n : new ScraperError(`Scrape failed for URL: ${options.url}`, false);\n } finally {\n // Always cleanup strategy resources after scrape completes\n // This releases browser instances, temp files, etc.\n try {\n await strategy.cleanup?.();\n } catch (cleanupError) {\n logger.error(`❌ Strategy cleanup failed for ${options.url}: ${cleanupError}`);\n if (!scrapeError) {\n cleanupErrorToThrow =\n cleanupError instanceof Error\n ? cleanupError\n : new ScraperError(\n `Strategy cleanup failed for URL: ${options.url}`,\n false,\n );\n }\n }\n }\n\n if (scrapeError) {\n throw scrapeError;\n }\n\n if (cleanupErrorToThrow) {\n throw cleanupErrorToThrow;\n }\n }\n}\n","import type { ScraperService } from \"../scraper\";\nimport type {\n ScrapeResult,\n ScraperProgressEvent as ScraperProgress,\n ScraperProgressEvent,\n} from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { logger } from \"../utils/logger\";\nimport { CancellationError } from \"./errors\";\nimport type { InternalPipelineJob } from \"./types\";\n\n/**\n * Internal callbacks used by PipelineWorker.\n * These work with InternalPipelineJob before conversion to public interface.\n */\ninterface WorkerCallbacks {\n onJobProgress?: (job: InternalPipelineJob, progress: ScraperProgress) => Promise<void>;\n onJobError?: (\n job: InternalPipelineJob,\n error: Error,\n page?: ScrapeResult,\n ) => Promise<void>;\n onJobStatusChange?: (job: InternalPipelineJob) => Promise<void>;\n}\n\n/**\n * Executes a single document processing job.\n * Handles scraping, storing documents, and reporting progress/errors via callbacks.\n */\nexport class PipelineWorker {\n // Dependencies are passed in, making the worker stateless regarding specific jobs\n private readonly store: DocumentManagementService;\n private readonly scraperService: ScraperService;\n\n // Constructor accepts dependencies needed for execution\n constructor(store: DocumentManagementService, scraperService: ScraperService) {\n this.store = store;\n this.scraperService = scraperService;\n }\n\n /**\n * Executes the given pipeline job.\n * @param job - The job to execute.\n * @param callbacks - Internal callbacks provided by the manager for reporting.\n */\n async executeJob(job: InternalPipelineJob, callbacks: WorkerCallbacks): Promise<void> {\n const { id: jobId, library, version, scraperOptions, abortController } = job;\n const signal = abortController.signal;\n\n logger.debug(`[${jobId}] Worker starting job for ${library}@${version}`);\n\n try {\n // Clear existing documents for this library/version before scraping\n // Skip this step for refresh operations or if clean is explicitly false\n if (!scraperOptions.isRefresh && scraperOptions.clean !== false) {\n await this.store.removeAllDocuments(library, version);\n logger.info(\n `💾 Cleared store for ${library}@${version || \"latest\"} before scraping.`,\n );\n } else {\n const message = scraperOptions.isRefresh\n ? `🔄 Refresh operation - preserving existing data for ${library}@${version || \"latest\"}.`\n : `💾 Appending to store for ${library}@${version || \"latest\"} (clean=false).`;\n logger.info(message);\n }\n\n // --- Core Job Logic ---\n await this.scraperService.scrape(\n scraperOptions,\n async (progress: ScraperProgressEvent) => {\n // Check for cancellation signal before processing each document\n if (signal.aborted) {\n throw new CancellationError(\"Job cancelled during scraping progress\");\n }\n\n // Update job object directly (manager holds the reference)\n // Report progress via manager's callback (single source of truth)\n await callbacks.onJobProgress?.(job, progress);\n\n // Handle deletion events (404 during refresh or broken links)\n if (progress.deleted && progress.pageId) {\n try {\n await this.store.deletePage(progress.pageId);\n logger.debug(\n `[${jobId}] Deleted page ${progress.pageId}: ${progress.currentUrl}`,\n );\n } catch (docError) {\n logger.error(\n `❌ [${jobId}] Failed to delete page ${progress.pageId}: ${docError}`,\n );\n\n // Report the error and fail the job to ensure data integrity\n const error =\n docError instanceof Error ? docError : new Error(String(docError));\n await callbacks.onJobError?.(job, error);\n // Re-throw to fail the job - deletion failures indicate serious database issues\n // and leaving orphaned documents would compromise index accuracy\n throw error;\n }\n }\n // Handle successful content processing\n else if (progress.result) {\n try {\n // For refresh operations, delete old documents before adding new ones\n if (progress.pageId) {\n await this.store.deletePage(progress.pageId);\n logger.debug(\n `[${jobId}] Refreshing page ${progress.pageId}: ${progress.currentUrl}`,\n );\n }\n\n // Add the processed content to the store\n await this.store.addScrapeResult(\n library,\n version,\n progress.depth,\n progress.result,\n );\n logger.debug(`[${jobId}] Stored processed content: ${progress.currentUrl}`);\n } catch (docError) {\n logger.error(\n `❌ [${jobId}] Failed to process content ${progress.currentUrl}: ${docError}`,\n );\n // Report document-specific errors via manager's callback\n await callbacks.onJobError?.(\n job,\n docError instanceof Error ? docError : new Error(String(docError)),\n progress.result,\n );\n // Decide if a single document error should fail the whole job\n // For now, we log and continue. To fail, re-throw here.\n }\n }\n },\n signal, // Pass signal to scraper service\n );\n // --- End Core Job Logic ---\n\n // Check signal one last time after scrape finishes\n if (signal.aborted) {\n throw new CancellationError(\"Job cancelled\");\n }\n\n // If successful and not cancelled, the manager will handle status update\n logger.debug(`[${jobId}] Worker finished job successfully.`);\n } catch (error) {\n // Re-throw error to be caught by the manager in _runJob\n logger.warn(`⚠️ [${jobId}] Worker encountered error: ${error}`);\n throw error;\n }\n // Note: The manager (_runJob) is responsible for updating final job status (COMPLETED/FAILED/CANCELLED)\n // and resolving/rejecting the completion promise based on the outcome here.\n }\n\n // --- Old methods removed ---\n // process()\n // stop()\n // setCallbacks()\n // handleScrapingProgress()\n}\n","/**\n * PipelineManager orchestrates a queue of scraping/indexing jobs.\n * - Controls concurrency, recovery, and job lifecycle\n * - Bridges in-memory job state with the persistent store\n * - Delegates execution to PipelineWorker and emits callbacks\n * Note: completionPromise has an attached no-op catch to avoid unhandled\n * promise rejection warnings when a job fails before a consumer awaits it.\n */\n\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { EventBusService } from \"../events/EventBusService\";\nimport { EventType } from \"../events/types\";\nimport { ScraperRegistry, ScraperService } from \"../scraper\";\nimport type { ScraperOptions, ScraperProgressEvent } from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { VersionStatus } from \"../store/types\";\nimport type { AppConfig } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { CancellationError, PipelineStateError } from \"./errors\";\nimport { PipelineWorker } from \"./PipelineWorker\"; // Import the worker\nimport type { IPipeline } from \"./trpc/interfaces\";\nimport type { InternalPipelineJob, PipelineJob } from \"./types\";\nimport { PipelineJobStatus } from \"./types\";\n\n/**\n * Manages a queue of document processing jobs, controlling concurrency and tracking progress.\n */\nexport class PipelineManager implements IPipeline {\n private jobMap: Map<string, InternalPipelineJob> = new Map();\n private jobQueue: string[] = [];\n private activeWorkers: Set<string> = new Set();\n private isRunning = false;\n private concurrency: number;\n private store: DocumentManagementService;\n private scraperService: ScraperService;\n private shouldRecoverJobs: boolean;\n private eventBus: EventBusService;\n private appConfig: AppConfig;\n\n constructor(\n store: DocumentManagementService,\n eventBus: EventBusService,\n options: { recoverJobs?: boolean; appConfig: AppConfig },\n ) {\n this.store = store;\n this.eventBus = eventBus;\n this.appConfig = options.appConfig;\n this.concurrency = this.appConfig.scraper.maxConcurrency;\n this.shouldRecoverJobs = options.recoverJobs ?? true; // Default to true for backward compatibility\n // ScraperService needs a registry. We create one internally for the manager.\n const registry = new ScraperRegistry(this.appConfig);\n this.scraperService = new ScraperService(registry);\n }\n\n /**\n * No-op method for backward compatibility with IPipeline interface.\n * Events are now emitted directly to EventBusService.\n */\n setCallbacks(_callbacks: unknown): void {\n // No-op: callbacks are no longer used\n }\n\n /**\n * Converts internal job representation to public job interface.\n */\n private toPublicJob(job: InternalPipelineJob): PipelineJob {\n return {\n id: job.id,\n library: job.library,\n version: job.version || null, // Convert empty string to null for public API\n status: job.status,\n progress: job.progress,\n error: job.error ? { message: job.error.message } : null,\n createdAt: job.createdAt,\n startedAt: job.startedAt,\n finishedAt: job.finishedAt,\n versionId: job.versionId,\n versionStatus: job.versionStatus,\n progressPages: job.progressPages,\n progressMaxPages: job.progressMaxPages,\n errorMessage: job.errorMessage,\n updatedAt: job.updatedAt,\n sourceUrl: job.sourceUrl,\n scraperOptions: job.scraperOptions,\n };\n }\n\n /**\n * Starts the pipeline manager's worker processing.\n */\n async start(): Promise<void> {\n if (this.isRunning) {\n logger.warn(\"⚠️ PipelineManager is already running.\");\n return;\n }\n this.isRunning = true;\n logger.debug(\n `PipelineManager started with concurrency ${this.concurrency}, recoverJobs: ${this.shouldRecoverJobs}.`,\n );\n\n // Recover pending jobs from database on startup only if enabled\n if (this.shouldRecoverJobs) {\n await this.recoverPendingJobs();\n } else {\n // Mark any interrupted jobs as failed so users can manually retry\n await this.markInterruptedJobsAsFailed();\n }\n\n this._processQueue().catch((error) => {\n logger.error(`❌ Error in processQueue during start: ${error}`);\n }); // Start processing any existing jobs\n }\n\n /**\n * Recovers pending jobs from the database after server restart.\n * Uses enqueueRefreshJob() to properly continue interrupted jobs,\n * leveraging existing pages and ETags when available.\n */\n async recoverPendingJobs(): Promise<void> {\n try {\n // Find all interrupted jobs (RUNNING + QUEUED)\n const interruptedVersions = await this.store.getVersionsByStatus([\n VersionStatus.RUNNING,\n VersionStatus.QUEUED,\n ]);\n\n if (interruptedVersions.length === 0) {\n logger.debug(\"No pending jobs to recover from database\");\n return;\n }\n\n logger.info(\n `📥 Recovering ${interruptedVersions.length} pending job(s) from database`,\n );\n\n for (const version of interruptedVersions) {\n const versionLabel = `${version.library_name}@${version.name || \"latest\"}`;\n try {\n // Use enqueueRefreshJob for recovery - it handles:\n // - Completed versions: incremental refresh with ETags\n // - Incomplete versions: falls back to enqueueJobWithStoredOptions()\n await this.enqueueRefreshJob(version.library_name, version.name);\n logger.info(`🔄 Recovering job: ${versionLabel}`);\n } catch (error) {\n // If recovery fails (e.g., no stored options), mark as failed\n const errorMessage = `Recovery failed: ${error instanceof Error ? error.message : String(error)}`;\n await this.store.updateVersionStatus(\n version.id,\n VersionStatus.FAILED,\n errorMessage,\n );\n logger.warn(`⚠️ Failed to recover job ${versionLabel}: ${error}`);\n }\n }\n } catch (error) {\n logger.error(`❌ Failed to recover pending jobs: ${error}`);\n }\n }\n\n /**\n * Marks all interrupted jobs (RUNNING/QUEUED) as FAILED.\n * Called when recoverJobs is false to allow users to manually retry via UI.\n */\n async markInterruptedJobsAsFailed(): Promise<void> {\n try {\n const interruptedVersions = await this.store.getVersionsByStatus([\n VersionStatus.RUNNING,\n VersionStatus.QUEUED,\n ]);\n\n if (interruptedVersions.length === 0) {\n logger.debug(\"No interrupted jobs to mark as failed\");\n return;\n }\n\n for (const version of interruptedVersions) {\n await this.store.updateVersionStatus(\n version.id,\n VersionStatus.FAILED,\n \"Job interrupted\",\n );\n logger.info(\n `❌ Marked interrupted job as failed: ${version.library_name}@${version.name || \"latest\"}`,\n );\n }\n } catch (error) {\n logger.error(`❌ Failed to mark interrupted jobs as failed: ${error}`);\n }\n }\n\n /**\n * Stops the pipeline manager and attempts to gracefully shut down workers.\n * Currently, it just stops processing new jobs. Cancellation of active jobs\n * needs explicit `cancelJob` calls.\n */\n async stop(): Promise<void> {\n if (!this.isRunning) {\n logger.warn(\"⚠️ PipelineManager is not running.\");\n return;\n }\n this.isRunning = false;\n logger.debug(\"PipelineManager stopping. No new jobs will be started.\");\n\n // Note: Strategy cleanup now happens per-scrape in ScraperService.scrape()\n // No cleanup call needed here since strategies are not cached.\n\n // Note: Does not automatically cancel active jobs.\n }\n\n /**\n * Enqueues a new document processing job, aborting any existing QUEUED/RUNNING job for the same library+version (including unversioned).\n */\n async enqueueScrapeJob(\n library: string,\n version: string | undefined | null,\n options: ScraperOptions,\n ): Promise<string> {\n // Normalize version: treat undefined/null as \"\" (unversioned)\n const normalizedVersion = version ?? \"\";\n\n // Abort any existing QUEUED or RUNNING job for the same library+version\n const allJobs = await this.getJobs();\n const duplicateJobs = allJobs.filter(\n (job) =>\n job.library === library &&\n (job.version ?? \"\") === normalizedVersion && // Normalize null to empty string for comparison\n [PipelineJobStatus.QUEUED, PipelineJobStatus.RUNNING].includes(job.status),\n );\n for (const job of duplicateJobs) {\n logger.info(\n `🚫 Aborting duplicate job for ${library}@${normalizedVersion}: ${job.id}`,\n );\n await this.cancelJob(job.id);\n }\n\n const jobId = uuidv4();\n const abortController = new AbortController();\n let resolveCompletion!: () => void;\n let rejectCompletion!: (reason?: unknown) => void;\n\n const completionPromise = new Promise<void>((resolve, reject) => {\n resolveCompletion = resolve;\n rejectCompletion = reject;\n });\n // Prevent unhandled rejection warnings if rejection occurs before consumers attach handlers\n completionPromise.catch(() => {});\n\n const job: InternalPipelineJob = {\n id: jobId,\n library,\n version: normalizedVersion,\n status: PipelineJobStatus.QUEUED,\n progress: null,\n error: null,\n createdAt: new Date(),\n startedAt: null,\n finishedAt: null,\n abortController,\n completionPromise,\n resolveCompletion,\n rejectCompletion,\n // Database fields (single source of truth)\n // Will be populated by updateJobStatus\n progressPages: 0,\n progressMaxPages: 0,\n errorMessage: null,\n updatedAt: new Date(),\n sourceUrl: options.url,\n scraperOptions: options,\n };\n\n this.jobMap.set(jobId, job);\n this.jobQueue.push(jobId);\n logger.info(\n `📝 Job enqueued: ${jobId} for ${library}${normalizedVersion ? `@${normalizedVersion}` : \" (latest)\"}`,\n );\n\n // Update database status to QUEUED\n await this.updateJobStatus(job, PipelineJobStatus.QUEUED);\n\n // Trigger processing if manager is running\n if (this.isRunning) {\n this._processQueue().catch((error) => {\n logger.error(`❌ Error in processQueue during enqueue: ${error}`);\n });\n }\n\n return jobId;\n }\n\n /**\n * Enqueues a refresh job for an existing library version by re-scraping all pages\n * and using ETag comparison to skip unchanged content.\n *\n * If the version was never completed (interrupted or failed scrape), performs a\n * full re-scrape from scratch instead of a refresh to ensure completeness.\n */\n async enqueueRefreshJob(\n library: string,\n version: string | undefined | null,\n ): Promise<string> {\n // Normalize version: treat undefined/null as \"\" (unversioned)\n const normalizedVersion = version ?? \"\";\n\n try {\n // First, check if the library version exists\n const versionId = await this.store.ensureVersion({\n library,\n version: normalizedVersion,\n });\n\n // Check the version's status to detect incomplete scrapes\n const versionInfo = await this.store.getVersionById(versionId);\n if (!versionInfo) {\n throw new Error(`Version ID ${versionId} not found`);\n }\n\n // Get library information\n const libraryInfo = await this.store.getLibraryById(versionInfo.library_id);\n if (!libraryInfo) {\n throw new Error(`Library ID ${versionInfo.library_id} not found`);\n }\n\n // If the version is not completed, it means the previous scrape was interrupted\n // or failed. In this case, perform a full re-scrape instead of a refresh.\n if (versionInfo && versionInfo.status !== VersionStatus.COMPLETED) {\n logger.info(\n `⚠️ Version ${library}@${normalizedVersion || \"latest\"} has status \"${versionInfo.status}\". Performing full re-scrape instead of refresh.`,\n );\n return this.enqueueJobWithStoredOptions(library, normalizedVersion);\n }\n\n // Get all pages for this version with their ETags and depths\n const pages = await this.store.getPagesByVersionId(versionId);\n\n // Debug: Log first page to see what data we're getting\n if (pages.length > 0) {\n logger.debug(\n `Sample page data: url=${pages[0].url}, etag=${pages[0].etag}, depth=${pages[0].depth}`,\n );\n }\n\n if (pages.length === 0) {\n throw new Error(\n `No pages found for ${library}@${normalizedVersion || \"latest\"}. Use scrape_docs to index it first.`,\n );\n }\n\n logger.info(\n `🔄 Preparing refresh job for ${library}@${normalizedVersion || \"latest\"} with ${pages.length} page(s)`,\n );\n\n // Build initialQueue from pages with original depth values\n const initialQueue = pages.map((page) => ({\n url: page.url,\n depth: page.depth ?? 0, // Use original depth, fallback to 0 for old data\n pageId: page.id,\n etag: page.etag,\n }));\n\n // Get stored scraper options to retrieve the source URL and other options\n const storedOptions = await this.store.getScraperOptions(versionId);\n\n // Build scraper options with initialQueue and isRefresh flag\n const scraperOptions = {\n url: storedOptions?.sourceUrl || pages[0].url, // Required but not used when initialQueue is set\n library,\n version: normalizedVersion,\n ...(storedOptions?.options || {}), // Include stored options if available (spread first)\n // Override with refresh-specific options (these must come after the spread)\n initialQueue, // Pre-populated queue with existing pages\n isRefresh: true, // Mark this as a refresh operation\n };\n\n // Enqueue as a standard scrape job with the initialQueue\n logger.info(\n `📝 Enqueueing refresh job for ${library}@${normalizedVersion || \"latest\"}`,\n );\n return this.enqueueScrapeJob(library, normalizedVersion, scraperOptions);\n } catch (error) {\n logger.error(`❌ Failed to enqueue refresh job: ${error}`);\n throw error;\n }\n }\n\n /**\n * Enqueues a job using stored scraper options from a previous indexing run.\n * If no stored options are found, throws an error.\n */\n async enqueueJobWithStoredOptions(\n library: string,\n version: string | undefined | null,\n ): Promise<string> {\n const normalizedVersion = version ?? \"\";\n\n try {\n // Get the version ID to retrieve stored options\n const versionId = await this.store.ensureVersion({\n library,\n version: normalizedVersion,\n });\n const stored = await this.store.getScraperOptions(versionId);\n\n if (!stored) {\n throw new Error(\n `No stored scraper options found for ${library}@${normalizedVersion || \"latest\"}`,\n );\n }\n\n const storedOptions = stored.options;\n\n // Reconstruct complete scraper options\n const completeOptions: ScraperOptions = {\n url: stored.sourceUrl,\n library,\n version: normalizedVersion,\n ...storedOptions,\n };\n\n logger.info(\n `🔄 Re-indexing ${library}@${normalizedVersion || \"latest\"} with stored options from ${stored.sourceUrl}`,\n );\n\n return this.enqueueScrapeJob(library, normalizedVersion, completeOptions);\n } catch (error) {\n logger.error(`❌ Failed to enqueue job with stored options: ${error}`);\n throw error;\n }\n }\n\n /**\n * Retrieves the current state of a specific job.\n */\n async getJob(jobId: string): Promise<PipelineJob | undefined> {\n const internalJob = this.jobMap.get(jobId);\n return internalJob ? this.toPublicJob(internalJob) : undefined;\n }\n\n /**\n * Retrieves the current state of all jobs (or a subset based on status).\n */\n async getJobs(status?: PipelineJobStatus): Promise<PipelineJob[]> {\n const allJobs = Array.from(this.jobMap.values());\n const filteredJobs = status\n ? allJobs.filter((job) => job.status === status)\n : allJobs;\n return filteredJobs.map((job) => this.toPublicJob(job));\n }\n\n /**\n * Returns a promise that resolves when the specified job completes, fails, or is cancelled.\n * For cancelled jobs, this resolves successfully rather than rejecting.\n */\n async waitForJobCompletion(jobId: string): Promise<void> {\n const job = this.jobMap.get(jobId);\n if (!job) {\n throw new PipelineStateError(`Job not found: ${jobId}`);\n }\n\n try {\n await job.completionPromise;\n } catch (error) {\n // If the job was cancelled, treat it as successful completion\n if (\n error instanceof CancellationError ||\n job.status === PipelineJobStatus.CANCELLED\n ) {\n return; // Resolve successfully for cancelled jobs\n }\n // Re-throw other errors (failed jobs)\n throw error;\n }\n }\n\n /**\n * Attempts to cancel a queued or running job.\n */\n async cancelJob(jobId: string): Promise<void> {\n const job = this.jobMap.get(jobId);\n if (!job) {\n logger.warn(`❓ Attempted to cancel non-existent job: ${jobId}`);\n return;\n }\n\n switch (job.status) {\n case PipelineJobStatus.QUEUED:\n // Remove from queue and mark as cancelled\n this.jobQueue = this.jobQueue.filter((id) => id !== jobId);\n await this.updateJobStatus(job, PipelineJobStatus.CANCELLED);\n job.finishedAt = new Date();\n logger.info(`🚫 Job cancelled (was queued): ${jobId}`);\n job.rejectCompletion(new PipelineStateError(\"Job cancelled before starting\"));\n break;\n\n case PipelineJobStatus.RUNNING:\n // Signal cancellation via AbortController\n await this.updateJobStatus(job, PipelineJobStatus.CANCELLING);\n job.abortController.abort();\n logger.info(`🚫 Signalling cancellation for running job: ${jobId}`);\n // The worker is responsible for transitioning to CANCELLED and rejecting\n break;\n\n case PipelineJobStatus.COMPLETED:\n case PipelineJobStatus.FAILED:\n case PipelineJobStatus.CANCELLED:\n case PipelineJobStatus.CANCELLING:\n logger.warn(\n `⚠️ Job ${jobId} cannot be cancelled in its current state: ${job.status}`,\n );\n break;\n\n default:\n logger.error(`❌ Unhandled job status for cancellation: ${job.status}`);\n break;\n }\n }\n\n /**\n * Removes all jobs that are in a final state (completed, cancelled, or failed).\n * Only removes jobs that are not currently in the queue or actively running.\n * @returns The number of jobs that were cleared.\n */\n async clearCompletedJobs(): Promise<number> {\n const completedStatuses = [\n PipelineJobStatus.COMPLETED,\n PipelineJobStatus.CANCELLED,\n PipelineJobStatus.FAILED,\n ];\n\n let clearedCount = 0;\n const jobsToRemove: string[] = [];\n\n // Find all jobs that can be cleared\n for (const [jobId, job] of this.jobMap.entries()) {\n if (completedStatuses.includes(job.status)) {\n jobsToRemove.push(jobId);\n clearedCount++;\n }\n }\n\n // Remove the jobs from the map\n for (const jobId of jobsToRemove) {\n this.jobMap.delete(jobId);\n }\n\n if (clearedCount > 0) {\n logger.info(`🧹 Cleared ${clearedCount} completed job(s) from the queue`);\n // Emit event to notify clients that the job list has changed\n this.eventBus.emit(EventType.JOB_LIST_CHANGE, undefined);\n } else {\n logger.debug(\"No completed jobs to clear\");\n }\n\n return clearedCount;\n }\n\n // --- Private Methods ---\n\n /**\n * Processes the job queue, starting new workers if capacity allows.\n */\n private async _processQueue(): Promise<void> {\n if (!this.isRunning) return;\n\n while (this.activeWorkers.size < this.concurrency && this.jobQueue.length > 0) {\n const jobId = this.jobQueue.shift();\n if (!jobId) continue; // Should not happen, but safety check\n\n const job = this.jobMap.get(jobId);\n if (!job || job.status !== PipelineJobStatus.QUEUED) {\n logger.warn(`⏭️ Skipping job ${jobId} in queue (not found or not queued).`);\n continue;\n }\n\n this.activeWorkers.add(jobId);\n await this.updateJobStatus(job, PipelineJobStatus.RUNNING);\n job.startedAt = new Date();\n\n // Start the actual job execution asynchronously\n this._runJob(job).catch(async (error) => {\n // Catch unexpected errors during job setup/execution not handled by _runJob itself\n logger.error(`❌ Unhandled error during job ${jobId} execution: ${error}`);\n if (\n job.status !== PipelineJobStatus.FAILED &&\n job.status !== PipelineJobStatus.CANCELLED\n ) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n await this.updateJobStatus(job, PipelineJobStatus.FAILED, errorMessage);\n job.error = error instanceof Error ? error : new Error(String(error));\n job.finishedAt = new Date();\n job.rejectCompletion(job.error);\n }\n this.activeWorkers.delete(jobId);\n this._processQueue().catch((error) => {\n logger.error(`❌ Error in processQueue after job completion: ${error}`);\n }); // Check if another job can start\n });\n }\n }\n\n /**\n * Executes a single pipeline job by delegating to a PipelineWorker.\n * Handles final status updates and promise resolution/rejection.\n */\n private async _runJob(job: InternalPipelineJob): Promise<void> {\n const { id: jobId, abortController } = job;\n const signal = abortController.signal; // Get signal for error checking\n\n // Instantiate a worker for this job.\n // Dependencies (store, scraperService) are held by the manager.\n const worker = new PipelineWorker(this.store, this.scraperService);\n\n try {\n // Delegate the actual work to the worker\n // The worker works with InternalPipelineJob, we convert to public when needed\n await worker.executeJob(job, {\n onJobProgress: async (internalJob, progress) => {\n await this.updateJobProgress(internalJob, progress);\n },\n onJobError: async (internalJob, error, document) => {\n // Log job errors\n logger.warn(\n `⚠️ Job ${internalJob.id} error ${document ? `on document ${document.url}` : \"\"}: ${error.message}`,\n );\n },\n });\n\n // If executeJob completes without throwing, and we weren't cancelled meanwhile...\n if (signal.aborted) {\n // Check signal again in case cancellation happened *during* the very last await in executeJob\n throw new CancellationError(\"Job cancelled just before completion\");\n }\n\n // Mark as completed\n await this.updateJobStatus(job, PipelineJobStatus.COMPLETED);\n job.finishedAt = new Date();\n job.resolveCompletion();\n\n logger.info(`✅ Job completed: ${jobId}`);\n } catch (error) {\n // Handle errors thrown by the worker, including CancellationError\n if (error instanceof CancellationError || signal.aborted) {\n // Explicitly check for CancellationError or if the signal was aborted\n await this.updateJobStatus(job, PipelineJobStatus.CANCELLED);\n job.finishedAt = new Date();\n // Don't set job.error for cancellations - cancellation is not an error condition\n const cancellationError =\n error instanceof CancellationError\n ? error\n : new CancellationError(\"Job cancelled by signal\");\n logger.info(`🚫 Job execution cancelled: ${jobId}: ${cancellationError.message}`);\n job.rejectCompletion(cancellationError);\n } else {\n // Handle other errors\n const errorMessage = error instanceof Error ? error.message : String(error);\n await this.updateJobStatus(job, PipelineJobStatus.FAILED, errorMessage);\n job.error = error instanceof Error ? error : new Error(String(error));\n job.finishedAt = new Date();\n logger.error(`❌ Job failed: ${jobId}: ${job.error}`);\n job.rejectCompletion(job.error);\n }\n } finally {\n // Ensure worker slot is freed and queue processing continues\n this.activeWorkers.delete(jobId);\n this._processQueue().catch((error) => {\n logger.error(`❌ Error in processQueue after job cleanup: ${error}`);\n });\n }\n }\n\n /**\n * Maps PipelineJobStatus to VersionStatus for database storage.\n */\n private mapJobStatusToVersionStatus(jobStatus: PipelineJobStatus): VersionStatus {\n switch (jobStatus) {\n case PipelineJobStatus.QUEUED:\n return VersionStatus.QUEUED;\n case PipelineJobStatus.RUNNING:\n return VersionStatus.RUNNING;\n case PipelineJobStatus.COMPLETED:\n return VersionStatus.COMPLETED;\n case PipelineJobStatus.FAILED:\n return VersionStatus.FAILED;\n case PipelineJobStatus.CANCELLED:\n return VersionStatus.CANCELLED;\n case PipelineJobStatus.CANCELLING:\n return VersionStatus.RUNNING; // Keep as running in DB until actually cancelled\n default:\n return VersionStatus.NOT_INDEXED;\n }\n }\n\n /**\n * Updates both in-memory job status and database version status (write-through).\n */\n private async updateJobStatus(\n job: InternalPipelineJob,\n newStatus: PipelineJobStatus,\n errorMessage?: string,\n ): Promise<void> {\n // Update in-memory status\n job.status = newStatus;\n if (errorMessage) {\n job.errorMessage = errorMessage;\n }\n job.updatedAt = new Date();\n\n // Update database status\n try {\n // Ensure the library and version exist and get the version ID\n const versionId = await this.store.ensureLibraryAndVersion(\n job.library,\n job.version,\n );\n\n // Update job object with database fields (single source of truth)\n job.versionId = versionId;\n job.versionStatus = this.mapJobStatusToVersionStatus(newStatus);\n\n const dbStatus = this.mapJobStatusToVersionStatus(newStatus);\n await this.store.updateVersionStatus(versionId, dbStatus, errorMessage);\n\n // Store scraper options when job is first queued\n if (newStatus === PipelineJobStatus.QUEUED && job.scraperOptions) {\n try {\n // Pass the complete scraper options (DocumentStore will filter runtime fields)\n await this.store.storeScraperOptions(versionId, job.scraperOptions);\n logger.debug(\n `Stored scraper options for ${job.library}@${job.version}: ${job.sourceUrl}`,\n );\n } catch (optionsError) {\n // Log warning but don't fail the job - options storage is not critical\n logger.warn(\n `⚠️ Failed to store scraper options for job ${job.id}: ${optionsError}`,\n );\n }\n }\n } catch (error) {\n logger.error(`❌ Failed to update database status for job ${job.id}: ${error}`);\n // Don't throw - we don't want to break the pipeline for database issues\n }\n\n // Emit events to EventBusService\n const publicJob = this.toPublicJob(job);\n\n this.eventBus.emit(EventType.JOB_STATUS_CHANGE, publicJob);\n this.eventBus.emit(EventType.LIBRARY_CHANGE, undefined);\n\n // Logging\n logger.debug(`Job ${job.id} status changed to: ${job.status}`);\n }\n\n /**\n * Updates both in-memory job progress and database progress (write-through).\n * Also emits progress events to the EventBusService.\n */\n async updateJobProgress(\n job: InternalPipelineJob,\n progress: ScraperProgressEvent,\n ): Promise<void> {\n // Update in-memory progress\n job.progress = progress;\n job.progressPages = progress.pagesScraped;\n job.progressMaxPages = progress.totalPages;\n job.updatedAt = new Date();\n\n // Update database progress if we have a version ID\n if (job.versionId) {\n try {\n await this.store.updateVersionProgress(\n job.versionId,\n progress.pagesScraped,\n progress.totalPages,\n );\n } catch (error) {\n logger.error(`❌ Failed to update database progress for job ${job.id}: ${error}`);\n // Don't throw - we don't want to break the pipeline for database issues\n }\n }\n\n // Emit progress event to EventBusService\n const publicJob = this.toPublicJob(job);\n\n this.eventBus.emit(EventType.JOB_PROGRESS, { job: publicJob, progress });\n\n // Logging\n logger.debug(\n `Job ${job.id} progress: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n }\n}\n","import type { EventBusService } from \"../events/EventBusService\";\nimport type { DocumentManagementService } from \"../store\";\nimport { logger } from \"../utils/logger\";\nimport { PipelineClient } from \"./PipelineClient\";\nimport { PipelineManager } from \"./PipelineManager\";\nimport type { IPipeline, PipelineOptions } from \"./trpc/interfaces\";\n\n/**\n * Factory for creating pipeline interfaces based on functionality requirements.\n */\nexport namespace PipelineFactory {\n /**\n * Creates the appropriate pipeline interface based on desired functionality.\n *\n * @param docService - Document management service instance\n * @param options - Pipeline configuration options\n * @returns Pipeline interface (PipelineManager or future PipelineClient)\n */\n // Overload: Local pipeline (in-process worker)\n export async function createPipeline(\n docService: DocumentManagementService,\n eventBus: EventBusService,\n options: Omit<PipelineOptions, \"serverUrl\">,\n ): Promise<PipelineManager>;\n // Overload: Remote pipeline client (out-of-process worker)\n export async function createPipeline(\n docService: undefined,\n eventBus: EventBusService,\n options: PipelineOptions & { serverUrl: string },\n ): Promise<PipelineClient>;\n // Implementation\n export async function createPipeline(\n docService: DocumentManagementService | undefined,\n eventBus: EventBusService | undefined,\n options: PipelineOptions,\n ): Promise<IPipeline> {\n const { recoverJobs = false, serverUrl, appConfig } = options;\n\n logger.debug(\n `Creating pipeline: recoverJobs=${recoverJobs}, serverUrl=${serverUrl || \"none\"}`,\n );\n\n if (serverUrl) {\n // External pipeline requested\n if (!eventBus) {\n throw new Error(\"Remote pipeline requires EventBusService\");\n }\n logger.debug(`Creating PipelineClient for external worker at: ${serverUrl}`);\n return new PipelineClient(serverUrl, eventBus);\n }\n\n // Local embedded pipeline with specified behavior\n if (!docService || !eventBus) {\n throw new Error(\n \"Local pipeline requires both DocumentManagementService and EventBusService\",\n );\n }\n\n return new PipelineManager(docService, eventBus, { recoverJobs, appConfig });\n }\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { AppServer } from \"../app\";\nimport type { IPipeline } from \"../pipeline\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { TelemetryService } from \"../telemetry\";\n\n// Module-level variables for active services\nlet activeAppServer: AppServer | null = null;\nlet activeMcpStdioServer: McpServer | null = null;\nlet activeDocService: IDocumentManagement | null = null;\nlet activePipelineManager: IPipeline | null = null;\nlet activeTelemetryService: TelemetryService | null = null;\n\nexport interface GlobalServices {\n appServer?: AppServer;\n mcpStdioServer?: McpServer;\n docService?: IDocumentManagement;\n pipeline?: IPipeline;\n telemetryService?: TelemetryService;\n}\n\n/**\n * Registers global services for shutdown handling\n */\nexport function registerGlobalServices(services: GlobalServices): void {\n if (services.appServer) activeAppServer = services.appServer;\n if (services.mcpStdioServer) activeMcpStdioServer = services.mcpStdioServer;\n if (services.docService) activeDocService = services.docService;\n if (services.pipeline) activePipelineManager = services.pipeline;\n if (services.telemetryService) activeTelemetryService = services.telemetryService;\n}\n\nexport function getActiveAppServer(): AppServer | null {\n return activeAppServer;\n}\n\nexport function setActiveAppServer(server: AppServer | null): void {\n activeAppServer = server;\n}\n\nexport function getActiveMcpStdioServer(): McpServer | null {\n return activeMcpStdioServer;\n}\n\nexport function setActiveMcpStdioServer(server: McpServer | null): void {\n activeMcpStdioServer = server;\n}\n\nexport function getActiveDocService(): IDocumentManagement | null {\n return activeDocService;\n}\n\nexport function setActiveDocService(service: IDocumentManagement | null): void {\n activeDocService = service;\n}\n\nexport function getActivePipelineManager(): IPipeline | null {\n return activePipelineManager;\n}\n\nexport function setActivePipelineManager(pipeline: IPipeline | null): void {\n activePipelineManager = pipeline;\n}\n\nexport function getActiveTelemetryService(): TelemetryService | null {\n return activeTelemetryService;\n}\n\nexport function setActiveTelemetryService(service: TelemetryService | null): void {\n activeTelemetryService = service;\n}\n","/**\n * Default command - Starts unified server when no subcommand is specified.\n */\n\nimport type { Argv } from \"yargs\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport { PipelineFactory, type PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { loadConfig } from \"../../utils/config\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { applyGlobalCliOutputMode } from \"../output\";\nimport { registerGlobalServices } from \"../services\";\nimport {\n type CliContext,\n createAppServerConfig,\n ensurePlaywrightBrowsersInstalled,\n getEventBus,\n parseAuthConfig,\n resolveProtocol,\n validateAuthConfig,\n warnHttpUsage,\n} from \"../utils\";\n\nexport function createDefaultAction(cli: Argv) {\n cli.command(\n [\"$0\", \"server\"],\n \"Starts the Docs MCP server (Unified Mode)\",\n (yargs) => {\n return (\n yargs\n .option(\"protocol\", {\n type: \"string\",\n description: \"Protocol for MCP server\",\n choices: [\"auto\", \"stdio\", \"http\"],\n default: \"auto\",\n })\n .option(\"port\", {\n type: \"string\", // Keep as string to match old behavior/validation, or number? Using string allows environment variable mapping via loadConfig if strict number parsing isn't desired immediately. Actually validation logic expects string often. But Yargs can parse number.\n description: \"Port for the server\",\n })\n .option(\"host\", {\n type: \"string\",\n description: \"Host to bind the server to\",\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"resume\", {\n type: \"boolean\",\n description: \"Resume interrupted jobs on startup\",\n default: false,\n })\n .option(\"read-only\", {\n type: \"boolean\",\n description:\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n default: false,\n alias: \"readOnly\",\n })\n // Auth options\n .option(\"auth-enabled\", {\n type: \"boolean\",\n description: \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n default: false,\n alias: \"authEnabled\",\n })\n .option(\"auth-issuer-url\", {\n type: \"string\",\n description: \"Issuer/discovery URL for OAuth2/OIDC provider\",\n alias: \"authIssuerUrl\",\n })\n .option(\"auth-audience\", {\n type: \"string\",\n description: \"JWT audience claim (identifies this protected resource)\",\n alias: \"authAudience\",\n })\n );\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"default\",\n protocol: argv.protocol,\n port: argv.port,\n host: argv.host,\n resume: argv.resume,\n readOnly: argv.readOnly,\n authEnabled: !!argv.authEnabled,\n });\n\n const resolvedProtocol = resolveProtocol(argv.protocol as string);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR);\n } else {\n applyGlobalCliOutputMode({\n verbose: argv.verbose as boolean,\n quiet: argv.quiet as boolean,\n });\n }\n\n logger.debug(\"No subcommand specified, starting unified server by default...\");\n\n // Validate inputs if provided, otherwise validation happens after config load?\n // Old logic validated options.port etc. but yargs parsing might be loose?\n // Since we don't have defaults in Yargs, argv.port might be undefined.\n // logic below uses loadConfig which fills defaults.\n // So validation should happen AFTER loadConfig on the RESULTING config?\n // OR we validate argv if present?\n // The old logic validated valid integers.\n // We will rely on Zod schema validation inside loadConfig.\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string,\n });\n\n // Propagate resolved store path? loadConfig logic handled it?\n // loadConfig takes argv, so it mapped `storePath` to `app.storePath`.\n // But `argv.storePath` was resolved by middleware in index.ts?\n // Yes. So appConfig has resolved path.\n\n // Parse and validate auth config\n const authConfig = parseAuthConfig({\n authEnabled: appConfig.auth.enabled,\n authIssuerUrl: appConfig.auth.issuerUrl,\n authAudience: appConfig.auth.audience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n warnHttpUsage(authConfig, appConfig.server.ports.default);\n }\n\n ensurePlaywrightBrowsersInstalled();\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService = await createLocalDocumentManagement(eventBus, appConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: (argv.resume as boolean) || false,\n appConfig: appConfig,\n };\n const pipeline = await PipelineFactory.createPipeline(\n docService,\n eventBus,\n pipelineOptions,\n );\n\n if (resolvedProtocol === \"stdio\") {\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n await pipeline.start();\n const mcpTools = await initializeTools(docService, pipeline, appConfig);\n const mcpServer = await startStdioServer(mcpTools, appConfig);\n\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {});\n } else {\n logger.debug(`Auto-detected http protocol (TTY available)`);\n const config = createAppServerConfig({\n enableWebInterface: true,\n enableMcpServer: true,\n enableApiServer: true,\n enableWorker: true,\n port: appConfig.server.ports.default,\n showLogo: argv.logo as boolean,\n startupContext: {\n cliCommand: \"default\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(\n docService,\n pipeline,\n eventBus,\n config,\n appConfig,\n );\n\n registerGlobalServices({\n appServer,\n docService,\n });\n\n await new Promise(() => {}); // Keep running\n }\n },\n );\n}\n","/**\n * Fetch URL command - Fetches a URL and converts its content to Markdown.\n */\n\nimport type { Argv } from \"yargs\";\nimport { AutoDetectFetcher } from \"../../scraper/fetcher\";\nimport { ScrapeMode } from \"../../scraper/types\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { FetchUrlTool } from \"../../tools\";\nimport { loadConfig } from \"../../utils/config\";\nimport { renderTextOutput } from \"../output\";\nimport { parseHeaders } from \"../utils\";\n\nexport function createFetchUrlCommand(cli: Argv) {\n cli.command(\n \"fetch-url <url>\",\n \"Fetch a URL and transform it into Markdown format\",\n (yargs) => {\n return yargs\n .positional(\"url\", {\n type: \"string\",\n description: \"URL to fetch\",\n demandOption: true,\n })\n .option(\"follow-redirects\", {\n type: \"boolean\",\n description: \"Follow HTTP redirects\",\n default: true,\n })\n .option(\"no-follow-redirects\", {\n type: \"boolean\",\n description: \"Disable following HTTP redirects\",\n hidden: true,\n })\n .option(\"scrape-mode\", {\n choices: Object.values(ScrapeMode),\n description: \"HTML processing strategy\",\n default: ScrapeMode.Auto,\n alias: \"scrapeMode\",\n })\n .option(\"header\", {\n type: \"string\",\n array: true,\n description:\n \"Custom HTTP header to send with the request (can be specified multiple times)\",\n default: [] as string[],\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"fetch-url\",\n url: argv.url,\n scrapeMode: argv.scrapeMode,\n followRedirects: argv.followRedirects,\n hasHeaders: (argv.header as string[])?.length > 0,\n });\n\n const url = argv.url as string;\n // parseHeaders expects string[]. Yargs array option gives string[] | undefined (if not default)\n // We set default: [] above.\n const headers = parseHeaders((argv.header as string[]) || []);\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally\n });\n\n const fetchUrlTool = new FetchUrlTool(\n new AutoDetectFetcher(appConfig.scraper),\n appConfig,\n );\n\n const content = await fetchUrlTool.execute({\n url,\n followRedirects: argv.followRedirects as boolean,\n scrapeMode: argv.scrapeMode as ScrapeMode,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n });\n\n renderTextOutput(content);\n },\n );\n}\n","/**\n * Find version command - Finds the best matching version for a library.\n */\n\nimport type { Argv } from \"yargs\";\nimport { createDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { FindVersionTool } from \"../../tools\";\nimport { loadConfig } from \"../../utils/config\";\nimport { renderStructuredOutput } from \"../output\";\nimport { type CliContext, getEventBus } from \"../utils\";\n\nexport function createFindVersionCommand(cli: Argv) {\n cli.command(\n \"find-version <library>\",\n \"Resolve and display the best matching documentation version for a library\",\n (yargs) => {\n return yargs\n .version(false)\n .positional(\"library\", {\n type: \"string\",\n description: \"Library name\",\n demandOption: true,\n })\n .option(\"version\", {\n type: \"string\",\n description: \"Pattern to match (optional, supports ranges)\",\n alias: \"v\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"find-version\",\n library: argv.library,\n version: argv.version,\n useServerUrl: !!argv.serverUrl,\n });\n\n const library = argv.library as string;\n const version = argv.version as string | undefined;\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n // Find version command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n try {\n const findVersionTool = new FindVersionTool(docService);\n\n // Call the tool directly - tracking is now handled inside the tool\n const versionInfo = await findVersionTool.execute({\n library,\n targetVersion: version,\n });\n\n if (!versionInfo) throw new Error(\"Failed to get version information\");\n renderStructuredOutput(versionInfo, argv);\n } finally {\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * List command - Lists all available libraries and their versions.\n */\n\nimport type { Argv } from \"yargs\";\nimport { createDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { ListLibrariesTool } from \"../../tools\";\nimport { loadConfig } from \"../../utils/config\";\nimport { renderStructuredOutput } from \"../output\";\nimport { type CliContext, getEventBus } from \"../utils\";\n\nexport function createListCommand(cli: Argv) {\n cli.command(\n \"list\",\n \"List all indexed libraries and their available versions\",\n (yargs) => {\n return yargs.option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"list\",\n useServerUrl: !!argv.serverUrl,\n });\n\n const serverUrl = argv.serverUrl as string | undefined;\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally in index.ts middleware\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n // List command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n eventBus,\n serverUrl,\n appConfig: appConfig,\n });\n try {\n const listLibrariesTool = new ListLibrariesTool(docService);\n\n // Call the tool directly - tracking is now handled inside the tool\n const result = await listLibrariesTool.execute();\n\n renderStructuredOutput(result.libraries, argv);\n } finally {\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * MCP command - Starts MCP server only.\n */\n\nimport type { Argv } from \"yargs\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport { PipelineFactory, type PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement, type DocumentManagementService } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { loadConfig } from \"../../utils/config\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { applyGlobalCliOutputMode } from \"../output\";\nimport { registerGlobalServices } from \"../services\";\nimport {\n type CliContext,\n createAppServerConfig,\n getEventBus,\n parseAuthConfig,\n resolveProtocol,\n validateAuthConfig,\n validatePort,\n} from \"../utils\";\n\nexport function createMcpCommand(cli: Argv) {\n cli.command(\n \"mcp\",\n \"Start the MCP server (Standalone Mode)\",\n (yargs) => {\n return (\n yargs\n .option(\"protocol\", {\n type: \"string\",\n description: \"Protocol for MCP server\",\n choices: [\"auto\", \"stdio\", \"http\"],\n default: \"auto\",\n })\n .option(\"port\", {\n type: \"string\",\n description: \"Port for the MCP server\",\n })\n .option(\"host\", {\n type: \"string\",\n description: \"Host to bind the MCP server to\",\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n })\n .option(\"read-only\", {\n type: \"boolean\",\n description:\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n default: false,\n alias: \"readOnly\",\n })\n // Auth options\n .option(\"auth-enabled\", {\n type: \"boolean\",\n description: \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n default: false,\n alias: \"authEnabled\",\n })\n .option(\"auth-issuer-url\", {\n type: \"string\",\n description: \"Issuer/discovery URL for OAuth2/OIDC provider\",\n alias: \"authIssuerUrl\",\n })\n .option(\"auth-audience\", {\n type: \"string\",\n description: \"JWT audience claim (identifies this protected resource)\",\n alias: \"authAudience\",\n })\n );\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"mcp\",\n protocol: argv.protocol,\n port: argv.port,\n host: argv.host,\n useServerUrl: !!argv.serverUrl,\n readOnly: argv.readOnly,\n authEnabled: !!argv.authEnabled,\n });\n\n const _port = validatePort((argv.port as string) || \"6280\"); // fallback for validation if undefined, but loadConfig handles defaults.\n // Wait, validatePort throws if invalid. If undefined, we should rely on loadConfig.\n // Current logic calls validatePort(cmdOptions.port). If undefined, what happens?\n // In Yargs, if no default, argv.port is undefined.\n // validatePort(undefined) -> depends on impl. It expects string.\n // I should modify validation or defer it.\n // loadConfig will fill default.\n // So I should load config FIRST.\n const resolvedProtocol = resolveProtocol(argv.protocol as string);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR);\n } else {\n applyGlobalCliOutputMode({\n verbose: argv.verbose as boolean,\n quiet: argv.quiet as boolean,\n });\n }\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolvedStorePath passed via argv by middleware\n });\n\n // Now we have appConfig with defaults.\n // Validate resolved values?\n // validatePort(appConfig.server.ports.mcp.toString());\n // The old code validated CLI input explicitly?\n // Yes. I will validate from appConfig.\n\n // Parse and validate auth configuration\n const authConfig = parseAuthConfig({\n authEnabled: appConfig.auth.enabled,\n authIssuerUrl: appConfig.auth.issuerUrl,\n authAudience: appConfig.auth.audience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n }\n\n try {\n const serverUrl = argv.serverUrl as string | undefined;\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // MCP command doesn't support job recovery\n serverUrl,\n appConfig: appConfig,\n };\n const pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, eventBus, {\n serverUrl,\n ...pipelineOptions,\n })\n : await PipelineFactory.createPipeline(\n docService as DocumentManagementService,\n eventBus,\n pipelineOptions,\n );\n\n if (resolvedProtocol === \"stdio\") {\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n await pipeline.start();\n const mcpTools = await initializeTools(docService, pipeline, appConfig);\n const mcpServer = await startStdioServer(mcpTools, appConfig);\n\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {});\n } else {\n logger.debug(`Auto-detected http protocol (TTY available)`);\n const config = createAppServerConfig({\n enableWebInterface: false,\n enableMcpServer: true,\n enableApiServer: false,\n enableWorker: !serverUrl,\n port: appConfig.server.ports.mcp,\n externalWorkerUrl: serverUrl,\n showLogo: argv.logo as boolean,\n startupContext: {\n cliCommand: \"mcp\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(\n docService,\n pipeline,\n eventBus,\n config,\n appConfig,\n );\n\n registerGlobalServices({\n appServer,\n docService,\n });\n\n await new Promise(() => {});\n }\n } catch (error) {\n logger.error(`❌ Failed to start MCP server: ${error}`);\n process.exit(1);\n }\n },\n );\n}\n","/**\n * Refresh command - Re-scrapes an existing library version using ETags to skip unchanged pages.\n */\n\nimport type { Argv } from \"yargs\";\nimport { EventType } from \"../../events\";\nimport { PipelineFactory, PipelineJobStatus, type PipelineOptions } from \"../../pipeline\";\nimport type { IPipeline } from \"../../pipeline/trpc/interfaces\";\nimport { createDocumentManagement, type DocumentManagementService } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { RefreshVersionTool } from \"../../tools/RefreshVersionTool\";\nimport { loadConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { renderTextOutput } from \"../output\";\nimport { type CliContext, getEventBus } from \"../utils\";\n\nexport function createRefreshCommand(cli: Argv) {\n cli.command(\n \"refresh <library>\",\n \"Update an existing library version by re-scraping changed pages\",\n (yargs) => {\n return yargs\n .version(false)\n .positional(\"library\", {\n type: \"string\",\n description: \"Library name to refresh\",\n demandOption: true,\n })\n .option(\"version\", {\n type: \"string\",\n description: \"Version of the library (optional)\",\n alias: \"v\",\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n })\n .usage(\n \"$0 refresh <library> [options]\\n\\n\" +\n \"Uses HTTP ETags to efficiently skip unchanged pages and only re-process\\n\" +\n \"content that has been modified or deleted since the last scrape.\\n\\n\" +\n \"Examples:\\n\" +\n \" refresh react --version 18.0.0\\n\" +\n \" refresh mylib\\n\" +\n \"\\nNote: The library and version must already be indexed. Use 'scrape' to index a new library/version.\",\n );\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"refresh\",\n library: argv.library,\n version: argv.version,\n useServerUrl: !!argv.serverUrl,\n });\n\n const library = argv.library as string;\n const version = argv.version as string | undefined;\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string,\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n let pipeline: IPipeline | null = null;\n\n // Display initial status\n logger.info(\"⏳ Initializing refresh job...\");\n\n // Subscribe to event bus for progress updates (only for local pipelines)\n let unsubscribeProgress: (() => void) | null = null;\n let unsubscribeStatus: (() => void) | null = null;\n\n if (!serverUrl) {\n unsubscribeProgress = eventBus.on(EventType.JOB_PROGRESS, (event) => {\n const { job, progress } = event;\n logger.info(\n `📄 Refreshing ${job.library}${job.version ? ` v${job.version}` : \"\"}: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n });\n\n unsubscribeStatus = eventBus.on(EventType.JOB_STATUS_CHANGE, (event) => {\n if (event.status === PipelineJobStatus.RUNNING) {\n logger.info(\n `🚀 Refreshing ${event.library}${event.version ? ` v${event.version}` : \"\"}...`,\n );\n }\n });\n }\n\n try {\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false,\n serverUrl,\n appConfig: appConfig,\n };\n\n pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, eventBus, {\n serverUrl,\n ...pipelineOptions,\n })\n : await PipelineFactory.createPipeline(\n docService as DocumentManagementService,\n eventBus,\n pipelineOptions,\n );\n\n await pipeline.start();\n const refreshTool = new RefreshVersionTool(pipeline);\n\n // Call the tool directly - tracking is now handled inside the tool\n const result = await refreshTool.execute({\n library,\n version,\n waitForCompletion: true, // Always wait for completion in CLI\n });\n\n if (\"pagesRefreshed\" in result) {\n renderTextOutput(`Successfully refreshed ${result.pagesRefreshed} pages`);\n } else {\n renderTextOutput(`Refresh job started with ID: ${result.jobId}`);\n }\n } catch (error) {\n logger.error(\n `❌ Refresh failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n } finally {\n // Clean up event listeners\n if (unsubscribeProgress) unsubscribeProgress();\n if (unsubscribeStatus) unsubscribeStatus();\n\n if (pipeline) await pipeline.stop();\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * Remove command - Removes documents for a specific library and version.\n */\n\nimport type { Argv } from \"yargs\";\nimport { createDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { loadConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { renderTextOutput } from \"../output\";\nimport { type CliContext, getEventBus } from \"../utils\";\n\nexport function createRemoveCommand(cli: Argv) {\n cli.command(\n \"remove <library>\",\n \"Delete a library's documentation from the index\",\n (yargs) => {\n return yargs\n .version(false)\n .positional(\"library\", {\n type: \"string\",\n description: \"Library name to remove\",\n demandOption: true,\n })\n .option(\"version\", {\n type: \"string\",\n description: \"Version to remove (optional, removes latest if omitted)\",\n alias: \"v\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"remove\",\n library: argv.library,\n version: argv.version,\n useServerUrl: !!argv.serverUrl,\n });\n\n const library = argv.library as string;\n const version = argv.version as string | undefined;\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n // Remove command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n try {\n // Call the document service directly - we could convert this to use RemoveTool if needed\n await docService.removeAllDocuments(library, version);\n\n renderTextOutput(\n `Successfully removed ${library}${version ? `@${version}` : \"\"}.`,\n );\n } catch (error) {\n logger.error(\n `❌ Failed to remove ${library}${version ? `@${version}` : \"\"}: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n } finally {\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * Scrape command - Scrapes and indexes documentation from a URL or local folder.\n */\n\nimport type { Argv } from \"yargs\";\nimport { EventType } from \"../../events\";\nimport { PipelineFactory, PipelineJobStatus, type PipelineOptions } from \"../../pipeline\";\nimport type { IPipeline } from \"../../pipeline/trpc/interfaces\";\nimport { ScrapeMode } from \"../../scraper/types\";\nimport { createDocumentManagement } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { ScrapeTool } from \"../../tools\";\nimport { loadConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { renderTextOutput } from \"../output\";\nimport { type CliContext, getEventBus, parseHeaders } from \"../utils\";\n\nexport function createScrapeCommand(cli: Argv) {\n cli.command(\n \"scrape <library> <url>\",\n \"Download and index documentation from a URL or local directory\",\n (yargs) => {\n return yargs\n .version(false)\n .positional(\"library\", {\n type: \"string\",\n description: \"Library name\",\n demandOption: true,\n })\n .positional(\"url\", {\n type: \"string\",\n description: \"URL or file:// path to scrape\",\n demandOption: true,\n })\n .option(\"version\", {\n type: \"string\",\n description: \"Version of the library (optional)\",\n alias: \"v\",\n })\n .option(\"max-pages\", {\n type: \"number\",\n description: \"Maximum pages to scrape\",\n alias: [\"p\", \"maxPages\"],\n })\n .option(\"max-depth\", {\n type: \"number\",\n description: \"Maximum navigation depth\",\n alias: [\"d\", \"maxDepth\"],\n })\n .option(\"max-concurrency\", {\n type: \"number\",\n description: \"Maximum concurrent page requests\",\n alias: [\"c\", \"maxConcurrency\"],\n })\n .option(\"ignore-errors\", {\n type: \"boolean\",\n description: \"Ignore errors during scraping\",\n default: true,\n alias: \"ignoreErrors\",\n })\n .option(\"scope\", {\n choices: [\"subpages\", \"hostname\", \"domain\"],\n description: \"Crawling boundary\",\n default: \"subpages\",\n })\n .option(\"follow-redirects\", {\n type: \"boolean\",\n description: \"Follow HTTP redirects\",\n default: true,\n alias: \"followRedirects\",\n })\n .option(\"no-follow-redirects\", {\n type: \"boolean\",\n description: \"Disable following HTTP redirects\",\n hidden: true,\n })\n .option(\"scrape-mode\", {\n choices: Object.values(ScrapeMode),\n description: \"HTML processing strategy\",\n default: ScrapeMode.Auto,\n alias: \"scrapeMode\",\n })\n .option(\"include-pattern\", {\n type: \"string\",\n array: true,\n description:\n \"Glob or regex pattern for URLs to include (can be specified multiple times). Regex patterns must be wrapped in slashes, e.g. /pattern/.\",\n alias: \"includePattern\",\n default: [] as string[],\n })\n .option(\"exclude-pattern\", {\n type: \"string\",\n array: true,\n description:\n \"Glob or regex pattern for URLs to exclude (can be specified multiple times, takes precedence over include). Regex patterns must be wrapped in slashes, e.g. /pattern/.\",\n alias: \"excludePattern\",\n default: [] as string[],\n })\n .option(\"header\", {\n type: \"string\",\n array: true,\n description:\n \"Custom HTTP header to send with each request (can be specified multiple times)\",\n default: [] as string[],\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n })\n .option(\"clean\", {\n type: \"boolean\",\n description: \"Clear existing documents before scraping\",\n default: true,\n })\n .usage(\n \"$0 scrape <library> <url> [options]\\n\\n\" +\n \"Scrape and index documentation from a URL or local folder.\\n\\n\" +\n \"To scrape local files or folders, use a file:// URL.\\n\" +\n \"Examples:\\n\" +\n \" scrape mylib https://react.dev/reference/react\\n\" +\n \" scrape mylib file:///Users/me/docs/index.html\\n\" +\n \" scrape mylib file:///Users/me/docs/my-library\\n\" +\n \"\\nNote: For local files/folders, you must use the file:// prefix. If running in Docker, mount the folder and use the container path. See README for details.\",\n );\n },\n async (argv) => {\n const library = argv.library as string;\n const url = argv.url as string;\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally\n });\n\n const maxPages = (argv.maxPages as number) ?? appConfig.scraper.maxPages;\n const maxDepth = (argv.maxDepth as number) ?? appConfig.scraper.maxDepth;\n const maxConcurrency =\n (argv.maxConcurrency as number) ?? appConfig.scraper.maxConcurrency;\n\n // Update appConfig with effective values\n appConfig.scraper.maxPages = maxPages;\n appConfig.scraper.maxDepth = maxDepth;\n appConfig.scraper.maxConcurrency = maxConcurrency;\n\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"scrape\",\n library,\n version: argv.version,\n url,\n maxPages,\n maxDepth,\n maxConcurrency,\n scope: argv.scope,\n scrapeMode: argv.scrapeMode,\n followRedirects: argv.followRedirects,\n hasHeaders: (argv.header as string[]).length > 0,\n hasIncludePatterns: (argv.includePattern as string[]).length > 0,\n hasExcludePatterns: (argv.excludePattern as string[]).length > 0,\n useServerUrl: !!serverUrl,\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n let pipeline: IPipeline | null = null;\n\n // Display initial status\n logger.info(\"⏳ Initializing scraping job...\");\n\n // Subscribe to event bus for progress updates (only for local pipelines)\n let unsubscribeProgress: (() => void) | null = null;\n let unsubscribeStatus: (() => void) | null = null;\n\n if (!serverUrl) {\n unsubscribeProgress = eventBus.on(EventType.JOB_PROGRESS, (event) => {\n const { job, progress } = event;\n logger.info(\n `📄 Scraping ${job.library}${job.version ? ` v${job.version}` : \"\"}: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n });\n\n unsubscribeStatus = eventBus.on(EventType.JOB_STATUS_CHANGE, (event) => {\n if (event.status === PipelineJobStatus.RUNNING) {\n logger.info(\n `🚀 Scraping ${event.library}${event.version ? ` v${event.version}` : \"\"}...`,\n );\n }\n });\n }\n\n try {\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false,\n serverUrl,\n appConfig: appConfig,\n };\n\n pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, eventBus, {\n serverUrl,\n ...pipelineOptions,\n })\n : await PipelineFactory.createPipeline(\n docService as unknown as never,\n eventBus,\n pipelineOptions,\n );\n\n await pipeline.start();\n const scrapeTool = new ScrapeTool(pipeline, appConfig.scraper);\n\n const headers = parseHeaders((argv.header as string[]) || []);\n\n const result = await scrapeTool.execute({\n url,\n library,\n version: argv.version as string | undefined,\n options: {\n maxPages,\n maxDepth,\n maxConcurrency,\n ignoreErrors: argv.ignoreErrors as boolean,\n scope: argv.scope as \"subpages\" | \"hostname\" | \"domain\",\n followRedirects: argv.followRedirects as boolean,\n scrapeMode: argv.scrapeMode as ScrapeMode,\n includePatterns:\n (argv.includePattern as string[])?.length > 0\n ? (argv.includePattern as string[])\n : undefined,\n excludePatterns:\n (argv.excludePattern as string[])?.length > 0\n ? (argv.excludePattern as string[])\n : undefined,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n clean: argv.clean as boolean,\n },\n });\n\n if (\"pagesScraped\" in result) {\n renderTextOutput(`Successfully scraped ${result.pagesScraped} pages`);\n } else {\n renderTextOutput(`Scraping job started with ID: ${result.jobId}`);\n }\n } catch (error) {\n logger.error(\n `❌ Scraping failed: ${error instanceof Error ? error.message : String(error)}`,\n );\n throw error;\n } finally {\n if (unsubscribeProgress) unsubscribeProgress();\n if (unsubscribeStatus) unsubscribeStatus();\n\n if (pipeline) await pipeline.stop();\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * Search command - Searches documents in a library.\n */\n\nimport type { Argv } from \"yargs\";\nimport { createDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { SearchTool } from \"../../tools\";\nimport { loadConfig } from \"../../utils/config\";\nimport { renderStructuredOutput } from \"../output\";\nimport { type CliContext, getEventBus } from \"../utils\";\n\nexport function createSearchCommand(cli: Argv) {\n cli.command(\n \"search <library> <query>\",\n \"Query the documentation index used by the MCP server\",\n (yargs) => {\n return yargs\n .version(false)\n .positional(\"library\", {\n type: \"string\",\n description: \"Library name\",\n demandOption: true,\n })\n .positional(\"query\", {\n type: \"string\",\n description: \"Search query\",\n demandOption: true,\n })\n .option(\"version\", {\n type: \"string\",\n description: \"Version of the library (optional, supports ranges)\",\n alias: \"v\",\n })\n .option(\"limit\", {\n type: \"number\",\n description: \"Maximum number of results\",\n alias: \"l\",\n default: 5,\n })\n .option(\"exact-match\", {\n type: \"boolean\",\n description: \"Only use exact version match\",\n default: false,\n alias: [\"e\", \"exactMatch\"],\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n })\n .usage(\n \"$0 search <library> <query> [options]\\n\\n\" +\n \"Search documents in a library. Version matching examples:\\n\" +\n \" - search react --version 18.0.0 'hooks' -> matches docs for React 18.0.0 or earlier versions\\n\" +\n \" - search react --version 18.0.0 'hooks' --exact-match -> only matches React 18.0.0\\n\" +\n \" - search typescript --version 5.x 'types' -> matches any TypeScript 5.x.x version\\n\" +\n \" - search typescript --version 5.2.x 'types' -> matches any TypeScript 5.2.x version\",\n );\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"search\",\n library: argv.library,\n version: argv.version,\n query: argv.query,\n limit: argv.limit,\n exactMatch: argv.exactMatch,\n useServerUrl: !!argv.serverUrl,\n });\n\n const library = argv.library as string;\n const query = argv.query as string;\n const limit = argv.limit as number;\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally\n });\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n\n try {\n const searchTool = new SearchTool(docService);\n\n // Call the tool directly - tracking is now handled inside the tool\n const result = await searchTool.execute({\n library,\n version: argv.version as string | undefined,\n query,\n limit,\n exactMatch: argv.exactMatch as boolean,\n });\n\n renderStructuredOutput(result.results, argv);\n } finally {\n await docService.shutdown();\n }\n },\n );\n}\n","/**\n * Web command - Starts web interface only.\n */\n\nimport type { Argv } from \"yargs\";\nimport { startAppServer } from \"../../app\";\nimport { PipelineFactory, type PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement, type DocumentManagementService } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { loadConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../services\";\nimport {\n type CliContext,\n createAppServerConfig,\n getEventBus,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWebCommand(cli: Argv) {\n cli.command(\n \"web\",\n \"Start the web dashboard (Standalone Mode)\",\n (yargs) => {\n return yargs\n .option(\"port\", {\n type: \"string\",\n description: \"Port for the web interface\",\n })\n .option(\"host\", {\n type: \"string\",\n description: \"Host to bind the web interface to\",\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"server-url\", {\n type: \"string\",\n description:\n \"URL of external pipeline worker RPC (e.g., http://localhost:8080/api)\",\n alias: \"serverUrl\",\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"web\",\n port: argv.port,\n host: argv.host,\n useServerUrl: !!argv.serverUrl,\n });\n\n const _port = validatePort((argv.port as string) || \"6281\"); // Fallback for validation, defaults via loadConfig\n const _host = validateHost((argv.host as string) || \"127.0.0.1\");\n const serverUrl = argv.serverUrl as string | undefined;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n // searchDir resolved via globalStorePath in index.ts -> available in appConfig?\n // loadConfig needs options to find file.\n searchDir: argv.storePath as string,\n });\n\n try {\n const eventBus = getEventBus(argv as CliContext);\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n eventBus,\n appConfig: appConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // Web command doesn't support job recovery\n serverUrl,\n appConfig: appConfig,\n };\n const pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, eventBus, {\n serverUrl,\n ...pipelineOptions,\n })\n : await PipelineFactory.createPipeline(\n docService as DocumentManagementService,\n eventBus,\n pipelineOptions,\n );\n\n // Configure web-only server\n const config = createAppServerConfig({\n enableWebInterface: true,\n enableMcpServer: false,\n enableApiServer: false,\n enableWorker: !serverUrl,\n port: appConfig.server.ports.web,\n externalWorkerUrl: serverUrl,\n showLogo: argv.logo as boolean,\n startupContext: {\n cliCommand: \"web\",\n },\n });\n\n const appServer = await startAppServer(\n docService,\n pipeline,\n eventBus,\n config,\n appConfig,\n );\n\n registerGlobalServices({\n appServer,\n docService,\n });\n\n await new Promise(() => {}); // Keep running forever\n } catch (error) {\n logger.error(`❌ Failed to start web interface: ${error}`);\n process.exit(1);\n }\n },\n );\n}\n","/**\n * Worker command - Starts external pipeline worker (HTTP API).\n */\n\nimport type { Argv } from \"yargs\";\nimport { startAppServer } from \"../../app\";\nimport { PipelineFactory, type PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { TelemetryEvent, telemetry } from \"../../telemetry\";\nimport { loadConfig } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../services\";\nimport {\n type CliContext,\n createAppServerConfig,\n ensurePlaywrightBrowsersInstalled,\n getEventBus,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWorkerCommand(cli: Argv) {\n cli.command(\n \"worker\",\n \"Start a background worker for processing scraping jobs\",\n (yargs) => {\n return yargs\n .option(\"port\", {\n type: \"string\",\n description: \"Port for worker API\",\n })\n .option(\"host\", {\n type: \"string\",\n description: \"Host to bind the worker API to\",\n })\n .option(\"embedding-model\", {\n type: \"string\",\n description:\n \"Embedding model configuration (e.g., 'openai:text-embedding-3-small')\",\n alias: \"embeddingModel\",\n })\n .option(\"resume\", {\n type: \"boolean\",\n description: \"Resume interrupted jobs on startup\",\n default: true,\n })\n .option(\"no-resume\", {\n type: \"boolean\",\n // Yargs handles boolean flags specially, --no-resume implies resume=false\n // But strict mode might complain if we don't define 'resume'\n // 'resume' defaulting to true handles --no-resume correctly in Yargs\n hidden: true,\n });\n },\n async (argv) => {\n await telemetry.track(TelemetryEvent.CLI_COMMAND, {\n command: \"worker\",\n port: argv.port,\n host: argv.host,\n resume: argv.resume,\n });\n\n const _port = validatePort((argv.port as string) || \"8080\");\n const _host = validateHost((argv.host as string) || \"127.0.0.1\");\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: argv.storePath as string, // resolved globally in index.ts middleware\n });\n\n try {\n // Ensure browsers are installed for scraping\n ensurePlaywrightBrowsersInstalled();\n\n const eventBus = getEventBus(argv as CliContext);\n\n const docService = await createLocalDocumentManagement(eventBus, appConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: (argv.resume as boolean) ?? true,\n appConfig: appConfig,\n };\n const pipeline = await PipelineFactory.createPipeline(\n docService,\n eventBus,\n pipelineOptions,\n );\n\n // Configure worker-only server\n const config = createAppServerConfig({\n enableWebInterface: false,\n enableMcpServer: false,\n enableApiServer: true,\n enableWorker: true,\n port: appConfig.server.ports.worker,\n showLogo: argv.logo as boolean,\n startupContext: {\n cliCommand: \"worker\",\n },\n });\n\n const appServer = await startAppServer(\n docService,\n pipeline,\n eventBus,\n config,\n appConfig,\n );\n\n registerGlobalServices({\n appServer,\n docService,\n });\n\n await new Promise(() => {}); // Keep running forever\n } catch (error) {\n logger.error(`❌ Failed to start external pipeline worker: ${error}`);\n process.exit(1);\n }\n },\n );\n}\n","/**\n * Main CLI setup and command registration using Yargs.\n */\n\nimport yargs, { type Argv } from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\nimport { EventBusService } from \"../events\";\nimport {\n initTelemetry,\n shouldEnableTelemetry,\n TelemetryService,\n telemetry,\n} from \"../telemetry\";\nimport { loadConfig } from \"../utils/config\";\nimport { resolveStorePath } from \"../utils/paths\";\n// Commands\nimport { createConfigCommand } from \"./commands/config\";\nimport { createDefaultAction } from \"./commands/default\";\nimport { createFetchUrlCommand } from \"./commands/fetchUrl\";\nimport { createFindVersionCommand } from \"./commands/findVersion\";\nimport { createListCommand } from \"./commands/list\";\nimport { createMcpCommand } from \"./commands/mcp\";\nimport { createRefreshCommand } from \"./commands/refresh\";\nimport { createRemoveCommand } from \"./commands/remove\";\nimport { createScrapeCommand } from \"./commands/scrape\";\nimport { createSearchCommand } from \"./commands/search\";\nimport { createWebCommand } from \"./commands/web\";\nimport { createWorkerCommand } from \"./commands/worker\";\nimport { applyGlobalCliOutputMode, registerGlobalOutputOptions } from \"./output\";\nimport { registerGlobalServices } from \"./services\";\n\n/**\n * Creates and configures the main CLI program with all commands.\n */\n\n/**\n * Creates and configures the main CLI program with all commands.\n */\nexport function createCli(argv: string[]): Argv {\n // Global service instances\n let globalEventBus: EventBusService | null = null;\n let globalTelemetryService: TelemetryService | null = null;\n const commandStartTimes = new Map<string, number>();\n\n const cli = registerGlobalOutputOptions(yargs(hideBin(argv)))\n .scriptName(\"docs-mcp-server\")\n .strict()\n .usage(\"Usage: $0 <command> [options]\")\n .version(__APP_VERSION__)\n // Global Options\n .option(\"verbose\", {\n type: \"boolean\",\n description: \"Enable verbose (debug) logging\",\n default: false,\n })\n .option(\"quiet\", {\n type: \"boolean\",\n description: \"Disable all logging except errors\",\n default: false,\n alias: [\"silent\"],\n })\n .option(\"telemetry\", {\n type: \"boolean\",\n description: \"Enable/disable telemetry collection\",\n // yargs handles boolean logic for --no-telemetry automatically if strictly typed\n // but we want tri-state or env var handling.\n // Yargs doesn't naturally do \"default: true, but respecting env var DOCS_MCP_TELEMETRY\"\n // without middleware overriding.\n default: undefined, // Let config loader handle defaults\n })\n .option(\"store-path\", {\n type: \"string\",\n description: \"Custom path for data storage directory\",\n alias: \"storePath\",\n })\n .option(\"config\", {\n type: \"string\",\n description: \"Path to configuration file\",\n })\n .option(\"logo\", {\n type: \"boolean\",\n description: \"Show ASCII art logo on startup\",\n default: true,\n })\n // Middleware for Global Setup (similar to preAction)\n .middleware(async (argv) => {\n // 0. Validate Options\n if (argv.verbose && argv.quiet) {\n throw new Error(\"Arguments verbose and quiet are mutually exclusive\");\n }\n\n // 1. Load Config & Resolve Paths\n const rawStorePath = (argv.storePath as string) || process.env.DOCS_MCP_STORE_PATH;\n const resolvedStorePath = resolveStorePath(rawStorePath);\n\n // Mutate argv to use resolved path\n argv.storePath = resolvedStorePath;\n\n const appConfig = loadConfig(argv, {\n configPath: argv.config as string,\n searchDir: resolvedStorePath,\n });\n\n // Update options with config values if not set by CLI\n if (argv.telemetry === undefined) {\n argv.telemetry = appConfig.app.telemetryEnabled;\n }\n\n // 2. Setup Logging\n applyGlobalCliOutputMode({\n verbose: argv.verbose as boolean,\n quiet: argv.quiet as boolean,\n });\n\n // 3. Init Telemetry\n initTelemetry({\n enabled: !!argv.telemetry,\n storePath: resolvedStorePath,\n });\n\n // 4. Init Services\n if (!globalEventBus) {\n globalEventBus = new EventBusService();\n }\n if (!globalTelemetryService) {\n globalTelemetryService = new TelemetryService(globalEventBus);\n registerGlobalServices({ telemetryService: globalTelemetryService });\n }\n\n // 5. Attach to argv context\n // This makes global services available to all commands\n argv._eventBus = globalEventBus;\n\n // 6. Telemetry Context\n if (shouldEnableTelemetry() && telemetry.isEnabled()) {\n const commandName = argv._[0]?.toString() || \"default\";\n telemetry.setGlobalContext({\n appVersion: __APP_VERSION__,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appInterface: \"cli\",\n cliCommand: commandName,\n });\n\n const commandKey = `${commandName}-${Date.now()}`;\n commandStartTimes.set(commandKey, Date.now());\n argv._trackingKey = commandKey;\n }\n })\n // Post-command tracking is handled by yargs 'onFinishCommand' or similar?\n // Yargs doesn't have a direct 'postAction' hook for all commands easily.\n // We can handle it in the handler wrapper or use 'onFinishCommand' if available (it isn't).\n // An alternative is using middleware that runs after? No.\n // We will rely on explicit telemetry calls in command handlers or wrap usage.\n // However, existing `main.ts` handles cleanup.\n // We can add a global `finish` logic if needed.\n // For now, we'll keep the start time tracking here, but actual completion tracking might need to be in handlers.\n // BUT legacy code had `postAction` hook for `CLI_COMMAND` event.\n // We can simulate this by wrapping commands or using `cli.on('finish', ...)`?\n // Yargs doesn't emit finish events.\n // We might need to move the `CLI_COMMAND` tracking into `main.ts` or inside `createDefaultAction`.\n\n .alias(\"help\", \"h\")\n .showHelpOnFail(true);\n\n // Register Commands\n createConfigCommand(cli);\n createDefaultAction(cli);\n createFetchUrlCommand(cli);\n createFindVersionCommand(cli);\n createListCommand(cli);\n createMcpCommand(cli);\n createRefreshCommand(cli);\n createRemoveCommand(cli);\n createScrapeCommand(cli);\n createSearchCommand(cli);\n createWebCommand(cli);\n createWorkerCommand(cli);\n\n return cli;\n}\n","/**\n * CLI main entry point with global shutdown and error handling.\n * Analytics is initialized immediately when imported for proper telemetry across all services.\n */\n\nimport {\n ModelConfigurationError,\n UnsupportedProviderError,\n} from \"../store/embeddings/EmbeddingFactory\";\nimport { telemetry } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { createCli } from \"./index\";\n\n// Module-level variables are now in ./services.ts\nimport {\n getActiveAppServer,\n getActiveDocService,\n getActiveMcpStdioServer,\n getActivePipelineManager,\n getActiveTelemetryService,\n setActiveAppServer,\n setActiveDocService,\n setActiveMcpStdioServer,\n setActivePipelineManager,\n setActiveTelemetryService,\n} from \"./services\";\n\nlet isShuttingDown = false;\n\n/**\n * Graceful shutdown handler for SIGINT\n */\nconst sigintHandler = async (): Promise<void> => {\n if (isShuttingDown) return;\n isShuttingDown = true;\n\n logger.debug(\"Received SIGINT. Shutting down gracefully...\");\n\n try {\n const appServer = getActiveAppServer();\n if (appServer) {\n logger.debug(\"SIGINT: Stopping AppServer...\");\n await appServer.stop();\n setActiveAppServer(null);\n logger.debug(\"SIGINT: AppServer stopped.\");\n }\n\n const mcpServer = getActiveMcpStdioServer();\n if (mcpServer) {\n logger.debug(\"SIGINT: Stopping MCP server...\");\n await mcpServer.close();\n setActiveMcpStdioServer(null);\n logger.debug(\"SIGINT: MCP server stopped.\");\n }\n\n // Shutdown active services\n logger.debug(\"SIGINT: Shutting down active services...\");\n // Only shutdown pipeline if not managed by AppServer (e.g., in stdio mode)\n const pipeline = getActivePipelineManager();\n if (pipeline && !appServer) {\n await pipeline.stop();\n setActivePipelineManager(null);\n logger.debug(\"SIGINT: PipelineManager stopped.\");\n }\n\n const docService = getActiveDocService();\n if (docService) {\n await docService.shutdown();\n setActiveDocService(null);\n logger.debug(\"SIGINT: DocumentManagementService shut down.\");\n }\n\n // Cleanup TelemetryService (removes event listeners)\n const telemetryService = getActiveTelemetryService();\n if (telemetryService) {\n telemetryService.shutdown();\n setActiveTelemetryService(null);\n logger.debug(\"SIGINT: TelemetryService shut down.\");\n }\n\n // Analytics shutdown is handled by AppServer.stop() above\n // Only shutdown analytics if no AppServer was running\n if (!appServer && telemetry.isEnabled()) {\n await telemetry.shutdown();\n logger.debug(\"SIGINT: Analytics shut down.\");\n }\n\n logger.info(\"✅ Graceful shutdown completed\");\n process.exit(0);\n } catch (error) {\n logger.error(`❌ Error during graceful shutdown: ${error}`);\n process.exit(1);\n }\n};\n\n/**\n * Performs cleanup for CLI commands that don't start long-running services.\n * This ensures proper analytics shutdown and process exit to prevent hanging.\n */\nexport async function cleanupCliCommand(): Promise<void> {\n if (!isShuttingDown) {\n logger.debug(\"CLI command executed. Cleaning up...\");\n\n // Remove SIGINT handler since command completed successfully\n process.removeListener(\"SIGINT\", sigintHandler);\n\n // Shutdown analytics for non-server CLI commands to ensure clean exit\n await telemetry.shutdown();\n\n // Avoid hanging processes by explicitly exiting\n process.exit(0);\n }\n}\n\n/**\n * Main CLI execution function\n */\nexport async function runCli(): Promise<void> {\n let commandExecuted = false;\n\n // Reset shutdown state for new execution\n isShuttingDown = false;\n\n // Ensure only one SIGINT handler is active\n process.removeListener(\"SIGINT\", sigintHandler);\n process.on(\"SIGINT\", sigintHandler);\n\n try {\n const cli = createCli(process.argv);\n\n // Track if a command was executed?? Yargs doesn't have preAction hook on instance easily.\n // But middleware runs.\n // We can rely on middleware to set tracking data.\n // commandExecuted variable was used to trigger cleanupCliCommand at end.\n // Yargs .parse() resolves when command finishes.\n // If it resolves, command executed.\n commandExecuted = true;\n\n await cli.parse();\n } catch (error) {\n // Handle embedding configuration errors with clean, helpful messages\n if (\n error instanceof ModelConfigurationError ||\n error instanceof UnsupportedProviderError\n ) {\n // These errors already have properly formatted messages\n logger.error(error.message);\n } else {\n logger.error(`❌ Error in CLI: ${error}`);\n }\n\n if (!isShuttingDown) {\n isShuttingDown = true;\n\n // Shutdown active services on error\n const shutdownPromises: Promise<void>[] = [];\n\n const appServer = getActiveAppServer();\n if (appServer) {\n shutdownPromises.push(\n appServer\n .stop()\n .then(() => {\n setActiveAppServer(null);\n })\n .catch((e) => logger.error(`❌ Error stopping AppServer: ${e}`)),\n );\n }\n\n const mcpServer = getActiveMcpStdioServer();\n if (mcpServer) {\n shutdownPromises.push(\n mcpServer\n .close()\n .then(() => {\n setActiveMcpStdioServer(null);\n })\n .catch((e) => logger.error(`❌ Error stopping MCP server: ${e}`)),\n );\n }\n\n const pipeline = getActivePipelineManager();\n if (pipeline && !appServer) {\n shutdownPromises.push(\n pipeline\n .stop()\n .then(() => {\n setActivePipelineManager(null);\n })\n .catch((e) => logger.error(`❌ Error stopping pipeline: ${e}`)),\n );\n }\n\n const docService = getActiveDocService();\n if (docService) {\n shutdownPromises.push(\n docService\n .shutdown()\n .then(() => {\n setActiveDocService(null);\n })\n .catch((e) => logger.error(`❌ Error shutting down doc service: ${e}`)),\n );\n }\n\n await Promise.allSettled(shutdownPromises);\n }\n process.exit(1);\n }\n\n // This block handles cleanup for CLI commands that completed successfully\n // and were not long-running servers.\n const appServer = getActiveAppServer();\n if (commandExecuted && !appServer) {\n await cleanupCliCommand();\n }\n}\n\n// Handle HMR for vite-node --watch\nif (import.meta.hot) {\n import.meta.hot.on(\"vite:beforeFullReload\", async () => {\n logger.info(\"🔥 Hot reload detected\");\n process.removeListener(\"SIGINT\", sigintHandler);\n\n const wasAlreadyShuttingDown = isShuttingDown;\n isShuttingDown = true;\n\n try {\n const shutdownPromises: Promise<void>[] = [];\n\n const appServer = getActiveAppServer();\n if (appServer) {\n logger.debug(\"Shutting down AppServer...\");\n shutdownPromises.push(\n appServer.stop().then(() => {\n setActiveAppServer(null);\n logger.debug(\"AppServer shut down.\");\n }),\n );\n }\n\n const pipeline = getActivePipelineManager();\n if (pipeline && !appServer) {\n shutdownPromises.push(\n pipeline.stop().then(() => {\n setActivePipelineManager(null);\n logger.debug(\"PipelineManager stopped.\");\n }),\n );\n }\n\n const docService = getActiveDocService();\n if (docService) {\n shutdownPromises.push(\n docService.shutdown().then(() => {\n setActiveDocService(null);\n logger.debug(\"DocumentManagementService shut down.\");\n }),\n );\n }\n\n await Promise.allSettled(shutdownPromises);\n logger.debug(\"Active services shut down.\");\n } catch (hmrError) {\n logger.error(`❌ Error during HMR cleanup: ${hmrError}`);\n } finally {\n // Reset state for the next module instantiation\n setActiveAppServer(null);\n if (!wasAlreadyShuttingDown) {\n isShuttingDown = false;\n }\n }\n });\n}\n"],"names":["config","t","path","encodeToToon","yargs","value","baseUrl","z","FetchStatus","fs","chunks","window","ScrapeMode","headers","contentType","StructuralNodeType","STRUCTURAL_DECL_TYPES","CONTENT_DECL_TYPES","isCandidateBoundary","isLocalHelper","findDocumentationStart","extractName","classifyBoundaryKind","unified","PipelineFactory","semver","VersionStatus","result","existingPage","parseHeaders","AppServer","URL","item","content","fsPromises","job","uuidv4","error","document","argv","appServer"],"mappings":"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"}
|