@danielsimonjr/memoryjs 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +385 -113
- package/README.md.backup-1768084780988 +266 -0
- package/dist/index.cjs +17364 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12371 -0
- package/dist/index.d.ts +12367 -11
- package/dist/index.js +17102 -19
- package/dist/index.js.map +1 -1
- package/dist/workers/levenshteinWorker.cjs +102 -0
- package/dist/workers/levenshteinWorker.cjs.map +1 -0
- package/dist/workers/levenshteinWorker.js +57 -91
- package/dist/workers/levenshteinWorker.js.map +1 -1
- package/package.json +12 -6
- package/dist/core/EntityManager.d.ts +0 -268
- package/dist/core/EntityManager.d.ts.map +0 -1
- package/dist/core/EntityManager.js +0 -512
- package/dist/core/EntityManager.js.map +0 -1
- package/dist/core/GraphEventEmitter.d.ts +0 -202
- package/dist/core/GraphEventEmitter.d.ts.map +0 -1
- package/dist/core/GraphEventEmitter.js +0 -347
- package/dist/core/GraphEventEmitter.js.map +0 -1
- package/dist/core/GraphStorage.d.ts +0 -395
- package/dist/core/GraphStorage.d.ts.map +0 -1
- package/dist/core/GraphStorage.js +0 -786
- package/dist/core/GraphStorage.js.map +0 -1
- package/dist/core/GraphTraversal.d.ts +0 -141
- package/dist/core/GraphTraversal.d.ts.map +0 -1
- package/dist/core/GraphTraversal.js +0 -574
- package/dist/core/GraphTraversal.js.map +0 -1
- package/dist/core/HierarchyManager.d.ts +0 -111
- package/dist/core/HierarchyManager.d.ts.map +0 -1
- package/dist/core/HierarchyManager.js +0 -225
- package/dist/core/HierarchyManager.js.map +0 -1
- package/dist/core/ManagerContext.d.ts +0 -76
- package/dist/core/ManagerContext.d.ts.map +0 -1
- package/dist/core/ManagerContext.js +0 -129
- package/dist/core/ManagerContext.js.map +0 -1
- package/dist/core/ObservationManager.d.ts +0 -85
- package/dist/core/ObservationManager.d.ts.map +0 -1
- package/dist/core/ObservationManager.js +0 -124
- package/dist/core/ObservationManager.js.map +0 -1
- package/dist/core/RelationManager.d.ts +0 -131
- package/dist/core/RelationManager.d.ts.map +0 -1
- package/dist/core/RelationManager.js +0 -212
- package/dist/core/RelationManager.js.map +0 -1
- package/dist/core/SQLiteStorage.d.ts +0 -354
- package/dist/core/SQLiteStorage.d.ts.map +0 -1
- package/dist/core/SQLiteStorage.js +0 -919
- package/dist/core/SQLiteStorage.js.map +0 -1
- package/dist/core/StorageFactory.d.ts +0 -45
- package/dist/core/StorageFactory.d.ts.map +0 -1
- package/dist/core/StorageFactory.js +0 -65
- package/dist/core/StorageFactory.js.map +0 -1
- package/dist/core/TransactionManager.d.ts +0 -464
- package/dist/core/TransactionManager.d.ts.map +0 -1
- package/dist/core/TransactionManager.js +0 -869
- package/dist/core/TransactionManager.js.map +0 -1
- package/dist/core/index.d.ts +0 -17
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -20
- package/dist/core/index.js.map +0 -1
- package/dist/features/AnalyticsManager.d.ts +0 -44
- package/dist/features/AnalyticsManager.d.ts.map +0 -1
- package/dist/features/AnalyticsManager.js +0 -224
- package/dist/features/AnalyticsManager.js.map +0 -1
- package/dist/features/ArchiveManager.d.ts +0 -133
- package/dist/features/ArchiveManager.d.ts.map +0 -1
- package/dist/features/ArchiveManager.js +0 -282
- package/dist/features/ArchiveManager.js.map +0 -1
- package/dist/features/CompressionManager.d.ts +0 -119
- package/dist/features/CompressionManager.d.ts.map +0 -1
- package/dist/features/CompressionManager.js +0 -470
- package/dist/features/CompressionManager.js.map +0 -1
- package/dist/features/IOManager.d.ts +0 -225
- package/dist/features/IOManager.d.ts.map +0 -1
- package/dist/features/IOManager.js +0 -1093
- package/dist/features/IOManager.js.map +0 -1
- package/dist/features/KeywordExtractor.d.ts +0 -61
- package/dist/features/KeywordExtractor.d.ts.map +0 -1
- package/dist/features/KeywordExtractor.js +0 -127
- package/dist/features/KeywordExtractor.js.map +0 -1
- package/dist/features/ObservationNormalizer.d.ts +0 -90
- package/dist/features/ObservationNormalizer.d.ts.map +0 -1
- package/dist/features/ObservationNormalizer.js +0 -194
- package/dist/features/ObservationNormalizer.js.map +0 -1
- package/dist/features/StreamingExporter.d.ts +0 -128
- package/dist/features/StreamingExporter.d.ts.map +0 -1
- package/dist/features/StreamingExporter.js +0 -212
- package/dist/features/StreamingExporter.js.map +0 -1
- package/dist/features/TagManager.d.ts +0 -147
- package/dist/features/TagManager.d.ts.map +0 -1
- package/dist/features/TagManager.js +0 -211
- package/dist/features/TagManager.js.map +0 -1
- package/dist/features/index.d.ts +0 -14
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/index.js +0 -15
- package/dist/features/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/search/BM25Search.d.ts +0 -148
- package/dist/search/BM25Search.d.ts.map +0 -1
- package/dist/search/BM25Search.js +0 -340
- package/dist/search/BM25Search.js.map +0 -1
- package/dist/search/BasicSearch.d.ts +0 -51
- package/dist/search/BasicSearch.d.ts.map +0 -1
- package/dist/search/BasicSearch.js +0 -138
- package/dist/search/BasicSearch.js.map +0 -1
- package/dist/search/BooleanSearch.d.ts +0 -98
- package/dist/search/BooleanSearch.d.ts.map +0 -1
- package/dist/search/BooleanSearch.js +0 -431
- package/dist/search/BooleanSearch.js.map +0 -1
- package/dist/search/EarlyTerminationManager.d.ts +0 -140
- package/dist/search/EarlyTerminationManager.d.ts.map +0 -1
- package/dist/search/EarlyTerminationManager.js +0 -280
- package/dist/search/EarlyTerminationManager.js.map +0 -1
- package/dist/search/EmbeddingCache.d.ts +0 -175
- package/dist/search/EmbeddingCache.d.ts.map +0 -1
- package/dist/search/EmbeddingCache.js +0 -247
- package/dist/search/EmbeddingCache.js.map +0 -1
- package/dist/search/EmbeddingService.d.ts +0 -277
- package/dist/search/EmbeddingService.d.ts.map +0 -1
- package/dist/search/EmbeddingService.js +0 -531
- package/dist/search/EmbeddingService.js.map +0 -1
- package/dist/search/FuzzySearch.d.ts +0 -118
- package/dist/search/FuzzySearch.d.ts.map +0 -1
- package/dist/search/FuzzySearch.js +0 -313
- package/dist/search/FuzzySearch.js.map +0 -1
- package/dist/search/HybridScorer.d.ts +0 -181
- package/dist/search/HybridScorer.d.ts.map +0 -1
- package/dist/search/HybridScorer.js +0 -258
- package/dist/search/HybridScorer.js.map +0 -1
- package/dist/search/HybridSearchManager.d.ts +0 -80
- package/dist/search/HybridSearchManager.d.ts.map +0 -1
- package/dist/search/HybridSearchManager.js +0 -188
- package/dist/search/HybridSearchManager.js.map +0 -1
- package/dist/search/IncrementalIndexer.d.ts +0 -201
- package/dist/search/IncrementalIndexer.d.ts.map +0 -1
- package/dist/search/IncrementalIndexer.js +0 -343
- package/dist/search/IncrementalIndexer.js.map +0 -1
- package/dist/search/OptimizedInvertedIndex.d.ts +0 -163
- package/dist/search/OptimizedInvertedIndex.d.ts.map +0 -1
- package/dist/search/OptimizedInvertedIndex.js +0 -359
- package/dist/search/OptimizedInvertedIndex.js.map +0 -1
- package/dist/search/ParallelSearchExecutor.d.ts +0 -172
- package/dist/search/ParallelSearchExecutor.d.ts.map +0 -1
- package/dist/search/ParallelSearchExecutor.js +0 -310
- package/dist/search/ParallelSearchExecutor.js.map +0 -1
- package/dist/search/QuantizedVectorStore.d.ts +0 -171
- package/dist/search/QuantizedVectorStore.d.ts.map +0 -1
- package/dist/search/QuantizedVectorStore.js +0 -308
- package/dist/search/QuantizedVectorStore.js.map +0 -1
- package/dist/search/QueryAnalyzer.d.ts +0 -76
- package/dist/search/QueryAnalyzer.d.ts.map +0 -1
- package/dist/search/QueryAnalyzer.js +0 -228
- package/dist/search/QueryAnalyzer.js.map +0 -1
- package/dist/search/QueryCostEstimator.d.ts +0 -244
- package/dist/search/QueryCostEstimator.d.ts.map +0 -1
- package/dist/search/QueryCostEstimator.js +0 -653
- package/dist/search/QueryCostEstimator.js.map +0 -1
- package/dist/search/QueryPlanCache.d.ts +0 -220
- package/dist/search/QueryPlanCache.d.ts.map +0 -1
- package/dist/search/QueryPlanCache.js +0 -380
- package/dist/search/QueryPlanCache.js.map +0 -1
- package/dist/search/QueryPlanner.d.ts +0 -58
- package/dist/search/QueryPlanner.d.ts.map +0 -1
- package/dist/search/QueryPlanner.js +0 -138
- package/dist/search/QueryPlanner.js.map +0 -1
- package/dist/search/RankedSearch.d.ts +0 -71
- package/dist/search/RankedSearch.d.ts.map +0 -1
- package/dist/search/RankedSearch.js +0 -239
- package/dist/search/RankedSearch.js.map +0 -1
- package/dist/search/ReflectionManager.d.ts +0 -120
- package/dist/search/ReflectionManager.d.ts.map +0 -1
- package/dist/search/ReflectionManager.js +0 -232
- package/dist/search/ReflectionManager.js.map +0 -1
- package/dist/search/SavedSearchManager.d.ts +0 -79
- package/dist/search/SavedSearchManager.d.ts.map +0 -1
- package/dist/search/SavedSearchManager.js +0 -147
- package/dist/search/SavedSearchManager.js.map +0 -1
- package/dist/search/SearchFilterChain.d.ts +0 -120
- package/dist/search/SearchFilterChain.d.ts.map +0 -1
- package/dist/search/SearchFilterChain.js +0 -186
- package/dist/search/SearchFilterChain.js.map +0 -1
- package/dist/search/SearchManager.d.ts +0 -326
- package/dist/search/SearchManager.d.ts.map +0 -1
- package/dist/search/SearchManager.js +0 -454
- package/dist/search/SearchManager.js.map +0 -1
- package/dist/search/SearchSuggestions.d.ts +0 -27
- package/dist/search/SearchSuggestions.d.ts.map +0 -1
- package/dist/search/SearchSuggestions.js +0 -58
- package/dist/search/SearchSuggestions.js.map +0 -1
- package/dist/search/SemanticSearch.d.ts +0 -149
- package/dist/search/SemanticSearch.d.ts.map +0 -1
- package/dist/search/SemanticSearch.js +0 -324
- package/dist/search/SemanticSearch.js.map +0 -1
- package/dist/search/SymbolicSearch.d.ts +0 -61
- package/dist/search/SymbolicSearch.d.ts.map +0 -1
- package/dist/search/SymbolicSearch.js +0 -164
- package/dist/search/SymbolicSearch.js.map +0 -1
- package/dist/search/TFIDFEventSync.d.ts +0 -85
- package/dist/search/TFIDFEventSync.d.ts.map +0 -1
- package/dist/search/TFIDFEventSync.js +0 -134
- package/dist/search/TFIDFEventSync.js.map +0 -1
- package/dist/search/TFIDFIndexManager.d.ts +0 -151
- package/dist/search/TFIDFIndexManager.d.ts.map +0 -1
- package/dist/search/TFIDFIndexManager.js +0 -433
- package/dist/search/TFIDFIndexManager.js.map +0 -1
- package/dist/search/VectorStore.d.ts +0 -235
- package/dist/search/VectorStore.d.ts.map +0 -1
- package/dist/search/VectorStore.js +0 -312
- package/dist/search/VectorStore.js.map +0 -1
- package/dist/search/index.d.ts +0 -35
- package/dist/search/index.d.ts.map +0 -1
- package/dist/search/index.js +0 -53
- package/dist/search/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -13
- package/dist/types/index.js.map +0 -1
- package/dist/types/types.d.ts +0 -1811
- package/dist/types/types.d.ts.map +0 -1
- package/dist/types/types.js +0 -10
- package/dist/types/types.js.map +0 -1
- package/dist/utils/BatchProcessor.d.ts +0 -271
- package/dist/utils/BatchProcessor.d.ts.map +0 -1
- package/dist/utils/BatchProcessor.js +0 -377
- package/dist/utils/BatchProcessor.js.map +0 -1
- package/dist/utils/MemoryMonitor.d.ts +0 -176
- package/dist/utils/MemoryMonitor.d.ts.map +0 -1
- package/dist/utils/MemoryMonitor.js +0 -306
- package/dist/utils/MemoryMonitor.js.map +0 -1
- package/dist/utils/WorkerPoolManager.d.ts +0 -233
- package/dist/utils/WorkerPoolManager.d.ts.map +0 -1
- package/dist/utils/WorkerPoolManager.js +0 -421
- package/dist/utils/WorkerPoolManager.js.map +0 -1
- package/dist/utils/compressedCache.d.ts +0 -221
- package/dist/utils/compressedCache.d.ts.map +0 -1
- package/dist/utils/compressedCache.js +0 -349
- package/dist/utils/compressedCache.js.map +0 -1
- package/dist/utils/compressionUtil.d.ts +0 -214
- package/dist/utils/compressionUtil.d.ts.map +0 -1
- package/dist/utils/compressionUtil.js +0 -248
- package/dist/utils/compressionUtil.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -245
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -253
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/entityUtils.d.ts +0 -379
- package/dist/utils/entityUtils.d.ts.map +0 -1
- package/dist/utils/entityUtils.js +0 -649
- package/dist/utils/entityUtils.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -95
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -146
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/formatters.d.ts +0 -145
- package/dist/utils/formatters.d.ts.map +0 -1
- package/dist/utils/formatters.js +0 -133
- package/dist/utils/formatters.js.map +0 -1
- package/dist/utils/index.d.ts +0 -26
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -88
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/indexes.d.ts +0 -270
- package/dist/utils/indexes.d.ts.map +0 -1
- package/dist/utils/indexes.js +0 -527
- package/dist/utils/indexes.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -31
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -41
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/operationUtils.d.ts +0 -124
- package/dist/utils/operationUtils.d.ts.map +0 -1
- package/dist/utils/operationUtils.js +0 -176
- package/dist/utils/operationUtils.js.map +0 -1
- package/dist/utils/parallelUtils.d.ts +0 -76
- package/dist/utils/parallelUtils.d.ts.map +0 -1
- package/dist/utils/parallelUtils.js +0 -192
- package/dist/utils/parallelUtils.js.map +0 -1
- package/dist/utils/schemas.d.ts +0 -556
- package/dist/utils/schemas.d.ts.map +0 -1
- package/dist/utils/schemas.js +0 -485
- package/dist/utils/schemas.js.map +0 -1
- package/dist/utils/searchAlgorithms.d.ts +0 -99
- package/dist/utils/searchAlgorithms.d.ts.map +0 -1
- package/dist/utils/searchAlgorithms.js +0 -168
- package/dist/utils/searchAlgorithms.js.map +0 -1
- package/dist/utils/searchCache.d.ts +0 -108
- package/dist/utils/searchCache.d.ts.map +0 -1
- package/dist/utils/searchCache.js +0 -210
- package/dist/utils/searchCache.js.map +0 -1
- package/dist/utils/taskScheduler.d.ts +0 -294
- package/dist/utils/taskScheduler.d.ts.map +0 -1
- package/dist/utils/taskScheduler.js +0 -487
- package/dist/utils/taskScheduler.js.map +0 -1
- package/dist/workers/index.d.ts +0 -12
- package/dist/workers/index.d.ts.map +0 -1
- package/dist/workers/index.js +0 -10
- package/dist/workers/index.js.map +0 -1
- package/dist/workers/levenshteinWorker.d.ts +0 -60
- package/dist/workers/levenshteinWorker.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../node_modules/tsup/assets/cjs_shims.js","../src/utils/errors.ts","../src/utils/constants.ts","../src/utils/compressionUtil.ts","../src/utils/compressedCache.ts","../src/utils/logger.ts","../src/utils/searchAlgorithms.ts","../src/utils/indexes.ts","../src/utils/searchCache.ts","../src/utils/schemas.ts","../src/utils/formatters.ts","../src/utils/entityUtils.ts","../src/utils/parallelUtils.ts","../src/utils/taskScheduler.ts","../src/utils/operationUtils.ts","../src/utils/WorkerPoolManager.ts","../src/utils/BatchProcessor.ts","../src/utils/MemoryMonitor.ts","../src/core/GraphStorage.ts","../src/features/IOManager.ts","../src/features/StreamingExporter.ts","../src/core/TransactionManager.ts","../src/core/GraphEventEmitter.ts","../src/core/SQLiteStorage.ts","../src/core/EntityManager.ts","../src/core/RelationManager.ts","../src/core/ObservationManager.ts","../src/core/HierarchyManager.ts","../src/core/ManagerContext.ts","../src/core/StorageFactory.ts","../src/core/GraphTraversal.ts","../src/search/SearchFilterChain.ts","../src/search/BasicSearch.ts","../src/search/TFIDFIndexManager.ts","../src/search/RankedSearch.ts","../src/search/BooleanSearch.ts","../src/search/FuzzySearch.ts","../src/search/SearchSuggestions.ts","../src/search/SavedSearchManager.ts","../src/search/QueryCostEstimator.ts","../src/search/SearchManager.ts","../src/search/EmbeddingService.ts","../src/search/EmbeddingCache.ts","../src/search/IncrementalIndexer.ts","../src/search/VectorStore.ts","../src/search/SemanticSearch.ts","../src/search/TFIDFEventSync.ts","../src/search/SymbolicSearch.ts","../src/search/HybridSearchManager.ts","../src/search/QueryAnalyzer.ts","../src/search/QueryPlanner.ts","../src/search/ReflectionManager.ts","../src/search/BM25Search.ts","../src/search/OptimizedInvertedIndex.ts","../src/search/HybridScorer.ts","../src/search/ParallelSearchExecutor.ts","../src/search/EarlyTerminationManager.ts","../src/search/QueryPlanCache.ts","../src/search/QuantizedVectorStore.ts","../src/features/TagManager.ts","../src/features/AnalyticsManager.ts","../src/features/CompressionManager.ts","../src/features/ArchiveManager.ts","../src/features/ObservationNormalizer.ts","../src/features/KeywordExtractor.ts"],"sourcesContent":["/**\n * MemoryJS - Knowledge Graph Storage Library\n *\n * A high-performance TypeScript library for knowledge graph storage\n * with hierarchical organization, advanced search, and event-driven architecture.\n *\n * @packageDocumentation\n * @module @danielsimonjr/memoryjs\n */\n\n// Export all types\nexport * from './types/index.js';\n\n// Export all utilities\nexport * from './utils/index.js';\n\n// Export core infrastructure\nexport * from './core/index.js';\n\n// Export feature managers\nexport * from './features/index.js';\n\n// Export search system\nexport * from './search/index.js';\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () => \n typeof document === \"undefined\" \n ? new URL(`file:${__filename}`).href \n : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT') \n ? document.currentScript.src \n : new URL(\"main.js\", document.baseURI).href;\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","/**\r\n * Custom Error Types\r\n *\r\n * Defines custom error classes for better error handling and debugging.\r\n *\r\n * @module utils/errors\r\n */\r\n\r\n/**\r\n * Base error class for all knowledge graph errors.\r\n * Extends the native Error class with additional context.\r\n */\r\nexport class KnowledgeGraphError extends Error {\r\n constructor(message: string, public readonly code?: string) {\r\n super(message);\r\n this.name = 'KnowledgeGraphError';\r\n // Maintains proper stack trace in V8 engines\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, this.constructor);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an entity is not found.\r\n */\r\nexport class EntityNotFoundError extends KnowledgeGraphError {\r\n constructor(entityName: string) {\r\n super(`Entity \"${entityName}\" not found`, 'ENTITY_NOT_FOUND');\r\n this.name = 'EntityNotFoundError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a relation is not found.\r\n */\r\nexport class RelationNotFoundError extends KnowledgeGraphError {\r\n constructor(from: string, to: string, relationType?: string) {\r\n const desc = relationType\r\n ? `Relation \"${from}\" --[${relationType}]--> \"${to}\"`\r\n : `Relation from \"${from}\" to \"${to}\"`;\r\n super(`${desc} not found`, 'RELATION_NOT_FOUND');\r\n this.name = 'RelationNotFoundError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when attempting to create a duplicate entity.\r\n */\r\nexport class DuplicateEntityError extends KnowledgeGraphError {\r\n constructor(entityName: string) {\r\n super(`Entity \"${entityName}\" already exists`, 'DUPLICATE_ENTITY');\r\n this.name = 'DuplicateEntityError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when validation fails.\r\n */\r\nexport class ValidationError extends KnowledgeGraphError {\r\n constructor(\r\n message: string,\r\n public readonly errors: string[]\r\n ) {\r\n super(message, 'VALIDATION_ERROR');\r\n this.name = 'ValidationError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a cycle is detected in hierarchies.\r\n */\r\nexport class CycleDetectedError extends KnowledgeGraphError {\r\n constructor(entityName: string, parentName: string) {\r\n super(\r\n `Setting parent \"${parentName}\" for entity \"${entityName}\" would create a cycle`,\r\n 'CYCLE_DETECTED'\r\n );\r\n this.name = 'CycleDetectedError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an invalid importance value is provided.\r\n */\r\nexport class InvalidImportanceError extends KnowledgeGraphError {\r\n constructor(value: number, min: number = 0, max: number = 10) {\r\n super(\r\n `Importance must be between ${min} and ${max}, got ${value}`,\r\n 'INVALID_IMPORTANCE'\r\n );\r\n this.name = 'InvalidImportanceError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a file operation fails.\r\n */\r\nexport class FileOperationError extends KnowledgeGraphError {\r\n constructor(\r\n operation: string,\r\n filePath: string,\r\n cause?: Error\r\n ) {\r\n super(\r\n `Failed to ${operation} file: ${filePath}${cause ? ` - ${cause.message}` : ''}`,\r\n 'FILE_OPERATION_ERROR'\r\n );\r\n this.name = 'FileOperationError';\r\n if (cause) {\r\n this.cause = cause;\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an import operation fails.\r\n */\r\nexport class ImportError extends KnowledgeGraphError {\r\n constructor(format: string, message: string) {\r\n super(`Import failed (${format}): ${message}`, 'IMPORT_ERROR');\r\n this.name = 'ImportError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when an export operation fails.\r\n */\r\nexport class ExportError extends KnowledgeGraphError {\r\n constructor(format: string, message: string) {\r\n super(`Export failed (${format}): ${message}`, 'EXPORT_ERROR');\r\n this.name = 'ExportError';\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when insufficient entities are provided for an operation.\r\n */\r\nexport class InsufficientEntitiesError extends KnowledgeGraphError {\r\n constructor(operation: string, required: number, provided: number) {\r\n super(\r\n `${operation} requires at least ${required} entities, got ${provided}`,\r\n 'INSUFFICIENT_ENTITIES'\r\n );\r\n this.name = 'InsufficientEntitiesError';\r\n }\r\n}\r\n\r\n/**\r\n * Phase 9B: Error thrown when an operation is cancelled via AbortSignal.\r\n *\r\n * @example\r\n * ```typescript\r\n * const controller = new AbortController();\r\n * try {\r\n * await manager.createEntities(entities, { signal: controller.signal });\r\n * } catch (error) {\r\n * if (error instanceof OperationCancelledError) {\r\n * console.log('Operation was cancelled');\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport class OperationCancelledError extends KnowledgeGraphError {\r\n constructor(operation?: string) {\r\n const message = operation\r\n ? `Operation '${operation}' was cancelled`\r\n : 'Operation was cancelled';\r\n super(message, 'OPERATION_CANCELLED');\r\n this.name = 'OperationCancelledError';\r\n }\r\n}\r\n","/**\r\n * Application Constants\r\n *\r\n * Centralized configuration constants for file paths, extensions, and default values.\r\n *\r\n * @module utils/constants\r\n */\r\n\r\n/**\r\n * File extensions used by the memory system.\r\n */\r\nexport const FILE_EXTENSIONS = {\r\n /** JSONL format for line-delimited JSON storage */\r\n JSONL: '.jsonl',\r\n /** Legacy JSON format (backward compatibility) */\r\n JSON: '.json',\r\n} as const;\r\n\r\n/**\r\n * File name suffixes for auxiliary data files.\r\n * These suffixes are appended to the base memory file name.\r\n */\r\nexport const FILE_SUFFIXES = {\r\n /** Suffix for saved searches file */\r\n SAVED_SEARCHES: '-saved-searches',\r\n /** Suffix for tag aliases file */\r\n TAG_ALIASES: '-tag-aliases',\r\n} as const;\r\n\r\n/**\r\n * Default file names used by the memory system.\r\n */\r\nexport const DEFAULT_FILE_NAMES = {\r\n /** Default memory file name */\r\n MEMORY: 'memory',\r\n /** Legacy memory file name (for backward compatibility) */\r\n MEMORY_LEGACY: 'memory',\r\n} as const;\r\n\r\n/**\r\n * Environment variable names used for configuration.\r\n */\r\nexport const ENV_VARS = {\r\n /** Environment variable for custom memory file path */\r\n MEMORY_FILE_PATH: 'MEMORY_FILE_PATH',\r\n} as const;\r\n\r\n/**\r\n * Default base directory relative to the compiled code.\r\n */\r\nexport const DEFAULT_BASE_DIR = '../';\r\n\r\n/**\r\n * Log message prefixes for consistent logging.\r\n */\r\nexport const LOG_PREFIXES = {\r\n /** Informational message prefix */\r\n INFO: '[INFO]',\r\n /** Error message prefix */\r\n ERROR: '[ERROR]',\r\n /** Warning message prefix */\r\n WARN: '[WARN]',\r\n} as const;\r\n\r\n/**\r\n * Similarity scoring weights for duplicate detection.\r\n * These weights determine the relative importance of each factor\r\n * when calculating entity similarity for duplicate detection.\r\n * Total weights must sum to 1.0 (100%).\r\n */\r\nexport const SIMILARITY_WEIGHTS = {\r\n /** Name similarity weight (40%) - Uses Levenshtein distance */\r\n NAME: 0.4,\r\n /** Entity type match weight (20%) - Exact match required */\r\n TYPE: 0.2,\r\n /** Observation overlap weight (30%) - Uses Jaccard similarity */\r\n OBSERVATIONS: 0.3,\r\n /** Tag overlap weight (10%) - Uses Jaccard similarity */\r\n TAGS: 0.1,\r\n} as const;\r\n\r\n/**\r\n * Default threshold for duplicate detection (80% similarity required).\r\n */\r\nexport const DEFAULT_DUPLICATE_THRESHOLD = 0.8;\r\n\r\n/**\r\n * Search result limits to prevent resource exhaustion.\r\n */\r\nexport const SEARCH_LIMITS = {\r\n /** Default number of results to return */\r\n DEFAULT: 50,\r\n /** Maximum number of results allowed */\r\n MAX: 200,\r\n /** Minimum number of results (must be at least 1) */\r\n MIN: 1,\r\n} as const;\r\n\r\n/**\r\n * Entity importance range validation constants.\r\n * Importance is used to prioritize entities (0 = lowest, 10 = highest).\r\n */\r\nexport const IMPORTANCE_RANGE = {\r\n /** Minimum importance value */\r\n MIN: 0,\r\n /** Maximum importance value */\r\n MAX: 10,\r\n} as const;\r\n\r\n/**\r\n * Graph size limits to prevent resource exhaustion and ensure performance.\r\n * These limits help maintain system stability and responsiveness.\r\n */\r\nexport const GRAPH_LIMITS = {\r\n /** Maximum number of entities in the graph */\r\n MAX_ENTITIES: 100000,\r\n /** Maximum number of relations in the graph */\r\n MAX_RELATIONS: 1000000,\r\n /** Maximum graph file size in megabytes */\r\n MAX_FILE_SIZE_MB: 500,\r\n /** Maximum observations per entity */\r\n MAX_OBSERVATIONS_PER_ENTITY: 1000,\r\n /** Maximum tags per entity */\r\n MAX_TAGS_PER_ENTITY: 100,\r\n} as const;\r\n\r\n/**\r\n * Query complexity limits to prevent expensive query operations.\r\n * These limits protect against denial-of-service through complex queries.\r\n */\r\nexport const QUERY_LIMITS = {\r\n /** Maximum nesting depth for boolean queries */\r\n MAX_DEPTH: 10,\r\n /** Maximum number of terms in a single query */\r\n MAX_TERMS: 50,\r\n /** Maximum number of boolean operators (AND/OR/NOT) */\r\n MAX_OPERATORS: 20,\r\n /** Maximum query string length */\r\n MAX_QUERY_LENGTH: 5000,\r\n} as const;\r\n\r\n/**\r\n * Brotli compression configuration constants.\r\n * Brotli is built into Node.js >=11.7.0 via the zlib module.\r\n * No external dependencies required.\r\n *\r\n * Quality levels determine compression ratio vs speed tradeoff:\r\n * - Lower values (0-4): Faster compression, lower ratio\r\n * - Higher values (9-11): Slower compression, higher ratio\r\n */\r\nexport const COMPRESSION_CONFIG = {\r\n // Quality levels (0-11)\r\n /** Fast compression for real-time entity writes (quality 4) */\r\n BROTLI_QUALITY_REALTIME: 4,\r\n /** Balanced compression for exports and imports (quality 6) */\r\n BROTLI_QUALITY_BATCH: 6,\r\n /** Maximum compression for backups and archives (quality 11) */\r\n BROTLI_QUALITY_ARCHIVE: 11,\r\n /** Fast decompress for cache compression (quality 5) */\r\n BROTLI_QUALITY_CACHE: 5,\r\n\r\n // Auto-compression thresholds (in bytes)\r\n /** Auto-compress exports larger than 100KB */\r\n AUTO_COMPRESS_EXPORT_SIZE: 100 * 1024,\r\n /** Auto-compress MCP responses larger than 256KB */\r\n AUTO_COMPRESS_RESPONSE_SIZE: 256 * 1024,\r\n /** Always compress backups by default */\r\n AUTO_COMPRESS_BACKUP: true,\r\n\r\n // File extension for compressed files\r\n /** Brotli compressed file extension */\r\n BROTLI_EXTENSION: '.br',\r\n\r\n // Performance tuning\r\n /** Chunk size for streaming compression (64KB) */\r\n COMPRESSION_CHUNK_SIZE: 65536,\r\n /** Default window size for brotli (lgwin parameter) */\r\n COMPRESSION_WINDOW_SIZE: 22,\r\n} as const;\r\n\r\n/**\r\n * Type representing valid brotli quality levels used in the application.\r\n */\r\nexport type CompressionQuality =\r\n | typeof COMPRESSION_CONFIG.BROTLI_QUALITY_REALTIME\r\n | typeof COMPRESSION_CONFIG.BROTLI_QUALITY_BATCH\r\n | typeof COMPRESSION_CONFIG.BROTLI_QUALITY_ARCHIVE\r\n | typeof COMPRESSION_CONFIG.BROTLI_QUALITY_CACHE;\r\n\r\n// ==================== Semantic Search Configuration (Phase 4 Sprint 10-12) ====================\r\n\r\n/**\r\n * Environment variable names for embedding configuration.\r\n */\r\nexport const EMBEDDING_ENV_VARS = {\r\n /** Embedding provider: 'openai', 'local', or 'none' (default: 'none') */\r\n PROVIDER: 'MEMORY_EMBEDDING_PROVIDER',\r\n /** OpenAI API key (required when provider is 'openai') */\r\n OPENAI_API_KEY: 'MEMORY_OPENAI_API_KEY',\r\n /** Optional model override for the embedding service */\r\n MODEL: 'MEMORY_EMBEDDING_MODEL',\r\n /** Auto-index entities on creation: 'true' or 'false' (default: 'false') */\r\n AUTO_INDEX: 'MEMORY_AUTO_INDEX_EMBEDDINGS',\r\n} as const;\r\n\r\n/**\r\n * Default embedding configuration values.\r\n */\r\nexport const EMBEDDING_DEFAULTS = {\r\n /** Default provider (disabled by default) */\r\n PROVIDER: 'none' as const,\r\n /** Default OpenAI model for embeddings (1536 dimensions) */\r\n OPENAI_MODEL: 'text-embedding-3-small',\r\n /** Default local model for embeddings (384 dimensions) */\r\n LOCAL_MODEL: 'Xenova/all-MiniLM-L6-v2',\r\n /** OpenAI embedding dimensions for text-embedding-3-small */\r\n OPENAI_DIMENSIONS: 1536,\r\n /** Local embedding dimensions for all-MiniLM-L6-v2 */\r\n LOCAL_DIMENSIONS: 384,\r\n /** Maximum texts per batch for OpenAI */\r\n OPENAI_MAX_BATCH_SIZE: 2048,\r\n /** Default batch size for embedding operations */\r\n DEFAULT_BATCH_SIZE: 100,\r\n /** Whether to auto-index entities by default */\r\n AUTO_INDEX: false,\r\n} as const;\r\n\r\n/**\r\n * Semantic search configuration limits.\r\n */\r\nexport const SEMANTIC_SEARCH_LIMITS = {\r\n /** Default number of results for semantic search */\r\n DEFAULT_LIMIT: 10,\r\n /** Maximum number of results for semantic search */\r\n MAX_LIMIT: 100,\r\n /** Minimum similarity score for results (0.0-1.0) */\r\n MIN_SIMILARITY: 0.0,\r\n} as const;\r\n\r\n/**\r\n * OpenAI API configuration.\r\n */\r\nexport const OPENAI_API_CONFIG: {\r\n BASE_URL: string;\r\n EMBEDDINGS_ENDPOINT: string;\r\n MAX_RETRIES: number;\r\n INITIAL_BACKOFF_MS: number;\r\n MAX_BACKOFF_MS: number;\r\n} = {\r\n /** Base URL for OpenAI API */\r\n BASE_URL: 'https://api.openai.com/v1',\r\n /** Embeddings endpoint */\r\n EMBEDDINGS_ENDPOINT: '/embeddings',\r\n /** Maximum retries for rate limiting */\r\n MAX_RETRIES: 3,\r\n /** Initial backoff delay in milliseconds */\r\n INITIAL_BACKOFF_MS: 1000,\r\n /** Maximum backoff delay in milliseconds */\r\n MAX_BACKOFF_MS: 10000,\r\n};\r\n\r\n/**\r\n * Get embedding configuration from environment variables.\r\n *\r\n * @returns EmbeddingConfig object with values from environment or defaults\r\n */\r\nexport function getEmbeddingConfig(): {\r\n provider: 'openai' | 'local' | 'none';\r\n apiKey?: string;\r\n model?: string;\r\n autoIndex: boolean;\r\n} {\r\n const provider = (process.env[EMBEDDING_ENV_VARS.PROVIDER] || EMBEDDING_DEFAULTS.PROVIDER) as 'openai' | 'local' | 'none';\r\n const apiKey = process.env[EMBEDDING_ENV_VARS.OPENAI_API_KEY];\r\n const model = process.env[EMBEDDING_ENV_VARS.MODEL];\r\n const autoIndex = process.env[EMBEDDING_ENV_VARS.AUTO_INDEX] === 'true';\r\n\r\n return { provider, apiKey, model, autoIndex };\r\n}\r\n\r\n// ==================== Streaming Export Configuration (Phase 7 Sprint 1) ====================\r\n\r\n/**\r\n * Streaming export configuration.\r\n *\r\n * Controls when to use streaming mode and buffer sizes for optimal memory usage.\r\n */\r\nexport const STREAMING_CONFIG = {\r\n /** Minimum entity count to trigger streaming mode */\r\n STREAMING_THRESHOLD: 5000,\r\n /** Chunk size for batched streaming operations */\r\n CHUNK_SIZE: 500,\r\n /** High water mark for stream buffers (bytes) */\r\n HIGH_WATER_MARK: 64 * 1024,\r\n /** Flush interval for long-running streams (ms) */\r\n FLUSH_INTERVAL_MS: 100,\r\n} as const;\r\n","/**\r\n * Compression Utility Module\r\n *\r\n * Provides brotli compression and decompression utilities using Node.js\r\n * built-in zlib module. No external dependencies required.\r\n *\r\n * Brotli offers 15-20% better compression than gzip, with 60-75%\r\n * compression typical for JSON data.\r\n *\r\n * @module utils/compressionUtil\r\n */\r\n\r\nimport { brotliCompress, brotliDecompress, constants } from 'zlib';\r\nimport { promisify } from 'util';\r\nimport { promises as fs } from 'fs';\r\nimport { COMPRESSION_CONFIG } from './constants.js';\r\n\r\n// Promisify Node.js zlib functions for async/await usage\r\nconst compressAsync = promisify(brotliCompress);\r\nconst decompressAsync = promisify(brotliDecompress);\r\n\r\n/**\r\n * Options for compression operations.\r\n */\r\nexport interface CompressionOptions {\r\n /**\r\n * Brotli quality level (0-11).\r\n * Higher values = better compression but slower.\r\n * @default 6\r\n */\r\n quality?: number;\r\n\r\n /**\r\n * Window size (10-24) for the LZ77 algorithm.\r\n * Larger windows = better compression for large files.\r\n * @default 22\r\n */\r\n lgwin?: number;\r\n\r\n /**\r\n * Compression mode hint.\r\n * - 'text': Optimized for UTF-8 text\r\n * - 'generic': General-purpose compression\r\n * @default 'generic'\r\n */\r\n mode?: 'text' | 'generic';\r\n}\r\n\r\n/**\r\n * Result of a compression operation.\r\n */\r\nexport interface CompressionResult {\r\n /** The compressed data as a Buffer */\r\n compressed: Buffer;\r\n /** Original size in bytes */\r\n originalSize: number;\r\n /** Compressed size in bytes */\r\n compressedSize: number;\r\n /** Compression ratio (compressedSize / originalSize). Lower is better. */\r\n ratio: number;\r\n}\r\n\r\n/**\r\n * Metadata about a compressed file for storage alongside the compressed data.\r\n */\r\nexport interface CompressionMetadata {\r\n /** Whether the data is compressed */\r\n compressed: boolean;\r\n /** Compression format used */\r\n compressionFormat: 'brotli' | 'none';\r\n /** Original size before compression in bytes */\r\n originalSize: number;\r\n /** Size after compression in bytes */\r\n compressedSize: number;\r\n /** Optional checksum of original data for integrity verification */\r\n originalChecksum?: string;\r\n /** ISO 8601 timestamp when compression was performed */\r\n createdAt: string;\r\n}\r\n\r\n/**\r\n * Check if a file path indicates brotli compression based on extension.\r\n *\r\n * Note: Brotli doesn't have reliable magic bytes for detection.\r\n * Using file extension (.br) is the recommended detection method.\r\n *\r\n * @param filePath - The file path to check\r\n * @returns True if the path ends with .br extension\r\n *\r\n * @example\r\n * ```typescript\r\n * hasBrotliExtension('backup.jsonl.br') // true\r\n * hasBrotliExtension('backup.jsonl') // false\r\n * hasBrotliExtension('data.json') // false\r\n * ```\r\n */\r\nexport function hasBrotliExtension(filePath: string): boolean {\r\n return filePath.endsWith(COMPRESSION_CONFIG.BROTLI_EXTENSION);\r\n}\r\n\r\n/**\r\n * Compress data using brotli algorithm.\r\n *\r\n * @param data - The data to compress (string or Buffer)\r\n * @param options - Compression options\r\n * @returns Compression result with compressed data and statistics\r\n *\r\n * @example\r\n * ```typescript\r\n * const jsonData = JSON.stringify({ entities: [...] });\r\n * const result = await compress(jsonData, { quality: 11 });\r\n * console.log(`Compressed from ${result.originalSize} to ${result.compressedSize} bytes`);\r\n * console.log(`Compression ratio: ${(result.ratio * 100).toFixed(1)}%`);\r\n * ```\r\n */\r\nexport async function compress(\r\n data: Buffer | string,\r\n options: CompressionOptions = {}\r\n): Promise<CompressionResult> {\r\n const input = Buffer.isBuffer(data) ? data : Buffer.from(data, 'utf-8');\r\n const quality = options.quality ?? COMPRESSION_CONFIG.BROTLI_QUALITY_BATCH;\r\n\r\n // Validate quality range\r\n if (quality < 0 || quality > 11) {\r\n throw new Error(`Invalid brotli quality level: ${quality}. Must be 0-11.`);\r\n }\r\n\r\n const zlibOptions: { params: Record<number, number> } = {\r\n params: {\r\n [constants.BROTLI_PARAM_QUALITY]: quality,\r\n [constants.BROTLI_PARAM_MODE]:\r\n options.mode === 'text'\r\n ? constants.BROTLI_MODE_TEXT\r\n : constants.BROTLI_MODE_GENERIC,\r\n },\r\n };\r\n\r\n if (options.lgwin !== undefined) {\r\n if (options.lgwin < 10 || options.lgwin > 24) {\r\n throw new Error(\r\n `Invalid brotli window size: ${options.lgwin}. Must be 10-24.`\r\n );\r\n }\r\n zlibOptions.params[constants.BROTLI_PARAM_LGWIN] = options.lgwin;\r\n }\r\n\r\n const compressed = await compressAsync(input, zlibOptions);\r\n\r\n return {\r\n compressed,\r\n originalSize: input.length,\r\n compressedSize: compressed.length,\r\n ratio: input.length > 0 ? compressed.length / input.length : 1,\r\n };\r\n}\r\n\r\n/**\r\n * Decompress brotli-compressed data.\r\n *\r\n * @param data - The compressed data as a Buffer\r\n * @returns The decompressed data as a Buffer\r\n * @throws Error if decompression fails (corrupt or invalid data)\r\n *\r\n * @example\r\n * ```typescript\r\n * const compressed = await fs.readFile('backup.jsonl.br');\r\n * const decompressed = await decompress(compressed);\r\n * const jsonData = decompressed.toString('utf-8');\r\n * ```\r\n */\r\nexport async function decompress(data: Buffer): Promise<Buffer> {\r\n if (!Buffer.isBuffer(data)) {\r\n throw new Error('Input must be a Buffer');\r\n }\r\n\r\n if (data.length === 0) {\r\n return Buffer.alloc(0);\r\n }\r\n\r\n try {\r\n return await decompressAsync(data);\r\n } catch (error) {\r\n const message = error instanceof Error ? error.message : 'Unknown error';\r\n throw new Error(`Brotli decompression failed: ${message}`);\r\n }\r\n}\r\n\r\n/**\r\n * Calculate compression ratio.\r\n *\r\n * @param originalSize - Original size in bytes\r\n * @param compressedSize - Compressed size in bytes\r\n * @returns Ratio as a decimal (e.g., 0.25 = 75% compression)\r\n *\r\n * @example\r\n * ```typescript\r\n * const ratio = getCompressionRatio(1000, 250);\r\n * console.log(`Compression ratio: ${(1 - ratio) * 100}%`); // \"Compression ratio: 75%\"\r\n * ```\r\n */\r\nexport function getCompressionRatio(\r\n originalSize: number,\r\n compressedSize: number\r\n): number {\r\n if (originalSize <= 0) return 1;\r\n return compressedSize / originalSize;\r\n}\r\n\r\n/**\r\n * Compress a file and write the result to disk.\r\n *\r\n * @param inputPath - Path to the input file\r\n * @param outputPath - Path for the compressed output file\r\n * @param options - Compression options\r\n * @returns Compression result with statistics\r\n *\r\n * @example\r\n * ```typescript\r\n * const result = await compressFile(\r\n * 'memory.jsonl',\r\n * 'memory.jsonl.br',\r\n * { quality: 11 }\r\n * );\r\n * console.log(`Saved ${result.originalSize - result.compressedSize} bytes`);\r\n * ```\r\n */\r\nexport async function compressFile(\r\n inputPath: string,\r\n outputPath: string,\r\n options?: CompressionOptions\r\n): Promise<CompressionResult> {\r\n const input = await fs.readFile(inputPath);\r\n const result = await compress(input, options);\r\n await fs.writeFile(outputPath, result.compressed);\r\n return result;\r\n}\r\n\r\n/**\r\n * Decompress a file and write the result to disk.\r\n *\r\n * @param inputPath - Path to the compressed input file\r\n * @param outputPath - Path for the decompressed output file\r\n * @throws Error if file not found or decompression fails\r\n *\r\n * @example\r\n * ```typescript\r\n * await decompressFile('backup.jsonl.br', 'restored.jsonl');\r\n * ```\r\n */\r\nexport async function decompressFile(\r\n inputPath: string,\r\n outputPath: string\r\n): Promise<void> {\r\n const input = await fs.readFile(inputPath);\r\n const decompressed = await decompress(input);\r\n await fs.writeFile(outputPath, decompressed);\r\n}\r\n\r\n/**\r\n * Create metadata object for a compression result.\r\n * This metadata should be stored alongside compressed files for\r\n * integrity verification and restoration.\r\n *\r\n * @param result - The compression result\r\n * @param checksum - Optional checksum of the original data\r\n * @returns Compression metadata object\r\n *\r\n * @example\r\n * ```typescript\r\n * const result = await compress(data);\r\n * const metadata = createMetadata(result);\r\n * await fs.writeFile('backup.meta.json', JSON.stringify(metadata, null, 2));\r\n * ```\r\n */\r\nexport function createMetadata(\r\n result: CompressionResult,\r\n checksum?: string\r\n): CompressionMetadata {\r\n return {\r\n compressed: true,\r\n compressionFormat: 'brotli',\r\n originalSize: result.originalSize,\r\n compressedSize: result.compressedSize,\r\n originalChecksum: checksum,\r\n createdAt: new Date().toISOString(),\r\n };\r\n}\r\n\r\n/**\r\n * Create metadata for uncompressed data.\r\n * Useful for consistent metadata format when compression is disabled.\r\n *\r\n * @param size - Size of the data in bytes\r\n * @returns Compression metadata indicating no compression\r\n */\r\nexport function createUncompressedMetadata(size: number): CompressionMetadata {\r\n return {\r\n compressed: false,\r\n compressionFormat: 'none',\r\n originalSize: size,\r\n compressedSize: size,\r\n createdAt: new Date().toISOString(),\r\n };\r\n}\r\n\r\n/**\r\n * Compress a string and return base64-encoded result.\r\n * Useful for embedding compressed data in JSON responses.\r\n *\r\n * @param data - String data to compress\r\n * @param options - Compression options\r\n * @returns Base64-encoded compressed data\r\n *\r\n * @example\r\n * ```typescript\r\n * const encoded = await compressToBase64(jsonString);\r\n * // Send in response: { compressed: true, data: encoded }\r\n * ```\r\n */\r\nexport async function compressToBase64(\r\n data: string,\r\n options?: CompressionOptions\r\n): Promise<string> {\r\n const result = await compress(data, options);\r\n return result.compressed.toString('base64');\r\n}\r\n\r\n/**\r\n * Decompress base64-encoded compressed data.\r\n * Counterpart to compressToBase64.\r\n *\r\n * @param base64Data - Base64-encoded compressed data\r\n * @returns Decompressed string\r\n *\r\n * @example\r\n * ```typescript\r\n * const original = await decompressFromBase64(response.data);\r\n * const parsed = JSON.parse(original);\r\n * ```\r\n */\r\nexport async function decompressFromBase64(base64Data: string): Promise<string> {\r\n const buffer = Buffer.from(base64Data, 'base64');\r\n const decompressed = await decompress(buffer);\r\n return decompressed.toString('utf-8');\r\n}\r\n","/**\r\n * Compressed Cache Utility\r\n *\r\n * Provides an LRU cache with automatic compression of old entries.\r\n * Reduces memory footprint for large knowledge graphs (50k+ entities).\r\n *\r\n * Uses synchronous brotli compression/decompression for cache operations\r\n * to avoid async complexity in hot paths.\r\n *\r\n * Phase 3 Sprint 5: Archive & Cache Compression\r\n *\r\n * @module utils/compressedCache\r\n */\r\n\r\nimport { brotliCompressSync, brotliDecompressSync, constants } from 'zlib';\r\nimport type { Entity } from '../types/index.js';\r\nimport { COMPRESSION_CONFIG } from './constants.js';\r\n\r\n/**\r\n * Internal cache entry structure.\r\n */\r\ninterface CacheEntry {\r\n /** The entity data (null if compressed) */\r\n entity: Entity | null;\r\n /** Whether this entry is compressed */\r\n compressed: boolean;\r\n /** Compressed data buffer (only present if compressed) */\r\n compressedData?: Buffer;\r\n /** Size of original entity JSON in bytes (for stats) */\r\n originalSize: number;\r\n /** Timestamp of last access for LRU eviction */\r\n lastAccessed: number;\r\n}\r\n\r\n/**\r\n * Options for CompressedCache configuration.\r\n */\r\nexport interface CompressedCacheOptions {\r\n /**\r\n * Maximum number of uncompressed (hot) entries to keep.\r\n * Entries beyond this limit may be compressed.\r\n * @default 1000\r\n */\r\n maxUncompressed?: number;\r\n\r\n /**\r\n * Time in milliseconds before an entry is eligible for compression.\r\n * Entries accessed within this time window stay uncompressed.\r\n * @default 300000 (5 minutes)\r\n */\r\n compressionThresholdMs?: number;\r\n\r\n /**\r\n * Whether to enable automatic compression.\r\n * If false, entries are never automatically compressed.\r\n * @default true\r\n */\r\n autoCompress?: boolean;\r\n\r\n /**\r\n * Minimum entry size in bytes before compression is applied.\r\n * Entries smaller than this are not compressed (overhead exceeds benefit).\r\n * Phase 12 Sprint 6: Adaptive compression.\r\n * @default 256\r\n */\r\n minCompressionSize?: number;\r\n\r\n /**\r\n * Minimum compression ratio to keep entry compressed.\r\n * If compression achieves less than this ratio, entry stays uncompressed.\r\n * Phase 12 Sprint 6: Adaptive compression.\r\n * @default 0.7 (30% reduction minimum)\r\n */\r\n minCompressionRatio?: number;\r\n}\r\n\r\n/**\r\n * Statistics about the cache state.\r\n */\r\nexport interface CompressedCacheStats {\r\n /** Total number of entries in the cache */\r\n total: number;\r\n /** Number of compressed entries */\r\n compressed: number;\r\n /** Number of uncompressed (hot) entries */\r\n uncompressed: number;\r\n /** Estimated memory saved by compression in bytes */\r\n memorySaved: number;\r\n /** Total original size of all entries in bytes */\r\n totalOriginalSize: number;\r\n /** Total compressed size in bytes */\r\n totalCompressedSize: number;\r\n /** Cache hit count since creation */\r\n hits: number;\r\n /** Cache miss count since creation */\r\n misses: number;\r\n /** Number of compressions performed */\r\n compressions: number;\r\n /** Number of decompressions performed */\r\n decompressions: number;\r\n /** Phase 12 Sprint 6: Number of entries skipped due to size */\r\n skippedSmallEntries: number;\r\n /** Phase 12 Sprint 6: Number of entries skipped due to poor ratio */\r\n skippedPoorRatio: number;\r\n /** Phase 12 Sprint 6: Average compression ratio (0-1) */\r\n avgCompressionRatio: number;\r\n /** Phase 12 Sprint 6: Estimated memory usage in bytes */\r\n estimatedMemoryBytes: number;\r\n}\r\n\r\n/**\r\n * LRU cache with automatic compression of old entries.\r\n *\r\n * Reduces memory footprint by compressing infrequently accessed entries\r\n * using brotli compression. Hot (recently accessed) entries stay\r\n * uncompressed for fast access.\r\n *\r\n * @example\r\n * ```typescript\r\n * const cache = new CompressedCache({\r\n * maxUncompressed: 500,\r\n * compressionThresholdMs: 60000 // 1 minute\r\n * });\r\n *\r\n * // Store entity\r\n * cache.set('Alice', { name: 'Alice', entityType: 'person', observations: [] });\r\n *\r\n * // Retrieve entity (decompresses if needed)\r\n * const entity = cache.get('Alice');\r\n *\r\n * // Check stats\r\n * const stats = cache.getStats();\r\n * console.log(`Memory saved: ${stats.memorySaved} bytes`);\r\n * ```\r\n */\r\nexport class CompressedCache {\r\n private _entryMap: Map<string, CacheEntry> = new Map();\r\n private readonly maxUncompressed: number;\r\n private readonly compressionThresholdMs: number;\r\n private readonly autoCompress: boolean;\r\n // Phase 12 Sprint 6: Adaptive compression options\r\n private readonly minCompressionSize: number;\r\n private readonly minCompressionRatio: number;\r\n\r\n // Statistics\r\n private hits: number = 0;\r\n private misses: number = 0;\r\n private compressions: number = 0;\r\n private decompressions: number = 0;\r\n // Phase 12 Sprint 6: Adaptive compression stats\r\n private skippedSmallEntries: number = 0;\r\n private skippedPoorRatio: number = 0;\r\n private compressionRatios: number[] = [];\r\n\r\n constructor(options: CompressedCacheOptions = {}) {\r\n this.maxUncompressed = options.maxUncompressed ?? 1000;\r\n this.compressionThresholdMs = options.compressionThresholdMs ?? 5 * 60 * 1000;\r\n this.autoCompress = options.autoCompress ?? true;\r\n // Phase 12 Sprint 6: Adaptive compression defaults\r\n this.minCompressionSize = options.minCompressionSize ?? 256;\r\n this.minCompressionRatio = options.minCompressionRatio ?? 0.7;\r\n }\r\n\r\n /**\r\n * Get an entity from the cache.\r\n *\r\n * If the entity is compressed, it will be decompressed on access.\r\n * The entry is marked as recently accessed to prevent re-compression.\r\n *\r\n * @param name - Entity name to retrieve\r\n * @returns The entity if found, undefined otherwise\r\n */\r\n get(name: string): Entity | undefined {\r\n const entry = this._entryMap.get(name);\r\n\r\n if (!entry) {\r\n this.misses++;\r\n return undefined;\r\n }\r\n\r\n this.hits++;\r\n entry.lastAccessed = Date.now();\r\n\r\n if (entry.compressed && entry.compressedData) {\r\n // Decompress on access\r\n try {\r\n const decompressed = brotliDecompressSync(entry.compressedData);\r\n entry.entity = JSON.parse(decompressed.toString('utf-8'));\r\n entry.compressed = false;\r\n entry.compressedData = undefined;\r\n this.decompressions++;\r\n } catch {\r\n // Decompression failed - remove corrupt entry\r\n this._entryMap.delete(name);\r\n return undefined;\r\n }\r\n }\r\n\r\n return entry.entity ?? undefined;\r\n }\r\n\r\n /**\r\n * Store an entity in the cache.\r\n *\r\n * Entries are stored uncompressed initially. Old entries may be\r\n * compressed automatically based on cache settings.\r\n *\r\n * @param name - Entity name (key)\r\n * @param entity - Entity to store\r\n */\r\n set(name: string, entity: Entity): void {\r\n const jsonStr = JSON.stringify(entity);\r\n const originalSize = Buffer.byteLength(jsonStr, 'utf-8');\r\n\r\n this._entryMap.set(name, {\r\n entity,\r\n compressed: false,\r\n originalSize,\r\n lastAccessed: Date.now(),\r\n });\r\n\r\n if (this.autoCompress) {\r\n this.maybeCompressOldEntries();\r\n }\r\n }\r\n\r\n /**\r\n * Check if an entity exists in the cache.\r\n *\r\n * @param name - Entity name to check\r\n * @returns True if entity exists in cache\r\n */\r\n has(name: string): boolean {\r\n return this._entryMap.has(name);\r\n }\r\n\r\n /**\r\n * Delete an entity from the cache.\r\n *\r\n * @param name - Entity name to delete\r\n * @returns True if entity was deleted, false if not found\r\n */\r\n delete(name: string): boolean {\r\n return this._entryMap.delete(name);\r\n }\r\n\r\n /**\r\n * Clear all entries from the cache.\r\n */\r\n clear(): void {\r\n this._entryMap.clear();\r\n // Don't reset statistics - they track lifetime totals\r\n }\r\n\r\n /**\r\n * Get the number of entries in the cache.\r\n */\r\n get size(): number {\r\n return this._entryMap.size;\r\n }\r\n\r\n /**\r\n * Get all entity names in the cache.\r\n */\r\n keys(): IterableIterator<string> {\r\n return this._entryMap.keys();\r\n }\r\n\r\n /**\r\n * Get comprehensive cache statistics.\r\n *\r\n * @returns Statistics about cache state and performance\r\n */\r\n getStats(): CompressedCacheStats {\r\n let compressed = 0;\r\n let uncompressed = 0;\r\n let memorySaved = 0;\r\n let totalOriginalSize = 0;\r\n let totalCompressedSize = 0;\r\n let estimatedMemoryBytes = 0;\r\n\r\n for (const entry of this._entryMap.values()) {\r\n totalOriginalSize += entry.originalSize;\r\n\r\n if (entry.compressed && entry.compressedData) {\r\n compressed++;\r\n totalCompressedSize += entry.compressedData.length;\r\n estimatedMemoryBytes += entry.compressedData.length;\r\n // Memory saved = original size - compressed size\r\n memorySaved += entry.originalSize - entry.compressedData.length;\r\n } else {\r\n uncompressed++;\r\n estimatedMemoryBytes += entry.originalSize;\r\n }\r\n }\r\n\r\n // Calculate average compression ratio\r\n const avgCompressionRatio = this.compressionRatios.length > 0\r\n ? this.compressionRatios.reduce((a, b) => a + b, 0) / this.compressionRatios.length\r\n : 0;\r\n\r\n return {\r\n total: this._entryMap.size,\r\n compressed,\r\n uncompressed,\r\n memorySaved: Math.max(0, memorySaved),\r\n totalOriginalSize,\r\n totalCompressedSize,\r\n hits: this.hits,\r\n misses: this.misses,\r\n compressions: this.compressions,\r\n decompressions: this.decompressions,\r\n // Phase 12 Sprint 6: Adaptive compression stats\r\n skippedSmallEntries: this.skippedSmallEntries,\r\n skippedPoorRatio: this.skippedPoorRatio,\r\n avgCompressionRatio,\r\n estimatedMemoryBytes,\r\n };\r\n }\r\n\r\n /**\r\n * Force compression of entries older than the threshold.\r\n *\r\n * Called automatically after set() when autoCompress is enabled.\r\n * Can be called manually to trigger compression on demand.\r\n *\r\n * @returns Number of entries compressed\r\n */\r\n compressOldEntries(): number {\r\n return this.maybeCompressOldEntries(true);\r\n }\r\n\r\n /**\r\n * Compress old entries if we're over the uncompressed limit.\r\n *\r\n * @param force - If true, compress regardless of limit\r\n * @returns Number of entries compressed\r\n */\r\n private maybeCompressOldEntries(force: boolean = false): number {\r\n // Count uncompressed entries\r\n let uncompressedCount = 0;\r\n for (const entry of this._entryMap.values()) {\r\n if (!entry.compressed) {\r\n uncompressedCount++;\r\n }\r\n }\r\n\r\n // Only compress if over limit (unless forced)\r\n if (!force && uncompressedCount <= this.maxUncompressed) {\r\n return 0;\r\n }\r\n\r\n const now = Date.now();\r\n let compressedCount = 0;\r\n\r\n // Sort uncompressed entries by last accessed time (oldest first)\r\n const sortedEntries = [...this._entryMap.entries()]\r\n .filter(([, e]) => !e.compressed && e.entity !== null)\r\n .sort((a, b) => a[1].lastAccessed - b[1].lastAccessed);\r\n\r\n // Compress oldest entries until we're under the limit\r\n for (const [, entry] of sortedEntries) {\r\n // Stop if we've compressed enough\r\n if (!force && (uncompressedCount - compressedCount) <= this.maxUncompressed) {\r\n break;\r\n }\r\n\r\n // Only compress if entry is old enough\r\n if (now - entry.lastAccessed < this.compressionThresholdMs) {\r\n continue;\r\n }\r\n\r\n // Compress the entry\r\n if (entry.entity) {\r\n // Phase 12 Sprint 6: Skip small entries (adaptive compression)\r\n if (entry.originalSize < this.minCompressionSize) {\r\n this.skippedSmallEntries++;\r\n continue;\r\n }\r\n\r\n try {\r\n const jsonStr = JSON.stringify(entry.entity);\r\n const compressed = brotliCompressSync(Buffer.from(jsonStr, 'utf-8'), {\r\n params: {\r\n [constants.BROTLI_PARAM_QUALITY]: COMPRESSION_CONFIG.BROTLI_QUALITY_CACHE,\r\n },\r\n });\r\n\r\n // Phase 12 Sprint 6: Check compression ratio\r\n const ratio = compressed.length / entry.originalSize;\r\n if (ratio > this.minCompressionRatio) {\r\n // Compression didn't achieve enough reduction\r\n this.skippedPoorRatio++;\r\n continue;\r\n }\r\n\r\n // Track compression ratio\r\n this.compressionRatios.push(ratio);\r\n\r\n entry.compressedData = compressed;\r\n entry.compressed = true;\r\n entry.entity = null; // Free memory\r\n this.compressions++;\r\n compressedCount++;\r\n } catch {\r\n // Compression failed - leave entry uncompressed\r\n continue;\r\n }\r\n }\r\n }\r\n\r\n return compressedCount;\r\n }\r\n\r\n /**\r\n * Decompress all entries in the cache.\r\n *\r\n * Useful when preparing for bulk operations or export.\r\n *\r\n * @returns Number of entries decompressed\r\n */\r\n decompressAll(): number {\r\n let decompressedCount = 0;\r\n\r\n for (const [name, entry] of this._entryMap) {\r\n if (entry.compressed && entry.compressedData) {\r\n try {\r\n const decompressed = brotliDecompressSync(entry.compressedData);\r\n entry.entity = JSON.parse(decompressed.toString('utf-8'));\r\n entry.compressed = false;\r\n entry.compressedData = undefined;\r\n this.decompressions++;\r\n decompressedCount++;\r\n } catch {\r\n // Decompression failed - remove corrupt entry\r\n this._entryMap.delete(name);\r\n }\r\n }\r\n }\r\n\r\n return decompressedCount;\r\n }\r\n\r\n /**\r\n * Get all entities from the cache (decompressing as needed).\r\n *\r\n * @returns Array of all entities in the cache\r\n */\r\n getAllEntities(): Entity[] {\r\n const entities: Entity[] = [];\r\n\r\n for (const [name] of this._entryMap) {\r\n const entity = this.get(name);\r\n if (entity) {\r\n entities.push(entity);\r\n }\r\n }\r\n\r\n return entities;\r\n }\r\n\r\n /**\r\n * Iterate over all entries with their compression status.\r\n *\r\n * Does not decompress entries - useful for inspection.\r\n *\r\n * @yields Entry information without decompressing\r\n */\r\n *entries(): IterableIterator<{\r\n name: string;\r\n compressed: boolean;\r\n originalSize: number;\r\n lastAccessed: number;\r\n }> {\r\n for (const [name, entry] of this._entryMap) {\r\n yield {\r\n name,\r\n compressed: entry.compressed,\r\n originalSize: entry.originalSize,\r\n lastAccessed: entry.lastAccessed,\r\n };\r\n }\r\n }\r\n}\r\n","/**\r\n * Simple logging utility for the Memory MCP Server\r\n *\r\n * Provides consistent log formatting with levels: debug, info, warn, error\r\n *\r\n * IMPORTANT: All log output goes to stderr to avoid interfering with\r\n * JSON-RPC communication on stdout when running as an MCP server.\r\n */\r\n\r\nexport const logger = {\r\n /**\r\n * Debug level logging (verbose, for development)\r\n * Output: stderr (to avoid interfering with JSON-RPC)\r\n */\r\n debug: (msg: string, ...args: unknown[]): void => {\r\n if (process.env.LOG_LEVEL === 'debug') {\r\n console.error(`[DEBUG] ${msg}`, ...args);\r\n }\r\n },\r\n\r\n /**\r\n * Info level logging (general informational messages)\r\n * Output: stderr (to avoid interfering with JSON-RPC)\r\n */\r\n info: (msg: string, ...args: unknown[]): void => {\r\n console.error(`[INFO] ${msg}`, ...args);\r\n },\r\n\r\n /**\r\n * Warning level logging (warnings that don't prevent operation)\r\n * Output: stderr (native console.warn behavior)\r\n */\r\n warn: (msg: string, ...args: unknown[]): void => {\r\n console.warn(`[WARN] ${msg}`, ...args);\r\n },\r\n\r\n /**\r\n * Error level logging (errors that affect functionality)\r\n * Output: stderr (native console.error behavior)\r\n */\r\n error: (msg: string, ...args: unknown[]): void => {\r\n console.error(`[ERROR] ${msg}`, ...args);\r\n },\r\n};\r\n","/**\r\n * Search Algorithms\r\n *\r\n * Algorithms for search operations: Levenshtein distance for fuzzy matching\r\n * and TF-IDF for relevance scoring.\r\n *\r\n * @module utils/searchAlgorithms\r\n */\r\n\r\n// ==================== Levenshtein Distance ====================\r\n\r\n/**\r\n * Calculate Levenshtein distance between two strings.\r\n *\r\n * Returns the minimum number of single-character edits needed to change\r\n * one word into another.\r\n *\r\n * **Algorithm**: Space-optimized dynamic programming using only two rows.\r\n * Time complexity: O(m*n), Space complexity: O(min(m,n)).\r\n *\r\n * This optimization reduces memory usage from O(m*n) to O(min(m,n)) by\r\n * observing that each row only depends on the previous row.\r\n *\r\n * @param str1 - First string to compare\r\n * @param str2 - Second string to compare\r\n * @returns Minimum number of edits required (0 = identical strings)\r\n *\r\n * @example\r\n * ```typescript\r\n * levenshteinDistance(\"kitten\", \"sitting\"); // Returns 3\r\n * levenshteinDistance(\"hello\", \"hello\"); // Returns 0\r\n * levenshteinDistance(\"abc\", \"\"); // Returns 3\r\n * ```\r\n */\r\nexport function levenshteinDistance(str1: string, str2: string): number {\r\n // Ensure str1 is the shorter string for optimal space usage\r\n if (str1.length > str2.length) {\r\n [str1, str2] = [str2, str1];\r\n }\r\n\r\n const m = str1.length;\r\n const n = str2.length;\r\n\r\n // Use two rows instead of full matrix - O(min(m,n)) space\r\n let prev: number[] = Array.from({ length: m + 1 }, (_, i) => i);\r\n let curr: number[] = new Array(m + 1);\r\n\r\n for (let j = 1; j <= n; j++) {\r\n curr[0] = j; // Distance from empty string\r\n\r\n for (let i = 1; i <= m; i++) {\r\n if (str1[i - 1] === str2[j - 1]) {\r\n // Characters match, no edit needed\r\n curr[i] = prev[i - 1];\r\n } else {\r\n // Take minimum of three operations\r\n curr[i] = 1 + Math.min(\r\n prev[i - 1], // substitution\r\n prev[i], // deletion\r\n curr[i - 1] // insertion\r\n );\r\n }\r\n }\r\n\r\n // Swap rows for next iteration\r\n [prev, curr] = [curr, prev];\r\n }\r\n\r\n return prev[m];\r\n}\r\n\r\n// ==================== TF-IDF ====================\r\n\r\n/**\r\n * Calculate Term Frequency (TF) for a term in a document.\r\n *\r\n * TF = (Number of times term appears in document) / (Total terms in document)\r\n *\r\n * @param term - The search term\r\n * @param document - The document text\r\n * @returns Term frequency (0.0 to 1.0)\r\n */\r\nexport function calculateTF(term: string, document: string): number {\r\n const termLower = term.toLowerCase();\r\n const tokens = tokenize(document);\r\n\r\n if (tokens.length === 0) return 0;\r\n\r\n const termCount = tokens.filter(t => t === termLower).length;\r\n return termCount / tokens.length;\r\n}\r\n\r\n/**\r\n * Calculate Inverse Document Frequency (IDF) for a term across documents.\r\n *\r\n * IDF = log(Total documents / Documents containing term)\r\n *\r\n * Note: For bulk IDF calculation, prefer calculateIDFFromTokenSets which\r\n * avoids re-tokenizing documents for each term.\r\n *\r\n * @param term - The search term\r\n * @param documents - Array of document texts\r\n * @returns Inverse document frequency\r\n */\r\nexport function calculateIDF(term: string, documents: string[]): number {\r\n if (documents.length === 0) return 0;\r\n\r\n const termLower = term.toLowerCase();\r\n const docsWithTerm = documents.filter(doc =>\r\n tokenize(doc).includes(termLower)\r\n ).length;\r\n\r\n if (docsWithTerm === 0) return 0;\r\n\r\n return Math.log(documents.length / docsWithTerm);\r\n}\r\n\r\n/**\r\n * Calculate Inverse Document Frequency (IDF) from pre-tokenized documents.\r\n *\r\n * IDF = log(Total documents / Documents containing term)\r\n *\r\n * **Optimized**: Avoids re-tokenizing documents for each term. Pre-tokenize\r\n * documents once and convert to Sets for O(1) lookup per document.\r\n *\r\n * @param term - The search term (should already be lowercase)\r\n * @param tokenSets - Array of Sets, each containing unique tokens for a document\r\n * @returns Inverse document frequency\r\n *\r\n * @example\r\n * ```typescript\r\n * const docs = [\"hello world\", \"hello there\"];\r\n * const tokenSets = docs.map(d => new Set(tokenize(d)));\r\n * calculateIDFFromTokenSets(\"hello\", tokenSets); // Low IDF (common term)\r\n * calculateIDFFromTokenSets(\"world\", tokenSets); // Higher IDF (less common)\r\n * ```\r\n */\r\nexport function calculateIDFFromTokenSets(term: string, tokenSets: Set<string>[]): number {\r\n if (tokenSets.length === 0) return 0;\r\n\r\n const termLower = term.toLowerCase();\r\n let docsWithTerm = 0;\r\n\r\n for (const tokenSet of tokenSets) {\r\n if (tokenSet.has(termLower)) {\r\n docsWithTerm++;\r\n }\r\n }\r\n\r\n if (docsWithTerm === 0) return 0;\r\n\r\n return Math.log(tokenSets.length / docsWithTerm);\r\n}\r\n\r\n/**\r\n * Calculate TF-IDF score for a term in a document.\r\n *\r\n * TF-IDF = TF * IDF\r\n *\r\n * Higher scores indicate more important/relevant terms.\r\n *\r\n * @param term - The search term\r\n * @param document - The document text\r\n * @param documents - Array of all documents\r\n * @returns TF-IDF score\r\n */\r\nexport function calculateTFIDF(\r\n term: string,\r\n document: string,\r\n documents: string[]\r\n): number {\r\n const tf = calculateTF(term, document);\r\n const idf = calculateIDF(term, documents);\r\n return tf * idf;\r\n}\r\n\r\n/**\r\n * Tokenize text into lowercase words.\r\n *\r\n * Splits on whitespace and removes punctuation.\r\n *\r\n * @param text - Text to tokenize\r\n * @returns Array of lowercase tokens\r\n */\r\nexport function tokenize(text: string): string[] {\r\n return text\r\n .toLowerCase()\r\n .replace(/[^\\w\\s]/g, ' ')\r\n .split(/\\s+/)\r\n .filter(token => token.length > 0);\r\n}\r\n","/**\r\n * Search Indexes\r\n *\r\n * Provides O(1) lookup structures to avoid repeated linear scans.\r\n * - NameIndex: O(1) entity lookup by name\r\n * - TypeIndex: O(1) entities by type\r\n * - LowercaseCache: Pre-computed lowercase strings to avoid repeated toLowerCase()\r\n * - RelationIndex: O(1) relation lookup by entity name (from/to)\r\n * - ObservationIndex: O(1) observation word lookup by entity\r\n *\r\n * @module utils/indexes\r\n */\r\n\r\nimport type { Entity, LowercaseData, Relation } from '../types/index.js';\r\n\r\n/**\r\n * NameIndex provides O(1) entity lookup by name.\r\n *\r\n * Uses a Map internally for constant-time access.\r\n */\r\nexport class NameIndex {\r\n private index: Map<string, Entity> = new Map();\r\n\r\n /**\r\n * Build the index from an array of entities.\r\n * Clears any existing index data first.\r\n */\r\n build(entities: Entity[]): void {\r\n this.index.clear();\r\n for (const entity of entities) {\r\n this.index.set(entity.name, entity);\r\n }\r\n }\r\n\r\n /**\r\n * Get an entity by name in O(1) time.\r\n */\r\n get(name: string): Entity | undefined {\r\n return this.index.get(name);\r\n }\r\n\r\n /**\r\n * Add a single entity to the index.\r\n */\r\n add(entity: Entity): void {\r\n this.index.set(entity.name, entity);\r\n }\r\n\r\n /**\r\n * Remove an entity from the index by name.\r\n */\r\n remove(name: string): void {\r\n this.index.delete(name);\r\n }\r\n\r\n /**\r\n * Check if an entity exists in the index.\r\n */\r\n has(name: string): boolean {\r\n return this.index.has(name);\r\n }\r\n\r\n /**\r\n * Get the number of entities in the index.\r\n */\r\n get size(): number {\r\n return this.index.size;\r\n }\r\n\r\n /**\r\n * Clear all entries from the index.\r\n */\r\n clear(): void {\r\n this.index.clear();\r\n }\r\n}\r\n\r\n/**\r\n * TypeIndex provides O(1) lookup of entities by type.\r\n *\r\n * Uses a Map<type, Set<entityName>> structure for efficient type queries.\r\n * Type comparisons are case-insensitive.\r\n */\r\nexport class TypeIndex {\r\n private index: Map<string, Set<string>> = new Map();\r\n\r\n /**\r\n * Build the index from an array of entities.\r\n * Clears any existing index data first.\r\n */\r\n build(entities: Entity[]): void {\r\n this.index.clear();\r\n for (const entity of entities) {\r\n this.addToIndex(entity.name, entity.entityType);\r\n }\r\n }\r\n\r\n /**\r\n * Get all entity names of a given type in O(1) time.\r\n * Type comparison is case-insensitive.\r\n */\r\n getNames(entityType: string): Set<string> {\r\n const typeLower = entityType.toLowerCase();\r\n return this.index.get(typeLower) ?? new Set();\r\n }\r\n\r\n /**\r\n * Add an entity to the type index.\r\n */\r\n add(entity: Entity): void {\r\n this.addToIndex(entity.name, entity.entityType);\r\n }\r\n\r\n /**\r\n * Remove an entity from the type index.\r\n * Requires the entity type to know which bucket to remove from.\r\n */\r\n remove(entityName: string, entityType: string): void {\r\n const typeLower = entityType.toLowerCase();\r\n const names = this.index.get(typeLower);\r\n if (names) {\r\n names.delete(entityName);\r\n if (names.size === 0) {\r\n this.index.delete(typeLower);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Update an entity's type in the index.\r\n * Removes from old type and adds to new type.\r\n */\r\n updateType(entityName: string, oldType: string, newType: string): void {\r\n this.remove(entityName, oldType);\r\n this.addToIndex(entityName, newType);\r\n }\r\n\r\n /**\r\n * Get all unique types in the index.\r\n */\r\n getTypes(): string[] {\r\n return Array.from(this.index.keys());\r\n }\r\n\r\n /**\r\n * Clear all entries from the index.\r\n */\r\n clear(): void {\r\n this.index.clear();\r\n }\r\n\r\n private addToIndex(entityName: string, entityType: string): void {\r\n const typeLower = entityType.toLowerCase();\r\n let names = this.index.get(typeLower);\r\n if (!names) {\r\n names = new Set();\r\n this.index.set(typeLower, names);\r\n }\r\n names.add(entityName);\r\n }\r\n}\r\n\r\n/**\r\n * LowercaseCache pre-computes lowercase versions of all searchable fields.\r\n *\r\n * Eliminates the need for repeated toLowerCase() calls during search,\r\n * which is expensive with many entities and observations.\r\n */\r\nexport class LowercaseCache {\r\n private cache: Map<string, LowercaseData> = new Map();\r\n\r\n /**\r\n * Build the cache from an array of entities.\r\n * Clears any existing cache data first.\r\n */\r\n build(entities: Entity[]): void {\r\n this.cache.clear();\r\n for (const entity of entities) {\r\n this.cache.set(entity.name, this.computeLowercase(entity));\r\n }\r\n }\r\n\r\n /**\r\n * Get pre-computed lowercase data for an entity.\r\n */\r\n get(entityName: string): LowercaseData | undefined {\r\n return this.cache.get(entityName);\r\n }\r\n\r\n /**\r\n * Add or update an entity in the cache.\r\n */\r\n set(entity: Entity): void {\r\n this.cache.set(entity.name, this.computeLowercase(entity));\r\n }\r\n\r\n /**\r\n * Remove an entity from the cache.\r\n */\r\n remove(entityName: string): void {\r\n this.cache.delete(entityName);\r\n }\r\n\r\n /**\r\n * Check if an entity exists in the cache.\r\n */\r\n has(entityName: string): boolean {\r\n return this.cache.has(entityName);\r\n }\r\n\r\n /**\r\n * Clear all entries from the cache.\r\n */\r\n clear(): void {\r\n this.cache.clear();\r\n }\r\n\r\n /**\r\n * Get the number of entries in the cache.\r\n */\r\n get size(): number {\r\n return this.cache.size;\r\n }\r\n\r\n private computeLowercase(entity: Entity): LowercaseData {\r\n return {\r\n name: entity.name.toLowerCase(),\r\n entityType: entity.entityType.toLowerCase(),\r\n observations: entity.observations.map(o => o.toLowerCase()),\r\n tags: entity.tags?.map(t => t.toLowerCase()) ?? [],\r\n };\r\n }\r\n}\r\n\r\n/**\r\n * RelationIndex provides O(1) lookup of relations by entity name.\r\n *\r\n * Maintains two separate indexes for efficient directional queries:\r\n * - fromIndex: Map from source entity name to its outgoing relations\r\n * - toIndex: Map from target entity name to its incoming relations\r\n *\r\n * This eliminates O(n) array scans when looking up relations for an entity.\r\n */\r\nexport class RelationIndex {\r\n /** Index of relations by source (from) entity */\r\n private fromIndex: Map<string, Set<Relation>> = new Map();\r\n\r\n /** Index of relations by target (to) entity */\r\n private toIndex: Map<string, Set<Relation>> = new Map();\r\n\r\n /**\r\n * Build the index from an array of relations.\r\n * Clears any existing index data first.\r\n */\r\n build(relations: Relation[]): void {\r\n this.fromIndex.clear();\r\n this.toIndex.clear();\r\n for (const relation of relations) {\r\n this.addToIndexes(relation);\r\n }\r\n }\r\n\r\n /**\r\n * Get all relations where the entity is the source (outgoing relations).\r\n * Returns empty array if no relations found.\r\n */\r\n getRelationsFrom(entityName: string): Relation[] {\r\n const relations = this.fromIndex.get(entityName);\r\n return relations ? Array.from(relations) : [];\r\n }\r\n\r\n /**\r\n * Get all relations where the entity is the target (incoming relations).\r\n * Returns empty array if no relations found.\r\n */\r\n getRelationsTo(entityName: string): Relation[] {\r\n const relations = this.toIndex.get(entityName);\r\n return relations ? Array.from(relations) : [];\r\n }\r\n\r\n /**\r\n * Get all relations involving the entity (both incoming and outgoing).\r\n * Returns empty array if no relations found.\r\n */\r\n getRelationsFor(entityName: string): Relation[] {\r\n const fromRelations = this.fromIndex.get(entityName);\r\n const toRelations = this.toIndex.get(entityName);\r\n\r\n // Combine sets to handle self-referential relations correctly\r\n const combined = new Set<Relation>();\r\n if (fromRelations) {\r\n for (const r of fromRelations) {\r\n combined.add(r);\r\n }\r\n }\r\n if (toRelations) {\r\n for (const r of toRelations) {\r\n combined.add(r);\r\n }\r\n }\r\n return Array.from(combined);\r\n }\r\n\r\n /**\r\n * Add a single relation to the index.\r\n */\r\n add(relation: Relation): void {\r\n this.addToIndexes(relation);\r\n }\r\n\r\n /**\r\n * Remove a relation from the index.\r\n * Matches by from, to, and relationType.\r\n */\r\n remove(relation: Relation): void {\r\n // Remove from fromIndex\r\n const fromRelations = this.fromIndex.get(relation.from);\r\n if (fromRelations) {\r\n for (const r of fromRelations) {\r\n if (r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType) {\r\n fromRelations.delete(r);\r\n break;\r\n }\r\n }\r\n if (fromRelations.size === 0) {\r\n this.fromIndex.delete(relation.from);\r\n }\r\n }\r\n\r\n // Remove from toIndex\r\n const toRelations = this.toIndex.get(relation.to);\r\n if (toRelations) {\r\n for (const r of toRelations) {\r\n if (r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType) {\r\n toRelations.delete(r);\r\n break;\r\n }\r\n }\r\n if (toRelations.size === 0) {\r\n this.toIndex.delete(relation.to);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Remove all relations involving a specific entity.\r\n * Returns the relations that were removed.\r\n */\r\n removeAllForEntity(entityName: string): Relation[] {\r\n const removed: Relation[] = [];\r\n\r\n // Remove outgoing relations\r\n const fromRelations = this.fromIndex.get(entityName);\r\n if (fromRelations) {\r\n for (const r of fromRelations) {\r\n removed.push(r);\r\n // Also remove from toIndex\r\n const toRels = this.toIndex.get(r.to);\r\n if (toRels) {\r\n toRels.delete(r);\r\n if (toRels.size === 0) {\r\n this.toIndex.delete(r.to);\r\n }\r\n }\r\n }\r\n this.fromIndex.delete(entityName);\r\n }\r\n\r\n // Remove incoming relations\r\n const toRelations = this.toIndex.get(entityName);\r\n if (toRelations) {\r\n for (const r of toRelations) {\r\n // Skip self-referential relations (already handled above)\r\n if (r.from === entityName) continue;\r\n removed.push(r);\r\n // Also remove from fromIndex\r\n const fromRels = this.fromIndex.get(r.from);\r\n if (fromRels) {\r\n fromRels.delete(r);\r\n if (fromRels.size === 0) {\r\n this.fromIndex.delete(r.from);\r\n }\r\n }\r\n }\r\n this.toIndex.delete(entityName);\r\n }\r\n\r\n return removed;\r\n }\r\n\r\n /**\r\n * Check if any relations exist for an entity.\r\n */\r\n hasRelations(entityName: string): boolean {\r\n return this.fromIndex.has(entityName) || this.toIndex.has(entityName);\r\n }\r\n\r\n /**\r\n * Get count of outgoing relations for an entity.\r\n */\r\n getOutgoingCount(entityName: string): number {\r\n return this.fromIndex.get(entityName)?.size ?? 0;\r\n }\r\n\r\n /**\r\n * Get count of incoming relations for an entity.\r\n */\r\n getIncomingCount(entityName: string): number {\r\n return this.toIndex.get(entityName)?.size ?? 0;\r\n }\r\n\r\n /**\r\n * Get total count of unique relations in the index.\r\n */\r\n get size(): number {\r\n // Count all relations in fromIndex (each relation appears exactly once there)\r\n let count = 0;\r\n for (const relations of this.fromIndex.values()) {\r\n count += relations.size;\r\n }\r\n return count;\r\n }\r\n\r\n /**\r\n * Clear all entries from the index.\r\n */\r\n clear(): void {\r\n this.fromIndex.clear();\r\n this.toIndex.clear();\r\n }\r\n\r\n private addToIndexes(relation: Relation): void {\r\n // Add to fromIndex\r\n let fromSet = this.fromIndex.get(relation.from);\r\n if (!fromSet) {\r\n fromSet = new Set();\r\n this.fromIndex.set(relation.from, fromSet);\r\n }\r\n fromSet.add(relation);\r\n\r\n // Add to toIndex\r\n let toSet = this.toIndex.get(relation.to);\r\n if (!toSet) {\r\n toSet = new Set();\r\n this.toIndex.set(relation.to, toSet);\r\n }\r\n toSet.add(relation);\r\n }\r\n}\r\n\r\n/**\r\n * Inverted index mapping observation keywords to entity names.\r\n * Enables O(1) lookup for 'which entities mention word X?' queries.\r\n * Words are normalized to lowercase and split on whitespace/punctuation.\r\n */\r\nexport class ObservationIndex {\r\n private index: Map<string, Set<string>> = new Map();\r\n private entityObservations: Map<string, Set<string>> = new Map();\r\n\r\n /**\r\n * Add an entity's observations to the index.\r\n * Tokenizes observations into words and creates reverse mapping.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param observations - Array of observation strings\r\n */\r\n add(entityName: string, observations: string[]): void {\r\n const entityWords = new Set<string>();\r\n\r\n for (const observation of observations) {\r\n const words = this.tokenize(observation);\r\n for (const word of words) {\r\n entityWords.add(word);\r\n if (!this.index.has(word)) {\r\n this.index.set(word, new Set());\r\n }\r\n this.index.get(word)!.add(entityName);\r\n }\r\n }\r\n\r\n this.entityObservations.set(entityName, entityWords);\r\n }\r\n\r\n /**\r\n * Remove an entity from the index.\r\n * Cleans up all word mappings for this entity.\r\n *\r\n * @param entityName - Name of the entity to remove\r\n */\r\n remove(entityName: string): void {\r\n const words = this.entityObservations.get(entityName);\r\n if (!words) return;\r\n\r\n for (const word of words) {\r\n const entities = this.index.get(word);\r\n if (entities) {\r\n entities.delete(entityName);\r\n if (entities.size === 0) {\r\n this.index.delete(word);\r\n }\r\n }\r\n }\r\n\r\n this.entityObservations.delete(entityName);\r\n }\r\n\r\n /**\r\n * Get all entities that have observations containing the given word.\r\n * Word matching is case-insensitive.\r\n *\r\n * @param word - Word to search for\r\n * @returns Set of entity names containing this word\r\n */\r\n getEntitiesWithWord(word: string): Set<string> {\r\n return this.index.get(word.toLowerCase()) ?? new Set();\r\n }\r\n\r\n /**\r\n * Get all entities that have observations containing ANY of the given words (union).\r\n *\r\n * @param words - Array of words to search for\r\n * @returns Set of entity names containing any of the words\r\n */\r\n getEntitiesWithAnyWord(words: string[]): Set<string> {\r\n const result = new Set<string>();\r\n for (const word of words) {\r\n const entities = this.getEntitiesWithWord(word);\r\n for (const entity of entities) {\r\n result.add(entity);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * Get all entities that have observations containing ALL of the given words (intersection).\r\n *\r\n * @param words - Array of words that must all be present\r\n * @returns Set of entity names containing all of the words\r\n */\r\n getEntitiesWithAllWords(words: string[]): Set<string> {\r\n if (words.length === 0) return new Set();\r\n\r\n let result = new Set(this.getEntitiesWithWord(words[0]));\r\n\r\n for (let i = 1; i < words.length && result.size > 0; i++) {\r\n const wordEntities = this.getEntitiesWithWord(words[i]);\r\n result = new Set([...result].filter(e => wordEntities.has(e)));\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Clear all entries from the index.\r\n */\r\n clear(): void {\r\n this.index.clear();\r\n this.entityObservations.clear();\r\n }\r\n\r\n /**\r\n * Get statistics about the index.\r\n *\r\n * @returns Object with wordCount and entityCount\r\n */\r\n getStats(): { wordCount: number; entityCount: number } {\r\n return {\r\n wordCount: this.index.size,\r\n entityCount: this.entityObservations.size,\r\n };\r\n }\r\n\r\n /**\r\n * Tokenize text into searchable words.\r\n * Normalizes to lowercase, splits on non-alphanumeric characters,\r\n * and filters out words less than 2 characters.\r\n *\r\n * @param text - Text to tokenize\r\n * @returns Array of normalized words\r\n */\r\n private tokenize(text: string): string[] {\r\n return text\r\n .toLowerCase()\r\n .split(/[^a-z0-9]+/)\r\n .filter(word => word.length >= 2);\r\n }\r\n}\r\n","/**\r\n * Search Result Cache\r\n *\r\n * Simple LRU-style cache for search results with TTL support.\r\n * Improves performance for repeated queries without external dependencies.\r\n *\r\n * @module utils/searchCache\r\n */\r\n\r\nimport type { SearchResult, KnowledgeGraph } from '../types/index.js';\r\n\r\n/**\r\n * Cache entry with expiration.\r\n */\r\ninterface CacheEntry<T> {\r\n value: T;\r\n timestamp: number;\r\n expiresAt: number;\r\n}\r\n\r\n/**\r\n * Cache statistics for monitoring.\r\n */\r\nexport interface CacheStats {\r\n hits: number;\r\n misses: number;\r\n size: number;\r\n hitRate: number;\r\n}\r\n\r\n/**\r\n * Simple LRU cache implementation for search results.\r\n *\r\n * Features:\r\n * - Maximum size limit (LRU eviction when full)\r\n * - TTL-based expiration\r\n * - Cache statistics tracking\r\n * - Hash-based key generation from query parameters\r\n */\r\nexport class SearchCache<T = SearchResult[] | KnowledgeGraph> {\r\n private cache: Map<string, CacheEntry<T>> = new Map();\r\n private accessOrder: string[] = [];\r\n private hits = 0;\r\n private misses = 0;\r\n\r\n constructor(\r\n private maxSize: number = 500,\r\n private ttlMs: number = 5 * 60 * 1000 // 5 minutes default\r\n ) {}\r\n\r\n /**\r\n * Generate cache key from query parameters.\r\n */\r\n private generateKey(params: Record<string, unknown>): string {\r\n // Sort keys for consistent hashing\r\n const sorted = Object.keys(params)\r\n .sort()\r\n .map(key => `${key}:${JSON.stringify(params[key])}`)\r\n .join('|');\r\n return sorted;\r\n }\r\n\r\n /**\r\n * Get value from cache.\r\n *\r\n * @param params - Query parameters to generate cache key\r\n * @returns Cached value or undefined if not found/expired\r\n */\r\n get(params: Record<string, unknown>): T | undefined {\r\n const key = this.generateKey(params);\r\n const entry = this.cache.get(key);\r\n\r\n if (!entry) {\r\n this.misses++;\r\n return undefined;\r\n }\r\n\r\n // Check expiration\r\n if (Date.now() > entry.expiresAt) {\r\n this.cache.delete(key);\r\n this.removeFromAccessOrder(key);\r\n this.misses++;\r\n return undefined;\r\n }\r\n\r\n // Update access order (move to end = most recently used)\r\n this.removeFromAccessOrder(key);\r\n this.accessOrder.push(key);\r\n this.hits++;\r\n\r\n return entry.value;\r\n }\r\n\r\n /**\r\n * Set value in cache.\r\n *\r\n * @param params - Query parameters to generate cache key\r\n * @param value - Value to cache\r\n */\r\n set(params: Record<string, unknown>, value: T): void {\r\n const key = this.generateKey(params);\r\n\r\n // Remove old entry if exists\r\n if (this.cache.has(key)) {\r\n this.removeFromAccessOrder(key);\r\n }\r\n\r\n // Evict least recently used if at capacity\r\n if (this.cache.size >= this.maxSize && !this.cache.has(key)) {\r\n const lruKey = this.accessOrder.shift();\r\n if (lruKey) {\r\n this.cache.delete(lruKey);\r\n }\r\n }\r\n\r\n // Add new entry\r\n this.cache.set(key, {\r\n value,\r\n timestamp: Date.now(),\r\n expiresAt: Date.now() + this.ttlMs,\r\n });\r\n this.accessOrder.push(key);\r\n }\r\n\r\n /**\r\n * Invalidate all cached entries.\r\n */\r\n clear(): void {\r\n this.cache.clear();\r\n this.accessOrder = [];\r\n }\r\n\r\n /**\r\n * Remove specific entry from access order.\r\n */\r\n private removeFromAccessOrder(key: string): void {\r\n const index = this.accessOrder.indexOf(key);\r\n if (index > -1) {\r\n this.accessOrder.splice(index, 1);\r\n }\r\n }\r\n\r\n /**\r\n * Get cache statistics.\r\n */\r\n getStats(): CacheStats {\r\n const total = this.hits + this.misses;\r\n return {\r\n hits: this.hits,\r\n misses: this.misses,\r\n size: this.cache.size,\r\n hitRate: total > 0 ? this.hits / total : 0,\r\n };\r\n }\r\n\r\n /**\r\n * Reset cache statistics.\r\n */\r\n resetStats(): void {\r\n this.hits = 0;\r\n this.misses = 0;\r\n }\r\n\r\n /**\r\n * Clean up expired entries.\r\n *\r\n * Should be called periodically to prevent memory buildup.\r\n */\r\n cleanupExpired(): void {\r\n const now = Date.now();\r\n const keysToDelete: string[] = [];\r\n\r\n for (const [key, entry] of this.cache.entries()) {\r\n if (now > entry.expiresAt) {\r\n keysToDelete.push(key);\r\n }\r\n }\r\n\r\n for (const key of keysToDelete) {\r\n this.cache.delete(key);\r\n this.removeFromAccessOrder(key);\r\n }\r\n }\r\n\r\n /**\r\n * Get current cache size.\r\n */\r\n get size(): number {\r\n return this.cache.size;\r\n }\r\n\r\n /**\r\n * Check if cache has entry for params.\r\n */\r\n has(params: Record<string, unknown>): boolean {\r\n const key = this.generateKey(params);\r\n const entry = this.cache.get(key);\r\n\r\n if (!entry) return false;\r\n\r\n // Check expiration\r\n if (Date.now() > entry.expiresAt) {\r\n this.cache.delete(key);\r\n this.removeFromAccessOrder(key);\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n}\r\n\r\n/**\r\n * Global search caches for different search types.\r\n */\r\nexport const searchCaches = {\r\n basic: new SearchCache<KnowledgeGraph>(),\r\n ranked: new SearchCache<SearchResult[]>(),\r\n boolean: new SearchCache<KnowledgeGraph>(),\r\n fuzzy: new SearchCache<KnowledgeGraph>(),\r\n};\r\n\r\n/**\r\n * Clear all search caches.\r\n *\r\n * Should be called when graph is modified to ensure cache consistency.\r\n */\r\nexport function clearAllSearchCaches(): void {\r\n searchCaches.basic.clear();\r\n searchCaches.ranked.clear();\r\n searchCaches.boolean.clear();\r\n searchCaches.fuzzy.clear();\r\n}\r\n\r\n/**\r\n * Get combined statistics for all caches.\r\n */\r\nexport function getAllCacheStats(): Record<string, CacheStats> {\r\n return {\r\n basic: searchCaches.basic.getStats(),\r\n ranked: searchCaches.ranked.getStats(),\r\n boolean: searchCaches.boolean.getStats(),\r\n fuzzy: searchCaches.fuzzy.getStats(),\r\n };\r\n}\r\n\r\n/**\r\n * Clean up expired entries in all caches.\r\n */\r\nexport function cleanupAllCaches(): void {\r\n searchCaches.basic.cleanupExpired();\r\n searchCaches.ranked.cleanupExpired();\r\n searchCaches.boolean.cleanupExpired();\r\n searchCaches.fuzzy.cleanupExpired();\r\n}\r\n","/**\r\n * Validation Schemas and Helpers\r\n *\r\n * Consolidated module for Zod schemas and validation utilities.\r\n * Provides runtime type safety and data validation.\r\n *\r\n * @module utils/schemas\r\n */\r\n\r\nimport { z, type ZodSchema, type ZodError } from 'zod';\r\nimport { IMPORTANCE_RANGE } from './constants.js';\r\nimport { ValidationError } from './errors.js';\r\n\r\n// ==================== Constants ====================\r\n\r\n/**\r\n * Importance range constants (imported from centralized constants).\r\n */\r\nconst MIN_IMPORTANCE = IMPORTANCE_RANGE.MIN;\r\nconst MAX_IMPORTANCE = IMPORTANCE_RANGE.MAX;\r\n\r\n// ==================== Base Schema Components ====================\r\n\r\n/**\r\n * ISO 8601 date string validation.\r\n * Accepts standard ISO format: YYYY-MM-DDTHH:mm:ss.sssZ\r\n */\r\nconst isoDateSchema = z.string().datetime({ message: 'Must be a valid ISO 8601 date string' });\r\n\r\n/**\r\n * Entity name validation.\r\n * Must be a non-empty string with reasonable length constraints.\r\n */\r\nconst entityNameSchema = z.string()\r\n .min(1, 'Entity name cannot be empty')\r\n .max(500, 'Entity name cannot exceed 500 characters')\r\n .trim();\r\n\r\n/**\r\n * Entity type validation.\r\n * Must be a non-empty string (e.g., \"person\", \"project\", \"concept\").\r\n */\r\nconst entityTypeSchema = z.string()\r\n .min(1, 'Entity type cannot be empty')\r\n .max(100, 'Entity type cannot exceed 100 characters')\r\n .trim();\r\n\r\n/**\r\n * Observation validation.\r\n * Each observation must be a non-empty string.\r\n */\r\nconst observationSchema = z.string()\r\n .min(1, 'Observation cannot be empty')\r\n .max(5000, 'Observation cannot exceed 5000 characters');\r\n\r\n/**\r\n * Tag validation.\r\n * Tags are normalized to lowercase and must be non-empty.\r\n */\r\nconst tagSchema = z.string()\r\n .min(1, 'Tag cannot be empty')\r\n .max(100, 'Tag cannot exceed 100 characters')\r\n .trim()\r\n .toLowerCase();\r\n\r\n/**\r\n * Importance validation.\r\n * Must be a number between MIN_IMPORTANCE and MAX_IMPORTANCE (0-10).\r\n */\r\nconst importanceSchema = z.number()\r\n .int('Importance must be an integer')\r\n .min(MIN_IMPORTANCE, `Importance must be at least ${MIN_IMPORTANCE}`)\r\n .max(MAX_IMPORTANCE, `Importance must be at most ${MAX_IMPORTANCE}`);\r\n\r\n/**\r\n * Relation type validation.\r\n * Should be in snake_case format (e.g., \"works_at\", \"manages\").\r\n */\r\nconst relationTypeSchema = z.string()\r\n .min(1, 'Relation type cannot be empty')\r\n .max(100, 'Relation type cannot exceed 100 characters')\r\n .trim();\r\n\r\n// ==================== Entity Schemas ====================\r\n\r\n/**\r\n * Complete Entity schema with all fields.\r\n * Used for validating full entity objects including timestamps.\r\n */\r\nexport const EntitySchema = z.object({\r\n name: entityNameSchema,\r\n entityType: entityTypeSchema,\r\n observations: z.array(observationSchema),\r\n createdAt: isoDateSchema.optional(),\r\n lastModified: isoDateSchema.optional(),\r\n tags: z.array(tagSchema).optional(),\r\n importance: importanceSchema.optional(),\r\n parentId: entityNameSchema.optional(),\r\n}).strict();\r\n\r\n/**\r\n * Entity creation input schema.\r\n * Used for validating user input when creating new entities.\r\n * Timestamps are optional and will be auto-generated if not provided.\r\n */\r\nexport const CreateEntitySchema = z.object({\r\n name: entityNameSchema,\r\n entityType: entityTypeSchema,\r\n observations: z.array(observationSchema),\r\n tags: z.array(tagSchema).optional(),\r\n importance: importanceSchema.optional(),\r\n parentId: entityNameSchema.optional(),\r\n createdAt: isoDateSchema.optional(),\r\n lastModified: isoDateSchema.optional(),\r\n}).strict();\r\n\r\n/**\r\n * Entity update input schema.\r\n * All fields are optional for partial updates.\r\n * Name cannot be updated (it's the unique identifier).\r\n */\r\nexport const UpdateEntitySchema = z.object({\r\n entityType: entityTypeSchema.optional(),\r\n observations: z.array(observationSchema).optional(),\r\n tags: z.array(tagSchema).optional(),\r\n importance: importanceSchema.optional(),\r\n parentId: entityNameSchema.optional(),\r\n}).strict();\r\n\r\n// ==================== Relation Schemas ====================\r\n\r\n/**\r\n * Complete Relation schema with all fields.\r\n * Used for validating full relation objects including timestamps.\r\n */\r\nexport const RelationSchema = z.object({\r\n from: entityNameSchema,\r\n to: entityNameSchema,\r\n relationType: relationTypeSchema,\r\n createdAt: isoDateSchema.optional(),\r\n lastModified: isoDateSchema.optional(),\r\n}).strict();\r\n\r\n/**\r\n * Relation creation input schema.\r\n * Used for validating user input when creating new relations.\r\n * Timestamps are optional and will be auto-generated if not provided.\r\n */\r\nexport const CreateRelationSchema = z.object({\r\n from: entityNameSchema,\r\n to: entityNameSchema,\r\n relationType: relationTypeSchema,\r\n createdAt: isoDateSchema.optional(),\r\n lastModified: isoDateSchema.optional(),\r\n}).strict();\r\n\r\n// ==================== Search Schemas ====================\r\n\r\n/**\r\n * Search query validation.\r\n * Validates text search queries with reasonable length constraints.\r\n */\r\nexport const SearchQuerySchema = z.string()\r\n .min(1, 'Search query cannot be empty')\r\n .max(1000, 'Search query cannot exceed 1000 characters')\r\n .trim();\r\n\r\n/**\r\n * Date range validation for search filters.\r\n */\r\nexport const DateRangeSchema = z.object({\r\n start: isoDateSchema,\r\n end: isoDateSchema,\r\n}).strict().refine(\r\n (data) => new Date(data.start) <= new Date(data.end),\r\n { message: 'Start date must be before or equal to end date' }\r\n);\r\n\r\n// ==================== Tag Schemas ====================\r\n\r\n/**\r\n * Tag alias validation for TagManager.\r\n */\r\nexport const TagAliasSchema = z.object({\r\n canonical: tagSchema,\r\n aliases: z.array(tagSchema).min(1, 'Must have at least one alias'),\r\n}).strict();\r\n\r\n// ==================== Export Schemas ====================\r\n\r\n/**\r\n * Export format validation.\r\n */\r\nexport const ExportFormatSchema = z.enum(['json', 'graphml', 'csv']);\r\n\r\n// ==================== Batch Operation Schemas ====================\r\n\r\n/**\r\n * Batch entity creation validation.\r\n * Validates array of entities with maximum constraints.\r\n * Empty arrays are allowed (no-op).\r\n */\r\nexport const BatchCreateEntitiesSchema = z.array(CreateEntitySchema)\r\n .max(1000, 'Cannot create more than 1000 entities in a single batch');\r\n\r\n/**\r\n * Batch relation creation validation.\r\n * Validates array of relations with maximum constraints.\r\n * Empty arrays are allowed (no-op).\r\n */\r\nexport const BatchCreateRelationsSchema = z.array(CreateRelationSchema)\r\n .max(1000, 'Cannot create more than 1000 relations in a single batch');\r\n\r\n/**\r\n * Entity name array validation for batch deletion.\r\n */\r\nexport const EntityNamesSchema = z.array(entityNameSchema)\r\n .min(1, 'Must specify at least one entity name')\r\n .max(1000, 'Cannot delete more than 1000 entities in a single batch');\r\n\r\n/**\r\n * Relation array validation for batch deletion.\r\n */\r\nexport const DeleteRelationsSchema = z.array(CreateRelationSchema)\r\n .min(1, 'Must specify at least one relation')\r\n .max(1000, 'Cannot delete more than 1000 relations in a single batch');\r\n\r\n// ==================== Observation Schemas ====================\r\n\r\n/**\r\n * Single observation input for add operations.\r\n * Empty contents array is allowed (no-op).\r\n */\r\nexport const AddObservationInputSchema = z.object({\r\n entityName: entityNameSchema,\r\n contents: z.array(observationSchema),\r\n}).strict();\r\n\r\n/**\r\n * Batch observation addition validation.\r\n * Empty array is allowed (no-op).\r\n */\r\nexport const AddObservationsInputSchema = z.array(AddObservationInputSchema)\r\n .max(1000, 'Cannot add observations to more than 1000 entities in a single batch');\r\n\r\n/**\r\n * Single observation deletion input.\r\n * Empty observations array is allowed (no-op).\r\n * Non-existent entities are silently skipped by the manager.\r\n */\r\nexport const DeleteObservationInputSchema = z.object({\r\n entityName: entityNameSchema,\r\n observations: z.array(observationSchema),\r\n}).strict();\r\n\r\n/**\r\n * Batch observation deletion validation.\r\n * Empty array is allowed (no-op).\r\n */\r\nexport const DeleteObservationsInputSchema = z.array(DeleteObservationInputSchema)\r\n .max(1000, 'Cannot delete observations from more than 1000 entities in a single batch');\r\n\r\n// ==================== Archive Schema ====================\r\n\r\n/**\r\n * Archive criteria validation.\r\n * All fields are optional - the manager handles the case when no criteria provided.\r\n */\r\nexport const ArchiveCriteriaSchema = z.object({\r\n olderThan: isoDateSchema.optional(),\r\n importanceLessThan: z.number().min(0).max(10).optional(),\r\n tags: z.array(tagSchema).optional(),\r\n}).strict();\r\n\r\n// ==================== Saved Search Schemas ====================\r\n\r\n/**\r\n * Saved search creation input validation.\r\n */\r\nexport const SavedSearchInputSchema = z.object({\r\n name: z.string().min(1, 'Search name cannot be empty').max(200, 'Search name cannot exceed 200 characters').trim(),\r\n description: z.string().max(1000, 'Description cannot exceed 1000 characters').optional(),\r\n query: SearchQuerySchema,\r\n tags: z.array(tagSchema).optional(),\r\n minImportance: importanceSchema.optional(),\r\n maxImportance: importanceSchema.optional(),\r\n entityType: entityTypeSchema.optional(),\r\n}).strict();\r\n\r\n/**\r\n * Saved search update validation.\r\n * All fields are optional for partial updates.\r\n */\r\nexport const SavedSearchUpdateSchema = z.object({\r\n description: z.string().max(1000, 'Description cannot exceed 1000 characters').optional(),\r\n query: SearchQuerySchema.optional(),\r\n tags: z.array(tagSchema).optional(),\r\n minImportance: importanceSchema.optional(),\r\n maxImportance: importanceSchema.optional(),\r\n entityType: entityTypeSchema.optional(),\r\n}).strict();\r\n\r\n// ==================== Import/Export Schemas ====================\r\n\r\n/**\r\n * Import format validation.\r\n */\r\nexport const ImportFormatSchema = z.enum(['json', 'csv', 'graphml']);\r\n\r\n/**\r\n * Export format validation (includes all output formats).\r\n */\r\nexport const ExtendedExportFormatSchema = z.enum(['json', 'csv', 'graphml', 'gexf', 'dot', 'markdown', 'mermaid']);\r\n\r\n/**\r\n * Merge strategy validation for imports.\r\n */\r\nexport const MergeStrategySchema = z.enum(['replace', 'skip', 'merge', 'fail']);\r\n\r\n/**\r\n * Export filter validation.\r\n */\r\nexport const ExportFilterSchema = z.object({\r\n startDate: isoDateSchema.optional(),\r\n endDate: isoDateSchema.optional(),\r\n entityType: entityTypeSchema.optional(),\r\n tags: z.array(tagSchema).optional(),\r\n}).strict();\r\n\r\n// ==================== Search Parameter Schemas ====================\r\n\r\n/**\r\n * Tags array validation (optional, for search filters).\r\n */\r\nexport const OptionalTagsSchema = z.array(tagSchema).optional();\r\n\r\n/**\r\n * Optional entity names array validation.\r\n */\r\nexport const OptionalEntityNamesSchema = z.array(entityNameSchema).optional();\r\n\r\n// ==================== Schema Type Exports ====================\r\n\r\nexport type EntityInput = z.infer<typeof EntitySchema>;\r\nexport type CreateEntityInput = z.infer<typeof CreateEntitySchema>;\r\nexport type UpdateEntityInput = z.infer<typeof UpdateEntitySchema>;\r\nexport type RelationInput = z.infer<typeof RelationSchema>;\r\nexport type CreateRelationInput = z.infer<typeof CreateRelationSchema>;\r\nexport type SearchQuery = z.infer<typeof SearchQuerySchema>;\r\nexport type DateRange = z.infer<typeof DateRangeSchema>;\r\nexport type TagAliasInput = z.infer<typeof TagAliasSchema>;\r\nexport type AddObservationInput = z.infer<typeof AddObservationInputSchema>;\r\nexport type DeleteObservationInput = z.infer<typeof DeleteObservationInputSchema>;\r\nexport type ArchiveCriteriaInput = z.infer<typeof ArchiveCriteriaSchema>;\r\nexport type SavedSearchInput = z.infer<typeof SavedSearchInputSchema>;\r\nexport type SavedSearchUpdateInput = z.infer<typeof SavedSearchUpdateSchema>;\r\nexport type ImportFormatInput = z.infer<typeof ImportFormatSchema>;\r\nexport type ExtendedExportFormatInput = z.infer<typeof ExtendedExportFormatSchema>;\r\nexport type MergeStrategyInput = z.infer<typeof MergeStrategySchema>;\r\nexport type ExportFilterInput = z.infer<typeof ExportFilterSchema>;\r\n\r\n// ==================== Validation Result Type ====================\r\n\r\n/**\r\n * Validation result with status and error messages.\r\n */\r\nexport interface ValidationResult {\r\n valid: boolean;\r\n errors: string[];\r\n}\r\n\r\n// ==================== Zod Validation Helpers ====================\r\n\r\n/**\r\n * Formats Zod errors into human-readable strings.\r\n *\r\n * @param error - Zod error object\r\n * @returns Array of formatted error messages\r\n */\r\nexport function formatZodErrors(error: ZodError): string[] {\r\n return error.issues.map(issue => {\r\n const path = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';\r\n return `${path}${issue.message}`;\r\n });\r\n}\r\n\r\n/**\r\n * Validates data against a Zod schema and returns the typed result.\r\n * Throws ValidationError with formatted error messages on failure.\r\n *\r\n * @param data - The data to validate\r\n * @param schema - The Zod schema to validate against\r\n * @param errorMessage - Custom error message prefix (default: 'Validation failed')\r\n * @returns The validated and typed data\r\n * @throws ValidationError if validation fails\r\n *\r\n * @example\r\n * ```typescript\r\n * const entities = validateWithSchema(\r\n * input,\r\n * BatchCreateEntitiesSchema,\r\n * 'Invalid entity data'\r\n * );\r\n * ```\r\n */\r\nexport function validateWithSchema<T>(\r\n data: unknown,\r\n schema: ZodSchema<T>,\r\n errorMessage: string = 'Validation failed'\r\n): T {\r\n const result = schema.safeParse(data);\r\n if (!result.success) {\r\n const errors = formatZodErrors(result.error);\r\n throw new ValidationError(errorMessage, errors);\r\n }\r\n return result.data;\r\n}\r\n\r\n/**\r\n * Validates data and returns a result object instead of throwing.\r\n * Useful when you want to handle validation errors gracefully.\r\n *\r\n * @param data - The data to validate\r\n * @param schema - The Zod schema to validate against\r\n * @returns Result object with success status and either data or errors\r\n *\r\n * @example\r\n * ```typescript\r\n * const result = validateSafe(input, EntitySchema);\r\n * if (result.success) {\r\n * console.log(result.data);\r\n * } else {\r\n * console.error(result.errors);\r\n * }\r\n * ```\r\n */\r\nexport function validateSafe<T>(\r\n data: unknown,\r\n schema: ZodSchema<T>\r\n): { success: true; data: T } | { success: false; errors: string[] } {\r\n const result = schema.safeParse(data);\r\n if (result.success) {\r\n return { success: true, data: result.data };\r\n }\r\n return { success: false, errors: formatZodErrors(result.error) };\r\n}\r\n\r\n/**\r\n * Validates an array of items against a schema.\r\n * Returns detailed information about which items failed validation.\r\n *\r\n * @param items - Array of items to validate\r\n * @param schema - Zod schema for individual items\r\n * @param errorMessage - Custom error message prefix\r\n * @returns Array of validated items\r\n * @throws ValidationError if any item fails validation\r\n */\r\nexport function validateArrayWithSchema<T>(\r\n items: unknown[],\r\n schema: ZodSchema<T>,\r\n errorMessage: string = 'Array validation failed'\r\n): T[] {\r\n const errors: string[] = [];\r\n const validated: T[] = [];\r\n\r\n for (let i = 0; i < items.length; i++) {\r\n const result = schema.safeParse(items[i]);\r\n if (result.success) {\r\n validated.push(result.data);\r\n } else {\r\n const itemErrors = formatZodErrors(result.error);\r\n errors.push(...itemErrors.map(e => `[${i}] ${e}`));\r\n }\r\n }\r\n\r\n if (errors.length > 0) {\r\n throw new ValidationError(errorMessage, errors);\r\n }\r\n\r\n return validated;\r\n}\r\n\r\n// ==================== Manual Validation Functions ====================\r\n\r\n/**\r\n * Type guard to check if value is a non-null object.\r\n */\r\nfunction isObject(value: unknown): value is Record<string, unknown> {\r\n return typeof value === 'object' && value !== null && !Array.isArray(value);\r\n}\r\n\r\n/**\r\n * Validate an entity object.\r\n *\r\n * Checks required fields and data types.\r\n *\r\n * @param entity - Entity to validate (unknown type for runtime validation)\r\n * @returns Validation result\r\n */\r\nexport function validateEntity(entity: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n if (!isObject(entity)) {\r\n return { valid: false, errors: ['Entity must be an object'] };\r\n }\r\n\r\n if (!entity.name || typeof entity.name !== 'string' || entity.name.trim() === '') {\r\n errors.push('Entity name is required and must be a non-empty string');\r\n }\r\n\r\n if (!entity.entityType || typeof entity.entityType !== 'string' || entity.entityType.trim() === '') {\r\n errors.push('Entity type is required and must be a non-empty string');\r\n }\r\n\r\n if (!Array.isArray(entity.observations)) {\r\n errors.push('Observations must be an array');\r\n } else if (!entity.observations.every((o: unknown) => typeof o === 'string')) {\r\n errors.push('All observations must be strings');\r\n }\r\n\r\n if (entity.tags !== undefined) {\r\n if (!Array.isArray(entity.tags)) {\r\n errors.push('Tags must be an array');\r\n } else if (!entity.tags.every((t: unknown) => typeof t === 'string')) {\r\n errors.push('All tags must be strings');\r\n }\r\n }\r\n\r\n if (entity.importance !== undefined) {\r\n if (typeof entity.importance !== 'number') {\r\n errors.push('Importance must be a number');\r\n } else if (!validateImportance(entity.importance)) {\r\n errors.push('Importance must be between 0 and 10');\r\n }\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate a relation object.\r\n *\r\n * Checks required fields and data types.\r\n *\r\n * @param relation - Relation to validate (unknown type for runtime validation)\r\n * @returns Validation result\r\n */\r\nexport function validateRelation(relation: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n if (!isObject(relation)) {\r\n return { valid: false, errors: ['Relation must be an object'] };\r\n }\r\n\r\n if (!relation.from || typeof relation.from !== 'string' || relation.from.trim() === '') {\r\n errors.push('Relation \"from\" is required and must be a non-empty string');\r\n }\r\n\r\n if (!relation.to || typeof relation.to !== 'string' || relation.to.trim() === '') {\r\n errors.push('Relation \"to\" is required and must be a non-empty string');\r\n }\r\n\r\n if (!relation.relationType || typeof relation.relationType !== 'string' || relation.relationType.trim() === '') {\r\n errors.push('Relation type is required and must be a non-empty string');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n\r\n/**\r\n * Validate importance level (must be 0-10).\r\n *\r\n * @param importance - Importance value to validate\r\n * @returns True if valid\r\n */\r\nexport function validateImportance(importance: number): boolean {\r\n return typeof importance === 'number'\r\n && !isNaN(importance)\r\n && importance >= IMPORTANCE_RANGE.MIN\r\n && importance <= IMPORTANCE_RANGE.MAX;\r\n}\r\n\r\n/**\r\n * Validate an array of tags.\r\n *\r\n * @param tags - Tags array to validate (unknown type for runtime validation)\r\n * @returns Validation result\r\n */\r\nexport function validateTags(tags: unknown): ValidationResult {\r\n const errors: string[] = [];\r\n\r\n if (!Array.isArray(tags)) {\r\n return { valid: false, errors: ['Tags must be an array'] };\r\n }\r\n\r\n if (!tags.every((t: unknown) => typeof t === 'string' && t.trim() !== '')) {\r\n errors.push('All tags must be non-empty strings');\r\n }\r\n\r\n return { valid: errors.length === 0, errors };\r\n}\r\n","/**\r\n * Response and Pagination Formatters\r\n *\r\n * Consolidated module for MCP tool response formatting and pagination utilities.\r\n * Centralizes response formatting for MCP tool calls to eliminate redundant patterns.\r\n *\r\n * @module utils/formatters\r\n */\r\n\r\nimport { SEARCH_LIMITS } from './constants.js';\r\n\r\n// ==================== MCP Tool Response Formatting ====================\r\n\r\n/**\r\n * MCP Tool Response type - uses the exact shape expected by the SDK.\r\n * The 'as const' assertion ensures the type literal is preserved.\r\n */\r\nexport type ToolResponse = {\r\n content: Array<{ type: 'text'; text: string }>;\r\n isError?: boolean;\r\n};\r\n\r\n/**\r\n * Formats data as an MCP tool response with JSON content.\r\n * Centralizes the response format to ensure consistency and reduce duplication.\r\n *\r\n * @param data - Any data to be JSON stringified\r\n * @returns Formatted MCP tool response\r\n */\r\nexport function formatToolResponse(data: unknown) {\r\n return {\r\n content: [{ type: 'text' as const, text: JSON.stringify(data, null, 2) }],\r\n };\r\n}\r\n\r\n/**\r\n * Formats a simple text message as an MCP tool response.\r\n * Use for success messages that don't need JSON formatting.\r\n *\r\n * @param message - Plain text message\r\n * @returns Formatted MCP tool response\r\n */\r\nexport function formatTextResponse(message: string) {\r\n return {\r\n content: [{ type: 'text' as const, text: message }],\r\n };\r\n}\r\n\r\n/**\r\n * Formats raw string content as an MCP tool response.\r\n * Use for export formats that return pre-formatted strings (markdown, CSV, etc.)\r\n *\r\n * @param content - Raw string content\r\n * @returns Formatted MCP tool response\r\n */\r\nexport function formatRawResponse(content: string) {\r\n return {\r\n content: [{ type: 'text' as const, text: content }],\r\n };\r\n}\r\n\r\n/**\r\n * Formats an error as an MCP tool response with isError flag.\r\n *\r\n * @param error - Error object or message string\r\n * @returns Formatted MCP tool error response\r\n */\r\nexport function formatErrorResponse(error: Error | string) {\r\n const message = error instanceof Error ? error.message : error;\r\n return {\r\n content: [{ type: 'text' as const, text: message }],\r\n isError: true,\r\n };\r\n}\r\n\r\n// ==================== Pagination Utilities ====================\r\n\r\n/**\r\n * Validated pagination parameters with helper methods.\r\n */\r\nexport interface ValidatedPagination {\r\n /** Validated offset (guaranteed >= 0) */\r\n offset: number;\r\n /** Validated limit (guaranteed within SEARCH_LIMITS.MIN to SEARCH_LIMITS.MAX) */\r\n limit: number;\r\n /**\r\n * Check if there are more results beyond the current page.\r\n * @param totalCount - Total number of items\r\n * @returns true if there are more items after this page\r\n */\r\n hasMore: (totalCount: number) => boolean;\r\n}\r\n\r\n/**\r\n * Validates and normalizes pagination parameters.\r\n * Ensures offset is non-negative and limit is within configured bounds.\r\n *\r\n * @param offset - Starting position (default: 0)\r\n * @param limit - Maximum results to return (default: SEARCH_LIMITS.DEFAULT)\r\n * @returns Validated pagination parameters with helper methods\r\n *\r\n * @example\r\n * ```typescript\r\n * const pagination = validatePagination(10, 50);\r\n * const results = items.slice(pagination.offset, pagination.offset + pagination.limit);\r\n * if (pagination.hasMore(items.length)) {\r\n * console.log('More results available');\r\n * }\r\n * ```\r\n */\r\nexport function validatePagination(\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n): ValidatedPagination {\r\n const validatedOffset = Math.max(0, offset);\r\n const validatedLimit = Math.min(\r\n Math.max(SEARCH_LIMITS.MIN, limit),\r\n SEARCH_LIMITS.MAX\r\n );\r\n\r\n return {\r\n offset: validatedOffset,\r\n limit: validatedLimit,\r\n hasMore: (totalCount: number) => validatedOffset + validatedLimit < totalCount,\r\n };\r\n}\r\n\r\n/**\r\n * Applies pagination to an array of items.\r\n *\r\n * @param items - Array to paginate\r\n * @param pagination - Validated pagination parameters\r\n * @returns Paginated slice of the array\r\n *\r\n * @example\r\n * ```typescript\r\n * const pagination = validatePagination(offset, limit);\r\n * const pageResults = applyPagination(allResults, pagination);\r\n * ```\r\n */\r\nexport function applyPagination<T>(\r\n items: T[],\r\n pagination: ValidatedPagination\r\n): T[] {\r\n return items.slice(pagination.offset, pagination.offset + pagination.limit);\r\n}\r\n\r\n/**\r\n * Applies pagination using raw offset and limit values.\r\n * Combines validation and application in one call.\r\n *\r\n * @param items - Array to paginate\r\n * @param offset - Starting position\r\n * @param limit - Maximum results\r\n * @returns Paginated slice of the array\r\n */\r\nexport function paginateArray<T>(\r\n items: T[],\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n): T[] {\r\n const pagination = validatePagination(offset, limit);\r\n return applyPagination(items, pagination);\r\n}\r\n\r\n/**\r\n * Calculates pagination metadata for a result set.\r\n *\r\n * @param totalCount - Total number of items\r\n * @param offset - Current offset\r\n * @param limit - Current limit\r\n * @returns Pagination metadata\r\n */\r\nexport function getPaginationMeta(\r\n totalCount: number,\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n): {\r\n totalCount: number;\r\n offset: number;\r\n limit: number;\r\n hasMore: boolean;\r\n pageNumber: number;\r\n totalPages: number;\r\n} {\r\n const pagination = validatePagination(offset, limit);\r\n\r\n return {\r\n totalCount,\r\n offset: pagination.offset,\r\n limit: pagination.limit,\r\n hasMore: pagination.hasMore(totalCount),\r\n pageNumber: Math.floor(pagination.offset / pagination.limit) + 1,\r\n totalPages: Math.ceil(totalCount / pagination.limit),\r\n };\r\n}\r\n","/**\r\n * Entity Utilities\r\n *\r\n * Consolidated module for entity-related utilities including:\r\n * - Entity lookup and manipulation functions\r\n * - Tag normalization and matching\r\n * - Date parsing and validation\r\n * - Entity filtering by various criteria\r\n * - Path utilities and validation\r\n *\r\n * @module utils/entityUtils\r\n */\r\n\r\nimport { promises as fs } from 'fs';\r\nimport path from 'path';\r\nimport { fileURLToPath } from 'url';\r\nimport type { Entity, KnowledgeGraph } from '../types/index.js';\r\nimport { EntityNotFoundError, FileOperationError } from './errors.js';\r\n\r\n// ==================== Hash Functions ====================\r\n\r\n/**\r\n * FNV-1a hash function for fast string hashing.\r\n *\r\n * This is a non-cryptographic hash function that provides good distribution\r\n * for bucketing and deduplication purposes. It's optimized for speed\r\n * and produces a 32-bit unsigned integer.\r\n *\r\n * FNV-1a has the following properties:\r\n * - Fast computation (single pass through string)\r\n * - Good distribution for hash table use\r\n * - Deterministic output for same input\r\n *\r\n * @param text - The string to hash\r\n * @returns A 32-bit unsigned integer hash value\r\n *\r\n * @example\r\n * ```typescript\r\n * const hash = fnv1aHash('hello');\r\n * console.log(hash); // 1335831723\r\n *\r\n * // Use for bucketing similar entities\r\n * const bucket = fnv1aHash(entity.name.toLowerCase()) % numBuckets;\r\n * ```\r\n */\r\nexport function fnv1aHash(text: string): number {\r\n let hash = 2166136261; // FNV offset basis\r\n for (let i = 0; i < text.length; i++) {\r\n hash ^= text.charCodeAt(i);\r\n hash = Math.imul(hash, 16777619); // FNV prime\r\n }\r\n return hash >>> 0; // Convert to unsigned 32-bit integer\r\n}\r\n\r\n// ==================== Entity Lookup Functions ====================\r\n\r\n/**\r\n * Finds an entity by name in the graph.\r\n * Overloaded to provide type-safe returns based on throwIfNotFound parameter.\r\n *\r\n * @param graph - The knowledge graph to search\r\n * @param name - The entity name to find\r\n * @param throwIfNotFound - Whether to throw if entity doesn't exist (default: true)\r\n * @returns The entity if found, null if not found and throwIfNotFound is false\r\n * @throws EntityNotFoundError if entity not found and throwIfNotFound is true\r\n */\r\nexport function findEntityByName(\r\n graph: KnowledgeGraph,\r\n name: string,\r\n throwIfNotFound: true\r\n): Entity;\r\nexport function findEntityByName(\r\n graph: KnowledgeGraph,\r\n name: string,\r\n throwIfNotFound: false\r\n): Entity | null;\r\nexport function findEntityByName(\r\n graph: KnowledgeGraph,\r\n name: string,\r\n throwIfNotFound?: boolean\r\n): Entity | null;\r\nexport function findEntityByName(\r\n graph: KnowledgeGraph,\r\n name: string,\r\n throwIfNotFound: boolean = true\r\n): Entity | null {\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity && throwIfNotFound) {\r\n throw new EntityNotFoundError(name);\r\n }\r\n return entity ?? null;\r\n}\r\n\r\n/**\r\n * Finds multiple entities by name.\r\n *\r\n * @param graph - The knowledge graph to search\r\n * @param names - Array of entity names to find\r\n * @param throwIfAnyNotFound - Whether to throw if any entity doesn't exist (default: true)\r\n * @returns Array of found entities (may be shorter than names if throwIfAnyNotFound is false)\r\n * @throws EntityNotFoundError if any entity not found and throwIfAnyNotFound is true\r\n */\r\nexport function findEntitiesByNames(\r\n graph: KnowledgeGraph,\r\n names: string[],\r\n throwIfAnyNotFound: boolean = true\r\n): Entity[] {\r\n const entities: Entity[] = [];\r\n\r\n for (const name of names) {\r\n const entity = findEntityByName(graph, name, false);\r\n if (entity) {\r\n entities.push(entity);\r\n } else if (throwIfAnyNotFound) {\r\n throw new EntityNotFoundError(name);\r\n }\r\n }\r\n\r\n return entities;\r\n}\r\n\r\n/**\r\n * Checks if an entity exists in the graph.\r\n *\r\n * @param graph - The knowledge graph to search\r\n * @param name - The entity name to check\r\n * @returns true if entity exists, false otherwise\r\n */\r\nexport function entityExists(graph: KnowledgeGraph, name: string): boolean {\r\n return graph.entities.some(e => e.name === name);\r\n}\r\n\r\n/**\r\n * Gets the index of an entity in the graph's entities array.\r\n *\r\n * @param graph - The knowledge graph to search\r\n * @param name - The entity name to find\r\n * @returns The index if found, -1 otherwise\r\n */\r\nexport function getEntityIndex(graph: KnowledgeGraph, name: string): number {\r\n return graph.entities.findIndex(e => e.name === name);\r\n}\r\n\r\n/**\r\n * Removes an entity from the graph by name.\r\n * Mutates the graph's entities array in place.\r\n *\r\n * @param graph - The knowledge graph to modify\r\n * @param name - The entity name to remove\r\n * @returns true if entity was removed, false if not found\r\n */\r\nexport function removeEntityByName(graph: KnowledgeGraph, name: string): boolean {\r\n const index = getEntityIndex(graph, name);\r\n if (index === -1) return false;\r\n graph.entities.splice(index, 1);\r\n return true;\r\n}\r\n\r\n/**\r\n * Gets all entity names as a Set for fast lookup.\r\n *\r\n * @param graph - The knowledge graph\r\n * @returns Set of all entity names\r\n */\r\nexport function getEntityNameSet(graph: KnowledgeGraph): Set<string> {\r\n return new Set(graph.entities.map(e => e.name));\r\n}\r\n\r\n/**\r\n * Groups entities by their type.\r\n *\r\n * @param entities - Array of entities to group\r\n * @returns Map of entity type to array of entities\r\n */\r\nexport function groupEntitiesByType(entities: Entity[]): Map<string, Entity[]> {\r\n const groups = new Map<string, Entity[]>();\r\n\r\n for (const entity of entities) {\r\n const type = entity.entityType;\r\n if (!groups.has(type)) {\r\n groups.set(type, []);\r\n }\r\n groups.get(type)!.push(entity);\r\n }\r\n\r\n return groups;\r\n}\r\n\r\n/**\r\n * Updates the lastModified timestamp on an entity.\r\n * Mutates the entity in place.\r\n *\r\n * @param entity - The entity to update\r\n * @returns The updated entity (same reference)\r\n */\r\nexport function touchEntity(entity: Entity): Entity {\r\n entity.lastModified = new Date().toISOString();\r\n return entity;\r\n}\r\n\r\n// ==================== Tag Normalization and Matching ====================\r\n\r\n/**\r\n * Normalizes a single tag to lowercase and trimmed.\r\n *\r\n * @param tag - Tag to normalize\r\n * @returns Normalized tag\r\n */\r\nexport function normalizeTag(tag: string): string {\r\n return tag.toLowerCase().trim();\r\n}\r\n\r\n/**\r\n * Normalizes an array of tags to lowercase.\r\n * Handles undefined/null input gracefully.\r\n *\r\n * @param tags - Array of tags to normalize, or undefined\r\n * @returns Normalized tags array, or empty array if input is undefined/null\r\n */\r\nexport function normalizeTags(tags: string[] | undefined | null): string[] {\r\n if (!tags || tags.length === 0) return [];\r\n return tags.map(tag => tag.toLowerCase());\r\n}\r\n\r\n/**\r\n * Checks if an entity's tags include any of the specified search tags.\r\n * Both inputs are normalized before comparison.\r\n *\r\n * @param entityTags - Tags on the entity (may be undefined)\r\n * @param searchTags - Tags to search for (may be undefined)\r\n * @returns true if any search tag matches any entity tag, false if no match or either is empty\r\n */\r\nexport function hasMatchingTag(\r\n entityTags: string[] | undefined,\r\n searchTags: string[] | undefined\r\n): boolean {\r\n if (!entityTags || entityTags.length === 0) return false;\r\n if (!searchTags || searchTags.length === 0) return false;\r\n\r\n const normalizedEntity = normalizeTags(entityTags);\r\n const normalizedSearch = normalizeTags(searchTags);\r\n\r\n return normalizedSearch.some(tag => normalizedEntity.includes(tag));\r\n}\r\n\r\n/**\r\n * Checks if entity tags include ALL of the specified required tags.\r\n *\r\n * @param entityTags - Tags on the entity (may be undefined)\r\n * @param requiredTags - All tags that must be present\r\n * @returns true if all required tags are present\r\n */\r\nexport function hasAllTags(\r\n entityTags: string[] | undefined,\r\n requiredTags: string[]\r\n): boolean {\r\n if (!entityTags || entityTags.length === 0) return false;\r\n if (requiredTags.length === 0) return true;\r\n\r\n const normalizedEntity = normalizeTags(entityTags);\r\n return normalizeTags(requiredTags).every(tag => normalizedEntity.includes(tag));\r\n}\r\n\r\n/**\r\n * Filters entities by tag match.\r\n * Returns all entities if searchTags is empty or undefined.\r\n *\r\n * @param entities - Array of entities with optional tags property\r\n * @param searchTags - Tags to filter by\r\n * @returns Filtered entities that have at least one matching tag\r\n */\r\nexport function filterByTags<T extends { tags?: string[] }>(\r\n entities: T[],\r\n searchTags: string[] | undefined\r\n): T[] {\r\n if (!searchTags || searchTags.length === 0) {\r\n return entities;\r\n }\r\n\r\n const normalizedSearch = normalizeTags(searchTags);\r\n\r\n return entities.filter(entity => {\r\n if (!entity.tags || entity.tags.length === 0) return false;\r\n const normalizedEntity = normalizeTags(entity.tags);\r\n return normalizedSearch.some(tag => normalizedEntity.includes(tag));\r\n });\r\n}\r\n\r\n/**\r\n * Adds new tags to an existing tag array, avoiding duplicates.\r\n * All tags are normalized to lowercase.\r\n *\r\n * @param existingTags - Current tags (may be undefined)\r\n * @param newTags - Tags to add\r\n * @returns Combined tags array with no duplicates\r\n */\r\nexport function addUniqueTags(\r\n existingTags: string[] | undefined,\r\n newTags: string[]\r\n): string[] {\r\n const existing = normalizeTags(existingTags);\r\n const toAdd = normalizeTags(newTags);\r\n\r\n const uniqueNew = toAdd.filter(tag => !existing.includes(tag));\r\n return [...existing, ...uniqueNew];\r\n}\r\n\r\n/**\r\n * Removes specified tags from an existing tag array.\r\n * Comparison is case-insensitive.\r\n *\r\n * @param existingTags - Current tags (may be undefined)\r\n * @param tagsToRemove - Tags to remove\r\n * @returns Tags array with specified tags removed\r\n */\r\nexport function removeTags(\r\n existingTags: string[] | undefined,\r\n tagsToRemove: string[]\r\n): string[] {\r\n if (!existingTags || existingTags.length === 0) return [];\r\n\r\n const toRemoveNormalized = normalizeTags(tagsToRemove);\r\n return existingTags.filter(tag => !toRemoveNormalized.includes(tag.toLowerCase()));\r\n}\r\n\r\n// ==================== Date Utilities ====================\r\n\r\n/**\r\n * Check if a date falls within a specified range.\r\n *\r\n * @param date - ISO 8601 date string to check (may be undefined)\r\n * @param start - Optional start date (inclusive)\r\n * @param end - Optional end date (inclusive)\r\n * @returns True if date is within range or no filters are set\r\n *\r\n * @example\r\n * ```typescript\r\n * isWithinDateRange('2024-06-15T00:00:00Z', '2024-01-01T00:00:00Z', '2024-12-31T23:59:59Z'); // true\r\n * isWithinDateRange('2024-06-15T00:00:00Z', '2024-07-01T00:00:00Z'); // false\r\n * isWithinDateRange(undefined); // true (no filters)\r\n * isWithinDateRange(undefined, '2024-01-01T00:00:00Z'); // false (has filter but no date)\r\n * ```\r\n */\r\nexport function isWithinDateRange(\r\n date: string | undefined,\r\n start?: string,\r\n end?: string\r\n): boolean {\r\n // If no filters set, always pass\r\n if (!start && !end) {\r\n return true;\r\n }\r\n\r\n // If date is undefined but we have filters, fail\r\n if (!date) {\r\n return false;\r\n }\r\n\r\n const dateObj = new Date(date);\r\n\r\n if (isNaN(dateObj.getTime())) {\r\n return false;\r\n }\r\n\r\n if (start) {\r\n const startObj = new Date(start);\r\n if (isNaN(startObj.getTime())) {\r\n return false;\r\n }\r\n if (dateObj < startObj) {\r\n return false;\r\n }\r\n }\r\n\r\n if (end) {\r\n const endObj = new Date(end);\r\n if (isNaN(endObj.getTime())) {\r\n return false;\r\n }\r\n if (dateObj > endObj) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Parse and validate date range strings.\r\n *\r\n * @param startDate - Optional ISO 8601 start date\r\n * @param endDate - Optional ISO 8601 end date\r\n * @returns Parsed Date objects or null\r\n */\r\nexport function parseDateRange(\r\n startDate?: string,\r\n endDate?: string\r\n): { start: Date | null; end: Date | null } {\r\n let start: Date | null = null;\r\n let end: Date | null = null;\r\n\r\n if (startDate) {\r\n start = new Date(startDate);\r\n if (isNaN(start.getTime())) {\r\n start = null;\r\n }\r\n }\r\n\r\n if (endDate) {\r\n end = new Date(endDate);\r\n if (isNaN(end.getTime())) {\r\n end = null;\r\n }\r\n }\r\n\r\n return { start, end };\r\n}\r\n\r\n/**\r\n * Validate if a string is a valid ISO 8601 date.\r\n *\r\n * @param date - Date string to validate\r\n * @returns True if valid ISO 8601 date\r\n */\r\nexport function isValidISODate(date: string): boolean {\r\n const dateObj = new Date(date);\r\n return !isNaN(dateObj.getTime()) && dateObj.toISOString() === date;\r\n}\r\n\r\n/**\r\n * Get current timestamp in ISO 8601 format.\r\n *\r\n * @returns Current timestamp string\r\n */\r\nexport function getCurrentTimestamp(): string {\r\n return new Date().toISOString();\r\n}\r\n\r\n// ==================== Filter Utilities ====================\r\n\r\n/**\r\n * Checks if an entity's importance is within the specified range.\r\n * Entities without importance are treated as not matching if any filter is set.\r\n *\r\n * @param importance - The entity's importance value (may be undefined)\r\n * @param minImportance - Minimum importance filter (inclusive)\r\n * @param maxImportance - Maximum importance filter (inclusive)\r\n * @returns true if importance is within range or no filters are set\r\n *\r\n * @example\r\n * ```typescript\r\n * // Check if entity passes importance filter\r\n * if (isWithinImportanceRange(entity.importance, 5, 10)) {\r\n * // Entity has importance between 5 and 10\r\n * }\r\n * ```\r\n */\r\nexport function isWithinImportanceRange(\r\n importance: number | undefined,\r\n minImportance?: number,\r\n maxImportance?: number\r\n): boolean {\r\n // If no filters set, always pass\r\n if (minImportance === undefined && maxImportance === undefined) {\r\n return true;\r\n }\r\n\r\n // Check minimum importance\r\n if (minImportance !== undefined) {\r\n if (importance === undefined || importance < minImportance) {\r\n return false;\r\n }\r\n }\r\n\r\n // Check maximum importance\r\n if (maxImportance !== undefined) {\r\n if (importance === undefined || importance > maxImportance) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\n\r\n/**\r\n * Filters entities by importance range.\r\n * Returns all entities if no importance filters are specified.\r\n *\r\n * @param entities - Array of entities to filter\r\n * @param minImportance - Minimum importance filter (inclusive)\r\n * @param maxImportance - Maximum importance filter (inclusive)\r\n * @returns Filtered entities within the importance range\r\n */\r\nexport function filterByImportance(\r\n entities: Entity[],\r\n minImportance?: number,\r\n maxImportance?: number\r\n): Entity[] {\r\n if (minImportance === undefined && maxImportance === undefined) {\r\n return entities;\r\n }\r\n return entities.filter(e =>\r\n isWithinImportanceRange(e.importance, minImportance, maxImportance)\r\n );\r\n}\r\n\r\n/**\r\n * Filters entities by creation date range.\r\n *\r\n * @param entities - Array of entities to filter\r\n * @param startDate - Start of date range (inclusive)\r\n * @param endDate - End of date range (inclusive)\r\n * @returns Filtered entities created within the date range\r\n */\r\nexport function filterByCreatedDate(\r\n entities: Entity[],\r\n startDate?: string,\r\n endDate?: string\r\n): Entity[] {\r\n if (!startDate && !endDate) {\r\n return entities;\r\n }\r\n return entities.filter(e =>\r\n isWithinDateRange(e.createdAt, startDate, endDate)\r\n );\r\n}\r\n\r\n/**\r\n * Filters entities by last modified date range.\r\n *\r\n * @param entities - Array of entities to filter\r\n * @param startDate - Start of date range (inclusive)\r\n * @param endDate - End of date range (inclusive)\r\n * @returns Filtered entities modified within the date range\r\n */\r\nexport function filterByModifiedDate(\r\n entities: Entity[],\r\n startDate?: string,\r\n endDate?: string\r\n): Entity[] {\r\n if (!startDate && !endDate) {\r\n return entities;\r\n }\r\n return entities.filter(e =>\r\n isWithinDateRange(e.lastModified, startDate, endDate)\r\n );\r\n}\r\n\r\n/**\r\n * Filters entities by entity type.\r\n *\r\n * @param entities - Array of entities to filter\r\n * @param entityType - Entity type to filter by (case-sensitive)\r\n * @returns Filtered entities of the specified type\r\n */\r\nexport function filterByEntityType(\r\n entities: Entity[],\r\n entityType?: string\r\n): Entity[] {\r\n if (!entityType) {\r\n return entities;\r\n }\r\n return entities.filter(e => e.entityType === entityType);\r\n}\r\n\r\n/**\r\n * Common search filters that can be applied to entities.\r\n */\r\nexport interface CommonSearchFilters {\r\n tags?: string[];\r\n minImportance?: number;\r\n maxImportance?: number;\r\n entityType?: string;\r\n createdAfter?: string;\r\n createdBefore?: string;\r\n modifiedAfter?: string;\r\n modifiedBefore?: string;\r\n}\r\n\r\n/**\r\n * Checks if an entity passes all the specified filters.\r\n * Short-circuits on first failing filter for performance.\r\n *\r\n * Note: Tag filtering should be handled separately using hasMatchingTag\r\n * as it requires special normalization logic.\r\n *\r\n * @param entity - Entity to check\r\n * @param filters - Filters to apply\r\n * @returns true if entity passes all filters\r\n */\r\nexport function entityPassesFilters(\r\n entity: Entity,\r\n filters: Omit<CommonSearchFilters, 'tags'>\r\n): boolean {\r\n // Importance filter\r\n if (!isWithinImportanceRange(entity.importance, filters.minImportance, filters.maxImportance)) {\r\n return false;\r\n }\r\n\r\n // Entity type filter\r\n if (filters.entityType && entity.entityType !== filters.entityType) {\r\n return false;\r\n }\r\n\r\n // Created date filter\r\n if (!isWithinDateRange(entity.createdAt, filters.createdAfter, filters.createdBefore)) {\r\n return false;\r\n }\r\n\r\n // Modified date filter\r\n if (!isWithinDateRange(entity.lastModified, filters.modifiedAfter, filters.modifiedBefore)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n}\r\n\r\n// ==================== Security Utilities ====================\r\n\r\n/**\r\n * Dangerous keys that should never be allowed in object assignment.\r\n * These can be used for prototype pollution attacks.\r\n */\r\nconst DANGEROUS_KEYS = new Set([\r\n '__proto__',\r\n 'constructor',\r\n 'prototype',\r\n]);\r\n\r\n/**\r\n * Sanitizes an object by removing potentially dangerous keys.\r\n * This prevents prototype pollution attacks when using Object.assign() or spread operators.\r\n *\r\n * @param obj - The object to sanitize\r\n * @returns A new object with dangerous keys removed\r\n *\r\n * @example\r\n * ```typescript\r\n * // Safe usage with Object.assign\r\n * const updates = sanitizeObject(userInput);\r\n * Object.assign(entity, updates);\r\n *\r\n * // Protects against prototype pollution\r\n * const malicious = { __proto__: { admin: true } };\r\n * const safe = sanitizeObject(malicious); // { }\r\n * ```\r\n */\r\nexport function sanitizeObject<T extends Record<string, unknown>>(obj: T): Partial<T> {\r\n if (obj === null || typeof obj !== 'object') {\r\n return obj;\r\n }\r\n\r\n const result: Partial<T> = {};\r\n\r\n for (const key of Object.keys(obj)) {\r\n // Skip dangerous keys\r\n if (DANGEROUS_KEYS.has(key)) {\r\n continue;\r\n }\r\n\r\n // Recursively sanitize nested objects\r\n const value = obj[key];\r\n if (value !== null && typeof value === 'object' && !Array.isArray(value)) {\r\n result[key as keyof T] = sanitizeObject(value as Record<string, unknown>) as T[keyof T];\r\n } else {\r\n result[key as keyof T] = value as T[keyof T];\r\n }\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * CSV formula injection dangerous characters.\r\n * These can cause spreadsheet applications to execute formulas.\r\n */\r\nconst CSV_FORMULA_CHARS = new Set(['=', '+', '-', '@', '\\t', '\\r']);\r\n\r\n/**\r\n * Escapes a CSV field to prevent formula injection attacks.\r\n * Prepends a single quote to values that start with dangerous characters.\r\n *\r\n * @param field - The field value to escape\r\n * @returns Escaped field value safe for CSV export\r\n *\r\n * @example\r\n * ```typescript\r\n * escapeCsvFormula('=SUM(A1:A10)'); // \"'=SUM(A1:A10)\"\r\n * escapeCsvFormula('normal text'); // 'normal text'\r\n * ```\r\n */\r\nexport function escapeCsvFormula(field: string | undefined | null): string {\r\n if (field === undefined || field === null) return '';\r\n const str = String(field);\r\n\r\n // Prefix with single quote if starts with dangerous character\r\n if (str.length > 0 && CSV_FORMULA_CHARS.has(str[0])) {\r\n return \"'\" + str;\r\n }\r\n return str;\r\n}\r\n\r\n// ==================== Path Utilities ====================\r\n\r\n/**\r\n * Validate and normalize a file path to prevent path traversal attacks.\r\n *\r\n * This function:\r\n * - Normalizes the path to canonical form\r\n * - Converts relative paths to absolute paths\r\n * - Detects and prevents path traversal attempts (..)\r\n *\r\n * @param filePath - The file path to validate\r\n * @param baseDir - Optional base directory for relative paths (defaults to process.cwd())\r\n * @returns Validated absolute file path\r\n * @throws {FileOperationError} If path traversal is detected or path is invalid\r\n *\r\n * @example\r\n * ```typescript\r\n * // Valid paths\r\n * validateFilePath('/var/data/memory.jsonl'); // Returns absolute path\r\n * validateFilePath('data/memory.jsonl'); // Returns absolute path from cwd\r\n *\r\n * // Invalid paths (throws FileOperationError)\r\n * validateFilePath('../../../etc/passwd'); // Path traversal detected\r\n * validateFilePath('/var/data/../../../etc/passwd'); // Path traversal detected\r\n * ```\r\n */\r\nexport function validateFilePath(filePath: string, baseDir: string = process.cwd()): string {\r\n // Normalize path to remove redundant separators and resolve . and ..\r\n const normalized = path.normalize(filePath);\r\n\r\n // Convert to absolute path\r\n const absolute = path.isAbsolute(normalized)\r\n ? normalized\r\n : path.join(baseDir, normalized);\r\n\r\n // After normalization, check if path still contains .. which would indicate\r\n // traversal beyond the base directory\r\n const finalNormalized = path.normalize(absolute);\r\n\r\n // Split path into segments and check for suspicious patterns\r\n const segments = finalNormalized.split(path.sep);\r\n if (segments.includes('..')) {\r\n throw new FileOperationError(\r\n `Path traversal detected in file path: ${filePath}`,\r\n filePath\r\n );\r\n }\r\n\r\n return finalNormalized;\r\n}\r\n\r\n/**\r\n * Default memory file path (in project root directory, outside dist/).\r\n */\r\nexport const defaultMemoryPath = path.join(\r\n path.dirname(fileURLToPath(import.meta.url)),\r\n '../../memory.jsonl'\r\n);\r\n\r\n/**\r\n * Ensure memory file path with backward compatibility migration.\r\n *\r\n * Handles:\r\n * 1. Custom MEMORY_FILE_PATH environment variable (with path traversal protection)\r\n * 2. Backward compatibility: migrates memory.json to memory.jsonl\r\n * 3. Absolute vs relative path resolution\r\n *\r\n * @returns Resolved and validated memory file path\r\n * @throws {FileOperationError} If path traversal is detected in MEMORY_FILE_PATH\r\n *\r\n * @example\r\n * ```typescript\r\n * // Use environment variable\r\n * process.env.MEMORY_FILE_PATH = '/data/memory.jsonl';\r\n * const path = await ensureMemoryFilePath(); // '/data/memory.jsonl'\r\n *\r\n * // Use default path\r\n * delete process.env.MEMORY_FILE_PATH;\r\n * const path = await ensureMemoryFilePath(); // './memory.jsonl'\r\n *\r\n * // Invalid path (throws error)\r\n * process.env.MEMORY_FILE_PATH = '../../../etc/passwd';\r\n * await ensureMemoryFilePath(); // Throws FileOperationError\r\n * ```\r\n */\r\nexport async function ensureMemoryFilePath(): Promise<string> {\r\n if (process.env.MEMORY_FILE_PATH) {\r\n // Custom path provided, validate and resolve to absolute\r\n const baseDir = path.dirname(fileURLToPath(import.meta.url)) + '/../';\r\n const validatedPath = validateFilePath(process.env.MEMORY_FILE_PATH, baseDir);\r\n return validatedPath;\r\n }\r\n\r\n // No custom path set, check for backward compatibility migration\r\n const oldMemoryPath = path.join(\r\n path.dirname(fileURLToPath(import.meta.url)),\r\n '../../memory.json'\r\n );\r\n const newMemoryPath = defaultMemoryPath;\r\n\r\n try {\r\n // Check if old file exists\r\n await fs.access(oldMemoryPath);\r\n\r\n try {\r\n // Check if new file exists\r\n await fs.access(newMemoryPath);\r\n // Both files exist, use new one (no migration needed)\r\n return newMemoryPath;\r\n } catch {\r\n // Old file exists, new file doesn't - migrate\r\n console.log('[INFO] Found legacy memory.json file, migrating to memory.jsonl for JSONL format compatibility');\r\n await fs.rename(oldMemoryPath, newMemoryPath);\r\n console.log('[INFO] Successfully migrated memory.json to memory.jsonl');\r\n return newMemoryPath;\r\n }\r\n } catch {\r\n // Old file doesn't exist, use new path\r\n return newMemoryPath;\r\n }\r\n}\r\n","/**\r\n * Parallel Utilities\r\n *\r\n * Utilities for parallel array operations using workerpool.\r\n * Phase 8 Sprint 3: Parallel array operations for improved performance.\r\n *\r\n * **SECURITY WARNING:** These functions use `new Function()` internally for worker serialization.\r\n * The `fn` parameter MUST be a real function object, never a user-provided string.\r\n * Runtime validation ensures only function objects are accepted.\r\n *\r\n * @module utils/parallelUtils\r\n */\r\n\r\nimport workerpool from '@danielsimonjr/workerpool';\r\n\r\n/**\r\n * Validates that the input is a real function object.\r\n * Prevents code injection through string masquerading as functions.\r\n *\r\n * @param fn - Function to validate\r\n * @param paramName - Parameter name for error message\r\n * @throws {TypeError} If fn is not a function\r\n * @internal\r\n */\r\nfunction validateFunction(fn: unknown, paramName: string): void {\r\n if (typeof fn !== 'function') {\r\n throw new TypeError(`${paramName} must be a function, got ${typeof fn}`);\r\n }\r\n}\r\n\r\n/**\r\n * Default chunk size for parallel operations.\r\n * Can be overridden per operation.\r\n */\r\nconst DEFAULT_CHUNK_SIZE = 100;\r\n\r\n/**\r\n * Minimum array size to activate parallel processing.\r\n * For smaller arrays, single-threaded is more efficient due to worker overhead.\r\n */\r\nconst MIN_PARALLEL_SIZE = 200;\r\n\r\n/**\r\n * Shared worker pool instance for all parallel utilities.\r\n * Initialized lazily on first use.\r\n */\r\nlet sharedPool: workerpool.Pool | null = null;\r\n\r\n/**\r\n * Get or create the shared worker pool.\r\n * Uses inline worker execution (no separate worker file needed).\r\n *\r\n * @returns Worker pool instance\r\n */\r\nfunction getPool(): workerpool.Pool {\r\n if (!sharedPool) {\r\n sharedPool = workerpool.pool({\r\n maxWorkers: Math.max(1, workerpool.cpus - 1),\r\n workerType: 'thread',\r\n });\r\n }\r\n return sharedPool;\r\n}\r\n\r\n/**\r\n * Shutdown the shared worker pool and clean up resources.\r\n * Should be called when parallel utilities are no longer needed.\r\n */\r\nexport async function shutdownParallelUtils(): Promise<void> {\r\n if (sharedPool) {\r\n await sharedPool.terminate();\r\n sharedPool = null;\r\n }\r\n}\r\n\r\n/**\r\n * Map items in parallel using workerpool.\r\n *\r\n * Splits the array into chunks and processes each chunk in a worker thread.\r\n * Falls back to single-threaded for small arrays (< MIN_PARALLEL_SIZE).\r\n *\r\n * **Note:** The mapping function must be serializable (no closures, external variables).\r\n * Due to ESM/worker thread compatibility issues, this may fall back to single-threaded\r\n * execution in some environments (e.g., vitest test runner).\r\n *\r\n * @template T - Input item type\r\n * @template R - Output item type\r\n * @param items - Array of items to map\r\n * @param fn - Mapping function (must be serializable)\r\n * @param chunkSize - Optional chunk size (default: DEFAULT_CHUNK_SIZE)\r\n * @returns Promise resolving to array of mapped results\r\n *\r\n * @example\r\n * ```typescript\r\n * // Map numbers to their squares\r\n * const numbers = [1, 2, 3, 4, 5];\r\n * const squared = await parallelMap(numbers, (n: number) => n * n);\r\n * // Result: [1, 4, 9, 16, 25]\r\n * ```\r\n */\r\nexport async function parallelMap<T, R>(\r\n items: T[],\r\n fn: (item: T) => R,\r\n chunkSize: number = DEFAULT_CHUNK_SIZE\r\n): Promise<R[]> {\r\n // Security: Validate that fn is a real function, not a user-provided string\r\n validateFunction(fn, 'fn');\r\n\r\n // Fall back to single-threaded for small arrays\r\n if (items.length < MIN_PARALLEL_SIZE) {\r\n return items.map(fn);\r\n }\r\n\r\n try {\r\n const pool = getPool();\r\n\r\n // Split items into chunks\r\n const chunks: T[][] = [];\r\n for (let i = 0; i < items.length; i += chunkSize) {\r\n chunks.push(items.slice(i, i + chunkSize));\r\n }\r\n\r\n // Convert function to string for serialization\r\n const fnString = fn.toString();\r\n\r\n // Process chunks in parallel using inline function execution\r\n const results = await Promise.all(\r\n chunks.map(chunk =>\r\n pool.exec(\r\n (chunkData: T[], fnStr: string) => {\r\n // Reconstruct function from string\r\n // eslint-disable-next-line no-new-func\r\n const mapFn = new Function('return ' + fnStr)() as (item: T) => R;\r\n return chunkData.map(mapFn);\r\n },\r\n [chunk, fnString]\r\n ) as Promise<R[]>\r\n )\r\n );\r\n\r\n // Flatten results\r\n return results.flat();\r\n } catch (error) {\r\n // Fall back to single-threaded if worker execution fails\r\n // (e.g., in test environments with ESM/worker compatibility issues)\r\n return items.map(fn);\r\n }\r\n}\r\n\r\n/**\r\n * Filter items in parallel using workerpool.\r\n *\r\n * Splits the array into chunks and processes each chunk in a worker thread.\r\n * Falls back to single-threaded for small arrays (< MIN_PARALLEL_SIZE).\r\n *\r\n * **Note:** The predicate function must be serializable (no closures, external variables).\r\n * Due to ESM/worker thread compatibility issues, this may fall back to single-threaded\r\n * execution in some environments (e.g., vitest test runner).\r\n *\r\n * @template T - Item type\r\n * @param items - Array of items to filter\r\n * @param predicate - Filter predicate (must be serializable)\r\n * @param chunkSize - Optional chunk size (default: DEFAULT_CHUNK_SIZE)\r\n * @returns Promise resolving to filtered array\r\n *\r\n * @example\r\n * ```typescript\r\n * // Filter even numbers\r\n * const numbers = [1, 2, 3, 4, 5, 6];\r\n * const evens = await parallelFilter(numbers, (n: number) => n % 2 === 0);\r\n * // Result: [2, 4, 6]\r\n * ```\r\n */\r\nexport async function parallelFilter<T>(\r\n items: T[],\r\n predicate: (item: T) => boolean,\r\n chunkSize: number = DEFAULT_CHUNK_SIZE\r\n): Promise<T[]> {\r\n // Security: Validate that predicate is a real function, not a user-provided string\r\n validateFunction(predicate, 'predicate');\r\n\r\n // Fall back to single-threaded for small arrays\r\n if (items.length < MIN_PARALLEL_SIZE) {\r\n return items.filter(predicate);\r\n }\r\n\r\n try {\r\n const pool = getPool();\r\n\r\n // Split items into chunks\r\n const chunks: T[][] = [];\r\n for (let i = 0; i < items.length; i += chunkSize) {\r\n chunks.push(items.slice(i, i + chunkSize));\r\n }\r\n\r\n // Convert function to string for serialization\r\n const predicateString = predicate.toString();\r\n\r\n // Process chunks in parallel using inline function execution\r\n const results = await Promise.all(\r\n chunks.map(chunk =>\r\n pool.exec(\r\n (chunkData: T[], predicateStr: string) => {\r\n // Reconstruct function from string\r\n // eslint-disable-next-line no-new-func\r\n const filterFn = new Function('return ' + predicateStr)() as (item: T) => boolean;\r\n return chunkData.filter(filterFn);\r\n },\r\n [chunk, predicateString]\r\n ) as Promise<T[]>\r\n )\r\n );\r\n\r\n // Flatten results\r\n return results.flat();\r\n } catch (error) {\r\n // Fall back to single-threaded if worker execution fails\r\n // (e.g., in test environments with ESM/worker compatibility issues)\r\n return items.filter(predicate);\r\n }\r\n}\r\n\r\n/**\r\n * Get statistics about the worker pool.\r\n *\r\n * @returns Pool statistics or null if pool is not initialized\r\n */\r\nexport function getPoolStats(): workerpool.PoolStats | null {\r\n if (!sharedPool) {\r\n return null;\r\n }\r\n return sharedPool.stats();\r\n}\r\n","/**\r\n * Task Scheduler\r\n *\r\n * Advanced task scheduling utilities using workerpool.\r\n * Phase 8 Sprint 4: Priority queues, concurrency control, progress tracking.\r\n *\r\n * **SECURITY WARNING:** TaskQueue uses `new Function()` internally for worker serialization.\r\n * Task functions MUST be real function objects, never user-provided strings.\r\n * Runtime validation ensures only function objects are accepted.\r\n *\r\n * @module utils/taskScheduler\r\n */\r\n\r\nimport workerpool from '@danielsimonjr/workerpool';\r\n\r\n/**\r\n * Validates that the input is a real function object.\r\n * Prevents code injection through string masquerading as functions.\r\n *\r\n * @param fn - Function to validate\r\n * @param paramName - Parameter name for error message\r\n * @throws {TypeError} If fn is not a function\r\n * @internal\r\n */\r\nfunction validateFunction(fn: unknown, paramName: string): void {\r\n if (typeof fn !== 'function') {\r\n throw new TypeError(`${paramName} must be a function, got ${typeof fn}`);\r\n }\r\n}\r\n\r\n// ==================== Types ====================\r\n\r\n/**\r\n * Task priority levels.\r\n * Higher priority tasks are executed first.\r\n */\r\nexport enum TaskPriority {\r\n LOW = 0,\r\n NORMAL = 1,\r\n HIGH = 2,\r\n CRITICAL = 3,\r\n}\r\n\r\n/**\r\n * Task status in the queue.\r\n */\r\nexport enum TaskStatus {\r\n PENDING = 'pending',\r\n RUNNING = 'running',\r\n COMPLETED = 'completed',\r\n FAILED = 'failed',\r\n CANCELLED = 'cancelled',\r\n}\r\n\r\n/**\r\n * Task definition for the queue.\r\n */\r\nexport interface Task<T = unknown, R = unknown> {\r\n /** Unique task identifier */\r\n id: string;\r\n /** Task priority */\r\n priority: TaskPriority;\r\n /** Function to execute (must be serializable) */\r\n fn: (input: T) => R;\r\n /** Input data for the function */\r\n input: T;\r\n /** Optional timeout in milliseconds */\r\n timeout?: number;\r\n /** Optional task metadata */\r\n metadata?: Record<string, unknown>;\r\n}\r\n\r\n/**\r\n * Task result after execution.\r\n */\r\nexport interface TaskResult<R = unknown> {\r\n /** Task identifier */\r\n id: string;\r\n /** Task status */\r\n status: TaskStatus;\r\n /** Result if successful */\r\n result?: R;\r\n /** Error if failed */\r\n error?: Error;\r\n /** Execution duration in milliseconds */\r\n duration: number;\r\n /** Start timestamp */\r\n startedAt: number;\r\n /** End timestamp */\r\n completedAt: number;\r\n}\r\n\r\n/**\r\n * Progress callback for batch operations.\r\n */\r\nexport type ProgressCallback = (progress: {\r\n completed: number;\r\n total: number;\r\n percentage: number;\r\n currentTaskId?: string;\r\n}) => void;\r\n\r\n/**\r\n * Batch processing options.\r\n */\r\nexport interface TaskBatchOptions {\r\n /** Maximum concurrent tasks (default: CPU count - 1) */\r\n concurrency?: number;\r\n /** Task timeout in milliseconds (default: 30000) */\r\n timeout?: number;\r\n /** Progress callback */\r\n onProgress?: ProgressCallback;\r\n /** Whether to stop on first error (default: false) */\r\n stopOnError?: boolean;\r\n}\r\n\r\n/**\r\n * Task queue statistics.\r\n */\r\nexport interface QueueStats {\r\n /** Number of pending tasks */\r\n pending: number;\r\n /** Number of running tasks */\r\n running: number;\r\n /** Number of completed tasks */\r\n completed: number;\r\n /** Number of failed tasks */\r\n failed: number;\r\n /** Average execution time in milliseconds */\r\n averageExecutionTime: number;\r\n /** Total tasks processed */\r\n totalProcessed: number;\r\n}\r\n\r\n// ==================== Task Queue Implementation ====================\r\n\r\n/**\r\n * Internal task wrapper with tracking information.\r\n */\r\ninterface QueuedTask<T = unknown, R = unknown> extends Task<T, R> {\r\n status: TaskStatus;\r\n addedAt: number;\r\n resolve: (result: TaskResult<R>) => void;\r\n reject: (error: Error) => void;\r\n}\r\n\r\n/**\r\n * Priority Task Queue with advanced scheduling.\r\n *\r\n * Features:\r\n * - Priority-based execution (CRITICAL > HIGH > NORMAL > LOW)\r\n * - Configurable concurrency limits\r\n * - Progress tracking\r\n * - Graceful error handling\r\n * - Task cancellation\r\n *\r\n * @example\r\n * ```typescript\r\n * const queue = new TaskQueue({ concurrency: 4 });\r\n *\r\n * // Add tasks with different priorities\r\n * queue.enqueue({\r\n * id: 'task1',\r\n * priority: TaskPriority.HIGH,\r\n * fn: (x: number) => x * 2,\r\n * input: 5,\r\n * });\r\n *\r\n * // Process all tasks\r\n * const results = await queue.processAll();\r\n * ```\r\n */\r\nexport class TaskQueue {\r\n private queue: QueuedTask[] = [];\r\n private running: Map<string, QueuedTask> = new Map();\r\n private completed: TaskResult[] = [];\r\n private pool: workerpool.Pool | null = null;\r\n private concurrency: number;\r\n private defaultTimeout: number;\r\n private isProcessing = false;\r\n private totalExecutionTime = 0;\r\n private totalProcessed = 0;\r\n private useWorkerPool: boolean;\r\n\r\n constructor(options: { concurrency?: number; timeout?: number; useWorkerPool?: boolean } = {}) {\r\n this.concurrency = options.concurrency ?? Math.max(1, workerpool.cpus - 1);\r\n this.defaultTimeout = options.timeout ?? 30000;\r\n this.useWorkerPool = options.useWorkerPool ?? true;\r\n }\r\n\r\n /**\r\n * Get or create the worker pool.\r\n */\r\n private getPool(): workerpool.Pool {\r\n if (!this.pool) {\r\n this.pool = workerpool.pool({\r\n maxWorkers: this.concurrency,\r\n workerType: 'thread',\r\n });\r\n }\r\n return this.pool;\r\n }\r\n\r\n /**\r\n * Add a task to the queue.\r\n *\r\n * @param task - Task to add\r\n * @returns Promise that resolves when the task completes\r\n */\r\n enqueue<T, R>(task: Task<T, R>): Promise<TaskResult<R>> {\r\n // Security: Validate that task.fn is a real function, not a user-provided string\r\n validateFunction(task.fn, 'task.fn');\r\n\r\n return new Promise((resolve, reject) => {\r\n const queuedTask: QueuedTask<T, R> = {\r\n ...task,\r\n status: TaskStatus.PENDING,\r\n addedAt: Date.now(),\r\n resolve: resolve as (result: TaskResult<unknown>) => void,\r\n reject,\r\n };\r\n\r\n // Insert based on priority (higher priority first)\r\n const insertIndex = this.queue.findIndex(t => t.priority < task.priority);\r\n if (insertIndex === -1) {\r\n this.queue.push(queuedTask as QueuedTask);\r\n } else {\r\n this.queue.splice(insertIndex, 0, queuedTask as QueuedTask);\r\n }\r\n\r\n // Start processing if not already running\r\n if (!this.isProcessing) {\r\n this.processNext();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Process the next task in the queue.\r\n */\r\n private async processNext(): Promise<void> {\r\n if (this.running.size >= this.concurrency || this.queue.length === 0) {\r\n if (this.running.size === 0 && this.queue.length === 0) {\r\n this.isProcessing = false;\r\n }\r\n return;\r\n }\r\n\r\n this.isProcessing = true;\r\n const task = this.queue.shift();\r\n if (!task) return;\r\n\r\n task.status = TaskStatus.RUNNING;\r\n this.running.set(task.id, task);\r\n\r\n const startTime = Date.now();\r\n\r\n try {\r\n // Execute task - try worker pool first, fall back to direct execution\r\n let result: unknown;\r\n\r\n if (this.useWorkerPool) {\r\n try {\r\n const pool = this.getPool();\r\n const fnString = task.fn.toString();\r\n const timeout = task.timeout ?? this.defaultTimeout;\r\n\r\n result = await pool\r\n .exec(\r\n (input: unknown, fnStr: string) => {\r\n // eslint-disable-next-line no-new-func\r\n const fn = new Function('return ' + fnStr)();\r\n return fn(input);\r\n },\r\n [task.input, fnString]\r\n )\r\n .timeout(timeout);\r\n } catch {\r\n // Fall back to direct execution\r\n result = await Promise.resolve(task.fn(task.input));\r\n }\r\n } else {\r\n // Direct execution without worker pool\r\n result = await Promise.resolve(task.fn(task.input));\r\n }\r\n\r\n const endTime = Date.now();\r\n const duration = endTime - startTime;\r\n\r\n const taskResult: TaskResult = {\r\n id: task.id,\r\n status: TaskStatus.COMPLETED,\r\n result,\r\n duration,\r\n startedAt: startTime,\r\n completedAt: endTime,\r\n };\r\n\r\n this.totalExecutionTime += duration;\r\n this.totalProcessed++;\r\n this.completed.push(taskResult);\r\n this.running.delete(task.id);\r\n task.resolve(taskResult);\r\n } catch (error) {\r\n const endTime = Date.now();\r\n const duration = endTime - startTime;\r\n\r\n const taskResult: TaskResult = {\r\n id: task.id,\r\n status: TaskStatus.FAILED,\r\n error: error instanceof Error ? error : new Error(String(error)),\r\n duration,\r\n startedAt: startTime,\r\n completedAt: endTime,\r\n };\r\n\r\n this.totalProcessed++;\r\n this.completed.push(taskResult);\r\n this.running.delete(task.id);\r\n task.resolve(taskResult);\r\n }\r\n\r\n // Process next task\r\n this.processNext();\r\n }\r\n\r\n /**\r\n * Cancel a pending task.\r\n *\r\n * @param taskId - ID of the task to cancel\r\n * @returns True if task was cancelled, false if not found or already running\r\n */\r\n cancel(taskId: string): boolean {\r\n const index = this.queue.findIndex(t => t.id === taskId);\r\n if (index === -1) return false;\r\n\r\n const task = this.queue.splice(index, 1)[0];\r\n task.status = TaskStatus.CANCELLED;\r\n\r\n const result: TaskResult = {\r\n id: task.id,\r\n status: TaskStatus.CANCELLED,\r\n duration: 0,\r\n startedAt: Date.now(),\r\n completedAt: Date.now(),\r\n };\r\n\r\n task.resolve(result);\r\n return true;\r\n }\r\n\r\n /**\r\n * Wait for all tasks to complete.\r\n *\r\n * @returns Array of all task results\r\n */\r\n async drain(): Promise<TaskResult[]> {\r\n // Wait for queue to empty and all running tasks to complete\r\n while (this.queue.length > 0 || this.running.size > 0) {\r\n await new Promise(resolve => setTimeout(resolve, 10));\r\n }\r\n return [...this.completed];\r\n }\r\n\r\n /**\r\n * Get queue statistics.\r\n */\r\n getStats(): QueueStats {\r\n return {\r\n pending: this.queue.length,\r\n running: this.running.size,\r\n completed: this.completed.filter(r => r.status === TaskStatus.COMPLETED).length,\r\n failed: this.completed.filter(r => r.status === TaskStatus.FAILED).length,\r\n averageExecutionTime:\r\n this.totalProcessed > 0 ? this.totalExecutionTime / this.totalProcessed : 0,\r\n totalProcessed: this.totalProcessed,\r\n };\r\n }\r\n\r\n /**\r\n * Clear all completed results.\r\n */\r\n clearCompleted(): void {\r\n this.completed = [];\r\n }\r\n\r\n /**\r\n * Shutdown the task queue and release resources.\r\n */\r\n async shutdown(): Promise<void> {\r\n // Cancel all pending tasks\r\n for (const task of this.queue) {\r\n task.status = TaskStatus.CANCELLED;\r\n task.resolve({\r\n id: task.id,\r\n status: TaskStatus.CANCELLED,\r\n duration: 0,\r\n startedAt: Date.now(),\r\n completedAt: Date.now(),\r\n });\r\n }\r\n this.queue = [];\r\n\r\n // Terminate worker pool\r\n if (this.pool) {\r\n await this.pool.terminate();\r\n this.pool = null;\r\n }\r\n\r\n this.isProcessing = false;\r\n }\r\n}\r\n\r\n// ==================== Batch Processing Utilities ====================\r\n\r\n/**\r\n * Process items in parallel batches with progress tracking.\r\n *\r\n * Unlike parallelMap, this provides:\r\n * - Progress callbacks\r\n * - Configurable concurrency\r\n * - Error handling options\r\n * - Task-level timeouts\r\n *\r\n * @template T - Input item type\r\n * @template R - Output item type\r\n * @param items - Array of items to process\r\n * @param fn - Processing function (must be serializable)\r\n * @param options - Batch processing options\r\n * @returns Array of results (or errors if stopOnError is false)\r\n *\r\n * @example\r\n * ```typescript\r\n * const results = await batchProcess(\r\n * urls,\r\n * (url: string) => fetch(url).then(r => r.json()),\r\n * {\r\n * concurrency: 5,\r\n * timeout: 10000,\r\n * onProgress: ({ completed, total, percentage }) => {\r\n * console.log(`Progress: ${percentage.toFixed(1)}%`);\r\n * },\r\n * }\r\n * );\r\n * ```\r\n */\r\nexport async function batchProcess<T, R>(\r\n items: T[],\r\n fn: (item: T) => R | Promise<R>,\r\n options: TaskBatchOptions = {}\r\n): Promise<Array<{ success: true; result: R } | { success: false; error: Error }>> {\r\n const {\r\n concurrency = Math.max(1, workerpool.cpus - 1),\r\n timeout = 30000,\r\n onProgress,\r\n stopOnError = false,\r\n } = options;\r\n\r\n const results: Array<{ success: true; result: R } | { success: false; error: Error }> = [];\r\n let completed = 0;\r\n const total = items.length;\r\n\r\n // Process in batches respecting concurrency\r\n for (let i = 0; i < items.length; i += concurrency) {\r\n const batch = items.slice(i, i + concurrency);\r\n\r\n const batchPromises = batch.map(async (item, batchIndex) => {\r\n const itemIndex = i + batchIndex;\r\n\r\n try {\r\n // Execute with timeout\r\n const result = await Promise.race([\r\n Promise.resolve(fn(item)),\r\n new Promise<never>((_, reject) =>\r\n setTimeout(() => reject(new Error('Task timeout')), timeout)\r\n ),\r\n ]);\r\n\r\n results[itemIndex] = { success: true, result };\r\n } catch (error) {\r\n const err = error instanceof Error ? error : new Error(String(error));\r\n results[itemIndex] = { success: false, error: err };\r\n\r\n if (stopOnError) {\r\n throw err;\r\n }\r\n } finally {\r\n completed++;\r\n if (onProgress) {\r\n onProgress({\r\n completed,\r\n total,\r\n percentage: (completed / total) * 100,\r\n currentTaskId: `item-${itemIndex}`,\r\n });\r\n }\r\n }\r\n });\r\n\r\n await Promise.all(batchPromises);\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Execute tasks with rate limiting.\r\n *\r\n * Ensures tasks don't exceed a specified rate (tasks per second).\r\n *\r\n * @template T - Input item type\r\n * @template R - Output item type\r\n * @param items - Items to process\r\n * @param fn - Processing function\r\n * @param rateLimit - Maximum tasks per second\r\n * @returns Array of results\r\n *\r\n * @example\r\n * ```typescript\r\n * // Process max 10 items per second\r\n * const results = await rateLimitedProcess(\r\n * items,\r\n * (item) => processItem(item),\r\n * 10\r\n * );\r\n * ```\r\n */\r\nexport async function rateLimitedProcess<T, R>(\r\n items: T[],\r\n fn: (item: T) => R | Promise<R>,\r\n rateLimit: number\r\n): Promise<R[]> {\r\n const results: R[] = [];\r\n const minInterval = 1000 / rateLimit;\r\n let lastExecutionTime = 0;\r\n\r\n for (const item of items) {\r\n // Calculate wait time\r\n const now = Date.now();\r\n const timeSinceLast = now - lastExecutionTime;\r\n if (timeSinceLast < minInterval) {\r\n await new Promise(resolve => setTimeout(resolve, minInterval - timeSinceLast));\r\n }\r\n\r\n lastExecutionTime = Date.now();\r\n const result = await fn(item);\r\n results.push(result);\r\n }\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Retry a function with exponential backoff.\r\n *\r\n * @template T - Return type\r\n * @param fn - Function to retry\r\n * @param options - Retry options\r\n * @returns Result of the function\r\n *\r\n * @example\r\n * ```typescript\r\n * const result = await withRetry(\r\n * () => fetchData(),\r\n * { maxRetries: 3, baseDelay: 1000 }\r\n * );\r\n * ```\r\n */\r\nexport async function withRetry<T>(\r\n fn: () => T | Promise<T>,\r\n options: {\r\n maxRetries?: number;\r\n baseDelay?: number;\r\n maxDelay?: number;\r\n onRetry?: (error: Error, attempt: number) => void;\r\n } = {}\r\n): Promise<T> {\r\n const { maxRetries = 3, baseDelay = 1000, maxDelay = 30000, onRetry } = options;\r\n\r\n let lastError: Error | undefined;\r\n\r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n return await fn();\r\n } catch (error) {\r\n lastError = error instanceof Error ? error : new Error(String(error));\r\n\r\n if (attempt < maxRetries) {\r\n const delay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay);\r\n if (onRetry) {\r\n onRetry(lastError, attempt + 1);\r\n }\r\n await new Promise(resolve => setTimeout(resolve, delay));\r\n }\r\n }\r\n }\r\n\r\n throw lastError;\r\n}\r\n\r\n/**\r\n * Create a debounced version of a function.\r\n *\r\n * @template T - Function arguments type\r\n * @template R - Return type\r\n * @param fn - Function to debounce\r\n * @param delay - Delay in milliseconds\r\n * @returns Debounced function\r\n */\r\nexport function debounce<T extends unknown[], R>(\r\n fn: (...args: T) => R,\r\n delay: number\r\n): (...args: T) => Promise<R> {\r\n let timeoutId: NodeJS.Timeout | null = null;\r\n let pendingResolve: ((value: R) => void) | null = null;\r\n\r\n return (...args: T): Promise<R> => {\r\n return new Promise(resolve => {\r\n if (timeoutId) {\r\n clearTimeout(timeoutId);\r\n }\r\n pendingResolve = resolve;\r\n\r\n timeoutId = setTimeout(() => {\r\n const result = fn(...args);\r\n if (pendingResolve) {\r\n pendingResolve(result);\r\n }\r\n timeoutId = null;\r\n pendingResolve = null;\r\n }, delay);\r\n });\r\n };\r\n}\r\n\r\n/**\r\n * Create a throttled version of a function.\r\n *\r\n * @template T - Function arguments type\r\n * @template R - Return type\r\n * @param fn - Function to throttle\r\n * @param limit - Minimum time between calls in milliseconds\r\n * @returns Throttled function\r\n */\r\nexport function throttle<T extends unknown[], R>(\r\n fn: (...args: T) => R,\r\n limit: number\r\n): (...args: T) => R | undefined {\r\n let lastCall = 0;\r\n\r\n return (...args: T): R | undefined => {\r\n const now = Date.now();\r\n if (now - lastCall >= limit) {\r\n lastCall = now;\r\n return fn(...args);\r\n }\r\n return undefined;\r\n };\r\n}\r\n","/**\r\n * Operation Utilities\r\n *\r\n * Phase 9B: Utilities for long-running operations with progress tracking\r\n * and cancellation support.\r\n *\r\n * @module utils/operationUtils\r\n */\r\n\r\nimport { OperationCancelledError } from './errors.js';\r\nimport type { ProgressCallback } from './taskScheduler.js';\r\n\r\n/**\r\n * Check if an operation has been cancelled via AbortSignal.\r\n * Throws OperationCancelledError if the signal is aborted.\r\n *\r\n * @param signal - Optional AbortSignal to check\r\n * @param operation - Optional operation name for error message\r\n * @throws OperationCancelledError if signal is aborted\r\n *\r\n * @example\r\n * ```typescript\r\n * for (const item of items) {\r\n * checkCancellation(options?.signal, 'batch processing');\r\n * await processItem(item);\r\n * }\r\n * ```\r\n */\r\nexport function checkCancellation(signal?: AbortSignal, operation?: string): void {\r\n if (signal?.aborted) {\r\n throw new OperationCancelledError(operation);\r\n }\r\n}\r\n\r\n/**\r\n * Create a throttled progress reporter to avoid excessive callback invocations.\r\n * Returns undefined if no callback is provided.\r\n *\r\n * @param callback - Optional progress callback to throttle\r\n * @param throttleMs - Minimum time between callbacks (default: 100ms)\r\n * @returns Throttled callback or undefined\r\n *\r\n * @example\r\n * ```typescript\r\n * const reportProgress = createProgressReporter(options?.onProgress, 50);\r\n * for (let i = 0; i < total; i++) {\r\n * reportProgress?.({ completed: i, total, percentage: (i / total) * 100 });\r\n * }\r\n * ```\r\n */\r\nexport function createProgressReporter(\r\n callback?: ProgressCallback,\r\n throttleMs: number = 100\r\n): ProgressCallback | undefined {\r\n if (!callback) return undefined;\r\n\r\n let lastCallTime = 0;\r\n\r\n return (progress) => {\r\n const now = Date.now();\r\n // Always report 0% and 100%\r\n if (progress.percentage === 0 || progress.percentage >= 100 || now - lastCallTime >= throttleMs) {\r\n lastCallTime = now;\r\n callback(progress);\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Create a progress object for reporting.\r\n *\r\n * @param completed - Number of completed items\r\n * @param total - Total number of items\r\n * @param currentTaskId - Optional current task identifier\r\n * @returns Progress object suitable for ProgressCallback\r\n *\r\n * @example\r\n * ```typescript\r\n * reportProgress?.(createProgress(50, 100, 'processing entities'));\r\n * // { completed: 50, total: 100, percentage: 50, currentTaskId: 'processing entities' }\r\n * ```\r\n */\r\nexport function createProgress(\r\n completed: number,\r\n total: number,\r\n currentTaskId?: string\r\n): { completed: number; total: number; percentage: number; currentTaskId?: string } {\r\n return {\r\n completed,\r\n total,\r\n percentage: total > 0 ? Math.round((completed / total) * 100) : 0,\r\n currentTaskId,\r\n };\r\n}\r\n\r\n/**\r\n * Phase definition for executeWithPhases.\r\n */\r\nexport interface PhaseDefinition<T> {\r\n /** Phase name (used for progress reporting and cancellation error messages) */\r\n name: string;\r\n /** Weight of this phase relative to others (higher = more of total progress) */\r\n weight: number;\r\n /** Executor function that performs the phase work */\r\n execute: (phaseProgress: (pct: number) => void) => Promise<T>;\r\n}\r\n\r\n/**\r\n * Execute an operation with multiple distinct phases.\r\n * Useful when an operation has multiple distinct phases with different weights.\r\n *\r\n * @param phases - Array of phase definitions with weight and executor\r\n * @param onProgress - Optional progress callback\r\n * @param signal - Optional abort signal\r\n * @returns Array of results from each phase\r\n * @throws OperationCancelledError if cancelled during any phase\r\n *\r\n * @example\r\n * ```typescript\r\n * const [parseResult, processResult, saveResult] = await executeWithPhases([\r\n * { name: 'parsing', weight: 20, execute: () => parseData() },\r\n * { name: 'processing', weight: 60, execute: () => processEntities() },\r\n * { name: 'saving', weight: 20, execute: () => saveResults() },\r\n * ], options?.onProgress, options?.signal);\r\n * ```\r\n */\r\nexport async function executeWithPhases<T>(\r\n phases: PhaseDefinition<T>[],\r\n onProgress?: ProgressCallback,\r\n signal?: AbortSignal\r\n): Promise<T[]> {\r\n const totalWeight = phases.reduce((sum, p) => sum + p.weight, 0);\r\n let completedWeight = 0;\r\n const results: T[] = [];\r\n\r\n for (const phase of phases) {\r\n checkCancellation(signal, phase.name);\r\n\r\n const phaseStartWeight = completedWeight;\r\n const phaseProgress = (phasePct: number) => {\r\n if (onProgress) {\r\n const overallPct = ((phaseStartWeight + (phase.weight * phasePct / 100)) / totalWeight) * 100;\r\n onProgress({\r\n completed: Math.round(overallPct),\r\n total: 100,\r\n percentage: Math.round(overallPct),\r\n currentTaskId: phase.name,\r\n });\r\n }\r\n };\r\n\r\n const result = await phase.execute(phaseProgress);\r\n results.push(result);\r\n completedWeight += phase.weight;\r\n }\r\n\r\n // Report 100% completion\r\n onProgress?.({\r\n completed: 100,\r\n total: 100,\r\n percentage: 100,\r\n });\r\n\r\n return results;\r\n}\r\n\r\n/**\r\n * Execute an operation in batches with progress tracking and cancellation support.\r\n *\r\n * @param items - Array of items to process\r\n * @param batchSize - Size of each batch\r\n * @param processBatch - Function to process each batch\r\n * @param onProgress - Optional progress callback\r\n * @param signal - Optional abort signal\r\n * @param operationName - Optional operation name for cancellation error\r\n * @returns Array of results from all batches\r\n *\r\n * @example\r\n * ```typescript\r\n * const results = await processBatchesWithProgress(\r\n * entities,\r\n * 100,\r\n * async (batch) => {\r\n * for (const entity of batch) {\r\n * await saveEntity(entity);\r\n * }\r\n * return batch.length;\r\n * },\r\n * options?.onProgress,\r\n * options?.signal,\r\n * 'createEntities'\r\n * );\r\n * ```\r\n */\r\nexport async function processBatchesWithProgress<T, R>(\r\n items: T[],\r\n batchSize: number,\r\n processBatch: (batch: T[], batchIndex: number) => Promise<R>,\r\n onProgress?: ProgressCallback,\r\n signal?: AbortSignal,\r\n operationName?: string\r\n): Promise<R[]> {\r\n const results: R[] = [];\r\n const total = items.length;\r\n let processed = 0;\r\n\r\n const reportProgress = createProgressReporter(onProgress);\r\n reportProgress?.(createProgress(0, total, operationName));\r\n\r\n for (let i = 0; i < items.length; i += batchSize) {\r\n checkCancellation(signal, operationName);\r\n\r\n const batch = items.slice(i, i + batchSize);\r\n const result = await processBatch(batch, Math.floor(i / batchSize));\r\n results.push(result);\r\n\r\n processed += batch.length;\r\n reportProgress?.(createProgress(processed, total, operationName));\r\n }\r\n\r\n reportProgress?.(createProgress(total, total, operationName));\r\n return results;\r\n}\r\n","/**\n * Worker Pool Manager\n *\n * Phase 12 Sprint 2: Unified worker pool management for all parallelizable operations.\n * Provides centralized lifecycle management, configuration, and statistics.\n *\n * @module utils/WorkerPoolManager\n */\n\nimport workerpool from '@danielsimonjr/workerpool';\nimport type { Pool, PoolStats } from '@danielsimonjr/workerpool';\n\n/**\n * Configuration options for worker pools.\n */\nexport interface WorkerPoolConfig {\n /** Maximum number of worker threads (default: CPU count - 1) */\n maxWorkers?: number;\n /** Worker type: 'thread' or 'process' (default: 'thread') */\n workerType?: 'thread' | 'process';\n /** Optional path to worker script file */\n workerPath?: string;\n /** Minimum array size to use parallel processing (default: 200) */\n minParallelSize?: number;\n /** Default task timeout in milliseconds (default: 30000) */\n defaultTimeout?: number;\n}\n\n/**\n * Extended pool statistics with additional metadata.\n */\nexport interface ExtendedPoolStats extends PoolStats {\n /** Pool identifier */\n poolId: string;\n /** When the pool was created */\n createdAt: number;\n /** Total tasks executed since creation */\n totalTasksExecuted: number;\n /** Total execution time in milliseconds */\n totalExecutionTime: number;\n /** Average task execution time in milliseconds */\n averageExecutionTime: number;\n}\n\n/**\n * Callback for pool events.\n */\nexport type PoolEventCallback = (poolId: string, event: 'created' | 'shutdown' | 'error', data?: unknown) => void;\n\n/**\n * Internal pool entry with metadata.\n */\ninterface PoolEntry {\n pool: Pool;\n config: WorkerPoolConfig;\n createdAt: number;\n totalTasksExecuted: number;\n totalExecutionTime: number;\n}\n\n/**\n * Default configuration values.\n */\nconst DEFAULT_CONFIG: Required<WorkerPoolConfig> = {\n maxWorkers: Math.max(1, workerpool.cpus - 1),\n workerType: 'thread',\n workerPath: '',\n minParallelSize: 200,\n defaultTimeout: 30000,\n};\n\n/**\n * WorkerPoolManager - Unified worker pool management\n *\n * Provides centralized management of worker pools for parallel processing.\n * Features:\n * - Named pool registration with automatic lifecycle management\n * - Pool cleanup on process exit\n * - Statistics tracking per pool\n * - Event callbacks for monitoring\n *\n * @example\n * ```typescript\n * const manager = WorkerPoolManager.getInstance();\n *\n * // Get or create a pool\n * const pool = manager.getPool('fuzzySearch', {\n * maxWorkers: 4,\n * workerPath: '/path/to/worker.js'\n * });\n *\n * // Execute task\n * const result = await pool.exec('searchEntities', [data]);\n *\n * // Get statistics\n * const stats = manager.getPoolStats('fuzzySearch');\n *\n * // Shutdown all pools on exit\n * await manager.shutdownAll();\n * ```\n */\nexport class WorkerPoolManager {\n private static instance: WorkerPoolManager | null = null;\n\n private pools: Map<string, PoolEntry> = new Map();\n private eventCallbacks: PoolEventCallback[] = [];\n private isShuttingDown = false;\n private shutdownRegistered = false;\n\n /**\n * Private constructor for singleton pattern.\n */\n private constructor() {\n this.registerShutdownHandlers();\n }\n\n /**\n * Get the singleton instance of WorkerPoolManager.\n *\n * @returns The WorkerPoolManager instance\n */\n static getInstance(): WorkerPoolManager {\n if (!WorkerPoolManager.instance) {\n WorkerPoolManager.instance = new WorkerPoolManager();\n }\n return WorkerPoolManager.instance;\n }\n\n /**\n * Reset the singleton instance (primarily for testing).\n */\n static resetInstance(): void {\n if (WorkerPoolManager.instance) {\n WorkerPoolManager.instance.shutdownAll().catch(() => {\n // Ignore errors during reset\n });\n WorkerPoolManager.instance = null;\n }\n }\n\n /**\n * Register process exit handlers for cleanup.\n */\n private registerShutdownHandlers(): void {\n if (this.shutdownRegistered) return;\n this.shutdownRegistered = true;\n\n const shutdownHandler = () => {\n if (!this.isShuttingDown) {\n this.shutdownAllSync();\n }\n };\n\n // Register for various exit signals\n process.on('exit', shutdownHandler);\n process.on('SIGINT', () => {\n this.shutdownAll().then(() => process.exit(0)).catch(() => process.exit(1));\n });\n process.on('SIGTERM', () => {\n this.shutdownAll().then(() => process.exit(0)).catch(() => process.exit(1));\n });\n process.on('uncaughtException', (err) => {\n console.error('Uncaught exception:', err);\n this.shutdownAllSync();\n process.exit(1);\n });\n }\n\n /**\n * Get or create a named worker pool.\n *\n * If a pool with the given ID exists, returns the existing pool.\n * Otherwise, creates a new pool with the provided configuration.\n *\n * @param poolId - Unique identifier for the pool\n * @param config - Pool configuration options\n * @returns The worker pool instance\n */\n getPool(poolId: string, config: WorkerPoolConfig = {}): Pool {\n const existing = this.pools.get(poolId);\n if (existing) {\n return existing.pool;\n }\n\n return this.createPool(poolId, config);\n }\n\n /**\n * Create a new worker pool with the given ID.\n *\n * @param poolId - Unique identifier for the pool\n * @param config - Pool configuration options\n * @returns The newly created worker pool\n * @throws Error if a pool with the same ID already exists\n */\n createPool(poolId: string, config: WorkerPoolConfig = {}): Pool {\n if (this.pools.has(poolId)) {\n throw new Error(`Pool with ID '${poolId}' already exists`);\n }\n\n const mergedConfig = { ...DEFAULT_CONFIG, ...config };\n\n // Create pool options with inline type definition\n // Using inline type since WorkerPoolOptions is not directly exported\n const poolOptions: {\n maxWorkers?: number;\n workerType?: 'auto' | 'web' | 'process' | 'thread';\n workerThreadOpts?: Record<string, unknown>;\n } = {\n maxWorkers: mergedConfig.maxWorkers,\n workerType: mergedConfig.workerType,\n };\n\n // Add worker thread options for ESM support\n if (mergedConfig.workerType === 'thread') {\n poolOptions.workerThreadOpts = { type: 'module' };\n }\n\n // Create pool with or without worker script\n let pool: Pool;\n if (mergedConfig.workerPath) {\n pool = workerpool.pool(mergedConfig.workerPath, poolOptions);\n } else {\n pool = workerpool.pool(poolOptions);\n }\n\n const entry: PoolEntry = {\n pool,\n config: mergedConfig,\n createdAt: Date.now(),\n totalTasksExecuted: 0,\n totalExecutionTime: 0,\n };\n\n this.pools.set(poolId, entry);\n this.emitEvent(poolId, 'created');\n\n return pool;\n }\n\n /**\n * Check if a pool with the given ID exists.\n *\n * @param poolId - Pool identifier to check\n * @returns True if pool exists\n */\n hasPool(poolId: string): boolean {\n return this.pools.has(poolId);\n }\n\n /**\n * Get the configuration for a pool.\n *\n * @param poolId - Pool identifier\n * @returns Pool configuration or undefined if not found\n */\n getPoolConfig(poolId: string): WorkerPoolConfig | undefined {\n const entry = this.pools.get(poolId);\n return entry ? { ...entry.config } : undefined;\n }\n\n /**\n * Get extended statistics for a pool.\n *\n * @param poolId - Pool identifier\n * @returns Extended pool statistics or undefined if not found\n */\n getPoolStats(poolId: string): ExtendedPoolStats | undefined {\n const entry = this.pools.get(poolId);\n if (!entry) return undefined;\n\n const baseStats = entry.pool.stats();\n return {\n ...baseStats,\n poolId,\n createdAt: entry.createdAt,\n totalTasksExecuted: entry.totalTasksExecuted,\n totalExecutionTime: entry.totalExecutionTime,\n averageExecutionTime:\n entry.totalTasksExecuted > 0\n ? entry.totalExecutionTime / entry.totalTasksExecuted\n : 0,\n };\n }\n\n /**\n * Get statistics for all pools.\n *\n * @returns Map of pool IDs to their statistics\n */\n getAllPoolStats(): Map<string, ExtendedPoolStats> {\n const stats = new Map<string, ExtendedPoolStats>();\n for (const poolId of this.pools.keys()) {\n const poolStats = this.getPoolStats(poolId);\n if (poolStats) {\n stats.set(poolId, poolStats);\n }\n }\n return stats;\n }\n\n /**\n * Record task execution for statistics tracking.\n *\n * @param poolId - Pool identifier\n * @param executionTimeMs - Task execution time in milliseconds\n */\n recordTaskExecution(poolId: string, executionTimeMs: number): void {\n const entry = this.pools.get(poolId);\n if (entry) {\n entry.totalTasksExecuted++;\n entry.totalExecutionTime += executionTimeMs;\n }\n }\n\n /**\n * Execute a task on a pool with automatic statistics tracking.\n *\n * @template T - Result type\n * @param poolId - Pool identifier\n * @param method - Method name to execute (for worker script pools) or inline function\n * @param args - Arguments to pass to the method/function\n * @param timeout - Optional timeout in milliseconds\n * @returns Promise resolving to the task result\n */\n async executeTask<T>(\n poolId: string,\n method: string | ((...args: unknown[]) => T),\n args: unknown[] = [],\n timeout?: number\n ): Promise<T> {\n const entry = this.pools.get(poolId);\n if (!entry) {\n throw new Error(`Pool '${poolId}' not found`);\n }\n\n const effectiveTimeout = timeout ?? entry.config.defaultTimeout ?? DEFAULT_CONFIG.defaultTimeout;\n const startTime = Date.now();\n\n try {\n let result: T;\n if (typeof method === 'string') {\n // Execute named method from worker script\n result = await entry.pool.exec(method, args).timeout(effectiveTimeout) as T;\n } else {\n // Execute inline function\n result = await entry.pool.exec(method, args).timeout(effectiveTimeout) as T;\n }\n\n const executionTime = Date.now() - startTime;\n this.recordTaskExecution(poolId, executionTime);\n\n return result;\n } catch (error) {\n const executionTime = Date.now() - startTime;\n this.recordTaskExecution(poolId, executionTime);\n throw error;\n }\n }\n\n /**\n * Shutdown a specific pool.\n *\n * @param poolId - Pool identifier\n * @param force - If true, forcefully terminate workers (default: false)\n * @returns Promise resolving when shutdown is complete\n */\n async shutdownPool(poolId: string, force = false): Promise<void> {\n const entry = this.pools.get(poolId);\n if (!entry) return;\n\n try {\n await entry.pool.terminate(force);\n this.emitEvent(poolId, 'shutdown');\n } catch (error) {\n this.emitEvent(poolId, 'error', error);\n throw error;\n } finally {\n this.pools.delete(poolId);\n }\n }\n\n /**\n * Shutdown all pools asynchronously.\n *\n * @param force - If true, forcefully terminate workers (default: false)\n * @returns Promise resolving when all pools are shut down\n */\n async shutdownAll(force = false): Promise<void> {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n const shutdownPromises: Promise<void>[] = [];\n for (const poolId of this.pools.keys()) {\n shutdownPromises.push(this.shutdownPool(poolId, force));\n }\n\n try {\n await Promise.allSettled(shutdownPromises);\n } finally {\n this.pools.clear();\n this.isShuttingDown = false;\n }\n }\n\n /**\n * Synchronous shutdown for process exit handlers.\n * Forces immediate termination of all pools.\n */\n private shutdownAllSync(): void {\n if (this.isShuttingDown) return;\n this.isShuttingDown = true;\n\n for (const [poolId, entry] of this.pools) {\n try {\n entry.pool.terminate(true);\n this.emitEvent(poolId, 'shutdown');\n } catch {\n // Ignore errors during sync shutdown\n }\n }\n\n this.pools.clear();\n this.isShuttingDown = false;\n }\n\n /**\n * Register an event callback for pool events.\n *\n * @param callback - Callback function to invoke on events\n * @returns Unsubscribe function\n */\n onEvent(callback: PoolEventCallback): () => void {\n this.eventCallbacks.push(callback);\n return () => {\n const index = this.eventCallbacks.indexOf(callback);\n if (index >= 0) {\n this.eventCallbacks.splice(index, 1);\n }\n };\n }\n\n /**\n * Emit an event to all registered callbacks.\n */\n private emitEvent(poolId: string, event: 'created' | 'shutdown' | 'error', data?: unknown): void {\n for (const callback of this.eventCallbacks) {\n try {\n callback(poolId, event, data);\n } catch {\n // Ignore callback errors\n }\n }\n }\n\n /**\n * Get the number of active pools.\n *\n * @returns Number of pools currently managed\n */\n get poolCount(): number {\n return this.pools.size;\n }\n\n /**\n * Get all pool IDs.\n *\n * @returns Array of pool identifiers\n */\n getPoolIds(): string[] {\n return Array.from(this.pools.keys());\n }\n\n /**\n * Check if the minimum parallel size threshold is met.\n *\n * @param poolId - Pool identifier\n * @param size - Size of the data to process\n * @returns True if size meets or exceeds minimum threshold\n */\n shouldUseParallel(poolId: string, size: number): boolean {\n const entry = this.pools.get(poolId);\n const minSize = entry?.config.minParallelSize ?? DEFAULT_CONFIG.minParallelSize;\n return size >= minSize;\n }\n\n /**\n * Get the default configuration values.\n *\n * @returns Copy of default configuration\n */\n static getDefaultConfig(): Required<WorkerPoolConfig> {\n return { ...DEFAULT_CONFIG };\n }\n\n /**\n * Get the CPU count available for workers.\n *\n * @returns Number of CPUs\n */\n static getCpuCount(): number {\n return workerpool.cpus;\n }\n}\n\n/**\n * Convenience function to get the WorkerPoolManager instance.\n *\n * @returns The WorkerPoolManager singleton\n */\nexport function getWorkerPoolManager(): WorkerPoolManager {\n return WorkerPoolManager.getInstance();\n}\n","/**\n * Batch Processor\n *\n * Phase 12 Sprint 2: Generic batch processing utility with parallel execution,\n * retry logic, progress callbacks, and error collection.\n *\n * @module utils/BatchProcessor\n */\n\n/**\n * Progress information for batch processing.\n */\nexport interface BatchProgress {\n /** Number of items completed (successfully or failed) */\n completed: number;\n /** Total number of items to process */\n total: number;\n /** Completion percentage (0-100) */\n percentage: number;\n /** Number of successful items */\n succeeded: number;\n /** Number of failed items */\n failed: number;\n /** Current item being processed (if applicable) */\n currentItem?: unknown;\n /** Current batch index */\n batchIndex: number;\n /** Total number of batches */\n totalBatches: number;\n}\n\n/**\n * Progress callback function type.\n */\nexport type BatchProgressCallback = (progress: BatchProgress) => void;\n\n/**\n * Result for a single item in the batch.\n */\nexport interface BatchItemResult<T> {\n /** Index of the item in the original array */\n index: number;\n /** Whether the processing succeeded */\n success: boolean;\n /** Result value if successful */\n result?: T;\n /** Error if failed */\n error?: Error;\n /** Number of attempts made */\n attempts: number;\n /** Processing time in milliseconds */\n durationMs: number;\n}\n\n/**\n * Overall result of batch processing.\n */\nexport interface BatchProcessResult<T> {\n /** Results for each item */\n results: BatchItemResult<T>[];\n /** Number of successful items */\n succeeded: number;\n /** Number of failed items */\n failed: number;\n /** Total processing time in milliseconds */\n totalTimeMs: number;\n /** Whether all items succeeded */\n allSucceeded: boolean;\n /** Errors encountered (indexed by item position) */\n errors: Map<number, Error>;\n}\n\n/**\n * Options for batch processing.\n */\nexport interface BatchProcessorOptions {\n /** Number of items to process concurrently (default: 4) */\n concurrency?: number;\n /** Size of each batch for progress reporting (default: equals concurrency) */\n batchSize?: number;\n /** Maximum retry attempts per item (default: 0 - no retries) */\n maxRetries?: number;\n /** Initial delay between retries in milliseconds (default: 1000) */\n retryDelayMs?: number;\n /** Exponential backoff multiplier (default: 2) */\n retryBackoffMultiplier?: number;\n /** Maximum retry delay in milliseconds (default: 30000) */\n maxRetryDelayMs?: number;\n /** Whether to continue processing on item failure (default: true) */\n continueOnError?: boolean;\n /** Progress callback function */\n onProgress?: BatchProgressCallback;\n /** Timeout per item in milliseconds (default: no timeout) */\n itemTimeoutMs?: number;\n /** Optional abort signal for cancellation */\n signal?: AbortSignal;\n}\n\n/**\n * Default options for batch processing.\n */\nconst DEFAULT_OPTIONS: Required<Omit<BatchProcessorOptions, 'onProgress' | 'signal'>> = {\n concurrency: 4,\n batchSize: 4,\n maxRetries: 0,\n retryDelayMs: 1000,\n retryBackoffMultiplier: 2,\n maxRetryDelayMs: 30000,\n continueOnError: true,\n itemTimeoutMs: 0,\n};\n\n/**\n * BatchProcessor - Generic batch processing with parallel execution\n *\n * Provides a flexible utility for processing arrays of items with:\n * - Configurable concurrency\n * - Automatic retry with exponential backoff\n * - Progress callbacks for monitoring\n * - Error collection without failing the entire batch\n * - Cancellation support via AbortSignal\n *\n * @example\n * ```typescript\n * const processor = new BatchProcessor<string, number>({\n * concurrency: 4,\n * maxRetries: 3,\n * onProgress: (p) => console.log(`${p.percentage.toFixed(1)}% complete`),\n * });\n *\n * const result = await processor.process(\n * ['item1', 'item2', 'item3'],\n * async (item) => {\n * const response = await fetchData(item);\n * return response.value;\n * }\n * );\n *\n * console.log(`Succeeded: ${result.succeeded}, Failed: ${result.failed}`);\n * ```\n */\nexport class BatchProcessor<TInput, TOutput> {\n private options: Required<Omit<BatchProcessorOptions, 'onProgress' | 'signal'>> & {\n onProgress?: BatchProgressCallback;\n signal?: AbortSignal;\n };\n\n constructor(options: BatchProcessorOptions = {}) {\n this.options = {\n ...DEFAULT_OPTIONS,\n ...options,\n batchSize: options.batchSize ?? options.concurrency ?? DEFAULT_OPTIONS.concurrency,\n };\n }\n\n /**\n * Process all items in batches with the specified processor function.\n *\n * @param items - Array of items to process\n * @param processor - Async function to process each item\n * @returns Batch processing result with all item results and statistics\n */\n async process(\n items: TInput[],\n processor: (item: TInput, index: number) => Promise<TOutput>\n ): Promise<BatchProcessResult<TOutput>> {\n const startTime = Date.now();\n const results: BatchItemResult<TOutput>[] = new Array(items.length);\n const errors = new Map<number, Error>();\n let succeeded = 0;\n let failed = 0;\n\n const totalBatches = Math.ceil(items.length / this.options.batchSize);\n\n // Process items in batches\n for (let batchIndex = 0; batchIndex < items.length; batchIndex += this.options.batchSize) {\n // Check for cancellation\n if (this.options.signal?.aborted) {\n throw new Error('Batch processing cancelled');\n }\n\n const batchItems = items.slice(batchIndex, batchIndex + this.options.batchSize);\n const batchStartIndex = batchIndex;\n const currentBatchIndex = Math.floor(batchIndex / this.options.batchSize);\n\n // Process batch concurrently\n const batchPromises = batchItems.map((item, localIndex) =>\n this.processItem(item, batchStartIndex + localIndex, processor)\n );\n\n const batchResults = await Promise.all(batchPromises);\n\n // Collect results\n for (let i = 0; i < batchResults.length; i++) {\n const itemResult = batchResults[i];\n const globalIndex = batchStartIndex + i;\n results[globalIndex] = itemResult;\n\n if (itemResult.success) {\n succeeded++;\n } else {\n failed++;\n if (itemResult.error) {\n errors.set(globalIndex, itemResult.error);\n }\n\n // Stop if continueOnError is false\n if (!this.options.continueOnError) {\n return {\n results,\n succeeded,\n failed,\n totalTimeMs: Date.now() - startTime,\n allSucceeded: false,\n errors,\n };\n }\n }\n }\n\n // Report progress\n if (this.options.onProgress) {\n const completed = Math.min(batchIndex + batchItems.length, items.length);\n this.options.onProgress({\n completed,\n total: items.length,\n percentage: (completed / items.length) * 100,\n succeeded,\n failed,\n batchIndex: currentBatchIndex,\n totalBatches,\n });\n }\n }\n\n return {\n results,\n succeeded,\n failed,\n totalTimeMs: Date.now() - startTime,\n allSucceeded: failed === 0,\n errors,\n };\n }\n\n /**\n * Process a single item with retry logic.\n */\n private async processItem(\n item: TInput,\n index: number,\n processor: (item: TInput, index: number) => Promise<TOutput>\n ): Promise<BatchItemResult<TOutput>> {\n const startTime = Date.now();\n let lastError: Error | undefined;\n let attempts = 0;\n\n for (let attempt = 0; attempt <= this.options.maxRetries; attempt++) {\n attempts = attempt + 1;\n\n // Check for cancellation before each attempt\n if (this.options.signal?.aborted) {\n return {\n index,\n success: false,\n error: new Error('Processing cancelled'),\n attempts,\n durationMs: Date.now() - startTime,\n };\n }\n\n try {\n let result: TOutput;\n\n if (this.options.itemTimeoutMs > 0) {\n // Process with timeout\n result = await Promise.race([\n processor(item, index),\n this.createTimeout<TOutput>(this.options.itemTimeoutMs),\n ]);\n } else {\n result = await processor(item, index);\n }\n\n return {\n index,\n success: true,\n result,\n attempts,\n durationMs: Date.now() - startTime,\n };\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // Don't retry on last attempt\n if (attempt < this.options.maxRetries) {\n const delay = this.calculateRetryDelay(attempt);\n await this.sleep(delay);\n }\n }\n }\n\n return {\n index,\n success: false,\n error: lastError,\n attempts,\n durationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Calculate delay for retry with exponential backoff.\n */\n private calculateRetryDelay(attempt: number): number {\n const delay = this.options.retryDelayMs * Math.pow(this.options.retryBackoffMultiplier, attempt);\n return Math.min(delay, this.options.maxRetryDelayMs);\n }\n\n /**\n * Create a timeout promise.\n */\n private createTimeout<T>(ms: number): Promise<T> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error('Item processing timeout')), ms);\n });\n }\n\n /**\n * Sleep for the specified duration.\n */\n private sleep(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n }\n\n /**\n * Get the configured options.\n */\n getOptions(): BatchProcessorOptions {\n return { ...this.options };\n }\n}\n\n/**\n * Process items in parallel batches (convenience function).\n *\n * @template TInput - Input item type\n * @template TOutput - Output result type\n * @param items - Array of items to process\n * @param processor - Async function to process each item\n * @param options - Batch processing options\n * @returns Batch processing result\n *\n * @example\n * ```typescript\n * const result = await processBatch(\n * urls,\n * async (url) => fetch(url).then(r => r.json()),\n * { concurrency: 5, maxRetries: 2 }\n * );\n * ```\n */\nexport async function processBatch<TInput, TOutput>(\n items: TInput[],\n processor: (item: TInput, index: number) => Promise<TOutput>,\n options: BatchProcessorOptions = {}\n): Promise<BatchProcessResult<TOutput>> {\n const batchProcessor = new BatchProcessor<TInput, TOutput>(options);\n return batchProcessor.process(items, processor);\n}\n\n/**\n * Process items with automatic retry on failure (convenience function).\n *\n * @template TInput - Input item type\n * @template TOutput - Output result type\n * @param items - Array of items to process\n * @param processor - Async function to process each item\n * @param maxRetries - Maximum retry attempts (default: 3)\n * @param onProgress - Optional progress callback\n * @returns Batch processing result\n *\n * @example\n * ```typescript\n * const result = await processWithRetry(\n * items,\n * async (item) => unreliableOperation(item),\n * 3,\n * (p) => console.log(`${p.percentage}%`)\n * );\n * ```\n */\nexport async function processWithRetry<TInput, TOutput>(\n items: TInput[],\n processor: (item: TInput, index: number) => Promise<TOutput>,\n maxRetries = 3,\n onProgress?: BatchProgressCallback\n): Promise<BatchProcessResult<TOutput>> {\n return processBatch(items, processor, { maxRetries, onProgress });\n}\n\n/**\n * Chunk an array into smaller arrays of specified size.\n *\n * @template T - Item type\n * @param array - Array to chunk\n * @param size - Size of each chunk\n * @returns Array of chunks\n *\n * @example\n * ```typescript\n * const chunks = chunkArray([1, 2, 3, 4, 5], 2);\n * // [[1, 2], [3, 4], [5]]\n * ```\n */\nexport function chunkArray<T>(array: T[], size: number): T[][] {\n if (size <= 0) {\n throw new Error('Chunk size must be greater than 0');\n }\n\n const chunks: T[][] = [];\n for (let i = 0; i < array.length; i += size) {\n chunks.push(array.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * Execute async functions in parallel with a concurrency limit.\n *\n * @template T - Result type\n * @param tasks - Array of async functions to execute\n * @param concurrency - Maximum concurrent executions\n * @returns Array of results (successful results or errors)\n *\n * @example\n * ```typescript\n * const tasks = urls.map(url => () => fetch(url));\n * const results = await parallelLimit(tasks, 5);\n * ```\n */\nexport async function parallelLimit<T>(\n tasks: Array<() => Promise<T>>,\n concurrency: number\n): Promise<Array<{ success: true; value: T } | { success: false; error: Error }>> {\n const results: Array<{ success: true; value: T } | { success: false; error: Error }> = [];\n\n for (let i = 0; i < tasks.length; i += concurrency) {\n const batch = tasks.slice(i, i + concurrency);\n\n const batchResults = await Promise.all(\n batch.map(async task => {\n try {\n const value = await task();\n return { success: true as const, value };\n } catch (error) {\n return {\n success: false as const,\n error: error instanceof Error ? error : new Error(String(error)),\n };\n }\n })\n );\n\n results.push(...batchResults);\n }\n\n return results;\n}\n\n/**\n * Map over items in parallel with a concurrency limit.\n *\n * @template TInput - Input item type\n * @template TOutput - Output result type\n * @param items - Array of items to map\n * @param mapper - Async mapping function\n * @param concurrency - Maximum concurrent operations (default: 4)\n * @returns Array of results (in order)\n *\n * @example\n * ```typescript\n * const results = await mapParallel(\n * ids,\n * async (id) => fetchUser(id),\n * 10\n * );\n * ```\n */\nexport async function mapParallel<TInput, TOutput>(\n items: TInput[],\n mapper: (item: TInput, index: number) => Promise<TOutput>,\n concurrency = 4\n): Promise<TOutput[]> {\n const results: TOutput[] = new Array(items.length);\n\n for (let i = 0; i < items.length; i += concurrency) {\n const batch = items.slice(i, i + concurrency);\n const startIndex = i;\n\n const batchResults = await Promise.all(\n batch.map((item, localIndex) => mapper(item, startIndex + localIndex))\n );\n\n for (let j = 0; j < batchResults.length; j++) {\n results[startIndex + j] = batchResults[j];\n }\n }\n\n return results;\n}\n\n/**\n * Filter items in parallel with a concurrency limit.\n *\n * @template T - Item type\n * @param items - Array of items to filter\n * @param predicate - Async predicate function\n * @param concurrency - Maximum concurrent operations (default: 4)\n * @returns Filtered array (in order)\n *\n * @example\n * ```typescript\n * const validItems = await filterParallel(\n * items,\n * async (item) => validateItem(item),\n * 10\n * );\n * ```\n */\nexport async function filterParallel<T>(\n items: T[],\n predicate: (item: T, index: number) => Promise<boolean>,\n concurrency = 4\n): Promise<T[]> {\n const includeFlags = await mapParallel(items, predicate, concurrency);\n return items.filter((_, index) => includeFlags[index]);\n}\n","/**\n * Memory Usage Monitor\n *\n * Phase 12 Sprint 6: Track memory usage across all components\n * with human-readable formatting.\n *\n * @module utils/MemoryMonitor\n */\n\n/**\n * Memory usage for a single component.\n */\nexport interface ComponentMemoryUsage {\n /** Component name */\n name: string;\n /** Estimated memory usage in bytes */\n bytes: number;\n /** Item count (if applicable) */\n itemCount?: number;\n /** Average bytes per item */\n bytesPerItem?: number;\n}\n\n/**\n * Aggregate memory usage statistics.\n */\nexport interface MemoryUsageStats {\n /** Total memory usage in bytes */\n totalBytes: number;\n /** Formatted total memory */\n totalFormatted: string;\n /** Per-component breakdown */\n components: ComponentMemoryUsage[];\n /** Timestamp of measurement */\n timestamp: Date;\n /** Node.js heap stats (if available) */\n heapStats?: {\n heapUsed: number;\n heapTotal: number;\n external: number;\n rss: number;\n };\n}\n\n/**\n * Memory threshold configuration.\n */\nexport interface MemoryThresholds {\n /** Warning threshold in bytes */\n warning: number;\n /** Critical threshold in bytes */\n critical: number;\n}\n\n/**\n * Memory alert.\n */\nexport interface MemoryAlert {\n /** Alert level */\n level: 'warning' | 'critical';\n /** Component that triggered the alert */\n component: string;\n /** Current usage */\n currentBytes: number;\n /** Threshold exceeded */\n threshold: number;\n /** Message */\n message: string;\n}\n\n/**\n * Callback for memory change events.\n */\nexport type MemoryChangeCallback = (usage: MemoryUsageStats) => void;\n\n/**\n * Default memory thresholds.\n */\nconst DEFAULT_THRESHOLDS: MemoryThresholds = {\n warning: 100 * 1024 * 1024, // 100 MB\n critical: 500 * 1024 * 1024, // 500 MB\n};\n\n/**\n * Memory Monitor for tracking usage across components.\n *\n * @example\n * ```typescript\n * const monitor = new MemoryMonitor();\n *\n * // Register components\n * monitor.registerComponent('entities', () => entities.length * 500);\n * monitor.registerComponent('vectors', () => vectors.size * dimension * 4);\n *\n * // Get usage stats\n * const stats = monitor.getUsage();\n * console.log(`Total memory: ${stats.totalFormatted}`);\n *\n * // Check for alerts\n * const alerts = monitor.checkThresholds();\n * ```\n */\nexport class MemoryMonitor {\n private componentEstimators: Map<string, () => number>;\n private itemCounters: Map<string, () => number>;\n private thresholds: MemoryThresholds;\n private listeners: MemoryChangeCallback[];\n private lastUsage: MemoryUsageStats | null = null;\n\n constructor(thresholds?: Partial<MemoryThresholds>) {\n this.componentEstimators = new Map();\n this.itemCounters = new Map();\n this.thresholds = { ...DEFAULT_THRESHOLDS, ...thresholds };\n this.listeners = [];\n }\n\n /**\n * Register a component for memory tracking.\n *\n * @param name - Component name\n * @param estimator - Function that returns estimated bytes\n * @param itemCounter - Optional function that returns item count\n */\n registerComponent(\n name: string,\n estimator: () => number,\n itemCounter?: () => number\n ): void {\n this.componentEstimators.set(name, estimator);\n if (itemCounter) {\n this.itemCounters.set(name, itemCounter);\n }\n }\n\n /**\n * Unregister a component.\n *\n * @param name - Component name\n */\n unregisterComponent(name: string): void {\n this.componentEstimators.delete(name);\n this.itemCounters.delete(name);\n }\n\n /**\n * Get current memory usage statistics.\n */\n getUsage(): MemoryUsageStats {\n const components: ComponentMemoryUsage[] = [];\n let totalBytes = 0;\n\n for (const [name, estimator] of this.componentEstimators) {\n const bytes = estimator();\n totalBytes += bytes;\n\n const itemCounter = this.itemCounters.get(name);\n const itemCount = itemCounter ? itemCounter() : undefined;\n const bytesPerItem = itemCount && itemCount > 0 ? Math.round(bytes / itemCount) : undefined;\n\n components.push({\n name,\n bytes,\n itemCount,\n bytesPerItem,\n });\n }\n\n // Sort by usage descending\n components.sort((a, b) => b.bytes - a.bytes);\n\n // Get Node.js heap stats if available\n let heapStats: MemoryUsageStats['heapStats'];\n if (typeof process !== 'undefined' && process.memoryUsage) {\n const mem = process.memoryUsage();\n heapStats = {\n heapUsed: mem.heapUsed,\n heapTotal: mem.heapTotal,\n external: mem.external,\n rss: mem.rss,\n };\n }\n\n const stats: MemoryUsageStats = {\n totalBytes,\n totalFormatted: this.formatBytes(totalBytes),\n components,\n timestamp: new Date(),\n heapStats,\n };\n\n this.lastUsage = stats;\n this.notifyListeners(stats);\n\n return stats;\n }\n\n /**\n * Get memory usage for a specific component.\n *\n * @param name - Component name\n */\n getComponentUsage(name: string): ComponentMemoryUsage | undefined {\n const estimator = this.componentEstimators.get(name);\n if (!estimator) return undefined;\n\n const bytes = estimator();\n const itemCounter = this.itemCounters.get(name);\n const itemCount = itemCounter ? itemCounter() : undefined;\n const bytesPerItem = itemCount && itemCount > 0 ? Math.round(bytes / itemCount) : undefined;\n\n return {\n name,\n bytes,\n itemCount,\n bytesPerItem,\n };\n }\n\n /**\n * Check memory thresholds and return alerts.\n */\n checkThresholds(): MemoryAlert[] {\n const alerts: MemoryAlert[] = [];\n const usage = this.getUsage();\n\n // Check total memory\n if (usage.totalBytes >= this.thresholds.critical) {\n alerts.push({\n level: 'critical',\n component: 'total',\n currentBytes: usage.totalBytes,\n threshold: this.thresholds.critical,\n message: `Total memory usage (${this.formatBytes(usage.totalBytes)}) exceeds critical threshold (${this.formatBytes(this.thresholds.critical)})`,\n });\n } else if (usage.totalBytes >= this.thresholds.warning) {\n alerts.push({\n level: 'warning',\n component: 'total',\n currentBytes: usage.totalBytes,\n threshold: this.thresholds.warning,\n message: `Total memory usage (${this.formatBytes(usage.totalBytes)}) exceeds warning threshold (${this.formatBytes(this.thresholds.warning)})`,\n });\n }\n\n // Check per-component thresholds (50% of total threshold per component)\n const componentWarning = this.thresholds.warning * 0.5;\n const componentCritical = this.thresholds.critical * 0.5;\n\n for (const component of usage.components) {\n if (component.bytes >= componentCritical) {\n alerts.push({\n level: 'critical',\n component: component.name,\n currentBytes: component.bytes,\n threshold: componentCritical,\n message: `Component '${component.name}' (${this.formatBytes(component.bytes)}) exceeds critical threshold`,\n });\n } else if (component.bytes >= componentWarning) {\n alerts.push({\n level: 'warning',\n component: component.name,\n currentBytes: component.bytes,\n threshold: componentWarning,\n message: `Component '${component.name}' (${this.formatBytes(component.bytes)}) exceeds warning threshold`,\n });\n }\n }\n\n return alerts;\n }\n\n /**\n * Set memory thresholds.\n *\n * @param thresholds - New threshold values\n */\n setThresholds(thresholds: Partial<MemoryThresholds>): void {\n this.thresholds = { ...this.thresholds, ...thresholds };\n }\n\n /**\n * Get current thresholds.\n */\n getThresholds(): MemoryThresholds {\n return { ...this.thresholds };\n }\n\n /**\n * Add a listener for memory changes.\n *\n * @param callback - Callback to invoke on memory changes\n */\n addListener(callback: MemoryChangeCallback): void {\n this.listeners.push(callback);\n }\n\n /**\n * Remove a listener.\n *\n * @param callback - Callback to remove\n */\n removeListener(callback: MemoryChangeCallback): void {\n const index = this.listeners.indexOf(callback);\n if (index !== -1) {\n this.listeners.splice(index, 1);\n }\n }\n\n /**\n * Get a human-readable summary of memory usage.\n */\n getSummary(): string {\n const usage = this.getUsage();\n const lines: string[] = [\n '=== Memory Usage Summary ===',\n `Total: ${usage.totalFormatted}`,\n '',\n 'By Component:',\n ];\n\n for (const component of usage.components) {\n const itemInfo = component.itemCount\n ? ` (${component.itemCount.toLocaleString()} items, ~${this.formatBytes(component.bytesPerItem ?? 0)}/item)`\n : '';\n lines.push(` ${component.name}: ${this.formatBytes(component.bytes)}${itemInfo}`);\n }\n\n if (usage.heapStats) {\n lines.push('');\n lines.push('Node.js Heap:');\n lines.push(` Heap Used: ${this.formatBytes(usage.heapStats.heapUsed)}`);\n lines.push(` Heap Total: ${this.formatBytes(usage.heapStats.heapTotal)}`);\n lines.push(` External: ${this.formatBytes(usage.heapStats.external)}`);\n lines.push(` RSS: ${this.formatBytes(usage.heapStats.rss)}`);\n }\n\n return lines.join('\\n');\n }\n\n /**\n * Get the last recorded usage without triggering a new measurement.\n */\n getLastUsage(): MemoryUsageStats | null {\n return this.lastUsage;\n }\n\n /**\n * Format bytes as human-readable string.\n *\n * @param bytes - Number of bytes\n */\n formatBytes(bytes: number): string {\n if (bytes === 0) return '0 B';\n\n const units = ['B', 'KB', 'MB', 'GB', 'TB'];\n const k = 1024;\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n const value = bytes / Math.pow(k, i);\n\n return `${value.toFixed(i === 0 ? 0 : 2)} ${units[i]}`;\n }\n\n /**\n * Parse a formatted byte string back to number.\n *\n * @param formatted - Formatted string like \"10 MB\"\n */\n parseBytes(formatted: string): number {\n const match = formatted.match(/^([\\d.]+)\\s*(B|KB|MB|GB|TB)$/i);\n if (!match) return 0;\n\n const value = parseFloat(match[1]);\n const unit = match[2].toUpperCase();\n const units: Record<string, number> = {\n 'B': 1,\n 'KB': 1024,\n 'MB': 1024 * 1024,\n 'GB': 1024 * 1024 * 1024,\n 'TB': 1024 * 1024 * 1024 * 1024,\n };\n\n return value * (units[unit] ?? 1);\n }\n\n /**\n * Clear all registered components.\n */\n clear(): void {\n this.componentEstimators.clear();\n this.itemCounters.clear();\n this.lastUsage = null;\n }\n\n // Private methods\n\n private notifyListeners(usage: MemoryUsageStats): void {\n for (const listener of this.listeners) {\n try {\n listener(usage);\n } catch {\n // Ignore listener errors\n }\n }\n }\n}\n\n/**\n * Singleton instance for global memory monitoring.\n */\nexport const globalMemoryMonitor = new MemoryMonitor();\n","/**\r\n * Graph Storage\r\n *\r\n * Handles file I/O operations for the knowledge graph using JSONL format.\r\n * Implements IGraphStorage interface for storage abstraction.\r\n *\r\n * @module core/GraphStorage\r\n */\r\n\r\nimport { promises as fs } from 'fs';\r\nimport { Mutex } from 'async-mutex';\r\nimport type { KnowledgeGraph, Entity, Relation, ReadonlyKnowledgeGraph, IGraphStorage, LowercaseData } from '../types/index.js';\r\nimport { clearAllSearchCaches } from '../utils/searchCache.js';\r\nimport { NameIndex, TypeIndex, LowercaseCache, RelationIndex, ObservationIndex } from '../utils/indexes.js';\r\nimport { sanitizeObject } from '../utils/index.js';\r\nimport { BatchTransaction } from './TransactionManager.js';\r\nimport { GraphEventEmitter } from './GraphEventEmitter.js';\r\n\r\n/**\r\n * GraphStorage manages persistence of the knowledge graph to disk.\r\n *\r\n * Uses JSONL (JSON Lines) format where each line is a separate JSON object\r\n * representing either an entity or a relation.\r\n *\r\n * OPTIMIZED: Implements in-memory caching to avoid repeated disk reads.\r\n * Cache is invalidated on every write operation to ensure consistency.\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/path/to/memory.jsonl');\r\n * const graph = await storage.loadGraph();\r\n * graph.entities.push(newEntity);\r\n * await storage.saveGraph(graph);\r\n * ```\r\n */\r\nexport class GraphStorage implements IGraphStorage {\r\n /**\r\n * Mutex for thread-safe access to storage operations.\r\n * Prevents concurrent writes from corrupting the file or cache.\r\n */\r\n private mutex = new Mutex();\r\n\r\n /**\r\n * In-memory cache of the knowledge graph.\r\n * Null when cache is empty or invalidated.\r\n */\r\n private cache: KnowledgeGraph | null = null;\r\n\r\n /**\r\n * Number of pending append operations since last compaction.\r\n * Used to trigger automatic compaction when threshold is reached.\r\n */\r\n private pendingAppends: number = 0;\r\n\r\n /**\r\n * Dynamic threshold for automatic compaction.\r\n *\r\n * Returns the larger of 100 or 10% of the current entity count.\r\n * This scales with graph size to avoid too-frequent compaction on large graphs\r\n * while maintaining a reasonable minimum for small graphs.\r\n *\r\n * @returns Compaction threshold value\r\n */\r\n private get compactionThreshold(): number {\r\n return Math.max(100, Math.floor((this.cache?.entities.length ?? 0) * 0.1));\r\n }\r\n\r\n /**\r\n * O(1) entity lookup by name.\r\n */\r\n private nameIndex: NameIndex = new NameIndex();\r\n\r\n /**\r\n * O(1) entity lookup by type.\r\n */\r\n private typeIndex: TypeIndex = new TypeIndex();\r\n\r\n /**\r\n * Pre-computed lowercase strings for search optimization.\r\n */\r\n private lowercaseCache: LowercaseCache = new LowercaseCache();\r\n\r\n /**\r\n * O(1) relation lookup by entity name.\r\n */\r\n private relationIndex: RelationIndex = new RelationIndex();\r\n\r\n /**\r\n * O(1) observation word lookup by entity.\r\n * Maps words in observations to entity names.\r\n */\r\n private observationIndex: ObservationIndex = new ObservationIndex();\r\n\r\n /**\r\n * Phase 10 Sprint 2: Event emitter for graph change notifications.\r\n * Allows external systems to subscribe to graph changes.\r\n */\r\n private eventEmitter: GraphEventEmitter = new GraphEventEmitter();\r\n\r\n /**\r\n * Create a new GraphStorage instance.\r\n *\r\n * @param memoryFilePath - Absolute path to the JSONL file\r\n */\r\n constructor(private memoryFilePath: string) {}\r\n\r\n // ==================== Phase 10 Sprint 2: Event Emitter Access ====================\r\n\r\n /**\r\n * Get the event emitter for subscribing to graph changes.\r\n *\r\n * @returns GraphEventEmitter instance\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/data/memory.jsonl');\r\n *\r\n * // Subscribe to entity creation events\r\n * storage.events.on('entity:created', (event) => {\r\n * console.log(`Entity ${event.entity.name} created`);\r\n * });\r\n *\r\n * // Subscribe to all events\r\n * storage.events.onAny((event) => {\r\n * console.log(`Graph event: ${event.type}`);\r\n * });\r\n * ```\r\n */\r\n get events(): GraphEventEmitter {\r\n return this.eventEmitter;\r\n }\r\n\r\n // ==================== Durable File Operations ====================\r\n\r\n /**\r\n * Write content to file with fsync for durability.\r\n *\r\n * @param content - Content to write\r\n */\r\n private async durableWriteFile(content: string): Promise<void> {\r\n const fd = await fs.open(this.memoryFilePath, 'w');\r\n try {\r\n await fd.write(content);\r\n await fd.sync();\r\n } finally {\r\n await fd.close();\r\n }\r\n }\r\n\r\n /**\r\n * Append content to file with fsync for durability.\r\n *\r\n * @param content - Content to append\r\n * @param prependNewline - Whether to prepend a newline\r\n */\r\n private async durableAppendFile(content: string, prependNewline: boolean): Promise<void> {\r\n const fd = await fs.open(this.memoryFilePath, 'a');\r\n try {\r\n const dataToWrite = prependNewline ? '\\n' + content : content;\r\n await fd.write(dataToWrite);\r\n await fd.sync();\r\n } finally {\r\n await fd.close();\r\n }\r\n }\r\n\r\n /**\r\n * Load the knowledge graph from disk (read-only access).\r\n *\r\n * OPTIMIZED: Returns cached reference directly without copying.\r\n * This is O(1) regardless of graph size. For mutation operations,\r\n * use getGraphForMutation() instead.\r\n *\r\n * @returns Promise resolving to read-only knowledge graph reference\r\n * @throws Error if file exists but cannot be read or parsed\r\n */\r\n async loadGraph(): Promise<ReadonlyKnowledgeGraph> {\r\n // Return cached graph directly (no copying - O(1))\r\n if (this.cache !== null) {\r\n return this.cache;\r\n }\r\n\r\n // Cache miss - load from disk\r\n await this.loadFromDisk();\r\n return this.cache!;\r\n }\r\n\r\n /**\r\n * Get a mutable copy of the graph for write operations.\r\n *\r\n * Creates deep copies of entity and relation arrays to allow\r\n * safe mutation without affecting the cached data.\r\n *\r\n * @returns Promise resolving to mutable knowledge graph copy\r\n */\r\n async getGraphForMutation(): Promise<KnowledgeGraph> {\r\n await this.ensureLoaded();\r\n return {\r\n entities: this.cache!.entities.map(e => ({\r\n ...e,\r\n observations: [...e.observations],\r\n tags: e.tags ? [...e.tags] : undefined,\r\n })),\r\n relations: this.cache!.relations.map(r => ({ ...r })),\r\n };\r\n }\r\n\r\n /**\r\n * Ensure the cache is loaded from disk.\r\n *\r\n * @returns Promise resolving when cache is populated\r\n */\r\n async ensureLoaded(): Promise<void> {\r\n if (this.cache === null) {\r\n await this.loadFromDisk();\r\n }\r\n }\r\n\r\n /**\r\n * Internal method to load graph from disk into cache.\r\n */\r\n private async loadFromDisk(): Promise<void> {\r\n try {\r\n const data = await fs.readFile(this.memoryFilePath, 'utf-8');\r\n const lines = data.split('\\n').filter((line: string) => line.trim() !== '');\r\n\r\n // Use Maps to deduplicate - later entries override earlier ones\r\n // This supports append-only updates where new versions are appended\r\n const entityMap = new Map<string, Entity>();\r\n const relationMap = new Map<string, Relation>();\r\n\r\n for (const line of lines) {\r\n const item = JSON.parse(line);\r\n\r\n if (item.type === 'entity') {\r\n // Add createdAt if missing for backward compatibility\r\n if (!item.createdAt) item.createdAt = new Date().toISOString();\r\n // Add lastModified if missing for backward compatibility\r\n if (!item.lastModified) item.lastModified = item.createdAt;\r\n\r\n // Use name as key - later entries override earlier ones\r\n entityMap.set(item.name, item as Entity);\r\n }\r\n\r\n if (item.type === 'relation') {\r\n // Add createdAt if missing for backward compatibility\r\n if (!item.createdAt) item.createdAt = new Date().toISOString();\r\n // Add lastModified if missing for backward compatibility\r\n if (!item.lastModified) item.lastModified = item.createdAt;\r\n\r\n // Use composite key for relations\r\n const key = `${item.from}:${item.to}:${item.relationType}`;\r\n relationMap.set(key, item as Relation);\r\n }\r\n }\r\n\r\n // Convert maps to arrays\r\n const graph: KnowledgeGraph = {\r\n entities: Array.from(entityMap.values()),\r\n relations: Array.from(relationMap.values()),\r\n };\r\n\r\n // Populate cache\r\n this.cache = graph;\r\n\r\n // Build indexes from loaded data\r\n this.buildEntityIndexes(graph.entities);\r\n this.buildRelationIndex(graph.relations);\r\n\r\n // Phase 10 Sprint 2: Emit graph:loaded event\r\n this.eventEmitter.emitGraphLoaded(graph.entities.length, graph.relations.length);\r\n } catch (error) {\r\n // File doesn't exist - create empty graph\r\n if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {\r\n this.cache = { entities: [], relations: [] };\r\n this.clearIndexes();\r\n\r\n // Phase 10 Sprint 2: Emit graph:loaded event for empty graph\r\n this.eventEmitter.emitGraphLoaded(0, 0);\r\n return;\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Build all entity indexes from entity array.\r\n */\r\n private buildEntityIndexes(entities: Entity[]): void {\r\n this.nameIndex.build(entities);\r\n this.typeIndex.build(entities);\r\n this.lowercaseCache.build(entities);\r\n\r\n // Build observation index\r\n this.observationIndex.clear();\r\n for (const entity of entities) {\r\n this.observationIndex.add(entity.name, entity.observations);\r\n }\r\n }\r\n\r\n /**\r\n * Build relation index from relation array.\r\n */\r\n private buildRelationIndex(relations: Relation[]): void {\r\n this.relationIndex.build(relations);\r\n }\r\n\r\n /**\r\n * Clear all indexes.\r\n */\r\n private clearIndexes(): void {\r\n this.nameIndex.clear();\r\n this.typeIndex.clear();\r\n this.lowercaseCache.clear();\r\n this.relationIndex.clear();\r\n this.observationIndex.clear();\r\n }\r\n\r\n /**\r\n * Save the knowledge graph to disk.\r\n *\r\n * OPTIMIZED: Updates cache directly after write to avoid re-reading.\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n *\r\n * Writes the graph to JSONL format, with one JSON object per line.\r\n *\r\n * @param graph - The knowledge graph to save\r\n * @returns Promise resolving when save is complete\r\n * @throws Error if file cannot be written\r\n */\r\n async saveGraph(graph: KnowledgeGraph): Promise<void> {\r\n return this.mutex.runExclusive(async () => {\r\n await this.saveGraphInternal(graph);\r\n });\r\n }\r\n\r\n /**\r\n * Append a single entity to the file (O(1) write operation).\r\n *\r\n * OPTIMIZED: Uses file append instead of full rewrite.\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n * Updates cache in-place and triggers compaction when threshold is reached.\r\n *\r\n * @param entity - The entity to append\r\n * @returns Promise resolving when append is complete\r\n */\r\n async appendEntity(entity: Entity): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n const entityData: Record<string, unknown> = {\r\n type: 'entity',\r\n name: entity.name,\r\n entityType: entity.entityType,\r\n observations: entity.observations,\r\n createdAt: entity.createdAt,\r\n lastModified: entity.lastModified,\r\n };\r\n\r\n // Only include optional fields if they exist\r\n if (entity.tags !== undefined) entityData.tags = entity.tags;\r\n if (entity.importance !== undefined) entityData.importance = entity.importance;\r\n if (entity.parentId !== undefined) entityData.parentId = entity.parentId;\r\n\r\n const line = JSON.stringify(entityData);\r\n\r\n // Append to file with fsync for durability (write FIRST, then update cache)\r\n try {\r\n const stat = await fs.stat(this.memoryFilePath);\r\n await this.durableAppendFile(line, stat.size > 0);\r\n } catch (error) {\r\n // File doesn't exist - create it\r\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n await this.durableWriteFile(line);\r\n } else {\r\n throw error;\r\n }\r\n }\r\n\r\n // Update cache in-place (after successful file write)\r\n this.cache!.entities.push(entity);\r\n\r\n // Update indexes\r\n this.nameIndex.add(entity);\r\n this.typeIndex.add(entity);\r\n this.lowercaseCache.set(entity);\r\n this.observationIndex.add(entity.name, entity.observations);\r\n\r\n this.pendingAppends++;\r\n\r\n // Clear search caches\r\n clearAllSearchCaches();\r\n\r\n // Phase 10 Sprint 2: Emit entity:created event\r\n this.eventEmitter.emitEntityCreated(entity);\r\n\r\n // Trigger compaction if threshold reached\r\n if (this.pendingAppends >= this.compactionThreshold) {\r\n await this.compactInternal();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Append a single relation to the file (O(1) write operation).\r\n *\r\n * OPTIMIZED: Uses file append instead of full rewrite.\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n * Updates cache in-place and triggers compaction when threshold is reached.\r\n *\r\n * @param relation - The relation to append\r\n * @returns Promise resolving when append is complete\r\n */\r\n async appendRelation(relation: Relation): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n const line = JSON.stringify({\r\n type: 'relation',\r\n from: relation.from,\r\n to: relation.to,\r\n relationType: relation.relationType,\r\n createdAt: relation.createdAt,\r\n lastModified: relation.lastModified,\r\n });\r\n\r\n // Append to file with fsync for durability (write FIRST, then update cache)\r\n try {\r\n const stat = await fs.stat(this.memoryFilePath);\r\n await this.durableAppendFile(line, stat.size > 0);\r\n } catch (error) {\r\n // File doesn't exist - create it\r\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n await this.durableWriteFile(line);\r\n } else {\r\n throw error;\r\n }\r\n }\r\n\r\n // Update cache in-place (after successful file write)\r\n this.cache!.relations.push(relation);\r\n\r\n // Update relation index\r\n this.relationIndex.add(relation);\r\n\r\n this.pendingAppends++;\r\n\r\n // Clear search caches\r\n clearAllSearchCaches();\r\n\r\n // Phase 10 Sprint 2: Emit relation:created event\r\n this.eventEmitter.emitRelationCreated(relation);\r\n\r\n // Trigger compaction if threshold reached\r\n if (this.pendingAppends >= this.compactionThreshold) {\r\n await this.compactInternal();\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * Compact the file by rewriting it with only current cache contents.\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent operations.\r\n * Removes duplicate entries and cleans up the file.\r\n * Resets pending appends counter.\r\n *\r\n * @returns Promise resolving when compaction is complete\r\n */\r\n async compact(): Promise<void> {\r\n return this.mutex.runExclusive(async () => {\r\n await this.compactInternal();\r\n });\r\n }\r\n\r\n /**\r\n * Internal compact implementation (must be called within mutex).\r\n *\r\n * @returns Promise resolving when compaction is complete\r\n */\r\n private async compactInternal(): Promise<void> {\r\n if (this.cache === null) {\r\n return;\r\n }\r\n\r\n // Rewrite file with current cache (removes duplicates/updates)\r\n await this.saveGraphInternal(this.cache);\r\n this.pendingAppends = 0;\r\n }\r\n\r\n /**\r\n * Internal saveGraph implementation (must be called within mutex).\r\n *\r\n * @param graph - The knowledge graph to save\r\n * @returns Promise resolving when save is complete\r\n */\r\n private async saveGraphInternal(graph: KnowledgeGraph): Promise<void> {\r\n const lines = [\r\n ...graph.entities.map(e => {\r\n const entityData: Record<string, unknown> = {\r\n type: 'entity',\r\n name: e.name,\r\n entityType: e.entityType,\r\n observations: e.observations,\r\n createdAt: e.createdAt,\r\n lastModified: e.lastModified,\r\n };\r\n\r\n // Only include optional fields if they exist\r\n if (e.tags !== undefined) entityData.tags = e.tags;\r\n if (e.importance !== undefined) entityData.importance = e.importance;\r\n if (e.parentId !== undefined) entityData.parentId = e.parentId;\r\n\r\n return JSON.stringify(entityData);\r\n }),\r\n ...graph.relations.map(r =>\r\n JSON.stringify({\r\n type: 'relation',\r\n from: r.from,\r\n to: r.to,\r\n relationType: r.relationType,\r\n createdAt: r.createdAt,\r\n lastModified: r.lastModified,\r\n })\r\n ),\r\n ];\r\n\r\n await this.durableWriteFile(lines.join('\\n'));\r\n\r\n // Update cache directly with the saved graph (avoid re-reading from disk)\r\n this.cache = graph;\r\n\r\n // Rebuild indexes with new graph data\r\n this.buildEntityIndexes(graph.entities);\r\n this.buildRelationIndex(graph.relations);\r\n\r\n // Reset pending appends since file is now clean\r\n this.pendingAppends = 0;\r\n\r\n // Clear all search caches since graph data has changed\r\n clearAllSearchCaches();\r\n\r\n // Phase 10 Sprint 2: Emit graph:saved event\r\n this.eventEmitter.emitGraphSaved(graph.entities.length, graph.relations.length);\r\n }\r\n\r\n /**\r\n * Get the current pending appends count.\r\n *\r\n * Useful for testing compaction behavior.\r\n *\r\n * @returns Number of pending appends since last compaction\r\n */\r\n getPendingAppends(): number {\r\n return this.pendingAppends;\r\n }\r\n\r\n /**\r\n * Update an entity in-place in the cache and append to file.\r\n *\r\n * OPTIMIZED: Modifies cache directly and appends updated version to file.\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n * Does not rewrite the entire file - compaction handles deduplication later.\r\n *\r\n * @param entityName - Name of the entity to update\r\n * @param updates - Partial entity updates to apply\r\n * @returns Promise resolving to true if entity was found and updated, false otherwise\r\n */\r\n async updateEntity(entityName: string, updates: Partial<Entity>): Promise<boolean> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n const entityIndex = this.cache!.entities.findIndex(e => e.name === entityName);\r\n if (entityIndex === -1) {\r\n return false;\r\n }\r\n\r\n const entity = this.cache!.entities[entityIndex];\r\n const oldType = entity.entityType;\r\n const timestamp = new Date().toISOString();\r\n\r\n // Phase 10 Sprint 2: Capture previous values for event\r\n const previousValues: Partial<Entity> = {};\r\n for (const key of Object.keys(updates) as Array<keyof Entity>) {\r\n if (key in entity) {\r\n previousValues[key] = entity[key] as any;\r\n }\r\n }\r\n\r\n // Build the updated entity data for file write BEFORE modifying cache\r\n // This ensures cache consistency if file write fails\r\n const updatedEntity = {\r\n ...entity,\r\n ...updates,\r\n lastModified: timestamp,\r\n };\r\n\r\n const entityData: Record<string, unknown> = {\r\n type: 'entity',\r\n name: updatedEntity.name,\r\n entityType: updatedEntity.entityType,\r\n observations: updatedEntity.observations,\r\n createdAt: updatedEntity.createdAt,\r\n lastModified: updatedEntity.lastModified,\r\n };\r\n\r\n if (updatedEntity.tags !== undefined) entityData.tags = updatedEntity.tags;\r\n if (updatedEntity.importance !== undefined) entityData.importance = updatedEntity.importance;\r\n if (updatedEntity.parentId !== undefined) entityData.parentId = updatedEntity.parentId;\r\n\r\n const line = JSON.stringify(entityData);\r\n\r\n // Write to file FIRST with durability - if this fails, cache remains consistent\r\n try {\r\n const stat = await fs.stat(this.memoryFilePath);\r\n await this.durableAppendFile(line, stat.size > 0);\r\n } catch (error) {\r\n if (error instanceof Error && 'code' in error && (error as NodeJS.ErrnoException).code === 'ENOENT') {\r\n await this.durableWriteFile(line);\r\n } else {\r\n throw error;\r\n }\r\n }\r\n\r\n // File write succeeded - NOW update cache in-place (sanitized to prevent prototype pollution)\r\n Object.assign(entity, sanitizeObject(updates as Record<string, unknown>));\r\n entity.lastModified = timestamp;\r\n\r\n // Update indexes\r\n this.nameIndex.add(entity); // Update reference\r\n if (updates.entityType && updates.entityType !== oldType) {\r\n this.typeIndex.updateType(entityName, oldType, updates.entityType);\r\n }\r\n this.lowercaseCache.set(entity); // Recompute lowercase\r\n if (updates.observations) {\r\n this.observationIndex.remove(entityName); // Remove old observations\r\n this.observationIndex.add(entityName, entity.observations); // Add new observations\r\n }\r\n\r\n this.pendingAppends++;\r\n\r\n // Clear search caches\r\n clearAllSearchCaches();\r\n\r\n // Phase 10 Sprint 2: Emit entity:updated event\r\n this.eventEmitter.emitEntityUpdated(entityName, updates, previousValues);\r\n\r\n // Trigger compaction if threshold reached\r\n if (this.pendingAppends >= this.compactionThreshold) {\r\n await this.compactInternal();\r\n }\r\n\r\n return true;\r\n });\r\n }\r\n\r\n /**\r\n * Manually clear the cache.\r\n *\r\n * Useful for testing or when external processes modify the file.\r\n *\r\n * @returns void\r\n */\r\n clearCache(): void {\r\n this.cache = null;\r\n this.clearIndexes();\r\n }\r\n\r\n /**\r\n * Get the file path being used for storage.\r\n *\r\n * @returns The memory file path\r\n */\r\n getFilePath(): string {\r\n return this.memoryFilePath;\r\n }\r\n\r\n // ==================== Index Accessors ====================\r\n\r\n /**\r\n * Get an entity by name in O(1) time.\r\n *\r\n * OPTIMIZED: Uses NameIndex for constant-time lookup.\r\n *\r\n * @param name - Entity name to look up\r\n * @returns Entity if found, undefined otherwise\r\n */\r\n getEntityByName(name: string): Entity | undefined {\r\n return this.nameIndex.get(name);\r\n }\r\n\r\n /**\r\n * Check if an entity exists by name in O(1) time.\r\n *\r\n * @param name - Entity name to check\r\n * @returns True if entity exists\r\n */\r\n hasEntity(name: string): boolean {\r\n return this.nameIndex.has(name);\r\n }\r\n\r\n /**\r\n * Get all entities of a given type in O(1) time.\r\n *\r\n * OPTIMIZED: Uses TypeIndex for constant-time lookup of entity names,\r\n * then uses NameIndex for O(1) entity retrieval.\r\n *\r\n * @param entityType - Entity type to filter by (case-insensitive)\r\n * @returns Array of entities with the given type\r\n */\r\n getEntitiesByType(entityType: string): Entity[] {\r\n const names = this.typeIndex.getNames(entityType);\r\n const entities: Entity[] = [];\r\n for (const name of names) {\r\n const entity = this.nameIndex.get(name);\r\n if (entity) {\r\n entities.push(entity);\r\n }\r\n }\r\n return entities;\r\n }\r\n\r\n /**\r\n * Get pre-computed lowercase data for an entity.\r\n *\r\n * OPTIMIZED: Avoids repeated toLowerCase() calls during search.\r\n *\r\n * @param entityName - Entity name to get lowercase data for\r\n * @returns LowercaseData if entity exists, undefined otherwise\r\n */\r\n getLowercased(entityName: string): LowercaseData | undefined {\r\n return this.lowercaseCache.get(entityName);\r\n }\r\n\r\n /**\r\n * Get all unique entity types in the graph.\r\n *\r\n * @returns Array of unique entity types (lowercase)\r\n */\r\n getEntityTypes(): string[] {\r\n return this.typeIndex.getTypes();\r\n }\r\n\r\n // ==================== Relation Index Accessors ====================\r\n\r\n /**\r\n * Get all relations where the entity is the source (outgoing relations) in O(1) time.\r\n *\r\n * OPTIMIZED: Uses RelationIndex for constant-time lookup.\r\n *\r\n * @param entityName - Entity name to look up outgoing relations for\r\n * @returns Array of relations where entity is the source\r\n */\r\n getRelationsFrom(entityName: string): Relation[] {\r\n return this.relationIndex.getRelationsFrom(entityName);\r\n }\r\n\r\n /**\r\n * Get all relations where the entity is the target (incoming relations) in O(1) time.\r\n *\r\n * OPTIMIZED: Uses RelationIndex for constant-time lookup.\r\n *\r\n * @param entityName - Entity name to look up incoming relations for\r\n * @returns Array of relations where entity is the target\r\n */\r\n getRelationsTo(entityName: string): Relation[] {\r\n return this.relationIndex.getRelationsTo(entityName);\r\n }\r\n\r\n /**\r\n * Get all relations involving the entity (both incoming and outgoing) in O(1) time.\r\n *\r\n * OPTIMIZED: Uses RelationIndex for constant-time lookup.\r\n *\r\n * @param entityName - Entity name to look up all relations for\r\n * @returns Array of all relations involving the entity\r\n */\r\n getRelationsFor(entityName: string): Relation[] {\r\n return this.relationIndex.getRelationsFor(entityName);\r\n }\r\n\r\n /**\r\n * Check if an entity has any relations.\r\n *\r\n * @param entityName - Entity name to check\r\n * @returns True if entity has any relations\r\n */\r\n hasRelations(entityName: string): boolean {\r\n return this.relationIndex.hasRelations(entityName);\r\n }\r\n\r\n // ==================== Observation Index Accessors ====================\r\n\r\n /**\r\n * Get entities that have observations containing the given word.\r\n * Uses the observation index for O(1) lookup.\r\n *\r\n * OPTIMIZED: Uses ObservationIndex for constant-time lookup instead of\r\n * linear scan through all entities and their observations.\r\n *\r\n * @param word - Word to search for in observations\r\n * @returns Set of entity names\r\n */\r\n getEntitiesByObservationWord(word: string): Set<string> {\r\n return this.observationIndex.getEntitiesWithWord(word);\r\n }\r\n\r\n /**\r\n * Get entities that have observations containing ANY of the given words (union).\r\n * Uses the observation index for O(1) lookup per word.\r\n *\r\n * OPTIMIZED: Uses ObservationIndex for constant-time lookups.\r\n *\r\n * @param words - Array of words to search for\r\n * @returns Set of entity names containing any of the words\r\n */\r\n getEntitiesByAnyObservationWord(words: string[]): Set<string> {\r\n return this.observationIndex.getEntitiesWithAnyWord(words);\r\n }\r\n\r\n /**\r\n * Get entities that have observations containing ALL of the given words (intersection).\r\n * Uses the observation index for O(1) lookup per word.\r\n *\r\n * OPTIMIZED: Uses ObservationIndex for constant-time lookups and set intersection.\r\n *\r\n * @param words - Array of words that must all be present\r\n * @returns Set of entity names containing all of the words\r\n */\r\n getEntitiesByAllObservationWords(words: string[]): Set<string> {\r\n return this.observationIndex.getEntitiesWithAllWords(words);\r\n }\r\n\r\n /**\r\n * Get statistics about the observation index.\r\n *\r\n * @returns Object with wordCount and entityCount\r\n */\r\n getObservationIndexStats(): { wordCount: number; entityCount: number } {\r\n return this.observationIndex.getStats();\r\n }\r\n\r\n // ==================== Phase 10 Sprint 1: Transaction Factory ====================\r\n\r\n /**\r\n * Create a new batch transaction for atomic operations.\r\n *\r\n * Returns a BatchTransaction instance that can be used to queue multiple\r\n * operations and execute them atomically with a single save operation.\r\n *\r\n * @returns A new BatchTransaction instance\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/data/memory.jsonl');\r\n *\r\n * // Create and execute a batch transaction\r\n * const result = await storage.transaction()\r\n * .createEntity({ name: 'Alice', entityType: 'person', observations: ['Developer'] })\r\n * .createEntity({ name: 'Bob', entityType: 'person', observations: ['Designer'] })\r\n * .createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' })\r\n * .execute();\r\n *\r\n * console.log(`Batch completed: ${result.operationsExecuted} operations`);\r\n * ```\r\n */\r\n transaction(): BatchTransaction {\r\n return new BatchTransaction(this);\r\n }\r\n}\r\n","/**\r\n * IO Manager\r\n *\r\n * Unified manager for import, export, and backup operations.\r\n * Consolidates BackupManager, ExportManager, and ImportManager (Sprint 11.4).\r\n *\r\n * @module features/IOManager\r\n */\r\n\r\nimport { promises as fs } from 'fs';\r\nimport { dirname, join } from 'path';\r\nimport type {\r\n Entity,\r\n Relation,\r\n KnowledgeGraph,\r\n ReadonlyKnowledgeGraph,\r\n ImportResult,\r\n BackupOptions,\r\n BackupResult,\r\n RestoreResult,\r\n ExportOptions,\r\n ExportResult,\r\n LongRunningOperationOptions,\r\n} from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { FileOperationError } from '../utils/errors.js';\r\nimport {\r\n compress,\r\n decompress,\r\n hasBrotliExtension,\r\n COMPRESSION_CONFIG,\r\n STREAMING_CONFIG,\r\n checkCancellation,\r\n createProgressReporter,\r\n createProgress,\r\n validateFilePath,\r\n sanitizeObject,\r\n escapeCsvFormula,\r\n} from '../utils/index.js';\r\nimport { StreamingExporter, type StreamResult } from './StreamingExporter.js';\r\n\r\n// ============================================================\r\n// TYPE DEFINITIONS\r\n// ============================================================\r\n\r\n/**\r\n * Supported export formats.\r\n */\r\nexport type ExportFormat = 'json' | 'csv' | 'graphml' | 'gexf' | 'dot' | 'markdown' | 'mermaid';\r\n\r\n/**\r\n * Supported import formats.\r\n */\r\nexport type ImportFormat = 'json' | 'csv' | 'graphml';\r\n\r\n/**\r\n * Merge strategies for handling existing entities during import.\r\n */\r\nexport type MergeStrategy = 'replace' | 'skip' | 'merge' | 'fail';\r\n\r\n/**\r\n * Metadata stored with each backup.\r\n * Extended with compression information for Phase 3 Sprint 2.\r\n */\r\nexport interface BackupMetadata {\r\n /** Timestamp when backup was created (ISO 8601) */\r\n timestamp: string;\r\n /** Number of entities in the backup */\r\n entityCount: number;\r\n /** Number of relations in the backup */\r\n relationCount: number;\r\n /** File size in bytes (compressed size if compressed) */\r\n fileSize: number;\r\n /** Optional description/reason for backup */\r\n description?: string;\r\n /** Whether the backup is compressed (default: true for new backups) */\r\n compressed?: boolean;\r\n /** Original size before compression in bytes */\r\n originalSize?: number;\r\n /** Compression ratio achieved (compressedSize / originalSize) */\r\n compressionRatio?: number;\r\n /** Compression format used */\r\n compressionFormat?: 'brotli' | 'none';\r\n}\r\n\r\n/**\r\n * Information about a backup file.\r\n * Extended with compression details for Phase 3 Sprint 2.\r\n */\r\nexport interface BackupInfo {\r\n /** Backup file name */\r\n fileName: string;\r\n /** Full path to backup file */\r\n filePath: string;\r\n /** Backup metadata */\r\n metadata: BackupMetadata;\r\n /** Whether the backup is compressed */\r\n compressed: boolean;\r\n /** File size in bytes */\r\n size: number;\r\n}\r\n\r\n// ============================================================\r\n// IO MANAGER CLASS\r\n// ============================================================\r\n\r\n/**\r\n * Unified manager for import, export, and backup operations.\r\n *\r\n * Combines functionality from:\r\n * - ExportManager: Graph export to various formats\r\n * - ImportManager: Graph import from various formats\r\n * - BackupManager: Point-in-time backup and restore\r\n */\r\nexport class IOManager {\r\n private readonly backupDir: string;\r\n\r\n constructor(private storage: GraphStorage) {\r\n const filePath = this.storage.getFilePath();\r\n const dir = dirname(filePath);\r\n this.backupDir = join(dir, '.backups');\r\n }\r\n\r\n // ============================================================\r\n // EXPORT OPERATIONS\r\n // ============================================================\r\n\r\n /**\r\n * Export graph to specified format.\r\n *\r\n * @param graph - Knowledge graph to export\r\n * @param format - Export format\r\n * @returns Formatted export string\r\n */\r\n exportGraph(graph: ReadonlyKnowledgeGraph, format: ExportFormat): string {\r\n switch (format) {\r\n case 'json':\r\n return this.exportAsJson(graph);\r\n case 'csv':\r\n return this.exportAsCsv(graph);\r\n case 'graphml':\r\n return this.exportAsGraphML(graph);\r\n case 'gexf':\r\n return this.exportAsGEXF(graph);\r\n case 'dot':\r\n return this.exportAsDOT(graph);\r\n case 'markdown':\r\n return this.exportAsMarkdown(graph);\r\n case 'mermaid':\r\n return this.exportAsMermaid(graph);\r\n default:\r\n throw new Error(`Unsupported export format: ${format}`);\r\n }\r\n }\r\n\r\n /**\r\n * Export graph with optional brotli compression.\r\n *\r\n * Compression is applied when:\r\n * - `options.compress` is explicitly set to `true`\r\n * - The exported content exceeds 100KB (auto-compress threshold)\r\n *\r\n * Compressed content is returned as base64-encoded string.\r\n * Uncompressed content is returned as UTF-8 string.\r\n *\r\n * @param graph - Knowledge graph to export\r\n * @param format - Export format\r\n * @param options - Export options including compression settings\r\n * @returns Export result with content and compression metadata\r\n *\r\n * @example\r\n * ```typescript\r\n * // Export with explicit compression\r\n * const result = await manager.exportGraphWithCompression(graph, 'json', {\r\n * compress: true,\r\n * compressionQuality: 11\r\n * });\r\n *\r\n * // Export with auto-compression for large graphs\r\n * const result = await manager.exportGraphWithCompression(graph, 'json');\r\n * // Compresses automatically if content > 100KB\r\n * ```\r\n */\r\n async exportGraphWithCompression(\r\n graph: ReadonlyKnowledgeGraph,\r\n format: ExportFormat,\r\n options?: ExportOptions\r\n ): Promise<ExportResult> {\r\n // Check if streaming should be used\r\n const shouldStream = options?.streaming ||\r\n (options?.outputPath && graph.entities.length >= STREAMING_CONFIG.STREAMING_THRESHOLD);\r\n\r\n if (shouldStream && options?.outputPath) {\r\n return this.streamExport(format, graph, options as ExportOptions & { outputPath: string });\r\n }\r\n\r\n // Generate export content using existing method\r\n const content = this.exportGraph(graph, format);\r\n const originalSize = Buffer.byteLength(content, 'utf-8');\r\n\r\n // Determine if compression should be applied\r\n const shouldCompress =\r\n options?.compress === true ||\r\n (options?.compress !== false &&\r\n originalSize > COMPRESSION_CONFIG.AUTO_COMPRESS_EXPORT_SIZE);\r\n\r\n if (shouldCompress) {\r\n const quality =\r\n options?.compressionQuality ?? COMPRESSION_CONFIG.BROTLI_QUALITY_BATCH;\r\n\r\n const compressionResult = await compress(content, {\r\n quality,\r\n mode: 'text',\r\n });\r\n\r\n return {\r\n format,\r\n content: compressionResult.compressed.toString('base64'),\r\n entityCount: graph.entities.length,\r\n relationCount: graph.relations.length,\r\n compressed: true,\r\n encoding: 'base64',\r\n originalSize,\r\n compressedSize: compressionResult.compressedSize,\r\n compressionRatio: compressionResult.ratio,\r\n };\r\n }\r\n\r\n // Return uncompressed content\r\n return {\r\n format,\r\n content,\r\n entityCount: graph.entities.length,\r\n relationCount: graph.relations.length,\r\n compressed: false,\r\n encoding: 'utf-8',\r\n originalSize,\r\n compressedSize: originalSize,\r\n compressionRatio: 1,\r\n };\r\n }\r\n\r\n /**\r\n * Stream export to a file for large graphs.\r\n *\r\n * Uses StreamingExporter to write entities and relations incrementally\r\n * to avoid loading the entire export content into memory.\r\n *\r\n * @param format - Export format\r\n * @param graph - Knowledge graph to export\r\n * @param options - Export options with required outputPath\r\n * @returns Export result with streaming metadata\r\n * @private\r\n */\r\n private async streamExport(\r\n format: ExportFormat,\r\n graph: ReadonlyKnowledgeGraph,\r\n options: ExportOptions & { outputPath: string }\r\n ): Promise<ExportResult> {\r\n // Validate path to prevent path traversal attacks (defense in depth)\r\n const validatedOutputPath = validateFilePath(options.outputPath);\r\n const exporter = new StreamingExporter(validatedOutputPath);\r\n let result: StreamResult;\r\n\r\n switch (format) {\r\n case 'json':\r\n // Use JSONL format for streaming (line-delimited JSON)\r\n result = await exporter.streamJSONL(graph);\r\n break;\r\n case 'csv':\r\n result = await exporter.streamCSV(graph);\r\n break;\r\n default:\r\n // Fallback to in-memory export for unsupported streaming formats\r\n const content = this.exportGraph(graph, format);\r\n await fs.writeFile(validatedOutputPath, content);\r\n result = {\r\n bytesWritten: Buffer.byteLength(content, 'utf-8'),\r\n entitiesWritten: graph.entities.length,\r\n relationsWritten: graph.relations.length,\r\n durationMs: 0,\r\n };\r\n }\r\n\r\n return {\r\n format,\r\n content: `Streamed to ${validatedOutputPath}`,\r\n entityCount: result.entitiesWritten,\r\n relationCount: result.relationsWritten,\r\n compressed: false,\r\n encoding: 'utf-8',\r\n originalSize: result.bytesWritten,\r\n compressedSize: result.bytesWritten,\r\n compressionRatio: 1,\r\n streamed: true,\r\n outputPath: validatedOutputPath,\r\n };\r\n }\r\n\r\n private exportAsJson(graph: ReadonlyKnowledgeGraph): string {\r\n return JSON.stringify(graph, null, 2);\r\n }\r\n\r\n private exportAsCsv(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n const escapeCsvField = (field: string | undefined | null): string => {\r\n if (field === undefined || field === null) return '';\r\n // First protect against CSV formula injection\r\n let str = escapeCsvFormula(String(field));\r\n // Then handle CSV special characters\r\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\r\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\r\n }\r\n return str;\r\n };\r\n\r\n lines.push('# ENTITIES');\r\n lines.push('name,entityType,observations,createdAt,lastModified,tags,importance');\r\n\r\n for (const entity of graph.entities) {\r\n const observationsStr = entity.observations.join('; ');\r\n const tagsStr = entity.tags ? entity.tags.join('; ') : '';\r\n const importanceStr = entity.importance !== undefined ? String(entity.importance) : '';\r\n\r\n lines.push(\r\n [\r\n escapeCsvField(entity.name),\r\n escapeCsvField(entity.entityType),\r\n escapeCsvField(observationsStr),\r\n escapeCsvField(entity.createdAt),\r\n escapeCsvField(entity.lastModified),\r\n escapeCsvField(tagsStr),\r\n escapeCsvField(importanceStr),\r\n ].join(',')\r\n );\r\n }\r\n\r\n lines.push('');\r\n lines.push('# RELATIONS');\r\n lines.push('from,to,relationType,createdAt,lastModified');\r\n\r\n for (const relation of graph.relations) {\r\n lines.push(\r\n [\r\n escapeCsvField(relation.from),\r\n escapeCsvField(relation.to),\r\n escapeCsvField(relation.relationType),\r\n escapeCsvField(relation.createdAt),\r\n escapeCsvField(relation.lastModified),\r\n ].join(',')\r\n );\r\n }\r\n\r\n return lines.join('\\n');\r\n }\r\n\r\n private exportAsGraphML(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n const escapeXml = (str: string | undefined | null): string => {\r\n if (str === undefined || str === null) return '';\r\n return String(str)\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n };\r\n\r\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\r\n lines.push('<graphml xmlns=\"http://graphml.graphdrawing.org/xmlns\">');\r\n lines.push(' <key id=\"d0\" for=\"node\" attr.name=\"entityType\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"d1\" for=\"node\" attr.name=\"observations\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"d2\" for=\"node\" attr.name=\"createdAt\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"d3\" for=\"node\" attr.name=\"lastModified\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"d4\" for=\"node\" attr.name=\"tags\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"d5\" for=\"node\" attr.name=\"importance\" attr.type=\"double\"/>');\r\n lines.push(' <key id=\"e0\" for=\"edge\" attr.name=\"relationType\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"e1\" for=\"edge\" attr.name=\"createdAt\" attr.type=\"string\"/>');\r\n lines.push(' <key id=\"e2\" for=\"edge\" attr.name=\"lastModified\" attr.type=\"string\"/>');\r\n lines.push(' <graph id=\"G\" edgedefault=\"directed\">');\r\n\r\n for (const entity of graph.entities) {\r\n const nodeId = escapeXml(entity.name);\r\n lines.push(` <node id=\"${nodeId}\">`);\r\n lines.push(` <data key=\"d0\">${escapeXml(entity.entityType)}</data>`);\r\n lines.push(` <data key=\"d1\">${escapeXml(entity.observations.join('; '))}</data>`);\r\n if (entity.createdAt) lines.push(` <data key=\"d2\">${escapeXml(entity.createdAt)}</data>`);\r\n if (entity.lastModified) lines.push(` <data key=\"d3\">${escapeXml(entity.lastModified)}</data>`);\r\n if (entity.tags?.length) lines.push(` <data key=\"d4\">${escapeXml(entity.tags.join('; '))}</data>`);\r\n if (entity.importance !== undefined) lines.push(` <data key=\"d5\">${entity.importance}</data>`);\r\n lines.push(' </node>');\r\n }\r\n\r\n let edgeId = 0;\r\n for (const relation of graph.relations) {\r\n const sourceId = escapeXml(relation.from);\r\n const targetId = escapeXml(relation.to);\r\n lines.push(` <edge id=\"e${edgeId}\" source=\"${sourceId}\" target=\"${targetId}\">`);\r\n lines.push(` <data key=\"e0\">${escapeXml(relation.relationType)}</data>`);\r\n if (relation.createdAt) lines.push(` <data key=\"e1\">${escapeXml(relation.createdAt)}</data>`);\r\n if (relation.lastModified) lines.push(` <data key=\"e2\">${escapeXml(relation.lastModified)}</data>`);\r\n lines.push(' </edge>');\r\n edgeId++;\r\n }\r\n\r\n lines.push(' </graph>');\r\n lines.push('</graphml>');\r\n return lines.join('\\n');\r\n }\r\n\r\n private exportAsGEXF(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n const escapeXml = (str: string | undefined | null): string => {\r\n if (str === undefined || str === null) return '';\r\n return String(str)\r\n .replace(/&/g, '&')\r\n .replace(/</g, '<')\r\n .replace(/>/g, '>')\r\n .replace(/\"/g, '"')\r\n .replace(/'/g, ''');\r\n };\r\n\r\n lines.push('<?xml version=\"1.0\" encoding=\"UTF-8\"?>');\r\n lines.push('<gexf xmlns=\"http://www.gexf.net/1.2draft\" version=\"1.2\">');\r\n lines.push(' <meta>');\r\n lines.push(' <creator>Memory MCP Server</creator>');\r\n lines.push(' </meta>');\r\n lines.push(' <graph mode=\"static\" defaultedgetype=\"directed\">');\r\n lines.push(' <attributes class=\"node\">');\r\n lines.push(' <attribute id=\"0\" title=\"entityType\" type=\"string\"/>');\r\n lines.push(' <attribute id=\"1\" title=\"observations\" type=\"string\"/>');\r\n lines.push(' </attributes>');\r\n lines.push(' <nodes>');\r\n\r\n for (const entity of graph.entities) {\r\n const nodeId = escapeXml(entity.name);\r\n lines.push(` <node id=\"${nodeId}\" label=\"${nodeId}\">`);\r\n lines.push(' <attvalues>');\r\n lines.push(` <attvalue for=\"0\" value=\"${escapeXml(entity.entityType)}\"/>`);\r\n lines.push(` <attvalue for=\"1\" value=\"${escapeXml(entity.observations.join('; '))}\"/>`);\r\n lines.push(' </attvalues>');\r\n lines.push(' </node>');\r\n }\r\n\r\n lines.push(' </nodes>');\r\n lines.push(' <edges>');\r\n\r\n let edgeId = 0;\r\n for (const relation of graph.relations) {\r\n const sourceId = escapeXml(relation.from);\r\n const targetId = escapeXml(relation.to);\r\n const label = escapeXml(relation.relationType);\r\n lines.push(` <edge id=\"${edgeId}\" source=\"${sourceId}\" target=\"${targetId}\" label=\"${label}\"/>`);\r\n edgeId++;\r\n }\r\n\r\n lines.push(' </edges>');\r\n lines.push(' </graph>');\r\n lines.push('</gexf>');\r\n return lines.join('\\n');\r\n }\r\n\r\n private exportAsDOT(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n const escapeDot = (str: string): string => {\r\n return '\"' + str.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\r\n };\r\n\r\n lines.push('digraph KnowledgeGraph {');\r\n lines.push(' rankdir=LR;');\r\n lines.push(' node [shape=box, style=rounded];');\r\n lines.push('');\r\n\r\n for (const entity of graph.entities) {\r\n const nodeId = escapeDot(entity.name);\r\n const label = [`${entity.name}`, `Type: ${entity.entityType}`];\r\n if (entity.tags?.length) label.push(`Tags: ${entity.tags.join(', ')}`);\r\n const labelStr = escapeDot(label.join('\\\\n'));\r\n lines.push(` ${nodeId} [label=${labelStr}];`);\r\n }\r\n\r\n lines.push('');\r\n\r\n for (const relation of graph.relations) {\r\n const fromId = escapeDot(relation.from);\r\n const toId = escapeDot(relation.to);\r\n const label = escapeDot(relation.relationType);\r\n lines.push(` ${fromId} -> ${toId} [label=${label}];`);\r\n }\r\n\r\n lines.push('}');\r\n return lines.join('\\n');\r\n }\r\n\r\n private exportAsMarkdown(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n lines.push('# Knowledge Graph Export');\r\n lines.push('');\r\n lines.push(`**Exported:** ${new Date().toISOString()}`);\r\n lines.push(`**Entities:** ${graph.entities.length}`);\r\n lines.push(`**Relations:** ${graph.relations.length}`);\r\n lines.push('');\r\n lines.push('## Entities');\r\n lines.push('');\r\n\r\n for (const entity of graph.entities) {\r\n lines.push(`### ${entity.name}`);\r\n lines.push('');\r\n lines.push(`- **Type:** ${entity.entityType}`);\r\n if (entity.tags?.length) lines.push(`- **Tags:** ${entity.tags.map(t => `\\`${t}\\``).join(', ')}`);\r\n if (entity.importance !== undefined) lines.push(`- **Importance:** ${entity.importance}/10`);\r\n if (entity.observations.length > 0) {\r\n lines.push('');\r\n lines.push('**Observations:**');\r\n for (const obs of entity.observations) {\r\n lines.push(`- ${obs}`);\r\n }\r\n }\r\n lines.push('');\r\n }\r\n\r\n if (graph.relations.length > 0) {\r\n lines.push('## Relations');\r\n lines.push('');\r\n for (const relation of graph.relations) {\r\n lines.push(`- **${relation.from}** → *${relation.relationType}* → **${relation.to}**`);\r\n }\r\n lines.push('');\r\n }\r\n\r\n return lines.join('\\n');\r\n }\r\n\r\n private exportAsMermaid(graph: ReadonlyKnowledgeGraph): string {\r\n const lines: string[] = [];\r\n\r\n const sanitizeId = (str: string): string => str.replace(/[^a-zA-Z0-9_]/g, '_');\r\n const escapeLabel = (str: string): string => str.replace(/\"/g, '#quot;');\r\n\r\n lines.push('graph LR');\r\n lines.push(' %% Knowledge Graph');\r\n lines.push('');\r\n\r\n const nodeIds = new Map<string, string>();\r\n for (const entity of graph.entities) {\r\n nodeIds.set(entity.name, sanitizeId(entity.name));\r\n }\r\n\r\n for (const entity of graph.entities) {\r\n const nodeId = nodeIds.get(entity.name)!;\r\n const labelParts: string[] = [entity.name, `Type: ${entity.entityType}`];\r\n if (entity.tags?.length) labelParts.push(`Tags: ${entity.tags.join(', ')}`);\r\n const label = escapeLabel(labelParts.join('<br/>'));\r\n lines.push(` ${nodeId}[\"${label}\"]`);\r\n }\r\n\r\n lines.push('');\r\n\r\n for (const relation of graph.relations) {\r\n const fromId = nodeIds.get(relation.from);\r\n const toId = nodeIds.get(relation.to);\r\n if (fromId && toId) {\r\n const label = escapeLabel(relation.relationType);\r\n lines.push(` ${fromId} -->|\"${label}\"| ${toId}`);\r\n }\r\n }\r\n\r\n return lines.join('\\n');\r\n }\r\n\r\n // ============================================================\r\n // IMPORT OPERATIONS\r\n // ============================================================\r\n\r\n /**\r\n * Import graph from formatted data.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param format - Import format\r\n * @param data - Import data string\r\n * @param mergeStrategy - How to handle conflicts\r\n * @param dryRun - If true, preview changes without applying\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Import result with statistics\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n */\r\n async importGraph(\r\n format: ImportFormat,\r\n data: string,\r\n mergeStrategy: MergeStrategy = 'skip',\r\n dryRun: boolean = false,\r\n options?: LongRunningOperationOptions\r\n ): Promise<ImportResult> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'importGraph');\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n reportProgress?.(createProgress(0, 100, 'importGraph'));\r\n\r\n let importedGraph: KnowledgeGraph;\r\n\r\n try {\r\n // Parsing phase (0-20% progress)\r\n reportProgress?.(createProgress(5, 100, 'parsing data'));\r\n checkCancellation(options?.signal, 'importGraph');\r\n\r\n switch (format) {\r\n case 'json':\r\n importedGraph = this.parseJsonImport(data);\r\n break;\r\n case 'csv':\r\n importedGraph = this.parseCsvImport(data);\r\n break;\r\n case 'graphml':\r\n importedGraph = this.parseGraphMLImport(data);\r\n break;\r\n default:\r\n throw new Error(`Unsupported import format: ${format}`);\r\n }\r\n\r\n reportProgress?.(createProgress(20, 100, 'parsing complete'));\r\n } catch (error) {\r\n return {\r\n entitiesAdded: 0,\r\n entitiesSkipped: 0,\r\n entitiesUpdated: 0,\r\n relationsAdded: 0,\r\n relationsSkipped: 0,\r\n errors: [`Failed to parse ${format} data: ${error instanceof Error ? error.message : String(error)}`],\r\n };\r\n }\r\n\r\n // Merging phase (20-100% progress)\r\n return await this.mergeImportedGraph(importedGraph, mergeStrategy, dryRun, options);\r\n }\r\n\r\n private parseJsonImport(data: string): KnowledgeGraph {\r\n // Security: Limit input size to prevent DoS (10MB max)\r\n const MAX_IMPORT_SIZE = 10 * 1024 * 1024;\r\n if (data.length > MAX_IMPORT_SIZE) {\r\n throw new FileOperationError(\r\n `JSON import data exceeds maximum size of ${MAX_IMPORT_SIZE / (1024 * 1024)}MB`,\r\n 'json-import'\r\n );\r\n }\r\n\r\n const parsed = JSON.parse(data);\r\n\r\n if (!parsed.entities || !Array.isArray(parsed.entities)) {\r\n throw new Error('Invalid JSON: missing or invalid entities array');\r\n }\r\n if (!parsed.relations || !Array.isArray(parsed.relations)) {\r\n throw new Error('Invalid JSON: missing or invalid relations array');\r\n }\r\n\r\n // Security: Limit maximum number of entities/relations\r\n const MAX_ITEMS = 100000;\r\n if (parsed.entities.length > MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `JSON import exceeds maximum entity count of ${MAX_ITEMS}`,\r\n 'json-import'\r\n );\r\n }\r\n if (parsed.relations.length > MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `JSON import exceeds maximum relation count of ${MAX_ITEMS}`,\r\n 'json-import'\r\n );\r\n }\r\n\r\n return {\r\n entities: parsed.entities as Entity[],\r\n relations: parsed.relations as Relation[],\r\n };\r\n }\r\n\r\n private parseCsvImport(data: string): KnowledgeGraph {\r\n // Security: Limit input size to prevent DoS (10MB max)\r\n const MAX_IMPORT_SIZE = 10 * 1024 * 1024;\r\n if (data.length > MAX_IMPORT_SIZE) {\r\n throw new FileOperationError(\r\n `CSV import data exceeds maximum size of ${MAX_IMPORT_SIZE / (1024 * 1024)}MB`,\r\n 'csv-import'\r\n );\r\n }\r\n\r\n // Security: Limit maximum number of entities/relations\r\n const MAX_ITEMS = 100000;\r\n\r\n const lines = data\r\n .split('\\n')\r\n .map(line => line.trim())\r\n .filter(line => line);\r\n const entities: Entity[] = [];\r\n const relations: Relation[] = [];\r\n\r\n let section: 'entities' | 'relations' | null = null;\r\n let headerParsed = false;\r\n\r\n const parseCsvLine = (line: string): string[] => {\r\n const fields: string[] = [];\r\n let current = '';\r\n let inQuotes = false;\r\n\r\n for (let i = 0; i < line.length; i++) {\r\n const char = line[i];\r\n\r\n if (char === '\"') {\r\n if (inQuotes && line[i + 1] === '\"') {\r\n current += '\"';\r\n i++;\r\n } else {\r\n inQuotes = !inQuotes;\r\n }\r\n } else if (char === ',' && !inQuotes) {\r\n fields.push(current);\r\n current = '';\r\n } else {\r\n current += char;\r\n }\r\n }\r\n\r\n fields.push(current);\r\n return fields;\r\n };\r\n\r\n for (const line of lines) {\r\n if (line.startsWith('# ENTITIES')) {\r\n section = 'entities';\r\n headerParsed = false;\r\n continue;\r\n } else if (line.startsWith('# RELATIONS')) {\r\n section = 'relations';\r\n headerParsed = false;\r\n continue;\r\n }\r\n\r\n if (line.startsWith('#')) continue;\r\n\r\n if (section === 'entities') {\r\n if (!headerParsed) {\r\n headerParsed = true;\r\n continue;\r\n }\r\n\r\n const fields = parseCsvLine(line);\r\n if (fields.length >= 2) {\r\n // Security: Check entity limit\r\n if (entities.length >= MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `CSV import exceeds maximum entity count of ${MAX_ITEMS}`,\r\n 'csv-import'\r\n );\r\n }\r\n const entity: Entity = {\r\n name: fields[0],\r\n entityType: fields[1],\r\n observations: fields[2]\r\n ? fields[2]\r\n .split(';')\r\n .map(s => s.trim())\r\n .filter(s => s)\r\n : [],\r\n createdAt: fields[3] || undefined,\r\n lastModified: fields[4] || undefined,\r\n tags: fields[5]\r\n ? fields[5]\r\n .split(';')\r\n .map(s => s.trim().toLowerCase())\r\n .filter(s => s)\r\n : undefined,\r\n importance: fields[6] ? parseFloat(fields[6]) : undefined,\r\n };\r\n entities.push(entity);\r\n }\r\n } else if (section === 'relations') {\r\n if (!headerParsed) {\r\n headerParsed = true;\r\n continue;\r\n }\r\n\r\n const fields = parseCsvLine(line);\r\n if (fields.length >= 3) {\r\n // Security: Check relation limit\r\n if (relations.length >= MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `CSV import exceeds maximum relation count of ${MAX_ITEMS}`,\r\n 'csv-import'\r\n );\r\n }\r\n const relation: Relation = {\r\n from: fields[0],\r\n to: fields[1],\r\n relationType: fields[2],\r\n createdAt: fields[3] || undefined,\r\n lastModified: fields[4] || undefined,\r\n };\r\n relations.push(relation);\r\n }\r\n }\r\n }\r\n\r\n return { entities, relations };\r\n }\r\n\r\n private parseGraphMLImport(data: string): KnowledgeGraph {\r\n const entities: Entity[] = [];\r\n const relations: Relation[] = [];\r\n\r\n // Security: Limit input size to prevent ReDoS attacks (10MB max)\r\n const MAX_IMPORT_SIZE = 10 * 1024 * 1024;\r\n if (data.length > MAX_IMPORT_SIZE) {\r\n throw new FileOperationError(\r\n `GraphML import data exceeds maximum size of ${MAX_IMPORT_SIZE / (1024 * 1024)}MB`,\r\n 'graphml-import'\r\n );\r\n }\r\n\r\n // Security: Limit maximum number of entities/relations to prevent infinite loops\r\n const MAX_ITEMS = 100000;\r\n let nodeCount = 0;\r\n let relationCount = 0;\r\n\r\n // Use non-greedy patterns with character class restrictions\r\n const nodeRegex = /<node\\s+id=\"([^\"]+)\"[^>]*>([\\s\\S]*?)<\\/node>/g;\r\n let nodeMatch;\r\n\r\n while ((nodeMatch = nodeRegex.exec(data)) !== null) {\r\n // Security: Limit iterations to prevent ReDoS\r\n if (++nodeCount > MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `GraphML import exceeds maximum entity count of ${MAX_ITEMS}`,\r\n 'graphml-import'\r\n );\r\n }\r\n const nodeId = nodeMatch[1];\r\n const nodeContent = nodeMatch[2];\r\n\r\n const getDataValue = (key: string): string | undefined => {\r\n const dataRegex = new RegExp(`<data\\\\s+key=\"${key}\">([^<]*)<\\/data>`);\r\n const match = dataRegex.exec(nodeContent);\r\n return match ? match[1] : undefined;\r\n };\r\n\r\n const entity: Entity = {\r\n name: nodeId,\r\n entityType: getDataValue('d0') || getDataValue('entityType') || 'unknown',\r\n observations: (getDataValue('d1') || getDataValue('observations') || '')\r\n .split(';')\r\n .map(s => s.trim())\r\n .filter(s => s),\r\n createdAt: getDataValue('d2') || getDataValue('createdAt'),\r\n lastModified: getDataValue('d3') || getDataValue('lastModified'),\r\n tags: (getDataValue('d4') || getDataValue('tags') || '')\r\n .split(';')\r\n .map(s => s.trim().toLowerCase())\r\n .filter(s => s),\r\n importance: getDataValue('d5') || getDataValue('importance') ? parseFloat(getDataValue('d5') || getDataValue('importance') || '0') : undefined,\r\n };\r\n\r\n entities.push(entity);\r\n }\r\n\r\n const edgeRegex = /<edge\\s+[^>]*source=\"([^\"]+)\"\\s+target=\"([^\"]+)\"[^>]*>([\\s\\S]*?)<\\/edge>/g;\r\n let edgeMatch;\r\n\r\n while ((edgeMatch = edgeRegex.exec(data)) !== null) {\r\n // Security: Limit iterations to prevent ReDoS\r\n if (++relationCount > MAX_ITEMS) {\r\n throw new FileOperationError(\r\n `GraphML import exceeds maximum relation count of ${MAX_ITEMS}`,\r\n 'graphml-import'\r\n );\r\n }\r\n const source = edgeMatch[1];\r\n const target = edgeMatch[2];\r\n const edgeContent = edgeMatch[3];\r\n\r\n const getDataValue = (key: string): string | undefined => {\r\n const dataRegex = new RegExp(`<data\\\\s+key=\"${key}\">([^<]*)<\\/data>`);\r\n const match = dataRegex.exec(edgeContent);\r\n return match ? match[1] : undefined;\r\n };\r\n\r\n const relation: Relation = {\r\n from: source,\r\n to: target,\r\n relationType: getDataValue('e0') || getDataValue('relationType') || 'related_to',\r\n createdAt: getDataValue('e1') || getDataValue('createdAt'),\r\n lastModified: getDataValue('e2') || getDataValue('lastModified'),\r\n };\r\n\r\n relations.push(relation);\r\n }\r\n\r\n return { entities, relations };\r\n }\r\n\r\n private async mergeImportedGraph(\r\n importedGraph: KnowledgeGraph,\r\n mergeStrategy: MergeStrategy,\r\n dryRun: boolean,\r\n options?: LongRunningOperationOptions\r\n ): Promise<ImportResult> {\r\n // Check for cancellation\r\n checkCancellation(options?.signal, 'importGraph');\r\n\r\n // Setup progress reporter (we're at 20% from parsing, need to go to 100%)\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n\r\n const existingGraph = await this.storage.getGraphForMutation();\r\n const result: ImportResult = {\r\n entitiesAdded: 0,\r\n entitiesSkipped: 0,\r\n entitiesUpdated: 0,\r\n relationsAdded: 0,\r\n relationsSkipped: 0,\r\n errors: [],\r\n };\r\n\r\n const existingEntitiesMap = new Map<string, Entity>();\r\n for (const entity of existingGraph.entities) {\r\n existingEntitiesMap.set(entity.name, entity);\r\n }\r\n\r\n const existingRelationsSet = new Set<string>();\r\n for (const relation of existingGraph.relations) {\r\n existingRelationsSet.add(`${relation.from}|${relation.to}|${relation.relationType}`);\r\n }\r\n\r\n // Process entities (20-60% progress)\r\n const totalEntities = importedGraph.entities.length;\r\n const totalRelations = importedGraph.relations.length;\r\n let processedEntities = 0;\r\n\r\n for (const importedEntity of importedGraph.entities) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'importGraph');\r\n\r\n const existing = existingEntitiesMap.get(importedEntity.name);\r\n\r\n if (!existing) {\r\n result.entitiesAdded++;\r\n if (!dryRun) {\r\n existingGraph.entities.push(importedEntity);\r\n existingEntitiesMap.set(importedEntity.name, importedEntity);\r\n }\r\n } else {\r\n switch (mergeStrategy) {\r\n case 'replace':\r\n result.entitiesUpdated++;\r\n if (!dryRun) {\r\n // Sanitize imported entity to prevent prototype pollution\r\n Object.assign(existing, sanitizeObject(importedEntity as unknown as Record<string, unknown>));\r\n }\r\n break;\r\n\r\n case 'skip':\r\n result.entitiesSkipped++;\r\n break;\r\n\r\n case 'merge':\r\n result.entitiesUpdated++;\r\n if (!dryRun) {\r\n existing.observations = [\r\n ...new Set([...existing.observations, ...importedEntity.observations]),\r\n ];\r\n if (importedEntity.tags) {\r\n existing.tags = existing.tags || [];\r\n existing.tags = [...new Set([...existing.tags, ...importedEntity.tags])];\r\n }\r\n if (importedEntity.importance !== undefined) {\r\n existing.importance = importedEntity.importance;\r\n }\r\n existing.lastModified = new Date().toISOString();\r\n }\r\n break;\r\n\r\n case 'fail':\r\n result.errors.push(`Entity \"${importedEntity.name}\" already exists`);\r\n break;\r\n }\r\n }\r\n\r\n processedEntities++;\r\n // Map entity progress (0-100%) to overall progress (20-60%)\r\n const entityProgress = totalEntities > 0 ? Math.round(20 + (processedEntities / totalEntities) * 40) : 60;\r\n reportProgress?.(createProgress(entityProgress, 100, 'importing entities'));\r\n }\r\n\r\n reportProgress?.(createProgress(60, 100, 'importing relations'));\r\n\r\n // Process relations (60-95% progress)\r\n let processedRelations = 0;\r\n\r\n for (const importedRelation of importedGraph.relations) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'importGraph');\r\n\r\n const relationKey = `${importedRelation.from}|${importedRelation.to}|${importedRelation.relationType}`;\r\n\r\n if (!existingEntitiesMap.has(importedRelation.from)) {\r\n result.errors.push(`Relation source entity \"${importedRelation.from}\" does not exist`);\r\n processedRelations++;\r\n continue;\r\n }\r\n if (!existingEntitiesMap.has(importedRelation.to)) {\r\n result.errors.push(`Relation target entity \"${importedRelation.to}\" does not exist`);\r\n processedRelations++;\r\n continue;\r\n }\r\n\r\n if (!existingRelationsSet.has(relationKey)) {\r\n result.relationsAdded++;\r\n if (!dryRun) {\r\n existingGraph.relations.push(importedRelation);\r\n existingRelationsSet.add(relationKey);\r\n }\r\n } else {\r\n if (mergeStrategy === 'fail') {\r\n result.errors.push(`Relation \"${relationKey}\" already exists`);\r\n } else {\r\n result.relationsSkipped++;\r\n }\r\n }\r\n\r\n processedRelations++;\r\n // Map relation progress (0-100%) to overall progress (60-95%)\r\n const relationProgress = totalRelations > 0 ? Math.round(60 + (processedRelations / totalRelations) * 35) : 95;\r\n reportProgress?.(createProgress(relationProgress, 100, 'importing relations'));\r\n }\r\n\r\n // Check for cancellation before final save\r\n checkCancellation(options?.signal, 'importGraph');\r\n reportProgress?.(createProgress(95, 100, 'saving graph'));\r\n\r\n if (!dryRun && (mergeStrategy !== 'fail' || result.errors.length === 0)) {\r\n await this.storage.saveGraph(existingGraph);\r\n }\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(100, 100, 'importGraph'));\r\n\r\n return result;\r\n }\r\n\r\n // ============================================================\r\n // BACKUP OPERATIONS\r\n // ============================================================\r\n\r\n /**\r\n * Ensure backup directory exists.\r\n */\r\n private async ensureBackupDir(): Promise<void> {\r\n try {\r\n await fs.mkdir(this.backupDir, { recursive: true });\r\n } catch (error) {\r\n throw new FileOperationError('create backup directory', this.backupDir, error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * Generate backup file name with timestamp.\r\n * @param compressed - Whether the backup will be compressed (affects extension)\r\n */\r\n private generateBackupFileName(compressed: boolean = true): string {\r\n const now = new Date();\r\n const timestamp = now.toISOString()\r\n .replace(/:/g, '-')\r\n .replace(/\\./g, '-')\r\n .replace('T', '_')\r\n .replace('Z', '');\r\n const extension = compressed ? '.jsonl.br' : '.jsonl';\r\n return `backup_${timestamp}${extension}`;\r\n }\r\n\r\n /**\r\n * Create a backup of the current knowledge graph.\r\n *\r\n * By default, backups are compressed with brotli for 50-70% space reduction.\r\n * Use `options.compress = false` to create uncompressed backups.\r\n *\r\n * @param options - Backup options (compress, description) or legacy description string\r\n * @returns Promise resolving to BackupResult with compression statistics\r\n *\r\n * @example\r\n * ```typescript\r\n * // Compressed backup (default)\r\n * const result = await manager.createBackup({ description: 'Pre-migration backup' });\r\n * console.log(`Compressed from ${result.originalSize} to ${result.compressedSize} bytes`);\r\n *\r\n * // Uncompressed backup\r\n * const result = await manager.createBackup({ compress: false });\r\n * ```\r\n */\r\n async createBackup(options?: BackupOptions | string): Promise<BackupResult> {\r\n await this.ensureBackupDir();\r\n\r\n // Handle legacy string argument (backward compatibility)\r\n const opts: BackupOptions = typeof options === 'string'\r\n ? { description: options, compress: COMPRESSION_CONFIG.AUTO_COMPRESS_BACKUP }\r\n : { compress: COMPRESSION_CONFIG.AUTO_COMPRESS_BACKUP, ...options };\r\n\r\n const shouldCompress = opts.compress ?? COMPRESSION_CONFIG.AUTO_COMPRESS_BACKUP;\r\n const graph = await this.storage.loadGraph();\r\n const timestamp = new Date().toISOString();\r\n const fileName = this.generateBackupFileName(shouldCompress);\r\n const backupPath = join(this.backupDir, fileName);\r\n\r\n try {\r\n const originalPath = this.storage.getFilePath();\r\n let fileContent: string;\r\n\r\n try {\r\n fileContent = await fs.readFile(originalPath, 'utf-8');\r\n } catch {\r\n // If file doesn't exist, generate content from graph\r\n const lines = [\r\n ...graph.entities.map(e => JSON.stringify({ type: 'entity', ...e })),\r\n ...graph.relations.map(r => JSON.stringify({ type: 'relation', ...r })),\r\n ];\r\n fileContent = lines.join('\\n');\r\n }\r\n\r\n const originalSize = Buffer.byteLength(fileContent, 'utf-8');\r\n let compressedSize = originalSize;\r\n let compressionRatio = 1;\r\n\r\n if (shouldCompress) {\r\n // Compress with maximum quality for backups (archive quality)\r\n const compressionResult = await compress(fileContent, {\r\n quality: COMPRESSION_CONFIG.BROTLI_QUALITY_ARCHIVE,\r\n mode: 'text',\r\n });\r\n\r\n await fs.writeFile(backupPath, compressionResult.compressed);\r\n compressedSize = compressionResult.compressedSize;\r\n compressionRatio = compressionResult.ratio;\r\n } else {\r\n // Write uncompressed backup\r\n await fs.writeFile(backupPath, fileContent);\r\n }\r\n\r\n const stats = await fs.stat(backupPath);\r\n\r\n const metadata: BackupMetadata = {\r\n timestamp,\r\n entityCount: graph.entities.length,\r\n relationCount: graph.relations.length,\r\n fileSize: stats.size,\r\n description: opts.description,\r\n compressed: shouldCompress,\r\n originalSize,\r\n compressionRatio: shouldCompress ? compressionRatio : undefined,\r\n compressionFormat: shouldCompress ? 'brotli' : 'none',\r\n };\r\n\r\n const metadataPath = `${backupPath}.meta.json`;\r\n await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));\r\n\r\n return {\r\n path: backupPath,\r\n timestamp,\r\n entityCount: graph.entities.length,\r\n relationCount: graph.relations.length,\r\n compressed: shouldCompress,\r\n originalSize,\r\n compressedSize,\r\n compressionRatio,\r\n description: opts.description,\r\n };\r\n } catch (error) {\r\n throw new FileOperationError('create backup', backupPath, error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * List all available backups, sorted by timestamp (newest first).\r\n *\r\n * Detects both compressed (.jsonl.br) and uncompressed (.jsonl) backups.\r\n *\r\n * @returns Promise resolving to array of backup information with compression details\r\n */\r\n async listBackups(): Promise<BackupInfo[]> {\r\n try {\r\n try {\r\n await fs.access(this.backupDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files = await fs.readdir(this.backupDir);\r\n // Match both .jsonl and .jsonl.br backup files, exclude metadata files\r\n const backupFiles = files.filter(f =>\r\n f.startsWith('backup_') &&\r\n (f.endsWith('.jsonl') || f.endsWith('.jsonl.br')) &&\r\n !f.endsWith('.meta.json')\r\n );\r\n\r\n const backups: BackupInfo[] = [];\r\n\r\n for (const fileName of backupFiles) {\r\n const filePath = join(this.backupDir, fileName);\r\n const isCompressed = hasBrotliExtension(fileName);\r\n\r\n // Try to read metadata file (handles both .jsonl.meta.json and .jsonl.br.meta.json)\r\n const metadataPath = `${filePath}.meta.json`;\r\n\r\n try {\r\n const [metadataContent, stats] = await Promise.all([\r\n fs.readFile(metadataPath, 'utf-8'),\r\n fs.stat(filePath),\r\n ]);\r\n const metadata: BackupMetadata = JSON.parse(metadataContent);\r\n\r\n // Ensure compression fields are present (backward compatibility)\r\n if (metadata.compressed === undefined) {\r\n metadata.compressed = isCompressed;\r\n }\r\n if (metadata.compressionFormat === undefined) {\r\n metadata.compressionFormat = isCompressed ? 'brotli' : 'none';\r\n }\r\n\r\n backups.push({\r\n fileName,\r\n filePath,\r\n metadata,\r\n compressed: isCompressed,\r\n size: stats.size,\r\n });\r\n } catch {\r\n // Skip backups without valid metadata\r\n continue;\r\n }\r\n }\r\n\r\n backups.sort((a, b) =>\r\n new Date(b.metadata.timestamp).getTime() - new Date(a.metadata.timestamp).getTime()\r\n );\r\n\r\n return backups;\r\n } catch (error) {\r\n throw new FileOperationError('list backups', this.backupDir, error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * Restore the knowledge graph from a backup file.\r\n *\r\n * Automatically detects and decompresses brotli-compressed backups (.br extension).\r\n * Maintains backward compatibility with uncompressed backups.\r\n *\r\n * @param backupPath - Path to the backup file to restore from\r\n * @returns Promise resolving to RestoreResult with restoration details\r\n *\r\n * @example\r\n * ```typescript\r\n * // Restore from compressed backup\r\n * const result = await manager.restoreFromBackup('/path/to/backup.jsonl.br');\r\n * console.log(`Restored ${result.entityCount} entities from compressed backup`);\r\n *\r\n * // Restore from uncompressed backup (legacy)\r\n * const result = await manager.restoreFromBackup('/path/to/backup.jsonl');\r\n * ```\r\n */\r\n async restoreFromBackup(backupPath: string): Promise<RestoreResult> {\r\n try {\r\n await fs.access(backupPath);\r\n\r\n const isCompressed = hasBrotliExtension(backupPath);\r\n const backupBuffer = await fs.readFile(backupPath);\r\n\r\n let backupContent: string;\r\n if (isCompressed) {\r\n // Decompress the backup\r\n const decompressedBuffer = await decompress(backupBuffer);\r\n backupContent = decompressedBuffer.toString('utf-8');\r\n } else {\r\n // Read as plain text\r\n backupContent = backupBuffer.toString('utf-8');\r\n }\r\n\r\n const mainPath = this.storage.getFilePath();\r\n await fs.writeFile(mainPath, backupContent);\r\n\r\n this.storage.clearCache();\r\n\r\n // Load the restored graph to get counts\r\n const graph = await this.storage.loadGraph();\r\n\r\n return {\r\n entityCount: graph.entities.length,\r\n relationCount: graph.relations.length,\r\n restoredFrom: backupPath,\r\n wasCompressed: isCompressed,\r\n };\r\n } catch (error) {\r\n throw new FileOperationError('restore from backup', backupPath, error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * Delete a specific backup file.\r\n *\r\n * @param backupPath - Path to the backup file to delete\r\n */\r\n async deleteBackup(backupPath: string): Promise<void> {\r\n try {\r\n await fs.unlink(backupPath);\r\n\r\n try {\r\n await fs.unlink(`${backupPath}.meta.json`);\r\n } catch {\r\n // Metadata file doesn't exist - that's ok\r\n }\r\n } catch (error) {\r\n throw new FileOperationError('delete backup', backupPath, error as Error);\r\n }\r\n }\r\n\r\n /**\r\n * Clean old backups, keeping only the most recent N backups.\r\n *\r\n * @param keepCount - Number of recent backups to keep (default: 10)\r\n * @returns Promise resolving to number of backups deleted\r\n */\r\n async cleanOldBackups(keepCount: number = 10): Promise<number> {\r\n const backups = await this.listBackups();\r\n\r\n if (backups.length <= keepCount) {\r\n return 0;\r\n }\r\n\r\n const backupsToDelete = backups.slice(keepCount);\r\n let deletedCount = 0;\r\n\r\n for (const backup of backupsToDelete) {\r\n try {\r\n await this.deleteBackup(backup.filePath);\r\n deletedCount++;\r\n } catch {\r\n continue;\r\n }\r\n }\r\n\r\n return deletedCount;\r\n }\r\n\r\n /**\r\n * Get the path to the backup directory.\r\n */\r\n getBackupDir(): string {\r\n return this.backupDir;\r\n }\r\n}\r\n","/**\r\n * Streaming Export Module\r\n *\r\n * Provides streaming export functionality for large knowledge graphs to avoid\r\n * loading entire graphs into memory. Supports JSONL and CSV formats with\r\n * incremental writing to disk.\r\n *\r\n * @module features/StreamingExporter\r\n */\r\n\r\nimport { createWriteStream } from 'fs';\r\nimport type { Entity, ReadonlyKnowledgeGraph, LongRunningOperationOptions } from '../types/types.js';\r\nimport { checkCancellation, createProgressReporter, createProgress, validateFilePath } from '../utils/index.js';\r\n\r\n/**\r\n * Result summary from a streaming export operation.\r\n *\r\n * Provides statistics about the export including bytes written,\r\n * entity/relation counts, and duration.\r\n *\r\n * @example\r\n * ```typescript\r\n * const result: StreamResult = {\r\n * bytesWritten: 125000,\r\n * entitiesWritten: 150,\r\n * relationsWritten: 320,\r\n * durationMs: 1250\r\n * };\r\n * ```\r\n */\r\nexport interface StreamResult {\r\n /** Total bytes written to the output file */\r\n bytesWritten: number;\r\n\r\n /** Number of entities written */\r\n entitiesWritten: number;\r\n\r\n /** Number of relations written */\r\n relationsWritten: number;\r\n\r\n /** Duration of the export operation in milliseconds */\r\n durationMs: number;\r\n}\r\n\r\n/**\r\n * Streaming exporter for knowledge graphs.\r\n *\r\n * Provides memory-efficient export by streaming data directly to files\r\n * instead of building large strings in memory. Supports JSONL and CSV formats.\r\n *\r\n * @example\r\n * ```typescript\r\n * const exporter = new StreamingExporter('/path/to/output.jsonl');\r\n * const result = await exporter.streamJSONL(graph);\r\n * console.log(`Wrote ${result.bytesWritten} bytes in ${result.durationMs}ms`);\r\n * ```\r\n */\r\nexport class StreamingExporter {\r\n private readonly validatedFilePath: string;\r\n\r\n /**\r\n * Create a new streaming exporter.\r\n *\r\n * @param filePath - Path to the output file\r\n * @throws {FileOperationError} If path traversal is detected\r\n */\r\n constructor(filePath: string) {\r\n // Validate path to prevent path traversal attacks\r\n this.validatedFilePath = validateFilePath(filePath);\r\n }\r\n\r\n /**\r\n * Get the validated file path.\r\n */\r\n get filePath(): string {\r\n return this.validatedFilePath;\r\n }\r\n\r\n /**\r\n * Stream a knowledge graph to JSONL format.\r\n *\r\n * Each entity and relation is written as a separate JSON line.\r\n * Memory usage is constant regardless of graph size.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param graph - The knowledge graph to export\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Promise resolving to export statistics\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n *\r\n * @example\r\n * ```typescript\r\n * const exporter = new StreamingExporter('export.jsonl');\r\n * const result = await exporter.streamJSONL(graph);\r\n * console.log(`Exported ${result.entitiesWritten} entities`);\r\n *\r\n * // With progress tracking (Phase 9B)\r\n * const result = await exporter.streamJSONL(graph, {\r\n * onProgress: (p) => console.log(`${p.percentage}% complete`),\r\n * });\r\n * ```\r\n */\r\n async streamJSONL(\r\n graph: ReadonlyKnowledgeGraph,\r\n options?: LongRunningOperationOptions\r\n ): Promise<StreamResult> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'streamJSONL');\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n const total = graph.entities.length + graph.relations.length;\r\n let processed = 0;\r\n reportProgress?.(createProgress(0, total, 'streamJSONL'));\r\n\r\n const start = Date.now();\r\n let bytesWritten = 0;\r\n let entitiesWritten = 0;\r\n let relationsWritten = 0;\r\n\r\n const writeStream = createWriteStream(this.filePath);\r\n\r\n // Write entities\r\n for (const entity of graph.entities) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'streamJSONL');\r\n\r\n const line = JSON.stringify(entity) + '\\n';\r\n writeStream.write(line);\r\n bytesWritten += Buffer.byteLength(line, 'utf-8');\r\n entitiesWritten++;\r\n processed++;\r\n reportProgress?.(createProgress(processed, total, 'writing entities'));\r\n }\r\n\r\n // Write relations\r\n for (const relation of graph.relations) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'streamJSONL');\r\n\r\n const line = JSON.stringify(relation) + '\\n';\r\n writeStream.write(line);\r\n bytesWritten += Buffer.byteLength(line, 'utf-8');\r\n relationsWritten++;\r\n processed++;\r\n reportProgress?.(createProgress(processed, total, 'writing relations'));\r\n }\r\n\r\n // Check for cancellation before finalizing\r\n checkCancellation(options?.signal, 'streamJSONL');\r\n\r\n // Wait for stream to finish\r\n await new Promise<void>((resolve, reject) => {\r\n writeStream.end(() => resolve());\r\n writeStream.on('error', reject);\r\n });\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(total, total, 'streamJSONL'));\r\n\r\n return {\r\n bytesWritten,\r\n entitiesWritten,\r\n relationsWritten,\r\n durationMs: Date.now() - start,\r\n };\r\n }\r\n\r\n /**\r\n * Stream a knowledge graph to CSV format.\r\n *\r\n * Exports entities as CSV rows with proper escaping for special characters.\r\n * Header row includes: name, type, observations, tags, importance, createdAt, lastModified.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param graph - The knowledge graph to export\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Promise resolving to export statistics\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n *\r\n * @example\r\n * ```typescript\r\n * const exporter = new StreamingExporter('export.csv');\r\n * const result = await exporter.streamCSV(graph);\r\n * console.log(`Exported ${result.entitiesWritten} entities as CSV`);\r\n *\r\n * // With progress tracking (Phase 9B)\r\n * const result = await exporter.streamCSV(graph, {\r\n * onProgress: (p) => console.log(`${p.percentage}% complete`),\r\n * });\r\n * ```\r\n */\r\n async streamCSV(\r\n graph: ReadonlyKnowledgeGraph,\r\n options?: LongRunningOperationOptions\r\n ): Promise<StreamResult> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'streamCSV');\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n const total = graph.entities.length;\r\n let processed = 0;\r\n reportProgress?.(createProgress(0, total, 'streamCSV'));\r\n\r\n const start = Date.now();\r\n let bytesWritten = 0;\r\n let entitiesWritten = 0;\r\n const relationsWritten = 0; // CSV format doesn't export relations\r\n\r\n const writeStream = createWriteStream(this.filePath);\r\n\r\n // Write header\r\n const header = 'name,type,observations,tags,importance,createdAt,lastModified\\n';\r\n writeStream.write(header);\r\n bytesWritten += Buffer.byteLength(header, 'utf-8');\r\n\r\n // Write entity rows\r\n for (const entity of graph.entities) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'streamCSV');\r\n\r\n const row = this.entityToCSVRow(entity) + '\\n';\r\n writeStream.write(row);\r\n bytesWritten += Buffer.byteLength(row, 'utf-8');\r\n entitiesWritten++;\r\n processed++;\r\n reportProgress?.(createProgress(processed, total, 'writing entities'));\r\n }\r\n\r\n // Check for cancellation before finalizing\r\n checkCancellation(options?.signal, 'streamCSV');\r\n\r\n // Wait for stream to finish\r\n await new Promise<void>((resolve, reject) => {\r\n writeStream.end(() => resolve());\r\n writeStream.on('error', reject);\r\n });\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(total, total, 'streamCSV'));\r\n\r\n return {\r\n bytesWritten,\r\n entitiesWritten,\r\n relationsWritten,\r\n durationMs: Date.now() - start,\r\n };\r\n }\r\n\r\n /**\r\n * Convert an entity to a CSV row with proper escaping.\r\n *\r\n * Escapes double quotes by doubling them and wraps fields in quotes.\r\n * Arrays (observations, tags) are joined with semicolons.\r\n *\r\n * @param entity - The entity to convert\r\n * @returns CSV row string (without trailing newline)\r\n *\r\n * @private\r\n */\r\n private entityToCSVRow(entity: Entity): string {\r\n const escape = (s: string) => `\"${s.replace(/\"/g, '\"\"')}\"`;\r\n\r\n return [\r\n escape(entity.name),\r\n escape(entity.entityType),\r\n escape(entity.observations.join('; ')),\r\n escape((entity.tags ?? []).join('; ')),\r\n entity.importance?.toString() ?? '',\r\n entity.createdAt ?? '',\r\n entity.lastModified ?? '',\r\n ].join(',');\r\n }\r\n\r\n}\r\n","/**\r\n * Transaction Manager\r\n *\r\n * Provides atomic transaction support for knowledge graph operations.\r\n * Ensures data consistency by allowing multiple operations to be\r\n * grouped together and committed atomically, with automatic rollback on failure.\r\n *\r\n * @module core/TransactionManager\r\n */\r\n\r\nimport type {\r\n Entity,\r\n Relation,\r\n KnowledgeGraph,\r\n LongRunningOperationOptions,\r\n BatchOperation,\r\n BatchResult,\r\n BatchOptions,\r\n} from '../types/index.js';\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { IOManager } from '../features/IOManager.js';\r\nimport { KnowledgeGraphError } from '../utils/errors.js';\r\nimport { checkCancellation, createProgressReporter, createProgress, sanitizeObject } from '../utils/index.js';\r\n\r\n/**\r\n * Types of operations that can be performed in a transaction.\r\n */\r\nexport enum OperationType {\r\n CREATE_ENTITY = 'CREATE_ENTITY',\r\n UPDATE_ENTITY = 'UPDATE_ENTITY',\r\n DELETE_ENTITY = 'DELETE_ENTITY',\r\n CREATE_RELATION = 'CREATE_RELATION',\r\n DELETE_RELATION = 'DELETE_RELATION',\r\n}\r\n\r\n/**\r\n * Represents a single operation in a transaction using discriminated union.\r\n */\r\nexport type TransactionOperation =\r\n | {\r\n type: OperationType.CREATE_ENTITY;\r\n data: Omit<Entity, 'createdAt' | 'lastModified'>;\r\n }\r\n | {\r\n type: OperationType.UPDATE_ENTITY;\r\n data: { name: string; updates: Partial<Entity> };\r\n }\r\n | {\r\n type: OperationType.DELETE_ENTITY;\r\n data: { name: string };\r\n }\r\n | {\r\n type: OperationType.CREATE_RELATION;\r\n data: Omit<Relation, 'createdAt' | 'lastModified'>;\r\n }\r\n | {\r\n type: OperationType.DELETE_RELATION;\r\n data: { from: string; to: string; relationType: string };\r\n };\r\n\r\n/**\r\n * Transaction execution result.\r\n */\r\nexport interface TransactionResult {\r\n /** Whether the transaction was successful */\r\n success: boolean;\r\n /** Number of operations executed */\r\n operationsExecuted: number;\r\n /** Error message if transaction failed */\r\n error?: string;\r\n /** Path to rollback backup if created */\r\n rollbackBackup?: string;\r\n}\r\n\r\n/**\r\n * Manages atomic transactions for knowledge graph operations.\r\n *\r\n * Provides ACID-like guarantees:\r\n * - Atomicity: All operations succeed or all fail\r\n * - Consistency: Graph is always in a valid state\r\n * - Isolation: Each transaction operates on a snapshot\r\n * - Durability: Changes are persisted to disk\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/data/memory.jsonl');\r\n * const txManager = new TransactionManager(storage);\r\n *\r\n * // Begin transaction\r\n * txManager.begin();\r\n *\r\n * // Stage operations\r\n * txManager.createEntity({ name: 'Alice', entityType: 'person', observations: [] });\r\n * txManager.createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' });\r\n *\r\n * // Commit atomically (or rollback on error)\r\n * const result = await txManager.commit();\r\n * if (result.success) {\r\n * console.log(`Transaction completed: ${result.operationsExecuted} operations`);\r\n * }\r\n * ```\r\n */\r\nexport class TransactionManager {\r\n private operations: TransactionOperation[] = [];\r\n private inTransaction: boolean = false;\r\n private ioManager: IOManager;\r\n private transactionBackup?: string;\r\n\r\n constructor(private storage: GraphStorage) {\r\n this.ioManager = new IOManager(storage);\r\n }\r\n\r\n /**\r\n * Begin a new transaction.\r\n *\r\n * Creates a backup of the current state for rollback purposes.\r\n * Only one transaction can be active at a time.\r\n *\r\n * @throws {KnowledgeGraphError} If a transaction is already in progress\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * // ... stage operations ...\r\n * await txManager.commit();\r\n * ```\r\n */\r\n begin(): void {\r\n if (this.inTransaction) {\r\n throw new KnowledgeGraphError('Transaction already in progress', 'TRANSACTION_ACTIVE');\r\n }\r\n\r\n this.operations = [];\r\n this.inTransaction = true;\r\n }\r\n\r\n /**\r\n * Stage a create entity operation.\r\n *\r\n * @param entity - Entity to create (without timestamps)\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.createEntity({\r\n * name: 'Alice',\r\n * entityType: 'person',\r\n * observations: ['Software engineer'],\r\n * importance: 8\r\n * });\r\n * ```\r\n */\r\n createEntity(entity: Omit<Entity, 'createdAt' | 'lastModified'>): void {\r\n this.ensureInTransaction();\r\n this.operations.push({\r\n type: OperationType.CREATE_ENTITY,\r\n data: entity,\r\n });\r\n }\r\n\r\n /**\r\n * Stage an update entity operation.\r\n *\r\n * @param name - Name of entity to update\r\n * @param updates - Partial entity updates\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.updateEntity('Alice', {\r\n * importance: 9,\r\n * observations: ['Lead software engineer']\r\n * });\r\n * ```\r\n */\r\n updateEntity(name: string, updates: Partial<Entity>): void {\r\n this.ensureInTransaction();\r\n this.operations.push({\r\n type: OperationType.UPDATE_ENTITY,\r\n data: { name, updates },\r\n });\r\n }\r\n\r\n /**\r\n * Stage a delete entity operation.\r\n *\r\n * @param name - Name of entity to delete\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.deleteEntity('OldEntity');\r\n * ```\r\n */\r\n deleteEntity(name: string): void {\r\n this.ensureInTransaction();\r\n this.operations.push({\r\n type: OperationType.DELETE_ENTITY,\r\n data: { name },\r\n });\r\n }\r\n\r\n /**\r\n * Stage a create relation operation.\r\n *\r\n * @param relation - Relation to create (without timestamps)\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.createRelation({\r\n * from: 'Alice',\r\n * to: 'Bob',\r\n * relationType: 'mentors'\r\n * });\r\n * ```\r\n */\r\n createRelation(relation: Omit<Relation, 'createdAt' | 'lastModified'>): void {\r\n this.ensureInTransaction();\r\n this.operations.push({\r\n type: OperationType.CREATE_RELATION,\r\n data: relation,\r\n });\r\n }\r\n\r\n /**\r\n * Stage a delete relation operation.\r\n *\r\n * @param from - Source entity name\r\n * @param to - Target entity name\r\n * @param relationType - Type of relation\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.deleteRelation('Alice', 'Bob', 'mentors');\r\n * ```\r\n */\r\n deleteRelation(from: string, to: string, relationType: string): void {\r\n this.ensureInTransaction();\r\n this.operations.push({\r\n type: OperationType.DELETE_RELATION,\r\n data: { from, to, relationType },\r\n });\r\n }\r\n\r\n /**\r\n * Commit the transaction, applying all staged operations atomically.\r\n *\r\n * Creates a backup before applying changes. If any operation fails,\r\n * automatically rolls back to the pre-transaction state.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Promise resolving to transaction result\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.createEntity({ name: 'Alice', entityType: 'person', observations: [] });\r\n * txManager.createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' });\r\n *\r\n * const result = await txManager.commit();\r\n * if (result.success) {\r\n * console.log(`Committed ${result.operationsExecuted} operations`);\r\n * } else {\r\n * console.error(`Transaction failed: ${result.error}`);\r\n * }\r\n *\r\n * // With progress tracking (Phase 9B)\r\n * const result = await txManager.commit({\r\n * onProgress: (p) => console.log(`${p.percentage}% complete`),\r\n * });\r\n * ```\r\n */\r\n async commit(options?: LongRunningOperationOptions): Promise<TransactionResult> {\r\n this.ensureInTransaction();\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n const totalOperations = this.operations.length;\r\n reportProgress?.(createProgress(0, 100, 'commit'));\r\n\r\n try {\r\n // Check for early cancellation (inside try block to handle gracefully)\r\n checkCancellation(options?.signal, 'commit');\r\n // Phase 1: Create backup for rollback (0-20% progress)\r\n reportProgress?.(createProgress(5, 100, 'creating backup'));\r\n const backupResult = await this.ioManager.createBackup({\r\n description: 'Transaction backup (auto-created)',\r\n });\r\n this.transactionBackup = backupResult.path;\r\n\r\n // Check for cancellation after backup\r\n checkCancellation(options?.signal, 'commit');\r\n reportProgress?.(createProgress(20, 100, 'backup created'));\r\n\r\n // Phase 2: Load graph (20-30% progress)\r\n reportProgress?.(createProgress(25, 100, 'loading graph'));\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n reportProgress?.(createProgress(30, 100, 'graph loaded'));\r\n\r\n // Phase 3: Apply all operations (30-80% progress)\r\n let operationsExecuted = 0;\r\n for (const operation of this.operations) {\r\n // Check for cancellation between operations\r\n checkCancellation(options?.signal, 'commit');\r\n\r\n this.applyOperation(graph, operation, timestamp);\r\n operationsExecuted++;\r\n\r\n // Map operation progress (0-100%) to overall progress (30-80%)\r\n const opProgress = totalOperations > 0 ? Math.round(30 + (operationsExecuted / totalOperations) * 50) : 80;\r\n reportProgress?.(createProgress(opProgress, 100, 'applying operations'));\r\n }\r\n\r\n // Check for cancellation before save\r\n checkCancellation(options?.signal, 'commit');\r\n\r\n // Phase 4: Save the modified graph (80-95% progress)\r\n reportProgress?.(createProgress(85, 100, 'saving graph'));\r\n await this.storage.saveGraph(graph);\r\n reportProgress?.(createProgress(95, 100, 'graph saved'));\r\n\r\n // Clean up transaction state\r\n this.inTransaction = false;\r\n this.operations = [];\r\n\r\n // Delete the transaction backup (no longer needed)\r\n if (this.transactionBackup) {\r\n await this.ioManager.deleteBackup(this.transactionBackup);\r\n this.transactionBackup = undefined;\r\n }\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(100, 100, 'commit'));\r\n\r\n return {\r\n success: true,\r\n operationsExecuted,\r\n };\r\n } catch (error) {\r\n // Rollback on error\r\n const rollbackResult = await this.rollback();\r\n\r\n return {\r\n success: false,\r\n operationsExecuted: 0,\r\n error: error instanceof Error ? error.message : String(error),\r\n rollbackBackup: rollbackResult.backupUsed,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Rollback the current transaction.\r\n *\r\n * Restores the graph to the pre-transaction state using the backup.\r\n * Automatically called by commit() on failure.\r\n *\r\n * @returns Promise resolving to rollback result\r\n *\r\n * @example\r\n * ```typescript\r\n * txManager.begin();\r\n * txManager.createEntity({ name: 'Test', entityType: 'temp', observations: [] });\r\n *\r\n * // Explicit rollback (e.g., user cancellation)\r\n * const result = await txManager.rollback();\r\n * console.log(`Rolled back, restored from: ${result.backupUsed}`);\r\n * ```\r\n */\r\n async rollback(): Promise<{ success: boolean; backupUsed?: string }> {\r\n if (!this.transactionBackup) {\r\n this.inTransaction = false;\r\n this.operations = [];\r\n return { success: false };\r\n }\r\n\r\n try {\r\n // Restore from backup\r\n await this.ioManager.restoreFromBackup(this.transactionBackup);\r\n\r\n // Clean up\r\n const backupUsed = this.transactionBackup;\r\n await this.ioManager.deleteBackup(this.transactionBackup);\r\n\r\n this.inTransaction = false;\r\n this.operations = [];\r\n this.transactionBackup = undefined;\r\n\r\n return { success: true, backupUsed };\r\n } catch (error) {\r\n // Rollback failed - keep backup for manual recovery\r\n this.inTransaction = false;\r\n this.operations = [];\r\n\r\n return { success: false, backupUsed: this.transactionBackup };\r\n }\r\n }\r\n\r\n /**\r\n * Check if a transaction is currently in progress.\r\n *\r\n * @returns True if transaction is active\r\n */\r\n isInTransaction(): boolean {\r\n return this.inTransaction;\r\n }\r\n\r\n /**\r\n * Get the number of staged operations in the current transaction.\r\n *\r\n * @returns Number of operations staged\r\n */\r\n getOperationCount(): number {\r\n return this.operations.length;\r\n }\r\n\r\n /**\r\n * Ensure a transaction is in progress, or throw an error.\r\n *\r\n * @private\r\n */\r\n private ensureInTransaction(): void {\r\n if (!this.inTransaction) {\r\n throw new KnowledgeGraphError('No transaction in progress. Call begin() first.', 'NO_TRANSACTION');\r\n }\r\n }\r\n\r\n /**\r\n * Apply a single operation to the graph.\r\n *\r\n * @private\r\n */\r\n private applyOperation(graph: KnowledgeGraph, operation: TransactionOperation, timestamp: string): void {\r\n switch (operation.type) {\r\n case OperationType.CREATE_ENTITY: {\r\n const entity: Entity = {\r\n ...operation.data,\r\n createdAt: timestamp,\r\n lastModified: timestamp,\r\n };\r\n // Check for duplicates\r\n if (graph.entities.some(e => e.name === entity.name)) {\r\n throw new KnowledgeGraphError(`Entity \"${entity.name}\" already exists`, 'DUPLICATE_ENTITY');\r\n }\r\n graph.entities.push(entity);\r\n break;\r\n }\r\n\r\n case OperationType.UPDATE_ENTITY: {\r\n const { name, updates } = operation.data;\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n // Sanitize updates to prevent prototype pollution\r\n Object.assign(entity, sanitizeObject(updates as Record<string, unknown>));\r\n entity.lastModified = timestamp;\r\n break;\r\n }\r\n\r\n case OperationType.DELETE_ENTITY: {\r\n const { name } = operation.data;\r\n const index = graph.entities.findIndex(e => e.name === name);\r\n if (index === -1) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n graph.entities.splice(index, 1);\r\n // Delete related relations\r\n graph.relations = graph.relations.filter(r => r.from !== name && r.to !== name);\r\n break;\r\n }\r\n\r\n case OperationType.CREATE_RELATION: {\r\n const relation: Relation = {\r\n ...operation.data,\r\n createdAt: timestamp,\r\n lastModified: timestamp,\r\n };\r\n // Check for duplicates\r\n const exists = graph.relations.some(\r\n r => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType\r\n );\r\n if (exists) {\r\n throw new KnowledgeGraphError(\r\n `Relation \"${relation.from}\" -> \"${relation.to}\" (${relation.relationType}) already exists`,\r\n 'DUPLICATE_RELATION'\r\n );\r\n }\r\n graph.relations.push(relation);\r\n break;\r\n }\r\n\r\n case OperationType.DELETE_RELATION: {\r\n const { from, to, relationType } = operation.data;\r\n const index = graph.relations.findIndex(\r\n r => r.from === from && r.to === to && r.relationType === relationType\r\n );\r\n if (index === -1) {\r\n throw new KnowledgeGraphError(\r\n `Relation \"${from}\" -> \"${to}\" (${relationType}) not found`,\r\n 'RELATION_NOT_FOUND'\r\n );\r\n }\r\n graph.relations.splice(index, 1);\r\n break;\r\n }\r\n\r\n default: {\r\n // Exhaustiveness check - TypeScript will error if we miss a case\r\n const _exhaustiveCheck: never = operation;\r\n throw new KnowledgeGraphError(`Unknown operation type: ${(_exhaustiveCheck as TransactionOperation).type}`, 'UNKNOWN_OPERATION');\r\n }\r\n }\r\n }\r\n}\r\n\r\n// ==================== Phase 10 Sprint 1: BatchTransaction ====================\r\n\r\n/**\r\n * Phase 10 Sprint 1: Fluent API for building and executing batch transactions.\r\n *\r\n * BatchTransaction provides a builder pattern for accumulating multiple\r\n * graph operations and executing them atomically in a single transaction.\r\n * This reduces I/O overhead and ensures consistency across related changes.\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/data/memory.jsonl');\r\n * const batch = new BatchTransaction(storage);\r\n *\r\n * // Build the batch with fluent API\r\n * const result = await batch\r\n * .createEntity({ name: 'Alice', entityType: 'person', observations: ['Developer'] })\r\n * .createEntity({ name: 'Bob', entityType: 'person', observations: ['Designer'] })\r\n * .createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' })\r\n * .updateEntity('Alice', { importance: 8 })\r\n * .execute();\r\n *\r\n * if (result.success) {\r\n * console.log(`Batch completed: ${result.operationsExecuted} operations in ${result.executionTimeMs}ms`);\r\n * }\r\n * ```\r\n */\r\nexport class BatchTransaction {\r\n private operations: BatchOperation[] = [];\r\n private storage: GraphStorage;\r\n\r\n /**\r\n * Create a new BatchTransaction instance.\r\n *\r\n * @param storage - GraphStorage instance to execute operations against\r\n */\r\n constructor(storage: GraphStorage) {\r\n this.storage = storage;\r\n }\r\n\r\n /**\r\n * Add a create entity operation to the batch.\r\n *\r\n * @param entity - Entity to create (without timestamps)\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.createEntity({\r\n * name: 'Alice',\r\n * entityType: 'person',\r\n * observations: ['Software engineer'],\r\n * importance: 8\r\n * });\r\n * ```\r\n */\r\n createEntity(entity: Omit<Entity, 'createdAt' | 'lastModified'>): this {\r\n this.operations.push({ type: 'createEntity', data: entity });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add an update entity operation to the batch.\r\n *\r\n * @param name - Name of entity to update\r\n * @param updates - Partial entity updates\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.updateEntity('Alice', { importance: 9 });\r\n * ```\r\n */\r\n updateEntity(name: string, updates: Partial<Entity>): this {\r\n this.operations.push({ type: 'updateEntity', data: { name, updates } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add a delete entity operation to the batch.\r\n *\r\n * @param name - Name of entity to delete\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.deleteEntity('OldEntity');\r\n * ```\r\n */\r\n deleteEntity(name: string): this {\r\n this.operations.push({ type: 'deleteEntity', data: { name } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add a create relation operation to the batch.\r\n *\r\n * @param relation - Relation to create (without timestamps)\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.createRelation({\r\n * from: 'Alice',\r\n * to: 'Bob',\r\n * relationType: 'mentors'\r\n * });\r\n * ```\r\n */\r\n createRelation(relation: Omit<Relation, 'createdAt' | 'lastModified'>): this {\r\n this.operations.push({ type: 'createRelation', data: relation });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add a delete relation operation to the batch.\r\n *\r\n * @param from - Source entity name\r\n * @param to - Target entity name\r\n * @param relationType - Type of relation\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.deleteRelation('Alice', 'Bob', 'mentors');\r\n * ```\r\n */\r\n deleteRelation(from: string, to: string, relationType: string): this {\r\n this.operations.push({ type: 'deleteRelation', data: { from, to, relationType } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add observations to an existing entity.\r\n *\r\n * @param name - Name of entity to add observations to\r\n * @param observations - Observations to add\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.addObservations('Alice', ['Knows TypeScript', 'Leads team']);\r\n * ```\r\n */\r\n addObservations(name: string, observations: string[]): this {\r\n this.operations.push({ type: 'addObservations', data: { name, observations } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Delete observations from an existing entity.\r\n *\r\n * @param name - Name of entity to delete observations from\r\n * @param observations - Observations to delete\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.deleteObservations('Alice', ['Old fact']);\r\n * ```\r\n */\r\n deleteObservations(name: string, observations: string[]): this {\r\n this.operations.push({ type: 'deleteObservations', data: { name, observations } });\r\n return this;\r\n }\r\n\r\n /**\r\n * Add multiple operations from an array.\r\n *\r\n * @param operations - Array of batch operations\r\n * @returns This BatchTransaction for chaining\r\n *\r\n * @example\r\n * ```typescript\r\n * batch.addOperations([\r\n * { type: 'createEntity', data: { name: 'A', entityType: 'x', observations: [] } },\r\n * { type: 'createEntity', data: { name: 'B', entityType: 'x', observations: [] } }\r\n * ]);\r\n * ```\r\n */\r\n addOperations(operations: BatchOperation[]): this {\r\n this.operations.push(...operations);\r\n return this;\r\n }\r\n\r\n /**\r\n * Get the number of operations in this batch.\r\n *\r\n * @returns Number of operations queued\r\n */\r\n size(): number {\r\n return this.operations.length;\r\n }\r\n\r\n /**\r\n * Clear all operations from the batch.\r\n *\r\n * @returns This BatchTransaction for chaining\r\n */\r\n clear(): this {\r\n this.operations = [];\r\n return this;\r\n }\r\n\r\n /**\r\n * Get a copy of the operations in this batch.\r\n *\r\n * @returns Array of batch operations\r\n */\r\n getOperations(): BatchOperation[] {\r\n return [...this.operations];\r\n }\r\n\r\n /**\r\n * Execute all operations in the batch atomically.\r\n *\r\n * All operations are applied within a single transaction. If any\r\n * operation fails, all changes are rolled back (when stopOnError is true).\r\n *\r\n * @param options - Batch execution options\r\n * @returns Promise resolving to batch result\r\n *\r\n * @example\r\n * ```typescript\r\n * const result = await batch.execute();\r\n * if (result.success) {\r\n * console.log(`Created ${result.entitiesCreated} entities`);\r\n * } else {\r\n * console.error(`Failed at operation ${result.failedOperationIndex}: ${result.error}`);\r\n * }\r\n * ```\r\n */\r\n async execute(options: BatchOptions = {}): Promise<BatchResult> {\r\n const startTime = Date.now();\r\n const { stopOnError = true, validateBeforeExecute = true } = options;\r\n\r\n const result: BatchResult = {\r\n success: true,\r\n operationsExecuted: 0,\r\n entitiesCreated: 0,\r\n entitiesUpdated: 0,\r\n entitiesDeleted: 0,\r\n relationsCreated: 0,\r\n relationsDeleted: 0,\r\n executionTimeMs: 0,\r\n };\r\n\r\n if (this.operations.length === 0) {\r\n result.executionTimeMs = Date.now() - startTime;\r\n return result;\r\n }\r\n\r\n // Load graph for mutation\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n\r\n // Optional: Validate all operations before executing\r\n if (validateBeforeExecute) {\r\n const validationError = this.validateOperations(graph);\r\n if (validationError) {\r\n return {\r\n ...result,\r\n success: false,\r\n error: validationError.message,\r\n failedOperationIndex: validationError.index,\r\n executionTimeMs: Date.now() - startTime,\r\n };\r\n }\r\n }\r\n\r\n // Execute operations\r\n for (let i = 0; i < this.operations.length; i++) {\r\n const operation = this.operations[i];\r\n\r\n try {\r\n this.applyBatchOperation(graph, operation, timestamp, result);\r\n result.operationsExecuted++;\r\n } catch (error) {\r\n result.success = false;\r\n result.error = error instanceof Error ? error.message : String(error);\r\n result.failedOperationIndex = i;\r\n\r\n if (stopOnError) {\r\n result.executionTimeMs = Date.now() - startTime;\r\n return result;\r\n }\r\n }\r\n }\r\n\r\n // Save the modified graph if successful (or if stopOnError is false)\r\n if (result.success || !stopOnError) {\r\n try {\r\n await this.storage.saveGraph(graph);\r\n } catch (error) {\r\n result.success = false;\r\n result.error = `Failed to save graph: ${error instanceof Error ? error.message : String(error)}`;\r\n }\r\n }\r\n\r\n result.executionTimeMs = Date.now() - startTime;\r\n return result;\r\n }\r\n\r\n /**\r\n * Validate all operations before executing.\r\n * @private\r\n */\r\n private validateOperations(\r\n graph: KnowledgeGraph\r\n ): { message: string; index: number } | null {\r\n const entityNames = new Set(graph.entities.map(e => e.name));\r\n const pendingCreates = new Set<string>();\r\n const pendingDeletes = new Set<string>();\r\n\r\n for (let i = 0; i < this.operations.length; i++) {\r\n const op = this.operations[i];\r\n\r\n switch (op.type) {\r\n case 'createEntity': {\r\n const name = op.data.name;\r\n if (entityNames.has(name) && !pendingDeletes.has(name)) {\r\n return { message: `Entity \"${name}\" already exists`, index: i };\r\n }\r\n if (pendingCreates.has(name)) {\r\n return { message: `Duplicate create for entity \"${name}\" in batch`, index: i };\r\n }\r\n pendingCreates.add(name);\r\n break;\r\n }\r\n\r\n case 'updateEntity':\r\n case 'addObservations':\r\n case 'deleteObservations': {\r\n const name = op.data.name;\r\n const exists = (entityNames.has(name) || pendingCreates.has(name)) && !pendingDeletes.has(name);\r\n if (!exists) {\r\n return { message: `Entity \"${name}\" not found`, index: i };\r\n }\r\n break;\r\n }\r\n\r\n case 'deleteEntity': {\r\n const name = op.data.name;\r\n const exists = (entityNames.has(name) || pendingCreates.has(name)) && !pendingDeletes.has(name);\r\n if (!exists) {\r\n return { message: `Entity \"${name}\" not found for deletion`, index: i };\r\n }\r\n pendingDeletes.add(name);\r\n break;\r\n }\r\n\r\n case 'createRelation': {\r\n const { from, to } = op.data;\r\n const fromExists = (entityNames.has(from) || pendingCreates.has(from)) && !pendingDeletes.has(from);\r\n const toExists = (entityNames.has(to) || pendingCreates.has(to)) && !pendingDeletes.has(to);\r\n if (!fromExists) {\r\n return { message: `Source entity \"${from}\" not found for relation`, index: i };\r\n }\r\n if (!toExists) {\r\n return { message: `Target entity \"${to}\" not found for relation`, index: i };\r\n }\r\n break;\r\n }\r\n\r\n case 'deleteRelation': {\r\n // Relations are validated at execution time\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return null;\r\n }\r\n\r\n /**\r\n * Apply a single batch operation to the graph.\r\n * @private\r\n */\r\n private applyBatchOperation(\r\n graph: KnowledgeGraph,\r\n operation: BatchOperation,\r\n timestamp: string,\r\n result: BatchResult\r\n ): void {\r\n switch (operation.type) {\r\n case 'createEntity': {\r\n const entity: Entity = {\r\n ...operation.data,\r\n createdAt: timestamp,\r\n lastModified: timestamp,\r\n };\r\n if (graph.entities.some(e => e.name === entity.name)) {\r\n throw new KnowledgeGraphError(`Entity \"${entity.name}\" already exists`, 'DUPLICATE_ENTITY');\r\n }\r\n graph.entities.push(entity);\r\n result.entitiesCreated++;\r\n break;\r\n }\r\n\r\n case 'updateEntity': {\r\n const { name, updates } = operation.data;\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n // Sanitize updates to prevent prototype pollution\r\n Object.assign(entity, sanitizeObject(updates as Record<string, unknown>));\r\n entity.lastModified = timestamp;\r\n result.entitiesUpdated++;\r\n break;\r\n }\r\n\r\n case 'deleteEntity': {\r\n const { name } = operation.data;\r\n const index = graph.entities.findIndex(e => e.name === name);\r\n if (index === -1) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n graph.entities.splice(index, 1);\r\n // Delete related relations\r\n graph.relations = graph.relations.filter(r => r.from !== name && r.to !== name);\r\n result.entitiesDeleted++;\r\n break;\r\n }\r\n\r\n case 'createRelation': {\r\n const relation: Relation = {\r\n ...operation.data,\r\n createdAt: timestamp,\r\n lastModified: timestamp,\r\n };\r\n const exists = graph.relations.some(\r\n r => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType\r\n );\r\n if (exists) {\r\n throw new KnowledgeGraphError(\r\n `Relation \"${relation.from}\" -> \"${relation.to}\" (${relation.relationType}) already exists`,\r\n 'DUPLICATE_RELATION'\r\n );\r\n }\r\n graph.relations.push(relation);\r\n result.relationsCreated++;\r\n break;\r\n }\r\n\r\n case 'deleteRelation': {\r\n const { from, to, relationType } = operation.data;\r\n const index = graph.relations.findIndex(\r\n r => r.from === from && r.to === to && r.relationType === relationType\r\n );\r\n if (index === -1) {\r\n throw new KnowledgeGraphError(\r\n `Relation \"${from}\" -> \"${to}\" (${relationType}) not found`,\r\n 'RELATION_NOT_FOUND'\r\n );\r\n }\r\n graph.relations.splice(index, 1);\r\n result.relationsDeleted++;\r\n break;\r\n }\r\n\r\n case 'addObservations': {\r\n const { name, observations } = operation.data;\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n // Add only new observations\r\n const existingSet = new Set(entity.observations);\r\n const newObs = observations.filter((o: string) => !existingSet.has(o));\r\n entity.observations.push(...newObs);\r\n entity.lastModified = timestamp;\r\n result.entitiesUpdated++;\r\n break;\r\n }\r\n\r\n case 'deleteObservations': {\r\n const { name, observations } = operation.data;\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity) {\r\n throw new KnowledgeGraphError(`Entity \"${name}\" not found`, 'ENTITY_NOT_FOUND');\r\n }\r\n const toDelete = new Set(observations);\r\n entity.observations = entity.observations.filter((o: string) => !toDelete.has(o));\r\n entity.lastModified = timestamp;\r\n result.entitiesUpdated++;\r\n break;\r\n }\r\n\r\n default: {\r\n const _exhaustiveCheck: never = operation;\r\n throw new KnowledgeGraphError(\r\n `Unknown batch operation type: ${(_exhaustiveCheck as BatchOperation).type}`,\r\n 'UNKNOWN_OPERATION'\r\n );\r\n }\r\n }\r\n }\r\n}\r\n","/**\r\n * Graph Event Emitter\r\n *\r\n * Phase 10 Sprint 2: Provides event-based notifications for graph changes.\r\n * Enables loose coupling between graph operations and dependent systems\r\n * like search indexes, analytics, and external integrations.\r\n *\r\n * @module core/GraphEventEmitter\r\n */\r\n\r\nimport type {\r\n GraphEventType,\r\n GraphEvent,\r\n GraphEventListener,\r\n GraphEventMap,\r\n Entity,\r\n Relation,\r\n EntityCreatedEvent,\r\n EntityUpdatedEvent,\r\n EntityDeletedEvent,\r\n RelationCreatedEvent,\r\n RelationDeletedEvent,\r\n ObservationAddedEvent,\r\n ObservationDeletedEvent,\r\n GraphSavedEvent,\r\n GraphLoadedEvent,\r\n} from '../types/index.js';\r\n\r\n/**\r\n * Phase 10 Sprint 2: Event emitter for graph change notifications.\r\n *\r\n * Provides a type-safe event system for subscribing to and emitting\r\n * graph change events. Supports wildcard listeners for all events.\r\n *\r\n * @example\r\n * ```typescript\r\n * const emitter = new GraphEventEmitter();\r\n *\r\n * // Listen to specific event types\r\n * emitter.on('entity:created', (event) => {\r\n * console.log(`Entity ${event.entity.name} created`);\r\n * });\r\n *\r\n * // Listen to all events\r\n * emitter.onAny((event) => {\r\n * console.log(`Event: ${event.type}`);\r\n * });\r\n *\r\n * // Emit an event\r\n * emitter.emitEntityCreated(entity);\r\n *\r\n * // Remove listener\r\n * const unsubscribe = emitter.on('entity:deleted', handler);\r\n * unsubscribe();\r\n * ```\r\n */\r\nexport class GraphEventEmitter {\r\n /**\r\n * Map of event types to their registered listeners.\r\n */\r\n private listeners: Map<GraphEventType, Set<GraphEventListener<any>>> = new Map();\r\n\r\n /**\r\n * Listeners that receive all events regardless of type.\r\n */\r\n private wildcardListeners: Set<GraphEventListener<GraphEvent>> = new Set();\r\n\r\n /**\r\n * Whether to suppress errors from listeners (default: true).\r\n * When true, listener errors are logged but don't stop event propagation.\r\n */\r\n private suppressListenerErrors: boolean = true;\r\n\r\n /**\r\n * Create a new GraphEventEmitter instance.\r\n *\r\n * @param options - Optional configuration\r\n */\r\n constructor(options?: { suppressListenerErrors?: boolean }) {\r\n if (options?.suppressListenerErrors !== undefined) {\r\n this.suppressListenerErrors = options.suppressListenerErrors;\r\n }\r\n }\r\n\r\n // ==================== Subscription Methods ====================\r\n\r\n /**\r\n * Subscribe to a specific event type.\r\n *\r\n * @template K - The event type key\r\n * @param eventType - The event type to listen for\r\n * @param listener - Callback function to invoke when event occurs\r\n * @returns Unsubscribe function to remove the listener\r\n *\r\n * @example\r\n * ```typescript\r\n * const unsubscribe = emitter.on('entity:created', (event) => {\r\n * console.log(`Created: ${event.entity.name}`);\r\n * });\r\n *\r\n * // Later: unsubscribe();\r\n * ```\r\n */\r\n on<K extends GraphEventType>(\r\n eventType: K,\r\n listener: GraphEventListener<GraphEventMap[K]>\r\n ): () => void {\r\n if (!this.listeners.has(eventType)) {\r\n this.listeners.set(eventType, new Set());\r\n }\r\n this.listeners.get(eventType)!.add(listener);\r\n\r\n // Return unsubscribe function\r\n return () => {\r\n this.off(eventType, listener);\r\n };\r\n }\r\n\r\n /**\r\n * Unsubscribe from a specific event type.\r\n *\r\n * @template K - The event type key\r\n * @param eventType - The event type to unsubscribe from\r\n * @param listener - The listener function to remove\r\n */\r\n off<K extends GraphEventType>(\r\n eventType: K,\r\n listener: GraphEventListener<GraphEventMap[K]>\r\n ): void {\r\n const listeners = this.listeners.get(eventType);\r\n if (listeners) {\r\n listeners.delete(listener);\r\n }\r\n }\r\n\r\n /**\r\n * Subscribe to all event types.\r\n *\r\n * @param listener - Callback function to invoke for any event\r\n * @returns Unsubscribe function to remove the listener\r\n *\r\n * @example\r\n * ```typescript\r\n * emitter.onAny((event) => {\r\n * console.log(`Event: ${event.type} at ${event.timestamp}`);\r\n * });\r\n * ```\r\n */\r\n onAny(listener: GraphEventListener<GraphEvent>): () => void {\r\n this.wildcardListeners.add(listener);\r\n return () => {\r\n this.offAny(listener);\r\n };\r\n }\r\n\r\n /**\r\n * Unsubscribe from all events.\r\n *\r\n * @param listener - The listener function to remove\r\n */\r\n offAny(listener: GraphEventListener<GraphEvent>): void {\r\n this.wildcardListeners.delete(listener);\r\n }\r\n\r\n /**\r\n * Subscribe to an event type, but only receive the first occurrence.\r\n *\r\n * @template K - The event type key\r\n * @param eventType - The event type to listen for once\r\n * @param listener - Callback function to invoke once\r\n * @returns Unsubscribe function to cancel before event occurs\r\n */\r\n once<K extends GraphEventType>(\r\n eventType: K,\r\n listener: GraphEventListener<GraphEventMap[K]>\r\n ): () => void {\r\n const wrappedListener = ((event: GraphEventMap[K]) => {\r\n this.off(eventType, wrappedListener);\r\n listener(event);\r\n }) as GraphEventListener<GraphEventMap[K]>;\r\n\r\n return this.on(eventType, wrappedListener);\r\n }\r\n\r\n /**\r\n * Remove all listeners for all event types.\r\n */\r\n removeAllListeners(): void {\r\n this.listeners.clear();\r\n this.wildcardListeners.clear();\r\n }\r\n\r\n /**\r\n * Get the count of listeners for a specific event type.\r\n *\r\n * @param eventType - The event type to count listeners for\r\n * @returns Number of listeners registered\r\n */\r\n listenerCount(eventType?: GraphEventType): number {\r\n if (eventType) {\r\n return (this.listeners.get(eventType)?.size ?? 0) + this.wildcardListeners.size;\r\n }\r\n // Count all listeners\r\n let count = this.wildcardListeners.size;\r\n for (const listeners of this.listeners.values()) {\r\n count += listeners.size;\r\n }\r\n return count;\r\n }\r\n\r\n // ==================== Emit Methods ====================\r\n\r\n /**\r\n * Emit an event to all registered listeners.\r\n *\r\n * @param event - The event to emit\r\n */\r\n emit(event: GraphEvent): void {\r\n // Notify type-specific listeners\r\n const typeListeners = this.listeners.get(event.type);\r\n if (typeListeners) {\r\n for (const listener of typeListeners) {\r\n this.invokeListener(listener, event);\r\n }\r\n }\r\n\r\n // Notify wildcard listeners\r\n for (const listener of this.wildcardListeners) {\r\n this.invokeListener(listener, event);\r\n }\r\n }\r\n\r\n /**\r\n * Emit an entity:created event.\r\n *\r\n * @param entity - The entity that was created\r\n */\r\n emitEntityCreated(entity: Entity): void {\r\n const event: EntityCreatedEvent = {\r\n type: 'entity:created',\r\n timestamp: new Date().toISOString(),\r\n entity,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit an entity:updated event.\r\n *\r\n * @param entityName - Name of the updated entity\r\n * @param changes - The changes that were applied\r\n * @param previousValues - Optional previous values before update\r\n */\r\n emitEntityUpdated(\r\n entityName: string,\r\n changes: Partial<Entity>,\r\n previousValues?: Partial<Entity>\r\n ): void {\r\n const event: EntityUpdatedEvent = {\r\n type: 'entity:updated',\r\n timestamp: new Date().toISOString(),\r\n entityName,\r\n changes,\r\n previousValues,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit an entity:deleted event.\r\n *\r\n * @param entityName - Name of the deleted entity\r\n * @param entity - Optional entity data before deletion\r\n */\r\n emitEntityDeleted(entityName: string, entity?: Entity): void {\r\n const event: EntityDeletedEvent = {\r\n type: 'entity:deleted',\r\n timestamp: new Date().toISOString(),\r\n entityName,\r\n entity,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit a relation:created event.\r\n *\r\n * @param relation - The relation that was created\r\n */\r\n emitRelationCreated(relation: Relation): void {\r\n const event: RelationCreatedEvent = {\r\n type: 'relation:created',\r\n timestamp: new Date().toISOString(),\r\n relation,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit a relation:deleted event.\r\n *\r\n * @param from - Source entity name\r\n * @param to - Target entity name\r\n * @param relationType - Type of the deleted relation\r\n */\r\n emitRelationDeleted(from: string, to: string, relationType: string): void {\r\n const event: RelationDeletedEvent = {\r\n type: 'relation:deleted',\r\n timestamp: new Date().toISOString(),\r\n from,\r\n to,\r\n relationType,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit an observation:added event.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param observations - Observations that were added\r\n */\r\n emitObservationAdded(entityName: string, observations: string[]): void {\r\n if (observations.length === 0) return;\r\n\r\n const event: ObservationAddedEvent = {\r\n type: 'observation:added',\r\n timestamp: new Date().toISOString(),\r\n entityName,\r\n observations,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit an observation:deleted event.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param observations - Observations that were deleted\r\n */\r\n emitObservationDeleted(entityName: string, observations: string[]): void {\r\n if (observations.length === 0) return;\r\n\r\n const event: ObservationDeletedEvent = {\r\n type: 'observation:deleted',\r\n timestamp: new Date().toISOString(),\r\n entityName,\r\n observations,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit a graph:saved event.\r\n *\r\n * @param entityCount - Number of entities in the saved graph\r\n * @param relationCount - Number of relations in the saved graph\r\n */\r\n emitGraphSaved(entityCount: number, relationCount: number): void {\r\n const event: GraphSavedEvent = {\r\n type: 'graph:saved',\r\n timestamp: new Date().toISOString(),\r\n entityCount,\r\n relationCount,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n /**\r\n * Emit a graph:loaded event.\r\n *\r\n * @param entityCount - Number of entities in the loaded graph\r\n * @param relationCount - Number of relations in the loaded graph\r\n */\r\n emitGraphLoaded(entityCount: number, relationCount: number): void {\r\n const event: GraphLoadedEvent = {\r\n type: 'graph:loaded',\r\n timestamp: new Date().toISOString(),\r\n entityCount,\r\n relationCount,\r\n };\r\n this.emit(event);\r\n }\r\n\r\n // ==================== Helper Methods ====================\r\n\r\n /**\r\n * Safely invoke a listener, optionally catching errors.\r\n * @private\r\n */\r\n private invokeListener(listener: GraphEventListener<any>, event: GraphEvent): void {\r\n if (this.suppressListenerErrors) {\r\n try {\r\n listener(event);\r\n } catch (error) {\r\n // Log but don't propagate errors from listeners\r\n console.error(`GraphEventEmitter: Listener error for ${event.type}:`, error);\r\n }\r\n } else {\r\n listener(event);\r\n }\r\n }\r\n}\r\n","/**\r\n * SQLite Storage\r\n *\r\n * Handles storage operations for the knowledge graph using better-sqlite3 (native SQLite).\r\n * Implements IGraphStorage interface for storage abstraction.\r\n *\r\n * Benefits over sql.js (WASM):\r\n * - 3-10x faster than WASM-based SQLite\r\n * - Native FTS5 full-text search support\r\n * - ACID transactions with proper durability\r\n * - Concurrent read access support\r\n * - No memory overhead from WASM runtime\r\n * - Direct disk I/O (no manual export/import)\r\n *\r\n * Features:\r\n * - Built-in indexes for O(1) lookups\r\n * - Referential integrity with ON DELETE CASCADE\r\n * - FTS5 full-text search on entity names and observations\r\n *\r\n * @module core/SQLiteStorage\r\n */\r\n\r\nimport Database from 'better-sqlite3';\r\nimport type { Database as DatabaseType } from 'better-sqlite3';\r\nimport { Mutex } from 'async-mutex';\r\nimport type { KnowledgeGraph, Entity, Relation, ReadonlyKnowledgeGraph, IGraphStorage, LowercaseData } from '../types/index.js';\r\nimport { clearAllSearchCaches } from '../utils/searchCache.js';\r\nimport { NameIndex, TypeIndex } from '../utils/indexes.js';\r\nimport { sanitizeObject } from '../utils/index.js';\r\n\r\n/**\r\n * SQLiteStorage manages persistence of the knowledge graph using native SQLite.\r\n *\r\n * Uses better-sqlite3 for native SQLite bindings with full FTS5 support,\r\n * referential integrity, and proper ACID transactions.\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new SQLiteStorage('/path/to/memory.db');\r\n * await storage.ensureLoaded();\r\n * const graph = await storage.loadGraph();\r\n * ```\r\n */\r\nexport class SQLiteStorage implements IGraphStorage {\r\n /**\r\n * Mutex for thread-safe access to storage operations.\r\n * Prevents concurrent writes from corrupting the cache.\r\n * Note: SQLite itself handles file-level locking, but we need\r\n * to protect our in-memory cache and index operations.\r\n */\r\n private mutex = new Mutex();\r\n\r\n /**\r\n * SQLite database instance.\r\n */\r\n private db: DatabaseType | null = null;\r\n\r\n /**\r\n * Whether the database has been initialized.\r\n */\r\n private initialized: boolean = false;\r\n\r\n /**\r\n * In-memory cache for fast read operations.\r\n * Synchronized with SQLite on writes.\r\n */\r\n private cache: KnowledgeGraph | null = null;\r\n\r\n /**\r\n * O(1) entity lookup by name.\r\n */\r\n private nameIndex: NameIndex = new NameIndex();\r\n\r\n /**\r\n * O(1) entity lookup by type.\r\n */\r\n private typeIndex: TypeIndex = new TypeIndex();\r\n\r\n /**\r\n * Pre-computed lowercase data for search optimization.\r\n */\r\n private lowercaseCache: Map<string, LowercaseData> = new Map();\r\n\r\n /**\r\n * Pending changes counter for batching disk writes.\r\n * Note: better-sqlite3 writes to disk immediately, but we track for API compatibility.\r\n */\r\n private pendingChanges: number = 0;\r\n\r\n /**\r\n * Phase 4 Sprint 1: Bidirectional relation cache for O(1) repeated lookups.\r\n * Maps entity name -> all relations involving that entity (both incoming and outgoing).\r\n */\r\n private bidirectionalRelationCache: Map<string, Relation[]> = new Map();\r\n\r\n /**\r\n * Create a new SQLiteStorage instance.\r\n *\r\n * @param dbFilePath - Absolute path to the SQLite database file\r\n */\r\n constructor(private dbFilePath: string) {}\r\n\r\n /**\r\n * Initialize the database connection and schema.\r\n */\r\n private initialize(): void {\r\n if (this.initialized) return;\r\n\r\n // Open database (creates file if it doesn't exist)\r\n this.db = new Database(this.dbFilePath);\r\n\r\n // Enable foreign keys and WAL mode for better performance\r\n this.db.pragma('foreign_keys = ON');\r\n this.db.pragma('journal_mode = WAL');\r\n\r\n // Create tables and indexes\r\n this.createTables();\r\n\r\n // Load cache from database\r\n this.loadCache();\r\n\r\n this.initialized = true;\r\n }\r\n\r\n /**\r\n * Create database tables, indexes, and FTS5 virtual table.\r\n */\r\n private createTables(): void {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n // Entities table with referential integrity for parentId\r\n this.db.exec(`\r\n CREATE TABLE IF NOT EXISTS entities (\r\n name TEXT PRIMARY KEY,\r\n entityType TEXT NOT NULL,\r\n observations TEXT NOT NULL,\r\n tags TEXT,\r\n importance INTEGER,\r\n parentId TEXT REFERENCES entities(name) ON DELETE SET NULL,\r\n createdAt TEXT NOT NULL,\r\n lastModified TEXT NOT NULL\r\n )\r\n `);\r\n\r\n // Relations table with referential integrity (CASCADE delete)\r\n this.db.exec(`\r\n CREATE TABLE IF NOT EXISTS relations (\r\n fromEntity TEXT NOT NULL REFERENCES entities(name) ON DELETE CASCADE,\r\n toEntity TEXT NOT NULL REFERENCES entities(name) ON DELETE CASCADE,\r\n relationType TEXT NOT NULL,\r\n createdAt TEXT NOT NULL,\r\n lastModified TEXT NOT NULL,\r\n PRIMARY KEY (fromEntity, toEntity, relationType)\r\n )\r\n `);\r\n\r\n // Indexes for fast lookups\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_type ON entities(entityType)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_parent ON entities(parentId)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_relation_from ON relations(fromEntity)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_relation_to ON relations(toEntity)`);\r\n\r\n // Phase 4 Sprint 1: Additional indexes for range queries (O(n) -> O(log n))\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_importance ON entities(importance)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_lastmodified ON entities(lastModified)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_createdat ON entities(createdAt)`);\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_relation_type ON relations(relationType)`);\r\n\r\n // Composite index for common query patterns (type + importance filtering)\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_entity_type_importance ON entities(entityType, importance)`);\r\n\r\n // FTS5 virtual table for full-text search\r\n // content='' makes it an external content table (we manage content ourselves)\r\n this.db.exec(`\r\n CREATE VIRTUAL TABLE IF NOT EXISTS entities_fts USING fts5(\r\n name,\r\n entityType,\r\n observations,\r\n tags,\r\n content='entities',\r\n content_rowid='rowid'\r\n )\r\n `);\r\n\r\n // Triggers to keep FTS5 index in sync with entities table\r\n this.db.exec(`\r\n CREATE TRIGGER IF NOT EXISTS entities_ai AFTER INSERT ON entities BEGIN\r\n INSERT INTO entities_fts(rowid, name, entityType, observations, tags)\r\n VALUES (NEW.rowid, NEW.name, NEW.entityType, NEW.observations, NEW.tags);\r\n END\r\n `);\r\n\r\n this.db.exec(`\r\n CREATE TRIGGER IF NOT EXISTS entities_ad AFTER DELETE ON entities BEGIN\r\n INSERT INTO entities_fts(entities_fts, rowid, name, entityType, observations, tags)\r\n VALUES ('delete', OLD.rowid, OLD.name, OLD.entityType, OLD.observations, OLD.tags);\r\n END\r\n `);\r\n\r\n this.db.exec(`\r\n CREATE TRIGGER IF NOT EXISTS entities_au AFTER UPDATE ON entities BEGIN\r\n INSERT INTO entities_fts(entities_fts, rowid, name, entityType, observations, tags)\r\n VALUES ('delete', OLD.rowid, OLD.name, OLD.entityType, OLD.observations, OLD.tags);\r\n INSERT INTO entities_fts(rowid, name, entityType, observations, tags)\r\n VALUES (NEW.rowid, NEW.name, NEW.entityType, NEW.observations, NEW.tags);\r\n END\r\n `);\r\n }\r\n\r\n /**\r\n * Load all data from SQLite into memory cache.\r\n */\r\n private loadCache(): void {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n const entities: Entity[] = [];\r\n const relations: Relation[] = [];\r\n\r\n // Load entities\r\n const entityRows = this.db.prepare(`SELECT * FROM entities`).all() as EntityRow[];\r\n for (const row of entityRows) {\r\n const entity = this.rowToEntity(row);\r\n entities.push(entity);\r\n this.updateLowercaseCache(entity);\r\n }\r\n\r\n // Load relations\r\n const relationRows = this.db.prepare(`SELECT * FROM relations`).all() as RelationRow[];\r\n for (const row of relationRows) {\r\n relations.push(this.rowToRelation(row));\r\n }\r\n\r\n this.cache = { entities, relations };\r\n\r\n // Build indexes for O(1) lookups\r\n this.nameIndex.build(entities);\r\n this.typeIndex.build(entities);\r\n }\r\n\r\n /**\r\n * Convert a database row to an Entity object.\r\n */\r\n private rowToEntity(row: EntityRow): Entity {\r\n return {\r\n name: row.name,\r\n entityType: row.entityType,\r\n observations: JSON.parse(row.observations),\r\n tags: row.tags ? JSON.parse(row.tags) : undefined,\r\n importance: row.importance ?? undefined,\r\n parentId: row.parentId ?? undefined,\r\n createdAt: row.createdAt,\r\n lastModified: row.lastModified,\r\n };\r\n }\r\n\r\n /**\r\n * Convert a database row to a Relation object.\r\n */\r\n private rowToRelation(row: RelationRow): Relation {\r\n return {\r\n from: row.fromEntity,\r\n to: row.toEntity,\r\n relationType: row.relationType,\r\n createdAt: row.createdAt,\r\n lastModified: row.lastModified,\r\n };\r\n }\r\n\r\n /**\r\n * Update lowercase cache for an entity.\r\n */\r\n private updateLowercaseCache(entity: Entity): void {\r\n this.lowercaseCache.set(entity.name, {\r\n name: entity.name.toLowerCase(),\r\n entityType: entity.entityType.toLowerCase(),\r\n observations: entity.observations.map(o => o.toLowerCase()),\r\n tags: entity.tags?.map(t => t.toLowerCase()) || [],\r\n });\r\n }\r\n\r\n // ==================== IGraphStorage Implementation ====================\r\n\r\n /**\r\n * Load the knowledge graph (read-only access).\r\n *\r\n * @returns Promise resolving to read-only knowledge graph reference\r\n */\r\n async loadGraph(): Promise<ReadonlyKnowledgeGraph> {\r\n await this.ensureLoaded();\r\n return this.cache!;\r\n }\r\n\r\n /**\r\n * Get a mutable copy of the graph for write operations.\r\n *\r\n * @returns Promise resolving to mutable knowledge graph copy\r\n */\r\n async getGraphForMutation(): Promise<KnowledgeGraph> {\r\n await this.ensureLoaded();\r\n return {\r\n entities: this.cache!.entities.map(e => ({\r\n ...e,\r\n observations: [...e.observations],\r\n tags: e.tags ? [...e.tags] : undefined,\r\n })),\r\n relations: this.cache!.relations.map(r => ({ ...r })),\r\n };\r\n }\r\n\r\n /**\r\n * Ensure the storage is loaded/initialized.\r\n *\r\n * @returns Promise resolving when ready\r\n */\r\n async ensureLoaded(): Promise<void> {\r\n if (!this.initialized) {\r\n this.initialize();\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 1: Invalidate bidirectional relation cache for an entity.\r\n *\r\n * @param entityName - Entity name to invalidate cache for\r\n */\r\n private invalidateBidirectionalCache(entityName: string): void {\r\n this.bidirectionalRelationCache.delete(entityName);\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 1: Clear the entire bidirectional relation cache.\r\n */\r\n private clearBidirectionalCache(): void {\r\n this.bidirectionalRelationCache.clear();\r\n }\r\n\r\n /**\r\n * Save the entire knowledge graph to storage.\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n *\r\n * @param graph - The knowledge graph to save\r\n * @returns Promise resolving when save is complete\r\n */\r\n async saveGraph(graph: KnowledgeGraph): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n // Disable foreign keys for bulk replace operation\r\n // This allows inserting entities with parentId references that may not exist\r\n // and relations with dangling references (which matches the original JSONL behavior)\r\n this.db.pragma('foreign_keys = OFF');\r\n\r\n // Use transaction for atomicity\r\n const transaction = this.db.transaction(() => {\r\n // Clear existing data\r\n this.db!.exec('DELETE FROM relations');\r\n this.db!.exec('DELETE FROM entities');\r\n\r\n // Insert all entities\r\n const entityStmt = this.db!.prepare(`\r\n INSERT INTO entities (name, entityType, observations, tags, importance, parentId, createdAt, lastModified)\r\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\r\n `);\r\n\r\n for (const entity of graph.entities) {\r\n entityStmt.run(\r\n entity.name,\r\n entity.entityType,\r\n JSON.stringify(entity.observations),\r\n entity.tags ? JSON.stringify(entity.tags) : null,\r\n entity.importance ?? null,\r\n entity.parentId ?? null,\r\n entity.createdAt || new Date().toISOString(),\r\n entity.lastModified || new Date().toISOString(),\r\n );\r\n }\r\n\r\n // Insert all relations\r\n const relationStmt = this.db!.prepare(`\r\n INSERT INTO relations (fromEntity, toEntity, relationType, createdAt, lastModified)\r\n VALUES (?, ?, ?, ?, ?)\r\n `);\r\n\r\n for (const relation of graph.relations) {\r\n relationStmt.run(\r\n relation.from,\r\n relation.to,\r\n relation.relationType,\r\n relation.createdAt || new Date().toISOString(),\r\n relation.lastModified || new Date().toISOString(),\r\n );\r\n }\r\n });\r\n\r\n transaction();\r\n\r\n // Re-enable foreign keys for future operations\r\n this.db.pragma('foreign_keys = ON');\r\n\r\n // Update cache\r\n this.cache = graph;\r\n this.lowercaseCache.clear();\r\n for (const entity of graph.entities) {\r\n this.updateLowercaseCache(entity);\r\n }\r\n\r\n // Rebuild indexes\r\n this.nameIndex.build(graph.entities);\r\n this.typeIndex.build(graph.entities);\r\n\r\n this.pendingChanges = 0;\r\n\r\n // Clear search caches\r\n clearAllSearchCaches();\r\n\r\n // Phase 4 Sprint 1: Clear bidirectional relation cache on full save\r\n this.clearBidirectionalCache();\r\n });\r\n }\r\n\r\n /**\r\n * Append a single entity to storage.\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n *\r\n * @param entity - The entity to append\r\n * @returns Promise resolving when append is complete\r\n */\r\n async appendEntity(entity: Entity): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n // Use INSERT OR REPLACE to handle updates\r\n const stmt = this.db.prepare(`\r\n INSERT OR REPLACE INTO entities (name, entityType, observations, tags, importance, parentId, createdAt, lastModified)\r\n VALUES (?, ?, ?, ?, ?, ?, ?, ?)\r\n `);\r\n\r\n stmt.run(\r\n entity.name,\r\n entity.entityType,\r\n JSON.stringify(entity.observations),\r\n entity.tags ? JSON.stringify(entity.tags) : null,\r\n entity.importance ?? null,\r\n entity.parentId ?? null,\r\n entity.createdAt || new Date().toISOString(),\r\n entity.lastModified || new Date().toISOString(),\r\n );\r\n\r\n // Update cache\r\n const existingIndex = this.cache!.entities.findIndex(e => e.name === entity.name);\r\n if (existingIndex >= 0) {\r\n this.cache!.entities[existingIndex] = entity;\r\n } else {\r\n this.cache!.entities.push(entity);\r\n }\r\n\r\n // Update indexes\r\n this.nameIndex.add(entity);\r\n this.typeIndex.add(entity);\r\n this.updateLowercaseCache(entity);\r\n clearAllSearchCaches();\r\n\r\n this.pendingChanges++;\r\n });\r\n }\r\n\r\n /**\r\n * Append a single relation to storage.\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n *\r\n * @param relation - The relation to append\r\n * @returns Promise resolving when append is complete\r\n */\r\n async appendRelation(relation: Relation): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n // Use INSERT OR REPLACE to handle updates\r\n const stmt = this.db.prepare(`\r\n INSERT OR REPLACE INTO relations (fromEntity, toEntity, relationType, createdAt, lastModified)\r\n VALUES (?, ?, ?, ?, ?)\r\n `);\r\n\r\n stmt.run(\r\n relation.from,\r\n relation.to,\r\n relation.relationType,\r\n relation.createdAt || new Date().toISOString(),\r\n relation.lastModified || new Date().toISOString(),\r\n );\r\n\r\n // Update cache\r\n const existingIndex = this.cache!.relations.findIndex(\r\n r => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType\r\n );\r\n if (existingIndex >= 0) {\r\n this.cache!.relations[existingIndex] = relation;\r\n } else {\r\n this.cache!.relations.push(relation);\r\n }\r\n\r\n clearAllSearchCaches();\r\n\r\n // Phase 4 Sprint 1: Invalidate bidirectional cache for both entities\r\n this.invalidateBidirectionalCache(relation.from);\r\n this.invalidateBidirectionalCache(relation.to);\r\n\r\n this.pendingChanges++;\r\n });\r\n }\r\n\r\n /**\r\n * Update an entity in storage.\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent write operations.\r\n *\r\n * @param entityName - Name of the entity to update\r\n * @param updates - Partial entity updates to apply\r\n * @returns Promise resolving to true if found and updated\r\n */\r\n async updateEntity(entityName: string, updates: Partial<Entity>): Promise<boolean> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n // Find entity in cache using index (O(1))\r\n const entity = this.nameIndex.get(entityName);\r\n if (!entity) {\r\n return false;\r\n }\r\n\r\n // Track old type for index update\r\n const oldType = entity.entityType;\r\n\r\n // Apply updates to cached entity (sanitized to prevent prototype pollution)\r\n Object.assign(entity, sanitizeObject(updates as Record<string, unknown>));\r\n entity.lastModified = new Date().toISOString();\r\n\r\n // Update in database\r\n const stmt = this.db.prepare(`\r\n UPDATE entities SET\r\n entityType = ?,\r\n observations = ?,\r\n tags = ?,\r\n importance = ?,\r\n parentId = ?,\r\n lastModified = ?\r\n WHERE name = ?\r\n `);\r\n\r\n stmt.run(\r\n entity.entityType,\r\n JSON.stringify(entity.observations),\r\n entity.tags ? JSON.stringify(entity.tags) : null,\r\n entity.importance ?? null,\r\n entity.parentId ?? null,\r\n entity.lastModified,\r\n entityName,\r\n );\r\n\r\n // Update indexes\r\n this.nameIndex.add(entity); // Update reference\r\n if (updates.entityType && updates.entityType !== oldType) {\r\n this.typeIndex.updateType(entityName, oldType, updates.entityType);\r\n }\r\n this.updateLowercaseCache(entity);\r\n clearAllSearchCaches();\r\n\r\n this.pendingChanges++;\r\n\r\n return true;\r\n });\r\n }\r\n\r\n /**\r\n * Compact the storage (runs VACUUM to reclaim space).\r\n *\r\n * THREAD-SAFE: Uses mutex to prevent concurrent operations.\r\n *\r\n * @returns Promise resolving when compaction is complete\r\n */\r\n async compact(): Promise<void> {\r\n await this.ensureLoaded();\r\n\r\n return this.mutex.runExclusive(async () => {\r\n if (!this.db) return;\r\n\r\n // Run SQLite VACUUM to reclaim space and defragment\r\n this.db.exec('VACUUM');\r\n\r\n // Rebuild FTS index for optimal search performance\r\n this.db.exec(`INSERT INTO entities_fts(entities_fts) VALUES('rebuild')`);\r\n\r\n this.pendingChanges = 0;\r\n });\r\n }\r\n\r\n /**\r\n * Clear any in-memory cache.\r\n */\r\n clearCache(): void {\r\n this.cache = null;\r\n this.nameIndex.clear();\r\n this.typeIndex.clear();\r\n this.lowercaseCache.clear();\r\n // Phase 4 Sprint 1: Clear bidirectional relation cache\r\n this.bidirectionalRelationCache.clear();\r\n this.initialized = false;\r\n if (this.db) {\r\n this.db.close();\r\n this.db = null;\r\n }\r\n }\r\n\r\n // ==================== Index Operations ====================\r\n\r\n /**\r\n * Get an entity by name in O(1) time.\r\n *\r\n * OPTIMIZED: Uses NameIndex for constant-time lookup.\r\n *\r\n * @param name - Entity name to look up\r\n * @returns Entity if found, undefined otherwise\r\n */\r\n getEntityByName(name: string): Entity | undefined {\r\n return this.nameIndex.get(name);\r\n }\r\n\r\n /**\r\n * Check if an entity exists by name in O(1) time.\r\n *\r\n * @param name - Entity name to check\r\n * @returns True if entity exists\r\n */\r\n hasEntity(name: string): boolean {\r\n return this.nameIndex.has(name);\r\n }\r\n\r\n /**\r\n * Get all entities of a given type in O(1) time.\r\n *\r\n * OPTIMIZED: Uses TypeIndex for constant-time lookup of entity names,\r\n * then uses NameIndex for O(1) entity retrieval.\r\n *\r\n * @param entityType - Entity type to filter by (case-insensitive)\r\n * @returns Array of entities with the given type\r\n */\r\n getEntitiesByType(entityType: string): Entity[] {\r\n const names = this.typeIndex.getNames(entityType);\r\n const entities: Entity[] = [];\r\n for (const name of names) {\r\n const entity = this.nameIndex.get(name);\r\n if (entity) {\r\n entities.push(entity);\r\n }\r\n }\r\n return entities;\r\n }\r\n\r\n /**\r\n * Get all unique entity types in the graph.\r\n *\r\n * @returns Array of unique entity types (lowercase)\r\n */\r\n getEntityTypes(): string[] {\r\n return this.typeIndex.getTypes();\r\n }\r\n\r\n /**\r\n * Get pre-computed lowercase data for an entity.\r\n *\r\n * @param entityName - Entity name to get lowercase data for\r\n * @returns LowercaseData if entity exists, undefined otherwise\r\n */\r\n getLowercased(entityName: string): LowercaseData | undefined {\r\n return this.lowercaseCache.get(entityName);\r\n }\r\n\r\n // ==================== FTS5 Full-Text Search ====================\r\n\r\n /**\r\n * Perform full-text search using FTS5.\r\n *\r\n * @param query - Search query (supports FTS5 query syntax)\r\n * @returns Array of matching entity names with relevance scores\r\n */\r\n fullTextSearch(query: string): Array<{ name: string; score: number }> {\r\n if (!this.db || !this.initialized) return [];\r\n\r\n try {\r\n // Use FTS5 MATCH for full-text search with BM25 ranking\r\n const stmt = this.db.prepare(`\r\n SELECT name, bm25(entities_fts, 10, 5, 3, 1) as score\r\n FROM entities_fts\r\n WHERE entities_fts MATCH ?\r\n ORDER BY score\r\n LIMIT 100\r\n `);\r\n\r\n const results = stmt.all(query) as Array<{ name: string; score: number }>;\r\n return results;\r\n } catch {\r\n // If FTS query fails (invalid syntax), fall back to empty results\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Perform a simple text search (LIKE-based, case-insensitive).\r\n *\r\n * @param searchTerm - Term to search for\r\n * @returns Array of matching entity names\r\n */\r\n simpleSearch(searchTerm: string): string[] {\r\n if (!this.db || !this.initialized) return [];\r\n\r\n const pattern = `%${searchTerm}%`;\r\n const stmt = this.db.prepare(`\r\n SELECT name FROM entities\r\n WHERE name LIKE ? COLLATE NOCASE\r\n OR entityType LIKE ? COLLATE NOCASE\r\n OR observations LIKE ? COLLATE NOCASE\r\n OR tags LIKE ? COLLATE NOCASE\r\n `);\r\n\r\n const results = stmt.all(pattern, pattern, pattern, pattern) as Array<{ name: string }>;\r\n return results.map(r => r.name);\r\n }\r\n\r\n // ==================== Utility Operations ====================\r\n\r\n /**\r\n * Get the storage path/location.\r\n *\r\n * @returns The storage path\r\n */\r\n getFilePath(): string {\r\n return this.dbFilePath;\r\n }\r\n\r\n /**\r\n * Get the current pending changes count.\r\n *\r\n * @returns Number of pending changes since last reset\r\n */\r\n getPendingAppends(): number {\r\n return this.pendingChanges;\r\n }\r\n\r\n /**\r\n * Force persistence to disk (no-op for better-sqlite3 as it writes immediately).\r\n *\r\n * @returns Promise resolving when persistence is complete\r\n */\r\n async flush(): Promise<void> {\r\n // better-sqlite3 writes to disk immediately, but we run a checkpoint for WAL mode\r\n if (this.db) {\r\n this.db.pragma('wal_checkpoint(TRUNCATE)');\r\n }\r\n this.pendingChanges = 0;\r\n }\r\n\r\n /**\r\n * Close the database connection.\r\n */\r\n close(): void {\r\n if (this.db) {\r\n this.db.close();\r\n this.db = null;\r\n }\r\n this.initialized = false;\r\n }\r\n\r\n // ==================== Relation Index Operations ====================\r\n\r\n /**\r\n * Get all relations where the entity is the source (outgoing relations).\r\n *\r\n * OPTIMIZED: Uses SQLite index on fromEntity for O(log n) lookup.\r\n *\r\n * @param entityName - Entity name to look up outgoing relations for\r\n * @returns Array of relations where entity is the source\r\n */\r\n getRelationsFrom(entityName: string): Relation[] {\r\n // Check cache first\r\n if (this.cache) {\r\n return this.cache.relations.filter(r => r.from === entityName);\r\n }\r\n\r\n // Fall back to database query\r\n if (!this.db || !this.initialized) return [];\r\n const stmt = this.db.prepare(\r\n 'SELECT fromEntity, toEntity, relationType, createdAt, lastModified FROM relations WHERE fromEntity = ?'\r\n );\r\n const rows = stmt.all(entityName) as RelationRow[];\r\n return rows.map(row => ({\r\n from: row.fromEntity,\r\n to: row.toEntity,\r\n relationType: row.relationType,\r\n createdAt: row.createdAt,\r\n lastModified: row.lastModified,\r\n }));\r\n }\r\n\r\n /**\r\n * Get all relations where the entity is the target (incoming relations).\r\n *\r\n * OPTIMIZED: Uses SQLite index on toEntity for O(log n) lookup.\r\n *\r\n * @param entityName - Entity name to look up incoming relations for\r\n * @returns Array of relations where entity is the target\r\n */\r\n getRelationsTo(entityName: string): Relation[] {\r\n // Check cache first\r\n if (this.cache) {\r\n return this.cache.relations.filter(r => r.to === entityName);\r\n }\r\n\r\n // Fall back to database query\r\n if (!this.db || !this.initialized) return [];\r\n const stmt = this.db.prepare(\r\n 'SELECT fromEntity, toEntity, relationType, createdAt, lastModified FROM relations WHERE toEntity = ?'\r\n );\r\n const rows = stmt.all(entityName) as RelationRow[];\r\n return rows.map(row => ({\r\n from: row.fromEntity,\r\n to: row.toEntity,\r\n relationType: row.relationType,\r\n createdAt: row.createdAt,\r\n lastModified: row.lastModified,\r\n }));\r\n }\r\n\r\n /**\r\n * Get all relations involving the entity (both incoming and outgoing).\r\n *\r\n * OPTIMIZED: Phase 4 Sprint 1 - Uses bidirectional cache for O(1) repeated lookups.\r\n *\r\n * @param entityName - Entity name to look up all relations for\r\n * @returns Array of all relations involving the entity\r\n */\r\n getRelationsFor(entityName: string): Relation[] {\r\n // Phase 4 Sprint 1: Check bidirectional cache first for O(1) repeated lookups\r\n const cached = this.bidirectionalRelationCache.get(entityName);\r\n if (cached !== undefined) {\r\n return cached;\r\n }\r\n\r\n // Check main cache and compute result\r\n let relations: Relation[];\r\n if (this.cache) {\r\n relations = this.cache.relations.filter(r => r.from === entityName || r.to === entityName);\r\n } else if (this.db && this.initialized) {\r\n // Fall back to database query\r\n const stmt = this.db.prepare(\r\n 'SELECT fromEntity, toEntity, relationType, createdAt, lastModified FROM relations WHERE fromEntity = ? OR toEntity = ?'\r\n );\r\n const rows = stmt.all(entityName, entityName) as RelationRow[];\r\n relations = rows.map(row => ({\r\n from: row.fromEntity,\r\n to: row.toEntity,\r\n relationType: row.relationType,\r\n createdAt: row.createdAt,\r\n lastModified: row.lastModified,\r\n }));\r\n } else {\r\n return [];\r\n }\r\n\r\n // Cache the result for O(1) subsequent lookups\r\n this.bidirectionalRelationCache.set(entityName, relations);\r\n return relations;\r\n }\r\n\r\n /**\r\n * Check if an entity has any relations.\r\n *\r\n * @param entityName - Entity name to check\r\n * @returns True if entity has any relations\r\n */\r\n hasRelations(entityName: string): boolean {\r\n // Check cache first\r\n if (this.cache) {\r\n return this.cache.relations.some(r => r.from === entityName || r.to === entityName);\r\n }\r\n\r\n // Fall back to database query\r\n if (!this.db || !this.initialized) return false;\r\n const stmt = this.db.prepare(\r\n 'SELECT 1 FROM relations WHERE fromEntity = ? OR toEntity = ? LIMIT 1'\r\n );\r\n const row = stmt.get(entityName, entityName);\r\n return row !== undefined;\r\n }\r\n\r\n // ==================== Embedding Storage (Phase 4 Sprint 11) ====================\r\n\r\n /**\r\n * Phase 4 Sprint 11: Ensure embeddings table exists.\r\n *\r\n * Creates the embeddings table if it doesn't exist.\r\n * Separate table from entities to avoid schema migration complexity.\r\n */\r\n private ensureEmbeddingsTable(): void {\r\n if (!this.db) throw new Error('Database not initialized');\r\n\r\n this.db.exec(`\r\n CREATE TABLE IF NOT EXISTS embeddings (\r\n entityName TEXT PRIMARY KEY REFERENCES entities(name) ON DELETE CASCADE,\r\n embedding BLOB NOT NULL,\r\n embeddingModel TEXT NOT NULL,\r\n embeddingUpdatedAt TEXT NOT NULL,\r\n dimensions INTEGER NOT NULL\r\n )\r\n `);\r\n\r\n // Index for quick lookup by model\r\n this.db.exec(`CREATE INDEX IF NOT EXISTS idx_embedding_model ON embeddings(embeddingModel)`);\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Store an embedding for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param vector - Embedding vector\r\n * @param model - Model name used for the embedding\r\n */\r\n storeEmbedding(entityName: string, vector: number[], model: string): void {\r\n if (!this.db || !this.initialized) {\r\n throw new Error('Database not initialized');\r\n }\r\n\r\n this.ensureEmbeddingsTable();\r\n\r\n // Convert to Float32Array for efficient storage\r\n const buffer = Buffer.from(new Float32Array(vector).buffer);\r\n\r\n const stmt = this.db.prepare(`\r\n INSERT OR REPLACE INTO embeddings (entityName, embedding, embeddingModel, embeddingUpdatedAt, dimensions)\r\n VALUES (?, ?, ?, ?, ?)\r\n `);\r\n\r\n stmt.run(entityName, buffer, model, new Date().toISOString(), vector.length);\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Get an embedding for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns Embedding vector if found, null otherwise\r\n */\r\n getEmbedding(entityName: string): number[] | null {\r\n if (!this.db || !this.initialized) return null;\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n\r\n const stmt = this.db.prepare(`SELECT embedding FROM embeddings WHERE entityName = ?`);\r\n const row = stmt.get(entityName) as { embedding: Buffer } | undefined;\r\n\r\n if (!row) return null;\r\n\r\n // Convert from Buffer to number array\r\n const float32Array = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.length / 4);\r\n return Array.from(float32Array);\r\n } catch {\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Load all embeddings from storage.\r\n *\r\n * @returns Array of [entityName, vector] pairs\r\n */\r\n async loadAllEmbeddings(): Promise<[string, number[]][]> {\r\n if (!this.db || !this.initialized) return [];\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n\r\n const stmt = this.db.prepare(`SELECT entityName, embedding FROM embeddings`);\r\n const rows = stmt.all() as Array<{ entityName: string; embedding: Buffer }>;\r\n\r\n return rows.map(row => {\r\n const float32Array = new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.length / 4);\r\n return [row.entityName, Array.from(float32Array)] as [string, number[]];\r\n });\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Remove an embedding for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n */\r\n removeEmbedding(entityName: string): void {\r\n if (!this.db || !this.initialized) return;\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n const stmt = this.db.prepare(`DELETE FROM embeddings WHERE entityName = ?`);\r\n stmt.run(entityName);\r\n } catch {\r\n // Ignore errors if table doesn't exist\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Clear all embeddings from storage.\r\n */\r\n clearAllEmbeddings(): void {\r\n if (!this.db || !this.initialized) return;\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n this.db.exec(`DELETE FROM embeddings`);\r\n } catch {\r\n // Ignore errors if table doesn't exist\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Check if an entity has an embedding.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns True if embedding exists\r\n */\r\n hasEmbedding(entityName: string): boolean {\r\n if (!this.db || !this.initialized) return false;\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n const stmt = this.db.prepare(`SELECT 1 FROM embeddings WHERE entityName = ? LIMIT 1`);\r\n const row = stmt.get(entityName);\r\n return row !== undefined;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 11: Get embedding statistics.\r\n *\r\n * @returns Stats about stored embeddings\r\n */\r\n getEmbeddingStats(): { count: number; models: string[] } {\r\n if (!this.db || !this.initialized) {\r\n return { count: 0, models: [] };\r\n }\r\n\r\n try {\r\n this.ensureEmbeddingsTable();\r\n\r\n const countRow = this.db.prepare(`SELECT COUNT(*) as count FROM embeddings`).get() as { count: number };\r\n const modelRows = this.db.prepare(`SELECT DISTINCT embeddingModel FROM embeddings`).all() as Array<{ embeddingModel: string }>;\r\n\r\n return {\r\n count: countRow.count,\r\n models: modelRows.map(r => r.embeddingModel),\r\n };\r\n } catch {\r\n return { count: 0, models: [] };\r\n }\r\n }\r\n}\r\n\r\n// ==================== Type Definitions for Database Rows ====================\r\n\r\ninterface EntityRow {\r\n name: string;\r\n entityType: string;\r\n observations: string;\r\n tags: string | null;\r\n importance: number | null;\r\n parentId: string | null;\r\n createdAt: string;\r\n lastModified: string;\r\n}\r\n\r\ninterface RelationRow {\r\n fromEntity: string;\r\n toEntity: string;\r\n relationType: string;\r\n createdAt: string;\r\n lastModified: string;\r\n}\r\n","/**\r\n * Entity Manager\r\n *\r\n * Handles CRUD operations for entities in the knowledge graph.\r\n * Focused on core entity and tag operations only (Phase 4: Consolidate God Objects).\r\n *\r\n * @module core/EntityManager\r\n */\r\n\r\nimport type { Entity, LongRunningOperationOptions } from '../types/index.js';\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { EntityNotFoundError, InvalidImportanceError, ValidationError } from '../utils/errors.js';\r\nimport {\r\n BatchCreateEntitiesSchema,\r\n UpdateEntitySchema,\r\n EntityNamesSchema,\r\n checkCancellation,\r\n createProgressReporter,\r\n createProgress,\r\n sanitizeObject,\r\n} from '../utils/index.js';\r\nimport { GRAPH_LIMITS } from '../utils/constants.js';\r\n\r\n/**\r\n * Minimum importance value (least important).\r\n * Note: Use IMPORTANCE_RANGE from constants.ts for external access.\r\n */\r\nconst MIN_IMPORTANCE = 0;\r\n\r\n/**\r\n * Maximum importance value (most important).\r\n * Note: Use IMPORTANCE_RANGE from constants.ts for external access.\r\n */\r\nconst MAX_IMPORTANCE = 10;\r\n\r\n/**\r\n * Manages entity operations with automatic timestamp handling.\r\n */\r\nexport class EntityManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Create multiple entities in a single batch operation.\r\n *\r\n * This method performs the following operations:\r\n * - Filters out entities that already exist (duplicate names)\r\n * - Automatically adds createdAt and lastModified timestamps\r\n * - Normalizes all tags to lowercase for consistent searching\r\n * - Validates importance values (must be between 0-10)\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param entities - Array of entities to create. Each entity must have a unique name.\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Promise resolving to array of newly created entities (excludes duplicates)\r\n * @throws {InvalidImportanceError} If any entity has importance outside the valid range [0-10]\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new EntityManager(storage);\r\n *\r\n * // Create single entity\r\n * const results = await manager.createEntities([{\r\n * name: 'Alice',\r\n * entityType: 'person',\r\n * observations: ['Works as engineer', 'Lives in Seattle'],\r\n * importance: 7,\r\n * tags: ['Team', 'Engineering']\r\n * }]);\r\n *\r\n * // Create multiple entities at once\r\n * const users = await manager.createEntities([\r\n * { name: 'Bob', entityType: 'person', observations: [] },\r\n * { name: 'Charlie', entityType: 'person', observations: [] }\r\n * ]);\r\n *\r\n * // With progress tracking and cancellation (Phase 9B)\r\n * const controller = new AbortController();\r\n * const results = await manager.createEntities(largeEntityArray, {\r\n * signal: controller.signal,\r\n * onProgress: (p) => console.log(`${p.percentage}% complete`),\r\n * });\r\n * ```\r\n */\r\n async createEntities(\r\n entities: Entity[],\r\n options?: LongRunningOperationOptions\r\n ): Promise<Entity[]> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'createEntities');\r\n\r\n // Validate input\r\n const validation = BatchCreateEntitiesSchema.safeParse(entities);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid entity data', errors);\r\n }\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n const total = entities.length;\r\n reportProgress?.(createProgress(0, total, 'createEntities'));\r\n\r\n // Use read-only graph for checking existing entities\r\n const readGraph = await this.storage.loadGraph();\r\n const timestamp = new Date().toISOString();\r\n\r\n // Check graph size limits\r\n const entitiesToAdd = entities.filter(e => !readGraph.entities.some(existing => existing.name === e.name));\r\n if (readGraph.entities.length + entitiesToAdd.length > GRAPH_LIMITS.MAX_ENTITIES) {\r\n throw new ValidationError(\r\n 'Graph size limit exceeded',\r\n [`Adding ${entitiesToAdd.length} entities would exceed maximum of ${GRAPH_LIMITS.MAX_ENTITIES} entities`]\r\n );\r\n }\r\n\r\n // Check for cancellation before processing\r\n checkCancellation(options?.signal, 'createEntities');\r\n\r\n const newEntities: Entity[] = [];\r\n let processed = 0;\r\n\r\n for (const e of entitiesToAdd) {\r\n // Check for cancellation periodically\r\n checkCancellation(options?.signal, 'createEntities');\r\n\r\n const entity: Entity = {\r\n ...e,\r\n createdAt: e.createdAt || timestamp,\r\n lastModified: e.lastModified || timestamp,\r\n };\r\n\r\n // Normalize tags to lowercase\r\n if (e.tags) {\r\n entity.tags = e.tags.map(tag => tag.toLowerCase());\r\n }\r\n\r\n // Validate importance\r\n if (e.importance !== undefined) {\r\n if (e.importance < MIN_IMPORTANCE || e.importance > MAX_IMPORTANCE) {\r\n throw new InvalidImportanceError(e.importance, MIN_IMPORTANCE, MAX_IMPORTANCE);\r\n }\r\n entity.importance = e.importance;\r\n }\r\n\r\n newEntities.push(entity);\r\n processed++;\r\n reportProgress?.(createProgress(processed, entitiesToAdd.length, 'createEntities'));\r\n }\r\n\r\n // OPTIMIZED: Use append for single entity, bulk save for multiple\r\n // (N individual appends is slower than one bulk write)\r\n if (newEntities.length === 1) {\r\n await this.storage.appendEntity(newEntities[0]);\r\n } else if (newEntities.length > 1) {\r\n const graph = await this.storage.getGraphForMutation();\r\n graph.entities.push(...newEntities);\r\n await this.storage.saveGraph(graph);\r\n }\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(entitiesToAdd.length, entitiesToAdd.length, 'createEntities'));\r\n\r\n return newEntities;\r\n }\r\n\r\n /**\r\n * Delete multiple entities by name in a single batch operation.\r\n *\r\n * This method performs cascading deletion:\r\n * - Removes all specified entities from the graph\r\n * - Automatically removes all relations where these entities are source or target\r\n * - Silently ignores entity names that don't exist (no error thrown)\r\n *\r\n * @param entityNames - Array of entity names to delete\r\n * @returns Promise that resolves when deletion is complete\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new EntityManager(storage);\r\n *\r\n * // Delete single entity\r\n * await manager.deleteEntities(['Alice']);\r\n *\r\n * // Delete multiple entities at once\r\n * await manager.deleteEntities(['Bob', 'Charlie', 'Dave']);\r\n *\r\n * // Safe to delete non-existent entities (no error)\r\n * await manager.deleteEntities(['NonExistent']); // No error thrown\r\n * ```\r\n */\r\n async deleteEntities(entityNames: string[]): Promise<void> {\r\n // Validate input\r\n const validation = EntityNamesSchema.safeParse(entityNames);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid entity names', errors);\r\n }\r\n\r\n const graph = await this.storage.getGraphForMutation();\r\n\r\n // OPTIMIZED: Use Set for O(1) lookups instead of O(n) includes()\r\n const namesToDelete = new Set(entityNames);\r\n graph.entities = graph.entities.filter(e => !namesToDelete.has(e.name));\r\n graph.relations = graph.relations.filter(\r\n r => !namesToDelete.has(r.from) && !namesToDelete.has(r.to)\r\n );\r\n\r\n await this.storage.saveGraph(graph);\r\n }\r\n\r\n /**\r\n * Retrieve a single entity by its unique name.\r\n *\r\n * This is a read-only operation that does not modify the graph.\r\n * Entity names are case-sensitive.\r\n *\r\n * @param name - The unique name of the entity to retrieve\r\n * @returns Promise resolving to the Entity object if found, or null if not found\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new EntityManager(storage);\r\n *\r\n * // Get an existing entity\r\n * const alice = await manager.getEntity('Alice');\r\n * if (alice) {\r\n * console.log(alice.observations);\r\n * console.log(alice.importance);\r\n * }\r\n *\r\n * // Handle non-existent entity\r\n * const missing = await manager.getEntity('NonExistent');\r\n * console.log(missing); // null\r\n * ```\r\n */\r\n async getEntity(name: string): Promise<Entity | null> {\r\n const graph = await this.storage.loadGraph();\r\n return graph.entities.find(e => e.name === name) || null;\r\n }\r\n\r\n /**\r\n * Update one or more fields of an existing entity.\r\n *\r\n * This method allows partial updates - only the fields specified in the updates\r\n * object will be changed. All other fields remain unchanged.\r\n * The lastModified timestamp is automatically updated.\r\n *\r\n * @param name - The unique name of the entity to update\r\n * @param updates - Partial entity object containing only the fields to update\r\n * @returns Promise resolving to the fully updated Entity object\r\n * @throws {EntityNotFoundError} If no entity with the given name exists\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new EntityManager(storage);\r\n *\r\n * // Update importance only\r\n * const updated = await manager.updateEntity('Alice', {\r\n * importance: 9\r\n * });\r\n *\r\n * // Update multiple fields\r\n * await manager.updateEntity('Bob', {\r\n * entityType: 'senior_engineer',\r\n * tags: ['leadership', 'architecture'],\r\n * observations: ['Led project X', 'Designed system Y']\r\n * });\r\n *\r\n * // Add observations (requires reading existing entity first)\r\n * const entity = await manager.getEntity('Charlie');\r\n * if (entity) {\r\n * await manager.updateEntity('Charlie', {\r\n * observations: [...entity.observations, 'New observation']\r\n * });\r\n * }\r\n * ```\r\n */\r\n async updateEntity(name: string, updates: Partial<Entity>): Promise<Entity> {\r\n // Validate input\r\n const validation = UpdateEntitySchema.safeParse(updates);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid update data', errors);\r\n }\r\n\r\n const graph = await this.storage.getGraphForMutation();\r\n const entity = graph.entities.find(e => e.name === name);\r\n\r\n if (!entity) {\r\n throw new EntityNotFoundError(name);\r\n }\r\n\r\n // Apply updates (sanitized to prevent prototype pollution)\r\n Object.assign(entity, sanitizeObject(updates as Record<string, unknown>));\r\n entity.lastModified = new Date().toISOString();\r\n\r\n await this.storage.saveGraph(graph);\r\n return entity;\r\n }\r\n\r\n /**\r\n * Update multiple entities in a single batch operation.\r\n *\r\n * This method is more efficient than calling updateEntity multiple times\r\n * as it loads and saves the graph only once. All updates are applied atomically.\r\n * The lastModified timestamp is automatically updated for all entities.\r\n *\r\n * @param updates - Array of updates, each containing entity name and changes\r\n * @returns Promise resolving to array of updated entities\r\n * @throws {EntityNotFoundError} If any entity is not found\r\n * @throws {ValidationError} If any update data is invalid\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new EntityManager(storage);\r\n *\r\n * // Update multiple entities at once\r\n * const updated = await manager.batchUpdate([\r\n * { name: 'Alice', updates: { importance: 9 } },\r\n * { name: 'Bob', updates: { importance: 8, tags: ['senior'] } },\r\n * { name: 'Charlie', updates: { entityType: 'lead_engineer' } }\r\n * ]);\r\n *\r\n * console.log(`Updated ${updated.length} entities`);\r\n *\r\n * // Efficiently update many entities (single graph load/save)\r\n * const massUpdate = employees.map(name => ({\r\n * name,\r\n * updates: { tags: ['team-2024'] }\r\n * }));\r\n * await manager.batchUpdate(massUpdate);\r\n * ```\r\n */\r\n async batchUpdate(\r\n updates: Array<{ name: string; updates: Partial<Entity> }>\r\n ): Promise<Entity[]> {\r\n // Validate all updates first\r\n for (const { updates: updateData } of updates) {\r\n const validation = UpdateEntitySchema.safeParse(updateData);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid update data', errors);\r\n }\r\n }\r\n\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n const updatedEntities: Entity[] = [];\r\n\r\n // OPTIMIZED: Build Map for O(1) lookups instead of O(n) find() per update\r\n const entityIndex = new Map<string, number>();\r\n graph.entities.forEach((e, i) => entityIndex.set(e.name, i));\r\n\r\n for (const { name, updates: updateData } of updates) {\r\n const idx = entityIndex.get(name);\r\n if (idx === undefined) {\r\n throw new EntityNotFoundError(name);\r\n }\r\n const entity = graph.entities[idx];\r\n\r\n // Apply updates (sanitized to prevent prototype pollution)\r\n Object.assign(entity, sanitizeObject(updateData as Record<string, unknown>));\r\n entity.lastModified = timestamp;\r\n updatedEntities.push(entity);\r\n }\r\n\r\n await this.storage.saveGraph(graph);\r\n return updatedEntities;\r\n }\r\n\r\n // ============================================================\r\n // TAG OPERATIONS\r\n // ============================================================\r\n\r\n /**\r\n * Add tags to an entity.\r\n *\r\n * Tags are normalized to lowercase and duplicates are filtered out.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param tags - Tags to add\r\n * @returns Result with entity name and added tags\r\n * @throws {EntityNotFoundError} If entity is not found\r\n */\r\n async addTags(entityName: string, tags: string[]): Promise<{ entityName: string; addedTags: string[] }> {\r\n // OPTIMIZED: Use O(1) NameIndex lookup instead of loadGraph() + O(n) find()\r\n const entity = this.storage.getEntityByName(entityName);\r\n if (!entity) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n // Initialize tags array if it doesn't exist\r\n const existingTags = entity.tags || [];\r\n\r\n // Normalize tags to lowercase and filter out duplicates\r\n const normalizedTags = tags.map(tag => tag.toLowerCase());\r\n const newTags = normalizedTags.filter(tag => !existingTags.includes(tag));\r\n\r\n if (newTags.length > 0) {\r\n // OPTIMIZED: Use updateEntity for in-place update + append\r\n await this.storage.updateEntity(entityName, { tags: [...existingTags, ...newTags] });\r\n }\r\n\r\n return { entityName, addedTags: newTags };\r\n }\r\n\r\n /**\r\n * Remove tags from an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param tags - Tags to remove\r\n * @returns Result with entity name and removed tags\r\n * @throws {EntityNotFoundError} If entity is not found\r\n */\r\n async removeTags(entityName: string, tags: string[]): Promise<{ entityName: string; removedTags: string[] }> {\r\n // OPTIMIZED: Use O(1) NameIndex lookup instead of loadGraph() + O(n) find()\r\n const entity = this.storage.getEntityByName(entityName);\r\n if (!entity) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n if (!entity.tags) {\r\n return { entityName, removedTags: [] };\r\n }\r\n\r\n // Normalize tags to lowercase\r\n const normalizedTags = tags.map(tag => tag.toLowerCase());\r\n const originalLength = entity.tags.length;\r\n\r\n // Capture existing tags (lowercase) BEFORE filtering to accurately track removals\r\n const existingTagsLower = entity.tags.map(t => t.toLowerCase());\r\n\r\n // Filter out the tags to remove\r\n const newTags = entity.tags.filter(tag => !normalizedTags.includes(tag.toLowerCase()));\r\n\r\n // A tag was removed if it existed in the original tags\r\n const removedTags = normalizedTags.filter(tag => existingTagsLower.includes(tag));\r\n\r\n // Update entity via storage if tags were removed\r\n if (newTags.length < originalLength) {\r\n await this.storage.updateEntity(entityName, { tags: newTags });\r\n }\r\n\r\n return { entityName, removedTags };\r\n }\r\n\r\n /**\r\n * Set importance level for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param importance - Importance level (0-10)\r\n * @returns Result with entity name and importance\r\n * @throws {EntityNotFoundError} If entity is not found\r\n * @throws {Error} If importance is out of range\r\n */\r\n async setImportance(entityName: string, importance: number): Promise<{ entityName: string; importance: number }> {\r\n // Validate importance range (0-10)\r\n if (importance < 0 || importance > 10) {\r\n throw new Error(`Importance must be between 0 and 10, got ${importance}`);\r\n }\r\n\r\n // OPTIMIZED: Use O(1) NameIndex lookup instead of loadGraph() + O(n) find()\r\n const entity = this.storage.getEntityByName(entityName);\r\n if (!entity) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n // Use updateEntity for in-place update + append\r\n await this.storage.updateEntity(entityName, { importance });\r\n\r\n return { entityName, importance };\r\n }\r\n\r\n /**\r\n * Add tags to multiple entities in a single operation.\r\n *\r\n * OPTIMIZED: Uses Map for O(1) entity lookups instead of O(n) find() per entity.\r\n *\r\n * @param entityNames - Names of entities to tag\r\n * @param tags - Tags to add to each entity\r\n * @returns Array of results showing which tags were added to each entity\r\n */\r\n async addTagsToMultipleEntities(entityNames: string[], tags: string[]): Promise<{ entityName: string; addedTags: string[] }[]> {\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n const normalizedTags = tags.map(tag => tag.toLowerCase());\r\n const results: { entityName: string; addedTags: string[] }[] = [];\r\n\r\n // OPTIMIZED: Build Map for O(1) lookups instead of O(n) find() per entity\r\n const entityMap = new Map<string, Entity>();\r\n for (const e of graph.entities) {\r\n entityMap.set(e.name, e);\r\n }\r\n\r\n for (const entityName of entityNames) {\r\n const entity = entityMap.get(entityName);\r\n if (!entity) {\r\n continue; // Skip non-existent entities\r\n }\r\n\r\n // Initialize tags array if it doesn't exist\r\n if (!entity.tags) {\r\n entity.tags = [];\r\n }\r\n\r\n // Filter out duplicates\r\n const newTags = normalizedTags.filter(tag => !entity.tags!.includes(tag));\r\n entity.tags.push(...newTags);\r\n\r\n // Update lastModified timestamp if tags were added\r\n if (newTags.length > 0) {\r\n entity.lastModified = timestamp;\r\n }\r\n\r\n results.push({ entityName, addedTags: newTags });\r\n }\r\n\r\n await this.storage.saveGraph(graph);\r\n return results;\r\n }\r\n\r\n /**\r\n * Replace a tag with a new tag across all entities (rename tag).\r\n *\r\n * @param oldTag - Tag to replace\r\n * @param newTag - New tag value\r\n * @returns Result with affected entities and count\r\n */\r\n async replaceTag(oldTag: string, newTag: string): Promise<{ affectedEntities: string[]; count: number }> {\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n const normalizedOldTag = oldTag.toLowerCase();\r\n const normalizedNewTag = newTag.toLowerCase();\r\n const affectedEntities: string[] = [];\r\n\r\n for (const entity of graph.entities) {\r\n if (!entity.tags || !entity.tags.includes(normalizedOldTag)) {\r\n continue;\r\n }\r\n\r\n // Replace old tag with new tag\r\n const index = entity.tags.indexOf(normalizedOldTag);\r\n entity.tags[index] = normalizedNewTag;\r\n entity.lastModified = timestamp;\r\n affectedEntities.push(entity.name);\r\n }\r\n\r\n await this.storage.saveGraph(graph);\r\n return { affectedEntities, count: affectedEntities.length };\r\n }\r\n\r\n /**\r\n * Merge two tags into one target tag across all entities.\r\n *\r\n * Combines tag1 and tag2 into targetTag. Any entity with either tag1 or tag2\r\n * will have both removed and targetTag added (if not already present).\r\n *\r\n * @param tag1 - First tag to merge\r\n * @param tag2 - Second tag to merge\r\n * @param targetTag - Target tag to merge into\r\n * @returns Object with affected entity names and count\r\n */\r\n async mergeTags(tag1: string, tag2: string, targetTag: string): Promise<{ affectedEntities: string[]; count: number }> {\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n const normalizedTag1 = tag1.toLowerCase();\r\n const normalizedTag2 = tag2.toLowerCase();\r\n const normalizedTargetTag = targetTag.toLowerCase();\r\n const affectedEntities: string[] = [];\r\n\r\n for (const entity of graph.entities) {\r\n if (!entity.tags) {\r\n continue;\r\n }\r\n\r\n const hasTag1 = entity.tags.includes(normalizedTag1);\r\n const hasTag2 = entity.tags.includes(normalizedTag2);\r\n\r\n if (!hasTag1 && !hasTag2) {\r\n continue;\r\n }\r\n\r\n // Remove both tags\r\n entity.tags = entity.tags.filter(tag => tag !== normalizedTag1 && tag !== normalizedTag2);\r\n\r\n // Add target tag if not already present\r\n if (!entity.tags.includes(normalizedTargetTag)) {\r\n entity.tags.push(normalizedTargetTag);\r\n }\r\n\r\n entity.lastModified = timestamp;\r\n affectedEntities.push(entity.name);\r\n }\r\n\r\n await this.storage.saveGraph(graph);\r\n return { affectedEntities, count: affectedEntities.length };\r\n }\r\n}\r\n","/**\r\n * Relation Manager\r\n *\r\n * Handles CRUD operations for relations in the knowledge graph.\r\n *\r\n * @module core/RelationManager\r\n */\r\n\r\nimport type { Relation } from '../types/index.js';\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { ValidationError } from '../utils/errors.js';\r\nimport { BatchCreateRelationsSchema, DeleteRelationsSchema } from '../utils/index.js';\r\nimport { GRAPH_LIMITS } from '../utils/constants.js';\r\n\r\n/**\r\n * Manages relation operations with automatic timestamp handling.\r\n */\r\nexport class RelationManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Create multiple relations in a single batch operation.\r\n *\r\n * This method performs the following operations:\r\n * - Validates that all referenced entities exist (prevents dangling relations)\r\n * - Filters out duplicate relations (same from, to, and relationType)\r\n * - Automatically adds createdAt and lastModified timestamps\r\n *\r\n * A relation is considered duplicate if another relation exists with the same:\r\n * - from entity name\r\n * - to entity name\r\n * - relationType\r\n *\r\n * @param relations - Array of relations to create\r\n * @returns Promise resolving to array of newly created relations (excludes duplicates)\r\n * @throws {ValidationError} If any relation references non-existent entities\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new RelationManager(storage);\r\n *\r\n * // Create single relation\r\n * const results = await manager.createRelations([{\r\n * from: 'Alice',\r\n * to: 'Bob',\r\n * relationType: 'works_with'\r\n * }]);\r\n *\r\n * // Create multiple relations at once\r\n * await manager.createRelations([\r\n * { from: 'Alice', to: 'Project_X', relationType: 'contributes_to' },\r\n * { from: 'Bob', to: 'Project_X', relationType: 'leads' },\r\n * { from: 'Charlie', to: 'Alice', relationType: 'reports_to' }\r\n * ]);\r\n *\r\n * // Duplicate relations are filtered out\r\n * await manager.createRelations([\r\n * { from: 'Alice', to: 'Bob', relationType: 'works_with' } // Already exists, won't be added\r\n * ]);\r\n * ```\r\n */\r\n async createRelations(relations: Relation[]): Promise<Relation[]> {\r\n // Validate input\r\n const validation = BatchCreateRelationsSchema.safeParse(relations);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid relation data', errors);\r\n }\r\n\r\n // Use read-only graph for checking existing relations and entity existence\r\n const readGraph = await this.storage.loadGraph();\r\n const timestamp = new Date().toISOString();\r\n\r\n // Build set of existing entity names for O(1) lookup\r\n const existingEntityNames = new Set(readGraph.entities.map(e => e.name));\r\n\r\n // Validate that all referenced entities exist (fixes bug 7.2 from analysis)\r\n const danglingRelations: string[] = [];\r\n for (const relation of relations) {\r\n const missingEntities: string[] = [];\r\n if (!existingEntityNames.has(relation.from)) {\r\n missingEntities.push(relation.from);\r\n }\r\n if (!existingEntityNames.has(relation.to)) {\r\n missingEntities.push(relation.to);\r\n }\r\n if (missingEntities.length > 0) {\r\n danglingRelations.push(\r\n `Relation from \"${relation.from}\" to \"${relation.to}\" references non-existent entities: ${missingEntities.join(', ')}`\r\n );\r\n }\r\n }\r\n\r\n if (danglingRelations.length > 0) {\r\n throw new ValidationError('Relations reference non-existent entities', danglingRelations);\r\n }\r\n\r\n // Check graph size limits\r\n const relationsToAdd = relations.filter(r => !readGraph.relations.some(existing =>\r\n existing.from === r.from &&\r\n existing.to === r.to &&\r\n existing.relationType === r.relationType\r\n ));\r\n\r\n if (readGraph.relations.length + relationsToAdd.length > GRAPH_LIMITS.MAX_RELATIONS) {\r\n throw new ValidationError(\r\n 'Graph size limit exceeded',\r\n [`Adding ${relationsToAdd.length} relations would exceed maximum of ${GRAPH_LIMITS.MAX_RELATIONS} relations`]\r\n );\r\n }\r\n\r\n const newRelations = relationsToAdd\r\n .map(r => ({\r\n ...r,\r\n createdAt: r.createdAt || timestamp,\r\n lastModified: r.lastModified || timestamp,\r\n }));\r\n\r\n // Get mutable copy for write operation\r\n const graph = await this.storage.getGraphForMutation();\r\n graph.relations.push(...newRelations);\r\n await this.storage.saveGraph(graph);\r\n\r\n return newRelations;\r\n }\r\n\r\n /**\r\n * Delete multiple relations in a single batch operation.\r\n *\r\n * This method performs the following operations:\r\n * - Removes all specified relations from the graph\r\n * - Automatically updates lastModified timestamp for all affected entities\r\n * - Silently ignores relations that don't exist (no error thrown)\r\n *\r\n * An entity is considered \"affected\" if it appears as either the source (from)\r\n * or target (to) of any deleted relation.\r\n *\r\n * @param relations - Array of relations to delete. Each relation is matched by from, to, and relationType.\r\n * @returns Promise that resolves when deletion is complete\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new RelationManager(storage);\r\n *\r\n * // Delete single relation\r\n * await manager.deleteRelations([{\r\n * from: 'Alice',\r\n * to: 'Bob',\r\n * relationType: 'works_with'\r\n * }]);\r\n *\r\n * // Delete multiple relations at once\r\n * await manager.deleteRelations([\r\n * { from: 'Alice', to: 'Project_X', relationType: 'contributes_to' },\r\n * { from: 'Bob', to: 'Project_X', relationType: 'leads' }\r\n * ]);\r\n * // Note: Alice, Bob, and Project_X will all have their lastModified timestamp updated\r\n *\r\n * // Safe to delete non-existent relations\r\n * await manager.deleteRelations([\r\n * { from: 'NonExistent', to: 'AlsoNonExistent', relationType: 'fake' } // No error\r\n * ]);\r\n * ```\r\n */\r\n async deleteRelations(relations: Relation[]): Promise<void> {\r\n // Validate input\r\n const validation = DeleteRelationsSchema.safeParse(relations);\r\n if (!validation.success) {\r\n const errors = validation.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`);\r\n throw new ValidationError('Invalid relation data', errors);\r\n }\r\n\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n\r\n // Track affected entities\r\n const affectedEntityNames = new Set<string>();\r\n relations.forEach(rel => {\r\n affectedEntityNames.add(rel.from);\r\n affectedEntityNames.add(rel.to);\r\n });\r\n\r\n // OPTIMIZED: Use Set<string> for O(1) lookup instead of O(n) array.some()\r\n // Create composite keys for relations to delete\r\n const relationsToDeleteSet = new Set(\r\n relations.map(r => `${r.from}|${r.to}|${r.relationType}`)\r\n );\r\n\r\n // Remove relations with O(1) Set lookup per relation instead of O(m) array scan\r\n graph.relations = graph.relations.filter(r =>\r\n !relationsToDeleteSet.has(`${r.from}|${r.to}|${r.relationType}`)\r\n );\r\n\r\n // Update lastModified for affected entities\r\n graph.entities.forEach(entity => {\r\n if (affectedEntityNames.has(entity.name)) {\r\n entity.lastModified = timestamp;\r\n }\r\n });\r\n\r\n await this.storage.saveGraph(graph);\r\n }\r\n\r\n /**\r\n * Retrieve all relations involving a specific entity.\r\n *\r\n * This is a read-only operation that returns all relations where the specified\r\n * entity appears as either the source (from) or target (to) of the relation.\r\n * Entity names are case-sensitive.\r\n *\r\n * @param entityName - The unique name of the entity to find relations for\r\n * @returns Promise resolving to array of Relation objects (empty array if no relations found)\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new RelationManager(storage);\r\n *\r\n * // Get all relations for an entity\r\n * const aliceRelations = await manager.getRelations('Alice');\r\n * // Returns: [\r\n * // { from: 'Alice', to: 'Bob', relationType: 'works_with' },\r\n * // { from: 'Alice', to: 'Project_X', relationType: 'contributes_to' },\r\n * // { from: 'Charlie', to: 'Alice', relationType: 'reports_to' }\r\n * // ]\r\n *\r\n * // Process relations by type\r\n * const relations = await manager.getRelations('Alice');\r\n * const outgoing = relations.filter(r => r.from === 'Alice');\r\n * const incoming = relations.filter(r => r.to === 'Alice');\r\n *\r\n * // Handle entity with no relations\r\n * const noRelations = await manager.getRelations('IsolatedEntity');\r\n * console.log(noRelations); // []\r\n * ```\r\n */\r\n async getRelations(entityName: string): Promise<Relation[]> {\r\n // OPTIMIZED: Uses RelationIndex for O(1) lookup instead of O(n) array scan\r\n await this.storage.ensureLoaded();\r\n return this.storage.getRelationsFor(entityName);\r\n }\r\n}\r\n","/**\r\n * Observation Manager\r\n *\r\n * Handles observation CRUD operations for entities.\r\n * Extracted from EntityManager (Phase 4: Consolidate God Objects).\r\n *\r\n * @module core/ObservationManager\r\n */\r\n\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { EntityNotFoundError } from '../utils/errors.js';\r\n\r\n/**\r\n * Manages observation operations for entities in the knowledge graph.\r\n */\r\nexport class ObservationManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Add observations to multiple entities in a single batch operation.\r\n *\r\n * This method performs the following operations:\r\n * - Adds new observations to specified entities\r\n * - Filters out duplicate observations (already present)\r\n * - Updates lastModified timestamp only if new observations were added\r\n * - ATOMIC: All updates are saved in a single operation\r\n *\r\n * @param observations - Array of entity names and observations to add\r\n * @returns Promise resolving to array of results showing which observations were added\r\n * @throws {EntityNotFoundError} If any entity is not found\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new ObservationManager(storage);\r\n *\r\n * // Add observations to multiple entities\r\n * const results = await manager.addObservations([\r\n * { entityName: 'Alice', contents: ['Completed project X', 'Started project Y'] },\r\n * { entityName: 'Bob', contents: ['Joined team meeting'] }\r\n * ]);\r\n *\r\n * // Check what was added (duplicates are filtered out)\r\n * results.forEach(r => {\r\n * console.log(`${r.entityName}: added ${r.addedObservations.length} new observations`);\r\n * });\r\n * ```\r\n */\r\n async addObservations(\r\n observations: { entityName: string; contents: string[] }[]\r\n ): Promise<{ entityName: string; addedObservations: string[] }[]> {\r\n // Get mutable graph for atomic update\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n const results: { entityName: string; addedObservations: string[] }[] = [];\r\n let hasChanges = false;\r\n\r\n for (const o of observations) {\r\n const entity = graph.entities.find(e => e.name === o.entityName);\r\n if (!entity) {\r\n throw new EntityNotFoundError(o.entityName);\r\n }\r\n\r\n const newObservations = o.contents.filter(content => !entity.observations.includes(content));\r\n\r\n if (newObservations.length > 0) {\r\n // Add new observations directly to the entity\r\n entity.observations.push(...newObservations);\r\n entity.lastModified = timestamp;\r\n hasChanges = true;\r\n }\r\n\r\n results.push({ entityName: o.entityName, addedObservations: newObservations });\r\n }\r\n\r\n // Save all changes in a single atomic operation\r\n if (hasChanges) {\r\n await this.storage.saveGraph(graph);\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Delete observations from multiple entities in a single batch operation.\r\n *\r\n * This method performs the following operations:\r\n * - Removes specified observations from entities\r\n * - Updates lastModified timestamp only if observations were deleted\r\n * - Silently ignores entities that don't exist (no error thrown)\r\n * - ATOMIC: All deletions are saved in a single operation\r\n *\r\n * @param deletions - Array of entity names and observations to delete\r\n * @returns Promise that resolves when deletion is complete\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new ObservationManager(storage);\r\n *\r\n * // Delete observations from multiple entities\r\n * await manager.deleteObservations([\r\n * { entityName: 'Alice', observations: ['Old observation 1', 'Old observation 2'] },\r\n * { entityName: 'Bob', observations: ['Outdated info'] }\r\n * ]);\r\n *\r\n * // Safe to delete from non-existent entities (no error)\r\n * await manager.deleteObservations([\r\n * { entityName: 'NonExistent', observations: ['Some text'] }\r\n * ]); // No error thrown\r\n * ```\r\n */\r\n async deleteObservations(\r\n deletions: { entityName: string; observations: string[] }[]\r\n ): Promise<void> {\r\n // Get mutable graph for atomic update\r\n const graph = await this.storage.getGraphForMutation();\r\n const timestamp = new Date().toISOString();\r\n let hasChanges = false;\r\n\r\n deletions.forEach(d => {\r\n const entity = graph.entities.find(e => e.name === d.entityName);\r\n if (entity) {\r\n const originalLength = entity.observations.length;\r\n entity.observations = entity.observations.filter(o => !d.observations.includes(o));\r\n\r\n // Update lastModified timestamp if observations were deleted\r\n if (entity.observations.length < originalLength) {\r\n entity.lastModified = timestamp;\r\n hasChanges = true;\r\n }\r\n }\r\n });\r\n\r\n // Save all changes in a single atomic operation\r\n if (hasChanges) {\r\n await this.storage.saveGraph(graph);\r\n }\r\n }\r\n}\r\n","/**\r\n * Hierarchy Manager\r\n *\r\n * Handles entity hierarchy operations (parent-child relationships).\r\n * Extracted from EntityManager (Phase 4: Consolidate God Objects).\r\n *\r\n * @module core/HierarchyManager\r\n */\r\n\r\nimport type { Entity, KnowledgeGraph, ReadonlyKnowledgeGraph } from '../types/index.js';\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { EntityNotFoundError, CycleDetectedError } from '../utils/errors.js';\r\n\r\n/**\r\n * Manages hierarchical relationships between entities.\r\n */\r\nexport class HierarchyManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Set the parent of an entity.\r\n *\r\n * Validates:\r\n * - Entity and parent exist\r\n * - Setting parent won't create a cycle\r\n *\r\n * @param entityName - Entity to set parent for\r\n * @param parentName - Parent entity name (null to remove parent)\r\n * @returns Updated entity\r\n * @throws {EntityNotFoundError} If entity or parent not found\r\n * @throws {CycleDetectedError} If setting parent would create a cycle\r\n */\r\n async setEntityParent(entityName: string, parentName: string | null): Promise<Entity> {\r\n // Use read-only graph for validation checks\r\n const readGraph = await this.storage.loadGraph();\r\n const entityExists = readGraph.entities.find(e => e.name === entityName);\r\n\r\n if (!entityExists) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n // If setting a parent, validate it exists and doesn't create a cycle\r\n if (parentName !== null) {\r\n const parent = readGraph.entities.find(e => e.name === parentName);\r\n if (!parent) {\r\n throw new EntityNotFoundError(parentName);\r\n }\r\n\r\n // Check for cycles\r\n if (this.wouldCreateCycle(readGraph, entityName, parentName)) {\r\n throw new CycleDetectedError(entityName, parentName);\r\n }\r\n }\r\n\r\n // Get mutable copy for write operation\r\n const graph = await this.storage.getGraphForMutation();\r\n const entity = graph.entities.find(e => e.name === entityName)!;\r\n entity.parentId = parentName || undefined;\r\n entity.lastModified = new Date().toISOString();\r\n\r\n await this.storage.saveGraph(graph);\r\n return entity;\r\n }\r\n\r\n /**\r\n * Check if setting a parent would create a cycle in the hierarchy.\r\n *\r\n * @param graph - Knowledge graph\r\n * @param entityName - Entity to set parent for\r\n * @param parentName - Proposed parent\r\n * @returns True if cycle would be created\r\n */\r\n private wouldCreateCycle(\r\n graph: ReadonlyKnowledgeGraph,\r\n entityName: string,\r\n parentName: string\r\n ): boolean {\r\n const visited = new Set<string>();\r\n let current: string | undefined = parentName;\r\n\r\n while (current) {\r\n if (visited.has(current)) {\r\n return true; // Cycle detected in existing hierarchy\r\n }\r\n if (current === entityName) {\r\n return true; // Would create a cycle\r\n }\r\n visited.add(current);\r\n\r\n const currentEntity = graph.entities.find(e => e.name === current);\r\n current = currentEntity?.parentId;\r\n }\r\n\r\n return false;\r\n }\r\n\r\n /**\r\n * Get the immediate children of an entity.\r\n *\r\n * @param entityName - Parent entity name\r\n * @returns Array of child entities\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getChildren(entityName: string): Promise<Entity[]> {\r\n const graph = await this.storage.loadGraph();\r\n\r\n // Verify entity exists\r\n if (!graph.entities.find(e => e.name === entityName)) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n return graph.entities.filter(e => e.parentId === entityName);\r\n }\r\n\r\n /**\r\n * Get the parent of an entity.\r\n *\r\n * @param entityName - Entity name\r\n * @returns Parent entity or null if no parent\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getParent(entityName: string): Promise<Entity | null> {\r\n const graph = await this.storage.loadGraph();\r\n const entity = graph.entities.find(e => e.name === entityName);\r\n\r\n if (!entity) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n if (!entity.parentId) {\r\n return null;\r\n }\r\n\r\n const parent = graph.entities.find(e => e.name === entity.parentId);\r\n return parent || null;\r\n }\r\n\r\n /**\r\n * Get all ancestors of an entity (parent, grandparent, etc.).\r\n *\r\n * @param entityName - Entity name\r\n * @returns Array of ancestor entities (ordered from immediate parent to root)\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getAncestors(entityName: string): Promise<Entity[]> {\r\n const graph = await this.storage.loadGraph();\r\n const ancestors: Entity[] = [];\r\n\r\n let current = graph.entities.find(e => e.name === entityName);\r\n if (!current) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n while (current.parentId) {\r\n const parent = graph.entities.find(e => e.name === current!.parentId);\r\n if (!parent) break;\r\n ancestors.push(parent);\r\n current = parent;\r\n }\r\n\r\n return ancestors;\r\n }\r\n\r\n /**\r\n * Get all descendants of an entity (children, grandchildren, etc.).\r\n *\r\n * Uses breadth-first traversal.\r\n *\r\n * @param entityName - Entity name\r\n * @returns Array of descendant entities\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getDescendants(entityName: string): Promise<Entity[]> {\r\n const graph = await this.storage.loadGraph();\r\n\r\n // Verify entity exists\r\n if (!graph.entities.find(e => e.name === entityName)) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n const descendants: Entity[] = [];\r\n const toProcess = [entityName];\r\n\r\n while (toProcess.length > 0) {\r\n const current = toProcess.shift()!;\r\n const children = graph.entities.filter(e => e.parentId === current);\r\n\r\n for (const child of children) {\r\n descendants.push(child);\r\n toProcess.push(child.name);\r\n }\r\n }\r\n\r\n return descendants;\r\n }\r\n\r\n /**\r\n * Get the entire subtree rooted at an entity (entity + all descendants).\r\n *\r\n * Includes relations between entities in the subtree.\r\n *\r\n * @param entityName - Root entity name\r\n * @returns Knowledge graph containing subtree\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getSubtree(entityName: string): Promise<KnowledgeGraph> {\r\n const graph = await this.storage.loadGraph();\r\n const entity = graph.entities.find(e => e.name === entityName);\r\n\r\n if (!entity) {\r\n throw new EntityNotFoundError(entityName);\r\n }\r\n\r\n const descendants = await this.getDescendants(entityName);\r\n const subtreeEntities = [entity, ...descendants];\r\n const subtreeEntityNames = new Set(subtreeEntities.map(e => e.name));\r\n\r\n // Include relations between entities in the subtree\r\n const subtreeRelations = graph.relations.filter(\r\n r => subtreeEntityNames.has(r.from) && subtreeEntityNames.has(r.to)\r\n );\r\n\r\n return {\r\n entities: subtreeEntities,\r\n relations: subtreeRelations,\r\n };\r\n }\r\n\r\n /**\r\n * Get root entities (entities with no parent).\r\n *\r\n * @returns Array of root entities\r\n */\r\n async getRootEntities(): Promise<Entity[]> {\r\n const graph = await this.storage.loadGraph();\r\n return graph.entities.filter(e => !e.parentId);\r\n }\r\n\r\n /**\r\n * Get the depth of an entity in the hierarchy.\r\n *\r\n * Root entities have depth 0, their children depth 1, etc.\r\n *\r\n * @param entityName - Entity name\r\n * @returns Depth (number of ancestors)\r\n * @throws {EntityNotFoundError} If entity not found\r\n */\r\n async getEntityDepth(entityName: string): Promise<number> {\r\n const ancestors = await this.getAncestors(entityName);\r\n return ancestors.length;\r\n }\r\n\r\n /**\r\n * Move an entity to a new parent (maintaining its descendants).\r\n *\r\n * Alias for setEntityParent.\r\n *\r\n * @param entityName - Entity to move\r\n * @param newParentName - New parent name (null to make root)\r\n * @returns Updated entity\r\n */\r\n async moveEntity(entityName: string, newParentName: string | null): Promise<Entity> {\r\n return await this.setEntityParent(entityName, newParentName);\r\n }\r\n}\r\n","/**\r\n * Manager Context\r\n *\r\n * Central context holding all manager instances with lazy initialization.\r\n * Provides direct manager access for toolHandlers.\r\n * Phase 4: Removed convenience methods - use managers directly.\r\n *\r\n * @module core/ManagerContext\r\n */\r\n\r\nimport path from 'path';\r\nimport { GraphStorage } from './GraphStorage.js';\r\nimport { createStorageFromPath } from './StorageFactory.js';\r\nimport { EntityManager } from './EntityManager.js';\r\nimport { RelationManager } from './RelationManager.js';\r\nimport { ObservationManager } from './ObservationManager.js';\r\nimport { HierarchyManager } from './HierarchyManager.js';\r\nimport { GraphTraversal } from './GraphTraversal.js';\r\nimport { SearchManager } from '../search/SearchManager.js';\r\nimport { RankedSearch } from '../search/RankedSearch.js';\r\nimport { SemanticSearch, createEmbeddingService, createVectorStore } from '../search/index.js';\r\nimport { IOManager } from '../features/IOManager.js';\r\nimport { TagManager } from '../features/TagManager.js';\r\nimport { AnalyticsManager } from '../features/AnalyticsManager.js';\r\nimport { CompressionManager } from '../features/CompressionManager.js';\r\nimport { ArchiveManager } from '../features/ArchiveManager.js';\r\nimport { getEmbeddingConfig } from '../utils/constants.js';\r\n\r\n/**\r\n * Context holding all manager instances with lazy initialization.\r\n * Provides direct manager access for toolHandlers.\r\n */\r\nexport class ManagerContext {\r\n // Type as GraphStorage for manager compatibility; actual instance may be SQLiteStorage\r\n // which implements the same interface via duck typing\r\n readonly storage: GraphStorage;\r\n private readonly savedSearchesFilePath: string;\r\n private readonly tagAliasesFilePath: string;\r\n\r\n // Lazy-initialized managers\r\n private _entityManager?: EntityManager;\r\n private _relationManager?: RelationManager;\r\n private _observationManager?: ObservationManager;\r\n private _hierarchyManager?: HierarchyManager;\r\n private _graphTraversal?: GraphTraversal;\r\n private _searchManager?: SearchManager;\r\n private _semanticSearch?: SemanticSearch | null;\r\n private _rankedSearch?: RankedSearch;\r\n private _ioManager?: IOManager;\r\n private _tagManager?: TagManager;\r\n private _analyticsManager?: AnalyticsManager;\r\n private _compressionManager?: CompressionManager;\r\n private _archiveManager?: ArchiveManager;\r\n\r\n constructor(memoryFilePath: string) {\r\n // Derive paths for saved searches and tag aliases\r\n const dir = path.dirname(memoryFilePath);\r\n const basename = path.basename(memoryFilePath, path.extname(memoryFilePath));\r\n this.savedSearchesFilePath = path.join(dir, `${basename}-saved-searches.jsonl`);\r\n this.tagAliasesFilePath = path.join(dir, `${basename}-tag-aliases.jsonl`);\r\n // Use StorageFactory to respect MEMORY_STORAGE_TYPE environment variable\r\n // Type assertion: SQLiteStorage implements same interface as GraphStorage\r\n this.storage = createStorageFromPath(memoryFilePath) as GraphStorage;\r\n }\r\n\r\n // ==================== MANAGER ACCESSORS ====================\r\n // Use these for direct manager access in toolHandlers\r\n\r\n /** EntityManager - Entity CRUD and tag operations */\r\n get entityManager(): EntityManager {\r\n return (this._entityManager ??= new EntityManager(this.storage));\r\n }\r\n\r\n /** RelationManager - Relation CRUD */\r\n get relationManager(): RelationManager {\r\n return (this._relationManager ??= new RelationManager(this.storage));\r\n }\r\n\r\n /** ObservationManager - Observation CRUD */\r\n get observationManager(): ObservationManager {\r\n return (this._observationManager ??= new ObservationManager(this.storage));\r\n }\r\n\r\n /** HierarchyManager - Entity hierarchy operations */\r\n get hierarchyManager(): HierarchyManager {\r\n return (this._hierarchyManager ??= new HierarchyManager(this.storage));\r\n }\r\n\r\n /** GraphTraversal - Phase 4 Sprint 6-8: Graph traversal algorithms */\r\n get graphTraversal(): GraphTraversal {\r\n return (this._graphTraversal ??= new GraphTraversal(this.storage));\r\n }\r\n\r\n /** SearchManager - All search operations */\r\n get searchManager(): SearchManager {\r\n return (this._searchManager ??= new SearchManager(this.storage, this.savedSearchesFilePath));\r\n }\r\n\r\n /**\r\n * SemanticSearch - Phase 4 Sprint 12: Semantic similarity search.\r\n * Returns null if no embedding provider is configured.\r\n */\r\n get semanticSearch(): SemanticSearch | null {\r\n if (this._semanticSearch === undefined) {\r\n const config = getEmbeddingConfig();\r\n const embeddingService = createEmbeddingService(config);\r\n\r\n if (embeddingService) {\r\n const vectorStore = createVectorStore('jsonl'); // Use in-memory for now\r\n this._semanticSearch = new SemanticSearch(embeddingService, vectorStore);\r\n } else {\r\n this._semanticSearch = null;\r\n }\r\n }\r\n return this._semanticSearch;\r\n }\r\n\r\n /** RankedSearch - Phase 11: TF-IDF/BM25 ranked search for hybrid search */\r\n get rankedSearch(): RankedSearch {\r\n return (this._rankedSearch ??= new RankedSearch(this.storage));\r\n }\r\n\r\n /** IOManager - Import, export, and backup operations */\r\n get ioManager(): IOManager {\r\n return (this._ioManager ??= new IOManager(this.storage));\r\n }\r\n\r\n /** TagManager - Tag alias management */\r\n get tagManager(): TagManager {\r\n return (this._tagManager ??= new TagManager(this.tagAliasesFilePath));\r\n }\r\n\r\n /** AnalyticsManager - Graph statistics and validation */\r\n get analyticsManager(): AnalyticsManager {\r\n return (this._analyticsManager ??= new AnalyticsManager(this.storage));\r\n }\r\n\r\n /** CompressionManager - Duplicate detection and entity merging */\r\n get compressionManager(): CompressionManager {\r\n return (this._compressionManager ??= new CompressionManager(this.storage));\r\n }\r\n\r\n /** ArchiveManager - Entity archival operations */\r\n get archiveManager(): ArchiveManager {\r\n return (this._archiveManager ??= new ArchiveManager(this.storage));\r\n }\r\n}\r\n","/**\r\n * Storage Factory\r\n *\r\n * Factory for creating IGraphStorage implementations.\r\n * Supports different storage backends based on configuration.\r\n *\r\n * Supported storage types:\r\n * - 'jsonl': JSONL file-based storage (default) - simple, human-readable\r\n * - 'sqlite': SQLite database storage (better-sqlite3 native) - indexed, ACID transactions, FTS5\r\n *\r\n * @module core/StorageFactory\r\n */\r\n\r\nimport { GraphStorage } from './GraphStorage.js';\r\nimport { SQLiteStorage } from './SQLiteStorage.js';\r\nimport type { IGraphStorage, StorageConfig } from '../types/index.js';\r\n\r\n/**\r\n * Default storage type when not specified.\r\n */\r\nconst DEFAULT_STORAGE_TYPE = 'jsonl';\r\n\r\n/**\r\n * Create a storage instance based on configuration.\r\n *\r\n * Uses environment variable MEMORY_STORAGE_TYPE to override default.\r\n *\r\n * @param config - Storage configuration\r\n * @returns IGraphStorage implementation\r\n * @throws Error if storage type is not supported\r\n *\r\n * @example\r\n * ```typescript\r\n * // Create default JSONL storage\r\n * const storage = createStorage({ type: 'jsonl', path: './memory.jsonl' });\r\n *\r\n * // Create SQLite storage\r\n * const storage = createStorage({ type: 'sqlite', path: './memory.db' });\r\n *\r\n * // Or use path-only shorthand (uses MEMORY_STORAGE_TYPE env var or defaults to jsonl)\r\n * const storage = createStorageFromPath('./memory.jsonl');\r\n * ```\r\n */\r\nexport function createStorage(config: StorageConfig): IGraphStorage {\r\n // Allow environment override\r\n const storageType = process.env.MEMORY_STORAGE_TYPE || config.type || DEFAULT_STORAGE_TYPE;\r\n\r\n switch (storageType) {\r\n case 'jsonl':\r\n return new GraphStorage(config.path);\r\n\r\n case 'sqlite':\r\n return new SQLiteStorage(config.path);\r\n\r\n default:\r\n throw new Error(\r\n `Unknown storage type: ${storageType}. ` +\r\n `Supported types: jsonl, sqlite`\r\n );\r\n }\r\n}\r\n\r\n/**\r\n * Create a storage instance from a file path.\r\n *\r\n * Uses default storage type (jsonl) or MEMORY_STORAGE_TYPE env var.\r\n *\r\n * @param path - Path to storage file\r\n * @returns IGraphStorage implementation\r\n */\r\nexport function createStorageFromPath(path: string): IGraphStorage {\r\n const storageType = (process.env.MEMORY_STORAGE_TYPE as 'jsonl' | 'sqlite') || DEFAULT_STORAGE_TYPE;\r\n return createStorage({ type: storageType, path });\r\n}\r\n","/**\r\n * Graph Traversal\r\n *\r\n * Phase 4 Sprints 6-8: Graph traversal algorithms for knowledge graph analysis.\r\n * Includes BFS, DFS, shortest path, all paths, connected components, and centrality.\r\n *\r\n * @module core/GraphTraversal\r\n */\r\n\r\nimport type {\r\n Entity,\r\n Relation,\r\n TraversalOptions,\r\n TraversalResult,\r\n PathResult,\r\n ConnectedComponentsResult,\r\n CentralityResult,\r\n} from '../types/index.js';\r\nimport type { GraphStorage } from './GraphStorage.js';\r\nimport { checkCancellation } from '../utils/index.js';\r\n\r\n/**\r\n * Phase 4 Sprint 6: Default traversal options.\r\n */\r\nconst DEFAULT_OPTIONS: Required<TraversalOptions> = {\r\n direction: 'both',\r\n maxDepth: Infinity,\r\n relationTypes: [],\r\n entityTypes: [],\r\n};\r\n\r\n/**\r\n * Graph traversal algorithms for knowledge graph analysis.\r\n *\r\n * Provides BFS, DFS, shortest path finding, connected component detection,\r\n * and centrality metrics for analyzing graph structure.\r\n */\r\nexport class GraphTraversal {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n // ==================== Sprint 6: BFS and DFS Traversal ====================\r\n\r\n /**\r\n * Get neighbors of a node based on traversal direction and filters.\r\n *\r\n * @param entityName - Entity to get neighbors for\r\n * @param options - Traversal options\r\n * @returns Array of neighbor entity names with their relations\r\n */\r\n getNeighborsWithRelations(\r\n entityName: string,\r\n options: TraversalOptions = {}\r\n ): Array<{ neighbor: string; relation: Relation }> {\r\n // Filter out undefined values before merging with defaults\r\n const definedOptions = Object.fromEntries(\r\n Object.entries(options).filter(([, v]) => v !== undefined)\r\n );\r\n const opts = { ...DEFAULT_OPTIONS, ...definedOptions };\r\n const neighbors: Array<{ neighbor: string; relation: Relation }> = [];\r\n\r\n // Get relations based on direction\r\n let relations: Relation[] = [];\r\n if (opts.direction === 'outgoing' || opts.direction === 'both') {\r\n relations = relations.concat(this.storage.getRelationsFrom(entityName));\r\n }\r\n if (opts.direction === 'incoming' || opts.direction === 'both') {\r\n relations = relations.concat(this.storage.getRelationsTo(entityName));\r\n }\r\n\r\n // Filter by relation types if specified\r\n if (opts.relationTypes && opts.relationTypes.length > 0) {\r\n const typeSet = new Set(opts.relationTypes.map(t => t.toLowerCase()));\r\n relations = relations.filter(r => typeSet.has(r.relationType.toLowerCase()));\r\n }\r\n\r\n // Process relations to get neighbors\r\n for (const relation of relations) {\r\n const neighbor = relation.from === entityName ? relation.to : relation.from;\r\n\r\n // Skip self-loops\r\n if (neighbor === entityName) continue;\r\n\r\n // Filter by entity types if specified\r\n if (opts.entityTypes && opts.entityTypes.length > 0) {\r\n const entity = this.storage.getEntityByName(neighbor);\r\n if (!entity) continue;\r\n const typeSet = new Set(opts.entityTypes.map(t => t.toLowerCase()));\r\n if (!typeSet.has(entity.entityType.toLowerCase())) continue;\r\n }\r\n\r\n neighbors.push({ neighbor, relation });\r\n }\r\n\r\n return neighbors;\r\n }\r\n\r\n /**\r\n * Breadth-First Search traversal starting from a given entity.\r\n *\r\n * @param startEntity - Entity name to start traversal from\r\n * @param options - Traversal options\r\n * @returns Traversal result with visited nodes, depths, and parent pointers\r\n */\r\n bfs(startEntity: string, options: TraversalOptions = {}): TraversalResult {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n // Validate start entity exists\r\n if (!this.storage.hasEntity(startEntity)) {\r\n return { nodes: [], depths: new Map(), parents: new Map() };\r\n }\r\n\r\n const visited = new Set<string>();\r\n const queue: Array<{ node: string; depth: number }> = [{ node: startEntity, depth: 0 }];\r\n const nodes: string[] = [];\r\n const depths = new Map<string, number>();\r\n const parents = new Map<string, string | null>();\r\n\r\n visited.add(startEntity);\r\n parents.set(startEntity, null);\r\n\r\n while (queue.length > 0) {\r\n const { node, depth } = queue.shift()!;\r\n\r\n // Respect maxDepth limit\r\n if (depth > opts.maxDepth) continue;\r\n\r\n nodes.push(node);\r\n depths.set(node, depth);\r\n\r\n // Get neighbors and add unvisited ones to queue\r\n const neighbors = this.getNeighborsWithRelations(node, opts);\r\n for (const { neighbor } of neighbors) {\r\n if (!visited.has(neighbor)) {\r\n visited.add(neighbor);\r\n queue.push({ node: neighbor, depth: depth + 1 });\r\n parents.set(neighbor, node);\r\n }\r\n }\r\n }\r\n\r\n return { nodes, depths, parents };\r\n }\r\n\r\n /**\r\n * Depth-First Search traversal starting from a given entity.\r\n *\r\n * @param startEntity - Entity name to start traversal from\r\n * @param options - Traversal options\r\n * @returns Traversal result with visited nodes, depths, and parent pointers\r\n */\r\n dfs(startEntity: string, options: TraversalOptions = {}): TraversalResult {\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n\r\n // Validate start entity exists\r\n if (!this.storage.hasEntity(startEntity)) {\r\n return { nodes: [], depths: new Map(), parents: new Map() };\r\n }\r\n\r\n const visited = new Set<string>();\r\n const stack: Array<{ node: string; depth: number }> = [{ node: startEntity, depth: 0 }];\r\n const nodes: string[] = [];\r\n const depths = new Map<string, number>();\r\n const parents = new Map<string, string | null>();\r\n\r\n parents.set(startEntity, null);\r\n\r\n while (stack.length > 0) {\r\n const { node, depth } = stack.pop()!;\r\n\r\n // Skip if already visited\r\n if (visited.has(node)) continue;\r\n\r\n // Respect maxDepth limit\r\n if (depth > opts.maxDepth) continue;\r\n\r\n visited.add(node);\r\n nodes.push(node);\r\n depths.set(node, depth);\r\n\r\n // Get neighbors and add unvisited ones to stack\r\n const neighbors = this.getNeighborsWithRelations(node, opts);\r\n for (const { neighbor } of neighbors) {\r\n if (!visited.has(neighbor)) {\r\n stack.push({ node: neighbor, depth: depth + 1 });\r\n if (!parents.has(neighbor)) {\r\n parents.set(neighbor, node);\r\n }\r\n }\r\n }\r\n }\r\n\r\n return { nodes, depths, parents };\r\n }\r\n\r\n // ==================== Sprint 7: Path Finding Algorithms ====================\r\n\r\n /**\r\n * Find the shortest path between two entities using BFS.\r\n *\r\n * @param source - Source entity name\r\n * @param target - Target entity name\r\n * @param options - Traversal options\r\n * @returns PathResult if path exists, null otherwise\r\n */\r\n async findShortestPath(\r\n source: string,\r\n target: string,\r\n options: TraversalOptions = {}\r\n ): Promise<PathResult | null> {\r\n // Ensure graph is loaded to populate indexes\r\n await this.storage.loadGraph();\r\n\r\n // Validate entities exist\r\n if (!this.storage.hasEntity(source) || !this.storage.hasEntity(target)) {\r\n return null;\r\n }\r\n\r\n // Same source and target\r\n if (source === target) {\r\n return { path: [source], length: 0, relations: [] };\r\n }\r\n\r\n const opts = { ...DEFAULT_OPTIONS, ...options };\r\n const visited = new Set<string>();\r\n const queue: string[] = [source];\r\n const parents = new Map<string, { parent: string; relation: Relation } | null>();\r\n\r\n visited.add(source);\r\n parents.set(source, null);\r\n\r\n while (queue.length > 0) {\r\n const current = queue.shift()!;\r\n\r\n // Found target, reconstruct path\r\n if (current === target) {\r\n return this.reconstructPath(source, target, parents);\r\n }\r\n\r\n // Get neighbors\r\n const neighbors = this.getNeighborsWithRelations(current, opts);\r\n for (const { neighbor, relation } of neighbors) {\r\n if (!visited.has(neighbor)) {\r\n visited.add(neighbor);\r\n queue.push(neighbor);\r\n parents.set(neighbor, { parent: current, relation });\r\n }\r\n }\r\n }\r\n\r\n // No path found\r\n return null;\r\n }\r\n\r\n /**\r\n * Reconstruct path from parent pointers.\r\n */\r\n private reconstructPath(\r\n _source: string,\r\n target: string,\r\n parents: Map<string, { parent: string; relation: Relation } | null>\r\n ): PathResult {\r\n const path: string[] = [];\r\n const relations: Relation[] = [];\r\n let current: string | null = target;\r\n\r\n while (current !== null) {\r\n path.unshift(current);\r\n const parentInfo = parents.get(current);\r\n if (parentInfo) {\r\n relations.unshift(parentInfo.relation);\r\n current = parentInfo.parent;\r\n } else {\r\n current = null;\r\n }\r\n }\r\n\r\n return {\r\n path,\r\n length: path.length - 1,\r\n relations,\r\n };\r\n }\r\n\r\n /**\r\n * Find all paths between two entities up to a maximum depth.\r\n *\r\n * Phase 9B: Supports cancellation via AbortSignal in options.\r\n *\r\n * @param source - Source entity name\r\n * @param target - Target entity name\r\n * @param maxDepth - Maximum path length (default: 5)\r\n * @param options - Traversal options (includes signal for cancellation)\r\n * @returns Array of PathResult objects for all found paths\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n */\r\n async findAllPaths(\r\n source: string,\r\n target: string,\r\n maxDepth: number = 5,\r\n options: TraversalOptions & { signal?: AbortSignal } = {}\r\n ): Promise<PathResult[]> {\r\n // Check for early cancellation\r\n const { signal, ...traversalOptions } = options;\r\n checkCancellation(signal, 'findAllPaths');\r\n\r\n // Ensure graph is loaded to populate indexes\r\n await this.storage.loadGraph();\r\n\r\n // Check for cancellation after load\r\n checkCancellation(signal, 'findAllPaths');\r\n\r\n // Validate entities exist\r\n if (!this.storage.hasEntity(source) || !this.storage.hasEntity(target)) {\r\n return [];\r\n }\r\n\r\n const opts = { ...DEFAULT_OPTIONS, ...traversalOptions };\r\n const allPaths: PathResult[] = [];\r\n const currentPath: string[] = [source];\r\n const currentRelations: Relation[] = [];\r\n const visited = new Set<string>([source]);\r\n\r\n // Track iterations for periodic cancellation checks\r\n let iterationCount = 0;\r\n const CANCELLATION_CHECK_INTERVAL = 100;\r\n\r\n const dfsAllPaths = (current: string, depth: number) => {\r\n // Periodic cancellation check\r\n iterationCount++;\r\n if (iterationCount % CANCELLATION_CHECK_INTERVAL === 0) {\r\n checkCancellation(signal, 'findAllPaths');\r\n }\r\n\r\n if (depth > maxDepth) return;\r\n\r\n if (current === target && depth > 0) {\r\n allPaths.push({\r\n path: [...currentPath],\r\n length: currentPath.length - 1,\r\n relations: [...currentRelations],\r\n });\r\n return;\r\n }\r\n\r\n const neighbors = this.getNeighborsWithRelations(current, opts);\r\n for (const { neighbor, relation } of neighbors) {\r\n if (!visited.has(neighbor)) {\r\n visited.add(neighbor);\r\n currentPath.push(neighbor);\r\n currentRelations.push(relation);\r\n\r\n dfsAllPaths(neighbor, depth + 1);\r\n\r\n currentPath.pop();\r\n currentRelations.pop();\r\n visited.delete(neighbor);\r\n }\r\n }\r\n };\r\n\r\n dfsAllPaths(source, 0);\r\n return allPaths;\r\n }\r\n\r\n // ==================== Sprint 8: Connected Components ====================\r\n\r\n /**\r\n * Find all connected components in the graph.\r\n *\r\n * Uses BFS to find all weakly connected components (treating the graph as undirected).\r\n *\r\n * @returns ConnectedComponentsResult with all components\r\n */\r\n async findConnectedComponents(): Promise<ConnectedComponentsResult> {\r\n const graph = await this.storage.loadGraph();\r\n const visited = new Set<string>();\r\n const components: string[][] = [];\r\n\r\n for (const entity of graph.entities) {\r\n if (!visited.has(entity.name)) {\r\n // BFS to find all nodes in this component\r\n const component: string[] = [];\r\n const queue: string[] = [entity.name];\r\n visited.add(entity.name);\r\n\r\n while (queue.length > 0) {\r\n const current = queue.shift()!;\r\n component.push(current);\r\n\r\n // Get all neighbors (both directions for weakly connected)\r\n const neighbors = this.getNeighborsWithRelations(current, { direction: 'both' });\r\n for (const { neighbor } of neighbors) {\r\n if (!visited.has(neighbor)) {\r\n visited.add(neighbor);\r\n queue.push(neighbor);\r\n }\r\n }\r\n }\r\n\r\n components.push(component);\r\n }\r\n }\r\n\r\n // Sort components by size (largest first)\r\n components.sort((a, b) => b.length - a.length);\r\n\r\n return {\r\n components,\r\n count: components.length,\r\n largestComponentSize: components.length > 0 ? components[0].length : 0,\r\n };\r\n }\r\n\r\n // ==================== Sprint 8: Centrality Algorithms ====================\r\n\r\n /**\r\n * Calculate degree centrality for all entities.\r\n *\r\n * Degree centrality is the number of connections an entity has,\r\n * normalized by the maximum possible connections.\r\n *\r\n * @param direction - Direction to count: 'in', 'out', or 'both' (default)\r\n * @param topN - Number of top entities to return (default: 10)\r\n * @returns CentralityResult with scores and top entities\r\n */\r\n async calculateDegreeCentrality(\r\n direction: 'in' | 'out' | 'both' = 'both',\r\n topN: number = 10\r\n ): Promise<CentralityResult> {\r\n const graph = await this.storage.loadGraph();\r\n const scores = new Map<string, number>();\r\n const n = graph.entities.length;\r\n\r\n // Calculate degree for each entity\r\n for (const entity of graph.entities) {\r\n let degree = 0;\r\n\r\n if (direction === 'in' || direction === 'both') {\r\n degree += this.storage.getRelationsTo(entity.name).length;\r\n }\r\n if (direction === 'out' || direction === 'both') {\r\n degree += this.storage.getRelationsFrom(entity.name).length;\r\n }\r\n\r\n // Normalize by maximum possible degree\r\n const normalizedDegree = n > 1 ? degree / (n - 1) : 0;\r\n scores.set(entity.name, normalizedDegree);\r\n }\r\n\r\n // Get top N entities\r\n const topEntities = this.getTopEntities(scores, topN);\r\n\r\n return {\r\n scores,\r\n topEntities,\r\n algorithm: 'degree',\r\n };\r\n }\r\n\r\n /**\r\n * Calculate betweenness centrality for all entities.\r\n *\r\n * Betweenness centrality measures how often a node appears on shortest paths\r\n * between other nodes. Uses Brandes' algorithm for efficiency.\r\n *\r\n * @param options - Configuration options\r\n * @param options.topN - Number of top entities to return (default: 10)\r\n * @param options.chunkSize - Yield control every N vertices (default: 50)\r\n * @param options.onProgress - Progress callback (0.0 to 1.0)\r\n * @param options.approximate - Use approximation for faster results (default: false)\r\n * @param options.sampleRate - Sample rate for approximation (default: 0.2)\r\n * @returns CentralityResult with scores and top entities\r\n */\r\n async calculateBetweennessCentrality(\r\n options: {\r\n topN?: number;\r\n chunkSize?: number;\r\n onProgress?: (progress: number) => void;\r\n approximate?: boolean;\r\n sampleRate?: number;\r\n } = {}\r\n ): Promise<CentralityResult> {\r\n const { topN = 10, chunkSize = 50, onProgress, approximate = false, sampleRate = 0.2 } = options;\r\n const graph = await this.storage.loadGraph();\r\n const scores = new Map<string, number>();\r\n\r\n // Initialize scores\r\n for (const entity of graph.entities) {\r\n scores.set(entity.name, 0);\r\n }\r\n\r\n // Determine which sources to process (full or sampled)\r\n let sourcesToProcess = graph.entities;\r\n if (approximate && graph.entities.length > 100) {\r\n const sampleSize = Math.max(10, Math.floor(graph.entities.length * sampleRate));\r\n sourcesToProcess = this.sampleEntities(graph.entities, sampleSize);\r\n }\r\n\r\n // Brandes' algorithm with chunked processing\r\n let processed = 0;\r\n for (const source of sourcesToProcess) {\r\n const stack: string[] = [];\r\n const predecessors = new Map<string, string[]>();\r\n const sigma = new Map<string, number>(); // Number of shortest paths\r\n const distance = new Map<string, number>(); // Distance from source\r\n const delta = new Map<string, number>(); // Dependency\r\n\r\n // Initialize\r\n for (const entity of graph.entities) {\r\n predecessors.set(entity.name, []);\r\n sigma.set(entity.name, 0);\r\n distance.set(entity.name, -1);\r\n delta.set(entity.name, 0);\r\n }\r\n\r\n sigma.set(source.name, 1);\r\n distance.set(source.name, 0);\r\n\r\n // BFS\r\n const queue: string[] = [source.name];\r\n while (queue.length > 0) {\r\n const v = queue.shift()!;\r\n stack.push(v);\r\n\r\n const neighbors = this.getNeighborsWithRelations(v, { direction: 'both' });\r\n for (const { neighbor: w } of neighbors) {\r\n // First time w is discovered\r\n if (distance.get(w) === -1) {\r\n distance.set(w, distance.get(v)! + 1);\r\n queue.push(w);\r\n }\r\n\r\n // w is on a shortest path from source via v\r\n if (distance.get(w) === distance.get(v)! + 1) {\r\n sigma.set(w, sigma.get(w)! + sigma.get(v)!);\r\n predecessors.get(w)!.push(v);\r\n }\r\n }\r\n }\r\n\r\n // Accumulation\r\n while (stack.length > 0) {\r\n const w = stack.pop()!;\r\n for (const v of predecessors.get(w)!) {\r\n const contribution = (sigma.get(v)! / sigma.get(w)!) * (1 + delta.get(w)!);\r\n delta.set(v, delta.get(v)! + contribution);\r\n }\r\n if (w !== source.name) {\r\n scores.set(w, scores.get(w)! + delta.get(w)!);\r\n }\r\n }\r\n\r\n // Yield control periodically to prevent blocking event loop\r\n processed++;\r\n if (processed % chunkSize === 0) {\r\n // Yield control to allow event loop to process other events\r\n await new Promise(resolve => setImmediate(resolve));\r\n\r\n // Report progress\r\n if (onProgress) {\r\n onProgress(processed / sourcesToProcess.length);\r\n }\r\n }\r\n }\r\n\r\n // Final progress update\r\n if (onProgress) {\r\n onProgress(1);\r\n }\r\n\r\n // Scale scores if using approximation\r\n if (approximate && sampleRate < 1.0) {\r\n const scaleFactor = 1 / sampleRate;\r\n for (const [entity, score] of scores) {\r\n scores.set(entity, score * scaleFactor);\r\n }\r\n }\r\n\r\n // Normalize scores\r\n const n = graph.entities.length;\r\n const normalization = n > 2 ? 2 / ((n - 1) * (n - 2)) : 1;\r\n for (const [name, score] of scores) {\r\n scores.set(name, score * normalization);\r\n }\r\n\r\n const topEntities = this.getTopEntities(scores, topN);\r\n\r\n return {\r\n scores,\r\n topEntities,\r\n algorithm: 'betweenness',\r\n };\r\n }\r\n\r\n /**\r\n * Sample entities randomly for approximation algorithms.\r\n *\r\n * @param entities - Array of entities to sample from\r\n * @param sampleSize - Number of entities to sample\r\n * @returns Array of sampled entities\r\n */\r\n private sampleEntities(entities: readonly Entity[], sampleSize: number): Entity[] {\r\n const shuffled = [...entities];\r\n // Fisher-Yates shuffle\r\n for (let i = shuffled.length - 1; i > 0; i--) {\r\n const j = Math.floor(Math.random() * (i + 1));\r\n [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];\r\n }\r\n return shuffled.slice(0, sampleSize);\r\n }\r\n\r\n /**\r\n * Calculate PageRank centrality for all entities.\r\n *\r\n * PageRank measures importance based on incoming connections from\r\n * other important nodes. Uses iterative power method.\r\n *\r\n * @param dampingFactor - Damping factor (default: 0.85)\r\n * @param maxIterations - Maximum iterations (default: 100)\r\n * @param tolerance - Convergence tolerance (default: 1e-6)\r\n * @param topN - Number of top entities to return (default: 10)\r\n * @returns CentralityResult with scores and top entities\r\n */\r\n async calculatePageRank(\r\n dampingFactor: number = 0.85,\r\n maxIterations: number = 100,\r\n tolerance: number = 1e-6,\r\n topN: number = 10\r\n ): Promise<CentralityResult> {\r\n const graph = await this.storage.loadGraph();\r\n const n = graph.entities.length;\r\n\r\n if (n === 0) {\r\n return { scores: new Map(), topEntities: [], algorithm: 'pagerank' };\r\n }\r\n\r\n // Initialize PageRank scores\r\n const scores = new Map<string, number>();\r\n const initialScore = 1 / n;\r\n for (const entity of graph.entities) {\r\n scores.set(entity.name, initialScore);\r\n }\r\n\r\n // Build outgoing links map\r\n const outLinks = new Map<string, string[]>();\r\n for (const entity of graph.entities) {\r\n const outgoing = this.storage.getRelationsFrom(entity.name);\r\n outLinks.set(entity.name, outgoing.map(r => r.to));\r\n }\r\n\r\n // Power iteration\r\n for (let iteration = 0; iteration < maxIterations; iteration++) {\r\n const newScores = new Map<string, number>();\r\n let totalDiff = 0;\r\n\r\n // Calculate dangling node contribution (nodes with no outgoing links)\r\n let danglingSum = 0;\r\n for (const entity of graph.entities) {\r\n if (outLinks.get(entity.name)!.length === 0) {\r\n danglingSum += scores.get(entity.name)!;\r\n }\r\n }\r\n const danglingContribution = (dampingFactor * danglingSum) / n;\r\n\r\n // Calculate new scores\r\n for (const entity of graph.entities) {\r\n let incomingScore = 0;\r\n const incoming = this.storage.getRelationsTo(entity.name);\r\n\r\n for (const relation of incoming) {\r\n const source = relation.from;\r\n const sourceOutCount = outLinks.get(source)?.length || 1;\r\n incomingScore += scores.get(source)! / sourceOutCount;\r\n }\r\n\r\n const newScore =\r\n (1 - dampingFactor) / n + dampingFactor * incomingScore + danglingContribution;\r\n\r\n newScores.set(entity.name, newScore);\r\n totalDiff += Math.abs(newScore - scores.get(entity.name)!);\r\n }\r\n\r\n // Update scores\r\n for (const [name, score] of newScores) {\r\n scores.set(name, score);\r\n }\r\n\r\n // Check convergence\r\n if (totalDiff < tolerance) {\r\n break;\r\n }\r\n }\r\n\r\n const topEntities = this.getTopEntities(scores, topN);\r\n\r\n return {\r\n scores,\r\n topEntities,\r\n algorithm: 'pagerank',\r\n };\r\n }\r\n\r\n /**\r\n * Get top N entities from a scores map.\r\n */\r\n private getTopEntities(\r\n scores: Map<string, number>,\r\n topN: number\r\n ): Array<{ name: string; score: number }> {\r\n return Array.from(scores.entries())\r\n .sort((a, b) => b[1] - a[1])\r\n .slice(0, topN)\r\n .map(([name, score]) => ({ name, score }));\r\n }\r\n}\r\n","/**\r\n * Search Filter Chain\r\n *\r\n * Centralizes filter logic for all search implementations to eliminate\r\n * duplicate filtering code across BasicSearch, BooleanSearch, FuzzySearch,\r\n * and RankedSearch.\r\n *\r\n * @module search/SearchFilterChain\r\n */\r\n\r\nimport type { Entity } from '../types/index.js';\r\nimport {\r\n normalizeTags,\r\n hasMatchingTag,\r\n isWithinImportanceRange,\r\n validatePagination,\r\n applyPagination,\r\n type ValidatedPagination,\r\n} from '../utils/index.js';\r\n\r\n/**\r\n * Search filter configuration options.\r\n * All filters are optional - entities pass if filter is not specified.\r\n */\r\nexport interface SearchFilters {\r\n /** Tags to filter by (any match) */\r\n tags?: string[];\r\n /** Minimum importance (0-10, inclusive) */\r\n minImportance?: number;\r\n /** Maximum importance (0-10, inclusive) */\r\n maxImportance?: number;\r\n /** Entity type to filter by (exact match) */\r\n entityType?: string;\r\n /** Created after date (ISO 8601, inclusive) */\r\n createdAfter?: string;\r\n /** Created before date (ISO 8601, inclusive) */\r\n createdBefore?: string;\r\n /** Modified after date (ISO 8601, inclusive) */\r\n modifiedAfter?: string;\r\n /** Modified before date (ISO 8601, inclusive) */\r\n modifiedBefore?: string;\r\n}\r\n\r\n/**\r\n * Centralized filter chain for all search implementations.\r\n * Ensures consistent filtering behavior across search types.\r\n *\r\n * @example\r\n * ```typescript\r\n * const filters: SearchFilters = { tags: ['important'], minImportance: 5 };\r\n * const filtered = SearchFilterChain.applyFilters(entities, filters);\r\n * const pagination = SearchFilterChain.validatePagination(0, 50);\r\n * const result = SearchFilterChain.paginate(filtered, pagination);\r\n * ```\r\n */\r\nexport class SearchFilterChain {\r\n /**\r\n * Applies all filters to an array of entities.\r\n * Entities must pass ALL specified filters to be included.\r\n *\r\n * @param entities - Entities to filter\r\n * @param filters - Filter criteria to apply\r\n * @returns Filtered entities array\r\n */\r\n static applyFilters(entities: readonly Entity[], filters: SearchFilters): Entity[] {\r\n // Early return if no filters are active\r\n if (!this.hasActiveFilters(filters)) {\r\n return [...entities];\r\n }\r\n\r\n // Pre-normalize tags once for efficiency\r\n const normalizedSearchTags = filters.tags?.length\r\n ? normalizeTags(filters.tags)\r\n : undefined;\r\n\r\n return entities.filter(entity =>\r\n this.entityPassesFilters(entity, filters, normalizedSearchTags)\r\n );\r\n }\r\n\r\n /**\r\n * Checks if an entity passes all active filters.\r\n * Short-circuits on first failing filter for performance.\r\n *\r\n * @param entity - Entity to check\r\n * @param filters - Filter criteria\r\n * @param normalizedSearchTags - Pre-normalized search tags (for efficiency)\r\n * @returns true if entity passes all filters\r\n */\r\n static entityPassesFilters(\r\n entity: Entity,\r\n filters: SearchFilters,\r\n normalizedSearchTags?: string[]\r\n ): boolean {\r\n // Tag filter - check if entity has any matching tag\r\n if (normalizedSearchTags && normalizedSearchTags.length > 0) {\r\n if (!entity.tags || entity.tags.length === 0) {\r\n return false;\r\n }\r\n const entityTags = normalizeTags(entity.tags);\r\n const hasMatch = normalizedSearchTags.some(tag => entityTags.includes(tag));\r\n if (!hasMatch) {\r\n return false;\r\n }\r\n }\r\n\r\n // Importance filter\r\n if (!isWithinImportanceRange(entity.importance, filters.minImportance, filters.maxImportance)) {\r\n return false;\r\n }\r\n\r\n // Entity type filter\r\n if (filters.entityType && entity.entityType !== filters.entityType) {\r\n return false;\r\n }\r\n\r\n // Created date filter\r\n if (filters.createdAfter || filters.createdBefore) {\r\n if (!entity.createdAt) {\r\n return false;\r\n }\r\n const createdAt = new Date(entity.createdAt);\r\n if (filters.createdAfter && createdAt < new Date(filters.createdAfter)) {\r\n return false;\r\n }\r\n if (filters.createdBefore && createdAt > new Date(filters.createdBefore)) {\r\n return false;\r\n }\r\n }\r\n\r\n // Modified date filter\r\n if (filters.modifiedAfter || filters.modifiedBefore) {\r\n if (!entity.lastModified) {\r\n return false;\r\n }\r\n const modifiedAt = new Date(entity.lastModified);\r\n if (filters.modifiedAfter && modifiedAt < new Date(filters.modifiedAfter)) {\r\n return false;\r\n }\r\n if (filters.modifiedBefore && modifiedAt > new Date(filters.modifiedBefore)) {\r\n return false;\r\n }\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Checks if any filters are actually specified.\r\n * Used for early return optimization.\r\n *\r\n * @param filters - Filter criteria to check\r\n * @returns true if at least one filter is active\r\n */\r\n static hasActiveFilters(filters: SearchFilters): boolean {\r\n return !!(\r\n (filters.tags && filters.tags.length > 0) ||\r\n filters.minImportance !== undefined ||\r\n filters.maxImportance !== undefined ||\r\n filters.entityType ||\r\n filters.createdAfter ||\r\n filters.createdBefore ||\r\n filters.modifiedAfter ||\r\n filters.modifiedBefore\r\n );\r\n }\r\n\r\n /**\r\n * Validates and returns pagination parameters.\r\n * Delegates to paginationUtils.validatePagination.\r\n *\r\n * @param offset - Starting position\r\n * @param limit - Maximum results\r\n * @returns Validated pagination object\r\n */\r\n static validatePagination(offset: number = 0, limit?: number): ValidatedPagination {\r\n return validatePagination(offset, limit);\r\n }\r\n\r\n /**\r\n * Applies pagination to a filtered result set.\r\n *\r\n * @param entities - Entities to paginate\r\n * @param pagination - Validated pagination parameters\r\n * @returns Paginated slice of entities\r\n */\r\n static paginate(entities: Entity[], pagination: ValidatedPagination): Entity[] {\r\n return applyPagination(entities, pagination);\r\n }\r\n\r\n /**\r\n * Convenience method to apply both filters and pagination in one call.\r\n *\r\n * @param entities - Entities to process\r\n * @param filters - Filter criteria\r\n * @param offset - Pagination offset\r\n * @param limit - Pagination limit\r\n * @returns Filtered and paginated entities\r\n */\r\n static filterAndPaginate(\r\n entities: Entity[],\r\n filters: SearchFilters,\r\n offset: number = 0,\r\n limit?: number\r\n ): Entity[] {\r\n const filtered = this.applyFilters(entities, filters);\r\n const pagination = this.validatePagination(offset, limit);\r\n return this.paginate(filtered, pagination);\r\n }\r\n\r\n /**\r\n * Applies tag filter only. Useful when other filters are handled separately.\r\n *\r\n * @param entities - Entities to filter\r\n * @param tags - Tags to filter by\r\n * @returns Filtered entities\r\n */\r\n static filterByTags(entities: Entity[], tags?: string[]): Entity[] {\r\n if (!tags || tags.length === 0) {\r\n return entities;\r\n }\r\n\r\n const normalizedTags = normalizeTags(tags);\r\n return entities.filter(entity => {\r\n if (!entity.tags || entity.tags.length === 0) {\r\n return false;\r\n }\r\n return hasMatchingTag(entity.tags, normalizedTags);\r\n });\r\n }\r\n\r\n /**\r\n * Applies importance filter only. Useful when other filters are handled separately.\r\n *\r\n * @param entities - Entities to filter\r\n * @param minImportance - Minimum importance\r\n * @param maxImportance - Maximum importance\r\n * @returns Filtered entities\r\n */\r\n static filterByImportance(\r\n entities: Entity[],\r\n minImportance?: number,\r\n maxImportance?: number\r\n ): Entity[] {\r\n if (minImportance === undefined && maxImportance === undefined) {\r\n return entities;\r\n }\r\n\r\n return entities.filter(entity =>\r\n isWithinImportanceRange(entity.importance, minImportance, maxImportance)\r\n );\r\n }\r\n}\r\n\r\n// Re-export types for convenience\r\nexport type { ValidatedPagination };\r\n","/**\r\n * Basic Search\r\n *\r\n * Simple text-based search with tag, importance, and date filters with result caching.\r\n *\r\n * @module search/BasicSearch\r\n */\r\n\r\nimport type { KnowledgeGraph } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { isWithinDateRange, SEARCH_LIMITS, searchCaches } from '../utils/index.js';\r\nimport { SearchFilterChain, type SearchFilters } from './SearchFilterChain.js';\r\n\r\n/**\r\n * Performs basic text search with optional filters and caching.\r\n */\r\nexport class BasicSearch {\r\n constructor(\r\n private storage: GraphStorage,\r\n private enableCache: boolean = true\r\n ) {}\r\n\r\n /**\r\n * Search nodes by text query with optional filters and pagination.\r\n *\r\n * Searches across entity names, types, and observations.\r\n *\r\n * @param query - Text to search for (case-insensitive)\r\n * @param tags - Optional tags to filter by\r\n * @param minImportance - Optional minimum importance (0-10)\r\n * @param maxImportance - Optional maximum importance (0-10)\r\n * @param offset - Number of results to skip (default: 0)\r\n * @param limit - Maximum number of results (default: 50, max: 200)\r\n * @returns Filtered knowledge graph with pagination applied\r\n */\r\n async searchNodes(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n ): Promise<KnowledgeGraph> {\r\n // Check cache first\r\n if (this.enableCache) {\r\n const cacheKey = { query, tags, minImportance, maxImportance, offset, limit };\r\n const cached = searchCaches.basic.get(cacheKey);\r\n if (cached) {\r\n return cached;\r\n }\r\n }\r\n\r\n const graph = await this.storage.loadGraph();\r\n const queryLower = query.toLowerCase();\r\n\r\n // First filter by text match (search-specific)\r\n // OPTIMIZED: Uses pre-computed lowercase cache to avoid repeated toLowerCase() calls\r\n const textMatched = graph.entities.filter(e => {\r\n const lowercased = this.storage.getLowercased(e.name);\r\n if (lowercased) {\r\n return (\r\n lowercased.name.includes(queryLower) ||\r\n lowercased.entityType.includes(queryLower) ||\r\n lowercased.observations.some(o => o.includes(queryLower))\r\n );\r\n }\r\n // Fallback for entities not in cache (shouldn't happen in normal use)\r\n return (\r\n e.name.toLowerCase().includes(queryLower) ||\r\n e.entityType.toLowerCase().includes(queryLower) ||\r\n e.observations.some(o => o.toLowerCase().includes(queryLower))\r\n );\r\n });\r\n\r\n // Apply tag and importance filters using SearchFilterChain\r\n const filters: SearchFilters = { tags, minImportance, maxImportance };\r\n const filteredEntities = SearchFilterChain.applyFilters(textMatched, filters);\r\n\r\n // Apply pagination using SearchFilterChain\r\n const pagination = SearchFilterChain.validatePagination(offset, limit);\r\n const paginatedEntities = SearchFilterChain.paginate(filteredEntities, pagination);\r\n\r\n const filteredEntityNames = new Set(paginatedEntities.map(e => e.name));\r\n const filteredRelations = graph.relations.filter(\r\n r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)\r\n );\r\n\r\n const result = { entities: paginatedEntities, relations: filteredRelations };\r\n\r\n // Cache the result\r\n if (this.enableCache) {\r\n const cacheKey = { query, tags, minImportance, maxImportance, offset, limit };\r\n searchCaches.basic.set(cacheKey, result);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Open specific nodes by name.\r\n *\r\n * @param names - Array of entity names to retrieve\r\n * @returns Knowledge graph with specified entities and their relations\r\n */\r\n async openNodes(names: string[]): Promise<KnowledgeGraph> {\r\n const graph = await this.storage.loadGraph();\r\n\r\n const filteredEntities = graph.entities.filter(e => names.includes(e.name));\r\n const filteredEntityNames = new Set(filteredEntities.map(e => e.name));\r\n const filteredRelations = graph.relations.filter(\r\n r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)\r\n );\r\n\r\n return { entities: filteredEntities, relations: filteredRelations };\r\n }\r\n\r\n /**\r\n * Search by date range with optional filters and pagination.\r\n *\r\n * @param startDate - Optional start date (ISO 8601)\r\n * @param endDate - Optional end date (ISO 8601)\r\n * @param entityType - Optional entity type filter\r\n * @param tags - Optional tags filter\r\n * @param offset - Number of results to skip (default: 0)\r\n * @param limit - Maximum number of results (default: 50, max: 200)\r\n * @returns Filtered knowledge graph with pagination applied\r\n */\r\n async searchByDateRange(\r\n startDate?: string,\r\n endDate?: string,\r\n entityType?: string,\r\n tags?: string[],\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n ): Promise<KnowledgeGraph> {\r\n // Check cache first\r\n if (this.enableCache) {\r\n const cacheKey = { method: 'dateRange', startDate, endDate, entityType, tags, offset, limit };\r\n const cached = searchCaches.basic.get(cacheKey);\r\n if (cached) {\r\n return cached;\r\n }\r\n }\r\n\r\n const graph = await this.storage.loadGraph();\r\n\r\n // First filter by date range (search-specific - uses createdAt OR lastModified)\r\n const dateFiltered = graph.entities.filter(e => {\r\n const dateToCheck = e.createdAt || e.lastModified;\r\n if (dateToCheck && !isWithinDateRange(dateToCheck, startDate, endDate)) {\r\n return false;\r\n }\r\n return true;\r\n });\r\n\r\n // Apply entity type and tag filters using SearchFilterChain\r\n const filters: SearchFilters = { tags, entityType };\r\n const filteredEntities = SearchFilterChain.applyFilters(dateFiltered, filters);\r\n\r\n // Apply pagination using SearchFilterChain\r\n const pagination = SearchFilterChain.validatePagination(offset, limit);\r\n const paginatedEntities = SearchFilterChain.paginate(filteredEntities, pagination);\r\n\r\n const filteredEntityNames = new Set(paginatedEntities.map(e => e.name));\r\n const filteredRelations = graph.relations.filter(r => {\r\n const dateToCheck = r.createdAt || r.lastModified;\r\n const inDateRange = !dateToCheck || isWithinDateRange(dateToCheck, startDate, endDate);\r\n const involvesFilteredEntities =\r\n filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to);\r\n\r\n return inDateRange && involvesFilteredEntities;\r\n });\r\n\r\n const result = { entities: paginatedEntities, relations: filteredRelations };\r\n\r\n // Cache the result\r\n if (this.enableCache) {\r\n const cacheKey = { method: 'dateRange', startDate, endDate, entityType, tags, offset, limit };\r\n searchCaches.basic.set(cacheKey, result);\r\n }\r\n\r\n return result;\r\n }\r\n}\r\n","/**\r\n * TF-IDF Index Manager\r\n *\r\n * Manages pre-calculated TF-IDF indexes for fast ranked search.\r\n * Handles index building, incremental updates, and persistence.\r\n *\r\n * @module search/TFIDFIndexManager\r\n */\r\n\r\nimport * as fs from 'fs/promises';\r\nimport * as path from 'path';\r\nimport type { TFIDFIndex, DocumentVector, KnowledgeGraph, ReadonlyKnowledgeGraph } from '../types/index.js';\r\nimport { calculateIDFFromTokenSets, tokenize } from '../utils/index.js';\r\n\r\nconst INDEX_VERSION = '1.0';\r\nconst INDEX_FILENAME = 'tfidf-index.json';\r\n\r\n/**\r\n * Serializable version of TFIDFIndex for JSON storage.\r\n */\r\ninterface SerializedTFIDFIndex {\r\n version: string;\r\n lastUpdated: string;\r\n documents: Array<[string, DocumentVector]>;\r\n idf: Array<[string, number]>;\r\n}\r\n\r\n/**\r\n * Manages TF-IDF index lifecycle: building, updating, and persistence.\r\n */\r\nexport class TFIDFIndexManager {\r\n private indexPath: string;\r\n private index: TFIDFIndex | null = null;\r\n\r\n constructor(storageDir: string) {\r\n this.indexPath = path.join(storageDir, '.indexes', INDEX_FILENAME);\r\n }\r\n\r\n /**\r\n * Build a complete TF-IDF index from a knowledge graph.\r\n *\r\n * @param graph - Knowledge graph to index\r\n * @returns Newly built TF-IDF index\r\n */\r\n async buildIndex(graph: ReadonlyKnowledgeGraph): Promise<TFIDFIndex> {\r\n const documents = new Map<string, DocumentVector>();\r\n const allTokenSets: Set<string>[] = [];\r\n\r\n // Build document vectors - tokenize once per document\r\n for (const entity of graph.entities) {\r\n const documentText = [\r\n entity.name,\r\n entity.entityType,\r\n ...entity.observations,\r\n ].join(' ');\r\n\r\n const tokens = tokenize(documentText);\r\n const tokenSet = new Set(tokens);\r\n allTokenSets.push(tokenSet);\r\n\r\n // Calculate term frequencies\r\n const termFreq: Record<string, number> = {};\r\n for (const term of tokens) {\r\n termFreq[term] = (termFreq[term] || 0) + 1;\r\n }\r\n\r\n documents.set(entity.name, {\r\n entityName: entity.name,\r\n terms: termFreq,\r\n documentText,\r\n });\r\n }\r\n\r\n // Calculate IDF for all terms using pre-tokenized sets (O(1) lookup per document)\r\n const idf = new Map<string, number>();\r\n const allTerms = new Set(allTokenSets.flatMap(s => Array.from(s)));\r\n\r\n for (const term of allTerms) {\r\n const idfScore = calculateIDFFromTokenSets(term, allTokenSets);\r\n idf.set(term, idfScore);\r\n }\r\n\r\n this.index = {\r\n version: INDEX_VERSION,\r\n lastUpdated: new Date().toISOString(),\r\n documents,\r\n idf,\r\n };\r\n\r\n return this.index;\r\n }\r\n\r\n /**\r\n * Update the index incrementally when entities change.\r\n *\r\n * More efficient than rebuilding the entire index.\r\n *\r\n * @param graph - Updated knowledge graph\r\n * @param changedEntityNames - Names of entities that changed\r\n */\r\n async updateIndex(graph: ReadonlyKnowledgeGraph, changedEntityNames: Set<string>): Promise<TFIDFIndex> {\r\n if (!this.index) {\r\n // No existing index, build from scratch\r\n return this.buildIndex(graph);\r\n }\r\n\r\n // Rebuild document vectors for changed entities\r\n const allTokenSets: Set<string>[] = [];\r\n const updatedDocuments = new Map(this.index.documents);\r\n\r\n // Remove deleted entities\r\n for (const entityName of changedEntityNames) {\r\n const entity = graph.entities.find(e => e.name === entityName);\r\n if (!entity) {\r\n updatedDocuments.delete(entityName);\r\n }\r\n }\r\n\r\n // Update/add changed entities - tokenize once per document\r\n for (const entity of graph.entities) {\r\n const documentText = [\r\n entity.name,\r\n entity.entityType,\r\n ...entity.observations,\r\n ].join(' ');\r\n\r\n const tokens = tokenize(documentText);\r\n const tokenSet = new Set(tokens);\r\n allTokenSets.push(tokenSet);\r\n\r\n if (changedEntityNames.has(entity.name)) {\r\n // Calculate term frequencies for changed entity\r\n const termFreq: Record<string, number> = {};\r\n for (const term of tokens) {\r\n termFreq[term] = (termFreq[term] || 0) + 1;\r\n }\r\n\r\n updatedDocuments.set(entity.name, {\r\n entityName: entity.name,\r\n terms: termFreq,\r\n documentText,\r\n });\r\n }\r\n }\r\n\r\n // Recalculate IDF using pre-tokenized sets (O(1) lookup per document)\r\n const idf = new Map<string, number>();\r\n const allTerms = new Set(allTokenSets.flatMap(s => Array.from(s)));\r\n\r\n for (const term of allTerms) {\r\n const idfScore = calculateIDFFromTokenSets(term, allTokenSets);\r\n idf.set(term, idfScore);\r\n }\r\n\r\n this.index = {\r\n version: INDEX_VERSION,\r\n lastUpdated: new Date().toISOString(),\r\n documents: updatedDocuments,\r\n idf,\r\n };\r\n\r\n return this.index;\r\n }\r\n\r\n /**\r\n * Load index from disk.\r\n *\r\n * @returns Loaded index or null if not found\r\n */\r\n async loadIndex(): Promise<TFIDFIndex | null> {\r\n try {\r\n const data = await fs.readFile(this.indexPath, 'utf-8');\r\n const serialized: SerializedTFIDFIndex = JSON.parse(data);\r\n\r\n this.index = {\r\n version: serialized.version,\r\n lastUpdated: serialized.lastUpdated,\r\n documents: new Map(serialized.documents),\r\n idf: new Map(serialized.idf),\r\n };\r\n\r\n return this.index;\r\n } catch (error) {\r\n // Index doesn't exist or is invalid\r\n return null;\r\n }\r\n }\r\n\r\n /**\r\n * Save index to disk.\r\n *\r\n * @param index - Index to save (uses cached index if not provided)\r\n */\r\n async saveIndex(index?: TFIDFIndex): Promise<void> {\r\n const indexToSave = index || this.index;\r\n if (!indexToSave) {\r\n throw new Error('No index to save');\r\n }\r\n\r\n // Ensure index directory exists\r\n const indexDir = path.dirname(this.indexPath);\r\n await fs.mkdir(indexDir, { recursive: true });\r\n\r\n // Serialize Map objects to arrays for JSON\r\n const serialized: SerializedTFIDFIndex = {\r\n version: indexToSave.version,\r\n lastUpdated: indexToSave.lastUpdated,\r\n documents: Array.from(indexToSave.documents.entries()),\r\n idf: Array.from(indexToSave.idf.entries()),\r\n };\r\n\r\n await fs.writeFile(this.indexPath, JSON.stringify(serialized, null, 2), 'utf-8');\r\n }\r\n\r\n /**\r\n * Get the current cached index.\r\n *\r\n * @returns Cached index or null if not loaded\r\n */\r\n getIndex(): TFIDFIndex | null {\r\n return this.index;\r\n }\r\n\r\n /**\r\n * Clear the cached index and delete from disk.\r\n */\r\n async clearIndex(): Promise<void> {\r\n this.index = null;\r\n try {\r\n await fs.unlink(this.indexPath);\r\n } catch {\r\n // Index file doesn't exist, nothing to delete\r\n }\r\n }\r\n\r\n /**\r\n * Check if the index needs rebuilding based on graph state.\r\n *\r\n * @param graph - Current knowledge graph\r\n * @returns True if index should be rebuilt\r\n */\r\n needsRebuild(graph: KnowledgeGraph): boolean {\r\n if (!this.index) {\r\n return true;\r\n }\r\n\r\n // Check if entity count matches\r\n if (this.index.documents.size !== graph.entities.length) {\r\n return true;\r\n }\r\n\r\n // Check if all entities are in index\r\n for (const entity of graph.entities) {\r\n if (!this.index.documents.has(entity.name)) {\r\n return true;\r\n }\r\n }\r\n\r\n return false;\r\n }\r\n\r\n // ==================== Phase 10 Sprint 3: Incremental Index Updates ====================\r\n\r\n /**\r\n * Phase 10 Sprint 3: Add a single document to the index incrementally.\r\n *\r\n * More efficient than rebuilding the entire index for single entity additions.\r\n * Updates TF for the new document and recalculates IDF for affected terms.\r\n *\r\n * @param entity - The entity to add\r\n *\r\n * @example\r\n * ```typescript\r\n * const indexManager = new TFIDFIndexManager('/data');\r\n * await indexManager.loadIndex();\r\n *\r\n * // Add new entity\r\n * indexManager.addDocument({\r\n * name: 'NewEntity',\r\n * entityType: 'person',\r\n * observations: ['Software engineer']\r\n * });\r\n * ```\r\n */\r\n addDocument(entity: { name: string; entityType: string; observations: string[] }): void {\r\n if (!this.index) {\r\n // Can't add to non-existent index\r\n return;\r\n }\r\n\r\n // Build document text and tokens\r\n const documentText = [entity.name, entity.entityType, ...entity.observations].join(' ');\r\n const tokens = tokenize(documentText);\r\n\r\n // Calculate term frequencies\r\n const termFreq: Record<string, number> = {};\r\n for (const term of tokens) {\r\n termFreq[term] = (termFreq[term] || 0) + 1;\r\n }\r\n\r\n // Add to documents map\r\n this.index.documents.set(entity.name, {\r\n entityName: entity.name,\r\n terms: termFreq,\r\n documentText,\r\n });\r\n\r\n // Update IDF for ALL terms because N changed (total document count)\r\n // IDF = log(N/df), and N has increased\r\n this.recalculateAllIDF();\r\n\r\n // Update timestamp\r\n this.index.lastUpdated = new Date().toISOString();\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Remove a single document from the index incrementally.\r\n *\r\n * More efficient than rebuilding the entire index for single entity deletions.\r\n * Recalculates IDF for terms that were in the removed document.\r\n *\r\n * @param entityName - Name of the entity to remove\r\n *\r\n * @example\r\n * ```typescript\r\n * indexManager.removeDocument('DeletedEntity');\r\n * ```\r\n */\r\n removeDocument(entityName: string): void {\r\n if (!this.index) {\r\n return;\r\n }\r\n\r\n const document = this.index.documents.get(entityName);\r\n if (!document) {\r\n return;\r\n }\r\n\r\n // Remove from documents map\r\n this.index.documents.delete(entityName);\r\n\r\n // Update IDF for ALL terms because N changed (total document count)\r\n // IDF = log(N/df), and N has decreased\r\n this.recalculateAllIDF();\r\n\r\n // Update timestamp\r\n this.index.lastUpdated = new Date().toISOString();\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Update a single document in the index incrementally.\r\n *\r\n * More efficient than rebuilding the entire index for single entity updates.\r\n * Handles both term changes and observation updates.\r\n *\r\n * @param entity - The updated entity\r\n *\r\n * @example\r\n * ```typescript\r\n * indexManager.updateDocument({\r\n * name: 'ExistingEntity',\r\n * entityType: 'person',\r\n * observations: ['Updated observations']\r\n * });\r\n * ```\r\n */\r\n updateDocument(entity: { name: string; entityType: string; observations: string[] }): void {\r\n if (!this.index) {\r\n return;\r\n }\r\n\r\n const oldDocument = this.index.documents.get(entity.name);\r\n const oldTerms = oldDocument ? new Set(Object.keys(oldDocument.terms)) : new Set<string>();\r\n\r\n // Build new document\r\n const documentText = [entity.name, entity.entityType, ...entity.observations].join(' ');\r\n const tokens = tokenize(documentText);\r\n const newTerms = new Set(tokens);\r\n\r\n // Calculate term frequencies\r\n const termFreq: Record<string, number> = {};\r\n for (const term of tokens) {\r\n termFreq[term] = (termFreq[term] || 0) + 1;\r\n }\r\n\r\n // Update documents map\r\n this.index.documents.set(entity.name, {\r\n entityName: entity.name,\r\n terms: termFreq,\r\n documentText,\r\n });\r\n\r\n // Find terms that changed (added or removed)\r\n const changedTerms = new Set<string>();\r\n for (const term of oldTerms) {\r\n if (!newTerms.has(term)) {\r\n changedTerms.add(term);\r\n }\r\n }\r\n for (const term of newTerms) {\r\n if (!oldTerms.has(term)) {\r\n changedTerms.add(term);\r\n }\r\n }\r\n\r\n // Recalculate IDF for changed terms\r\n if (changedTerms.size > 0) {\r\n this.recalculateIDFForTerms(changedTerms);\r\n }\r\n\r\n // Update timestamp\r\n this.index.lastUpdated = new Date().toISOString();\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Recalculate IDF scores for a set of terms.\r\n *\r\n * @param terms - Set of terms to recalculate IDF for\r\n * @private\r\n */\r\n private recalculateIDFForTerms(terms: Set<string>): void {\r\n if (!this.index) {\r\n return;\r\n }\r\n\r\n const totalDocs = this.index.documents.size;\r\n if (totalDocs === 0) {\r\n // No documents, clear all IDF for these terms\r\n for (const term of terms) {\r\n this.index.idf.delete(term);\r\n }\r\n return;\r\n }\r\n\r\n // Count documents containing each term\r\n for (const term of terms) {\r\n let docCount = 0;\r\n for (const doc of this.index.documents.values()) {\r\n if (term in doc.terms) {\r\n docCount++;\r\n }\r\n }\r\n\r\n if (docCount > 0) {\r\n // IDF = log(N / df) where N = total docs, df = doc frequency\r\n const idfScore = Math.log(totalDocs / docCount);\r\n this.index.idf.set(term, idfScore);\r\n } else {\r\n // Term no longer exists in any document\r\n this.index.idf.delete(term);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Recalculate IDF scores for ALL terms in the index.\r\n *\r\n * Called when the total document count changes (add/remove document).\r\n * @private\r\n */\r\n private recalculateAllIDF(): void {\r\n if (!this.index) {\r\n return;\r\n }\r\n\r\n const totalDocs = this.index.documents.size;\r\n\r\n if (totalDocs === 0) {\r\n // No documents, clear all IDF\r\n this.index.idf.clear();\r\n return;\r\n }\r\n\r\n // Build term -> document count map\r\n const termDocCounts = new Map<string, number>();\r\n for (const doc of this.index.documents.values()) {\r\n for (const term of Object.keys(doc.terms)) {\r\n termDocCounts.set(term, (termDocCounts.get(term) ?? 0) + 1);\r\n }\r\n }\r\n\r\n // Clear old IDF and recalculate\r\n this.index.idf.clear();\r\n for (const [term, docCount] of termDocCounts) {\r\n // IDF = log(N / df) where N = total docs, df = doc frequency\r\n const idfScore = Math.log(totalDocs / docCount);\r\n this.index.idf.set(term, idfScore);\r\n }\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Check if the index is loaded/initialized.\r\n *\r\n * @returns True if index is available\r\n */\r\n isInitialized(): boolean {\r\n return this.index !== null;\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 3: Get the number of documents in the index.\r\n *\r\n * @returns Document count or 0 if not initialized\r\n */\r\n getDocumentCount(): number {\r\n return this.index?.documents.size ?? 0;\r\n }\r\n}\r\n","/**\r\n * Ranked Search\r\n *\r\n * TF-IDF relevance-based search with scoring and pre-calculated indexes.\r\n *\r\n * @module search/RankedSearch\r\n */\r\n\r\nimport type { Entity, SearchResult, TFIDFIndex, TokenizedEntity } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { calculateTF, calculateIDFFromTokenSets, tokenize } from '../utils/index.js';\r\nimport { SEARCH_LIMITS } from '../utils/constants.js';\r\nimport { TFIDFIndexManager } from './TFIDFIndexManager.js';\r\nimport { SearchFilterChain, type SearchFilters } from './SearchFilterChain.js';\r\n\r\n/**\r\n * Performs TF-IDF ranked search with optional pre-calculated indexes.\r\n */\r\nexport class RankedSearch {\r\n private indexManager: TFIDFIndexManager | null = null;\r\n\r\n /**\r\n * Phase 4 Sprint 2: Fallback token cache for entities.\r\n * Maps entity name -> pre-tokenized entity data.\r\n * Invalidated when graph changes (detected by entity count mismatch).\r\n */\r\n private fallbackTokenCache: Map<string, TokenizedEntity> = new Map();\r\n private cachedEntityCount: number = 0;\r\n\r\n constructor(\r\n private storage: GraphStorage,\r\n storageDir?: string\r\n ) {\r\n // Initialize index manager if storage directory is provided\r\n if (storageDir) {\r\n this.indexManager = new TFIDFIndexManager(storageDir);\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 2: Clear the fallback token cache.\r\n * Called when graph changes are detected or explicitly by external code.\r\n */\r\n clearTokenCache(): void {\r\n this.fallbackTokenCache.clear();\r\n this.cachedEntityCount = 0;\r\n }\r\n\r\n /**\r\n * Initialize and build the TF-IDF index for fast searches.\r\n *\r\n * Should be called after graph changes to keep index up-to-date.\r\n */\r\n async buildIndex(): Promise<void> {\r\n if (!this.indexManager) {\r\n throw new Error('Index manager not initialized. Provide storageDir to constructor.');\r\n }\r\n\r\n const graph = await this.storage.loadGraph();\r\n await this.indexManager.buildIndex(graph);\r\n await this.indexManager.saveIndex();\r\n }\r\n\r\n /**\r\n * Update the index incrementally after entity changes.\r\n *\r\n * @param changedEntityNames - Names of entities that were created, updated, or deleted\r\n */\r\n async updateIndex(changedEntityNames: Set<string>): Promise<void> {\r\n if (!this.indexManager) {\r\n return; // No index manager, skip\r\n }\r\n\r\n const graph = await this.storage.loadGraph();\r\n await this.indexManager.updateIndex(graph, changedEntityNames);\r\n await this.indexManager.saveIndex();\r\n }\r\n\r\n /**\r\n * Load the TF-IDF index from disk if available.\r\n */\r\n private async ensureIndexLoaded(): Promise<TFIDFIndex | null> {\r\n if (!this.indexManager) {\r\n return null;\r\n }\r\n\r\n // Return cached index if already loaded\r\n const cached = this.indexManager.getIndex();\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n // Try to load from disk\r\n return await this.indexManager.loadIndex();\r\n }\r\n\r\n /**\r\n * Search with TF-IDF relevance ranking.\r\n *\r\n * Uses pre-calculated index if available, falls back to on-the-fly calculation.\r\n *\r\n * @param query - Search query\r\n * @param tags - Optional tags filter\r\n * @param minImportance - Optional minimum importance\r\n * @param maxImportance - Optional maximum importance\r\n * @param limit - Maximum results to return (default 50, max 200)\r\n * @returns Array of search results sorted by relevance\r\n */\r\n async searchNodesRanked(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n ): Promise<SearchResult[]> {\r\n // Enforce maximum search limit\r\n const effectiveLimit = Math.min(limit, SEARCH_LIMITS.MAX);\r\n const graph = await this.storage.loadGraph();\r\n\r\n // Apply tag and importance filters using SearchFilterChain\r\n const filters: SearchFilters = { tags, minImportance, maxImportance };\r\n const filteredEntities = SearchFilterChain.applyFilters(graph.entities, filters);\r\n\r\n // Try to use pre-calculated index\r\n const index = await this.ensureIndexLoaded();\r\n const queryTerms = tokenize(query);\r\n\r\n if (index) {\r\n // Use pre-calculated index for fast search\r\n return this.searchWithIndex(filteredEntities, queryTerms, index, effectiveLimit);\r\n } else {\r\n // Fall back to on-the-fly calculation\r\n return this.searchWithoutIndex(filteredEntities, queryTerms, effectiveLimit);\r\n }\r\n }\r\n\r\n /**\r\n * Search using pre-calculated TF-IDF index (fast path).\r\n */\r\n private searchWithIndex(\r\n entities: Entity[],\r\n queryTerms: string[],\r\n index: TFIDFIndex,\r\n limit: number\r\n ): SearchResult[] {\r\n const results: SearchResult[] = [];\r\n\r\n for (const entity of entities) {\r\n const docVector = index.documents.get(entity.name);\r\n if (!docVector) {\r\n continue; // Entity not in index\r\n }\r\n\r\n // Calculate total terms in document (sum of all term frequencies)\r\n const totalTerms = Object.values(docVector.terms).reduce((sum, count) => sum + count, 0);\r\n if (totalTerms === 0) continue;\r\n\r\n // Calculate score using pre-calculated term frequencies and IDF\r\n let totalScore = 0;\r\n const matchedFields: SearchResult['matchedFields'] = {};\r\n\r\n for (const term of queryTerms) {\r\n const termCount = docVector.terms[term] || 0;\r\n const idf = index.idf.get(term) || 0;\r\n\r\n // Calculate TF-IDF: (termCount / totalTerms) * IDF\r\n const tf = termCount / totalTerms;\r\n const tfidf = tf * idf;\r\n totalScore += tfidf;\r\n\r\n // Track which fields matched\r\n if (termCount > 0) {\r\n if (entity.name.toLowerCase().includes(term)) {\r\n matchedFields.name = true;\r\n }\r\n if (entity.entityType.toLowerCase().includes(term)) {\r\n matchedFields.entityType = true;\r\n }\r\n const matchedObs = entity.observations.filter(o =>\r\n o.toLowerCase().includes(term)\r\n );\r\n if (matchedObs.length > 0) {\r\n matchedFields.observations = matchedObs;\r\n }\r\n }\r\n }\r\n\r\n // Only include entities with non-zero scores\r\n if (totalScore > 0) {\r\n results.push({\r\n entity,\r\n score: totalScore,\r\n matchedFields,\r\n });\r\n }\r\n }\r\n\r\n // Sort by score descending and apply limit\r\n return results\r\n .sort((a, b) => b.score - a.score)\r\n .slice(0, limit);\r\n }\r\n\r\n /**\r\n * Search without index (on-the-fly calculation, slow path).\r\n *\r\n * OPTIMIZED: Phase 4 Sprint 2 - Uses fallback token cache to avoid\r\n * repeated tokenization of entities. Pre-tokenizes all documents once\r\n * and caches for subsequent searches.\r\n */\r\n private searchWithoutIndex(\r\n entities: Entity[],\r\n queryTerms: string[],\r\n limit: number\r\n ): SearchResult[] {\r\n const results: SearchResult[] = [];\r\n\r\n // Phase 4 Sprint 2: Check if cache needs invalidation\r\n if (entities.length !== this.cachedEntityCount) {\r\n this.clearTokenCache();\r\n this.cachedEntityCount = entities.length;\r\n }\r\n\r\n // Phase 4 Sprint 2: Get or compute tokenized data for each entity\r\n const documentData: TokenizedEntity[] = entities.map(e => {\r\n // Check cache first\r\n const cached = this.fallbackTokenCache.get(e.name);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n // Compute and cache tokenized data\r\n const text = [e.name, e.entityType, ...e.observations].join(' ');\r\n const tokens = tokenize(text);\r\n const tokenized: TokenizedEntity = {\r\n entity: e,\r\n text,\r\n tokens,\r\n tokenSet: new Set(tokens),\r\n };\r\n this.fallbackTokenCache.set(e.name, tokenized);\r\n return tokenized;\r\n });\r\n\r\n // Pre-compute token sets for IDF calculation (O(1) lookup per document)\r\n const tokenSets = documentData.map(d => d.tokenSet);\r\n\r\n for (const docData of documentData) {\r\n const { entity, text } = docData;\r\n\r\n // Calculate score for each query term\r\n let totalScore = 0;\r\n const matchedFields: SearchResult['matchedFields'] = {};\r\n\r\n for (const term of queryTerms) {\r\n // Calculate TF using pre-tokenized tokens\r\n const tf = calculateTF(term, text);\r\n\r\n // Calculate IDF using pre-computed token sets (O(1) per document)\r\n const idf = calculateIDFFromTokenSets(term, tokenSets);\r\n\r\n // TF-IDF score\r\n const score = tf * idf;\r\n totalScore += score;\r\n\r\n // Track which fields matched\r\n if (entity.name.toLowerCase().includes(term)) {\r\n matchedFields.name = true;\r\n }\r\n if (entity.entityType.toLowerCase().includes(term)) {\r\n matchedFields.entityType = true;\r\n }\r\n const matchedObs = entity.observations.filter(o =>\r\n o.toLowerCase().includes(term)\r\n );\r\n if (matchedObs.length > 0) {\r\n matchedFields.observations = matchedObs;\r\n }\r\n }\r\n\r\n // Only include entities with non-zero scores\r\n if (totalScore > 0) {\r\n results.push({\r\n entity,\r\n score: totalScore,\r\n matchedFields,\r\n });\r\n }\r\n }\r\n\r\n // Sort by score descending and apply limit\r\n return results\r\n .sort((a, b) => b.score - a.score)\r\n .slice(0, limit);\r\n }\r\n}\r\n","/**\r\n * Boolean Search\r\n *\r\n * Advanced search with boolean operators (AND, OR, NOT) and field-specific queries.\r\n *\r\n * @module search/BooleanSearch\r\n */\r\n\r\nimport type { BooleanQueryNode, Entity, KnowledgeGraph } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { SEARCH_LIMITS, QUERY_LIMITS } from '../utils/constants.js';\r\nimport { ValidationError } from '../utils/errors.js';\r\nimport { SearchFilterChain, type SearchFilters } from './SearchFilterChain.js';\r\n\r\n/**\r\n * Phase 4 Sprint 4: Cache entry for Boolean search AST and results.\r\n */\r\ninterface BooleanCacheEntry {\r\n /** Parsed AST */\r\n ast: BooleanQueryNode;\r\n /** Cached entity names that matched */\r\n entityNames: string[];\r\n /** Entity count when cache was created (for invalidation) */\r\n entityCount: number;\r\n /** Timestamp when cached */\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Phase 4 Sprint 4: Maximum AST cache size.\r\n */\r\nconst AST_CACHE_MAX_SIZE = 50;\r\n\r\n/**\r\n * Phase 4 Sprint 4: Result cache max size.\r\n */\r\nconst RESULT_CACHE_MAX_SIZE = 100;\r\n\r\n/**\r\n * Phase 4 Sprint 4: Cache TTL in milliseconds (5 minutes).\r\n */\r\nconst BOOLEAN_CACHE_TTL_MS = 5 * 60 * 1000;\r\n\r\n/**\r\n * Performs boolean search with query parsing and AST evaluation.\r\n */\r\nexport class BooleanSearch {\r\n /**\r\n * Phase 4 Sprint 4: AST cache to avoid re-parsing queries.\r\n * Maps query string -> parsed AST.\r\n */\r\n private astCache: Map<string, BooleanQueryNode> = new Map();\r\n\r\n /**\r\n * Phase 4 Sprint 4: Result cache for boolean search.\r\n * Maps cache key -> cached results.\r\n */\r\n private resultCache: Map<string, BooleanCacheEntry> = new Map();\r\n\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Phase 4 Sprint 4: Generate cache key for boolean search.\r\n */\r\n private generateCacheKey(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n offset?: number,\r\n limit?: number\r\n ): string {\r\n return JSON.stringify({\r\n q: query,\r\n tags: tags?.sort().join(',') ?? '',\r\n min: minImportance,\r\n max: maxImportance,\r\n off: offset,\r\n lim: limit,\r\n });\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 4: Clear all caches.\r\n */\r\n clearCache(): void {\r\n this.astCache.clear();\r\n this.resultCache.clear();\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 4: Cleanup old cache entries.\r\n */\r\n private cleanupResultCache(): void {\r\n const now = Date.now();\r\n const entries = Array.from(this.resultCache.entries());\r\n\r\n // Remove expired entries\r\n for (const [key, entry] of entries) {\r\n if (now - entry.timestamp > BOOLEAN_CACHE_TTL_MS) {\r\n this.resultCache.delete(key);\r\n }\r\n }\r\n\r\n // If still over limit, remove oldest entries\r\n if (this.resultCache.size > RESULT_CACHE_MAX_SIZE) {\r\n const sortedEntries = entries\r\n .filter(([k]) => this.resultCache.has(k))\r\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\r\n\r\n const toRemove = sortedEntries.slice(0, this.resultCache.size - RESULT_CACHE_MAX_SIZE);\r\n for (const [key] of toRemove) {\r\n this.resultCache.delete(key);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 4: Get or parse AST for a query.\r\n */\r\n private getOrParseAST(query: string): BooleanQueryNode {\r\n // Check AST cache\r\n const cached = this.astCache.get(query);\r\n if (cached) {\r\n return cached;\r\n }\r\n\r\n // Parse and cache\r\n const ast = this.parseBooleanQuery(query);\r\n\r\n // Enforce cache size limit\r\n if (this.astCache.size >= AST_CACHE_MAX_SIZE) {\r\n // Remove first entry (oldest)\r\n const firstKey = this.astCache.keys().next().value;\r\n if (firstKey) this.astCache.delete(firstKey);\r\n }\r\n\r\n this.astCache.set(query, ast);\r\n return ast;\r\n }\r\n\r\n /**\r\n * Boolean search with support for AND, OR, NOT operators, field-specific queries, and pagination.\r\n *\r\n * Phase 4 Sprint 4: Implements AST caching and result caching for repeated queries.\r\n *\r\n * Query syntax examples:\r\n * - \"alice AND programming\" - Both terms must match\r\n * - \"type:person OR type:organization\" - Either type matches\r\n * - \"NOT archived\" - Exclude archived items\r\n * - \"name:alice AND (observation:coding OR observation:teaching)\"\r\n *\r\n * @param query - Boolean query string\r\n * @param tags - Optional tags filter\r\n * @param minImportance - Optional minimum importance\r\n * @param maxImportance - Optional maximum importance\r\n * @param offset - Number of results to skip (default: 0)\r\n * @param limit - Maximum number of results (default: 50, max: 200)\r\n * @returns Filtered knowledge graph matching the boolean query with pagination applied\r\n */\r\n async booleanSearch(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n ): Promise<KnowledgeGraph> {\r\n // Validate query length\r\n if (query.length > QUERY_LIMITS.MAX_QUERY_LENGTH) {\r\n throw new ValidationError(\r\n 'Query too long',\r\n [`Query length ${query.length} exceeds maximum of ${QUERY_LIMITS.MAX_QUERY_LENGTH} characters`]\r\n );\r\n }\r\n\r\n const graph = await this.storage.loadGraph();\r\n\r\n // Phase 4 Sprint 4: Check result cache\r\n const cacheKey = this.generateCacheKey(query, tags, minImportance, maxImportance, offset, limit);\r\n const cached = this.resultCache.get(cacheKey);\r\n\r\n if (cached && cached.entityCount === graph.entities.length) {\r\n const now = Date.now();\r\n if (now - cached.timestamp < BOOLEAN_CACHE_TTL_MS) {\r\n // Return cached results\r\n const cachedNameSet = new Set(cached.entityNames);\r\n const cachedEntities = graph.entities.filter(e => cachedNameSet.has(e.name));\r\n const cachedRelations = graph.relations.filter(\r\n r => cachedNameSet.has(r.from) && cachedNameSet.has(r.to)\r\n );\r\n return { entities: cachedEntities as Entity[], relations: cachedRelations };\r\n }\r\n }\r\n\r\n // Phase 4 Sprint 4: Use cached AST or parse new one\r\n let queryAst: BooleanQueryNode;\r\n try {\r\n queryAst = this.getOrParseAST(query);\r\n } catch (error) {\r\n throw new Error(\r\n `Failed to parse boolean query: ${error instanceof Error ? error.message : String(error)}`\r\n );\r\n }\r\n\r\n // Validate query complexity\r\n this.validateQueryComplexity(queryAst);\r\n\r\n // First filter by boolean query evaluation (search-specific)\r\n const booleanMatched = graph.entities.filter(e =>\r\n this.evaluateBooleanQuery(queryAst, e)\r\n );\r\n\r\n // Apply tag and importance filters using SearchFilterChain\r\n const filters: SearchFilters = { tags, minImportance, maxImportance };\r\n const filteredEntities = SearchFilterChain.applyFilters(booleanMatched, filters);\r\n\r\n // Apply pagination using SearchFilterChain\r\n const pagination = SearchFilterChain.validatePagination(offset, limit);\r\n const paginatedEntities = SearchFilterChain.paginate(filteredEntities, pagination);\r\n\r\n // Phase 4 Sprint 4: Cache the results\r\n this.resultCache.set(cacheKey, {\r\n ast: queryAst,\r\n entityNames: paginatedEntities.map(e => e.name),\r\n entityCount: graph.entities.length,\r\n timestamp: Date.now(),\r\n });\r\n\r\n // Cleanup old cache entries periodically\r\n if (this.resultCache.size > RESULT_CACHE_MAX_SIZE / 2) {\r\n this.cleanupResultCache();\r\n }\r\n\r\n const filteredEntityNames = new Set(paginatedEntities.map(e => e.name));\r\n const filteredRelations = graph.relations.filter(\r\n r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)\r\n );\r\n\r\n return { entities: paginatedEntities, relations: filteredRelations };\r\n }\r\n\r\n /**\r\n * Tokenize a boolean query into tokens.\r\n *\r\n * Handles quoted strings, parentheses, and operators.\r\n */\r\n private tokenizeBooleanQuery(query: string): string[] {\r\n const tokens: string[] = [];\r\n let current = '';\r\n let inQuotes = false;\r\n\r\n for (let i = 0; i < query.length; i++) {\r\n const char = query[i];\r\n\r\n if (char === '\"') {\r\n if (inQuotes) {\r\n // End of quoted string\r\n tokens.push(current);\r\n current = '';\r\n inQuotes = false;\r\n } else {\r\n // Start of quoted string\r\n if (current.trim()) {\r\n tokens.push(current.trim());\r\n current = '';\r\n }\r\n inQuotes = true;\r\n }\r\n } else if (!inQuotes && (char === '(' || char === ')')) {\r\n // Parentheses are separate tokens\r\n if (current.trim()) {\r\n tokens.push(current.trim());\r\n current = '';\r\n }\r\n tokens.push(char);\r\n } else if (!inQuotes && /\\s/.test(char)) {\r\n // Whitespace outside quotes\r\n if (current.trim()) {\r\n tokens.push(current.trim());\r\n current = '';\r\n }\r\n } else {\r\n current += char;\r\n }\r\n }\r\n\r\n if (current.trim()) {\r\n tokens.push(current.trim());\r\n }\r\n\r\n return tokens;\r\n }\r\n\r\n /**\r\n * Parse a boolean search query into an AST.\r\n *\r\n * Supports: AND, OR, NOT, parentheses, field-specific queries (field:value)\r\n */\r\n private parseBooleanQuery(query: string): BooleanQueryNode {\r\n const tokens = this.tokenizeBooleanQuery(query);\r\n let position = 0;\r\n\r\n const peek = (): string | undefined => tokens[position];\r\n const consume = (): string | undefined => tokens[position++];\r\n\r\n // Parse OR expressions (lowest precedence)\r\n const parseOr = (): BooleanQueryNode => {\r\n let left = parseAnd();\r\n\r\n while (peek()?.toUpperCase() === 'OR') {\r\n consume(); // consume 'OR'\r\n const right = parseAnd();\r\n left = { type: 'OR', children: [left, right] };\r\n }\r\n\r\n return left;\r\n };\r\n\r\n // Parse AND expressions\r\n const parseAnd = (): BooleanQueryNode => {\r\n let left = parseNot();\r\n\r\n while (peek() && peek()?.toUpperCase() !== 'OR' && peek() !== ')') {\r\n // Implicit AND if next token is not OR or )\r\n if (peek()?.toUpperCase() === 'AND') {\r\n consume(); // consume 'AND'\r\n }\r\n const right = parseNot();\r\n left = { type: 'AND', children: [left, right] };\r\n }\r\n\r\n return left;\r\n };\r\n\r\n // Parse NOT expressions\r\n const parseNot = (): BooleanQueryNode => {\r\n if (peek()?.toUpperCase() === 'NOT') {\r\n consume(); // consume 'NOT'\r\n const child = parseNot();\r\n return { type: 'NOT', child };\r\n }\r\n return parsePrimary();\r\n };\r\n\r\n // Parse primary expressions (terms, field queries, parentheses)\r\n const parsePrimary = (): BooleanQueryNode => {\r\n const token = peek();\r\n\r\n if (!token) {\r\n throw new Error('Unexpected end of query');\r\n }\r\n\r\n // Parentheses\r\n if (token === '(') {\r\n consume(); // consume '('\r\n const node = parseOr();\r\n if (consume() !== ')') {\r\n throw new Error('Expected closing parenthesis');\r\n }\r\n return node;\r\n }\r\n\r\n // Field-specific query (field:value)\r\n if (token.includes(':')) {\r\n consume();\r\n const [field, ...valueParts] = token.split(':');\r\n const value = valueParts.join(':'); // Handle colons in value\r\n return { type: 'TERM', field: field.toLowerCase(), value: value.toLowerCase() };\r\n }\r\n\r\n // Regular term\r\n consume();\r\n return { type: 'TERM', value: token.toLowerCase() };\r\n };\r\n\r\n const result = parseOr();\r\n\r\n // Check for unconsumed tokens\r\n if (position < tokens.length) {\r\n throw new Error(`Unexpected token: ${tokens[position]}`);\r\n }\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Evaluate a boolean query AST against an entity.\r\n */\r\n private evaluateBooleanQuery(node: BooleanQueryNode, entity: Entity): boolean {\r\n switch (node.type) {\r\n case 'AND':\r\n return node.children.every(child => this.evaluateBooleanQuery(child, entity));\r\n\r\n case 'OR':\r\n return node.children.some(child => this.evaluateBooleanQuery(child, entity));\r\n\r\n case 'NOT':\r\n return !this.evaluateBooleanQuery(node.child, entity);\r\n\r\n case 'TERM': {\r\n const value = node.value;\r\n // OPTIMIZED: Use pre-computed lowercase cache\r\n const lowercased = this.storage.getLowercased(entity.name);\r\n\r\n // Field-specific search\r\n if (node.field) {\r\n switch (node.field) {\r\n case 'name':\r\n return lowercased ? lowercased.name.includes(value) : entity.name.toLowerCase().includes(value);\r\n case 'type':\r\n case 'entitytype':\r\n return lowercased ? lowercased.entityType.includes(value) : entity.entityType.toLowerCase().includes(value);\r\n case 'observation':\r\n case 'observations':\r\n // OPTIMIZED: Use observation index for simple single-word terms (O(1) vs O(n))\r\n // The index only matches complete words, not substrings, so we can only\r\n // use it as a quick positive check. If not found in index, fall through\r\n // to substring matching for compatibility.\r\n if (this.isSimpleTerm(value) && !value.includes(' ')) {\r\n const candidateNames = this.storage.getEntitiesByObservationWord(value);\r\n if (candidateNames.has(entity.name)) {\r\n return true; // O(1) positive match\r\n }\r\n // Not found in index - entity doesn't have this complete word,\r\n // but might contain it as substring - fall through to check\r\n }\r\n // Linear scan for substring matches, phrases, and patterns\r\n return lowercased\r\n ? lowercased.observations.some(obs => obs.includes(value))\r\n : entity.observations.some(obs => obs.toLowerCase().includes(value));\r\n case 'tag':\r\n case 'tags':\r\n return lowercased\r\n ? lowercased.tags.some(tag => tag.includes(value))\r\n : (entity.tags?.some(tag => tag.toLowerCase().includes(value)) || false);\r\n default:\r\n // Unknown field, search all text fields\r\n return this.entityMatchesTerm(entity, value, lowercased);\r\n }\r\n }\r\n\r\n // General search across all fields\r\n return this.entityMatchesTerm(entity, value, lowercased);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Check if a search term is simple (no regex or wildcards).\r\n * Simple terms can use the O(1) observation index.\r\n */\r\n private isSimpleTerm(term: string): boolean {\r\n const specialChars = /[.*+?^${}()|\\\\[\\]]/;\r\n return !specialChars.test(term);\r\n }\r\n\r\n /**\r\n * Check if entity matches a search term in any text field.\r\n * OPTIMIZED: Uses pre-computed lowercase data when available.\r\n */\r\n private entityMatchesTerm(entity: Entity, term: string, lowercased?: ReturnType<typeof this.storage.getLowercased>): boolean {\r\n if (lowercased) {\r\n return (\r\n lowercased.name.includes(term) ||\r\n lowercased.entityType.includes(term) ||\r\n lowercased.observations.some(obs => obs.includes(term)) ||\r\n lowercased.tags.some(tag => tag.includes(term))\r\n );\r\n }\r\n\r\n // Fallback for entities not in cache\r\n const termLower = term.toLowerCase();\r\n return (\r\n entity.name.toLowerCase().includes(termLower) ||\r\n entity.entityType.toLowerCase().includes(termLower) ||\r\n entity.observations.some(obs => obs.toLowerCase().includes(termLower)) ||\r\n (entity.tags?.some(tag => tag.toLowerCase().includes(termLower)) || false)\r\n );\r\n }\r\n\r\n /**\r\n * Validate query complexity to prevent resource exhaustion.\r\n * Checks nesting depth, term count, and operator count against configured limits.\r\n */\r\n private validateQueryComplexity(node: BooleanQueryNode, depth: number = 0): void {\r\n // Check nesting depth\r\n if (depth > QUERY_LIMITS.MAX_DEPTH) {\r\n throw new ValidationError(\r\n 'Query too complex',\r\n [`Query nesting depth ${depth} exceeds maximum of ${QUERY_LIMITS.MAX_DEPTH}`]\r\n );\r\n }\r\n\r\n // Count terms and operators recursively\r\n const complexity = this.calculateQueryComplexity(node);\r\n\r\n if (complexity.terms > QUERY_LIMITS.MAX_TERMS) {\r\n throw new ValidationError(\r\n 'Query too complex',\r\n [`Query has ${complexity.terms} terms, exceeds maximum of ${QUERY_LIMITS.MAX_TERMS}`]\r\n );\r\n }\r\n\r\n if (complexity.operators > QUERY_LIMITS.MAX_OPERATORS) {\r\n throw new ValidationError(\r\n 'Query too complex',\r\n [`Query has ${complexity.operators} operators, exceeds maximum of ${QUERY_LIMITS.MAX_OPERATORS}`]\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Calculate query complexity metrics.\r\n */\r\n private calculateQueryComplexity(\r\n node: BooleanQueryNode,\r\n depth: number = 0\r\n ): { terms: number; operators: number; maxDepth: number } {\r\n switch (node.type) {\r\n case 'AND':\r\n case 'OR':\r\n const childResults = node.children.map(child => this.calculateQueryComplexity(child, depth + 1));\r\n return {\r\n terms: childResults.reduce((sum, r) => sum + r.terms, 0),\r\n operators: childResults.reduce((sum, r) => sum + r.operators, 1), // +1 for current operator\r\n maxDepth: Math.max(depth, ...childResults.map(r => r.maxDepth)),\r\n };\r\n\r\n case 'NOT':\r\n const notResult = this.calculateQueryComplexity(node.child, depth + 1);\r\n return {\r\n terms: notResult.terms,\r\n operators: notResult.operators + 1,\r\n maxDepth: Math.max(depth, notResult.maxDepth),\r\n };\r\n\r\n case 'TERM':\r\n return {\r\n terms: 1,\r\n operators: 0,\r\n maxDepth: depth,\r\n };\r\n }\r\n }\r\n}\r\n","/**\r\n * Fuzzy Search\r\n *\r\n * Search with typo tolerance using Levenshtein distance similarity.\r\n * Uses workerpool for parallel processing on large datasets.\r\n *\r\n * @module search/FuzzySearch\r\n */\r\n\r\nimport type { Entity, KnowledgeGraph } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { levenshteinDistance } from '../utils/index.js';\r\nimport { SEARCH_LIMITS } from '../utils/constants.js';\r\nimport { SearchFilterChain, type SearchFilters } from './SearchFilterChain.js';\r\nimport workerpool, { type Pool } from '@danielsimonjr/workerpool';\r\nimport { fileURLToPath } from 'url';\r\nimport { dirname, join, sep } from 'path';\r\n\r\n/**\r\n * Default fuzzy search similarity threshold (70% match required).\r\n * Lower values are more permissive (more typos tolerated).\r\n * Higher values are stricter (fewer typos tolerated).\r\n */\r\nexport const DEFAULT_FUZZY_THRESHOLD = 0.7;\r\n\r\n/**\r\n * Phase 4 Sprint 3: Cache entry for fuzzy search results.\r\n */\r\ninterface FuzzyCacheEntry {\r\n /** Cached entity names that matched */\r\n entityNames: string[];\r\n /** Entity count when cache was created (for invalidation) */\r\n entityCount: number;\r\n /** Timestamp when cached */\r\n timestamp: number;\r\n}\r\n\r\n/**\r\n * Phase 4 Sprint 3: Maximum cache size to prevent memory bloat.\r\n */\r\nconst FUZZY_CACHE_MAX_SIZE = 100;\r\n\r\n/**\r\n * Phase 4 Sprint 3: Cache TTL in milliseconds (5 minutes).\r\n */\r\nconst FUZZY_CACHE_TTL_MS = 5 * 60 * 1000;\r\n\r\n/**\r\n * Phase 7 Sprint 3: Minimum number of entities to activate worker pool.\r\n */\r\nconst WORKER_MIN_ENTITIES = 500;\r\n\r\n/**\r\n * Phase 7 Sprint 3: Maximum threshold for worker pool activation.\r\n * Higher thresholds have fewer matches, so single-threaded is faster.\r\n */\r\nconst WORKER_MAX_THRESHOLD = 0.8;\r\n\r\n/**\r\n * Match result from worker.\r\n */\r\ninterface MatchResult {\r\n name: string;\r\n score: number;\r\n matchedIn: 'name' | 'observation';\r\n}\r\n\r\n/**\r\n * Options for FuzzySearch constructor.\r\n */\r\nexport interface FuzzySearchOptions {\r\n /**\r\n * Whether to use worker pool for parallel processing.\r\n * Set to false for testing or when workers are not available.\r\n * Default: true\r\n */\r\n useWorkerPool?: boolean;\r\n}\r\n\r\n/**\r\n * Performs fuzzy search with configurable similarity threshold.\r\n */\r\nexport class FuzzySearch {\r\n /**\r\n * Phase 4 Sprint 3: Result cache for fuzzy search.\r\n * Maps cache key -> cached entity names.\r\n */\r\n private fuzzyResultCache: Map<string, FuzzyCacheEntry> = new Map();\r\n\r\n /**\r\n * Phase 8: Worker pool using workerpool library.\r\n * Initialized lazily when needed.\r\n */\r\n private workerPool: Pool | null = null;\r\n\r\n /**\r\n * Phase 7 Sprint 3: Path to the worker script.\r\n */\r\n private workerPath: string;\r\n\r\n /**\r\n * Phase 8: Whether to use worker pool for parallel processing.\r\n * Can be disabled for testing or when workers are not available.\r\n */\r\n private useWorkerPool: boolean;\r\n\r\n constructor(private storage: GraphStorage, options: FuzzySearchOptions = {}) {\r\n this.useWorkerPool = options.useWorkerPool ?? true;\r\n // Calculate worker path using ESM module resolution\r\n const currentFileUrl = import.meta.url;\r\n const currentDir = dirname(fileURLToPath(currentFileUrl));\r\n\r\n // Check if we're running from src/ (during tests) or dist/ (production)\r\n const isRunningFromSrc = currentDir.includes(`${sep}src${sep}`);\r\n\r\n if (isRunningFromSrc) {\r\n // During tests, worker is in dist/workers/ relative to project root\r\n const projectRoot = join(currentDir, '..', '..');\r\n this.workerPath = join(projectRoot, 'dist', 'workers', 'levenshteinWorker.js');\r\n } else {\r\n // In production, worker is in dist/workers/ relative to current dist/search/\r\n this.workerPath = join(currentDir, '..', 'workers', 'levenshteinWorker.js');\r\n }\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 3: Generate cache key for fuzzy search parameters.\r\n */\r\n private generateCacheKey(\r\n query: string,\r\n threshold: number,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n offset?: number,\r\n limit?: number\r\n ): string {\r\n return JSON.stringify({\r\n q: query.toLowerCase(),\r\n t: threshold,\r\n tags: tags?.sort().join(',') ?? '',\r\n min: minImportance,\r\n max: maxImportance,\r\n off: offset,\r\n lim: limit,\r\n });\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 3: Clear the fuzzy search cache.\r\n */\r\n clearCache(): void {\r\n this.fuzzyResultCache.clear();\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 3: Invalidate stale cache entries.\r\n */\r\n private cleanupCache(): void {\r\n const now = Date.now();\r\n const entries = Array.from(this.fuzzyResultCache.entries());\r\n\r\n // Remove expired entries\r\n for (const [key, entry] of entries) {\r\n if (now - entry.timestamp > FUZZY_CACHE_TTL_MS) {\r\n this.fuzzyResultCache.delete(key);\r\n }\r\n }\r\n\r\n // If still over limit, remove oldest entries\r\n if (this.fuzzyResultCache.size > FUZZY_CACHE_MAX_SIZE) {\r\n const sortedEntries = entries\r\n .filter(([k]) => this.fuzzyResultCache.has(k))\r\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\r\n\r\n const toRemove = sortedEntries.slice(0, this.fuzzyResultCache.size - FUZZY_CACHE_MAX_SIZE);\r\n for (const [key] of toRemove) {\r\n this.fuzzyResultCache.delete(key);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Fuzzy search for entities with typo tolerance and pagination.\r\n *\r\n * Uses Levenshtein distance to calculate similarity between strings.\r\n * Matches if similarity >= threshold (0.0 to 1.0).\r\n *\r\n * Phase 4 Sprint 3: Implements result caching for repeated queries.\r\n *\r\n * @param query - Search query\r\n * @param threshold - Similarity threshold (0.0 to 1.0), default DEFAULT_FUZZY_THRESHOLD\r\n * @param tags - Optional tags filter\r\n * @param minImportance - Optional minimum importance\r\n * @param maxImportance - Optional maximum importance\r\n * @param offset - Number of results to skip (default: 0)\r\n * @param limit - Maximum number of results (default: 50, max: 200)\r\n * @returns Filtered knowledge graph with fuzzy matches and pagination applied\r\n */\r\n async fuzzySearch(\r\n query: string,\r\n threshold: number = DEFAULT_FUZZY_THRESHOLD,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n offset: number = 0,\r\n limit: number = SEARCH_LIMITS.DEFAULT\r\n ): Promise<KnowledgeGraph> {\r\n const graph = await this.storage.loadGraph();\r\n const queryLower = query.toLowerCase();\r\n\r\n // Phase 4 Sprint 3: Generate cache key and check cache\r\n const cacheKey = this.generateCacheKey(query, threshold, tags, minImportance, maxImportance, offset, limit);\r\n const cached = this.fuzzyResultCache.get(cacheKey);\r\n\r\n // Check if cache is valid (entity count hasn't changed)\r\n if (cached && cached.entityCount === graph.entities.length) {\r\n const now = Date.now();\r\n if (now - cached.timestamp < FUZZY_CACHE_TTL_MS) {\r\n // Return cached results\r\n const cachedNameSet = new Set(cached.entityNames);\r\n const cachedEntities = graph.entities.filter(e => cachedNameSet.has(e.name));\r\n const cachedEntityNames = new Set(cached.entityNames);\r\n const cachedRelations = graph.relations.filter(\r\n r => cachedEntityNames.has(r.from) && cachedEntityNames.has(r.to)\r\n );\r\n return { entities: cachedEntities, relations: cachedRelations };\r\n }\r\n }\r\n\r\n // Phase 7 Sprint 3: Use worker pool for large graphs with low thresholds\r\n // Phase 8: Respect useWorkerPool flag for testing\r\n const shouldUseWorkers =\r\n this.useWorkerPool &&\r\n graph.entities.length >= WORKER_MIN_ENTITIES &&\r\n threshold < WORKER_MAX_THRESHOLD;\r\n\r\n let fuzzyMatched: Entity[];\r\n\r\n if (shouldUseWorkers) {\r\n fuzzyMatched = await this.searchWithWorkers(query, threshold, graph.entities as Entity[]);\r\n } else {\r\n // Perform single-threaded fuzzy search\r\n fuzzyMatched = this.performFuzzyMatch(graph.entities, queryLower, threshold);\r\n }\r\n\r\n // Apply tag and importance filters using SearchFilterChain\r\n const filters: SearchFilters = { tags, minImportance, maxImportance };\r\n const filteredEntities = SearchFilterChain.applyFilters(fuzzyMatched, filters);\r\n\r\n // Apply pagination using SearchFilterChain\r\n const pagination = SearchFilterChain.validatePagination(offset, limit);\r\n const paginatedEntities = SearchFilterChain.paginate(filteredEntities, pagination);\r\n\r\n // Phase 4 Sprint 3: Cache the results\r\n this.fuzzyResultCache.set(cacheKey, {\r\n entityNames: paginatedEntities.map(e => e.name),\r\n entityCount: graph.entities.length,\r\n timestamp: Date.now(),\r\n });\r\n\r\n // Cleanup old cache entries periodically\r\n if (this.fuzzyResultCache.size > FUZZY_CACHE_MAX_SIZE / 2) {\r\n this.cleanupCache();\r\n }\r\n\r\n const filteredEntityNames = new Set(paginatedEntities.map(e => e.name));\r\n const filteredRelations = graph.relations.filter(\r\n r => filteredEntityNames.has(r.from) && filteredEntityNames.has(r.to)\r\n );\r\n\r\n return {\r\n entities: paginatedEntities,\r\n relations: filteredRelations,\r\n };\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 3: Perform the actual fuzzy matching logic.\r\n * Extracted from fuzzySearch for cleaner code structure.\r\n */\r\n private performFuzzyMatch(entities: readonly Entity[], queryLower: string, threshold: number): Entity[] {\r\n return entities.filter(e => {\r\n const lowercased = this.storage.getLowercased(e.name);\r\n\r\n // Check name match (use pre-computed lowercase)\r\n const nameLower = lowercased?.name ?? e.name.toLowerCase();\r\n if (this.isFuzzyMatchLower(nameLower, queryLower, threshold)) return true;\r\n\r\n // Check type match (use pre-computed lowercase)\r\n const typeLower = lowercased?.entityType ?? e.entityType.toLowerCase();\r\n if (this.isFuzzyMatchLower(typeLower, queryLower, threshold)) return true;\r\n\r\n // Check observations (use pre-computed lowercase array)\r\n const obsLower = lowercased?.observations ?? e.observations.map(o => o.toLowerCase());\r\n return obsLower.some(\r\n o =>\r\n // For observations, split into words and check each word\r\n o\r\n .split(/\\s+/)\r\n .some(word => this.isFuzzyMatchLower(word, queryLower, threshold)) ||\r\n // Also check if the observation contains the query\r\n this.isFuzzyMatchLower(o, queryLower, threshold)\r\n );\r\n }) as Entity[];\r\n }\r\n\r\n /**\r\n * Check if two already-lowercase strings match with fuzzy logic.\r\n *\r\n * OPTIMIZED: Skips toLowerCase() calls when strings are already lowercase.\r\n *\r\n * @param s1 - First string (already lowercase)\r\n * @param s2 - Second string (already lowercase)\r\n * @param threshold - Similarity threshold (0.0 to 1.0)\r\n * @returns True if strings match fuzzily\r\n */\r\n private isFuzzyMatchLower(s1: string, s2: string, threshold: number = 0.7): boolean {\r\n // Exact match\r\n if (s1 === s2) return true;\r\n\r\n // One contains the other\r\n if (s1.includes(s2) || s2.includes(s1)) return true;\r\n\r\n // Calculate similarity using Levenshtein distance\r\n const distance = levenshteinDistance(s1, s2);\r\n const maxLength = Math.max(s1.length, s2.length);\r\n const similarity = 1 - distance / maxLength;\r\n\r\n return similarity >= threshold;\r\n }\r\n\r\n /**\r\n * Phase 8: Perform fuzzy search using workerpool for parallel processing.\r\n *\r\n * Splits entities into chunks and processes them in parallel using worker threads.\r\n * Falls back to single-threaded search if worker execution fails.\r\n *\r\n * @param query - Search query\r\n * @param threshold - Similarity threshold\r\n * @param entities - Entities to search\r\n * @returns Array of matched entities\r\n */\r\n private async searchWithWorkers(\r\n query: string,\r\n threshold: number,\r\n entities: Entity[]\r\n ): Promise<Entity[]> {\r\n try {\r\n // Initialize worker pool lazily using workerpool\r\n if (!this.workerPool) {\r\n // Enable ESM module support for Node.js 20+\r\n // The 'type: module' option is needed for ESM workers but may not be in @types/node\r\n const workerThreadOpts = { type: 'module' } as Record<string, unknown>;\r\n this.workerPool = workerpool.pool(this.workerPath, {\r\n maxWorkers: Math.max(1, workerpool.cpus - 1),\r\n workerType: 'thread',\r\n workerThreadOpts,\r\n });\r\n }\r\n\r\n // Split entities into chunks based on CPU count\r\n const numWorkers = Math.max(1, workerpool.cpus - 1);\r\n const chunkSize = Math.ceil(entities.length / numWorkers);\r\n const chunks: Entity[][] = [];\r\n for (let i = 0; i < entities.length; i += chunkSize) {\r\n chunks.push(entities.slice(i, i + chunkSize));\r\n }\r\n\r\n // Prepare worker inputs with lowercased data\r\n const workerInputs = chunks.map(chunk => ({\r\n query,\r\n threshold,\r\n entities: chunk.map(e => ({\r\n name: e.name,\r\n nameLower: e.name.toLowerCase(),\r\n observations: e.observations.map(o => o.toLowerCase()),\r\n })),\r\n }));\r\n\r\n // Execute all chunks in parallel using workerpool with timeout\r\n const WORKER_TIMEOUT_MS = 30000; // 30 seconds\r\n const results = await Promise.all(\r\n workerInputs.map(input =>\r\n this.workerPool!.exec('searchEntities', [input])\r\n .timeout(WORKER_TIMEOUT_MS) as Promise<MatchResult[]>\r\n )\r\n );\r\n\r\n // Flatten results and extract matched entity names\r\n const matchedNames = new Set(results.flat().map(r => r.name));\r\n\r\n // Return entities that matched\r\n return entities.filter(e => matchedNames.has(e.name));\r\n } catch (error) {\r\n // Worker execution failed - fall back to single-threaded mode\r\n console.warn(\r\n `Worker pool execution failed, falling back to single-threaded fuzzy search: ${\r\n error instanceof Error ? error.message : String(error)\r\n }`\r\n );\r\n\r\n // Use the existing single-threaded implementation\r\n const queryLower = query.toLowerCase();\r\n return this.performFuzzyMatch(entities, queryLower, threshold);\r\n }\r\n }\r\n\r\n /**\r\n * Phase 8: Shutdown the worker pool and clean up resources.\r\n *\r\n * Should be called when FuzzySearch is no longer needed.\r\n */\r\n async shutdown(): Promise<void> {\r\n if (this.workerPool) {\r\n await this.workerPool.terminate();\r\n this.workerPool = null;\r\n }\r\n }\r\n}\r\n","/**\r\n * Search Suggestions\r\n *\r\n * Provides \"did you mean?\" suggestions using Levenshtein distance.\r\n *\r\n * @module search/SearchSuggestions\r\n */\r\n\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { levenshteinDistance } from '../utils/index.js';\r\n\r\n/**\r\n * Internal suggestion with similarity score.\r\n */\r\ninterface Suggestion {\r\n text: string;\r\n similarity: number;\r\n}\r\n\r\n/**\r\n * Generates search suggestions based on entity names and types.\r\n */\r\nexport class SearchSuggestions {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Get \"did you mean?\" suggestions for a query.\r\n *\r\n * Returns similar entity names and types that might be what the user intended.\r\n * Excludes exact matches (similarity < 1.0) and very dissimilar strings (similarity > 0.5).\r\n *\r\n * @param query - The search query\r\n * @param maxSuggestions - Maximum number of suggestions to return (default 5)\r\n * @returns Array of suggested entity/type names sorted by similarity\r\n */\r\n async getSearchSuggestions(query: string, maxSuggestions: number = 5): Promise<string[]> {\r\n const graph = await this.storage.loadGraph();\r\n const queryLower = query.toLowerCase();\r\n\r\n const suggestions: Suggestion[] = [];\r\n\r\n // Check entity names\r\n for (const entity of graph.entities) {\r\n const distance = levenshteinDistance(queryLower, entity.name.toLowerCase());\r\n const maxLength = Math.max(queryLower.length, entity.name.length);\r\n const similarity = 1 - distance / maxLength;\r\n\r\n if (similarity > 0.5 && similarity < 1.0) {\r\n // Not exact match but similar\r\n suggestions.push({ text: entity.name, similarity });\r\n }\r\n }\r\n\r\n // Check entity types\r\n const uniqueTypes = [...new Set(graph.entities.map(e => e.entityType))];\r\n for (const type of uniqueTypes) {\r\n const distance = levenshteinDistance(queryLower, type.toLowerCase());\r\n const maxLength = Math.max(queryLower.length, type.length);\r\n const similarity = 1 - distance / maxLength;\r\n\r\n if (similarity > 0.5 && similarity < 1.0) {\r\n suggestions.push({ text: type, similarity });\r\n }\r\n }\r\n\r\n // Sort by similarity and return top suggestions\r\n return suggestions\r\n .sort((a, b) => b.similarity - a.similarity)\r\n .slice(0, maxSuggestions)\r\n .map(s => s.text);\r\n }\r\n}\r\n","/**\r\n * Saved Search Manager\r\n *\r\n * Manages persistent saved searches with JSONL storage.\r\n *\r\n * @module search/SavedSearchManager\r\n */\r\n\r\nimport * as fs from 'fs/promises';\r\nimport type { SavedSearch, KnowledgeGraph } from '../types/index.js';\r\nimport type { BasicSearch } from './BasicSearch.js';\r\nimport { sanitizeObject } from '../utils/index.js';\r\n\r\n/**\r\n * Manages saved search queries with usage tracking.\r\n */\r\nexport class SavedSearchManager {\r\n constructor(\r\n private savedSearchesFilePath: string,\r\n private basicSearch: BasicSearch\r\n ) {}\r\n\r\n /**\r\n * Load all saved searches from JSONL file.\r\n *\r\n * @returns Array of saved searches\r\n */\r\n private async loadSavedSearches(): Promise<SavedSearch[]> {\r\n try {\r\n const data = await fs.readFile(this.savedSearchesFilePath, 'utf-8');\r\n const lines = data.split('\\n').filter((line: string) => line.trim() !== '');\r\n return lines.map((line: string) => JSON.parse(line) as SavedSearch);\r\n } catch (error) {\r\n if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {\r\n return [];\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Save searches to JSONL file.\r\n *\r\n * @param searches - Array of saved searches\r\n */\r\n private async saveSavedSearches(searches: SavedSearch[]): Promise<void> {\r\n const lines = searches.map(s => JSON.stringify(s));\r\n await fs.writeFile(this.savedSearchesFilePath, lines.join('\\n'));\r\n }\r\n\r\n /**\r\n * Save a search query for later reuse.\r\n *\r\n * @param search - Search parameters (without createdAt, useCount, lastUsed)\r\n * @returns The newly created saved search\r\n * @throws Error if search name already exists\r\n */\r\n async saveSearch(\r\n search: Omit<SavedSearch, 'createdAt' | 'useCount' | 'lastUsed'>\r\n ): Promise<SavedSearch> {\r\n const searches = await this.loadSavedSearches();\r\n\r\n // Check if name already exists\r\n if (searches.some(s => s.name === search.name)) {\r\n throw new Error(`Saved search with name \"${search.name}\" already exists`);\r\n }\r\n\r\n const newSearch: SavedSearch = {\r\n ...search,\r\n createdAt: new Date().toISOString(),\r\n useCount: 0,\r\n };\r\n\r\n searches.push(newSearch);\r\n await this.saveSavedSearches(searches);\r\n\r\n return newSearch;\r\n }\r\n\r\n /**\r\n * List all saved searches.\r\n *\r\n * @returns Array of all saved searches\r\n */\r\n async listSavedSearches(): Promise<SavedSearch[]> {\r\n return await this.loadSavedSearches();\r\n }\r\n\r\n /**\r\n * Get a specific saved search by name.\r\n *\r\n * @param name - Search name\r\n * @returns Saved search or null if not found\r\n */\r\n async getSavedSearch(name: string): Promise<SavedSearch | null> {\r\n const searches = await this.loadSavedSearches();\r\n return searches.find(s => s.name === name) || null;\r\n }\r\n\r\n /**\r\n * Execute a saved search by name.\r\n *\r\n * Updates usage statistics (lastUsed, useCount) before executing.\r\n *\r\n * @param name - Search name\r\n * @returns Search results as knowledge graph\r\n * @throws Error if search not found\r\n */\r\n async executeSavedSearch(name: string): Promise<KnowledgeGraph> {\r\n const searches = await this.loadSavedSearches();\r\n const search = searches.find(s => s.name === name);\r\n\r\n if (!search) {\r\n throw new Error(`Saved search \"${name}\" not found`);\r\n }\r\n\r\n // Update usage statistics\r\n search.lastUsed = new Date().toISOString();\r\n search.useCount++;\r\n await this.saveSavedSearches(searches);\r\n\r\n // Execute the search using BasicSearch\r\n return await this.basicSearch.searchNodes(\r\n search.query,\r\n search.tags,\r\n search.minImportance,\r\n search.maxImportance\r\n );\r\n }\r\n\r\n /**\r\n * Delete a saved search.\r\n *\r\n * @param name - Search name\r\n * @returns True if deleted, false if not found\r\n */\r\n async deleteSavedSearch(name: string): Promise<boolean> {\r\n const searches = await this.loadSavedSearches();\r\n const initialLength = searches.length;\r\n const filtered = searches.filter(s => s.name !== name);\r\n\r\n if (filtered.length === initialLength) {\r\n return false; // Search not found\r\n }\r\n\r\n await this.saveSavedSearches(filtered);\r\n return true;\r\n }\r\n\r\n /**\r\n * Update a saved search.\r\n *\r\n * Cannot update name, createdAt, useCount, or lastUsed fields.\r\n *\r\n * @param name - Search name\r\n * @param updates - Partial search with fields to update\r\n * @returns Updated saved search\r\n * @throws Error if search not found\r\n */\r\n async updateSavedSearch(\r\n name: string,\r\n updates: Partial<Omit<SavedSearch, 'name' | 'createdAt' | 'useCount' | 'lastUsed'>>\r\n ): Promise<SavedSearch> {\r\n const searches = await this.loadSavedSearches();\r\n const search = searches.find(s => s.name === name);\r\n\r\n if (!search) {\r\n throw new Error(`Saved search \"${name}\" not found`);\r\n }\r\n\r\n // Apply updates (sanitized to prevent prototype pollution)\r\n Object.assign(search, sanitizeObject(updates as Record<string, unknown>));\r\n\r\n await this.saveSavedSearches(searches);\r\n return search;\r\n }\r\n}\r\n","/**\r\n * Query Cost Estimator\r\n *\r\n * Phase 10 Sprint 4: Estimates the cost of different search methods\r\n * and recommends the optimal method based on query characteristics\r\n * and graph size.\r\n *\r\n * Phase 12 Sprint 4: Enhanced with adaptive depth calculation,\r\n * layer recommendations, and token estimation.\r\n *\r\n * @module search/QueryCostEstimator\r\n */\r\n\r\nimport type {\r\n SearchMethod,\r\n QueryCostEstimate,\r\n QueryCostEstimatorOptions,\r\n QueryAnalysis,\r\n} from '../types/index.js';\r\n\r\n/**\r\n * Phase 12 Sprint 4: Layer type for hybrid search.\r\n */\r\nexport type SearchLayer = 'semantic' | 'lexical' | 'symbolic';\r\n\r\n/**\r\n * Phase 12 Sprint 4: Extended cost estimate with layer recommendations.\r\n */\r\nexport interface ExtendedQueryCostEstimate extends QueryCostEstimate {\r\n /** Recommended layers in priority order */\r\n recommendedLayers: SearchLayer[];\r\n /** Estimated tokens for this query */\r\n estimatedTokens: number;\r\n /** Adaptive depth (k_dyn) for search results */\r\n adaptiveDepth: number;\r\n /** Layer-specific cost estimates */\r\n layerCosts: Record<SearchLayer, number>;\r\n}\r\n\r\n/**\r\n * Phase 12 Sprint 4: Layer recommendation options.\r\n */\r\nexport interface LayerRecommendationOptions {\r\n /** Query analysis from QueryAnalyzer */\r\n analysis?: QueryAnalysis;\r\n /** Whether semantic search is available */\r\n semanticAvailable?: boolean;\r\n /** Maximum layers to recommend (default: 3) */\r\n maxLayers?: number;\r\n}\r\n\r\n/**\r\n * Phase 12 Sprint 4: Token estimation options.\r\n */\r\nexport interface TokenEstimationOptions {\r\n /** Average characters per token (default: 4) */\r\n charsPerToken?: number;\r\n /** Include entity count in estimate (default: true) */\r\n includeEntityCount?: boolean;\r\n}\r\n\r\n/**\r\n * Phase 12 Sprint 4: Adaptive depth calculation parameters.\r\n */\r\nexport interface AdaptiveDepthConfig {\r\n /** Base number of results (k_base, default: 10) */\r\n kBase?: number;\r\n /** Complexity scaling factor (δ, delta, default: 0.5) */\r\n delta?: number;\r\n /** Maximum depth cap (default: 100) */\r\n maxDepth?: number;\r\n}\r\n\r\n/**\r\n * Default options for the QueryCostEstimator.\r\n */\r\nconst DEFAULT_OPTIONS: Required<QueryCostEstimatorOptions> = {\r\n basicTimePerEntity: 0.01,\r\n rankedTimePerEntity: 0.05,\r\n booleanTimePerEntity: 0.02,\r\n fuzzyTimePerEntity: 0.1,\r\n semanticTimePerEntity: 0.5,\r\n lowComplexityThreshold: 100,\r\n highComplexityThreshold: 1000,\r\n};\r\n\r\n/**\r\n * Default adaptive depth configuration.\r\n */\r\nconst DEFAULT_ADAPTIVE_DEPTH: Required<AdaptiveDepthConfig> = {\r\n kBase: 10,\r\n delta: 0.5,\r\n maxDepth: 100,\r\n};\r\n\r\n/**\r\n * Default token estimation options.\r\n */\r\nconst DEFAULT_TOKEN_ESTIMATION: Required<TokenEstimationOptions> = {\r\n charsPerToken: 4,\r\n includeEntityCount: true,\r\n};\r\n\r\n/**\r\n * Phase 10 Sprint 4: Estimates search query costs and recommends optimal methods.\r\n *\r\n * Analyzes query characteristics and graph size to estimate execution time\r\n * and recommend the most appropriate search method.\r\n *\r\n * @example\r\n * ```typescript\r\n * const estimator = new QueryCostEstimator();\r\n *\r\n * // Get estimate for a specific method\r\n * const estimate = estimator.estimateMethod('ranked', 'test query', 1000);\r\n *\r\n * // Get the recommended method for a query\r\n * const recommendation = estimator.recommendMethod('test query', 1000);\r\n *\r\n * // Get estimates for all methods\r\n * const allEstimates = estimator.estimateAllMethods('test query', 1000);\r\n * ```\r\n */\r\nexport class QueryCostEstimator {\r\n private options: Required<QueryCostEstimatorOptions>;\r\n private adaptiveDepthConfig: Required<AdaptiveDepthConfig>;\r\n private tokenEstimationConfig: Required<TokenEstimationOptions>;\r\n\r\n /**\r\n * Create a new QueryCostEstimator instance.\r\n *\r\n * @param options - Optional configuration overrides\r\n * @param adaptiveDepthConfig - Optional adaptive depth configuration\r\n * @param tokenEstimationConfig - Optional token estimation configuration\r\n */\r\n constructor(\r\n options?: QueryCostEstimatorOptions,\r\n adaptiveDepthConfig?: AdaptiveDepthConfig,\r\n tokenEstimationConfig?: TokenEstimationOptions\r\n ) {\r\n this.options = { ...DEFAULT_OPTIONS, ...options };\r\n this.adaptiveDepthConfig = { ...DEFAULT_ADAPTIVE_DEPTH, ...adaptiveDepthConfig };\r\n this.tokenEstimationConfig = { ...DEFAULT_TOKEN_ESTIMATION, ...tokenEstimationConfig };\r\n }\r\n\r\n /**\r\n * Estimate the cost of a specific search method.\r\n *\r\n * @param method - The search method to estimate\r\n * @param query - The search query\r\n * @param entityCount - Number of entities in the graph\r\n * @returns Cost estimate for the method\r\n */\r\n estimateMethod(\r\n method: SearchMethod,\r\n query: string,\r\n entityCount: number\r\n ): QueryCostEstimate {\r\n // Get the recommended method first to determine isRecommended\r\n const recommendedMethod = this.getRecommendedMethodOnly(query, entityCount);\r\n return this.estimateMethodInternal(method, query, entityCount, method === recommendedMethod);\r\n }\r\n\r\n /**\r\n * Internal method to estimate without triggering recursion.\r\n * @private\r\n */\r\n private estimateMethodInternal(\r\n method: SearchMethod,\r\n query: string,\r\n entityCount: number,\r\n isRecommended: boolean\r\n ): QueryCostEstimate {\r\n const baseTime = this.getBaseTimeForMethod(method);\r\n const queryComplexityFactor = this.getQueryComplexityFactor(query, method);\r\n const estimatedTimeMs = baseTime * entityCount * queryComplexityFactor;\r\n const complexity = this.getComplexity(entityCount);\r\n const recommendation = this.getRecommendation(method, query, entityCount, complexity);\r\n\r\n return {\r\n method,\r\n estimatedTimeMs: Math.round(estimatedTimeMs * 100) / 100,\r\n complexity,\r\n entityCount,\r\n recommendation,\r\n isRecommended,\r\n };\r\n }\r\n\r\n /**\r\n * Get just the recommended method without full estimate (avoids recursion).\r\n * @private\r\n */\r\n private getRecommendedMethodOnly(\r\n query: string,\r\n entityCount: number,\r\n preferredMethods?: SearchMethod[]\r\n ): SearchMethod {\r\n const methods = preferredMethods ?? (['basic', 'ranked', 'boolean', 'fuzzy', 'semantic'] as SearchMethod[]);\r\n\r\n // Score each method and find the best\r\n let bestMethod = methods[0];\r\n let bestScore = this.scoreMethod(methods[0], query, entityCount);\r\n\r\n for (let i = 1; i < methods.length; i++) {\r\n const score = this.scoreMethod(methods[i], query, entityCount);\r\n if (score > bestScore) {\r\n bestScore = score;\r\n bestMethod = methods[i];\r\n }\r\n }\r\n\r\n return bestMethod;\r\n }\r\n\r\n /**\r\n * Get estimates for all available search methods.\r\n *\r\n * @param query - The search query\r\n * @param entityCount - Number of entities in the graph\r\n * @returns Array of estimates for all methods\r\n */\r\n estimateAllMethods(query: string, entityCount: number): QueryCostEstimate[] {\r\n const methods: SearchMethod[] = ['basic', 'ranked', 'boolean', 'fuzzy', 'semantic'];\r\n const recommendedMethod = this.getRecommendedMethodOnly(query, entityCount);\r\n return methods.map(method =>\r\n this.estimateMethodInternal(method, query, entityCount, method === recommendedMethod)\r\n );\r\n }\r\n\r\n /**\r\n * Recommend the best search method for a query.\r\n *\r\n * @param query - The search query\r\n * @param entityCount - Number of entities in the graph\r\n * @param preferredMethods - Optional array of methods to consider (default: all)\r\n * @returns The recommended method and reason\r\n */\r\n recommendMethod(\r\n query: string,\r\n entityCount: number,\r\n preferredMethods?: SearchMethod[]\r\n ): { method: SearchMethod; reason: string; estimate: QueryCostEstimate } {\r\n const methods = preferredMethods ?? (['basic', 'ranked', 'boolean', 'fuzzy', 'semantic'] as SearchMethod[]);\r\n\r\n // Score each method based on query characteristics and cost\r\n const scores = methods.map(method => ({\r\n method,\r\n score: this.scoreMethod(method, query, entityCount),\r\n estimate: this.estimateMethod(method, query, entityCount),\r\n }));\r\n\r\n // Sort by score (higher is better)\r\n scores.sort((a, b) => b.score - a.score);\r\n\r\n const best = scores[0];\r\n const reason = this.getSelectionReason(best.method, query, entityCount);\r\n\r\n return {\r\n method: best.method,\r\n reason,\r\n estimate: best.estimate,\r\n };\r\n }\r\n\r\n /**\r\n * Get the base time per entity for a search method.\r\n * @private\r\n */\r\n private getBaseTimeForMethod(method: SearchMethod): number {\r\n switch (method) {\r\n case 'basic':\r\n return this.options.basicTimePerEntity;\r\n case 'ranked':\r\n return this.options.rankedTimePerEntity;\r\n case 'boolean':\r\n return this.options.booleanTimePerEntity;\r\n case 'fuzzy':\r\n return this.options.fuzzyTimePerEntity;\r\n case 'semantic':\r\n return this.options.semanticTimePerEntity;\r\n }\r\n }\r\n\r\n /**\r\n * Calculate a complexity factor based on query characteristics.\r\n * @private\r\n */\r\n private getQueryComplexityFactor(query: string, method: SearchMethod): number {\r\n const words = query.trim().split(/\\s+/).length;\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n const hasWildcard = query.includes('*');\r\n const hasQuotes = query.includes('\"');\r\n\r\n let factor = 1.0;\r\n\r\n // More words = slightly more complex\r\n factor += (words - 1) * 0.1;\r\n\r\n // Boolean operators increase boolean search cost, decrease others\r\n if (hasOperators) {\r\n if (method === 'boolean') {\r\n factor *= 0.8; // Boolean search is optimized for operators\r\n } else {\r\n factor *= 1.5; // Other methods struggle with operators\r\n }\r\n }\r\n\r\n // Wildcards increase fuzzy search efficiency\r\n if (hasWildcard) {\r\n if (method === 'fuzzy') {\r\n factor *= 0.9;\r\n } else {\r\n factor *= 1.3;\r\n }\r\n }\r\n\r\n // Quoted phrases benefit ranked search\r\n if (hasQuotes) {\r\n if (method === 'ranked') {\r\n factor *= 0.9;\r\n } else {\r\n factor *= 1.1;\r\n }\r\n }\r\n\r\n return Math.max(0.5, Math.min(factor, 3.0)); // Clamp between 0.5 and 3.0\r\n }\r\n\r\n /**\r\n * Get the complexity level based on entity count.\r\n * @private\r\n */\r\n private getComplexity(entityCount: number): 'low' | 'medium' | 'high' {\r\n if (entityCount <= this.options.lowComplexityThreshold) {\r\n return 'low';\r\n }\r\n if (entityCount >= this.options.highComplexityThreshold) {\r\n return 'high';\r\n }\r\n return 'medium';\r\n }\r\n\r\n /**\r\n * Generate a human-readable recommendation.\r\n * @private\r\n */\r\n private getRecommendation(\r\n method: SearchMethod,\r\n _query: string,\r\n _entityCount: number,\r\n complexity: 'low' | 'medium' | 'high'\r\n ): string {\r\n const recommendations: Record<SearchMethod, string> = {\r\n basic: 'Fast substring matching, best for simple queries',\r\n ranked: 'TF-IDF relevance ranking, best for finding most relevant results',\r\n boolean: 'Boolean operators (AND/OR/NOT), best for precise filtering',\r\n fuzzy: 'Tolerant of typos and misspellings, best for uncertain queries',\r\n semantic: 'Meaning-based search using embeddings, best for conceptual queries',\r\n };\r\n\r\n let recommendation = recommendations[method];\r\n\r\n if (complexity === 'high' && method === 'semantic') {\r\n recommendation += ' - may be slow for large graphs';\r\n }\r\n\r\n if (complexity === 'low') {\r\n recommendation += ' - fast for small graphs';\r\n }\r\n\r\n return recommendation;\r\n }\r\n\r\n /**\r\n * Score a method based on query characteristics and graph size.\r\n * Higher score = better fit.\r\n * @private\r\n */\r\n private scoreMethod(method: SearchMethod, query: string, entityCount: number): number {\r\n let score = 50; // Base score\r\n\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n const hasWildcard = query.includes('*');\r\n const hasQuotes = query.includes('\"');\r\n const words = query.trim().split(/\\s+/).length;\r\n const isShortQuery = words <= 2;\r\n const isLongQuery = words >= 5;\r\n const complexity = this.getComplexity(entityCount);\r\n\r\n switch (method) {\r\n case 'basic':\r\n // Basic is good for simple, short queries on any size graph\r\n if (isShortQuery && !hasOperators && !hasWildcard) {\r\n score += 30;\r\n }\r\n if (complexity === 'low') {\r\n score += 20;\r\n }\r\n // Basic is fastest, give bonus for speed\r\n score += 10;\r\n break;\r\n\r\n case 'ranked':\r\n // Ranked is good for relevance-focused queries\r\n if (words >= 2) {\r\n score += 25; // Better for multi-word queries\r\n }\r\n if (hasQuotes) {\r\n score += 15; // Good for phrase matching\r\n }\r\n if (!hasOperators) {\r\n score += 10; // Not optimized for boolean\r\n }\r\n // Good balance of speed and quality\r\n score += 15;\r\n break;\r\n\r\n case 'boolean':\r\n // Boolean is best for queries with operators\r\n if (hasOperators) {\r\n score += 40;\r\n }\r\n if (!hasOperators) {\r\n score -= 20; // Not useful without operators\r\n }\r\n // Fast for filtering\r\n score += 10;\r\n break;\r\n\r\n case 'fuzzy':\r\n // Fuzzy is good for typo-tolerant search\r\n if (hasWildcard) {\r\n score += 25;\r\n }\r\n if (isShortQuery) {\r\n score += 15; // Works best on shorter queries\r\n }\r\n if (isLongQuery) {\r\n score -= 15; // Slow on long queries\r\n }\r\n if (complexity === 'high') {\r\n score -= 20; // Slow on large graphs\r\n }\r\n break;\r\n\r\n case 'semantic':\r\n // Semantic is good for conceptual/meaning-based queries\r\n if (isLongQuery) {\r\n score += 30; // Better for longer, more descriptive queries\r\n }\r\n if (!hasOperators && !hasWildcard) {\r\n score += 15; // Natural language queries\r\n }\r\n // Penalize for large graphs (slow)\r\n if (complexity === 'high') {\r\n score -= 30;\r\n }\r\n if (complexity === 'medium') {\r\n score -= 10;\r\n }\r\n break;\r\n }\r\n\r\n return score;\r\n }\r\n\r\n /**\r\n * Get a human-readable reason for why a method was selected.\r\n * @private\r\n */\r\n private getSelectionReason(method: SearchMethod, query: string, entityCount: number): string {\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n const hasWildcard = query.includes('*');\r\n const words = query.trim().split(/\\s+/).length;\r\n const complexity = this.getComplexity(entityCount);\r\n\r\n switch (method) {\r\n case 'basic':\r\n if (complexity === 'low') {\r\n return 'Basic search selected for small graph size - fast and efficient';\r\n }\r\n return 'Basic search selected for simple query pattern';\r\n\r\n case 'ranked':\r\n if (words >= 2) {\r\n return 'Ranked search selected for multi-word query - provides relevance ordering';\r\n }\r\n return 'Ranked search selected for best balance of speed and relevance';\r\n\r\n case 'boolean':\r\n if (hasOperators) {\r\n return 'Boolean search selected - query contains logical operators (AND/OR/NOT)';\r\n }\r\n return 'Boolean search selected for precise filtering';\r\n\r\n case 'fuzzy':\r\n if (hasWildcard) {\r\n return 'Fuzzy search selected - query contains wildcard patterns';\r\n }\r\n return 'Fuzzy search selected for typo-tolerant matching';\r\n\r\n case 'semantic':\r\n if (words >= 5) {\r\n return 'Semantic search selected - longer natural language query benefits from meaning-based matching';\r\n }\r\n return 'Semantic search selected for conceptual/meaning-based matching';\r\n }\r\n }\r\n\r\n // ==================== Phase 12 Sprint 4: Enhanced Features ====================\r\n\r\n /**\r\n * Calculate adaptive depth (k_dyn) based on query complexity.\r\n *\r\n * Formula: k_dyn = k_base × (1 + δ × C_q)\r\n * where:\r\n * - k_base: base number of results\r\n * - δ (delta): complexity scaling factor\r\n * - C_q: query complexity score (0-1)\r\n *\r\n * @param query - The search query\r\n * @param analysis - Optional query analysis for more accurate complexity\r\n * @returns Adaptive depth value\r\n */\r\n calculateAdaptiveDepth(query: string, analysis?: QueryAnalysis): number {\r\n const { kBase, delta, maxDepth } = this.adaptiveDepthConfig;\r\n\r\n // Calculate complexity score (0-1)\r\n const complexityScore = this.calculateComplexityScore(query, analysis);\r\n\r\n // Apply formula: k_dyn = k_base × (1 + δ × C_q)\r\n const kDyn = kBase * (1 + delta * complexityScore);\r\n\r\n // Round and cap at maxDepth\r\n return Math.min(Math.round(kDyn), maxDepth);\r\n }\r\n\r\n /**\r\n * Calculate query complexity score (0-1).\r\n * @private\r\n */\r\n private calculateComplexityScore(query: string, analysis?: QueryAnalysis): number {\r\n let score = 0;\r\n\r\n // Use analysis if provided\r\n if (analysis) {\r\n switch (analysis.complexity) {\r\n case 'low':\r\n score = 0.2;\r\n break;\r\n case 'medium':\r\n score = 0.5;\r\n break;\r\n case 'high':\r\n score = 0.8;\r\n break;\r\n }\r\n\r\n // Adjust for sub-queries\r\n if (analysis.subQueries && analysis.subQueries.length > 1) {\r\n score += 0.1 * (analysis.subQueries.length - 1);\r\n }\r\n\r\n // Adjust for temporal range (adds complexity)\r\n if (analysis.temporalRange) {\r\n score += 0.1;\r\n }\r\n\r\n // Adjust for entities (more entities = more complex)\r\n if (analysis.entities.length > 0) {\r\n score += 0.05 * Math.min(analysis.entities.length, 3);\r\n }\r\n } else {\r\n // Fallback to query-based estimation\r\n const words = query.trim().split(/\\s+/).length;\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n const hasMultipleClauses = /[,;]/.test(query);\r\n const hasConjunctions = /\\b(and|or|but|then|therefore)\\b/i.test(query);\r\n\r\n score = Math.min(words / 20, 0.5); // Base score from length\r\n if (hasOperators) score += 0.2;\r\n if (hasMultipleClauses) score += 0.15;\r\n if (hasConjunctions) score += 0.1;\r\n }\r\n\r\n // Clamp to 0-1\r\n return Math.max(0, Math.min(1, score));\r\n }\r\n\r\n /**\r\n * Estimate tokens for query and expected results.\r\n *\r\n * @param query - The search query\r\n * @param entityCount - Number of entities in the graph\r\n * @param expectedResults - Expected number of results (default: 10)\r\n * @returns Estimated token count\r\n */\r\n estimateTokens(query: string, entityCount: number, expectedResults = 10): number {\r\n const { charsPerToken, includeEntityCount } = this.tokenEstimationConfig;\r\n\r\n // Query tokens\r\n const queryTokens = Math.ceil(query.length / charsPerToken);\r\n\r\n // Result tokens (approximate average entity text size)\r\n const avgEntityTextSize = 200; // Conservative estimate\r\n const resultTokens = Math.ceil((avgEntityTextSize * expectedResults) / charsPerToken);\r\n\r\n // Add overhead for entity count if enabled\r\n let totalTokens = queryTokens + resultTokens;\r\n if (includeEntityCount) {\r\n // More entities = slightly more processing/context\r\n const entityOverhead = Math.ceil(Math.log10(entityCount + 1) * 10);\r\n totalTokens += entityOverhead;\r\n }\r\n\r\n return totalTokens;\r\n }\r\n\r\n /**\r\n * Recommend search layers based on query characteristics.\r\n *\r\n * @param query - The search query\r\n * @param options - Layer recommendation options\r\n * @returns Ordered array of recommended layers (fastest first)\r\n */\r\n recommendLayers(query: string, options: LayerRecommendationOptions = {}): SearchLayer[] {\r\n const {\r\n analysis,\r\n semanticAvailable = true,\r\n maxLayers = 3,\r\n } = options;\r\n\r\n const layers: Array<{ layer: SearchLayer; score: number; cost: number }> = [];\r\n\r\n // Score lexical layer\r\n const lexicalScore = this.scoreLexicalLayer(query, analysis);\r\n layers.push({\r\n layer: 'lexical',\r\n score: lexicalScore,\r\n cost: this.estimateLayerCost('lexical', query),\r\n });\r\n\r\n // Score symbolic layer\r\n const symbolicScore = this.scoreSymbolicLayer(query, analysis);\r\n layers.push({\r\n layer: 'symbolic',\r\n score: symbolicScore,\r\n cost: this.estimateLayerCost('symbolic', query),\r\n });\r\n\r\n // Score semantic layer (if available)\r\n if (semanticAvailable) {\r\n const semanticScore = this.scoreSemanticLayer(query, analysis);\r\n layers.push({\r\n layer: 'semantic',\r\n score: semanticScore,\r\n cost: this.estimateLayerCost('semantic', query),\r\n });\r\n }\r\n\r\n // Sort by score (higher is better), then by cost (lower is better)\r\n layers.sort((a, b) => {\r\n if (b.score !== a.score) return b.score - a.score;\r\n return a.cost - b.cost;\r\n });\r\n\r\n // Return up to maxLayers\r\n return layers.slice(0, maxLayers).map(l => l.layer);\r\n }\r\n\r\n /**\r\n * Estimate cost for a specific layer.\r\n * @private\r\n */\r\n private estimateLayerCost(layer: SearchLayer, query: string): number {\r\n const words = query.trim().split(/\\s+/).length;\r\n\r\n switch (layer) {\r\n case 'lexical':\r\n return 1 + words * 0.1; // Fast, scales with words\r\n case 'symbolic':\r\n return 0.5; // Very fast, constant time\r\n case 'semantic':\r\n return 5 + words * 0.5; // Slow, embedding computation\r\n }\r\n }\r\n\r\n /**\r\n * Score lexical layer for query.\r\n * @private\r\n */\r\n private scoreLexicalLayer(query: string, analysis?: QueryAnalysis): number {\r\n let score = 50;\r\n\r\n const words = query.trim().split(/\\s+/).length;\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n\r\n // Good for keyword queries\r\n if (words >= 2 && words <= 5) score += 20;\r\n if (!hasOperators) score += 10;\r\n\r\n // Check analysis for factual questions\r\n if (analysis?.questionType === 'factual') score += 15;\r\n if (analysis?.questionType === 'aggregation') score += 10;\r\n\r\n return score;\r\n }\r\n\r\n /**\r\n * Score symbolic layer for query.\r\n * @private\r\n */\r\n private scoreSymbolicLayer(query: string, analysis?: QueryAnalysis): number {\r\n let score = 40;\r\n\r\n // Good for temporal queries\r\n if (analysis?.temporalRange) score += 30;\r\n if (analysis?.questionType === 'temporal') score += 25;\r\n\r\n // Good for structured filtering\r\n const hasFilterKeywords = /\\b(type|tag|importance|date|created|modified)\\b/i.test(query);\r\n if (hasFilterKeywords) score += 20;\r\n\r\n // If analysis detected entities, might benefit from symbolic\r\n if (analysis?.entities && analysis.entities.length > 0) score += 10;\r\n\r\n return score;\r\n }\r\n\r\n /**\r\n * Score semantic layer for query.\r\n * @private\r\n */\r\n private scoreSemanticLayer(query: string, analysis?: QueryAnalysis): number {\r\n let score = 45;\r\n\r\n const words = query.trim().split(/\\s+/).length;\r\n const hasOperators = /\\b(AND|OR|NOT)\\b/.test(query);\r\n\r\n // Good for longer, natural language queries\r\n if (words >= 5) score += 25;\r\n if (!hasOperators) score += 10;\r\n\r\n // Good for conceptual questions\r\n if (analysis?.questionType === 'conceptual') score += 30;\r\n if (analysis?.questionType === 'comparative') score += 20;\r\n\r\n // High complexity benefits from semantic understanding\r\n if (analysis?.complexity === 'high') score += 15;\r\n\r\n return score;\r\n }\r\n\r\n /**\r\n * Get extended cost estimate with all Phase 12 features.\r\n *\r\n * @param method - The search method\r\n * @param query - The search query\r\n * @param entityCount - Number of entities in the graph\r\n * @param analysis - Optional query analysis\r\n * @returns Extended cost estimate\r\n */\r\n estimateExtended(\r\n method: SearchMethod,\r\n query: string,\r\n entityCount: number,\r\n analysis?: QueryAnalysis\r\n ): ExtendedQueryCostEstimate {\r\n const base = this.estimateMethod(method, query, entityCount);\r\n const adaptiveDepth = this.calculateAdaptiveDepth(query, analysis);\r\n const estimatedTokens = this.estimateTokens(query, entityCount, adaptiveDepth);\r\n const recommendedLayers = this.recommendLayers(query, {\r\n analysis,\r\n semanticAvailable: method === 'semantic' || true, // Assume available\r\n });\r\n\r\n const layerCosts: Record<SearchLayer, number> = {\r\n semantic: this.estimateLayerCost('semantic', query) * entityCount * 0.001,\r\n lexical: this.estimateLayerCost('lexical', query) * entityCount * 0.001,\r\n symbolic: this.estimateLayerCost('symbolic', query) * entityCount * 0.001,\r\n };\r\n\r\n return {\r\n ...base,\r\n recommendedLayers,\r\n estimatedTokens,\r\n adaptiveDepth,\r\n layerCosts,\r\n };\r\n }\r\n\r\n /**\r\n * Get layers sorted by estimated cost (fastest first).\r\n *\r\n * @param query - The search query\r\n * @param entityCount - Number of entities\r\n * @param semanticAvailable - Whether semantic search is available\r\n * @returns Layers sorted by cost\r\n */\r\n getLayersByCost(\r\n query: string,\r\n entityCount: number,\r\n semanticAvailable = true\r\n ): Array<{ layer: SearchLayer; estimatedMs: number }> {\r\n const layers: Array<{ layer: SearchLayer; estimatedMs: number }> = [\r\n {\r\n layer: 'symbolic',\r\n estimatedMs: this.estimateLayerCost('symbolic', query) * Math.log10(entityCount + 1),\r\n },\r\n {\r\n layer: 'lexical',\r\n estimatedMs: this.estimateLayerCost('lexical', query) * Math.sqrt(entityCount),\r\n },\r\n ];\r\n\r\n if (semanticAvailable) {\r\n layers.push({\r\n layer: 'semantic',\r\n estimatedMs: this.estimateLayerCost('semantic', query) * entityCount * 0.1,\r\n });\r\n }\r\n\r\n return layers.sort((a, b) => a.estimatedMs - b.estimatedMs);\r\n }\r\n}\r\n","/**\r\n * Search Manager\r\n *\r\n * Orchestrates all search types (basic, ranked, boolean, fuzzy).\r\n * Focused on search operations only (Phase 4: Consolidate God Objects).\r\n *\r\n * @module search/SearchManager\r\n */\r\n\r\nimport type { KnowledgeGraph, SearchResult, SavedSearch, AutoSearchResult, Entity } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport { BasicSearch } from './BasicSearch.js';\r\nimport { RankedSearch } from './RankedSearch.js';\r\nimport { BooleanSearch } from './BooleanSearch.js';\r\nimport { FuzzySearch } from './FuzzySearch.js';\r\nimport { SearchSuggestions } from './SearchSuggestions.js';\r\nimport { SavedSearchManager } from './SavedSearchManager.js';\r\nimport { QueryCostEstimator } from './QueryCostEstimator.js';\r\n\r\n/**\r\n * Unified search manager providing access to all search types.\r\n *\r\n * Phase 4 Sprint 5: Manages search caches across all search types.\r\n */\r\nexport class SearchManager {\r\n private basicSearch: BasicSearch;\r\n private rankedSearch: RankedSearch;\r\n private booleanSearcher: BooleanSearch;\r\n private fuzzySearcher: FuzzySearch;\r\n private searchSuggestions: SearchSuggestions;\r\n private savedSearchManager: SavedSearchManager;\r\n private storage: GraphStorage;\r\n private queryEstimator: QueryCostEstimator;\r\n\r\n constructor(storage: GraphStorage, savedSearchesFilePath: string) {\r\n this.storage = storage;\r\n this.basicSearch = new BasicSearch(storage);\r\n this.rankedSearch = new RankedSearch(storage);\r\n this.booleanSearcher = new BooleanSearch(storage);\r\n this.fuzzySearcher = new FuzzySearch(storage);\r\n this.searchSuggestions = new SearchSuggestions(storage);\r\n this.savedSearchManager = new SavedSearchManager(savedSearchesFilePath, this.basicSearch);\r\n this.queryEstimator = new QueryCostEstimator();\r\n }\r\n\r\n // ==================== Cache Management (Phase 4 Sprint 5) ====================\r\n\r\n /**\r\n * Phase 4 Sprint 5: Clear all search caches.\r\n *\r\n * Clears caches in all search types: fuzzy, boolean, and ranked token cache.\r\n * Call this when the graph has been modified to ensure fresh results.\r\n */\r\n clearAllCaches(): void {\r\n this.fuzzySearcher.clearCache();\r\n this.booleanSearcher.clearCache();\r\n this.rankedSearch.clearTokenCache();\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 5: Clear fuzzy search cache.\r\n */\r\n clearFuzzyCache(): void {\r\n this.fuzzySearcher.clearCache();\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 5: Clear boolean search cache.\r\n */\r\n clearBooleanCache(): void {\r\n this.booleanSearcher.clearCache();\r\n }\r\n\r\n /**\r\n * Phase 4 Sprint 5: Clear ranked search token cache.\r\n */\r\n clearRankedCache(): void {\r\n this.rankedSearch.clearTokenCache();\r\n }\r\n\r\n // ==================== Basic Search ====================\r\n\r\n /**\r\n * Perform a simple text-based search across entity names and observations.\r\n *\r\n * This is the primary search method that searches through entity names,\r\n * observations, and types using case-insensitive substring matching.\r\n * Optionally filter by tags and importance range.\r\n *\r\n * @param query - Text to search for (case-insensitive, searches names/observations/types)\r\n * @param tags - Optional array of tags to filter results (lowercase)\r\n * @param minImportance - Optional minimum importance value (0-10)\r\n * @param maxImportance - Optional maximum importance value (0-10)\r\n * @returns KnowledgeGraph containing matching entities and their relations\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Simple text search\r\n * const results = await manager.searchNodes('Alice');\r\n *\r\n * // Search with tag filter\r\n * const engineeringResults = await manager.searchNodes('project', ['engineering']);\r\n *\r\n * // Search with importance range\r\n * const importantResults = await manager.searchNodes('critical', undefined, 8, 10);\r\n *\r\n * // Combined filters\r\n * const filtered = await manager.searchNodes('bug', ['backend'], 5, 10);\r\n * ```\r\n */\r\n async searchNodes(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number\r\n ): Promise<KnowledgeGraph> {\r\n return this.basicSearch.searchNodes(query, tags, minImportance, maxImportance);\r\n }\r\n\r\n /**\r\n * Open specific nodes by name.\r\n *\r\n * @param names - Array of entity names\r\n * @returns Knowledge graph with specified entities\r\n */\r\n async openNodes(names: string[]): Promise<KnowledgeGraph> {\r\n return this.basicSearch.openNodes(names);\r\n }\r\n\r\n /**\r\n * Search by date range.\r\n *\r\n * @param startDate - Optional start date (ISO 8601)\r\n * @param endDate - Optional end date (ISO 8601)\r\n * @param entityType - Optional entity type filter\r\n * @param tags - Optional tags filter\r\n * @returns Filtered knowledge graph\r\n */\r\n async searchByDateRange(\r\n startDate?: string,\r\n endDate?: string,\r\n entityType?: string,\r\n tags?: string[]\r\n ): Promise<KnowledgeGraph> {\r\n return this.basicSearch.searchByDateRange(startDate, endDate, entityType, tags);\r\n }\r\n\r\n // ==================== Ranked Search ====================\r\n\r\n /**\r\n * Perform TF-IDF ranked search with relevance scoring.\r\n *\r\n * Uses Term Frequency-Inverse Document Frequency algorithm to rank results\r\n * by relevance to the query. Results are sorted by score (highest first).\r\n * This is ideal for finding the most relevant entities for a search query.\r\n *\r\n * @param query - Search query (analyzed for term frequency)\r\n * @param tags - Optional array of tags to filter results (lowercase)\r\n * @param minImportance - Optional minimum importance value (0-10)\r\n * @param maxImportance - Optional maximum importance value (0-10)\r\n * @param limit - Maximum number of results to return (default: 50, max: 200)\r\n * @returns Array of SearchResult objects sorted by relevance score (descending)\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Basic ranked search\r\n * const results = await manager.searchNodesRanked('machine learning algorithms');\r\n * results.forEach(r => {\r\n * console.log(`${r.entity.name} (score: ${r.score})`);\r\n * });\r\n *\r\n * // Limit to top 10 most relevant results\r\n * const top10 = await manager.searchNodesRanked('database optimization', undefined, undefined, undefined, 10);\r\n *\r\n * // Ranked search with filters\r\n * const relevantImportant = await manager.searchNodesRanked(\r\n * 'security vulnerability',\r\n * ['security', 'critical'],\r\n * 8,\r\n * 10,\r\n * 20\r\n * );\r\n * ```\r\n */\r\n async searchNodesRanked(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number,\r\n limit?: number\r\n ): Promise<SearchResult[]> {\r\n return this.rankedSearch.searchNodesRanked(query, tags, minImportance, maxImportance, limit);\r\n }\r\n\r\n // ==================== Boolean Search ====================\r\n\r\n /**\r\n * Perform boolean search with AND, OR, NOT operators.\r\n *\r\n * Supports complex boolean logic for precise search queries.\r\n * Use AND/OR/NOT operators (case-insensitive) to combine search terms.\r\n * Parentheses are supported for grouping.\r\n *\r\n * @param query - Boolean query string (e.g., \"alice AND bob\", \"frontend OR backend NOT legacy\")\r\n * @param tags - Optional array of tags to filter results (lowercase)\r\n * @param minImportance - Optional minimum importance value (0-10)\r\n * @param maxImportance - Optional maximum importance value (0-10)\r\n * @returns KnowledgeGraph containing entities matching the boolean expression\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // AND operator - entities matching all terms\r\n * const both = await manager.booleanSearch('database AND performance');\r\n *\r\n * // OR operator - entities matching any term\r\n * const either = await manager.booleanSearch('frontend OR backend');\r\n *\r\n * // NOT operator - exclude terms\r\n * const excluding = await manager.booleanSearch('API NOT deprecated');\r\n *\r\n * // Complex queries with grouping\r\n * const complex = await manager.booleanSearch('(react OR vue) AND (component OR hook) NOT legacy');\r\n * ```\r\n */\r\n async booleanSearch(\r\n query: string,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number\r\n ): Promise<KnowledgeGraph> {\r\n return this.booleanSearcher.booleanSearch(query, tags, minImportance, maxImportance);\r\n }\r\n\r\n // ==================== Fuzzy Search ====================\r\n\r\n /**\r\n * Perform fuzzy search with typo tolerance.\r\n *\r\n * Uses Levenshtein distance to find entities that approximately match the query,\r\n * making it ideal for handling typos and variations in spelling.\r\n * Higher threshold values require closer matches.\r\n *\r\n * @param query - Search query (will match approximate spellings)\r\n * @param threshold - Similarity threshold from 0.0 (very lenient) to 1.0 (exact match). Default: 0.7\r\n * @param tags - Optional array of tags to filter results (lowercase)\r\n * @param minImportance - Optional minimum importance value (0-10)\r\n * @param maxImportance - Optional maximum importance value (0-10)\r\n * @returns KnowledgeGraph containing entities with similar names/observations\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Find entities even with typos\r\n * const results = await manager.fuzzySearch('databse'); // Will match \"database\"\r\n *\r\n * // Adjust threshold for strictness\r\n * const strict = await manager.fuzzySearch('optmization', 0.9); // Requires very close match\r\n * const lenient = await manager.fuzzySearch('optmization', 0.6); // More tolerant of differences\r\n *\r\n * // Fuzzy search with filters\r\n * const filtered = await manager.fuzzySearch('secrity', 0.7, ['important'], 7, 10);\r\n * ```\r\n */\r\n async fuzzySearch(\r\n query: string,\r\n threshold?: number,\r\n tags?: string[],\r\n minImportance?: number,\r\n maxImportance?: number\r\n ): Promise<KnowledgeGraph> {\r\n return this.fuzzySearcher.fuzzySearch(query, threshold, tags, minImportance, maxImportance);\r\n }\r\n\r\n // ==================== Search Suggestions ====================\r\n\r\n /**\r\n * Get search suggestions for a query.\r\n *\r\n * @param query - Search query\r\n * @param maxSuggestions - Maximum suggestions to return\r\n * @returns Array of suggested terms\r\n */\r\n async getSearchSuggestions(query: string, maxSuggestions?: number): Promise<string[]> {\r\n return this.searchSuggestions.getSearchSuggestions(query, maxSuggestions);\r\n }\r\n\r\n // ==================== Saved Searches ====================\r\n\r\n /**\r\n * Save a search query for later reuse.\r\n *\r\n * Saved searches store query parameters and can be re-executed later.\r\n * The system tracks usage count and last used timestamp automatically.\r\n *\r\n * @param search - Search parameters (name, query, and optional filters)\r\n * @returns Newly created SavedSearch object with metadata\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Save a simple search\r\n * const saved = await manager.saveSearch({\r\n * name: 'High Priority Bugs',\r\n * query: 'bug',\r\n * tags: ['critical'],\r\n * minImportance: 8\r\n * });\r\n *\r\n * // Save a complex search\r\n * await manager.saveSearch({\r\n * name: 'Recent Frontend Work',\r\n * query: 'component OR hook',\r\n * tags: ['frontend', 'react'],\r\n * searchType: 'boolean'\r\n * });\r\n * ```\r\n */\r\n async saveSearch(\r\n search: Omit<SavedSearch, 'createdAt' | 'useCount' | 'lastUsed'>\r\n ): Promise<SavedSearch> {\r\n return this.savedSearchManager.saveSearch(search);\r\n }\r\n\r\n /**\r\n * List all saved searches.\r\n *\r\n * @returns Array of saved searches\r\n */\r\n async listSavedSearches(): Promise<SavedSearch[]> {\r\n return this.savedSearchManager.listSavedSearches();\r\n }\r\n\r\n /**\r\n * Get a saved search by name.\r\n *\r\n * @param name - Search name\r\n * @returns Saved search or null\r\n */\r\n async getSavedSearch(name: string): Promise<SavedSearch | null> {\r\n return this.savedSearchManager.getSavedSearch(name);\r\n }\r\n\r\n /**\r\n * Execute a saved search by name.\r\n *\r\n * Runs a previously saved search with its stored parameters.\r\n * Automatically updates the search's useCount and lastUsed timestamp.\r\n *\r\n * @param name - The unique name of the saved search to execute\r\n * @returns KnowledgeGraph containing the search results\r\n * @throws Error if saved search not found\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Execute a saved search\r\n * const results = await manager.executeSavedSearch('High Priority Bugs');\r\n * console.log(`Found ${results.entities.length} high priority bugs`);\r\n *\r\n * // Handle missing saved search\r\n * try {\r\n * await manager.executeSavedSearch('NonExistent');\r\n * } catch (error) {\r\n * console.error('Search not found');\r\n * }\r\n * ```\r\n */\r\n async executeSavedSearch(name: string): Promise<KnowledgeGraph> {\r\n return this.savedSearchManager.executeSavedSearch(name);\r\n }\r\n\r\n /**\r\n * Delete a saved search.\r\n *\r\n * @param name - Search name\r\n * @returns True if deleted\r\n */\r\n async deleteSavedSearch(name: string): Promise<boolean> {\r\n return this.savedSearchManager.deleteSavedSearch(name);\r\n }\r\n\r\n /**\r\n * Update a saved search.\r\n *\r\n * @param name - Search name\r\n * @param updates - Fields to update\r\n * @returns Updated saved search\r\n */\r\n async updateSavedSearch(\r\n name: string,\r\n updates: Partial<Omit<SavedSearch, 'name' | 'createdAt' | 'useCount' | 'lastUsed'>>\r\n ): Promise<SavedSearch> {\r\n return this.savedSearchManager.updateSavedSearch(name, updates);\r\n }\r\n\r\n // ==================== Phase 10 Sprint 4: Automatic Search ====================\r\n\r\n /**\r\n * Phase 10 Sprint 4: Automatically select and execute the best search method.\r\n *\r\n * Analyzes the query and graph size to determine the optimal search method,\r\n * then executes it and returns both the results and the selection reasoning.\r\n *\r\n * @param query - The search query\r\n * @param limit - Maximum number of results (default: 10)\r\n * @returns AutoSearchResult with selected method, results, and estimates\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new SearchManager(storage, savedSearchesPath);\r\n *\r\n * // Let the system choose the best search method\r\n * const result = await manager.autoSearch('software engineer skills');\r\n *\r\n * console.log(`Used ${result.selectedMethod} because: ${result.selectionReason}`);\r\n * console.log(`Found ${result.results.length} results in ${result.executionTimeMs}ms`);\r\n * ```\r\n */\r\n async autoSearch(query: string, limit: number = 10): Promise<AutoSearchResult> {\r\n const startTime = Date.now();\r\n\r\n // Get entity count from graph\r\n const graph = await this.storage.loadGraph();\r\n const entityCount = graph.entities.length;\r\n\r\n // Get cost estimates for all methods\r\n const estimates = this.queryEstimator.estimateAllMethods(query, entityCount);\r\n\r\n // Get the recommended method\r\n const recommendation = this.queryEstimator.recommendMethod(query, entityCount);\r\n const selectedMethod = recommendation.method;\r\n const selectionReason = recommendation.reason;\r\n\r\n // Execute the selected search method\r\n let results: SearchResult[];\r\n\r\n switch (selectedMethod) {\r\n case 'basic': {\r\n const basicResult = await this.basicSearch.searchNodes(query);\r\n results = basicResult.entities.map((e: Entity, idx: number) => ({\r\n entity: e,\r\n score: 1.0 - idx * 0.01, // Rank by position\r\n matchedFields: { name: true, observations: e.observations },\r\n }));\r\n break;\r\n }\r\n\r\n case 'ranked': {\r\n results = await this.rankedSearch.searchNodesRanked(query, undefined, undefined, undefined, limit);\r\n break;\r\n }\r\n\r\n case 'boolean': {\r\n const booleanResult = await this.booleanSearcher.booleanSearch(query);\r\n results = booleanResult.entities.map((e: Entity, idx: number) => ({\r\n entity: e,\r\n score: 1.0 - idx * 0.01, // Rank by position\r\n matchedFields: { name: true, observations: e.observations },\r\n }));\r\n break;\r\n }\r\n\r\n case 'fuzzy': {\r\n const fuzzyResult = await this.fuzzySearcher.fuzzySearch(query);\r\n results = fuzzyResult.entities.map((e: Entity, idx: number) => ({\r\n entity: e,\r\n score: 1.0 - idx * 0.01, // Rank by position\r\n matchedFields: { name: true, observations: e.observations },\r\n }));\r\n break;\r\n }\r\n\r\n case 'semantic': {\r\n // Semantic search not available through SearchManager\r\n // Fall back to ranked search\r\n results = await this.rankedSearch.searchNodesRanked(query, undefined, undefined, undefined, limit);\r\n break;\r\n }\r\n\r\n default: {\r\n const _exhaustiveCheck: never = selectedMethod;\r\n throw new Error(`Unknown search method: ${_exhaustiveCheck}`);\r\n }\r\n }\r\n\r\n // Limit results\r\n const limitedResults = results.slice(0, limit);\r\n\r\n return {\r\n selectedMethod,\r\n selectionReason,\r\n estimates,\r\n results: limitedResults,\r\n executionTimeMs: Date.now() - startTime,\r\n };\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 4: Get cost estimates for all search methods.\r\n *\r\n * Useful for clients that want to display cost information or\r\n * make their own method selection decisions.\r\n *\r\n * @param query - The search query\r\n * @returns Array of cost estimates for all methods\r\n */\r\n async getSearchCostEstimates(query: string): Promise<import('../types/index.js').QueryCostEstimate[]> {\r\n const graph = await this.storage.loadGraph();\r\n const entityCount = graph.entities.length;\r\n return this.queryEstimator.estimateAllMethods(query, entityCount);\r\n }\r\n\r\n /**\r\n * Phase 10 Sprint 4: Get the query cost estimator instance.\r\n *\r\n * @returns The QueryCostEstimator instance\r\n */\r\n getQueryEstimator(): QueryCostEstimator {\r\n return this.queryEstimator;\r\n }\r\n}\r\n","/**\r\n * Embedding Service\r\n *\r\n * Phase 4 Sprint 10: Provides embedding abstractions for semantic search.\r\n * Phase 12 Sprint 5: Added query/document prefixes, l2Normalize, mode parameter,\r\n * and batch embedding with progress callback.\r\n * Supports multiple providers: OpenAI (cloud) and local (transformers.js).\r\n *\r\n * @module search/EmbeddingService\r\n */\r\n\r\nimport type { EmbeddingService, EmbeddingConfig, EmbeddingMode } from '../types/index.js';\r\nimport {\r\n EMBEDDING_DEFAULTS,\r\n OPENAI_API_CONFIG,\r\n getEmbeddingConfig,\r\n} from '../utils/constants.js';\r\n\r\n/**\r\n * Phase 12 Sprint 5: Prefixes for query-optimized embedding encoding.\r\n *\r\n * These prefixes are used to distinguish between query and document embeddings,\r\n * which can improve retrieval performance with asymmetric embedding models.\r\n */\r\nexport const QUERY_PREFIX = 'query: ';\r\nexport const DOCUMENT_PREFIX = 'passage: ';\r\n\r\n/**\r\n * Phase 12 Sprint 5: Callback for batch embedding progress.\r\n */\r\nexport type EmbeddingProgressCallback = (progress: {\r\n current: number;\r\n total: number;\r\n percentage: number;\r\n}) => void;\r\n\r\n/**\r\n * Phase 12 Sprint 5: L2 normalize a vector for cosine similarity.\r\n *\r\n * Normalizes a vector to unit length (magnitude 1), which is required\r\n * for accurate cosine similarity calculations.\r\n *\r\n * @param vector - Input vector to normalize\r\n * @returns L2 normalized vector\r\n *\r\n * @example\r\n * ```typescript\r\n * const normalized = l2Normalize([3, 4]); // [0.6, 0.8]\r\n * ```\r\n */\r\nexport function l2Normalize(vector: number[]): number[] {\r\n let magnitude = 0;\r\n for (const v of vector) {\r\n magnitude += v * v;\r\n }\r\n magnitude = Math.sqrt(magnitude);\r\n\r\n if (magnitude === 0 || magnitude === 1) {\r\n return magnitude === 0 ? vector : vector.slice();\r\n }\r\n\r\n return vector.map(v => v / magnitude);\r\n}\r\n\r\n/**\r\n * OpenAI Embedding Service\r\n *\r\n * Uses OpenAI's text-embedding-3-small model for generating embeddings.\r\n * Supports single and batch embedding with rate limit handling.\r\n * Phase 12 Sprint 5: Added mode parameter and progress callback support.\r\n *\r\n * @example\r\n * ```typescript\r\n * const service = new OpenAIEmbeddingService('sk-...');\r\n * const embedding = await service.embed(\"Hello world\", 'query');\r\n * console.log(`Generated ${embedding.length} dimensions`);\r\n * ```\r\n */\r\nexport class OpenAIEmbeddingService implements EmbeddingService {\r\n readonly dimensions: number;\r\n readonly provider = 'openai';\r\n readonly model: string;\r\n private apiKey: string;\r\n\r\n /**\r\n * Create an OpenAI embedding service.\r\n *\r\n * @param apiKey - OpenAI API key\r\n * @param model - Optional model override (default: text-embedding-3-small)\r\n */\r\n constructor(apiKey: string, model?: string) {\r\n if (!apiKey) {\r\n throw new Error('OpenAI API key is required');\r\n }\r\n this.apiKey = apiKey;\r\n this.model = model || EMBEDDING_DEFAULTS.OPENAI_MODEL;\r\n this.dimensions = EMBEDDING_DEFAULTS.OPENAI_DIMENSIONS;\r\n }\r\n\r\n /**\r\n * Check if the service is ready.\r\n */\r\n async isReady(): Promise<boolean> {\r\n return !!this.apiKey;\r\n }\r\n\r\n /**\r\n * Apply prefix to text based on embedding mode.\r\n *\r\n * @param text - Original text\r\n * @param mode - Embedding mode ('query' or 'document')\r\n * @returns Text with appropriate prefix\r\n */\r\n private applyPrefix(text: string, mode: EmbeddingMode = 'document'): string {\r\n return mode === 'query' ? `${QUERY_PREFIX}${text}` : `${DOCUMENT_PREFIX}${text}`;\r\n }\r\n\r\n /**\r\n * Generate embedding for a single text.\r\n *\r\n * @param text - Text to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Embedding vector\r\n */\r\n async embed(text: string, mode: EmbeddingMode = 'document'): Promise<number[]> {\r\n const results = await this.embedBatch([text], mode);\r\n return results[0];\r\n }\r\n\r\n /**\r\n * Generate embeddings for multiple texts in batch.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Array of embedding vectors\r\n */\r\n async embedBatch(texts: string[], mode: EmbeddingMode = 'document'): Promise<number[][]> {\r\n if (texts.length === 0) {\r\n return [];\r\n }\r\n\r\n // Apply prefix based on mode\r\n const prefixedTexts = texts.map(text => this.applyPrefix(text, mode));\r\n\r\n // Split into batches if needed\r\n const maxBatchSize = EMBEDDING_DEFAULTS.OPENAI_MAX_BATCH_SIZE;\r\n const results: number[][] = [];\r\n\r\n for (let i = 0; i < prefixedTexts.length; i += maxBatchSize) {\r\n const batch = prefixedTexts.slice(i, i + maxBatchSize);\r\n const batchResults = await this.embedBatchInternal(batch);\r\n results.push(...batchResults);\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Generate embeddings with progress callback support.\r\n *\r\n * Phase 12 Sprint 5: Added for tracking progress on large batch operations.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @param onProgress - Optional progress callback\r\n * @returns Array of embedding vectors\r\n */\r\n async embedBatchWithProgress(\r\n texts: string[],\r\n mode: EmbeddingMode = 'document',\r\n onProgress?: EmbeddingProgressCallback\r\n ): Promise<number[][]> {\r\n if (texts.length === 0) {\r\n return [];\r\n }\r\n\r\n // Apply prefix based on mode\r\n const prefixedTexts = texts.map(text => this.applyPrefix(text, mode));\r\n\r\n // Split into batches if needed\r\n const maxBatchSize = EMBEDDING_DEFAULTS.OPENAI_MAX_BATCH_SIZE;\r\n const results: number[][] = [];\r\n const total = prefixedTexts.length;\r\n\r\n for (let i = 0; i < prefixedTexts.length; i += maxBatchSize) {\r\n const batch = prefixedTexts.slice(i, i + maxBatchSize);\r\n const batchResults = await this.embedBatchInternal(batch);\r\n results.push(...batchResults);\r\n\r\n // Report progress\r\n if (onProgress) {\r\n const current = Math.min(i + maxBatchSize, total);\r\n onProgress({\r\n current,\r\n total,\r\n percentage: Math.round((current / total) * 100),\r\n });\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Internal batch embedding with retry logic.\r\n */\r\n private async embedBatchInternal(texts: string[]): Promise<number[][]> {\r\n let lastError: Error | null = null;\r\n let backoff = OPENAI_API_CONFIG.INITIAL_BACKOFF_MS;\r\n\r\n for (let attempt = 0; attempt <= OPENAI_API_CONFIG.MAX_RETRIES; attempt++) {\r\n try {\r\n const response = await fetch(\r\n `${OPENAI_API_CONFIG.BASE_URL}${OPENAI_API_CONFIG.EMBEDDINGS_ENDPOINT}`,\r\n {\r\n method: 'POST',\r\n headers: {\r\n 'Content-Type': 'application/json',\r\n 'Authorization': `Bearer ${this.apiKey}`,\r\n },\r\n body: JSON.stringify({\r\n model: this.model,\r\n input: texts,\r\n }),\r\n }\r\n );\r\n\r\n if (!response.ok) {\r\n const errorBody = await response.text();\r\n\r\n // Handle rate limiting\r\n if (response.status === 429) {\r\n if (attempt < OPENAI_API_CONFIG.MAX_RETRIES) {\r\n await this.sleep(backoff);\r\n backoff = Math.min(backoff * 2, OPENAI_API_CONFIG.MAX_BACKOFF_MS);\r\n continue;\r\n }\r\n }\r\n\r\n throw new Error(`OpenAI API error: ${response.status} - ${errorBody}`);\r\n }\r\n\r\n const data = await response.json() as OpenAIEmbeddingResponse;\r\n\r\n // Sort by index to ensure correct order\r\n const sortedData = [...data.data].sort((a, b) => a.index - b.index);\r\n return sortedData.map(item => item.embedding);\r\n } catch (error) {\r\n lastError = error instanceof Error ? error : new Error(String(error));\r\n\r\n // Retry on network errors\r\n if (attempt < OPENAI_API_CONFIG.MAX_RETRIES && this.isRetryableError(error)) {\r\n await this.sleep(backoff);\r\n backoff = Math.min(backoff * 2, OPENAI_API_CONFIG.MAX_BACKOFF_MS);\r\n continue;\r\n }\r\n\r\n throw lastError;\r\n }\r\n }\r\n\r\n throw lastError || new Error('Failed to generate embeddings after retries');\r\n }\r\n\r\n /**\r\n * Check if an error is retryable.\r\n */\r\n private isRetryableError(error: unknown): boolean {\r\n if (error instanceof Error) {\r\n // Network errors and rate limits are retryable\r\n return error.message.includes('fetch') ||\r\n error.message.includes('network') ||\r\n error.message.includes('429');\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Sleep for a given duration.\r\n */\r\n private sleep(ms: number): Promise<void> {\r\n return new Promise(resolve => setTimeout(resolve, ms));\r\n }\r\n}\r\n\r\n/**\r\n * OpenAI API response type for embeddings.\r\n */\r\ninterface OpenAIEmbeddingResponse {\r\n object: string;\r\n data: Array<{\r\n object: string;\r\n embedding: number[];\r\n index: number;\r\n }>;\r\n model: string;\r\n usage: {\r\n prompt_tokens: number;\r\n total_tokens: number;\r\n };\r\n}\r\n\r\n/**\r\n * Local Embedding Service\r\n *\r\n * Uses @xenova/transformers for local embedding generation.\r\n * No API calls needed - runs entirely offline after initial model download.\r\n * Phase 12 Sprint 5: Added mode parameter and progress callback support.\r\n *\r\n * Note: Requires @xenova/transformers to be installed as an optional dependency.\r\n * If not available, initialization will fail gracefully.\r\n *\r\n * @example\r\n * ```typescript\r\n * const service = new LocalEmbeddingService();\r\n * await service.initialize();\r\n * const embedding = await service.embed(\"Hello world\", 'query');\r\n * ```\r\n */\r\nexport class LocalEmbeddingService implements EmbeddingService {\r\n readonly dimensions: number = EMBEDDING_DEFAULTS.LOCAL_DIMENSIONS;\r\n readonly provider = 'local';\r\n readonly model: string;\r\n\r\n private pipeline: unknown = null;\r\n private initialized = false;\r\n private initPromise: Promise<void> | null = null;\r\n\r\n /**\r\n * Create a local embedding service.\r\n *\r\n * @param model - Optional model override (default: Xenova/all-MiniLM-L6-v2)\r\n */\r\n constructor(model?: string) {\r\n this.model = model || EMBEDDING_DEFAULTS.LOCAL_MODEL;\r\n }\r\n\r\n /**\r\n * Initialize the model pipeline.\r\n * Must be called before using embed/embedBatch.\r\n */\r\n async initialize(): Promise<void> {\r\n if (this.initialized) return;\r\n\r\n if (this.initPromise) {\r\n return this.initPromise;\r\n }\r\n\r\n this.initPromise = this.initializeInternal();\r\n return this.initPromise;\r\n }\r\n\r\n /**\r\n * Internal initialization.\r\n */\r\n private async initializeInternal(): Promise<void> {\r\n try {\r\n // Dynamic import to allow optional dependency\r\n // @ts-expect-error - @xenova/transformers is an optional peer dependency\r\n const transformers = await import('@xenova/transformers');\r\n const { pipeline } = transformers;\r\n\r\n this.pipeline = await pipeline('feature-extraction', this.model);\r\n this.initialized = true;\r\n } catch (error) {\r\n this.initPromise = null;\r\n throw new Error(\r\n `Failed to initialize local embedding service: ${error instanceof Error ? error.message : String(error)}. ` +\r\n 'Make sure @xenova/transformers is installed.'\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Check if the service is ready.\r\n */\r\n async isReady(): Promise<boolean> {\r\n if (!this.initialized && !this.initPromise) {\r\n try {\r\n await this.initialize();\r\n } catch {\r\n return false;\r\n }\r\n }\r\n return this.initialized;\r\n }\r\n\r\n /**\r\n * Apply prefix to text based on embedding mode.\r\n *\r\n * @param text - Original text\r\n * @param mode - Embedding mode ('query' or 'document')\r\n * @returns Text with appropriate prefix\r\n */\r\n private applyPrefix(text: string, mode: EmbeddingMode = 'document'): string {\r\n return mode === 'query' ? `${QUERY_PREFIX}${text}` : `${DOCUMENT_PREFIX}${text}`;\r\n }\r\n\r\n /**\r\n * Generate embedding for a single text.\r\n *\r\n * @param text - Text to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Embedding vector\r\n */\r\n async embed(text: string, mode: EmbeddingMode = 'document'): Promise<number[]> {\r\n await this.ensureInitialized();\r\n\r\n const prefixedText = this.applyPrefix(text, mode);\r\n const pipelineFn = this.pipeline as (text: string, options: { pooling: string; normalize: boolean }) => Promise<{ data: Float32Array }>;\r\n const output = await pipelineFn(prefixedText, { pooling: 'mean', normalize: true });\r\n\r\n return Array.from(output.data);\r\n }\r\n\r\n /**\r\n * Generate embeddings for multiple texts in batch.\r\n * Note: Local processing is done sequentially to avoid memory issues.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Array of embedding vectors\r\n */\r\n async embedBatch(texts: string[], mode: EmbeddingMode = 'document'): Promise<number[][]> {\r\n await this.ensureInitialized();\r\n\r\n const results: number[][] = [];\r\n for (const text of texts) {\r\n const embedding = await this.embed(text, mode);\r\n results.push(embedding);\r\n }\r\n return results;\r\n }\r\n\r\n /**\r\n * Generate embeddings with progress callback support.\r\n *\r\n * Phase 12 Sprint 5: Added for tracking progress on large batch operations.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @param onProgress - Optional progress callback\r\n * @returns Array of embedding vectors\r\n */\r\n async embedBatchWithProgress(\r\n texts: string[],\r\n mode: EmbeddingMode = 'document',\r\n onProgress?: EmbeddingProgressCallback\r\n ): Promise<number[][]> {\r\n await this.ensureInitialized();\r\n\r\n const results: number[][] = [];\r\n const total = texts.length;\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n const embedding = await this.embed(texts[i], mode);\r\n results.push(embedding);\r\n\r\n // Report progress\r\n if (onProgress) {\r\n const current = i + 1;\r\n onProgress({\r\n current,\r\n total,\r\n percentage: Math.round((current / total) * 100),\r\n });\r\n }\r\n }\r\n return results;\r\n }\r\n\r\n /**\r\n * Ensure the service is initialized.\r\n */\r\n private async ensureInitialized(): Promise<void> {\r\n if (!this.initialized) {\r\n await this.initialize();\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Mock Embedding Service for testing\r\n *\r\n * Generates deterministic mock embeddings for testing purposes.\r\n * Useful for unit tests that don't need real embeddings.\r\n * Phase 12 Sprint 5: Added mode parameter and progress callback support.\r\n */\r\nexport class MockEmbeddingService implements EmbeddingService {\r\n readonly dimensions: number;\r\n readonly provider = 'mock';\r\n readonly model = 'mock-model';\r\n\r\n /**\r\n * Create a mock embedding service.\r\n *\r\n * @param dimensions - Number of dimensions for mock embeddings\r\n */\r\n constructor(dimensions: number = 384) {\r\n this.dimensions = dimensions;\r\n }\r\n\r\n /**\r\n * Check if the service is ready.\r\n */\r\n async isReady(): Promise<boolean> {\r\n return true;\r\n }\r\n\r\n /**\r\n * Apply prefix to text based on embedding mode.\r\n *\r\n * @param text - Original text\r\n * @param mode - Embedding mode ('query' or 'document')\r\n * @returns Text with appropriate prefix\r\n */\r\n private applyPrefix(text: string, mode: EmbeddingMode = 'document'): string {\r\n return mode === 'query' ? `${QUERY_PREFIX}${text}` : `${DOCUMENT_PREFIX}${text}`;\r\n }\r\n\r\n /**\r\n * Generate a deterministic mock embedding for a text.\r\n *\r\n * @param text - Text to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Mock embedding vector\r\n */\r\n async embed(text: string, mode: EmbeddingMode = 'document'): Promise<number[]> {\r\n // Apply prefix based on mode (affects hash for different embeddings per mode)\r\n const prefixedText = this.applyPrefix(text, mode);\r\n\r\n // Generate deterministic embedding based on text hash\r\n const hash = this.hashString(prefixedText);\r\n const embedding: number[] = [];\r\n\r\n for (let i = 0; i < this.dimensions; i++) {\r\n // Use hash and index to generate deterministic values\r\n const value = Math.sin(hash + i * 0.1) * 0.5;\r\n embedding.push(value);\r\n }\r\n\r\n // Normalize the vector\r\n return this.normalize(embedding);\r\n }\r\n\r\n /**\r\n * Generate mock embeddings for multiple texts.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @returns Array of mock embedding vectors\r\n */\r\n async embedBatch(texts: string[], mode: EmbeddingMode = 'document'): Promise<number[][]> {\r\n return Promise.all(texts.map(text => this.embed(text, mode)));\r\n }\r\n\r\n /**\r\n * Generate mock embeddings with progress callback support.\r\n *\r\n * Phase 12 Sprint 5: Added for tracking progress on large batch operations.\r\n *\r\n * @param texts - Array of texts to embed\r\n * @param mode - Embedding mode ('query' or 'document', default: 'document')\r\n * @param onProgress - Optional progress callback\r\n * @returns Array of mock embedding vectors\r\n */\r\n async embedBatchWithProgress(\r\n texts: string[],\r\n mode: EmbeddingMode = 'document',\r\n onProgress?: EmbeddingProgressCallback\r\n ): Promise<number[][]> {\r\n const results: number[][] = [];\r\n const total = texts.length;\r\n\r\n for (let i = 0; i < texts.length; i++) {\r\n const embedding = await this.embed(texts[i], mode);\r\n results.push(embedding);\r\n\r\n // Report progress\r\n if (onProgress) {\r\n const current = i + 1;\r\n onProgress({\r\n current,\r\n total,\r\n percentage: Math.round((current / total) * 100),\r\n });\r\n }\r\n }\r\n return results;\r\n }\r\n\r\n /**\r\n * Simple string hash function.\r\n */\r\n private hashString(str: string): number {\r\n let hash = 0;\r\n for (let i = 0; i < str.length; i++) {\r\n const char = str.charCodeAt(i);\r\n hash = ((hash << 5) - hash) + char;\r\n hash = hash & hash; // Convert to 32bit integer\r\n }\r\n return hash;\r\n }\r\n\r\n /**\r\n * Normalize a vector to unit length.\r\n */\r\n private normalize(vector: number[]): number[] {\r\n let magnitude = 0;\r\n for (const v of vector) {\r\n magnitude += v * v;\r\n }\r\n magnitude = Math.sqrt(magnitude);\r\n\r\n if (magnitude === 0) {\r\n return vector;\r\n }\r\n\r\n return vector.map(v => v / magnitude);\r\n }\r\n}\r\n\r\n/**\r\n * Create an embedding service based on configuration.\r\n *\r\n * @param config - Optional configuration override\r\n * @returns Embedding service instance, or null if provider is 'none'\r\n */\r\nexport function createEmbeddingService(config?: Partial<EmbeddingConfig>): EmbeddingService | null {\r\n const envConfig = getEmbeddingConfig();\r\n const mergedConfig = { ...envConfig, ...config };\r\n\r\n switch (mergedConfig.provider) {\r\n case 'openai':\r\n if (!mergedConfig.apiKey) {\r\n throw new Error(\r\n 'OpenAI API key is required. Set MEMORY_OPENAI_API_KEY environment variable or provide apiKey in config.'\r\n );\r\n }\r\n return new OpenAIEmbeddingService(mergedConfig.apiKey, mergedConfig.model);\r\n\r\n case 'local':\r\n return new LocalEmbeddingService(mergedConfig.model);\r\n\r\n case 'none':\r\n default:\r\n return null;\r\n }\r\n}\r\n","/**\n * Embedding Cache\n *\n * Phase 12 Sprint 5: LRU cache for embedding vectors with hit/miss tracking,\n * auto-invalidation on text hash changes, and TTL support.\n *\n * @module search/EmbeddingCache\n */\n\nimport { createHash } from 'crypto';\n\n/**\n * Cache entry for embedding vectors.\n */\ninterface CacheEntry {\n /** The embedding vector */\n vector: number[];\n /** Hash of the original text for invalidation */\n textHash: string;\n /** Timestamp when the entry was created (for TTL) */\n createdAt: number;\n /** Timestamp of last access (for LRU) */\n lastAccess: number;\n}\n\n/**\n * Statistics for the embedding cache.\n */\nexport interface EmbeddingCacheStats {\n /** Number of entries in the cache */\n size: number;\n /** Estimated memory usage in bytes */\n memoryBytes: number;\n /** Cache hit rate (0-1) */\n hitRate: number;\n /** Total cache hits */\n hits: number;\n /** Total cache misses */\n misses: number;\n}\n\n/**\n * Options for EmbeddingCache.\n */\nexport interface EmbeddingCacheOptions {\n /** Maximum number of entries in the cache (default: 1000) */\n maxSize?: number;\n /** Time-to-live in milliseconds (default: 3600000 = 1 hour) */\n ttlMs?: number;\n /** Dimensions of embedding vectors for memory estimation (default: 384) */\n dimensions?: number;\n}\n\n/**\n * Default cache options.\n */\nexport const DEFAULT_EMBEDDING_CACHE_OPTIONS: Required<EmbeddingCacheOptions> = {\n maxSize: 1000,\n ttlMs: 3600000, // 1 hour\n dimensions: 384,\n};\n\n/**\n * LRU cache for embedding vectors with hit/miss tracking.\n *\n * Features:\n * - LRU eviction when max size is reached\n * - Text hash-based invalidation (detects stale entries)\n * - TTL support for automatic expiration\n * - Hit/miss statistics tracking\n *\n * @example\n * ```typescript\n * const cache = new EmbeddingCache({ maxSize: 500, ttlMs: 60000 });\n *\n * // Cache an embedding\n * cache.set('entity1', 'Original text content', [0.1, 0.2, ...]);\n *\n * // Retrieve from cache\n * const result = cache.get('entity1', 'Original text content');\n * if (result) {\n * console.log('Cache hit!', result);\n * }\n *\n * // Check stats\n * console.log(cache.getStats()); // { size, memoryBytes, hitRate, hits, misses }\n * ```\n */\nexport class EmbeddingCache {\n private cache: Map<string, CacheEntry>;\n private options: Required<EmbeddingCacheOptions>;\n private hits: number = 0;\n private misses: number = 0;\n\n /**\n * Create a new embedding cache.\n *\n * @param options - Cache configuration options\n */\n constructor(options?: EmbeddingCacheOptions) {\n this.options = { ...DEFAULT_EMBEDDING_CACHE_OPTIONS, ...options };\n this.cache = new Map();\n }\n\n /**\n * Hash a text string for cache invalidation.\n *\n * @param text - Text to hash\n * @returns MD5 hash of the text\n */\n private hashText(text: string): string {\n return createHash('md5').update(text).digest('hex');\n }\n\n /**\n * Check if an entry is expired based on TTL.\n *\n * @param entry - Cache entry to check\n * @returns True if expired\n */\n private isExpired(entry: CacheEntry): boolean {\n return Date.now() - entry.createdAt > this.options.ttlMs;\n }\n\n /**\n * Evict the least recently used entry.\n */\n private evictLRU(): void {\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n for (const [key, entry] of this.cache) {\n if (entry.lastAccess < oldestTime) {\n oldestTime = entry.lastAccess;\n oldestKey = key;\n }\n }\n\n if (oldestKey) {\n this.cache.delete(oldestKey);\n }\n }\n\n /**\n * Get an embedding from the cache.\n *\n * Returns null if:\n * - Key not found\n * - Entry is expired (TTL)\n * - Text hash doesn't match (content changed)\n *\n * @param key - Cache key (typically entity name)\n * @param text - Current text content (for hash validation)\n * @returns Embedding vector if found and valid, null otherwise\n */\n get(key: string, text: string): number[] | null {\n const entry = this.cache.get(key);\n\n if (!entry) {\n this.misses++;\n return null;\n }\n\n // Check TTL\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n this.misses++;\n return null;\n }\n\n // Check text hash for invalidation\n const currentHash = this.hashText(text);\n if (entry.textHash !== currentHash) {\n this.cache.delete(key);\n this.misses++;\n return null;\n }\n\n // Update last access time\n entry.lastAccess = Date.now();\n this.hits++;\n\n return entry.vector;\n }\n\n /**\n * Set an embedding in the cache.\n *\n * Automatically evicts LRU entries if max size is reached.\n *\n * @param key - Cache key (typically entity name)\n * @param text - Text content (used for hash-based invalidation)\n * @param vector - Embedding vector to cache\n */\n set(key: string, text: string, vector: number[]): void {\n // Evict if at capacity and not updating existing key\n if (this.cache.size >= this.options.maxSize && !this.cache.has(key)) {\n this.evictLRU();\n }\n\n const now = Date.now();\n this.cache.set(key, {\n vector,\n textHash: this.hashText(text),\n createdAt: now,\n lastAccess: now,\n });\n }\n\n /**\n * Check if a key exists in the cache (without affecting hit/miss stats).\n *\n * @param key - Cache key to check\n * @returns True if key exists (may be expired or stale)\n */\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n /**\n * Delete an entry from the cache.\n *\n * @param key - Cache key to delete\n * @returns True if entry was deleted\n */\n delete(key: string): boolean {\n return this.cache.delete(key);\n }\n\n /**\n * Clear all entries from the cache.\n */\n clear(): void {\n this.cache.clear();\n }\n\n /**\n * Get cache statistics.\n *\n * @returns Cache statistics including size, memory usage, and hit rate\n */\n getStats(): EmbeddingCacheStats {\n const size = this.cache.size;\n // Estimate memory: each entry has vector (dimensions * 8 bytes for float64)\n // plus overhead for hash (~32 bytes), timestamps (~16 bytes), and Map overhead (~50 bytes)\n const memoryPerEntry = this.options.dimensions * 8 + 32 + 16 + 50;\n const memoryBytes = size * memoryPerEntry;\n\n const totalRequests = this.hits + this.misses;\n const hitRate = totalRequests > 0 ? this.hits / totalRequests : 0;\n\n return {\n size,\n memoryBytes,\n hitRate,\n hits: this.hits,\n misses: this.misses,\n };\n }\n\n /**\n * Reset hit/miss statistics (useful for benchmarks).\n */\n resetStats(): void {\n this.hits = 0;\n this.misses = 0;\n }\n\n /**\n * Remove expired entries from the cache.\n *\n * Called automatically during get operations, but can be\n * manually triggered for maintenance.\n *\n * @returns Number of entries removed\n */\n pruneExpired(): number {\n let removed = 0;\n for (const [key, entry] of this.cache) {\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n removed++;\n }\n }\n return removed;\n }\n\n /**\n * Get the current cache size.\n *\n * @returns Number of entries in the cache\n */\n size(): number {\n return this.cache.size;\n }\n\n /**\n * Get all cached keys.\n *\n * @returns Array of cache keys\n */\n keys(): string[] {\n return Array.from(this.cache.keys());\n }\n\n /**\n * Update options dynamically.\n *\n * Note: Reducing maxSize will not immediately evict entries.\n *\n * @param options - New options to apply\n */\n updateOptions(options: Partial<EmbeddingCacheOptions>): void {\n this.options = { ...this.options, ...options };\n }\n}\n","/**\n * Incremental Indexer\n *\n * Phase 12 Sprint 5: Queues embedding index updates and batch-processes them\n * efficiently using flush thresholds and timer-based flushing.\n *\n * @module search/IncrementalIndexer\n */\n\nimport type { EmbeddingService, EmbeddingMode } from '../types/index.js';\nimport type { IVectorStore } from '../types/index.js';\nimport type { EmbeddingProgressCallback } from './EmbeddingService.js';\n\n/**\n * Types of index operations.\n */\nexport type IndexOperationType = 'create' | 'update' | 'delete';\n\n/**\n * Queued index operation.\n */\nexport interface IndexOperation {\n /** Type of operation */\n type: IndexOperationType;\n /** Entity name */\n entityName: string;\n /** Text to embed (for create/update) */\n text?: string;\n /** Timestamp when operation was queued */\n queuedAt: number;\n}\n\n/**\n * Options for IncrementalIndexer.\n */\nexport interface IncrementalIndexerOptions {\n /** Flush threshold - number of operations to queue before auto-flush (default: 50) */\n flushThreshold?: number;\n /** Timer-based flush interval in milliseconds (default: 5000ms) */\n flushIntervalMs?: number;\n /** Embedding mode for new embeddings (default: 'document') */\n embeddingMode?: EmbeddingMode;\n /** Progress callback for batch operations */\n onProgress?: EmbeddingProgressCallback;\n}\n\n/**\n * Default indexer options.\n */\nexport const DEFAULT_INDEXER_OPTIONS: Required<Omit<IncrementalIndexerOptions, 'onProgress'>> = {\n flushThreshold: 50,\n flushIntervalMs: 5000,\n embeddingMode: 'document',\n};\n\n/**\n * Result of a flush operation.\n */\nexport interface FlushResult {\n /** Number of operations processed */\n processed: number;\n /** Number of successful operations */\n succeeded: number;\n /** Number of failed operations */\n failed: number;\n /** Errors encountered during flush */\n errors: Array<{ entityName: string; error: string }>;\n /** Duration of flush in milliseconds */\n durationMs: number;\n}\n\n/**\n * Incremental indexer for embedding vectors.\n *\n * Queues index updates and batch-processes them efficiently:\n * - Auto-flush when queue reaches threshold\n * - Timer-based flush for time-sensitive updates\n * - Supports create, update, and delete operations\n * - Graceful shutdown with final flush\n *\n * @example\n * ```typescript\n * const indexer = new IncrementalIndexer(embeddingService, vectorStore, {\n * flushThreshold: 100,\n * flushIntervalMs: 10000,\n * });\n *\n * // Queue operations\n * indexer.queueCreate('entity1', 'Entity text content');\n * indexer.queueUpdate('entity2', 'Updated text content');\n * indexer.queueDelete('entity3');\n *\n * // Manual flush\n * const result = await indexer.flush();\n * console.log(`Processed ${result.processed} operations`);\n *\n * // Graceful shutdown\n * await indexer.shutdown();\n * ```\n */\nexport class IncrementalIndexer {\n private embeddingService: EmbeddingService;\n private vectorStore: IVectorStore;\n private options: Required<Omit<IncrementalIndexerOptions, 'onProgress'>>;\n private onProgress?: EmbeddingProgressCallback;\n\n private queue: IndexOperation[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private isFlushing = false;\n private isShutdown = false;\n\n /**\n * Create a new incremental indexer.\n *\n * @param embeddingService - Service for generating embeddings\n * @param vectorStore - Store for embedding vectors\n * @param options - Indexer configuration options\n */\n constructor(\n embeddingService: EmbeddingService,\n vectorStore: IVectorStore,\n options?: IncrementalIndexerOptions\n ) {\n this.embeddingService = embeddingService;\n this.vectorStore = vectorStore;\n this.options = { ...DEFAULT_INDEXER_OPTIONS, ...options };\n this.onProgress = options?.onProgress;\n\n // Start the flush timer\n this.startFlushTimer();\n }\n\n /**\n * Start the timer-based flush interval.\n */\n private startFlushTimer(): void {\n if (this.flushTimer || this.isShutdown) {\n return;\n }\n\n this.flushTimer = setInterval(async () => {\n if (this.queue.length > 0 && !this.isFlushing) {\n await this.flush();\n }\n }, this.options.flushIntervalMs);\n }\n\n /**\n * Stop the flush timer.\n */\n private stopFlushTimer(): void {\n if (this.flushTimer) {\n clearInterval(this.flushTimer);\n this.flushTimer = null;\n }\n }\n\n /**\n * Queue a create operation.\n *\n * @param entityName - Name of the entity to create\n * @param text - Text content to embed\n */\n queueCreate(entityName: string, text: string): void {\n if (this.isShutdown) {\n throw new Error('Indexer is shutdown');\n }\n\n // Remove any existing operations for this entity\n this.removeFromQueue(entityName);\n\n this.queue.push({\n type: 'create',\n entityName,\n text,\n queuedAt: Date.now(),\n });\n\n this.checkAutoFlush();\n }\n\n /**\n * Queue an update operation.\n *\n * @param entityName - Name of the entity to update\n * @param text - Updated text content to embed\n */\n queueUpdate(entityName: string, text: string): void {\n if (this.isShutdown) {\n throw new Error('Indexer is shutdown');\n }\n\n // Remove any existing operations for this entity\n this.removeFromQueue(entityName);\n\n this.queue.push({\n type: 'update',\n entityName,\n text,\n queuedAt: Date.now(),\n });\n\n this.checkAutoFlush();\n }\n\n /**\n * Queue a delete operation.\n *\n * @param entityName - Name of the entity to delete\n */\n queueDelete(entityName: string): void {\n if (this.isShutdown) {\n throw new Error('Indexer is shutdown');\n }\n\n // Remove any existing operations for this entity\n this.removeFromQueue(entityName);\n\n this.queue.push({\n type: 'delete',\n entityName,\n queuedAt: Date.now(),\n });\n\n this.checkAutoFlush();\n }\n\n /**\n * Remove all queued operations for an entity.\n *\n * @param entityName - Entity name to remove from queue\n */\n private removeFromQueue(entityName: string): void {\n this.queue = this.queue.filter(op => op.entityName !== entityName);\n }\n\n /**\n * Check if auto-flush threshold is reached.\n */\n private checkAutoFlush(): void {\n if (this.queue.length >= this.options.flushThreshold && !this.isFlushing) {\n // Use setImmediate to avoid blocking\n setImmediate(() => this.flush());\n }\n }\n\n /**\n * Flush the queue and process all pending operations.\n *\n * @returns Result of the flush operation\n */\n async flush(): Promise<FlushResult> {\n if (this.isFlushing) {\n return {\n processed: 0,\n succeeded: 0,\n failed: 0,\n errors: [],\n durationMs: 0,\n };\n }\n\n this.isFlushing = true;\n const startTime = Date.now();\n const errors: Array<{ entityName: string; error: string }> = [];\n let succeeded = 0;\n let failed = 0;\n\n // Take the current queue\n const operations = [...this.queue];\n this.queue = [];\n\n try {\n // Separate operations by type\n const createOps = operations.filter(op => op.type === 'create');\n const updateOps = operations.filter(op => op.type === 'update');\n const deleteOps = operations.filter(op => op.type === 'delete');\n\n // Process deletes first (fast, O(1))\n for (const op of deleteOps) {\n try {\n this.vectorStore.remove(op.entityName);\n succeeded++;\n } catch (error) {\n failed++;\n errors.push({\n entityName: op.entityName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n // Batch process creates and updates together\n const embedOps = [...createOps, ...updateOps];\n if (embedOps.length > 0) {\n const texts = embedOps.map(op => op.text!);\n const entityNames = embedOps.map(op => op.entityName);\n\n try {\n // Check if the embedding service has the batch with progress method\n let embeddings: number[][];\n\n if (this.onProgress && 'embedBatchWithProgress' in this.embeddingService) {\n embeddings = await (this.embeddingService as { embedBatchWithProgress: (texts: string[], mode: EmbeddingMode, onProgress?: EmbeddingProgressCallback) => Promise<number[][]> })\n .embedBatchWithProgress(texts, this.options.embeddingMode, this.onProgress);\n } else {\n embeddings = await this.embeddingService.embedBatch(texts, this.options.embeddingMode);\n }\n\n // Store each embedding\n for (let i = 0; i < embeddings.length; i++) {\n try {\n this.vectorStore.add(entityNames[i], embeddings[i]);\n succeeded++;\n } catch (error) {\n failed++;\n errors.push({\n entityName: entityNames[i],\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n } catch (error) {\n // Batch embedding failed, count all as failed\n failed += embedOps.length;\n for (const op of embedOps) {\n errors.push({\n entityName: op.entityName,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n }\n } finally {\n this.isFlushing = false;\n }\n\n return {\n processed: operations.length,\n succeeded,\n failed,\n errors,\n durationMs: Date.now() - startTime,\n };\n }\n\n /**\n * Get the current queue size.\n *\n * @returns Number of operations in the queue\n */\n getQueueSize(): number {\n return this.queue.length;\n }\n\n /**\n * Get the current queue contents (for debugging/monitoring).\n *\n * @returns Copy of the current queue\n */\n getQueue(): IndexOperation[] {\n return [...this.queue];\n }\n\n /**\n * Check if the indexer is currently flushing.\n *\n * @returns True if flushing\n */\n isBusy(): boolean {\n return this.isFlushing;\n }\n\n /**\n * Check if the indexer is shutdown.\n *\n * @returns True if shutdown\n */\n isShutdownComplete(): boolean {\n return this.isShutdown;\n }\n\n /**\n * Graceful shutdown with final flush.\n *\n * Stops the timer and flushes any remaining operations.\n *\n * @returns Result of the final flush\n */\n async shutdown(): Promise<FlushResult> {\n this.isShutdown = true;\n this.stopFlushTimer();\n\n // Wait for any in-progress flush to complete\n while (this.isFlushing) {\n await new Promise(resolve => setTimeout(resolve, 10));\n }\n\n // Final flush\n return this.flush();\n }\n\n /**\n * Update indexer options.\n *\n * Note: Changes to flushIntervalMs will take effect on the next interval.\n *\n * @param options - New options to apply\n */\n updateOptions(options: Partial<IncrementalIndexerOptions>): void {\n const { onProgress, ...rest } = options;\n\n if (onProgress !== undefined) {\n this.onProgress = onProgress;\n }\n\n if (Object.keys(rest).length > 0) {\n this.options = { ...this.options, ...rest } as Required<Omit<IncrementalIndexerOptions, 'onProgress'>>;\n\n // Restart timer if interval changed\n if (rest.flushIntervalMs !== undefined) {\n this.stopFlushTimer();\n this.startFlushTimer();\n }\n }\n }\n\n /**\n * Clear the queue without processing.\n *\n * @returns Number of operations cleared\n */\n clearQueue(): number {\n const count = this.queue.length;\n this.queue = [];\n return count;\n }\n}\n","/**\r\n * Vector Store\r\n *\r\n * Phase 4 Sprint 11: Vector storage and retrieval for semantic search.\r\n * Provides in-memory and SQLite-backed implementations.\r\n *\r\n * @module search/VectorStore\r\n */\r\n\r\nimport type { IVectorStore, VectorSearchResult } from '../types/index.js';\r\n\r\n/**\r\n * Calculate cosine similarity between two vectors.\r\n *\r\n * Uses an optimized inner loop without array methods for maximum performance.\r\n *\r\n * @param a - First vector\r\n * @param b - Second vector\r\n * @returns Cosine similarity score (0.0 to 1.0)\r\n */\r\nexport function cosineSimilarity(a: number[], b: number[]): number {\r\n if (a.length !== b.length) {\r\n throw new Error(`Vector dimensions mismatch: ${a.length} vs ${b.length}`);\r\n }\r\n\r\n let dotProduct = 0;\r\n let magnitudeA = 0;\r\n let magnitudeB = 0;\r\n\r\n // Optimized single-pass loop\r\n for (let i = 0; i < a.length; i++) {\r\n const ai = a[i];\r\n const bi = b[i];\r\n dotProduct += ai * bi;\r\n magnitudeA += ai * ai;\r\n magnitudeB += bi * bi;\r\n }\r\n\r\n const magnitude = Math.sqrt(magnitudeA) * Math.sqrt(magnitudeB);\r\n\r\n if (magnitude === 0) {\r\n return 0;\r\n }\r\n\r\n // Clamp to [-1, 1] to handle floating point errors\r\n const similarity = dotProduct / magnitude;\r\n return Math.max(-1, Math.min(1, similarity));\r\n}\r\n\r\n/**\r\n * In-Memory Vector Store\r\n *\r\n * Stores vectors in memory using a Map for O(1) add/remove operations.\r\n * Search uses brute-force cosine similarity which is O(n) but fast for\r\n * small to medium graphs (<10K entities).\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = new InMemoryVectorStore();\r\n * store.add(\"entity1\", [0.1, 0.2, 0.3]);\r\n * store.add(\"entity2\", [0.4, 0.5, 0.6]);\r\n * const results = store.search([0.1, 0.2, 0.3], 5);\r\n * console.log(results); // [{ name: \"entity1\", score: 1.0 }, ...]\r\n * ```\r\n */\r\nexport class InMemoryVectorStore implements IVectorStore {\r\n /** Map of entity name to embedding vector */\r\n private vectors: Map<string, number[]> = new Map();\r\n\r\n /**\r\n * Add a vector for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param vector - Embedding vector\r\n */\r\n add(entityName: string, vector: number[]): void {\r\n this.vectors.set(entityName, vector);\r\n }\r\n\r\n /**\r\n * Search for similar vectors using cosine similarity.\r\n *\r\n * @param queryVector - Query embedding vector\r\n * @param k - Number of results to return\r\n * @returns Array of results with entity name and similarity score\r\n */\r\n search(queryVector: number[], k: number): VectorSearchResult[] {\r\n if (this.vectors.size === 0) {\r\n return [];\r\n }\r\n\r\n // Calculate similarity for all vectors\r\n const results: VectorSearchResult[] = [];\r\n\r\n for (const [name, vector] of this.vectors) {\r\n try {\r\n const score = cosineSimilarity(queryVector, vector);\r\n results.push({ name, score });\r\n } catch {\r\n // Skip vectors with dimension mismatch\r\n continue;\r\n }\r\n }\r\n\r\n // Sort by score descending and take top k\r\n results.sort((a, b) => b.score - a.score);\r\n return results.slice(0, k);\r\n }\r\n\r\n /**\r\n * Remove a vector by entity name.\r\n *\r\n * @param entityName - Name of the entity to remove\r\n * @returns True if found and removed\r\n */\r\n remove(entityName: string): boolean {\r\n return this.vectors.delete(entityName);\r\n }\r\n\r\n /**\r\n * Get the number of vectors stored.\r\n *\r\n * @returns Number of vectors\r\n */\r\n size(): number {\r\n return this.vectors.size;\r\n }\r\n\r\n /**\r\n * Clear all vectors from the store.\r\n */\r\n clear(): void {\r\n this.vectors.clear();\r\n }\r\n\r\n /**\r\n * Check if a vector exists for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns True if vector exists\r\n */\r\n has(entityName: string): boolean {\r\n return this.vectors.has(entityName);\r\n }\r\n\r\n /**\r\n * Get the vector for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns Vector if found, undefined otherwise\r\n */\r\n get(entityName: string): number[] | undefined {\r\n return this.vectors.get(entityName);\r\n }\r\n\r\n /**\r\n * Get all entity names with stored vectors.\r\n *\r\n * @returns Array of entity names\r\n */\r\n getEntityNames(): string[] {\r\n return Array.from(this.vectors.keys());\r\n }\r\n\r\n /**\r\n * Load vectors from an iterable source.\r\n *\r\n * @param entries - Iterable of [entityName, vector] pairs\r\n */\r\n loadFrom(entries: Iterable<[string, number[]]>): void {\r\n for (const [name, vector] of entries) {\r\n this.vectors.set(name, vector);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * SQLite Vector Store\r\n *\r\n * Persists vectors to SQLite storage while maintaining an in-memory cache\r\n * for fast search operations. Combines persistence with performance.\r\n *\r\n * Uses SQLiteStorage's embedding storage methods for persistence.\r\n *\r\n * @example\r\n * ```typescript\r\n * const store = new SQLiteVectorStore(sqliteStorage);\r\n * await store.initialize();\r\n * store.add(\"entity1\", [0.1, 0.2, 0.3]);\r\n * const results = store.search([0.1, 0.2, 0.3], 5);\r\n * ```\r\n */\r\nexport class SQLiteVectorStore implements IVectorStore {\r\n /** In-memory cache for fast search */\r\n private memoryStore: InMemoryVectorStore = new InMemoryVectorStore();\r\n\r\n /** SQLite storage reference for persistence */\r\n private storage: SQLiteStorageWithEmbeddings | null = null;\r\n\r\n /** Whether the store has been initialized */\r\n private initialized = false;\r\n\r\n /** Model name used for embeddings */\r\n private embeddingModel: string = '';\r\n\r\n /**\r\n * Create a SQLite vector store.\r\n *\r\n * @param storage - SQLite storage instance with embedding support\r\n * @param embeddingModel - Model name used for embeddings\r\n */\r\n constructor(storage?: SQLiteStorageWithEmbeddings, embeddingModel: string = 'unknown') {\r\n this.storage = storage || null;\r\n this.embeddingModel = embeddingModel;\r\n }\r\n\r\n /**\r\n * Initialize the store by loading vectors from SQLite.\r\n */\r\n async initialize(): Promise<void> {\r\n if (this.initialized) return;\r\n\r\n if (this.storage) {\r\n // Load all embeddings from SQLite\r\n const embeddings = await this.storage.loadAllEmbeddings();\r\n this.memoryStore.loadFrom(embeddings);\r\n }\r\n\r\n this.initialized = true;\r\n }\r\n\r\n /**\r\n * Add a vector for an entity.\r\n * Stores in both memory and SQLite for persistence.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param vector - Embedding vector\r\n */\r\n add(entityName: string, vector: number[]): void {\r\n // Add to in-memory cache\r\n this.memoryStore.add(entityName, vector);\r\n\r\n // Persist to SQLite if available\r\n if (this.storage) {\r\n this.storage.storeEmbedding(entityName, vector, this.embeddingModel);\r\n }\r\n }\r\n\r\n /**\r\n * Search for similar vectors using cosine similarity.\r\n *\r\n * @param queryVector - Query embedding vector\r\n * @param k - Number of results to return\r\n * @returns Array of results with entity name and similarity score\r\n */\r\n search(queryVector: number[], k: number): VectorSearchResult[] {\r\n return this.memoryStore.search(queryVector, k);\r\n }\r\n\r\n /**\r\n * Remove a vector by entity name.\r\n *\r\n * @param entityName - Name of the entity to remove\r\n * @returns True if found and removed\r\n */\r\n remove(entityName: string): boolean {\r\n const removed = this.memoryStore.remove(entityName);\r\n\r\n // Remove from SQLite if available\r\n if (this.storage && removed) {\r\n this.storage.removeEmbedding(entityName);\r\n }\r\n\r\n return removed;\r\n }\r\n\r\n /**\r\n * Get the number of vectors stored.\r\n *\r\n * @returns Number of vectors\r\n */\r\n size(): number {\r\n return this.memoryStore.size();\r\n }\r\n\r\n /**\r\n * Clear all vectors from the store.\r\n */\r\n clear(): void {\r\n this.memoryStore.clear();\r\n\r\n if (this.storage) {\r\n this.storage.clearAllEmbeddings();\r\n }\r\n }\r\n\r\n /**\r\n * Check if a vector exists for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns True if vector exists\r\n */\r\n has(entityName: string): boolean {\r\n return this.memoryStore.has(entityName);\r\n }\r\n\r\n /**\r\n * Get the vector for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @returns Vector if found, undefined otherwise\r\n */\r\n get(entityName: string): number[] | undefined {\r\n return this.memoryStore.get(entityName);\r\n }\r\n\r\n /**\r\n * Set the SQLite storage reference.\r\n *\r\n * @param storage - SQLite storage instance\r\n */\r\n setStorage(storage: SQLiteStorageWithEmbeddings): void {\r\n this.storage = storage;\r\n }\r\n\r\n /**\r\n * Set the embedding model name.\r\n *\r\n * @param model - Model name\r\n */\r\n setEmbeddingModel(model: string): void {\r\n this.embeddingModel = model;\r\n }\r\n}\r\n\r\n/**\r\n * Interface for SQLite storage with embedding support.\r\n *\r\n * This is a subset of SQLiteStorage that only includes embedding-related methods.\r\n * Allows for loose coupling between VectorStore and SQLiteStorage.\r\n */\r\nexport interface SQLiteStorageWithEmbeddings {\r\n /**\r\n * Store an embedding for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n * @param vector - Embedding vector\r\n * @param model - Model name used for the embedding\r\n */\r\n storeEmbedding(entityName: string, vector: number[], model: string): void;\r\n\r\n /**\r\n * Load all embeddings from storage.\r\n *\r\n * @returns Array of [entityName, vector] pairs\r\n */\r\n loadAllEmbeddings(): Promise<[string, number[]][]>;\r\n\r\n /**\r\n * Remove an embedding for an entity.\r\n *\r\n * @param entityName - Name of the entity\r\n */\r\n removeEmbedding(entityName: string): void;\r\n\r\n /**\r\n * Clear all embeddings from storage.\r\n */\r\n clearAllEmbeddings(): void;\r\n}\r\n\r\n/**\r\n * Create a vector store based on storage type.\r\n *\r\n * @param storageType - Storage type: 'jsonl' or 'sqlite'\r\n * @param storage - Optional SQLite storage reference for 'sqlite' type\r\n * @param embeddingModel - Optional model name for embedding tracking\r\n * @returns Vector store instance\r\n */\r\nexport function createVectorStore(\r\n storageType: 'jsonl' | 'sqlite' = 'jsonl',\r\n storage?: SQLiteStorageWithEmbeddings,\r\n embeddingModel?: string\r\n): IVectorStore {\r\n switch (storageType) {\r\n case 'sqlite':\r\n return new SQLiteVectorStore(storage, embeddingModel);\r\n case 'jsonl':\r\n default:\r\n return new InMemoryVectorStore();\r\n }\r\n}\r\n","/**\r\n * Semantic Search Manager\r\n *\r\n * Phase 4 Sprint 12: Orchestrates embedding service and vector store\r\n * to provide semantic similarity search capabilities.\r\n *\r\n * @module search/SemanticSearch\r\n */\r\n\r\nimport type {\r\n Entity,\r\n EmbeddingService,\r\n IVectorStore,\r\n SemanticSearchResult,\r\n SemanticIndexOptions,\r\n ReadonlyKnowledgeGraph,\r\n} from '../types/index.js';\r\nimport { InMemoryVectorStore } from './VectorStore.js';\r\nimport { EMBEDDING_DEFAULTS, SEMANTIC_SEARCH_LIMITS } from '../utils/constants.js';\r\nimport { checkCancellation } from '../utils/index.js';\r\n\r\n/**\r\n * Convert an entity to a text representation for embedding.\r\n *\r\n * Creates a structured text that captures the entity's key information\r\n * for generating meaningful embeddings.\r\n *\r\n * @param entity - Entity to convert\r\n * @returns Text representation suitable for embedding\r\n */\r\nexport function entityToText(entity: Entity): string {\r\n const parts: string[] = [];\r\n\r\n // Name and type are most important\r\n parts.push(`${entity.name} (${entity.entityType})`);\r\n\r\n // Add observations (limited to prevent overly long text)\r\n if (entity.observations.length > 0) {\r\n const observationText = entity.observations.slice(0, 10).join('. ');\r\n parts.push(observationText);\r\n }\r\n\r\n // Add tags if present\r\n if (entity.tags && entity.tags.length > 0) {\r\n parts.push(`Tags: ${entity.tags.join(', ')}`);\r\n }\r\n\r\n return parts.join('\\n');\r\n}\r\n\r\n/**\r\n * Semantic Search Manager\r\n *\r\n * Provides semantic similarity search by converting entities to embeddings\r\n * and storing them in a vector store. Supports search by query text and\r\n * finding similar entities.\r\n *\r\n * @example\r\n * ```typescript\r\n * const semanticSearch = new SemanticSearch(embeddingService, vectorStore);\r\n * await semanticSearch.indexAll(graph);\r\n * const results = await semanticSearch.search(graph, \"machine learning\");\r\n * ```\r\n */\r\nexport class SemanticSearch {\r\n /** Embedding service for generating vectors */\r\n private embeddingService: EmbeddingService;\r\n\r\n /** Vector store for storing and searching embeddings */\r\n private vectorStore: IVectorStore;\r\n\r\n /** Whether embeddings have been indexed */\r\n private indexed = false;\r\n\r\n /** Number of entities currently indexed */\r\n private indexedCount = 0;\r\n\r\n /**\r\n * Create a semantic search manager.\r\n *\r\n * @param embeddingService - Service for generating embeddings\r\n * @param vectorStore - Store for vector storage and search\r\n */\r\n constructor(embeddingService: EmbeddingService, vectorStore?: IVectorStore) {\r\n this.embeddingService = embeddingService;\r\n this.vectorStore = vectorStore || new InMemoryVectorStore();\r\n }\r\n\r\n /**\r\n * Index all entities in the knowledge graph.\r\n *\r\n * Generates embeddings for all entities and stores them in the vector store.\r\n * Can be called incrementally - only indexes entities that aren't already indexed.\r\n *\r\n * Phase 9B: Supports cancellation via AbortSignal in options.\r\n *\r\n * @param graph - Knowledge graph to index\r\n * @param options - Indexing options (includes signal for cancellation)\r\n * @returns Index statistics\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n */\r\n async indexAll(\r\n graph: ReadonlyKnowledgeGraph,\r\n options: SemanticIndexOptions = {}\r\n ): Promise<{ indexed: number; skipped: number; errors: number }> {\r\n const {\r\n forceReindex = false,\r\n onProgress,\r\n batchSize = EMBEDDING_DEFAULTS.DEFAULT_BATCH_SIZE,\r\n signal,\r\n } = options;\r\n\r\n // Check for early cancellation\r\n checkCancellation(signal, 'indexAll');\r\n\r\n let indexed = 0;\r\n let skipped = 0;\r\n let errors = 0;\r\n\r\n const entities = graph.entities;\r\n const total = entities.length;\r\n\r\n // Collect entities to index\r\n const toIndex: Entity[] = [];\r\n for (const entity of entities) {\r\n if (forceReindex || !this.vectorStore.has(entity.name)) {\r\n toIndex.push(entity);\r\n } else {\r\n skipped++;\r\n }\r\n }\r\n\r\n // Process in batches\r\n for (let i = 0; i < toIndex.length; i += batchSize) {\r\n // Check for cancellation between batches\r\n checkCancellation(signal, 'indexAll');\r\n\r\n const batch = toIndex.slice(i, i + batchSize);\r\n const texts = batch.map(entityToText);\r\n\r\n try {\r\n const embeddings = await this.embeddingService.embedBatch(texts);\r\n\r\n for (let j = 0; j < batch.length; j++) {\r\n this.vectorStore.add(batch[j].name, embeddings[j]);\r\n indexed++;\r\n }\r\n } catch (error) {\r\n // Try individual embeddings on batch failure\r\n for (const entity of batch) {\r\n // Check for cancellation during fallback\r\n checkCancellation(signal, 'indexAll');\r\n\r\n try {\r\n const text = entityToText(entity);\r\n const embedding = await this.embeddingService.embed(text);\r\n this.vectorStore.add(entity.name, embedding);\r\n indexed++;\r\n } catch {\r\n errors++;\r\n }\r\n }\r\n }\r\n\r\n // Report progress\r\n if (onProgress) {\r\n onProgress(indexed + skipped + errors, total);\r\n }\r\n }\r\n\r\n this.indexed = true;\r\n this.indexedCount = this.vectorStore.size();\r\n\r\n return { indexed, skipped, errors };\r\n }\r\n\r\n /**\r\n * Index a single entity.\r\n *\r\n * @param entity - Entity to index\r\n * @returns True if indexed successfully\r\n */\r\n async indexEntity(entity: Entity): Promise<boolean> {\r\n try {\r\n const text = entityToText(entity);\r\n const embedding = await this.embeddingService.embed(text);\r\n this.vectorStore.add(entity.name, embedding);\r\n this.indexedCount = this.vectorStore.size();\r\n return true;\r\n } catch {\r\n return false;\r\n }\r\n }\r\n\r\n /**\r\n * Remove an entity from the index.\r\n *\r\n * @param entityName - Name of entity to remove\r\n * @returns True if found and removed\r\n */\r\n removeEntity(entityName: string): boolean {\r\n const removed = this.vectorStore.remove(entityName);\r\n if (removed) {\r\n this.indexedCount = this.vectorStore.size();\r\n }\r\n return removed;\r\n }\r\n\r\n /**\r\n * Search for entities semantically similar to a query.\r\n *\r\n * @param graph - Knowledge graph to search in\r\n * @param query - Search query text\r\n * @param limit - Maximum number of results (default: 10)\r\n * @param minSimilarity - Minimum similarity threshold (default: 0)\r\n * @returns Array of search results with similarity scores\r\n */\r\n async search(\r\n graph: ReadonlyKnowledgeGraph,\r\n query: string,\r\n limit: number = SEMANTIC_SEARCH_LIMITS.DEFAULT_LIMIT,\r\n minSimilarity: number = SEMANTIC_SEARCH_LIMITS.MIN_SIMILARITY\r\n ): Promise<SemanticSearchResult[]> {\r\n // Ensure limit is within bounds\r\n const effectiveLimit = Math.min(limit, SEMANTIC_SEARCH_LIMITS.MAX_LIMIT);\r\n\r\n // Generate embedding for query\r\n const queryEmbedding = await this.embeddingService.embed(query);\r\n\r\n // Search vector store\r\n const vectorResults = this.vectorStore.search(queryEmbedding, effectiveLimit * 2); // Get extra for filtering\r\n\r\n // Convert to SemanticSearchResult with entity lookup\r\n const entityMap = new Map<string, Entity>();\r\n for (const entity of graph.entities) {\r\n entityMap.set(entity.name, entity);\r\n }\r\n\r\n const results: SemanticSearchResult[] = [];\r\n for (const result of vectorResults) {\r\n if (result.score < minSimilarity) {\r\n continue;\r\n }\r\n\r\n const entity = entityMap.get(result.name);\r\n if (entity) {\r\n results.push({\r\n entity,\r\n similarity: result.score,\r\n });\r\n }\r\n\r\n if (results.length >= effectiveLimit) {\r\n break;\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Find entities similar to a given entity.\r\n *\r\n * @param graph - Knowledge graph to search in\r\n * @param entityName - Name of entity to find similar entities for\r\n * @param limit - Maximum number of results (default: 10)\r\n * @param minSimilarity - Minimum similarity threshold (default: 0)\r\n * @returns Array of search results with similarity scores\r\n */\r\n async findSimilar(\r\n graph: ReadonlyKnowledgeGraph,\r\n entityName: string,\r\n limit: number = SEMANTIC_SEARCH_LIMITS.DEFAULT_LIMIT,\r\n minSimilarity: number = SEMANTIC_SEARCH_LIMITS.MIN_SIMILARITY\r\n ): Promise<SemanticSearchResult[]> {\r\n // Get the entity's embedding\r\n const embedding = this.vectorStore.get(entityName);\r\n if (!embedding) {\r\n // Try to find and index the entity\r\n const entity = graph.entities.find(e => e.name === entityName);\r\n if (entity) {\r\n await this.indexEntity(entity);\r\n return this.findSimilar(graph, entityName, limit, minSimilarity);\r\n }\r\n return [];\r\n }\r\n\r\n // Ensure limit is within bounds\r\n const effectiveLimit = Math.min(limit, SEMANTIC_SEARCH_LIMITS.MAX_LIMIT);\r\n\r\n // Search vector store (request extra to filter out self)\r\n const vectorResults = this.vectorStore.search(embedding, effectiveLimit + 1);\r\n\r\n // Convert to SemanticSearchResult with entity lookup\r\n const entityMap = new Map<string, Entity>();\r\n for (const entity of graph.entities) {\r\n entityMap.set(entity.name, entity);\r\n }\r\n\r\n const results: SemanticSearchResult[] = [];\r\n for (const result of vectorResults) {\r\n // Skip self\r\n if (result.name === entityName) {\r\n continue;\r\n }\r\n\r\n if (result.score < minSimilarity) {\r\n continue;\r\n }\r\n\r\n const entity = entityMap.get(result.name);\r\n if (entity) {\r\n results.push({\r\n entity,\r\n similarity: result.score,\r\n });\r\n }\r\n\r\n if (results.length >= effectiveLimit) {\r\n break;\r\n }\r\n }\r\n\r\n return results;\r\n }\r\n\r\n /**\r\n * Get the embedding service.\r\n *\r\n * @returns Embedding service instance\r\n */\r\n getEmbeddingService(): EmbeddingService {\r\n return this.embeddingService;\r\n }\r\n\r\n /**\r\n * Get the vector store.\r\n *\r\n * @returns Vector store instance\r\n */\r\n getVectorStore(): IVectorStore {\r\n return this.vectorStore;\r\n }\r\n\r\n /**\r\n * Check if the index has been built.\r\n *\r\n * @returns True if indexAll has been called\r\n */\r\n isIndexed(): boolean {\r\n return this.indexed;\r\n }\r\n\r\n /**\r\n * Get the number of indexed entities.\r\n *\r\n * @returns Number of entities in the vector store\r\n */\r\n getIndexedCount(): number {\r\n return this.indexedCount;\r\n }\r\n\r\n /**\r\n * Clear all indexed embeddings.\r\n */\r\n clearIndex(): void {\r\n this.vectorStore.clear();\r\n this.indexed = false;\r\n this.indexedCount = 0;\r\n }\r\n\r\n /**\r\n * Check if semantic search is available.\r\n *\r\n * @returns True if embedding service is ready\r\n */\r\n async isAvailable(): Promise<boolean> {\r\n return this.embeddingService.isReady();\r\n }\r\n\r\n /**\r\n * Get semantic search statistics.\r\n *\r\n * @returns Statistics about the semantic search index\r\n */\r\n getStats(): {\r\n indexed: boolean;\r\n indexedCount: number;\r\n provider: string;\r\n model: string;\r\n dimensions: number;\r\n } {\r\n return {\r\n indexed: this.indexed,\r\n indexedCount: this.indexedCount,\r\n provider: this.embeddingService.provider,\r\n model: this.embeddingService.model,\r\n dimensions: this.embeddingService.dimensions,\r\n };\r\n }\r\n}\r\n","/**\r\n * TF-IDF Event Sync\r\n *\r\n * Phase 10 Sprint 3: Hooks TFIDFIndexManager to graph events for automatic\r\n * incremental index updates when entities change.\r\n *\r\n * @module search/TFIDFEventSync\r\n */\r\n\r\nimport type { GraphEventEmitter } from '../core/GraphEventEmitter.js';\r\nimport type { TFIDFIndexManager } from './TFIDFIndexManager.js';\r\nimport type { IGraphStorage } from '../types/index.js';\r\nimport type {\r\n EntityCreatedEvent,\r\n EntityUpdatedEvent,\r\n EntityDeletedEvent,\r\n} from '../types/types.js';\r\n\r\n/**\r\n * Phase 10 Sprint 3: Synchronizes TF-IDF index with graph changes via events.\r\n *\r\n * Listens to graph events and triggers incremental index updates automatically.\r\n * More efficient than rebuilding the entire index on every change.\r\n *\r\n * @example\r\n * ```typescript\r\n * const storage = new GraphStorage('/data/memory.jsonl');\r\n * const indexManager = new TFIDFIndexManager('/data');\r\n *\r\n * // Load or build index\r\n * await indexManager.loadIndex();\r\n *\r\n * // Enable automatic sync\r\n * const sync = new TFIDFEventSync(indexManager, storage.events, storage);\r\n * sync.enable();\r\n *\r\n * // Now entities added to storage will automatically update the index\r\n * await storage.appendEntity({ name: 'New', entityType: 'test', observations: [] });\r\n *\r\n * // Disable when done\r\n * sync.disable();\r\n * ```\r\n */\r\nexport class TFIDFEventSync {\r\n private indexManager: TFIDFIndexManager;\r\n private eventEmitter: GraphEventEmitter;\r\n private storage: IGraphStorage;\r\n private unsubscribers: Array<() => void> = [];\r\n private enabled: boolean = false;\r\n\r\n /**\r\n * Create a new TFIDFEventSync instance.\r\n *\r\n * @param indexManager - TFIDFIndexManager to sync\r\n * @param eventEmitter - GraphEventEmitter to listen to\r\n * @param storage - Storage to fetch entity data from (for updates)\r\n */\r\n constructor(\r\n indexManager: TFIDFIndexManager,\r\n eventEmitter: GraphEventEmitter,\r\n storage: IGraphStorage\r\n ) {\r\n this.indexManager = indexManager;\r\n this.eventEmitter = eventEmitter;\r\n this.storage = storage;\r\n }\r\n\r\n /**\r\n * Enable automatic index synchronization.\r\n *\r\n * Subscribes to entity:created, entity:updated, and entity:deleted events.\r\n */\r\n enable(): void {\r\n if (this.enabled) {\r\n return;\r\n }\r\n\r\n // Subscribe to entity events\r\n this.unsubscribers.push(\r\n this.eventEmitter.on('entity:created', this.handleEntityCreated.bind(this))\r\n );\r\n\r\n this.unsubscribers.push(\r\n this.eventEmitter.on('entity:updated', this.handleEntityUpdated.bind(this))\r\n );\r\n\r\n this.unsubscribers.push(\r\n this.eventEmitter.on('entity:deleted', this.handleEntityDeleted.bind(this))\r\n );\r\n\r\n this.enabled = true;\r\n }\r\n\r\n /**\r\n * Disable automatic index synchronization.\r\n *\r\n * Unsubscribes from all events.\r\n */\r\n disable(): void {\r\n if (!this.enabled) {\r\n return;\r\n }\r\n\r\n for (const unsubscribe of this.unsubscribers) {\r\n unsubscribe();\r\n }\r\n this.unsubscribers = [];\r\n this.enabled = false;\r\n }\r\n\r\n /**\r\n * Check if synchronization is enabled.\r\n *\r\n * @returns True if enabled\r\n */\r\n isEnabled(): boolean {\r\n return this.enabled;\r\n }\r\n\r\n /**\r\n * Handle entity:created event.\r\n * @private\r\n */\r\n private handleEntityCreated(event: EntityCreatedEvent): void {\r\n if (!this.indexManager.isInitialized()) {\r\n return;\r\n }\r\n\r\n this.indexManager.addDocument({\r\n name: event.entity.name,\r\n entityType: event.entity.entityType,\r\n observations: event.entity.observations,\r\n });\r\n }\r\n\r\n /**\r\n * Handle entity:updated event.\r\n * @private\r\n */\r\n private async handleEntityUpdated(event: EntityUpdatedEvent): Promise<void> {\r\n if (!this.indexManager.isInitialized()) {\r\n return;\r\n }\r\n\r\n // Fetch the current entity state\r\n const graph = await this.storage.loadGraph();\r\n const entity = graph.entities.find(e => e.name === event.entityName);\r\n\r\n if (entity) {\r\n this.indexManager.updateDocument({\r\n name: entity.name,\r\n entityType: entity.entityType,\r\n observations: entity.observations,\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * Handle entity:deleted event.\r\n * @private\r\n */\r\n private handleEntityDeleted(event: EntityDeletedEvent): void {\r\n if (!this.indexManager.isInitialized()) {\r\n return;\r\n }\r\n\r\n this.indexManager.removeDocument(event.entityName);\r\n }\r\n}\r\n","/**\n * Symbolic Search Layer\n *\n * Phase 11: Provides metadata-based filtering using structured predicates.\n * Part of the three-layer hybrid search architecture.\n *\n * @module search/SymbolicSearch\n */\n\nimport type { Entity, SymbolicFilters } from '../types/index.js';\n\n/**\n * Result from symbolic search with match score.\n */\nexport interface SymbolicResult {\n entity: Entity;\n score: number;\n matchedFilters: string[];\n}\n\n/**\n * Symbolic Search provides metadata-based filtering.\n *\n * Filters entities using structured predicates on tags, types,\n * dates, importance, and hierarchy.\n *\n * @example\n * ```typescript\n * const symbolic = new SymbolicSearch();\n * const results = symbolic.search(entities, {\n * tags: ['important'],\n * entityTypes: ['person'],\n * importance: { min: 5 }\n * });\n * ```\n */\nexport class SymbolicSearch {\n /**\n * Filter entities using structured metadata predicates.\n * All filters are AND-combined.\n *\n * @param entities - Entities to filter\n * @param filters - Symbolic filter criteria\n * @returns Filtered entities with match scores\n */\n search(entities: readonly Entity[], filters: SymbolicFilters): SymbolicResult[] {\n const results: SymbolicResult[] = [];\n\n for (const entity of entities) {\n const { matches, score, matchedFilters } = this.evaluateFilters(entity, filters);\n if (matches) {\n results.push({ entity, score, matchedFilters });\n }\n }\n\n // Sort by score descending\n return results.sort((a, b) => b.score - a.score);\n }\n\n /**\n * Evaluate all filters against an entity.\n */\n private evaluateFilters(\n entity: Entity,\n filters: SymbolicFilters\n ): { matches: boolean; score: number; matchedFilters: string[] } {\n const matchedFilters: string[] = [];\n let totalFilters = 0;\n let matchedCount = 0;\n\n // Tag filter\n if (filters.tags && filters.tags.length > 0) {\n totalFilters++;\n const entityTags = entity.tags ?? [];\n const matchingTags = filters.tags.filter((t: string) =>\n entityTags.some((et: string) => et.toLowerCase() === t.toLowerCase())\n );\n if (matchingTags.length > 0) {\n matchedCount++;\n matchedFilters.push(`tags:${matchingTags.join(',')}`);\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // Entity type filter\n if (filters.entityTypes && filters.entityTypes.length > 0) {\n totalFilters++;\n if (filters.entityTypes.some((t: string) =>\n t.toLowerCase() === entity.entityType.toLowerCase()\n )) {\n matchedCount++;\n matchedFilters.push(`type:${entity.entityType}`);\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // Date range filter\n if (filters.dateRange) {\n totalFilters++;\n const entityDate = entity.createdAt || entity.lastModified;\n if (!entityDate) {\n // Entities without dates are excluded when date filter is applied\n return { matches: false, score: 0, matchedFilters: [] };\n }\n const date = new Date(entityDate);\n const start = filters.dateRange.start ? new Date(filters.dateRange.start) : null;\n const end = filters.dateRange.end ? new Date(filters.dateRange.end) : null;\n\n const inRange = (!start || date >= start) && (!end || date <= end);\n if (inRange) {\n matchedCount++;\n matchedFilters.push('dateRange');\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // Importance filter\n if (filters.importance) {\n totalFilters++;\n const importance = entity.importance ?? 5;\n const { min, max } = filters.importance;\n const inRange = (min === undefined || importance >= min) &&\n (max === undefined || importance <= max);\n if (inRange) {\n matchedCount++;\n matchedFilters.push(`importance:${importance}`);\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // Parent filter\n if (filters.parentId !== undefined) {\n totalFilters++;\n if (entity.parentId === filters.parentId) {\n matchedCount++;\n matchedFilters.push(`parent:${filters.parentId}`);\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // Has observations filter\n if (filters.hasObservations !== undefined) {\n totalFilters++;\n const hasObs = entity.observations.length > 0;\n if (hasObs === filters.hasObservations) {\n matchedCount++;\n matchedFilters.push('hasObservations');\n } else {\n return { matches: false, score: 0, matchedFilters: [] };\n }\n }\n\n // If no filters specified, match all with base score\n if (totalFilters === 0) {\n return { matches: true, score: 0.5, matchedFilters: [] };\n }\n\n // Score based on proportion of filters matched\n const score = matchedCount / totalFilters;\n return { matches: true, score, matchedFilters };\n }\n\n /**\n * Get entities matching a specific tag.\n */\n byTag(entities: readonly Entity[], tag: string): Entity[] {\n return entities.filter(e =>\n e.tags?.some(t => t.toLowerCase() === tag.toLowerCase())\n );\n }\n\n /**\n * Get entities of a specific type.\n */\n byType(entities: readonly Entity[], entityType: string): Entity[] {\n return entities.filter(e =>\n e.entityType.toLowerCase() === entityType.toLowerCase()\n );\n }\n\n /**\n * Get entities within importance range.\n */\n byImportance(entities: readonly Entity[], min: number, max: number): Entity[] {\n return entities.filter(e => {\n const imp = e.importance ?? 5;\n return imp >= min && imp <= max;\n });\n }\n}\n","/**\n * Hybrid Search Manager\n *\n * Phase 11: Orchestrates three-layer hybrid search combining\n * semantic, lexical, and symbolic signals.\n *\n * @module search/HybridSearchManager\n */\n\nimport type {\n Entity,\n HybridSearchOptions,\n HybridSearchResult,\n ReadonlyKnowledgeGraph,\n SymbolicFilters,\n} from '../types/index.js';\nimport type { SemanticSearch } from './SemanticSearch.js';\nimport type { RankedSearch } from './RankedSearch.js';\nimport { SymbolicSearch } from './SymbolicSearch.js';\nimport { SEMANTIC_SEARCH_LIMITS } from '../utils/constants.js';\n\n/**\n * Default weights for hybrid search layers.\n */\nexport const DEFAULT_HYBRID_WEIGHTS = {\n semantic: 0.5,\n lexical: 0.3,\n symbolic: 0.2,\n};\n\n/**\n * Hybrid Search Manager\n *\n * Combines three search layers:\n * 1. Semantic: Vector similarity via embeddings\n * 2. Lexical: Keyword matching via TF-IDF/BM25\n * 3. Symbolic: Structured metadata filtering\n *\n * @example\n * ```typescript\n * const hybrid = new HybridSearchManager(semanticSearch, rankedSearch);\n * const results = await hybrid.search(graph, 'machine learning', {\n * semanticWeight: 0.5,\n * lexicalWeight: 0.3,\n * symbolicWeight: 0.2,\n * symbolic: { tags: ['ai'] }\n * });\n * ```\n */\nexport class HybridSearchManager {\n private symbolicSearch: SymbolicSearch;\n\n constructor(\n private semanticSearch: SemanticSearch | null,\n private rankedSearch: RankedSearch\n ) {\n this.symbolicSearch = new SymbolicSearch();\n }\n\n /**\n * Perform hybrid search combining all three layers.\n *\n * @param graph - Knowledge graph to search\n * @param query - Search query text\n * @param options - Hybrid search options with weights\n * @returns Combined and ranked results\n */\n async search(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: Partial<HybridSearchOptions> = {}\n ): Promise<HybridSearchResult[]> {\n const {\n semanticWeight = DEFAULT_HYBRID_WEIGHTS.semantic,\n lexicalWeight = DEFAULT_HYBRID_WEIGHTS.lexical,\n symbolicWeight = DEFAULT_HYBRID_WEIGHTS.symbolic,\n semantic = {},\n lexical = {},\n symbolic = {},\n limit = SEMANTIC_SEARCH_LIMITS.DEFAULT_LIMIT,\n } = options;\n\n // Normalize weights\n const totalWeight = semanticWeight + lexicalWeight + symbolicWeight;\n const normSemantic = semanticWeight / totalWeight;\n const normLexical = lexicalWeight / totalWeight;\n const normSymbolic = symbolicWeight / totalWeight;\n\n // Execute searches in parallel\n const [semanticResults, lexicalResults, symbolicResults] = await Promise.all([\n this.executeSemanticSearch(graph, query, semantic, limit * 2),\n this.executeLexicalSearch(query, lexical, limit * 2),\n this.executeSymbolicSearch(graph.entities, symbolic),\n ]);\n\n // Merge results\n const merged = this.mergeResults(\n graph.entities,\n semanticResults,\n lexicalResults,\n symbolicResults,\n { semantic: normSemantic, lexical: normLexical, symbolic: normSymbolic }\n );\n\n // Sort by combined score and limit\n return merged\n .sort((a, b) => b.scores.combined - a.scores.combined)\n .slice(0, limit);\n }\n\n /**\n * Execute semantic search layer.\n */\n private async executeSemanticSearch(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: { minSimilarity?: number; topK?: number },\n limit: number\n ): Promise<Map<string, number>> {\n const results = new Map<string, number>();\n\n if (!this.semanticSearch) {\n return results; // Semantic search not available\n }\n\n try {\n const semanticResults = await this.semanticSearch.search(\n graph,\n query,\n options.topK ?? limit,\n options.minSimilarity ?? 0\n );\n\n for (const result of semanticResults) {\n results.set(result.entity.name, result.similarity);\n }\n } catch {\n // Semantic search may fail if not indexed\n }\n\n return results;\n }\n\n /**\n * Execute lexical search layer (TF-IDF/BM25).\n */\n private async executeLexicalSearch(\n query: string,\n _options: { useStopwords?: boolean; useStemming?: boolean },\n limit: number\n ): Promise<Map<string, number>> {\n const results = new Map<string, number>();\n\n try {\n const lexicalResults = await this.rankedSearch.searchNodesRanked(\n query,\n undefined, // tags\n undefined, // minImportance\n undefined, // maxImportance\n limit\n );\n\n // Normalize scores to 0-1 range\n const maxScore = Math.max(...lexicalResults.map(r => r.score), 1);\n for (const result of lexicalResults) {\n results.set(result.entity.name, result.score / maxScore);\n }\n } catch {\n // Lexical search may fail\n }\n\n return results;\n }\n\n /**\n * Execute symbolic search layer.\n */\n private executeSymbolicSearch(\n entities: readonly Entity[],\n filters: SymbolicFilters | undefined\n ): Map<string, number> {\n const results = new Map<string, number>();\n\n if (!filters || Object.keys(filters).length === 0) {\n // No symbolic filters, give all entities base score\n for (const entity of entities) {\n results.set(entity.name, 0.5);\n }\n return results;\n }\n\n const symbolicResults = this.symbolicSearch.search(entities, filters);\n for (const result of symbolicResults) {\n results.set(result.entity.name, result.score);\n }\n\n return results;\n }\n\n /**\n * Merge results from all three layers.\n */\n private mergeResults(\n entities: readonly Entity[],\n semanticScores: Map<string, number>,\n lexicalScores: Map<string, number>,\n symbolicScores: Map<string, number>,\n weights: { semantic: number; lexical: number; symbolic: number }\n ): HybridSearchResult[] {\n // Collect all unique entity names that have at least one non-zero score\n const allNames = new Set([\n ...semanticScores.keys(),\n ...lexicalScores.keys(),\n ...symbolicScores.keys(),\n ]);\n\n // Create entity lookup map\n const entityMap = new Map(entities.map(e => [e.name, e]));\n\n const results: HybridSearchResult[] = [];\n\n for (const name of allNames) {\n const entity = entityMap.get(name);\n if (!entity) continue;\n\n const semantic = semanticScores.get(name) ?? 0;\n const lexical = lexicalScores.get(name) ?? 0;\n const symbolic = symbolicScores.get(name) ?? 0;\n\n const combined =\n semantic * weights.semantic +\n lexical * weights.lexical +\n symbolic * weights.symbolic;\n\n const matchedLayers: ('semantic' | 'lexical' | 'symbolic')[] = [];\n if (semantic > 0) matchedLayers.push('semantic');\n if (lexical > 0) matchedLayers.push('lexical');\n if (symbolic > 0) matchedLayers.push('symbolic');\n\n // Skip if no layers matched meaningfully\n if (matchedLayers.length === 0) continue;\n\n results.push({\n entity,\n scores: { semantic, lexical, symbolic, combined },\n matchedLayers,\n });\n }\n\n return results;\n }\n\n /**\n * Search with full entity resolution.\n * Alias for search() since we now always resolve entities.\n */\n async searchWithEntities(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: Partial<HybridSearchOptions> = {}\n ): Promise<HybridSearchResult[]> {\n return this.search(graph, query, options);\n }\n\n /**\n * Get the symbolic search instance for direct access.\n */\n getSymbolicSearch(): SymbolicSearch {\n return this.symbolicSearch;\n }\n}\n","/**\n * Query Analyzer\n *\n * Phase 11: Extracts structured information from natural language queries\n * to enable intelligent search planning.\n *\n * @module search/QueryAnalyzer\n */\n\nimport type { QueryAnalysis, ExtractedEntity, TemporalRange } from '../types/index.js';\n\n/**\n * Query Analyzer extracts structured information from queries.\n *\n * Uses rule-based heuristics for reliable extraction of:\n * - Person names\n * - Location names\n * - Organization names\n * - Temporal references\n * - Question type\n * - Query complexity\n *\n * @example\n * ```typescript\n * const analyzer = new QueryAnalyzer();\n * const analysis = analyzer.analyze(\n * 'What projects did Alice work on last month?'\n * );\n * // { query: '...', entities: [...], persons: ['Alice'], temporalRange: { relative: 'last month' }, ... }\n * ```\n */\nexport class QueryAnalyzer {\n private personIndicators = ['Mr.', 'Mrs.', 'Ms.', 'Dr.', 'Prof.'];\n private temporalKeywords = [\n 'yesterday', 'today', 'tomorrow',\n 'last week', 'last month', 'last year',\n 'this week', 'this month', 'this year',\n 'next week', 'next month', 'next year',\n ];\n private questionKeywords = {\n factual: ['what', 'who', 'where', 'which'],\n temporal: ['when', 'how long', 'since', 'until'],\n comparative: ['compare', 'difference', 'vs', 'versus', 'better', 'worse'],\n aggregation: ['how many', 'count', 'total', 'sum', 'average'],\n 'multi-hop': ['and then', 'which means', 'therefore', 'related to'],\n conceptual: ['explain', 'why', 'how does', 'what is the meaning', 'understand'],\n };\n\n /**\n * Analyze a query using rule-based heuristics.\n * Main entry point - returns full QueryAnalysis.\n */\n analyze(query: string): QueryAnalysis {\n const lowerQuery = query.toLowerCase();\n const persons = this.extractPersons(query);\n const locations = this.extractLocations(query);\n const organizations = this.extractOrganizations(query);\n const questionType = this.detectQuestionType(lowerQuery);\n const complexity = this.estimateComplexity(query);\n\n // Build entities array from extracted names\n const entities: ExtractedEntity[] = [\n ...persons.map(name => ({ name, type: 'person' as const })),\n ...locations.map(name => ({ name, type: 'location' as const })),\n ...organizations.map(name => ({ name, type: 'organization' as const })),\n ];\n\n // Calculate confidence based on extraction quality\n const confidence = this.calculateConfidence(entities, complexity, questionType);\n\n return {\n query,\n entities,\n persons,\n locations,\n organizations,\n temporalRange: this.extractTemporalRange(query) ?? null,\n questionType,\n complexity,\n confidence,\n requiredInfoTypes: this.detectRequiredInfoTypes(lowerQuery),\n subQueries: this.decomposeQuery(query),\n };\n }\n\n /**\n * Calculate confidence score for the analysis.\n */\n private calculateConfidence(\n entities: ExtractedEntity[],\n complexity: 'low' | 'medium' | 'high',\n questionType: QueryAnalysis['questionType']\n ): number {\n let confidence = 0.5;\n\n // Higher confidence for simple queries\n if (complexity === 'low') confidence += 0.3;\n else if (complexity === 'medium') confidence += 0.1;\n\n // Higher confidence when entities are detected\n if (entities.length > 0) confidence += 0.1;\n\n // Lower confidence for conceptual queries (harder to satisfy)\n if (questionType === 'conceptual') confidence -= 0.2;\n\n return Math.max(0, Math.min(1, confidence));\n }\n\n /**\n * Extract person names from query.\n */\n private extractPersons(query: string): string[] {\n const persons: string[] = [];\n const words = query.split(/\\s+/);\n\n for (let i = 0; i < words.length; i++) {\n const word = words[i];\n // Check for titles followed by names\n if (this.personIndicators.some(ind => word.startsWith(ind))) {\n if (i + 1 < words.length) {\n persons.push(words[i + 1].replace(/[^a-zA-Z]/g, ''));\n }\n }\n // Check for capitalized words that might be names\n if (/^[A-Z][a-z]+$/.test(word) && i > 0 && !/^[A-Z]/.test(words[i - 1])) {\n persons.push(word);\n }\n }\n\n return [...new Set(persons)];\n }\n\n /**\n * Extract location names from query.\n */\n private extractLocations(query: string): string[] {\n const locationIndicators = ['in', 'at', 'from', 'to', 'near'];\n const locations: string[] = [];\n const words = query.split(/\\s+/);\n\n for (let i = 0; i < words.length; i++) {\n if (locationIndicators.includes(words[i].toLowerCase())) {\n if (i + 1 < words.length && /^[A-Z]/.test(words[i + 1])) {\n locations.push(words[i + 1].replace(/[^a-zA-Z]/g, ''));\n }\n }\n }\n\n return [...new Set(locations)];\n }\n\n /**\n * Extract organization names from query.\n */\n private extractOrganizations(query: string): string[] {\n const orgIndicators = ['Inc.', 'Corp.', 'LLC', 'Ltd.', 'Company', 'Co.'];\n const organizations: string[] = [];\n\n for (const indicator of orgIndicators) {\n const regex = new RegExp(`([A-Z][a-zA-Z]*)\\\\s*${indicator.replace('.', '\\\\.')}`, 'g');\n const matches = query.match(regex);\n if (matches) {\n organizations.push(...matches);\n }\n }\n\n return [...new Set(organizations)];\n }\n\n /**\n * Extract temporal range from query.\n */\n private extractTemporalRange(query: string): TemporalRange | undefined {\n const lowerQuery = query.toLowerCase();\n\n for (const keyword of this.temporalKeywords) {\n if (lowerQuery.includes(keyword)) {\n return { relative: keyword };\n }\n }\n\n // Check for date patterns\n const datePattern = /\\d{4}-\\d{2}-\\d{2}/g;\n const dates = query.match(datePattern);\n if (dates && dates.length >= 1) {\n return {\n start: dates[0],\n end: dates.length > 1 ? dates[1] : undefined,\n };\n }\n\n return undefined;\n }\n\n /**\n * Detect the type of question.\n */\n private detectQuestionType(query: string): QueryAnalysis['questionType'] {\n for (const [type, keywords] of Object.entries(this.questionKeywords)) {\n if (keywords.some(kw => query.includes(kw))) {\n return type as QueryAnalysis['questionType'];\n }\n }\n return 'factual';\n }\n\n /**\n * Estimate query complexity.\n */\n private estimateComplexity(query: string): QueryAnalysis['complexity'] {\n const wordCount = query.split(/\\s+/).length;\n const hasConjunctions = /\\b(and|or|but|then|therefore)\\b/i.test(query);\n const hasMultipleClauses = /[,;]/.test(query);\n\n if (wordCount > 20 || (hasConjunctions && hasMultipleClauses)) {\n return 'high';\n }\n if (wordCount > 10 || hasConjunctions || hasMultipleClauses) {\n return 'medium';\n }\n return 'low';\n }\n\n /**\n * Detect what types of information are being requested.\n */\n private detectRequiredInfoTypes(query: string): string[] {\n const infoTypes: string[] = [];\n\n if (/\\b(who|person|people|name)\\b/.test(query)) infoTypes.push('person');\n if (/\\b(where|location|place|city)\\b/.test(query)) infoTypes.push('location');\n if (/\\b(when|date|time|year|month)\\b/.test(query)) infoTypes.push('temporal');\n if (/\\b(how many|count|number|total)\\b/.test(query)) infoTypes.push('quantity');\n if (/\\b(why|reason|because)\\b/.test(query)) infoTypes.push('reason');\n if (/\\b(what|which|project|task)\\b/.test(query)) infoTypes.push('entity');\n\n return infoTypes;\n }\n\n /**\n * Decompose complex queries into sub-queries.\n */\n private decomposeQuery(query: string): string[] | undefined {\n // Split on conjunctions\n const parts = query.split(/\\b(and then|and|but|or)\\b/i)\n .map(p => p.trim())\n .filter(p => p && !/^(and then|and|but|or)$/i.test(p));\n\n if (parts.length > 1) {\n return parts;\n }\n\n return undefined;\n }\n}\n","/**\n * Query Planner\n *\n * Phase 11: Generates execution plans for queries based on analysis.\n *\n * @module search/QueryPlanner\n */\n\nimport type { QueryAnalysis, QueryPlan, SubQuery, SymbolicFilters } from '../types/index.js';\n\n/**\n * Query Planner generates execution plans from query analysis.\n *\n * Creates optimized plans that:\n * - Select appropriate search layers\n * - Determine execution strategy (parallel/sequential)\n * - Set up query dependencies\n * - Configure merge strategies\n *\n * @example\n * ```typescript\n * const analyzer = new QueryAnalyzer();\n * const planner = new QueryPlanner();\n *\n * const analysis = analyzer.analyze('Find projects by Alice');\n * const plan = planner.createPlan('Find projects by Alice', analysis);\n * // { originalQuery: '...', subQueries: [...], executionStrategy: 'iterative', ... }\n * ```\n */\nexport class QueryPlanner {\n /**\n * Create an execution plan from query analysis.\n */\n createPlan(query: string, analysis: QueryAnalysis): QueryPlan {\n const subQueries = this.createSubQueries(query, analysis);\n const executionStrategy = this.selectExecutionStrategy(subQueries);\n const mergeStrategy = this.selectMergeStrategy(analysis);\n\n return {\n originalQuery: query,\n subQueries,\n executionStrategy,\n mergeStrategy,\n estimatedComplexity: this.calculateComplexity(subQueries),\n };\n }\n\n /**\n * Create sub-queries from analysis.\n */\n private createSubQueries(query: string, analysis: QueryAnalysis): SubQuery[] {\n const subQueries: SubQuery[] = [];\n let id = 0;\n\n // If query was decomposed, create sub-query for each part\n if (analysis.subQueries && analysis.subQueries.length > 1) {\n for (const sq of analysis.subQueries) {\n subQueries.push({\n id: `sq_${id++}`,\n query: sq,\n targetLayer: this.selectLayer(analysis),\n priority: id === 1 ? 1 : 2,\n dependsOn: id > 1 ? [`sq_${id - 2}`] : undefined,\n });\n }\n } else {\n // Single query\n subQueries.push({\n id: `sq_${id}`,\n query,\n targetLayer: this.selectLayer(analysis),\n priority: 1,\n filters: this.buildFilters(analysis),\n });\n }\n\n return subQueries;\n }\n\n /**\n * Select the most appropriate search layer.\n */\n private selectLayer(analysis: QueryAnalysis): SubQuery['targetLayer'] {\n // Use symbolic for tag/type/date filtered queries\n if (analysis.temporalRange || analysis.requiredInfoTypes.includes('temporal')) {\n return 'symbolic';\n }\n // Use semantic for complex concept queries\n if (analysis.complexity === 'high' || analysis.questionType === 'comparative') {\n return 'semantic';\n }\n // Use hybrid for balanced approach\n return 'hybrid';\n }\n\n /**\n * Select execution strategy based on sub-queries.\n */\n private selectExecutionStrategy(subQueries: SubQuery[]): QueryPlan['executionStrategy'] {\n const hasDependencies = subQueries.some(sq => sq.dependsOn && sq.dependsOn.length > 0);\n if (hasDependencies) return 'sequential';\n if (subQueries.length > 1) return 'parallel';\n return 'iterative';\n }\n\n /**\n * Select merge strategy based on question type.\n */\n private selectMergeStrategy(analysis: QueryAnalysis): QueryPlan['mergeStrategy'] {\n switch (analysis.questionType) {\n case 'aggregation': return 'union';\n case 'comparative': return 'intersection';\n default: return 'weighted';\n }\n }\n\n /**\n * Build symbolic filters from analysis.\n */\n private buildFilters(analysis: QueryAnalysis): SymbolicFilters | undefined {\n const filters: SymbolicFilters = {};\n let hasFilters = false;\n\n if (analysis.temporalRange) {\n filters.dateRange = {\n start: analysis.temporalRange.start || '',\n end: analysis.temporalRange.end || '',\n };\n hasFilters = true;\n }\n\n return hasFilters ? filters : undefined;\n }\n\n /**\n * Calculate plan complexity score.\n */\n private calculateComplexity(subQueries: SubQuery[]): number {\n let complexity = subQueries.length;\n for (const sq of subQueries) {\n if (sq.dependsOn) complexity += sq.dependsOn.length * 0.5;\n if (sq.filters) complexity += 0.5;\n }\n return Math.min(complexity, 10);\n }\n}\n","/**\n * Reflection Manager\n *\n * Phase 11: Implements reflection-based iterative retrieval\n * that refines results until adequate.\n *\n * @module search/ReflectionManager\n */\n\nimport type {\n ReadonlyKnowledgeGraph,\n QueryAnalysis,\n HybridSearchResult,\n HybridSearchOptions,\n} from '../types/index.js';\nimport type { HybridSearchManager } from './HybridSearchManager.js';\nimport type { QueryAnalyzer } from './QueryAnalyzer.js';\n\n/**\n * Options for reflection-based retrieval.\n */\nexport interface ReflectionOptions {\n /** Maximum number of reflection iterations (default: 3) */\n maxIterations?: number;\n /** Adequacy threshold 0-1 (default: 0.7) */\n adequacyThreshold?: number;\n /** Minimum results required (default: 3) */\n minResults?: number;\n /** Hybrid search options */\n searchOptions?: Partial<HybridSearchOptions>;\n /** Phase 12 Sprint 4: Progressive limit increase factor (default: 1.5) */\n limitIncreaseFactor?: number;\n /** Phase 12 Sprint 4: Initial search limit (default: 10) */\n initialLimit?: number;\n /** Phase 12 Sprint 4: Focus on specific missing info types */\n focusMissingTypes?: boolean;\n}\n\n/**\n * Phase 12 Sprint 4: Refinement history entry.\n */\nexport interface RefinementHistoryEntry {\n /** Iteration number (1-based) */\n iteration: number;\n /** Query used in this iteration */\n query: string;\n /** Search limit used */\n limit: number;\n /** Results found in this iteration */\n resultsFound: number;\n /** Adequacy score after this iteration */\n adequacyScore: number;\n /** Reason for refinement (if not final) */\n refinementReason?: string;\n /** Missing info types that triggered refinement */\n missingInfoTypes?: string[];\n}\n\n/**\n * Result of reflection-based retrieval.\n */\nexport interface ReflectionResult {\n results: HybridSearchResult[];\n iterations: number;\n adequate: boolean;\n refinements: string[];\n adequacyScore: number;\n /** Phase 12 Sprint 4: Detailed refinement history */\n refinementHistory: RefinementHistoryEntry[];\n /** Phase 12 Sprint 4: Final search limit used */\n finalLimit: number;\n}\n\n/**\n * Reflection Manager for iterative retrieval refinement.\n *\n * Implements the SimpleMem-inspired reflection loop:\n * 1. Execute initial search\n * 2. Check result adequacy\n * 3. If inadequate, refine query and repeat\n * 4. Combine results from all iterations\n *\n * @example\n * ```typescript\n * const reflection = new ReflectionManager(hybridSearch, analyzer);\n * const result = await reflection.retrieveWithReflection(\n * graph,\n * 'What projects did Alice work on?',\n * { maxIterations: 3 }\n * );\n * ```\n */\nexport class ReflectionManager {\n constructor(\n private hybridSearch: HybridSearchManager,\n private analyzer: QueryAnalyzer\n ) {}\n\n /**\n * Perform retrieval with reflection-based refinement.\n *\n * Phase 12 Sprint 4 enhancements:\n * - Progressive limit increase per round\n * - Focused query refinement based on missing information\n * - Detailed refinement history tracking\n */\n async retrieveWithReflection(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: ReflectionOptions = {}\n ): Promise<ReflectionResult> {\n const {\n maxIterations = 3,\n adequacyThreshold = 0.7,\n minResults = 3,\n searchOptions = {},\n limitIncreaseFactor = 1.5,\n initialLimit = 10,\n focusMissingTypes = true,\n } = options;\n\n const allResults: HybridSearchResult[] = [];\n const refinements: string[] = [];\n const refinementHistory: RefinementHistoryEntry[] = [];\n let currentQuery = query;\n let iteration = 0;\n let adequacyScore = 0;\n let currentLimit = initialLimit;\n\n // Get initial analysis for tracking\n const analysis = this.analyzer.analyze(query);\n\n while (iteration < maxIterations) {\n iteration++;\n\n // Calculate current limit with progressive increase\n currentLimit = Math.round(initialLimit * Math.pow(limitIncreaseFactor, iteration - 1));\n\n // Execute search with current limit\n const results = await this.hybridSearch.searchWithEntities(\n graph,\n currentQuery,\n { ...searchOptions, limit: currentLimit }\n );\n\n // Track results found this iteration (before deduplication)\n const newResultsCount = results.filter(\n r => !allResults.some(existing => existing.entity.name === r.entity.name)\n ).length;\n\n // Add new results (deduplicated)\n for (const result of results) {\n if (!allResults.some(r => r.entity.name === result.entity.name)) {\n allResults.push(result);\n }\n }\n\n // Check adequacy\n const iterAnalysis = this.analyzer.analyze(currentQuery);\n adequacyScore = this.calculateAdequacy(allResults, iterAnalysis, minResults);\n\n // Determine missing info types\n const missingInfoTypes = this.findMissingInfoTypes(allResults, analysis);\n\n // Record history entry\n const historyEntry: RefinementHistoryEntry = {\n iteration,\n query: currentQuery,\n limit: currentLimit,\n resultsFound: newResultsCount,\n adequacyScore,\n };\n\n if (adequacyScore >= adequacyThreshold) {\n refinementHistory.push(historyEntry);\n break;\n }\n\n // Refine query if not adequate\n const refinedQuery = focusMissingTypes && missingInfoTypes.length > 0\n ? this.refineQueryFocused(currentQuery, allResults, analysis, missingInfoTypes)\n : this.refineQuery(currentQuery, allResults, analysis);\n\n if (refinedQuery === currentQuery) {\n // No refinement possible\n historyEntry.refinementReason = 'No further refinement possible';\n refinementHistory.push(historyEntry);\n break;\n }\n\n // Record refinement details in history\n historyEntry.refinementReason = this.getRefinementReason(\n currentQuery,\n refinedQuery,\n missingInfoTypes\n );\n historyEntry.missingInfoTypes = missingInfoTypes;\n refinementHistory.push(historyEntry);\n\n refinements.push(refinedQuery);\n currentQuery = refinedQuery;\n }\n\n return {\n results: allResults.sort((a, b) => b.scores.combined - a.scores.combined),\n iterations: iteration,\n adequate: adequacyScore >= adequacyThreshold,\n refinements,\n adequacyScore,\n refinementHistory,\n finalLimit: currentLimit,\n };\n }\n\n /**\n * Find missing information types based on analysis requirements.\n * @private\n */\n private findMissingInfoTypes(\n results: HybridSearchResult[],\n analysis: QueryAnalysis\n ): string[] {\n const coveredTypes = new Set<string>();\n\n for (const result of results) {\n const entityType = result.entity.entityType.toLowerCase();\n coveredTypes.add(entityType);\n\n // Map entity types to info types\n if (['person', 'people', 'user', 'employee'].includes(entityType)) {\n coveredTypes.add('person');\n }\n if (['location', 'place', 'city', 'country', 'address'].includes(entityType)) {\n coveredTypes.add('location');\n }\n if (result.entity.createdAt || result.entity.lastModified) {\n coveredTypes.add('temporal');\n }\n // Entity type covers 'entity' requirement\n coveredTypes.add('entity');\n }\n\n // Find which required types are missing\n return analysis.requiredInfoTypes.filter(type => !coveredTypes.has(type));\n }\n\n /**\n * Refine query with focus on missing information types.\n * @private\n */\n private refineQueryFocused(\n query: string,\n results: HybridSearchResult[],\n analysis: QueryAnalysis,\n missingInfoTypes: string[]\n ): string {\n const additions: string[] = [];\n\n for (const missingType of missingInfoTypes) {\n switch (missingType) {\n case 'person':\n additions.push('person people who');\n break;\n case 'location':\n additions.push('location place where');\n break;\n case 'temporal':\n additions.push('when date time');\n break;\n case 'quantity':\n additions.push('count number how many');\n break;\n case 'reason':\n additions.push('reason why because');\n break;\n }\n }\n\n if (additions.length > 0) {\n // Add relevant keywords to query\n return `${query} ${additions.slice(0, 2).join(' ')}`;\n }\n\n // Fallback to standard refinement\n return this.refineQuery(query, results, analysis);\n }\n\n /**\n * Get human-readable reason for refinement.\n * @private\n */\n private getRefinementReason(\n _originalQuery: string,\n refinedQuery: string,\n missingInfoTypes: string[]\n ): string {\n if (missingInfoTypes.length > 0) {\n return `Added keywords for missing info types: ${missingInfoTypes.join(', ')}`;\n }\n\n if (refinedQuery.includes('person people')) {\n return 'Expanded query to find person-related entities';\n }\n\n if (refinedQuery.includes('recent history timeline')) {\n return 'Added temporal context to query';\n }\n\n return 'Broadened query by removing constraints';\n }\n\n /**\n * Calculate result adequacy score.\n */\n private calculateAdequacy(\n results: HybridSearchResult[],\n analysis: QueryAnalysis,\n minResults: number\n ): number {\n let score = 0;\n const weights = { quantity: 0.4, diversity: 0.3, relevance: 0.3 };\n\n // Quantity: Do we have enough results?\n const quantityScore = Math.min(results.length / minResults, 1);\n score += quantityScore * weights.quantity;\n\n // Diversity: Do results cover different entity types?\n const types = new Set(results.map(r => r.entity.entityType));\n const diversityScore = Math.min(types.size / 3, 1);\n score += diversityScore * weights.diversity;\n\n // Relevance: Average combined score\n const avgScore = results.length > 0\n ? results.reduce((sum, r) => sum + r.scores.combined, 0) / results.length\n : 0;\n score += avgScore * weights.relevance;\n\n // Suppress unused parameter warning - analysis reserved for future enhancements\n void analysis;\n\n return score;\n }\n\n /**\n * Refine query based on current results and analysis.\n */\n private refineQuery(\n query: string,\n results: HybridSearchResult[],\n analysis: QueryAnalysis\n ): string {\n // Check what information types are missing\n const coveredTypes = new Set<string>();\n for (const result of results) {\n coveredTypes.add(result.entity.entityType.toLowerCase());\n }\n\n // If looking for persons but no person results, refine\n if (analysis.requiredInfoTypes.includes('person') && !coveredTypes.has('person')) {\n return `${query} person people`;\n }\n\n // If temporal query but no temporal context, add time hint\n if (analysis.temporalRange && results.length < 3) {\n return `${query} recent history timeline`;\n }\n\n // Broaden query by removing constraints\n const broader = query.replace(/\\b(only|just|exactly|specific)\\b/gi, '');\n if (broader !== query) {\n return broader.trim();\n }\n\n return query;\n }\n}\n","/**\n * BM25 Search\n *\n * BM25 (Best Matching 25) relevance scoring algorithm for lexical search.\n * Provides improved ranking over TF-IDF by incorporating document length normalization.\n *\n * Phase 12 Sprint 3: Search Algorithm Optimization\n *\n * @module search/BM25Search\n */\n\nimport type { Entity, SearchResult } from '../types/index.js';\nimport type { GraphStorage } from '../core/GraphStorage.js';\nimport { SEARCH_LIMITS } from '../utils/constants.js';\n\n/**\n * Common English stopwords to filter from queries and documents.\n * These words are too common to provide meaningful ranking signal.\n */\nexport const STOPWORDS = new Set([\n 'a', 'an', 'and', 'are', 'as', 'at', 'be', 'by', 'for', 'from',\n 'has', 'he', 'in', 'is', 'it', 'its', 'of', 'on', 'or', 'that',\n 'the', 'to', 'was', 'were', 'will', 'with', 'you', 'your',\n 'this', 'but', 'they', 'have', 'had', 'what', 'when', 'where',\n 'who', 'which', 'why', 'how', 'all', 'each', 'every', 'both',\n 'few', 'more', 'most', 'other', 'some', 'such', 'no', 'not',\n 'only', 'own', 'same', 'so', 'than', 'too', 'very', 'can',\n 'just', 'should', 'now', 'also', 'being', 'been', 'would',\n 'could', 'into', 'over', 'after', 'before', 'between', 'under',\n 'again', 'then', 'once', 'here', 'there', 'any', 'about',\n]);\n\n/**\n * BM25 index entry for a single document.\n */\nexport interface BM25DocumentEntry {\n /** Entity name */\n entityName: string;\n /** Term frequencies in this document */\n termFreqs: Map<string, number>;\n /** Total number of tokens in document */\n docLength: number;\n}\n\n/**\n * BM25 index structure.\n */\nexport interface BM25Index {\n /** Document entries keyed by entity name */\n documents: Map<string, BM25DocumentEntry>;\n /** Document frequency for each term (number of docs containing term) */\n documentFrequency: Map<string, number>;\n /** Average document length */\n avgDocLength: number;\n /** Total number of documents */\n totalDocs: number;\n}\n\n/**\n * BM25 configuration parameters.\n */\nexport interface BM25Config {\n /** Term frequency saturation parameter (default: 1.2) */\n k1: number;\n /** Length normalization parameter (default: 0.75) */\n b: number;\n}\n\n/**\n * Default BM25 parameters based on research recommendations.\n */\nexport const DEFAULT_BM25_CONFIG: BM25Config = {\n k1: 1.2,\n b: 0.75,\n};\n\n/**\n * BM25 Search implementation.\n *\n * BM25 improves over TF-IDF by:\n * 1. Saturating term frequency - prevents long documents from dominating\n * 2. Document length normalization - accounts for varying document sizes\n *\n * Formula:\n * score(D,Q) = sum_i( IDF(qi) * (f(qi,D) * (k1 + 1)) / (f(qi,D) + k1 * (1 - b + b * |D|/avgdl)) )\n *\n * Where:\n * - f(qi,D) is the term frequency of qi in document D\n * - |D| is the length of document D\n * - avgdl is the average document length\n * - k1 and b are free parameters\n *\n * @example\n * ```typescript\n * const bm25 = new BM25Search(storage);\n * await bm25.buildIndex();\n * const results = await bm25.search('machine learning');\n * ```\n */\nexport class BM25Search {\n private index: BM25Index | null = null;\n private config: BM25Config;\n\n constructor(\n private storage: GraphStorage,\n config: Partial<BM25Config> = {}\n ) {\n this.config = { ...DEFAULT_BM25_CONFIG, ...config };\n }\n\n /**\n * Get the current configuration.\n */\n getConfig(): BM25Config {\n return { ...this.config };\n }\n\n /**\n * Update configuration parameters.\n *\n * @param config - New configuration values\n */\n setConfig(config: Partial<BM25Config>): void {\n this.config = { ...this.config, ...config };\n }\n\n /**\n * Tokenize text into lowercase terms with stopword filtering.\n *\n * @param text - Text to tokenize\n * @param filterStopwords - Whether to filter stopwords (default: true)\n * @returns Array of lowercase tokens\n */\n tokenize(text: string, filterStopwords: boolean = true): string[] {\n const tokens = text\n .toLowerCase()\n .replace(/[^\\w\\s]/g, ' ')\n .split(/\\s+/)\n .filter(token => token.length > 0);\n\n if (filterStopwords) {\n return tokens.filter(token => !STOPWORDS.has(token));\n }\n return tokens;\n }\n\n /**\n * Build the BM25 index from the current graph.\n *\n * Should be called after significant graph changes.\n */\n async buildIndex(): Promise<void> {\n const graph = await this.storage.loadGraph();\n const documents = new Map<string, BM25DocumentEntry>();\n const documentFrequency = new Map<string, number>();\n const termsSeen = new Set<string>();\n let totalDocLength = 0;\n\n // First pass: tokenize all documents and count term frequencies\n for (const entity of graph.entities) {\n const text = this.entityToText(entity);\n const tokens = this.tokenize(text);\n const termFreqs = new Map<string, number>();\n\n // Count term frequencies for this document\n for (const token of tokens) {\n termFreqs.set(token, (termFreqs.get(token) || 0) + 1);\n }\n\n // Track which terms appear in this document (for IDF calculation)\n termsSeen.clear();\n for (const token of tokens) {\n if (!termsSeen.has(token)) {\n termsSeen.add(token);\n documentFrequency.set(token, (documentFrequency.get(token) || 0) + 1);\n }\n }\n\n const entry: BM25DocumentEntry = {\n entityName: entity.name,\n termFreqs,\n docLength: tokens.length,\n };\n\n documents.set(entity.name, entry);\n totalDocLength += tokens.length;\n }\n\n const totalDocs = documents.size;\n const avgDocLength = totalDocs > 0 ? totalDocLength / totalDocs : 0;\n\n this.index = {\n documents,\n documentFrequency,\n avgDocLength,\n totalDocs,\n };\n }\n\n /**\n * Search using the BM25 algorithm.\n *\n * @param query - Search query\n * @param limit - Maximum results to return\n * @returns Array of search results sorted by BM25 score\n */\n async search(query: string, limit: number = SEARCH_LIMITS.DEFAULT): Promise<SearchResult[]> {\n const effectiveLimit = Math.min(limit, SEARCH_LIMITS.MAX);\n\n // Ensure index is built\n if (!this.index) {\n await this.buildIndex();\n }\n\n if (!this.index || this.index.documents.size === 0) {\n return [];\n }\n\n const graph = await this.storage.loadGraph();\n const entityMap = new Map(graph.entities.map(e => [e.name, e]));\n\n // Tokenize query\n const queryTerms = this.tokenize(query);\n if (queryTerms.length === 0) {\n return [];\n }\n\n const { k1, b } = this.config;\n const { documents, documentFrequency, avgDocLength, totalDocs } = this.index;\n const results: SearchResult[] = [];\n\n // Calculate BM25 score for each document\n for (const [entityName, docEntry] of documents) {\n const entity = entityMap.get(entityName);\n if (!entity) continue;\n\n let score = 0;\n const matchedFields: SearchResult['matchedFields'] = {};\n\n for (const term of queryTerms) {\n const tf = docEntry.termFreqs.get(term) || 0;\n if (tf === 0) continue;\n\n // Calculate IDF\n const df = documentFrequency.get(term) || 0;\n const idf = df > 0 ? Math.log((totalDocs - df + 0.5) / (df + 0.5) + 1) : 0;\n\n // Calculate BM25 score component\n const numerator = tf * (k1 + 1);\n const denominator = tf + k1 * (1 - b + b * (docEntry.docLength / avgDocLength));\n const termScore = idf * (numerator / denominator);\n\n score += termScore;\n\n // Track which fields matched\n if (entity.name.toLowerCase().includes(term)) {\n matchedFields.name = true;\n }\n if (entity.entityType.toLowerCase().includes(term)) {\n matchedFields.entityType = true;\n }\n const matchedObs = entity.observations.filter(o =>\n o.toLowerCase().includes(term)\n );\n if (matchedObs.length > 0) {\n matchedFields.observations = matchedObs;\n }\n }\n\n if (score > 0) {\n results.push({\n entity,\n score,\n matchedFields,\n });\n }\n }\n\n // Sort by score descending and limit\n return results\n .sort((a, b) => b.score - a.score)\n .slice(0, effectiveLimit);\n }\n\n /**\n * Update the index for changed entities.\n *\n * @param changedEntityNames - Names of entities that changed\n */\n async update(changedEntityNames: Set<string>): Promise<void> {\n if (!this.index) {\n await this.buildIndex();\n return;\n }\n\n const graph = await this.storage.loadGraph();\n const entityMap = new Map(graph.entities.map(e => [e.name, e]));\n\n // Process each changed entity\n for (const entityName of changedEntityNames) {\n const entity = entityMap.get(entityName);\n const existingEntry = this.index.documents.get(entityName);\n\n if (existingEntry) {\n // Remove old term frequencies from document frequency counts\n for (const [term] of existingEntry.termFreqs) {\n const df = this.index.documentFrequency.get(term) || 0;\n if (df <= 1) {\n this.index.documentFrequency.delete(term);\n } else {\n this.index.documentFrequency.set(term, df - 1);\n }\n }\n this.index.documents.delete(entityName);\n }\n\n if (entity) {\n // Add new entry\n const text = this.entityToText(entity);\n const tokens = this.tokenize(text);\n const termFreqs = new Map<string, number>();\n const termsSeen = new Set<string>();\n\n for (const token of tokens) {\n termFreqs.set(token, (termFreqs.get(token) || 0) + 1);\n if (!termsSeen.has(token)) {\n termsSeen.add(token);\n this.index.documentFrequency.set(\n token,\n (this.index.documentFrequency.get(token) || 0) + 1\n );\n }\n }\n\n const entry: BM25DocumentEntry = {\n entityName: entity.name,\n termFreqs,\n docLength: tokens.length,\n };\n\n this.index.documents.set(entityName, entry);\n }\n }\n\n // Recalculate average document length\n this.index.totalDocs = this.index.documents.size;\n let totalLength = 0;\n for (const doc of this.index.documents.values()) {\n totalLength += doc.docLength;\n }\n this.index.avgDocLength = this.index.totalDocs > 0\n ? totalLength / this.index.totalDocs\n : 0;\n }\n\n /**\n * Remove an entity from the index.\n *\n * @param entityName - Name of entity to remove\n */\n remove(entityName: string): boolean {\n if (!this.index) {\n return false;\n }\n\n const entry = this.index.documents.get(entityName);\n if (!entry) {\n return false;\n }\n\n // Update document frequency counts\n for (const [term] of entry.termFreqs) {\n const df = this.index.documentFrequency.get(term) || 0;\n if (df <= 1) {\n this.index.documentFrequency.delete(term);\n } else {\n this.index.documentFrequency.set(term, df - 1);\n }\n }\n\n this.index.documents.delete(entityName);\n\n // Update totals\n this.index.totalDocs = this.index.documents.size;\n let totalLength = 0;\n for (const doc of this.index.documents.values()) {\n totalLength += doc.docLength;\n }\n this.index.avgDocLength = this.index.totalDocs > 0\n ? totalLength / this.index.totalDocs\n : 0;\n\n return true;\n }\n\n /**\n * Clear the index.\n */\n clearIndex(): void {\n this.index = null;\n }\n\n /**\n * Check if the index is built.\n */\n isIndexed(): boolean {\n return this.index !== null;\n }\n\n /**\n * Get index statistics.\n */\n getIndexStats(): { documents: number; terms: number; avgDocLength: number } | null {\n if (!this.index) {\n return null;\n }\n return {\n documents: this.index.documents.size,\n terms: this.index.documentFrequency.size,\n avgDocLength: this.index.avgDocLength,\n };\n }\n\n /**\n * Convert an entity to searchable text.\n */\n private entityToText(entity: Entity): string {\n return [entity.name, entity.entityType, ...entity.observations].join(' ');\n }\n}\n","/**\n * Optimized Inverted Index\n *\n * Memory-efficient inverted index using integer IDs and Uint32Array\n * for fast multi-term intersection queries.\n *\n * Phase 12 Sprint 3: Search Algorithm Optimization\n *\n * @module search/OptimizedInvertedIndex\n */\n\n/**\n * Statistics about memory usage.\n */\nexport interface IndexMemoryUsage {\n /** Total bytes used by posting lists */\n postingListBytes: number;\n /** Total bytes used by ID map */\n idMapBytes: number;\n /** Total bytes used by term index */\n termIndexBytes: number;\n /** Total estimated memory usage in bytes */\n totalBytes: number;\n /** Number of unique terms */\n termCount: number;\n /** Number of documents indexed */\n documentCount: number;\n}\n\n/**\n * Result from a posting list lookup.\n */\nexport interface PostingListResult {\n /** Term that was looked up */\n term: string;\n /** Document IDs containing the term (sorted) */\n docIds: Uint32Array;\n}\n\n/**\n * Optimized Inverted Index using integer document IDs.\n *\n * Memory Optimizations:\n * 1. Uses integer IDs instead of string entity names\n * 2. Stores posting lists as Uint32Array (4 bytes per ID vs ~20+ bytes per string)\n * 3. Maintains sorted posting lists for efficient intersection\n *\n * Performance Optimizations:\n * 1. Sorted array intersection is O(n+m) where n,m are posting list lengths\n * 2. Early termination when one list is exhausted\n * 3. Binary search available for unbalanced list sizes\n *\n * @example\n * ```typescript\n * const index = new OptimizedInvertedIndex();\n * index.addDocument('entity1', ['machine', 'learning', 'ai']);\n * index.addDocument('entity2', ['deep', 'learning', 'neural']);\n *\n * // Find documents containing both 'machine' AND 'learning'\n * const results = index.intersect(['machine', 'learning']);\n * console.log(results); // ['entity1']\n * ```\n */\nexport class OptimizedInvertedIndex {\n /** Map from entity name to integer ID */\n private entityToId: Map<string, number> = new Map();\n\n /** Map from integer ID to entity name */\n private idToEntity: Map<number, string> = new Map();\n\n /** Next available ID */\n private nextId: number = 0;\n\n /** Inverted index: term -> sorted array of document IDs */\n private postingLists: Map<string, Uint32Array> = new Map();\n\n /** Temporary posting lists (before finalization) */\n private tempPostingLists: Map<string, number[]> = new Map();\n\n /** Whether the index is finalized (posting lists converted to Uint32Array) */\n private finalized: boolean = false;\n\n /**\n * Add a document to the index.\n *\n * @param entityName - Unique document identifier\n * @param terms - Array of terms in the document (should be lowercase)\n */\n addDocument(entityName: string, terms: string[]): void {\n // Unfinalize if already finalized (allows incremental updates)\n if (this.finalized) {\n this.unfinalize();\n }\n\n // Get or assign document ID\n let docId = this.entityToId.get(entityName);\n if (docId === undefined) {\n docId = this.nextId++;\n this.entityToId.set(entityName, docId);\n this.idToEntity.set(docId, entityName);\n }\n\n // Add unique terms to posting lists\n const seenTerms = new Set<string>();\n for (const term of terms) {\n if (seenTerms.has(term)) continue;\n seenTerms.add(term);\n\n let postingList = this.tempPostingLists.get(term);\n if (!postingList) {\n postingList = [];\n this.tempPostingLists.set(term, postingList);\n }\n\n // Only add if not already present (maintains sorted order if added in order)\n if (postingList.length === 0 || postingList[postingList.length - 1] !== docId) {\n postingList.push(docId);\n }\n }\n }\n\n /**\n * Remove a document from the index.\n *\n * @param entityName - Document to remove\n * @returns True if document was found and removed\n */\n removeDocument(entityName: string): boolean {\n const docId = this.entityToId.get(entityName);\n if (docId === undefined) {\n return false;\n }\n\n // Unfinalize if needed\n if (this.finalized) {\n this.unfinalize();\n }\n\n // Remove from all posting lists\n for (const [term, postingList] of this.tempPostingLists) {\n const idx = postingList.indexOf(docId);\n if (idx !== -1) {\n postingList.splice(idx, 1);\n if (postingList.length === 0) {\n this.tempPostingLists.delete(term);\n }\n }\n }\n\n // Remove ID mappings\n this.entityToId.delete(entityName);\n this.idToEntity.delete(docId);\n\n return true;\n }\n\n /**\n * Finalize the index by converting posting lists to Uint32Array.\n *\n * This should be called after bulk indexing for optimal memory usage.\n * The index can still be updated after finalization, but it will\n * temporarily use more memory during updates.\n */\n finalize(): void {\n if (this.finalized) return;\n\n // Convert temp posting lists to Uint32Array and sort\n this.postingLists.clear();\n for (const [term, list] of this.tempPostingLists) {\n // Sort and convert to Uint32Array\n list.sort((a, b) => a - b);\n const arr = new Uint32Array(list);\n this.postingLists.set(term, arr);\n }\n\n // Clear temp posting lists to save memory\n this.tempPostingLists.clear();\n this.finalized = true;\n }\n\n /**\n * Convert finalized index back to mutable format.\n */\n private unfinalize(): void {\n if (!this.finalized) return;\n\n // Convert Uint32Array back to regular arrays\n this.tempPostingLists.clear();\n for (const [term, arr] of this.postingLists) {\n this.tempPostingLists.set(term, Array.from(arr));\n }\n\n this.postingLists.clear();\n this.finalized = false;\n }\n\n /**\n * Get posting list for a term.\n *\n * @param term - Term to look up\n * @returns Posting list result or null if term not found\n */\n getPostingList(term: string): PostingListResult | null {\n if (this.finalized) {\n const arr = this.postingLists.get(term);\n if (!arr) return null;\n return { term, docIds: arr };\n } else {\n const list = this.tempPostingLists.get(term);\n if (!list) return null;\n // Sort and return as Uint32Array\n const sorted = list.slice().sort((a, b) => a - b);\n return { term, docIds: new Uint32Array(sorted) };\n }\n }\n\n /**\n * Perform intersection of posting lists for multiple terms.\n *\n * Returns entity names that contain ALL specified terms.\n *\n * @param terms - Array of terms to intersect\n * @returns Array of entity names containing all terms\n */\n intersect(terms: string[]): string[] {\n if (terms.length === 0) {\n return [];\n }\n\n // Ensure finalized for optimal performance\n if (!this.finalized) {\n this.finalize();\n }\n\n // Get posting lists for all terms\n const postingLists: Uint32Array[] = [];\n for (const term of terms) {\n const list = this.postingLists.get(term);\n if (!list || list.length === 0) {\n // If any term has no posting list, intersection is empty\n return [];\n }\n postingLists.push(list);\n }\n\n // Sort by length (smallest first for early termination)\n postingLists.sort((a, b) => a.length - b.length);\n\n // Perform multi-way sorted intersection\n let result = postingLists[0];\n for (let i = 1; i < postingLists.length; i++) {\n result = this.intersectTwo(result, postingLists[i]);\n if (result.length === 0) {\n return [];\n }\n }\n\n // Convert IDs back to entity names\n return Array.from(result).map(id => this.idToEntity.get(id)!);\n }\n\n /**\n * Perform union of posting lists for multiple terms.\n *\n * Returns entity names that contain ANY of the specified terms.\n *\n * @param terms - Array of terms to union\n * @returns Array of entity names containing any term\n */\n union(terms: string[]): string[] {\n if (terms.length === 0) {\n return [];\n }\n\n // Ensure finalized for optimal performance\n if (!this.finalized) {\n this.finalize();\n }\n\n // Collect all unique document IDs\n const allIds = new Set<number>();\n for (const term of terms) {\n const list = this.postingLists.get(term);\n if (list) {\n for (const id of list) {\n allIds.add(id);\n }\n }\n }\n\n // Convert IDs back to entity names\n return Array.from(allIds).map(id => this.idToEntity.get(id)!);\n }\n\n /**\n * Get entities containing a single term.\n *\n * @param term - Term to search for\n * @returns Array of entity names containing the term\n */\n search(term: string): string[] {\n if (!this.finalized) {\n const list = this.tempPostingLists.get(term);\n if (!list) return [];\n return list.map(id => this.idToEntity.get(id)!);\n }\n\n const list = this.postingLists.get(term);\n if (!list) return [];\n return Array.from(list).map(id => this.idToEntity.get(id)!);\n }\n\n /**\n * Intersect two sorted Uint32Arrays.\n *\n * Uses merge-style intersection which is O(n+m).\n */\n private intersectTwo(a: Uint32Array, b: Uint32Array): Uint32Array {\n const result: number[] = [];\n let i = 0;\n let j = 0;\n\n while (i < a.length && j < b.length) {\n if (a[i] === b[j]) {\n result.push(a[i]);\n i++;\n j++;\n } else if (a[i] < b[j]) {\n i++;\n } else {\n j++;\n }\n }\n\n return new Uint32Array(result);\n }\n\n /**\n * Get memory usage statistics.\n */\n getMemoryUsage(): IndexMemoryUsage {\n let postingListBytes = 0;\n let termCount = 0;\n\n if (this.finalized) {\n for (const arr of this.postingLists.values()) {\n // Uint32Array uses 4 bytes per element\n postingListBytes += arr.byteLength;\n termCount++;\n }\n } else {\n for (const list of this.tempPostingLists.values()) {\n // Regular array uses 8 bytes per element (64-bit numbers in V8)\n // Plus array overhead\n postingListBytes += list.length * 8 + 32; // Approximate overhead\n termCount++;\n }\n }\n\n // Estimate ID map overhead\n // Map has ~100 bytes overhead + ~50 bytes per entry for string keys\n const idMapBytes =\n 100 +\n this.entityToId.size * 50 +\n this.idToEntity.size * 8; // number key is ~8 bytes\n\n // Estimate term index overhead\n // ~50 bytes per term entry (key + pointer)\n const termIndexBytes = termCount * 50;\n\n return {\n postingListBytes,\n idMapBytes,\n termIndexBytes,\n totalBytes: postingListBytes + idMapBytes + termIndexBytes,\n termCount,\n documentCount: this.entityToId.size,\n };\n }\n\n /**\n * Clear the entire index.\n */\n clear(): void {\n this.entityToId.clear();\n this.idToEntity.clear();\n this.postingLists.clear();\n this.tempPostingLists.clear();\n this.nextId = 0;\n this.finalized = false;\n }\n\n /**\n * Get the number of documents in the index.\n */\n get documentCount(): number {\n return this.entityToId.size;\n }\n\n /**\n * Get the number of unique terms in the index.\n */\n get termCount(): number {\n return this.finalized\n ? this.postingLists.size\n : this.tempPostingLists.size;\n }\n\n /**\n * Check if an entity is indexed.\n */\n hasDocument(entityName: string): boolean {\n return this.entityToId.has(entityName);\n }\n\n /**\n * Check if a term exists in the index.\n */\n hasTerm(term: string): boolean {\n return this.finalized\n ? this.postingLists.has(term)\n : this.tempPostingLists.has(term);\n }\n}\n","/**\n * Hybrid Scorer\n *\n * Combines semantic, lexical, and symbolic search scores with\n * min-max normalization and configurable weights.\n *\n * Phase 12 Sprint 3: Search Algorithm Optimization\n *\n * @module search/HybridScorer\n */\n\nimport type { Entity } from '../types/index.js';\n\n/**\n * Result from semantic search layer.\n */\nexport interface SemanticLayerResult {\n /** Entity name */\n entityName: string;\n /** Similarity score (typically 0-1 for cosine similarity) */\n similarity: number;\n /** The matched entity (if resolved) */\n entity?: Entity;\n}\n\n/**\n * Result from lexical search layer (TF-IDF or BM25).\n */\nexport interface LexicalSearchResult {\n /** Entity name */\n entityName: string;\n /** Relevance score (unbounded, higher is better) */\n score: number;\n /** The matched entity (if resolved) */\n entity?: Entity;\n}\n\n/**\n * Result from symbolic search layer.\n */\nexport interface SymbolicSearchResult {\n /** Entity name */\n entityName: string;\n /** Match score (typically 0-1) */\n score: number;\n /** The matched entity (if resolved) */\n entity?: Entity;\n}\n\n/**\n * Combined result with scores from all layers.\n */\nexport interface ScoredResult {\n /** Entity name */\n entityName: string;\n /** The matched entity */\n entity: Entity;\n /** Individual layer scores (normalized 0-1) */\n scores: {\n semantic: number;\n lexical: number;\n symbolic: number;\n combined: number;\n };\n /** Which layers contributed to this result */\n matchedLayers: ('semantic' | 'lexical' | 'symbolic')[];\n /** Original raw scores before normalization */\n rawScores: {\n semantic?: number;\n lexical?: number;\n symbolic?: number;\n };\n}\n\n/**\n * Configurable weights for hybrid scoring.\n */\nexport interface HybridWeights {\n /** Weight for semantic layer (default: 0.4) */\n semantic: number;\n /** Weight for lexical layer (default: 0.4) */\n lexical: number;\n /** Weight for symbolic layer (default: 0.2) */\n symbolic: number;\n}\n\n/**\n * Default weights for hybrid search.\n */\nexport const DEFAULT_SCORER_WEIGHTS: HybridWeights = {\n semantic: 0.4,\n lexical: 0.4,\n symbolic: 0.2,\n};\n\n/**\n * Options for the HybridScorer.\n */\nexport interface HybridScorerOptions {\n /** Weights for each layer */\n weights?: Partial<HybridWeights>;\n /** Minimum score to include in results (default: 0) */\n minScore?: number;\n /** Whether to normalize weights to sum to 1 (default: true) */\n normalizeWeights?: boolean;\n}\n\n/**\n * HybridScorer combines multiple search signals using min-max normalization.\n *\n * Features:\n * 1. Min-max normalization brings all scores to 0-1 range\n * 2. Configurable weights for each layer\n * 3. Handles missing layers gracefully (redistributes weights)\n * 4. Tracks which layers contributed to each result\n *\n * @example\n * ```typescript\n * const scorer = new HybridScorer({\n * weights: { semantic: 0.5, lexical: 0.3, symbolic: 0.2 }\n * });\n *\n * const results = scorer.combine(\n * semanticResults,\n * lexicalResults,\n * symbolicResults,\n * entityMap\n * );\n * ```\n */\nexport class HybridScorer {\n private weights: HybridWeights;\n private minScore: number;\n private normalizeWeights: boolean;\n\n constructor(options: HybridScorerOptions = {}) {\n this.weights = {\n ...DEFAULT_SCORER_WEIGHTS,\n ...options.weights,\n };\n this.minScore = options.minScore ?? 0;\n this.normalizeWeights = options.normalizeWeights ?? true;\n }\n\n /**\n * Get current weights configuration.\n */\n getWeights(): HybridWeights {\n return { ...this.weights };\n }\n\n /**\n * Update weights configuration.\n */\n setWeights(weights: Partial<HybridWeights>): void {\n this.weights = { ...this.weights, ...weights };\n }\n\n /**\n * Perform min-max normalization on scores.\n *\n * Formula: normalized = (x - min) / (max - min)\n *\n * @param scores - Map of entity name to raw score\n * @returns Map of entity name to normalized score (0-1)\n */\n minMaxNormalize(scores: Map<string, number>): Map<string, number> {\n if (scores.size === 0) {\n return new Map();\n }\n\n // Find min and max\n let min = Infinity;\n let max = -Infinity;\n for (const score of scores.values()) {\n if (score < min) min = score;\n if (score > max) max = score;\n }\n\n // Handle edge case where all scores are the same\n if (max === min) {\n const normalized = new Map<string, number>();\n for (const name of scores.keys()) {\n // If all scores are zero, keep them zero; otherwise normalize to 1\n normalized.set(name, max === 0 ? 0 : 1);\n }\n return normalized;\n }\n\n // Normalize\n const range = max - min;\n const normalized = new Map<string, number>();\n for (const [name, score] of scores) {\n normalized.set(name, (score - min) / range);\n }\n\n return normalized;\n }\n\n /**\n * Combine results from all three search layers.\n *\n * @param semanticResults - Results from semantic search\n * @param lexicalResults - Results from lexical search\n * @param symbolicResults - Results from symbolic search\n * @param entityMap - Map of entity names to Entity objects\n * @returns Array of combined results sorted by score\n */\n combine(\n semanticResults: SemanticLayerResult[],\n lexicalResults: LexicalSearchResult[],\n symbolicResults: SymbolicSearchResult[],\n entityMap: Map<string, Entity>\n ): ScoredResult[] {\n // Build score maps\n const semanticScores = new Map<string, number>();\n for (const result of semanticResults) {\n semanticScores.set(result.entityName, result.similarity);\n }\n\n const lexicalScores = new Map<string, number>();\n for (const result of lexicalResults) {\n lexicalScores.set(result.entityName, result.score);\n }\n\n const symbolicScores = new Map<string, number>();\n for (const result of symbolicResults) {\n symbolicScores.set(result.entityName, result.score);\n }\n\n // Normalize scores\n const normalizedSemantic = this.minMaxNormalize(semanticScores);\n const normalizedLexical = this.minMaxNormalize(lexicalScores);\n const normalizedSymbolic = this.minMaxNormalize(symbolicScores);\n\n // Calculate effective weights\n let effectiveWeights = { ...this.weights };\n if (this.normalizeWeights) {\n effectiveWeights = this.getNormalizedWeights(\n semanticResults.length > 0,\n lexicalResults.length > 0,\n symbolicResults.length > 0\n );\n }\n\n // Collect all unique entity names\n const allNames = new Set<string>([\n ...normalizedSemantic.keys(),\n ...normalizedLexical.keys(),\n ...normalizedSymbolic.keys(),\n ]);\n\n // Calculate combined scores\n const results: ScoredResult[] = [];\n for (const entityName of allNames) {\n const entity = entityMap.get(entityName);\n if (!entity) continue;\n\n const semanticScore = normalizedSemantic.get(entityName) ?? 0;\n const lexicalScore = normalizedLexical.get(entityName) ?? 0;\n const symbolicScore = normalizedSymbolic.get(entityName) ?? 0;\n\n // Calculate weighted combination\n const combined =\n semanticScore * effectiveWeights.semantic +\n lexicalScore * effectiveWeights.lexical +\n symbolicScore * effectiveWeights.symbolic;\n\n // Track matched layers\n const matchedLayers: ('semantic' | 'lexical' | 'symbolic')[] = [];\n const rawScores: ScoredResult['rawScores'] = {};\n\n if (semanticScores.has(entityName)) {\n matchedLayers.push('semantic');\n rawScores.semantic = semanticScores.get(entityName);\n }\n if (lexicalScores.has(entityName)) {\n matchedLayers.push('lexical');\n rawScores.lexical = lexicalScores.get(entityName);\n }\n if (symbolicScores.has(entityName)) {\n matchedLayers.push('symbolic');\n rawScores.symbolic = symbolicScores.get(entityName);\n }\n\n // Skip if below minimum score or no layers matched\n if (combined < this.minScore || matchedLayers.length === 0) {\n continue;\n }\n\n results.push({\n entityName,\n entity,\n scores: {\n semantic: semanticScore,\n lexical: lexicalScore,\n symbolic: symbolicScore,\n combined,\n },\n matchedLayers,\n rawScores,\n });\n }\n\n // Sort by combined score descending\n return results.sort((a, b) => b.scores.combined - a.scores.combined);\n }\n\n /**\n * Get weights normalized to sum to 1, redistributing for missing layers.\n *\n * @param hasSemantic - Whether semantic results are available\n * @param hasLexical - Whether lexical results are available\n * @param hasSymbolic - Whether symbolic results are available\n * @returns Normalized weights\n */\n getNormalizedWeights(\n hasSemantic: boolean,\n hasLexical: boolean,\n hasSymbolic: boolean\n ): HybridWeights {\n let totalActiveWeight = 0;\n if (hasSemantic) totalActiveWeight += this.weights.semantic;\n if (hasLexical) totalActiveWeight += this.weights.lexical;\n if (hasSymbolic) totalActiveWeight += this.weights.symbolic;\n\n // If no layers are active, return zero weights\n if (totalActiveWeight === 0) {\n return { semantic: 0, lexical: 0, symbolic: 0 };\n }\n\n // Normalize active weights to sum to 1\n return {\n semantic: hasSemantic ? this.weights.semantic / totalActiveWeight : 0,\n lexical: hasLexical ? this.weights.lexical / totalActiveWeight : 0,\n symbolic: hasSymbolic ? this.weights.symbolic / totalActiveWeight : 0,\n };\n }\n\n /**\n * Combine scores from maps directly (alternative interface).\n *\n * @param semanticScores - Map of entity name to semantic score\n * @param lexicalScores - Map of entity name to lexical score\n * @param symbolicScores - Map of entity name to symbolic score\n * @param entityMap - Map of entity names to Entity objects\n * @returns Array of combined results sorted by score\n */\n combineFromMaps(\n semanticScores: Map<string, number>,\n lexicalScores: Map<string, number>,\n symbolicScores: Map<string, number>,\n entityMap: Map<string, Entity>\n ): ScoredResult[] {\n // Convert maps to result arrays\n const semanticResults: SemanticLayerResult[] = [];\n for (const [entityName, similarity] of semanticScores) {\n semanticResults.push({ entityName, similarity });\n }\n\n const lexicalResults: LexicalSearchResult[] = [];\n for (const [entityName, score] of lexicalScores) {\n lexicalResults.push({ entityName, score });\n }\n\n const symbolicResults: SymbolicSearchResult[] = [];\n for (const [entityName, score] of symbolicScores) {\n symbolicResults.push({ entityName, score });\n }\n\n return this.combine(semanticResults, lexicalResults, symbolicResults, entityMap);\n }\n\n /**\n * Calculate combined score for a single entity.\n *\n * Useful for scoring individual results without full normalization.\n *\n * @param semanticScore - Normalized semantic score (0-1)\n * @param lexicalScore - Normalized lexical score (0-1)\n * @param symbolicScore - Normalized symbolic score (0-1)\n * @returns Combined weighted score\n */\n calculateScore(\n semanticScore: number,\n lexicalScore: number,\n symbolicScore: number\n ): number {\n return (\n semanticScore * this.weights.semantic +\n lexicalScore * this.weights.lexical +\n symbolicScore * this.weights.symbolic\n );\n }\n}\n","/**\n * Parallel Search Executor\n *\n * Phase 12 Sprint 2: Executes search layers (semantic, lexical, symbolic)\n * concurrently using Promise.all with graceful fallback on failures.\n *\n * @module search/ParallelSearchExecutor\n */\n\nimport type {\n Entity,\n ReadonlyKnowledgeGraph,\n SymbolicFilters,\n} from '../types/index.js';\nimport type { SemanticSearch } from './SemanticSearch.js';\nimport type { RankedSearch } from './RankedSearch.js';\nimport { SymbolicSearch } from './SymbolicSearch.js';\nimport { SEMANTIC_SEARCH_LIMITS } from '../utils/constants.js';\n\n/**\n * Timing information for a search layer.\n */\nexport interface LayerTiming {\n /** Layer identifier */\n layer: 'semantic' | 'lexical' | 'symbolic';\n /** Start timestamp */\n startTime: number;\n /** End timestamp */\n endTime: number;\n /** Duration in milliseconds */\n durationMs: number;\n /** Whether the layer succeeded */\n success: boolean;\n /** Error message if failed */\n error?: string;\n /** Number of results returned */\n resultCount: number;\n}\n\n/**\n * Result from parallel search execution with timing metadata.\n */\nexport interface ParallelSearchResult {\n /** Semantic search results: entity name -> similarity score */\n semanticResults: Map<string, number>;\n /** Lexical search results: entity name -> normalized score */\n lexicalResults: Map<string, number>;\n /** Symbolic search results: entity name -> filter match score */\n symbolicResults: Map<string, number>;\n /** Timing information for each layer */\n timings: LayerTiming[];\n /** Total execution time in milliseconds */\n totalTimeMs: number;\n /** Whether all layers succeeded */\n allSucceeded: boolean;\n /** Summary of failed layers */\n failedLayers: string[];\n}\n\n/**\n * Options for parallel search execution.\n */\nexport interface ParallelSearchOptions {\n /** Semantic search options */\n semantic?: {\n minSimilarity?: number;\n topK?: number;\n };\n /** Lexical search options */\n lexical?: {\n useStopwords?: boolean;\n useStemming?: boolean;\n };\n /** Symbolic filter criteria */\n symbolic?: SymbolicFilters;\n /** Maximum results per layer */\n limit?: number;\n /** Timeout per layer in milliseconds (default: 30000) */\n timeoutMs?: number;\n}\n\n/**\n * ParallelSearchExecutor - Execute search layers concurrently\n *\n * Orchestrates parallel execution of semantic, lexical, and symbolic search\n * layers using Promise.all. Provides:\n * - Concurrent execution for improved latency\n * - Per-layer timing metadata\n * - Graceful fallback on individual layer failures\n * - Layer-specific error isolation\n *\n * @example\n * ```typescript\n * const executor = new ParallelSearchExecutor(semanticSearch, rankedSearch);\n *\n * const result = await executor.execute(graph, 'machine learning', {\n * semantic: { minSimilarity: 0.5 },\n * symbolic: { tags: ['ai'] }\n * });\n *\n * console.log(`Total time: ${result.totalTimeMs}ms`);\n * for (const timing of result.timings) {\n * console.log(`${timing.layer}: ${timing.durationMs}ms, ${timing.resultCount} results`);\n * }\n * ```\n */\nexport class ParallelSearchExecutor {\n private symbolicSearch: SymbolicSearch;\n\n constructor(\n private semanticSearch: SemanticSearch | null,\n private rankedSearch: RankedSearch\n ) {\n this.symbolicSearch = new SymbolicSearch();\n }\n\n /**\n * Execute all search layers in parallel.\n *\n * @param graph - Knowledge graph to search\n * @param query - Search query text\n * @param options - Search options for each layer\n * @returns Parallel search results with timing metadata\n */\n async execute(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: ParallelSearchOptions = {}\n ): Promise<ParallelSearchResult> {\n const {\n semantic = {},\n lexical = {},\n symbolic = {},\n limit = SEMANTIC_SEARCH_LIMITS.DEFAULT_LIMIT,\n timeoutMs = 30000,\n } = options;\n\n const overallStart = Date.now();\n const timings: LayerTiming[] = [];\n const failedLayers: string[] = [];\n\n // Execute all three layers in parallel\n const [semanticResult, lexicalResult, symbolicResult] = await Promise.all([\n this.executeSemanticLayer(graph, query, semantic, limit * 2, timeoutMs),\n this.executeLexicalLayer(query, lexical, limit * 2, timeoutMs),\n this.executeSymbolicLayer(graph.entities, symbolic, timeoutMs),\n ]);\n\n // Collect timing information\n timings.push(semanticResult.timing);\n timings.push(lexicalResult.timing);\n timings.push(symbolicResult.timing);\n\n // Track failed layers\n if (!semanticResult.timing.success) failedLayers.push('semantic');\n if (!lexicalResult.timing.success) failedLayers.push('lexical');\n if (!symbolicResult.timing.success) failedLayers.push('symbolic');\n\n const totalTimeMs = Date.now() - overallStart;\n\n return {\n semanticResults: semanticResult.results,\n lexicalResults: lexicalResult.results,\n symbolicResults: symbolicResult.results,\n timings,\n totalTimeMs,\n allSucceeded: failedLayers.length === 0,\n failedLayers,\n };\n }\n\n /**\n * Execute semantic search layer with timing.\n */\n private async executeSemanticLayer(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: { minSimilarity?: number; topK?: number },\n limit: number,\n _timeoutMs: number\n ): Promise<{ results: Map<string, number>; timing: LayerTiming }> {\n const startTime = Date.now();\n const results = new Map<string, number>();\n\n let success = true;\n let error: string | undefined;\n\n if (!this.semanticSearch) {\n // Semantic search not available - treat as graceful degradation\n return {\n results,\n timing: {\n layer: 'semantic',\n startTime,\n endTime: Date.now(),\n durationMs: Date.now() - startTime,\n success: true, // Not an error, just not configured\n resultCount: 0,\n },\n };\n }\n\n try {\n // Execute semantic search with timeout\n const semanticResults = await Promise.race([\n this.semanticSearch.search(\n graph,\n query,\n options.topK ?? limit,\n options.minSimilarity ?? 0\n ),\n this.createTimeout<never>(_timeoutMs, 'Semantic search timeout'),\n ]);\n\n for (const result of semanticResults) {\n results.set(result.entity.name, result.similarity);\n }\n } catch (err) {\n success = false;\n error = err instanceof Error ? err.message : String(err);\n }\n\n const endTime = Date.now();\n return {\n results,\n timing: {\n layer: 'semantic',\n startTime,\n endTime,\n durationMs: endTime - startTime,\n success,\n error,\n resultCount: results.size,\n },\n };\n }\n\n /**\n * Execute lexical search layer (TF-IDF/BM25) with timing.\n */\n private async executeLexicalLayer(\n query: string,\n _options: { useStopwords?: boolean; useStemming?: boolean },\n limit: number,\n _timeoutMs: number\n ): Promise<{ results: Map<string, number>; timing: LayerTiming }> {\n const startTime = Date.now();\n const results = new Map<string, number>();\n\n let success = true;\n let error: string | undefined;\n\n try {\n // Execute lexical search with timeout\n const lexicalResults = await Promise.race([\n this.rankedSearch.searchNodesRanked(\n query,\n undefined, // tags\n undefined, // minImportance\n undefined, // maxImportance\n limit\n ),\n this.createTimeout<never>(_timeoutMs, 'Lexical search timeout'),\n ]);\n\n // Normalize scores to 0-1 range\n const maxScore = Math.max(...lexicalResults.map(r => r.score), 1);\n for (const result of lexicalResults) {\n results.set(result.entity.name, result.score / maxScore);\n }\n } catch (err) {\n success = false;\n error = err instanceof Error ? err.message : String(err);\n }\n\n const endTime = Date.now();\n return {\n results,\n timing: {\n layer: 'lexical',\n startTime,\n endTime,\n durationMs: endTime - startTime,\n success,\n error,\n resultCount: results.size,\n },\n };\n }\n\n /**\n * Execute symbolic search layer with timing.\n */\n private async executeSymbolicLayer(\n entities: readonly Entity[],\n filters: SymbolicFilters | undefined,\n _timeoutMs: number\n ): Promise<{ results: Map<string, number>; timing: LayerTiming }> {\n const startTime = Date.now();\n const results = new Map<string, number>();\n\n let success = true;\n let error: string | undefined;\n\n try {\n // Symbolic search is synchronous but wrap for consistency\n if (!filters || Object.keys(filters).length === 0) {\n // No filters - give all entities base score\n for (const entity of entities) {\n results.set(entity.name, 0.5);\n }\n } else {\n const symbolicResults = this.symbolicSearch.search(entities, filters);\n for (const result of symbolicResults) {\n results.set(result.entity.name, result.score);\n }\n }\n } catch (err) {\n success = false;\n error = err instanceof Error ? err.message : String(err);\n }\n\n const endTime = Date.now();\n return {\n results,\n timing: {\n layer: 'symbolic',\n startTime,\n endTime,\n durationMs: endTime - startTime,\n success,\n error,\n resultCount: results.size,\n },\n };\n }\n\n /**\n * Create a timeout promise.\n */\n private createTimeout<T>(ms: number, message: string): Promise<T> {\n return new Promise((_, reject) => {\n setTimeout(() => reject(new Error(message)), ms);\n });\n }\n\n /**\n * Execute a single layer independently.\n *\n * @param layer - Layer to execute\n * @param graph - Knowledge graph\n * @param query - Search query\n * @param options - Layer-specific options\n * @returns Layer results with timing\n */\n async executeLayer(\n layer: 'semantic' | 'lexical' | 'symbolic',\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: ParallelSearchOptions = {}\n ): Promise<{ results: Map<string, number>; timing: LayerTiming }> {\n const limit = options.limit ?? SEMANTIC_SEARCH_LIMITS.DEFAULT_LIMIT;\n const timeoutMs = options.timeoutMs ?? 30000;\n\n switch (layer) {\n case 'semantic':\n return this.executeSemanticLayer(\n graph,\n query,\n options.semantic ?? {},\n limit * 2,\n timeoutMs\n );\n case 'lexical':\n return this.executeLexicalLayer(\n query,\n options.lexical ?? {},\n limit * 2,\n timeoutMs\n );\n case 'symbolic':\n return this.executeSymbolicLayer(\n graph.entities,\n options.symbolic,\n timeoutMs\n );\n }\n }\n\n /**\n * Execute only specific layers in parallel.\n *\n * @param layers - Layers to execute\n * @param graph - Knowledge graph\n * @param query - Search query\n * @param options - Search options\n * @returns Partial results for requested layers\n */\n async executeSelectedLayers(\n layers: Array<'semantic' | 'lexical' | 'symbolic'>,\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: ParallelSearchOptions = {}\n ): Promise<{\n results: Map<'semantic' | 'lexical' | 'symbolic', Map<string, number>>;\n timings: LayerTiming[];\n totalTimeMs: number;\n }> {\n const overallStart = Date.now();\n const results = new Map<'semantic' | 'lexical' | 'symbolic', Map<string, number>>();\n const timings: LayerTiming[] = [];\n\n const layerPromises = layers.map(layer =>\n this.executeLayer(layer, graph, query, options).then(result => ({\n layer,\n ...result,\n }))\n );\n\n const layerExecutionResults = await Promise.all(layerPromises);\n\n for (const { layer, results: layerData, timing } of layerExecutionResults) {\n results.set(layer, layerData);\n timings.push(timing);\n }\n\n return {\n results,\n timings,\n totalTimeMs: Date.now() - overallStart,\n };\n }\n\n /**\n * Get timing summary from results.\n *\n * @param timings - Array of layer timings\n * @returns Formatted timing summary\n */\n static formatTimingSummary(timings: LayerTiming[]): string {\n const lines: string[] = [];\n let totalTime = 0;\n let maxTime = 0;\n\n for (const timing of timings) {\n const status = timing.success ? 'OK' : `FAILED: ${timing.error}`;\n lines.push(\n ` ${timing.layer}: ${timing.durationMs}ms (${timing.resultCount} results) [${status}]`\n );\n totalTime += timing.durationMs;\n maxTime = Math.max(maxTime, timing.durationMs);\n }\n\n lines.unshift('Search Layer Timings:');\n lines.push(` Total (sequential): ${totalTime}ms`);\n lines.push(` Max (parallel bottleneck): ${maxTime}ms`);\n lines.push(` Speedup: ${(totalTime / maxTime).toFixed(2)}x`);\n\n return lines.join('\\n');\n }\n\n /**\n * Calculate potential speedup from parallel execution.\n *\n * @param timings - Array of layer timings\n * @returns Speedup metrics\n */\n static calculateSpeedup(timings: LayerTiming[]): {\n sequentialTime: number;\n parallelTime: number;\n speedup: number;\n } {\n const sequentialTime = timings.reduce((sum, t) => sum + t.durationMs, 0);\n const parallelTime = Math.max(...timings.map(t => t.durationMs));\n const speedup = sequentialTime / parallelTime;\n\n return { sequentialTime, parallelTime, speedup };\n }\n}\n","/**\n * Early Termination Manager\n *\n * Phase 12 Sprint 4: Manages early termination of hybrid search when\n * results are adequate, executing layers in cost-order for efficiency.\n *\n * @module search/EarlyTerminationManager\n */\n\nimport type {\n HybridSearchResult,\n QueryAnalysis,\n ReadonlyKnowledgeGraph,\n} from '../types/index.js';\nimport type { HybridSearchManager } from './HybridSearchManager.js';\nimport type { SearchLayer } from './QueryCostEstimator.js';\nimport { QueryCostEstimator } from './QueryCostEstimator.js';\n\n/**\n * Adequacy check result for a set of search results.\n */\nexport interface AdequacyCheck {\n /** Whether results are adequate */\n adequate: boolean;\n /** Adequacy score (0-1) */\n score: number;\n /** Reasons for the adequacy determination */\n reasons: string[];\n /** Layers that contributed to results */\n contributingLayers: SearchLayer[];\n}\n\n/**\n * Options for early termination.\n */\nexport interface EarlyTerminationOptions {\n /** Adequacy threshold (0-1, default: 0.7) */\n adequacyThreshold?: number;\n /** Minimum results required (default: 3) */\n minResults?: number;\n /** Maximum results to collect (default: 20) */\n maxResults?: number;\n /** Whether semantic search is available (default: true) */\n semanticAvailable?: boolean;\n /** Query analysis for smarter adequacy checking */\n analysis?: QueryAnalysis;\n /** Minimum diversity score (0-1, default: 0.3) */\n minDiversity?: number;\n /** Minimum average relevance score (0-1, default: 0.4) */\n minRelevance?: number;\n}\n\n/**\n * Result from early termination search.\n */\nexport interface EarlyTerminationResult {\n /** Final search results */\n results: HybridSearchResult[];\n /** Layers that were executed (in order) */\n executedLayers: SearchLayer[];\n /** Adequacy check details */\n adequacy: AdequacyCheck;\n /** Whether early termination occurred */\n earlyTerminated: boolean;\n /** Total time in milliseconds */\n executionTimeMs: number;\n}\n\n/**\n * Default options for early termination.\n */\nconst DEFAULT_OPTIONS: Required<Omit<EarlyTerminationOptions, 'analysis'>> = {\n adequacyThreshold: 0.7,\n minResults: 3,\n maxResults: 20,\n semanticAvailable: true,\n minDiversity: 0.3,\n minRelevance: 0.4,\n};\n\n/**\n * Early Termination Manager\n *\n * Executes search layers in cost-order (fastest first) and terminates\n * early when results are adequate, saving computation time.\n *\n * @example\n * ```typescript\n * const manager = new EarlyTerminationManager(hybridSearch);\n * const result = await manager.searchWithEarlyTermination(\n * graph,\n * 'machine learning',\n * { adequacyThreshold: 0.8, minResults: 5 }\n * );\n *\n * if (result.earlyTerminated) {\n * console.log(`Terminated after ${result.executedLayers.length} layers`);\n * }\n * ```\n */\nexport class EarlyTerminationManager {\n private costEstimator: QueryCostEstimator;\n\n constructor(\n private hybridSearch: HybridSearchManager,\n costEstimator?: QueryCostEstimator\n ) {\n this.costEstimator = costEstimator ?? new QueryCostEstimator();\n }\n\n /**\n * Execute search with early termination support.\n *\n * Executes layers in cost-order and terminates early when results\n * meet the adequacy threshold.\n *\n * @param graph - Knowledge graph to search\n * @param query - Search query\n * @param options - Early termination options\n * @returns Search results with termination details\n */\n async searchWithEarlyTermination(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n options: EarlyTerminationOptions = {}\n ): Promise<EarlyTerminationResult> {\n const startTime = Date.now();\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const { semanticAvailable, maxResults } = opts;\n\n // Get layers sorted by cost (fastest first)\n const orderedLayers = this.costEstimator.getLayersByCost(\n query,\n graph.entities.length,\n semanticAvailable\n );\n\n const allResults: HybridSearchResult[] = [];\n const executedLayers: SearchLayer[] = [];\n let earlyTerminated = false;\n\n // Execute layers in cost order\n for (const { layer } of orderedLayers) {\n executedLayers.push(layer);\n\n // Execute single layer search\n const layerResults = await this.executeLayerSearch(\n graph,\n query,\n layer,\n maxResults\n );\n\n // Merge results (deduplicate by entity name)\n for (const result of layerResults) {\n const existing = allResults.find(r => r.entity.name === result.entity.name);\n if (existing) {\n // Update scores if this layer has higher score\n if (result.scores.combined > existing.scores.combined) {\n Object.assign(existing, result);\n }\n // Add to matched layers\n if (!existing.matchedLayers.includes(layer)) {\n existing.matchedLayers.push(layer);\n }\n } else {\n // Ensure matchedLayers includes current layer\n if (!result.matchedLayers.includes(layer)) {\n result.matchedLayers = [...result.matchedLayers, layer];\n }\n allResults.push(result);\n }\n }\n\n // Check adequacy\n const adequacy = this.checkAdequacy(allResults, opts, executedLayers);\n\n if (adequacy.adequate) {\n earlyTerminated = executedLayers.length < orderedLayers.length;\n break;\n }\n }\n\n // Final sort by combined score\n const sortedResults = allResults\n .sort((a, b) => b.scores.combined - a.scores.combined)\n .slice(0, maxResults);\n\n const finalAdequacy = this.checkAdequacy(sortedResults, opts, executedLayers);\n\n return {\n results: sortedResults,\n executedLayers,\n adequacy: finalAdequacy,\n earlyTerminated,\n executionTimeMs: Date.now() - startTime,\n };\n }\n\n /**\n * Execute a single layer search.\n * @private\n */\n private async executeLayerSearch(\n graph: ReadonlyKnowledgeGraph,\n query: string,\n layer: SearchLayer,\n limit: number\n ): Promise<HybridSearchResult[]> {\n // Configure weights to emphasize the current layer\n const weights = this.getLayerWeights(layer);\n\n try {\n return await this.hybridSearch.search(graph, query, {\n ...weights,\n limit: limit * 2, // Over-fetch for better merging\n });\n } catch {\n // Layer failed, return empty results\n return [];\n }\n }\n\n /**\n * Get weight configuration for a specific layer.\n * @private\n */\n private getLayerWeights(layer: SearchLayer): {\n semanticWeight: number;\n lexicalWeight: number;\n symbolicWeight: number;\n } {\n switch (layer) {\n case 'semantic':\n return { semanticWeight: 1.0, lexicalWeight: 0.0, symbolicWeight: 0.0 };\n case 'lexical':\n return { semanticWeight: 0.0, lexicalWeight: 1.0, symbolicWeight: 0.0 };\n case 'symbolic':\n return { semanticWeight: 0.0, lexicalWeight: 0.0, symbolicWeight: 1.0 };\n }\n }\n\n /**\n * Check if results are adequate based on configured thresholds.\n *\n * @param results - Current search results\n * @param options - Adequacy options\n * @param executedLayers - Layers that have been executed\n * @returns Adequacy check result\n */\n checkAdequacy(\n results: HybridSearchResult[],\n options: EarlyTerminationOptions,\n executedLayers: SearchLayer[]\n ): AdequacyCheck {\n const opts = { ...DEFAULT_OPTIONS, ...options };\n const {\n adequacyThreshold,\n minResults,\n minDiversity,\n minRelevance,\n analysis,\n } = opts;\n\n const reasons: string[] = [];\n let score = 0;\n\n // Weight components\n const weights = {\n quantity: 0.35,\n diversity: 0.25,\n relevance: 0.25,\n coverage: 0.15,\n };\n\n // 1. Quantity score (do we have enough results?)\n const quantityScore = Math.min(results.length / minResults, 1);\n score += quantityScore * weights.quantity;\n\n if (quantityScore < 1) {\n reasons.push(`Insufficient results: ${results.length}/${minResults}`);\n } else {\n reasons.push(`Sufficient results: ${results.length}`);\n }\n\n // 2. Diversity score (variety of entity types and layers)\n const diversityScore = this.calculateDiversityScore(results);\n score += Math.min(diversityScore / minDiversity, 1) * weights.diversity;\n\n if (diversityScore < minDiversity) {\n reasons.push(`Low diversity: ${diversityScore.toFixed(2)}/${minDiversity}`);\n } else {\n reasons.push(`Good diversity: ${diversityScore.toFixed(2)}`);\n }\n\n // 3. Relevance score (average combined score)\n const avgRelevance = results.length > 0\n ? results.reduce((sum, r) => sum + r.scores.combined, 0) / results.length\n : 0;\n const relevanceScore = Math.min(avgRelevance / minRelevance, 1);\n score += relevanceScore * weights.relevance;\n\n if (avgRelevance < minRelevance) {\n reasons.push(`Low relevance: ${avgRelevance.toFixed(2)}/${minRelevance}`);\n } else {\n reasons.push(`Good relevance: ${avgRelevance.toFixed(2)}`);\n }\n\n // 4. Coverage score (how many layers contributed)\n const contributingLayers = this.getContributingLayers(results);\n const coverageScore = contributingLayers.length / executedLayers.length;\n score += coverageScore * weights.coverage;\n\n if (coverageScore < 0.5) {\n reasons.push(`Low layer coverage: ${contributingLayers.length}/${executedLayers.length}`);\n } else {\n reasons.push(`Good layer coverage: ${contributingLayers.length}/${executedLayers.length}`);\n }\n\n // Bonus for matching required info types (if analysis provided)\n if (analysis && analysis.requiredInfoTypes.length > 0) {\n const entityTypes = new Set(results.map(r => r.entity.entityType.toLowerCase()));\n const matchedTypes = analysis.requiredInfoTypes.filter(t =>\n entityTypes.has(t) ||\n (t === 'person' && entityTypes.has('person')) ||\n (t === 'entity' && entityTypes.size > 0)\n );\n const typeMatchScore = matchedTypes.length / analysis.requiredInfoTypes.length;\n score += typeMatchScore * 0.1; // Bonus weight\n reasons.push(`Info type coverage: ${matchedTypes.length}/${analysis.requiredInfoTypes.length}`);\n }\n\n // Normalize score\n score = Math.min(score, 1);\n\n return {\n adequate: score >= adequacyThreshold,\n score,\n reasons,\n contributingLayers,\n };\n }\n\n /**\n * Calculate diversity score from results.\n * @private\n */\n private calculateDiversityScore(results: HybridSearchResult[]): number {\n if (results.length === 0) return 0;\n\n // Type diversity\n const entityTypes = new Set(results.map(r => r.entity.entityType));\n const typeDiversity = Math.min(entityTypes.size / 3, 1);\n\n // Layer diversity\n const layerCounts = { semantic: 0, lexical: 0, symbolic: 0 };\n for (const result of results) {\n for (const layer of result.matchedLayers) {\n layerCounts[layer]++;\n }\n }\n const activeLayers = Object.values(layerCounts).filter(c => c > 0).length;\n const layerDiversity = activeLayers / 3;\n\n // Combined diversity\n return (typeDiversity + layerDiversity) / 2;\n }\n\n /**\n * Get layers that contributed to results.\n * @private\n */\n private getContributingLayers(results: HybridSearchResult[]): SearchLayer[] {\n const layers = new Set<SearchLayer>();\n for (const result of results) {\n for (const layer of result.matchedLayers) {\n layers.add(layer);\n }\n }\n return Array.from(layers);\n }\n\n /**\n * Calculate adequacy score for a set of results.\n *\n * Standalone method for checking result adequacy without full search.\n *\n * @param results - Results to evaluate\n * @param options - Adequacy options\n * @returns Adequacy score (0-1)\n */\n calculateAdequacyScore(\n results: HybridSearchResult[],\n options: EarlyTerminationOptions = {}\n ): number {\n const allLayers: SearchLayer[] = ['semantic', 'lexical', 'symbolic'];\n return this.checkAdequacy(results, options, allLayers).score;\n }\n\n /**\n * Get the cost estimator for external use.\n */\n getCostEstimator(): QueryCostEstimator {\n return this.costEstimator;\n }\n}\n","/**\n * Query Plan Cache\n *\n * Phase 12 Sprint 4: Caches query analysis and planning results\n * with LRU eviction for improved performance.\n *\n * @module search/QueryPlanCache\n */\n\nimport type { QueryAnalysis, QueryPlan } from '../types/index.js';\n\n/**\n * Cached query entry with metadata.\n */\nexport interface CachedQueryEntry {\n /** Normalized query string */\n normalizedQuery: string;\n /** Original query string */\n originalQuery: string;\n /** Cached analysis result */\n analysis: QueryAnalysis;\n /** Cached plan result (if available) */\n plan?: QueryPlan;\n /** Cache entry creation time */\n createdAt: number;\n /** Last access time for LRU */\n lastAccessed: number;\n /** Number of times this entry was accessed */\n hitCount: number;\n}\n\n/**\n * Cache statistics for monitoring.\n */\nexport interface QueryPlanCacheStats {\n /** Total cache entries */\n size: number;\n /** Maximum cache size */\n maxSize: number;\n /** Total cache hits */\n hits: number;\n /** Total cache misses */\n misses: number;\n /** Hit rate (0-1) */\n hitRate: number;\n /** Total evictions */\n evictions: number;\n /** Average entry age in milliseconds */\n averageEntryAgeMs: number;\n}\n\n/**\n * Options for the query plan cache.\n */\nexport interface QueryPlanCacheOptions {\n /** Maximum number of entries (default: 1000) */\n maxSize?: number;\n /** Entry TTL in milliseconds (default: 5 minutes) */\n ttlMs?: number;\n /** Whether to normalize queries for better hit rate (default: true) */\n normalizeQueries?: boolean;\n /** Whether to enable cache statistics (default: true) */\n enableStats?: boolean;\n}\n\n/**\n * Default cache options.\n */\nconst DEFAULT_OPTIONS: Required<QueryPlanCacheOptions> = {\n maxSize: 1000,\n ttlMs: 5 * 60 * 1000, // 5 minutes\n normalizeQueries: true,\n enableStats: true,\n};\n\n/**\n * Query Plan Cache with LRU eviction.\n *\n * Caches query analysis and planning results to avoid redundant computation.\n * Uses LRU (Least Recently Used) eviction when cache is full.\n *\n * @example\n * ```typescript\n * const cache = new QueryPlanCache({ maxSize: 500 });\n *\n * // Cache an analysis\n * cache.setAnalysis('Find Alice', analysis);\n *\n * // Retrieve cached analysis\n * const cached = cache.getAnalysis('Find Alice');\n * if (cached) {\n * console.log('Cache hit!', cached);\n * }\n *\n * // Get statistics\n * const stats = cache.getStats();\n * console.log(`Hit rate: ${(stats.hitRate * 100).toFixed(1)}%`);\n * ```\n */\nexport class QueryPlanCache {\n private cache: Map<string, CachedQueryEntry>;\n private options: Required<QueryPlanCacheOptions>;\n\n // Statistics\n private hits = 0;\n private misses = 0;\n private evictions = 0;\n\n constructor(options?: QueryPlanCacheOptions) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.cache = new Map();\n }\n\n /**\n * Get cached analysis for a query.\n *\n * @param query - The search query\n * @returns Cached analysis or undefined if not found\n */\n getAnalysis(query: string): QueryAnalysis | undefined {\n const entry = this.getEntry(query);\n return entry?.analysis;\n }\n\n /**\n * Get cached plan for a query.\n *\n * @param query - The search query\n * @returns Cached plan or undefined if not found\n */\n getPlan(query: string): QueryPlan | undefined {\n const entry = this.getEntry(query);\n return entry?.plan;\n }\n\n /**\n * Get full cached entry for a query.\n *\n * @param query - The search query\n * @returns Cached entry or undefined if not found\n */\n getEntry(query: string): CachedQueryEntry | undefined {\n const key = this.normalizeQuery(query);\n const entry = this.cache.get(key);\n\n if (!entry) {\n if (this.options.enableStats) this.misses++;\n return undefined;\n }\n\n // Check TTL\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n if (this.options.enableStats) this.misses++;\n return undefined;\n }\n\n // Update access time and count\n entry.lastAccessed = Date.now();\n entry.hitCount++;\n\n if (this.options.enableStats) this.hits++;\n\n return entry;\n }\n\n /**\n * Cache analysis results for a query.\n *\n * @param query - The search query\n * @param analysis - The analysis result to cache\n */\n setAnalysis(query: string, analysis: QueryAnalysis): void {\n const key = this.normalizeQuery(query);\n const now = Date.now();\n\n const existing = this.cache.get(key);\n if (existing) {\n // Update existing entry\n existing.analysis = analysis;\n existing.lastAccessed = now;\n } else {\n // Evict if necessary\n this.evictIfNeeded();\n\n // Create new entry\n this.cache.set(key, {\n normalizedQuery: key,\n originalQuery: query,\n analysis,\n createdAt: now,\n lastAccessed: now,\n hitCount: 0,\n });\n }\n }\n\n /**\n * Cache plan results for a query.\n *\n * @param query - The search query\n * @param analysis - The analysis result to cache\n * @param plan - The plan result to cache\n */\n setPlan(query: string, analysis: QueryAnalysis, plan: QueryPlan): void {\n const key = this.normalizeQuery(query);\n const now = Date.now();\n\n const existing = this.cache.get(key);\n if (existing) {\n // Update existing entry\n existing.analysis = analysis;\n existing.plan = plan;\n existing.lastAccessed = now;\n } else {\n // Evict if necessary\n this.evictIfNeeded();\n\n // Create new entry\n this.cache.set(key, {\n normalizedQuery: key,\n originalQuery: query,\n analysis,\n plan,\n createdAt: now,\n lastAccessed: now,\n hitCount: 0,\n });\n }\n }\n\n /**\n * Check if a query is cached.\n *\n * @param query - The search query\n * @returns True if cached (and not expired)\n */\n has(query: string): boolean {\n const key = this.normalizeQuery(query);\n const entry = this.cache.get(key);\n\n if (!entry) return false;\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n return false;\n }\n\n return true;\n }\n\n /**\n * Invalidate a specific query from cache.\n *\n * @param query - The search query to invalidate\n * @returns True if entry was found and removed\n */\n invalidate(query: string): boolean {\n const key = this.normalizeQuery(query);\n return this.cache.delete(key);\n }\n\n /**\n * Invalidate all entries matching a pattern.\n *\n * @param pattern - Regex pattern to match against queries\n * @returns Number of entries invalidated\n */\n invalidatePattern(pattern: RegExp): number {\n let count = 0;\n for (const [key, entry] of this.cache) {\n if (pattern.test(entry.originalQuery) || pattern.test(key)) {\n this.cache.delete(key);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Clear all cache entries.\n */\n clear(): void {\n this.cache.clear();\n this.hits = 0;\n this.misses = 0;\n this.evictions = 0;\n }\n\n /**\n * Get cache statistics.\n *\n * @returns Cache statistics\n */\n getStats(): QueryPlanCacheStats {\n const now = Date.now();\n let totalAge = 0;\n\n for (const entry of this.cache.values()) {\n totalAge += now - entry.createdAt;\n }\n\n const total = this.hits + this.misses;\n\n return {\n size: this.cache.size,\n maxSize: this.options.maxSize,\n hits: this.hits,\n misses: this.misses,\n hitRate: total > 0 ? this.hits / total : 0,\n evictions: this.evictions,\n averageEntryAgeMs: this.cache.size > 0 ? totalAge / this.cache.size : 0,\n };\n }\n\n /**\n * Get the current cache size.\n */\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * Normalize a query for cache lookup.\n *\n * Normalization helps improve cache hit rate by treating\n * similar queries as equivalent.\n *\n * @param query - The query to normalize\n * @returns Normalized query string\n */\n normalizeQuery(query: string): string {\n if (!this.options.normalizeQueries) {\n return query;\n }\n\n return query\n .toLowerCase()\n .trim()\n // Normalize whitespace\n .replace(/\\s+/g, ' ')\n // Remove punctuation that doesn't affect meaning\n .replace(/[.,!?;:]+$/g, '')\n // Sort operators for consistent matching\n .replace(/\\b(AND|OR|NOT)\\b/gi, match => match.toUpperCase());\n }\n\n /**\n * Check if an entry has expired.\n * @private\n */\n private isExpired(entry: CachedQueryEntry): boolean {\n return Date.now() - entry.createdAt > this.options.ttlMs;\n }\n\n /**\n * Evict entries if cache is at capacity.\n * Uses LRU (Least Recently Used) eviction.\n * @private\n */\n private evictIfNeeded(): void {\n if (this.cache.size < this.options.maxSize) {\n return;\n }\n\n // Find the least recently used entry\n let oldestKey: string | null = null;\n let oldestTime = Infinity;\n\n for (const [key, entry] of this.cache) {\n // Also check for expired entries during eviction\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n if (this.options.enableStats) this.evictions++;\n if (this.cache.size < this.options.maxSize) return;\n continue;\n }\n\n if (entry.lastAccessed < oldestTime) {\n oldestTime = entry.lastAccessed;\n oldestKey = key;\n }\n }\n\n // Evict the oldest entry\n if (oldestKey) {\n this.cache.delete(oldestKey);\n if (this.options.enableStats) this.evictions++;\n }\n }\n\n /**\n * Clean up expired entries.\n *\n * Call this periodically to remove stale entries.\n *\n * @returns Number of entries removed\n */\n cleanup(): number {\n let count = 0;\n for (const [key, entry] of this.cache) {\n if (this.isExpired(entry)) {\n this.cache.delete(key);\n count++;\n }\n }\n return count;\n }\n\n /**\n * Get all cache keys (for debugging).\n */\n keys(): string[] {\n return Array.from(this.cache.keys());\n }\n\n /**\n * Preload cache with common queries.\n *\n * @param queries - Array of query-analysis pairs to preload\n */\n preload(queries: Array<{ query: string; analysis: QueryAnalysis; plan?: QueryPlan }>): void {\n for (const { query, analysis, plan } of queries) {\n if (plan) {\n this.setPlan(query, analysis, plan);\n } else {\n this.setAnalysis(query, analysis);\n }\n }\n }\n\n /**\n * Export cache entries for persistence.\n *\n * @returns Array of cache entries\n */\n export(): CachedQueryEntry[] {\n const entries: CachedQueryEntry[] = [];\n for (const entry of this.cache.values()) {\n if (!this.isExpired(entry)) {\n entries.push({ ...entry });\n }\n }\n return entries;\n }\n\n /**\n * Import cache entries from persistence.\n *\n * @param entries - Array of cache entries to import\n * @param preserveTimestamps - Whether to preserve original timestamps\n */\n import(entries: CachedQueryEntry[], preserveTimestamps = false): void {\n const now = Date.now();\n for (const entry of entries) {\n if (!preserveTimestamps) {\n entry.createdAt = now;\n entry.lastAccessed = now;\n }\n\n // Skip if expired\n if (this.isExpired(entry)) continue;\n\n // Evict if needed\n this.evictIfNeeded();\n\n this.cache.set(entry.normalizedQuery, entry);\n }\n }\n}\n","/**\n * Quantized Vector Store\n *\n * Phase 12 Sprint 6: 8-bit scalar quantization for 4x vector memory reduction.\n * Uses asymmetric similarity computation for improved accuracy.\n *\n * @module search/QuantizedVectorStore\n */\n\n/**\n * Quantization parameters for a vector set.\n */\nexport interface QuantizationParams {\n /** Minimum value in the original vectors */\n min: number;\n /** Maximum value in the original vectors */\n max: number;\n /** Scale factor for quantization */\n scale: number;\n /** Dimension of vectors */\n dimension: number;\n}\n\n/**\n * Statistics for the quantized vector store.\n */\nexport interface QuantizedVectorStoreStats {\n /** Number of stored vectors */\n vectorCount: number;\n /** Vector dimension */\n dimension: number;\n /** Full precision memory usage (bytes) */\n fullPrecisionBytes: number;\n /** Quantized memory usage (bytes) */\n quantizedBytes: number;\n /** Memory reduction ratio */\n memoryReductionRatio: number;\n /** Average quantization error */\n avgQuantizationError: number;\n}\n\n/**\n * Search result from quantized vector store.\n */\nexport interface QuantizedSearchResult {\n /** Entity ID */\n id: string;\n /** Similarity score (0-1) */\n similarity: number;\n /** Whether result used quantized computation */\n quantized: boolean;\n}\n\n/**\n * Configuration options for QuantizedVectorStore.\n */\nexport interface QuantizedVectorStoreOptions {\n /** Use asymmetric similarity (query in full precision) */\n asymmetric?: boolean;\n /** Minimum vectors before enabling quantization */\n minVectorsForQuantization?: number;\n /** Enable accuracy tracking */\n trackAccuracy?: boolean;\n}\n\nconst DEFAULT_OPTIONS: Required<QuantizedVectorStoreOptions> = {\n asymmetric: true,\n minVectorsForQuantization: 100,\n trackAccuracy: false,\n};\n\n/**\n * Quantized Vector Store with 8-bit scalar quantization.\n *\n * Provides 4x memory reduction while maintaining >95% accuracy\n * using asymmetric similarity computation.\n *\n * @example\n * ```typescript\n * const store = new QuantizedVectorStore();\n *\n * // Add vectors\n * store.add('entity1', [0.1, 0.2, 0.3, ...]);\n * store.add('entity2', [0.4, 0.5, 0.6, ...]);\n *\n * // Search\n * const results = store.search([0.15, 0.25, 0.35, ...], 10);\n *\n * // Get stats\n * const stats = store.getStats();\n * console.log(`Memory reduction: ${stats.memoryReductionRatio}x`);\n * ```\n */\nexport class QuantizedVectorStore {\n private fullPrecisionVectors: Map<string, Float32Array>;\n private quantizedVectors: Map<string, Uint8Array>;\n private quantizationParams: QuantizationParams | null = null;\n private options: Required<QuantizedVectorStoreOptions>;\n private isQuantized = false;\n private quantizationErrors: number[] = [];\n\n constructor(options?: QuantizedVectorStoreOptions) {\n this.options = { ...DEFAULT_OPTIONS, ...options };\n this.fullPrecisionVectors = new Map();\n this.quantizedVectors = new Map();\n }\n\n /**\n * Add a vector to the store.\n *\n * @param id - Entity identifier\n * @param vector - Float vector (any dimension, must be consistent)\n */\n add(id: string, vector: number[]): void {\n const float32 = new Float32Array(vector);\n this.fullPrecisionVectors.set(id, float32);\n\n // Check if we should quantize\n if (\n !this.isQuantized &&\n this.fullPrecisionVectors.size >= this.options.minVectorsForQuantization\n ) {\n this.quantize();\n } else if (this.isQuantized) {\n // Add to quantized store\n const quantized = this.quantizeVector(float32);\n this.quantizedVectors.set(id, quantized);\n\n // Track error if enabled\n if (this.options.trackAccuracy) {\n const reconstructed = this.dequantizeVector(quantized);\n this.quantizationErrors.push(this.computeError(float32, reconstructed));\n }\n }\n }\n\n /**\n * Remove a vector from the store.\n *\n * @param id - Entity identifier\n * @returns True if vector was removed\n */\n remove(id: string): boolean {\n const existed = this.fullPrecisionVectors.delete(id);\n this.quantizedVectors.delete(id);\n return existed;\n }\n\n /**\n * Check if a vector exists.\n *\n * @param id - Entity identifier\n */\n has(id: string): boolean {\n return this.fullPrecisionVectors.has(id);\n }\n\n /**\n * Get a vector (dequantized if necessary).\n *\n * @param id - Entity identifier\n * @returns Vector or undefined\n */\n get(id: string): number[] | undefined {\n const vector = this.fullPrecisionVectors.get(id);\n return vector ? Array.from(vector) : undefined;\n }\n\n /**\n * Search for similar vectors.\n *\n * @param query - Query vector\n * @param k - Number of results to return\n * @returns Top k similar vectors with scores\n */\n search(query: number[], k: number): QuantizedSearchResult[] {\n const queryVector = new Float32Array(query);\n const results: QuantizedSearchResult[] = [];\n\n if (this.isQuantized && this.options.asymmetric) {\n // Asymmetric search: query in full precision, stored vectors quantized\n for (const [id, quantized] of this.quantizedVectors) {\n const reconstructed = this.dequantizeVector(quantized);\n const similarity = this.cosineSimilarity(queryVector, reconstructed);\n results.push({ id, similarity, quantized: true });\n }\n } else {\n // Full precision search\n for (const [id, vector] of this.fullPrecisionVectors) {\n const similarity = this.cosineSimilarity(queryVector, vector);\n results.push({ id, similarity, quantized: false });\n }\n }\n\n // Sort by similarity descending and take top k\n return results\n .sort((a, b) => b.similarity - a.similarity)\n .slice(0, k);\n }\n\n /**\n * Compute similarity between a query and specific entity.\n *\n * @param query - Query vector\n * @param id - Entity identifier\n * @returns Similarity score or undefined if not found\n */\n computeSimilarity(query: number[], id: string): number | undefined {\n const queryVector = new Float32Array(query);\n\n if (this.isQuantized && this.options.asymmetric) {\n const quantized = this.quantizedVectors.get(id);\n if (!quantized) return undefined;\n const reconstructed = this.dequantizeVector(quantized);\n return this.cosineSimilarity(queryVector, reconstructed);\n } else {\n const vector = this.fullPrecisionVectors.get(id);\n if (!vector) return undefined;\n return this.cosineSimilarity(queryVector, vector);\n }\n }\n\n /**\n * Force quantization of all vectors.\n */\n quantize(): void {\n if (this.fullPrecisionVectors.size === 0) return;\n\n // Compute quantization parameters\n this.quantizationParams = this.computeQuantizationParams();\n this.isQuantized = true;\n\n // Quantize all vectors\n this.quantizedVectors.clear();\n for (const [id, vector] of this.fullPrecisionVectors) {\n const quantized = this.quantizeVector(vector);\n this.quantizedVectors.set(id, quantized);\n\n // Track error if enabled\n if (this.options.trackAccuracy) {\n const reconstructed = this.dequantizeVector(quantized);\n this.quantizationErrors.push(this.computeError(vector, reconstructed));\n }\n }\n }\n\n /**\n * Get store statistics.\n */\n getStats(): QuantizedVectorStoreStats {\n const vectorCount = this.fullPrecisionVectors.size;\n const dimension = this.quantizationParams?.dimension ??\n (vectorCount > 0 ? this.fullPrecisionVectors.values().next().value!.length : 0);\n\n const fullPrecisionBytes = vectorCount * dimension * 4; // Float32\n const quantizedBytes = vectorCount * dimension * 1; // Uint8\n\n const avgQuantizationError = this.quantizationErrors.length > 0\n ? this.quantizationErrors.reduce((a, b) => a + b, 0) / this.quantizationErrors.length\n : 0;\n\n return {\n vectorCount,\n dimension,\n fullPrecisionBytes,\n quantizedBytes,\n memoryReductionRatio: fullPrecisionBytes > 0 ? fullPrecisionBytes / quantizedBytes : 1,\n avgQuantizationError,\n };\n }\n\n /**\n * Check if store is currently using quantization.\n */\n isUsingQuantization(): boolean {\n return this.isQuantized;\n }\n\n /**\n * Get the number of stored vectors.\n */\n size(): number {\n return this.fullPrecisionVectors.size;\n }\n\n /**\n * Clear all vectors from the store.\n */\n clear(): void {\n this.fullPrecisionVectors.clear();\n this.quantizedVectors.clear();\n this.quantizationParams = null;\n this.isQuantized = false;\n this.quantizationErrors = [];\n }\n\n /**\n * Export all vectors.\n */\n export(): Map<string, number[]> {\n const result = new Map<string, number[]>();\n for (const [id, vector] of this.fullPrecisionVectors) {\n result.set(id, Array.from(vector));\n }\n return result;\n }\n\n /**\n * Import vectors from a map.\n *\n * @param vectors - Map of id to vector\n * @param quantize - Whether to quantize after import\n */\n import(vectors: Map<string, number[]>, quantize = true): void {\n for (const [id, vector] of vectors) {\n const float32 = new Float32Array(vector);\n this.fullPrecisionVectors.set(id, float32);\n }\n\n if (quantize && this.fullPrecisionVectors.size >= this.options.minVectorsForQuantization) {\n this.quantize();\n }\n }\n\n // Private methods\n\n private computeQuantizationParams(): QuantizationParams {\n let min = Infinity;\n let max = -Infinity;\n let dimension = 0;\n\n for (const vector of this.fullPrecisionVectors.values()) {\n dimension = vector.length;\n for (let i = 0; i < vector.length; i++) {\n if (vector[i] < min) min = vector[i];\n if (vector[i] > max) max = vector[i];\n }\n }\n\n const scale = (max - min) / 255;\n\n return { min, max, scale, dimension };\n }\n\n private quantizeVector(vector: Float32Array): Uint8Array {\n if (!this.quantizationParams) {\n throw new Error('Quantization params not initialized');\n }\n\n const { min, scale } = this.quantizationParams;\n const quantized = new Uint8Array(vector.length);\n\n for (let i = 0; i < vector.length; i++) {\n // Clamp to 0-255 range\n const normalized = (vector[i] - min) / scale;\n quantized[i] = Math.max(0, Math.min(255, Math.round(normalized)));\n }\n\n return quantized;\n }\n\n private dequantizeVector(quantized: Uint8Array): Float32Array {\n if (!this.quantizationParams) {\n throw new Error('Quantization params not initialized');\n }\n\n const { min, scale } = this.quantizationParams;\n const vector = new Float32Array(quantized.length);\n\n for (let i = 0; i < quantized.length; i++) {\n vector[i] = quantized[i] * scale + min;\n }\n\n return vector;\n }\n\n private cosineSimilarity(a: Float32Array, b: Float32Array): number {\n if (a.length !== b.length) return 0;\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i] * b[i];\n normA += a[i] * a[i];\n normB += b[i] * b[i];\n }\n\n const denominator = Math.sqrt(normA) * Math.sqrt(normB);\n return denominator === 0 ? 0 : dotProduct / denominator;\n }\n\n private computeError(original: Float32Array, reconstructed: Float32Array): number {\n let sumSquaredError = 0;\n for (let i = 0; i < original.length; i++) {\n const diff = original[i] - reconstructed[i];\n sumSquaredError += diff * diff;\n }\n return Math.sqrt(sumSquaredError / original.length);\n }\n}\n","/**\r\n * Tag Manager\r\n *\r\n * Manages tag aliases and canonical tag resolution.\r\n *\r\n * @module features/TagManager\r\n */\r\n\r\nimport * as fs from 'fs/promises';\r\nimport type { TagAlias } from '../types/index.js';\r\n\r\n/**\r\n * Manages tag alias system for synonym mapping.\r\n */\r\nexport class TagManager {\r\n constructor(private tagAliasesFilePath: string) {}\r\n\r\n /**\r\n * Load all tag aliases from JSONL file.\r\n *\r\n * @returns Array of tag aliases\r\n */\r\n private async loadTagAliases(): Promise<TagAlias[]> {\r\n try {\r\n const data = await fs.readFile(this.tagAliasesFilePath, 'utf-8');\r\n const lines = data.split('\\n').filter((line: string) => line.trim() !== '');\r\n return lines.map((line: string) => JSON.parse(line) as TagAlias);\r\n } catch (error) {\r\n if (error instanceof Error && 'code' in error && (error as any).code === 'ENOENT') {\r\n return [];\r\n }\r\n throw error;\r\n }\r\n }\r\n\r\n /**\r\n * Save tag aliases to JSONL file.\r\n *\r\n * @param aliases - Array of tag aliases\r\n */\r\n private async saveTagAliases(aliases: TagAlias[]): Promise<void> {\r\n const lines = aliases.map(a => JSON.stringify(a));\r\n await fs.writeFile(this.tagAliasesFilePath, lines.join('\\n'));\r\n }\r\n\r\n /**\r\n * Resolve a tag through aliases to get its canonical form.\r\n *\r\n * This method follows the alias chain to find the canonical (main) tag name.\r\n * All tags are normalized to lowercase for consistency.\r\n * If the tag has no alias, it returns the tag itself as canonical.\r\n *\r\n * @param tag - Tag to resolve (can be alias or canonical)\r\n * @returns Canonical tag name (lowercase)\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new TagManager(tagAliasesPath);\r\n *\r\n * // Set up: assume \"js\" is aliased to \"javascript\"\r\n * await manager.addTagAlias('js', 'javascript');\r\n *\r\n * // Resolve alias to canonical\r\n * const canonical = await manager.resolveTag('js');\r\n * console.log(canonical); // \"javascript\"\r\n *\r\n * // Resolve canonical tag (returns as-is)\r\n * const unchanged = await manager.resolveTag('javascript');\r\n * console.log(unchanged); // \"javascript\"\r\n *\r\n * // Resolve unknown tag (returns normalized)\r\n * const unknown = await manager.resolveTag('PYTHON');\r\n * console.log(unknown); // \"python\"\r\n * ```\r\n */\r\n async resolveTag(tag: string): Promise<string> {\r\n const aliases = await this.loadTagAliases();\r\n const normalized = tag.toLowerCase();\r\n\r\n // Check if this tag is an alias\r\n const alias = aliases.find(a => a.alias === normalized);\r\n if (alias) {\r\n return alias.canonical;\r\n }\r\n\r\n // Return as-is (might be canonical or unaliased tag)\r\n return normalized;\r\n }\r\n\r\n /**\r\n * Add a tag alias (synonym mapping).\r\n *\r\n * Creates a mapping from an alias (synonym) to a canonical (main) tag.\r\n * This enables flexible tagging where users can use different terms\r\n * that all resolve to the same canonical tag.\r\n *\r\n * Validation rules:\r\n * - Prevents duplicate aliases (same alias can't map to different canonicals)\r\n * - Prevents chained aliases (alias must point to canonical, not another alias)\r\n * - All tags are normalized to lowercase\r\n *\r\n * @param alias - The alias/synonym (will be normalized to lowercase)\r\n * @param canonical - The canonical (main) tag name (will be normalized to lowercase)\r\n * @param description - Optional description explaining the alias relationship\r\n * @returns Newly created TagAlias object with metadata\r\n * @throws {Error} If alias already exists or would create chained aliases\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new TagManager(tagAliasesPath);\r\n *\r\n * // Create simple alias\r\n * await manager.addTagAlias('js', 'javascript', 'Common abbreviation');\r\n *\r\n * // Create multiple aliases for same canonical\r\n * await manager.addTagAlias('py', 'python');\r\n * await manager.addTagAlias('py3', 'python', 'Python 3.x');\r\n *\r\n * // Error: duplicate alias\r\n * try {\r\n * await manager.addTagAlias('js', 'ecmascript'); // Fails - 'js' already aliased\r\n * } catch (error) {\r\n * console.error('Alias already exists');\r\n * }\r\n *\r\n * // Error: chained alias\r\n * await manager.addTagAlias('js', 'javascript');\r\n * try {\r\n * await manager.addTagAlias('javascript', 'ecmascript'); // Fails - can't alias canonical\r\n * } catch (error) {\r\n * console.error('Cannot create chained aliases');\r\n * }\r\n * ```\r\n */\r\n async addTagAlias(alias: string, canonical: string, description?: string): Promise<TagAlias> {\r\n const aliases = await this.loadTagAliases();\r\n const normalizedAlias = alias.toLowerCase();\r\n const normalizedCanonical = canonical.toLowerCase();\r\n\r\n // Check if alias already exists\r\n if (aliases.some(a => a.alias === normalizedAlias)) {\r\n throw new Error(`Tag alias \"${alias}\" already exists`);\r\n }\r\n\r\n // Prevent aliasing to another alias (aliases should point to canonical tags)\r\n if (aliases.some(a => a.canonical === normalizedAlias)) {\r\n throw new Error(\r\n `Cannot create alias to \"${alias}\" because it is a canonical tag with existing aliases`\r\n );\r\n }\r\n\r\n const newAlias: TagAlias = {\r\n alias: normalizedAlias,\r\n canonical: normalizedCanonical,\r\n description,\r\n createdAt: new Date().toISOString(),\r\n };\r\n\r\n aliases.push(newAlias);\r\n await this.saveTagAliases(aliases);\r\n\r\n return newAlias;\r\n }\r\n\r\n /**\r\n * List all tag aliases.\r\n *\r\n * @returns Array of all tag aliases\r\n */\r\n async listTagAliases(): Promise<TagAlias[]> {\r\n return await this.loadTagAliases();\r\n }\r\n\r\n /**\r\n * Remove a tag alias.\r\n *\r\n * @param alias - Alias to remove\r\n * @returns True if removed, false if not found\r\n */\r\n async removeTagAlias(alias: string): Promise<boolean> {\r\n const aliases = await this.loadTagAliases();\r\n const normalizedAlias = alias.toLowerCase();\r\n const initialLength = aliases.length;\r\n const filtered = aliases.filter(a => a.alias !== normalizedAlias);\r\n\r\n if (filtered.length === initialLength) {\r\n return false; // Alias not found\r\n }\r\n\r\n await this.saveTagAliases(filtered);\r\n return true;\r\n }\r\n\r\n /**\r\n * Get all aliases (synonyms) for a canonical tag.\r\n *\r\n * Returns all alias names that resolve to the specified canonical tag.\r\n * Useful for discovering alternative names users might use for a tag.\r\n * The canonical tag name is normalized to lowercase.\r\n *\r\n * @param canonicalTag - Canonical tag name (will be normalized to lowercase)\r\n * @returns Array of alias names (all lowercase)\r\n *\r\n * @example\r\n * ```typescript\r\n * const manager = new TagManager(tagAliasesPath);\r\n *\r\n * // Set up some aliases\r\n * await manager.addTagAlias('js', 'javascript');\r\n * await manager.addTagAlias('ecmascript', 'javascript');\r\n * await manager.addTagAlias('es6', 'javascript');\r\n *\r\n * // Get all aliases for canonical tag\r\n * const aliases = await manager.getAliasesForTag('javascript');\r\n * console.log(aliases); // ['js', 'ecmascript', 'es6']\r\n *\r\n * // Empty array if no aliases\r\n * const noAliases = await manager.getAliasesForTag('python');\r\n * console.log(noAliases); // []\r\n * ```\r\n */\r\n async getAliasesForTag(canonicalTag: string): Promise<string[]> {\r\n const aliases = await this.loadTagAliases();\r\n const normalized = canonicalTag.toLowerCase();\r\n return aliases.filter(a => a.canonical === normalized).map(a => a.alias);\r\n }\r\n}\r\n","/**\r\n * Analytics Manager\r\n *\r\n * Handles graph statistics and validation operations.\r\n * Extracted from SearchManager (Phase 4: Consolidate God Objects).\r\n *\r\n * @module features/AnalyticsManager\r\n */\r\n\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport type { GraphStats, ValidationReport, ValidationIssue, ValidationWarning } from '../types/index.js';\r\n\r\n/**\r\n * Manages analytics operations for the knowledge graph.\r\n */\r\nexport class AnalyticsManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Validate the knowledge graph structure and data integrity.\r\n *\r\n * Checks for:\r\n * - Orphaned relations (pointing to non-existent entities)\r\n * - Duplicate entity names\r\n * - Invalid entity data (missing name/type, invalid observations)\r\n * - Isolated entities (no relations)\r\n * - Empty observations\r\n * - Missing metadata (createdAt, lastModified)\r\n *\r\n * @returns Validation report with errors, warnings, and summary\r\n */\r\n async validateGraph(): Promise<ValidationReport> {\r\n const graph = await this.storage.loadGraph();\r\n const issues: ValidationIssue[] = [];\r\n const warnings: ValidationWarning[] = [];\r\n\r\n // Create a set of all entity names for fast lookup\r\n const entityNames = new Set(graph.entities.map(e => e.name));\r\n\r\n // Check for orphaned relations (relations pointing to non-existent entities)\r\n for (const relation of graph.relations) {\r\n if (!entityNames.has(relation.from)) {\r\n issues.push({\r\n type: 'orphaned_relation',\r\n message: `Relation has non-existent source entity: \"${relation.from}\"`,\r\n details: { relation, missingEntity: relation.from },\r\n });\r\n }\r\n if (!entityNames.has(relation.to)) {\r\n issues.push({\r\n type: 'orphaned_relation',\r\n message: `Relation has non-existent target entity: \"${relation.to}\"`,\r\n details: { relation, missingEntity: relation.to },\r\n });\r\n }\r\n }\r\n\r\n // Check for duplicate entity names\r\n const entityNameCounts = new Map<string, number>();\r\n for (const entity of graph.entities) {\r\n const count = entityNameCounts.get(entity.name) || 0;\r\n entityNameCounts.set(entity.name, count + 1);\r\n }\r\n for (const [name, count] of entityNameCounts.entries()) {\r\n if (count > 1) {\r\n issues.push({\r\n type: 'duplicate_entity',\r\n message: `Duplicate entity name found: \"${name}\" (${count} instances)`,\r\n details: { entityName: name, count },\r\n });\r\n }\r\n }\r\n\r\n // Check for entities with invalid data\r\n for (const entity of graph.entities) {\r\n if (!entity.name || entity.name.trim() === '') {\r\n issues.push({\r\n type: 'invalid_data',\r\n message: 'Entity has empty or missing name',\r\n details: { entity },\r\n });\r\n }\r\n if (!entity.entityType || entity.entityType.trim() === '') {\r\n issues.push({\r\n type: 'invalid_data',\r\n message: `Entity \"${entity.name}\" has empty or missing entityType`,\r\n details: { entity },\r\n });\r\n }\r\n if (!Array.isArray(entity.observations)) {\r\n issues.push({\r\n type: 'invalid_data',\r\n message: `Entity \"${entity.name}\" has invalid observations (not an array)`,\r\n details: { entity },\r\n });\r\n }\r\n }\r\n\r\n // Warnings: Check for isolated entities (no relations)\r\n const entitiesInRelations = new Set<string>();\r\n for (const relation of graph.relations) {\r\n entitiesInRelations.add(relation.from);\r\n entitiesInRelations.add(relation.to);\r\n }\r\n for (const entity of graph.entities) {\r\n if (!entitiesInRelations.has(entity.name) && graph.relations.length > 0) {\r\n warnings.push({\r\n type: 'isolated_entity',\r\n message: `Entity \"${entity.name}\" has no relations to other entities`,\r\n details: { entityName: entity.name },\r\n });\r\n }\r\n }\r\n\r\n // Warnings: Check for entities with empty observations\r\n for (const entity of graph.entities) {\r\n if (entity.observations.length === 0) {\r\n warnings.push({\r\n type: 'empty_observations',\r\n message: `Entity \"${entity.name}\" has no observations`,\r\n details: { entityName: entity.name },\r\n });\r\n }\r\n }\r\n\r\n // Warnings: Check for missing metadata (createdAt, lastModified)\r\n for (const entity of graph.entities) {\r\n if (!entity.createdAt) {\r\n warnings.push({\r\n type: 'missing_metadata',\r\n message: `Entity \"${entity.name}\" is missing createdAt timestamp`,\r\n details: { entityName: entity.name, field: 'createdAt' },\r\n });\r\n }\r\n if (!entity.lastModified) {\r\n warnings.push({\r\n type: 'missing_metadata',\r\n message: `Entity \"${entity.name}\" is missing lastModified timestamp`,\r\n details: { entityName: entity.name, field: 'lastModified' },\r\n });\r\n }\r\n }\r\n\r\n // Count specific issues\r\n const orphanedRelationsCount = issues.filter(e => e.type === 'orphaned_relation').length;\r\n const entitiesWithoutRelationsCount = warnings.filter(\r\n w => w.type === 'isolated_entity'\r\n ).length;\r\n\r\n return {\r\n isValid: issues.length === 0,\r\n issues,\r\n warnings,\r\n summary: {\r\n totalErrors: issues.length,\r\n totalWarnings: warnings.length,\r\n orphanedRelationsCount,\r\n entitiesWithoutRelationsCount,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Get comprehensive statistics about the knowledge graph.\r\n *\r\n * Provides metrics including:\r\n * - Total counts of entities and relations\r\n * - Entity and relation type distributions\r\n * - Oldest and newest entities/relations\r\n * - Date ranges for entities and relations\r\n *\r\n * @returns Graph statistics object\r\n */\r\n async getGraphStats(): Promise<GraphStats> {\r\n const graph = await this.storage.loadGraph();\r\n\r\n // Calculate entity type counts\r\n const entityTypesCounts: Record<string, number> = {};\r\n graph.entities.forEach(e => {\r\n entityTypesCounts[e.entityType] = (entityTypesCounts[e.entityType] || 0) + 1;\r\n });\r\n\r\n // Calculate relation type counts\r\n const relationTypesCounts: Record<string, number> = {};\r\n graph.relations.forEach(r => {\r\n relationTypesCounts[r.relationType] = (relationTypesCounts[r.relationType] || 0) + 1;\r\n });\r\n\r\n // Find oldest and newest entities\r\n let oldestEntity: { name: string; date: string } | undefined;\r\n let newestEntity: { name: string; date: string } | undefined;\r\n let earliestEntityDate: Date | null = null;\r\n let latestEntityDate: Date | null = null;\r\n\r\n graph.entities.forEach(e => {\r\n const date = new Date(e.createdAt || '');\r\n if (!earliestEntityDate || date < earliestEntityDate) {\r\n earliestEntityDate = date;\r\n oldestEntity = { name: e.name, date: e.createdAt || '' };\r\n }\r\n if (!latestEntityDate || date > latestEntityDate) {\r\n latestEntityDate = date;\r\n newestEntity = { name: e.name, date: e.createdAt || '' };\r\n }\r\n });\r\n\r\n // Find oldest and newest relations\r\n let oldestRelation: { from: string; to: string; relationType: string; date: string } | undefined;\r\n let newestRelation: { from: string; to: string; relationType: string; date: string } | undefined;\r\n let earliestRelationDate: Date | null = null;\r\n let latestRelationDate: Date | null = null;\r\n\r\n graph.relations.forEach(r => {\r\n const date = new Date(r.createdAt || '');\r\n if (!earliestRelationDate || date < earliestRelationDate) {\r\n earliestRelationDate = date;\r\n oldestRelation = { from: r.from, to: r.to, relationType: r.relationType, date: r.createdAt || '' };\r\n }\r\n if (!latestRelationDate || date > latestRelationDate) {\r\n latestRelationDate = date;\r\n newestRelation = { from: r.from, to: r.to, relationType: r.relationType, date: r.createdAt || '' };\r\n }\r\n });\r\n\r\n return {\r\n totalEntities: graph.entities.length,\r\n totalRelations: graph.relations.length,\r\n entityTypesCounts,\r\n relationTypesCounts,\r\n oldestEntity,\r\n newestEntity,\r\n oldestRelation,\r\n newestRelation,\r\n entityDateRange: earliestEntityDate && latestEntityDate ? {\r\n earliest: (earliestEntityDate as Date).toISOString(),\r\n latest: (latestEntityDate as Date).toISOString()\r\n } : undefined,\r\n relationDateRange: earliestRelationDate && latestRelationDate ? {\r\n earliest: (earliestRelationDate as Date).toISOString(),\r\n latest: (latestRelationDate as Date).toISOString()\r\n } : undefined,\r\n };\r\n }\r\n}\r\n","/**\r\n * Compression Manager\r\n *\r\n * Handles duplicate detection, entity merging, and graph compression.\r\n * Extracted from SearchManager (Phase 4: Consolidate God Objects).\r\n *\r\n * @module features/CompressionManager\r\n */\r\n\r\nimport type { Entity, Relation, GraphCompressionResult, KnowledgeGraph, LongRunningOperationOptions, PreparedEntity } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport {\r\n levenshteinDistance,\r\n checkCancellation,\r\n createProgressReporter,\r\n createProgress,\r\n fnv1aHash,\r\n} from '../utils/index.js';\r\nimport { EntityNotFoundError, InsufficientEntitiesError } from '../utils/errors.js';\r\nimport { SIMILARITY_WEIGHTS, DEFAULT_DUPLICATE_THRESHOLD } from '../utils/constants.js';\r\n\r\n/**\r\n * Manages compression operations for the knowledge graph.\r\n */\r\nexport class CompressionManager {\r\n constructor(private storage: GraphStorage) {}\r\n\r\n /**\r\n * Prepare an entity for efficient similarity comparisons.\r\n * Pre-computes all normalized data to avoid repeated computation.\r\n *\r\n * Phase 12 Sprint 1: Added nameHash for fast bucketing.\r\n *\r\n * @param entity - The entity to prepare\r\n * @returns PreparedEntity with pre-computed data including hash\r\n */\r\n private prepareEntity(entity: Entity): PreparedEntity {\r\n const nameLower = entity.name.toLowerCase();\r\n return {\r\n entity,\r\n nameLower,\r\n typeLower: entity.entityType.toLowerCase(),\r\n observationSet: new Set(entity.observations.map(o => o.toLowerCase())),\r\n tagSet: new Set((entity.tags ?? []).map(t => t.toLowerCase())),\r\n nameHash: fnv1aHash(nameLower),\r\n };\r\n }\r\n\r\n /**\r\n * Prepare multiple entities for efficient similarity comparisons.\r\n * Use this before batch comparison operations.\r\n *\r\n * @param entities - Entities to prepare\r\n * @returns Map of entity name to PreparedEntity\r\n */\r\n private prepareEntities(entities: readonly Entity[]): Map<string, PreparedEntity> {\r\n const prepared = new Map<string, PreparedEntity>();\r\n for (const entity of entities) {\r\n prepared.set(entity.name, this.prepareEntity(entity));\r\n }\r\n return prepared;\r\n }\r\n\r\n /**\r\n * Calculate similarity between two entities using multiple heuristics.\r\n *\r\n * Uses configurable weights defined in SIMILARITY_WEIGHTS constant.\r\n * See SIMILARITY_WEIGHTS for the breakdown of scoring factors.\r\n *\r\n * NOTE: For batch comparisons, use prepareEntities() + calculatePreparedSimilarity() for better performance.\r\n *\r\n * @param e1 - First entity\r\n * @param e2 - Second entity\r\n * @returns Similarity score from 0 (completely different) to 1 (identical)\r\n */\r\n calculateEntitySimilarity(e1: Entity, e2: Entity): number {\r\n let score = 0;\r\n let factors = 0;\r\n\r\n // Name similarity (Levenshtein-based)\r\n const nameDistance = levenshteinDistance(e1.name.toLowerCase(), e2.name.toLowerCase());\r\n const maxNameLength = Math.max(e1.name.length, e2.name.length);\r\n const nameSimilarity = 1 - nameDistance / maxNameLength;\r\n score += nameSimilarity * SIMILARITY_WEIGHTS.NAME;\r\n factors += SIMILARITY_WEIGHTS.NAME;\r\n\r\n // Type similarity (exact match)\r\n if (e1.entityType.toLowerCase() === e2.entityType.toLowerCase()) {\r\n score += SIMILARITY_WEIGHTS.TYPE;\r\n }\r\n factors += SIMILARITY_WEIGHTS.TYPE;\r\n\r\n // Observation overlap (Jaccard similarity)\r\n const obs1Set = new Set(e1.observations.map(o => o.toLowerCase()));\r\n const obs2Set = new Set(e2.observations.map(o => o.toLowerCase()));\r\n const intersection = new Set([...obs1Set].filter(x => obs2Set.has(x)));\r\n const union = new Set([...obs1Set, ...obs2Set]);\r\n const observationSimilarity = union.size > 0 ? intersection.size / union.size : 0;\r\n score += observationSimilarity * SIMILARITY_WEIGHTS.OBSERVATIONS;\r\n factors += SIMILARITY_WEIGHTS.OBSERVATIONS;\r\n\r\n // Tag overlap (Jaccard similarity)\r\n if (e1.tags && e2.tags && (e1.tags.length > 0 || e2.tags.length > 0)) {\r\n const tags1Set = new Set(e1.tags.map(t => t.toLowerCase()));\r\n const tags2Set = new Set(e2.tags.map(t => t.toLowerCase()));\r\n const tagIntersection = new Set([...tags1Set].filter(x => tags2Set.has(x)));\r\n const tagUnion = new Set([...tags1Set, ...tags2Set]);\r\n const tagSimilarity = tagUnion.size > 0 ? tagIntersection.size / tagUnion.size : 0;\r\n score += tagSimilarity * SIMILARITY_WEIGHTS.TAGS;\r\n factors += SIMILARITY_WEIGHTS.TAGS;\r\n }\r\n\r\n return factors > 0 ? score / factors : 0;\r\n }\r\n\r\n /**\r\n * Efficiently calculate intersection size of two Sets without creating a new Set.\r\n * Iterates over the smaller set for O(min(m,n)) complexity.\r\n */\r\n private setIntersectionSize(a: Set<string>, b: Set<string>): number {\r\n // Always iterate over smaller set\r\n const [smaller, larger] = a.size <= b.size ? [a, b] : [b, a];\r\n let count = 0;\r\n for (const item of smaller) {\r\n if (larger.has(item)) count++;\r\n }\r\n return count;\r\n }\r\n\r\n /**\r\n * Calculate similarity between two prepared entities.\r\n * OPTIMIZED: Uses pre-computed Sets to avoid O(n) set creation per comparison.\r\n *\r\n * @param p1 - First prepared entity\r\n * @param p2 - Second prepared entity\r\n * @returns Similarity score from 0 (completely different) to 1 (identical)\r\n */\r\n private calculatePreparedSimilarity(p1: PreparedEntity, p2: PreparedEntity): number {\r\n let score = 0;\r\n let factors = 0;\r\n\r\n // Name similarity (Levenshtein-based) - use pre-computed lowercase\r\n const nameDistance = levenshteinDistance(p1.nameLower, p2.nameLower);\r\n const maxNameLength = Math.max(p1.nameLower.length, p2.nameLower.length);\r\n const nameSimilarity = 1 - nameDistance / maxNameLength;\r\n score += nameSimilarity * SIMILARITY_WEIGHTS.NAME;\r\n factors += SIMILARITY_WEIGHTS.NAME;\r\n\r\n // Type similarity (exact match) - use pre-computed lowercase\r\n if (p1.typeLower === p2.typeLower) {\r\n score += SIMILARITY_WEIGHTS.TYPE;\r\n }\r\n factors += SIMILARITY_WEIGHTS.TYPE;\r\n\r\n // Observation overlap (Jaccard similarity) - use pre-computed Sets\r\n const obsIntersectionSize = this.setIntersectionSize(p1.observationSet, p2.observationSet);\r\n const obsUnionSize = p1.observationSet.size + p2.observationSet.size - obsIntersectionSize;\r\n const observationSimilarity = obsUnionSize > 0 ? obsIntersectionSize / obsUnionSize : 0;\r\n score += observationSimilarity * SIMILARITY_WEIGHTS.OBSERVATIONS;\r\n factors += SIMILARITY_WEIGHTS.OBSERVATIONS;\r\n\r\n // Tag overlap (Jaccard similarity) - use pre-computed Sets\r\n if (p1.tagSet.size > 0 || p2.tagSet.size > 0) {\r\n const tagIntersectionSize = this.setIntersectionSize(p1.tagSet, p2.tagSet);\r\n const tagUnionSize = p1.tagSet.size + p2.tagSet.size - tagIntersectionSize;\r\n const tagSimilarity = tagUnionSize > 0 ? tagIntersectionSize / tagUnionSize : 0;\r\n score += tagSimilarity * SIMILARITY_WEIGHTS.TAGS;\r\n factors += SIMILARITY_WEIGHTS.TAGS;\r\n }\r\n\r\n return factors > 0 ? score / factors : 0;\r\n }\r\n\r\n /**\r\n * Find duplicate entities in the graph based on similarity threshold.\r\n *\r\n * OPTIMIZED: Uses bucketing strategies to reduce O(n²) comparisons:\r\n * 1. Buckets entities by entityType (only compare same types)\r\n * 2. Within each type, buckets by name prefix (first 2 chars normalized)\r\n * 3. Only compares entities within same or adjacent buckets\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * Complexity: O(n·k) where k is average bucket size (typically << n)\r\n *\r\n * @param threshold - Similarity threshold (0.0 to 1.0), default DEFAULT_DUPLICATE_THRESHOLD\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Array of duplicate groups (each group has similar entities)\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n */\r\n async findDuplicates(\r\n threshold: number = DEFAULT_DUPLICATE_THRESHOLD,\r\n options?: LongRunningOperationOptions\r\n ): Promise<string[][]> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'findDuplicates');\r\n\r\n const graph = await this.storage.loadGraph();\r\n const duplicateGroups: string[][] = [];\r\n const processed = new Set<string>();\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n const totalEntities = graph.entities.length;\r\n let processedCount = 0;\r\n reportProgress?.(createProgress(0, totalEntities, 'findDuplicates'));\r\n\r\n // OPTIMIZATION: Pre-prepare all entities once before comparisons\r\n const preparedEntities = this.prepareEntities(graph.entities);\r\n\r\n // Step 1: Bucket entities by type (reduces comparisons drastically)\r\n const typeMap = new Map<string, Entity[]>();\r\n for (const entity of graph.entities) {\r\n const normalizedType = entity.entityType.toLowerCase();\r\n if (!typeMap.has(normalizedType)) {\r\n typeMap.set(normalizedType, []);\r\n }\r\n typeMap.get(normalizedType)!.push(entity);\r\n }\r\n\r\n // Step 2: For each type bucket, sub-bucket by name prefix\r\n for (const entities of typeMap.values()) {\r\n // Check for cancellation between type buckets\r\n checkCancellation(options?.signal, 'findDuplicates');\r\n\r\n // Skip single-entity types (no duplicates possible)\r\n if (entities.length < 2) {\r\n processedCount += entities.length;\r\n reportProgress?.(createProgress(processedCount, totalEntities, 'findDuplicates'));\r\n continue;\r\n }\r\n\r\n // Create name prefix buckets (first 2 chars, normalized)\r\n const prefixMap = new Map<string, Entity[]>();\r\n for (const entity of entities) {\r\n const prefix = entity.name.toLowerCase().slice(0, 2);\r\n if (!prefixMap.has(prefix)) {\r\n prefixMap.set(prefix, []);\r\n }\r\n prefixMap.get(prefix)!.push(entity);\r\n }\r\n\r\n // Step 3: Compare only within buckets (or adjacent buckets for fuzzy matching)\r\n const prefixKeys = Array.from(prefixMap.keys()).sort();\r\n\r\n for (let bucketIdx = 0; bucketIdx < prefixKeys.length; bucketIdx++) {\r\n // Check for cancellation between prefix buckets\r\n checkCancellation(options?.signal, 'findDuplicates');\r\n\r\n const currentPrefix = prefixKeys[bucketIdx];\r\n const currentBucket = prefixMap.get(currentPrefix)!;\r\n\r\n // Collect entities to compare: current bucket + adjacent buckets\r\n const candidateEntities: Entity[] = [...currentBucket];\r\n\r\n // Add next bucket if exists (handles fuzzy prefix matching)\r\n if (bucketIdx + 1 < prefixKeys.length) {\r\n candidateEntities.push(...prefixMap.get(prefixKeys[bucketIdx + 1])!);\r\n }\r\n\r\n // Compare entities within candidate pool\r\n for (let i = 0; i < currentBucket.length; i++) {\r\n const entity1 = currentBucket[i];\r\n if (processed.has(entity1.name)) continue;\r\n\r\n // OPTIMIZATION: Use prepared entity for comparison\r\n const prepared1 = preparedEntities.get(entity1.name)!;\r\n const group: string[] = [entity1.name];\r\n\r\n for (let j = 0; j < candidateEntities.length; j++) {\r\n const entity2 = candidateEntities[j];\r\n if (entity1.name === entity2.name || processed.has(entity2.name)) continue;\r\n\r\n // OPTIMIZATION: Use prepared entity and optimized similarity\r\n const prepared2 = preparedEntities.get(entity2.name)!;\r\n const similarity = this.calculatePreparedSimilarity(prepared1, prepared2);\r\n if (similarity >= threshold) {\r\n group.push(entity2.name);\r\n processed.add(entity2.name);\r\n }\r\n }\r\n\r\n if (group.length > 1) {\r\n duplicateGroups.push(group);\r\n processed.add(entity1.name);\r\n }\r\n\r\n processedCount++;\r\n reportProgress?.(createProgress(processedCount, totalEntities, 'findDuplicates'));\r\n }\r\n }\r\n }\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(totalEntities, totalEntities, 'findDuplicates'));\r\n\r\n return duplicateGroups;\r\n }\r\n\r\n /**\r\n * Merge a group of entities into a single entity.\r\n *\r\n * Merging strategy:\r\n * - First entity is kept (or renamed to targetName)\r\n * - Observations: Union of all observations\r\n * - Tags: Union of all tags\r\n * - Importance: Maximum importance value\r\n * - createdAt: Earliest date\r\n * - lastModified: Current timestamp\r\n * - Relations: Redirected to kept entity, duplicates removed\r\n *\r\n * @param entityNames - Names of entities to merge (first one is kept)\r\n * @param targetName - Optional new name for merged entity (default: first entity name)\r\n * @param options - Optional configuration\r\n * @param options.graph - Pre-loaded graph to use (avoids reload)\r\n * @param options.skipSave - If true, don't save (caller will save)\r\n * @returns The merged entity\r\n * @throws {InsufficientEntitiesError} If less than 2 entities provided\r\n * @throws {EntityNotFoundError} If any entity not found\r\n */\r\n async mergeEntities(\r\n entityNames: string[],\r\n targetName?: string,\r\n options: {\r\n graph?: KnowledgeGraph;\r\n skipSave?: boolean;\r\n } = {}\r\n ): Promise<Entity> {\r\n if (entityNames.length < 2) {\r\n throw new InsufficientEntitiesError('merging', 2, entityNames.length);\r\n }\r\n\r\n // Use provided graph or load fresh\r\n const graph = options.graph ?? await this.storage.getGraphForMutation();\r\n const entitiesToMerge = entityNames.map(name => {\r\n const entity = graph.entities.find(e => e.name === name);\r\n if (!entity) {\r\n throw new EntityNotFoundError(name);\r\n }\r\n return entity;\r\n });\r\n\r\n const keepEntity = entitiesToMerge[0];\r\n const mergeEntities = entitiesToMerge.slice(1);\r\n\r\n // Merge observations (unique)\r\n const allObservations = new Set<string>();\r\n for (const entity of entitiesToMerge) {\r\n entity.observations.forEach(obs => allObservations.add(obs));\r\n }\r\n keepEntity.observations = Array.from(allObservations);\r\n\r\n // Merge tags (unique)\r\n const allTags = new Set<string>();\r\n for (const entity of entitiesToMerge) {\r\n if (entity.tags) {\r\n entity.tags.forEach(tag => allTags.add(tag));\r\n }\r\n }\r\n if (allTags.size > 0) {\r\n keepEntity.tags = Array.from(allTags);\r\n }\r\n\r\n // Use highest importance\r\n const importances = entitiesToMerge\r\n .map(e => e.importance)\r\n .filter(imp => imp !== undefined) as number[];\r\n if (importances.length > 0) {\r\n keepEntity.importance = Math.max(...importances);\r\n }\r\n\r\n // Use earliest createdAt\r\n const createdDates = entitiesToMerge\r\n .map(e => e.createdAt)\r\n .filter(date => date !== undefined) as string[];\r\n if (createdDates.length > 0) {\r\n keepEntity.createdAt = createdDates.sort()[0];\r\n }\r\n\r\n // Update lastModified\r\n keepEntity.lastModified = new Date().toISOString();\r\n\r\n // Rename if requested\r\n if (targetName && targetName !== keepEntity.name) {\r\n // Update all relations pointing to old name\r\n graph.relations.forEach(rel => {\r\n if (rel.from === keepEntity.name) rel.from = targetName;\r\n if (rel.to === keepEntity.name) rel.to = targetName;\r\n });\r\n keepEntity.name = targetName;\r\n }\r\n\r\n // Update relations from merged entities to point to kept entity\r\n for (const mergeEntity of mergeEntities) {\r\n graph.relations.forEach(rel => {\r\n if (rel.from === mergeEntity.name) rel.from = keepEntity.name;\r\n if (rel.to === mergeEntity.name) rel.to = keepEntity.name;\r\n });\r\n }\r\n\r\n // Remove duplicate relations\r\n const uniqueRelations = new Map<string, Relation>();\r\n for (const relation of graph.relations) {\r\n const key = `${relation.from}|${relation.to}|${relation.relationType}`;\r\n if (!uniqueRelations.has(key)) {\r\n uniqueRelations.set(key, relation);\r\n }\r\n }\r\n graph.relations = Array.from(uniqueRelations.values());\r\n\r\n // Remove merged entities\r\n const mergeNames = new Set(mergeEntities.map(e => e.name));\r\n graph.entities = graph.entities.filter(e => !mergeNames.has(e.name));\r\n\r\n // Save unless caller said to skip\r\n if (!options.skipSave) {\r\n await this.storage.saveGraph(graph);\r\n }\r\n return keepEntity;\r\n }\r\n\r\n /**\r\n * Compress the knowledge graph by finding and merging duplicates.\r\n * OPTIMIZED: Loads graph once, performs all merges, saves once.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.\r\n *\r\n * @param threshold - Similarity threshold for duplicate detection (0.0 to 1.0), default DEFAULT_DUPLICATE_THRESHOLD\r\n * @param dryRun - If true, only report what would be compressed without applying changes\r\n * @param options - Optional progress/cancellation options (Phase 9B)\r\n * @returns Compression result with statistics\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n */\r\n async compressGraph(\r\n threshold: number = DEFAULT_DUPLICATE_THRESHOLD,\r\n dryRun: boolean = false,\r\n options?: LongRunningOperationOptions\r\n ): Promise<GraphCompressionResult> {\r\n // Check for early cancellation\r\n checkCancellation(options?.signal, 'compressGraph');\r\n\r\n // Setup progress reporter (we'll use phases: 50% finding duplicates, 50% merging)\r\n const reportProgress = createProgressReporter(options?.onProgress);\r\n reportProgress?.(createProgress(0, 100, 'compressGraph'));\r\n\r\n // Phase 1: Find duplicates (0-50% progress)\r\n const duplicateGroups = await this.findDuplicates(threshold, {\r\n signal: options?.signal,\r\n onProgress: (p) => {\r\n // Map findDuplicates progress (0-100%) to compressGraph progress (0-50%)\r\n const compressProgress = Math.round(p.percentage * 0.5);\r\n reportProgress?.(createProgress(compressProgress, 100, 'finding duplicates'));\r\n },\r\n });\r\n\r\n // Check for cancellation after finding duplicates\r\n checkCancellation(options?.signal, 'compressGraph');\r\n reportProgress?.(createProgress(50, 100, 'compressGraph'));\r\n\r\n // OPTIMIZATION: Load graph once for all operations\r\n const graph = await this.storage.getGraphForMutation();\r\n const initialSize = JSON.stringify(graph).length;\r\n const result: GraphCompressionResult = {\r\n duplicatesFound: duplicateGroups.reduce((sum, group) => sum + group.length, 0),\r\n entitiesMerged: 0,\r\n observationsCompressed: 0,\r\n relationsConsolidated: 0,\r\n spaceFreed: 0,\r\n mergedEntities: [],\r\n };\r\n\r\n if (dryRun) {\r\n // Just report what would happen\r\n for (const group of duplicateGroups) {\r\n result.mergedEntities.push({\r\n kept: group[0],\r\n merged: group.slice(1),\r\n });\r\n result.entitiesMerged += group.length - 1;\r\n }\r\n reportProgress?.(createProgress(100, 100, 'compressGraph'));\r\n return result;\r\n }\r\n\r\n // Phase 2: Merge duplicates (50-100% progress)\r\n const totalGroups = duplicateGroups.length;\r\n let mergedGroups = 0;\r\n\r\n // OPTIMIZATION: Build entity lookup map for O(1) access during merges\r\n const entityMap = new Map<string, Entity>();\r\n for (const entity of graph.entities) {\r\n entityMap.set(entity.name, entity);\r\n }\r\n\r\n // Merge all duplicates using the same graph instance\r\n for (const group of duplicateGroups) {\r\n // Check for cancellation between merges\r\n checkCancellation(options?.signal, 'compressGraph');\r\n\r\n try {\r\n // Count observations before merge using O(1) lookup\r\n let totalObservationsBefore = 0;\r\n for (const name of group) {\r\n const entity = entityMap.get(name);\r\n if (entity) {\r\n totalObservationsBefore += entity.observations.length;\r\n }\r\n }\r\n\r\n // OPTIMIZATION: Pass graph and skip individual saves\r\n const mergedEntity = await this.mergeEntities(group, undefined, {\r\n graph,\r\n skipSave: true,\r\n });\r\n\r\n const observationsAfter = mergedEntity.observations.length;\r\n result.observationsCompressed += totalObservationsBefore - observationsAfter;\r\n\r\n result.mergedEntities.push({\r\n kept: group[0],\r\n merged: group.slice(1),\r\n });\r\n result.entitiesMerged += group.length - 1;\r\n } catch (error) {\r\n // Skip groups that fail to merge\r\n console.error(`Failed to merge group ${group}:`, error);\r\n }\r\n\r\n mergedGroups++;\r\n // Map merge progress (0-100%) to compressGraph progress (50-100%)\r\n const mergeProgress = totalGroups > 0 ? Math.round(50 + (mergedGroups / totalGroups) * 50) : 100;\r\n reportProgress?.(createProgress(mergeProgress, 100, 'merging entities'));\r\n }\r\n\r\n // Check for cancellation before final save\r\n checkCancellation(options?.signal, 'compressGraph');\r\n\r\n // OPTIMIZATION: Save once after all merges complete\r\n await this.storage.saveGraph(graph);\r\n\r\n const finalSize = JSON.stringify(graph).length;\r\n result.spaceFreed = initialSize - finalSize;\r\n result.relationsConsolidated = result.entitiesMerged;\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(100, 100, 'compressGraph'));\r\n\r\n return result;\r\n }\r\n}\r\n","/**\r\n * Archive Manager\r\n *\r\n * Handles archiving (removal) of entities based on criteria.\r\n * Archives are stored as compressed files for space-efficient long-term storage.\r\n * Extracted from EntityManager (Phase 4: Consolidate God Objects).\r\n * Enhanced with brotli compression in Phase 3 Sprint 5.\r\n *\r\n * @module features/ArchiveManager\r\n */\r\n\r\nimport { promises as fs } from 'fs';\r\nimport { dirname, join } from 'path';\r\nimport type { Entity, LongRunningOperationOptions } from '../types/index.js';\r\nimport type { GraphStorage } from '../core/GraphStorage.js';\r\nimport {\r\n compress,\r\n COMPRESSION_CONFIG,\r\n checkCancellation,\r\n createProgressReporter,\r\n createProgress,\r\n} from '../utils/index.js';\r\n\r\n/**\r\n * Criteria for archiving entities.\r\n */\r\nexport interface ArchiveCriteria {\r\n /** Entities older than this date (ISO 8601) */\r\n olderThan?: string;\r\n /** Entities with importance less than this value */\r\n importanceLessThan?: number;\r\n /** Entities with any of these tags */\r\n tags?: string[];\r\n}\r\n\r\n/**\r\n * Options for archive operations.\r\n * Phase 9B: Extended with LongRunningOperationOptions.\r\n */\r\nexport interface ArchiveOptions extends LongRunningOperationOptions {\r\n /** Dry run mode - preview without making changes */\r\n dryRun?: boolean;\r\n /** Whether to save archived entities to a compressed file (default: true) */\r\n saveToFile?: boolean;\r\n}\r\n\r\n/**\r\n * Result of archive operation.\r\n * Extends ArchiveResultExtended with compression statistics.\r\n */\r\nexport interface ArchiveResult {\r\n /** Number of entities archived */\r\n archived: number;\r\n /** Names of archived entities */\r\n entityNames: string[];\r\n /** Path to the archive file (if created) */\r\n archivePath?: string;\r\n /** Original size of archive data in bytes */\r\n originalSize?: number;\r\n /** Compressed size in bytes */\r\n compressedSize?: number;\r\n /** Compression ratio (compressedSize / originalSize). Lower is better. */\r\n compressionRatio?: number;\r\n}\r\n\r\n/**\r\n * Manages archive operations for the knowledge graph.\r\n *\r\n * Archives are stored as brotli-compressed files in the `.archives` directory.\r\n * Maximum compression quality is used for optimal long-term storage.\r\n */\r\nexport class ArchiveManager {\r\n private readonly archiveDir: string;\r\n\r\n constructor(private storage: GraphStorage) {\r\n const filePath = this.storage.getFilePath();\r\n const dir = dirname(filePath);\r\n this.archiveDir = join(dir, '.archives');\r\n }\r\n\r\n /**\r\n * Archive old or low-importance entities.\r\n *\r\n * Entities matching ANY of the criteria are archived:\r\n * - lastModified older than olderThan date\r\n * - importance less than importanceLessThan\r\n * - has at least one tag from tags array\r\n *\r\n * By default, archived entities are saved to a compressed file before\r\n * being removed from the active graph. Use `saveToFile: false` to\r\n * skip creating the archive file.\r\n *\r\n * Phase 9B: Supports progress tracking and cancellation via options.\r\n *\r\n * @param criteria - Archiving criteria\r\n * @param options - Archive options (dryRun, saveToFile, onProgress, signal)\r\n * @returns Archive result with count, entity names, and compression stats\r\n * @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)\r\n *\r\n * @example\r\n * ```typescript\r\n * // Archive old entities with compression\r\n * const result = await manager.archiveEntities({\r\n * olderThan: '2023-01-01T00:00:00Z',\r\n * importanceLessThan: 3\r\n * });\r\n * console.log(`Archived ${result.archived} entities`);\r\n * console.log(`Compressed from ${result.originalSize} to ${result.compressedSize} bytes`);\r\n *\r\n * // Preview without making changes\r\n * const preview = await manager.archiveEntities(criteria, { dryRun: true });\r\n *\r\n * // With progress tracking and cancellation (Phase 9B)\r\n * const controller = new AbortController();\r\n * const result = await manager.archiveEntities(criteria, {\r\n * signal: controller.signal,\r\n * onProgress: (p) => console.log(`${p.percentage}% complete`),\r\n * });\r\n * ```\r\n */\r\n async archiveEntities(\r\n criteria: ArchiveCriteria,\r\n options: ArchiveOptions | boolean = {}\r\n ): Promise<ArchiveResult> {\r\n // Handle legacy boolean argument (backward compatibility)\r\n const opts: ArchiveOptions = typeof options === 'boolean'\r\n ? { dryRun: options, saveToFile: true }\r\n : { saveToFile: true, ...options };\r\n\r\n // Check for early cancellation\r\n checkCancellation(opts.signal, 'archiveEntities');\r\n\r\n // Setup progress reporter\r\n const reportProgress = createProgressReporter(opts.onProgress);\r\n reportProgress?.(createProgress(0, 100, 'archiveEntities'));\r\n\r\n // Use read-only graph for analysis\r\n const readGraph = await this.storage.loadGraph();\r\n const toArchive: Entity[] = [];\r\n const totalEntities = readGraph.entities.length;\r\n let processedEntities = 0;\r\n\r\n // Phase 1: Identify entities to archive (0-40% progress)\r\n reportProgress?.(createProgress(5, 100, 'analyzing entities'));\r\n\r\n for (const entity of readGraph.entities) {\r\n // Check for cancellation periodically\r\n checkCancellation(opts.signal, 'archiveEntities');\r\n\r\n let shouldArchive = false;\r\n\r\n // Check age criteria\r\n if (criteria.olderThan && entity.lastModified) {\r\n const entityDate = new Date(entity.lastModified);\r\n const cutoffDate = new Date(criteria.olderThan);\r\n if (entityDate < cutoffDate) {\r\n shouldArchive = true;\r\n }\r\n }\r\n\r\n // Check importance criteria\r\n if (criteria.importanceLessThan !== undefined) {\r\n if (entity.importance === undefined || entity.importance < criteria.importanceLessThan) {\r\n shouldArchive = true;\r\n }\r\n }\r\n\r\n // Check tag criteria (must have at least one matching tag)\r\n if (criteria.tags && criteria.tags.length > 0) {\r\n const normalizedCriteriaTags = criteria.tags.map(t => t.toLowerCase());\r\n const entityTags = (entity.tags || []).map(t => t.toLowerCase());\r\n const hasMatchingTag = normalizedCriteriaTags.some(tag => entityTags.includes(tag));\r\n if (hasMatchingTag) {\r\n shouldArchive = true;\r\n }\r\n }\r\n\r\n if (shouldArchive) {\r\n toArchive.push(entity);\r\n }\r\n\r\n processedEntities++;\r\n // Map analysis progress (0-100%) to overall progress (0-40%)\r\n const analysisProgress = totalEntities > 0 ? Math.round((processedEntities / totalEntities) * 40) : 40;\r\n reportProgress?.(createProgress(analysisProgress, 100, 'analyzing entities'));\r\n }\r\n\r\n reportProgress?.(createProgress(40, 100, 'analysis complete'));\r\n\r\n // Dry run - return preview without changes\r\n if (opts.dryRun) {\r\n reportProgress?.(createProgress(100, 100, 'archiveEntities'));\r\n return {\r\n archived: toArchive.length,\r\n entityNames: toArchive.map(e => e.name),\r\n };\r\n }\r\n\r\n // No entities to archive\r\n if (toArchive.length === 0) {\r\n reportProgress?.(createProgress(100, 100, 'archiveEntities'));\r\n return {\r\n archived: 0,\r\n entityNames: [],\r\n };\r\n }\r\n\r\n // Check for cancellation before archiving\r\n checkCancellation(opts.signal, 'archiveEntities');\r\n\r\n // Phase 2: Save to compressed archive file (40-80% progress)\r\n let archivePath: string | undefined;\r\n let originalSize: number | undefined;\r\n let compressedSize: number | undefined;\r\n let compressionRatio: number | undefined;\r\n\r\n if (opts.saveToFile) {\r\n reportProgress?.(createProgress(50, 100, 'compressing archive'));\r\n const archiveResult = await this.saveToArchive(toArchive);\r\n archivePath = archiveResult.archivePath;\r\n originalSize = archiveResult.originalSize;\r\n compressedSize = archiveResult.compressedSize;\r\n compressionRatio = archiveResult.compressionRatio;\r\n reportProgress?.(createProgress(80, 100, 'archive saved'));\r\n } else {\r\n reportProgress?.(createProgress(80, 100, 'skipped archive file'));\r\n }\r\n\r\n // Check for cancellation before graph modification\r\n checkCancellation(opts.signal, 'archiveEntities');\r\n\r\n // Phase 3: Remove from main graph (80-100% progress)\r\n reportProgress?.(createProgress(85, 100, 'updating graph'));\r\n\r\n // Get mutable copy for write operation\r\n const graph = await this.storage.getGraphForMutation();\r\n\r\n // Remove archived entities from main graph\r\n const archiveNames = new Set(toArchive.map(e => e.name));\r\n graph.entities = graph.entities.filter(e => !archiveNames.has(e.name));\r\n graph.relations = graph.relations.filter(\r\n r => !archiveNames.has(r.from) && !archiveNames.has(r.to)\r\n );\r\n await this.storage.saveGraph(graph);\r\n\r\n // Report completion\r\n reportProgress?.(createProgress(100, 100, 'archiveEntities'));\r\n\r\n return {\r\n archived: toArchive.length,\r\n entityNames: toArchive.map(e => e.name),\r\n archivePath,\r\n originalSize,\r\n compressedSize,\r\n compressionRatio,\r\n };\r\n }\r\n\r\n /**\r\n * Save entities to a compressed archive file.\r\n *\r\n * Creates a brotli-compressed file in the `.archives` directory\r\n * with maximum compression quality for space efficiency.\r\n *\r\n * @param entities - Entities to archive\r\n * @returns Archive file path and compression statistics\r\n */\r\n private async saveToArchive(entities: Entity[]): Promise<{\r\n archivePath: string;\r\n originalSize: number;\r\n compressedSize: number;\r\n compressionRatio: number;\r\n }> {\r\n // Ensure archive directory exists\r\n await fs.mkdir(this.archiveDir, { recursive: true });\r\n\r\n // Generate timestamp-based filename\r\n const timestamp = new Date().toISOString()\r\n .replace(/:/g, '-')\r\n .replace(/\\./g, '-')\r\n .replace('T', '_')\r\n .replace('Z', '');\r\n const archivePath = join(this.archiveDir, `archive_${timestamp}.jsonl.br`);\r\n\r\n // Serialize entities to JSONL format\r\n const content = entities.map(e => JSON.stringify(e)).join('\\n');\r\n\r\n // Compress with maximum quality for archives\r\n const compressionResult = await compress(content, {\r\n quality: COMPRESSION_CONFIG.BROTLI_QUALITY_ARCHIVE,\r\n mode: 'text',\r\n });\r\n\r\n // Write compressed archive\r\n await fs.writeFile(archivePath, compressionResult.compressed);\r\n\r\n // Write metadata file\r\n const metadataPath = `${archivePath}.meta.json`;\r\n const metadata = {\r\n timestamp: new Date().toISOString(),\r\n entityCount: entities.length,\r\n entityNames: entities.map(e => e.name),\r\n compressed: true,\r\n compressionFormat: 'brotli',\r\n originalSize: compressionResult.originalSize,\r\n compressedSize: compressionResult.compressedSize,\r\n compressionRatio: compressionResult.ratio,\r\n };\r\n await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));\r\n\r\n return {\r\n archivePath,\r\n originalSize: compressionResult.originalSize,\r\n compressedSize: compressionResult.compressedSize,\r\n compressionRatio: compressionResult.ratio,\r\n };\r\n }\r\n\r\n /**\r\n * List all available archives.\r\n *\r\n * @returns Array of archive information with compression details\r\n */\r\n async listArchives(): Promise<Array<{\r\n fileName: string;\r\n filePath: string;\r\n timestamp: string;\r\n entityCount: number;\r\n compressed: boolean;\r\n originalSize?: number;\r\n compressedSize?: number;\r\n compressionRatio?: number;\r\n }>> {\r\n try {\r\n try {\r\n await fs.access(this.archiveDir);\r\n } catch {\r\n return [];\r\n }\r\n\r\n const files = await fs.readdir(this.archiveDir);\r\n const archiveFiles = files.filter(f =>\r\n f.startsWith('archive_') &&\r\n (f.endsWith('.jsonl') || f.endsWith('.jsonl.br')) &&\r\n !f.endsWith('.meta.json')\r\n );\r\n\r\n const archives: Array<{\r\n fileName: string;\r\n filePath: string;\r\n timestamp: string;\r\n entityCount: number;\r\n compressed: boolean;\r\n originalSize?: number;\r\n compressedSize?: number;\r\n compressionRatio?: number;\r\n }> = [];\r\n\r\n for (const fileName of archiveFiles) {\r\n const filePath = join(this.archiveDir, fileName);\r\n const metadataPath = `${filePath}.meta.json`;\r\n\r\n try {\r\n const metadataContent = await fs.readFile(metadataPath, 'utf-8');\r\n const metadata = JSON.parse(metadataContent);\r\n\r\n archives.push({\r\n fileName,\r\n filePath,\r\n timestamp: metadata.timestamp,\r\n entityCount: metadata.entityCount,\r\n compressed: metadata.compressed ?? fileName.endsWith('.br'),\r\n originalSize: metadata.originalSize,\r\n compressedSize: metadata.compressedSize,\r\n compressionRatio: metadata.compressionRatio,\r\n });\r\n } catch {\r\n // Skip archives without valid metadata\r\n continue;\r\n }\r\n }\r\n\r\n // Sort by timestamp (newest first)\r\n archives.sort((a, b) =>\r\n new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()\r\n );\r\n\r\n return archives;\r\n } catch {\r\n return [];\r\n }\r\n }\r\n\r\n /**\r\n * Get the path to the archives directory.\r\n */\r\n getArchiveDir(): string {\r\n return this.archiveDir;\r\n }\r\n}\r\n","/**\n * Observation Normalizer\n *\n * Phase 11: Transforms observations to be self-contained facts\n * through coreference resolution and temporal anchoring.\n *\n * @module features/ObservationNormalizer\n */\n\nimport type { Entity } from '../types/index.js';\n\n/**\n * Options for observation normalization.\n */\nexport interface NormalizationOptions {\n /** Resolve pronouns to entity names */\n resolveCoreferences?: boolean;\n /** Convert relative dates to absolute dates */\n anchorTimestamps?: boolean;\n /** Extract and tag keywords */\n extractKeywords?: boolean;\n /** Reference date for relative date conversion (default: now) */\n referenceDate?: Date;\n}\n\n/**\n * Result of normalizing an observation.\n */\nexport interface NormalizationResult {\n original: string;\n normalized: string;\n changes: string[];\n keywords?: string[];\n}\n\n/**\n * Observation Normalizer transforms observations to self-contained facts.\n *\n * Applies transformations:\n * 1. Coreference resolution: 'He works' -> 'Alice works'\n * 2. Temporal anchoring: 'yesterday' -> '2026-01-07'\n * 3. Keyword extraction: Identifies important terms\n *\n * @example\n * ```typescript\n * const normalizer = new ObservationNormalizer();\n * const result = normalizer.normalize(\n * 'He started the project yesterday',\n * { name: 'Bob', entityType: 'person', observations: [] }\n * );\n * // result.normalized = 'Bob started the project on 2026-01-07'\n * ```\n */\nexport class ObservationNormalizer {\n private pronounPatterns = {\n masculine: /\\b(he|him|his)\\b/gi,\n feminine: /\\b(she|her|hers)\\b/gi,\n neutral: /\\b(they|them|their|theirs)\\b/gi,\n };\n\n private relativeTimePatterns: [RegExp, (ref: Date) => string][] = [\n [/\\byesterday\\b/i, (ref) => this.formatDate(this.addDays(ref, -1))],\n [/\\btoday\\b/i, (ref) => this.formatDate(ref)],\n [/\\btomorrow\\b/i, (ref) => this.formatDate(this.addDays(ref, 1))],\n [/\\blast week\\b/i, (ref) => `week of ${this.formatDate(this.addDays(ref, -7))}`],\n [/\\blast month\\b/i, (ref) => this.formatMonth(this.addMonths(ref, -1))],\n [/\\blast year\\b/i, (ref) => `${ref.getFullYear() - 1}`],\n [/\\bthis week\\b/i, (ref) => `week of ${this.formatDate(ref)}`],\n [/\\bthis month\\b/i, (ref) => this.formatMonth(ref)],\n [/\\bthis year\\b/i, (ref) => `${ref.getFullYear()}`],\n ];\n\n /**\n * Normalize an observation for an entity.\n */\n normalize(\n observation: string,\n entity: Entity,\n options: NormalizationOptions = {}\n ): NormalizationResult {\n const {\n resolveCoreferences = true,\n anchorTimestamps = true,\n extractKeywords = false,\n referenceDate = new Date(),\n } = options;\n\n let normalized = observation;\n const changes: string[] = [];\n\n if (resolveCoreferences) {\n const corefResult = this.resolveCoreferences(normalized, entity);\n if (corefResult.changed) {\n normalized = corefResult.text;\n changes.push(`Resolved pronouns to '${entity.name}'`);\n }\n }\n\n if (anchorTimestamps) {\n const timeResult = this.anchorTimestamps(normalized, referenceDate);\n if (timeResult.changed) {\n normalized = timeResult.text;\n changes.push(...timeResult.replacements);\n }\n }\n\n const keywords = extractKeywords\n ? this.extractKeywords(normalized)\n : undefined;\n\n return {\n original: observation,\n normalized,\n changes,\n keywords,\n };\n }\n\n /**\n * Resolve pronouns to entity name.\n */\n resolveCoreferences(\n text: string,\n entity: Entity\n ): { text: string; changed: boolean } {\n let result = text;\n let changed = false;\n\n // Determine gender hint from entity type or name patterns\n const isMasculine = this.guessMasculine(entity);\n const isFeminine = this.guessFeminine(entity);\n\n // Replace pronouns based on detected gender\n if (isMasculine) {\n const newText = result\n .replace(this.pronounPatterns.masculine, entity.name);\n if (newText !== result) {\n result = newText;\n changed = true;\n }\n } else if (isFeminine) {\n const newText = result\n .replace(this.pronounPatterns.feminine, entity.name);\n if (newText !== result) {\n result = newText;\n changed = true;\n }\n }\n\n // Always try neutral pronouns for non-person entities\n if (entity.entityType.toLowerCase() !== 'person') {\n const newText = result\n .replace(this.pronounPatterns.neutral, entity.name);\n if (newText !== result) {\n result = newText;\n changed = true;\n }\n }\n\n return { text: result, changed };\n }\n\n private guessMasculine(entity: Entity): boolean {\n const masculineNames = ['john', 'james', 'bob', 'mike', 'david', 'alex'];\n return masculineNames.some(n => entity.name.toLowerCase().includes(n));\n }\n\n private guessFeminine(entity: Entity): boolean {\n const feminineNames = ['alice', 'jane', 'sarah', 'mary', 'emma', 'lisa'];\n return feminineNames.some(n => entity.name.toLowerCase().includes(n));\n }\n\n /**\n * Convert relative timestamps to absolute dates.\n */\n anchorTimestamps(\n text: string,\n referenceDate: Date\n ): { text: string; changed: boolean; replacements: string[] } {\n let result = text;\n const replacements: string[] = [];\n\n for (const [pattern, resolver] of this.relativeTimePatterns) {\n const match = result.match(pattern);\n if (match) {\n const replacement = resolver(referenceDate);\n result = result.replace(pattern, replacement);\n replacements.push(`'${match[0]}' -> '${replacement}'`);\n }\n }\n\n return {\n text: result,\n changed: replacements.length > 0,\n replacements,\n };\n }\n\n private addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n }\n\n private addMonths(date: Date, months: number): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + months);\n return result;\n }\n\n private formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n }\n\n private formatMonth(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;\n }\n\n /**\n * Extract important keywords from text.\n */\n extractKeywords(text: string): string[] {\n const stopwords = new Set([\n 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been',\n 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',\n 'would', 'could', 'should', 'may', 'might', 'must', 'shall',\n 'can', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by',\n 'from', 'as', 'into', 'through', 'during', 'before', 'after',\n 'above', 'below', 'between', 'under', 'again', 'further',\n 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',\n 'all', 'each', 'few', 'more', 'most', 'other', 'some', 'such',\n 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too',\n 'very', 's', 't', 'just', 'don', 'now', 'and', 'but', 'or',\n ]);\n\n const words = text\n .toLowerCase()\n .replace(/[^a-z0-9\\s]/g, '')\n .split(/\\s+/)\n .filter(w => w.length > 2 && !stopwords.has(w));\n\n // Return unique keywords\n return [...new Set(words)];\n }\n\n /**\n * Normalize all observations for an entity.\n */\n normalizeEntity(\n entity: Entity,\n options: NormalizationOptions = {}\n ): { entity: Entity; results: NormalizationResult[] } {\n const results = entity.observations.map(obs =>\n this.normalize(obs, entity, options)\n );\n\n return {\n entity: {\n ...entity,\n observations: results.map(r => r.normalized),\n },\n results,\n };\n }\n}\n","/**\n * Keyword Extractor\n *\n * Phase 11: Extracts and scores keywords from text\n * for lexical search enhancement.\n *\n * @module features/KeywordExtractor\n */\n\n/**\n * A keyword with importance score.\n */\nexport interface ScoredKeyword {\n keyword: string;\n score: number;\n positions: number[];\n}\n\n/**\n * Keyword Extractor extracts and scores keywords from text.\n *\n * Features:\n * - Position-based scoring (earlier = more important)\n * - Domain-specific keyword boosting\n * - Length-based scoring (longer words often more specific)\n * - Stopword filtering\n *\n * @example\n * ```typescript\n * const extractor = new KeywordExtractor();\n * const keywords = extractor.extract('The software project was completed on time');\n * // Returns scored keywords sorted by importance\n * ```\n */\nexport class KeywordExtractor {\n private stopwords: Set<string>;\n private domainBoosts: Map<string, number>;\n\n constructor() {\n this.stopwords = new Set([\n 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been',\n 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',\n 'could', 'should', 'can', 'to', 'of', 'in', 'for', 'on',\n 'with', 'at', 'by', 'from', 'as', 'and', 'or', 'but',\n ]);\n this.domainBoosts = new Map([\n ['project', 1.5],\n ['task', 1.5],\n ['meeting', 1.3],\n ['deadline', 1.4],\n ['completed', 1.2],\n ['started', 1.2],\n ['person', 1.3],\n ['company', 1.3],\n ['team', 1.3],\n ['release', 1.4],\n ['feature', 1.3],\n ['bug', 1.2],\n ['issue', 1.2],\n ['milestone', 1.4],\n ]);\n }\n\n /**\n * Extract keywords with scores from text.\n */\n extract(text: string): ScoredKeyword[] {\n const words = this.tokenize(text);\n const keywordMap = new Map<string, ScoredKeyword>();\n\n for (let i = 0; i < words.length; i++) {\n const word = words[i].toLowerCase();\n if (this.isKeyword(word)) {\n const existing = keywordMap.get(word);\n if (existing) {\n existing.positions.push(i);\n existing.score += this.calculateScore(word, i, words.length);\n } else {\n keywordMap.set(word, {\n keyword: word,\n score: this.calculateScore(word, i, words.length),\n positions: [i],\n });\n }\n }\n }\n\n return Array.from(keywordMap.values())\n .sort((a, b) => b.score - a.score);\n }\n\n private tokenize(text: string): string[] {\n return text\n .replace(/[^a-zA-Z0-9\\s]/g, ' ')\n .split(/\\s+/)\n .filter(w => w.length > 0);\n }\n\n private isKeyword(word: string): boolean {\n return word.length > 2 && !this.stopwords.has(word);\n }\n\n private calculateScore(word: string, position: number, totalWords: number): number {\n let score = 1.0;\n\n // Position boost (earlier = more important)\n const positionFactor = 1 - (position / totalWords) * 0.3;\n score *= positionFactor;\n\n // Domain boost\n const boost = this.domainBoosts.get(word) ?? 1.0;\n score *= boost;\n\n // Length boost (longer words often more specific)\n if (word.length > 6) score *= 1.1;\n\n return score;\n }\n\n /**\n * Extract top N keywords.\n */\n extractTop(text: string, n: number): string[] {\n return this.extract(text)\n .slice(0, n)\n .map(k => k.keyword);\n }\n\n /**\n * Add custom domain boost for a keyword.\n */\n addDomainBoost(keyword: string, boost: number): void {\n this.domainBoosts.set(keyword.toLowerCase(), boost);\n }\n\n /**\n * Remove a domain boost.\n */\n removeDomainBoost(keyword: string): boolean {\n return this.domainBoosts.delete(keyword.toLowerCase());\n }\n\n /**\n * Get all domain boosts.\n */\n getDomainBoosts(): Map<string, number> {\n return new Map(this.domainBoosts);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKA,IAAM,mBAAmB,MACvB,OAAO,aAAa,cAChB,IAAI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","names":["fs","import_zlib","document","path","import_fs","path","fs","workerpool","import_workerpool","validateFunction","TaskPriority","TaskStatus","workerpool","processBatch","import_workerpool","workerpool","import_fs","import_fs","import_path","import_fs","fs","OperationType","fs","import_async_mutex","Database","MIN_IMPORTANCE","MAX_IMPORTANCE","entityExists","import_path","path","DEFAULT_OPTIONS","path","fs","path","document","import_workerpool","import_url","import_path","workerpool","fs","DEFAULT_OPTIONS","b","list","normalized","DEFAULT_OPTIONS","DEFAULT_OPTIONS","DEFAULT_OPTIONS","fs","import_fs","import_path","hasMatchingTag","fs","path"]}
|