/** * Unified Index Manager - Combines local, GitHub, and collection portfolio indexing * * Features: * - Unified search across local, GitHub, and collection portfolios * - Intelligent result merging and deduplication * - Version conflict detection and resolution * - Performance optimization with parallel indexing * - Advanced fallback strategies for resilient operation * - Comprehensive search capabilities with pagination * - Smart result ranking and duplicate detection */ import { PortfolioIndexManager } from './PortfolioIndexManager.js'; import { GitHubPortfolioIndexer } from './GitHubPortfolioIndexer.js'; import { CollectionIndexCache } from '../cache/CollectionIndexCache.js'; import { GitHubClient } from '../collection/GitHubClient.js'; import { APICache } from '../cache/APICache.js'; import { ElementType } from './types.js'; import { FileOperationsService } from '../services/FileOperationsService.js'; import { LRUCache } from '../cache/LRUCache.js'; import { PerformanceMonitor } from '../utils/PerformanceMonitor.js'; import { ElementSource } from '../config/sourcePriority.js'; /** * Unified search options for searching across local, GitHub, and collection portfolios */ export interface UnifiedSearchOptions { /** Search query string */ query: string; /** * Include local portfolio in search (default: true) * * Local portfolio contains elements stored on the local filesystem in ~/.dollhouse/portfolio/. * This is typically the primary source for user's custom elements. */ includeLocal?: boolean; /** * Include GitHub portfolio in search (default: true) * * GitHub portfolio contains elements stored in the user's GitHub dollhouse-portfolio repository. * This provides backup and sync capabilities for elements. */ includeGitHub?: boolean; /** * Include collection in search (default: false) * * The collection contains community-contributed elements from the DollhouseMCP collection repository. * * **Why default is false:** * - Collection searches require network access and are slower than local/GitHub searches * - Most searches are for user's own elements, not community elements * - Reduces unnecessary network traffic and API rate limit usage * - Users can explicitly opt-in when browsing community content * * **When to use includeCollection: true:** * - Browsing or searching for community-contributed elements * - Looking for examples or templates from the collection * - Checking for updates to collection-sourced elements * - Using the browse/search collection features */ includeCollection?: boolean; /** Filter by specific element type */ elementType?: ElementType; /** Page number for pagination (default: 1) */ page?: number; /** Number of results per page (default: 20) */ pageSize?: number; /** Sort order for results (default: 'relevance') */ sortBy?: 'relevance' | 'source' | 'name' | 'version'; /** Enable result streaming for large result sets */ streamResults?: boolean; /** Pagination cursor for streaming results */ cursor?: string; /** Hard limit on total results returned */ maxResults?: number; /** Enable lazy loading of result details */ lazyLoad?: boolean; /** * Force search all enabled sources, ignoring stopOnFirst optimization (default: false) * * When true, searches all enabled sources even if earlier sources return results. * Useful for: * - Checking for duplicates across sources * - Finding all versions of an element * - Comprehensive searches where you need complete results */ includeAll?: boolean; /** * Prefer a specific source to search first * * Overrides the default priority order to search the specified source first. * Other sources are searched in default priority order after the preferred source. */ preferredSource?: ElementSource; /** * Custom source priority order * * Completely overrides the default source priority order. * Sources are searched in the order specified. */ sourcePriority?: ElementSource[]; } export interface VersionConflict { local?: string; github?: string; collection?: string; recommended: 'local' | 'github' | 'collection'; reason: string; } export interface DuplicateInfo { name: string; elementType: ElementType; sources: Array<{ source: 'local' | 'github' | 'collection'; version?: string; lastModified: Date; path?: string; }>; hasVersionConflict: boolean; versionConflict?: VersionConflict; } export interface VersionInfo { name: string; elementType: ElementType; versions: { local?: { version: string; lastModified: Date; path: string; }; github?: { version: string; lastModified: Date; path: string; }; collection?: { version: string; lastModified: Date; path: string; }; }; recommended: { source: 'local' | 'github' | 'collection'; reason: string; }; updateAvailable: boolean; updateFrom?: 'local' | 'github' | 'collection'; } export interface UnifiedIndexEntry { name: string; description?: string; version?: string; author?: string; elementType: ElementType; lastModified: Date; source: 'local' | 'github' | 'collection'; localFilePath?: string; filename?: string; tags?: string[]; keywords?: string[]; triggers?: string[]; category?: string; githubPath?: string; githubSha?: string; githubHtmlUrl?: string; githubDownloadUrl?: string; githubSize?: number; collectionPath?: string; collectionSha?: string; collectionTags?: string[]; collectionCategory?: string; collectionLicense?: string; } export interface UnifiedSearchResult { source: 'local' | 'github' | 'collection'; entry: UnifiedIndexEntry; matchType: string; score: number; version?: string; isDuplicate?: boolean; versionConflict?: VersionConflict; cursor?: string; } export interface StreamedSearchResult { results: UnifiedSearchResult[]; hasMore: boolean; nextCursor?: string; totalEstimate?: number; processingTimeMs: number; } export interface UnifiedIndexStats { local: { totalElements: number; elementsByType: Record; lastBuilt: Date | null; isStale: boolean; }; github: { totalElements: number; elementsByType: Record; lastFetched: Date | null; isStale: boolean; username?: string; repository?: string; }; collection: { totalElements: number; elementsByType: Record; lastFetched: Date | null; isStale: boolean; version?: string; }; combined: { totalElements: number; uniqueElements: number; duplicates: number; }; performance: { averageSearchTime: number; cacheHitRate: number; lastOptimized: Date | null; }; } export interface UnifiedIndexManagerDependencies { portfolioIndexManager: PortfolioIndexManager; githubIndexer: GitHubPortfolioIndexer; collectionIndexCache: CollectionIndexCache; githubClient: GitHubClient; apiCache: APICache; rateLimitTracker: Map; performanceMonitor: PerformanceMonitor; fileOperations: FileOperationsService; resultCache?: LRUCache; indexCache?: LRUCache; } export declare class UnifiedIndexManager { private localIndexManager; private githubIndexer; private collectionIndexCache; private githubClient; private performanceMonitor; private resultCache; private indexCache; private readonly BATCH_SIZE; private readonly MAX_CONCURRENT_SOURCES; private ownsLocalIndexManager; private ownsGithubIndexer; private ownsCollectionIndexCache; private ownsPerformanceMonitor; private ownsResultCache; private ownsIndexCache; private readonly sourcePriorityConfig; private static readonly SOURCE_ENUM_TO_STRING_MAP; private static readonly SOURCE_STRING_TO_ENUM_MAP; private readonly sourceAvailabilityCache; private readonly sourceUsageTelemetry; constructor(dependencies: UnifiedIndexManagerDependencies); /** * Enhanced search across local, GitHub, and collection portfolios with performance optimization * * Implements source priority-based search (Issue #1446): * - Sources are searched sequentially in priority order (local → github → collection by default) * - Early termination when stopOnFirst is enabled and results are found * - Configurable priority order via sourcePriority or preferredSource options * - includeAll option forces search of all sources for comprehensive results */ search(searchOptions: UnifiedSearchOptions): Promise; /** * Normalize search options (extracted from search()) */ private normalizeSearchOptions; /** * Log security event for search operation (extracted from search()) */ private logSearchSecurityEvent; /** * Check search cache and return cached results if available (extracted from search()) */ private checkSearchCache; /** * Perform priority-based search across sources (extracted from search()) * * This is the core implementation of source priority search (Issue #1446): * - Searches sources sequentially in priority order * - Supports early termination with stopOnFirst * - Handles fallback on error * - Records telemetry for each source */ private performPriorityBasedSearch; /** * Search a single source and update results (extracted from performPriorityBasedSearch()) */ private searchSingleSource; /** * Check memory usage and trigger cleanup if needed (extracted from performPriorityBasedSearch()) */ private checkMemoryUsage; /** * Process final results: apply processing, pagination, caching, and metrics (extracted from search()) */ private processFinalResults; /** * Find element by name across all portfolios */ findByName(name: string, options?: Partial): Promise; /** * Get elements by type from all portfolios */ getElementsByType(elementType: ElementType, options?: Partial): Promise; /** * Check for duplicates across all sources */ checkDuplicates(name: string): Promise; /** * Get version comparison across all sources */ getVersionComparison(name: string): Promise; /** * Get comprehensive statistics across all sources */ getStats(): Promise; /** * Invalidate caches after user actions with performance monitoring */ invalidateAfterAction(action: string): void; /** * Force rebuild of all indexes with performance optimization */ rebuildAll(): Promise; /** * Search with fallback strategies for resilient operation */ private searchWithFallback; /** * Handle search fallback strategies */ private handleSearchFallback; /** * Search local portfolio */ private searchLocal; /** * Search local with stale data fallback */ private searchLocalStale; /** * Search GitHub portfolio */ private searchGitHub; /** * Search GitHub with cached data fallback */ private searchGitHubCached; /** * Search collection portfolio */ private searchCollection; /** * Search collection with cached data fallback */ private searchCollectionCached; /** * Process search results with advanced features */ private processSearchResults; /** * Apply smart result ranking */ private applySmartRanking; /** * Detect duplicates and version conflicts */ private detectDuplicatesAndConflicts; /** * Apply pagination to results */ private applyPagination; /** * Apply sorting to results */ private applySorting; /** * Calculate match score for GitHub entries */ private calculateGitHubMatchScore; /** * Calculate match score for collection entries */ private calculateCollectionMatchScore; /** * Get all elements by type across sources */ private getAllElementsByType; /** * Get local elements by type */ private getLocalElementsByType; /** * Get GitHub elements by type */ private getGitHubElementsByType; /** * Get collection elements by type */ private getCollectionElementsByType; /** * Get local portfolio statistics */ private getLocalStats; /** * Get GitHub portfolio statistics */ private getGitHubStats; /** * Get collection portfolio statistics */ private getCollectionStats; /** * Calculate duplicates count across all sources * * Identifies elements that exist in multiple sources (local, GitHub, collection) */ private calculateDuplicatesCount; /** * Convert local index entry to unified format */ private convertLocalEntry; /** * Convert GitHub index entry to unified format */ private convertGitHubEntry; /** * Convert collection index entry to unified format */ private convertCollectionEntry; /** * Map string to ElementType enum */ private mapStringToElementType; /** * Convert unified search options to local search options */ private convertToLocalOptions; /** * Determine match type for GitHub entries */ private determineMatchType; /** * Determine match type for collection entries */ private determineCollectionMatchType; /** * Get path from unified entry */ private getPathFromEntry; /** * Detect version conflict from sources */ private detectVersionConflict; /** * Detect version conflict from search results */ private detectVersionConflictFromResults; /** * Determine version recommendation from version info */ private determineVersionRecommendation; /** * Determine version recommendation from sources */ private determineVersionRecommendationFromSources; /** * Compare semantic versions */ private compareVersions; /** * Remove duplicate results based on name and type */ private deduplicateResults; /** * Remove duplicate entries based on name and type */ private deduplicateEntries; /** * Stream search results for better performance with large datasets */ private streamSearch; /** * Process search results with memory-efficient batching */ private processSearchResultsOptimized; /** * Get enabled search sources */ private getEnabledSources; /** * Batch sources for concurrent processing */ private batchSources; /** * Generate cursor for pagination */ private generateCursor; /** * Trigger memory cleanup when usage is high */ private triggerMemoryCleanup; /** * Record search performance metrics */ private recordSearchMetrics; /** * Create cache key for search options */ private createCacheKey; /** * Initialize and validate source priority configuration * * Extracts configuration initialization into a separate method for testability * and adds validation to ensure configuration is valid. * * @returns Validated source priority configuration * @throws Error if configuration is invalid */ private initializeSourcePriorityConfig; /** * Validate source priority configuration * * @param config - Configuration to validate * @throws Error if configuration is invalid */ private validateSourcePriorityConfig; /** * Get enabled sources in priority order based on search options * * This method determines which sources to search and in what order based on: * 1. Custom sourcePriority override in options * 2. Preferred source override in options * 3. Default configuration priority order * * Then filters to only include sources that are enabled in the search options. * * @param options - Search options * @returns Array of enabled source names in priority order */ private getEnabledSourcesByPriority; /** * Determine source priority order from options (extracted from getEnabledSourcesByPriority) * * @param options - Search options * @returns Priority order as ElementSource array */ private determinePriorityOrder; /** * Filter sources to only those that are enabled (extracted from getEnabledSourcesByPriority) * * @param priorityOrder - Priority order to filter * @param options - Search options * @returns Filtered array of enabled source strings */ private filterEnabledSources; /** * Check if a source is enabled in search options * * Caches results for performance optimization. * * @param source - Source to check * @param options - Search options * @returns true if source is enabled */ private isSourceEnabled; /** * Clear the source availability cache * * Used for testing and when source availability changes */ private clearSourceAvailabilityCache; /** * Convert ElementSource enum to source string * * Uses optimized lookup table for O(1) performance. * * @param source - ElementSource enum value * @returns Source string ('local', 'github', or 'collection') * @throws Error if source is invalid */ private mapSourceEnumToString; /** * Convert source string to ElementSource enum * * Uses optimized lookup table for O(1) performance. * * @param source - Source string * @returns ElementSource enum value * @throws Error if source is invalid */ private mapSourceStringToEnum; /** * Record source usage telemetry * * Tracks search patterns for optimization insights. * * @param source - Source that was used * @param resultCount - Number of results returned * @param duration - Duration in milliseconds */ private recordSourceUsage; /** * Get source usage telemetry statistics * * @returns Map of source usage statistics */ getSourceUsageTelemetry(): Map; /** * Reset source usage telemetry * * Used for testing and periodic cleanup */ resetSourceUsageTelemetry(): void; /** * Get performance statistics */ getPerformanceStats(): { searchStats: any; memoryStats: any; cacheStats: any; trends: any; }; dispose(): void; } //# sourceMappingURL=UnifiedIndexManager.d.ts.map