@soulcraft/brainy 3.0.0 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (260) hide show
  1. package/CHANGELOG.md +53 -3
  2. package/README.md +353 -110
  3. package/bin/brainy.js +340 -62
  4. package/dist/api/ConfigAPI.d.ts +67 -0
  5. package/dist/api/ConfigAPI.js +166 -0
  6. package/dist/api/DataAPI.d.ts +123 -0
  7. package/dist/api/DataAPI.js +391 -0
  8. package/dist/api/SecurityAPI.d.ts +50 -0
  9. package/dist/api/SecurityAPI.js +139 -0
  10. package/dist/api/UniversalImportAPI.d.ts +134 -0
  11. package/dist/api/UniversalImportAPI.js +615 -0
  12. package/dist/augmentationManager.js +12 -7
  13. package/dist/augmentationPipeline.d.ts +0 -61
  14. package/dist/augmentationPipeline.js +0 -87
  15. package/dist/augmentationRegistry.d.ts +1 -1
  16. package/dist/augmentationRegistry.js +1 -1
  17. package/dist/augmentations/apiServerAugmentation.d.ts +27 -1
  18. package/dist/augmentations/apiServerAugmentation.js +290 -9
  19. package/dist/augmentations/auditLogAugmentation.d.ts +109 -0
  20. package/dist/augmentations/auditLogAugmentation.js +358 -0
  21. package/dist/augmentations/batchProcessingAugmentation.d.ts +3 -2
  22. package/dist/augmentations/batchProcessingAugmentation.js +123 -22
  23. package/dist/augmentations/brainyAugmentation.d.ts +142 -8
  24. package/dist/augmentations/brainyAugmentation.js +179 -2
  25. package/dist/augmentations/cacheAugmentation.d.ts +8 -5
  26. package/dist/augmentations/cacheAugmentation.js +116 -17
  27. package/dist/augmentations/conduitAugmentations.d.ts +2 -2
  28. package/dist/augmentations/conduitAugmentations.js +2 -2
  29. package/dist/augmentations/configResolver.d.ts +122 -0
  30. package/dist/augmentations/configResolver.js +440 -0
  31. package/dist/augmentations/connectionPoolAugmentation.d.ts +3 -1
  32. package/dist/augmentations/connectionPoolAugmentation.js +37 -12
  33. package/dist/augmentations/defaultAugmentations.d.ts +14 -10
  34. package/dist/augmentations/defaultAugmentations.js +16 -11
  35. package/dist/augmentations/discovery/catalogDiscovery.d.ts +142 -0
  36. package/dist/augmentations/discovery/catalogDiscovery.js +249 -0
  37. package/dist/augmentations/discovery/localDiscovery.d.ts +84 -0
  38. package/dist/augmentations/discovery/localDiscovery.js +246 -0
  39. package/dist/augmentations/discovery/runtimeLoader.d.ts +97 -0
  40. package/dist/augmentations/discovery/runtimeLoader.js +337 -0
  41. package/dist/augmentations/discovery.d.ts +152 -0
  42. package/dist/augmentations/discovery.js +441 -0
  43. package/dist/augmentations/display/cache.d.ts +130 -0
  44. package/dist/augmentations/display/cache.js +319 -0
  45. package/dist/augmentations/display/fieldPatterns.d.ts +52 -0
  46. package/dist/augmentations/display/fieldPatterns.js +393 -0
  47. package/dist/augmentations/display/iconMappings.d.ts +57 -0
  48. package/dist/augmentations/display/iconMappings.js +68 -0
  49. package/dist/augmentations/display/intelligentComputation.d.ts +109 -0
  50. package/dist/augmentations/display/intelligentComputation.js +462 -0
  51. package/dist/augmentations/display/types.d.ts +203 -0
  52. package/dist/augmentations/display/types.js +7 -0
  53. package/dist/augmentations/entityRegistryAugmentation.d.ts +3 -1
  54. package/dist/augmentations/entityRegistryAugmentation.js +5 -1
  55. package/dist/augmentations/indexAugmentation.d.ts +5 -3
  56. package/dist/augmentations/indexAugmentation.js +5 -2
  57. package/dist/augmentations/intelligentVerbScoringAugmentation.d.ts +24 -7
  58. package/dist/augmentations/intelligentVerbScoringAugmentation.js +111 -27
  59. package/dist/augmentations/manifest.d.ts +176 -0
  60. package/dist/augmentations/manifest.js +8 -0
  61. package/dist/augmentations/marketplace/AugmentationMarketplace.d.ts +168 -0
  62. package/dist/augmentations/marketplace/AugmentationMarketplace.js +329 -0
  63. package/dist/augmentations/marketplace/cli.d.ts +47 -0
  64. package/dist/augmentations/marketplace/cli.js +265 -0
  65. package/dist/augmentations/metricsAugmentation.d.ts +3 -3
  66. package/dist/augmentations/metricsAugmentation.js +2 -2
  67. package/dist/augmentations/monitoringAugmentation.d.ts +3 -3
  68. package/dist/augmentations/monitoringAugmentation.js +2 -2
  69. package/dist/augmentations/neuralImport.d.ts +1 -1
  70. package/dist/augmentations/neuralImport.js +4 -4
  71. package/dist/augmentations/rateLimitAugmentation.d.ts +82 -0
  72. package/dist/augmentations/rateLimitAugmentation.js +321 -0
  73. package/dist/augmentations/requestDeduplicatorAugmentation.d.ts +2 -2
  74. package/dist/augmentations/requestDeduplicatorAugmentation.js +1 -1
  75. package/dist/augmentations/storageAugmentation.d.ts +1 -1
  76. package/dist/augmentations/storageAugmentation.js +2 -2
  77. package/dist/augmentations/storageAugmentations.d.ts +37 -8
  78. package/dist/augmentations/storageAugmentations.js +204 -15
  79. package/dist/augmentations/synapseAugmentation.d.ts +1 -1
  80. package/dist/augmentations/synapseAugmentation.js +35 -16
  81. package/dist/augmentations/typeMatching/brainyTypes.d.ts +83 -0
  82. package/dist/augmentations/typeMatching/brainyTypes.js +425 -0
  83. package/dist/augmentations/typeMatching/intelligentTypeMatcher.d.ts +39 -59
  84. package/dist/augmentations/typeMatching/intelligentTypeMatcher.js +103 -389
  85. package/dist/augmentations/universalDisplayAugmentation.d.ts +191 -0
  86. package/dist/augmentations/universalDisplayAugmentation.js +371 -0
  87. package/dist/brainy-unified.d.ts +106 -0
  88. package/dist/brainy-unified.js +327 -0
  89. package/dist/brainy.d.ts +273 -0
  90. package/dist/brainy.js +1181 -0
  91. package/dist/brainyData.d.ts +56 -111
  92. package/dist/brainyData.js +912 -756
  93. package/dist/brainyDataV3.d.ts +186 -0
  94. package/dist/brainyDataV3.js +337 -0
  95. package/dist/browserFramework.d.ts +6 -6
  96. package/dist/browserFramework.js +11 -8
  97. package/dist/browserFramework.minimal.d.ts +5 -5
  98. package/dist/browserFramework.minimal.js +11 -8
  99. package/dist/config/distributedPresets-new.d.ts +118 -0
  100. package/dist/config/distributedPresets-new.js +318 -0
  101. package/dist/config/distributedPresets.d.ts +118 -0
  102. package/dist/config/distributedPresets.js +318 -0
  103. package/dist/config/extensibleConfig.d.ts +99 -0
  104. package/dist/config/extensibleConfig.js +268 -0
  105. package/dist/config/index.d.ts +17 -0
  106. package/dist/config/index.js +35 -0
  107. package/dist/config/modelAutoConfig.d.ts +32 -0
  108. package/dist/config/modelAutoConfig.js +139 -0
  109. package/dist/config/modelPrecisionManager.d.ts +42 -0
  110. package/dist/config/modelPrecisionManager.js +98 -0
  111. package/dist/config/sharedConfigManager.d.ts +67 -0
  112. package/dist/config/sharedConfigManager.js +215 -0
  113. package/dist/config/storageAutoConfig.d.ts +41 -0
  114. package/dist/config/storageAutoConfig.js +328 -0
  115. package/dist/config/zeroConfig.d.ts +68 -0
  116. package/dist/config/zeroConfig.js +301 -0
  117. package/dist/cortex/backupRestore.d.ts +2 -2
  118. package/dist/cortex/backupRestore.js +85 -27
  119. package/dist/cortex/healthCheck.d.ts +2 -2
  120. package/dist/cortex/neuralImport.d.ts +2 -2
  121. package/dist/cortex/neuralImport.js +18 -13
  122. package/dist/cortex/performanceMonitor.d.ts +2 -2
  123. package/dist/critical/model-guardian.d.ts +4 -0
  124. package/dist/critical/model-guardian.js +31 -11
  125. package/dist/demo.d.ts +4 -4
  126. package/dist/demo.js +7 -7
  127. package/dist/distributed/cacheSync.d.ts +112 -0
  128. package/dist/distributed/cacheSync.js +265 -0
  129. package/dist/distributed/coordinator.d.ts +193 -0
  130. package/dist/distributed/coordinator.js +548 -0
  131. package/dist/distributed/httpTransport.d.ts +120 -0
  132. package/dist/distributed/httpTransport.js +446 -0
  133. package/dist/distributed/index.d.ts +8 -0
  134. package/dist/distributed/index.js +5 -0
  135. package/dist/distributed/networkTransport.d.ts +132 -0
  136. package/dist/distributed/networkTransport.js +633 -0
  137. package/dist/distributed/queryPlanner.d.ts +104 -0
  138. package/dist/distributed/queryPlanner.js +327 -0
  139. package/dist/distributed/readWriteSeparation.d.ts +134 -0
  140. package/dist/distributed/readWriteSeparation.js +350 -0
  141. package/dist/distributed/shardManager.d.ts +114 -0
  142. package/dist/distributed/shardManager.js +357 -0
  143. package/dist/distributed/shardMigration.d.ts +110 -0
  144. package/dist/distributed/shardMigration.js +289 -0
  145. package/dist/distributed/storageDiscovery.d.ts +160 -0
  146. package/dist/distributed/storageDiscovery.js +551 -0
  147. package/dist/embeddings/CachedEmbeddings.d.ts +40 -0
  148. package/dist/embeddings/CachedEmbeddings.js +146 -0
  149. package/dist/embeddings/EmbeddingManager.d.ts +102 -0
  150. package/dist/embeddings/EmbeddingManager.js +291 -0
  151. package/dist/embeddings/SingletonModelManager.d.ts +95 -0
  152. package/dist/embeddings/SingletonModelManager.js +220 -0
  153. package/dist/embeddings/index.d.ts +12 -0
  154. package/dist/embeddings/index.js +16 -0
  155. package/dist/embeddings/lightweight-embedder.d.ts +0 -1
  156. package/dist/embeddings/lightweight-embedder.js +4 -12
  157. package/dist/embeddings/model-manager.d.ts +11 -0
  158. package/dist/embeddings/model-manager.js +43 -7
  159. package/dist/embeddings/universal-memory-manager.d.ts +1 -1
  160. package/dist/embeddings/universal-memory-manager.js +27 -67
  161. package/dist/embeddings/worker-embedding.js +4 -8
  162. package/dist/errors/brainyError.d.ts +5 -1
  163. package/dist/errors/brainyError.js +12 -0
  164. package/dist/examples/basicUsage.js +7 -4
  165. package/dist/graph/graphAdjacencyIndex.d.ts +96 -0
  166. package/dist/graph/graphAdjacencyIndex.js +288 -0
  167. package/dist/graph/pathfinding.js +4 -2
  168. package/dist/hnsw/scaledHNSWSystem.js +11 -2
  169. package/dist/importManager.js +8 -5
  170. package/dist/index.d.ts +17 -22
  171. package/dist/index.js +37 -23
  172. package/dist/mcp/brainyMCPAdapter.d.ts +4 -4
  173. package/dist/mcp/brainyMCPAdapter.js +5 -5
  174. package/dist/mcp/brainyMCPService.d.ts +3 -3
  175. package/dist/mcp/brainyMCPService.js +3 -11
  176. package/dist/mcp/mcpAugmentationToolset.js +20 -30
  177. package/dist/neural/embeddedPatterns.d.ts +1 -1
  178. package/dist/neural/embeddedPatterns.js +2 -2
  179. package/dist/neural/entityExtractor.d.ts +65 -0
  180. package/dist/neural/entityExtractor.js +316 -0
  181. package/dist/neural/improvedNeuralAPI.d.ts +357 -0
  182. package/dist/neural/improvedNeuralAPI.js +2628 -0
  183. package/dist/neural/naturalLanguageProcessor.d.ts +155 -10
  184. package/dist/neural/naturalLanguageProcessor.js +941 -66
  185. package/dist/neural/naturalLanguageProcessorStatic.d.ts +2 -2
  186. package/dist/neural/naturalLanguageProcessorStatic.js +3 -3
  187. package/dist/neural/neuralAPI.js +8 -2
  188. package/dist/neural/patternLibrary.d.ts +57 -3
  189. package/dist/neural/patternLibrary.js +348 -13
  190. package/dist/neural/staticPatternMatcher.d.ts +2 -2
  191. package/dist/neural/staticPatternMatcher.js +2 -2
  192. package/dist/neural/types.d.ts +287 -0
  193. package/dist/neural/types.js +24 -0
  194. package/dist/shared/default-augmentations.d.ts +3 -3
  195. package/dist/shared/default-augmentations.js +5 -5
  196. package/dist/storage/adapters/baseStorageAdapter.d.ts +42 -0
  197. package/dist/storage/adapters/fileSystemStorage.d.ts +26 -2
  198. package/dist/storage/adapters/fileSystemStorage.js +218 -15
  199. package/dist/storage/adapters/memoryStorage.d.ts +4 -4
  200. package/dist/storage/adapters/memoryStorage.js +17 -12
  201. package/dist/storage/adapters/opfsStorage.d.ts +2 -2
  202. package/dist/storage/adapters/opfsStorage.js +2 -2
  203. package/dist/storage/adapters/s3CompatibleStorage.d.ts +2 -2
  204. package/dist/storage/adapters/s3CompatibleStorage.js +2 -2
  205. package/dist/storage/backwardCompatibility.d.ts +10 -78
  206. package/dist/storage/backwardCompatibility.js +17 -132
  207. package/dist/storage/baseStorage.d.ts +18 -2
  208. package/dist/storage/baseStorage.js +74 -3
  209. package/dist/storage/cacheManager.js +2 -2
  210. package/dist/storage/readOnlyOptimizations.js +8 -3
  211. package/dist/streaming/pipeline.d.ts +154 -0
  212. package/dist/streaming/pipeline.js +551 -0
  213. package/dist/triple/TripleIntelligence.d.ts +25 -110
  214. package/dist/triple/TripleIntelligence.js +4 -574
  215. package/dist/triple/TripleIntelligenceSystem.d.ts +159 -0
  216. package/dist/triple/TripleIntelligenceSystem.js +519 -0
  217. package/dist/types/apiTypes.d.ts +278 -0
  218. package/dist/types/apiTypes.js +33 -0
  219. package/dist/types/brainy.types.d.ts +308 -0
  220. package/dist/types/brainy.types.js +8 -0
  221. package/dist/types/brainyDataInterface.d.ts +5 -8
  222. package/dist/types/brainyDataInterface.js +2 -2
  223. package/dist/types/graphTypes.js +2 -2
  224. package/dist/utils/brainyTypes.d.ts +217 -0
  225. package/dist/utils/brainyTypes.js +261 -0
  226. package/dist/utils/cacheAutoConfig.d.ts +3 -3
  227. package/dist/utils/embedding.d.ts +9 -4
  228. package/dist/utils/embedding.js +89 -26
  229. package/dist/utils/enhancedLogger.d.ts +104 -0
  230. package/dist/utils/enhancedLogger.js +232 -0
  231. package/dist/utils/hybridModelManager.d.ts +19 -28
  232. package/dist/utils/hybridModelManager.js +36 -200
  233. package/dist/utils/index.d.ts +1 -1
  234. package/dist/utils/index.js +1 -1
  235. package/dist/utils/intelligentTypeMapper.d.ts +60 -0
  236. package/dist/utils/intelligentTypeMapper.js +349 -0
  237. package/dist/utils/metadataIndex.d.ts +118 -1
  238. package/dist/utils/metadataIndex.js +539 -16
  239. package/dist/utils/nodeVersionCheck.d.ts +24 -0
  240. package/dist/utils/nodeVersionCheck.js +65 -0
  241. package/dist/utils/paramValidation.d.ts +39 -0
  242. package/dist/utils/paramValidation.js +192 -0
  243. package/dist/utils/rateLimiter.d.ts +160 -0
  244. package/dist/utils/rateLimiter.js +271 -0
  245. package/dist/utils/statistics.d.ts +4 -4
  246. package/dist/utils/statistics.js +3 -3
  247. package/dist/utils/structuredLogger.d.ts +146 -0
  248. package/dist/utils/structuredLogger.js +394 -0
  249. package/dist/utils/textEncoding.js +2 -1
  250. package/dist/utils/typeValidation.d.ts +59 -0
  251. package/dist/utils/typeValidation.js +374 -0
  252. package/dist/utils/version.js +19 -3
  253. package/package.json +15 -4
  254. package/scripts/download-models.cjs +94 -20
  255. package/dist/augmentations/walAugmentation.d.ts +0 -109
  256. package/dist/augmentations/walAugmentation.js +0 -516
  257. package/dist/chat/BrainyChat.d.ts +0 -121
  258. package/dist/chat/BrainyChat.js +0 -396
  259. package/dist/chat/ChatCLI.d.ts +0 -61
  260. package/dist/chat/ChatCLI.js +0 -351
@@ -18,6 +18,17 @@ export class MetadataIndexManager {
18
18
  // Sorted indices for range queries (only for numeric/date fields)
19
19
  this.sortedIndices = new Map();
20
20
  this.numericFields = new Set(); // Track which fields are numeric
21
+ // Cardinality and field statistics tracking
22
+ this.fieldStats = new Map();
23
+ this.cardinalityUpdateInterval = 100; // Update cardinality every N operations
24
+ this.operationCount = 0;
25
+ // Smart normalization thresholds
26
+ this.HIGH_CARDINALITY_THRESHOLD = 1000;
27
+ this.TIMESTAMP_PRECISION_MS = 60000; // 1 minute buckets
28
+ this.FLOAT_PRECISION = 2; // decimal places
29
+ // Type-Field Affinity Tracking for intelligent NLP
30
+ this.typeFieldAffinity = new Map(); // nounType -> field -> count
31
+ this.totalEntitiesByType = new Map(); // nounType -> total count
21
32
  this.storage = storage;
22
33
  this.config = {
23
34
  maxIndexSize: config.maxIndexSize ?? 10000,
@@ -53,10 +64,10 @@ export class MetadataIndexManager {
53
64
  this.sortedIndices.set(field, loaded);
54
65
  }
55
66
  else {
56
- // Create new sorted index
67
+ // Create new sorted index - NOT dirty since we maintain incrementally
57
68
  this.sortedIndices.set(field, {
58
69
  values: [],
59
- isDirty: true,
70
+ isDirty: false, // Clean by default with incremental updates
60
71
  fieldType: 'mixed'
61
72
  });
62
73
  }
@@ -108,6 +119,107 @@ export class MetadataIndexManager {
108
119
  sortedIndex.values = sorted;
109
120
  sortedIndex.isDirty = false;
110
121
  }
122
+ /**
123
+ * Detect field type from value
124
+ */
125
+ detectFieldType(value) {
126
+ if (typeof value === 'number' && !isNaN(value))
127
+ return 'number';
128
+ if (value instanceof Date)
129
+ return 'date';
130
+ return 'string';
131
+ }
132
+ /**
133
+ * Compare two values based on field type for sorting
134
+ */
135
+ compareValues(a, b, fieldType) {
136
+ switch (fieldType) {
137
+ case 'number':
138
+ return a - b;
139
+ case 'date':
140
+ return a.getTime() - b.getTime();
141
+ case 'string':
142
+ default:
143
+ const aStr = String(a);
144
+ const bStr = String(b);
145
+ return aStr < bStr ? -1 : aStr > bStr ? 1 : 0;
146
+ }
147
+ }
148
+ /**
149
+ * Binary search to find insertion position for a value
150
+ * Returns the index where the value should be inserted to maintain sorted order
151
+ */
152
+ findInsertPosition(sortedArray, value, fieldType) {
153
+ if (sortedArray.length === 0)
154
+ return 0;
155
+ let left = 0;
156
+ let right = sortedArray.length - 1;
157
+ while (left <= right) {
158
+ const mid = Math.floor((left + right) / 2);
159
+ const midVal = sortedArray[mid][0];
160
+ const comparison = this.compareValues(midVal, value, fieldType);
161
+ if (comparison < 0) {
162
+ left = mid + 1;
163
+ }
164
+ else if (comparison > 0) {
165
+ right = mid - 1;
166
+ }
167
+ else {
168
+ return mid; // Value already exists at this position
169
+ }
170
+ }
171
+ return left; // Insert position
172
+ }
173
+ /**
174
+ * Incrementally update sorted index when adding an ID
175
+ */
176
+ updateSortedIndexAdd(field, value, id) {
177
+ // Ensure sorted index exists
178
+ if (!this.sortedIndices.has(field)) {
179
+ this.sortedIndices.set(field, {
180
+ values: [],
181
+ isDirty: false,
182
+ fieldType: this.detectFieldType(value)
183
+ });
184
+ }
185
+ const sortedIndex = this.sortedIndices.get(field);
186
+ const normalizedValue = this.normalizeValue(value);
187
+ // Find where this value should be in the sorted array
188
+ const insertPos = this.findInsertPosition(sortedIndex.values, normalizedValue, sortedIndex.fieldType);
189
+ if (insertPos < sortedIndex.values.length &&
190
+ sortedIndex.values[insertPos][0] === normalizedValue) {
191
+ // Value already exists, just add the ID to the existing Set
192
+ sortedIndex.values[insertPos][1].add(id);
193
+ }
194
+ else {
195
+ // New value, insert at the correct position
196
+ sortedIndex.values.splice(insertPos, 0, [normalizedValue, new Set([id])]);
197
+ }
198
+ // Mark as clean since we're maintaining it incrementally
199
+ sortedIndex.isDirty = false;
200
+ }
201
+ /**
202
+ * Incrementally update sorted index when removing an ID
203
+ */
204
+ updateSortedIndexRemove(field, value, id) {
205
+ const sortedIndex = this.sortedIndices.get(field);
206
+ if (!sortedIndex || sortedIndex.values.length === 0)
207
+ return;
208
+ const normalizedValue = this.normalizeValue(value);
209
+ // Binary search to find the value
210
+ const pos = this.findInsertPosition(sortedIndex.values, normalizedValue, sortedIndex.fieldType);
211
+ if (pos < sortedIndex.values.length &&
212
+ sortedIndex.values[pos][0] === normalizedValue) {
213
+ // Remove the ID from the Set
214
+ sortedIndex.values[pos][1].delete(id);
215
+ // If no IDs left for this value, remove the entire entry
216
+ if (sortedIndex.values[pos][1].size === 0) {
217
+ sortedIndex.values.splice(pos, 1);
218
+ }
219
+ }
220
+ // Keep it clean
221
+ sortedIndex.isDirty = false;
222
+ }
111
223
  /**
112
224
  * Binary search for range start (inclusive or exclusive)
113
225
  */
@@ -148,14 +260,126 @@ export class MetadataIndexManager {
148
260
  }
149
261
  return result;
150
262
  }
263
+ /**
264
+ * Update cardinality statistics for a field
265
+ */
266
+ updateCardinalityStats(field, value, operation) {
267
+ // Initialize field stats if needed
268
+ if (!this.fieldStats.has(field)) {
269
+ this.fieldStats.set(field, {
270
+ cardinality: {
271
+ uniqueValues: 0,
272
+ totalValues: 0,
273
+ distribution: 'uniform',
274
+ updateFrequency: 0,
275
+ lastAnalyzed: Date.now()
276
+ },
277
+ queryCount: 0,
278
+ rangeQueryCount: 0,
279
+ exactQueryCount: 0,
280
+ avgQueryTime: 0,
281
+ indexType: 'hash'
282
+ });
283
+ }
284
+ const stats = this.fieldStats.get(field);
285
+ const cardinality = stats.cardinality;
286
+ // Track unique values
287
+ const fieldIndexKey = `${field}:${String(value)}`;
288
+ const entry = this.indexCache.get(fieldIndexKey);
289
+ if (operation === 'add') {
290
+ if (!entry || entry.ids.size === 1) {
291
+ cardinality.uniqueValues++;
292
+ }
293
+ cardinality.totalValues++;
294
+ }
295
+ else if (operation === 'remove') {
296
+ if (entry && entry.ids.size === 0) {
297
+ cardinality.uniqueValues--;
298
+ }
299
+ cardinality.totalValues = Math.max(0, cardinality.totalValues - 1);
300
+ }
301
+ // Update frequency tracking
302
+ cardinality.updateFrequency++;
303
+ // Periodically analyze distribution
304
+ if (++this.operationCount % this.cardinalityUpdateInterval === 0) {
305
+ this.analyzeFieldDistribution(field);
306
+ }
307
+ // Determine optimal index type based on cardinality
308
+ this.updateIndexStrategy(field, stats);
309
+ }
310
+ /**
311
+ * Analyze field distribution for optimization
312
+ */
313
+ analyzeFieldDistribution(field) {
314
+ const stats = this.fieldStats.get(field);
315
+ if (!stats)
316
+ return;
317
+ const cardinality = stats.cardinality;
318
+ const ratio = cardinality.uniqueValues / Math.max(1, cardinality.totalValues);
319
+ // Determine distribution type
320
+ if (ratio > 0.9) {
321
+ cardinality.distribution = 'sparse'; // High uniqueness (like IDs, timestamps)
322
+ }
323
+ else if (ratio < 0.1) {
324
+ cardinality.distribution = 'skewed'; // Low uniqueness (like status, type)
325
+ }
326
+ else {
327
+ cardinality.distribution = 'uniform'; // Balanced distribution
328
+ }
329
+ cardinality.lastAnalyzed = Date.now();
330
+ }
331
+ /**
332
+ * Update index strategy based on field statistics
333
+ */
334
+ updateIndexStrategy(field, stats) {
335
+ const isNumeric = this.numericFields.has(field);
336
+ const hasHighCardinality = stats.cardinality.uniqueValues > this.HIGH_CARDINALITY_THRESHOLD;
337
+ const hasRangeQueries = stats.rangeQueryCount > stats.exactQueryCount * 0.3;
338
+ // Determine optimal index type
339
+ if (isNumeric && hasRangeQueries) {
340
+ stats.indexType = 'both'; // Need both hash and sorted
341
+ }
342
+ else if (hasRangeQueries) {
343
+ stats.indexType = 'sorted';
344
+ }
345
+ else {
346
+ stats.indexType = 'hash';
347
+ }
348
+ // Determine normalization strategy for high cardinality fields
349
+ if (hasHighCardinality) {
350
+ if (field.toLowerCase().includes('time') || field.toLowerCase().includes('date')) {
351
+ stats.normalizationStrategy = 'bucket'; // Time bucketing
352
+ }
353
+ else if (isNumeric) {
354
+ stats.normalizationStrategy = 'precision'; // Reduce float precision
355
+ }
356
+ else {
357
+ stats.normalizationStrategy = 'none'; // Keep as-is for strings
358
+ }
359
+ }
360
+ else {
361
+ stats.normalizationStrategy = 'none';
362
+ }
363
+ }
151
364
  /**
152
365
  * Get IDs matching a range query
153
366
  */
154
367
  async getIdsForRange(field, min, max, includeMin = true, includeMax = true) {
155
- // Ensure sorted index exists and is up to date
368
+ // Track range query for field statistics
369
+ if (this.fieldStats.has(field)) {
370
+ const stats = this.fieldStats.get(field);
371
+ stats.rangeQueryCount++;
372
+ }
373
+ // Ensure sorted index exists
156
374
  await this.ensureSortedIndex(field);
157
- await this.buildSortedIndex(field);
158
- const sortedIndex = this.sortedIndices.get(field);
375
+ // With incremental updates, we should rarely need to rebuild
376
+ // Only rebuild if it's marked dirty (e.g., after a bulk load or migration)
377
+ let sortedIndex = this.sortedIndices.get(field);
378
+ if (sortedIndex?.isDirty) {
379
+ prodLog.warn(`MetadataIndex: Sorted index for field '${field}' was dirty, rebuilding...`);
380
+ await this.buildSortedIndex(field);
381
+ sortedIndex = this.sortedIndices.get(field);
382
+ }
159
383
  if (!sortedIndex || sortedIndex.values.length === 0)
160
384
  return [];
161
385
  const sorted = sortedIndex.values;
@@ -201,17 +425,36 @@ export class MetadataIndexManager {
201
425
  .toLowerCase();
202
426
  }
203
427
  /**
204
- * Normalize value for consistent indexing
428
+ * Normalize value for consistent indexing with smart optimization
205
429
  */
206
- normalizeValue(value) {
430
+ normalizeValue(value, field) {
207
431
  if (value === null || value === undefined)
208
432
  return '__NULL__';
209
433
  if (typeof value === 'boolean')
210
434
  return value ? '__TRUE__' : '__FALSE__';
435
+ // Apply smart normalization based on field statistics
436
+ if (field && this.fieldStats.has(field)) {
437
+ const stats = this.fieldStats.get(field);
438
+ const strategy = stats.normalizationStrategy;
439
+ if (strategy === 'bucket' && typeof value === 'number') {
440
+ // Time bucketing for timestamps
441
+ if (field.toLowerCase().includes('time') || field.toLowerCase().includes('date')) {
442
+ const bucketSize = this.TIMESTAMP_PRECISION_MS;
443
+ const bucketed = Math.floor(value / bucketSize) * bucketSize;
444
+ return bucketed.toString();
445
+ }
446
+ }
447
+ else if (strategy === 'precision' && typeof value === 'number') {
448
+ // Reduce float precision for high cardinality numeric fields
449
+ const rounded = Math.round(value * Math.pow(10, this.FLOAT_PRECISION)) / Math.pow(10, this.FLOAT_PRECISION);
450
+ return rounded.toString();
451
+ }
452
+ }
453
+ // Default normalization
211
454
  if (typeof value === 'number')
212
455
  return value.toString();
213
456
  if (Array.isArray(value)) {
214
- const joined = value.map(v => this.normalizeValue(v)).join(',');
457
+ const joined = value.map(v => this.normalizeValue(v, field)).join(',');
215
458
  // Hash very long array values to avoid filesystem limits
216
459
  if (joined.length > 100) {
217
460
  return this.hashValue(joined);
@@ -285,13 +528,16 @@ export class MetadataIndexManager {
285
528
  */
286
529
  async addToIndex(id, metadata, skipFlush = false) {
287
530
  const fields = this.extractIndexableFields(metadata);
288
- // Mark sorted indices as dirty when adding new data
289
- for (const { field } of fields) {
290
- const sortedIndex = this.sortedIndices.get(field);
291
- if (sortedIndex) {
292
- sortedIndex.isDirty = true;
293
- }
294
- }
531
+ // Sort fields to process 'noun' field first for type-field affinity tracking
532
+ fields.sort((a, b) => {
533
+ if (a.field === 'noun')
534
+ return -1;
535
+ if (b.field === 'noun')
536
+ return 1;
537
+ return 0;
538
+ });
539
+ // Track which fields we're updating for incremental sorted index maintenance
540
+ const updatedFields = new Set();
295
541
  for (let i = 0; i < fields.length; i++) {
296
542
  const { field, value } = fields[i];
297
543
  const key = this.getIndexKey(field, value);
@@ -308,9 +554,25 @@ export class MetadataIndexManager {
308
554
  this.indexCache.set(key, entry);
309
555
  }
310
556
  // Add ID to entry
557
+ const wasNew = entry.ids.size === 0;
311
558
  entry.ids.add(id);
312
559
  entry.lastUpdated = Date.now();
313
560
  this.dirtyEntries.add(key);
561
+ // Update cardinality statistics
562
+ if (wasNew) {
563
+ this.updateCardinalityStats(field, value, 'add');
564
+ }
565
+ // Track type-field affinity for intelligent NLP
566
+ this.updateTypeFieldAffinity(id, field, value, 'add');
567
+ // Incrementally update sorted index for this field
568
+ if (!updatedFields.has(field)) {
569
+ this.updateSortedIndexAdd(field, value, id);
570
+ updatedFields.add(field);
571
+ }
572
+ else {
573
+ // Multiple values for same field - still update
574
+ this.updateSortedIndexAdd(field, value, id);
575
+ }
314
576
  // Update field index
315
577
  await this.updateFieldIndex(field, value, 1);
316
578
  // Yield to event loop every 5 fields to prevent blocking
@@ -382,9 +644,20 @@ export class MetadataIndexManager {
382
644
  entry = loadedEntry ?? undefined;
383
645
  }
384
646
  if (entry) {
647
+ const hadId = entry.ids.has(id);
385
648
  entry.ids.delete(id);
386
649
  entry.lastUpdated = Date.now();
387
650
  this.dirtyEntries.add(key);
651
+ // Update cardinality statistics
652
+ if (hadId && entry.ids.size === 0) {
653
+ this.updateCardinalityStats(field, value, 'remove');
654
+ }
655
+ // Track type-field affinity for intelligent NLP
656
+ if (hadId) {
657
+ this.updateTypeFieldAffinity(id, field, value, 'remove');
658
+ }
659
+ // Incrementally update sorted index when removing
660
+ this.updateSortedIndexRemove(field, value, id);
388
661
  // Update field index
389
662
  await this.updateFieldIndex(field, value, -1);
390
663
  // If no IDs left, mark for cleanup
@@ -404,6 +677,8 @@ export class MetadataIndexManager {
404
677
  entry.ids.delete(id);
405
678
  entry.lastUpdated = Date.now();
406
679
  this.dirtyEntries.add(key);
680
+ // Incrementally update sorted index
681
+ this.updateSortedIndexRemove(entry.field, entry.value, id);
407
682
  if (entry.ids.size === 0) {
408
683
  this.indexCache.delete(key);
409
684
  await this.deleteIndexEntry(key);
@@ -446,6 +721,11 @@ export class MetadataIndexManager {
446
721
  * Get IDs for a specific field-value combination with caching
447
722
  */
448
723
  async getIds(field, value) {
724
+ // Track exact query for field statistics
725
+ if (this.fieldStats.has(field)) {
726
+ const stats = this.fieldStats.get(field);
727
+ stats.exactQueryCount++;
728
+ }
449
729
  const key = this.getIndexKey(field, value);
450
730
  // Check metadata cache first
451
731
  const cacheKey = `ids_${key}`;
@@ -944,7 +1224,7 @@ export class MetadataIndexManager {
944
1224
  totalEntries,
945
1225
  totalIds,
946
1226
  fieldsIndexed: Array.from(fields),
947
- lastRebuild: 0, // TODO: track rebuild timestamp
1227
+ lastRebuild: Date.now(),
948
1228
  indexSize: totalEntries * 100 // rough estimate
949
1229
  };
950
1230
  }
@@ -1174,5 +1454,248 @@ export class MetadataIndexManager {
1174
1454
  // Entry might not exist
1175
1455
  }
1176
1456
  }
1457
+ /**
1458
+ * Get field statistics for optimization and discovery
1459
+ */
1460
+ async getFieldStatistics() {
1461
+ // Initialize stats for fields we haven't seen yet
1462
+ for (const field of this.fieldIndexes.keys()) {
1463
+ if (!this.fieldStats.has(field)) {
1464
+ this.fieldStats.set(field, {
1465
+ cardinality: {
1466
+ uniqueValues: 0,
1467
+ totalValues: 0,
1468
+ distribution: 'uniform',
1469
+ updateFrequency: 0,
1470
+ lastAnalyzed: Date.now()
1471
+ },
1472
+ queryCount: 0,
1473
+ rangeQueryCount: 0,
1474
+ exactQueryCount: 0,
1475
+ avgQueryTime: 0,
1476
+ indexType: 'hash'
1477
+ });
1478
+ }
1479
+ }
1480
+ return new Map(this.fieldStats);
1481
+ }
1482
+ /**
1483
+ * Get field cardinality information
1484
+ */
1485
+ async getFieldCardinality(field) {
1486
+ const stats = this.fieldStats.get(field);
1487
+ return stats ? stats.cardinality : null;
1488
+ }
1489
+ /**
1490
+ * Get all field names with their cardinality (for query optimization)
1491
+ */
1492
+ async getFieldsWithCardinality() {
1493
+ const fields = [];
1494
+ for (const [field, stats] of this.fieldStats) {
1495
+ fields.push({
1496
+ field,
1497
+ cardinality: stats.cardinality.uniqueValues,
1498
+ distribution: stats.cardinality.distribution
1499
+ });
1500
+ }
1501
+ // Sort by cardinality (low cardinality fields are better for filtering)
1502
+ fields.sort((a, b) => a.cardinality - b.cardinality);
1503
+ return fields;
1504
+ }
1505
+ /**
1506
+ * Get optimal query plan based on field statistics
1507
+ */
1508
+ async getOptimalQueryPlan(filters) {
1509
+ const fieldOrder = [];
1510
+ let hasRangeQueries = false;
1511
+ let totalEstimatedCost = 0;
1512
+ // Analyze each filter
1513
+ for (const [field, value] of Object.entries(filters)) {
1514
+ const stats = this.fieldStats.get(field);
1515
+ if (!stats)
1516
+ continue;
1517
+ // Check if this is a range query
1518
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
1519
+ hasRangeQueries = true;
1520
+ }
1521
+ // Estimate cost based on cardinality
1522
+ const cardinality = stats.cardinality.uniqueValues;
1523
+ const estimatedCost = Math.log2(Math.max(1, cardinality));
1524
+ totalEstimatedCost += estimatedCost;
1525
+ fieldOrder.push(field);
1526
+ }
1527
+ // Sort fields by cardinality (process low cardinality first)
1528
+ fieldOrder.sort((a, b) => {
1529
+ const statsA = this.fieldStats.get(a);
1530
+ const statsB = this.fieldStats.get(b);
1531
+ if (!statsA || !statsB)
1532
+ return 0;
1533
+ return statsA.cardinality.uniqueValues - statsB.cardinality.uniqueValues;
1534
+ });
1535
+ return {
1536
+ strategy: hasRangeQueries ? 'hybrid' : 'exact',
1537
+ fieldOrder,
1538
+ estimatedCost: totalEstimatedCost
1539
+ };
1540
+ }
1541
+ /**
1542
+ * Export field statistics for analysis
1543
+ */
1544
+ async exportFieldStats() {
1545
+ const stats = {
1546
+ fields: {},
1547
+ summary: {
1548
+ totalFields: this.fieldStats.size,
1549
+ highCardinalityFields: 0,
1550
+ sparseFields: 0,
1551
+ skewedFields: 0,
1552
+ uniformFields: 0
1553
+ }
1554
+ };
1555
+ for (const [field, fieldStats] of this.fieldStats) {
1556
+ stats.fields[field] = {
1557
+ cardinality: fieldStats.cardinality,
1558
+ queryStats: {
1559
+ total: fieldStats.queryCount,
1560
+ exact: fieldStats.exactQueryCount,
1561
+ range: fieldStats.rangeQueryCount,
1562
+ avgTime: fieldStats.avgQueryTime
1563
+ },
1564
+ indexType: fieldStats.indexType,
1565
+ normalization: fieldStats.normalizationStrategy
1566
+ };
1567
+ // Update summary
1568
+ if (fieldStats.cardinality.uniqueValues > this.HIGH_CARDINALITY_THRESHOLD) {
1569
+ stats.summary.highCardinalityFields++;
1570
+ }
1571
+ switch (fieldStats.cardinality.distribution) {
1572
+ case 'sparse':
1573
+ stats.summary.sparseFields++;
1574
+ break;
1575
+ case 'skewed':
1576
+ stats.summary.skewedFields++;
1577
+ break;
1578
+ case 'uniform':
1579
+ stats.summary.uniformFields++;
1580
+ break;
1581
+ }
1582
+ }
1583
+ return stats;
1584
+ }
1585
+ /**
1586
+ * Update type-field affinity tracking for intelligent NLP
1587
+ * Tracks which fields commonly appear with which entity types
1588
+ */
1589
+ updateTypeFieldAffinity(entityId, field, value, operation) {
1590
+ // Only track affinity for non-system fields (but allow 'noun' for type detection)
1591
+ if (this.config.excludeFields.includes(field) && field !== 'noun')
1592
+ return;
1593
+ // For the 'noun' field, the value IS the entity type
1594
+ let entityType = null;
1595
+ if (field === 'noun') {
1596
+ // This is the type definition itself
1597
+ entityType = this.normalizeValue(value);
1598
+ }
1599
+ else {
1600
+ // Find the noun type for this entity by looking for entries with this entityId
1601
+ for (const [key, entry] of this.indexCache.entries()) {
1602
+ if (key.startsWith('noun:') && entry.ids.has(entityId)) {
1603
+ entityType = key.split(':', 2)[1];
1604
+ break;
1605
+ }
1606
+ }
1607
+ }
1608
+ if (!entityType)
1609
+ return; // No type found, skip affinity tracking
1610
+ // Initialize affinity tracking for this type
1611
+ if (!this.typeFieldAffinity.has(entityType)) {
1612
+ this.typeFieldAffinity.set(entityType, new Map());
1613
+ }
1614
+ if (!this.totalEntitiesByType.has(entityType)) {
1615
+ this.totalEntitiesByType.set(entityType, 0);
1616
+ }
1617
+ const typeFields = this.typeFieldAffinity.get(entityType);
1618
+ if (operation === 'add') {
1619
+ // Increment field count for this type
1620
+ const currentCount = typeFields.get(field) || 0;
1621
+ typeFields.set(field, currentCount + 1);
1622
+ // Update total entities of this type (only count once per entity)
1623
+ if (field === 'noun') {
1624
+ this.totalEntitiesByType.set(entityType, this.totalEntitiesByType.get(entityType) + 1);
1625
+ }
1626
+ }
1627
+ else if (operation === 'remove') {
1628
+ // Decrement field count for this type
1629
+ const currentCount = typeFields.get(field) || 0;
1630
+ if (currentCount > 1) {
1631
+ typeFields.set(field, currentCount - 1);
1632
+ }
1633
+ else {
1634
+ typeFields.delete(field);
1635
+ }
1636
+ // Update total entities of this type
1637
+ if (field === 'noun') {
1638
+ const total = this.totalEntitiesByType.get(entityType);
1639
+ if (total > 1) {
1640
+ this.totalEntitiesByType.set(entityType, total - 1);
1641
+ }
1642
+ else {
1643
+ this.totalEntitiesByType.delete(entityType);
1644
+ this.typeFieldAffinity.delete(entityType);
1645
+ }
1646
+ }
1647
+ }
1648
+ }
1649
+ /**
1650
+ * Get fields that commonly appear with a specific entity type
1651
+ * Returns fields with their affinity scores (0-1)
1652
+ */
1653
+ async getFieldsForType(nounType) {
1654
+ const typeFields = this.typeFieldAffinity.get(nounType);
1655
+ const totalEntities = this.totalEntitiesByType.get(nounType);
1656
+ if (!typeFields || !totalEntities) {
1657
+ return [];
1658
+ }
1659
+ const fieldsWithAffinity = [];
1660
+ for (const [field, count] of typeFields.entries()) {
1661
+ const affinity = count / totalEntities; // 0-1 score
1662
+ fieldsWithAffinity.push({
1663
+ field,
1664
+ affinity,
1665
+ occurrences: count,
1666
+ totalEntities
1667
+ });
1668
+ }
1669
+ // Sort by affinity (most common fields first)
1670
+ fieldsWithAffinity.sort((a, b) => b.affinity - a.affinity);
1671
+ return fieldsWithAffinity;
1672
+ }
1673
+ /**
1674
+ * Get type-field affinity statistics for analysis
1675
+ */
1676
+ async getTypeFieldAffinityStats() {
1677
+ const typeBreakdown = {};
1678
+ let totalFields = 0;
1679
+ for (const [nounType, fieldsMap] of this.typeFieldAffinity.entries()) {
1680
+ const totalEntities = this.totalEntitiesByType.get(nounType) || 0;
1681
+ const fields = Array.from(fieldsMap.entries());
1682
+ // Get top 5 fields for this type
1683
+ const topFields = fields
1684
+ .map(([field, count]) => ({ field, affinity: count / totalEntities }))
1685
+ .sort((a, b) => b.affinity - a.affinity)
1686
+ .slice(0, 5);
1687
+ typeBreakdown[nounType] = {
1688
+ totalEntities,
1689
+ uniqueFields: fieldsMap.size,
1690
+ topFields
1691
+ };
1692
+ totalFields += fieldsMap.size;
1693
+ }
1694
+ return {
1695
+ totalTypes: this.typeFieldAffinity.size,
1696
+ averageFieldsPerType: totalFields / Math.max(1, this.typeFieldAffinity.size),
1697
+ typeBreakdown
1698
+ };
1699
+ }
1177
1700
  }
1178
1701
  //# sourceMappingURL=metadataIndex.js.map
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Node.js Version Compatibility Check
3
+ *
4
+ * Brainy requires Node.js 22.x LTS for maximum stability with ONNX Runtime.
5
+ * This prevents V8 HandleScope locking issues in worker threads.
6
+ */
7
+ export interface VersionInfo {
8
+ current: string;
9
+ major: number;
10
+ isSupported: boolean;
11
+ recommendation: string;
12
+ }
13
+ /**
14
+ * Check if the current Node.js version is supported
15
+ */
16
+ export declare function checkNodeVersion(): VersionInfo;
17
+ /**
18
+ * Enforce Node.js version requirement with helpful error messaging
19
+ */
20
+ export declare function enforceNodeVersion(): void;
21
+ /**
22
+ * Soft warning for version issues (non-blocking)
23
+ */
24
+ export declare function warnNodeVersion(): boolean;