@arabold/docs-mcp-server 1.24.0 → 1.25.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 +22 -8
- package/dist/assets/main.css +1 -1
- package/dist/index.js +3079 -1332
- package/dist/index.js.map +1 -1
- package/package.json +5 -1
- package/public/assets/main.css +1 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/store/types.ts","../src/store/errors.ts","../src/store/embeddings/FixedDimensionEmbeddings.ts","../src/store/embeddings/EmbeddingFactory.ts","../src/utils/logger.ts","../src/telemetry/postHogClient.ts","../src/telemetry/TelemetryConfig.ts","../src/telemetry/analytics.ts","../src/telemetry/sanitizer.ts","../src/auth/middleware.ts","../src/auth/ProxyAuthManager.ts","../src/pipeline/errors.ts","../src/pipeline/PipelineClient.ts","../src/utils/config.ts","../src/utils/dom.ts","../src/utils/errors.ts","../src/utils/mimeTypeUtils.ts","../src/utils/paths.ts","../src/utils/string.ts","../src/utils/url.ts","../src/scraper/fetcher/FileFetcher.ts","../src/scraper/fetcher/FingerprintGenerator.ts","../src/scraper/fetcher/HttpFetcher.ts","../src/splitter/errors.ts","../src/splitter/GreedySplitter.ts","../src/splitter/JsonDocumentSplitter.ts","../src/splitter/splitters/CodeContentSplitter.ts","../src/splitter/splitters/TableContentSplitter.ts","../src/splitter/splitters/TextContentSplitter.ts","../src/splitter/SemanticMarkdownSplitter.ts","../src/scraper/middleware/HtmlCheerioParserMiddleware.ts","../src/scraper/middleware/HtmlLinkExtractorMiddleware.ts","../src/scraper/middleware/HtmlMetadataExtractorMiddleware.ts","../src/scraper/types.ts","../src/scraper/middleware/HtmlPlaywrightMiddleware.ts","../src/scraper/middleware/HtmlSanitizerMiddleware.ts","../src/scraper/middleware/HtmlToMarkdownMiddleware.ts","../src/scraper/middleware/MarkdownLinkExtractorMiddleware.ts","../src/scraper/middleware/MarkdownMetadataExtractorMiddleware.ts","../src/scraper/utils/charset.ts","../src/scraper/utils/buffer.ts","../src/scraper/pipelines/BasePipeline.ts","../src/scraper/pipelines/HtmlPipeline.ts","../src/scraper/pipelines/JsonPipeline.ts","../src/scraper/pipelines/MarkdownPipeline.ts","../src/splitter/TextDocumentSplitter.ts","../src/scraper/pipelines/SourceCodePipeline.ts","../src/scraper/pipelines/TextPipeline.ts","../src/scraper/pipelines/PipelineFactory.ts","../src/scraper/utils/defaultPatterns.ts","../src/scraper/utils/patternMatcher.ts","../src/scraper/utils/scope.ts","../src/scraper/strategies/BaseScraperStrategy.ts","../src/scraper/strategies/GitHubScraperStrategy.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/types.ts","../src/pipeline/PipelineManager.ts","../src/pipeline/PipelineFactory.ts","../src/store/embeddings/EmbeddingConfig.ts","../src/cli/utils.ts","../src/tools/CancelJobTool.ts","../src/tools/ClearCompletedJobsTool.ts","../src/tools/errors.ts","../src/tools/FetchUrlTool.ts","../src/tools/FindVersionTool.ts","../src/tools/GetJobInfoTool.ts","../src/tools/ListJobsTool.ts","../src/tools/ListLibrariesTool.ts","../src/tools/RemoveTool.ts","../src/tools/ScrapeTool.ts","../src/tools/SearchTool.ts","../src/mcp/utils.ts","../src/mcp/mcpServer.ts","../src/mcp/tools.ts","../src/services/mcpService.ts","../src/pipeline/trpc/router.ts","../src/store/trpc/router.ts","../src/services/trpcService.ts","../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/Alert.tsx","../src/web/components/Tooltip.tsx","../src/web/components/ScrapeFormContent.tsx","../src/web/components/ScrapeForm.tsx","../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/services/webService.ts","../src/services/workerService.ts","../src/app/AppServer.ts","../src/app/index.ts","../src/mcp/startStdioServer.ts","../src/store/DocumentManagementClient.ts","../src/store/DocumentRetrieverService.ts","../src/store/applyMigrations.ts","../src/store/DocumentStore.ts","../src/store/DocumentManagementService.ts","../src/store/index.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/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","../src/index.ts"],"sourcesContent":["import type { ScrapeMode } from \"../scraper/types\";\nimport type { DocumentMetadata } from \"../types\";\n\n/** Default vector dimension used across the application */\nexport const VECTOR_DIMENSION = 1536;\n\n/**\n * Database document record type matching the documents table schema\n */\nexport interface DbDocument {\n id: string;\n library_id: number;\n version_id: number; // Changed from version: string to use foreign key\n url: string;\n content: string;\n metadata: string; // JSON string of DocumentMetadata\n embedding: string | null; // JSON string of number[]\n sort_order: number;\n score: number | null;\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 * Maps raw database document to the Document type used by the application\n */\nexport function mapDbDocumentToDocument(doc: DbDocument) {\n return {\n id: doc.id,\n pageContent: doc.content,\n metadata: JSON.parse(doc.metadata) as DocumentMetadata,\n };\n}\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;\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 * Validates if a status transition is allowed.\n * Prevents invalid state changes and ensures data consistency.\n */\nexport function isValidStatusTransition(\n currentStatus: VersionStatus,\n newStatus: VersionStatus,\n): boolean {\n // Define valid transitions for each status\n const validTransitions: Record<VersionStatus, VersionStatus[]> = {\n [VersionStatus.NOT_INDEXED]: [VersionStatus.QUEUED],\n [VersionStatus.QUEUED]: [VersionStatus.RUNNING, VersionStatus.CANCELLED],\n [VersionStatus.RUNNING]: [\n VersionStatus.COMPLETED,\n VersionStatus.FAILED,\n VersionStatus.CANCELLED,\n ],\n [VersionStatus.COMPLETED]: [VersionStatus.UPDATING],\n [VersionStatus.UPDATING]: [VersionStatus.RUNNING, VersionStatus.CANCELLED],\n [VersionStatus.FAILED]: [\n VersionStatus.QUEUED, // Allow retry\n ],\n [VersionStatus.CANCELLED]: [\n VersionStatus.QUEUED, // Allow retry\n ],\n };\n\n return validTransitions[currentStatus]?.includes(newStatus) ?? false;\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","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\nclass 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\nclass ConnectionError extends StoreError {}\n\nclass DocumentNotFoundError extends StoreError {\n constructor(public readonly id: string) {\n super(`Document ${id} not found`);\n }\n}\n\nexport { StoreError, ConnectionError, DocumentNotFoundError, DimensionError };\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 { VECTOR_DIMENSION } from \"../types\";\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 = \"openai\" | \"vertex\" | \"gemini\" | \"aws\" | \"microsoft\";\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\\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 * 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(providerAndModel: string): Embeddings {\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 ModelConfigurationError(\n \"❌ Missing API key for embedding provider\\n\" +\n \" Please set OPENAI_API_KEY or configure an alternative embedding model.\\n\" +\n \" See README.md for configuration options or run with --help for more details.\",\n );\n }\n const config: Partial<OpenAIEmbeddingsParams> & { configuration?: ClientOptions } =\n {\n ...baseConfig,\n modelName: model,\n batchSize: 512, // OpenAI supports large batches\n };\n // Add custom base URL if specified\n const baseURL = process.env.OPENAI_API_BASE;\n if (baseURL) {\n config.configuration = { baseURL };\n }\n return new OpenAIEmbeddings(config);\n }\n\n case \"vertex\": {\n if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) {\n throw new ModelConfigurationError(\n \"❌ Missing credentials for Google Cloud Vertex AI\\n\" +\n \" Please set GOOGLE_APPLICATION_CREDENTIALS or configure an alternative embedding model.\\n\" +\n \" See README.md for configuration options or run with --help for more details.\",\n );\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 ModelConfigurationError(\n \"❌ Missing API key for Google AI (Gemini)\\n\" +\n \" Please set GOOGLE_API_KEY or configure an alternative embedding model.\\n\" +\n \" See README.md for configuration options or run with --help for more details.\",\n );\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 VECTOR_DIMENSION,\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 if (!region) {\n throw new ModelConfigurationError(\n \"BEDROCK_AWS_REGION or AWS_REGION environment variable is required for AWS Bedrock\",\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 throw new ModelConfigurationError(\n \"Either AWS_PROFILE or both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables are required for AWS Bedrock\",\n );\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 if (!process.env.AZURE_OPENAI_API_KEY) {\n throw new ModelConfigurationError(\n \"AZURE_OPENAI_API_KEY environment variable is required for Azure OpenAI\",\n );\n }\n if (!process.env.AZURE_OPENAI_API_INSTANCE_NAME) {\n throw new ModelConfigurationError(\n \"AZURE_OPENAI_API_INSTANCE_NAME environment variable is required for Azure OpenAI\",\n );\n }\n if (!process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME) {\n throw new ModelConfigurationError(\n \"AZURE_OPENAI_API_DEPLOYMENT_NAME environment variable is required for Azure OpenAI\",\n );\n }\n if (!process.env.AZURE_OPENAI_API_VERSION) {\n throw new ModelConfigurationError(\n \"AZURE_OPENAI_API_VERSION environment variable is required for Azure OpenAI\",\n );\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","/**\n * Defines the available log levels.\n */\nexport const LogLevel = {\n ERROR: 0,\n WARN: 1,\n INFO: 2,\n DEBUG: 3,\n} as const;\n\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\n/**\n * Maps string log level names to their numeric values.\n */\nconst LOG_LEVEL_MAP: Record<string, LogLevel> = {\n ERROR: LogLevel.ERROR,\n WARN: LogLevel.WARN,\n INFO: LogLevel.INFO,\n DEBUG: LogLevel.DEBUG,\n};\n\n/**\n * Gets the log level from environment variable or returns default.\n */\nfunction getLogLevelFromEnv(): LogLevel {\n const envLevel = process.env.LOG_LEVEL?.toUpperCase();\n return envLevel && envLevel in LOG_LEVEL_MAP ? LOG_LEVEL_MAP[envLevel] : LogLevel.INFO;\n}\n\nlet currentLogLevel: LogLevel = getLogLevelFromEnv();\n\n/**\n * Sets the current logging level for the application.\n */\nexport function setLogLevel(level: LogLevel): void {\n currentLogLevel = level;\n}\n\n/**\n * Provides logging functionalities with level control.\n */\nexport const logger = {\n /**\n * Logs a debug message if the current log level is DEBUG or higher.\n * @param message - The message to log.\n */\n debug: (message: string) => {\n if (currentLogLevel >= LogLevel.DEBUG && !process.env.VITEST_WORKER_ID) {\n console.debug(message);\n }\n },\n /**\n * Logs an info message if the current log level is INFO or higher.\n * @param message - The message to log.\n */\n info: (message: string) => {\n if (currentLogLevel >= LogLevel.INFO && !process.env.VITEST_WORKER_ID) {\n console.log(message); // Using console.log for INFO\n }\n },\n /**\n * Logs a warning message if the current log level is WARN or higher.\n * @param message - The message to log.\n */\n warn: (message: string) => {\n if (currentLogLevel >= LogLevel.WARN && !process.env.VITEST_WORKER_ID) {\n console.warn(message);\n }\n },\n /**\n * Logs an error message if the current log level is ERROR or higher (always logs).\n * @param message - The message to log.\n */\n error: (message: string) => {\n if (currentLogLevel >= LogLevel.ERROR && !process.env.VITEST_WORKER_ID) {\n console.error(message);\n }\n },\n};\n","/**\n * PostHog client wrapper for telemetry events.\n * Handles PostHog SDK integration and event capture with privacy-first configuration.\n * Automatically converts camelCase property names to snake_case for PostHog compatibility.\n */\n\nimport { PostHog } from \"posthog-node\";\nimport { logger } from \"../utils/logger\";\n\n/**\n * Convert camelCase string to snake_case\n * Specifically designed for PostHog property name conversion\n */\nfunction camelToSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Recursively convert object keys from camelCase to snake_case\n * Handles nested objects and arrays while preserving values\n */\nfunction convertPropertiesToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnakeCase(key);\n\n if (\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n ) {\n // Recursively convert nested objects\n result[snakeKey] = convertPropertiesToSnakeCase(value as Record<string, unknown>);\n } else if (Array.isArray(value)) {\n // Handle arrays - convert elements if they are objects\n result[snakeKey] = value.map((item) =>\n item && typeof item === \"object\" && !(item instanceof Date)\n ? convertPropertiesToSnakeCase(item as Record<string, unknown>)\n : item,\n );\n } else {\n // Primitive values, dates, and null/undefined - keep as-is\n result[snakeKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Add PostHog standard properties and remove duplicates\n * Maps our properties to PostHog's expected property names\n */\nfunction addPostHogStandardProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...properties };\n\n // Add PostHog standard session properties\n if (properties.sessionId) {\n result.$session_id = properties.sessionId;\n delete result.sessionId; // Remove duplicate\n }\n\n if (properties.startTime) {\n result.$start_timestamp = (properties.startTime as Date).toISOString();\n delete result.startTime; // Remove duplicate\n }\n\n // Add PostHog standard app properties\n if (properties.appVersion) {\n result.$app_version = properties.appVersion;\n delete result.appVersion; // Remove duplicate\n }\n\n return result;\n}\n\n/**\n * PostHog client wrapper for telemetry events\n */\nexport class PostHogClient {\n private client?: PostHog;\n private enabled: boolean;\n\n // PostHog configuration\n private static readonly CONFIG = {\n host: \"https://app.posthog.com\",\n\n // Performance optimizations\n flushAt: 20, // Batch size - send after 20 events\n flushInterval: 10000, // 10 seconds - send after time\n\n // Privacy settings\n disableGeoip: true, // Don't collect IP geolocation\n disableSessionRecording: true, // Never record sessions\n disableSurveys: true, // No user surveys\n\n // Data handling\n persistence: \"memory\" as const, // No disk persistence for privacy\n };\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n\n if (!this.enabled) {\n return; // Early return if analytics is disabled\n }\n\n if (!__POSTHOG_API_KEY__) {\n logger.debug(\"PostHog API key not provided\");\n this.enabled = false;\n return;\n }\n\n try {\n this.client = new PostHog(__POSTHOG_API_KEY__, {\n host: PostHogClient.CONFIG.host,\n flushAt: PostHogClient.CONFIG.flushAt,\n flushInterval: PostHogClient.CONFIG.flushInterval,\n disableGeoip: PostHogClient.CONFIG.disableGeoip,\n });\n logger.debug(\"PostHog client initialized\");\n } catch (error) {\n logger.debug(\n `PostHog initialization failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n this.enabled = false;\n }\n }\n\n /**\n * Send event to PostHog\n */\n capture(distinctId: string, event: string, properties: Record<string, unknown>): void {\n if (!this.enabled || !this.client) return;\n\n try {\n // Add PostHog standard properties and remove duplicates\n const enhancedProperties = addPostHogStandardProperties(properties);\n\n // Convert camelCase properties to snake_case for PostHog\n const snakeCaseProperties = convertPropertiesToSnakeCase(enhancedProperties);\n\n this.client.capture({\n distinctId,\n event,\n properties: snakeCaseProperties,\n });\n logger.debug(`PostHog event captured: ${event}`);\n } catch (error) {\n logger.debug(\n `PostHog capture error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n }\n\n /**\n * Capture exception using PostHog's native error tracking\n */\n captureException(\n distinctId: string,\n error: Error,\n properties?: Record<string, unknown>,\n ): void {\n if (!this.enabled || !this.client) return;\n\n try {\n // Add PostHog standard properties and remove duplicates\n const enhancedProperties = addPostHogStandardProperties(properties || {});\n\n // Convert camelCase properties to snake_case for PostHog\n const snakeCaseProperties = convertPropertiesToSnakeCase(enhancedProperties);\n\n this.client.captureException({\n error,\n distinctId,\n properties: snakeCaseProperties,\n });\n logger.debug(`PostHog exception captured: ${error.constructor.name}`);\n } catch (captureError) {\n logger.debug(\n `PostHog captureException error: ${captureError instanceof Error ? captureError.message : \"Unknown error\"}`,\n );\n }\n }\n\n /**\n * Graceful shutdown with event flushing\n */\n async shutdown(): Promise<void> {\n if (this.client) {\n try {\n await this.client.shutdown();\n logger.debug(\"PostHog client shutdown complete\");\n } catch (error) {\n logger.debug(\n `PostHog shutdown error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n }\n }\n\n /**\n * Check if client is enabled and ready\n */\n isEnabled(): boolean {\n return this.enabled && !!this.client;\n }\n}\n","/**\n * Telemetry configuration management for enabling/disabling analytics collection.\n * Handles CLI flags, environment variables, and default settings.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport envPaths from \"env-paths\";\n\nexport class TelemetryConfig {\n private static instance?: TelemetryConfig;\n private enabled: boolean;\n\n constructor() {\n this.enabled = this.determineEnabledState();\n }\n\n /**\n * Determines if telemetry should be enabled based on CLI flags and environment variables.\n * Priority: CLI flags > environment variables > default (true)\n */\n private determineEnabledState(): boolean {\n // Environment variable takes precedence\n if (process.env.DOCS_MCP_TELEMETRY === \"false\") {\n return false;\n }\n\n // Check for CLI flag (passed during initialization)\n const args = process.argv;\n if (args.includes(\"--no-telemetry\")) {\n return false;\n }\n\n // Default to enabled for optional analytics\n return true;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n enable(): void {\n this.enabled = true;\n }\n\n static getInstance(): TelemetryConfig {\n if (!TelemetryConfig.instance) {\n TelemetryConfig.instance = new TelemetryConfig();\n }\n return TelemetryConfig.instance;\n }\n}\n\n/**\n * Generate or retrieve a persistent installation identifier.\n * Creates a UUID and stores it in a file in the user data directory.\n * Supports DOCS_MCP_STORE_PATH environment variable override for Docker deployments.\n * This ensures truly unique identification that persists across runs.\n */\nexport function generateInstallationId(): string {\n try {\n // Use DOCS_MCP_STORE_PATH if set (for Docker/custom deployments), otherwise use standard paths\n const envStorePath = process.env.DOCS_MCP_STORE_PATH;\n const dataDir = envStorePath || envPaths(\"docs-mcp-server\", { suffix: \"\" }).data;\n const installationIdPath = path.join(dataDir, \"installation.id\");\n\n // Try to read existing installation ID\n if (fs.existsSync(installationIdPath)) {\n const existingId = fs.readFileSync(installationIdPath, \"utf8\").trim();\n if (existingId) {\n return existingId;\n }\n }\n\n // Generate new UUID and store it\n const newId = randomUUID();\n\n // Ensure directory exists\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Write the installation ID\n fs.writeFileSync(installationIdPath, newId, \"utf8\");\n\n return newId;\n } catch {\n // Fallback to a session-only UUID if file operations fail\n // This ensures analytics always has a valid distinct ID\n return randomUUID();\n }\n}\n\n/**\n * Check if telemetry should be enabled based on environment and CLI flags.\n */\nexport function shouldEnableTelemetry(): boolean {\n return TelemetryConfig.getInstance().isEnabled();\n}\n","/**\n * Analytics wrapper for privacy-first telemetry using PostHog.\n * Provides global context and automatic data sanitization.\n *\n * Architecture:\n * - PostHogClient: Handles PostHog SDK integration and event capture\n * - Analytics: High-level coordinator providing public API with global context\n */\n\nimport { logger } from \"../utils/logger\";\nimport type { TelemetryEventPropertiesMap } from \"./eventTypes\";\nimport { PostHogClient } from \"./postHogClient\";\nimport { generateInstallationId, TelemetryConfig } from \"./TelemetryConfig\";\n\n/**\n * Telemetry event types for structured analytics\n */\nexport enum TelemetryEvent {\n APP_STARTED = \"app_started\",\n APP_SHUTDOWN = \"app_shutdown\",\n CLI_COMMAND = \"cli_command\",\n TOOL_USED = \"tool_used\",\n HTTP_REQUEST_COMPLETED = \"http_request_completed\",\n PIPELINE_JOB_PROGRESS = \"pipeline_job_progress\",\n PIPELINE_JOB_COMPLETED = \"pipeline_job_completed\",\n DOCUMENT_PROCESSED = \"document_processed\",\n}\n\n/**\n * Main analytics class providing privacy-first telemetry\n */\nexport class Analytics {\n private postHogClient: PostHogClient;\n private enabled: boolean;\n private distinctId: string;\n private globalContext: Record<string, unknown> = {};\n\n /**\n * Create a new Analytics instance with proper initialization\n * This is the recommended way to create Analytics instances\n */\n static create(): Analytics {\n const config = TelemetryConfig.getInstance();\n\n // Single determination point for enabled status\n const shouldEnable = config.isEnabled() && !!__POSTHOG_API_KEY__;\n\n const analytics = new Analytics(shouldEnable);\n\n // Single log message after everything is initialized\n if (analytics.isEnabled()) {\n logger.debug(\"Analytics enabled\");\n } else {\n logger.debug(\"Analytics disabled\");\n }\n\n return analytics;\n }\n\n /**\n * Private constructor - use Analytics.create() instead\n */\n private constructor(enabled: boolean = true) {\n this.enabled = enabled;\n this.distinctId = generateInstallationId();\n this.postHogClient = new PostHogClient(this.enabled);\n }\n\n /**\n * Set global application context that will be included in all events\n */\n setGlobalContext(context: Record<string, unknown>): void {\n this.globalContext = { ...context };\n }\n\n /**\n * Get current global context\n */\n getGlobalContext(): Record<string, unknown> {\n return { ...this.globalContext };\n }\n\n /**\n * Track an event with automatic global context inclusion\n *\n * Type-safe overloads for specific events:\n */\n track<T extends keyof TelemetryEventPropertiesMap>(\n event: T,\n properties: TelemetryEventPropertiesMap[T],\n ): void;\n track(event: string, properties?: Record<string, unknown>): void;\n track(event: string, properties: Record<string, unknown> = {}): void {\n if (!this.enabled) return;\n\n // Merge global context and event properties with timestamp\n const enrichedProperties = {\n ...this.globalContext,\n ...properties,\n timestamp: new Date().toISOString(),\n };\n this.postHogClient.capture(this.distinctId, event, enrichedProperties);\n }\n\n /**\n * Capture exception using PostHog's native error tracking with global context\n */\n captureException(error: Error, properties: Record<string, unknown> = {}): void {\n if (!this.enabled) return;\n\n // Merge global context and error properties with timestamp\n const enrichedProperties = {\n ...this.globalContext,\n ...properties,\n timestamp: new Date().toISOString(),\n };\n this.postHogClient.captureException(this.distinctId, error, enrichedProperties);\n }\n\n /**\n * Graceful shutdown with event flushing\n */\n async shutdown(): Promise<void> {\n if (!this.enabled) return;\n\n await this.postHogClient.shutdown();\n }\n\n /**\n * Check if analytics is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Track tool usage with error handling and automatic timing\n */\n async trackTool<T>(\n toolName: string,\n operation: () => Promise<T>,\n getProperties?: (result: T) => Record<string, unknown>,\n ): Promise<T> {\n const startTime = Date.now();\n\n try {\n const result = await operation();\n\n this.track(TelemetryEvent.TOOL_USED, {\n tool: toolName,\n success: true,\n durationMs: Date.now() - startTime,\n ...(getProperties ? getProperties(result) : {}),\n });\n\n return result;\n } catch (error) {\n // Track the tool usage failure\n this.track(TelemetryEvent.TOOL_USED, {\n tool: toolName,\n success: false,\n durationMs: Date.now() - startTime,\n });\n\n // Capture the exception with full error tracking\n if (error instanceof Error) {\n this.captureException(error, {\n tool: toolName,\n context: \"tool_execution\",\n durationMs: Date.now() - startTime,\n });\n }\n\n throw error;\n }\n }\n}\n\n/**\n * Global analytics instance\n */\nexport const analytics = Analytics.create();\n","/**\n * Data sanitization utilities for privacy-first telemetry.\n * Simplified to only include essential functions we actually use.\n */\n\n/**\n * Extracts hostname from URL for aggregated analytics without exposing paths.\n * Examples:\n * - https://docs.python.org/3/library/os.html -> docs.python.org\n * - https://github.com/owner/repo -> github.com\n * - http://localhost:3000/api -> localhost\n */\nexport function extractHostname(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n return \"invalid-hostname\";\n }\n}\n\n/**\n * Extracts protocol from URL or file path for privacy-safe analytics.\n * Examples:\n * - https://github.com/owner/repo -> \"https\"\n * - file:///local/path -> \"file\"\n * - /local/path -> \"file\" (detected as local file)\n * - C:\\local\\path -> \"file\" (detected as local file)\n */\nexport function extractProtocol(urlOrPath: string): string {\n try {\n const parsed = new URL(urlOrPath);\n return parsed.protocol.replace(\":\", \"\");\n } catch {\n // If URL parsing fails, check if it looks like a local file path\n if (urlOrPath.startsWith(\"/\") || /^[A-Za-z]:/.test(urlOrPath)) {\n return \"file\";\n }\n return \"unknown\";\n }\n}\n\n/**\n * Analyzes search query patterns without storing content.\n * Returns metadata about the query for usage analytics.\n */\nexport function analyzeSearchQuery(query: string): {\n length: number;\n wordCount: number;\n hasCodeTerms: boolean;\n hasSpecialChars: boolean;\n} {\n return {\n length: query.length,\n wordCount: query.trim().split(/\\s+/).length,\n hasCodeTerms:\n /\\b(function|class|import|export|const|let|var|def|async|await)\\b/i.test(query),\n hasSpecialChars: /[^\\w\\s]/.test(query),\n };\n}\n\n/**\n * Sanitizes error messages to remove sensitive information while preserving diagnostic value.\n * Examples:\n * - \"Failed to fetch https://secret.com/api\" -> \"Failed to fetch [url]\"\n * - \"File not found: /home/user/secret.txt\" -> \"File not found: [path]\"\n */\nexport function sanitizeErrorMessage(message: string): string {\n return message\n .replace(/https?:\\/\\/[^\\s]+/gi, \"[url]\")\n .replace(/file:\\/\\/[^\\s]+/gi, \"[file-url]\")\n .replace(/\\/[^\\s]*\\.[a-z]{2,4}/gi, \"[path]\")\n .replace(/[A-Za-z]:\\\\[^\\s]+/g, \"[path]\")\n .replace(/Bearer\\s+[^\\s]+/gi, \"Bearer [token]\")\n .replace(/api[_-]?key[=:]\\s*[^\\s]+/gi, \"api_key=[redacted]\")\n .replace(/token[=:]\\s*[^\\s]+/gi, \"token=[redacted]\")\n .substring(0, 200); // Limit length\n}\n\n/**\n * Sanitizes error information for telemetry collection.\n * Simple approach: just track error type and sanitized message.\n */\nexport function sanitizeError(error: Error): {\n type: string;\n message: string;\n hasStack: boolean;\n} {\n return {\n type: error.constructor.name,\n message: sanitizeErrorMessage(error.message),\n hasStack: Boolean(error.stack),\n };\n}\n\n/**\n * Extract CLI flags from process arguments for telemetry (without values)\n * Examples:\n * - [\"--verbose\", \"--max-depth\", \"3\"] -> [\"--verbose\", \"--max-depth\"]\n */\nexport function extractCliFlags(argv: string[]): string[] {\n return argv.filter((arg) => arg.startsWith(\"--\") || arg.startsWith(\"-\"));\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","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","/**\n * tRPC client implementation of the Pipeline interface.\n * Delegates all pipeline operations to an external worker via tRPC router.\n */\n\nimport { createTRPCProxyClient, httpBatchLink } from \"@trpc/client\";\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 * Deserializes a job object from JSON, converting date strings back to Date objects.\n * Only includes public fields - no internal job management fields.\n */\nfunction deserializeJob(serializedJob: Record<string, unknown>): PipelineJob {\n return {\n ...serializedJob,\n createdAt: new Date(serializedJob.createdAt as string),\n startedAt: serializedJob.startedAt\n ? new Date(serializedJob.startedAt as string)\n : null,\n finishedAt: serializedJob.finishedAt\n ? new Date(serializedJob.finishedAt as string)\n : null,\n updatedAt: serializedJob.updatedAt\n ? new Date(serializedJob.updatedAt as string)\n : undefined,\n } as PipelineJob;\n}\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 client: ReturnType<typeof createTRPCProxyClient<PipelineRouter>>;\n private pollingInterval: number = 1000; // 1 second\n private activePolling = new Set<string>(); // Track jobs being polled for completion\n\n constructor(serverUrl: string) {\n this.baseUrl = serverUrl.replace(/\\/$/, \"\");\n this.client = createTRPCProxyClient<PipelineRouter>({\n links: [httpBatchLink({ url: this.baseUrl })],\n });\n logger.debug(`PipelineClient (tRPC) created for: ${this.baseUrl}`);\n }\n\n async start(): Promise<void> {\n // Check connectivity via ping\n try {\n // Root-level ping exists on the unified router; cast for this health check only\n await (\n this.client as unknown as { ping: { query: () => Promise<unknown> } }\n ).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 // Clear any active polling\n this.activePolling.clear();\n logger.debug(\"PipelineClient stopped\");\n }\n\n async enqueueJob(\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.enqueueJob.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 getJob(jobId: string): Promise<PipelineJob | undefined> {\n try {\n const serializedJob = await this.client.getJob.query({ id: jobId });\n return serializedJob\n ? deserializeJob(serializedJob as unknown as Record<string, unknown>)\n : undefined;\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 const result = await this.client.getJobs.query({ status });\n const serializedJobs = result.jobs || [];\n return serializedJobs.map((j) =>\n deserializeJob(j as unknown as Record<string, unknown>),\n );\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 if (this.activePolling.has(jobId)) {\n throw new Error(`Already waiting for completion of job ${jobId}`);\n }\n\n this.activePolling.add(jobId);\n\n try {\n while (this.activePolling.has(jobId)) {\n const job = await this.getJob(jobId);\n if (!job) {\n throw new Error(`Job ${jobId} not found`);\n }\n\n // Check if job is in final state\n if (\n job.status === \"completed\" ||\n job.status === \"failed\" ||\n job.status === \"cancelled\"\n ) {\n if (job.status === \"failed\" && job.error) {\n // Normalize to real Error instance\n throw new Error(job.error.message);\n }\n return;\n }\n\n // Poll every second\n await new Promise((resolve) => setTimeout(resolve, this.pollingInterval));\n }\n } finally {\n this.activePolling.delete(jobId);\n }\n }\n\n setCallbacks(_callbacks: PipelineManagerCallbacks): void {\n // For external pipeline, callbacks are not used since all updates come via polling\n logger.debug(\"PipelineClient.setCallbacks called - no-op for external worker\");\n }\n}\n","/**\n * Default configuration values for the scraping pipeline and server\n */\n\n/** Maximum number of pages to scrape in a single job */\nexport const DEFAULT_MAX_PAGES = 1000;\n\n/** Maximum navigation depth when crawling links */\nexport const DEFAULT_MAX_DEPTH = 3;\n\n/** Maximum number of concurrent page requests */\nexport const DEFAULT_MAX_CONCURRENCY = 3;\n\n/** Default protocol for the MCP server */\nexport const DEFAULT_PROTOCOL = \"auto\";\n\n/** Default port for the HTTP protocol */\nexport const DEFAULT_HTTP_PORT = 6280;\n\n/** Default port for the Web UI */\nexport const DEFAULT_WEB_PORT = 6281;\n\n/** Default host for server binding */\nexport const DEFAULT_HOST = \"127.0.0.1\";\n\n/**\n * Default timeout in milliseconds for page operations (e.g., Playwright waitForSelector).\n */\nexport const DEFAULT_PAGE_TIMEOUT = 5000;\n\n/**\n * Maximum number of retries for HTTP fetcher requests.\n */\nexport const FETCHER_MAX_RETRIES = 6;\n\n/**\n * Base delay in milliseconds for HTTP fetcher retry backoff.\n */\nexport const FETCHER_BASE_DELAY = 1000;\n\n/**\n * Default chunk size settings for splitters\n */\nexport const SPLITTER_MIN_CHUNK_SIZE = 500;\nexport const SPLITTER_PREFERRED_CHUNK_SIZE = 1500;\nexport const SPLITTER_MAX_CHUNK_SIZE = 5000;\n\n/**\n * Maximum number of documents to process in a single batch for embeddings.\n */\nexport const EMBEDDING_BATCH_SIZE = 100;\n\n/**\n * Maximum total character size for a single embedding batch request.\n * This prevents \"413 Request entity too large\" errors from embedding APIs.\n * Default is 50000 (~50KB), can be overridden with DOCS_MCP_EMBEDDING_BATCH_CHARS environment variable.\n */\nexport const EMBEDDING_BATCH_CHARS = 50000;\n\n/**\n * Maximum number of retries for database migrations if busy.\n */\nexport const MIGRATION_MAX_RETRIES = 5;\n\n/**\n * Delay in milliseconds between migration retry attempts.\n */\nexport const MIGRATION_RETRY_DELAY_MS = 300;\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","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 NetworkError extends ScraperError {\n constructor(\n message: string,\n public readonly statusCode?: number,\n cause?: Error,\n ) {\n super(message, true, cause);\n }\n}\n\nclass RateLimitError extends ScraperError {\n constructor(\n message: string,\n public readonly retryAfter?: number,\n ) {\n super(message, true);\n }\n}\n\nclass InvalidUrlError extends ScraperError {\n constructor(url: string, cause?: Error) {\n super(`Invalid URL: ${url}`, false, cause);\n }\n}\n\nclass ParsingError extends ScraperError {\n constructor(message: string, cause?: Error) {\n super(`Failed to parse content: ${message}`, 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\nexport {\n ScraperError,\n NetworkError,\n RateLimitError,\n InvalidUrlError,\n ParsingError,\n RedirectError,\n};\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 mimeType === \"text/markdown\" || mimeType === \"text/x-markdown\";\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 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, with special handling for common source code extensions\n * that the mime package doesn't handle well or gets wrong.\n *\n * @param filePath - The file path 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 const extension = filePath.toLowerCase().split(\".\").pop();\n\n // Handle common source code extensions that mime package gets wrong or doesn't know\n const customMimeTypes: Record<string, string> = {\n ts: \"text/x-typescript\",\n tsx: \"text/x-tsx\",\n js: \"text/javascript\",\n jsx: \"text/x-jsx\",\n cjs: \"text/javascript\", // CommonJS modules\n mjs: \"text/javascript\", // ES modules\n py: \"text/x-python\",\n pyw: \"text/x-python\",\n pyi: \"text/x-python\",\n go: \"text/x-go\",\n rs: \"text/x-rust\",\n kt: \"text/x-kotlin\",\n scala: \"text/x-scala\",\n swift: \"text/x-swift\",\n rb: \"text/x-ruby\",\n php: \"text/x-php\",\n cs: \"text/x-csharp\",\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 h: \"text/x-chdr\",\n c: \"text/x-csrc\",\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 sql: \"text/x-sql\",\n graphql: \"text/x-graphql\",\n gql: \"text/x-graphql\",\n proto: \"text/x-proto\",\n dockerfile: \"text/x-dockerfile\",\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(filePath);\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 const mimeTypeNormalization: Record<string, string> = {\n \"application/node\": \"text/javascript\", // .cjs files are detected as this\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 \"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 \"text/x-python\": \"python\",\n \"text/x-java\": \"java\",\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-csharp\": \"csharp\",\n \"text/x-go\": \"go\",\n \"text/x-rust\": \"rust\",\n \"text/x-php\": \"php\",\n \"text/x-ruby\": \"ruby\",\n \"text/x-swift\": \"swift\",\n \"text/x-kotlin\": \"kotlin\",\n \"text/x-scala\": \"scala\",\n \"text/x-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-sh\": \"bash\",\n \"text/x-shellscript\": \"bash\",\n \"application/x-sh\": \"bash\",\n \"text/x-powershell\": \"powershell\",\n \"text/x-graphql\": \"graphql\",\n \"text/x-proto\": \"protobuf\",\n \"text/x-dockerfile\": \"dockerfile\",\n };\n\n return mimeToLanguage[mimeType] || \"\";\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nlet projectRoot: string | null = null;\n\n/**\n * Finds the project root directory by searching upwards from the current file\n * for a directory containing 'package.json'. Caches the result.\n *\n * @returns {string} The absolute path to the project root.\n * @throws {Error} If package.json cannot be found.\n */\nexport function getProjectRoot(): string {\n // Return cached result if available\n if (projectRoot) {\n return projectRoot;\n }\n\n // Start from the directory of the current module\n const currentFilePath = fileURLToPath(import.meta.url);\n let currentDir = path.dirname(currentFilePath);\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const packageJsonPath = path.join(currentDir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n projectRoot = currentDir; // Cache the result\n return projectRoot;\n }\n\n const parentDir = path.dirname(currentDir);\n // Check if we have reached the filesystem root\n if (parentDir === currentDir) {\n throw new Error(\"Could not find project root containing package.json.\");\n }\n currentDir = parentDir;\n }\n}\n","/**\n * Thoroughly removes all types of whitespace characters from both ends of a string.\n * Handles spaces, tabs, line breaks, and carriage returns.\n */\nexport const fullTrim = (str: string): string => {\n return str.replace(/^[\\s\\r\\n\\t]+|[\\s\\r\\n\\t]+$/g, \"\");\n};\n","import 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 // Create a new URL to ensure proper structure\n const normalized = new URL(parsedUrl.origin + parsedUrl.pathname);\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 in correct order (query before hash)\n let result = normalized.origin + normalized.pathname;\n if (preservedSearch) {\n result += preservedSearch;\n }\n if (preservedHash) {\n result += preservedHash;\n }\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 * Checks if two URLs have the exact same hostname\n */\nexport function hasSameHostname(urlA: URL, urlB: URL): boolean {\n return urlA.hostname.toLowerCase() === urlB.hostname.toLowerCase();\n}\n\n/**\n * Checks if two URLs are on the same domain (including subdomains)\n * Using the public suffix list to properly handle domains like .co.uk\n */\nexport function hasSameDomain(urlA: URL, urlB: URL): boolean {\n const domainA = psl.get(urlA.hostname.toLowerCase());\n const domainB = psl.get(urlB.hostname.toLowerCase());\n return domainA !== null && domainA === domainB;\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\n/**\n * Checks if a target URL is under the same path as the base URL\n * Example: base = https://example.com/docs/\n * target = https://example.com/docs/getting-started\n * result = true\n */\nexport function isSubpath(baseUrl: URL, targetUrl: URL): boolean {\n const basePath = baseUrl.pathname.endsWith(\"/\")\n ? baseUrl.pathname\n : `${baseUrl.pathname}/`;\n return targetUrl.pathname.startsWith(basePath);\n}\n\nexport type { UrlNormalizerOptions };\n","import fs from \"node:fs/promises\";\nimport { ScraperError } from \"../../utils/errors\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, FetchOptions, RawContent } 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 */\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 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 // Don't assume charset for text files - let the pipeline detect it\n };\n } catch (error: unknown) {\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","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","import axios, { type AxiosError, type AxiosRequestConfig } from \"axios\";\nimport { CancellationError } from \"../../pipeline/errors\";\nimport { analytics, extractHostname, extractProtocol } from \"../../telemetry\";\nimport { FETCHER_BASE_DELAY, FETCHER_MAX_RETRIES } from \"../../utils/config\";\nimport { RedirectError, ScraperError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { FingerprintGenerator } from \"./FingerprintGenerator\";\nimport type { ContentFetcher, FetchOptions, RawContent } from \"./types\";\n\n/**\n * Fetches content from remote sources using HTTP/HTTPS.\n */\nexport class HttpFetcher implements ContentFetcher {\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 fingerprintGenerator: FingerprintGenerator;\n\n constructor() {\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 startTime = performance.now();\n const maxRetries = options?.maxRetries ?? FETCHER_MAX_RETRIES;\n const baseDelay = options?.retryDelay ?? FETCHER_BASE_DELAY;\n // Default to following redirects if not specified\n const followRedirects = options?.followRedirects ?? true;\n\n try {\n const result = await this.performFetch(\n source,\n options,\n maxRetries,\n baseDelay,\n followRedirects,\n );\n\n // Track successful HTTP request\n const duration = performance.now() - startTime;\n analytics.track(\"http_request_completed\", {\n success: true,\n hostname: extractHostname(source),\n protocol: extractProtocol(source),\n durationMs: Math.round(duration),\n contentSizeBytes: result.content.length,\n mimeType: result.mimeType,\n hasEncoding: !!result.encoding,\n followRedirects: followRedirects,\n hadRedirects: result.source !== source,\n });\n\n return result;\n } catch (error) {\n // Track failed HTTP request\n const duration = performance.now() - startTime;\n const axiosError = error as AxiosError;\n const status = axiosError.response?.status;\n\n analytics.track(\"http_request_completed\", {\n success: false,\n hostname: extractHostname(source),\n protocol: extractProtocol(source),\n durationMs: Math.round(duration),\n statusCode: status,\n errorType:\n error instanceof CancellationError\n ? \"cancellation\"\n : error instanceof RedirectError\n ? \"redirect\"\n : error instanceof ScraperError\n ? \"scraper\"\n : \"unknown\",\n errorCode: axiosError.code,\n followRedirects: followRedirects,\n });\n\n throw error;\n }\n }\n\n private async performFetch(\n source: string,\n options?: FetchOptions,\n maxRetries = FETCHER_MAX_RETRIES,\n baseDelay = FETCHER_BASE_DELAY,\n followRedirects = true,\n ): Promise<RawContent> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const fingerprint = this.fingerprintGenerator.generateHeaders();\n const headers = {\n ...fingerprint,\n ...options?.headers, // User-provided headers override generated ones\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 };\n\n const response = await axios.get(source, config);\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 return {\n content,\n mimeType,\n charset,\n encoding: contentEncoding,\n source: finalUrl,\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 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 if (\n attempt < maxRetries &&\n (status === undefined || this.retryableStatusCodes.includes(status))\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 * Base error class for all splitter-related errors\n */\nexport class SplitterError extends Error {}\n\n/**\n * Thrown when content cannot be split further while maintaining its validity\n * (e.g., markdown tables require headers, code blocks require language and backticks)\n */\nexport class MinimumChunkSizeError extends SplitterError {\n constructor(size: number, maxSize: number) {\n super(\n `Cannot split content any further. Content requires minimum chunk size of ${size} bytes, but maximum allowed is ${maxSize} bytes.`,\n );\n }\n}\n\n/**\n * Generic error for content splitting failures\n */\nexport class ContentSplitterError extends SplitterError {}\n","import type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Takes small document chunks and greedily concatenates them into larger, more meaningful units\n * while preserving document structure and semantic boundaries.\n *\n * This approach improves embedding quality by:\n * - Maintaining context by keeping related content together\n * - Respecting natural document breaks at major section boundaries (H1/H2)\n * - Ensuring chunks are large enough to capture meaningful relationships\n * - Preventing chunks from becoming too large for effective embedding\n */\nexport class GreedySplitter implements DocumentSplitter {\n private baseSplitter: DocumentSplitter;\n private minChunkSize: number;\n private preferredChunkSize: number;\n\n /**\n * Combines a base document splitter with size constraints to produce optimally-sized chunks.\n * The base splitter handles the initial semantic splitting, while this class handles\n * the concatenation strategy.\n */\n constructor(\n baseSplitter: DocumentSplitter,\n minChunkSize: number,\n preferredChunkSize: number,\n ) {\n this.baseSplitter = baseSplitter;\n this.minChunkSize = minChunkSize;\n this.preferredChunkSize = preferredChunkSize;\n }\n\n /**\n * Uses a greedy concatenation strategy to build optimally-sized chunks. Small chunks\n * are combined until they reach the minimum size, but splits are preserved at major\n * section boundaries to maintain document structure. This balances the need for\n * context with semantic coherence.\n */\n async splitText(markdown: string, contentType?: string): Promise<ContentChunk[]> {\n const initialChunks = await this.baseSplitter.splitText(markdown, contentType);\n const concatenatedChunks: ContentChunk[] = [];\n let currentChunk: ContentChunk | null = null;\n\n for (const nextChunk of initialChunks) {\n if (currentChunk) {\n if (this.wouldExceedMaxSize(currentChunk, nextChunk)) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n if (\n currentChunk.content.length >= this.minChunkSize &&\n this.startsNewMajorSection(nextChunk)\n ) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n currentChunk.content += `\\n${nextChunk.content}`;\n currentChunk.section = this.mergeSectionInfo(currentChunk, nextChunk);\n currentChunk.types = this.mergeTypes(currentChunk.types, nextChunk.types);\n } else {\n currentChunk = this.cloneChunk(nextChunk);\n }\n }\n\n if (currentChunk) {\n concatenatedChunks.push(currentChunk);\n }\n\n return concatenatedChunks;\n }\n\n private cloneChunk(chunk: ContentChunk): ContentChunk {\n return {\n types: [...chunk.types],\n content: chunk.content,\n section: {\n level: chunk.section.level,\n path: [...chunk.section.path],\n },\n };\n }\n\n /**\n * H1 and H2 headings represent major conceptual breaks in the document.\n * Preserving these splits helps maintain the document's logical structure.\n */\n private startsNewMajorSection(chunk: ContentChunk): boolean {\n return chunk.section.level === 1 || chunk.section.level === 2;\n }\n\n /**\n * Size limit check to ensure chunks remain within embedding model constraints.\n * Essential for maintaining consistent embedding quality and avoiding truncation.\n */\n private wouldExceedMaxSize(\n currentChunk: ContentChunk | null,\n nextChunk: ContentChunk,\n ): boolean {\n if (!currentChunk) {\n return false;\n }\n return (\n currentChunk.content.length + nextChunk.content.length > this.preferredChunkSize\n );\n }\n\n /**\n * Checks if one path is a prefix of another path, indicating a parent-child relationship\n */\n private isPathIncluded(parentPath: string[], childPath: string[]): boolean {\n if (parentPath.length >= childPath.length) return false;\n return parentPath.every((part, i) => part === childPath[i]);\n }\n\n /**\n * Merges section metadata when concatenating chunks, following these rules:\n * 1. Level: Always uses the lowest (most general) level between chunks\n * 2. Path selection:\n * - For parent-child relationships (one path includes the other), uses the child's path\n * - For siblings/unrelated sections, uses the common parent path\n * - If no common path exists, uses the root path ([])\n */\n private mergeSectionInfo(\n currentChunk: ContentChunk,\n nextChunk: ContentChunk,\n ): ContentChunk[\"section\"] {\n // Always use the lowest level\n const level = Math.min(currentChunk.section.level, nextChunk.section.level);\n\n // If sections are exactly equal, preserve all metadata\n if (\n currentChunk.section.level === nextChunk.section.level &&\n currentChunk.section.path.length === nextChunk.section.path.length &&\n currentChunk.section.path.every((p, i) => p === nextChunk.section.path[i])\n ) {\n return currentChunk.section;\n }\n\n // Check if one path includes the other\n if (this.isPathIncluded(currentChunk.section.path, nextChunk.section.path)) {\n return {\n path: nextChunk.section.path,\n level,\n };\n }\n\n if (this.isPathIncluded(nextChunk.section.path, currentChunk.section.path)) {\n return {\n path: currentChunk.section.path,\n level,\n };\n }\n\n // Find common parent path\n const commonPath = this.findCommonPrefix(\n currentChunk.section.path,\n nextChunk.section.path,\n );\n\n return {\n path: commonPath,\n level,\n };\n }\n\n private mergeTypes(\n currentTypes: SectionContentType[],\n nextTypes: SectionContentType[],\n ): SectionContentType[] {\n return [...new Set([...currentTypes, ...nextTypes])];\n }\n\n /**\n * Returns longest common prefix between two paths\n */\n private findCommonPrefix(path1: string[], path2: string[]): string[] {\n const common: string[] = [];\n for (let i = 0; i < Math.min(path1.length, path2.length); i++) {\n if (path1[i] === path2[i]) {\n common.push(path1[i]);\n } else {\n break;\n }\n }\n return common;\n }\n}\n","/**\n * 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\n * 4. Maintain proper indentation and hierarchical paths\n * 5. Let GreedySplitter handle size optimization\n */\n\nimport type { ContentChunk, DocumentSplitter } from \"./types\";\n\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport interface JsonDocumentSplitterOptions {\n // No size constraints - we create minimal chunks and let GreedySplitter optimize\n preserveFormatting?: boolean;\n}\n\nexport class JsonDocumentSplitter implements DocumentSplitter {\n private preserveFormatting: boolean;\n\n constructor(options: JsonDocumentSplitterOptions = {}) {\n this.preserveFormatting = options.preserveFormatting ?? true;\n }\n\n async splitText(content: string, _contentType?: string): Promise<ContentChunk[]> {\n try {\n const parsed: JsonValue = JSON.parse(content);\n const chunks: ContentChunk[] = [];\n\n // Process the JSON structure recursively, starting with root path\n this.processValue(parsed, [\"root\"], 1, 0, chunks, true);\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 processValue(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): void {\n if (Array.isArray(value)) {\n this.processArray(value, path, level, indentLevel, chunks, isLastItem);\n } else if (value !== null && typeof value === \"object\") {\n this.processObject(value, path, level, indentLevel, chunks, isLastItem);\n } else {\n this.processPrimitive(value, path, level, indentLevel, chunks, isLastItem);\n }\n }\n\n private processArray(\n array: JsonValue[],\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): 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, \"opening\"] },\n });\n\n // Process each array element\n array.forEach((item, index) => {\n const isLast = index === array.length - 1;\n const itemPath = [...path, `[${index}]`];\n 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, \"closing\"] },\n });\n }\n\n private processObject(\n obj: Record<string, JsonValue>,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): 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, \"opening\"] },\n });\n\n // Process each property\n entries.forEach(([key, value], index) => {\n const isLast = index === entries.length - 1;\n const propertyPath = [...path, key];\n 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, \"closing\"] },\n });\n }\n\n private processProperty(\n key: string,\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastProperty: boolean,\n ): 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: [...path, \"key\"] },\n });\n\n // Process the complex value (it handles its own comma)\n this.processValue(value, path, level, indentLevel, chunks, isLastProperty);\n } else {\n // For primitive values, create a complete property chunk\n const comma = isLastProperty ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n chunks.push({\n types: [\"code\"],\n content: `${indent}\"${key}\": ${formattedValue}${comma}`,\n section: { level, path },\n });\n }\n }\n\n private processPrimitive(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): void {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n\n chunks.push({\n types: [\"code\"],\n content: `${indent}${formattedValue}${comma}`,\n section: { level, path },\n });\n }\n\n private getIndent(level: number): string {\n return this.preserveFormatting ? \" \".repeat(level) : \"\";\n }\n}\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Splits code content while preserving language information and formatting.\n * Uses line boundaries for splitting and ensures each chunk is properly\n * wrapped with language-specific code block markers.\n */\nexport class CodeContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n async split(content: string): Promise<string[]> {\n // Determine language and strip triple backticks from content\n const language = content.match(/^```(\\w+)\\n/)?.[1];\n const strippedContent = content.replace(/^```(\\w*)\\n/, \"\").replace(/```\\s*$/, \"\");\n\n const lines = strippedContent.split(\"\\n\");\n const chunks: string[] = [];\n let currentChunkLines: string[] = [];\n\n for (const line of lines) {\n // Check if a single line with code block markers exceeds chunkSize\n const singleLineSize = this.wrap(line, language).length;\n if (singleLineSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleLineSize, this.options.chunkSize);\n }\n\n currentChunkLines.push(line);\n const newChunkContent = this.wrap(currentChunkLines.join(\"\\n\"), language);\n const newChunkSize = newChunkContent.length;\n\n if (newChunkSize > this.options.chunkSize && currentChunkLines.length > 1) {\n // remove last item\n const lastLine = currentChunkLines.pop();\n // wrap content and create chunk\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n currentChunkLines = [lastLine as string];\n }\n }\n\n if (currentChunkLines.length > 0) {\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n }\n\n return chunks;\n }\n\n protected wrap(content: string, language?: string | null): string {\n return `\\`\\`\\`${language || \"\"}\\n${content.replace(/\\n+$/, \"\")}\\n\\`\\`\\``;\n }\n}\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Interface representing the structure of a parsed markdown table\n */\ninterface ParsedTable {\n headers: string[];\n separator: string;\n rows: string[];\n}\n\n/**\n * Splits table content while preserving headers and table formatting.\n * Each chunk maintains the table structure with headers and separator row.\n */\nexport class TableContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n /**\n * Splits table content into chunks while preserving table structure\n */\n async split(content: string): Promise<string[]> {\n const parsedTable = this.parseTable(content);\n if (!parsedTable) {\n return [content];\n }\n\n const { headers, rows } = parsedTable;\n\n const chunks: string[] = [];\n let currentRows: string[] = [];\n\n for (const row of rows) {\n // Check if a single row with headers exceeds chunkSize\n const singleRowSize = this.wrap(row, headers).length;\n if (singleRowSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleRowSize, this.options.chunkSize);\n }\n\n const newChunkContent = this.wrap([...currentRows, row].join(\"\\n\"), headers);\n const newChunkSize = newChunkContent.length;\n if (newChunkSize > this.options.chunkSize && currentRows.length > 0) {\n // Add current chunk, start new\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n currentRows = [row];\n } else {\n currentRows.push(row);\n }\n }\n\n if (currentRows.length > 0) {\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n }\n\n // No merging of table chunks\n return chunks;\n }\n\n protected wrap(content: string, headers: string[]): string {\n const headerRow = `| ${headers.join(\" | \")} |`;\n const separatorRow = `|${headers.map(() => \"---\").join(\"|\")}|`;\n return [headerRow, separatorRow, content].join(\"\\n\");\n }\n\n private parseTable(content: string): ParsedTable | null {\n const lines = content.trim().split(\"\\n\");\n if (lines.length < 3) return null; // Need at least headers, separator, and one data row\n\n const headers = this.parseRow(lines[0]);\n if (!headers) return null;\n\n const separator = lines[1];\n if (!this.isValidSeparator(separator)) return null;\n\n const rows = lines.slice(2).filter((row) => row.trim() !== \"\");\n\n return { headers, separator, rows };\n }\n\n /**\n * Parses a table row into cells\n */\n private parseRow(row: string): string[] | null {\n if (!row.includes(\"|\")) return null;\n return row\n .split(\"|\")\n .map((cell) => cell.trim())\n .filter((cell) => cell !== \"\");\n }\n\n /**\n * Validates the separator row of the table\n */\n private isValidSeparator(separator: string): boolean {\n return separator.includes(\"|\") && /^\\|?[\\s-|]+\\|?$/.test(separator);\n }\n}\n","import { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport { fullTrim } from \"../../utils/string\";\nimport { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Splits text content using a hierarchical approach:\n * 1. Try splitting by paragraphs (double newlines)\n * 2. If chunks still too large, split by single newlines\n * 3. Finally, use word boundaries via LangChain's splitter\n */\nexport class TextContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n /**\n * Splits text content into chunks while trying to preserve semantic boundaries.\n * Prefers paragraph breaks, then line breaks, finally falling back to word boundaries.\n */\n async split(content: string): Promise<string[]> {\n const trimmedContent = fullTrim(content);\n\n if (trimmedContent.length <= this.options.chunkSize) {\n return [trimmedContent];\n }\n\n // Check for unsplittable content (e.g., a single word longer than chunkSize)\n const words = trimmedContent.split(/\\s+/);\n const longestWord = words.reduce((max, word) =>\n word.length > max.length ? word : max,\n );\n if (longestWord.length > this.options.chunkSize) {\n throw new MinimumChunkSizeError(longestWord.length, this.options.chunkSize);\n }\n\n // First try splitting by paragraphs (double newlines)\n const paragraphChunks = this.splitByParagraphs(trimmedContent);\n if (this.areChunksValid(paragraphChunks)) {\n // No merging for paragraph chunks; they are already semantically separated\n return paragraphChunks;\n }\n\n // If that doesn't work, try splitting by single newlines\n const lineChunks = this.splitByLines(trimmedContent);\n if (this.areChunksValid(lineChunks)) {\n return this.mergeChunks(lineChunks, \"\\n\");\n }\n\n // Finally, fall back to word-based splitting using LangChain\n const wordChunks = await this.splitByWords(trimmedContent);\n return this.mergeChunks(wordChunks, \" \");\n }\n\n /**\n * Checks if all chunks are within the maximum size limit\n */\n private areChunksValid(chunks: string[]): boolean {\n return chunks.every((chunk) => chunk.length <= this.options.chunkSize);\n }\n\n /**\n * Splits text into chunks by paragraph boundaries (double newlines)\n */\n private splitByParagraphs(text: string): string[] {\n const paragraphs = text\n .split(/\\n\\s*\\n/)\n .map((p) => fullTrim(p))\n .filter(Boolean);\n\n return paragraphs.filter((chunk) => chunk.length > 2);\n }\n\n /**\n * Splits text into chunks by line boundaries\n */\n private splitByLines(text: string): string[] {\n const lines = text\n .split(/\\n/)\n .map((line) => fullTrim(line))\n .filter(Boolean);\n\n return lines.filter((chunk) => chunk.length > 1);\n }\n\n /**\n * Uses LangChain's recursive splitter for word-based splitting as a last resort\n */\n private async splitByWords(text: string): Promise<string[]> {\n const splitter = new RecursiveCharacterTextSplitter({\n chunkSize: this.options.chunkSize,\n chunkOverlap: 0,\n });\n\n const chunks = await splitter.splitText(text);\n return chunks;\n }\n\n /**\n * Attempts to merge small chunks with previous chunks to minimize fragmentation.\n * Only merges if combined size is within maxChunkSize.\n */\n protected mergeChunks(chunks: string[], separator: string): string[] {\n const mergedChunks: string[] = [];\n let currentChunk: string | null = null;\n\n for (const chunk of chunks) {\n if (currentChunk === null) {\n currentChunk = chunk;\n continue;\n }\n\n const currentChunkSize = this.getChunkSize(currentChunk);\n const nextChunkSize = this.getChunkSize(chunk);\n\n if (currentChunkSize + nextChunkSize + separator.length <= this.options.chunkSize) {\n // Merge chunks\n currentChunk = `${currentChunk}${separator}${chunk}`;\n } else {\n // Add the current chunk to the result and start a new one\n mergedChunks.push(currentChunk);\n currentChunk = chunk;\n }\n }\n\n if (currentChunk) {\n mergedChunks.push(currentChunk);\n }\n\n return mergedChunks;\n }\n\n protected getChunkSize(chunk: string): number {\n return chunk.length;\n }\n\n protected wrap(content: string): string {\n return content;\n }\n}\n","import { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkHtml from \"remark-html\";\nimport remarkParse from \"remark-parse\";\nimport TurndownService from \"turndown\";\nimport { unified } from \"unified\";\nimport { createJSDOM } from \"../utils/dom\";\nimport { logger } from \"../utils/logger\";\nimport { fullTrim } from \"../utils/string\";\nimport { ContentSplitterError, MinimumChunkSizeError } from \"./errors\";\nimport { CodeContentSplitter } from \"./splitters/CodeContentSplitter\";\nimport { TableContentSplitter } from \"./splitters/TableContentSplitter\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Represents a section of content within a document,\n * typically defined by a heading\n */\ninterface DocumentSection {\n level: number;\n path: string[]; // Full path including parent headings\n content: {\n type: SectionContentType;\n text: string;\n }[];\n}\n\n/**\n * Splits markdown documents into semantic chunks while preserving\n * structure and distinguishing between different content types.\n *\n * The splitting process happens in two steps:\n * 1. Split document into sections based on headings (H1-H3 only)\n * 2. Split section content into smaller chunks based on preferredChunkSize\n */\nexport class SemanticMarkdownSplitter implements DocumentSplitter {\n private turndownService: TurndownService;\n public textSplitter: TextContentSplitter;\n public codeSplitter: CodeContentSplitter;\n public tableSplitter: TableContentSplitter;\n\n constructor(\n private preferredChunkSize: number,\n private maxChunkSize: number,\n ) {\n this.turndownService = new TurndownService({\n headingStyle: \"atx\",\n hr: \"---\",\n bulletListMarker: \"-\",\n codeBlockStyle: \"fenced\",\n emDelimiter: \"_\",\n strongDelimiter: \"**\",\n linkStyle: \"inlined\",\n });\n\n // Add table rule to preserve markdown table format\n this.turndownService.addRule(\"table\", {\n filter: [\"table\"],\n replacement: (_content, node) => {\n const table = node as HTMLTableElement;\n const headers = Array.from(table.querySelectorAll(\"th\")).map(\n (th) => th.textContent?.trim() || \"\",\n );\n const rows = Array.from(table.querySelectorAll(\"tr\")).filter(\n (tr) => !tr.querySelector(\"th\"),\n );\n\n if (headers.length === 0 && rows.length === 0) return \"\";\n\n let markdown = \"\\n\";\n if (headers.length > 0) {\n markdown += `| ${headers.join(\" | \")} |\\n`;\n markdown += `|${headers.map(() => \"---\").join(\"|\")}|\\n`;\n }\n\n for (const row of rows) {\n const cells = Array.from(row.querySelectorAll(\"td\")).map(\n (td) => td.textContent?.trim() || \"\",\n );\n markdown += `| ${cells.join(\" | \")} |\\n`;\n }\n\n return markdown;\n },\n });\n\n // Text splitter uses preferred chunk size (keeps paragraphs together if possible)\n this.textSplitter = new TextContentSplitter({\n chunkSize: this.preferredChunkSize,\n });\n // Code/table splitters use the hard chunk size (avoid splitting unless necessary)\n this.codeSplitter = new CodeContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n this.tableSplitter = new TableContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n }\n\n /**\n * Main entry point for splitting markdown content\n */\n async splitText(markdown: string, _contentType?: string): Promise<ContentChunk[]> {\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 // For markdown, HTML, or plain text, process normally\n const html = await this.markdownToHtml(markdown);\n const dom = await this.parseHtml(html);\n const sections = await this.splitIntoSections(dom);\n return this.splitSectionContent(sections);\n }\n\n /**\n * Step 1: Split document into sections based on H1-H6 headings,\n * as well as code blocks and tables.\n */\n private async splitIntoSections(dom: Document): Promise<DocumentSection[]> {\n const body = dom.querySelector(\"body\");\n if (!body) {\n throw new Error(\"Invalid HTML structure: no body element found\");\n }\n\n let currentSection = this.createRootSection();\n const sections: DocumentSection[] = [];\n const stack: DocumentSection[] = [currentSection];\n\n // Process each child of the body\n for (const element of Array.from(body.children)) {\n const headingMatch = element.tagName.match(/H([1-6])/);\n\n if (headingMatch) {\n // Create new section for H1-H6 heading\n const level = Number.parseInt(headingMatch[1], 10);\n const title = fullTrim(element.textContent || \"\");\n\n // Pop sections from stack until we find the parent level\n while (stack.length > 1 && stack[stack.length - 1].level >= level) {\n stack.pop();\n }\n\n // Start new section with the header\n currentSection = {\n level,\n path: [\n ...stack.slice(1).reduce((acc: string[], s) => {\n const lastPath = s.path[s.path.length - 1];\n if (lastPath) acc.push(lastPath);\n return acc;\n }, []),\n title,\n ],\n content: [\n {\n type: \"heading\",\n text: `${\"#\".repeat(level)} ${title}`,\n },\n ],\n };\n\n sections.push(currentSection);\n stack.push(currentSection);\n } else if (element.tagName === \"PRE\") {\n // Code blocks are kept as separate chunks\n const code = element.querySelector(\"code\");\n const language = code?.className.replace(\"language-\", \"\") || \"\";\n const content = code?.textContent || element.textContent || \"\";\n const markdown = `${\"```\"}${language}\\n${content}\\n${\"```\"}`;\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"code\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else if (element.tagName === \"TABLE\") {\n // Tables are kept as separate chunks\n const markdown = fullTrim(this.turndownService.turndown(element.outerHTML));\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"table\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else {\n const markdown = fullTrim(this.turndownService.turndown(element.innerHTML));\n if (markdown) {\n // Create a new section for the text content\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"text\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n }\n }\n }\n\n return sections;\n }\n\n /**\n * Step 2: Split section content into smaller chunks\n */\n private async splitSectionContent(\n sections: DocumentSection[],\n ): Promise<ContentChunk[]> {\n const chunks: ContentChunk[] = [];\n\n for (const section of sections) {\n for (const content of section.content) {\n let splitContent: string[] = [];\n\n try {\n switch (content.type) {\n case \"heading\":\n case \"text\": {\n splitContent = await this.textSplitter.split(content.text);\n break;\n }\n case \"code\": {\n splitContent = await this.codeSplitter.split(content.text);\n break;\n }\n case \"table\": {\n splitContent = await this.tableSplitter.split(content.text);\n break;\n }\n }\n } catch (err) {\n // If it's a MinimumChunkSizeError, use RecursiveCharacterTextSplitter directly\n if (err instanceof MinimumChunkSizeError) {\n logger.warn(\n `⚠ Cannot split ${content.type} chunk normally, using RecursiveCharacterTextSplitter: ${err.message}`,\n );\n\n // Create a RecursiveCharacterTextSplitter with aggressive settings to ensure splitting\n const splitter = new RecursiveCharacterTextSplitter({\n chunkSize: this.maxChunkSize,\n chunkOverlap: Math.min(20, Math.floor(this.maxChunkSize * 0.1)),\n // Use more aggressive separators including empty string as last resort\n separators: [\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\\t\",\n \".\",\n \",\",\n \";\",\n \":\",\n \"-\",\n \"(\",\n \")\",\n \"[\",\n \"]\",\n \"{\",\n \"}\",\n \"\",\n ],\n });\n\n const chunks = await splitter.splitText(content.text);\n if (chunks.length === 0) {\n // If still no chunks, use the most extreme approach: just truncate\n splitContent = [content.text.substring(0, this.maxChunkSize)];\n } else {\n splitContent = chunks;\n }\n } else {\n // Convert other error message to string, handling non-Error objects\n const errMessage = err instanceof Error ? err.message : String(err);\n throw new ContentSplitterError(\n `Failed to split ${content.type} content: ${errMessage}`,\n );\n }\n }\n\n // Create chunks from split content\n chunks.push(\n ...splitContent.map(\n (text): ContentChunk => ({\n types: [content.type],\n content: text,\n section: {\n level: section.level,\n path: section.path,\n },\n }),\n ),\n );\n }\n }\n\n return chunks;\n }\n\n /**\n * Helper to create the root section\n */\n private createRootSection(): DocumentSection {\n return {\n level: 0,\n path: [],\n content: [],\n };\n }\n\n /**\n * Convert markdown to HTML using remark\n */\n private async markdownToHtml(markdown: string): Promise<string> {\n const html = await unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkHtml)\n .process(markdown);\n\n return `<!DOCTYPE html>\n <html>\n <body>\n ${String(html)}\n </body>\n </html>`;\n }\n\n /**\n * Parse HTML\n */\n private async parseHtml(html: string): Promise<Document> {\n // Use createJSDOM which includes default options like virtualConsole\n const { window } = createJSDOM(html);\n return window.document;\n }\n}\n","import * 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.metadata.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 { Document, ProgressCallback } from \"../types\";\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<ScraperProgress>,\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 * Options for configuring the scraping process\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\n/**\n * Result of scraping a single page. Used internally by HtmlScraper.\n */\nexport interface ScrapedPage {\n content: string;\n title: string;\n url: string;\n /** URLs extracted from page links, used for recursive scraping */\n links: string[];\n}\n\n/**\n * Progress information during scraping\n */\nexport interface ScraperProgress {\n pagesScraped: number;\n totalPages: number; // Effective total pages (limited by maxPages configuration)\n totalDiscovered: number; // Actual number of pages discovered (may exceed totalPages)\n currentUrl: string;\n depth: number;\n maxDepth: number;\n document?: Document;\n}\n","import {\n type Browser,\n type BrowserContext,\n chromium,\n type ElementHandle,\n type Frame,\n type Page,\n} from \"playwright\";\nimport { DEFAULT_PAGE_TIMEOUT } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { ScrapeMode } from \"../types\";\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 * 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\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 const launchArgs = process.env.PLAYWRIGHT_LAUNCH_ARGS?.split(\" \") ?? [];\n const executablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || undefined;\n logger.debug(\n `Launching new Playwright browser instance (Chromium) with args: ${launchArgs.join(\" \") || \"none\"}...`,\n );\n this.browser = await chromium.launch({\n channel: \"chromium\",\n args: launchArgs,\n executablePath: executablePath,\n });\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 */\n async closeBrowser(): Promise<void> {\n if (this.browser?.isConnected()) {\n logger.debug(\"Closing Playwright browser instance...\");\n await this.browser.close();\n this.browser = null;\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: DEFAULT_PAGE_TIMEOUT,\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\n await frame.waitForSelector(\"body\", { timeout: DEFAULT_PAGE_TIMEOUT }).catch(() => {\n logger.debug(`Timeout waiting for body in iframe ${index + 1}`);\n });\n\n // Wait for loading indicators in the iframe to complete\n await this.waitForLoadingToComplete(frame);\n\n // Extract and replace iframe content\n const content = await this.extractIframeContent(frame);\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 waiting for 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 * Fetches content from a frame URL by navigating to it in a new page.\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 let framePage: Page | null = null;\n try {\n // Resolve relative URLs against the parent page URL\n const resolvedUrl = new URL(frameUrl, parentPage.url()).href;\n\n // Create a new page in the same browser context for consistency\n framePage = await parentPage.context().newPage();\n\n // Use the same route handling as the parent page for consistency\n await framePage.route(\"**/*\", async (route) => {\n const resourceType = route.request().resourceType();\n\n // Abort non-essential resources (but keep stylesheets for content rendering)\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n return route.abort();\n }\n\n return route.continue();\n });\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: DEFAULT_PAGE_TIMEOUT,\n });\n await framePage.waitForSelector(\"body\", { timeout: DEFAULT_PAGE_TIMEOUT });\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 logger.debug(`Successfully fetched frame content from: ${resolvedUrl}`);\n return bodyContent || \"\";\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 // Always process, content type is handled by pipeline selection\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 // Block unnecessary resources and inject credentials and custom headers for same-origin requests\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 // Serve the initial HTML for the main page\n if (reqUrl === context.source) {\n return route.fulfill({\n status: 200,\n contentType: \"text/html; charset=utf-8\",\n body: context.content, // context.content is always a string in middleware\n });\n }\n // Abort non-essential resources (but keep stylesheets for modern web apps)\n const resourceType = route.request().resourceType();\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n return route.abort();\n }\n // Use helper to merge headers\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials ?? undefined,\n origin ?? undefined,\n reqOrigin ?? undefined,\n );\n return route.continue({ headers });\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 await page.waitForSelector(\"body, frameset\", { timeout: DEFAULT_PAGE_TIMEOUT });\n\n // Wait for network idle to let dynamic content initialize\n try {\n await page.waitForLoadState(\"networkidle\", { timeout: DEFAULT_PAGE_TIMEOUT });\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 \"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 \".modal\",\n \".nav-bar\",\n \".overlay\",\n \".popup\",\n \".promo\",\n \".mw-editsection\",\n \".side-bar\",\n \".social-share\",\n \".sticky\",\n \"#ads\",\n \"#banner\",\n \"#cookieBanner\",\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=\"dialog\"]',\n '[role=\"alertdialog\"]',\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 // 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 const count = elements.length;\n if (count > 0) {\n elements.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 // 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\"; // Added logger\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, 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, node) => {\n const href = (node as HTMLElement).getAttribute(\"href\");\n if (!content || content === \"#\") {\n return \"\"; // Remove if content is # or empty\n }\n if (!href) {\n return content; // Preserve content if href is missing or empty\n }\n return `[${content}](${href})`; // Standard link conversion\n },\n });\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 } 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","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 type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to extract the title (first H1 heading) from Markdown content.\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 const match = context.content.match(/^#\\s+(.*)$/m);\n if (match?.[1]) {\n title = match[1].trim();\n }\n context.metadata.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","/**\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 type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport type { ContentPipeline, ProcessedContent } 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 the given content.\n * Must be implemented by derived classes.\n */\n public canProcess(_rawContent: RawContent): 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<ProcessedContent> {\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","import { GreedySplitter, SemanticMarkdownSplitter } from \"../../splitter\";\nimport {\n SPLITTER_MAX_CHUNK_SIZE,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport { HtmlSanitizerMiddleware } from \"../middleware\";\nimport { HtmlCheerioParserMiddleware } from \"../middleware/HtmlCheerioParserMiddleware\";\nimport { HtmlLinkExtractorMiddleware } from \"../middleware/HtmlLinkExtractorMiddleware\";\nimport { HtmlMetadataExtractorMiddleware } from \"../middleware/HtmlMetadataExtractorMiddleware\";\nimport { HtmlPlaywrightMiddleware } from \"../middleware/HtmlPlaywrightMiddleware\";\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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing HTML content using middleware and semantic splitting with size optimization.\n * Converts HTML to clean markdown format then uses SemanticMarkdownSplitter for semantic chunking,\n * followed by GreedySplitter for universal size optimization.\n */\nexport class HtmlPipeline extends BasePipeline {\n private readonly playwrightMiddleware: HtmlPlaywrightMiddleware;\n private readonly standardMiddleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(\n preferredChunkSize = SPLITTER_PREFERRED_CHUNK_SIZE,\n maxChunkSize = SPLITTER_MAX_CHUNK_SIZE,\n ) {\n super();\n this.playwrightMiddleware = new HtmlPlaywrightMiddleware();\n this.standardMiddleware = [\n new HtmlCheerioParserMiddleware(),\n new HtmlMetadataExtractorMiddleware(),\n new HtmlLinkExtractorMiddleware(),\n new HtmlSanitizerMiddleware(),\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 SPLITTER_MIN_CHUNK_SIZE,\n preferredChunkSize,\n );\n }\n\n canProcess(rawContent: RawContent): boolean {\n return MimeTypeUtils.isHtml(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\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 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 textContent: typeof context.content === \"string\" ? context.content : \"\",\n metadata: context.metadata,\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 */\n public async close(): Promise<void> {\n await super.close(); // Call base class close (no-op by default)\n await this.playwrightMiddleware.closeBrowser();\n }\n}\n","import { GreedySplitter } from \"../../splitter\";\nimport { JsonDocumentSplitter } from \"../../splitter/JsonDocumentSplitter\";\nimport {\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing JSON content with semantic splitting and size optimization.\n * Handles JSON files by validating structure and using JsonDocumentSplitter for hierarchical splitting\n * that creates proper ContentChunks with semantic paths, followed by GreedySplitter for universal size optimization.\n */\nexport class JsonPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\n // JSON processing doesn't need complex middleware since we preserve raw structure\n this.middleware = [];\n\n // Create the two-phase splitting: semantic + size optimization\n const jsonSplitter = new JsonDocumentSplitter({\n preserveFormatting: true,\n });\n this.greedySplitter = new GreedySplitter(\n jsonSplitter,\n SPLITTER_MIN_CHUNK_SIZE,\n chunkSize,\n );\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return MimeTypeUtils.isJson(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\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.greedySplitter.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 context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\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.greedySplitter.splitText(context.content);\n\n return {\n textContent: context.content,\n metadata: context.metadata,\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 * Analyzes the structure of valid JSON for metadata\n */\n private analyzeJsonStructure(parsedJson: unknown): {\n type: string;\n depth: number;\n itemCount?: number;\n propertyCount?: number;\n } {\n if (Array.isArray(parsedJson)) {\n return {\n type: \"array\",\n depth: this.calculateDepth(parsedJson),\n itemCount: parsedJson.length,\n };\n } else if (typeof parsedJson === \"object\" && parsedJson !== null) {\n const obj = parsedJson as Record<string, unknown>;\n return {\n type: \"object\",\n depth: this.calculateDepth(parsedJson),\n propertyCount: Object.keys(obj).length,\n };\n } else {\n return {\n type: typeof parsedJson,\n depth: 1,\n };\n }\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 { GreedySplitter, SemanticMarkdownSplitter } from \"../../splitter\";\nimport {\n SPLITTER_MAX_CHUNK_SIZE,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing Markdown content using middleware and semantic splitting with size optimization.\n * Uses SemanticMarkdownSplitter for content-type-aware semantic chunking,\n * followed by GreedySplitter for universal size optimization.\n */\nexport class MarkdownPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(\n preferredChunkSize = SPLITTER_PREFERRED_CHUNK_SIZE,\n maxChunkSize = SPLITTER_MAX_CHUNK_SIZE,\n ) {\n super();\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 SPLITTER_MIN_CHUNK_SIZE,\n preferredChunkSize,\n );\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return (\n MimeTypeUtils.isMarkdown(rawContent.mimeType) ||\n MimeTypeUtils.isText(rawContent.mimeType)\n );\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\n metadata: {},\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 textContent: typeof context.content === \"string\" ? context.content : \"\",\n metadata: context.metadata,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n}\n","/**\n * TextDocumentSplitter - Simple text-based document splitter\n *\n * This splitter provides basic text splitting functionality for source code and plain text files.\n * It uses line-based splitting with basic hierarchical organization. This serves as a foundation\n * for source code files until a more sophisticated syntax-aware splitter (with TreeSitter) is implemented.\n */\n\nimport { MinimumChunkSizeError } from \"./errors\";\nimport type { ContentChunk, DocumentSplitter } from \"./types\";\n\n/**\n * Configuration options for text document splitting\n */\nexport interface TextDocumentSplitterOptions {\n /** Maximum size for individual chunks */\n maxChunkSize: number;\n /** Minimum lines to include in each chunk when possible */\n minLinesPerChunk: number;\n /** Whether to detect and preserve language from content */\n detectLanguage: boolean;\n}\n\n/**\n * Simple document splitter for plain text and source code files.\n * Uses line-based splitting with basic hierarchical organization.\n * Suitable for source code until TreeSitter-based syntax-aware splitting is implemented.\n */\nexport class TextDocumentSplitter implements DocumentSplitter {\n private options: TextDocumentSplitterOptions;\n\n constructor(options: Partial<TextDocumentSplitterOptions> = {}) {\n this.options = {\n maxChunkSize: options.maxChunkSize ?? 2000,\n minLinesPerChunk: options.minLinesPerChunk ?? 5,\n detectLanguage: options.detectLanguage ?? true,\n };\n }\n\n async splitText(content: string, contentType?: string): Promise<ContentChunk[]> {\n if (!content.trim()) {\n return [];\n }\n\n const lines = content.split(\"\\n\");\n const chunks: ContentChunk[] = [];\n let currentChunkLines: string[] = [];\n let chunkIndex = 1;\n\n // Detect language from content type or content\n const language = this.detectLanguageFromContent(content, contentType);\n\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n currentChunkLines.push(line);\n\n // Calculate current chunk size\n const currentChunkContent = currentChunkLines.join(\"\\n\");\n const currentChunkSize = currentChunkContent.length;\n\n // Check if we should create a chunk\n const shouldCreateChunk = this.shouldCreateChunk(\n currentChunkSize,\n currentChunkLines.length,\n i === lines.length - 1, // is last line\n );\n\n if (shouldCreateChunk) {\n // Check if chunk is too large\n if (\n currentChunkSize > this.options.maxChunkSize &&\n currentChunkLines.length > 1\n ) {\n // Remove the last line and create chunk with remaining lines\n const lastLine = currentChunkLines.pop();\n if (!lastLine) continue; // Should not happen due to length check above\n\n const chunkContent = currentChunkLines.join(\"\\n\");\n\n if (chunkContent.trim()) {\n chunks.push(this.createChunk(chunkContent, chunkIndex, language));\n chunkIndex++;\n }\n\n // Start new chunk with the last line\n currentChunkLines = [lastLine];\n } else {\n // Create chunk with current content\n if (currentChunkContent.trim()) {\n chunks.push(this.createChunk(currentChunkContent, chunkIndex, language));\n chunkIndex++;\n }\n currentChunkLines = [];\n }\n }\n }\n\n // Handle remaining lines\n if (currentChunkLines.length > 0) {\n const remainingContent = currentChunkLines.join(\"\\n\");\n if (remainingContent.trim()) {\n // Check if this final chunk is too large\n if (remainingContent.length > this.options.maxChunkSize) {\n throw new MinimumChunkSizeError(\n remainingContent.length,\n this.options.maxChunkSize,\n );\n }\n chunks.push(this.createChunk(remainingContent, chunkIndex, language));\n }\n }\n\n return chunks;\n }\n\n /**\n * Determines if a chunk should be created based on size and line count\n */\n private shouldCreateChunk(\n currentSize: number,\n currentLineCount: number,\n isLastLine: boolean,\n ): boolean {\n // Always create chunk if it's the last line\n if (isLastLine) {\n return true;\n }\n\n // Create chunk if we've reached minimum lines and a reasonable size\n if (\n currentLineCount >= this.options.minLinesPerChunk &&\n currentSize >= this.options.maxChunkSize * 0.5\n ) {\n return true;\n }\n\n // Create chunk if we're approaching max size\n if (currentSize >= this.options.maxChunkSize * 0.8) {\n return true;\n }\n\n return false;\n }\n\n /**\n * Creates a ContentChunk with appropriate metadata\n */\n private createChunk(\n content: string,\n chunkIndex: number,\n language?: string,\n ): ContentChunk {\n // Create basic hierarchical path\n const pathElements: string[] = [];\n\n if (language) {\n pathElements.push(`${language}-file`);\n } else {\n pathElements.push(\"text-file\");\n }\n\n // Add section identifier\n pathElements.push(`section-${chunkIndex}`);\n\n return {\n types: [\"code\"],\n content: content.trim(),\n section: {\n level: pathElements.length,\n path: pathElements,\n },\n };\n }\n\n /**\n * Attempts to detect programming language from content or content type\n */\n private detectLanguageFromContent(\n content: string,\n contentType?: string,\n ): string | undefined {\n if (!this.options.detectLanguage) {\n return undefined;\n }\n\n // Try to detect from content type first\n if (contentType) {\n const languageFromMime = this.getLanguageFromMimeType(contentType);\n if (languageFromMime) {\n return languageFromMime;\n }\n }\n\n // Try to detect from content patterns\n return this.getLanguageFromContentPatterns(content);\n }\n\n /**\n * Maps MIME types to language names\n */\n private getLanguageFromMimeType(contentType: string): string | undefined {\n const mimeToLanguage: Record<string, string> = {\n \"text/javascript\": \"javascript\",\n \"application/javascript\": \"javascript\",\n \"text/typescript\": \"typescript\",\n \"application/typescript\": \"typescript\",\n \"text/x-python\": \"python\",\n \"application/x-python\": \"python\",\n \"text/x-java\": \"java\",\n \"text/x-c\": \"c\",\n \"text/x-c++\": \"cpp\",\n \"text/x-csharp\": \"csharp\",\n \"text/x-go\": \"go\",\n \"text/x-rust\": \"rust\",\n \"text/x-php\": \"php\",\n \"text/x-ruby\": \"ruby\",\n \"text/x-shell\": \"bash\",\n \"application/x-sh\": \"bash\",\n };\n\n return mimeToLanguage[contentType.toLowerCase()];\n }\n\n /**\n * Attempts to detect language from content patterns\n */\n private getLanguageFromContentPatterns(content: string): string | undefined {\n const firstLines = content.split(\"\\n\").slice(0, 10).join(\"\\n\");\n\n // Check for common patterns\n if (\n firstLines.includes(\"#!/usr/bin/env python\") ||\n firstLines.includes(\"#!/usr/bin/python\")\n ) {\n return \"python\";\n }\n if (firstLines.includes(\"#!/bin/bash\") || firstLines.includes(\"#!/usr/bin/bash\")) {\n return \"bash\";\n }\n if (\n firstLines.includes(\"#!/usr/bin/env node\") ||\n firstLines.includes(\"#!/usr/bin/node\")\n ) {\n return \"javascript\";\n }\n\n // Check for syntax patterns\n if (\n /import\\s+.*\\s+from\\s+['\"]/.test(firstLines) &&\n /export\\s+(default\\s+)?/.test(firstLines)\n ) {\n return \"javascript\";\n }\n if (/interface\\s+\\w+|type\\s+\\w+\\s*=/.test(firstLines)) {\n return \"typescript\";\n }\n if (/def\\s+\\w+\\s*\\(|import\\s+\\w+|from\\s+\\w+\\s+import/.test(firstLines)) {\n return \"python\";\n }\n if (/class\\s+\\w+|public\\s+(static\\s+)?void\\s+main/.test(firstLines)) {\n return \"java\";\n }\n if (/#include\\s*<|int\\s+main\\s*\\(/.test(firstLines)) {\n return \"c\";\n }\n\n return undefined;\n }\n}\n","import { GreedySplitter } from \"../../splitter\";\nimport { TextDocumentSplitter } from \"../../splitter/TextDocumentSplitter\";\nimport {\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing source code content with semantic splitting and size optimization.\n * Handles programming language files by using TextDocumentSplitter for line-based splitting\n * with proper language detection, followed by GreedySplitter for universal size optimization.\n */\nexport class SourceCodePipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: GreedySplitter;\n\n constructor(chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\n // Source code processing uses minimal middleware since we preserve raw structure\n this.middleware = [];\n\n // Create the two-phase splitting: semantic + size optimization\n const textSplitter = new TextDocumentSplitter({ maxChunkSize: chunkSize });\n this.splitter = new GreedySplitter(textSplitter, SPLITTER_MIN_CHUNK_SIZE, chunkSize);\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return MimeTypeUtils.isSourceCode(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\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 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\";\nimport { TextDocumentSplitter } from \"../../splitter/TextDocumentSplitter\";\nimport {\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Fallback pipeline for processing text content with basic splitting and size optimization.\n * Handles text-based content types by using TextDocumentSplitter for simple line-based splitting\n * followed by GreedySplitter for universal size optimization. This pipeline uses MIME type filtering\n * and binary detection to ensure it only processes appropriate text content.\n */\nexport class TextPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: GreedySplitter;\n\n constructor(chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\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({ maxChunkSize: chunkSize });\n this.splitter = new GreedySplitter(textSplitter, SPLITTER_MIN_CHUNK_SIZE, chunkSize);\n }\n\n canProcess(rawContent: RawContent): 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(rawContent.mimeType)) {\n return false;\n }\n\n // Second check: binary detection via null bytes\n if (MimeTypeUtils.isBinary(rawContent.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<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\n metadata: {\n contentType: rawContent.mimeType || \"text/plain\",\n isGenericText: true,\n },\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 textContent: context.content,\n metadata: context.metadata,\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 { SPLITTER_PREFERRED_CHUNK_SIZE } from \"../../utils/config\";\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 * Configuration options for pipeline creation\n */\nexport interface PipelineConfiguration {\n chunkSizes?: {\n /** Preferred chunk size for most content types */\n preferred?: number;\n /** Maximum chunk size for content that shouldn't be split aggressively */\n max?: number;\n };\n}\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, 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 * @param config - Optional configuration for pipeline chunk sizes\n * @returns Array of content pipelines in processing order\n */\n public static createStandardPipelines(\n config?: PipelineConfiguration,\n ): ContentPipeline[] {\n const preferredChunkSize =\n config?.chunkSizes?.preferred ?? SPLITTER_PREFERRED_CHUNK_SIZE;\n const maxChunkSize = config?.chunkSizes?.max ?? 2000;\n\n return [\n new JsonPipeline(preferredChunkSize),\n new SourceCodePipeline(preferredChunkSize),\n new HtmlPipeline(preferredChunkSize, maxChunkSize),\n new MarkdownPipeline(preferredChunkSize, maxChunkSize),\n new TextPipeline(preferredChunkSize), // Universal fallback - must be last\n ];\n }\n}\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 { 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 // minimatch expects no leading slash for relative globs, but we keep it for consistency\n // so we strip the leading slash for minimatch\n return minimatch(normalizedPath.replace(/^\\//, \"\"), pattern, { 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 */\nexport function shouldIncludeUrl(\n url: string,\n includePatterns?: string[],\n excludePatterns?: string[],\n): boolean {\n // Always match from a leading slash for path-based globs\n const path = extractPathAndQuery(url);\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\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 if (\n matchesAnyPattern(normalizedPath, effectiveExcludePatterns) ||\n (basename && matchesAnyPattern(basename, stripSlash(effectiveExcludePatterns)))\n )\n return false;\n if (!includePatterns || includePatterns.length === 0) return true;\n return (\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 { Document, ProgressCallback } from \"../../types\";\nimport { DEFAULT_MAX_PAGES } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { normalizeUrl, type UrlNormalizerOptions } from \"../../utils/url\";\nimport type { ScraperOptions, ScraperProgress, ScraperStrategy } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { isInScope } from \"../utils/scope\";\n\n// Define defaults for optional options\nconst DEFAULT_MAX_DEPTH = 3;\nconst DEFAULT_CONCURRENCY = 3;\n\nexport type QueueItem = {\n url: string;\n depth: number;\n};\n\nexport interface BaseScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n}\n\nexport abstract class BaseScraperStrategy implements ScraperStrategy {\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\n constructor(options: BaseScraperStrategyOptions = {}) {\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 A list of URLs to add to the queue\n */\n protected abstract processItem(\n item: QueueItem,\n options: ScraperOptions,\n progressCallback?: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<{\n document?: Document;\n links?: string[];\n finalUrl?: string; // Effective fetched URL (post-redirect)\n }>;\n\n // Removed getProcessor method as processing is now handled by strategies using middleware pipelines\n\n protected async processBatch(\n batch: QueueItem[],\n baseUrl: URL,\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<QueueItem[]> {\n const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\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 ?? DEFAULT_MAX_DEPTH;\n if (item.depth > maxDepth) {\n return [];\n }\n\n try {\n // Pass signal to processItem\n const result = await this.processItem(item, options, undefined, signal);\n // If this is the root (depth 0) and we have a finalUrl differing from original, set canonicalBaseUrl\n if (item.depth === 0 && !this.canonicalBaseUrl && result?.finalUrl) {\n try {\n const finalUrlStr = result.finalUrl as string;\n const original = new URL(options.url);\n const finalUrlObj = new URL(finalUrlStr);\n if (\n finalUrlObj.href !== original.href &&\n (finalUrlObj.protocol === \"http:\" || finalUrlObj.protocol === \"https:\")\n ) {\n this.canonicalBaseUrl = finalUrlObj;\n logger.debug(\n `Updated scope base after redirect: ${original.href} -> ${finalUrlObj.href}`,\n );\n } else {\n this.canonicalBaseUrl = original;\n }\n } catch {\n // Ignore canonical base errors\n this.canonicalBaseUrl = new URL(options.url);\n }\n }\n\n if (result.document) {\n this.pageCount++;\n // maxDepth already resolved above\n logger.info(\n `🌐 Scraping page ${this.pageCount}/${this.effectiveTotal} (depth ${item.depth}/${maxDepth}): ${item.url}`,\n );\n await progressCallback({\n pagesScraped: this.pageCount,\n totalPages: this.effectiveTotal,\n totalDiscovered: this.totalDiscovered,\n currentUrl: item.url,\n depth: item.depth,\n maxDepth: maxDepth,\n document: result.document,\n });\n }\n\n const nextItems = result.links || [];\n return nextItems\n .map((value) => {\n try {\n const targetUrl = new URL(value, baseUrl);\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 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<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<void> {\n this.visited.clear();\n this.pageCount = 0;\n this.totalDiscovered = 1; // Start with the initial URL (unlimited counter)\n this.effectiveTotal = 1; // Start with the initial URL (limited counter)\n\n this.canonicalBaseUrl = new URL(options.url);\n let baseUrl = this.canonicalBaseUrl;\n const queue = [{ url: options.url, depth: 0 } satisfies QueueItem];\n\n // Track values we've seen (either queued or visited)\n this.visited.add(normalizeUrl(options.url, this.options.urlNormalizerOptions));\n\n // Resolve optional values to defaults using temporary variables\n const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\n const maxConcurrency = options.maxConcurrency ?? DEFAULT_CONCURRENCY;\n\n while (queue.length > 0 && this.pageCount < maxPages) {\n // Use variable\n // Check for cancellation at the start of each loop iteration\n if (signal?.aborted) {\n logger.debug(\"Scraping cancelled by signal.\");\n throw new CancellationError(\"Scraping cancelled by signal\");\n }\n\n const remainingPages = maxPages - this.pageCount; // Use variable\n if (remainingPages <= 0) {\n break;\n }\n\n const batchSize = Math.min(\n maxConcurrency, // Use variable\n remainingPages,\n queue.length,\n );\n\n const batch = queue.splice(0, batchSize);\n // Pass signal to processBatch\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 { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { HttpFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../pipelines/types\";\nimport type { ScraperOptions, ScraperProgress } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { BaseScraperStrategy, type QueueItem } from \"./BaseScraperStrategy\";\n\ninterface GitHubRepoInfo {\n owner: string;\n repo: string;\n branch?: string;\n}\n\ninterface GitHubTreeItem {\n path: string;\n type: \"blob\" | \"tree\";\n sha: string;\n size?: number;\n url: string;\n}\n\ninterface GitHubTreeResponse {\n sha: string;\n url: string;\n tree: GitHubTreeItem[];\n truncated: boolean;\n}\n\n/**\n * GitHubScraperStrategy handles native repository crawling by accessing GitHub's tree API\n * to discover repository structure and fetching raw file contents. This treats repositories\n * more like file systems rather than web pages.\n *\n * Features:\n * - Uses GitHub tree API for efficient repository structure discovery\n * - Fetches raw file contents from raw.githubusercontent.com\n * - Processes all text files (source code, markdown, documentation, etc.)\n * - Supports branch-specific crawling (defaults to main/default branch)\n * - Automatically detects repository default branch when no branch specified\n * - Filters out binary files and processes only text-based content\n *\n * Note: Wiki pages are not currently supported in this native mode. For wiki access,\n * consider using the web scraping approach or a separate scraping job.\n */\nexport class GitHubScraperStrategy extends BaseScraperStrategy {\n private readonly httpFetcher = new HttpFetcher();\n private readonly pipelines: ContentPipeline[];\n private resolvedBranch?: string; // Cache the resolved default branch\n\n constructor() {\n super();\n this.pipelines = PipelineFactory.createStandardPipelines();\n }\n\n canHandle(url: string): boolean {\n const { hostname } = new URL(url);\n return [\"github.com\", \"www.github.com\"].includes(hostname);\n }\n\n /**\n * Override shouldProcessUrl to handle github-file:// URLs specially.\n * These URLs bypass scope checking since they're internal file references.\n */\n protected shouldProcessUrl(url: string, options: ScraperOptions): boolean {\n // For github-file:// URLs, only apply include/exclude patterns, skip scope checking\n if (url.startsWith(\"github-file://\")) {\n const filePath = url.replace(\"github-file://\", \"\");\n return shouldIncludeUrl(filePath, options.includePatterns, options.excludePatterns);\n }\n\n // For regular URLs, use the base implementation\n return super.shouldProcessUrl(url, options);\n }\n\n /**\n * Parses a GitHub URL to extract repository information.\n */\n parseGitHubUrl(url: string): GitHubRepoInfo {\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 from URL if present (e.g., /tree/branch-name/)\n const branchMatch = parsedUrl.pathname.match(/\\/tree\\/([^/]+)/);\n const branch = branchMatch?.[1];\n\n return { owner, repo, branch };\n }\n\n /**\n * Fetches the repository tree structure from GitHub API.\n * Uses 'HEAD' to get the default branch if no branch is specified.\n */\n async fetchRepositoryTree(\n repoInfo: GitHubRepoInfo,\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 try {\n // Get repository information to find the default branch\n const repoUrl = `https://api.github.com/repos/${owner}/${repo}`;\n logger.debug(`Fetching repository info: ${repoUrl}`);\n\n const repoContent = await this.httpFetcher.fetch(repoUrl, { signal });\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 targetBranch = repoData.default_branch;\n\n logger.debug(`Using default branch: ${targetBranch}`);\n } catch (error) {\n logger.warn(`⚠️ Could not fetch default branch, using 'main': ${error}`);\n targetBranch = \"main\";\n }\n }\n\n // Cache the resolved branch for file fetching\n this.resolvedBranch = targetBranch;\n\n const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${targetBranch}?recursive=1`;\n\n logger.debug(`Fetching repository tree: ${treeUrl}`);\n\n const rawContent = await this.httpFetcher.fetch(treeUrl, { signal });\n const content =\n typeof rawContent.content === \"string\"\n ? rawContent.content\n : rawContent.content.toString(\"utf-8\");\n const treeData = JSON.parse(content) as GitHubTreeResponse;\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 // Only process blob (file) items, not trees (directories)\n if (item.type !== \"blob\") {\n return false;\n }\n\n const path = item.path;\n\n // Whitelist of text-based file extensions that we can process\n const textExtensions = [\n // Documentation\n \".md\",\n \".mdx\",\n \".txt\",\n \".rst\",\n \".adoc\",\n \".asciidoc\",\n\n // Web technologies\n \".html\",\n \".htm\",\n \".xml\",\n \".css\",\n \".scss\",\n \".sass\",\n \".less\",\n\n // Programming languages\n \".js\",\n \".jsx\",\n \".ts\",\n \".tsx\",\n \".py\",\n \".java\",\n \".c\",\n \".cpp\",\n \".cc\",\n \".cxx\",\n \".h\",\n \".hpp\",\n \".cs\",\n \".go\",\n \".rs\",\n \".rb\",\n \".php\",\n \".swift\",\n \".kt\",\n \".scala\",\n \".clj\",\n \".cljs\",\n \".hs\",\n \".elm\",\n \".dart\",\n \".r\",\n \".m\",\n \".mm\",\n \".sh\",\n \".bash\",\n \".zsh\",\n \".fish\",\n \".ps1\",\n \".bat\",\n \".cmd\",\n\n // Configuration and data\n \".json\",\n \".yaml\",\n \".yml\",\n \".toml\",\n \".ini\",\n \".cfg\",\n \".conf\",\n \".properties\",\n \".env\",\n \".gitignore\",\n \".dockerignore\",\n \".gitattributes\",\n \".editorconfig\",\n\n // Build and package management\n \".gradle\",\n \".pom\",\n \".sbt\",\n \".maven\",\n \".cmake\",\n \".make\",\n \".dockerfile\",\n \".mod\", // Go modules (go.mod)\n \".sum\", // Go checksums (go.sum)\n\n // Other text formats\n \".sql\",\n \".graphql\",\n \".gql\",\n \".proto\",\n \".thrift\",\n \".avro\",\n \".csv\",\n \".tsv\",\n \".log\",\n ];\n\n const pathLower = path.toLowerCase();\n\n // Check for known text extensions\n const hasTextExtension = textExtensions.some((ext) => pathLower.endsWith(ext));\n\n // Check for compound extensions and special cases\n const hasCompoundExtension =\n pathLower.includes(\".env.\") || // .env.example, .env.local, etc.\n pathLower.endsWith(\".env\") ||\n pathLower.includes(\".config.\") || // webpack.config.js, etc.\n pathLower.includes(\".lock\"); // package-lock.json, etc.\n\n // Also include files without extensions that are commonly text files\n const fileName = path.split(\"/\").pop() || \"\";\n const fileNameLower = fileName.toLowerCase();\n const commonTextFiles = [\n // Documentation files without extensions\n \"readme\",\n \"license\",\n \"changelog\",\n \"contributing\",\n \"authors\",\n \"maintainers\",\n\n // Build files without extensions\n \"dockerfile\",\n \"makefile\",\n \"rakefile\",\n \"gemfile\",\n \"podfile\",\n \"cartfile\",\n \"brewfile\",\n \"procfile\",\n \"vagrantfile\",\n \"gulpfile\",\n \"gruntfile\",\n\n // Configuration files (dotfiles)\n \".prettierrc\",\n \".eslintrc\",\n \".babelrc\",\n \".nvmrc\",\n \".npmrc\",\n ];\n\n const isCommonTextFile = commonTextFiles.some((name) => {\n if (name.startsWith(\".\")) {\n // For dotfiles, match exactly or with additional extension (e.g., .prettierrc.js)\n return fileNameLower === name || fileNameLower.startsWith(`${name}.`);\n }\n // For regular files, match exactly or with extension\n return fileNameLower === name || fileNameLower.startsWith(`${name}.`);\n });\n\n // Process file if it has a text extension, compound extension, or is a common text file\n if (!hasTextExtension && !hasCompoundExtension && !isCommonTextFile) {\n return false;\n }\n\n // Apply user-defined include/exclude patterns (use the file path directly)\n return shouldIncludeUrl(path, options.includePatterns, options.excludePatterns);\n }\n\n /**\n * Fetches the raw content of a file from GitHub.\n */\n async fetchFileContent(\n repoInfo: GitHubRepoInfo,\n filePath: string,\n signal?: AbortSignal,\n ): Promise<RawContent> {\n const { owner, repo } = repoInfo;\n // Use resolved branch if available, otherwise use provided branch or default to main\n const branch = this.resolvedBranch || repoInfo.branch || \"main\";\n const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`;\n\n const rawContent = await this.httpFetcher.fetch(rawUrl, { signal });\n\n // Override GitHub's generic 'text/plain' MIME type with file extension-based detection\n const detectedMimeType = MimeTypeUtils.detectMimeTypeFromPath(filePath);\n if (detectedMimeType && rawContent.mimeType === \"text/plain\") {\n return {\n ...rawContent,\n mimeType: detectedMimeType,\n };\n }\n\n return rawContent;\n }\n\n protected async processItem(\n item: QueueItem,\n options: ScraperOptions,\n _progressCallback?: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal,\n ): Promise<{ document?: Document; links?: string[] }> {\n // Parse the URL to get repository information\n const repoInfo = this.parseGitHubUrl(options.url);\n\n // For the initial item, fetch the repository tree\n if (item.depth === 0) {\n logger.info(\n `🗂️ Discovering repository structure for ${repoInfo.owner}/${repoInfo.repo}`,\n );\n\n const { tree, resolvedBranch } = await this.fetchRepositoryTree(repoInfo, signal);\n const fileItems = tree.tree.filter((treeItem) =>\n this.shouldProcessFile(treeItem, options),\n );\n\n logger.info(\n `📁 Found ${fileItems.length} processable files in repository (branch: ${resolvedBranch})`,\n );\n\n // Convert tree items to URLs for the queue\n const links = fileItems.map((treeItem) => `github-file://${treeItem.path}`);\n\n return { links };\n }\n\n // Process individual files\n if (item.url.startsWith(\"github-file://\")) {\n const filePath = item.url.replace(\"github-file://\", \"\");\n\n logger.info(\n `🗂️ Processing file ${this.pageCount}/${options.maxPages}: ${filePath}`,\n );\n\n const rawContent = await this.fetchFileContent(repoInfo, filePath, signal);\n\n // Process content through appropriate pipeline\n let processed: Awaited<ReturnType<ContentPipeline[\"process\"]>> | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${filePath})`,\n );\n processed = await pipeline.process(rawContent, options, 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 { document: undefined, links: [] };\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/${repoInfo.owner}/${repoInfo.repo}/blob/${this.resolvedBranch || repoInfo.branch || \"main\"}/${filePath}`;\n\n return {\n document: {\n content: typeof processed.textContent === \"string\" ? processed.textContent : \"\",\n metadata: {\n url: githubUrl,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : filePath.split(\"/\").pop() || \"Untitled\",\n library: options.library,\n version: options.version,\n },\n contentType: rawContent.mimeType, // Preserve the detected MIME type\n } satisfies Document,\n links: [], // Always return empty links array for individual files\n };\n }\n\n return { document: undefined, links: [] };\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal,\n ): Promise<void> {\n // Validate it's a GitHub URL\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 return super.scrape(options, progressCallback, signal);\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","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport { FileFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../pipelines/types\";\nimport type { ScraperOptions, ScraperProgress } from \"../types\";\nimport { BaseScraperStrategy, type QueueItem } 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() {\n super();\n this.pipelines = PipelineFactory.createStandardPipelines();\n }\n\n canHandle(url: string): boolean {\n return url.startsWith(\"file://\");\n }\n\n protected async processItem(\n item: QueueItem,\n options: ScraperOptions,\n _progressCallback?: ProgressCallback<ScraperProgress>,\n _signal?: AbortSignal,\n ): Promise<{ document?: Document; links?: string[] }> {\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 const stats = await fs.stat(filePath);\n\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) => `file://${path.join(filePath, name)}`)\n .filter((url) => this.shouldProcessUrl(url, options));\n return { links };\n }\n\n logger.info(`🗂️ Processing file ${this.pageCount}/${options.maxPages}: ${filePath}`);\n\n const rawContent: RawContent = await this.fileFetcher.fetch(item.url);\n\n let processed: Awaited<ReturnType<ContentPipeline[\"process\"]>> | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${filePath})`,\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 ${filePath}. Skipping processing.`,\n );\n return { document: undefined, links: [] };\n }\n\n for (const err of processed.errors) {\n logger.warn(`⚠️ Processing error for ${filePath}: ${err.message}`);\n }\n\n return {\n document: {\n content: typeof processed.textContent === \"string\" ? processed.textContent : \"\",\n contentType: rawContent.mimeType,\n metadata: {\n url: rawContent.source,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : \"Untitled\",\n library: options.library,\n version: options.version,\n },\n } satisfies Document,\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","import type { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport type { UrlNormalizerOptions } from \"../../utils/url\";\nimport { HttpFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, ProcessedContent } from \"../pipelines/types\";\nimport type { ScraperOptions, ScraperProgress } from \"../types\";\nimport { isInScope } from \"../utils/scope\";\nimport { BaseScraperStrategy, type QueueItem } from \"./BaseScraperStrategy\";\n\nexport interface WebScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n shouldFollowLink?: (baseUrl: URL, targetUrl: URL) => boolean;\n}\n\nexport class WebScraperStrategy extends BaseScraperStrategy {\n private readonly httpFetcher = new HttpFetcher();\n private readonly shouldFollowLinkFn?: (baseUrl: URL, targetUrl: URL) => boolean;\n private readonly pipelines: ContentPipeline[];\n\n constructor(options: WebScraperStrategyOptions = {}) {\n super({ urlNormalizerOptions: options.urlNormalizerOptions });\n this.shouldFollowLinkFn = options.shouldFollowLink;\n this.pipelines = PipelineFactory.createStandardPipelines();\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 _progressCallback?: ProgressCallback<ScraperProgress>, // Base class passes it, but not used here\n signal?: AbortSignal, // Add signal\n ): Promise<{ document?: Document; links?: string[]; finalUrl?: string }> {\n const { url } = item;\n\n try {\n // Define fetch options, passing signal, followRedirects, and headers\n const fetchOptions = {\n signal,\n followRedirects: options.followRedirects,\n headers: options.headers, // Forward custom headers\n };\n\n // Pass options to fetcher\n const rawContent: RawContent = await this.httpFetcher.fetch(url, fetchOptions);\n\n // --- Start Pipeline Processing ---\n let processed: ProcessedContent | undefined;\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${url})`,\n );\n processed = await pipeline.process(rawContent, options, this.httpFetcher);\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for URL ${url}. Skipping processing.`,\n );\n return { document: undefined, links: [] };\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 { document: undefined, links: processed.links };\n }\n\n // Determine base for scope filtering:\n // For depth 0 (initial page) use the final fetched URL (rawContent.source) so protocol/host redirects don't drop links.\n // For deeper pages, use canonicalBaseUrl (set after first page) or fallback to original.\n const baseUrl =\n item.depth === 0\n ? new URL(rawContent.source)\n : (this.canonicalBaseUrl ?? new URL(options.url));\n\n const filteredLinks = processed.links.filter((link) => {\n try {\n const targetUrl = new URL(link);\n const scope = options.scope || \"subpages\";\n return (\n isInScope(baseUrl, targetUrl, scope) &&\n (!this.shouldFollowLinkFn || this.shouldFollowLinkFn(baseUrl, targetUrl))\n );\n } catch {\n return false;\n }\n });\n\n return {\n document: {\n content: processed.textContent,\n metadata: {\n url,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : \"Untitled\",\n library: options.library,\n version: options.version,\n ...processed.metadata,\n },\n } satisfies Document,\n links: filteredLinks,\n finalUrl: rawContent.source,\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 /**\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","import type { ProgressCallback } from \"../../types\";\nimport type { ScraperOptions, ScraperProgress, 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() {\n this.defaultStrategy = new WebScraperStrategy({\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<ScraperProgress>,\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 { ScraperOptions, ScraperProgress, 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() {\n this.defaultStrategy = new WebScraperStrategy({\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<ScraperProgress>,\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 { logger } from \"../utils\";\nimport { ScraperError } from \"../utils/errors\";\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\nexport class ScraperRegistry {\n private strategies: ScraperStrategy[];\n\n constructor() {\n this.strategies = [\n new NpmScraperStrategy(),\n new PyPiScraperStrategy(),\n new GitHubScraperStrategy(),\n new WebScraperStrategy(),\n new LocalFileStrategy(),\n ];\n }\n\n getStrategy(url: string): ScraperStrategy {\n validateUrl(url);\n const strategy = this.strategies.find((s) => s.canHandle(url));\n if (!strategy) {\n throw new ScraperError(`No strategy found for URL: ${url}`);\n }\n logger.debug(`Using strategy \"${strategy.constructor.name}\" for URL: ${url}`);\n return strategy;\n }\n\n /**\n * Cleanup all registered strategies to prevent resource leaks.\n * Should be called when the registry is no longer needed.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled(this.strategies.map((strategy) => strategy.cleanup?.()));\n }\n}\n","import type { ProgressCallback } from \"../types\";\nimport { ScraperError } from \"../utils/errors\";\nimport type { ScraperRegistry } from \"./ScraperRegistry\";\nimport type { ScraperOptions, ScraperProgress } from \"./types\";\n\n/**\n * Orchestrates document scraping operations using registered scraping strategies.\n * Automatically selects appropriate strategy based on URL patterns.\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 */\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add optional signal parameter\n ): Promise<void> {\n // Find strategy for this URL\n const strategy = this.registry.getStrategy(options.url);\n if (!strategy) {\n throw new ScraperError(`No scraper strategy found for URL: ${options.url}`, false);\n }\n\n // Pass the signal down to the strategy\n await strategy.scrape(options, progressCallback, signal);\n }\n\n /**\n * Cleanup the scraper registry and all its strategies.\n * Should be called when the service is no longer needed.\n */\n async cleanup(): Promise<void> {\n await this.registry.cleanup();\n }\n}\n","import type { ScraperService } from \"../scraper\";\nimport type { ScraperProgress } from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { logger } from \"../utils/logger\";\nimport { CancellationError } from \"./errors\";\nimport type { InternalPipelineJob, PipelineManagerCallbacks } from \"./types\";\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 - Callbacks provided by the manager for reporting.\n */\n async executeJob(\n job: InternalPipelineJob,\n callbacks: PipelineManagerCallbacks,\n ): Promise<void> {\n const {\n id: jobId,\n library,\n version,\n sourceUrl,\n scraperOptions,\n abortController,\n } = 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 await this.store.removeAllDocuments(library, version);\n logger.info(\n `💾 Cleared store for ${library}@${version || \"[no version]\"} before scraping.`,\n );\n\n // Construct runtime options from job context + stored configuration\n const runtimeOptions = {\n url: sourceUrl ?? \"\",\n library,\n version,\n ...scraperOptions,\n };\n\n // --- Core Job Logic ---\n await this.scraperService.scrape(\n runtimeOptions,\n async (progress: ScraperProgress) => {\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 if (progress.document) {\n try {\n await this.store.addDocument(library, version, {\n pageContent: progress.document.content,\n metadata: {\n ...progress.document.metadata,\n mimeType: progress.document.contentType, // Pass contentType as mimeType in metadata\n },\n });\n logger.debug(\n `[${jobId}] Stored document: ${progress.document.metadata.url}`,\n );\n } catch (docError) {\n logger.error(\n `❌ [${jobId}] Failed to store document ${progress.document.metadata.url}: ${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.document,\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","import type { ScraperProgress } from \"../scraper/types\";\nimport type { VersionScraperOptions, VersionStatus } from \"../store/types\";\nimport type { Document } from \"../types\"; // Use local Document type\n\n/**\n * Represents the possible states of a pipeline job.\n */\nexport enum PipelineJobStatus {\n QUEUED = \"queued\",\n RUNNING = \"running\",\n COMPLETED = \"completed\",\n FAILED = \"failed\",\n CANCELLING = \"cancelling\",\n CANCELLED = \"cancelled\",\n}\n\n/**\n * Public interface for pipeline jobs exposed through API boundaries.\n * Contains only serializable fields suitable for JSON transport.\n */\nexport interface PipelineJob {\n /** Unique identifier for the job. */\n id: string;\n /** The library name associated with the job. */\n library: string;\n /** The library version associated with the job. */\n version: string | null;\n /** Current pipeline status of the job. */\n status: PipelineJobStatus;\n /** Detailed progress information. */\n progress: ScraperProgress | null;\n /** Error information if the job failed. */\n error: { message: string } | null;\n /** Timestamp when the job was created. */\n createdAt: Date;\n /** Timestamp when the job started running. */\n startedAt: Date | null;\n /** Timestamp when the job finished (completed, failed, or cancelled). */\n finishedAt: Date | null;\n /** Database version ID for direct updates. */\n versionId?: number;\n /** Database version status (authoritative). */\n versionStatus?: VersionStatus;\n /** Current number of pages processed. */\n progressPages?: number;\n /** Maximum number of pages to process. */\n progressMaxPages?: number;\n /** Database error message (more detailed than Error object). */\n errorMessage?: string | null;\n /** Last update timestamp from database. */\n updatedAt?: Date;\n /** Original scraping URL. */\n sourceUrl: string | null;\n /** Stored scraper options for reproducibility. */\n scraperOptions: VersionScraperOptions | null;\n}\n\n/**\n * Internal pipeline job representation used within PipelineManager.\n * Contains non-serializable fields for job management and control.\n */\nexport interface InternalPipelineJob extends Omit<PipelineJob, \"version\" | \"error\"> {\n /** The library version associated with the job (internal uses string). */\n version: string;\n /** Error object if the job failed. */\n error: Error | null;\n /** AbortController to signal cancellation. */\n abortController: AbortController;\n /** Promise that resolves/rejects when the job finishes. */\n completionPromise: Promise<void>;\n /** Resolver function for the completion promise. */\n resolveCompletion: () => void;\n /** Rejector function for the completion promise. */\n rejectCompletion: (reason?: unknown) => void;\n}\n\n/**\n * Defines the structure for callback functions used with the PipelineManager.\n * Allows external components to hook into job lifecycle events.\n */\nexport interface PipelineManagerCallbacks {\n /** Callback triggered when a job's status changes. */\n onJobStatusChange?: (job: InternalPipelineJob) => Promise<void>;\n /** Callback triggered when a job makes progress. */\n onJobProgress?: (job: InternalPipelineJob, progress: ScraperProgress) => Promise<void>;\n /** Callback triggered when a job encounters an error during processing (e.g., storing a doc). */\n onJobError?: (\n job: InternalPipelineJob,\n error: Error,\n document?: Document,\n ) => Promise<void>;\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 { ScraperRegistry, ScraperService } from \"../scraper\";\nimport type { ScraperOptions, ScraperProgress } from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { VersionStatus } from \"../store/types\";\nimport { DEFAULT_MAX_CONCURRENCY } 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, PipelineManagerCallbacks } 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 callbacks: PipelineManagerCallbacks = {};\n private composedCallbacks: PipelineManagerCallbacks = {};\n private store: DocumentManagementService;\n private scraperService: ScraperService;\n private shouldRecoverJobs: boolean;\n\n constructor(\n store: DocumentManagementService,\n concurrency: number = DEFAULT_MAX_CONCURRENCY,\n options: { recoverJobs?: boolean } = {},\n ) {\n this.store = store;\n this.concurrency = concurrency;\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();\n this.scraperService = new ScraperService(registry);\n\n // Initialize composed callbacks to ensure progress persistence even before setCallbacks is called\n this.rebuildComposedCallbacks();\n }\n\n /**\n * Registers callback handlers for pipeline manager events.\n */\n setCallbacks(callbacks: PipelineManagerCallbacks): void {\n this.callbacks = callbacks || {};\n this.rebuildComposedCallbacks();\n }\n\n /** Build composed callbacks that ensure persistence then delegate to user callbacks */\n private rebuildComposedCallbacks(): void {\n const user = this.callbacks;\n this.composedCallbacks = {\n onJobProgress: async (job, progress) => {\n await this.updateJobProgress(job, progress);\n await user.onJobProgress?.(job, progress);\n },\n onJobStatusChange: async (job) => {\n await user.onJobStatusChange?.(job);\n },\n onJobError: async (job, error, document) => {\n await user.onJobError?.(job, error, document);\n },\n };\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 logger.debug(\"Job recovery disabled for this PipelineManager instance\");\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 * Finds versions with RUNNING status and resets them to QUEUED for re-processing.\n * Also loads all QUEUED versions back into the pipeline queue.\n */\n async recoverPendingJobs(): Promise<void> {\n try {\n // Reset RUNNING jobs to QUEUED (they were interrupted by server restart)\n const runningVersions = await this.store.getVersionsByStatus([\n VersionStatus.RUNNING,\n ]);\n for (const version of runningVersions) {\n await this.store.updateVersionStatus(version.id, VersionStatus.QUEUED);\n logger.info(\n `🔄 Reset interrupted job to QUEUED: ${version.library_name}@${version.name || \"unversioned\"}`,\n );\n }\n\n // Load all QUEUED versions back into pipeline\n const queuedVersions = await this.store.getVersionsByStatus([VersionStatus.QUEUED]);\n for (const version of queuedVersions) {\n // Create complete job with all database state restored\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 // Parse stored scraper options\n let parsedScraperOptions = null;\n if (version.scraper_options) {\n try {\n parsedScraperOptions = JSON.parse(version.scraper_options);\n } catch (error) {\n logger.warn(\n `⚠️ Failed to parse scraper options for ${version.library_name}@${version.name || \"unversioned\"}: ${error}`,\n );\n }\n }\n\n const job: InternalPipelineJob = {\n id: jobId,\n library: version.library_name,\n version: version.name || \"\",\n status: PipelineJobStatus.QUEUED,\n progress: null,\n error: null,\n createdAt: new Date(version.created_at),\n // For recovered QUEUED jobs, startedAt must be null to reflect queued state.\n startedAt: null,\n finishedAt: null,\n abortController,\n completionPromise,\n resolveCompletion,\n rejectCompletion,\n\n // Database fields (single source of truth)\n versionId: version.id,\n versionStatus: version.status,\n progressPages: version.progress_pages,\n progressMaxPages: version.progress_max_pages,\n errorMessage: version.error_message,\n updatedAt: new Date(version.updated_at),\n sourceUrl: version.source_url,\n scraperOptions: parsedScraperOptions,\n };\n\n this.jobMap.set(jobId, job);\n this.jobQueue.push(jobId);\n }\n\n if (queuedVersions.length > 0) {\n logger.info(`📥 Recovered ${queuedVersions.length} pending job(s) from database`);\n } else {\n logger.debug(\"No pending jobs to recover from database\");\n }\n } catch (error) {\n logger.error(`❌ Failed to recover pending jobs: ${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 // Cleanup scraper service to prevent resource leaks\n await this.scraperService.cleanup();\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 enqueueJob(\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 // Extract URL and convert ScraperOptions to VersionScraperOptions\n const {\n url,\n library: _library,\n version: _version,\n signal: _signal,\n ...versionOptions\n } = options;\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: url,\n scraperOptions: versionOptions,\n };\n\n this.jobMap.set(jobId, job);\n this.jobQueue.push(jobId);\n logger.info(\n `📝 Job enqueued: ${jobId} for ${library}${normalizedVersion ? `@${normalizedVersion}` : \" (unversioned)\"}`,\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 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 || \"unversioned\"}`,\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 || \"unversioned\"} with stored options from ${stored.sourceUrl}`,\n );\n\n return this.enqueueJob(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 } 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 using composed callbacks\n await worker.executeJob(job, this.composedCallbacks);\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 // Reconstruct ScraperOptions for storage (DocumentStore will filter runtime fields)\n const fullOptions = {\n url: job.sourceUrl ?? \"\",\n library: job.library,\n version: job.version,\n ...job.scraperOptions,\n };\n await this.store.storeScraperOptions(versionId, fullOptions);\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 // Fire callback\n await this.callbacks.onJobStatusChange?.(job);\n }\n\n /**\n * Updates both in-memory job progress and database progress (write-through).\n */\n async updateJobProgress(\n job: InternalPipelineJob,\n progress: ScraperProgress,\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 // Note: Do not invoke onJobProgress callback here.\n // Callbacks are wired by services (e.g., workerService/CLI) and already call this method.\n }\n}\n","import type { DocumentManagementService } from \"../store\";\nimport { DEFAULT_MAX_CONCURRENCY } from \"../utils/config\";\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 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 options: Required<Pick<PipelineOptions, \"serverUrl\">> &\n Omit<PipelineOptions, \"serverUrl\">,\n ): Promise<PipelineClient>;\n // Implementation\n export async function createPipeline(\n docService?: DocumentManagementService,\n options: PipelineOptions = {},\n ): Promise<IPipeline> {\n const {\n recoverJobs = false, // Default to false for safety\n serverUrl,\n concurrency = DEFAULT_MAX_CONCURRENCY,\n } = options;\n\n logger.debug(\n `Creating pipeline: recoverJobs=${recoverJobs}, serverUrl=${serverUrl || \"none\"}, concurrency=${concurrency}`,\n );\n\n if (serverUrl) {\n // External pipeline requested\n logger.debug(`Creating PipelineClient for external worker at: ${serverUrl}`);\n return new PipelineClient(serverUrl);\n }\n\n // Local embedded pipeline with specified behavior\n return new PipelineManager(docService as DocumentManagementService, concurrency, {\n recoverJobs,\n });\n }\n}\n","/**\n * Shared embedding model configuration service.\n * Provides synchronous parsing of embedding model configuration and known dimensions lookup.\n * Eliminates code duplication between DocumentStore and telemetry systems.\n *\n * All model lookups are case-insensitive to handle variations in model name capitalization.\n * Uses class-based approach to avoid mutable global state and improve testability.\n */\n\n/**\n * Supported embedding model providers.\n */\nexport type EmbeddingProvider =\n | \"openai\"\n | \"vertex\"\n | \"gemini\"\n | \"aws\"\n | \"microsoft\"\n | \"sagemaker\";\n\n/**\n * Embedding model configuration parsed from environment variables.\n */\nexport interface EmbeddingModelConfig {\n /** The provider (e.g., \"openai\", \"gemini\") */\n provider: EmbeddingProvider;\n /** The model name (e.g., \"text-embedding-3-small\") */\n model: string;\n /** Known dimensions for this model, or null if unknown */\n dimensions: number | null;\n /** The full model specification string (e.g., \"openai:text-embedding-3-small\") */\n modelSpec: string;\n}\n\n/**\n * Embedding configuration manager that handles model parsing and dimension caching.\n * Encapsulates state to avoid global variable issues and improve testability.\n */\nexport class EmbeddingConfig {\n private static instance: EmbeddingConfig | null = null;\n\n /**\n * Get the singleton instance of EmbeddingConfig.\n * Creates the instance if it doesn't exist.\n */\n static getInstance(): EmbeddingConfig {\n if (EmbeddingConfig.instance === null) {\n EmbeddingConfig.instance = new EmbeddingConfig();\n }\n return EmbeddingConfig.instance;\n }\n\n /**\n * Reset the singleton instance (useful for testing).\n */\n static resetInstance(): void {\n EmbeddingConfig.instance = null;\n }\n /**\n * Known dimensions for common embedding models.\n * This avoids expensive API calls for dimension detection in telemetry.\n *\n * Note: The \"openai\" provider also supports OpenAI-compatible APIs like:\n * - Ollama (local models)\n * - LMStudio (local models)\n * - Any service implementing OpenAI's embedding API\n */\n private readonly knownModelDimensions: Record<string, number> = {\n // OpenAI models (also works with Ollama, LMStudio, and other OpenAI-compatible APIs)\n \"text-embedding-3-small\": 1536,\n \"text-embedding-3-large\": 3072,\n \"text-embedding-ada-002\": 1536,\n\n // Google Vertex AI models\n \"text-embedding-004\": 768,\n \"textembedding-gecko@003\": 768,\n \"textembedding-gecko@002\": 768,\n \"textembedding-gecko@001\": 768,\n\n // Google Gemini models (with MRL support)\n \"text-embedding-preview-0409\": 768,\n \"embedding-001\": 768,\n\n // AWS Bedrock models\n // Amazon Titan models\n \"amazon.titan-embed-text-v1\": 1536,\n \"amazon.titan-embed-text-v2:0\": 1024,\n \"amazon.titan-embed-image-v1\": 1024, // Image embedding model\n\n // Cohere models\n \"cohere.embed-english-v3\": 1024,\n \"cohere.embed-multilingual-v3\": 1024,\n\n // SageMaker models (hosted on AWS SageMaker)\n \"intfloat/multilingual-e5-large\": 1024,\n\n // Additional AWS models that might be supported\n // Note: Some of these might be placeholders - verify dimensions before use\n // \"amazon.nova-embed-multilingual-v1:0\": 4096, // Commented out as noted in source\n\n // MTEB Leaderboard models (source: https://huggingface.co/spaces/mteb/leaderboard)\n // Top performing models from Massive Text Embedding Benchmark\n \"sentence-transformers/all-MiniLM-L6-v2\": 384,\n \"gemini-embedding-001\": 3072,\n \"Qwen/Qwen3-Embedding-8B\": 4096,\n \"Qwen/Qwen3-Embedding-4B\": 2560,\n \"Qwen/Qwen3-Embedding-0.6B\": 1024,\n \"Linq-AI-Research/Linq-Embed-Mistral\": 4096,\n \"Alibaba-NLP/gte-Qwen2-7B-instruct\": 3584,\n \"intfloat/multilingual-e5-large-instruct\": 1024,\n \"Salesforce/SFR-Embedding-Mistral\": 4096,\n \"text-multilingual-embedding-002\": 768,\n \"GritLM/GritLM-7B\": 4096,\n \"GritLM/GritLM-8x7B\": 4096,\n \"intfloat/e5-mistral-7b-instruct\": 4096,\n \"Cohere/Cohere-embed-multilingual-v3.0\": 1024,\n \"Alibaba-NLP/gte-Qwen2-1.5B-instruct\": 8960,\n \"Lajavaness/bilingual-embedding-large\": 1024,\n \"Salesforce/SFR-Embedding-2_R\": 4096,\n \"NovaSearch/stella_en_1.5B_v5\": 8960,\n \"NovaSearch/jasper_en_vision_language_v1\": 8960,\n \"nvidia/NV-Embed-v2\": 4096,\n \"OrdalieTech/Solon-embeddings-large-0.1\": 1024,\n \"BAAI/bge-m3\": 1024,\n \"HIT-TMG/KaLM-embedding-multilingual-mini-v1\": 896,\n \"jinaai/jina-embeddings-v3\": 1024,\n \"Alibaba-NLP/gte-multilingual-base\": 768,\n \"Lajavaness/bilingual-embedding-base\": 768,\n \"HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1\": 896,\n \"nvidia/NV-Embed-v1\": 4096,\n \"Cohere/Cohere-embed-multilingual-light-v3.0\": 384,\n \"manu/bge-m3-custom-fr\": 1024,\n \"Lajavaness/bilingual-embedding-small\": 384,\n \"Snowflake/snowflake-arctic-embed-l-v2.0\": 1024,\n \"intfloat/multilingual-e5-base\": 768,\n \"voyage-3-lite\": 512,\n \"voyage-3\": 1024,\n \"intfloat/multilingual-e5-small\": 384,\n \"Alibaba-NLP/gte-Qwen1.5-7B-instruct\": 4096,\n \"Snowflake/snowflake-arctic-embed-m-v2.0\": 768,\n \"deepvk/USER-bge-m3\": 1024,\n \"Cohere/Cohere-embed-english-v3.0\": 1024,\n \"Omartificial-Intelligence-Space/Arabic-labse-Matryoshka\": 768,\n \"ibm-granite/granite-embedding-278m-multilingual\": 768,\n \"NovaSearch/stella_en_400M_v5\": 4096,\n \"omarelshehy/arabic-english-sts-matryoshka\": 1024,\n \"sentence-transformers/paraphrase-multilingual-mpnet-base-v2\": 768,\n \"Omartificial-Intelligence-Space/Arabic-all-nli-triplet-Matryoshka\": 768,\n \"Haon-Chen/speed-embedding-7b-instruct\": 4096,\n \"sentence-transformers/LaBSE\": 768,\n \"WhereIsAI/UAE-Large-V1\": 1024,\n \"ibm-granite/granite-embedding-107m-multilingual\": 384,\n \"mixedbread-ai/mxbai-embed-large-v1\": 1024,\n \"intfloat/e5-large-v2\": 1024,\n \"avsolatorio/GIST-large-Embedding-v0\": 1024,\n \"sdadas/mmlw-e5-large\": 1024,\n \"nomic-ai/nomic-embed-text-v1\": 768,\n \"nomic-ai/nomic-embed-text-v1-ablated\": 768,\n \"intfloat/e5-base-v2\": 768,\n \"BAAI/bge-large-en-v1.5\": 1024,\n \"intfloat/e5-large\": 1024,\n \"Omartificial-Intelligence-Space/Arabic-MiniLM-L12-v2-all-nli-triplet\": 384,\n \"Cohere/Cohere-embed-english-light-v3.0\": 384,\n \"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2\": 768,\n \"Gameselo/STS-multilingual-mpnet-base-v2\": 768,\n \"thenlper/gte-large\": 1024,\n \"avsolatorio/GIST-Embedding-v0\": 768,\n \"nomic-ai/nomic-embed-text-v1-unsupervised\": 768,\n \"infgrad/stella-base-en-v2\": 768,\n \"avsolatorio/NoInstruct-small-Embedding-v0\": 384,\n \"dwzhu/e5-base-4k\": 768,\n \"sdadas/mmlw-e5-base\": 768,\n \"voyage-multilingual-2\": 1024,\n \"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-supervised\": 4096,\n \"BAAI/bge-base-en-v1.5\": 768,\n \"avsolatorio/GIST-small-Embedding-v0\": 384,\n \"sdadas/mmlw-roberta-large\": 1024,\n \"nomic-ai/nomic-embed-text-v1.5\": 768,\n \"minishlab/potion-multilingual-128M\": 256,\n \"shibing624/text2vec-base-multilingual\": 384,\n \"thenlper/gte-base\": 768,\n \"intfloat/e5-small-v2\": 384,\n \"intfloat/e5-base\": 768,\n \"sentence-transformers/static-similarity-mrl-multilingual-v1\": 1024,\n \"manu/sentence_croissant_alpha_v0.3\": 2048,\n \"BAAI/bge-small-en-v1.5\": 512,\n \"thenlper/gte-small\": 384,\n \"sdadas/mmlw-e5-small\": 384,\n \"manu/sentence_croissant_alpha_v0.4\": 2048,\n \"manu/sentence_croissant_alpha_v0.2\": 2048,\n \"abhinand/MedEmbed-small-v0.1\": 384,\n \"ibm-granite/granite-embedding-125m-english\": 768,\n \"intfloat/e5-small\": 384,\n \"voyage-large-2-instruct\": 1024,\n \"sdadas/mmlw-roberta-base\": 768,\n \"Snowflake/snowflake-arctic-embed-l\": 1024,\n \"Mihaiii/Ivysaur\": 384,\n \"Snowflake/snowflake-arctic-embed-m-long\": 768,\n \"bigscience/sgpt-bloom-7b1-msmarco\": 4096,\n \"avsolatorio/GIST-all-MiniLM-L6-v2\": 384,\n \"sergeyzh/LaBSE-ru-turbo\": 768,\n \"sentence-transformers/all-mpnet-base-v2\": 768,\n \"Snowflake/snowflake-arctic-embed-m\": 768,\n \"Snowflake/snowflake-arctic-embed-s\": 384,\n \"sentence-transformers/all-MiniLM-L12-v2\": 384,\n \"Mihaiii/gte-micro-v4\": 384,\n \"Snowflake/snowflake-arctic-embed-m-v1.5\": 768,\n \"cointegrated/LaBSE-en-ru\": 768,\n \"Mihaiii/Bulbasaur\": 384,\n \"ibm-granite/granite-embedding-30m-english\": 384,\n \"deepfile/embedder-100p\": 768,\n \"Jaume/gemma-2b-embeddings\": 2048,\n \"OrlikB/KartonBERT-USE-base-v1\": 768,\n \"izhx/udever-bloom-7b1\": 4096,\n \"izhx/udever-bloom-1b1\": 1024,\n \"brahmairesearch/slx-v0.1\": 384,\n \"Mihaiii/Wartortle\": 384,\n \"izhx/udever-bloom-3b\": 2048,\n \"deepvk/USER-base\": 768,\n \"ai-forever/ru-en-RoSBERTa\": 1024,\n \"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-unsup-simcse\": 4096,\n \"Mihaiii/Venusaur\": 384,\n \"Snowflake/snowflake-arctic-embed-xs\": 384,\n \"jinaai/jina-embedding-b-en-v1\": 768,\n \"Mihaiii/gte-micro\": 384,\n \"aari1995/German_Semantic_STS_V2\": 1024,\n \"Mihaiii/Squirtle\": 384,\n \"OrlikB/st-polish-kartonberta-base-alpha-v1\": 768,\n \"sergeyzh/rubert-tiny-turbo\": 312,\n \"minishlab/potion-base-8M\": 256,\n \"minishlab/M2V_base_glove_subword\": 256,\n \"jinaai/jina-embedding-s-en-v1\": 512,\n \"minishlab/potion-base-4M\": 128,\n \"minishlab/M2V_base_output\": 256,\n \"DeepPavlov/rubert-base-cased-sentence\": 768,\n \"jinaai/jina-embeddings-v2-small-en\": 512,\n \"cointegrated/rubert-tiny2\": 312,\n \"minishlab/M2V_base_glove\": 256,\n \"cointegrated/rubert-tiny\": 312,\n \"silma-ai/silma-embeddding-matryoshka-v0.1\": 768,\n \"DeepPavlov/rubert-base-cased\": 768,\n \"Omartificial-Intelligence-Space/Arabic-mpnet-base-all-nli-triplet\": 768,\n \"izhx/udever-bloom-560m\": 1024,\n \"minishlab/potion-base-2M\": 64,\n \"DeepPavlov/distilrubert-small-cased-conversational\": 768,\n \"consciousAI/cai-lunaris-text-embeddings\": 1024,\n \"deepvk/deberta-v1-base\": 768,\n \"Omartificial-Intelligence-Space/Arabert-all-nli-triplet-Matryoshka\": 768,\n \"Omartificial-Intelligence-Space/Marbert-all-nli-triplet-Matryoshka\": 768,\n \"ai-forever/sbert_large_mt_nlu_ru\": 1024,\n \"ai-forever/sbert_large_nlu_ru\": 1024,\n \"malenia1/ternary-weight-embedding\": 1024,\n \"jinaai/jina-embeddings-v2-base-en\": 768,\n \"VPLabs/SearchMap_Preview\": 4096,\n \"Hum-Works/lodestone-base-4096-v1\": 768,\n \"jinaai/jina-embeddings-v4\": 2048,\n };\n\n /**\n * Lowercase lookup map for case-insensitive model dimension queries.\n * Built lazily from knownModelDimensions to ensure consistency.\n */\n private modelLookup: Map<string, number>;\n\n constructor() {\n this.modelLookup = new Map();\n for (const [model, dimensions] of Object.entries(this.knownModelDimensions)) {\n this.modelLookup.set(model.toLowerCase(), dimensions);\n }\n }\n\n /**\n * Parse embedding model configuration from environment variables.\n * This is a synchronous operation that extracts provider, model, and known dimensions.\n *\n * Supports various providers:\n * - openai: OpenAI models and OpenAI-compatible APIs (Ollama, LMStudio, etc.)\n * - vertex: Google Cloud Vertex AI\n * - gemini: Google Generative AI\n * - aws: AWS Bedrock models\n * - microsoft: Azure OpenAI\n * - sagemaker: AWS SageMaker hosted models\n *\n * @param modelSpec Optional model specification, defaults to DOCS_MCP_EMBEDDING_MODEL env var\n * @returns Parsed embedding model configuration\n */\n parse(modelSpec?: string): EmbeddingModelConfig {\n const spec =\n modelSpec || process.env.DOCS_MCP_EMBEDDING_MODEL || \"text-embedding-3-small\";\n\n // Parse provider and model from string (e.g., \"gemini:embedding-001\" or just \"text-embedding-3-small\")\n // Handle models that contain colons in their names (e.g., \"aws:amazon.titan-embed-text-v2:0\")\n const colonIndex = spec.indexOf(\":\");\n let provider: EmbeddingProvider;\n let model: string;\n\n if (colonIndex === -1) {\n // No colon found, default to OpenAI\n provider = \"openai\";\n model = spec;\n } else {\n // Split only on the first colon\n provider = spec.substring(0, colonIndex) as EmbeddingProvider;\n model = spec.substring(colonIndex + 1);\n }\n\n // Look up known dimensions (case-insensitive)\n const dimensions = this.modelLookup?.get(model.toLowerCase()) || null;\n\n return {\n provider,\n model,\n dimensions,\n modelSpec: spec,\n };\n }\n\n /**\n * Get the known dimensions for a specific model.\n * Returns null if the model dimensions are not known.\n * Uses case-insensitive lookup.\n *\n * @param model The model name (e.g., \"text-embedding-3-small\")\n * @returns Known dimensions or null\n */\n getKnownDimensions(model: string): number | null {\n return this.modelLookup?.get(model.toLowerCase()) || null;\n }\n\n /**\n * Add or update known dimensions for a model.\n * This can be used to cache discovered dimensions.\n * Stores both original case and lowercase for consistent lookup.\n *\n * @param model The model name\n * @param dimensions The dimensions to cache\n */\n setKnownDimensions(model: string, dimensions: number): void {\n this.knownModelDimensions[model] = dimensions;\n\n // Update lowercase lookup map\n if (this.modelLookup) {\n this.modelLookup.set(model.toLowerCase(), dimensions);\n }\n }\n\n /**\n * Static method to parse embedding model configuration using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static parseEmbeddingConfig(modelSpec?: string): EmbeddingModelConfig {\n return EmbeddingConfig.getInstance().parse(modelSpec);\n }\n\n /**\n * Static method to get known model dimensions using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static getKnownModelDimensions(model: string): number | null {\n return EmbeddingConfig.getInstance().getKnownDimensions(model);\n }\n\n /**\n * Static method to set known model dimensions using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static setKnownModelDimensions(model: string, dimensions: number): void {\n EmbeddingConfig.getInstance().setKnownDimensions(model, dimensions);\n }\n}\n","/**\n * Shared CLI utilities and helper functions.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { chromium } from \"playwright\";\nimport type { AppServerConfig } from \"../app\";\nimport type { AuthConfig } from \"../auth/types\";\nimport type { IPipeline, PipelineOptions } from \"../pipeline\";\nimport { PipelineFactory } from \"../pipeline\";\nimport type { DocumentManagementService } from \"../store\";\nimport {\n EmbeddingConfig,\n type EmbeddingModelConfig,\n} from \"../store/embeddings/EmbeddingConfig\";\nimport {\n DEFAULT_HOST,\n DEFAULT_HTTP_PORT,\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_PROTOCOL,\n DEFAULT_WEB_PORT,\n} from \"../utils/config\";\nimport { LogLevel, logger, setLogLevel } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport type { GlobalOptions } from \"./types\";\n\n/**\n * Embedding context.\n * Simplified subset of EmbeddingModelConfig for telemetry purposes.\n */\nexport interface EmbeddingContext {\n aiEmbeddingProvider: string;\n aiEmbeddingModel: string;\n aiEmbeddingDimensions: number | null;\n}\n\n/**\n * Ensures that the Playwright browsers are installed, unless a system Chromium path is set.\n */\nexport function ensurePlaywrightBrowsersInstalled(): void {\n // If PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set, skip install\n const chromiumEnvPath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH;\n if (chromiumEnvPath && existsSync(chromiumEnvPath)) {\n logger.debug(\n `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set to '${chromiumEnvPath}', skipping Playwright browser install.`,\n );\n return;\n }\n try {\n // Dynamically require Playwright and check for Chromium browser\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const chromiumPath = chromium.executablePath();\n if (!chromiumPath || !existsSync(chromiumPath)) {\n throw new Error(\"Playwright Chromium browser not found\");\n }\n } catch (_err) {\n // Not installed or not found, attempt to install\n logger.debug(\n \"Playwright browsers not found. Installing Chromium browser for dynamic scraping (this may take a minute)...\",\n );\n try {\n logger.debug(\"Installing Playwright Chromium browser...\");\n execSync(\"npm exec -y playwright install --no-shell --with-deps chromium\", {\n stdio: \"ignore\", // Suppress output\n cwd: getProjectRoot(),\n });\n } catch (_installErr) {\n console.error(\n \"❌ Failed to install Playwright browsers automatically. Please run:\\n npx playwright install --no-shell --with-deps chromium\\nand try again.\",\n );\n process.exit(1);\n }\n }\n}\n\n/**\n * Resolves the protocol based on auto-detection or explicit specification.\n * Auto-detection uses TTY status to determine appropriate protocol.\n */\nexport function resolveProtocol(protocol: string): \"stdio\" | \"http\" {\n if (protocol === \"auto\") {\n // VS Code and CI/CD typically run without TTY\n if (!process.stdin.isTTY && !process.stdout.isTTY) {\n return \"stdio\";\n }\n return \"http\";\n }\n\n // Explicit protocol specification\n if (protocol === \"stdio\" || protocol === \"http\") {\n return protocol;\n }\n\n throw new Error(`Invalid protocol: ${protocol}. Must be 'auto', 'stdio', or 'http'`);\n}\n\n/**\n * Validates that --resume flag is only used with in-process workers.\n */\nexport function validateResumeFlag(resume: boolean, serverUrl?: string): void {\n if (resume && serverUrl) {\n throw new Error(\n \"--resume flag is incompatible with --server-url. \" +\n \"External workers handle their own job recovery.\",\n );\n }\n}\n\n/**\n * Formats output for CLI commands\n */\nexport const formatOutput = (data: unknown): string => JSON.stringify(data, null, 2);\n\n/**\n * Sets up logging based on global options\n */\nexport function setupLogging(options: GlobalOptions, protocol?: \"stdio\" | \"http\"): void {\n // Suppress logging in stdio mode (before any logger calls)\n if (protocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR);\n } else if (options.silent) {\n setLogLevel(LogLevel.ERROR);\n } else if (options.verbose) {\n setLogLevel(LogLevel.DEBUG);\n }\n}\n\n/**\n * Validates and parses port number\n */\nexport function validatePort(portString: string): number {\n const port = Number.parseInt(portString, 10);\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n throw new Error(\"❌ Invalid port number\");\n }\n return port;\n}\n\n/**\n * Validates host string for basic format checking\n */\nexport function validateHost(hostString: string): string {\n // Basic validation - allow IPv4, IPv6, and hostnames\n const trimmed = hostString.trim();\n if (!trimmed) {\n throw new Error(\"❌ Host cannot be empty\");\n }\n\n // Very basic format check - reject obviously invalid values\n if (trimmed.includes(\" \") || trimmed.includes(\"\\t\") || trimmed.includes(\"\\n\")) {\n throw new Error(\"❌ Host cannot contain whitespace\");\n }\n\n return trimmed;\n}\n\n/**\n * Creates a pipeline (local or client) and attaches default CLI callbacks.\n * This makes the side-effects explicit and keeps creation consistent.\n */\nexport async function createPipelineWithCallbacks(\n docService: DocumentManagementService | undefined,\n options: PipelineOptions = {},\n): Promise<IPipeline> {\n logger.debug(`Initializing pipeline with options: ${JSON.stringify(options)}`);\n const { serverUrl, ...rest } = options;\n const pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, { serverUrl, ...rest })\n : await (async () => {\n if (!docService) {\n throw new Error(\"Local pipeline requires a DocumentManagementService instance\");\n }\n return PipelineFactory.createPipeline(docService, rest);\n })();\n\n // Configure progress callbacks for real-time updates\n pipeline.setCallbacks({\n onJobProgress: async (job, progress) => {\n logger.debug(\n `Job ${job.id} progress: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n },\n onJobStatusChange: async (job) => {\n logger.debug(`Job ${job.id} status changed to: ${job.status}`);\n },\n onJobError: async (job, error, document) => {\n logger.warn(\n `⚠️ Job ${job.id} error ${document ? `on document ${document.metadata.url}` : \"\"}: ${error.message}`,\n );\n },\n });\n\n return pipeline;\n}\n\n/**\n * Creates AppServerConfig based on service requirements\n */\nexport function createAppServerConfig(options: {\n enableWebInterface?: boolean;\n enableMcpServer?: boolean;\n enableApiServer?: boolean;\n enableWorker?: boolean;\n port: number;\n host: string;\n externalWorkerUrl?: string;\n readOnly?: boolean;\n auth?: AuthConfig;\n startupContext?: {\n cliCommand?: string;\n mcpProtocol?: \"stdio\" | \"http\";\n mcpTransport?: \"sse\" | \"streamable\";\n };\n}): AppServerConfig {\n return {\n enableWebInterface: options.enableWebInterface ?? false,\n enableMcpServer: options.enableMcpServer ?? true,\n enableApiServer: options.enableApiServer ?? false,\n enableWorker: options.enableWorker ?? true,\n port: options.port,\n host: options.host,\n externalWorkerUrl: options.externalWorkerUrl,\n readOnly: options.readOnly ?? false,\n auth: options.auth,\n startupContext: options.startupContext,\n };\n}\n\n/**\n * Parses custom headers from CLI options\n */\nexport function parseHeaders(headerOptions: string[]): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (Array.isArray(headerOptions)) {\n for (const entry of headerOptions) {\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 }\n\n return headers;\n}\n\n/**\n * Default configuration values\n */\nexport const CLI_DEFAULTS = {\n PROTOCOL: DEFAULT_PROTOCOL,\n HTTP_PORT: DEFAULT_HTTP_PORT,\n WEB_PORT: DEFAULT_WEB_PORT,\n HOST: DEFAULT_HOST,\n MAX_CONCURRENCY: DEFAULT_MAX_CONCURRENCY,\n TELEMETRY: true,\n} as const;\n\n/**\n * Parses auth configuration from CLI options and environment variables.\n * Precedence: CLI flags > env vars > defaults\n */\nexport function parseAuthConfig(options: {\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n}): AuthConfig | undefined {\n // Check CLI flags first, then env vars, then defaults\n const enabled =\n options.authEnabled ??\n (process.env.DOCS_MCP_AUTH_ENABLED?.toLowerCase() === \"true\" || false);\n\n if (!enabled) {\n return undefined;\n }\n\n const issuerUrl = options.authIssuerUrl ?? process.env.DOCS_MCP_AUTH_ISSUER_URL;\n\n const audience = options.authAudience ?? process.env.DOCS_MCP_AUTH_AUDIENCE;\n\n return {\n enabled,\n issuerUrl,\n audience,\n scopes: [\"openid\", \"profile\"], // Default scopes for OAuth2/OIDC\n };\n}\n\n/**\n * Validates auth configuration when auth is enabled.\n */\nexport function validateAuthConfig(authConfig: AuthConfig): void {\n if (!authConfig.enabled) {\n return;\n }\n\n const errors: string[] = [];\n\n // Issuer URL is required when auth is enabled\n if (!authConfig.issuerUrl) {\n errors.push(\"--auth-issuer-url is required when auth is enabled\");\n } else {\n try {\n const url = new URL(authConfig.issuerUrl);\n if (url.protocol !== \"https:\") {\n errors.push(\"Issuer URL must use HTTPS protocol\");\n }\n } catch {\n errors.push(\"Issuer URL must be a valid URL\");\n }\n }\n\n // Audience is required when auth is enabled\n if (!authConfig.audience) {\n errors.push(\"--auth-audience is required when auth is enabled\");\n } else {\n // Audience can be any valid URI (URL or URN)\n // Examples: https://api.example.com, urn:docs-mcp-server:api, urn:company:service\n try {\n // Try parsing as URL first (most common case)\n const url = new URL(authConfig.audience);\n if (url.protocol === \"http:\" && url.hostname !== \"localhost\") {\n // Warn about HTTP in production but don't fail\n logger.warn(\n \"⚠️ Audience uses HTTP protocol - consider using HTTPS for production\",\n );\n }\n if (url.hash) {\n errors.push(\"Audience must not contain URL fragments\");\n }\n } catch {\n // If not a valid URL, check if it's a valid URN\n if (authConfig.audience.startsWith(\"urn:\")) {\n // Basic URN validation: urn:namespace:specific-string\n const urnParts = authConfig.audience.split(\":\");\n if (urnParts.length < 3 || !urnParts[1] || !urnParts[2]) {\n errors.push(\"URN audience must follow format: urn:namespace:specific-string\");\n }\n } else {\n errors.push(\n \"Audience must be a valid absolute URL or URN (e.g., https://api.example.com or urn:company:service)\",\n );\n }\n }\n }\n\n // Scopes are not validated in binary authentication mode\n // They're handled internally by the OAuth proxy\n\n if (errors.length > 0) {\n throw new Error(`Auth configuration validation failed:\\n${errors.join(\"\\n\")}`);\n }\n}\n\n/**\n * Warns about HTTP usage in production when auth is enabled.\n */\nexport function warnHttpUsage(authConfig: AuthConfig | undefined, port: number): void {\n if (!authConfig?.enabled) {\n return;\n }\n\n // Check if we're likely running in production (not localhost)\n const isLocalhost =\n process.env.NODE_ENV !== \"production\" ||\n port === 6280 || // default dev port\n process.env.HOSTNAME?.includes(\"localhost\");\n\n if (!isLocalhost) {\n logger.warn(\n \"⚠️ Authentication is enabled but running over HTTP in production. \" +\n \"Consider using HTTPS for security.\",\n );\n }\n}\n\n/**\n * Resolves embedding configuration from environment variables and CLI args.\n * This function always attempts to resolve embedding configuration regardless of deployment mode.\n * @param cliArgs Future: CLI arguments that might override environment\n * @returns Embedding configuration or null if config is unavailable\n */\nexport function resolveEmbeddingContext(cliArgs?: {\n embeddingModel?: string;\n}): EmbeddingModelConfig | null {\n try {\n // Future: CLI args take precedence over environment\n const modelSpec = cliArgs?.embeddingModel || process.env.DOCS_MCP_EMBEDDING_MODEL;\n\n logger.debug(\"Resolving embedding configuration\");\n const config = EmbeddingConfig.parseEmbeddingConfig(modelSpec);\n\n return config;\n } catch (error) {\n logger.debug(`Failed to resolve embedding configuration: ${error}`);\n return null;\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\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 /** Indicates if the cancellation request was successfully initiated or if the job was already finished/cancelled. */\n success: boolean;\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 */\n async execute(input: CancelJobInput): Promise<CancelJobResult> {\n return analytics.trackTool(\n \"cancel_job\",\n async () => {\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 return {\n message: `Job with ID ${input.jobId} not found.`,\n success: false,\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(\n `Job ${input.jobId} is already in a final state: ${job.status}.`,\n );\n return {\n message: `Job ${input.jobId} is already ${job.status}. No action taken.`,\n success: true, // Considered success as no cancellation needed\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 success: true,\n };\n } catch (error) {\n logger.error(`❌ Error cancelling job ${input.jobId}: ${error}`);\n return {\n message: `Failed to cancel job ${input.jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n success: false,\n };\n }\n },\n (result) => {\n return {\n success: result.success,\n // Note: success flag already indicates if cancellation was successful\n };\n },\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\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 /** Indicates if the clear operation was successful. */\n success: boolean;\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 */\n async execute(_input: ClearCompletedJobsInput): Promise<ClearCompletedJobsResult> {\n return analytics.trackTool(\n \"clear_completed_jobs\",\n async () => {\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 success: true,\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 return {\n message: errorMessage,\n success: false,\n clearedCount: 0,\n };\n }\n },\n (result) => ({\n success: result.success,\n clearedCount: result.clearedCount,\n }),\n );\n }\n}\n","import semver from \"semver\";\n\n// LibraryVersionDetails removed; use minimal inline shape for available versions metadata\n\nclass 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\nclass VersionNotFoundError extends ToolError {\n constructor(\n public readonly library: string,\n public readonly requestedVersion: string,\n public readonly availableVersions: Array<{\n version: string;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>,\n ) {\n super(\n `Version ${requestedVersion} not found for ${library}. Available versions: ${availableVersions.map((v) => v.version).join(\", \")}`,\n \"SearchTool\",\n );\n }\n\n getLatestVersion() {\n return this.availableVersions.sort((a, b) => semver.compare(b.version, a.version))[0];\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 */\nclass LibraryNotFoundError extends ToolError {\n constructor(\n public readonly requestedLibrary: string,\n public readonly suggestions: string[] = [],\n ) {\n let message = `Library '${requestedLibrary}' not found.`;\n if (suggestions.length > 0) {\n message += ` Did you mean one of these: ${suggestions.join(\", \")}?`;\n }\n // Assuming this error might originate from various tools, but SearchTool is a primary candidate.\n // We might need to adjust the toolName if it's thrown elsewhere.\n super(message, \"SearchTool\");\n }\n}\n\nexport { LibraryNotFoundError, ToolError, VersionNotFoundError };\n","import type {\n ContentFetcher,\n FileFetcher,\n HttpFetcher,\n RawContent,\n} from \"../scraper/fetcher\";\nimport { HtmlPipeline } from \"../scraper/pipelines/HtmlPipeline\";\nimport { MarkdownPipeline } from \"../scraper/pipelines/MarkdownPipeline\";\nimport type { ContentPipeline, ProcessedContent } from \"../scraper/pipelines/types\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport { convertToString } from \"../scraper/utils/buffer\";\nimport { resolveCharset } from \"../scraper/utils/charset\";\nimport { analytics } from \"../telemetry\";\nimport { ScraperError } from \"../utils/errors\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError } 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 * Collection of fetchers that will be tried in order for a given URL.\n */\n private readonly fetchers: ContentFetcher[];\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 and MarkdownPipeline.\n */\n private readonly pipelines: ContentPipeline[];\n\n constructor(httpFetcher: HttpFetcher, fileFetcher: FileFetcher) {\n this.fetchers = [httpFetcher, fileFetcher];\n const htmlPipeline = new HtmlPipeline();\n const markdownPipeline = new MarkdownPipeline();\n this.pipelines = [htmlPipeline, markdownPipeline];\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 return analytics.trackTool(\n \"fetch_url\",\n async () => {\n const { url, scrapeMode = ScrapeMode.Auto, headers } = options;\n\n const canFetchResults = this.fetchers.map((f) => f.canFetch(url));\n const fetcherIndex = canFetchResults.indexOf(true);\n if (fetcherIndex === -1) {\n throw new ToolError(\n `Invalid URL: ${url}. Must be an HTTP/HTTPS URL or a file:// URL.`,\n this.constructor.name,\n );\n }\n\n const fetcher = this.fetchers[fetcherIndex];\n logger.debug(`Using fetcher \"${fetcher.constructor.name}\" for URL: ${url}`);\n\n try {\n logger.info(`📡 Fetching ${url}...`);\n const rawContent: RawContent = await fetcher.fetch(url, {\n followRedirects: options.followRedirects ?? true,\n maxRetries: 3,\n headers, // propagate custom headers\n });\n\n logger.info(\"🔄 Processing content...\");\n\n let processed: Awaited<ProcessedContent> | undefined;\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\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 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 (\n typeof processed.textContent !== \"string\" ||\n !processed.textContent.trim()\n ) {\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 if (error instanceof ScraperError || error instanceof ToolError) {\n throw new ToolError(\n `Failed to fetch or process URL: ${error.message}`,\n this.constructor.name,\n );\n }\n throw new ToolError(\n `Failed to fetch or process URL: ${error instanceof Error ? error.message : String(error)}`,\n this.constructor.name,\n );\n } finally {\n // Cleanup all pipelines to prevent resource leaks (e.g., browser instances)\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n }\n },\n (result) => {\n const { url, scrapeMode, followRedirects, headers } = options;\n return {\n url,\n scrapeMode,\n followRedirects,\n contentLength: result.length,\n hasHeaders: !!headers,\n };\n },\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { VersionNotFoundError } from \"./errors\";\n\nexport interface FindVersionToolOptions {\n library: string;\n targetVersion?: 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 descriptive string indicating the best match and unversioned status, or an error message.\n */\n async execute(options: FindVersionToolOptions): Promise<string> {\n return analytics\n .trackTool(\n \"find_version\",\n async () => {\n const { library, targetVersion } = options;\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n\n try {\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 VersionNotFoundError below,\n // but added for completeness.\n message = `No matching version or unversioned documents found for ${libraryAndVersion}.`;\n }\n\n // Return both the message and the structured data for tracking\n return { message, bestMatch, hasUnversioned };\n } catch (error) {\n if (error instanceof VersionNotFoundError) {\n // This error is thrown when no semver versions AND no unversioned docs exist.\n logger.info(`ℹ️ Version not found: ${error.message}`);\n const message = `No matching version or unversioned documents found for ${libraryAndVersion}. Available: ${\n error.availableVersions.length > 0\n ? error.availableVersions.map((v) => v.version).join(\", \")\n : \"None\"\n }.`;\n return { message, bestMatch: null, hasUnversioned: false };\n }\n // Re-throw unexpected errors\n logger.error(\n `❌ Error finding version for ${libraryAndVersion}: ${error instanceof Error ? error.message : error}`,\n );\n throw error;\n }\n },\n (result) => {\n const { library, targetVersion } = options;\n return {\n library,\n targetVersion,\n foundMatch: !!result.bestMatch,\n hasUnversioned: result.hasUnversioned,\n };\n },\n )\n .then((result) => result.message); // Return just the message to maintain interface\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJobStatus } from \"../pipeline/types\";\nimport type { VersionStatus } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\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 | null;\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 or null if not found.\n */\n async execute(input: GetJobInfoInput): Promise<GetJobInfoToolResponse> {\n return analytics.trackTool(\n \"get_job_info\",\n async () => {\n const job = await this.pipeline.getJob(input.jobId);\n\n if (!job) {\n // Return null in the result if job not found\n return { job: null };\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 (result) => {\n return {\n found: result.job !== null,\n library: result.job?.library,\n version: result.job?.version,\n };\n },\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJob, PipelineJobStatus } from \"../pipeline/types\";\nimport { analytics } from \"../telemetry\";\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 return analytics.trackTool(\n \"list_jobs\",\n async () => {\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:\n 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 (result) => {\n return {\n jobCount: result.jobs.length,\n statusCounts: result.jobs.reduce(\n (acc, job) => {\n acc[job.status] = (acc[job.status] || 0) + 1;\n return acc;\n },\n {} as Record<string, number>,\n ),\n };\n },\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { VersionStatus, VersionSummary } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\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 return analytics.trackTool(\n \"list_libraries\",\n async () => {\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 (result) => ({\n libraryCount: result.libraries.length,\n totalVersions: result.libraries.reduce(\n (sum, lib) => sum + lib.versions.length,\n 0,\n ),\n }),\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError } 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 return analytics.trackTool(\n \"remove_docs\",\n async () => {\n const { library, version } = args;\n\n logger.info(`🗑️ Removing library: ${library}${version ? `@${version}` : \"\"}`);\n\n try {\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 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 const { library, version } = args;\n return {\n library,\n version,\n // Success is implicit since if this callback runs, no exception was thrown\n };\n },\n );\n }\n}\n","import * as semver from \"semver\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport { analytics } from \"../telemetry\";\nimport {\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_MAX_DEPTH,\n DEFAULT_MAX_PAGES,\n} from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\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 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\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\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 return analytics.trackTool(\n \"scrape_docs\",\n async () => {\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 Error(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n );\n }\n } else {\n throw new Error(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\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 =\n internalVersion === \"\" ? null : internalVersion;\n\n // Enqueue the job using the injected pipeline\n const jobId = await pipeline.enqueueJob(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 ?? DEFAULT_MAX_PAGES,\n maxDepth: scraperOptions?.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxConcurrency: scraperOptions?.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY,\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 });\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 (result) => ({\n library,\n version,\n url,\n waitForCompletion,\n ...scraperOptions,\n isBackgroundJob: \"jobId\" in result,\n pagesScraped: \"pagesScraped\" in result ? result.pagesScraped : undefined,\n }),\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { StoreSearchResult, VersionSummary } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { VersionNotFoundError } 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 LibraryNotFoundError\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 return analytics.trackTool(\n \"search_docs\",\n async () => {\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 detailedVersions = libraryInfo\n ? (libraryInfo.versions as VersionSummary[]).map((v) => ({\n version: v.ref.version,\n documentCount: v.counts.documents,\n uniqueUrlCount: v.counts.uniqueUrls,\n indexedAt: v.indexedAt,\n }))\n : [];\n throw new VersionNotFoundError(library, version ?? \"latest\", detailedVersions);\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 VersionNotFoundError, 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 (result) => ({\n library,\n version,\n query,\n limit,\n exactMatch,\n resultCount: result.results.length,\n }),\n );\n }\n}\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(text: string): CallToolResult {\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 { type JobInfo, LibraryNotFoundError, VersionNotFoundError } from \"../tools\";\nimport { DEFAULT_MAX_DEPTH, DEFAULT_MAX_PAGES } 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 readOnly Whether to run in read-only mode (only expose read tools).\n * @returns A configured McpServer instance.\n */\nexport function createMcpServerInstance(\n tools: McpServerTools,\n readOnly = false,\n): McpServer {\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 // @ts-expect-error TypeScript has issues with deep Zod inference in MCP SDK\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().describe(\"Library name.\"),\n version: z.string().optional().describe(\"Library version (optional).\"),\n maxPages: z\n .number()\n .optional()\n .default(DEFAULT_MAX_PAGES)\n .describe(`Maximum number of pages to scrape (default: ${DEFAULT_MAX_PAGES}).`),\n maxDepth: z\n .number()\n .optional()\n .default(DEFAULT_MAX_DEPTH)\n .describe(`Maximum navigation depth (default: ${DEFAULT_MAX_DEPTH}).`),\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 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(\n `Failed to scrape documentation: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n version: z\n .string()\n .optional()\n .describe(\"Library version (exact or X-Range, optional).\"),\n query: z.string().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 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 if (error instanceof LibraryNotFoundError) {\n return createResponse(\n [\n `Library \"${library}\" not found.`,\n error.suggestions?.length\n ? `Did you mean: ${error.suggestions?.join(\", \")}?`\n : undefined,\n ].join(\" \"),\n );\n }\n if (error instanceof VersionNotFoundError) {\n const indexedVersions = error.availableVersions.map((v) => v.version);\n return createResponse(\n [\n `Version \"${version}\" not found.`,\n indexedVersions.length > 0\n ? `Available indexed versions for ${library}: ${indexedVersions.join(\", \")}`\n : undefined,\n ].join(\" \"),\n );\n }\n return createError(\n `Failed to search documentation: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 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(\n `Failed to list libraries: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n targetVersion: z\n .string()\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 try {\n const message = await tools.findVersion.execute({\n library,\n targetVersion,\n });\n\n if (!message) {\n return createError(\"No matching version found\");\n }\n\n return createResponse(message);\n } catch (error) {\n return createError(\n `Failed to find version: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 // @ts-expect-error TypeScript has issues with deep Zod inference in MCP SDK\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 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(\n `Failed to list jobs: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.getJobInfo.execute({ jobId });\n if (!result.job) {\n return createError(`Job with ID ${jobId} not found.`);\n }\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 return createError(\n `Failed to get job info for ${jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.cancelJob.execute({ jobId });\n // Use the message and success status from the tool's result\n if (result.success) {\n return createResponse(result.message);\n }\n // If not successful according to the tool, treat it as an error in MCP\n return createError(result.message);\n } catch (error) {\n // Catch any unexpected errors during the tool execution itself\n return createError(\n `Failed to cancel job ${jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n version: z\n .string()\n .optional()\n .describe(\"Library version (optional, removes unversioned if omitted).\"),\n },\n {\n title: \"Remove Library Documentation\",\n destructiveHint: true,\n },\n async ({ library, version }) => {\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(\n `Failed to remove documents: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.fetchUrl.execute({ url, followRedirects });\n return createResponse(result);\n } catch (error) {\n return createError(\n `Failed to fetch URL: ${error instanceof Error ? error.message : String(error)}`,\n );\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 // Fetch the simplified job info using GetJobInfoTool\n const result = await tools.getJobInfo.execute({ jobId });\n\n // result.job is either the simplified job object or null\n if (!result.job) {\n // Job not found, return empty content\n return { contents: [] };\n }\n\n // Job found, return its simplified details as JSON\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 },\n );\n }\n\n return server;\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { FileFetcher, HttpFetcher } from \"../scraper/fetcher\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport {\n CancelJobTool,\n FetchUrlTool,\n FindVersionTool,\n GetJobInfoTool,\n ListJobsTool,\n ListLibrariesTool,\n RemoveTool,\n ScrapeTool,\n SearchTool,\n} from \"../tools\";\n\n/**\n * Interface for the shared tool instances.\n */\nexport interface McpServerTools {\n listLibraries: ListLibrariesTool;\n findVersion: FindVersionTool;\n scrape: ScrapeTool;\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 * @returns An object containing all instantiated tool instances.\n */\nexport async function initializeTools(\n docService: IDocumentManagement,\n pipeline: IPipeline,\n): Promise<McpServerTools> {\n const tools: McpServerTools = {\n listLibraries: new ListLibrariesTool(docService),\n findVersion: new FindVersionTool(docService),\n scrape: new ScrapeTool(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 HttpFetcher(), new FileFetcher()),\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 { analytics } from \"../telemetry\";\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 readOnly Whether to run in read-only mode\n * @returns The McpServer instance for cleanup\n */\nexport async function registerMcpService(\n server: FastifyInstance,\n docService: IDocumentManagement,\n pipeline: IPipeline,\n readOnly = false,\n authManager?: ProxyAuthManager,\n): Promise<McpServer> {\n // Initialize MCP server and tools\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = createMcpServerInstance(mcpTools, readOnly);\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 // 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 // Log client connection (simple connection tracking without sessions)\n if (analytics.isEnabled()) {\n logger.info(`🔗 MCP client connected: ${transport.sessionId}`);\n }\n\n reply.raw.on(\"close\", () => {\n delete sseTransports[transport.sessionId];\n transport.close();\n\n // Log client disconnection\n if (analytics.isEnabled()) {\n logger.info(`🔗 MCP client disconnected: ${transport.sessionId}`);\n }\n });\n\n await mcpServer.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, readOnly);\n const requestTransport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n });\n\n reply.raw.on(\"close\", () => {\n logger.debug(\"Streamable HTTP request closed\");\n requestTransport.close();\n requestServer.close(); // Close the per-request server instance\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 // Store reference to SSE transports on the server instance for cleanup\n (\n mcpServer as unknown as {\n _sseTransports: Record<string, SSEServerTransport>;\n }\n )._sseTransports = sseTransports;\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 // 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 // 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 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 { 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\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 enqueueInput = z.object({\n library: nonEmptyTrimmed,\n version: optionalTrimmed,\n options: z.custom<ScraperOptions>(),\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 enqueueJob: tt.procedure\n .input(enqueueInput)\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof enqueueInput>;\n }) => {\n const jobId = await ctx.pipeline.enqueueJob(\n input.library,\n input.version ?? null,\n input.options,\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 { 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\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 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 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 and data store routers under a single endpoint.\n */\n\nimport { initTRPC } from \"@trpc/server\";\nimport { fastifyTRPCPlugin } from \"@trpc/server/adapters/fastify\";\nimport type { FastifyInstance } from \"fastify\";\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;\n\nexport async function registerTrpcService(\n server: FastifyInstance,\n pipeline: IPipeline,\n docService: IDocumentManagement,\n): Promise<void> {\n const t = initTRPC.context<UnifiedContext>().create();\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.mergeRouters(\n healthRouter,\n createPipelineRouter(t),\n createDataRouter(t),\n );\n\n await server.register(fastifyTRPCPlugin, {\n prefix: \"/api\",\n trpcOptions: {\n router,\n createContext: async (): Promise<UnifiedContext> => ({ pipeline, docService }),\n },\n });\n}\n","import type { PropsWithChildren } from \"@kitajs/html\";\nimport { readFileSync } from \"node:fs\";\nimport { logger } from \"../../utils/logger\";\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}\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, and children.\n */\nconst Layout = ({ title, version, children }: LayoutProps) => {\n let versionString = version;\n if (!versionString) {\n // If no version is provided, use the version from package.json\n // We cannot bake the version into the bundle, as the package.json will\n // be updated by the build process, after the bundle is created.\n try {\n const packageJson = JSON.parse(readFileSync(\"package.json\", \"utf-8\")) as {\n version: string;\n };\n versionString = packageJson.version;\n } catch (error) {\n logger.error(`Error reading package.json: ${error}`);\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 {/* 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\">\n <div class=\"container max-w-2xl mx-auto px-4 py-4\">\n <header class=\"mb-4\">\n <h1 class=\"text-3xl font-bold text-gray-900 dark:text-white\">\n <a href=\"/\">MCP Docs</a>\n {versionString ? (\n <span\n safe\n class=\"ml-2 text-base font-normal text-gray-500 dark:text-gray-400 align-baseline\"\n title={`Version ${versionString}`}\n >\n v{versionString}\n </span>\n ) : null}\n </h1>\n </header>\n\n <main>{children}</main>\n </div>\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 Layout from \"../components/Layout\"; // Import the Layout component\n\n/**\n * Registers the root route that serves the main HTML page.\n * @param server - The Fastify instance.\n */\nexport function registerIndexRoute(server: FastifyInstance) {\n server.get(\"/\", async (_, reply) => {\n reply.type(\"text/html\");\n // Use the Layout component and define the main content within it\n return (\n \"<!DOCTYPE html>\" +\n (\n <Layout title=\"MCP Docs\">\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 type=\"button\"\n class=\"text-xs px-3 py-1.5 text-gray-700 bg-gray-100 border border-gray-300 rounded-lg hover:bg-gray-200 focus:ring-4 focus:outline-none 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 transition-colors duration-150\"\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 >\n Clear Completed Jobs\n </button>\n </div>\n {/* Container for the job list, loaded via HTMX */}\n <div id=\"job-queue\" hx-get=\"/web/jobs\" hx-trigger=\"load, every 1s\">\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 {/* Container for the add job form, loaded via HTMX */}\n <div id=\"addJobForm\" hx-get=\"/web/jobs/new\" hx-trigger=\"load\">\n {/* Initial loading state (optional, could just be empty) */}\n <div class=\"p-6 bg-white rounded-lg shadow dark:bg-gray-800 animate-pulse\">\n <div class=\"h-6 bg-gray-200 rounded-full dark:bg-gray-700 w-1/3 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 {/* 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, every 10s\"\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\";\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 const result = await cancelJobTool.execute({ jobId });\n if (result.success) {\n return { success: true, message: result.message };\n } else {\n reply.status(400);\n return { success: false, message: result.message };\n }\n }\n );\n}\n","import type { FastifyInstance } from \"fastify\";\nimport type { ClearCompletedJobsTool } from \"../../../tools/ClearCompletedJobsTool\";\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 const result = await clearCompletedJobsTool.execute({});\n\n reply.type(\"application/json\");\n return {\n success: result.success,\n message: result.message,\n };\n } catch (error) {\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","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-purple-100 text-purple-800 text-xs font-medium me-2 px-1.5 py-0.5 rounded dark:bg-purple-900 dark:text-purple-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","/**\n * Renders an SVG loading spinner icon.\n * Used for indicating loading states in buttons or other elements.\n */\nconst LoadingSpinner = () => (\n <svg\n class=\"animate-spin h-4 w-4 text-white\" // Adjusted size to h-4 w-4 to match usage\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 return (\n <div class=\"block p-3 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600\">\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 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 title=\"Stop this job\"\n x-data=\"{}\"\n x-on:click={`\n if ($store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}') {\n $store.confirmingAction.isStopping = true;\n fetch('/web/jobs/' + '${job.id}' + '/cancel', {\n method: 'POST',\n headers: { 'Accept': 'application/json' },\n })\n .then(r => r.json())\n .then(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isStopping = false;\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n document.dispatchEvent(new CustomEvent('job-list-refresh'));\n })\n .catch(() => { $store.confirmingAction.isStopping = false; });\n } else {\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n $store.confirmingAction.type = 'job-cancel';\n $store.confirmingAction.id = '${job.id}';\n $store.confirmingAction.isStopping = false;\n $store.confirmingAction.timeoutId = setTimeout(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isStopping = false;\n $store.confirmingAction.timeoutId = null;\n }, 3000);\n }\n `}\n x-bind:disabled={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && $store.confirmingAction.isStopping`}\n >\n <span\n x-show={`$store.confirmingAction.type !== 'job-cancel' || $store.confirmingAction.id !== '${job.id}' || $store.confirmingAction.isStopping`}\n >\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\n x-show={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && !$store.confirmingAction.isStopping`}\n class=\"px-2\"\n >\n Cancel?\n </span>\n <span\n x-show={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && $store.confirmingAction.isStopping`}\n >\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\"; // Adjusted import path\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 * Adds a listener for the 'job-list-refresh' event to trigger a reload of the job list using HTMX.\n * @param props - Component props including the array of jobs.\n */\nconst JobList = ({ jobs }: JobListProps) => (\n <div id=\"job-list\" class=\"space-y-2\">\n {jobs.length === 0 ? (\n <p class=\"text-center text-gray-500 dark:text-gray-400\">\n No pending jobs.\n </p>\n ) : (\n jobs.map((job) => <JobItem job={job} />)\n )}\n {/* NOTE: To enable live job list refresh after stopping a job, add the following script to your main HTML layout or main.client.ts:\n document.addEventListener('job-list-refresh', function () {\n if (window.htmx) {\n window.htmx.ajax('GET', '/web/jobs', '#job-list');\n } else {\n window.location.reload();\n }\n });\n */}\n </div>\n);\n\nexport default JobList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListJobsTool } from \"../../../tools/ListJobsTool\"; // Adjusted import path\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 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=\"flex-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=\"flex-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=\"flex-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=\"flex-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 { ScrapeMode } from \"../../scraper/types\"; // Adjusted import path\nimport Alert from \"./Alert\";\nimport Tooltip from \"./Tooltip\";\n\ninterface ScrapeFormContentProps {\n defaultExcludePatterns?: string[];\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 */\nconst ScrapeFormContent = ({\n defaultExcludePatterns,\n}: ScrapeFormContentProps) => {\n // Format default patterns for display in textarea (one per line)\n const defaultExcludePatternsText = defaultExcludePatterns?.join(\"\\n\") || \"\";\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\">\n <h3 class=\"text-xl font-semibold text-gray-900 dark:text-white mb-2\">\n Queue New Scrape Job\n </h3>\n <form\n hx-post=\"/web/jobs/scrape\"\n hx-target=\"#job-response\"\n hx-swap=\"innerHTML\"\n class=\"space-y-2\"\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 >\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 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-indigo-500 focus:border-indigo-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 <input\n type=\"text\"\n name=\"library\"\n id=\"library\"\n required\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-indigo-500 focus:border-indigo-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=\"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. This allows for version-specific searches.\" />\n </div>\n <input\n type=\"text\"\n name=\"version\"\n id=\"version\"\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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n </div>\n\n {/* Consider using Flowbite Accordion here */}\n <details class=\"bg-gray-50 dark:bg-gray-900 p-2 rounded-md\">\n <summary class=\"cursor-pointer text-sm font-medium text-gray-600 dark:text-gray-400\">\n Advanced Options\n </summary>\n <div class=\"mt-2 space-y-2\" x-data=\"{ headers: [] }\">\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 text=\"The maximum number of pages to scrape. Default is 1000. Setting this too high may result in longer processing times.\" />\n </div>\n <input\n type=\"number\"\n name=\"maxPages\"\n id=\"maxPages\"\n min=\"1\"\n placeholder=\"1000\"\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-indigo-500 focus:border-indigo-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 text=\"How many links deep the scraper should follow. Default is 3. Higher values capture more content but increase processing time.\" />\n </div>\n <input\n type=\"number\"\n name=\"maxDepth\"\n id=\"maxDepth\"\n min=\"0\"\n placeholder=\"3\"\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-indigo-500 focus:border-indigo-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-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option value=\"subpages\" selected>\n Subpages (Default)\n </option>\n <option value=\"hostname\">Hostname</option>\n <option value=\"domain\">Domain</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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono text-xs\"\n >\n {defaultExcludePatternsText}\n </textarea>\n <p class=\"mt-1 text-xs text-gray-500 dark:text-gray-400\">\n Default patterns are pre-filled. Edit to customize or clear to\n 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-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option value={ScrapeMode.Auto} selected>\n Auto (Default)\n </option>\n <option value={ScrapeMode.Fetch}>Fetch</option>\n <option value={ScrapeMode.Playwright}>Playwright</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-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-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\n class=\"h-4 w-4 text-indigo-600 focus:ring-indigo-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\n class=\"h-4 w-4 text-indigo-600 focus:ring-indigo-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 </details>\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-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500\"\n >\n Queue Job\n </button>\n </div>\n </form>\n {/* Target div for HTMX response */}\n <div id=\"job-response\" class=\"mt-2 text-sm\"></div>\n </div>\n );\n};\n\nexport default ScrapeFormContent;\n","import ScrapeFormContent from \"./ScrapeFormContent\"; // Adjusted import path\n\ninterface ScrapeFormProps {\n defaultExcludePatterns?: string[];\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 = ({ defaultExcludePatterns }: ScrapeFormProps) => (\n <div id=\"scrape-form-container\">\n <ScrapeFormContent defaultExcludePatterns={defaultExcludePatterns} />\n </div>\n);\n\nexport default ScrapeForm;\n","import type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport type { ScrapeTool } from \"../../../tools/ScrapeTool\"; // Adjusted import path\nimport { ScrapeMode } from \"../../../scraper/types\"; // Adjusted import path\nimport { logger } from \"../../../utils/logger\"; // Adjusted import path\nimport ScrapeForm from \"../../components/ScrapeForm\"; // Import extracted component\nimport Alert from \"../../components/Alert\"; // Import Alert component\nimport ScrapeFormContent from \"../../components/ScrapeFormContent\"; // Import for OOB swap\nimport { DEFAULT_EXCLUSION_PATTERNS } from \"../../../scraper/utils/defaultPatterns\"; // Import default patterns\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) {\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 <ScrapeForm defaultExcludePatterns={DEFAULT_EXCLUSION_PATTERNS} />;\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 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) return undefined;\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 // Prepare options for ScrapeTool\n const scrapeOptions = {\n url: body.url,\n library: body.library,\n version: body.version || null, // Handle empty string as null\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: Use Alert component and OOB swap\n return (\n <>\n {/* Main target response */}\n <Alert\n type=\"success\"\n message={\n <>\n Job queued successfully! ID:{\" \"}\n <span safe>{result.jobId}</span>\n </>\n }\n />\n {/* OOB target response - contains only the inner form content */}\n <div id=\"scrape-form-container\" hx-swap-oob=\"innerHTML\">\n <ScrapeFormContent defaultExcludePatterns={DEFAULT_EXCLUSION_PATTERNS} />\n </div>\n </>\n );\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 reply.status(500); // Keep status code for errors\n // Use Alert component for server error\n return (\n <Alert\n type=\"error\"\n message={<>Failed to queue job: {errorMessage}</>}\n />\n );\n }\n }\n );\n}\n","import type { VersionSummary } from \"../../store/types\";\nimport VersionBadge from \"./VersionBadge\"; // Adjusted import path\nimport LoadingSpinner from \"./LoadingSpinner\"; // Import spinner\n\n/**\n * Props for the VersionDetailsRow component.\n */\ninterface VersionDetailsRowProps {\n version: VersionSummary;\n libraryName: string;\n showDelete?: boolean; // Optional prop to control delete button visibility\n}\n\n/**\n * Renders details for a single library version in a row format.\n * Includes version, stats, and an optional delete button.\n * @param props - Component props including version, libraryName, and showDelete flag.\n */\nconst VersionDetailsRow = ({\n version,\n libraryName,\n showDelete = true, // Default to true\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 'Unversioned' if version string is empty\n const versionLabel = version.ref.version || \"Unversioned\";\n // Use empty string for unversioned 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 // 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 >\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>Unversioned</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 Snippets:{\" \"}\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 {/**\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=\"ml-2 font-medium rounded-lg text-sm p-1 text-center inline-flex items-center transition-colors duration-150 ease-in-out\"\n title=\"Remove this version\"\n x-data=\"{}\"\n x-bind:class={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' ? '${confirmingStateClasses}' : '${defaultStateClasses}'`}\n x-bind:disabled={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting`}\n x-on:click={`\n if ($store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}') {\n $store.confirmingAction.isDeleting = true;\n $el.dispatchEvent(new CustomEvent('confirmed-delete', { bubbles: true }));\n } else {\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n $store.confirmingAction.type = 'version-delete';\n $store.confirmingAction.id = '${libraryName}:${versionParam}';\n $store.confirmingAction.isDeleting = false;\n $store.confirmingAction.timeoutId = setTimeout(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isDeleting = false;\n $store.confirmingAction.timeoutId = null;\n }, 3000);\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\n x-show={`!($store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting)`}\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 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\n x-show={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && !$store.confirmingAction.isDeleting`}\n >\n Confirm?<span class=\"sr-only\">Confirm delete</span>\n </span>\n\n {/* Deleting State: Spinner Icon */}\n <span\n x-show={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting`}\n >\n <LoadingSpinner />\n <span class=\"sr-only\">Loading...</span>\n </span>\n </button>\n )}\n </div>\n );\n};\n\nexport default VersionDetailsRow;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport VersionDetailsRow from \"./VersionDetailsRow\"; // Adjusted import path\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 * Uses VersionDetailsRow without the delete button.\n * @param props - Component props including the library information.\n */\nconst LibraryDetailCard = ({ library }: LibraryDetailCardProps) => (\n // Use Flowbite Card structure with updated padding and border, and white background\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 <h3 class=\"text-lg font-medium text-gray-900 dark:text-white mb-1\">\n <span safe>{library.name}</span>\n </h3>\n {/* Container for version rows */}\n <div class=\"mt-1\">\n {library.versions.length > 0 ? (\n library.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={false}\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 </div>\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 || \"unversioned\"} safe>\n {version.version || \"Unversioned\"}\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\";\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 : false;\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 sanitizedContent = purifier.sanitize(result.content);\n contentElement = (\n <pre\n class=\"format dark:format-invert max-w-none whitespace-pre-wrap text-sm overflow-x-auto\"\n safe\n >\n {sanitizedContent}\n </pre>\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\">\n <a\n href={result.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"underline underline-offset-4 flex-1\"\n safe\n >\n {result.url}\n </a>\n {result.mimeType ? (\n <span class=\"text-xs opacity-75 font-mono\" safe>\n {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\"; // Adjusted import path\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 { SearchTool } from \"../../../tools/SearchTool\";\nimport Layout from \"../../components/Layout\";\nimport LibraryDetailCard from \"../../components/LibraryDetailCard\";\nimport LibrarySearchCard from \"../../components/LibrarySearchCard\";\nimport SearchResultList from \"../../components/SearchResultList\";\nimport SearchResultSkeletonItem from \"../../components/SearchResultSkeletonItem\";\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 */\nexport function registerLibraryDetailRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n searchTool: SearchTool\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 === libraryName\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 \"unversioned\" string to undefined for the tool\n const versionParam = version === \"unversioned\" ? 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 on catch\n reply.type(\"text/html; charset=utf-8\");\n return (\n <p class=\"text-red-500 dark:text-red-400 italic\">\n An unexpected error occurred during the search.\n </p>\n );\n }\n }\n );\n}\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport VersionDetailsRow from \"./VersionDetailsRow\"; // Adjusted import path\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 // Use Flowbite Card structure with updated padding and border, and white background\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\">\n <h3 class=\"text-lg font-medium text-gray-900 dark:text-white mb-1\">\n <a\n href={`/libraries/${encodeURIComponent(library.name)}`}\n class=\"hover:underline\"\n >\n <span safe>{library.name}</span>\n </a>\n </h3>\n {/* Container for version rows */}\n <div class=\"mt-1\">\n {library.versions.length > 0 ? (\n library.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\nexport default LibraryItem;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport LibraryItem from \"./LibraryItem\"; // Adjusted import path\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 return (\n <>\n <div class=\"space-y-2\">\n {libraries.map((library) => (\n <LibraryItem library={library} />\n ))}\n </div>\n </>\n );\n};\n\nexport default LibraryList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListLibrariesTool } from \"../../../tools/ListLibrariesTool\";\nimport { RemoveTool } from \"../../../tools\";\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 */\nexport function registerLibrariesRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n removeTool: RemoveTool // Accept RemoveTool\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 server.log.error(error, \"Failed to list libraries\");\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 === \"unversioned\" ? undefined : versionParam;\n try {\n await removeTool.execute({ library: libraryName, version });\n reply.status(204).send(); // No Content on success\n } catch (error: any) {\n server.log.error(\n error,\n `Failed to remove ${libraryName}@${versionParam}`\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","/**\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 { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { SearchTool } from \"../tools\";\n\nimport { CancelJobTool } from \"../tools/CancelJobTool\";\nimport { ClearCompletedJobsTool } from \"../tools/ClearCompletedJobsTool\";\nimport { ListJobsTool } from \"../tools/ListJobsTool\";\nimport { ListLibrariesTool } from \"../tools/ListLibrariesTool\";\nimport { RemoveTool } from \"../tools/RemoveTool\";\nimport { ScrapeTool } from \"../tools/ScrapeTool\";\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\";\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): 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);\n const removeTool = new RemoveTool(docService, 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);\n registerLibrariesRoutes(server, listLibrariesTool, removeTool);\n registerLibraryDetailRoutes(server, listLibrariesTool, searchTool);\n registerJobListRoutes(server, listJobsTool);\n registerNewJobRoutes(server, scrapeTool);\n registerCancelJobRoute(server, cancelJobTool);\n registerClearCompletedJobsRoute(server, clearCompletedJobsTool);\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 { analytics, TelemetryEvent } from \"../telemetry\";\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 */\nexport async function registerWorkerService(pipeline: IPipeline): Promise<void> {\n // Configure progress callbacks for logging and analytics\n pipeline.setCallbacks({\n onJobProgress: async (job, progress) => {\n logger.debug(\n `Job ${job.id} progress: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n\n // Track job progress for analytics with enhanced metrics\n analytics.track(TelemetryEvent.PIPELINE_JOB_PROGRESS, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n pagesScraped: progress.pagesScraped,\n totalPages: progress.totalPages,\n totalDiscovered: progress.totalDiscovered,\n progressPercent: Math.round((progress.pagesScraped / progress.totalPages) * 100),\n currentDepth: progress.depth,\n maxDepth: progress.maxDepth,\n discoveryRatio: Math.round(\n (progress.totalDiscovered / progress.totalPages) * 100,\n ), // How much we discovered vs limited total\n queueEfficiency:\n progress.totalPages > 0\n ? Math.round((progress.pagesScraped / progress.totalPages) * 100)\n : 0,\n });\n },\n onJobStatusChange: async (job) => {\n logger.debug(`Job ${job.id} status changed to: ${job.status}`);\n\n // Enhanced job completion tracking\n const duration = job.startedAt ? Date.now() - job.startedAt.getTime() : null;\n const queueWaitTime =\n job.startedAt && job.createdAt\n ? job.startedAt.getTime() - job.createdAt.getTime()\n : null;\n\n analytics.track(TelemetryEvent.PIPELINE_JOB_COMPLETED, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n status: job.status,\n durationMs: duration,\n queueWaitTimeMs: queueWaitTime,\n pagesProcessed: job.progressPages || 0,\n maxPagesConfigured: job.progressMaxPages || 0,\n hasVersion: !!job.version,\n hasError: !!job.error,\n throughputPagesPerSecond:\n duration && job.progressPages\n ? Math.round((job.progressPages / duration) * 1000)\n : 0,\n });\n },\n onJobError: async (job, error, document) => {\n logger.warn(\n `⚠️ Job ${job.id} error ${document ? `on document ${document.metadata.url}` : \"\"}: ${error.message}`,\n );\n\n // Use PostHog's native error tracking instead of custom events\n analytics.captureException(error, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n hasDocument: !!document,\n stage: document ? \"document_processing\" : \"job_setup\",\n pages_processed_before_error: job.progressPages || 0,\n });\n },\n });\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 * 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 FastifyInstance } from \"fastify\";\nimport packageJson from \"../../package.json\";\nimport { ProxyAuthManager } from \"../auth\";\nimport { resolveEmbeddingContext } from \"../cli/utils\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { cleanupMcpService, registerMcpService } from \"../services/mcpService\";\nimport { registerTrpcService } from \"../services/trpcService\";\nimport { registerWebService } from \"../services/webService\";\nimport { registerWorkerService, stopWorkerService } from \"../services/workerService\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics, TelemetryEvent } from \"../telemetry\";\nimport { shouldEnableTelemetry } from \"../telemetry/TelemetryConfig\";\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 config: AppServerConfig;\n\n constructor(\n private docService: IDocumentManagement,\n private pipeline: IPipeline,\n config: AppServerConfig,\n ) {\n this.config = config;\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.config.enableWebInterface) {\n if (!this.config.enableWorker && !this.config.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.config.enableMcpServer) {\n if (!this.config.enableWorker && !this.config.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 // Initialize telemetry if enabled\n if (this.config.telemetry !== false && shouldEnableTelemetry()) {\n try {\n // Set global application context that will be included in all events\n if (analytics.isEnabled()) {\n // Resolve embedding configuration for global context\n const embeddingConfig = resolveEmbeddingContext();\n\n analytics.setGlobalContext({\n appVersion: packageJson.version,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appServicesEnabled: this.getActiveServicesList(),\n appAuthEnabled: Boolean(this.config.auth),\n appReadOnly: Boolean(this.config.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 analytics.track(TelemetryEvent.APP_STARTED, {\n services: this.getActiveServicesList(),\n port: this.config.port,\n externalWorker: Boolean(this.config.externalWorkerUrl),\n // Include startup context when available\n ...(this.config.startupContext?.cliCommand && {\n cliCommand: this.config.startupContext.cliCommand,\n }),\n ...(this.config.startupContext?.mcpProtocol && {\n mcpProtocol: this.config.startupContext.mcpProtocol,\n }),\n ...(this.config.startupContext?.mcpTransport && {\n mcpTransport: this.config.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.config.port,\n host: this.config.host,\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 // Stop worker service if enabled\n if (this.config.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 // Track app shutdown\n if (analytics.isEnabled()) {\n analytics.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: true,\n });\n }\n\n // Shutdown telemetry service (this will flush remaining events)\n await analytics.shutdown();\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 (analytics.isEnabled()) {\n analytics.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: false,\n error: error instanceof Error ? error.constructor.name : \"UnknownError\",\n });\n await analytics.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 (analytics.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 analytics.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 (analytics.isEnabled()) {\n analytics.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(async (error, request, reply) => {\n if (analytics.isEnabled()) {\n analytics.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.config.enableMcpServer) services.push(\"mcp\");\n if (this.config.enableWebInterface) services.push(\"web\");\n if (this.config.enableApiServer) services.push(\"api\");\n if (this.config.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 // Initialize authentication if enabled\n if (this.config.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.config.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.config.auth?.enabled && this.authManager) {\n await this.setupAuthMetadataEndpoint();\n }\n\n // Conditionally enable services based on configuration\n if (this.config.enableWebInterface) {\n await this.enableWebInterface();\n }\n\n if (this.config.enableMcpServer) {\n await this.enableMcpServer();\n }\n\n if (this.config.enableApiServer) {\n await this.enableTrpcApi();\n }\n\n if (this.config.enableWorker) {\n await this.enableWorker();\n }\n\n // Setup static file serving as fallback (must be last)\n if (this.config.enableWebInterface) {\n await this.setupStaticFiles();\n }\n }\n\n /**\n * Enable web interface service.\n */\n private async enableWebInterface(): Promise<void> {\n await registerWebService(this.server, this.docService, this.pipeline);\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.config.readOnly,\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);\n logger.debug(\"API server (tRPC) enabled\");\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.config.auth) {\n return;\n }\n\n this.authManager = new ProxyAuthManager(this.config.auth);\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.config.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 logger.info(`🚀 AppServer available at ${address}`);\n\n const enabledServices: string[] = [];\n\n if (this.config.enableWebInterface) {\n enabledServices.push(`Web interface: ${address}`);\n }\n\n if (this.config.enableMcpServer) {\n enabledServices.push(`MCP endpoints: ${address}/mcp, ${address}/sse`);\n }\n\n if (this.config.enableApiServer) {\n enabledServices.push(`API: ${address}/api`);\n }\n\n if (this.config.enableWorker) {\n enabledServices.push(\"Embedded worker: enabled\");\n } else if (this.config.externalWorkerUrl) {\n enabledServices.push(`External worker: ${this.config.externalWorkerUrl}`);\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 { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\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 config: AppServerConfig,\n): Promise<AppServer> {\n const appServer = new AppServer(docService, pipeline, config);\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 { 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 readOnly Whether to run in read-only mode.\n * @returns The created McpServer instance.\n */\nexport async function startStdioServer(\n tools: McpServerTools,\n readOnly = false,\n): Promise<McpServer> {\n // Create a server instance using the factory and shared tools\n const server = createMcpServerInstance(tools, readOnly);\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 for the document management API.\n * Implements IDocumentManagement and delegates to /api data router.\n */\nimport { createTRPCProxyClient, httpBatchLink } from \"@trpc/client\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { logger } from \"../utils/logger\";\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: [httpBatchLink({ url: this.baseUrl })],\n });\n logger.debug(`DocumentManagementClient (tRPC) created for: ${this.baseUrl}`);\n }\n\n async initialize(): Promise<void> {\n // Connectivity check\n await (\n this.client as unknown as { ping: { query: () => Promise<unknown> } }\n ).ping.query();\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","import type { Document } from \"@langchain/core/documents\";\nimport type { DocumentStore } from \"./DocumentStore\";\nimport type { StoreSearchResult } from \"./types\";\n\nconst CHILD_LIMIT = 5;\nconst SIBLING_LIMIT = 2;\n\nexport class DocumentRetrieverService {\n private documentStore: DocumentStore;\n\n constructor(documentStore: DocumentStore) {\n this.documentStore = documentStore;\n }\n\n /**\n * Collects all related chunk IDs for a given initial hit.\n * Returns an object with url, hitId, relatedIds (Set), and score.\n */\n private async getRelatedChunkIds(\n library: string,\n version: string,\n doc: Document,\n siblingLimit = SIBLING_LIMIT,\n childLimit = CHILD_LIMIT,\n ): Promise<{\n url: string;\n hitId: string;\n relatedIds: Set<string>;\n score: number;\n }> {\n const id = doc.id as string;\n const url = doc.metadata.url as string;\n const score = doc.metadata.score as number;\n const relatedIds = new Set<string>();\n relatedIds.add(id);\n\n // Parent\n const parent = await this.documentStore.findParentChunk(library, version, id);\n if (parent) {\n relatedIds.add(parent.id as string);\n }\n\n // Preceding Siblings\n const precedingSiblings = await this.documentStore.findPrecedingSiblingChunks(\n library,\n version,\n id,\n siblingLimit,\n );\n for (const sib of precedingSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n // Child Chunks\n const childChunks = await this.documentStore.findChildChunks(\n library,\n version,\n id,\n childLimit,\n );\n for (const child of childChunks) {\n relatedIds.add(child.id as string);\n }\n\n // Subsequent Siblings\n const subsequentSiblings = await this.documentStore.findSubsequentSiblingChunks(\n library,\n version,\n id,\n siblingLimit,\n );\n for (const sib of subsequentSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n return { url, hitId: id, relatedIds, score };\n }\n\n /**\n * Groups related chunk info by URL, deduplicates IDs, and finds max score per URL.\n */\n private groupAndPrepareFetch(\n relatedInfos: Array<{\n url: string;\n hitId: string;\n relatedIds: Set<string>;\n score: number;\n }>,\n ): Map<string, { uniqueChunkIds: Set<string>; maxScore: number }> {\n const urlMap = new Map<string, { uniqueChunkIds: Set<string>; maxScore: number }>();\n for (const info of relatedInfos) {\n let entry = urlMap.get(info.url);\n if (!entry) {\n entry = { uniqueChunkIds: new Set(), maxScore: info.score };\n urlMap.set(info.url, entry);\n }\n for (const id of info.relatedIds) {\n entry.uniqueChunkIds.add(id);\n }\n if (info.score > entry.maxScore) {\n entry.maxScore = info.score;\n }\n }\n return urlMap;\n }\n\n /**\n * Finalizes the merged result for a URL group by fetching, sorting, and joining content.\n */\n private async finalizeResult(\n library: string,\n version: string,\n url: string,\n uniqueChunkIds: Set<string>,\n maxScore: number,\n ): Promise<StoreSearchResult> {\n const ids = Array.from(uniqueChunkIds);\n const docs = await this.documentStore.findChunksByIds(library, version, ids);\n // Already sorted by sort_order in findChunksByIds\n const content = docs.map((d) => d.pageContent).join(\"\\n\\n\");\n\n // Extract mimeType from the first document's metadata (all chunks from same URL should have same MIME type)\n const mimeType =\n docs.length > 0 ? (docs[0].metadata.mimeType as string | undefined) : undefined;\n\n // TODO: Apply code block merging here if/when implemented\n return {\n url,\n content,\n score: maxScore,\n mimeType,\n };\n }\n\n /**\n * Searches for documents and expands the context around the matches.\n * @param library The library name.\n * @param version The library version.\n * @param query The search query.\n * @param version The library version (optional, defaults to searching documents without a version).\n * @param query The search query.\n * @param limit The optional limit for the initial search results.\n * @returns An array of strings representing the aggregated content of the retrieved chunks.\n */\n async search(\n library: string,\n version: string | null | undefined,\n query: string,\n limit?: number,\n ): Promise<StoreSearchResult[]> {\n // Normalize version: null/undefined becomes empty string, then lowercase\n const normalizedVersion = (version ?? \"\").toLowerCase();\n\n const initialResults = await this.documentStore.findByContent(\n library,\n normalizedVersion,\n query,\n limit ?? 10,\n );\n\n // Step 1: Expand context for each initial hit (collect related chunk IDs)\n const relatedInfos = await Promise.all(\n initialResults.map((doc) =>\n this.getRelatedChunkIds(library, normalizedVersion, doc),\n ),\n );\n\n // Step 2: Group by URL, deduplicate, and find max score\n const urlMap = this.groupAndPrepareFetch(relatedInfos);\n\n // Step 3: For each URL group, fetch, sort, and format the merged result\n const results: StoreSearchResult[] = [];\n for (const [url, { uniqueChunkIds, maxScore }] of urlMap.entries()) {\n const result = await this.finalizeResult(\n library,\n normalizedVersion,\n url,\n uniqueChunkIds,\n maxScore,\n );\n results.push(result);\n }\n\n return results;\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { MIGRATION_MAX_RETRIES, MIGRATION_RETRY_DELAY_MS } from \"../utils/config\";\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 * @throws {StoreError} If any migration fails.\n */\nexport async function applyMigrations(db: Database): Promise<void> {\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 < MIGRATION_MAX_RETRIES) {\n retries++;\n logger.warn(\n `⚠️ Migrations busy (SQLITE_BUSY), retrying attempt ${retries}/${MIGRATION_MAX_RETRIES} in ${MIGRATION_RETRY_DELAY_MS}ms...`,\n );\n await new Promise((resolve) => setTimeout(resolve, MIGRATION_RETRY_DELAY_MS));\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 ${MIGRATION_MAX_RETRIES} 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 { Document } from \"@langchain/core/documents\";\nimport type { Embeddings } from \"@langchain/core/embeddings\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport semver from \"semver\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport type { DocumentMetadata } from \"../types\";\nimport { EMBEDDING_BATCH_CHARS, EMBEDDING_BATCH_SIZE } from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\nimport { applyMigrations } from \"./applyMigrations\";\nimport { EmbeddingConfig, type EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport {\n createEmbeddingModel,\n ModelConfigurationError,\n UnsupportedProviderError,\n} from \"./embeddings/EmbeddingFactory\";\nimport { ConnectionError, DimensionError, StoreError } from \"./errors\";\nimport type { StoredScraperOptions } from \"./types\";\nimport {\n type DbDocument,\n type DbQueryResult,\n type DbVersion,\n type DbVersionWithLibrary,\n denormalizeVersionName,\n mapDbDocumentToDocument,\n normalizeVersionName,\n VECTOR_DIMENSION,\n type VersionScraperOptions,\n type VersionStatus,\n} from \"./types\";\n\ninterface RawSearchResult extends DbDocument {\n vec_score?: number;\n fts_score?: number;\n}\n\ninterface RankedResult extends RawSearchResult {\n vec_rank?: number;\n fts_rank?: number;\n rrf_score: number;\n}\n\n/**\n * Manages document storage and retrieval using SQLite with vector and full-text search capabilities.\n * Provides direct access to SQLite with prepared statements to store and query document\n * embeddings along with their metadata. Supports versioned storage of documents for different\n * libraries, enabling version-specific document retrieval and searches.\n */\nexport class DocumentStore {\n private readonly db: DatabaseType;\n private embeddings!: Embeddings;\n private readonly dbDimension: number = VECTOR_DIMENSION;\n private modelDimension!: number;\n private readonly embeddingConfig?: EmbeddingModelConfig | null;\n private statements!: {\n getById: Database.Statement<[bigint]>;\n insertDocument: Database.Statement<\n [bigint, bigint, string, string, string, number, string]\n >;\n insertEmbedding: Database.Statement<[bigint, bigint, bigint, string]>;\n deleteDocuments: Database.Statement<[string, string, string]>;\n deleteDocumentsByUrl: Database.Statement<[string, string, string, string]>;\n queryVersions: Database.Statement<[string]>;\n checkExists: Database.Statement<[string, string]>;\n queryLibraryVersions: Database.Statement<[]>;\n getChildChunks: Database.Statement<\n [string, string, string, number, string, bigint, number]\n >;\n getPrecedingSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getSubsequentSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getParentChunk: Database.Statement<[string, string, string, string, bigint]>;\n insertLibrary: Database.Statement<[string]>;\n getLibraryIdByName: Database.Statement<[string]>;\n // New version-related statements\n insertVersion: Database.Statement<[number, string | null]>;\n resolveVersionId: Database.Statement<[number, string | null]>;\n getVersionById: Database.Statement<[number]>;\n queryVersionsByLibraryId: Database.Statement<[number]>;\n // Status tracking statements\n updateVersionStatus: Database.Statement<[string, string | null, number]>;\n updateVersionProgress: Database.Statement<[number, number, number]>;\n getVersionsByStatus: Database.Statement<string[]>;\n // Scraper options statements\n updateVersionScraperOptions: Database.Statement<[string, string, number]>;\n getVersionWithOptions: Database.Statement<[number]>;\n getVersionsBySourceUrl: Database.Statement<[string]>;\n // 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 };\n\n /**\n * Calculates Reciprocal Rank Fusion score for a result\n */\n private calculateRRF(vecRank?: number, ftsRank?: number, k = 60): number {\n let rrf = 0;\n if (vecRank !== undefined) {\n rrf += 1 / (k + vecRank);\n }\n if (ftsRank !== undefined) {\n rrf += 1 / (k + ftsRank);\n }\n return rrf;\n }\n\n /**\n * Assigns ranks to search results based on their scores\n */\n private assignRanks(results: RawSearchResult[]): RankedResult[] {\n // Create maps to store ranks\n const vecRanks = new Map<number, number>();\n const ftsRanks = new Map<number, number>();\n\n // Sort by vector scores and assign ranks\n results\n .filter((r) => r.vec_score !== undefined)\n .sort((a, b) => (b.vec_score ?? 0) - (a.vec_score ?? 0))\n .forEach((result, index) => {\n vecRanks.set(Number(result.id), index + 1);\n });\n\n // Sort by BM25 scores and assign ranks\n results\n .filter((r) => r.fts_score !== undefined)\n .sort((a, b) => (b.fts_score ?? 0) - (a.fts_score ?? 0))\n .forEach((result, index) => {\n ftsRanks.set(Number(result.id), index + 1);\n });\n\n // Combine results with ranks and calculate RRF\n return results.map((result) => ({\n ...result,\n vec_rank: vecRanks.get(Number(result.id)),\n fts_rank: ftsRanks.get(Number(result.id)),\n rrf_score: this.calculateRRF(\n vecRanks.get(Number(result.id)),\n ftsRanks.get(Number(result.id)),\n ),\n }));\n }\n\n constructor(dbPath: string, embeddingConfig?: EmbeddingModelConfig | null) {\n if (!dbPath) {\n throw new StoreError(\"Missing required database path\");\n }\n\n // Only establish database connection in constructor\n this.db = new Database(dbPath);\n\n // Store embedding config for later initialization\n this.embeddingConfig = embeddingConfig;\n }\n\n /**\n * Sets up prepared statements for database queries\n */\n private prepareStatements(): void {\n const statements = {\n getById: this.db.prepare<[bigint]>(\"SELECT * FROM documents WHERE id = ?\"),\n insertDocument: this.db.prepare<\n [bigint, bigint, string, string, string, number, string]\n >(\n \"INSERT INTO documents (library_id, version_id, url, content, metadata, sort_order, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n ),\n insertEmbedding: this.db.prepare<[bigint, bigint, bigint, string]>(\n \"INSERT INTO documents_vec (rowid, library_id, version_id, embedding) VALUES (?, ?, ?, ?)\",\n ),\n insertLibrary: this.db.prepare<[string]>(\n \"INSERT INTO libraries (name) VALUES (?) ON CONFLICT(name) DO NOTHING\",\n ),\n getLibraryIdByName: this.db.prepare<[string]>(\n \"SELECT id FROM libraries WHERE name = ?\",\n ),\n // New version-related statements\n insertVersion: this.db.prepare<[number, string | null]>(\n \"INSERT INTO versions (library_id, name, status) VALUES (?, ?, 'not_indexed') ON CONFLICT(library_id, name) DO NOTHING\",\n ),\n resolveVersionId: this.db.prepare<[number, string | null]>(\n \"SELECT id FROM versions WHERE library_id = ? AND name IS ?\",\n ),\n getVersionById: this.db.prepare<[number]>(\"SELECT * FROM versions WHERE id = ?\"),\n queryVersionsByLibraryId: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE library_id = ? ORDER BY name\",\n ),\n deleteLibraryDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocumentsByUrl: this.db.prepare<[string, string, string, string]>(\n `DELETE FROM documents\n WHERE url = ?\n AND library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n getDocumentBySort: this.db.prepare<[string, string]>(\n `SELECT d.id\n FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n queryVersions: this.db.prepare<[string]>(\n `SELECT DISTINCT v.name\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n ORDER BY v.name`,\n ),\n checkExists: this.db.prepare<[string, string]>(\n `SELECT d.id FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n // Library/version aggregation including versions without documents and status/progress fields\n queryLibraryVersions: this.db.prepare<[]>(\n `SELECT\n l.name as library,\n COALESCE(v.name, '') as version,\n v.id as versionId,\n v.status as status,\n v.progress_pages as progressPages,\n v.progress_max_pages as progressMaxPages,\n v.source_url as sourceUrl,\n MIN(d.indexed_at) as indexedAt,\n COUNT(d.id) as documentCount,\n COUNT(DISTINCT d.url) as uniqueUrlCount\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n LEFT JOIN documents d ON d.version_id = v.id\n GROUP BY v.id\n ORDER BY l.name, version`,\n ),\n getChildChunks: this.db.prepare<\n [string, string, string, number, string, bigint, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_array_length(json_extract(d.metadata, '$.path')) = ?\n AND json_extract(d.metadata, '$.path') LIKE ? || '%'\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getPrecedingSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order DESC\n LIMIT ?\n `),\n getSubsequentSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getParentChunk: this.db.prepare<[string, string, string, string, bigint]>(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_extract(d.metadata, '$.path') = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order DESC\n LIMIT 1\n `),\n // Status tracking statements\n updateVersionStatus: this.db.prepare<[string, string | null, number]>(\n \"UPDATE versions SET status = ?, error_message = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n updateVersionProgress: this.db.prepare<[number, number, number]>(\n \"UPDATE versions SET progress_pages = ?, progress_max_pages = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionsByStatus: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.status IN (SELECT value FROM json_each(?))\",\n ),\n // Scraper options statements\n updateVersionScraperOptions: this.db.prepare<[string, string, number]>(\n \"UPDATE versions SET source_url = ?, scraper_options = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionWithOptions: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE id = ?\",\n ),\n getVersionsBySourceUrl: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.source_url = ? ORDER BY v.created_at DESC\",\n ),\n // 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 };\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 either provided config or environment variables.\n * If no embedding config is provided (null), embeddings will not be initialized.\n * This allows DocumentStore to be used without embeddings for operations that don't need them.\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, skip embedding initialization\n if (this.embeddingConfig === null) {\n logger.debug(\"Embedding initialization skipped (explicitly disabled)\");\n return;\n }\n\n // Use provided config or fall back to parsing environment\n const config = this.embeddingConfig || EmbeddingConfig.parseEmbeddingConfig();\n\n // Create embedding model\n try {\n this.embeddings = createEmbeddingModel(config.modelSpec);\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 const testVector = await this.embeddings.embedQuery(\"test\");\n this.modelDimension = testVector.length;\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 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 }\n // Re-throw other embedding errors (like DimensionError) as-is\n throw error;\n }\n }\n\n /**\n * Escapes a query string for use with SQLite FTS5 MATCH operator.\n * Wraps the query in double quotes and escapes internal double quotes.\n */\n private escapeFtsQuery(query: string): string {\n // Escape internal double quotes by doubling them\n const escapedQuotes = query.replace(/\"/g, '\"\"');\n // Wrap the entire string in double quotes\n return `\"${escapedQuotes}\"`;\n }\n\n /**\n * Initializes database connection and ensures readiness\n */\n async initialize(): Promise<void> {\n try {\n // 1. Load extensions first (moved before migrations)\n sqliteVec.load(this.db);\n\n // 2. Apply migrations (after extensions are loaded)\n applyMigrations(this.db);\n\n // 3. Initialize prepared statements\n this.prepareStatements();\n\n // 4. Initialize embeddings client (await to catch errors)\n await this.initializeEmbeddings();\n } catch (error) {\n // Re-throw StoreError, 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 library_id and version_id.\n * Creates library and version records if they don't exist.\n */\n async resolveLibraryAndVersionIds(\n library: string,\n version: string,\n ): Promise<{ libraryId: number; versionId: number }> {\n const normalizedLibrary = library.toLowerCase();\n const normalizedVersion = denormalizeVersionName(version.toLowerCase());\n\n // Insert or get library_id\n this.statements.insertLibrary.run(normalizedLibrary);\n const libraryIdRow = this.statements.getLibraryIdByName.get(normalizedLibrary) as\n | { id: number }\n | undefined;\n if (!libraryIdRow || typeof libraryIdRow.id !== \"number\") {\n throw new StoreError(`Failed to resolve library_id for library: ${library}`);\n }\n const libraryId = libraryIdRow.id;\n\n // Insert or get version_id\n // Reuse existing unversioned entry if present; storing '' ensures UNIQUE constraint applies\n this.statements.insertVersion.run(libraryId, normalizedVersion);\n const versionIdRow = this.statements.resolveVersionId.get(\n libraryId,\n normalizedVersion === null ? \"\" : normalizedVersion,\n ) as { id: number } | undefined;\n if (!versionIdRow || typeof versionIdRow.id !== \"number\") {\n throw new StoreError(\n `Failed to resolve version_id for library: ${library}, version: ${version}`,\n );\n }\n\n return { libraryId, versionId: versionIdRow.id };\n }\n\n /**\n * Retrieves all unique versions for a specific library\n */\n async queryUniqueVersions(library: string): Promise<string[]> {\n try {\n const rows = this.statements.queryVersions.all(library.toLowerCase()) as Array<{\n name: string | null;\n }>;\n return rows.map((row) => normalizeVersionName(row.name));\n } catch (error) {\n throw new ConnectionError(\"Failed to query versions\", error);\n }\n }\n\n /**\n * Updates the status of a version record in the database.\n * @param versionId The version ID to update\n * @param status The new status to set\n * @param errorMessage Optional error message for failed statuses\n */\n async updateVersionStatus(\n versionId: number,\n status: VersionStatus,\n errorMessage?: string,\n ): Promise<void> {\n try {\n this.statements.updateVersionStatus.run(status, errorMessage ?? null, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version status: ${error}`);\n }\n }\n\n /**\n * Updates the progress counters for a version being indexed.\n * @param versionId The version ID to update\n * @param pages Current number of pages processed\n * @param maxPages Total number of pages to process\n */\n async updateVersionProgress(\n versionId: number,\n pages: number,\n maxPages: number,\n ): Promise<void> {\n try {\n this.statements.updateVersionProgress.run(pages, maxPages, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version progress: ${error}`);\n }\n }\n\n /**\n * Retrieves versions by their status.\n * @param statuses Array of statuses to filter by\n * @returns Array of version records matching the statuses\n */\n async getVersionsByStatus(statuses: VersionStatus[]): Promise<DbVersionWithLibrary[]> {\n try {\n const statusJson = JSON.stringify(statuses);\n const rows = this.statements.getVersionsByStatus.all(\n statusJson,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to get versions by status: ${error}`);\n }\n }\n\n /**\n * Stores scraper options for a version to enable reproducible indexing.\n * @param versionId The version ID to update\n * @param options Complete scraper options used for indexing\n */\n async storeScraperOptions(versionId: number, options: ScraperOptions): Promise<void> {\n try {\n // biome-ignore lint/correctness/noUnusedVariables: Extract source URL and exclude runtime-only fields using destructuring\n const { url: source_url, library, version, signal, ...scraper_options } = options;\n\n const optionsJson = JSON.stringify(scraper_options);\n this.statements.updateVersionScraperOptions.run(source_url, optionsJson, versionId);\n } catch (error) {\n throw new StoreError(`Failed to store scraper options: ${error}`);\n }\n }\n\n /**\n * Retrieves stored scraping configuration (source URL and options) for a version.\n * Returns null when no source URL is recorded (not re-indexable).\n */\n async getScraperOptions(versionId: number): Promise<StoredScraperOptions | null> {\n try {\n const row = this.statements.getVersionWithOptions.get(versionId) as\n | DbVersion\n | undefined;\n\n if (!row?.source_url) {\n return null;\n }\n\n let parsed: VersionScraperOptions = {} as VersionScraperOptions;\n if (row.scraper_options) {\n try {\n parsed = JSON.parse(row.scraper_options) as VersionScraperOptions;\n } catch (e) {\n logger.warn(`⚠️ Invalid scraper_options JSON for version ${versionId}: ${e}`);\n parsed = {} as VersionScraperOptions;\n }\n }\n\n return { sourceUrl: row.source_url, options: parsed };\n } catch (error) {\n throw new StoreError(`Failed to get scraper options: ${error}`);\n }\n }\n\n /**\n * Finds versions that were indexed from the same source URL.\n * Useful for finding similar configurations or detecting duplicates.\n * @param url Source URL to search for\n * @returns Array of versions with the same source URL\n */\n async findVersionsBySourceUrl(url: string): Promise<DbVersionWithLibrary[]> {\n try {\n const rows = this.statements.getVersionsBySourceUrl.all(\n url,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to find versions by source URL: ${error}`);\n }\n }\n\n /**\n * Verifies existence of documents for a specific library version\n */\n async checkDocumentExists(library: string, version: string): Promise<boolean> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.checkExists.get(\n library.toLowerCase(),\n normalizedVersion,\n );\n return result !== undefined;\n } catch (error) {\n throw new ConnectionError(\"Failed to check document existence\", error);\n }\n }\n\n /**\n * Retrieves a mapping of all libraries to their available versions with details.\n */\n async queryLibraryVersions(): Promise<\n Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus; // Persisted enum value\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >\n > {\n try {\n // Define the expected row structure from the GROUP BY query (including versions without documents)\n interface LibraryVersionRow {\n library: string;\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null; // MIN() may return null\n }\n\n const rows = this.statements.queryLibraryVersions.all() as LibraryVersionRow[];\n const libraryMap = new Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >();\n\n for (const row of rows) {\n // Process all rows, including those where version is \"\" (unversioned)\n const library = row.library;\n if (!libraryMap.has(library)) {\n libraryMap.set(library, []);\n }\n\n // Format indexedAt to ISO string if available\n const indexedAtISO = row.indexedAt ? new Date(row.indexedAt).toISOString() : null;\n\n libraryMap.get(library)?.push({\n version: row.version,\n versionId: row.versionId,\n // Preserve raw string status here; DocumentManagementService will cast to VersionStatus\n status: row.status,\n progressPages: row.progressPages,\n progressMaxPages: row.progressMaxPages,\n sourceUrl: row.sourceUrl,\n documentCount: row.documentCount,\n uniqueUrlCount: row.uniqueUrlCount,\n indexedAt: indexedAtISO,\n });\n }\n\n // Sort versions within each library: unversioned first, then semantically\n for (const versions of libraryMap.values()) {\n versions.sort((a, b) => {\n if (a.version === \"\" && b.version !== \"\") {\n return -1; // a (unversioned) comes first\n }\n if (a.version !== \"\" && b.version === \"\") {\n return 1; // b (unversioned) comes first\n }\n if (a.version === \"\" && b.version === \"\") {\n return 0; // Should not happen with GROUP BY, but handle anyway\n }\n // Both are non-empty, use semver compare with fallback to string compare\n try {\n return semver.compare(a.version, b.version);\n } catch (_error) {\n // Fallback to lexicographic comparison if semver parsing fails\n return a.version.localeCompare(b.version);\n }\n });\n }\n\n return libraryMap;\n } catch (error) {\n throw new ConnectionError(\"Failed to query library versions\", error);\n }\n }\n\n /**\n * Stores documents with library and version metadata, generating embeddings\n * for vector similarity search. Automatically removes any existing documents\n * for the same URLs before adding new ones to prevent UNIQUE constraint violations.\n */\n async addDocuments(\n library: string,\n version: string,\n documents: Document[],\n ): Promise<void> {\n try {\n if (documents.length === 0) {\n return;\n }\n\n // Extract unique URLs from the documents being added\n const urls = new Set<string>();\n for (const doc of documents) {\n const url = doc.metadata.url as string;\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n urls.add(url);\n }\n\n // Generate embeddings in batch\n const texts = documents.map((doc) => {\n const header = `<title>${doc.metadata.title}</title>\\n<url>${doc.metadata.url}</url>\\n<path>${doc.metadata.path.join(\" / \")}</path>\\n`;\n return `${header}${doc.pageContent}`;\n });\n\n // Batch embedding creation to avoid token limit errors\n // Use size-based batching to prevent 413 errors\n const maxBatchChars =\n Number(process.env.DOCS_MCP_EMBEDDING_BATCH_CHARS) || EMBEDDING_BATCH_CHARS;\n const rawEmbeddings: number[][] = [];\n\n let currentBatch: string[] = [];\n let currentBatchSize = 0;\n let batchCount = 0;\n\n for (const text of texts) {\n const textSize = text.length;\n\n // If adding this text would exceed the limit, process the current batch first\n if (currentBatchSize + textSize > maxBatchChars && currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n\n // Add text to current batch\n currentBatch.push(text);\n currentBatchSize += textSize;\n\n // Also respect the count-based limit for APIs that have per-request item limits\n if (currentBatch.length >= EMBEDDING_BATCH_SIZE) {\n batchCount++;\n logger.debug(\n `Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n }\n\n // Process any remaining texts in the final batch\n if (currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `Processing final embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n }\n const paddedEmbeddings = rawEmbeddings.map((vector) => this.padVector(vector));\n\n // Resolve library and version IDs (creates them if they don't exist)\n const { libraryId, versionId } = await this.resolveLibraryAndVersionIds(\n library,\n version,\n );\n\n // Delete existing documents for these URLs to prevent UNIQUE constraint violations\n // This must happen AFTER resolveLibraryAndVersionIds to ensure the library/version exist\n for (const url of urls) {\n const deletedCount = await this.deleteDocumentsByUrl(library, version, url);\n if (deletedCount > 0) {\n logger.debug(`Deleted ${deletedCount} existing documents for URL: ${url}`);\n }\n }\n\n // Insert documents in a transaction\n const transaction = this.db.transaction((docs: typeof documents) => {\n for (let i = 0; i < docs.length; i++) {\n const doc = docs[i];\n const url = doc.metadata.url as string;\n\n // Insert into main documents table using foreign key IDs\n const result = this.statements.insertDocument.run(\n BigInt(libraryId),\n BigInt(versionId),\n url,\n doc.pageContent,\n JSON.stringify(doc.metadata),\n i,\n new Date().toISOString(), // Pass current timestamp for indexed_at\n );\n const rowId = result.lastInsertRowid;\n\n // Insert into vector table using foreign key IDs\n this.statements.insertEmbedding.run(\n BigInt(rowId),\n BigInt(libraryId),\n BigInt(versionId),\n JSON.stringify(paddedEmbeddings[i]),\n );\n }\n });\n\n transaction(documents);\n } catch (error) {\n throw new ConnectionError(\"Failed to add documents to store\", error);\n }\n }\n\n /**\n * Removes documents matching specified library and version\n * @returns Number of documents deleted\n */\n async deleteDocuments(library: string, version: string): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocuments.run(\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents\", error);\n }\n }\n\n /**\n * Removes documents for a specific URL within a library and version\n * @returns Number of documents deleted\n */\n async deleteDocumentsByUrl(\n library: string,\n version: string,\n url: string,\n ): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocumentsByUrl.run(\n url,\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents by URL\", error);\n }\n }\n\n /**\n * 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 all documents for this version\n const documentsDeleted = await this.deleteDocuments(library, version);\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 * Retrieves a document by its ID.\n * @param id The ID of the document.\n * @returns The document, or null if not found.\n */\n async getById(id: string): Promise<Document | null> {\n try {\n const row = this.statements.getById.get(BigInt(id)) as DbQueryResult<DbDocument>;\n if (!row) {\n return null;\n }\n\n return mapDbDocumentToDocument(row);\n } catch (error) {\n throw new ConnectionError(`Failed to get document by ID ${id}`, error);\n }\n }\n\n /**\n * Finds documents matching a text query using hybrid search.\n * Combines vector similarity search with full-text search using Reciprocal Rank Fusion.\n */\n async findByContent(\n library: string,\n version: string,\n query: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const rawEmbedding = await this.embeddings.embedQuery(query);\n const embedding = this.padVector(rawEmbedding);\n const ftsQuery = this.escapeFtsQuery(query); // Escape the query for FTS\n const normalizedVersion = version.toLowerCase();\n\n const stmt = this.db.prepare(`\n WITH vec_distances AS (\n SELECT\n dv.rowid as id,\n dv.distance as vec_distance\n FROM documents_vec dv\n JOIN versions v ON dv.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND dv.embedding MATCH ?\n AND dv.k = ?\n ORDER BY dv.distance\n ),\n fts_scores AS (\n SELECT\n f.rowid as id,\n bm25(documents_fts, 10.0, 1.0, 5.0, 1.0) as fts_score\n FROM documents_fts f\n JOIN documents d ON f.rowid = d.id\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND documents_fts MATCH ?\n ORDER BY fts_score\n LIMIT ?\n )\n SELECT\n d.id,\n d.content,\n d.metadata,\n COALESCE(1 / (1 + v.vec_distance), 0) as vec_score,\n COALESCE(-MIN(f.fts_score, 0), 0) as fts_score\n FROM documents d\n LEFT JOIN vec_distances v ON d.id = v.id\n LEFT JOIN fts_scores f ON d.id = f.id\n WHERE v.id IS NOT NULL OR f.id IS NOT NULL\n `);\n\n const rawResults = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n JSON.stringify(embedding),\n limit,\n library.toLowerCase(),\n normalizedVersion,\n ftsQuery, // Use the escaped query\n limit,\n ) as RawSearchResult[];\n\n // Apply RRF ranking\n const rankedResults = this.assignRanks(rawResults);\n\n // Sort by RRF score and take top results\n const topResults = rankedResults\n .sort((a, b) => b.rrf_score - a.rrf_score)\n .slice(0, limit);\n\n return topResults.map((row) => ({\n ...mapDbDocumentToDocument(row),\n metadata: {\n ...JSON.parse(row.metadata),\n id: row.id,\n score: row.rrf_score,\n vec_rank: row.vec_rank,\n fts_rank: row.fts_rank,\n },\n }));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find documents by content with query \"${query}\"`,\n error,\n );\n }\n }\n\n /**\n * Finds child chunks of a given document based on path hierarchy.\n */\n async findChildChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const parent = await this.getById(id);\n if (!parent) {\n return [];\n }\n\n const parentPath = (parent.metadata as DocumentMetadata).path ?? [];\n const parentUrl = (parent.metadata as DocumentMetadata).url;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getChildChunks.all(\n library.toLowerCase(),\n normalizedVersion,\n parentUrl,\n parentPath.length + 1,\n JSON.stringify(parentPath),\n BigInt(id),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(`Failed to find child chunks for ID ${id}`, error);\n }\n }\n\n /**\n * Finds preceding sibling chunks of a given document.\n */\n async findPrecedingSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata as DocumentMetadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getPrecedingSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.reverse().map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find preceding sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds subsequent sibling chunks of a given document.\n */\n async findSubsequentSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getSubsequentSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find subsequent sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds the parent chunk of a given document.\n */\n async findParentChunk(\n library: string,\n version: string,\n id: string,\n ): Promise<Document | null> {\n try {\n const child = await this.getById(id);\n if (!child) {\n return null;\n }\n\n const childMetadata = child.metadata as DocumentMetadata;\n const path = childMetadata.path ?? [];\n const parentPath = path.slice(0, -1);\n\n if (parentPath.length === 0) {\n return null;\n }\n\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.getParentChunk.get(\n library.toLowerCase(),\n normalizedVersion,\n childMetadata.url,\n JSON.stringify(parentPath),\n BigInt(id),\n ) as DbQueryResult<DbDocument>;\n\n if (!result) {\n return null;\n }\n\n return mapDbDocumentToDocument(result);\n } catch (error) {\n throw new ConnectionError(`Failed to find parent chunk for ID ${id}`, error);\n }\n }\n\n /**\n * Fetches multiple documents by their IDs in a single call.\n * Returns an array of Document objects, sorted by their sort_order.\n */\n async findChunksByIds(\n library: string,\n version: string,\n ids: string[],\n ): Promise<Document[]> {\n if (!ids.length) return [];\n try {\n const normalizedVersion = version.toLowerCase();\n // Use parameterized query for variable number of IDs\n const placeholders = ids.map(() => \"?\").join(\",\");\n const stmt = this.db.prepare(\n `SELECT d.* FROM documents d\n JOIN libraries l ON d.library_id = l.id\n JOIN versions v ON d.version_id = v.id\n WHERE l.name = ? \n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.id IN (${placeholders}) \n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n ...ids,\n ) as DbDocument[];\n return rows.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\"Failed to fetch documents by IDs\", error);\n }\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Document } from \"@langchain/core/documents\";\nimport envPaths from \"env-paths\";\nimport Fuse from \"fuse.js\";\nimport semver from \"semver\";\nimport {\n type PipelineConfiguration,\n PipelineFactory,\n} from \"../scraper/pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../scraper/pipelines/types\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport type { ContentChunk } from \"../splitter/types\";\nimport { analytics, extractHostname, TelemetryEvent } from \"../telemetry\";\nimport { LibraryNotFoundError, VersionNotFoundError } from \"../tools\";\nimport { logger } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport { DocumentRetrieverService } from \"./DocumentRetrieverService\";\nimport { DocumentStore } from \"./DocumentStore\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport { StoreError } from \"./errors\";\nimport type {\n DbVersionWithLibrary,\n FindVersionResult,\n LibrarySummary,\n ScraperConfig,\n StoreSearchResult,\n VersionRef,\n VersionStatus,\n VersionSummary,\n} from \"./types\";\n\n/**\n * Provides semantic search capabilities across different versions of library documentation.\n * Uses content-type-specific pipelines for processing and splitting content.\n */\nexport class DocumentManagementService {\n private readonly store: DocumentStore;\n private readonly documentRetriever: DocumentRetrieverService;\n private readonly pipelines: ContentPipeline[];\n\n /**\n * Normalizes a version string, converting null or undefined to an empty string\n * and converting to lowercase.\n */\n private normalizeVersion(version?: string | null): string {\n return (version ?? \"\").toLowerCase();\n }\n\n constructor(\n embeddingConfig?: EmbeddingModelConfig | null,\n pipelineConfig?: PipelineConfiguration,\n ) {\n let dbPath: string;\n let dbDir: string;\n\n // 1. Check Environment Variable\n const envStorePath = process.env.DOCS_MCP_STORE_PATH;\n if (envStorePath) {\n dbDir = envStorePath;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`Using database directory from DOCS_MCP_STORE_PATH: ${dbDir}`);\n } else {\n // 2. Check Old Local Path\n const projectRoot = getProjectRoot();\n const oldDbDir = path.join(projectRoot, \".store\");\n const oldDbPath = path.join(oldDbDir, \"documents.db\");\n const oldDbExists = fs.existsSync(oldDbPath); // Check file existence specifically\n\n if (oldDbExists) {\n dbPath = oldDbPath;\n dbDir = oldDbDir;\n logger.debug(`Using legacy database path: ${dbPath}`);\n } else {\n // 3. Use Standard Path\n const standardPaths = envPaths(\"docs-mcp-server\", { suffix: \"\" });\n dbDir = standardPaths.data;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`Using standard database directory: ${dbDir}`);\n }\n }\n\n // Ensure the chosen directory exists\n try {\n fs.mkdirSync(dbDir, { recursive: true });\n } catch (error) {\n // Log potential error during directory creation but proceed\n // The DocumentStore constructor might handle DB file creation errors\n logger.error(`⚠️ Failed to create database directory ${dbDir}: ${error}`);\n }\n\n this.store = new DocumentStore(dbPath, embeddingConfig);\n this.documentRetriever = new DocumentRetrieverService(this.store);\n\n // Initialize content pipelines for different content types including universal TextPipeline fallback\n this.pipelines = PipelineFactory.createStandardPipelines(pipelineConfig);\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 (either versioned or unversioned).\n * Throws LibraryNotFoundError with suggestions if the library is not found.\n * @param library The name of the library to validate.\n * @throws {LibraryNotFoundError} If the library does not exist.\n */\n async validateLibraryExists(library: string): Promise<void> {\n logger.info(`🔎 Validating existence of library: ${library}`);\n const normalizedLibrary = library.toLowerCase(); // Ensure consistent casing\n\n // Check for both versioned and unversioned documents\n const versions = await this.listVersions(normalizedLibrary);\n const hasUnversioned = await this.exists(normalizedLibrary, \"\"); // Check explicitly for unversioned\n\n if (versions.length === 0 && !hasUnversioned) {\n logger.warn(`⚠️ Library '${library}' not found.`);\n\n // Library doesn't exist, fetch all libraries to provide suggestions\n const allLibraries = await this.listLibraries();\n const libraryNames = allLibraries.map((lib) => lib.library);\n\n let suggestions: string[] = [];\n if (libraryNames.length > 0) {\n const fuse = new Fuse(libraryNames, {\n // Configure fuse.js options if needed (e.g., threshold)\n // isCaseSensitive: false, // Handled by normalizing library names\n // includeScore: true,\n threshold: 0.4, // Adjust threshold for desired fuzziness (0=exact, 1=match anything)\n });\n const results = fuse.search(normalizedLibrary);\n // Take top 3 suggestions\n suggestions = results.slice(0, 3).map((result) => result.item);\n logger.info(`🔍 Found suggestions: ${suggestions.join(\", \")}`);\n }\n\n throw new LibraryNotFoundError(library, suggestions);\n }\n\n logger.info(`✅ Library '${library}' confirmed to exist.`);\n }\n\n /**\n * Returns a list of all available semantic versions for a library.\n */\n async listVersions(library: string): Promise<string[]> {\n const versions = await this.store.queryUniqueVersions(library);\n return versions.filter((v) => semver.valid(v));\n }\n\n /**\n * Checks if documents exist for a given library and optional version.\n * If version is omitted, checks for documents without a specific version.\n */\n async exists(library: string, version?: string | null): Promise<boolean> {\n const normalizedVersion = this.normalizeVersion(version);\n return this.store.checkDocumentExists(library, normalizedVersion);\n }\n\n /**\n * Finds the most appropriate version of documentation based on the requested version.\n * When no target version is specified, returns the latest version.\n *\n * Version matching behavior:\n * - Exact versions (e.g., \"18.0.0\"): Matches that version or any earlier version\n * - X-Range patterns (e.g., \"5.x\", \"5.2.x\"): Matches within the specified range\n * - \"latest\" or no version: Returns the latest available version\n *\n * For documentation, we prefer matching older versions over no match at all,\n * since older docs are often still relevant and useful.\n * Also checks if unversioned documents exist for the library.\n */\n async findBestVersion(\n library: string,\n targetVersion?: string,\n ): Promise<FindVersionResult> {\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n logger.info(`🔍 Finding best version for ${libraryAndVersion}`);\n\n // Check if unversioned documents exist *before* filtering for valid semver\n const hasUnversioned = await this.store.checkDocumentExists(library, \"\");\n const versionStrings = await this.listVersions(library);\n\n if (versionStrings.length === 0) {\n if (hasUnversioned) {\n logger.info(`ℹ️ Unversioned documents exist for ${library}`);\n return { bestMatch: null, hasUnversioned: true };\n }\n // Throw error only if NO versions (semver or unversioned) exist\n logger.warn(`⚠️ No valid versions found for ${library}`);\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n let bestMatch: string | null = null;\n\n if (!targetVersion || targetVersion === \"latest\") {\n bestMatch = semver.maxSatisfying(versionStrings, \"*\");\n } else {\n const versionRegex = /^(\\d+)(?:\\.(?:x(?:\\.x)?|\\d+(?:\\.(?:x|\\d+))?))?$|^$/;\n if (!versionRegex.test(targetVersion)) {\n logger.warn(`⚠️ Invalid target version format: ${targetVersion}`);\n // Don't throw yet, maybe unversioned exists\n } else {\n // Restore the previous logic with fallback\n let range = targetVersion;\n if (!semver.validRange(targetVersion)) {\n // If it's not a valid range (like '1.2' or '1'), treat it like a tilde range\n range = `~${targetVersion}`;\n } else if (semver.valid(targetVersion)) {\n // If it's an exact version, allow matching it OR any older version\n range = `${range} || <=${targetVersion}`;\n }\n // If it was already a valid range (like '1.x'), use it directly\n bestMatch = semver.maxSatisfying(versionStrings, range);\n }\n }\n\n if (bestMatch) {\n logger.info(`✅ Found best match version ${bestMatch} for ${libraryAndVersion}`);\n } else {\n logger.warn(`⚠️ No matching semver version found for ${libraryAndVersion}`);\n }\n\n // If no semver match found, but unversioned exists, return that info.\n // If a semver match was found, return it along with unversioned status.\n // If no semver match AND no unversioned, throw error.\n if (!bestMatch && !hasUnversioned) {\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n return { bestMatch, hasUnversioned };\n }\n\n /**\n * Removes all documents for a specific library and optional version.\n * If version is omitted, removes documents without a specific version.\n */\n async removeAllDocuments(library: string, version?: string | null): Promise<void> {\n const normalizedVersion = this.normalizeVersion(version);\n logger.info(\n `🗑️ Removing all documents from ${library}@${normalizedVersion || \"[no version]\"} store`,\n );\n const count = await this.store.deleteDocuments(library, normalizedVersion);\n logger.info(`🗑️ Deleted ${count} documents`);\n }\n\n /**\n * Completely removes a library version and all associated documents.\n * Also removes the library if no other versions remain.\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.info(`🗑️ Removing version: ${library}@${normalizedVersion || \"[no version]\"}`);\n\n const result = await this.store.removeVersion(library, normalizedVersion, true);\n\n logger.info(\n `🗑️ Removed ${result.documentsDeleted} documents, version: ${result.versionDeleted}, library: ${result.libraryDeleted}`,\n );\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 || \"[no version]\"}`);\n } else {\n logger.warn(\n `⚠️ Version ${library}@${normalizedVersion || \"[no version]\"} not found`,\n );\n }\n }\n\n /**\n * Adds a document to the store, splitting it into smaller chunks for better search results.\n * Uses SemanticMarkdownSplitter to maintain markdown structure and content types during splitting.\n * Preserves hierarchical structure of documents and distinguishes between text and code segments.\n * If version is omitted, the document is added without a specific version.\n */\n async addDocument(\n library: string,\n version: string | null | undefined,\n document: Document,\n ): Promise<void> {\n const processingStart = performance.now();\n const normalizedVersion = this.normalizeVersion(version);\n const url = document.metadata.url as string;\n\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n\n logger.info(`📚 Adding document: ${document.metadata.title}`);\n\n if (!document.pageContent.trim()) {\n throw new Error(\"Document content cannot be empty\");\n }\n\n const contentType = document.metadata.mimeType as string | undefined;\n\n try {\n // Create a mock RawContent for pipeline selection\n const rawContent = {\n source: url,\n content: document.pageContent,\n mimeType: contentType || \"text/plain\",\n };\n\n // Find appropriate pipeline for content type\n const pipeline = this.pipelines.find((p) => p.canProcess(rawContent));\n\n if (!pipeline) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for document ${url}. Skipping processing.`,\n );\n return;\n }\n\n // Debug logging for pipeline selection\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${url})`,\n );\n\n // Use content-type-specific pipeline for processing and splitting\n // Create minimal scraper options for processing\n const scraperOptions = {\n url: url,\n library: library,\n version: normalizedVersion,\n scrapeMode: ScrapeMode.Fetch,\n ignoreErrors: false,\n maxConcurrency: 1,\n };\n\n const processed = await pipeline.process(rawContent, scraperOptions);\n const chunks = processed.chunks;\n\n // Convert semantic chunks to documents\n const splitDocs = chunks.map((chunk: ContentChunk) => ({\n pageContent: chunk.content,\n metadata: {\n ...document.metadata,\n level: chunk.section.level,\n path: chunk.section.path,\n },\n }));\n logger.info(`✂️ Split document into ${splitDocs.length} chunks`);\n\n // Add split documents to store\n await this.store.addDocuments(library, normalizedVersion, splitDocs);\n\n // Track successful document processing\n const processingTime = performance.now() - processingStart;\n analytics.track(TelemetryEvent.DOCUMENT_PROCESSED, {\n // Content characteristics (privacy-safe)\n mimeType: contentType || \"unknown\",\n contentSizeBytes: document.pageContent.length,\n\n // Processing metrics\n processingTimeMs: Math.round(processingTime),\n chunksCreated: splitDocs.length,\n\n // Document characteristics\n hasTitle: !!document.metadata.title,\n hasDescription: !!document.metadata.description,\n urlDomain: extractHostname(url),\n depth: document.metadata.depth,\n\n // Library context\n library,\n libraryVersion: normalizedVersion || null,\n\n // Processing efficiency\n avgChunkSizeBytes: Math.round(document.pageContent.length / splitDocs.length),\n processingSpeedKbPerSec: Math.round(\n document.pageContent.length / 1024 / (processingTime / 1000),\n ),\n });\n } catch (error) {\n // Track processing failures with native error tracking\n const processingTime = performance.now() - processingStart;\n\n if (error instanceof Error) {\n analytics.captureException(error, {\n mimeType: contentType || \"unknown\",\n contentSizeBytes: document.pageContent.length,\n processingTimeMs: Math.round(processingTime),\n library,\n libraryVersion: normalizedVersion || null,\n context: \"document_processing\",\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.resolveLibraryAndVersionIds(\n normalizedLibrary,\n normalizedVersion,\n );\n\n return versionId;\n }\n}\n","import { DocumentManagementClient } from \"./DocumentManagementClient\";\nimport { DocumentManagementService } from \"./DocumentManagementService\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\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(\n options: { serverUrl?: string; embeddingConfig?: EmbeddingModelConfig | null } = {},\n) {\n if (options.serverUrl) {\n const client = new DocumentManagementClient(options.serverUrl);\n await client.initialize();\n return client as IDocumentManagement;\n }\n const service = new DocumentManagementService(options.embeddingConfig);\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 embeddingConfig?: EmbeddingModelConfig | null,\n) {\n const service = new DocumentManagementService(embeddingConfig);\n await service.initialize();\n return service;\n}\n","/**\n * Default command - Starts unified server when no subcommand is specified.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n ensurePlaywrightBrowsersInstalled,\n parseAuthConfig,\n resolveEmbeddingContext,\n resolveProtocol,\n validateAuthConfig,\n validateHost,\n validatePort,\n warnHttpUsage,\n} from \"../utils\";\n\nexport function createDefaultAction(program: Command): Command {\n return (\n program\n .addOption(\n new Option(\"--protocol <protocol>\", \"Protocol for MCP server\")\n .choices([\"auto\", \"stdio\", \"http\"])\n .default(\"auto\"),\n )\n .addOption(\n new Option(\"--port <number>\", \"Port for the server\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.HTTP_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the server to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\"--resume\", \"Resume interrupted jobs on startup\", false)\n .option(\"--no-resume\", \"Do not resume jobs on startup\")\n .option(\n \"--read-only\",\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n false,\n )\n // Auth options\n .option(\n \"--auth-enabled\",\n \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n false,\n )\n .option(\"--auth-issuer-url <url>\", \"Issuer/discovery URL for OAuth2/OIDC provider\")\n .option(\n \"--auth-audience <id>\",\n \"JWT audience claim (identifies this protected resource)\",\n )\n .action(\n async (options: {\n protocol: string;\n port: string;\n host: string;\n resume: boolean;\n readOnly: boolean;\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n }) => {\n // Resolve protocol and validate flags\n const resolvedProtocol = resolveProtocol(options.protocol);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR); // Force quiet logging in stdio mode\n }\n\n logger.debug(\"No subcommand specified, starting unified server by default...\");\n const port = validatePort(options.port);\n const host = validateHost(options.host);\n\n // Parse and validate auth configuration\n const authConfig = parseAuthConfig({\n authEnabled: options.authEnabled,\n authIssuerUrl: options.authIssuerUrl,\n authAudience: options.authAudience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n warnHttpUsage(authConfig, port);\n }\n\n // Ensure browsers are installed\n ensurePlaywrightBrowsersInstalled();\n\n // Resolve embedding configuration for local execution (default action needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n const docService = await createLocalDocumentManagement(embeddingConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: options.resume || false, // Use --resume flag for job recovery\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(docService, pipelineOptions);\n\n if (resolvedProtocol === \"stdio\") {\n // Direct stdio mode - bypass AppServer entirely\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n\n await pipeline.start(); // Start pipeline for stdio mode\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = await startStdioServer(mcpTools, options.readOnly);\n\n // Register for graceful shutdown (stdio mode)\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {}); // Keep running forever\n } else {\n // HTTP mode - use AppServer\n logger.debug(`Auto-detected http protocol (TTY available)`);\n\n // Configure services based on resolved protocol\n const config = createAppServerConfig({\n enableWebInterface: true, // Enable web interface in http mode\n enableMcpServer: true, // Always enable MCP server\n enableApiServer: true, // Enable API (tRPC) in http mode\n enableWorker: true, // Always enable in-process worker for unified server\n port,\n host,\n readOnly: options.readOnly,\n auth: authConfig,\n startupContext: {\n cliCommand: \"default\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown (http mode)\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\n });\n\n await new Promise(() => {}); // Keep running forever\n }\n },\n )\n );\n}\n","/**\n * Fetch URL command - Fetches a URL and converts its content to Markdown.\n */\n\nimport type { Command } from \"commander\";\nimport { FileFetcher, HttpFetcher } from \"../../scraper/fetcher\";\nimport { ScrapeMode } from \"../../scraper/types\";\nimport { FetchUrlTool } from \"../../tools\";\nimport { parseHeaders } from \"../utils\";\n\nexport async function fetchUrlAction(\n url: string,\n options: { followRedirects: boolean; scrapeMode: ScrapeMode; header: string[] },\n) {\n const headers = parseHeaders(options.header);\n const fetchUrlTool = new FetchUrlTool(new HttpFetcher(), new FileFetcher());\n\n // Call the tool directly - tracking is now handled inside the tool\n const content = await fetchUrlTool.execute({\n url,\n followRedirects: options.followRedirects,\n scrapeMode: options.scrapeMode,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n });\n\n console.log(content);\n}\n\nexport function createFetchUrlCommand(program: Command): Command {\n return program\n .command(\"fetch-url <url>\")\n .description(\"Fetch a URL and convert its content to Markdown\")\n .option(\n \"--no-follow-redirects\",\n \"Disable following HTTP redirects (default: follow redirects)\",\n )\n .option(\n \"--scrape-mode <mode>\",\n `HTML processing strategy: '${ScrapeMode.Fetch}', '${ScrapeMode.Playwright}', '${ScrapeMode.Auto}' (default)`,\n (value: string): ScrapeMode => {\n const validModes = Object.values(ScrapeMode);\n if (!validModes.includes(value as ScrapeMode)) {\n console.warn(\n `Warning: Invalid scrape mode '${value}'. Using default '${ScrapeMode.Auto}'.`,\n );\n return ScrapeMode.Auto;\n }\n return value as ScrapeMode;\n },\n ScrapeMode.Auto,\n )\n .option(\n \"--header <name:value>\",\n \"Custom HTTP header to send with the request (can be specified multiple times)\",\n (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .action(fetchUrlAction);\n}\n","/**\n * Find version command - Finds the best matching version for a library.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { FindVersionTool } from \"../../tools\";\n\nexport async function findVersionAction(\n library: string,\n options: { version?: string; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Find version command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\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: options.version,\n });\n\n if (!versionInfo) throw new Error(\"Failed to get version information\");\n console.log(versionInfo);\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createFindVersionCommand(program: Command): Command {\n return program\n .command(\"find-version <library>\")\n .description(\"Find the best matching version for a library\")\n .option(\"-v, --version <string>\", \"Pattern to match (optional, supports ranges)\")\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(findVersionAction);\n}\n","/**\n * List command - Lists all available libraries and their versions.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { ListLibrariesTool } from \"../../tools\";\nimport { formatOutput } from \"../utils\";\n\nexport async function listAction(options: { serverUrl?: string }) {\n const { serverUrl } = options;\n\n // List command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\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 console.log(formatOutput(result.libraries));\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createListCommand(program: Command): Command {\n return program\n .command(\"list\")\n .description(\"List all available libraries and their versions\")\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(listAction);\n}\n","/**\n * MCP command - Starts MCP server only.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n parseAuthConfig,\n resolveEmbeddingContext,\n resolveProtocol,\n validateAuthConfig,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createMcpCommand(program: Command): Command {\n return (\n program\n .command(\"mcp\")\n .description(\"Start MCP server only\")\n .addOption(\n new Option(\"--protocol <protocol>\", \"Protocol for MCP server\")\n .choices([\"auto\", \"stdio\", \"http\"])\n .default(CLI_DEFAULTS.PROTOCOL),\n )\n .addOption(\n new Option(\"--port <number>\", \"Port for the MCP server\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.HTTP_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the MCP server to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .option(\n \"--read-only\",\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n false,\n )\n // Auth options\n .option(\n \"--auth-enabled\",\n \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n false,\n )\n .option(\"--auth-issuer-url <url>\", \"Issuer/discovery URL for OAuth2/OIDC provider\")\n .option(\n \"--auth-audience <id>\",\n \"JWT audience claim (identifies this protected resource)\",\n )\n .action(\n async (cmdOptions: {\n protocol: string;\n port: string;\n host: string;\n serverUrl?: string;\n readOnly: boolean;\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n const serverUrl = cmdOptions.serverUrl;\n // Resolve protocol using same logic as default action\n const resolvedProtocol = resolveProtocol(cmdOptions.protocol);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR); // Force quiet logging in stdio mode\n }\n\n // Parse and validate auth configuration\n const authConfig = parseAuthConfig({\n authEnabled: cmdOptions.authEnabled,\n authIssuerUrl: cmdOptions.authIssuerUrl,\n authAudience: cmdOptions.authAudience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n }\n\n try {\n // Resolve embedding configuration for local execution\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n logger.error(\n \"❌ Embedding configuration is required for local mode. Configure an embedding provider with CLI options or environment variables.\",\n );\n process.exit(1);\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // MCP command doesn't support job recovery\n serverUrl,\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\n pipelineOptions,\n );\n\n if (resolvedProtocol === \"stdio\") {\n // Direct stdio mode - bypass AppServer entirely\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n logger.info(\"🚀 Starting MCP server (stdio mode)\");\n\n await pipeline.start(); // Start pipeline for stdio mode\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = await startStdioServer(mcpTools, cmdOptions.readOnly);\n\n // Register for graceful shutdown (stdio mode)\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {}); // Keep running forever\n } else {\n // HTTP mode - use AppServer\n logger.debug(`Auto-detected http protocol (TTY available)`);\n logger.info(\"🚀 Starting MCP server (http mode)\");\n\n // Configure MCP-only server\n const config = createAppServerConfig({\n enableWebInterface: false, // Never enable web interface in mcp command\n enableMcpServer: true,\n enableApiServer: false, // Never enable API in mcp command\n enableWorker: !serverUrl,\n port,\n host,\n externalWorkerUrl: serverUrl,\n readOnly: cmdOptions.readOnly,\n auth: authConfig,\n startupContext: {\n cliCommand: \"mcp\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown (http mode)\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\n });\n\n await new Promise(() => {}); // Keep running forever\n }\n } catch (error) {\n logger.error(`❌ Failed to start MCP server: ${error}`);\n process.exit(1);\n }\n },\n )\n );\n}\n","/**\n * Remove command - Removes documents for a specific library and version.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\n\nexport async function removeAction(\n library: string,\n options: { version?: string; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Remove command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\n });\n const { version } = options;\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 console.log(`✅ Successfully removed ${library}${version ? `@${version}` : \"\"}.`);\n } catch (error) {\n console.error(\n `❌ Failed to remove ${library}${version ? `@${version}` : \"\"}:`,\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createRemoveCommand(program: Command): Command {\n return program\n .command(\"remove <library>\")\n .description(\"Remove documents for a specific library and version\")\n .option(\n \"-v, --version <string>\",\n \"Version to remove (optional, removes unversioned if omitted)\",\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(removeAction);\n}\n","/**\n * Scrape command - Scrapes and indexes documentation from a URL or local folder.\n */\n\nimport type { Command } from \"commander\";\nimport 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 { ScrapeTool } from \"../../tools\";\nimport {\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_MAX_DEPTH,\n DEFAULT_MAX_PAGES,\n} from \"../../utils/config\";\nimport {\n createPipelineWithCallbacks,\n parseHeaders,\n resolveEmbeddingContext,\n} from \"../utils\";\n\nexport async function scrapeAction(\n library: string,\n url: string,\n options: {\n version?: string;\n maxPages: string;\n maxDepth: string;\n maxConcurrency: string;\n ignoreErrors: boolean;\n scope: string;\n followRedirects: boolean;\n scrapeMode: ScrapeMode;\n includePattern: string[];\n excludePattern: string[];\n header: string[];\n serverUrl?: string;\n },\n) {\n const serverUrl = options.serverUrl;\n\n // Resolve embedding configuration for local execution (scrape needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n throw new Error(\n \"Embedding configuration is required for local scraping. \" +\n \"Please set DOCS_MCP_EMBEDDING_MODEL environment variable or use --server-url for remote execution.\",\n );\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n let pipeline: IPipeline | null = null;\n\n try {\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false,\n concurrency: 1,\n serverUrl,\n };\n\n pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\n pipelineOptions,\n );\n await pipeline.start();\n const scrapeTool = new ScrapeTool(pipeline);\n\n const headers = parseHeaders(options.header);\n\n // Call the tool directly - tracking is now handled inside the tool\n const result = await scrapeTool.execute({\n url,\n library,\n version: options.version,\n options: {\n maxPages: Number.parseInt(options.maxPages, 10),\n maxDepth: Number.parseInt(options.maxDepth, 10),\n maxConcurrency: Number.parseInt(options.maxConcurrency, 10),\n ignoreErrors: options.ignoreErrors,\n scope: options.scope as \"subpages\" | \"hostname\" | \"domain\",\n followRedirects: options.followRedirects,\n scrapeMode: options.scrapeMode,\n includePatterns:\n Array.isArray(options.includePattern) && options.includePattern.length > 0\n ? options.includePattern\n : undefined,\n excludePatterns:\n Array.isArray(options.excludePattern) && options.excludePattern.length > 0\n ? options.excludePattern\n : undefined,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n },\n });\n\n if (\"pagesScraped\" in result) {\n console.log(`✅ Successfully scraped ${result.pagesScraped} pages`);\n } else {\n console.log(`🚀 Scraping job started with ID: ${result.jobId}`);\n }\n } finally {\n if (pipeline) await pipeline.stop();\n await docService.shutdown();\n }\n}\n\nexport function createScrapeCommand(program: Command): Command {\n return program\n .command(\"scrape <library> <url>\")\n .description(\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 .option(\"-v, --version <string>\", \"Version of the library (optional)\")\n .option(\n \"-p, --max-pages <number>\",\n \"Maximum pages to scrape\",\n DEFAULT_MAX_PAGES.toString(),\n )\n .option(\n \"-d, --max-depth <number>\",\n \"Maximum navigation depth\",\n DEFAULT_MAX_DEPTH.toString(),\n )\n .option(\n \"-c, --max-concurrency <number>\",\n \"Maximum concurrent page requests\",\n DEFAULT_MAX_CONCURRENCY.toString(),\n )\n .option(\"--ignore-errors\", \"Ignore errors during scraping\", true)\n .option(\n \"--scope <scope>\",\n \"Crawling boundary: 'subpages' (default), 'hostname', or 'domain'\",\n (value) => {\n const validScopes = [\"subpages\", \"hostname\", \"domain\"];\n if (!validScopes.includes(value)) {\n console.warn(`Warning: Invalid scope '${value}'. Using default 'subpages'.`);\n return \"subpages\";\n }\n return value;\n },\n \"subpages\",\n )\n .option(\n \"--no-follow-redirects\",\n \"Disable following HTTP redirects (default: follow redirects)\",\n )\n .option(\n \"--scrape-mode <mode>\",\n `HTML processing strategy: '${ScrapeMode.Fetch}', '${ScrapeMode.Playwright}', '${ScrapeMode.Auto}' (default)`,\n (value: string): ScrapeMode => {\n const validModes = Object.values(ScrapeMode);\n if (!validModes.includes(value as ScrapeMode)) {\n console.warn(\n `Warning: Invalid scrape mode '${value}'. Using default '${ScrapeMode.Auto}'.`,\n );\n return ScrapeMode.Auto;\n }\n return value as ScrapeMode;\n },\n ScrapeMode.Auto,\n )\n .option(\n \"--include-pattern <pattern>\",\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 (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--exclude-pattern <pattern>\",\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 (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--header <name:value>\",\n \"Custom HTTP header to send with each request (can be specified multiple times)\",\n (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(scrapeAction);\n}\n","/**\n * Search command - Searches documents in a library.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { SearchTool } from \"../../tools\";\nimport { formatOutput, resolveEmbeddingContext } from \"../utils\";\n\nexport async function searchAction(\n library: string,\n query: string,\n options: { version?: string; limit: string; exactMatch: boolean; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Resolve embedding configuration for local execution (search needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n throw new Error(\n \"Embedding configuration is required for local search. \" +\n \"Please set DOCS_MCP_EMBEDDING_MODEL environment variable or use --server-url for remote execution.\",\n );\n }\n\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\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: options.version,\n query,\n limit: Number.parseInt(options.limit, 10),\n exactMatch: options.exactMatch,\n });\n\n console.log(formatOutput(result.results));\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createSearchCommand(program: Command): Command {\n return program\n .command(\"search <library> <query>\")\n .description(\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 .option(\n \"-v, --version <string>\",\n \"Version of the library (optional, supports ranges)\",\n )\n .option(\"-l, --limit <number>\", \"Maximum number of results\", \"5\")\n .option(\"-e, --exact-match\", \"Only use exact version match (default: false)\", false)\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(searchAction);\n}\n","/**\n * Web command - Starts web interface only.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n resolveEmbeddingContext,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWebCommand(program: Command): Command {\n return program\n .command(\"web\")\n .description(\"Start web interface only\")\n .addOption(\n new Option(\"--port <number>\", \"Port for the web interface\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.WEB_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the web interface to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(async (cmdOptions: { port: string; host: string; serverUrl?: string }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n const serverUrl = cmdOptions.serverUrl;\n\n try {\n // Resolve embedding configuration for local execution\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n logger.error(\n \"❌ Embedding configuration is required for local mode. Configure an embedding provider with CLI options or environment variables.\",\n );\n process.exit(1);\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // Web command doesn't support job recovery\n serverUrl,\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\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,\n host,\n externalWorkerUrl: serverUrl,\n startupContext: {\n cliCommand: \"web\",\n },\n });\n\n logger.info(\n `🚀 Starting web interface${serverUrl ? ` connecting to worker at ${serverUrl}` : \"\"}`,\n );\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\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 * Worker command - Starts external pipeline worker (HTTP API).\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n ensurePlaywrightBrowsersInstalled,\n resolveEmbeddingContext,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWorkerCommand(program: Command): Command {\n return program\n .command(\"worker\")\n .description(\"Start external pipeline worker (HTTP API)\")\n .addOption(\n new Option(\"--port <number>\", \"Port for worker API\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(\"8080\"),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the worker API to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\"--resume\", \"Resume interrupted jobs on startup\", true)\n .option(\"--no-resume\", \"Do not resume jobs on startup\")\n .action(async (cmdOptions: { port: string; host: string; resume: boolean }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n\n try {\n logger.info(`🚀 Starting external pipeline worker on port ${port}`);\n\n // Ensure browsers are installed for scraping\n ensurePlaywrightBrowsersInstalled();\n\n // Resolve embedding configuration for worker (worker needs embeddings for indexing)\n const embeddingConfig = resolveEmbeddingContext();\n\n // Initialize services\n const docService = await createLocalDocumentManagement(embeddingConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: cmdOptions.resume, // Use the resume option\n concurrency: CLI_DEFAULTS.MAX_CONCURRENCY,\n };\n const pipeline = await createPipelineWithCallbacks(docService, pipelineOptions);\n\n // Configure worker-only server\n const config = createAppServerConfig({\n enableWebInterface: false,\n enableMcpServer: false,\n enableApiServer: true,\n enableWorker: true,\n port,\n host,\n startupContext: {\n cliCommand: \"worker\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\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 * Main CLI setup and command registration.\n */\n\nimport { Command, Option } from \"commander\";\nimport packageJson from \"../../package.json\";\nimport {\n analytics,\n shouldEnableTelemetry,\n TelemetryConfig,\n TelemetryEvent,\n} from \"../telemetry\";\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 { 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 type { GlobalOptions } from \"./types\";\nimport { setupLogging } from \"./utils\";\n\n/**\n * Creates and configures the main CLI program with all commands.\n */\nexport function createCliProgram(): Command {\n const program = new Command();\n\n // Store command start times for duration tracking\n const commandStartTimes = new Map<string, number>();\n\n // Configure main program\n program\n .name(\"docs-mcp-server\")\n .description(\"Unified CLI, MCP Server, and Web Interface for Docs MCP Server.\")\n .version(packageJson.version)\n // Mutually exclusive logging flags\n .addOption(\n new Option(\"--verbose\", \"Enable verbose (debug) logging\").conflicts(\"silent\"),\n )\n .addOption(new Option(\"--silent\", \"Disable all logging except errors\"))\n .addOption(new Option(\"--no-telemetry\", \"Disable telemetry collection\"))\n .enablePositionalOptions()\n .allowExcessArguments(false)\n .showHelpAfterError(true);\n\n // Set up global options handling\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\n const globalOptions: GlobalOptions = thisCommand.opts();\n\n // Setup logging\n setupLogging(globalOptions);\n\n // Initialize telemetry if enabled\n if (shouldEnableTelemetry()) {\n // Set global context for CLI commands\n if (analytics.isEnabled()) {\n analytics.setGlobalContext({\n appVersion: packageJson.version,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appInterface: \"cli\",\n cliCommand: actionCommand.name(),\n });\n\n // Store command start time for duration tracking\n const commandKey = `${actionCommand.name()}-${Date.now()}`;\n commandStartTimes.set(commandKey, Date.now());\n // Store the key for retrieval in postAction\n (actionCommand as { _trackingKey?: string })._trackingKey = commandKey;\n }\n } else {\n TelemetryConfig.getInstance().disable();\n }\n });\n\n // Track CLI command completion\n program.hook(\"postAction\", async (_thisCommand, actionCommand) => {\n if (analytics.isEnabled()) {\n // Track CLI_COMMAND event for all CLI commands (standalone and server)\n const trackingKey = (actionCommand as { _trackingKey?: string })._trackingKey;\n const startTime = trackingKey ? commandStartTimes.get(trackingKey) : Date.now();\n const durationMs = startTime ? Date.now() - startTime : 0;\n\n // Clean up the tracking data\n if (trackingKey) {\n commandStartTimes.delete(trackingKey);\n }\n\n analytics.track(TelemetryEvent.CLI_COMMAND, {\n cliCommand: actionCommand.name(),\n success: true, // If we reach postAction, command succeeded\n durationMs,\n });\n\n await analytics.shutdown();\n }\n });\n\n // Register all commands\n createMcpCommand(program);\n createWebCommand(program);\n createWorkerCommand(program);\n createScrapeCommand(program);\n createSearchCommand(program);\n createListCommand(program);\n createFindVersionCommand(program);\n createRemoveCommand(program);\n createFetchUrlCommand(program);\n\n // Set default action for when no subcommand is specified\n createDefaultAction(program);\n\n return program;\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 type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { AppServer } from \"../app\";\nimport type { IPipeline } from \"../pipeline\";\nimport {\n ModelConfigurationError,\n UnsupportedProviderError,\n} from \"../store/embeddings/EmbeddingFactory\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { createCliProgram } from \"./index\";\n\n// Module-level variables for active services and shutdown state\nlet activeAppServer: AppServer | null = null;\nlet activeMcpStdioServer: McpServer | null = null;\nlet activeDocService: IDocumentManagement | null = null;\nlet activePipelineManager: IPipeline | null = null;\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 if (activeAppServer) {\n logger.debug(\"SIGINT: Stopping AppServer...\");\n await activeAppServer.stop();\n activeAppServer = null;\n logger.debug(\"SIGINT: AppServer stopped.\");\n }\n\n if (activeMcpStdioServer) {\n logger.debug(\"SIGINT: Stopping MCP server...\");\n await activeMcpStdioServer.close();\n activeMcpStdioServer = 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 if (activePipelineManager && !activeAppServer) {\n await activePipelineManager.stop();\n activePipelineManager = null;\n logger.debug(\"SIGINT: PipelineManager stopped.\");\n }\n\n if (activeDocService) {\n await activeDocService.shutdown();\n activeDocService = null;\n logger.debug(\"SIGINT: DocumentManagementService shut down.\");\n }\n\n // Analytics shutdown is handled by AppServer.stop() above\n // Only shutdown analytics if no AppServer was running\n if (!activeAppServer && analytics.isEnabled()) {\n await analytics.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 analytics.shutdown();\n\n // Avoid hanging processes by explicitly exiting\n process.exit(0);\n }\n}\n\n/**\n * Registers global services for shutdown handling\n */\nexport function registerGlobalServices(services: {\n appServer?: AppServer;\n mcpStdioServer?: McpServer;\n docService?: IDocumentManagement;\n pipeline?: IPipeline;\n}): 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}\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 program = createCliProgram();\n\n // Track if a command was executed\n program.hook(\"preAction\", () => {\n commandExecuted = true;\n });\n\n await program.parseAsync(process.argv);\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 if (activeAppServer) {\n shutdownPromises.push(\n activeAppServer\n .stop()\n .then(() => {\n activeAppServer = null;\n })\n .catch((e) => logger.error(`❌ Error stopping AppServer: ${e}`)),\n );\n }\n\n if (activeMcpStdioServer) {\n shutdownPromises.push(\n activeMcpStdioServer\n .close()\n .then(() => {\n activeMcpStdioServer = null;\n })\n .catch((e) => logger.error(`❌ Error stopping MCP server: ${e}`)),\n );\n }\n\n if (activePipelineManager && !activeAppServer) {\n shutdownPromises.push(\n activePipelineManager\n .stop()\n .then(() => {\n activePipelineManager = null;\n })\n .catch((e) => logger.error(`❌ Error stopping pipeline: ${e}`)),\n );\n }\n\n if (activeDocService) {\n shutdownPromises.push(\n activeDocService\n .shutdown()\n .then(() => {\n activeDocService = 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 if (commandExecuted && !activeAppServer) {\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 if (activeAppServer) {\n logger.debug(\"Shutting down AppServer...\");\n shutdownPromises.push(\n activeAppServer.stop().then(() => {\n activeAppServer = null;\n logger.debug(\"AppServer shut down.\");\n }),\n );\n }\n\n if (activePipelineManager && !activeAppServer) {\n shutdownPromises.push(\n activePipelineManager.stop().then(() => {\n activePipelineManager = null;\n logger.debug(\"PipelineManager stopped.\");\n }),\n );\n }\n\n if (activeDocService) {\n shutdownPromises.push(\n activeDocService.shutdown().then(() => {\n activeDocService = 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 activeAppServer = null;\n if (!wasAlreadyShuttingDown) {\n isShuttingDown = false;\n }\n }\n });\n}\n","import \"dotenv/config\";\nimport { runCli } from \"./cli/main\";\nimport { ensurePlaywrightBrowsersInstalled } from \"./cli/utils\";\n\n// Ensure Playwright browsers are installed\nensurePlaywrightBrowsersInstalled();\n\n// Run the CLI\nrunCli().catch((error) => {\n console.error(`🔥 Fatal error in main execution: ${error}`);\n process.exit(1);\n});\n"],"names":["VersionStatus","name","TelemetryEvent","analytics","baseUrl","config","version","DEFAULT_MAX_DEPTH","fs","path","chunks","window","ScrapeMode","URL","item","PipelineFactory","content","PipelineJobStatus","document","uuidv4","job","error","semver","t","z","packageJson","type","parseHeaders","AppServer","projectRoot"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/store/errors.ts","../src/store/types.ts","../src/store/embeddings/FixedDimensionEmbeddings.ts","../src/store/embeddings/EmbeddingFactory.ts","../src/utils/logger.ts","../src/telemetry/postHogClient.ts","../src/telemetry/TelemetryConfig.ts","../src/telemetry/analytics.ts","../src/telemetry/sanitizer.ts","../src/auth/middleware.ts","../src/auth/ProxyAuthManager.ts","../src/pipeline/errors.ts","../src/pipeline/PipelineClient.ts","../src/utils/config.ts","../src/utils/dom.ts","../src/utils/errors.ts","../src/utils/mimeTypeUtils.ts","../src/utils/paths.ts","../src/utils/string.ts","../src/utils/url.ts","../src/scraper/fetcher/FileFetcher.ts","../src/scraper/fetcher/FingerprintGenerator.ts","../src/scraper/fetcher/HttpFetcher.ts","../src/splitter/errors.ts","../src/splitter/GreedySplitter.ts","../src/splitter/JsonDocumentSplitter.ts","../src/splitter/splitters/CodeContentSplitter.ts","../src/splitter/splitters/TableContentSplitter.ts","../src/splitter/splitters/TextContentSplitter.ts","../src/splitter/SemanticMarkdownSplitter.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/middleware/HtmlCheerioParserMiddleware.ts","../src/scraper/middleware/HtmlLinkExtractorMiddleware.ts","../src/scraper/middleware/HtmlMetadataExtractorMiddleware.ts","../src/scraper/types.ts","../src/scraper/middleware/HtmlPlaywrightMiddleware.ts","../src/scraper/middleware/HtmlSanitizerMiddleware.ts","../src/scraper/middleware/HtmlToMarkdownMiddleware.ts","../src/scraper/middleware/MarkdownLinkExtractorMiddleware.ts","../src/scraper/middleware/MarkdownMetadataExtractorMiddleware.ts","../src/scraper/utils/charset.ts","../src/scraper/utils/buffer.ts","../src/scraper/pipelines/BasePipeline.ts","../src/scraper/pipelines/HtmlPipeline.ts","../src/scraper/pipelines/JsonPipeline.ts","../src/scraper/pipelines/MarkdownPipeline.ts","../src/scraper/pipelines/SourceCodePipeline.ts","../src/splitter/TextDocumentSplitter.ts","../src/scraper/pipelines/TextPipeline.ts","../src/scraper/pipelines/PipelineFactory.ts","../src/scraper/utils/defaultPatterns.ts","../src/scraper/utils/patternMatcher.ts","../src/scraper/utils/scope.ts","../src/scraper/strategies/BaseScraperStrategy.ts","../src/scraper/strategies/GitHubScraperStrategy.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/types.ts","../src/pipeline/PipelineManager.ts","../src/pipeline/PipelineFactory.ts","../src/store/embeddings/EmbeddingConfig.ts","../src/cli/utils.ts","../src/tools/CancelJobTool.ts","../src/tools/ClearCompletedJobsTool.ts","../src/tools/errors.ts","../src/tools/FetchUrlTool.ts","../src/tools/FindVersionTool.ts","../src/tools/GetJobInfoTool.ts","../src/tools/ListJobsTool.ts","../src/tools/ListLibrariesTool.ts","../src/tools/RemoveTool.ts","../src/tools/ScrapeTool.ts","../src/tools/SearchTool.ts","../src/mcp/utils.ts","../src/mcp/mcpServer.ts","../src/mcp/tools.ts","../src/services/mcpService.ts","../src/pipeline/trpc/router.ts","../src/store/trpc/router.ts","../src/services/trpcService.ts","../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/Alert.tsx","../src/web/components/Tooltip.tsx","../src/web/components/ScrapeFormContent.tsx","../src/web/components/ScrapeForm.tsx","../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/services/webService.ts","../src/services/workerService.ts","../src/app/AppServer.ts","../src/app/index.ts","../src/mcp/startStdioServer.ts","../src/store/DocumentManagementClient.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/DocumentStore.ts","../src/store/DocumentManagementService.ts","../src/store/index.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/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","../src/index.ts"],"sourcesContent":["/**\n * Base error class for all store-related errors.\n * Provides consistent error handling with optional cause tracking.\n */\nclass 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 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 */\nclass 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 */\nclass ConnectionError extends StoreError {}\n\n/**\n * Error thrown when attempting to retrieve a document that doesn't exist.\n */\nclass 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 */\nclass 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\nexport {\n StoreError,\n ConnectionError,\n DocumentNotFoundError,\n DimensionError,\n MissingCredentialsError,\n};\n","import type { ScrapeMode } from \"../scraper/types\";\nimport type { DocumentMetadata } from \"../types\";\n\n/** Default vector dimension used across the application */\nexport const VECTOR_DIMENSION = 1536;\n\n/**\n * Database document record type matching the documents table schema\n */\nexport interface DbDocument {\n id: string;\n library_id: number;\n version_id: number; // Changed from version: string to use foreign key\n url: string;\n content: string;\n metadata: string; // JSON string of DocumentMetadata\n embedding: string | null; // JSON string of number[]\n sort_order: number;\n score: number | null;\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 * Maps raw database document to the Document type used by the application\n */\nexport function mapDbDocumentToDocument(doc: DbDocument) {\n return {\n id: doc.id,\n pageContent: doc.content,\n metadata: JSON.parse(doc.metadata) as DocumentMetadata,\n };\n}\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;\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 * Validates if a status transition is allowed.\n * Prevents invalid state changes and ensures data consistency.\n */\nexport function isValidStatusTransition(\n currentStatus: VersionStatus,\n newStatus: VersionStatus,\n): boolean {\n // Define valid transitions for each status\n const validTransitions: Record<VersionStatus, VersionStatus[]> = {\n [VersionStatus.NOT_INDEXED]: [VersionStatus.QUEUED],\n [VersionStatus.QUEUED]: [VersionStatus.RUNNING, VersionStatus.CANCELLED],\n [VersionStatus.RUNNING]: [\n VersionStatus.COMPLETED,\n VersionStatus.FAILED,\n VersionStatus.CANCELLED,\n ],\n [VersionStatus.COMPLETED]: [VersionStatus.UPDATING],\n [VersionStatus.UPDATING]: [VersionStatus.RUNNING, VersionStatus.CANCELLED],\n [VersionStatus.FAILED]: [\n VersionStatus.QUEUED, // Allow retry\n ],\n [VersionStatus.CANCELLED]: [\n VersionStatus.QUEUED, // Allow retry\n ],\n };\n\n return validTransitions[currentStatus]?.includes(newStatus) ?? false;\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","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 { MissingCredentialsError } from \"../errors\";\nimport { VECTOR_DIMENSION } from \"../types\";\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(providerAndModel: string): Embeddings {\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 };\n // Add custom base URL if specified\n const baseURL = process.env.OPENAI_API_BASE;\n if (baseURL) {\n config.configuration = { baseURL };\n }\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 VECTOR_DIMENSION,\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","/**\n * Defines the available log levels.\n */\nexport const LogLevel = {\n ERROR: 0,\n WARN: 1,\n INFO: 2,\n DEBUG: 3,\n} as const;\n\nexport type LogLevel = (typeof LogLevel)[keyof typeof LogLevel];\n\n/**\n * Maps string log level names to their numeric values.\n */\nconst LOG_LEVEL_MAP: Record<string, LogLevel> = {\n ERROR: LogLevel.ERROR,\n WARN: LogLevel.WARN,\n INFO: LogLevel.INFO,\n DEBUG: LogLevel.DEBUG,\n};\n\n/**\n * Gets the log level from environment variable or returns default.\n */\nfunction getLogLevelFromEnv(): LogLevel {\n const envLevel = process.env.LOG_LEVEL?.toUpperCase();\n return envLevel && envLevel in LOG_LEVEL_MAP ? LOG_LEVEL_MAP[envLevel] : LogLevel.INFO;\n}\n\nlet currentLogLevel: LogLevel = getLogLevelFromEnv();\n\n/**\n * Sets the current logging level for the application.\n */\nexport function setLogLevel(level: LogLevel): void {\n currentLogLevel = level;\n}\n\n/**\n * Provides logging functionalities with level control.\n */\nexport const logger = {\n /**\n * Logs a debug message if the current log level is DEBUG or higher.\n * @param message - The message to log.\n */\n debug: (message: string) => {\n if (currentLogLevel >= LogLevel.DEBUG && !process.env.VITEST_WORKER_ID) {\n console.debug(message);\n }\n },\n /**\n * Logs an info message if the current log level is INFO or higher.\n * @param message - The message to log.\n */\n info: (message: string) => {\n if (currentLogLevel >= LogLevel.INFO && !process.env.VITEST_WORKER_ID) {\n console.log(message); // Using console.log for INFO\n }\n },\n /**\n * Logs a warning message if the current log level is WARN or higher.\n * @param message - The message to log.\n */\n warn: (message: string) => {\n if (currentLogLevel >= LogLevel.WARN && !process.env.VITEST_WORKER_ID) {\n console.warn(message);\n }\n },\n /**\n * Logs an error message if the current log level is ERROR or higher (always logs).\n * @param message - The message to log.\n */\n error: (message: string) => {\n if (currentLogLevel >= LogLevel.ERROR && !process.env.VITEST_WORKER_ID) {\n console.error(message);\n }\n },\n};\n","/**\n * PostHog client wrapper for telemetry events.\n * Handles PostHog SDK integration and event capture with privacy-first configuration.\n * Automatically converts camelCase property names to snake_case for PostHog compatibility.\n */\n\nimport { PostHog } from \"posthog-node\";\nimport { logger } from \"../utils/logger\";\n\n/**\n * Convert camelCase string to snake_case\n * Specifically designed for PostHog property name conversion\n */\nfunction camelToSnakeCase(str: string): string {\n return str.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);\n}\n\n/**\n * Recursively convert object keys from camelCase to snake_case\n * Handles nested objects and arrays while preserving values\n */\nfunction convertPropertiesToSnakeCase(\n obj: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(obj)) {\n const snakeKey = camelToSnakeCase(key);\n\n if (\n value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n !(value instanceof Date)\n ) {\n // Recursively convert nested objects\n result[snakeKey] = convertPropertiesToSnakeCase(value as Record<string, unknown>);\n } else if (Array.isArray(value)) {\n // Handle arrays - convert elements if they are objects\n result[snakeKey] = value.map((item) =>\n item && typeof item === \"object\" && !(item instanceof Date)\n ? convertPropertiesToSnakeCase(item as Record<string, unknown>)\n : item,\n );\n } else {\n // Primitive values, dates, and null/undefined - keep as-is\n result[snakeKey] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Add PostHog standard properties and remove duplicates\n * Maps our properties to PostHog's expected property names\n */\nfunction addPostHogStandardProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const result = { ...properties };\n\n // Add PostHog standard session properties\n if (properties.sessionId) {\n result.$session_id = properties.sessionId;\n delete result.sessionId; // Remove duplicate\n }\n\n if (properties.startTime) {\n result.$start_timestamp = (properties.startTime as Date).toISOString();\n delete result.startTime; // Remove duplicate\n }\n\n // Add PostHog standard app properties\n if (properties.appVersion) {\n result.$app_version = properties.appVersion;\n delete result.appVersion; // Remove duplicate\n }\n\n return result;\n}\n\n/**\n * PostHog client wrapper for telemetry events\n */\nexport class PostHogClient {\n private client?: PostHog;\n private enabled: boolean;\n\n // PostHog configuration\n private static readonly CONFIG = {\n host: \"https://app.posthog.com\",\n\n // Performance optimizations\n flushAt: 20, // Batch size - send after 20 events\n flushInterval: 10000, // 10 seconds - send after time\n\n // Privacy settings\n disableGeoip: true, // Don't collect IP geolocation\n disableSessionRecording: true, // Never record sessions\n disableSurveys: true, // No user surveys\n\n // Data handling\n persistence: \"memory\" as const, // No disk persistence for privacy\n };\n\n constructor(enabled: boolean) {\n this.enabled = enabled;\n\n if (!this.enabled) {\n return; // Early return if analytics is disabled\n }\n\n if (!__POSTHOG_API_KEY__) {\n logger.debug(\"PostHog API key not provided\");\n this.enabled = false;\n return;\n }\n\n try {\n this.client = new PostHog(__POSTHOG_API_KEY__, {\n host: PostHogClient.CONFIG.host,\n flushAt: PostHogClient.CONFIG.flushAt,\n flushInterval: PostHogClient.CONFIG.flushInterval,\n disableGeoip: PostHogClient.CONFIG.disableGeoip,\n });\n logger.debug(\"PostHog client initialized\");\n } catch (error) {\n logger.debug(\n `PostHog initialization failed: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n this.enabled = false;\n }\n }\n\n /**\n * Send event to PostHog\n */\n capture(distinctId: string, event: string, properties: Record<string, unknown>): void {\n if (!this.enabled || !this.client) return;\n\n try {\n // Add PostHog standard properties and remove duplicates\n const enhancedProperties = addPostHogStandardProperties(properties);\n\n // Convert camelCase properties to snake_case for PostHog\n const snakeCaseProperties = convertPropertiesToSnakeCase(enhancedProperties);\n\n this.client.capture({\n distinctId,\n event,\n properties: snakeCaseProperties,\n });\n logger.debug(`PostHog event captured: ${event}`);\n } catch (error) {\n logger.debug(\n `PostHog capture error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n }\n\n /**\n * Capture exception using PostHog's native error tracking\n */\n captureException(\n distinctId: string,\n error: Error,\n properties?: Record<string, unknown>,\n ): void {\n if (!this.enabled || !this.client) return;\n\n try {\n // Add PostHog standard properties and remove duplicates\n const enhancedProperties = addPostHogStandardProperties(properties || {});\n\n // Convert camelCase properties to snake_case for PostHog\n const snakeCaseProperties = convertPropertiesToSnakeCase(enhancedProperties);\n\n this.client.captureException({\n error,\n distinctId,\n properties: snakeCaseProperties,\n });\n logger.debug(`PostHog exception captured: ${error.constructor.name}`);\n } catch (captureError) {\n logger.debug(\n `PostHog captureException error: ${captureError instanceof Error ? captureError.message : \"Unknown error\"}`,\n );\n }\n }\n\n /**\n * Graceful shutdown with event flushing\n */\n async shutdown(): Promise<void> {\n if (this.client) {\n try {\n await this.client.shutdown();\n logger.debug(\"PostHog client shutdown complete\");\n } catch (error) {\n logger.debug(\n `PostHog shutdown error: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n );\n }\n }\n }\n\n /**\n * Check if client is enabled and ready\n */\n isEnabled(): boolean {\n return this.enabled && !!this.client;\n }\n}\n","/**\n * Telemetry configuration management for enabling/disabling analytics collection.\n * Handles CLI flags, environment variables, and default settings.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport envPaths from \"env-paths\";\n\nexport class TelemetryConfig {\n private static instance?: TelemetryConfig;\n private enabled: boolean;\n\n constructor() {\n this.enabled = this.determineEnabledState();\n }\n\n /**\n * Determines if telemetry should be enabled based on CLI flags and environment variables.\n * Priority: CLI flags > environment variables > default (true)\n */\n private determineEnabledState(): boolean {\n // Environment variable takes precedence\n if (process.env.DOCS_MCP_TELEMETRY === \"false\") {\n return false;\n }\n\n // Check for CLI flag (passed during initialization)\n const args = process.argv;\n if (args.includes(\"--no-telemetry\")) {\n return false;\n }\n\n // Default to enabled for optional analytics\n return true;\n }\n\n isEnabled(): boolean {\n return this.enabled;\n }\n\n disable(): void {\n this.enabled = false;\n }\n\n enable(): void {\n this.enabled = true;\n }\n\n static getInstance(): TelemetryConfig {\n if (!TelemetryConfig.instance) {\n TelemetryConfig.instance = new TelemetryConfig();\n }\n return TelemetryConfig.instance;\n }\n}\n\n/**\n * Generate or retrieve a persistent installation identifier.\n * Creates a UUID and stores it in a file in the user data directory.\n * Supports DOCS_MCP_STORE_PATH environment variable override for Docker deployments.\n * This ensures truly unique identification that persists across runs.\n */\nexport function generateInstallationId(): string {\n try {\n // Use DOCS_MCP_STORE_PATH if set (for Docker/custom deployments), otherwise use standard paths\n const envStorePath = process.env.DOCS_MCP_STORE_PATH;\n const dataDir = envStorePath || envPaths(\"docs-mcp-server\", { suffix: \"\" }).data;\n const installationIdPath = path.join(dataDir, \"installation.id\");\n\n // Try to read existing installation ID\n if (fs.existsSync(installationIdPath)) {\n const existingId = fs.readFileSync(installationIdPath, \"utf8\").trim();\n if (existingId) {\n return existingId;\n }\n }\n\n // Generate new UUID and store it\n const newId = randomUUID();\n\n // Ensure directory exists\n fs.mkdirSync(dataDir, { recursive: true });\n\n // Write the installation ID\n fs.writeFileSync(installationIdPath, newId, \"utf8\");\n\n return newId;\n } catch {\n // Fallback to a session-only UUID if file operations fail\n // This ensures analytics always has a valid distinct ID\n return randomUUID();\n }\n}\n\n/**\n * Check if telemetry should be enabled based on environment and CLI flags.\n */\nexport function shouldEnableTelemetry(): boolean {\n return TelemetryConfig.getInstance().isEnabled();\n}\n","/**\n * Analytics wrapper for privacy-first telemetry using PostHog.\n * Provides global context and automatic data sanitization.\n *\n * Architecture:\n * - PostHogClient: Handles PostHog SDK integration and event capture\n * - Analytics: High-level coordinator providing public API with global context\n */\n\nimport { logger } from \"../utils/logger\";\nimport type { TelemetryEventPropertiesMap } from \"./eventTypes\";\nimport { PostHogClient } from \"./postHogClient\";\nimport { generateInstallationId, TelemetryConfig } from \"./TelemetryConfig\";\n\n/**\n * Telemetry event types for structured analytics\n */\nexport enum TelemetryEvent {\n APP_STARTED = \"app_started\",\n APP_SHUTDOWN = \"app_shutdown\",\n CLI_COMMAND = \"cli_command\",\n TOOL_USED = \"tool_used\",\n HTTP_REQUEST_COMPLETED = \"http_request_completed\",\n PIPELINE_JOB_PROGRESS = \"pipeline_job_progress\",\n PIPELINE_JOB_COMPLETED = \"pipeline_job_completed\",\n DOCUMENT_PROCESSED = \"document_processed\",\n}\n\n/**\n * Main analytics class providing privacy-first telemetry\n */\nexport class Analytics {\n private postHogClient: PostHogClient;\n private enabled: boolean;\n private distinctId: string;\n private globalContext: Record<string, unknown> = {};\n\n /**\n * Create a new Analytics instance with proper initialization\n * This is the recommended way to create Analytics instances\n */\n static create(): Analytics {\n const config = TelemetryConfig.getInstance();\n\n // Single determination point for enabled status\n const shouldEnable = config.isEnabled() && !!__POSTHOG_API_KEY__;\n\n const analytics = new Analytics(shouldEnable);\n\n // Single log message after everything is initialized\n if (analytics.isEnabled()) {\n logger.debug(\"Analytics enabled\");\n } else {\n logger.debug(\"Analytics disabled\");\n }\n\n return analytics;\n }\n\n /**\n * Private constructor - use Analytics.create() instead\n */\n private constructor(enabled: boolean = true) {\n this.enabled = enabled;\n this.distinctId = generateInstallationId();\n this.postHogClient = new PostHogClient(this.enabled);\n }\n\n /**\n * Set global application context that will be included in all events\n */\n setGlobalContext(context: Record<string, unknown>): void {\n this.globalContext = { ...context };\n }\n\n /**\n * Get current global context\n */\n getGlobalContext(): Record<string, unknown> {\n return { ...this.globalContext };\n }\n\n /**\n * Track an event with automatic global context inclusion\n *\n * Type-safe overloads for specific events:\n */\n track<T extends keyof TelemetryEventPropertiesMap>(\n event: T,\n properties: TelemetryEventPropertiesMap[T],\n ): void;\n track(event: string, properties?: Record<string, unknown>): void;\n track(event: string, properties: Record<string, unknown> = {}): void {\n if (!this.enabled) return;\n\n // Merge global context and event properties with timestamp\n const enrichedProperties = {\n ...this.globalContext,\n ...properties,\n timestamp: new Date().toISOString(),\n };\n this.postHogClient.capture(this.distinctId, event, enrichedProperties);\n }\n\n /**\n * Capture exception using PostHog's native error tracking with global context\n */\n captureException(error: Error, properties: Record<string, unknown> = {}): void {\n if (!this.enabled) return;\n\n // Merge global context and error properties with timestamp\n const enrichedProperties = {\n ...this.globalContext,\n ...properties,\n timestamp: new Date().toISOString(),\n };\n this.postHogClient.captureException(this.distinctId, error, enrichedProperties);\n }\n\n /**\n * Graceful shutdown with event flushing\n */\n async shutdown(): Promise<void> {\n if (!this.enabled) return;\n\n await this.postHogClient.shutdown();\n }\n\n /**\n * Check if analytics is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Track tool usage with error handling and automatic timing\n */\n async trackTool<T>(\n toolName: string,\n operation: () => Promise<T>,\n getProperties?: (result: T) => Record<string, unknown>,\n ): Promise<T> {\n const startTime = Date.now();\n\n try {\n const result = await operation();\n\n this.track(TelemetryEvent.TOOL_USED, {\n tool: toolName,\n success: true,\n durationMs: Date.now() - startTime,\n ...(getProperties ? getProperties(result) : {}),\n });\n\n return result;\n } catch (error) {\n // Track the tool usage failure\n this.track(TelemetryEvent.TOOL_USED, {\n tool: toolName,\n success: false,\n durationMs: Date.now() - startTime,\n });\n\n // Capture the exception with full error tracking\n if (error instanceof Error) {\n this.captureException(error, {\n tool: toolName,\n context: \"tool_execution\",\n durationMs: Date.now() - startTime,\n });\n }\n\n throw error;\n }\n }\n}\n\n/**\n * Global analytics instance\n */\nexport const analytics = Analytics.create();\n","/**\n * Data sanitization utilities for privacy-first telemetry.\n * Simplified to only include essential functions we actually use.\n */\n\n/**\n * Extracts hostname from URL for aggregated analytics without exposing paths.\n * Examples:\n * - https://docs.python.org/3/library/os.html -> docs.python.org\n * - https://github.com/owner/repo -> github.com\n * - http://localhost:3000/api -> localhost\n */\nexport function extractHostname(url: string): string {\n try {\n const parsed = new URL(url);\n return parsed.hostname;\n } catch {\n return \"invalid-hostname\";\n }\n}\n\n/**\n * Extracts protocol from URL or file path for privacy-safe analytics.\n * Examples:\n * - https://github.com/owner/repo -> \"https\"\n * - file:///local/path -> \"file\"\n * - /local/path -> \"file\" (detected as local file)\n * - C:\\local\\path -> \"file\" (detected as local file)\n */\nexport function extractProtocol(urlOrPath: string): string {\n try {\n const parsed = new URL(urlOrPath);\n return parsed.protocol.replace(\":\", \"\");\n } catch {\n // If URL parsing fails, check if it looks like a local file path\n if (urlOrPath.startsWith(\"/\") || /^[A-Za-z]:/.test(urlOrPath)) {\n return \"file\";\n }\n return \"unknown\";\n }\n}\n\n/**\n * Analyzes search query patterns without storing content.\n * Returns metadata about the query for usage analytics.\n */\nexport function analyzeSearchQuery(query: string): {\n length: number;\n wordCount: number;\n hasCodeTerms: boolean;\n hasSpecialChars: boolean;\n} {\n return {\n length: query.length,\n wordCount: query.trim().split(/\\s+/).length,\n hasCodeTerms:\n /\\b(function|class|import|export|const|let|var|def|async|await)\\b/i.test(query),\n hasSpecialChars: /[^\\w\\s]/.test(query),\n };\n}\n\n/**\n * Sanitizes error messages to remove sensitive information while preserving diagnostic value.\n * Examples:\n * - \"Failed to fetch https://secret.com/api\" -> \"Failed to fetch [url]\"\n * - \"File not found: /home/user/secret.txt\" -> \"File not found: [path]\"\n */\nexport function sanitizeErrorMessage(message: string): string {\n return message\n .replace(/https?:\\/\\/[^\\s]+/gi, \"[url]\")\n .replace(/file:\\/\\/[^\\s]+/gi, \"[file-url]\")\n .replace(/\\/[^\\s]*\\.[a-z]{2,4}/gi, \"[path]\")\n .replace(/[A-Za-z]:\\\\[^\\s]+/g, \"[path]\")\n .replace(/Bearer\\s+[^\\s]+/gi, \"Bearer [token]\")\n .replace(/api[_-]?key[=:]\\s*[^\\s]+/gi, \"api_key=[redacted]\")\n .replace(/token[=:]\\s*[^\\s]+/gi, \"token=[redacted]\")\n .substring(0, 200); // Limit length\n}\n\n/**\n * Sanitizes error information for telemetry collection.\n * Simple approach: just track error type and sanitized message.\n */\nexport function sanitizeError(error: Error): {\n type: string;\n message: string;\n hasStack: boolean;\n} {\n return {\n type: error.constructor.name,\n message: sanitizeErrorMessage(error.message),\n hasStack: Boolean(error.stack),\n };\n}\n\n/**\n * Extract CLI flags from process arguments for telemetry (without values)\n * Examples:\n * - [\"--verbose\", \"--max-depth\", \"3\"] -> [\"--verbose\", \"--max-depth\"]\n */\nexport function extractCliFlags(argv: string[]): string[] {\n return argv.filter((arg) => arg.startsWith(\"--\") || arg.startsWith(\"-\"));\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","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","/**\n * tRPC client implementation of the Pipeline interface.\n * Delegates all pipeline operations to an external worker via tRPC router.\n */\n\nimport { createTRPCProxyClient, httpBatchLink } from \"@trpc/client\";\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 * Deserializes a job object from JSON, converting date strings back to Date objects.\n * Only includes public fields - no internal job management fields.\n */\nfunction deserializeJob(serializedJob: Record<string, unknown>): PipelineJob {\n return {\n ...serializedJob,\n createdAt: new Date(serializedJob.createdAt as string),\n startedAt: serializedJob.startedAt\n ? new Date(serializedJob.startedAt as string)\n : null,\n finishedAt: serializedJob.finishedAt\n ? new Date(serializedJob.finishedAt as string)\n : null,\n updatedAt: serializedJob.updatedAt\n ? new Date(serializedJob.updatedAt as string)\n : undefined,\n } as PipelineJob;\n}\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 client: ReturnType<typeof createTRPCProxyClient<PipelineRouter>>;\n private pollingInterval: number = 1000; // 1 second\n private activePolling = new Set<string>(); // Track jobs being polled for completion\n\n constructor(serverUrl: string) {\n this.baseUrl = serverUrl.replace(/\\/$/, \"\");\n this.client = createTRPCProxyClient<PipelineRouter>({\n links: [httpBatchLink({ url: this.baseUrl })],\n });\n logger.debug(`PipelineClient (tRPC) created for: ${this.baseUrl}`);\n }\n\n async start(): Promise<void> {\n // Check connectivity via ping\n try {\n // Root-level ping exists on the unified router; cast for this health check only\n await (\n this.client as unknown as { ping: { query: () => Promise<unknown> } }\n ).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 // Clear any active polling\n this.activePolling.clear();\n logger.debug(\"PipelineClient stopped\");\n }\n\n async enqueueJob(\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.enqueueJob.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 getJob(jobId: string): Promise<PipelineJob | undefined> {\n try {\n const serializedJob = await this.client.getJob.query({ id: jobId });\n return serializedJob\n ? deserializeJob(serializedJob as unknown as Record<string, unknown>)\n : undefined;\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 const result = await this.client.getJobs.query({ status });\n const serializedJobs = result.jobs || [];\n return serializedJobs.map((j) =>\n deserializeJob(j as unknown as Record<string, unknown>),\n );\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 if (this.activePolling.has(jobId)) {\n throw new Error(`Already waiting for completion of job ${jobId}`);\n }\n\n this.activePolling.add(jobId);\n\n try {\n while (this.activePolling.has(jobId)) {\n const job = await this.getJob(jobId);\n if (!job) {\n throw new Error(`Job ${jobId} not found`);\n }\n\n // Check if job is in final state\n if (\n job.status === \"completed\" ||\n job.status === \"failed\" ||\n job.status === \"cancelled\"\n ) {\n if (job.status === \"failed\" && job.error) {\n // Normalize to real Error instance\n throw new Error(job.error.message);\n }\n return;\n }\n\n // Poll every second\n await new Promise((resolve) => setTimeout(resolve, this.pollingInterval));\n }\n } finally {\n this.activePolling.delete(jobId);\n }\n }\n\n setCallbacks(_callbacks: PipelineManagerCallbacks): void {\n // For external pipeline, callbacks are not used since all updates come via polling\n logger.debug(\"PipelineClient.setCallbacks called - no-op for external worker\");\n }\n}\n","/**\n * Default configuration values for the scraping pipeline and server\n */\n\n/** Maximum number of pages to scrape in a single job */\nexport const DEFAULT_MAX_PAGES = 1000;\n\n/** Maximum navigation depth when crawling links */\nexport const DEFAULT_MAX_DEPTH = 3;\n\n/** Maximum number of concurrent page requests */\nexport const DEFAULT_MAX_CONCURRENCY = 3;\n\n/** Default protocol for the MCP server */\nexport const DEFAULT_PROTOCOL = \"auto\";\n\n/** Default port for the HTTP protocol */\nexport const DEFAULT_HTTP_PORT = 6280;\n\n/** Default port for the Web UI */\nexport const DEFAULT_WEB_PORT = 6281;\n\n/** Default host for server binding */\nexport const DEFAULT_HOST = \"127.0.0.1\";\n\n/**\n * Default timeout in milliseconds for page operations (e.g., Playwright waitForSelector).\n */\nexport const DEFAULT_PAGE_TIMEOUT = 5000;\n\n/**\n * Maximum number of retries for HTTP fetcher requests.\n */\nexport const FETCHER_MAX_RETRIES = 6;\n\n/**\n * Base delay in milliseconds for HTTP fetcher retry backoff.\n */\nexport const FETCHER_BASE_DELAY = 1000;\n\n/**\n * Default chunk size settings for splitters\n */\nexport const SPLITTER_MIN_CHUNK_SIZE = 500;\nexport const SPLITTER_PREFERRED_CHUNK_SIZE = 1500;\nexport const SPLITTER_MAX_CHUNK_SIZE = 5000;\n\n/**\n * Maximum number of documents to process in a single batch for embeddings.\n */\nexport const EMBEDDING_BATCH_SIZE = 100;\n\n/**\n * Maximum total character size for a single embedding batch request.\n * This prevents \"413 Request entity too large\" errors from embedding APIs.\n * Default is 50000 (~50KB), can be overridden with DOCS_MCP_EMBEDDING_BATCH_CHARS environment variable.\n */\nexport const EMBEDDING_BATCH_CHARS = 50000;\n\n/**\n * Maximum number of retries for database migrations if busy.\n */\nexport const MIGRATION_MAX_RETRIES = 5;\n\n/**\n * Delay in milliseconds between migration retry attempts.\n */\nexport const MIGRATION_RETRY_DELAY_MS = 300;\n\n/**\n * Factor to overfetch vector and FTS candidates before applying Reciprocal Rank Fusion.\n * A factor of 2 means we fetch 2x the requested limit from each source before ranking.\n */\nexport const SEARCH_OVERFETCH_FACTOR = 2;\n\n/**\n * Weight applied to vector search scores in hybrid search ranking.\n */\nexport const SEARCH_WEIGHT_VEC = 1.0;\n\n/**\n * Weight applied to full-text search scores in hybrid search ranking.\n */\nexport const SEARCH_WEIGHT_FTS = 1.0;\n\n/**\n * Multiplier to cast a wider net in vector search before final ranking.\n * Used to increase the number of vector search candidates retrieved.\n */\nexport const VECTOR_SEARCH_MULTIPLIER = 10;\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","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 NetworkError extends ScraperError {\n constructor(\n message: string,\n public readonly statusCode?: number,\n cause?: Error,\n ) {\n super(message, true, cause);\n }\n}\n\nclass RateLimitError extends ScraperError {\n constructor(\n message: string,\n public readonly retryAfter?: number,\n ) {\n super(message, true);\n }\n}\n\nclass InvalidUrlError extends ScraperError {\n constructor(url: string, cause?: Error) {\n super(`Invalid URL: ${url}`, false, cause);\n }\n}\n\nclass ParsingError extends ScraperError {\n constructor(message: string, cause?: Error) {\n super(`Failed to parse content: ${message}`, 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\nexport {\n ScraperError,\n NetworkError,\n RateLimitError,\n InvalidUrlError,\n ParsingError,\n RedirectError,\n};\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 mimeType === \"text/markdown\" || mimeType === \"text/x-markdown\";\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 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, with special handling for common source code extensions\n * that the mime package doesn't handle well or gets wrong.\n *\n * @param filePath - The file path 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 const extension = filePath.toLowerCase().split(\".\").pop();\n\n // Handle common source code extensions that mime package gets wrong or doesn't know\n const customMimeTypes: Record<string, string> = {\n ts: \"text/x-typescript\",\n tsx: \"text/x-tsx\",\n js: \"text/javascript\",\n jsx: \"text/x-jsx\",\n cjs: \"text/javascript\", // CommonJS modules\n mjs: \"text/javascript\", // ES modules\n py: \"text/x-python\",\n pyw: \"text/x-python\",\n pyi: \"text/x-python\",\n go: \"text/x-go\",\n rs: \"text/x-rust\",\n kt: \"text/x-kotlin\",\n scala: \"text/x-scala\",\n swift: \"text/x-swift\",\n rb: \"text/x-ruby\",\n php: \"text/x-php\",\n cs: \"text/x-csharp\",\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 h: \"text/x-chdr\",\n c: \"text/x-csrc\",\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 sql: \"text/x-sql\",\n graphql: \"text/x-graphql\",\n gql: \"text/x-graphql\",\n proto: \"text/x-proto\",\n dockerfile: \"text/x-dockerfile\",\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(filePath);\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 const mimeTypeNormalization: Record<string, string> = {\n \"application/node\": \"text/javascript\", // .cjs files are detected as this\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 \"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 \"text/x-python\": \"python\",\n \"text/x-java\": \"java\",\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-csharp\": \"csharp\",\n \"text/x-go\": \"go\",\n \"text/x-rust\": \"rust\",\n \"text/x-php\": \"php\",\n \"text/x-ruby\": \"ruby\",\n \"text/x-swift\": \"swift\",\n \"text/x-kotlin\": \"kotlin\",\n \"text/x-scala\": \"scala\",\n \"text/x-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-sh\": \"bash\",\n \"text/x-shellscript\": \"bash\",\n \"application/x-sh\": \"bash\",\n \"text/x-powershell\": \"powershell\",\n \"text/x-graphql\": \"graphql\",\n \"text/x-proto\": \"protobuf\",\n \"text/x-dockerfile\": \"dockerfile\",\n };\n\n return mimeToLanguage[mimeType] || \"\";\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nlet projectRoot: string | null = null;\n\n/**\n * Finds the project root directory by searching upwards from the current file\n * for a directory containing 'package.json'. Caches the result.\n *\n * @returns {string} The absolute path to the project root.\n * @throws {Error} If package.json cannot be found.\n */\nexport function getProjectRoot(): string {\n // Return cached result if available\n if (projectRoot) {\n return projectRoot;\n }\n\n // Start from the directory of the current module\n const currentFilePath = fileURLToPath(import.meta.url);\n let currentDir = path.dirname(currentFilePath);\n\n // eslint-disable-next-line no-constant-condition\n while (true) {\n const packageJsonPath = path.join(currentDir, \"package.json\");\n if (fs.existsSync(packageJsonPath)) {\n projectRoot = currentDir; // Cache the result\n return projectRoot;\n }\n\n const parentDir = path.dirname(currentDir);\n // Check if we have reached the filesystem root\n if (parentDir === currentDir) {\n throw new Error(\"Could not find project root containing package.json.\");\n }\n currentDir = parentDir;\n }\n}\n","/**\n * Thoroughly removes all types of whitespace characters from both ends of a string.\n * Handles spaces, tabs, line breaks, and carriage returns.\n */\nexport const fullTrim = (str: string): string => {\n return str.replace(/^[\\s\\r\\n\\t]+|[\\s\\r\\n\\t]+$/g, \"\");\n};\n","import 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 // Create a new URL to ensure proper structure\n const normalized = new URL(parsedUrl.origin + parsedUrl.pathname);\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 in correct order (query before hash)\n let result = normalized.origin + normalized.pathname;\n if (preservedSearch) {\n result += preservedSearch;\n }\n if (preservedHash) {\n result += preservedHash;\n }\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 * Checks if two URLs have the exact same hostname\n */\nexport function hasSameHostname(urlA: URL, urlB: URL): boolean {\n return urlA.hostname.toLowerCase() === urlB.hostname.toLowerCase();\n}\n\n/**\n * Checks if two URLs are on the same domain (including subdomains)\n * Using the public suffix list to properly handle domains like .co.uk\n */\nexport function hasSameDomain(urlA: URL, urlB: URL): boolean {\n const domainA = psl.get(urlA.hostname.toLowerCase());\n const domainB = psl.get(urlB.hostname.toLowerCase());\n return domainA !== null && domainA === domainB;\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\n/**\n * Checks if a target URL is under the same path as the base URL\n * Example: base = https://example.com/docs/\n * target = https://example.com/docs/getting-started\n * result = true\n */\nexport function isSubpath(baseUrl: URL, targetUrl: URL): boolean {\n const basePath = baseUrl.pathname.endsWith(\"/\")\n ? baseUrl.pathname\n : `${baseUrl.pathname}/`;\n return targetUrl.pathname.startsWith(basePath);\n}\n\nexport type { UrlNormalizerOptions };\n","import fs from \"node:fs/promises\";\nimport { ScraperError } from \"../../utils/errors\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, FetchOptions, RawContent } 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 */\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 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 // Don't assume charset for text files - let the pipeline detect it\n };\n } catch (error: unknown) {\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","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","import axios, { type AxiosError, type AxiosRequestConfig } from \"axios\";\nimport { CancellationError } from \"../../pipeline/errors\";\nimport { analytics, extractHostname, extractProtocol } from \"../../telemetry\";\nimport { FETCHER_BASE_DELAY, FETCHER_MAX_RETRIES } from \"../../utils/config\";\nimport { RedirectError, ScraperError } from \"../../utils/errors\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { FingerprintGenerator } from \"./FingerprintGenerator\";\nimport type { ContentFetcher, FetchOptions, RawContent } from \"./types\";\n\n/**\n * Fetches content from remote sources using HTTP/HTTPS.\n */\nexport class HttpFetcher implements ContentFetcher {\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 fingerprintGenerator: FingerprintGenerator;\n\n constructor() {\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 startTime = performance.now();\n const maxRetries = options?.maxRetries ?? FETCHER_MAX_RETRIES;\n const baseDelay = options?.retryDelay ?? FETCHER_BASE_DELAY;\n // Default to following redirects if not specified\n const followRedirects = options?.followRedirects ?? true;\n\n try {\n const result = await this.performFetch(\n source,\n options,\n maxRetries,\n baseDelay,\n followRedirects,\n );\n\n // Track successful HTTP request\n const duration = performance.now() - startTime;\n analytics.track(\"http_request_completed\", {\n success: true,\n hostname: extractHostname(source),\n protocol: extractProtocol(source),\n durationMs: Math.round(duration),\n contentSizeBytes: result.content.length,\n mimeType: result.mimeType,\n hasEncoding: !!result.encoding,\n followRedirects: followRedirects,\n hadRedirects: result.source !== source,\n });\n\n return result;\n } catch (error) {\n // Track failed HTTP request\n const duration = performance.now() - startTime;\n const axiosError = error as AxiosError;\n const status = axiosError.response?.status;\n\n analytics.track(\"http_request_completed\", {\n success: false,\n hostname: extractHostname(source),\n protocol: extractProtocol(source),\n durationMs: Math.round(duration),\n statusCode: status,\n errorType:\n error instanceof CancellationError\n ? \"cancellation\"\n : error instanceof RedirectError\n ? \"redirect\"\n : error instanceof ScraperError\n ? \"scraper\"\n : \"unknown\",\n errorCode: axiosError.code,\n followRedirects: followRedirects,\n });\n\n throw error;\n }\n }\n\n private async performFetch(\n source: string,\n options?: FetchOptions,\n maxRetries = FETCHER_MAX_RETRIES,\n baseDelay = FETCHER_BASE_DELAY,\n followRedirects = true,\n ): Promise<RawContent> {\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const fingerprint = this.fingerprintGenerator.generateHeaders();\n const headers = {\n ...fingerprint,\n ...options?.headers, // User-provided headers override generated ones\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 };\n\n const response = await axios.get(source, config);\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 return {\n content,\n mimeType,\n charset,\n encoding: contentEncoding,\n source: finalUrl,\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 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 if (\n attempt < maxRetries &&\n (status === undefined || this.retryableStatusCodes.includes(status))\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 * Base error class for all splitter-related errors\n */\nexport class SplitterError extends Error {}\n\n/**\n * Thrown when content cannot be split further while maintaining its validity\n * (e.g., markdown tables require headers, code blocks require language and backticks)\n */\nexport class MinimumChunkSizeError extends SplitterError {\n constructor(size: number, maxSize: number) {\n super(\n `Cannot split content any further. Content requires minimum chunk size of ${size} bytes, but maximum allowed is ${maxSize} bytes.`,\n );\n }\n}\n\n/**\n * Generic error for content splitting failures\n */\nexport class ContentSplitterError extends SplitterError {}\n","import type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Takes small document chunks and greedily concatenates them into larger, more meaningful units\n * while preserving document structure and semantic boundaries.\n *\n * This approach improves embedding quality by:\n * - Maintaining context by keeping related content together\n * - Respecting natural document breaks at major section boundaries (H1/H2)\n * - Ensuring chunks are large enough to capture meaningful relationships\n * - Preventing chunks from becoming too large for effective embedding\n */\nexport class GreedySplitter implements DocumentSplitter {\n private baseSplitter: DocumentSplitter;\n private minChunkSize: number;\n private preferredChunkSize: number;\n\n /**\n * Combines a base document splitter with size constraints to produce optimally-sized chunks.\n * The base splitter handles the initial semantic splitting, while this class handles\n * the concatenation strategy.\n */\n constructor(\n baseSplitter: DocumentSplitter,\n minChunkSize: number,\n preferredChunkSize: number,\n ) {\n this.baseSplitter = baseSplitter;\n this.minChunkSize = minChunkSize;\n this.preferredChunkSize = preferredChunkSize;\n }\n\n /**\n * Uses a greedy concatenation strategy to build optimally-sized chunks. Small chunks\n * are combined until they reach the minimum size, but splits are preserved at major\n * section boundaries to maintain document structure. This balances the need for\n * context with semantic coherence.\n */\n async splitText(markdown: string, contentType?: string): Promise<ContentChunk[]> {\n const initialChunks = await this.baseSplitter.splitText(markdown, contentType);\n const concatenatedChunks: ContentChunk[] = [];\n let currentChunk: ContentChunk | null = null;\n\n for (const nextChunk of initialChunks) {\n if (currentChunk) {\n if (this.wouldExceedMaxSize(currentChunk, nextChunk)) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n if (\n currentChunk.content.length >= this.minChunkSize &&\n this.startsNewMajorSection(nextChunk)\n ) {\n concatenatedChunks.push(currentChunk);\n currentChunk = this.cloneChunk(nextChunk);\n continue;\n }\n currentChunk.content += `${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: ContentChunk): ContentChunk {\n return {\n types: [...chunk.types],\n content: chunk.content,\n section: {\n level: chunk.section.level,\n path: [...chunk.section.path],\n },\n };\n }\n\n /**\n * H1 and H2 headings represent major conceptual breaks in the document.\n * Preserving these splits helps maintain the document's logical structure.\n */\n private startsNewMajorSection(chunk: ContentChunk): boolean {\n return chunk.section.level === 1 || chunk.section.level === 2;\n }\n\n /**\n * Size limit check to ensure chunks remain within embedding model constraints.\n * Essential for maintaining consistent embedding quality and avoiding truncation.\n */\n private wouldExceedMaxSize(\n currentChunk: ContentChunk | null,\n nextChunk: ContentChunk,\n ): boolean {\n if (!currentChunk) {\n return false;\n }\n return (\n currentChunk.content.length + nextChunk.content.length > this.preferredChunkSize\n );\n }\n\n /**\n * Checks if one path is a prefix of another path, indicating a parent-child relationship\n */\n private isPathIncluded(parentPath: string[], childPath: string[]): boolean {\n if (parentPath.length >= childPath.length) return false;\n return parentPath.every((part, i) => part === childPath[i]);\n }\n\n /**\n * Merges section metadata when concatenating chunks, following these rules:\n * 1. Level: Always uses the lowest (most general) level between chunks\n * 2. Path selection:\n * - For parent-child relationships (one path includes the other), uses the child's path\n * - For siblings/unrelated sections, uses the common parent path\n * - If no common path exists, uses the root path ([])\n */\n private mergeSectionInfo(\n currentChunk: ContentChunk,\n nextChunk: ContentChunk,\n ): ContentChunk[\"section\"] {\n // Always use the lowest level\n const level = Math.min(currentChunk.section.level, nextChunk.section.level);\n\n // If sections are exactly equal, preserve all metadata\n if (\n currentChunk.section.level === nextChunk.section.level &&\n currentChunk.section.path.length === nextChunk.section.path.length &&\n currentChunk.section.path.every((p, i) => p === nextChunk.section.path[i])\n ) {\n return currentChunk.section;\n }\n\n // Check if one path includes the other\n if (this.isPathIncluded(currentChunk.section.path, nextChunk.section.path)) {\n return {\n path: nextChunk.section.path,\n level,\n };\n }\n\n if (this.isPathIncluded(nextChunk.section.path, currentChunk.section.path)) {\n return {\n path: currentChunk.section.path,\n level,\n };\n }\n\n // Find common parent path\n const commonPath = this.findCommonPrefix(\n currentChunk.section.path,\n nextChunk.section.path,\n );\n\n return {\n path: commonPath,\n level,\n };\n }\n\n private mergeTypes(\n currentTypes: SectionContentType[],\n nextTypes: SectionContentType[],\n ): SectionContentType[] {\n return [...new Set([...currentTypes, ...nextTypes])];\n }\n\n /**\n * Returns longest common prefix between two paths\n */\n private findCommonPrefix(path1: string[], path2: string[]): string[] {\n const common: string[] = [];\n for (let i = 0; i < Math.min(path1.length, path2.length); i++) {\n if (path1[i] === path2[i]) {\n common.push(path1[i]);\n } else {\n break;\n }\n }\n return common;\n }\n}\n","/**\n * 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\n * 4. Maintain proper indentation and hierarchical paths\n * 5. Let GreedySplitter handle size optimization\n */\n\nimport type { ContentChunk, DocumentSplitter } from \"./types\";\n\ntype JsonValue =\n | string\n | number\n | boolean\n | null\n | JsonValue[]\n | { [key: string]: JsonValue };\n\nexport interface JsonDocumentSplitterOptions {\n // No size constraints - we create minimal chunks and let GreedySplitter optimize\n preserveFormatting?: boolean;\n}\n\nexport class JsonDocumentSplitter implements DocumentSplitter {\n private preserveFormatting: boolean;\n\n constructor(options: JsonDocumentSplitterOptions = {}) {\n this.preserveFormatting = options.preserveFormatting ?? true;\n }\n\n async splitText(content: string, _contentType?: string): Promise<ContentChunk[]> {\n try {\n const parsed: JsonValue = JSON.parse(content);\n const chunks: ContentChunk[] = [];\n\n // Process the JSON structure recursively, starting with root path\n this.processValue(parsed, [\"root\"], 1, 0, chunks, true);\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 processValue(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): void {\n if (Array.isArray(value)) {\n this.processArray(value, path, level, indentLevel, chunks, isLastItem);\n } else if (value !== null && typeof value === \"object\") {\n this.processObject(value, path, level, indentLevel, chunks, isLastItem);\n } else {\n this.processPrimitive(value, path, level, indentLevel, chunks, isLastItem);\n }\n }\n\n private processArray(\n array: JsonValue[],\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): 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 array.forEach((item, index) => {\n const isLast = index === array.length - 1;\n const itemPath = [...path, `[${index}]`];\n 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 processObject(\n obj: Record<string, JsonValue>,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): 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 entries.forEach(([key, value], index) => {\n const isLast = index === entries.length - 1;\n const propertyPath = [...path, key];\n 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 processProperty(\n key: string,\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastProperty: boolean,\n ): 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 this.processValue(value, path, level, indentLevel, chunks, isLastProperty);\n } else {\n // For primitive values, create a complete property chunk\n const comma = isLastProperty ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n chunks.push({\n types: [\"code\"],\n content: `${indent}\"${key}\": ${formattedValue}${comma}`,\n section: { level, path },\n });\n }\n }\n\n private processPrimitive(\n value: JsonValue,\n path: string[],\n level: number,\n indentLevel: number,\n chunks: ContentChunk[],\n isLastItem: boolean,\n ): void {\n const indent = this.getIndent(indentLevel);\n const comma = isLastItem ? \"\" : \",\";\n const formattedValue = JSON.stringify(value);\n\n chunks.push({\n types: [\"code\"],\n content: `${indent}${formattedValue}${comma}`,\n section: { level, path },\n });\n }\n\n private getIndent(level: number): string {\n return this.preserveFormatting ? \" \".repeat(level) : \"\";\n }\n}\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Splits code content while preserving language information and formatting.\n * Uses line boundaries for splitting and ensures each chunk is properly\n * wrapped with language-specific code block markers.\n */\nexport class CodeContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n async split(content: string): Promise<string[]> {\n // Determine language and strip triple backticks from content\n const language = content.match(/^```(\\w+)\\n/)?.[1];\n const strippedContent = content.replace(/^```(\\w*)\\n/, \"\").replace(/```\\s*$/, \"\");\n\n const lines = strippedContent.split(\"\\n\");\n const chunks: string[] = [];\n let currentChunkLines: string[] = [];\n\n for (const line of lines) {\n // Check if a single line with code block markers exceeds chunkSize\n const singleLineSize = this.wrap(line, language).length;\n if (singleLineSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleLineSize, this.options.chunkSize);\n }\n\n currentChunkLines.push(line);\n const newChunkContent = this.wrap(currentChunkLines.join(\"\\n\"), language);\n const newChunkSize = newChunkContent.length;\n\n if (newChunkSize > this.options.chunkSize && currentChunkLines.length > 1) {\n // remove last item\n const lastLine = currentChunkLines.pop();\n // wrap content and create chunk\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n currentChunkLines = [lastLine as string];\n }\n }\n\n if (currentChunkLines.length > 0) {\n chunks.push(this.wrap(currentChunkLines.join(\"\\n\"), language));\n }\n\n return chunks;\n }\n\n protected wrap(content: string, language?: string | null): string {\n return `\\`\\`\\`${language || \"\"}\\n${content.replace(/\\n+$/, \"\")}\\n\\`\\`\\``;\n }\n}\n","import { MinimumChunkSizeError } from \"../errors\";\nimport type { ContentSplitter, ContentSplitterOptions } from \"./types\";\n\n/**\n * Interface representing the structure of a parsed markdown table\n */\ninterface ParsedTable {\n headers: string[];\n separator: string;\n rows: string[];\n}\n\n/**\n * Splits table content while preserving headers and table formatting.\n * Each chunk maintains the table structure with headers and separator row.\n */\nexport class TableContentSplitter implements ContentSplitter {\n constructor(private options: ContentSplitterOptions) {}\n\n /**\n * Splits table content into chunks while preserving table structure\n */\n async split(content: string): Promise<string[]> {\n const parsedTable = this.parseTable(content);\n if (!parsedTable) {\n return [content];\n }\n\n const { headers, rows } = parsedTable;\n\n const chunks: string[] = [];\n let currentRows: string[] = [];\n\n for (const row of rows) {\n // Check if a single row with headers exceeds chunkSize\n const singleRowSize = this.wrap(row, headers).length;\n if (singleRowSize > this.options.chunkSize) {\n throw new MinimumChunkSizeError(singleRowSize, this.options.chunkSize);\n }\n\n const newChunkContent = this.wrap([...currentRows, row].join(\"\\n\"), headers);\n const newChunkSize = newChunkContent.length;\n if (newChunkSize > this.options.chunkSize && currentRows.length > 0) {\n // Add current chunk, start new\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n currentRows = [row];\n } else {\n currentRows.push(row);\n }\n }\n\n if (currentRows.length > 0) {\n chunks.push(this.wrap(currentRows.join(\"\\n\"), headers));\n }\n\n // No merging of table chunks\n return chunks;\n }\n\n protected wrap(content: string, headers: string[]): string {\n const headerRow = `| ${headers.join(\" | \")} |`;\n const separatorRow = `|${headers.map(() => \"---\").join(\"|\")}|`;\n return [headerRow, separatorRow, content].join(\"\\n\");\n }\n\n private parseTable(content: string): ParsedTable | null {\n const lines = content.trim().split(\"\\n\");\n if (lines.length < 3) return null; // Need at least headers, separator, and one data row\n\n const headers = this.parseRow(lines[0]);\n if (!headers) return null;\n\n const separator = lines[1];\n if (!this.isValidSeparator(separator)) return null;\n\n const rows = lines.slice(2).filter((row) => row.trim() !== \"\");\n\n return { headers, separator, rows };\n }\n\n /**\n * Parses a table row into cells\n */\n private parseRow(row: string): string[] | null {\n if (!row.includes(\"|\")) return null;\n return row\n .split(\"|\")\n .map((cell) => cell.trim())\n .filter((cell) => cell !== \"\");\n }\n\n /**\n * Validates the separator row of the table\n */\n private isValidSeparator(separator: string): boolean {\n return separator.includes(\"|\") && /^\\|?[\\s-|]+\\|?$/.test(separator);\n }\n}\n","import { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport { 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 { RecursiveCharacterTextSplitter } from \"langchain/text_splitter\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkHtml from \"remark-html\";\nimport remarkParse from \"remark-parse\";\nimport TurndownService from \"turndown\";\nimport { unified } from \"unified\";\nimport { createJSDOM } from \"../utils/dom\";\nimport { logger } from \"../utils/logger\";\nimport { fullTrim } from \"../utils/string\";\nimport { ContentSplitterError, MinimumChunkSizeError } from \"./errors\";\nimport { CodeContentSplitter } from \"./splitters/CodeContentSplitter\";\nimport { TableContentSplitter } from \"./splitters/TableContentSplitter\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { ContentChunk, DocumentSplitter, SectionContentType } from \"./types\";\n\n/**\n * Represents a section of content within a document,\n * typically defined by a heading\n */\ninterface DocumentSection {\n level: number;\n path: string[]; // Full path including parent headings\n content: {\n type: SectionContentType;\n text: string;\n }[];\n}\n\n/**\n * Splits markdown documents into semantic chunks while preserving\n * structure and distinguishing between different content types.\n *\n * The splitting process happens in two steps:\n * 1. Split document into sections based on headings (H1-H3 only)\n * 2. Split section content into smaller chunks based on preferredChunkSize\n */\nexport class SemanticMarkdownSplitter implements DocumentSplitter {\n private turndownService: TurndownService;\n public textSplitter: TextContentSplitter;\n public codeSplitter: CodeContentSplitter;\n public tableSplitter: TableContentSplitter;\n\n constructor(\n private preferredChunkSize: number,\n private maxChunkSize: number,\n ) {\n this.turndownService = new TurndownService({\n headingStyle: \"atx\",\n hr: \"---\",\n bulletListMarker: \"-\",\n codeBlockStyle: \"fenced\",\n emDelimiter: \"_\",\n strongDelimiter: \"**\",\n linkStyle: \"inlined\",\n });\n\n // Add table rule to preserve markdown table format\n this.turndownService.addRule(\"table\", {\n filter: [\"table\"],\n replacement: (_content, node) => {\n const table = node as HTMLTableElement;\n const headers = Array.from(table.querySelectorAll(\"th\")).map(\n (th) => th.textContent?.trim() || \"\",\n );\n const rows = Array.from(table.querySelectorAll(\"tr\")).filter(\n (tr) => !tr.querySelector(\"th\"),\n );\n\n if (headers.length === 0 && rows.length === 0) return \"\";\n\n let markdown = \"\\n\";\n if (headers.length > 0) {\n markdown += `| ${headers.join(\" | \")} |\\n`;\n markdown += `|${headers.map(() => \"---\").join(\"|\")}|\\n`;\n }\n\n for (const row of rows) {\n const cells = Array.from(row.querySelectorAll(\"td\")).map(\n (td) => td.textContent?.trim() || \"\",\n );\n markdown += `| ${cells.join(\" | \")} |\\n`;\n }\n\n return markdown;\n },\n });\n\n // Text splitter uses preferred chunk size (keeps paragraphs together if possible)\n this.textSplitter = new TextContentSplitter({\n chunkSize: this.preferredChunkSize,\n });\n // Code/table splitters use the hard chunk size (avoid splitting unless necessary)\n this.codeSplitter = new CodeContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n this.tableSplitter = new TableContentSplitter({\n chunkSize: this.maxChunkSize,\n });\n }\n\n /**\n * Main entry point for splitting markdown content\n */\n async splitText(markdown: string, _contentType?: string): Promise<ContentChunk[]> {\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 // For markdown, HTML, or plain text, process normally\n const html = await this.markdownToHtml(markdown);\n const dom = await this.parseHtml(html);\n const sections = await this.splitIntoSections(dom);\n return this.splitSectionContent(sections);\n }\n\n /**\n * Step 1: Split document into sections based on H1-H6 headings,\n * as well as code blocks and tables.\n */\n private async splitIntoSections(dom: Document): Promise<DocumentSection[]> {\n const body = dom.querySelector(\"body\");\n if (!body) {\n throw new Error(\"Invalid HTML structure: no body element found\");\n }\n\n let currentSection = this.createRootSection();\n const sections: DocumentSection[] = [];\n const stack: DocumentSection[] = [currentSection];\n\n // Process each child of the body\n for (const element of Array.from(body.children)) {\n const headingMatch = element.tagName.match(/H([1-6])/);\n\n if (headingMatch) {\n // Create new section for H1-H6 heading\n const level = Number.parseInt(headingMatch[1], 10);\n const title = fullTrim(element.textContent || \"\");\n\n // Pop sections from stack until we find the parent level\n while (stack.length > 1 && stack[stack.length - 1].level >= level) {\n stack.pop();\n }\n\n // Start new section with the header\n currentSection = {\n level,\n path: [\n ...stack.slice(1).reduce((acc: string[], s) => {\n const lastPath = s.path[s.path.length - 1];\n if (lastPath) acc.push(lastPath);\n return acc;\n }, []),\n title,\n ],\n content: [\n {\n type: \"heading\",\n text: `${\"#\".repeat(level)} ${title}`,\n },\n ],\n };\n\n sections.push(currentSection);\n stack.push(currentSection);\n } else if (element.tagName === \"PRE\") {\n // Code blocks are kept as separate chunks\n const code = element.querySelector(\"code\");\n const language = code?.className.replace(\"language-\", \"\") || \"\";\n const content = code?.textContent || element.textContent || \"\";\n const markdown = `${\"```\"}${language}\\n${content}\\n${\"```\"}`;\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"code\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else if (element.tagName === \"TABLE\") {\n // Tables are kept as separate chunks\n const markdown = fullTrim(this.turndownService.turndown(element.outerHTML));\n\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"table\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n } else {\n const markdown = fullTrim(this.turndownService.turndown(element.innerHTML));\n if (markdown) {\n // Create a new section for the text content\n currentSection = {\n level: currentSection.level,\n path: currentSection.path,\n content: [\n {\n type: \"text\",\n text: markdown,\n },\n ],\n } satisfies DocumentSection;\n sections.push(currentSection);\n }\n }\n }\n\n return sections;\n }\n\n /**\n * Step 2: Split section content into smaller chunks\n */\n private async splitSectionContent(\n sections: DocumentSection[],\n ): Promise<ContentChunk[]> {\n const chunks: ContentChunk[] = [];\n\n for (const section of sections) {\n for (const content of section.content) {\n let splitContent: string[] = [];\n\n try {\n switch (content.type) {\n case \"heading\":\n case \"text\": {\n // 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 }\n } catch (err) {\n // If it's a MinimumChunkSizeError, use RecursiveCharacterTextSplitter directly\n if (err instanceof MinimumChunkSizeError) {\n logger.warn(\n `⚠ Cannot split ${content.type} chunk normally, using RecursiveCharacterTextSplitter: ${err.message}`,\n );\n\n // Create a RecursiveCharacterTextSplitter with aggressive settings to ensure splitting\n const splitter = new RecursiveCharacterTextSplitter({\n chunkSize: this.maxChunkSize,\n chunkOverlap: Math.min(20, Math.floor(this.maxChunkSize * 0.1)),\n // Use more aggressive separators including empty string as last resort\n separators: [\n \"\\n\\n\",\n \"\\n\",\n \" \",\n \"\\t\",\n \".\",\n \",\",\n \";\",\n \":\",\n \"-\",\n \"(\",\n \")\",\n \"[\",\n \"]\",\n \"{\",\n \"}\",\n \"\",\n ],\n });\n\n const chunks = await splitter.splitText(content.text);\n if (chunks.length === 0) {\n // If still no chunks, use the most extreme approach: just truncate\n splitContent = [content.text.substring(0, this.maxChunkSize)];\n } else {\n splitContent = chunks;\n }\n } else {\n // Convert other error message to string, handling non-Error objects\n const errMessage = err instanceof Error ? err.message : String(err);\n throw new ContentSplitterError(\n `Failed to split ${content.type} content: ${errMessage}`,\n );\n }\n }\n\n // Create chunks from split content\n chunks.push(\n ...splitContent.map(\n (text): ContentChunk => ({\n types: [content.type],\n content: text,\n section: {\n level: section.level,\n path: section.path,\n },\n }),\n ),\n );\n }\n }\n\n return chunks;\n }\n\n /**\n * Helper to create the root section\n */\n private createRootSection(): DocumentSection {\n return {\n level: 0,\n path: [],\n content: [],\n };\n }\n\n /**\n * Convert markdown to HTML using remark\n */\n private async markdownToHtml(markdown: string): Promise<string> {\n const html = await unified()\n .use(remarkParse)\n .use(remarkGfm)\n .use(remarkHtml)\n .process(markdown);\n\n return `<!DOCTYPE html>\n <html>\n <body>\n ${String(html)}\n </body>\n </html>`;\n }\n\n /**\n * Parse HTML\n */\n private async parseHtml(html: string): Promise<Document> {\n // Use createJSDOM which includes default options like virtualConsole\n const { window } = createJSDOM(html);\n return window.document;\n }\n}\n","/**\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 */\nexport const TREE_SITTER_SIZE_LIMIT = 30000;\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 type { CodeBoundary, LanguageParser, ParseResult, StructuralNode } from \"./types\";\nimport { StructuralNodeType, TREE_SITTER_SIZE_LIMIT } 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 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 if (source.length > TREE_SITTER_SIZE_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, TREE_SITTER_SIZE_LIMIT);\n const lastNewline = truncatedSource.lastIndexOf(\"\\n\");\n if (lastNewline > TREE_SITTER_SIZE_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 type { CodeBoundary, LanguageParser, ParseResult, StructuralNode } from \"./types\";\nimport { StructuralNodeType, TREE_SITTER_SIZE_LIMIT } 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 if (s.type === \"comment\") {\n sawComment = true;\n startByte = s.startIndex;\n startLine = s.startPosition.row + 1;\n continue;\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 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 readonly name = \"typescript\";\n\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/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 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 if (source.length > TREE_SITTER_SIZE_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, TREE_SITTER_SIZE_LIMIT);\n const lastNewline = truncatedSource.lastIndexOf(\"\\n\");\n if (lastNewline > TREE_SITTER_SIZE_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 ) {\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 { 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\n constructor() {\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 /**\n * Initialize built-in parsers\n */\n private initializeParsers(): void {\n // Unified TypeScript parser handles the full TS/JS family.\n const unified = new TypeScriptParser();\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/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/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();\n this.registerParser(pythonParser);\n }\n}\n","/**\n * Tree-sitter based source code splitter.\n *\n * Provides semantic parsing and chunking of source code using tree-sitter ASTs.\n * Maintains compatibility with existing chunk patterns and hierarchical structure,\n * while providing robust parsing for JavaScript, TypeScript, JSX, and TSX files.\n */\n\nimport { SPLITTER_MAX_CHUNK_SIZE } from \"../../utils\";\nimport { TextContentSplitter } from \"../splitters/TextContentSplitter\";\nimport type { ContentChunk, DocumentSplitter } from \"../types\";\nimport { LanguageParserRegistry } from \"./LanguageParserRegistry\";\nimport type { CodeBoundary, LanguageParser } from \"./parsers/types\";\n\n/**\n * Configuration options for tree-sitter source code splitting\n */\nexport interface TreesitterSourceCodeSplitterOptions {\n /** Maximum size for individual chunks before delegating to TextSplitter */\n maxChunkSize?: number;\n}\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 options: Required<TreesitterSourceCodeSplitterOptions>;\n\n constructor(options: TreesitterSourceCodeSplitterOptions = {}) {\n this.options = {\n maxChunkSize: options.maxChunkSize ?? SPLITTER_MAX_CHUNK_SIZE,\n };\n\n // Initialize registry and text content splitter\n this.registry = new LanguageParserRegistry();\n this.textContentSplitter = new TextContentSplitter({\n chunkSize: this.options.maxChunkSize,\n });\n }\n\n async splitText(content: string, contentType?: string): Promise<ContentChunk[]> {\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<ContentChunk[]> {\n const textChunks = await this.textContentSplitter.split(content);\n return textChunks.map((chunk) => ({\n types: [\"code\"],\n content: chunk,\n section: {\n level: 0,\n path: [],\n },\n }));\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<ContentChunk[]> {\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.options.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 },\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<ContentChunk[]> {\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 const subChunks = await this.splitContentIntoChunks(content, [], 0);\n return subChunks;\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: ContentChunk[] = [];\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 * 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.metadata.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 { Document, ProgressCallback } from \"../types\";\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<ScraperProgress>,\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 * Options for configuring the scraping process\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\n/**\n * Result of scraping a single page. Used internally by HtmlScraper.\n */\nexport interface ScrapedPage {\n content: string;\n title: string;\n url: string;\n /** URLs extracted from page links, used for recursive scraping */\n links: string[];\n}\n\n/**\n * Progress information during scraping\n */\nexport interface ScraperProgress {\n pagesScraped: number;\n totalPages: number; // Effective total pages (limited by maxPages configuration)\n totalDiscovered: number; // Actual number of pages discovered (may exceed totalPages)\n currentUrl: string;\n depth: number;\n maxDepth: number;\n document?: Document;\n}\n","import {\n type Browser,\n type BrowserContext,\n chromium,\n type ElementHandle,\n type Frame,\n type Page,\n} from \"playwright\";\nimport { DEFAULT_PAGE_TIMEOUT } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { ScrapeMode } from \"../types\";\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 * 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\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 const launchArgs = process.env.PLAYWRIGHT_LAUNCH_ARGS?.split(\" \") ?? [];\n const executablePath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH || undefined;\n logger.debug(\n `Launching new Playwright browser instance (Chromium) with args: ${launchArgs.join(\" \") || \"none\"}...`,\n );\n this.browser = await chromium.launch({\n channel: \"chromium\",\n args: launchArgs,\n executablePath: executablePath,\n });\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 */\n async closeBrowser(): Promise<void> {\n if (this.browser?.isConnected()) {\n logger.debug(\"Closing Playwright browser instance...\");\n await this.browser.close();\n this.browser = null;\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: DEFAULT_PAGE_TIMEOUT,\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\n await frame.waitForSelector(\"body\", { timeout: DEFAULT_PAGE_TIMEOUT }).catch(() => {\n logger.debug(`Timeout waiting for body in iframe ${index + 1}`);\n });\n\n // Wait for loading indicators in the iframe to complete\n await this.waitForLoadingToComplete(frame);\n\n // Extract and replace iframe content\n const content = await this.extractIframeContent(frame);\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 waiting for 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 * Fetches content from a frame URL by navigating to it in a new page.\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 let framePage: Page | null = null;\n try {\n // Resolve relative URLs against the parent page URL\n const resolvedUrl = new URL(frameUrl, parentPage.url()).href;\n\n // Create a new page in the same browser context for consistency\n framePage = await parentPage.context().newPage();\n\n // Use the same route handling as the parent page for consistency\n await framePage.route(\"**/*\", async (route) => {\n const resourceType = route.request().resourceType();\n\n // Abort non-essential resources (but keep stylesheets for content rendering)\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n return route.abort();\n }\n\n return route.continue();\n });\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: DEFAULT_PAGE_TIMEOUT,\n });\n await framePage.waitForSelector(\"body\", { timeout: DEFAULT_PAGE_TIMEOUT });\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 logger.debug(`Successfully fetched frame content from: ${resolvedUrl}`);\n return bodyContent || \"\";\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 =\n context.options?.headers?.[\"content-type\"] ||\n context.metadata?.contentType ||\n context.metadata?.mimeType;\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 // Block unnecessary resources and inject credentials and custom headers for same-origin requests\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 // Serve the initial HTML for the main page\n if (reqUrl === context.source) {\n return route.fulfill({\n status: 200,\n contentType: \"text/html; charset=utf-8\",\n body: context.content, // context.content is always a string in middleware\n });\n }\n // Abort non-essential resources (but keep stylesheets for modern web apps)\n const resourceType = route.request().resourceType();\n if ([\"image\", \"font\", \"media\"].includes(resourceType)) {\n return route.abort();\n }\n // Use helper to merge headers\n const headers = mergePlaywrightHeaders(\n route.request().headers(),\n customHeaders,\n credentials ?? undefined,\n origin ?? undefined,\n reqOrigin ?? undefined,\n );\n return route.continue({ headers });\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 await page.waitForSelector(\"body, frameset\", { timeout: DEFAULT_PAGE_TIMEOUT });\n\n // Wait for network idle to let dynamic content initialize\n try {\n await page.waitForLoadState(\"networkidle\", { timeout: DEFAULT_PAGE_TIMEOUT });\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 \"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 \".modal\",\n \".nav-bar\",\n \".overlay\",\n \".popup\",\n \".promo\",\n \".mw-editsection\",\n \".side-bar\",\n \".social-share\",\n \".sticky\",\n \"#ads\",\n \"#banner\",\n \"#cookieBanner\",\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=\"dialog\"]',\n '[role=\"alertdialog\"]',\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 // 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 const count = elements.length;\n if (count > 0) {\n elements.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 // 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\"; // Added logger\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, 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, node) => {\n const href = (node as HTMLElement).getAttribute(\"href\");\n if (!content || content === \"#\") {\n return \"\"; // Remove if content is # or empty\n }\n if (!href) {\n return content; // Preserve content if href is missing or empty\n }\n return `[${content}](${href})`; // Standard link conversion\n },\n });\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 } 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","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 type { ContentProcessorMiddleware, MiddlewareContext } from \"./types\";\n\n/**\n * Middleware to extract the title (first H1 heading) from Markdown content.\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 const match = context.content.match(/^#\\s+(.*)$/m);\n if (match?.[1]) {\n title = match[1].trim();\n }\n context.metadata.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","/**\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 type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport type { ContentProcessorMiddleware, MiddlewareContext } from \"../middleware/types\";\nimport type { ScraperOptions } from \"../types\";\nimport type { ContentPipeline, ProcessedContent } 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 the given content.\n * Must be implemented by derived classes.\n */\n public canProcess(_rawContent: RawContent): 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<ProcessedContent> {\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","import { GreedySplitter, SemanticMarkdownSplitter } from \"../../splitter\";\nimport {\n SPLITTER_MAX_CHUNK_SIZE,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} from \"../../utils/config\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport type { ContentFetcher, RawContent } from \"../fetcher/types\";\nimport { HtmlSanitizerMiddleware } from \"../middleware\";\nimport { HtmlCheerioParserMiddleware } from \"../middleware/HtmlCheerioParserMiddleware\";\nimport { HtmlLinkExtractorMiddleware } from \"../middleware/HtmlLinkExtractorMiddleware\";\nimport { HtmlMetadataExtractorMiddleware } from \"../middleware/HtmlMetadataExtractorMiddleware\";\nimport { HtmlPlaywrightMiddleware } from \"../middleware/HtmlPlaywrightMiddleware\";\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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing HTML content using middleware and semantic splitting with size optimization.\n * Converts HTML to clean markdown format then uses SemanticMarkdownSplitter for semantic chunking,\n * followed by GreedySplitter for universal size optimization.\n */\nexport class HtmlPipeline extends BasePipeline {\n private readonly playwrightMiddleware: HtmlPlaywrightMiddleware;\n private readonly standardMiddleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(\n preferredChunkSize = SPLITTER_PREFERRED_CHUNK_SIZE,\n maxChunkSize = SPLITTER_MAX_CHUNK_SIZE,\n ) {\n super();\n this.playwrightMiddleware = new HtmlPlaywrightMiddleware();\n this.standardMiddleware = [\n new HtmlCheerioParserMiddleware(),\n new HtmlMetadataExtractorMiddleware(),\n new HtmlLinkExtractorMiddleware(),\n new HtmlSanitizerMiddleware(),\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 SPLITTER_MIN_CHUNK_SIZE,\n preferredChunkSize,\n );\n }\n\n canProcess(rawContent: RawContent): boolean {\n return MimeTypeUtils.isHtml(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\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 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 textContent: typeof context.content === \"string\" ? context.content : \"\",\n metadata: context.metadata,\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 */\n public async close(): Promise<void> {\n await super.close(); // Call base class close (no-op by default)\n await this.playwrightMiddleware.closeBrowser();\n }\n}\n","import { JsonDocumentSplitter } from \"../../splitter/JsonDocumentSplitter\";\nimport type { DocumentSplitter } from \"../../splitter/types\";\nimport { SPLITTER_PREFERRED_CHUNK_SIZE } 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 { ProcessedContent } 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(_chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\n this.middleware = [];\n // Structure-preserving splitter only (no greedy size merging)\n this.splitter = new JsonDocumentSplitter({\n preserveFormatting: true,\n });\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return MimeTypeUtils.isJson(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\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 context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\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 textContent: context.content,\n metadata: context.metadata,\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 * Analyzes the structure of valid JSON for metadata\n */\n private analyzeJsonStructure(parsedJson: unknown): {\n type: string;\n depth: number;\n itemCount?: number;\n propertyCount?: number;\n } {\n if (Array.isArray(parsedJson)) {\n return {\n type: \"array\",\n depth: this.calculateDepth(parsedJson),\n itemCount: parsedJson.length,\n };\n } else if (typeof parsedJson === \"object\" && parsedJson !== null) {\n const obj = parsedJson as Record<string, unknown>;\n return {\n type: \"object\",\n depth: this.calculateDepth(parsedJson),\n propertyCount: Object.keys(obj).length,\n };\n } else {\n return {\n type: typeof parsedJson,\n depth: 1,\n };\n }\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 { GreedySplitter, SemanticMarkdownSplitter } from \"../../splitter\";\nimport {\n SPLITTER_MAX_CHUNK_SIZE,\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Pipeline for processing Markdown content using middleware and semantic splitting with size optimization.\n * Uses SemanticMarkdownSplitter for content-type-aware semantic chunking,\n * followed by GreedySplitter for universal size optimization.\n */\nexport class MarkdownPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly greedySplitter: GreedySplitter;\n\n constructor(\n preferredChunkSize = SPLITTER_PREFERRED_CHUNK_SIZE,\n maxChunkSize = SPLITTER_MAX_CHUNK_SIZE,\n ) {\n super();\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 SPLITTER_MIN_CHUNK_SIZE,\n preferredChunkSize,\n );\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return MimeTypeUtils.isMarkdown(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\n metadata: {},\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 textContent: typeof context.content === \"string\" ? context.content : \"\",\n metadata: context.metadata,\n links: context.links,\n errors: context.errors,\n chunks,\n };\n }\n}\n","import { TreesitterSourceCodeSplitter } from \"../../splitter/treesitter/TreesitterSourceCodeSplitter\";\nimport type { DocumentSplitter } from \"../../splitter/types\";\nimport { SPLITTER_PREFERRED_CHUNK_SIZE } 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 { ProcessedContent } 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: DocumentSplitter;\n\n constructor(chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\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({ maxChunkSize: chunkSize });\n }\n\n canProcess(rawContent: RawContent): boolean {\n if (!rawContent.mimeType) return false;\n return MimeTypeUtils.isSourceCode(rawContent.mimeType);\n }\n\n async process(\n rawContent: RawContent,\n options: ScraperOptions,\n fetcher?: ContentFetcher,\n ): Promise<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\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 textContent: context.content,\n metadata: context.metadata,\n links: context.links,\n errors: context.errors,\n chunks,\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 { SPLITTER_MAX_CHUNK_SIZE } from \"../utils\";\nimport { TextContentSplitter } from \"./splitters/TextContentSplitter\";\nimport type { ContentChunk, DocumentSplitter } from \"./types\";\n\n/**\n * Configuration options for text document splitting\n */\nexport interface TextDocumentSplitterOptions {\n /** Maximum size for individual chunks */\n maxChunkSize: number;\n}\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 options: TextDocumentSplitterOptions;\n private textSplitter: TextContentSplitter;\n\n constructor(options: Partial<TextDocumentSplitterOptions> = {}) {\n this.options = {\n maxChunkSize: options.maxChunkSize ?? SPLITTER_MAX_CHUNK_SIZE,\n };\n\n this.textSplitter = new TextContentSplitter({\n chunkSize: this.options.maxChunkSize,\n });\n }\n\n async splitText(content: string): Promise<ContentChunk[]> {\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 {\n // If splitting fails (e.g., MinimumChunkSizeError), return single chunk\n return [\n {\n types: [\"text\"] as const,\n content,\n section: {\n level: 0,\n path: [],\n },\n },\n ];\n }\n }\n}\n","import { GreedySplitter } from \"../../splitter\";\nimport { TextDocumentSplitter } from \"../../splitter/TextDocumentSplitter\";\nimport {\n SPLITTER_MIN_CHUNK_SIZE,\n SPLITTER_PREFERRED_CHUNK_SIZE,\n} 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 { ProcessedContent } from \"./types\";\n\n/**\n * Fallback pipeline for processing text content with basic splitting and size optimization.\n * Handles text-based content types by using TextDocumentSplitter for simple line-based splitting\n * followed by GreedySplitter for universal size optimization. This pipeline uses MIME type filtering\n * and binary detection to ensure it only processes appropriate text content.\n */\nexport class TextPipeline extends BasePipeline {\n private readonly middleware: ContentProcessorMiddleware[];\n private readonly splitter: GreedySplitter;\n\n constructor(chunkSize = SPLITTER_PREFERRED_CHUNK_SIZE) {\n super();\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({ maxChunkSize: chunkSize });\n this.splitter = new GreedySplitter(textSplitter, SPLITTER_MIN_CHUNK_SIZE, chunkSize);\n }\n\n canProcess(rawContent: RawContent): 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(rawContent.mimeType)) {\n return false;\n }\n\n // Second check: binary detection via null bytes\n if (MimeTypeUtils.isBinary(rawContent.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<ProcessedContent> {\n const contentString = convertToString(rawContent.content, rawContent.charset);\n\n const context: MiddlewareContext = {\n content: contentString,\n source: rawContent.source,\n metadata: {\n contentType: rawContent.mimeType || \"text/plain\",\n isGenericText: true,\n },\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 textContent: context.content,\n metadata: context.metadata,\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 { SPLITTER_PREFERRED_CHUNK_SIZE } from \"../../utils/config\";\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 * Configuration options for pipeline creation\n */\nexport interface PipelineConfiguration {\n chunkSizes?: {\n /** Preferred chunk size for most content types */\n preferred?: number;\n /** Maximum chunk size for content that shouldn't be split aggressively */\n max?: number;\n };\n}\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, 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 * @param config - Optional configuration for pipeline chunk sizes\n * @returns Array of content pipelines in processing order\n */\n public static createStandardPipelines(\n config?: PipelineConfiguration,\n ): ContentPipeline[] {\n const preferredChunkSize =\n config?.chunkSizes?.preferred ?? SPLITTER_PREFERRED_CHUNK_SIZE;\n const maxChunkSize = config?.chunkSizes?.max ?? 2000;\n\n return [\n new JsonPipeline(preferredChunkSize),\n new SourceCodePipeline(preferredChunkSize),\n new HtmlPipeline(preferredChunkSize, maxChunkSize),\n new MarkdownPipeline(preferredChunkSize, maxChunkSize),\n new TextPipeline(preferredChunkSize), // Universal fallback - must be last\n ];\n }\n}\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 { 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 // minimatch expects no leading slash for relative globs, but we keep it for consistency\n // so we strip the leading slash for minimatch\n return minimatch(normalizedPath.replace(/^\\//, \"\"), pattern, { 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 */\nexport function shouldIncludeUrl(\n url: string,\n includePatterns?: string[],\n excludePatterns?: string[],\n): boolean {\n // Always match from a leading slash for path-based globs\n const path = extractPathAndQuery(url);\n const normalizedPath = path.startsWith(\"/\") ? path : `/${path}`;\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 if (\n matchesAnyPattern(normalizedPath, effectiveExcludePatterns) ||\n (basename && matchesAnyPattern(basename, stripSlash(effectiveExcludePatterns)))\n )\n return false;\n if (!includePatterns || includePatterns.length === 0) return true;\n return (\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 { Document, ProgressCallback } from \"../../types\";\nimport { DEFAULT_MAX_PAGES } from \"../../utils/config\";\nimport { logger } from \"../../utils/logger\";\nimport { normalizeUrl, type UrlNormalizerOptions } from \"../../utils/url\";\nimport type { ScraperOptions, ScraperProgress, ScraperStrategy } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { isInScope } from \"../utils/scope\";\n\n// Define defaults for optional options\nconst DEFAULT_MAX_DEPTH = 3;\nconst DEFAULT_CONCURRENCY = 3;\n\nexport type QueueItem = {\n url: string;\n depth: number;\n};\n\nexport interface BaseScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n}\n\nexport abstract class BaseScraperStrategy implements ScraperStrategy {\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\n constructor(options: BaseScraperStrategyOptions = {}) {\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 A list of URLs to add to the queue\n */\n protected abstract processItem(\n item: QueueItem,\n options: ScraperOptions,\n progressCallback?: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<{\n document?: Document;\n links?: string[];\n finalUrl?: string; // Effective fetched URL (post-redirect)\n }>;\n\n // Removed getProcessor method as processing is now handled by strategies using middleware pipelines\n\n protected async processBatch(\n batch: QueueItem[],\n baseUrl: URL,\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<QueueItem[]> {\n const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\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 ?? DEFAULT_MAX_DEPTH;\n if (item.depth > maxDepth) {\n return [];\n }\n\n try {\n // Pass signal to processItem\n const result = await this.processItem(item, options, undefined, signal);\n // If this is the root (depth 0) and we have a finalUrl differing from original, set canonicalBaseUrl\n if (item.depth === 0 && !this.canonicalBaseUrl && result?.finalUrl) {\n try {\n const finalUrlStr = result.finalUrl as string;\n const original = new URL(options.url);\n const finalUrlObj = new URL(finalUrlStr);\n if (\n finalUrlObj.href !== original.href &&\n (finalUrlObj.protocol === \"http:\" || finalUrlObj.protocol === \"https:\")\n ) {\n this.canonicalBaseUrl = finalUrlObj;\n logger.debug(\n `Updated scope base after redirect: ${original.href} -> ${finalUrlObj.href}`,\n );\n } else {\n this.canonicalBaseUrl = original;\n }\n } catch {\n // Ignore canonical base errors\n this.canonicalBaseUrl = new URL(options.url);\n }\n }\n\n if (result.document) {\n this.pageCount++;\n // maxDepth already resolved above\n logger.info(\n `🌐 Scraping page ${this.pageCount}/${this.effectiveTotal} (depth ${item.depth}/${maxDepth}): ${item.url}`,\n );\n await progressCallback({\n pagesScraped: this.pageCount,\n totalPages: this.effectiveTotal,\n totalDiscovered: this.totalDiscovered,\n currentUrl: item.url,\n depth: item.depth,\n maxDepth: maxDepth,\n document: result.document,\n });\n }\n\n const nextItems = result.links || [];\n return nextItems\n .map((value) => {\n try {\n const targetUrl = new URL(value, baseUrl);\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 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<ScraperProgress>,\n signal?: AbortSignal, // Add signal\n ): Promise<void> {\n this.visited.clear();\n this.pageCount = 0;\n this.totalDiscovered = 1; // Start with the initial URL (unlimited counter)\n this.effectiveTotal = 1; // Start with the initial URL (limited counter)\n\n this.canonicalBaseUrl = new URL(options.url);\n let baseUrl = this.canonicalBaseUrl;\n const queue = [{ url: options.url, depth: 0 } satisfies QueueItem];\n\n // Track values we've seen (either queued or visited)\n this.visited.add(normalizeUrl(options.url, this.options.urlNormalizerOptions));\n\n // Resolve optional values to defaults using temporary variables\n const maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\n const maxConcurrency = options.maxConcurrency ?? DEFAULT_CONCURRENCY;\n\n while (queue.length > 0 && this.pageCount < maxPages) {\n // Use variable\n // Check for cancellation at the start of each loop iteration\n if (signal?.aborted) {\n logger.debug(\"Scraping cancelled by signal.\");\n throw new CancellationError(\"Scraping cancelled by signal\");\n }\n\n const remainingPages = maxPages - this.pageCount; // Use variable\n if (remainingPages <= 0) {\n break;\n }\n\n const batchSize = Math.min(\n maxConcurrency, // Use variable\n remainingPages,\n queue.length,\n );\n\n const batch = queue.splice(0, batchSize);\n // Pass signal to processBatch\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 { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport { MimeTypeUtils } from \"../../utils/mimeTypeUtils\";\nimport { HttpFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../pipelines/types\";\nimport { ScrapeMode, type ScraperOptions, type ScraperProgress } from \"../types\";\nimport { shouldIncludeUrl } from \"../utils/patternMatcher\";\nimport { BaseScraperStrategy, type QueueItem } from \"./BaseScraperStrategy\";\n\ninterface GitHubRepoInfo {\n owner: string;\n repo: string;\n branch?: string;\n}\n\ninterface GitHubTreeItem {\n path: string;\n type: \"blob\" | \"tree\";\n sha: string;\n size?: number;\n url: string;\n}\n\ninterface GitHubTreeResponse {\n sha: string;\n url: string;\n tree: GitHubTreeItem[];\n truncated: boolean;\n}\n\n/**\n * GitHubScraperStrategy handles native repository crawling by accessing GitHub's tree API\n * to discover repository structure and fetching raw file contents. This treats repositories\n * more like file systems rather than web pages.\n *\n * Features:\n * - Uses GitHub tree API for efficient repository structure discovery\n * - Fetches raw file contents from raw.githubusercontent.com\n * - Processes all text files (source code, markdown, documentation, etc.)\n * - Supports branch-specific crawling (defaults to main/default branch)\n * - Automatically detects repository default branch when no branch specified\n * - Filters out binary files and processes only text-based content\n *\n * Note: Wiki pages are not currently supported in this native mode. For wiki access,\n * consider using the web scraping approach or a separate scraping job.\n */\nexport class GitHubScraperStrategy extends BaseScraperStrategy {\n private readonly httpFetcher = new HttpFetcher();\n private readonly pipelines: ContentPipeline[];\n private resolvedBranch?: string; // Cache the resolved default branch\n\n constructor() {\n super();\n this.pipelines = PipelineFactory.createStandardPipelines();\n }\n\n canHandle(url: string): boolean {\n const { hostname } = new URL(url);\n return [\"github.com\", \"www.github.com\"].includes(hostname);\n }\n\n /**\n * Override shouldProcessUrl to handle github-file:// URLs specially.\n * These URLs bypass scope checking since they're internal file references.\n */\n protected shouldProcessUrl(url: string, options: ScraperOptions): boolean {\n // For github-file:// URLs, only apply include/exclude patterns, skip scope checking\n if (url.startsWith(\"github-file://\")) {\n const filePath = url.replace(\"github-file://\", \"\");\n return shouldIncludeUrl(filePath, options.includePatterns, options.excludePatterns);\n }\n\n // For regular URLs, use the base implementation\n return super.shouldProcessUrl(url, options);\n }\n\n /**\n * Parses a GitHub URL to extract repository information.\n */\n parseGitHubUrl(url: string): GitHubRepoInfo {\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 from URL if present (e.g., /tree/branch-name/)\n const branchMatch = parsedUrl.pathname.match(/\\/tree\\/([^/]+)/);\n const branch = branchMatch?.[1];\n\n return { owner, repo, branch };\n }\n\n /**\n * Fetches the repository tree structure from GitHub API.\n * Uses 'HEAD' to get the default branch if no branch is specified.\n */\n async fetchRepositoryTree(\n repoInfo: GitHubRepoInfo,\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 try {\n // Get repository information to find the default branch\n const repoUrl = `https://api.github.com/repos/${owner}/${repo}`;\n logger.debug(`Fetching repository info: ${repoUrl}`);\n\n const repoContent = await this.httpFetcher.fetch(repoUrl, { signal });\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 targetBranch = repoData.default_branch;\n\n logger.debug(`Using default branch: ${targetBranch}`);\n } catch (error) {\n logger.warn(`⚠️ Could not fetch default branch, using 'main': ${error}`);\n targetBranch = \"main\";\n }\n }\n\n // Cache the resolved branch for file fetching\n this.resolvedBranch = targetBranch;\n\n const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${targetBranch}?recursive=1`;\n\n logger.debug(`Fetching repository tree: ${treeUrl}`);\n\n const rawContent = await this.httpFetcher.fetch(treeUrl, { signal });\n const content =\n typeof rawContent.content === \"string\"\n ? rawContent.content\n : rawContent.content.toString(\"utf-8\");\n const treeData = JSON.parse(content) as GitHubTreeResponse;\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 // Only process blob (file) items, not trees (directories)\n if (item.type !== \"blob\") {\n return false;\n }\n\n const path = item.path;\n\n // Whitelist of text-based file extensions that we can process\n const textExtensions = [\n // Documentation\n \".md\",\n \".mdx\",\n \".txt\",\n \".rst\",\n \".adoc\",\n \".asciidoc\",\n\n // Web technologies\n \".html\",\n \".htm\",\n \".xml\",\n \".css\",\n \".scss\",\n \".sass\",\n \".less\",\n\n // Programming languages\n \".js\",\n \".jsx\",\n \".ts\",\n \".tsx\",\n \".py\",\n \".java\",\n \".c\",\n \".cpp\",\n \".cc\",\n \".cxx\",\n \".h\",\n \".hpp\",\n \".cs\",\n \".go\",\n \".rs\",\n \".rb\",\n \".php\",\n \".swift\",\n \".kt\",\n \".scala\",\n \".clj\",\n \".cljs\",\n \".hs\",\n \".elm\",\n \".dart\",\n \".r\",\n \".m\",\n \".mm\",\n \".sh\",\n \".bash\",\n \".zsh\",\n \".fish\",\n \".ps1\",\n \".bat\",\n \".cmd\",\n\n // Configuration and data\n \".json\",\n \".yaml\",\n \".yml\",\n \".toml\",\n \".ini\",\n \".cfg\",\n \".conf\",\n \".properties\",\n \".env\",\n \".gitignore\",\n \".dockerignore\",\n \".gitattributes\",\n \".editorconfig\",\n\n // Build and package management\n \".gradle\",\n \".pom\",\n \".sbt\",\n \".maven\",\n \".cmake\",\n \".make\",\n \".dockerfile\",\n \".mod\", // Go modules (go.mod)\n \".sum\", // Go checksums (go.sum)\n\n // Other text formats\n \".sql\",\n \".graphql\",\n \".gql\",\n \".proto\",\n \".thrift\",\n \".avro\",\n \".csv\",\n \".tsv\",\n \".log\",\n ];\n\n const pathLower = path.toLowerCase();\n\n // Check for known text extensions\n const hasTextExtension = textExtensions.some((ext) => pathLower.endsWith(ext));\n\n // Check for compound extensions and special cases\n const hasCompoundExtension =\n pathLower.includes(\".env.\") || // .env.example, .env.local, etc.\n pathLower.endsWith(\".env\") ||\n pathLower.includes(\".config.\") || // webpack.config.js, etc.\n pathLower.includes(\".lock\"); // package-lock.json, etc.\n\n // Also include files without extensions that are commonly text files\n const fileName = path.split(\"/\").pop() || \"\";\n const fileNameLower = fileName.toLowerCase();\n const commonTextFiles = [\n // Documentation files without extensions\n \"readme\",\n \"license\",\n \"changelog\",\n \"contributing\",\n \"authors\",\n \"maintainers\",\n\n // Build files without extensions\n \"dockerfile\",\n \"makefile\",\n \"rakefile\",\n \"gemfile\",\n \"podfile\",\n \"cartfile\",\n \"brewfile\",\n \"procfile\",\n \"vagrantfile\",\n \"gulpfile\",\n \"gruntfile\",\n\n // Configuration files (dotfiles)\n \".prettierrc\",\n \".eslintrc\",\n \".babelrc\",\n \".nvmrc\",\n \".npmrc\",\n ];\n\n const isCommonTextFile = commonTextFiles.some((name) => {\n if (name.startsWith(\".\")) {\n // For dotfiles, match exactly or with additional extension (e.g., .prettierrc.js)\n return fileNameLower === name || fileNameLower.startsWith(`${name}.`);\n }\n // For regular files, match exactly or with extension\n return fileNameLower === name || fileNameLower.startsWith(`${name}.`);\n });\n\n // Process file if it has a text extension, compound extension, or is a common text file\n if (!hasTextExtension && !hasCompoundExtension && !isCommonTextFile) {\n return false;\n }\n\n // Apply user-defined include/exclude patterns (use the file path directly)\n return shouldIncludeUrl(path, options.includePatterns, options.excludePatterns);\n }\n\n /**\n * Fetches the raw content of a file from GitHub.\n */\n async fetchFileContent(\n repoInfo: GitHubRepoInfo,\n filePath: string,\n signal?: AbortSignal,\n ): Promise<RawContent> {\n const { owner, repo } = repoInfo;\n // Use resolved branch if available, otherwise use provided branch or default to main\n const branch = this.resolvedBranch || repoInfo.branch || \"main\";\n const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${filePath}`;\n\n const rawContent = await this.httpFetcher.fetch(rawUrl, { signal });\n\n // Override GitHub's generic 'text/plain' MIME type with file extension-based detection\n const detectedMimeType = MimeTypeUtils.detectMimeTypeFromPath(filePath);\n if (detectedMimeType && rawContent.mimeType === \"text/plain\") {\n return {\n ...rawContent,\n mimeType: detectedMimeType,\n };\n }\n\n return rawContent;\n }\n\n protected async processItem(\n item: QueueItem,\n options: ScraperOptions,\n _progressCallback?: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal,\n ): Promise<{ document?: Document; links?: string[] }> {\n // Parse the URL to get repository information\n const repoInfo = this.parseGitHubUrl(options.url);\n\n // For the initial item, fetch the repository tree\n if (item.depth === 0) {\n logger.info(\n `🗂️ Discovering repository structure for ${repoInfo.owner}/${repoInfo.repo}`,\n );\n\n const { tree, resolvedBranch } = await this.fetchRepositoryTree(repoInfo, signal);\n const fileItems = tree.tree.filter((treeItem) =>\n this.shouldProcessFile(treeItem, options),\n );\n\n logger.info(\n `📁 Found ${fileItems.length} processable files in repository (branch: ${resolvedBranch})`,\n );\n\n // Convert tree items to URLs for the queue\n const links = fileItems.map((treeItem) => `github-file://${treeItem.path}`);\n\n return { links };\n }\n\n // Process individual files\n if (item.url.startsWith(\"github-file://\")) {\n const filePath = item.url.replace(\"github-file://\", \"\");\n\n logger.info(\n `🗂️ Processing file ${this.pageCount}/${options.maxPages}: ${filePath}`,\n );\n\n const rawContent = await this.fetchFileContent(repoInfo, filePath, signal);\n\n // Process content through appropriate pipeline\n let processed: Awaited<ReturnType<ContentPipeline[\"process\"]>> | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\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 { document: undefined, links: [] };\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/${repoInfo.owner}/${repoInfo.repo}/blob/${this.resolvedBranch || repoInfo.branch || \"main\"}/${filePath}`;\n\n return {\n document: {\n content: typeof processed.textContent === \"string\" ? processed.textContent : \"\",\n metadata: {\n url: githubUrl,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : filePath.split(\"/\").pop() || \"Untitled\",\n library: options.library,\n version: options.version,\n },\n contentType: rawContent.mimeType, // Preserve the detected MIME type\n } satisfies Document,\n links: [], // Always return empty links array for individual files\n };\n }\n\n return { document: undefined, links: [] };\n }\n\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal,\n ): Promise<void> {\n // Validate it's a GitHub URL\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 return super.scrape(options, progressCallback, signal);\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","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport type { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport { FileFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../pipelines/types\";\nimport type { ScraperOptions, ScraperProgress } from \"../types\";\nimport { BaseScraperStrategy, type QueueItem } 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() {\n super();\n this.pipelines = PipelineFactory.createStandardPipelines();\n }\n\n canHandle(url: string): boolean {\n return url.startsWith(\"file://\");\n }\n\n protected async processItem(\n item: QueueItem,\n options: ScraperOptions,\n _progressCallback?: ProgressCallback<ScraperProgress>,\n _signal?: AbortSignal,\n ): Promise<{ document?: Document; links?: string[] }> {\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 const stats = await fs.stat(filePath);\n\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) => `file://${path.join(filePath, name)}`)\n .filter((url) => this.shouldProcessUrl(url, options));\n return { links };\n }\n\n logger.info(`🗂️ Processing file ${this.pageCount}/${options.maxPages}: ${filePath}`);\n\n const rawContent: RawContent = await this.fileFetcher.fetch(item.url);\n\n let processed: Awaited<ReturnType<ContentPipeline[\"process\"]>> | undefined;\n\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${filePath})`,\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 ${filePath}. Skipping processing.`,\n );\n return { document: undefined, links: [] };\n }\n\n for (const err of processed.errors) {\n logger.warn(`⚠️ Processing error for ${filePath}: ${err.message}`);\n }\n\n return {\n document: {\n content: typeof processed.textContent === \"string\" ? processed.textContent : \"\",\n contentType: rawContent.mimeType,\n metadata: {\n url: rawContent.source,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : \"Untitled\",\n library: options.library,\n version: options.version,\n },\n } satisfies Document,\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","import type { Document, ProgressCallback } from \"../../types\";\nimport { logger } from \"../../utils/logger\";\nimport type { UrlNormalizerOptions } from \"../../utils/url\";\nimport { HttpFetcher } from \"../fetcher\";\nimport type { RawContent } from \"../fetcher/types\";\nimport { PipelineFactory } from \"../pipelines/PipelineFactory\";\nimport type { ContentPipeline, ProcessedContent } from \"../pipelines/types\";\nimport type { ScraperOptions, ScraperProgress } from \"../types\";\nimport { isInScope } from \"../utils/scope\";\nimport { BaseScraperStrategy, type QueueItem } from \"./BaseScraperStrategy\";\n\nexport interface WebScraperStrategyOptions {\n urlNormalizerOptions?: UrlNormalizerOptions;\n shouldFollowLink?: (baseUrl: URL, targetUrl: URL) => boolean;\n}\n\nexport class WebScraperStrategy extends BaseScraperStrategy {\n private readonly httpFetcher = new HttpFetcher();\n private readonly shouldFollowLinkFn?: (baseUrl: URL, targetUrl: URL) => boolean;\n private readonly pipelines: ContentPipeline[];\n\n constructor(options: WebScraperStrategyOptions = {}) {\n super({ urlNormalizerOptions: options.urlNormalizerOptions });\n this.shouldFollowLinkFn = options.shouldFollowLink;\n this.pipelines = PipelineFactory.createStandardPipelines();\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 _progressCallback?: ProgressCallback<ScraperProgress>, // Base class passes it, but not used here\n signal?: AbortSignal, // Add signal\n ): Promise<{ document?: Document; links?: string[]; finalUrl?: string }> {\n const { url } = item;\n\n try {\n // Define fetch options, passing signal, followRedirects, and headers\n const fetchOptions = {\n signal,\n followRedirects: options.followRedirects,\n headers: options.headers, // Forward custom headers\n };\n\n // Pass options to fetcher\n const rawContent: RawContent = await this.httpFetcher.fetch(url, fetchOptions);\n\n // --- Start Pipeline Processing ---\n let processed: ProcessedContent | undefined;\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${url})`,\n );\n processed = await pipeline.process(rawContent, options, this.httpFetcher);\n break;\n }\n }\n\n if (!processed) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for URL ${url}. Skipping processing.`,\n );\n return { document: undefined, links: [] };\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 { document: undefined, links: processed.links };\n }\n\n // Determine base for scope filtering:\n // For depth 0 (initial page) use the final fetched URL (rawContent.source) so protocol/host redirects don't drop links.\n // For deeper pages, use canonicalBaseUrl (set after first page) or fallback to original.\n const baseUrl =\n item.depth === 0\n ? new URL(rawContent.source)\n : (this.canonicalBaseUrl ?? new URL(options.url));\n\n const filteredLinks = processed.links.filter((link) => {\n try {\n const targetUrl = new URL(link);\n const scope = options.scope || \"subpages\";\n return (\n isInScope(baseUrl, targetUrl, scope) &&\n (!this.shouldFollowLinkFn || this.shouldFollowLinkFn(baseUrl, targetUrl))\n );\n } catch {\n return false;\n }\n });\n\n return {\n document: {\n content: processed.textContent,\n metadata: {\n url,\n title:\n typeof processed.metadata.title === \"string\"\n ? processed.metadata.title\n : \"Untitled\",\n library: options.library,\n version: options.version,\n ...processed.metadata,\n },\n } satisfies Document,\n links: filteredLinks,\n finalUrl: rawContent.source,\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 /**\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","import type { ProgressCallback } from \"../../types\";\nimport type { ScraperOptions, ScraperProgress, 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() {\n this.defaultStrategy = new WebScraperStrategy({\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<ScraperProgress>,\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 { ScraperOptions, ScraperProgress, 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() {\n this.defaultStrategy = new WebScraperStrategy({\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<ScraperProgress>,\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 { logger } from \"../utils\";\nimport { ScraperError } from \"../utils/errors\";\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\nexport class ScraperRegistry {\n private strategies: ScraperStrategy[];\n\n constructor() {\n this.strategies = [\n new NpmScraperStrategy(),\n new PyPiScraperStrategy(),\n new GitHubScraperStrategy(),\n new WebScraperStrategy(),\n new LocalFileStrategy(),\n ];\n }\n\n getStrategy(url: string): ScraperStrategy {\n validateUrl(url);\n const strategy = this.strategies.find((s) => s.canHandle(url));\n if (!strategy) {\n throw new ScraperError(`No strategy found for URL: ${url}`);\n }\n logger.debug(`Using strategy \"${strategy.constructor.name}\" for URL: ${url}`);\n return strategy;\n }\n\n /**\n * Cleanup all registered strategies to prevent resource leaks.\n * Should be called when the registry is no longer needed.\n */\n async cleanup(): Promise<void> {\n await Promise.allSettled(this.strategies.map((strategy) => strategy.cleanup?.()));\n }\n}\n","import type { ProgressCallback } from \"../types\";\nimport { ScraperError } from \"../utils/errors\";\nimport type { ScraperRegistry } from \"./ScraperRegistry\";\nimport type { ScraperOptions, ScraperProgress } from \"./types\";\n\n/**\n * Orchestrates document scraping operations using registered scraping strategies.\n * Automatically selects appropriate strategy based on URL patterns.\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 */\n async scrape(\n options: ScraperOptions,\n progressCallback: ProgressCallback<ScraperProgress>,\n signal?: AbortSignal, // Add optional signal parameter\n ): Promise<void> {\n // Find strategy for this URL\n const strategy = this.registry.getStrategy(options.url);\n if (!strategy) {\n throw new ScraperError(`No scraper strategy found for URL: ${options.url}`, false);\n }\n\n // Pass the signal down to the strategy\n await strategy.scrape(options, progressCallback, signal);\n }\n\n /**\n * Cleanup the scraper registry and all its strategies.\n * Should be called when the service is no longer needed.\n */\n async cleanup(): Promise<void> {\n await this.registry.cleanup();\n }\n}\n","import type { ScraperService } from \"../scraper\";\nimport type { ScraperProgress } from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { logger } from \"../utils/logger\";\nimport { CancellationError } from \"./errors\";\nimport type { InternalPipelineJob, PipelineManagerCallbacks } from \"./types\";\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 - Callbacks provided by the manager for reporting.\n */\n async executeJob(\n job: InternalPipelineJob,\n callbacks: PipelineManagerCallbacks,\n ): Promise<void> {\n const {\n id: jobId,\n library,\n version,\n sourceUrl,\n scraperOptions,\n abortController,\n } = 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 await this.store.removeAllDocuments(library, version);\n logger.info(\n `💾 Cleared store for ${library}@${version || \"[no version]\"} before scraping.`,\n );\n\n // Construct runtime options from job context + stored configuration\n const runtimeOptions = {\n url: sourceUrl ?? \"\",\n library,\n version,\n ...scraperOptions,\n };\n\n // --- Core Job Logic ---\n await this.scraperService.scrape(\n runtimeOptions,\n async (progress: ScraperProgress) => {\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 if (progress.document) {\n try {\n await this.store.addDocument(library, version, {\n pageContent: progress.document.content,\n metadata: {\n ...progress.document.metadata,\n mimeType: progress.document.contentType, // Pass contentType as mimeType in metadata\n },\n });\n logger.debug(\n `[${jobId}] Stored document: ${progress.document.metadata.url}`,\n );\n } catch (docError) {\n logger.error(\n `❌ [${jobId}] Failed to store document ${progress.document.metadata.url}: ${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.document,\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","import type { ScraperProgress } from \"../scraper/types\";\nimport type { VersionScraperOptions, VersionStatus } from \"../store/types\";\nimport type { Document } from \"../types\"; // Use local Document type\n\n/**\n * Represents the possible states of a pipeline job.\n */\nexport enum PipelineJobStatus {\n QUEUED = \"queued\",\n RUNNING = \"running\",\n COMPLETED = \"completed\",\n FAILED = \"failed\",\n CANCELLING = \"cancelling\",\n CANCELLED = \"cancelled\",\n}\n\n/**\n * Public interface for pipeline jobs exposed through API boundaries.\n * Contains only serializable fields suitable for JSON transport.\n */\nexport interface PipelineJob {\n /** Unique identifier for the job. */\n id: string;\n /** The library name associated with the job. */\n library: string;\n /** The library version associated with the job. */\n version: string | null;\n /** Current pipeline status of the job. */\n status: PipelineJobStatus;\n /** Detailed progress information. */\n progress: ScraperProgress | null;\n /** Error information if the job failed. */\n error: { message: string } | null;\n /** Timestamp when the job was created. */\n createdAt: Date;\n /** Timestamp when the job started running. */\n startedAt: Date | null;\n /** Timestamp when the job finished (completed, failed, or cancelled). */\n finishedAt: Date | null;\n /** Database version ID for direct updates. */\n versionId?: number;\n /** Database version status (authoritative). */\n versionStatus?: VersionStatus;\n /** Current number of pages processed. */\n progressPages?: number;\n /** Maximum number of pages to process. */\n progressMaxPages?: number;\n /** Database error message (more detailed than Error object). */\n errorMessage?: string | null;\n /** Last update timestamp from database. */\n updatedAt?: Date;\n /** Original scraping URL. */\n sourceUrl: string | null;\n /** Stored scraper options for reproducibility. */\n scraperOptions: VersionScraperOptions | null;\n}\n\n/**\n * Internal pipeline job representation used within PipelineManager.\n * Contains non-serializable fields for job management and control.\n */\nexport interface InternalPipelineJob extends Omit<PipelineJob, \"version\" | \"error\"> {\n /** The library version associated with the job (internal uses string). */\n version: string;\n /** Error object if the job failed. */\n error: Error | null;\n /** AbortController to signal cancellation. */\n abortController: AbortController;\n /** Promise that resolves/rejects when the job finishes. */\n completionPromise: Promise<void>;\n /** Resolver function for the completion promise. */\n resolveCompletion: () => void;\n /** Rejector function for the completion promise. */\n rejectCompletion: (reason?: unknown) => void;\n}\n\n/**\n * Defines the structure for callback functions used with the PipelineManager.\n * Allows external components to hook into job lifecycle events.\n */\nexport interface PipelineManagerCallbacks {\n /** Callback triggered when a job's status changes. */\n onJobStatusChange?: (job: InternalPipelineJob) => Promise<void>;\n /** Callback triggered when a job makes progress. */\n onJobProgress?: (job: InternalPipelineJob, progress: ScraperProgress) => Promise<void>;\n /** Callback triggered when a job encounters an error during processing (e.g., storing a doc). */\n onJobError?: (\n job: InternalPipelineJob,\n error: Error,\n document?: Document,\n ) => Promise<void>;\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 { ScraperRegistry, ScraperService } from \"../scraper\";\nimport type { ScraperOptions, ScraperProgress } from \"../scraper/types\";\nimport type { DocumentManagementService } from \"../store\";\nimport { VersionStatus } from \"../store/types\";\nimport { DEFAULT_MAX_CONCURRENCY } 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, PipelineManagerCallbacks } 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 callbacks: PipelineManagerCallbacks = {};\n private composedCallbacks: PipelineManagerCallbacks = {};\n private store: DocumentManagementService;\n private scraperService: ScraperService;\n private shouldRecoverJobs: boolean;\n\n constructor(\n store: DocumentManagementService,\n concurrency: number = DEFAULT_MAX_CONCURRENCY,\n options: { recoverJobs?: boolean } = {},\n ) {\n this.store = store;\n this.concurrency = concurrency;\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();\n this.scraperService = new ScraperService(registry);\n\n // Initialize composed callbacks to ensure progress persistence even before setCallbacks is called\n this.rebuildComposedCallbacks();\n }\n\n /**\n * Registers callback handlers for pipeline manager events.\n */\n setCallbacks(callbacks: PipelineManagerCallbacks): void {\n this.callbacks = callbacks || {};\n this.rebuildComposedCallbacks();\n }\n\n /** Build composed callbacks that ensure persistence then delegate to user callbacks */\n private rebuildComposedCallbacks(): void {\n const user = this.callbacks;\n this.composedCallbacks = {\n onJobProgress: async (job, progress) => {\n await this.updateJobProgress(job, progress);\n await user.onJobProgress?.(job, progress);\n },\n onJobStatusChange: async (job) => {\n await user.onJobStatusChange?.(job);\n },\n onJobError: async (job, error, document) => {\n await user.onJobError?.(job, error, document);\n },\n };\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 logger.debug(\"Job recovery disabled for this PipelineManager instance\");\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 * Finds versions with RUNNING status and resets them to QUEUED for re-processing.\n * Also loads all QUEUED versions back into the pipeline queue.\n */\n async recoverPendingJobs(): Promise<void> {\n try {\n // Reset RUNNING jobs to QUEUED (they were interrupted by server restart)\n const runningVersions = await this.store.getVersionsByStatus([\n VersionStatus.RUNNING,\n ]);\n for (const version of runningVersions) {\n await this.store.updateVersionStatus(version.id, VersionStatus.QUEUED);\n logger.info(\n `🔄 Reset interrupted job to QUEUED: ${version.library_name}@${version.name || \"unversioned\"}`,\n );\n }\n\n // Load all QUEUED versions back into pipeline\n const queuedVersions = await this.store.getVersionsByStatus([VersionStatus.QUEUED]);\n for (const version of queuedVersions) {\n // Create complete job with all database state restored\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 // Parse stored scraper options\n let parsedScraperOptions = null;\n if (version.scraper_options) {\n try {\n parsedScraperOptions = JSON.parse(version.scraper_options);\n } catch (error) {\n logger.warn(\n `⚠️ Failed to parse scraper options for ${version.library_name}@${version.name || \"unversioned\"}: ${error}`,\n );\n }\n }\n\n const job: InternalPipelineJob = {\n id: jobId,\n library: version.library_name,\n version: version.name || \"\",\n status: PipelineJobStatus.QUEUED,\n progress: null,\n error: null,\n createdAt: new Date(version.created_at),\n // For recovered QUEUED jobs, startedAt must be null to reflect queued state.\n startedAt: null,\n finishedAt: null,\n abortController,\n completionPromise,\n resolveCompletion,\n rejectCompletion,\n\n // Database fields (single source of truth)\n versionId: version.id,\n versionStatus: version.status,\n progressPages: version.progress_pages,\n progressMaxPages: version.progress_max_pages,\n errorMessage: version.error_message,\n updatedAt: new Date(version.updated_at),\n sourceUrl: version.source_url,\n scraperOptions: parsedScraperOptions,\n };\n\n this.jobMap.set(jobId, job);\n this.jobQueue.push(jobId);\n }\n\n if (queuedVersions.length > 0) {\n logger.info(`📥 Recovered ${queuedVersions.length} pending job(s) from database`);\n } else {\n logger.debug(\"No pending jobs to recover from database\");\n }\n } catch (error) {\n logger.error(`❌ Failed to recover pending jobs: ${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 // Cleanup scraper service to prevent resource leaks\n await this.scraperService.cleanup();\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 enqueueJob(\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 // Extract URL and convert ScraperOptions to VersionScraperOptions\n const {\n url,\n library: _library,\n version: _version,\n signal: _signal,\n ...versionOptions\n } = options;\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: url,\n scraperOptions: versionOptions,\n };\n\n this.jobMap.set(jobId, job);\n this.jobQueue.push(jobId);\n logger.info(\n `📝 Job enqueued: ${jobId} for ${library}${normalizedVersion ? `@${normalizedVersion}` : \" (unversioned)\"}`,\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 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 || \"unversioned\"}`,\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 || \"unversioned\"} with stored options from ${stored.sourceUrl}`,\n );\n\n return this.enqueueJob(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 } 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 using composed callbacks\n await worker.executeJob(job, this.composedCallbacks);\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 // Reconstruct ScraperOptions for storage (DocumentStore will filter runtime fields)\n const fullOptions = {\n url: job.sourceUrl ?? \"\",\n library: job.library,\n version: job.version,\n ...job.scraperOptions,\n };\n await this.store.storeScraperOptions(versionId, fullOptions);\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 // Fire callback\n await this.callbacks.onJobStatusChange?.(job);\n }\n\n /**\n * Updates both in-memory job progress and database progress (write-through).\n */\n async updateJobProgress(\n job: InternalPipelineJob,\n progress: ScraperProgress,\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 // Note: Do not invoke onJobProgress callback here.\n // Callbacks are wired by services (e.g., workerService/CLI) and already call this method.\n }\n}\n","import type { DocumentManagementService } from \"../store\";\nimport { DEFAULT_MAX_CONCURRENCY } from \"../utils/config\";\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 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 options: Required<Pick<PipelineOptions, \"serverUrl\">> &\n Omit<PipelineOptions, \"serverUrl\">,\n ): Promise<PipelineClient>;\n // Implementation\n export async function createPipeline(\n docService?: DocumentManagementService,\n options: PipelineOptions = {},\n ): Promise<IPipeline> {\n const {\n recoverJobs = false, // Default to false for safety\n serverUrl,\n concurrency = DEFAULT_MAX_CONCURRENCY,\n } = options;\n\n logger.debug(\n `Creating pipeline: recoverJobs=${recoverJobs}, serverUrl=${serverUrl || \"none\"}, concurrency=${concurrency}`,\n );\n\n if (serverUrl) {\n // External pipeline requested\n logger.debug(`Creating PipelineClient for external worker at: ${serverUrl}`);\n return new PipelineClient(serverUrl);\n }\n\n // Local embedded pipeline with specified behavior\n return new PipelineManager(docService as DocumentManagementService, concurrency, {\n recoverJobs,\n });\n }\n}\n","/**\n * Shared embedding model configuration service.\n * Provides synchronous parsing of embedding model configuration and known dimensions lookup.\n * Eliminates code duplication between DocumentStore and telemetry systems.\n *\n * All model lookups are case-insensitive to handle variations in model name capitalization.\n * Uses class-based approach to avoid mutable global state and improve testability.\n */\n\n/**\n * Supported embedding model providers.\n */\nexport type EmbeddingProvider =\n | \"openai\"\n | \"vertex\"\n | \"gemini\"\n | \"aws\"\n | \"microsoft\"\n | \"sagemaker\";\n\n/**\n * Embedding model configuration parsed from environment variables.\n */\nexport interface EmbeddingModelConfig {\n /** The provider (e.g., \"openai\", \"gemini\") */\n provider: EmbeddingProvider;\n /** The model name (e.g., \"text-embedding-3-small\") */\n model: string;\n /** Known dimensions for this model, or null if unknown */\n dimensions: number | null;\n /** The full model specification string (e.g., \"openai:text-embedding-3-small\") */\n modelSpec: string;\n}\n\n/**\n * Embedding configuration manager that handles model parsing and dimension caching.\n * Encapsulates state to avoid global variable issues and improve testability.\n */\nexport class EmbeddingConfig {\n private static instance: EmbeddingConfig | null = null;\n\n /**\n * Get the singleton instance of EmbeddingConfig.\n * Creates the instance if it doesn't exist.\n */\n static getInstance(): EmbeddingConfig {\n if (EmbeddingConfig.instance === null) {\n EmbeddingConfig.instance = new EmbeddingConfig();\n }\n return EmbeddingConfig.instance;\n }\n\n /**\n * Reset the singleton instance (useful for testing).\n */\n static resetInstance(): void {\n EmbeddingConfig.instance = null;\n }\n /**\n * Known dimensions for common embedding models.\n * This avoids expensive API calls for dimension detection in telemetry.\n *\n * Note: The \"openai\" provider also supports OpenAI-compatible APIs like:\n * - Ollama (local models)\n * - LMStudio (local models)\n * - Any service implementing OpenAI's embedding API\n */\n private readonly knownModelDimensions: Record<string, number> = {\n // OpenAI models (also works with Ollama, LMStudio, and other OpenAI-compatible APIs)\n \"text-embedding-3-small\": 1536,\n \"text-embedding-3-large\": 3072,\n \"text-embedding-ada-002\": 1536,\n\n // Google Vertex AI models\n \"text-embedding-004\": 768,\n \"textembedding-gecko@003\": 768,\n \"textembedding-gecko@002\": 768,\n \"textembedding-gecko@001\": 768,\n\n // Google Gemini models (with MRL support)\n \"text-embedding-preview-0409\": 768,\n \"embedding-001\": 768,\n\n // AWS Bedrock models\n // Amazon Titan models\n \"amazon.titan-embed-text-v1\": 1536,\n \"amazon.titan-embed-text-v2:0\": 1024,\n \"amazon.titan-embed-image-v1\": 1024, // Image embedding model\n\n // Cohere models\n \"cohere.embed-english-v3\": 1024,\n \"cohere.embed-multilingual-v3\": 1024,\n\n // SageMaker models (hosted on AWS SageMaker)\n \"intfloat/multilingual-e5-large\": 1024,\n\n // Additional AWS models that might be supported\n // Note: Some of these might be placeholders - verify dimensions before use\n // \"amazon.nova-embed-multilingual-v1:0\": 4096, // Commented out as noted in source\n\n // MTEB Leaderboard models (source: https://huggingface.co/spaces/mteb/leaderboard)\n // Top performing models from Massive Text Embedding Benchmark\n \"sentence-transformers/all-MiniLM-L6-v2\": 384,\n \"gemini-embedding-001\": 3072,\n \"Qwen/Qwen3-Embedding-8B\": 4096,\n \"Qwen/Qwen3-Embedding-4B\": 2560,\n \"Qwen/Qwen3-Embedding-0.6B\": 1024,\n \"Linq-AI-Research/Linq-Embed-Mistral\": 4096,\n \"Alibaba-NLP/gte-Qwen2-7B-instruct\": 3584,\n \"intfloat/multilingual-e5-large-instruct\": 1024,\n \"Salesforce/SFR-Embedding-Mistral\": 4096,\n \"text-multilingual-embedding-002\": 768,\n \"GritLM/GritLM-7B\": 4096,\n \"GritLM/GritLM-8x7B\": 4096,\n \"intfloat/e5-mistral-7b-instruct\": 4096,\n \"Cohere/Cohere-embed-multilingual-v3.0\": 1024,\n \"Alibaba-NLP/gte-Qwen2-1.5B-instruct\": 8960,\n \"Lajavaness/bilingual-embedding-large\": 1024,\n \"Salesforce/SFR-Embedding-2_R\": 4096,\n \"NovaSearch/stella_en_1.5B_v5\": 8960,\n \"NovaSearch/jasper_en_vision_language_v1\": 8960,\n \"nvidia/NV-Embed-v2\": 4096,\n \"OrdalieTech/Solon-embeddings-large-0.1\": 1024,\n \"BAAI/bge-m3\": 1024,\n \"HIT-TMG/KaLM-embedding-multilingual-mini-v1\": 896,\n \"jinaai/jina-embeddings-v3\": 1024,\n \"Alibaba-NLP/gte-multilingual-base\": 768,\n \"Lajavaness/bilingual-embedding-base\": 768,\n \"HIT-TMG/KaLM-embedding-multilingual-mini-instruct-v1\": 896,\n \"nvidia/NV-Embed-v1\": 4096,\n \"Cohere/Cohere-embed-multilingual-light-v3.0\": 384,\n \"manu/bge-m3-custom-fr\": 1024,\n \"Lajavaness/bilingual-embedding-small\": 384,\n \"Snowflake/snowflake-arctic-embed-l-v2.0\": 1024,\n \"intfloat/multilingual-e5-base\": 768,\n \"voyage-3-lite\": 512,\n \"voyage-3\": 1024,\n \"intfloat/multilingual-e5-small\": 384,\n \"Alibaba-NLP/gte-Qwen1.5-7B-instruct\": 4096,\n \"Snowflake/snowflake-arctic-embed-m-v2.0\": 768,\n \"deepvk/USER-bge-m3\": 1024,\n \"Cohere/Cohere-embed-english-v3.0\": 1024,\n \"Omartificial-Intelligence-Space/Arabic-labse-Matryoshka\": 768,\n \"ibm-granite/granite-embedding-278m-multilingual\": 768,\n \"NovaSearch/stella_en_400M_v5\": 4096,\n \"omarelshehy/arabic-english-sts-matryoshka\": 1024,\n \"sentence-transformers/paraphrase-multilingual-mpnet-base-v2\": 768,\n \"Omartificial-Intelligence-Space/Arabic-all-nli-triplet-Matryoshka\": 768,\n \"Haon-Chen/speed-embedding-7b-instruct\": 4096,\n \"sentence-transformers/LaBSE\": 768,\n \"WhereIsAI/UAE-Large-V1\": 1024,\n \"ibm-granite/granite-embedding-107m-multilingual\": 384,\n \"mixedbread-ai/mxbai-embed-large-v1\": 1024,\n \"intfloat/e5-large-v2\": 1024,\n \"avsolatorio/GIST-large-Embedding-v0\": 1024,\n \"sdadas/mmlw-e5-large\": 1024,\n \"nomic-ai/nomic-embed-text-v1\": 768,\n \"nomic-ai/nomic-embed-text-v1-ablated\": 768,\n \"intfloat/e5-base-v2\": 768,\n \"BAAI/bge-large-en-v1.5\": 1024,\n \"intfloat/e5-large\": 1024,\n \"Omartificial-Intelligence-Space/Arabic-MiniLM-L12-v2-all-nli-triplet\": 384,\n \"Cohere/Cohere-embed-english-light-v3.0\": 384,\n \"sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2\": 768,\n \"Gameselo/STS-multilingual-mpnet-base-v2\": 768,\n \"thenlper/gte-large\": 1024,\n \"avsolatorio/GIST-Embedding-v0\": 768,\n \"nomic-ai/nomic-embed-text-v1-unsupervised\": 768,\n \"infgrad/stella-base-en-v2\": 768,\n \"avsolatorio/NoInstruct-small-Embedding-v0\": 384,\n \"dwzhu/e5-base-4k\": 768,\n \"sdadas/mmlw-e5-base\": 768,\n \"voyage-multilingual-2\": 1024,\n \"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-supervised\": 4096,\n \"BAAI/bge-base-en-v1.5\": 768,\n \"avsolatorio/GIST-small-Embedding-v0\": 384,\n \"sdadas/mmlw-roberta-large\": 1024,\n \"nomic-ai/nomic-embed-text-v1.5\": 768,\n \"minishlab/potion-multilingual-128M\": 256,\n \"shibing624/text2vec-base-multilingual\": 384,\n \"thenlper/gte-base\": 768,\n \"intfloat/e5-small-v2\": 384,\n \"intfloat/e5-base\": 768,\n \"sentence-transformers/static-similarity-mrl-multilingual-v1\": 1024,\n \"manu/sentence_croissant_alpha_v0.3\": 2048,\n \"BAAI/bge-small-en-v1.5\": 512,\n \"thenlper/gte-small\": 384,\n \"sdadas/mmlw-e5-small\": 384,\n \"manu/sentence_croissant_alpha_v0.4\": 2048,\n \"manu/sentence_croissant_alpha_v0.2\": 2048,\n \"abhinand/MedEmbed-small-v0.1\": 384,\n \"ibm-granite/granite-embedding-125m-english\": 768,\n \"intfloat/e5-small\": 384,\n \"voyage-large-2-instruct\": 1024,\n \"sdadas/mmlw-roberta-base\": 768,\n \"Snowflake/snowflake-arctic-embed-l\": 1024,\n \"Mihaiii/Ivysaur\": 384,\n \"Snowflake/snowflake-arctic-embed-m-long\": 768,\n \"bigscience/sgpt-bloom-7b1-msmarco\": 4096,\n \"avsolatorio/GIST-all-MiniLM-L6-v2\": 384,\n \"sergeyzh/LaBSE-ru-turbo\": 768,\n \"sentence-transformers/all-mpnet-base-v2\": 768,\n \"Snowflake/snowflake-arctic-embed-m\": 768,\n \"Snowflake/snowflake-arctic-embed-s\": 384,\n \"sentence-transformers/all-MiniLM-L12-v2\": 384,\n \"Mihaiii/gte-micro-v4\": 384,\n \"Snowflake/snowflake-arctic-embed-m-v1.5\": 768,\n \"cointegrated/LaBSE-en-ru\": 768,\n \"Mihaiii/Bulbasaur\": 384,\n \"ibm-granite/granite-embedding-30m-english\": 384,\n \"deepfile/embedder-100p\": 768,\n \"Jaume/gemma-2b-embeddings\": 2048,\n \"OrlikB/KartonBERT-USE-base-v1\": 768,\n \"izhx/udever-bloom-7b1\": 4096,\n \"izhx/udever-bloom-1b1\": 1024,\n \"brahmairesearch/slx-v0.1\": 384,\n \"Mihaiii/Wartortle\": 384,\n \"izhx/udever-bloom-3b\": 2048,\n \"deepvk/USER-base\": 768,\n \"ai-forever/ru-en-RoSBERTa\": 1024,\n \"McGill-NLP/LLM2Vec-Mistral-7B-Instruct-v2-mntp-unsup-simcse\": 4096,\n \"Mihaiii/Venusaur\": 384,\n \"Snowflake/snowflake-arctic-embed-xs\": 384,\n \"jinaai/jina-embedding-b-en-v1\": 768,\n \"Mihaiii/gte-micro\": 384,\n \"aari1995/German_Semantic_STS_V2\": 1024,\n \"Mihaiii/Squirtle\": 384,\n \"OrlikB/st-polish-kartonberta-base-alpha-v1\": 768,\n \"sergeyzh/rubert-tiny-turbo\": 312,\n \"minishlab/potion-base-8M\": 256,\n \"minishlab/M2V_base_glove_subword\": 256,\n \"jinaai/jina-embedding-s-en-v1\": 512,\n \"minishlab/potion-base-4M\": 128,\n \"minishlab/M2V_base_output\": 256,\n \"DeepPavlov/rubert-base-cased-sentence\": 768,\n \"jinaai/jina-embeddings-v2-small-en\": 512,\n \"cointegrated/rubert-tiny2\": 312,\n \"minishlab/M2V_base_glove\": 256,\n \"cointegrated/rubert-tiny\": 312,\n \"silma-ai/silma-embeddding-matryoshka-v0.1\": 768,\n \"DeepPavlov/rubert-base-cased\": 768,\n \"Omartificial-Intelligence-Space/Arabic-mpnet-base-all-nli-triplet\": 768,\n \"izhx/udever-bloom-560m\": 1024,\n \"minishlab/potion-base-2M\": 64,\n \"DeepPavlov/distilrubert-small-cased-conversational\": 768,\n \"consciousAI/cai-lunaris-text-embeddings\": 1024,\n \"deepvk/deberta-v1-base\": 768,\n \"Omartificial-Intelligence-Space/Arabert-all-nli-triplet-Matryoshka\": 768,\n \"Omartificial-Intelligence-Space/Marbert-all-nli-triplet-Matryoshka\": 768,\n \"ai-forever/sbert_large_mt_nlu_ru\": 1024,\n \"ai-forever/sbert_large_nlu_ru\": 1024,\n \"malenia1/ternary-weight-embedding\": 1024,\n \"jinaai/jina-embeddings-v2-base-en\": 768,\n \"VPLabs/SearchMap_Preview\": 4096,\n \"Hum-Works/lodestone-base-4096-v1\": 768,\n \"jinaai/jina-embeddings-v4\": 2048,\n };\n\n /**\n * Lowercase lookup map for case-insensitive model dimension queries.\n * Built lazily from knownModelDimensions to ensure consistency.\n */\n private modelLookup: Map<string, number>;\n\n constructor() {\n this.modelLookup = new Map();\n for (const [model, dimensions] of Object.entries(this.knownModelDimensions)) {\n this.modelLookup.set(model.toLowerCase(), dimensions);\n }\n }\n\n /**\n * Parse embedding model configuration from environment variables.\n * This is a synchronous operation that extracts provider, model, and known dimensions.\n *\n * Supports various providers:\n * - openai: OpenAI models and OpenAI-compatible APIs (Ollama, LMStudio, etc.)\n * - vertex: Google Cloud Vertex AI\n * - gemini: Google Generative AI\n * - aws: AWS Bedrock models\n * - microsoft: Azure OpenAI\n * - sagemaker: AWS SageMaker hosted models\n *\n * @param modelSpec Optional model specification, defaults to DOCS_MCP_EMBEDDING_MODEL env var\n * @returns Parsed embedding model configuration\n */\n parse(modelSpec?: string): EmbeddingModelConfig {\n const spec =\n modelSpec || process.env.DOCS_MCP_EMBEDDING_MODEL || \"text-embedding-3-small\";\n\n // Parse provider and model from string (e.g., \"gemini:embedding-001\" or just \"text-embedding-3-small\")\n // Handle models that contain colons in their names (e.g., \"aws:amazon.titan-embed-text-v2:0\")\n const colonIndex = spec.indexOf(\":\");\n let provider: EmbeddingProvider;\n let model: string;\n\n if (colonIndex === -1) {\n // No colon found, default to OpenAI\n provider = \"openai\";\n model = spec;\n } else {\n // Split only on the first colon\n provider = spec.substring(0, colonIndex) as EmbeddingProvider;\n model = spec.substring(colonIndex + 1);\n }\n\n // Look up known dimensions (case-insensitive)\n const dimensions = this.modelLookup?.get(model.toLowerCase()) || null;\n\n return {\n provider,\n model,\n dimensions,\n modelSpec: spec,\n };\n }\n\n /**\n * Get the known dimensions for a specific model.\n * Returns null if the model dimensions are not known.\n * Uses case-insensitive lookup.\n *\n * @param model The model name (e.g., \"text-embedding-3-small\")\n * @returns Known dimensions or null\n */\n getKnownDimensions(model: string): number | null {\n return this.modelLookup?.get(model.toLowerCase()) || null;\n }\n\n /**\n * Add or update known dimensions for a model.\n * This can be used to cache discovered dimensions.\n * Stores both original case and lowercase for consistent lookup.\n *\n * @param model The model name\n * @param dimensions The dimensions to cache\n */\n setKnownDimensions(model: string, dimensions: number): void {\n this.knownModelDimensions[model] = dimensions;\n\n // Update lowercase lookup map\n if (this.modelLookup) {\n this.modelLookup.set(model.toLowerCase(), dimensions);\n }\n }\n\n /**\n * Static method to parse embedding model configuration using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static parseEmbeddingConfig(modelSpec?: string): EmbeddingModelConfig {\n return EmbeddingConfig.getInstance().parse(modelSpec);\n }\n\n /**\n * Static method to get known model dimensions using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static getKnownModelDimensions(model: string): number | null {\n return EmbeddingConfig.getInstance().getKnownDimensions(model);\n }\n\n /**\n * Static method to set known model dimensions using the singleton instance.\n * This maintains backward compatibility while using the class-based approach.\n */\n static setKnownModelDimensions(model: string, dimensions: number): void {\n EmbeddingConfig.getInstance().setKnownDimensions(model, dimensions);\n }\n}\n","/**\n * Shared CLI utilities and helper functions.\n */\n\nimport { execSync } from \"node:child_process\";\nimport { existsSync } from \"node:fs\";\nimport { chromium } from \"playwright\";\nimport type { AppServerConfig } from \"../app\";\nimport type { AuthConfig } from \"../auth/types\";\nimport type { IPipeline, PipelineOptions } from \"../pipeline\";\nimport { PipelineFactory } from \"../pipeline\";\nimport type { DocumentManagementService } from \"../store\";\nimport {\n EmbeddingConfig,\n type EmbeddingModelConfig,\n} from \"../store/embeddings/EmbeddingConfig\";\nimport {\n DEFAULT_HOST,\n DEFAULT_HTTP_PORT,\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_PROTOCOL,\n DEFAULT_WEB_PORT,\n} from \"../utils/config\";\nimport { LogLevel, logger, setLogLevel } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport type { GlobalOptions } from \"./types\";\n\n/**\n * Embedding context.\n * Simplified subset of EmbeddingModelConfig for telemetry purposes.\n */\nexport interface EmbeddingContext {\n aiEmbeddingProvider: string;\n aiEmbeddingModel: string;\n aiEmbeddingDimensions: number | null;\n}\n\n/**\n * Ensures that the Playwright browsers are installed, unless a system Chromium path is set.\n */\nexport function ensurePlaywrightBrowsersInstalled(): void {\n // If PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set, skip install\n const chromiumEnvPath = process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH;\n if (chromiumEnvPath && existsSync(chromiumEnvPath)) {\n logger.debug(\n `PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH is set to '${chromiumEnvPath}', skipping Playwright browser install.`,\n );\n return;\n }\n try {\n // Dynamically require Playwright and check for Chromium browser\n // eslint-disable-next-line @typescript-eslint/no-var-requires\n const chromiumPath = chromium.executablePath();\n if (!chromiumPath || !existsSync(chromiumPath)) {\n throw new Error(\"Playwright Chromium browser not found\");\n }\n } catch (_err) {\n // Not installed or not found, attempt to install\n logger.debug(\n \"Playwright browsers not found. Installing Chromium browser for dynamic scraping (this may take a minute)...\",\n );\n try {\n logger.debug(\"Installing Playwright Chromium browser...\");\n execSync(\"npm exec -y playwright install --no-shell --with-deps chromium\", {\n stdio: \"ignore\", // Suppress output\n cwd: getProjectRoot(),\n });\n } catch (_installErr) {\n console.error(\n \"❌ Failed to install Playwright browsers automatically. Please run:\\n npx playwright install --no-shell --with-deps chromium\\nand try again.\",\n );\n process.exit(1);\n }\n }\n}\n\n/**\n * Resolves the protocol based on auto-detection or explicit specification.\n * Auto-detection uses TTY status to determine appropriate protocol.\n */\nexport function resolveProtocol(protocol: string): \"stdio\" | \"http\" {\n if (protocol === \"auto\") {\n // VS Code and CI/CD typically run without TTY\n if (!process.stdin.isTTY && !process.stdout.isTTY) {\n return \"stdio\";\n }\n return \"http\";\n }\n\n // Explicit protocol specification\n if (protocol === \"stdio\" || protocol === \"http\") {\n return protocol;\n }\n\n throw new Error(`Invalid protocol: ${protocol}. Must be 'auto', 'stdio', or 'http'`);\n}\n\n/**\n * Validates that --resume flag is only used with in-process workers.\n */\nexport function validateResumeFlag(resume: boolean, serverUrl?: string): void {\n if (resume && serverUrl) {\n throw new Error(\n \"--resume flag is incompatible with --server-url. \" +\n \"External workers handle their own job recovery.\",\n );\n }\n}\n\n/**\n * Formats output for CLI commands\n */\nexport const formatOutput = (data: unknown): string => JSON.stringify(data, null, 2);\n\n/**\n * Sets up logging based on global options\n */\nexport function setupLogging(options: GlobalOptions, protocol?: \"stdio\" | \"http\"): void {\n // Suppress logging in stdio mode (before any logger calls)\n if (protocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR);\n } else if (options.silent) {\n setLogLevel(LogLevel.ERROR);\n } else if (options.verbose) {\n setLogLevel(LogLevel.DEBUG);\n }\n}\n\n/**\n * Validates and parses port number\n */\nexport function validatePort(portString: string): number {\n const port = Number.parseInt(portString, 10);\n if (Number.isNaN(port) || port < 1 || port > 65535) {\n throw new Error(\"❌ Invalid port number\");\n }\n return port;\n}\n\n/**\n * Validates host string for basic format checking\n */\nexport function validateHost(hostString: string): string {\n // Basic validation - allow IPv4, IPv6, and hostnames\n const trimmed = hostString.trim();\n if (!trimmed) {\n throw new Error(\"❌ Host cannot be empty\");\n }\n\n // Very basic format check - reject obviously invalid values\n if (trimmed.includes(\" \") || trimmed.includes(\"\\t\") || trimmed.includes(\"\\n\")) {\n throw new Error(\"❌ Host cannot contain whitespace\");\n }\n\n return trimmed;\n}\n\n/**\n * Creates a pipeline (local or client) and attaches default CLI callbacks.\n * This makes the side-effects explicit and keeps creation consistent.\n */\nexport async function createPipelineWithCallbacks(\n docService: DocumentManagementService | undefined,\n options: PipelineOptions = {},\n): Promise<IPipeline> {\n logger.debug(`Initializing pipeline with options: ${JSON.stringify(options)}`);\n const { serverUrl, ...rest } = options;\n const pipeline = serverUrl\n ? await PipelineFactory.createPipeline(undefined, { serverUrl, ...rest })\n : await (async () => {\n if (!docService) {\n throw new Error(\"Local pipeline requires a DocumentManagementService instance\");\n }\n return PipelineFactory.createPipeline(docService, rest);\n })();\n\n // Configure progress callbacks for real-time updates\n pipeline.setCallbacks({\n onJobProgress: async (job, progress) => {\n logger.debug(\n `Job ${job.id} progress: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n },\n onJobStatusChange: async (job) => {\n logger.debug(`Job ${job.id} status changed to: ${job.status}`);\n },\n onJobError: async (job, error, document) => {\n logger.warn(\n `⚠️ Job ${job.id} error ${document ? `on document ${document.metadata.url}` : \"\"}: ${error.message}`,\n );\n },\n });\n\n return pipeline;\n}\n\n/**\n * Creates AppServerConfig based on service requirements\n */\nexport function createAppServerConfig(options: {\n enableWebInterface?: boolean;\n enableMcpServer?: boolean;\n enableApiServer?: boolean;\n enableWorker?: boolean;\n port: number;\n host: string;\n externalWorkerUrl?: string;\n readOnly?: boolean;\n auth?: AuthConfig;\n startupContext?: {\n cliCommand?: string;\n mcpProtocol?: \"stdio\" | \"http\";\n mcpTransport?: \"sse\" | \"streamable\";\n };\n}): AppServerConfig {\n return {\n enableWebInterface: options.enableWebInterface ?? false,\n enableMcpServer: options.enableMcpServer ?? true,\n enableApiServer: options.enableApiServer ?? false,\n enableWorker: options.enableWorker ?? true,\n port: options.port,\n host: options.host,\n externalWorkerUrl: options.externalWorkerUrl,\n readOnly: options.readOnly ?? false,\n auth: options.auth,\n startupContext: options.startupContext,\n };\n}\n\n/**\n * Parses custom headers from CLI options\n */\nexport function parseHeaders(headerOptions: string[]): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (Array.isArray(headerOptions)) {\n for (const entry of headerOptions) {\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 }\n\n return headers;\n}\n\n/**\n * Default configuration values\n */\nexport const CLI_DEFAULTS = {\n PROTOCOL: DEFAULT_PROTOCOL,\n HTTP_PORT: DEFAULT_HTTP_PORT,\n WEB_PORT: DEFAULT_WEB_PORT,\n HOST: DEFAULT_HOST,\n MAX_CONCURRENCY: DEFAULT_MAX_CONCURRENCY,\n TELEMETRY: true,\n} as const;\n\n/**\n * Parses auth configuration from CLI options and environment variables.\n * Precedence: CLI flags > env vars > defaults\n */\nexport function parseAuthConfig(options: {\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n}): AuthConfig | undefined {\n // Check CLI flags first, then env vars, then defaults\n const enabled =\n options.authEnabled ??\n (process.env.DOCS_MCP_AUTH_ENABLED?.toLowerCase() === \"true\" || false);\n\n if (!enabled) {\n return undefined;\n }\n\n const issuerUrl = options.authIssuerUrl ?? process.env.DOCS_MCP_AUTH_ISSUER_URL;\n\n const audience = options.authAudience ?? process.env.DOCS_MCP_AUTH_AUDIENCE;\n\n return {\n enabled,\n issuerUrl,\n audience,\n scopes: [\"openid\", \"profile\"], // Default scopes for OAuth2/OIDC\n };\n}\n\n/**\n * Validates auth configuration when auth is enabled.\n */\nexport function validateAuthConfig(authConfig: AuthConfig): void {\n if (!authConfig.enabled) {\n return;\n }\n\n const errors: string[] = [];\n\n // Issuer URL is required when auth is enabled\n if (!authConfig.issuerUrl) {\n errors.push(\"--auth-issuer-url is required when auth is enabled\");\n } else {\n try {\n const url = new URL(authConfig.issuerUrl);\n if (url.protocol !== \"https:\") {\n errors.push(\"Issuer URL must use HTTPS protocol\");\n }\n } catch {\n errors.push(\"Issuer URL must be a valid URL\");\n }\n }\n\n // Audience is required when auth is enabled\n if (!authConfig.audience) {\n errors.push(\"--auth-audience is required when auth is enabled\");\n } else {\n // Audience can be any valid URI (URL or URN)\n // Examples: https://api.example.com, urn:docs-mcp-server:api, urn:company:service\n try {\n // Try parsing as URL first (most common case)\n const url = new URL(authConfig.audience);\n if (url.protocol === \"http:\" && url.hostname !== \"localhost\") {\n // Warn about HTTP in production but don't fail\n logger.warn(\n \"⚠️ Audience uses HTTP protocol - consider using HTTPS for production\",\n );\n }\n if (url.hash) {\n errors.push(\"Audience must not contain URL fragments\");\n }\n } catch {\n // If not a valid URL, check if it's a valid URN\n if (authConfig.audience.startsWith(\"urn:\")) {\n // Basic URN validation: urn:namespace:specific-string\n const urnParts = authConfig.audience.split(\":\");\n if (urnParts.length < 3 || !urnParts[1] || !urnParts[2]) {\n errors.push(\"URN audience must follow format: urn:namespace:specific-string\");\n }\n } else {\n errors.push(\n \"Audience must be a valid absolute URL or URN (e.g., https://api.example.com or urn:company:service)\",\n );\n }\n }\n }\n\n // Scopes are not validated in binary authentication mode\n // They're handled internally by the OAuth proxy\n\n if (errors.length > 0) {\n throw new Error(`Auth configuration validation failed:\\n${errors.join(\"\\n\")}`);\n }\n}\n\n/**\n * Warns about HTTP usage in production when auth is enabled.\n */\nexport function warnHttpUsage(authConfig: AuthConfig | undefined, port: number): void {\n if (!authConfig?.enabled) {\n return;\n }\n\n // Check if we're likely running in production (not localhost)\n const isLocalhost =\n process.env.NODE_ENV !== \"production\" ||\n port === 6280 || // default dev port\n process.env.HOSTNAME?.includes(\"localhost\");\n\n if (!isLocalhost) {\n logger.warn(\n \"⚠️ Authentication is enabled but running over HTTP in production. \" +\n \"Consider using HTTPS for security.\",\n );\n }\n}\n\n/**\n * Resolves embedding configuration from environment variables and CLI args.\n * This function always attempts to resolve embedding configuration regardless of deployment mode.\n * @param cliArgs Future: CLI arguments that might override environment\n * @returns Embedding configuration or null if config is unavailable\n */\nexport function resolveEmbeddingContext(cliArgs?: {\n embeddingModel?: string;\n}): EmbeddingModelConfig | null {\n try {\n // Future: CLI args take precedence over environment\n const modelSpec = cliArgs?.embeddingModel || process.env.DOCS_MCP_EMBEDDING_MODEL;\n\n logger.debug(\"Resolving embedding configuration\");\n const config = EmbeddingConfig.parseEmbeddingConfig(modelSpec);\n\n return config;\n } catch (error) {\n logger.debug(`Failed to resolve embedding configuration: ${error}`);\n return null;\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\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 /** Indicates if the cancellation request was successfully initiated or if the job was already finished/cancelled. */\n success: boolean;\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 */\n async execute(input: CancelJobInput): Promise<CancelJobResult> {\n return analytics.trackTool(\n \"cancel_job\",\n async () => {\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 return {\n message: `Job with ID ${input.jobId} not found.`,\n success: false,\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(\n `Job ${input.jobId} is already in a final state: ${job.status}.`,\n );\n return {\n message: `Job ${input.jobId} is already ${job.status}. No action taken.`,\n success: true, // Considered success as no cancellation needed\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 success: true,\n };\n } catch (error) {\n logger.error(`❌ Error cancelling job ${input.jobId}: ${error}`);\n return {\n message: `Failed to cancel job ${input.jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n success: false,\n };\n }\n },\n (result) => {\n return {\n success: result.success,\n // Note: success flag already indicates if cancellation was successful\n };\n },\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\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 /** Indicates if the clear operation was successful. */\n success: boolean;\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 */\n async execute(_input: ClearCompletedJobsInput): Promise<ClearCompletedJobsResult> {\n return analytics.trackTool(\n \"clear_completed_jobs\",\n async () => {\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 success: true,\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 return {\n message: errorMessage,\n success: false,\n clearedCount: 0,\n };\n }\n },\n (result) => ({\n success: result.success,\n clearedCount: result.clearedCount,\n }),\n );\n }\n}\n","import semver from \"semver\";\n\n// LibraryVersionDetails removed; use minimal inline shape for available versions metadata\n\nclass 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\nclass VersionNotFoundError extends ToolError {\n constructor(\n public readonly library: string,\n public readonly requestedVersion: string,\n public readonly availableVersions: Array<{\n version: string;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>,\n ) {\n super(\n `Version ${requestedVersion} not found for ${library}. Available versions: ${availableVersions.map((v) => v.version).join(\", \")}`,\n \"SearchTool\",\n );\n }\n\n getLatestVersion() {\n return this.availableVersions.sort((a, b) => semver.compare(b.version, a.version))[0];\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 */\nclass LibraryNotFoundError extends ToolError {\n constructor(\n public readonly requestedLibrary: string,\n public readonly suggestions: string[] = [],\n ) {\n let message = `Library '${requestedLibrary}' not found.`;\n if (suggestions.length > 0) {\n message += ` Did you mean one of these: ${suggestions.join(\", \")}?`;\n }\n // Assuming this error might originate from various tools, but SearchTool is a primary candidate.\n // We might need to adjust the toolName if it's thrown elsewhere.\n super(message, \"SearchTool\");\n }\n}\n\nexport { LibraryNotFoundError, ToolError, VersionNotFoundError };\n","import type {\n ContentFetcher,\n FileFetcher,\n HttpFetcher,\n RawContent,\n} from \"../scraper/fetcher\";\nimport { HtmlPipeline } from \"../scraper/pipelines/HtmlPipeline\";\nimport { MarkdownPipeline } from \"../scraper/pipelines/MarkdownPipeline\";\nimport { TextPipeline } from \"../scraper/pipelines/TextPipeline\";\nimport type { ContentPipeline, ProcessedContent } from \"../scraper/pipelines/types\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport { convertToString } from \"../scraper/utils/buffer\";\nimport { resolveCharset } from \"../scraper/utils/charset\";\nimport { analytics } from \"../telemetry\";\nimport { ScraperError } from \"../utils/errors\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError } 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 * Collection of fetchers that will be tried in order for a given URL.\n */\n private readonly fetchers: ContentFetcher[];\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(httpFetcher: HttpFetcher, fileFetcher: FileFetcher) {\n this.fetchers = [httpFetcher, fileFetcher];\n const htmlPipeline = new HtmlPipeline();\n const markdownPipeline = new MarkdownPipeline();\n const textPipeline = new TextPipeline();\n // Order matters: more specific pipelines first, fallback (text) pipeline last\n this.pipelines = [htmlPipeline, markdownPipeline, textPipeline];\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 return analytics.trackTool(\n \"fetch_url\",\n async () => {\n const { url, scrapeMode = ScrapeMode.Auto, headers } = options;\n\n const canFetchResults = this.fetchers.map((f) => f.canFetch(url));\n const fetcherIndex = canFetchResults.indexOf(true);\n if (fetcherIndex === -1) {\n throw new ToolError(\n `Invalid URL: ${url}. Must be an HTTP/HTTPS URL or a file:// URL.`,\n this.constructor.name,\n );\n }\n\n const fetcher = this.fetchers[fetcherIndex];\n logger.debug(`Using fetcher \"${fetcher.constructor.name}\" for URL: ${url}`);\n\n try {\n logger.info(`📡 Fetching ${url}...`);\n const rawContent: RawContent = await fetcher.fetch(url, {\n followRedirects: options.followRedirects ?? true,\n maxRetries: 3,\n headers, // propagate custom headers\n });\n\n logger.info(\"🔄 Processing content...\");\n\n let processed: Awaited<ProcessedContent> | undefined;\n for (const pipeline of this.pipelines) {\n if (pipeline.canProcess(rawContent)) {\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 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 (\n typeof processed.textContent !== \"string\" ||\n !processed.textContent.trim()\n ) {\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 if (error instanceof ScraperError || error instanceof ToolError) {\n throw new ToolError(\n `Failed to fetch or process URL: ${error.message}`,\n this.constructor.name,\n );\n }\n throw new ToolError(\n `Failed to fetch or process URL: ${error instanceof Error ? error.message : String(error)}`,\n this.constructor.name,\n );\n } finally {\n // Cleanup all pipelines to prevent resource leaks (e.g., browser instances)\n await Promise.allSettled(this.pipelines.map((pipeline) => pipeline.close()));\n }\n },\n (result) => {\n const { url, scrapeMode, followRedirects, headers } = options;\n return {\n url,\n scrapeMode,\n followRedirects,\n contentLength: result.length,\n hasHeaders: !!headers,\n };\n },\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { VersionNotFoundError } from \"./errors\";\n\nexport interface FindVersionToolOptions {\n library: string;\n targetVersion?: 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 descriptive string indicating the best match and unversioned status, or an error message.\n */\n async execute(options: FindVersionToolOptions): Promise<string> {\n return analytics\n .trackTool(\n \"find_version\",\n async () => {\n const { library, targetVersion } = options;\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n\n try {\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 VersionNotFoundError below,\n // but added for completeness.\n message = `No matching version or unversioned documents found for ${libraryAndVersion}.`;\n }\n\n // Return both the message and the structured data for tracking\n return { message, bestMatch, hasUnversioned };\n } catch (error) {\n if (error instanceof VersionNotFoundError) {\n // This error is thrown when no semver versions AND no unversioned docs exist.\n logger.info(`ℹ️ Version not found: ${error.message}`);\n const message = `No matching version or unversioned documents found for ${libraryAndVersion}. Available: ${\n error.availableVersions.length > 0\n ? error.availableVersions.map((v) => v.version).join(\", \")\n : \"None\"\n }.`;\n return { message, bestMatch: null, hasUnversioned: false };\n }\n // Re-throw unexpected errors\n logger.error(\n `❌ Error finding version for ${libraryAndVersion}: ${error instanceof Error ? error.message : error}`,\n );\n throw error;\n }\n },\n (result) => {\n const { library, targetVersion } = options;\n return {\n library,\n targetVersion,\n foundMatch: !!result.bestMatch,\n hasUnversioned: result.hasUnversioned,\n };\n },\n )\n .then((result) => result.message); // Return just the message to maintain interface\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJobStatus } from \"../pipeline/types\";\nimport type { VersionStatus } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\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 | null;\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 or null if not found.\n */\n async execute(input: GetJobInfoInput): Promise<GetJobInfoToolResponse> {\n return analytics.trackTool(\n \"get_job_info\",\n async () => {\n const job = await this.pipeline.getJob(input.jobId);\n\n if (!job) {\n // Return null in the result if job not found\n return { job: null };\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 (result) => {\n return {\n found: result.job !== null,\n library: result.job?.library,\n version: result.job?.version,\n };\n },\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { PipelineJob, PipelineJobStatus } from \"../pipeline/types\";\nimport { analytics } from \"../telemetry\";\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 return analytics.trackTool(\n \"list_jobs\",\n async () => {\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:\n 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 (result) => {\n return {\n jobCount: result.jobs.length,\n statusCounts: result.jobs.reduce(\n (acc, job) => {\n acc[job.status] = (acc[job.status] || 0) + 1;\n return acc;\n },\n {} as Record<string, number>,\n ),\n };\n },\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { VersionStatus, VersionSummary } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\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 return analytics.trackTool(\n \"list_libraries\",\n async () => {\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 (result) => ({\n libraryCount: result.libraries.length,\n totalVersions: result.libraries.reduce(\n (sum, lib) => sum + lib.versions.length,\n 0,\n ),\n }),\n );\n }\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { PipelineJobStatus } from \"../pipeline/types\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { ToolError } 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 return analytics.trackTool(\n \"remove_docs\",\n async () => {\n const { library, version } = args;\n\n logger.info(`🗑️ Removing library: ${library}${version ? `@${version}` : \"\"}`);\n\n try {\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 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 const { library, version } = args;\n return {\n library,\n version,\n // Success is implicit since if this callback runs, no exception was thrown\n };\n },\n );\n }\n}\n","import * as semver from \"semver\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport { analytics } from \"../telemetry\";\nimport {\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_MAX_DEPTH,\n DEFAULT_MAX_PAGES,\n} from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\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 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\n constructor(pipeline: IPipeline) {\n this.pipeline = pipeline;\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 return analytics.trackTool(\n \"scrape_docs\",\n async () => {\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 Error(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\n );\n }\n } else {\n throw new Error(\n `Invalid version format for scraping: '${version}'. Use 'X.Y.Z', 'X.Y.Z-prerelease', 'X.Y', 'X', or omit.`,\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 =\n internalVersion === \"\" ? null : internalVersion;\n\n // Enqueue the job using the injected pipeline\n const jobId = await pipeline.enqueueJob(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 ?? DEFAULT_MAX_PAGES,\n maxDepth: scraperOptions?.maxDepth ?? DEFAULT_MAX_DEPTH,\n maxConcurrency: scraperOptions?.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY,\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 });\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 (result) => ({\n library,\n version,\n url,\n waitForCompletion,\n ...scraperOptions,\n isBackgroundJob: \"jobId\" in result,\n pagesScraped: \"pagesScraped\" in result ? result.pagesScraped : undefined,\n }),\n );\n }\n}\n","import type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport type { StoreSearchResult, VersionSummary } from \"../store/types\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { VersionNotFoundError } 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 LibraryNotFoundError\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 return analytics.trackTool(\n \"search_docs\",\n async () => {\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 detailedVersions = libraryInfo\n ? (libraryInfo.versions as VersionSummary[]).map((v) => ({\n version: v.ref.version,\n documentCount: v.counts.documents,\n uniqueUrlCount: v.counts.uniqueUrls,\n indexedAt: v.indexedAt,\n }))\n : [];\n throw new VersionNotFoundError(library, version ?? \"latest\", detailedVersions);\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 VersionNotFoundError, 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 (result) => ({\n library,\n version,\n query,\n limit,\n exactMatch,\n resultCount: result.results.length,\n }),\n );\n }\n}\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(text: string): CallToolResult {\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 { type JobInfo, LibraryNotFoundError, VersionNotFoundError } from \"../tools\";\nimport { DEFAULT_MAX_DEPTH, DEFAULT_MAX_PAGES } 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 readOnly Whether to run in read-only mode (only expose read tools).\n * @returns A configured McpServer instance.\n */\nexport function createMcpServerInstance(\n tools: McpServerTools,\n readOnly = false,\n): McpServer {\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 // @ts-expect-error TypeScript has issues with deep Zod inference in MCP SDK\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().describe(\"Library name.\"),\n version: z.string().optional().describe(\"Library version (optional).\"),\n maxPages: z\n .number()\n .optional()\n .default(DEFAULT_MAX_PAGES)\n .describe(`Maximum number of pages to scrape (default: ${DEFAULT_MAX_PAGES}).`),\n maxDepth: z\n .number()\n .optional()\n .default(DEFAULT_MAX_DEPTH)\n .describe(`Maximum navigation depth (default: ${DEFAULT_MAX_DEPTH}).`),\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 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(\n `Failed to scrape documentation: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n version: z\n .string()\n .optional()\n .describe(\"Library version (exact or X-Range, optional).\"),\n query: z.string().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 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 if (error instanceof LibraryNotFoundError) {\n return createResponse(\n [\n `Library \"${library}\" not found.`,\n error.suggestions?.length\n ? `Did you mean: ${error.suggestions?.join(\", \")}?`\n : undefined,\n ].join(\" \"),\n );\n }\n if (error instanceof VersionNotFoundError) {\n const indexedVersions = error.availableVersions.map((v) => v.version);\n return createResponse(\n [\n `Version \"${version}\" not found.`,\n indexedVersions.length > 0\n ? `Available indexed versions for ${library}: ${indexedVersions.join(\", \")}`\n : undefined,\n ].join(\" \"),\n );\n }\n return createError(\n `Failed to search documentation: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 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(\n `Failed to list libraries: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n targetVersion: z\n .string()\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 try {\n const message = await tools.findVersion.execute({\n library,\n targetVersion,\n });\n\n if (!message) {\n return createError(\"No matching version found\");\n }\n\n return createResponse(message);\n } catch (error) {\n return createError(\n `Failed to find version: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 // @ts-expect-error TypeScript has issues with deep Zod inference in MCP SDK\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 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(\n `Failed to list jobs: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.getJobInfo.execute({ jobId });\n if (!result.job) {\n return createError(`Job with ID ${jobId} not found.`);\n }\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 return createError(\n `Failed to get job info for ${jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.cancelJob.execute({ jobId });\n // Use the message and success status from the tool's result\n if (result.success) {\n return createResponse(result.message);\n }\n // If not successful according to the tool, treat it as an error in MCP\n return createError(result.message);\n } catch (error) {\n // Catch any unexpected errors during the tool execution itself\n return createError(\n `Failed to cancel job ${jobId}: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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().describe(\"Library name.\"),\n version: z\n .string()\n .optional()\n .describe(\"Library version (optional, removes unversioned if omitted).\"),\n },\n {\n title: \"Remove Library Documentation\",\n destructiveHint: true,\n },\n async ({ library, version }) => {\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(\n `Failed to remove documents: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\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 try {\n const result = await tools.fetchUrl.execute({ url, followRedirects });\n return createResponse(result);\n } catch (error) {\n return createError(\n `Failed to fetch URL: ${error instanceof Error ? error.message : String(error)}`,\n );\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 // Fetch the simplified job info using GetJobInfoTool\n const result = await tools.getJobInfo.execute({ jobId });\n\n // result.job is either the simplified job object or null\n if (!result.job) {\n // Job not found, return empty content\n return { contents: [] };\n }\n\n // Job found, return its simplified details as JSON\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 },\n );\n }\n\n return server;\n}\n","import type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { FileFetcher, HttpFetcher } from \"../scraper/fetcher\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport {\n CancelJobTool,\n FetchUrlTool,\n FindVersionTool,\n GetJobInfoTool,\n ListJobsTool,\n ListLibrariesTool,\n RemoveTool,\n ScrapeTool,\n SearchTool,\n} from \"../tools\";\n\n/**\n * Interface for the shared tool instances.\n */\nexport interface McpServerTools {\n listLibraries: ListLibrariesTool;\n findVersion: FindVersionTool;\n scrape: ScrapeTool;\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 * @returns An object containing all instantiated tool instances.\n */\nexport async function initializeTools(\n docService: IDocumentManagement,\n pipeline: IPipeline,\n): Promise<McpServerTools> {\n const tools: McpServerTools = {\n listLibraries: new ListLibrariesTool(docService),\n findVersion: new FindVersionTool(docService),\n scrape: new ScrapeTool(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 HttpFetcher(), new FileFetcher()),\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 { analytics } from \"../telemetry\";\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 readOnly Whether to run in read-only mode\n * @returns The McpServer instance for cleanup\n */\nexport async function registerMcpService(\n server: FastifyInstance,\n docService: IDocumentManagement,\n pipeline: IPipeline,\n readOnly = false,\n authManager?: ProxyAuthManager,\n): Promise<McpServer> {\n // Initialize MCP server and tools\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = createMcpServerInstance(mcpTools, readOnly);\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 // 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 // Log client connection (simple connection tracking without sessions)\n if (analytics.isEnabled()) {\n logger.info(`🔗 MCP client connected: ${transport.sessionId}`);\n }\n\n reply.raw.on(\"close\", () => {\n delete sseTransports[transport.sessionId];\n transport.close();\n\n // Log client disconnection\n if (analytics.isEnabled()) {\n logger.info(`🔗 MCP client disconnected: ${transport.sessionId}`);\n }\n });\n\n await mcpServer.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, readOnly);\n const requestTransport = new StreamableHTTPServerTransport({\n sessionIdGenerator: undefined,\n });\n\n reply.raw.on(\"close\", () => {\n logger.debug(\"Streamable HTTP request closed\");\n requestTransport.close();\n requestServer.close(); // Close the per-request server instance\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 // Store reference to SSE transports on the server instance for cleanup\n (\n mcpServer as unknown as {\n _sseTransports: Record<string, SSEServerTransport>;\n }\n )._sseTransports = sseTransports;\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 // 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 // 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 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 { 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\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 enqueueInput = z.object({\n library: nonEmptyTrimmed,\n version: optionalTrimmed,\n options: z.custom<ScraperOptions>(),\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 enqueueJob: tt.procedure\n .input(enqueueInput)\n .mutation(\n async ({\n ctx,\n input,\n }: {\n ctx: PipelineTrpcContext;\n input: z.infer<typeof enqueueInput>;\n }) => {\n const jobId = await ctx.pipeline.enqueueJob(\n input.library,\n input.version ?? null,\n input.options,\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 { 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\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 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 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 and data store routers under a single endpoint.\n */\n\nimport { initTRPC } from \"@trpc/server\";\nimport { fastifyTRPCPlugin } from \"@trpc/server/adapters/fastify\";\nimport type { FastifyInstance } from \"fastify\";\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;\n\nexport async function registerTrpcService(\n server: FastifyInstance,\n pipeline: IPipeline,\n docService: IDocumentManagement,\n): Promise<void> {\n const t = initTRPC.context<UnifiedContext>().create();\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.mergeRouters(\n healthRouter,\n createPipelineRouter(t),\n createDataRouter(t),\n );\n\n await server.register(fastifyTRPCPlugin, {\n prefix: \"/api\",\n trpcOptions: {\n router,\n createContext: async (): Promise<UnifiedContext> => ({ pipeline, docService }),\n },\n });\n}\n","import type { PropsWithChildren } from \"@kitajs/html\";\nimport { readFileSync } from \"node:fs\";\nimport { logger } from \"../../utils/logger\";\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}\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, and children.\n */\nconst Layout = ({ title, version, children }: LayoutProps) => {\n let versionString = version;\n if (!versionString) {\n // If no version is provided, use the version from package.json\n // We cannot bake the version into the bundle, as the package.json will\n // be updated by the build process, after the bundle is created.\n try {\n const packageJson = JSON.parse(readFileSync(\"package.json\", \"utf-8\")) as {\n version: string;\n };\n versionString = packageJson.version;\n } catch (error) {\n logger.error(`Error reading package.json: ${error}`);\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 {/* 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\">\n <div class=\"container max-w-2xl mx-auto px-4 py-4\">\n <header class=\"mb-4\">\n <h1 class=\"text-3xl font-bold text-gray-900 dark:text-white\">\n <a href=\"/\">MCP Docs</a>\n {versionString ? (\n <span\n safe\n class=\"ml-2 text-base font-normal text-gray-500 dark:text-gray-400 align-baseline\"\n title={`Version ${versionString}`}\n >\n v{versionString}\n </span>\n ) : null}\n </h1>\n </header>\n\n <main>{children}</main>\n </div>\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 Layout from \"../components/Layout\"; // Import the Layout component\n\n/**\n * Registers the root route that serves the main HTML page.\n * @param server - The Fastify instance.\n */\nexport function registerIndexRoute(server: FastifyInstance) {\n server.get(\"/\", async (_, reply) => {\n reply.type(\"text/html\");\n // Use the Layout component and define the main content within it\n return (\n \"<!DOCTYPE html>\" +\n (\n <Layout title=\"MCP Docs\">\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 type=\"button\"\n class=\"text-xs px-3 py-1.5 text-gray-700 bg-gray-100 border border-gray-300 rounded-lg hover:bg-gray-200 focus:ring-4 focus:outline-none 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 transition-colors duration-150\"\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 >\n Clear Completed Jobs\n </button>\n </div>\n {/* Container for the job list, loaded via HTMX */}\n <div id=\"job-queue\" hx-get=\"/web/jobs\" hx-trigger=\"load, every 1s\">\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 {/* Container for the add job form, loaded via HTMX */}\n <div id=\"addJobForm\" hx-get=\"/web/jobs/new\" hx-trigger=\"load\">\n {/* Initial loading state (optional, could just be empty) */}\n <div class=\"p-6 bg-white rounded-lg shadow dark:bg-gray-800 animate-pulse\">\n <div class=\"h-6 bg-gray-200 rounded-full dark:bg-gray-700 w-1/3 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 {/* 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, every 10s\"\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\";\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 const result = await cancelJobTool.execute({ jobId });\n if (result.success) {\n return { success: true, message: result.message };\n } else {\n reply.status(400);\n return { success: false, message: result.message };\n }\n }\n );\n}\n","import type { FastifyInstance } from \"fastify\";\nimport type { ClearCompletedJobsTool } from \"../../../tools/ClearCompletedJobsTool\";\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 const result = await clearCompletedJobsTool.execute({});\n\n reply.type(\"application/json\");\n return {\n success: result.success,\n message: result.message,\n };\n } catch (error) {\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","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-purple-100 text-purple-800 text-xs font-medium me-2 px-1.5 py-0.5 rounded dark:bg-purple-900 dark:text-purple-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","/**\n * Renders an SVG loading spinner icon.\n * Used for indicating loading states in buttons or other elements.\n */\nconst LoadingSpinner = () => (\n <svg\n class=\"animate-spin h-4 w-4 text-white\" // Adjusted size to h-4 w-4 to match usage\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 return (\n <div class=\"block p-3 bg-gray-50 dark:bg-gray-700 rounded-lg border border-gray-200 dark:border-gray-600\">\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 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 title=\"Stop this job\"\n x-data=\"{}\"\n x-on:click={`\n if ($store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}') {\n $store.confirmingAction.isStopping = true;\n fetch('/web/jobs/' + '${job.id}' + '/cancel', {\n method: 'POST',\n headers: { 'Accept': 'application/json' },\n })\n .then(r => r.json())\n .then(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isStopping = false;\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n document.dispatchEvent(new CustomEvent('job-list-refresh'));\n })\n .catch(() => { $store.confirmingAction.isStopping = false; });\n } else {\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n $store.confirmingAction.type = 'job-cancel';\n $store.confirmingAction.id = '${job.id}';\n $store.confirmingAction.isStopping = false;\n $store.confirmingAction.timeoutId = setTimeout(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isStopping = false;\n $store.confirmingAction.timeoutId = null;\n }, 3000);\n }\n `}\n x-bind:disabled={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && $store.confirmingAction.isStopping`}\n >\n <span\n x-show={`$store.confirmingAction.type !== 'job-cancel' || $store.confirmingAction.id !== '${job.id}' || $store.confirmingAction.isStopping`}\n >\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\n x-show={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && !$store.confirmingAction.isStopping`}\n class=\"px-2\"\n >\n Cancel?\n </span>\n <span\n x-show={`$store.confirmingAction.type === 'job-cancel' && $store.confirmingAction.id === '${job.id}' && $store.confirmingAction.isStopping`}\n >\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\"; // Adjusted import path\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 * Adds a listener for the 'job-list-refresh' event to trigger a reload of the job list using HTMX.\n * @param props - Component props including the array of jobs.\n */\nconst JobList = ({ jobs }: JobListProps) => (\n <div id=\"job-list\" class=\"space-y-2\">\n {jobs.length === 0 ? (\n <p class=\"text-center text-gray-500 dark:text-gray-400\">\n No pending jobs.\n </p>\n ) : (\n jobs.map((job) => <JobItem job={job} />)\n )}\n {/* NOTE: To enable live job list refresh after stopping a job, add the following script to your main HTML layout or main.client.ts:\n document.addEventListener('job-list-refresh', function () {\n if (window.htmx) {\n window.htmx.ajax('GET', '/web/jobs', '#job-list');\n } else {\n window.location.reload();\n }\n });\n */}\n </div>\n);\n\nexport default JobList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListJobsTool } from \"../../../tools/ListJobsTool\"; // Adjusted import path\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 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=\"flex-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=\"flex-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=\"flex-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=\"flex-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 { ScrapeMode } from \"../../scraper/types\"; // Adjusted import path\nimport Alert from \"./Alert\";\nimport Tooltip from \"./Tooltip\";\n\ninterface ScrapeFormContentProps {\n defaultExcludePatterns?: string[];\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 */\nconst ScrapeFormContent = ({\n defaultExcludePatterns,\n}: ScrapeFormContentProps) => {\n // Format default patterns for display in textarea (one per line)\n const defaultExcludePatternsText = defaultExcludePatterns?.join(\"\\n\") || \"\";\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\">\n <h3 class=\"text-xl font-semibold text-gray-900 dark:text-white mb-2\">\n Queue New Scrape Job\n </h3>\n <form\n hx-post=\"/web/jobs/scrape\"\n hx-target=\"#job-response\"\n hx-swap=\"innerHTML\"\n class=\"space-y-2\"\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 >\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 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-indigo-500 focus:border-indigo-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 <input\n type=\"text\"\n name=\"library\"\n id=\"library\"\n required\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-indigo-500 focus:border-indigo-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=\"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. This allows for version-specific searches.\" />\n </div>\n <input\n type=\"text\"\n name=\"version\"\n id=\"version\"\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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n />\n </div>\n\n {/* Consider using Flowbite Accordion here */}\n <details class=\"bg-gray-50 dark:bg-gray-900 p-2 rounded-md\">\n <summary class=\"cursor-pointer text-sm font-medium text-gray-600 dark:text-gray-400\">\n Advanced Options\n </summary>\n <div class=\"mt-2 space-y-2\" x-data=\"{ headers: [] }\">\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 text=\"The maximum number of pages to scrape. Default is 1000. Setting this too high may result in longer processing times.\" />\n </div>\n <input\n type=\"number\"\n name=\"maxPages\"\n id=\"maxPages\"\n min=\"1\"\n placeholder=\"1000\"\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-indigo-500 focus:border-indigo-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 text=\"How many links deep the scraper should follow. Default is 3. Higher values capture more content but increase processing time.\" />\n </div>\n <input\n type=\"number\"\n name=\"maxDepth\"\n id=\"maxDepth\"\n min=\"0\"\n placeholder=\"3\"\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-indigo-500 focus:border-indigo-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-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option value=\"subpages\" selected>\n Subpages (Default)\n </option>\n <option value=\"hostname\">Hostname</option>\n <option value=\"domain\">Domain</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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\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-indigo-500 focus:border-indigo-500 sm:text-sm bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono text-xs\"\n >\n {defaultExcludePatternsText}\n </textarea>\n <p class=\"mt-1 text-xs text-gray-500 dark:text-gray-400\">\n Default patterns are pre-filled. Edit to customize or clear to\n 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-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white\"\n >\n <option value={ScrapeMode.Auto} selected>\n Auto (Default)\n </option>\n <option value={ScrapeMode.Fetch}>Fetch</option>\n <option value={ScrapeMode.Playwright}>Playwright</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-indigo-100 dark:bg-indigo-900 text-indigo-700 dark:text-indigo-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\n class=\"h-4 w-4 text-indigo-600 focus:ring-indigo-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\n class=\"h-4 w-4 text-indigo-600 focus:ring-indigo-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 </details>\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-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500\"\n >\n Queue Job\n </button>\n </div>\n </form>\n {/* Target div for HTMX response */}\n <div id=\"job-response\" class=\"mt-2 text-sm\"></div>\n </div>\n );\n};\n\nexport default ScrapeFormContent;\n","import ScrapeFormContent from \"./ScrapeFormContent\"; // Adjusted import path\n\ninterface ScrapeFormProps {\n defaultExcludePatterns?: string[];\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 = ({ defaultExcludePatterns }: ScrapeFormProps) => (\n <div id=\"scrape-form-container\">\n <ScrapeFormContent defaultExcludePatterns={defaultExcludePatterns} />\n </div>\n);\n\nexport default ScrapeForm;\n","import type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport type { ScrapeTool } from \"../../../tools/ScrapeTool\"; // Adjusted import path\nimport { ScrapeMode } from \"../../../scraper/types\"; // Adjusted import path\nimport { logger } from \"../../../utils/logger\"; // Adjusted import path\nimport ScrapeForm from \"../../components/ScrapeForm\"; // Import extracted component\nimport Alert from \"../../components/Alert\"; // Import Alert component\nimport ScrapeFormContent from \"../../components/ScrapeFormContent\"; // Import for OOB swap\nimport { DEFAULT_EXCLUSION_PATTERNS } from \"../../../scraper/utils/defaultPatterns\"; // Import default patterns\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) {\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 <ScrapeForm defaultExcludePatterns={DEFAULT_EXCLUSION_PATTERNS} />;\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 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) return undefined;\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 // Prepare options for ScrapeTool\n const scrapeOptions = {\n url: body.url,\n library: body.library,\n version: body.version || null, // Handle empty string as null\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: Use Alert component and OOB swap\n return (\n <>\n {/* Main target response */}\n <Alert\n type=\"success\"\n message={\n <>\n Job queued successfully! ID:{\" \"}\n <span safe>{result.jobId}</span>\n </>\n }\n />\n {/* OOB target response - contains only the inner form content */}\n <div id=\"scrape-form-container\" hx-swap-oob=\"innerHTML\">\n <ScrapeFormContent defaultExcludePatterns={DEFAULT_EXCLUSION_PATTERNS} />\n </div>\n </>\n );\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 reply.status(500); // Keep status code for errors\n // Use Alert component for server error\n return (\n <Alert\n type=\"error\"\n message={<>Failed to queue job: {errorMessage}</>}\n />\n );\n }\n }\n );\n}\n","import type { VersionSummary } from \"../../store/types\";\nimport VersionBadge from \"./VersionBadge\"; // Adjusted import path\nimport LoadingSpinner from \"./LoadingSpinner\"; // Import spinner\n\n/**\n * Props for the VersionDetailsRow component.\n */\ninterface VersionDetailsRowProps {\n version: VersionSummary;\n libraryName: string;\n showDelete?: boolean; // Optional prop to control delete button visibility\n}\n\n/**\n * Renders details for a single library version in a row format.\n * Includes version, stats, and an optional delete button.\n * @param props - Component props including version, libraryName, and showDelete flag.\n */\nconst VersionDetailsRow = ({\n version,\n libraryName,\n showDelete = true, // Default to true\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 'Unversioned' if version string is empty\n const versionLabel = version.ref.version || \"Unversioned\";\n // Use empty string for unversioned 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 // 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 >\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>Unversioned</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 Snippets:{\" \"}\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 {/**\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=\"ml-2 font-medium rounded-lg text-sm p-1 text-center inline-flex items-center transition-colors duration-150 ease-in-out\"\n title=\"Remove this version\"\n x-data=\"{}\"\n x-bind:class={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' ? '${confirmingStateClasses}' : '${defaultStateClasses}'`}\n x-bind:disabled={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting`}\n x-on:click={`\n if ($store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}') {\n $store.confirmingAction.isDeleting = true;\n $el.dispatchEvent(new CustomEvent('confirmed-delete', { bubbles: true }));\n } else {\n if ($store.confirmingAction.timeoutId) { clearTimeout($store.confirmingAction.timeoutId); $store.confirmingAction.timeoutId = null; }\n $store.confirmingAction.type = 'version-delete';\n $store.confirmingAction.id = '${libraryName}:${versionParam}';\n $store.confirmingAction.isDeleting = false;\n $store.confirmingAction.timeoutId = setTimeout(() => {\n $store.confirmingAction.type = null;\n $store.confirmingAction.id = null;\n $store.confirmingAction.isDeleting = false;\n $store.confirmingAction.timeoutId = null;\n }, 3000);\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\n x-show={`!($store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting)`}\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 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\n x-show={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && !$store.confirmingAction.isDeleting`}\n >\n Confirm?<span class=\"sr-only\">Confirm delete</span>\n </span>\n\n {/* Deleting State: Spinner Icon */}\n <span\n x-show={`$store.confirmingAction.type === 'version-delete' && $store.confirmingAction.id === '${libraryName}:${versionParam}' && $store.confirmingAction.isDeleting`}\n >\n <LoadingSpinner />\n <span class=\"sr-only\">Loading...</span>\n </span>\n </button>\n )}\n </div>\n );\n};\n\nexport default VersionDetailsRow;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport VersionDetailsRow from \"./VersionDetailsRow\"; // Adjusted import path\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 * Uses VersionDetailsRow without the delete button.\n * @param props - Component props including the library information.\n */\nconst LibraryDetailCard = ({ library }: LibraryDetailCardProps) => (\n // Use Flowbite Card structure with updated padding and border, and white background\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 <h3 class=\"text-lg font-medium text-gray-900 dark:text-white mb-1\">\n <span safe>{library.name}</span>\n </h3>\n {/* Container for version rows */}\n <div class=\"mt-1\">\n {library.versions.length > 0 ? (\n library.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={false}\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 </div>\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 || \"unversioned\"} safe>\n {version.version || \"Unversioned\"}\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 : 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\">\n <a\n href={result.url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n class=\"underline underline-offset-4 flex-1\"\n safe\n >\n {result.url}\n </a>\n {result.mimeType ? (\n <span class=\"text-xs opacity-75 font-mono\" safe>\n {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\"; // Adjusted import path\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 { SearchTool } from \"../../../tools/SearchTool\";\nimport Layout from \"../../components/Layout\";\nimport LibraryDetailCard from \"../../components/LibraryDetailCard\";\nimport LibrarySearchCard from \"../../components/LibrarySearchCard\";\nimport SearchResultList from \"../../components/SearchResultList\";\nimport SearchResultSkeletonItem from \"../../components/SearchResultSkeletonItem\";\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 */\nexport function registerLibraryDetailRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n searchTool: SearchTool\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 === libraryName\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 \"unversioned\" string to undefined for the tool\n const versionParam = version === \"unversioned\" ? 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 on catch\n reply.type(\"text/html; charset=utf-8\");\n return (\n <p class=\"text-red-500 dark:text-red-400 italic\">\n An unexpected error occurred during the search.\n </p>\n );\n }\n }\n );\n}\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport type { VersionSummary } from \"../../store/types\";\nimport VersionDetailsRow from \"./VersionDetailsRow\"; // Adjusted import path\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 // Use Flowbite Card structure with updated padding and border, and white background\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\">\n <h3 class=\"text-lg font-medium text-gray-900 dark:text-white mb-1\">\n <a\n href={`/libraries/${encodeURIComponent(library.name)}`}\n class=\"hover:underline\"\n >\n <span safe>{library.name}</span>\n </a>\n </h3>\n {/* Container for version rows */}\n <div class=\"mt-1\">\n {library.versions.length > 0 ? (\n library.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\nexport default LibraryItem;\n","import type { LibraryInfo } from \"../../tools/ListLibrariesTool\";\nimport LibraryItem from \"./LibraryItem\"; // Adjusted import path\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 return (\n <>\n <div class=\"space-y-2\">\n {libraries.map((library) => (\n <LibraryItem library={library} />\n ))}\n </div>\n </>\n );\n};\n\nexport default LibraryList;\n","import type { FastifyInstance } from \"fastify\";\nimport type { ListLibrariesTool } from \"../../../tools/ListLibrariesTool\";\nimport { RemoveTool } from \"../../../tools\";\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 */\nexport function registerLibrariesRoutes(\n server: FastifyInstance,\n listLibrariesTool: ListLibrariesTool,\n removeTool: RemoveTool // Accept RemoveTool\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 server.log.error(error, \"Failed to list libraries\");\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 === \"unversioned\" ? undefined : versionParam;\n try {\n await removeTool.execute({ library: libraryName, version });\n reply.status(204).send(); // No Content on success\n } catch (error: any) {\n server.log.error(\n error,\n `Failed to remove ${libraryName}@${versionParam}`\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","/**\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 { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { SearchTool } from \"../tools\";\n\nimport { CancelJobTool } from \"../tools/CancelJobTool\";\nimport { ClearCompletedJobsTool } from \"../tools/ClearCompletedJobsTool\";\nimport { ListJobsTool } from \"../tools/ListJobsTool\";\nimport { ListLibrariesTool } from \"../tools/ListLibrariesTool\";\nimport { RemoveTool } from \"../tools/RemoveTool\";\nimport { ScrapeTool } from \"../tools/ScrapeTool\";\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\";\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): 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);\n const removeTool = new RemoveTool(docService, 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);\n registerLibrariesRoutes(server, listLibrariesTool, removeTool);\n registerLibraryDetailRoutes(server, listLibrariesTool, searchTool);\n registerJobListRoutes(server, listJobsTool);\n registerNewJobRoutes(server, scrapeTool);\n registerCancelJobRoute(server, cancelJobTool);\n registerClearCompletedJobsRoute(server, clearCompletedJobsTool);\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 { analytics, TelemetryEvent } from \"../telemetry\";\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 */\nexport async function registerWorkerService(pipeline: IPipeline): Promise<void> {\n // Configure progress callbacks for logging and analytics\n pipeline.setCallbacks({\n onJobProgress: async (job, progress) => {\n logger.debug(\n `Job ${job.id} progress: ${progress.pagesScraped}/${progress.totalPages} pages`,\n );\n\n // Track job progress for analytics with enhanced metrics\n analytics.track(TelemetryEvent.PIPELINE_JOB_PROGRESS, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n pagesScraped: progress.pagesScraped,\n totalPages: progress.totalPages,\n totalDiscovered: progress.totalDiscovered,\n progressPercent: Math.round((progress.pagesScraped / progress.totalPages) * 100),\n currentDepth: progress.depth,\n maxDepth: progress.maxDepth,\n discoveryRatio: Math.round(\n (progress.totalDiscovered / progress.totalPages) * 100,\n ), // How much we discovered vs limited total\n queueEfficiency:\n progress.totalPages > 0\n ? Math.round((progress.pagesScraped / progress.totalPages) * 100)\n : 0,\n });\n },\n onJobStatusChange: async (job) => {\n logger.debug(`Job ${job.id} status changed to: ${job.status}`);\n\n // Enhanced job completion tracking\n const duration = job.startedAt ? Date.now() - job.startedAt.getTime() : null;\n const queueWaitTime =\n job.startedAt && job.createdAt\n ? job.startedAt.getTime() - job.createdAt.getTime()\n : null;\n\n analytics.track(TelemetryEvent.PIPELINE_JOB_COMPLETED, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n status: job.status,\n durationMs: duration,\n queueWaitTimeMs: queueWaitTime,\n pagesProcessed: job.progressPages || 0,\n maxPagesConfigured: job.progressMaxPages || 0,\n hasVersion: !!job.version,\n hasError: !!job.error,\n throughputPagesPerSecond:\n duration && job.progressPages\n ? Math.round((job.progressPages / duration) * 1000)\n : 0,\n });\n },\n onJobError: async (job, error, document) => {\n logger.warn(\n `⚠️ Job ${job.id} error ${document ? `on document ${document.metadata.url}` : \"\"}: ${error.message}`,\n );\n\n // Use PostHog's native error tracking instead of custom events\n analytics.captureException(error, {\n jobId: job.id, // Job IDs are already anonymous\n library: job.library,\n hasDocument: !!document,\n stage: document ? \"document_processing\" : \"job_setup\",\n pages_processed_before_error: job.progressPages || 0,\n });\n },\n });\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 * 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 FastifyInstance } from \"fastify\";\nimport packageJson from \"../../package.json\";\nimport { ProxyAuthManager } from \"../auth\";\nimport { resolveEmbeddingContext } from \"../cli/utils\";\nimport type { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport { cleanupMcpService, registerMcpService } from \"../services/mcpService\";\nimport { registerTrpcService } from \"../services/trpcService\";\nimport { registerWebService } from \"../services/webService\";\nimport { registerWorkerService, stopWorkerService } from \"../services/workerService\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics, TelemetryEvent } from \"../telemetry\";\nimport { shouldEnableTelemetry } from \"../telemetry/TelemetryConfig\";\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 config: AppServerConfig;\n\n constructor(\n private docService: IDocumentManagement,\n private pipeline: IPipeline,\n config: AppServerConfig,\n ) {\n this.config = config;\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.config.enableWebInterface) {\n if (!this.config.enableWorker && !this.config.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.config.enableMcpServer) {\n if (!this.config.enableWorker && !this.config.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 // Initialize telemetry if enabled\n if (this.config.telemetry !== false && shouldEnableTelemetry()) {\n try {\n // Set global application context that will be included in all events\n if (analytics.isEnabled()) {\n // Resolve embedding configuration for global context\n const embeddingConfig = resolveEmbeddingContext();\n\n analytics.setGlobalContext({\n appVersion: packageJson.version,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appServicesEnabled: this.getActiveServicesList(),\n appAuthEnabled: Boolean(this.config.auth),\n appReadOnly: Boolean(this.config.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 analytics.track(TelemetryEvent.APP_STARTED, {\n services: this.getActiveServicesList(),\n port: this.config.port,\n externalWorker: Boolean(this.config.externalWorkerUrl),\n // Include startup context when available\n ...(this.config.startupContext?.cliCommand && {\n cliCommand: this.config.startupContext.cliCommand,\n }),\n ...(this.config.startupContext?.mcpProtocol && {\n mcpProtocol: this.config.startupContext.mcpProtocol,\n }),\n ...(this.config.startupContext?.mcpTransport && {\n mcpTransport: this.config.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.config.port,\n host: this.config.host,\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 // Stop worker service if enabled\n if (this.config.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 // Track app shutdown\n if (analytics.isEnabled()) {\n analytics.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: true,\n });\n }\n\n // Shutdown telemetry service (this will flush remaining events)\n await analytics.shutdown();\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 (analytics.isEnabled()) {\n analytics.track(TelemetryEvent.APP_SHUTDOWN, {\n graceful: false,\n error: error instanceof Error ? error.constructor.name : \"UnknownError\",\n });\n await analytics.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 (analytics.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 analytics.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 (analytics.isEnabled()) {\n analytics.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(async (error, request, reply) => {\n if (analytics.isEnabled()) {\n analytics.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.config.enableMcpServer) services.push(\"mcp\");\n if (this.config.enableWebInterface) services.push(\"web\");\n if (this.config.enableApiServer) services.push(\"api\");\n if (this.config.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 // Initialize authentication if enabled\n if (this.config.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.config.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.config.auth?.enabled && this.authManager) {\n await this.setupAuthMetadataEndpoint();\n }\n\n // Conditionally enable services based on configuration\n if (this.config.enableWebInterface) {\n await this.enableWebInterface();\n }\n\n if (this.config.enableMcpServer) {\n await this.enableMcpServer();\n }\n\n if (this.config.enableApiServer) {\n await this.enableTrpcApi();\n }\n\n if (this.config.enableWorker) {\n await this.enableWorker();\n }\n\n // Setup static file serving as fallback (must be last)\n if (this.config.enableWebInterface) {\n await this.setupStaticFiles();\n }\n }\n\n /**\n * Enable web interface service.\n */\n private async enableWebInterface(): Promise<void> {\n await registerWebService(this.server, this.docService, this.pipeline);\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.config.readOnly,\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);\n logger.debug(\"API server (tRPC) enabled\");\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.config.auth) {\n return;\n }\n\n this.authManager = new ProxyAuthManager(this.config.auth);\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.config.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 logger.info(`🚀 AppServer available at ${address}`);\n\n const enabledServices: string[] = [];\n\n if (this.config.enableWebInterface) {\n enabledServices.push(`Web interface: ${address}`);\n }\n\n if (this.config.enableMcpServer) {\n enabledServices.push(`MCP endpoints: ${address}/mcp, ${address}/sse`);\n }\n\n if (this.config.enableApiServer) {\n enabledServices.push(`API: ${address}/api`);\n }\n\n if (this.config.enableWorker) {\n enabledServices.push(\"Embedded worker: enabled\");\n } else if (this.config.externalWorkerUrl) {\n enabledServices.push(`External worker: ${this.config.externalWorkerUrl}`);\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 { IPipeline } from \"../pipeline/trpc/interfaces\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\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 config: AppServerConfig,\n): Promise<AppServer> {\n const appServer = new AppServer(docService, pipeline, config);\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 { 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 readOnly Whether to run in read-only mode.\n * @returns The created McpServer instance.\n */\nexport async function startStdioServer(\n tools: McpServerTools,\n readOnly = false,\n): Promise<McpServer> {\n // Create a server instance using the factory and shared tools\n const server = createMcpServerInstance(tools, readOnly);\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 for the document management API.\n * Implements IDocumentManagement and delegates to /api data router.\n */\nimport { createTRPCProxyClient, httpBatchLink } from \"@trpc/client\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { logger } from \"../utils/logger\";\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: [httpBatchLink({ url: this.baseUrl })],\n });\n logger.debug(`DocumentManagementClient (tRPC) created for: ${this.baseUrl}`);\n }\n\n async initialize(): Promise<void> {\n // Connectivity check\n await (\n this.client as unknown as { ping: { query: () => Promise<unknown> } }\n ).ping.query();\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","import type { Document } from \"@langchain/core/documents\";\nimport { logger } from \"../../../utils/logger\";\nimport { MimeTypeUtils } from \"../../../utils/mimeTypeUtils\";\nimport type { DocumentStore } from \"../../DocumentStore\";\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 /**\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: Document[],\n documentStore: DocumentStore,\n ): Promise<Document[]> {\n if (initialChunks.length === 0) {\n return [];\n }\n\n try {\n // Group chunks by document URL\n const chunksByDocument = new Map<string, Document[]>();\n for (const chunk of initialChunks) {\n const url = chunk.metadata.url as string;\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 as string);\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 as string);\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: Document[], 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.pageContent,\n )\n .join(\"\");\n }\n // Production/default: simple concatenation leveraging splitter guarantees.\n return chunks.map((chunk) => chunk.pageContent).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: Document,\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const chainIds: string[] = [];\n const visited = new Set<string>();\n let currentChunk: Document | null = chunk;\n const maxDepth = 50; // 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 < maxDepth) {\n const currentId = currentChunk.id as string;\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 {\n // Try normal parent lookup first\n const parentChunk = await documentStore.findParentChunk(\n library,\n version,\n currentId,\n );\n\n if (parentChunk) {\n currentChunk = parentChunk;\n } else {\n // If normal parent lookup fails, try to find ancestors with gaps\n currentChunk = await this.findAncestorWithGaps(\n library,\n version,\n currentChunk.metadata as { url: string; path?: string[] },\n documentStore,\n );\n }\n } catch (error) {\n // If standard lookup fails, try gap-aware ancestor search\n try {\n const currentMetadata = currentChunk?.metadata as {\n url: string;\n path?: string[];\n };\n if (currentMetadata) {\n currentChunk = await this.findAncestorWithGaps(\n library,\n version,\n currentMetadata,\n documentStore,\n );\n } else {\n currentChunk = null;\n }\n } catch (gapError) {\n logger.warn(\n `Parent lookup failed for chunk ${currentId}: ${error}. Gap search also failed: ${gapError}`,\n );\n break;\n }\n }\n }\n\n if (depth >= maxDepth) {\n logger.warn(\n `Maximum parent chain depth (${maxDepth}) 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 metadata: { url: string; path?: string[] },\n documentStore: DocumentStore,\n ): Promise<Document | null> {\n const path = metadata.path || [];\n const url = metadata.url;\n\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<Document[]> {\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.metadata.url as string;\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: Document,\n documentStore: DocumentStore,\n ): Promise<Document | null> {\n let current: Document | null = chunk;\n\n // If current is structural already, return it\n const isStructural = (c: Document | 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(\n library,\n version,\n current.id as string,\n );\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: Document[],\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: Document[]): 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: Document,\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.metadata.url as string,\n ancestorPath,\n documentStore,\n );\n\n for (const chunk of ancestorChunks) {\n containerIds.push(chunk.id as string);\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<Document[]> {\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: Document,\n documentStore: DocumentStore,\n ): Promise<string[]> {\n const subtreeIds: string[] = [];\n const visited = new Set<string>();\n const queue: Document[] = [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 as string;\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: Document[],\n documentStore: DocumentStore,\n ): Promise<Document[]> {\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 as string;\n chunkIds.add(id);\n\n // Add parent for context\n try {\n const parent = await documentStore.findParentChunk(library, version, id);\n if (parent) {\n chunkIds.add(parent.id as string);\n }\n } catch (error) {\n logger.warn(`Failed to find parent for chunk ${id}: ${error}`);\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 { Document } from \"@langchain/core/documents\";\nimport { MimeTypeUtils } from \"../../../utils/mimeTypeUtils\";\nimport type { DocumentStore } from \"../../DocumentStore\";\nimport type { ContentAssemblyStrategy } from \"../types\";\n\nconst CHILD_LIMIT = 3;\nconst PRECEDING_SIBLINGS_LIMIT = 1;\nconst SUBSEQUENT_SIBLINGS_LIMIT = 2;\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 /**\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 * 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: Document[],\n documentStore: DocumentStore,\n ): Promise<Document[]> {\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: Document[]): string {\n return chunks.map((chunk) => chunk.pageContent).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: Document,\n documentStore: DocumentStore,\n ): Promise<Set<string>> {\n const id = doc.id as string;\n const relatedIds = new Set<string>();\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 as string);\n }\n\n // Preceding Siblings\n const precedingSiblings = await documentStore.findPrecedingSiblingChunks(\n library,\n version,\n id,\n PRECEDING_SIBLINGS_LIMIT,\n );\n for (const sib of precedingSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n // Child Chunks\n const childChunks = await documentStore.findChildChunks(\n library,\n version,\n id,\n CHILD_LIMIT,\n );\n for (const child of childChunks) {\n relatedIds.add(child.id as string);\n }\n\n // Subsequent Siblings\n const subsequentSiblings = await documentStore.findSubsequentSiblingChunks(\n library,\n version,\n id,\n SUBSEQUENT_SIBLINGS_LIMIT,\n );\n for (const sib of subsequentSiblings) {\n relatedIds.add(sib.id as string);\n }\n\n return relatedIds;\n }\n}\n","import { 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 * @returns The appropriate strategy instance\n */\nexport function createContentAssemblyStrategy(\n mimeType?: string,\n): ContentAssemblyStrategy {\n // Default to MarkdownAssemblyStrategy for unknown or missing MIME types\n if (!mimeType) {\n return new MarkdownAssemblyStrategy();\n }\n\n // Try each strategy to see which one can handle the content type\n const strategies = [new HierarchicalAssemblyStrategy(), new MarkdownAssemblyStrategy()];\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();\n}\n\n/**\n * Gets a human-readable name for the strategy that would be selected.\n * Useful for logging and debugging.\n */\nexport function getStrategyName(mimeType?: string): string {\n if (!mimeType) {\n return \"MarkdownAssemblyStrategy (default)\";\n }\n\n const hierarchicalStrategy = new HierarchicalAssemblyStrategy();\n if (hierarchicalStrategy.canHandle(mimeType)) {\n return \"HierarchicalAssemblyStrategy\";\n }\n\n return \"MarkdownAssemblyStrategy\";\n}\n","import type { Document } from \"@langchain/core/documents\";\nimport { createContentAssemblyStrategy } from \"./assembly/ContentAssemblyStrategyFactory\";\nimport type { DocumentStore } from \"./DocumentStore\";\nimport type { StoreSearchResult } from \"./types\";\n\nexport class DocumentRetrieverService {\n private documentStore: DocumentStore;\n\n constructor(documentStore: DocumentStore) {\n this.documentStore = documentStore;\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 const result = await this.processUrlGroup(\n library,\n normalizedVersion,\n url,\n urlResults,\n );\n results.push(result);\n }\n\n return results;\n }\n\n /**\n * Groups search results by URL.\n */\n private groupResultsByUrl(results: Document[]): Map<string, Document[]> {\n const resultsByUrl = new Map<string, Document[]>();\n\n for (const result of results) {\n const url = result.metadata.url as string;\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: Document[],\n ): Promise<StoreSearchResult> {\n // Extract mimeType from the first document's metadata\n const mimeType =\n initialChunks.length > 0\n ? (initialChunks[0].metadata.mimeType as string | undefined)\n : undefined;\n\n // Find the maximum score from the initial results\n const maxScore = Math.max(\n ...initialChunks.map((chunk) => chunk.metadata.score as number),\n );\n\n // Create appropriate assembly strategy based on content type\n const strategy = createContentAssemblyStrategy(mimeType);\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 };\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Database } from \"better-sqlite3\";\nimport { MIGRATION_MAX_RETRIES, MIGRATION_RETRY_DELAY_MS } from \"../utils/config\";\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 * @throws {StoreError} If any migration fails.\n */\nexport async function applyMigrations(db: Database): Promise<void> {\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 < MIGRATION_MAX_RETRIES) {\n retries++;\n logger.warn(\n `⚠️ Migrations busy (SQLITE_BUSY), retrying attempt ${retries}/${MIGRATION_MAX_RETRIES} in ${MIGRATION_RETRY_DELAY_MS}ms...`,\n );\n await new Promise((resolve) => setTimeout(resolve, MIGRATION_RETRY_DELAY_MS));\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 ${MIGRATION_MAX_RETRIES} 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 { Document } from \"@langchain/core/documents\";\nimport type { Embeddings } from \"@langchain/core/embeddings\";\nimport Database, { type Database as DatabaseType } from \"better-sqlite3\";\nimport semver from \"semver\";\nimport * as sqliteVec from \"sqlite-vec\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport type { DocumentMetadata } from \"../types\";\nimport {\n EMBEDDING_BATCH_CHARS,\n EMBEDDING_BATCH_SIZE,\n SEARCH_OVERFETCH_FACTOR,\n SEARCH_WEIGHT_FTS,\n SEARCH_WEIGHT_VEC,\n VECTOR_SEARCH_MULTIPLIER,\n} from \"../utils/config\";\nimport { logger } from \"../utils/logger\";\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 { StoredScraperOptions } from \"./types\";\nimport {\n type DbDocument,\n type DbQueryResult,\n type DbVersion,\n type DbVersionWithLibrary,\n denormalizeVersionName,\n mapDbDocumentToDocument,\n normalizeVersionName,\n VECTOR_DIMENSION,\n type VersionScraperOptions,\n type VersionStatus,\n} from \"./types\";\n\ninterface RawSearchResult extends DbDocument {\n vec_score?: number;\n fts_score?: number;\n}\n\ninterface RankedResult extends RawSearchResult {\n vec_rank?: number;\n fts_rank?: number;\n rrf_score: number;\n}\n\n/**\n * Manages document storage and retrieval using SQLite with vector and full-text search capabilities.\n * Provides direct access to SQLite with prepared statements to store and query document\n * embeddings along with their metadata. Supports versioned storage of documents for different\n * libraries, enabling version-specific document retrieval and searches.\n */\nexport class DocumentStore {\n private readonly db: DatabaseType;\n private embeddings!: Embeddings;\n private readonly dbDimension: number = VECTOR_DIMENSION;\n private modelDimension!: number;\n private readonly embeddingConfig?: EmbeddingModelConfig | null;\n private isVectorSearchEnabled: boolean = false;\n private statements!: {\n getById: Database.Statement<[bigint]>;\n insertDocument: Database.Statement<\n [bigint, bigint, string, string, string, number, string]\n >;\n insertEmbedding: Database.Statement<[bigint, bigint, bigint, string]>;\n deleteDocuments: Database.Statement<[string, string, string]>;\n deleteDocumentsByUrl: Database.Statement<[string, string, string, string]>;\n queryVersions: Database.Statement<[string]>;\n checkExists: Database.Statement<[string, string]>;\n queryLibraryVersions: Database.Statement<[]>;\n getChildChunks: Database.Statement<\n [string, string, string, number, string, bigint, number]\n >;\n getPrecedingSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getSubsequentSiblings: Database.Statement<\n [string, string, string, bigint, string, number]\n >;\n getParentChunk: Database.Statement<[string, string, string, string, bigint]>;\n insertLibrary: Database.Statement<[string]>;\n getLibraryIdByName: Database.Statement<[string]>;\n // New version-related statements\n insertVersion: Database.Statement<[number, string | null]>;\n resolveVersionId: Database.Statement<[number, string | null]>;\n getVersionById: Database.Statement<[number]>;\n queryVersionsByLibraryId: Database.Statement<[number]>;\n // Status tracking statements\n updateVersionStatus: Database.Statement<[string, string | null, number]>;\n updateVersionProgress: Database.Statement<[number, number, number]>;\n getVersionsByStatus: Database.Statement<string[]>;\n // Scraper options statements\n updateVersionScraperOptions: Database.Statement<[string, string, number]>;\n getVersionWithOptions: Database.Statement<[number]>;\n getVersionsBySourceUrl: Database.Statement<[string]>;\n // 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 };\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 += SEARCH_WEIGHT_VEC / (k + vecRank);\n }\n if (ftsRank !== undefined) {\n rrf += SEARCH_WEIGHT_FTS / (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, embeddingConfig?: EmbeddingModelConfig | null) {\n if (!dbPath) {\n throw new StoreError(\"Missing required database path\");\n }\n\n // Only establish database connection in constructor\n this.db = new Database(dbPath);\n\n // Store embedding config for later initialization\n this.embeddingConfig = embeddingConfig;\n }\n\n /**\n * Sets up prepared statements for database queries\n */\n private prepareStatements(): void {\n const statements = {\n getById: this.db.prepare<[bigint]>(\"SELECT * FROM documents WHERE id = ?\"),\n insertDocument: this.db.prepare<\n [bigint, bigint, string, string, string, number, string]\n >(\n \"INSERT INTO documents (library_id, version_id, url, content, metadata, sort_order, indexed_at) VALUES (?, ?, ?, ?, ?, ?, ?)\",\n ),\n insertEmbedding: this.db.prepare<[bigint, bigint, bigint, string]>(\n \"INSERT INTO documents_vec (rowid, library_id, version_id, embedding) VALUES (?, ?, ?, ?)\",\n ),\n insertLibrary: this.db.prepare<[string]>(\n \"INSERT INTO libraries (name) VALUES (?) ON CONFLICT(name) DO NOTHING\",\n ),\n getLibraryIdByName: this.db.prepare<[string]>(\n \"SELECT id FROM libraries WHERE name = ?\",\n ),\n // New version-related statements\n insertVersion: this.db.prepare<[number, string | null]>(\n \"INSERT INTO versions (library_id, name, status) VALUES (?, ?, 'not_indexed') ON CONFLICT(library_id, name) DO NOTHING\",\n ),\n resolveVersionId: this.db.prepare<[number, string | null]>(\n \"SELECT id FROM versions WHERE library_id = ? AND name IS ?\",\n ),\n getVersionById: this.db.prepare<[number]>(\"SELECT * FROM versions WHERE id = ?\"),\n queryVersionsByLibraryId: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE library_id = ? ORDER BY name\",\n ),\n deleteLibraryDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocuments: this.db.prepare<[string, string, string]>(\n `DELETE FROM documents\n WHERE library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n deleteDocumentsByUrl: this.db.prepare<[string, string, string, string]>(\n `DELETE FROM documents\n WHERE url = ?\n AND library_id = (SELECT id FROM libraries WHERE name = ?)\n AND version_id = (\n SELECT v.id FROM versions v \n WHERE v.library_id = (SELECT id FROM libraries WHERE name = ?)\n AND COALESCE(v.name, '') = COALESCE(?, '')\n )`,\n ),\n getDocumentBySort: this.db.prepare<[string, string]>(\n `SELECT d.id\n FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n queryVersions: this.db.prepare<[string]>(\n `SELECT DISTINCT v.name\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n ORDER BY v.name`,\n ),\n checkExists: this.db.prepare<[string, string]>(\n `SELECT d.id FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n LIMIT 1`,\n ),\n // Library/version aggregation including versions without documents and status/progress fields\n queryLibraryVersions: this.db.prepare<[]>(\n `SELECT\n l.name as library,\n COALESCE(v.name, '') as version,\n v.id as versionId,\n v.status as status,\n v.progress_pages as progressPages,\n v.progress_max_pages as progressMaxPages,\n v.source_url as sourceUrl,\n MIN(d.indexed_at) as indexedAt,\n COUNT(d.id) as documentCount,\n COUNT(DISTINCT d.url) as uniqueUrlCount\n FROM versions v\n JOIN libraries l ON v.library_id = l.id\n LEFT JOIN documents d ON d.version_id = v.id\n GROUP BY v.id\n ORDER BY l.name, version`,\n ),\n getChildChunks: this.db.prepare<\n [string, string, string, number, string, bigint, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_array_length(json_extract(d.metadata, '$.path')) = ?\n AND json_extract(d.metadata, '$.path') LIKE ? || '%'\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getPrecedingSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order DESC\n LIMIT ?\n `),\n getSubsequentSiblings: this.db.prepare<\n [string, string, string, bigint, string, number]\n >(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND d.sort_order > (SELECT sort_order FROM documents WHERE id = ?)\n AND json_extract(d.metadata, '$.path') = ?\n ORDER BY d.sort_order\n LIMIT ?\n `),\n getParentChunk: this.db.prepare<[string, string, string, string, bigint]>(`\n SELECT d.* FROM documents d\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n AND json_extract(d.metadata, '$.path') = ?\n AND d.sort_order < (SELECT sort_order FROM documents WHERE id = ?)\n ORDER BY d.sort_order DESC\n LIMIT 1\n `),\n // Status tracking statements\n updateVersionStatus: this.db.prepare<[string, string | null, number]>(\n \"UPDATE versions SET status = ?, error_message = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n updateVersionProgress: this.db.prepare<[number, number, number]>(\n \"UPDATE versions SET progress_pages = ?, progress_max_pages = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionsByStatus: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.status IN (SELECT value FROM json_each(?))\",\n ),\n // Scraper options statements\n updateVersionScraperOptions: this.db.prepare<[string, string, number]>(\n \"UPDATE versions SET source_url = ?, scraper_options = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?\",\n ),\n getVersionWithOptions: this.db.prepare<[number]>(\n \"SELECT * FROM versions WHERE id = ?\",\n ),\n getVersionsBySourceUrl: this.db.prepare<[string]>(\n \"SELECT v.*, l.name as library_name FROM versions v JOIN libraries l ON v.library_id = l.id WHERE v.source_url = ? ORDER BY v.created_at DESC\",\n ),\n // 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 };\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 either provided config or environment variables.\n * If no embedding config is provided (null), embeddings will not be initialized.\n * This allows DocumentStore to be used without embeddings for operations that don't need them.\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, skip embedding initialization\n if (this.embeddingConfig === null) {\n logger.debug(\"Embedding initialization skipped (explicitly disabled)\");\n return;\n }\n\n // Use provided config or fall back to parsing environment\n const config = this.embeddingConfig || EmbeddingConfig.parseEmbeddingConfig();\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\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 const testVector = await this.embeddings.embedQuery(\"test\");\n this.modelDimension = testVector.length;\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 }\n // Re-throw other embedding errors (like DimensionError) as-is\n throw error;\n }\n }\n\n /**\n * Generates a dual-mode FTS query that combines phrase and keyword matching.\n * Creates a query like: \"exact phrase\" OR (\"word1\" OR \"word2\" OR \"word3\")\n * This provides better recall by matching both exact phrases and individual terms,\n * while safely handling special FTS keywords by quoting everything.\n */\n private escapeFtsQuery(query: string): string {\n // If the query already contains quotes, respect them and return as-is (escaped)\n if (query.includes('\"')) {\n return query.replace(/\"/g, '\"\"');\n }\n\n // Escape internal double quotes for the phrase part\n const escapedQuotes = query.replace(/\"/g, '\"\"');\n const phraseQuery = `\"${escapedQuotes}\"`;\n\n // Split query into individual terms for keyword matching\n const terms = query\n .trim()\n .split(/\\s+/)\n .filter((term) => term.length > 0);\n\n // If only one term, just return the phrase query\n if (terms.length <= 1) {\n return phraseQuery;\n }\n\n // Create keyword query with each term safely quoted: (\"term1\" OR \"term2\" OR \"term3\")\n const keywordQuery = terms\n .map((term) => `\"${term.replace(/\"/g, '\"\"')}\"`)\n .join(\" OR \");\n\n // Combine phrase and keyword queries\n return `${phraseQuery} OR (${keywordQuery})`;\n }\n\n /**\n * Initializes database connection and ensures readiness\n */\n async initialize(): Promise<void> {\n try {\n // 1. Load extensions first (moved before migrations)\n sqliteVec.load(this.db);\n\n // 2. Apply migrations (after extensions are loaded)\n applyMigrations(this.db);\n\n // 3. Initialize prepared statements\n this.prepareStatements();\n\n // 4. Initialize embeddings client (await to catch errors)\n await this.initializeEmbeddings();\n } catch (error) {\n // Re-throw StoreError, 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 library_id and version_id.\n * Creates library and version records if they don't exist.\n */\n async resolveLibraryAndVersionIds(\n library: string,\n version: string,\n ): Promise<{ libraryId: number; versionId: number }> {\n const normalizedLibrary = library.toLowerCase();\n const normalizedVersion = denormalizeVersionName(version.toLowerCase());\n\n // Insert or get library_id\n this.statements.insertLibrary.run(normalizedLibrary);\n const libraryIdRow = this.statements.getLibraryIdByName.get(normalizedLibrary) as\n | { id: number }\n | undefined;\n if (!libraryIdRow || typeof libraryIdRow.id !== \"number\") {\n throw new StoreError(`Failed to resolve library_id for library: ${library}`);\n }\n const libraryId = libraryIdRow.id;\n\n // Insert or get version_id\n // Reuse existing unversioned entry if present; storing '' ensures UNIQUE constraint applies\n this.statements.insertVersion.run(libraryId, normalizedVersion);\n const versionIdRow = this.statements.resolveVersionId.get(\n libraryId,\n normalizedVersion === null ? \"\" : normalizedVersion,\n ) as { id: number } | undefined;\n if (!versionIdRow || typeof versionIdRow.id !== \"number\") {\n throw new StoreError(\n `Failed to resolve version_id for library: ${library}, version: ${version}`,\n );\n }\n\n return { libraryId, versionId: versionIdRow.id };\n }\n\n /**\n * Retrieves all unique versions for a specific library\n */\n async queryUniqueVersions(library: string): Promise<string[]> {\n try {\n const rows = this.statements.queryVersions.all(library.toLowerCase()) as Array<{\n name: string | null;\n }>;\n return rows.map((row) => normalizeVersionName(row.name));\n } catch (error) {\n throw new ConnectionError(\"Failed to query versions\", error);\n }\n }\n\n /**\n * Updates the status of a version record in the database.\n * @param versionId The version ID to update\n * @param status The new status to set\n * @param errorMessage Optional error message for failed statuses\n */\n async updateVersionStatus(\n versionId: number,\n status: VersionStatus,\n errorMessage?: string,\n ): Promise<void> {\n try {\n this.statements.updateVersionStatus.run(status, errorMessage ?? null, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version status: ${error}`);\n }\n }\n\n /**\n * Updates the progress counters for a version being indexed.\n * @param versionId The version ID to update\n * @param pages Current number of pages processed\n * @param maxPages Total number of pages to process\n */\n async updateVersionProgress(\n versionId: number,\n pages: number,\n maxPages: number,\n ): Promise<void> {\n try {\n this.statements.updateVersionProgress.run(pages, maxPages, versionId);\n } catch (error) {\n throw new StoreError(`Failed to update version progress: ${error}`);\n }\n }\n\n /**\n * Retrieves versions by their status.\n * @param statuses Array of statuses to filter by\n * @returns Array of version records matching the statuses\n */\n async getVersionsByStatus(statuses: VersionStatus[]): Promise<DbVersionWithLibrary[]> {\n try {\n const statusJson = JSON.stringify(statuses);\n const rows = this.statements.getVersionsByStatus.all(\n statusJson,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to get versions by status: ${error}`);\n }\n }\n\n /**\n * Stores scraper options for a version to enable reproducible indexing.\n * @param versionId The version ID to update\n * @param options Complete scraper options used for indexing\n */\n async storeScraperOptions(versionId: number, options: ScraperOptions): Promise<void> {\n try {\n // biome-ignore lint/correctness/noUnusedVariables: Extract source URL and exclude runtime-only fields using destructuring\n const { url: source_url, library, version, signal, ...scraper_options } = options;\n\n const optionsJson = JSON.stringify(scraper_options);\n this.statements.updateVersionScraperOptions.run(source_url, optionsJson, versionId);\n } catch (error) {\n throw new StoreError(`Failed to store scraper options: ${error}`);\n }\n }\n\n /**\n * Retrieves stored scraping configuration (source URL and options) for a version.\n * Returns null when no source URL is recorded (not re-indexable).\n */\n async getScraperOptions(versionId: number): Promise<StoredScraperOptions | null> {\n try {\n const row = this.statements.getVersionWithOptions.get(versionId) as\n | DbVersion\n | undefined;\n\n if (!row?.source_url) {\n return null;\n }\n\n let parsed: VersionScraperOptions = {} as VersionScraperOptions;\n if (row.scraper_options) {\n try {\n parsed = JSON.parse(row.scraper_options) as VersionScraperOptions;\n } catch (e) {\n logger.warn(`⚠️ Invalid scraper_options JSON for version ${versionId}: ${e}`);\n parsed = {} as VersionScraperOptions;\n }\n }\n\n return { sourceUrl: row.source_url, options: parsed };\n } catch (error) {\n throw new StoreError(`Failed to get scraper options: ${error}`);\n }\n }\n\n /**\n * Finds versions that were indexed from the same source URL.\n * Useful for finding similar configurations or detecting duplicates.\n * @param url Source URL to search for\n * @returns Array of versions with the same source URL\n */\n async findVersionsBySourceUrl(url: string): Promise<DbVersionWithLibrary[]> {\n try {\n const rows = this.statements.getVersionsBySourceUrl.all(\n url,\n ) as DbVersionWithLibrary[];\n return rows;\n } catch (error) {\n throw new StoreError(`Failed to find versions by source URL: ${error}`);\n }\n }\n\n /**\n * Verifies existence of documents for a specific library version\n */\n async checkDocumentExists(library: string, version: string): Promise<boolean> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.checkExists.get(\n library.toLowerCase(),\n normalizedVersion,\n );\n return result !== undefined;\n } catch (error) {\n throw new ConnectionError(\"Failed to check document existence\", error);\n }\n }\n\n /**\n * Retrieves a mapping of all libraries to their available versions with details.\n */\n async queryLibraryVersions(): Promise<\n Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus; // Persisted enum value\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >\n > {\n try {\n // Define the expected row structure from the GROUP BY query (including versions without documents)\n interface LibraryVersionRow {\n library: string;\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null; // MIN() may return null\n }\n\n const rows = this.statements.queryLibraryVersions.all() as LibraryVersionRow[];\n const libraryMap = new Map<\n string,\n Array<{\n version: string;\n versionId: number;\n status: VersionStatus;\n progressPages: number;\n progressMaxPages: number;\n sourceUrl: string | null;\n documentCount: number;\n uniqueUrlCount: number;\n indexedAt: string | null;\n }>\n >();\n\n for (const row of rows) {\n // Process all rows, including those where version is \"\" (unversioned)\n const library = row.library;\n if (!libraryMap.has(library)) {\n libraryMap.set(library, []);\n }\n\n // Format indexedAt to ISO string if available\n const indexedAtISO = row.indexedAt ? new Date(row.indexedAt).toISOString() : null;\n\n libraryMap.get(library)?.push({\n version: row.version,\n versionId: row.versionId,\n // Preserve raw string status here; DocumentManagementService will cast to VersionStatus\n status: row.status,\n progressPages: row.progressPages,\n progressMaxPages: row.progressMaxPages,\n sourceUrl: row.sourceUrl,\n documentCount: row.documentCount,\n uniqueUrlCount: row.uniqueUrlCount,\n indexedAt: indexedAtISO,\n });\n }\n\n // Sort versions within each library: unversioned first, then semantically\n for (const versions of libraryMap.values()) {\n versions.sort((a, b) => {\n if (a.version === \"\" && b.version !== \"\") {\n return -1; // a (unversioned) comes first\n }\n if (a.version !== \"\" && b.version === \"\") {\n return 1; // b (unversioned) comes first\n }\n if (a.version === \"\" && b.version === \"\") {\n return 0; // Should not happen with GROUP BY, but handle anyway\n }\n // Both are non-empty, use semver compare with fallback to string compare\n try {\n return semver.compare(a.version, b.version);\n } catch (_error) {\n // Fallback to lexicographic comparison if semver parsing fails\n return a.version.localeCompare(b.version);\n }\n });\n }\n\n return libraryMap;\n } catch (error) {\n throw new ConnectionError(\"Failed to query library versions\", error);\n }\n }\n\n /**\n * Stores documents with library and version metadata, generating embeddings\n * for vector similarity search. Automatically removes any existing documents\n * for the same URLs before adding new ones to prevent UNIQUE constraint violations.\n */\n async addDocuments(\n library: string,\n version: string,\n documents: Document[],\n ): Promise<void> {\n try {\n if (documents.length === 0) {\n return;\n }\n\n // Extract unique URLs from the documents being added\n const urls = new Set<string>();\n for (const doc of documents) {\n const url = doc.metadata.url as string;\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n urls.add(url);\n }\n\n // Generate embeddings in batch only if vector search is enabled\n let paddedEmbeddings: number[][] = [];\n\n if (this.isVectorSearchEnabled) {\n const texts = documents.map((doc) => {\n const header = `<title>${doc.metadata.title}</title>\\n<url>${doc.metadata.url}</url>\\n<path>${doc.metadata.path.join(\" / \")}</path>\\n`;\n return `${header}${doc.pageContent}`;\n });\n\n // Batch embedding creation to avoid token limit errors\n // Use size-based batching to prevent 413 errors\n const maxBatchChars =\n Number(process.env.DOCS_MCP_EMBEDDING_BATCH_CHARS) || EMBEDDING_BATCH_CHARS;\n const rawEmbeddings: number[][] = [];\n\n let currentBatch: string[] = [];\n let currentBatchSize = 0;\n let batchCount = 0;\n\n for (const text of texts) {\n const textSize = text.length;\n\n // If adding this text would exceed the limit, process the current batch first\n if (currentBatchSize + textSize > maxBatchChars && currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n\n // Add text to current batch\n currentBatch.push(text);\n currentBatchSize += textSize;\n\n // Also respect the count-based limit for APIs that have per-request item limits\n if (currentBatch.length >= EMBEDDING_BATCH_SIZE) {\n batchCount++;\n logger.debug(\n `Processing embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n currentBatch = [];\n currentBatchSize = 0;\n }\n }\n\n // Process any remaining texts in the final batch\n if (currentBatch.length > 0) {\n batchCount++;\n logger.debug(\n `Processing final embedding batch ${batchCount}: ${currentBatch.length} texts, ${currentBatchSize} chars`,\n );\n const batchEmbeddings = await this.embeddings.embedDocuments(currentBatch);\n rawEmbeddings.push(...batchEmbeddings);\n }\n paddedEmbeddings = rawEmbeddings.map((vector) => this.padVector(vector));\n }\n\n // Resolve library and version IDs (creates them if they don't exist)\n const { libraryId, versionId } = await this.resolveLibraryAndVersionIds(\n library,\n version,\n );\n\n // Delete existing documents for these URLs to prevent UNIQUE constraint violations\n // This must happen AFTER resolveLibraryAndVersionIds to ensure the library/version exist\n for (const url of urls) {\n const deletedCount = await this.deleteDocumentsByUrl(library, version, url);\n if (deletedCount > 0) {\n logger.debug(`Deleted ${deletedCount} existing documents for URL: ${url}`);\n }\n }\n\n // Insert documents in a transaction\n const transaction = this.db.transaction((docs: typeof documents) => {\n for (let i = 0; i < docs.length; i++) {\n const doc = docs[i];\n const url = doc.metadata.url as string;\n\n // Insert into main documents table using foreign key IDs\n const result = this.statements.insertDocument.run(\n BigInt(libraryId),\n BigInt(versionId),\n url,\n doc.pageContent,\n JSON.stringify(doc.metadata),\n i,\n new Date().toISOString(), // Pass current timestamp for indexed_at\n );\n const rowId = result.lastInsertRowid;\n\n // Insert into vector table only if vector search is enabled\n if (this.isVectorSearchEnabled && paddedEmbeddings.length > 0) {\n this.statements.insertEmbedding.run(\n BigInt(rowId),\n BigInt(libraryId),\n BigInt(versionId),\n JSON.stringify(paddedEmbeddings[i]),\n );\n }\n }\n });\n\n transaction(documents);\n } catch (error) {\n throw new ConnectionError(\"Failed to add documents to store\", error);\n }\n }\n\n /**\n * Removes documents matching specified library and version\n * @returns Number of documents deleted\n */\n async deleteDocuments(library: string, version: string): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocuments.run(\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents\", error);\n }\n }\n\n /**\n * Removes documents for a specific URL within a library and version\n * @returns Number of documents deleted\n */\n async deleteDocumentsByUrl(\n library: string,\n version: string,\n url: string,\n ): Promise<number> {\n try {\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.deleteDocumentsByUrl.run(\n url,\n library.toLowerCase(),\n library.toLowerCase(), // library name appears twice in the query\n normalizedVersion,\n );\n return result.changes;\n } catch (error) {\n throw new ConnectionError(\"Failed to delete documents by URL\", error);\n }\n }\n\n /**\n * 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 all documents for this version\n const documentsDeleted = await this.deleteDocuments(library, version);\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 * Retrieves a document by its ID.\n * @param id The ID of the document.\n * @returns The document, or null if not found.\n */\n async getById(id: string): Promise<Document | null> {\n try {\n const row = this.statements.getById.get(BigInt(id)) as DbQueryResult<DbDocument>;\n if (!row) {\n return null;\n }\n\n return mapDbDocumentToDocument(row);\n } catch (error) {\n throw new ConnectionError(`Failed to get document by ID ${id}`, error);\n }\n }\n\n /**\n * Finds documents matching a text query using hybrid search 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<Document[]> {\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 * SEARCH_OVERFETCH_FACTOR);\n\n // Use a multiplier to cast a wider net in vector search before final ranking\n const vectorSearchK = overfetchLimit * VECTOR_SEARCH_MULTIPLIER;\n\n const stmt = this.db.prepare(`\n WITH vec_distances AS (\n SELECT\n dv.rowid as id,\n dv.distance as vec_distance\n FROM documents_vec dv\n JOIN versions v ON dv.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND dv.embedding MATCH ?\n AND dv.k = ?\n ORDER BY dv.distance\n ),\n fts_scores AS (\n SELECT\n f.rowid as id,\n bm25(documents_fts, 10.0, 1.0, 5.0, 1.0) as fts_score\n FROM documents_fts f\n JOIN documents d ON f.rowid = d.id\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND documents_fts MATCH ?\n ORDER BY fts_score\n LIMIT ?\n )\n SELECT\n d.id,\n d.content,\n d.metadata,\n COALESCE(1 / (1 + v.vec_distance), 0) as vec_score,\n COALESCE(-MIN(f.fts_score, 0), 0) as fts_score\n FROM documents d\n LEFT JOIN vec_distances v ON d.id = v.id\n LEFT JOIN fts_scores f ON d.id = f.id\n WHERE (v.id IS NOT NULL OR f.id IS NOT NULL)\n 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 ...mapDbDocumentToDocument(row),\n metadata: {\n ...JSON.parse(row.metadata),\n id: row.id,\n score: row.rrf_score,\n vec_rank: row.vec_rank,\n fts_rank: row.fts_rank,\n },\n }));\n } 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 bm25(documents_fts, 10.0, 1.0, 5.0, 1.0) as fts_score\n FROM documents_fts f\n JOIN documents d ON f.rowid = d.id\n JOIN versions v ON d.version_id = v.id\n JOIN libraries l ON v.library_id = l.id\n WHERE l.name = ?\n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND documents_fts MATCH ?\n 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 ...mapDbDocumentToDocument(row),\n metadata: {\n ...JSON.parse(row.metadata),\n id: row.id,\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 // Explicitly ensure vec_rank is not included in FTS-only mode\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<Document[]> {\n try {\n const parent = await this.getById(id);\n if (!parent) {\n return [];\n }\n\n const parentPath = (parent.metadata as DocumentMetadata).path ?? [];\n const parentUrl = (parent.metadata as DocumentMetadata).url;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getChildChunks.all(\n library.toLowerCase(),\n normalizedVersion,\n parentUrl,\n parentPath.length + 1,\n JSON.stringify(parentPath),\n BigInt(id),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(`Failed to find child chunks for ID ${id}`, error);\n }\n }\n\n /**\n * Finds preceding sibling chunks of a given document.\n */\n async findPrecedingSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata as DocumentMetadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getPrecedingSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.reverse().map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find preceding sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds subsequent sibling chunks of a given document.\n */\n async findSubsequentSiblingChunks(\n library: string,\n version: string,\n id: string,\n limit: number,\n ): Promise<Document[]> {\n try {\n const reference = await this.getById(id);\n if (!reference) {\n return [];\n }\n\n const refMetadata = reference.metadata;\n const normalizedVersion = version.toLowerCase();\n\n const result = this.statements.getSubsequentSiblings.all(\n library.toLowerCase(),\n normalizedVersion,\n refMetadata.url,\n BigInt(id),\n JSON.stringify(refMetadata.path),\n limit,\n ) as Array<DbDocument>;\n\n return result.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\n `Failed to find subsequent sibling chunks for ID ${id}`,\n error,\n );\n }\n }\n\n /**\n * Finds the parent chunk of a given document.\n */\n async findParentChunk(\n library: string,\n version: string,\n id: string,\n ): Promise<Document | null> {\n try {\n const child = await this.getById(id);\n if (!child) {\n return null;\n }\n\n const childMetadata = child.metadata as DocumentMetadata;\n const path = childMetadata.path ?? [];\n const parentPath = path.slice(0, -1);\n\n if (parentPath.length === 0) {\n return null;\n }\n\n const normalizedVersion = version.toLowerCase();\n const result = this.statements.getParentChunk.get(\n library.toLowerCase(),\n normalizedVersion,\n childMetadata.url,\n JSON.stringify(parentPath),\n BigInt(id),\n ) as DbQueryResult<DbDocument>;\n\n if (!result) {\n return null;\n }\n\n return mapDbDocumentToDocument(result);\n } catch (error) {\n throw new ConnectionError(`Failed to find parent chunk for ID ${id}`, error);\n }\n }\n\n /**\n * Fetches multiple documents by their IDs in a single call.\n * Returns an array of Document objects, sorted by their sort_order.\n */\n async findChunksByIds(\n library: string,\n version: string,\n ids: string[],\n ): Promise<Document[]> {\n if (!ids.length) return [];\n try {\n const normalizedVersion = version.toLowerCase();\n // Use parameterized query for variable number of IDs\n const placeholders = ids.map(() => \"?\").join(\",\");\n const stmt = this.db.prepare(\n `SELECT d.* FROM documents d\n JOIN libraries l ON d.library_id = l.id\n JOIN versions v ON d.version_id = v.id\n WHERE l.name = ? \n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.id IN (${placeholders}) \n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n ...ids,\n ) as DbDocument[];\n return rows.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(\"Failed to fetch documents by IDs\", error);\n }\n }\n\n /**\n * Fetches all document chunks for a specific URL within a library and version.\n * Returns documents sorted by their sort_order for proper reassembly.\n */\n async findChunksByUrl(\n library: string,\n version: string,\n url: string,\n ): Promise<Document[]> {\n try {\n const normalizedVersion = version.toLowerCase();\n const stmt = this.db.prepare(\n `SELECT d.* FROM documents d\n JOIN libraries l ON d.library_id = l.id\n JOIN versions v ON d.version_id = v.id\n WHERE l.name = ? \n AND COALESCE(v.name, '') = COALESCE(?, '')\n AND d.url = ?\n ORDER BY d.sort_order`,\n );\n const rows = stmt.all(\n library.toLowerCase(),\n normalizedVersion,\n url,\n ) as DbDocument[];\n return rows.map((row) => mapDbDocumentToDocument(row));\n } catch (error) {\n throw new ConnectionError(`Failed to fetch documents by URL ${url}`, error);\n }\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { Document } from \"@langchain/core/documents\";\nimport envPaths from \"env-paths\";\nimport Fuse from \"fuse.js\";\nimport semver from \"semver\";\nimport {\n type PipelineConfiguration,\n PipelineFactory,\n} from \"../scraper/pipelines/PipelineFactory\";\nimport type { ContentPipeline } from \"../scraper/pipelines/types\";\nimport type { ScraperOptions } from \"../scraper/types\";\nimport { ScrapeMode } from \"../scraper/types\";\nimport type { ContentChunk } from \"../splitter/types\";\nimport { analytics, extractHostname, TelemetryEvent } from \"../telemetry\";\nimport { LibraryNotFoundError, VersionNotFoundError } from \"../tools\";\nimport { logger } from \"../utils/logger\";\nimport { getProjectRoot } from \"../utils/paths\";\nimport { DocumentRetrieverService } from \"./DocumentRetrieverService\";\nimport { DocumentStore } from \"./DocumentStore\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\nimport { StoreError } from \"./errors\";\nimport type {\n DbVersionWithLibrary,\n FindVersionResult,\n LibrarySummary,\n ScraperConfig,\n StoreSearchResult,\n VersionRef,\n VersionStatus,\n VersionSummary,\n} from \"./types\";\n\n/**\n * Provides semantic search capabilities across different versions of library documentation.\n * Uses content-type-specific pipelines for processing and splitting content.\n */\nexport class DocumentManagementService {\n private readonly store: DocumentStore;\n private readonly documentRetriever: DocumentRetrieverService;\n private readonly pipelines: ContentPipeline[];\n\n /**\n * Normalizes a version string, converting null or undefined to an empty string\n * and converting to lowercase.\n */\n private normalizeVersion(version?: string | null): string {\n return (version ?? \"\").toLowerCase();\n }\n\n constructor(\n embeddingConfig?: EmbeddingModelConfig | null,\n pipelineConfig?: PipelineConfiguration,\n ) {\n let dbPath: string;\n let dbDir: string;\n\n // 1. Check Environment Variable\n const envStorePath = process.env.DOCS_MCP_STORE_PATH;\n if (envStorePath) {\n dbDir = envStorePath;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`Using database directory from DOCS_MCP_STORE_PATH: ${dbDir}`);\n } else {\n // 2. Check Old Local Path\n const projectRoot = getProjectRoot();\n const oldDbDir = path.join(projectRoot, \".store\");\n const oldDbPath = path.join(oldDbDir, \"documents.db\");\n const oldDbExists = fs.existsSync(oldDbPath); // Check file existence specifically\n\n if (oldDbExists) {\n dbPath = oldDbPath;\n dbDir = oldDbDir;\n logger.debug(`Using legacy database path: ${dbPath}`);\n } else {\n // 3. Use Standard Path\n const standardPaths = envPaths(\"docs-mcp-server\", { suffix: \"\" });\n dbDir = standardPaths.data;\n dbPath = path.join(dbDir, \"documents.db\");\n logger.debug(`Using standard database directory: ${dbDir}`);\n }\n }\n\n // Ensure the chosen directory exists\n try {\n fs.mkdirSync(dbDir, { recursive: true });\n } catch (error) {\n // Log potential error during directory creation but proceed\n // The DocumentStore constructor might handle DB file creation errors\n logger.error(`⚠️ Failed to create database directory ${dbDir}: ${error}`);\n }\n\n this.store = new DocumentStore(dbPath, embeddingConfig);\n this.documentRetriever = new DocumentRetrieverService(this.store);\n\n // Initialize content pipelines for different content types including universal TextPipeline fallback\n this.pipelines = PipelineFactory.createStandardPipelines(pipelineConfig);\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 (either versioned or unversioned).\n * Throws LibraryNotFoundError with suggestions if the library is not found.\n * @param library The name of the library to validate.\n * @throws {LibraryNotFoundError} If the library does not exist.\n */\n async validateLibraryExists(library: string): Promise<void> {\n logger.info(`🔎 Validating existence of library: ${library}`);\n const normalizedLibrary = library.toLowerCase(); // Ensure consistent casing\n\n // Check for both versioned and unversioned documents\n const versions = await this.listVersions(normalizedLibrary);\n const hasUnversioned = await this.exists(normalizedLibrary, \"\"); // Check explicitly for unversioned\n\n if (versions.length === 0 && !hasUnversioned) {\n logger.warn(`⚠️ Library '${library}' not found.`);\n\n // Library doesn't exist, fetch all libraries to provide suggestions\n const allLibraries = await this.listLibraries();\n const libraryNames = allLibraries.map((lib) => lib.library);\n\n let suggestions: string[] = [];\n if (libraryNames.length > 0) {\n const fuse = new Fuse(libraryNames, {\n // Configure fuse.js options if needed (e.g., threshold)\n // isCaseSensitive: false, // Handled by normalizing library names\n // includeScore: true,\n threshold: 0.4, // Adjust threshold for desired fuzziness (0=exact, 1=match anything)\n });\n const results = fuse.search(normalizedLibrary);\n // Take top 3 suggestions\n suggestions = results.slice(0, 3).map((result) => result.item);\n logger.info(`🔍 Found suggestions: ${suggestions.join(\", \")}`);\n }\n\n throw new LibraryNotFoundError(library, suggestions);\n }\n\n logger.info(`✅ Library '${library}' confirmed to exist.`);\n }\n\n /**\n * Returns a list of all available semantic versions for a library.\n */\n async listVersions(library: string): Promise<string[]> {\n const versions = await this.store.queryUniqueVersions(library);\n return versions.filter((v) => semver.valid(v));\n }\n\n /**\n * Checks if documents exist for a given library and optional version.\n * If version is omitted, checks for documents without a specific version.\n */\n async exists(library: string, version?: string | null): Promise<boolean> {\n const normalizedVersion = this.normalizeVersion(version);\n return this.store.checkDocumentExists(library, normalizedVersion);\n }\n\n /**\n * Finds the most appropriate version of documentation based on the requested version.\n * When no target version is specified, returns the latest version.\n *\n * Version matching behavior:\n * - Exact versions (e.g., \"18.0.0\"): Matches that version or any earlier version\n * - X-Range patterns (e.g., \"5.x\", \"5.2.x\"): Matches within the specified range\n * - \"latest\" or no version: Returns the latest available version\n *\n * For documentation, we prefer matching older versions over no match at all,\n * since older docs are often still relevant and useful.\n * Also checks if unversioned documents exist for the library.\n */\n async findBestVersion(\n library: string,\n targetVersion?: string,\n ): Promise<FindVersionResult> {\n const libraryAndVersion = `${library}${targetVersion ? `@${targetVersion}` : \"\"}`;\n logger.info(`🔍 Finding best version for ${libraryAndVersion}`);\n\n // Check if unversioned documents exist *before* filtering for valid semver\n const hasUnversioned = await this.store.checkDocumentExists(library, \"\");\n const versionStrings = await this.listVersions(library);\n\n if (versionStrings.length === 0) {\n if (hasUnversioned) {\n logger.info(`ℹ️ Unversioned documents exist for ${library}`);\n return { bestMatch: null, hasUnversioned: true };\n }\n // Throw error only if NO versions (semver or unversioned) exist\n logger.warn(`⚠️ No valid versions found for ${library}`);\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n let bestMatch: string | null = null;\n\n if (!targetVersion || targetVersion === \"latest\") {\n bestMatch = semver.maxSatisfying(versionStrings, \"*\");\n } else {\n const versionRegex = /^(\\d+)(?:\\.(?:x(?:\\.x)?|\\d+(?:\\.(?:x|\\d+))?))?$|^$/;\n if (!versionRegex.test(targetVersion)) {\n logger.warn(`⚠️ Invalid target version format: ${targetVersion}`);\n // Don't throw yet, maybe unversioned exists\n } else {\n // Restore the previous logic with fallback\n let range = targetVersion;\n if (!semver.validRange(targetVersion)) {\n // If it's not a valid range (like '1.2' or '1'), treat it like a tilde range\n range = `~${targetVersion}`;\n } else if (semver.valid(targetVersion)) {\n // If it's an exact version, allow matching it OR any older version\n range = `${range} || <=${targetVersion}`;\n }\n // If it was already a valid range (like '1.x'), use it directly\n bestMatch = semver.maxSatisfying(versionStrings, range);\n }\n }\n\n if (bestMatch) {\n logger.info(`✅ Found best match version ${bestMatch} for ${libraryAndVersion}`);\n } else {\n logger.warn(`⚠️ No matching semver version found for ${libraryAndVersion}`);\n }\n\n // If no semver match found, but unversioned exists, return that info.\n // If a semver match was found, return it along with unversioned status.\n // If no semver match AND no unversioned, throw error.\n if (!bestMatch && !hasUnversioned) {\n // Fetch detailed versions to pass to the error constructor\n const allLibraryDetails = await this.store.queryLibraryVersions();\n const libraryDetails = allLibraryDetails.get(library) ?? [];\n throw new VersionNotFoundError(library, targetVersion ?? \"\", libraryDetails);\n }\n\n return { bestMatch, hasUnversioned };\n }\n\n /**\n * Removes all documents for a specific library and optional version.\n * If version is omitted, removes documents without a specific version.\n */\n async removeAllDocuments(library: string, version?: string | null): Promise<void> {\n const normalizedVersion = this.normalizeVersion(version);\n logger.info(\n `🗑️ Removing all documents from ${library}@${normalizedVersion || \"[no version]\"} store`,\n );\n const count = await this.store.deleteDocuments(library, normalizedVersion);\n logger.info(`🗑️ Deleted ${count} documents`);\n }\n\n /**\n * Completely removes a library version and all associated documents.\n * Also removes the library if no other versions remain.\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.info(`🗑️ Removing version: ${library}@${normalizedVersion || \"[no version]\"}`);\n\n const result = await this.store.removeVersion(library, normalizedVersion, true);\n\n logger.info(\n `🗑️ Removed ${result.documentsDeleted} documents, version: ${result.versionDeleted}, library: ${result.libraryDeleted}`,\n );\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 || \"[no version]\"}`);\n } else {\n logger.warn(\n `⚠️ Version ${library}@${normalizedVersion || \"[no version]\"} not found`,\n );\n }\n }\n\n /**\n * Adds a document to the store, splitting it into smaller chunks for better search results.\n * Uses SemanticMarkdownSplitter to maintain markdown structure and content types during splitting.\n * Preserves hierarchical structure of documents and distinguishes between text and code segments.\n * If version is omitted, the document is added without a specific version.\n */\n async addDocument(\n library: string,\n version: string | null | undefined,\n document: Document,\n ): Promise<void> {\n const processingStart = performance.now();\n const normalizedVersion = this.normalizeVersion(version);\n const url = document.metadata.url as string;\n\n if (!url || typeof url !== \"string\" || !url.trim()) {\n throw new StoreError(\"Document metadata must include a valid URL\");\n }\n\n logger.info(`📚 Adding document: ${document.metadata.title}`);\n\n if (!document.pageContent.trim()) {\n throw new Error(\"Document content cannot be empty\");\n }\n\n const contentType = document.metadata.mimeType as string | undefined;\n\n try {\n // Create a mock RawContent for pipeline selection\n const rawContent = {\n source: url,\n content: document.pageContent,\n mimeType: contentType || \"text/plain\",\n };\n\n // Find appropriate pipeline for content type\n const pipeline = this.pipelines.find((p) => p.canProcess(rawContent));\n\n if (!pipeline) {\n logger.warn(\n `⚠️ Unsupported content type \"${rawContent.mimeType}\" for document ${url}. Skipping processing.`,\n );\n return;\n }\n\n // Debug logging for pipeline selection\n logger.debug(\n `Selected ${pipeline.constructor.name} for content type \"${rawContent.mimeType}\" (${url})`,\n );\n\n // Use content-type-specific pipeline for processing and splitting\n // Create minimal scraper options for processing\n const scraperOptions = {\n url: url,\n library: library,\n version: normalizedVersion,\n scrapeMode: ScrapeMode.Fetch,\n ignoreErrors: false,\n maxConcurrency: 1,\n };\n\n const processed = await pipeline.process(rawContent, scraperOptions);\n const chunks = processed.chunks;\n\n // Convert semantic chunks to documents\n const splitDocs = chunks.map((chunk: ContentChunk) => ({\n pageContent: chunk.content,\n metadata: {\n ...document.metadata,\n level: chunk.section.level,\n path: chunk.section.path,\n },\n }));\n logger.info(`✂️ Split document into ${splitDocs.length} chunks`);\n\n // Add split documents to store\n await this.store.addDocuments(library, normalizedVersion, splitDocs);\n\n // Track successful document processing\n const processingTime = performance.now() - processingStart;\n analytics.track(TelemetryEvent.DOCUMENT_PROCESSED, {\n // Content characteristics (privacy-safe)\n mimeType: contentType || \"unknown\",\n contentSizeBytes: document.pageContent.length,\n\n // Processing metrics\n processingTimeMs: Math.round(processingTime),\n chunksCreated: splitDocs.length,\n\n // Document characteristics\n hasTitle: !!document.metadata.title,\n hasDescription: !!document.metadata.description,\n urlDomain: extractHostname(url),\n depth: document.metadata.depth,\n\n // Library context\n library,\n libraryVersion: normalizedVersion || null,\n\n // Processing efficiency\n avgChunkSizeBytes: Math.round(document.pageContent.length / splitDocs.length),\n processingSpeedKbPerSec: Math.round(\n document.pageContent.length / 1024 / (processingTime / 1000),\n ),\n });\n } catch (error) {\n // Track processing failures with native error tracking\n const processingTime = performance.now() - processingStart;\n\n if (error instanceof Error) {\n analytics.captureException(error, {\n mimeType: contentType || \"unknown\",\n contentSizeBytes: document.pageContent.length,\n processingTimeMs: Math.round(processingTime),\n library,\n libraryVersion: normalizedVersion || null,\n context: \"document_processing\",\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.resolveLibraryAndVersionIds(\n normalizedLibrary,\n normalizedVersion,\n );\n\n return versionId;\n }\n}\n","import { DocumentManagementClient } from \"./DocumentManagementClient\";\nimport { DocumentManagementService } from \"./DocumentManagementService\";\nimport type { EmbeddingModelConfig } from \"./embeddings/EmbeddingConfig\";\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(\n options: { serverUrl?: string; embeddingConfig?: EmbeddingModelConfig | null } = {},\n) {\n if (options.serverUrl) {\n const client = new DocumentManagementClient(options.serverUrl);\n await client.initialize();\n return client as IDocumentManagement;\n }\n const service = new DocumentManagementService(options.embeddingConfig);\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 embeddingConfig?: EmbeddingModelConfig | null,\n) {\n const service = new DocumentManagementService(embeddingConfig);\n await service.initialize();\n return service;\n}\n","/**\n * Default command - Starts unified server when no subcommand is specified.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n ensurePlaywrightBrowsersInstalled,\n parseAuthConfig,\n resolveEmbeddingContext,\n resolveProtocol,\n validateAuthConfig,\n validateHost,\n validatePort,\n warnHttpUsage,\n} from \"../utils\";\n\nexport function createDefaultAction(program: Command): Command {\n return (\n program\n .addOption(\n new Option(\"--protocol <protocol>\", \"Protocol for MCP server\")\n .choices([\"auto\", \"stdio\", \"http\"])\n .default(\"auto\"),\n )\n .addOption(\n new Option(\"--port <number>\", \"Port for the server\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.HTTP_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the server to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\"--resume\", \"Resume interrupted jobs on startup\", false)\n .option(\"--no-resume\", \"Do not resume jobs on startup\")\n .option(\n \"--read-only\",\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n false,\n )\n // Auth options\n .option(\n \"--auth-enabled\",\n \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n false,\n )\n .option(\"--auth-issuer-url <url>\", \"Issuer/discovery URL for OAuth2/OIDC provider\")\n .option(\n \"--auth-audience <id>\",\n \"JWT audience claim (identifies this protected resource)\",\n )\n .action(\n async (options: {\n protocol: string;\n port: string;\n host: string;\n resume: boolean;\n readOnly: boolean;\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n }) => {\n // Resolve protocol and validate flags\n const resolvedProtocol = resolveProtocol(options.protocol);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR); // Force quiet logging in stdio mode\n }\n\n logger.debug(\"No subcommand specified, starting unified server by default...\");\n const port = validatePort(options.port);\n const host = validateHost(options.host);\n\n // Parse and validate auth configuration\n const authConfig = parseAuthConfig({\n authEnabled: options.authEnabled,\n authIssuerUrl: options.authIssuerUrl,\n authAudience: options.authAudience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n warnHttpUsage(authConfig, port);\n }\n\n // Ensure browsers are installed\n ensurePlaywrightBrowsersInstalled();\n\n // Resolve embedding configuration for local execution (default action needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n const docService = await createLocalDocumentManagement(embeddingConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: options.resume || false, // Use --resume flag for job recovery\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(docService, pipelineOptions);\n\n if (resolvedProtocol === \"stdio\") {\n // Direct stdio mode - bypass AppServer entirely\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n\n await pipeline.start(); // Start pipeline for stdio mode\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = await startStdioServer(mcpTools, options.readOnly);\n\n // Register for graceful shutdown (stdio mode)\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {}); // Keep running forever\n } else {\n // HTTP mode - use AppServer\n logger.debug(`Auto-detected http protocol (TTY available)`);\n\n // Configure services based on resolved protocol\n const config = createAppServerConfig({\n enableWebInterface: true, // Enable web interface in http mode\n enableMcpServer: true, // Always enable MCP server\n enableApiServer: true, // Enable API (tRPC) in http mode\n enableWorker: true, // Always enable in-process worker for unified server\n port,\n host,\n readOnly: options.readOnly,\n auth: authConfig,\n startupContext: {\n cliCommand: \"default\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown (http mode)\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\n });\n\n await new Promise(() => {}); // Keep running forever\n }\n },\n )\n );\n}\n","/**\n * Fetch URL command - Fetches a URL and converts its content to Markdown.\n */\n\nimport type { Command } from \"commander\";\nimport { FileFetcher, HttpFetcher } from \"../../scraper/fetcher\";\nimport { ScrapeMode } from \"../../scraper/types\";\nimport { FetchUrlTool } from \"../../tools\";\nimport { parseHeaders } from \"../utils\";\n\nexport async function fetchUrlAction(\n url: string,\n options: { followRedirects: boolean; scrapeMode: ScrapeMode; header: string[] },\n) {\n const headers = parseHeaders(options.header);\n const fetchUrlTool = new FetchUrlTool(new HttpFetcher(), new FileFetcher());\n\n // Call the tool directly - tracking is now handled inside the tool\n const content = await fetchUrlTool.execute({\n url,\n followRedirects: options.followRedirects,\n scrapeMode: options.scrapeMode,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n });\n\n console.log(content);\n}\n\nexport function createFetchUrlCommand(program: Command): Command {\n return program\n .command(\"fetch-url <url>\")\n .description(\"Fetch a URL and convert its content to Markdown\")\n .option(\n \"--no-follow-redirects\",\n \"Disable following HTTP redirects (default: follow redirects)\",\n )\n .option(\n \"--scrape-mode <mode>\",\n `HTML processing strategy: '${ScrapeMode.Fetch}', '${ScrapeMode.Playwright}', '${ScrapeMode.Auto}' (default)`,\n (value: string): ScrapeMode => {\n const validModes = Object.values(ScrapeMode);\n if (!validModes.includes(value as ScrapeMode)) {\n console.warn(\n `Warning: Invalid scrape mode '${value}'. Using default '${ScrapeMode.Auto}'.`,\n );\n return ScrapeMode.Auto;\n }\n return value as ScrapeMode;\n },\n ScrapeMode.Auto,\n )\n .option(\n \"--header <name:value>\",\n \"Custom HTTP header to send with the request (can be specified multiple times)\",\n (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .action(fetchUrlAction);\n}\n","/**\n * Find version command - Finds the best matching version for a library.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { FindVersionTool } from \"../../tools\";\n\nexport async function findVersionAction(\n library: string,\n options: { version?: string; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Find version command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\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: options.version,\n });\n\n if (!versionInfo) throw new Error(\"Failed to get version information\");\n console.log(versionInfo);\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createFindVersionCommand(program: Command): Command {\n return program\n .command(\"find-version <library>\")\n .description(\"Find the best matching version for a library\")\n .option(\"-v, --version <string>\", \"Pattern to match (optional, supports ranges)\")\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(findVersionAction);\n}\n","/**\n * List command - Lists all available libraries and their versions.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { ListLibrariesTool } from \"../../tools\";\nimport { formatOutput } from \"../utils\";\n\nexport async function listAction(options: { serverUrl?: string }) {\n const { serverUrl } = options;\n\n // List command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\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 console.log(formatOutput(result.libraries));\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createListCommand(program: Command): Command {\n return program\n .command(\"list\")\n .description(\"List all available libraries and their versions\")\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(listAction);\n}\n","/**\n * MCP command - Starts MCP server only.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport { startStdioServer } from \"../../mcp/startStdioServer\";\nimport { initializeTools } from \"../../mcp/tools\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { LogLevel, logger, setLogLevel } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n parseAuthConfig,\n resolveEmbeddingContext,\n resolveProtocol,\n validateAuthConfig,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createMcpCommand(program: Command): Command {\n return (\n program\n .command(\"mcp\")\n .description(\"Start MCP server only\")\n .addOption(\n new Option(\"--protocol <protocol>\", \"Protocol for MCP server\")\n .choices([\"auto\", \"stdio\", \"http\"])\n .default(CLI_DEFAULTS.PROTOCOL),\n )\n .addOption(\n new Option(\"--port <number>\", \"Port for the MCP server\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.HTTP_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the MCP server to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .option(\n \"--read-only\",\n \"Run in read-only mode (only expose read tools, disable write/job tools)\",\n false,\n )\n // Auth options\n .option(\n \"--auth-enabled\",\n \"Enable OAuth2/OIDC authentication for MCP endpoints\",\n false,\n )\n .option(\"--auth-issuer-url <url>\", \"Issuer/discovery URL for OAuth2/OIDC provider\")\n .option(\n \"--auth-audience <id>\",\n \"JWT audience claim (identifies this protected resource)\",\n )\n .action(\n async (cmdOptions: {\n protocol: string;\n port: string;\n host: string;\n serverUrl?: string;\n readOnly: boolean;\n authEnabled?: boolean;\n authIssuerUrl?: string;\n authAudience?: string;\n }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n const serverUrl = cmdOptions.serverUrl;\n // Resolve protocol using same logic as default action\n const resolvedProtocol = resolveProtocol(cmdOptions.protocol);\n if (resolvedProtocol === \"stdio\") {\n setLogLevel(LogLevel.ERROR); // Force quiet logging in stdio mode\n }\n\n // Parse and validate auth configuration\n const authConfig = parseAuthConfig({\n authEnabled: cmdOptions.authEnabled,\n authIssuerUrl: cmdOptions.authIssuerUrl,\n authAudience: cmdOptions.authAudience,\n });\n\n if (authConfig) {\n validateAuthConfig(authConfig);\n }\n\n try {\n // Resolve embedding configuration for local execution\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n logger.error(\n \"❌ Embedding configuration is required for local mode. Configure an embedding provider with CLI options or environment variables.\",\n );\n process.exit(1);\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // MCP command doesn't support job recovery\n serverUrl,\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\n pipelineOptions,\n );\n\n if (resolvedProtocol === \"stdio\") {\n // Direct stdio mode - bypass AppServer entirely\n logger.debug(`Auto-detected stdio protocol (no TTY)`);\n logger.info(\"🚀 Starting MCP server (stdio mode)\");\n\n await pipeline.start(); // Start pipeline for stdio mode\n const mcpTools = await initializeTools(docService, pipeline);\n const mcpServer = await startStdioServer(mcpTools, cmdOptions.readOnly);\n\n // Register for graceful shutdown (stdio mode)\n registerGlobalServices({\n mcpStdioServer: mcpServer,\n docService,\n pipeline,\n });\n\n await new Promise(() => {}); // Keep running forever\n } else {\n // HTTP mode - use AppServer\n logger.debug(`Auto-detected http protocol (TTY available)`);\n logger.info(\"🚀 Starting MCP server (http mode)\");\n\n // Configure MCP-only server\n const config = createAppServerConfig({\n enableWebInterface: false, // Never enable web interface in mcp command\n enableMcpServer: true,\n enableApiServer: false, // Never enable API in mcp command\n enableWorker: !serverUrl,\n port,\n host,\n externalWorkerUrl: serverUrl,\n readOnly: cmdOptions.readOnly,\n auth: authConfig,\n startupContext: {\n cliCommand: \"mcp\",\n mcpProtocol: \"http\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown (http mode)\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\n });\n\n await new Promise(() => {}); // Keep running forever\n }\n } catch (error) {\n logger.error(`❌ Failed to start MCP server: ${error}`);\n process.exit(1);\n }\n },\n )\n );\n}\n","/**\n * Remove command - Removes documents for a specific library and version.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\n\nexport async function removeAction(\n library: string,\n options: { version?: string; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Remove command doesn't need embeddings - explicitly disable for local execution\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig: serverUrl ? undefined : null,\n });\n const { version } = options;\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 console.log(`✅ Successfully removed ${library}${version ? `@${version}` : \"\"}.`);\n } catch (error) {\n console.error(\n `❌ Failed to remove ${library}${version ? `@${version}` : \"\"}:`,\n error instanceof Error ? error.message : String(error),\n );\n throw error;\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createRemoveCommand(program: Command): Command {\n return program\n .command(\"remove <library>\")\n .description(\"Remove documents for a specific library and version\")\n .option(\n \"-v, --version <string>\",\n \"Version to remove (optional, removes unversioned if omitted)\",\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(removeAction);\n}\n","/**\n * Scrape command - Scrapes and indexes documentation from a URL or local folder.\n */\n\nimport type { Command } from \"commander\";\nimport 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 { ScrapeTool } from \"../../tools\";\nimport {\n DEFAULT_MAX_CONCURRENCY,\n DEFAULT_MAX_DEPTH,\n DEFAULT_MAX_PAGES,\n} from \"../../utils/config\";\nimport {\n createPipelineWithCallbacks,\n parseHeaders,\n resolveEmbeddingContext,\n} from \"../utils\";\n\nexport async function scrapeAction(\n library: string,\n url: string,\n options: {\n version?: string;\n maxPages: string;\n maxDepth: string;\n maxConcurrency: string;\n ignoreErrors: boolean;\n scope: string;\n followRedirects: boolean;\n scrapeMode: ScrapeMode;\n includePattern: string[];\n excludePattern: string[];\n header: string[];\n serverUrl?: string;\n },\n) {\n const serverUrl = options.serverUrl;\n\n // Resolve embedding configuration for local execution (scrape needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n throw new Error(\n \"Embedding configuration is required for local scraping. \" +\n \"Please set DOCS_MCP_EMBEDDING_MODEL environment variable or use --server-url for remote execution.\",\n );\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n let pipeline: IPipeline | null = null;\n\n try {\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false,\n concurrency: 1,\n serverUrl,\n };\n\n pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\n pipelineOptions,\n );\n await pipeline.start();\n const scrapeTool = new ScrapeTool(pipeline);\n\n const headers = parseHeaders(options.header);\n\n // Call the tool directly - tracking is now handled inside the tool\n const result = await scrapeTool.execute({\n url,\n library,\n version: options.version,\n options: {\n maxPages: Number.parseInt(options.maxPages, 10),\n maxDepth: Number.parseInt(options.maxDepth, 10),\n maxConcurrency: Number.parseInt(options.maxConcurrency, 10),\n ignoreErrors: options.ignoreErrors,\n scope: options.scope as \"subpages\" | \"hostname\" | \"domain\",\n followRedirects: options.followRedirects,\n scrapeMode: options.scrapeMode,\n includePatterns:\n Array.isArray(options.includePattern) && options.includePattern.length > 0\n ? options.includePattern\n : undefined,\n excludePatterns:\n Array.isArray(options.excludePattern) && options.excludePattern.length > 0\n ? options.excludePattern\n : undefined,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n },\n });\n\n if (\"pagesScraped\" in result) {\n console.log(`✅ Successfully scraped ${result.pagesScraped} pages`);\n } else {\n console.log(`🚀 Scraping job started with ID: ${result.jobId}`);\n }\n } finally {\n if (pipeline) await pipeline.stop();\n await docService.shutdown();\n }\n}\n\nexport function createScrapeCommand(program: Command): Command {\n return program\n .command(\"scrape <library> <url>\")\n .description(\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 .option(\"-v, --version <string>\", \"Version of the library (optional)\")\n .option(\n \"-p, --max-pages <number>\",\n \"Maximum pages to scrape\",\n DEFAULT_MAX_PAGES.toString(),\n )\n .option(\n \"-d, --max-depth <number>\",\n \"Maximum navigation depth\",\n DEFAULT_MAX_DEPTH.toString(),\n )\n .option(\n \"-c, --max-concurrency <number>\",\n \"Maximum concurrent page requests\",\n DEFAULT_MAX_CONCURRENCY.toString(),\n )\n .option(\"--ignore-errors\", \"Ignore errors during scraping\", true)\n .option(\n \"--scope <scope>\",\n \"Crawling boundary: 'subpages' (default), 'hostname', or 'domain'\",\n (value) => {\n const validScopes = [\"subpages\", \"hostname\", \"domain\"];\n if (!validScopes.includes(value)) {\n console.warn(`Warning: Invalid scope '${value}'. Using default 'subpages'.`);\n return \"subpages\";\n }\n return value;\n },\n \"subpages\",\n )\n .option(\n \"--no-follow-redirects\",\n \"Disable following HTTP redirects (default: follow redirects)\",\n )\n .option(\n \"--scrape-mode <mode>\",\n `HTML processing strategy: '${ScrapeMode.Fetch}', '${ScrapeMode.Playwright}', '${ScrapeMode.Auto}' (default)`,\n (value: string): ScrapeMode => {\n const validModes = Object.values(ScrapeMode);\n if (!validModes.includes(value as ScrapeMode)) {\n console.warn(\n `Warning: Invalid scrape mode '${value}'. Using default '${ScrapeMode.Auto}'.`,\n );\n return ScrapeMode.Auto;\n }\n return value as ScrapeMode;\n },\n ScrapeMode.Auto,\n )\n .option(\n \"--include-pattern <pattern>\",\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 (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--exclude-pattern <pattern>\",\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 (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--header <name:value>\",\n \"Custom HTTP header to send with each request (can be specified multiple times)\",\n (val: string, prev: string[] = []) => prev.concat([val]),\n [] as string[],\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(scrapeAction);\n}\n","/**\n * Search command - Searches documents in a library.\n */\n\nimport type { Command } from \"commander\";\nimport { createDocumentManagement } from \"../../store\";\nimport { SearchTool } from \"../../tools\";\nimport { formatOutput, resolveEmbeddingContext } from \"../utils\";\n\nexport async function searchAction(\n library: string,\n query: string,\n options: { version?: string; limit: string; exactMatch: boolean; serverUrl?: string },\n) {\n const serverUrl = options.serverUrl;\n\n // Resolve embedding configuration for local execution (search needs embeddings)\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n throw new Error(\n \"Embedding configuration is required for local search. \" +\n \"Please set DOCS_MCP_EMBEDDING_MODEL environment variable or use --server-url for remote execution.\",\n );\n }\n\n const docService = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\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: options.version,\n query,\n limit: Number.parseInt(options.limit, 10),\n exactMatch: options.exactMatch,\n });\n\n console.log(formatOutput(result.results));\n } finally {\n await docService.shutdown();\n }\n}\n\nexport function createSearchCommand(program: Command): Command {\n return program\n .command(\"search <library> <query>\")\n .description(\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 .option(\n \"-v, --version <string>\",\n \"Version of the library (optional, supports ranges)\",\n )\n .option(\"-l, --limit <number>\", \"Maximum number of results\", \"5\")\n .option(\"-e, --exact-match\", \"Only use exact version match (default: false)\", false)\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(searchAction);\n}\n","/**\n * Web command - Starts web interface only.\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createDocumentManagement } from \"../../store\";\nimport type { IDocumentManagement } from \"../../store/trpc/interfaces\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n resolveEmbeddingContext,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWebCommand(program: Command): Command {\n return program\n .command(\"web\")\n .description(\"Start web interface only\")\n .addOption(\n new Option(\"--port <number>\", \"Port for the web interface\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(CLI_DEFAULTS.WEB_PORT.toString()),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the web interface to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\n \"--server-url <url>\",\n \"URL of external pipeline worker RPC (e.g., http://localhost:6280/api)\",\n )\n .action(async (cmdOptions: { port: string; host: string; serverUrl?: string }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n const serverUrl = cmdOptions.serverUrl;\n\n try {\n // Resolve embedding configuration for local execution\n const embeddingConfig = resolveEmbeddingContext();\n if (!serverUrl && !embeddingConfig) {\n logger.error(\n \"❌ Embedding configuration is required for local mode. Configure an embedding provider with CLI options or environment variables.\",\n );\n process.exit(1);\n }\n\n const docService: IDocumentManagement = await createDocumentManagement({\n serverUrl,\n embeddingConfig,\n });\n const pipelineOptions: PipelineOptions = {\n recoverJobs: false, // Web command doesn't support job recovery\n serverUrl,\n concurrency: 3,\n };\n const pipeline = await createPipelineWithCallbacks(\n serverUrl ? undefined : (docService as unknown as never),\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,\n host,\n externalWorkerUrl: serverUrl,\n startupContext: {\n cliCommand: \"web\",\n },\n });\n\n logger.info(\n `🚀 Starting web interface${serverUrl ? ` connecting to worker at ${serverUrl}` : \"\"}`,\n );\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\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 * Worker command - Starts external pipeline worker (HTTP API).\n */\n\nimport type { Command } from \"commander\";\nimport { Option } from \"commander\";\nimport { startAppServer } from \"../../app\";\nimport type { PipelineOptions } from \"../../pipeline\";\nimport { createLocalDocumentManagement } from \"../../store\";\nimport { logger } from \"../../utils/logger\";\nimport { registerGlobalServices } from \"../main\";\nimport {\n CLI_DEFAULTS,\n createAppServerConfig,\n createPipelineWithCallbacks,\n ensurePlaywrightBrowsersInstalled,\n resolveEmbeddingContext,\n validateHost,\n validatePort,\n} from \"../utils\";\n\nexport function createWorkerCommand(program: Command): Command {\n return program\n .command(\"worker\")\n .description(\"Start external pipeline worker (HTTP API)\")\n .addOption(\n new Option(\"--port <number>\", \"Port for worker API\")\n .argParser((v) => {\n const n = Number(v);\n if (!Number.isInteger(n) || n < 1 || n > 65535) {\n throw new Error(\"Port must be an integer between 1 and 65535\");\n }\n return String(n);\n })\n .default(\"8080\"),\n )\n .addOption(\n new Option(\"--host <host>\", \"Host to bind the worker API to\")\n .argParser(validateHost)\n .default(CLI_DEFAULTS.HOST),\n )\n .option(\"--resume\", \"Resume interrupted jobs on startup\", true)\n .option(\"--no-resume\", \"Do not resume jobs on startup\")\n .action(async (cmdOptions: { port: string; host: string; resume: boolean }) => {\n const port = validatePort(cmdOptions.port);\n const host = validateHost(cmdOptions.host);\n\n try {\n logger.info(`🚀 Starting external pipeline worker on port ${port}`);\n\n // Ensure browsers are installed for scraping\n ensurePlaywrightBrowsersInstalled();\n\n // Resolve embedding configuration for worker (worker needs embeddings for indexing)\n const embeddingConfig = resolveEmbeddingContext();\n\n // Initialize services\n const docService = await createLocalDocumentManagement(embeddingConfig);\n const pipelineOptions: PipelineOptions = {\n recoverJobs: cmdOptions.resume, // Use the resume option\n concurrency: CLI_DEFAULTS.MAX_CONCURRENCY,\n };\n const pipeline = await createPipelineWithCallbacks(docService, pipelineOptions);\n\n // Configure worker-only server\n const config = createAppServerConfig({\n enableWebInterface: false,\n enableMcpServer: false,\n enableApiServer: true,\n enableWorker: true,\n port,\n host,\n startupContext: {\n cliCommand: \"worker\",\n },\n });\n\n const appServer = await startAppServer(docService, pipeline, config);\n\n // Register for graceful shutdown\n // Note: pipeline is managed by AppServer, so don't register it globally\n registerGlobalServices({\n appServer,\n docService,\n // pipeline is owned by AppServer - don't register globally to avoid double shutdown\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 * Main CLI setup and command registration.\n */\n\nimport { Command, Option } from \"commander\";\nimport packageJson from \"../../package.json\";\nimport {\n analytics,\n shouldEnableTelemetry,\n TelemetryConfig,\n TelemetryEvent,\n} from \"../telemetry\";\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 { 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 type { GlobalOptions } from \"./types\";\nimport { setupLogging } from \"./utils\";\n\n/**\n * Creates and configures the main CLI program with all commands.\n */\nexport function createCliProgram(): Command {\n const program = new Command();\n\n // Store command start times for duration tracking\n const commandStartTimes = new Map<string, number>();\n\n // Configure main program\n program\n .name(\"docs-mcp-server\")\n .description(\"Unified CLI, MCP Server, and Web Interface for Docs MCP Server.\")\n .version(packageJson.version)\n // Mutually exclusive logging flags\n .addOption(\n new Option(\"--verbose\", \"Enable verbose (debug) logging\").conflicts(\"silent\"),\n )\n .addOption(new Option(\"--silent\", \"Disable all logging except errors\"))\n .addOption(new Option(\"--no-telemetry\", \"Disable telemetry collection\"))\n .enablePositionalOptions()\n .allowExcessArguments(false)\n .showHelpAfterError(true);\n\n // Set up global options handling\n program.hook(\"preAction\", async (thisCommand, actionCommand) => {\n const globalOptions: GlobalOptions = thisCommand.opts();\n\n // Setup logging\n setupLogging(globalOptions);\n\n // Initialize telemetry if enabled\n if (shouldEnableTelemetry()) {\n // Set global context for CLI commands\n if (analytics.isEnabled()) {\n analytics.setGlobalContext({\n appVersion: packageJson.version,\n appPlatform: process.platform,\n appNodeVersion: process.version,\n appInterface: \"cli\",\n cliCommand: actionCommand.name(),\n });\n\n // Store command start time for duration tracking\n const commandKey = `${actionCommand.name()}-${Date.now()}`;\n commandStartTimes.set(commandKey, Date.now());\n // Store the key for retrieval in postAction\n (actionCommand as { _trackingKey?: string })._trackingKey = commandKey;\n }\n } else {\n TelemetryConfig.getInstance().disable();\n }\n });\n\n // Track CLI command completion\n program.hook(\"postAction\", async (_thisCommand, actionCommand) => {\n if (analytics.isEnabled()) {\n // Track CLI_COMMAND event for all CLI commands (standalone and server)\n const trackingKey = (actionCommand as { _trackingKey?: string })._trackingKey;\n const startTime = trackingKey ? commandStartTimes.get(trackingKey) : Date.now();\n const durationMs = startTime ? Date.now() - startTime : 0;\n\n // Clean up the tracking data\n if (trackingKey) {\n commandStartTimes.delete(trackingKey);\n }\n\n analytics.track(TelemetryEvent.CLI_COMMAND, {\n cliCommand: actionCommand.name(),\n success: true, // If we reach postAction, command succeeded\n durationMs,\n });\n\n await analytics.shutdown();\n }\n });\n\n // Register all commands\n createMcpCommand(program);\n createWebCommand(program);\n createWorkerCommand(program);\n createScrapeCommand(program);\n createSearchCommand(program);\n createListCommand(program);\n createFindVersionCommand(program);\n createRemoveCommand(program);\n createFetchUrlCommand(program);\n\n // Set default action for when no subcommand is specified\n createDefaultAction(program);\n\n return program;\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 type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport type { AppServer } from \"../app\";\nimport type { IPipeline } from \"../pipeline\";\nimport {\n ModelConfigurationError,\n UnsupportedProviderError,\n} from \"../store/embeddings/EmbeddingFactory\";\nimport type { IDocumentManagement } from \"../store/trpc/interfaces\";\nimport { analytics } from \"../telemetry\";\nimport { logger } from \"../utils/logger\";\nimport { createCliProgram } from \"./index\";\n\n// Module-level variables for active services and shutdown state\nlet activeAppServer: AppServer | null = null;\nlet activeMcpStdioServer: McpServer | null = null;\nlet activeDocService: IDocumentManagement | null = null;\nlet activePipelineManager: IPipeline | null = null;\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 if (activeAppServer) {\n logger.debug(\"SIGINT: Stopping AppServer...\");\n await activeAppServer.stop();\n activeAppServer = null;\n logger.debug(\"SIGINT: AppServer stopped.\");\n }\n\n if (activeMcpStdioServer) {\n logger.debug(\"SIGINT: Stopping MCP server...\");\n await activeMcpStdioServer.close();\n activeMcpStdioServer = 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 if (activePipelineManager && !activeAppServer) {\n await activePipelineManager.stop();\n activePipelineManager = null;\n logger.debug(\"SIGINT: PipelineManager stopped.\");\n }\n\n if (activeDocService) {\n await activeDocService.shutdown();\n activeDocService = null;\n logger.debug(\"SIGINT: DocumentManagementService shut down.\");\n }\n\n // Analytics shutdown is handled by AppServer.stop() above\n // Only shutdown analytics if no AppServer was running\n if (!activeAppServer && analytics.isEnabled()) {\n await analytics.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 analytics.shutdown();\n\n // Avoid hanging processes by explicitly exiting\n process.exit(0);\n }\n}\n\n/**\n * Registers global services for shutdown handling\n */\nexport function registerGlobalServices(services: {\n appServer?: AppServer;\n mcpStdioServer?: McpServer;\n docService?: IDocumentManagement;\n pipeline?: IPipeline;\n}): 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}\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 program = createCliProgram();\n\n // Track if a command was executed\n program.hook(\"preAction\", () => {\n commandExecuted = true;\n });\n\n await program.parseAsync(process.argv);\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 if (activeAppServer) {\n shutdownPromises.push(\n activeAppServer\n .stop()\n .then(() => {\n activeAppServer = null;\n })\n .catch((e) => logger.error(`❌ Error stopping AppServer: ${e}`)),\n );\n }\n\n if (activeMcpStdioServer) {\n shutdownPromises.push(\n activeMcpStdioServer\n .close()\n .then(() => {\n activeMcpStdioServer = null;\n })\n .catch((e) => logger.error(`❌ Error stopping MCP server: ${e}`)),\n );\n }\n\n if (activePipelineManager && !activeAppServer) {\n shutdownPromises.push(\n activePipelineManager\n .stop()\n .then(() => {\n activePipelineManager = null;\n })\n .catch((e) => logger.error(`❌ Error stopping pipeline: ${e}`)),\n );\n }\n\n if (activeDocService) {\n shutdownPromises.push(\n activeDocService\n .shutdown()\n .then(() => {\n activeDocService = 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 if (commandExecuted && !activeAppServer) {\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 if (activeAppServer) {\n logger.debug(\"Shutting down AppServer...\");\n shutdownPromises.push(\n activeAppServer.stop().then(() => {\n activeAppServer = null;\n logger.debug(\"AppServer shut down.\");\n }),\n );\n }\n\n if (activePipelineManager && !activeAppServer) {\n shutdownPromises.push(\n activePipelineManager.stop().then(() => {\n activePipelineManager = null;\n logger.debug(\"PipelineManager stopped.\");\n }),\n );\n }\n\n if (activeDocService) {\n shutdownPromises.push(\n activeDocService.shutdown().then(() => {\n activeDocService = 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 activeAppServer = null;\n if (!wasAlreadyShuttingDown) {\n isShuttingDown = false;\n }\n }\n });\n}\n","import \"dotenv/config\";\nimport { runCli } from \"./cli/main\";\nimport { ensurePlaywrightBrowsersInstalled } from \"./cli/utils\";\n\n// Ensure Playwright browsers are installed\nensurePlaywrightBrowsersInstalled();\n\n// Run the CLI\nrunCli().catch((error) => {\n console.error(`🔥 Fatal error in main execution: ${error}`);\n process.exit(1);\n});\n"],"names":["VersionStatus","name","TelemetryEvent","analytics","baseUrl","config","version","DEFAULT_MAX_DEPTH","fs","path","chunks","window","StructuralNodeType","STRUCTURAL_DECL_TYPES","CONTENT_DECL_TYPES","isCandidateBoundary","isLocalHelper","findDocumentationStart","extractName","classifyBoundaryKind","unified","ScrapeMode","URL","item","PipelineFactory","content","PipelineJobStatus","document","uuidv4","job","error","semver","t","z","packageJson","type","parseHeaders","AppServer","projectRoot"],"mappings":"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"}
|