@soulcraft/brainy 0.39.0 → 0.41.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/dist/unified.js CHANGED
@@ -4939,7 +4939,8 @@ class HNSWIndex {
4939
4939
  const noun = {
4940
4940
  id,
4941
4941
  vector,
4942
- connections: new Map()
4942
+ connections: new Map(),
4943
+ level: nounLevel
4943
4944
  };
4944
4945
  // Initialize empty connection sets for each level
4945
4946
  for (let level = 0; level <= nounLevel; level++) {
@@ -5264,6 +5265,29 @@ class HNSWIndex {
5264
5265
  getConfig() {
5265
5266
  return { ...this.config };
5266
5267
  }
5268
+ /**
5269
+ * Get index health metrics
5270
+ */
5271
+ getIndexHealth() {
5272
+ let totalConnections = 0;
5273
+ const layerCounts = new Array(this.maxLevel + 1).fill(0);
5274
+ // Count connections and layer distribution
5275
+ this.nouns.forEach(noun => {
5276
+ // Count connections at each layer
5277
+ for (let level = 0; level <= noun.level; level++) {
5278
+ totalConnections += noun.connections.get(level)?.size || 0;
5279
+ layerCounts[level]++;
5280
+ }
5281
+ });
5282
+ const totalNodes = this.nouns.size;
5283
+ const averageConnections = totalNodes > 0 ? totalConnections / totalNodes : 0;
5284
+ return {
5285
+ averageConnections,
5286
+ layerDistribution: layerCounts,
5287
+ maxLayer: this.maxLevel,
5288
+ totalNodes
5289
+ };
5290
+ }
5267
5291
  /**
5268
5292
  * Search within a specific layer
5269
5293
  * Returns a map of noun IDs to distances, sorted by distance
@@ -5721,7 +5745,8 @@ class HNSWIndexOptimized extends HNSWIndex {
5721
5745
  const noun = {
5722
5746
  id,
5723
5747
  vector,
5724
- connections: new Map()
5748
+ connections: new Map(),
5749
+ level: 0
5725
5750
  };
5726
5751
  // Store the noun
5727
5752
  this.storage.saveNoun(noun).catch((error) => {
@@ -6253,6 +6278,7 @@ class BaseStorage extends BaseStorageAdapter {
6253
6278
  constructor() {
6254
6279
  super(...arguments);
6255
6280
  this.isInitialized = false;
6281
+ this.readOnly = false;
6256
6282
  }
6257
6283
  /**
6258
6284
  * Ensure the storage adapter is initialized
@@ -6869,7 +6895,8 @@ class MemoryStorage extends BaseStorage {
6869
6895
  const nounCopy = {
6870
6896
  id: noun.id,
6871
6897
  vector: [...noun.vector],
6872
- connections: new Map()
6898
+ connections: new Map(),
6899
+ level: noun.level || 0
6873
6900
  };
6874
6901
  // Copy connections
6875
6902
  for (const [level, connections] of noun.connections.entries()) {
@@ -6892,7 +6919,8 @@ class MemoryStorage extends BaseStorage {
6892
6919
  const nounCopy = {
6893
6920
  id: noun.id,
6894
6921
  vector: [...noun.vector],
6895
- connections: new Map()
6922
+ connections: new Map(),
6923
+ level: noun.level || 0
6896
6924
  };
6897
6925
  // Copy connections
6898
6926
  for (const [level, connections] of noun.connections.entries()) {
@@ -6911,7 +6939,8 @@ class MemoryStorage extends BaseStorage {
6911
6939
  const nounCopy = {
6912
6940
  id: noun.id,
6913
6941
  vector: [...noun.vector],
6914
- connections: new Map()
6942
+ connections: new Map(),
6943
+ level: noun.level || 0
6915
6944
  };
6916
6945
  // Copy connections
6917
6946
  for (const [level, connections] of noun.connections.entries()) {
@@ -6986,7 +7015,8 @@ class MemoryStorage extends BaseStorage {
6986
7015
  const nounCopy = {
6987
7016
  id: noun.id,
6988
7017
  vector: [...noun.vector],
6989
- connections: new Map()
7018
+ connections: new Map(),
7019
+ level: noun.level || 0
6990
7020
  };
6991
7021
  // Copy connections
6992
7022
  for (const [level, connections] of noun.connections.entries()) {
@@ -7294,6 +7324,9 @@ class MemoryStorage extends BaseStorage {
7294
7324
  this.nounMetadata.clear();
7295
7325
  this.verbMetadata.clear();
7296
7326
  this.statistics = null;
7327
+ // Clear the statistics cache
7328
+ this.statisticsCache = null;
7329
+ this.statisticsModified = false;
7297
7330
  }
7298
7331
  /**
7299
7332
  * Get information about storage usage and capacity
@@ -7527,7 +7560,8 @@ class OPFSStorage extends BaseStorage {
7527
7560
  return {
7528
7561
  id: data.id,
7529
7562
  vector: data.vector,
7530
- connections
7563
+ connections,
7564
+ level: data.level || 0
7531
7565
  };
7532
7566
  }
7533
7567
  catch (error) {
@@ -7558,7 +7592,8 @@ class OPFSStorage extends BaseStorage {
7558
7592
  allNouns.push({
7559
7593
  id: data.id,
7560
7594
  vector: data.vector,
7561
- connections
7595
+ connections,
7596
+ level: data.level || 0
7562
7597
  });
7563
7598
  }
7564
7599
  catch (error) {
@@ -7609,7 +7644,8 @@ class OPFSStorage extends BaseStorage {
7609
7644
  nodes.push({
7610
7645
  id: data.id,
7611
7646
  vector: data.vector,
7612
- connections
7647
+ connections,
7648
+ level: data.level || 0
7613
7649
  });
7614
7650
  }
7615
7651
  }
@@ -7979,6 +8015,9 @@ class OPFSStorage extends BaseStorage {
7979
8015
  await removeDirectoryContents(this.verbMetadataDir);
7980
8016
  // Remove all files in the index directory
7981
8017
  await removeDirectoryContents(this.indexDir);
8018
+ // Clear the statistics cache
8019
+ this.statisticsCache = null;
8020
+ this.statisticsModified = false;
7982
8021
  }
7983
8022
  catch (error) {
7984
8023
  console.error('Error clearing storage:', error);
@@ -10001,6 +10040,135 @@ class CacheManager {
10001
10040
  }
10002
10041
  }
10003
10042
 
10043
+ /**
10044
+ * Centralized logging utility for Brainy
10045
+ * Provides configurable log levels and consistent logging across the codebase
10046
+ */
10047
+ var LogLevel;
10048
+ (function (LogLevel) {
10049
+ LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
10050
+ LogLevel[LogLevel["WARN"] = 1] = "WARN";
10051
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
10052
+ LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
10053
+ LogLevel[LogLevel["TRACE"] = 4] = "TRACE";
10054
+ })(LogLevel || (LogLevel = {}));
10055
+ let Logger$1 = class Logger {
10056
+ constructor() {
10057
+ this.config = {
10058
+ level: LogLevel.WARN, // Default to WARN - only critical messages
10059
+ timestamps: true,
10060
+ includeModule: true
10061
+ };
10062
+ // Set log level from environment variable if available
10063
+ const envLogLevel = process.env.BRAINY_LOG_LEVEL;
10064
+ if (envLogLevel) {
10065
+ const level = LogLevel[envLogLevel.toUpperCase()];
10066
+ if (level !== undefined) {
10067
+ this.config.level = level;
10068
+ }
10069
+ }
10070
+ // Parse module-specific log levels
10071
+ const moduleLogLevels = process.env.BRAINY_MODULE_LOG_LEVELS;
10072
+ if (moduleLogLevels) {
10073
+ try {
10074
+ this.config.modules = JSON.parse(moduleLogLevels);
10075
+ }
10076
+ catch (e) {
10077
+ // Ignore parsing errors
10078
+ }
10079
+ }
10080
+ }
10081
+ static getInstance() {
10082
+ if (!Logger.instance) {
10083
+ Logger.instance = new Logger();
10084
+ }
10085
+ return Logger.instance;
10086
+ }
10087
+ configure(config) {
10088
+ this.config = { ...this.config, ...config };
10089
+ }
10090
+ shouldLog(level, module) {
10091
+ // Check module-specific level first
10092
+ if (this.config.modules && this.config.modules[module] !== undefined) {
10093
+ return level <= this.config.modules[module];
10094
+ }
10095
+ // Otherwise use global level
10096
+ return level <= this.config.level;
10097
+ }
10098
+ formatMessage(level, module, message) {
10099
+ const parts = [];
10100
+ if (this.config.timestamps) {
10101
+ parts.push(`[${new Date().toISOString()}]`);
10102
+ }
10103
+ parts.push(`[${LogLevel[level]}]`);
10104
+ if (this.config.includeModule) {
10105
+ parts.push(`[${module}]`);
10106
+ }
10107
+ parts.push(message);
10108
+ return parts.join(' ');
10109
+ }
10110
+ log(level, module, message, ...args) {
10111
+ if (!this.shouldLog(level, module)) {
10112
+ return;
10113
+ }
10114
+ if (this.config.handler) {
10115
+ this.config.handler(level, module, message, ...args);
10116
+ return;
10117
+ }
10118
+ const formattedMessage = this.formatMessage(level, module, message);
10119
+ switch (level) {
10120
+ case LogLevel.ERROR:
10121
+ console.error(formattedMessage, ...args);
10122
+ break;
10123
+ case LogLevel.WARN:
10124
+ console.warn(formattedMessage, ...args);
10125
+ break;
10126
+ case LogLevel.INFO:
10127
+ console.info(formattedMessage, ...args);
10128
+ break;
10129
+ case LogLevel.DEBUG:
10130
+ case LogLevel.TRACE:
10131
+ console.log(formattedMessage, ...args);
10132
+ break;
10133
+ }
10134
+ }
10135
+ error(module, message, ...args) {
10136
+ this.log(LogLevel.ERROR, module, message, ...args);
10137
+ }
10138
+ warn(module, message, ...args) {
10139
+ this.log(LogLevel.WARN, module, message, ...args);
10140
+ }
10141
+ info(module, message, ...args) {
10142
+ this.log(LogLevel.INFO, module, message, ...args);
10143
+ }
10144
+ debug(module, message, ...args) {
10145
+ this.log(LogLevel.DEBUG, module, message, ...args);
10146
+ }
10147
+ trace(module, message, ...args) {
10148
+ this.log(LogLevel.TRACE, module, message, ...args);
10149
+ }
10150
+ // Create a module-specific logger
10151
+ createModuleLogger(module) {
10152
+ return {
10153
+ error: (message, ...args) => this.error(module, message, ...args),
10154
+ warn: (message, ...args) => this.warn(module, message, ...args),
10155
+ info: (message, ...args) => this.info(module, message, ...args),
10156
+ debug: (message, ...args) => this.debug(module, message, ...args),
10157
+ trace: (message, ...args) => this.trace(module, message, ...args)
10158
+ };
10159
+ }
10160
+ };
10161
+ // Export singleton instance
10162
+ const logger = Logger$1.getInstance();
10163
+ // Export convenience function for creating module loggers
10164
+ function createModuleLogger(module) {
10165
+ return logger.createModuleLogger(module);
10166
+ }
10167
+ // Export function to configure logger
10168
+ function configureLogger(config) {
10169
+ logger.configure(config);
10170
+ }
10171
+
10004
10172
  /**
10005
10173
  * S3-Compatible Storage Adapter
10006
10174
  * Uses the AWS S3 client to interact with S3-compatible storage services
@@ -10043,6 +10211,8 @@ class S3CompatibleStorage extends BaseStorage {
10043
10211
  this.activeLocks = new Set();
10044
10212
  // Change log for efficient synchronization
10045
10213
  this.changeLogPrefix = 'change-log/';
10214
+ // Module logger
10215
+ this.logger = createModuleLogger('S3Storage');
10046
10216
  // Node cache to avoid redundant API calls
10047
10217
  this.nodeCache = new Map();
10048
10218
  // Batch update timer ID
@@ -10063,6 +10233,7 @@ class S3CompatibleStorage extends BaseStorage {
10063
10233
  this.secretAccessKey = options.secretAccessKey;
10064
10234
  this.sessionToken = options.sessionToken;
10065
10235
  this.serviceType = options.serviceType || 's3';
10236
+ this.readOnly = options.readOnly || false;
10066
10237
  // Initialize operation executors with timeout and retry configuration
10067
10238
  this.operationExecutors = new StorageOperationExecutors(options.operationConfig);
10068
10239
  // Set up prefixes for different types of data
@@ -10184,9 +10355,10 @@ class S3CompatibleStorage extends BaseStorage {
10184
10355
  this.nounCacheManager.setStorageAdapters(nounStorageAdapter, nounStorageAdapter);
10185
10356
  this.verbCacheManager.setStorageAdapters(verbStorageAdapter, verbStorageAdapter);
10186
10357
  this.isInitialized = true;
10358
+ this.logger.info(`Initialized ${this.serviceType} storage with bucket ${this.bucketName}`);
10187
10359
  }
10188
10360
  catch (error) {
10189
- console.error(`Failed to initialize ${this.serviceType} storage:`, error);
10361
+ this.logger.error(`Failed to initialize ${this.serviceType} storage:`, error);
10190
10362
  throw new Error(`Failed to initialize ${this.serviceType} storage: ${error}`);
10191
10363
  }
10192
10364
  }
@@ -10202,7 +10374,7 @@ class S3CompatibleStorage extends BaseStorage {
10202
10374
  async saveNode(node) {
10203
10375
  await this.ensureInitialized();
10204
10376
  try {
10205
- console.log(`Saving node ${node.id} to bucket ${this.bucketName}`);
10377
+ this.logger.trace(`Saving node ${node.id}`);
10206
10378
  // Convert connections Map to a serializable format
10207
10379
  const serializableNode = {
10208
10380
  ...node,
@@ -10212,8 +10384,7 @@ class S3CompatibleStorage extends BaseStorage {
10212
10384
  const { PutObjectCommand } = await import('@aws-sdk/client-s3');
10213
10385
  const key = `${this.nounPrefix}${node.id}.json`;
10214
10386
  const body = JSON.stringify(serializableNode, null, 2);
10215
- console.log(`Saving node to key: ${key}`);
10216
- console.log(`Node data: ${body.substring(0, 100)}${body.length > 100 ? '...' : ''}`);
10387
+ this.logger.trace(`Saving to key: ${key}`);
10217
10388
  // Save the node to S3-compatible storage
10218
10389
  const result = await this.s3Client.send(new PutObjectCommand({
10219
10390
  Bucket: this.bucketName,
@@ -10221,7 +10392,7 @@ class S3CompatibleStorage extends BaseStorage {
10221
10392
  Body: body,
10222
10393
  ContentType: 'application/json'
10223
10394
  }));
10224
- console.log(`Node ${node.id} saved successfully:`, result);
10395
+ this.logger.debug(`Node ${node.id} saved successfully`);
10225
10396
  // Log the change for efficient synchronization
10226
10397
  await this.appendToChangeLog({
10227
10398
  timestamp: Date.now(),
@@ -10241,18 +10412,18 @@ class S3CompatibleStorage extends BaseStorage {
10241
10412
  Key: key
10242
10413
  }));
10243
10414
  if (verifyResponse && verifyResponse.Body) {
10244
- console.log(`Verified node ${node.id} was saved correctly`);
10415
+ this.logger.trace(`Verified node ${node.id} was saved correctly`);
10245
10416
  }
10246
10417
  else {
10247
- console.error(`Failed to verify node ${node.id} was saved correctly: no response or body`);
10418
+ this.logger.warn(`Failed to verify node ${node.id} was saved correctly: no response or body`);
10248
10419
  }
10249
10420
  }
10250
10421
  catch (verifyError) {
10251
- console.error(`Failed to verify node ${node.id} was saved correctly:`, verifyError);
10422
+ this.logger.warn(`Failed to verify node ${node.id} was saved correctly:`, verifyError);
10252
10423
  }
10253
10424
  }
10254
10425
  catch (error) {
10255
- console.error(`Failed to save node ${node.id}:`, error);
10426
+ this.logger.error(`Failed to save node ${node.id}:`, error);
10256
10427
  throw new Error(`Failed to save node ${node.id}: ${error}`);
10257
10428
  }
10258
10429
  }
@@ -10270,9 +10441,8 @@ class S3CompatibleStorage extends BaseStorage {
10270
10441
  try {
10271
10442
  // Import the GetObjectCommand only when needed
10272
10443
  const { GetObjectCommand } = await import('@aws-sdk/client-s3');
10273
- console.log(`Getting node ${id} from bucket ${this.bucketName}`);
10274
10444
  const key = `${this.nounPrefix}${id}.json`;
10275
- console.log(`Looking for node at key: ${key}`);
10445
+ this.logger.trace(`Getting node ${id} from key: ${key}`);
10276
10446
  // Try to get the node from the nouns directory
10277
10447
  const response = await this.s3Client.send(new GetObjectCommand({
10278
10448
  Bucket: this.bucketName,
@@ -10280,22 +10450,22 @@ class S3CompatibleStorage extends BaseStorage {
10280
10450
  }));
10281
10451
  // Check if response is null or undefined
10282
10452
  if (!response || !response.Body) {
10283
- console.log(`No node found for ${id}`);
10453
+ this.logger.trace(`No node found for ${id}`);
10284
10454
  return null;
10285
10455
  }
10286
10456
  // Convert the response body to a string
10287
10457
  const bodyContents = await response.Body.transformToString();
10288
- console.log(`Retrieved node body: ${bodyContents.substring(0, 100)}${bodyContents.length > 100 ? '...' : ''}`);
10458
+ this.logger.trace(`Retrieved node body for ${id}`);
10289
10459
  // Parse the JSON string
10290
10460
  try {
10291
10461
  const parsedNode = JSON.parse(bodyContents);
10292
- console.log(`Parsed node data for ${id}:`, parsedNode);
10462
+ this.logger.trace(`Parsed node data for ${id}`);
10293
10463
  // Ensure the parsed node has the expected properties
10294
10464
  if (!parsedNode ||
10295
10465
  !parsedNode.id ||
10296
10466
  !parsedNode.vector ||
10297
10467
  !parsedNode.connections) {
10298
- console.error(`Invalid node data for ${id}:`, parsedNode);
10468
+ this.logger.warn(`Invalid node data for ${id}`);
10299
10469
  return null;
10300
10470
  }
10301
10471
  // Convert serialized connections back to Map<number, Set<string>>
@@ -10306,19 +10476,20 @@ class S3CompatibleStorage extends BaseStorage {
10306
10476
  const node = {
10307
10477
  id: parsedNode.id,
10308
10478
  vector: parsedNode.vector,
10309
- connections
10479
+ connections,
10480
+ level: parsedNode.level || 0
10310
10481
  };
10311
- console.log(`Successfully retrieved node ${id}:`, node);
10482
+ this.logger.trace(`Successfully retrieved node ${id}`);
10312
10483
  return node;
10313
10484
  }
10314
10485
  catch (parseError) {
10315
- console.error(`Failed to parse node data for ${id}:`, parseError);
10486
+ this.logger.error(`Failed to parse node data for ${id}:`, parseError);
10316
10487
  return null;
10317
10488
  }
10318
10489
  }
10319
10490
  catch (error) {
10320
10491
  // Node not found or other error
10321
- console.log(`Error getting node for ${id}:`, error);
10492
+ this.logger.trace(`Node not found for ${id}`);
10322
10493
  return null;
10323
10494
  }
10324
10495
  }
@@ -10326,7 +10497,12 @@ class S3CompatibleStorage extends BaseStorage {
10326
10497
  * Get all nouns from storage (internal implementation)
10327
10498
  */
10328
10499
  async getAllNouns_internal() {
10329
- return this.getAllNodes();
10500
+ // Use paginated method to avoid deprecation warning
10501
+ const result = await this.getNodesWithPagination({
10502
+ limit: 1000,
10503
+ useCache: true
10504
+ });
10505
+ return result.nodes;
10330
10506
  }
10331
10507
  /**
10332
10508
  * Get all nodes from storage
@@ -10335,7 +10511,7 @@ class S3CompatibleStorage extends BaseStorage {
10335
10511
  */
10336
10512
  async getAllNodes() {
10337
10513
  await this.ensureInitialized();
10338
- console.warn('WARNING: getAllNodes() is deprecated and will be removed in a future version. Use getNodesWithPagination() instead.');
10514
+ this.logger.warn('getAllNodes() is deprecated and will be removed in a future version. Use getNodesWithPagination() instead.');
10339
10515
  try {
10340
10516
  // Use the paginated method with a large limit to maintain backward compatibility
10341
10517
  // but warn about potential issues
@@ -10344,12 +10520,12 @@ class S3CompatibleStorage extends BaseStorage {
10344
10520
  useCache: true
10345
10521
  });
10346
10522
  if (result.hasMore) {
10347
- console.warn(`WARNING: Only returning the first 1000 nodes. There are more nodes available. Use getNodesWithPagination() for proper pagination.`);
10523
+ this.logger.warn(`Only returning the first 1000 nodes. There are more nodes available. Use getNodesWithPagination() for proper pagination.`);
10348
10524
  }
10349
10525
  return result.nodes;
10350
10526
  }
10351
10527
  catch (error) {
10352
- console.error('Failed to get all nodes:', error);
10528
+ this.logger.error('Failed to get all nodes:', error);
10353
10529
  return [];
10354
10530
  }
10355
10531
  }
@@ -10437,7 +10613,7 @@ class S3CompatibleStorage extends BaseStorage {
10437
10613
  };
10438
10614
  }
10439
10615
  catch (error) {
10440
- console.error('Failed to get nodes with pagination:', error);
10616
+ this.logger.error('Failed to get nodes with pagination:', error);
10441
10617
  return {
10442
10618
  nodes: [],
10443
10619
  hasMore: false
@@ -10483,14 +10659,14 @@ class S3CompatibleStorage extends BaseStorage {
10483
10659
  cursor = result.nextCursor;
10484
10660
  // Safety check to prevent infinite loops
10485
10661
  if (!cursor && hasMore) {
10486
- console.warn('No cursor returned but hasMore is true, breaking loop');
10662
+ this.logger.warn('No cursor returned but hasMore is true, breaking loop');
10487
10663
  break;
10488
10664
  }
10489
10665
  }
10490
10666
  return filteredNodes;
10491
10667
  }
10492
10668
  catch (error) {
10493
- console.error(`Failed to get nodes by noun type ${nounType}:`, error);
10669
+ this.logger.error(`Failed to get nodes by noun type ${nounType}:`, error);
10494
10670
  return [];
10495
10671
  }
10496
10672
  }
@@ -10522,7 +10698,7 @@ class S3CompatibleStorage extends BaseStorage {
10522
10698
  });
10523
10699
  }
10524
10700
  catch (error) {
10525
- console.error(`Failed to delete node ${id}:`, error);
10701
+ this.logger.error(`Failed to delete node ${id}:`, error);
10526
10702
  throw new Error(`Failed to delete node ${id}: ${error}`);
10527
10703
  }
10528
10704
  }
@@ -10564,7 +10740,7 @@ class S3CompatibleStorage extends BaseStorage {
10564
10740
  });
10565
10741
  }
10566
10742
  catch (error) {
10567
- console.error(`Failed to save edge ${edge.id}:`, error);
10743
+ this.logger.error(`Failed to save edge ${edge.id}:`, error);
10568
10744
  throw new Error(`Failed to save edge ${edge.id}: ${error}`);
10569
10745
  }
10570
10746
  }
@@ -10582,9 +10758,8 @@ class S3CompatibleStorage extends BaseStorage {
10582
10758
  try {
10583
10759
  // Import the GetObjectCommand only when needed
10584
10760
  const { GetObjectCommand } = await import('@aws-sdk/client-s3');
10585
- console.log(`Getting edge ${id} from bucket ${this.bucketName}`);
10586
10761
  const key = `${this.verbPrefix}${id}.json`;
10587
- console.log(`Looking for edge at key: ${key}`);
10762
+ this.logger.trace(`Getting edge ${id} from key: ${key}`);
10588
10763
  // Try to get the edge from the verbs directory
10589
10764
  const response = await this.s3Client.send(new GetObjectCommand({
10590
10765
  Bucket: this.bucketName,
@@ -10592,22 +10767,22 @@ class S3CompatibleStorage extends BaseStorage {
10592
10767
  }));
10593
10768
  // Check if response is null or undefined
10594
10769
  if (!response || !response.Body) {
10595
- console.log(`No edge found for ${id}`);
10770
+ this.logger.trace(`No edge found for ${id}`);
10596
10771
  return null;
10597
10772
  }
10598
10773
  // Convert the response body to a string
10599
10774
  const bodyContents = await response.Body.transformToString();
10600
- console.log(`Retrieved edge body: ${bodyContents.substring(0, 100)}${bodyContents.length > 100 ? '...' : ''}`);
10775
+ this.logger.trace(`Retrieved edge body for ${id}`);
10601
10776
  // Parse the JSON string
10602
10777
  try {
10603
10778
  const parsedEdge = JSON.parse(bodyContents);
10604
- console.log(`Parsed edge data for ${id}:`, parsedEdge);
10779
+ this.logger.trace(`Parsed edge data for ${id}`);
10605
10780
  // Ensure the parsed edge has the expected properties
10606
10781
  if (!parsedEdge ||
10607
10782
  !parsedEdge.id ||
10608
10783
  !parsedEdge.vector ||
10609
10784
  !parsedEdge.connections) {
10610
- console.error(`Invalid edge data for ${id}:`, parsedEdge);
10785
+ this.logger.warn(`Invalid edge data for ${id}`);
10611
10786
  return null;
10612
10787
  }
10613
10788
  // Convert serialized connections back to Map<number, Set<string>>
@@ -10620,17 +10795,17 @@ class S3CompatibleStorage extends BaseStorage {
10620
10795
  vector: parsedEdge.vector,
10621
10796
  connections
10622
10797
  };
10623
- console.log(`Successfully retrieved edge ${id}:`, edge);
10798
+ this.logger.trace(`Successfully retrieved edge ${id}`);
10624
10799
  return edge;
10625
10800
  }
10626
10801
  catch (parseError) {
10627
- console.error(`Failed to parse edge data for ${id}:`, parseError);
10802
+ this.logger.error(`Failed to parse edge data for ${id}:`, parseError);
10628
10803
  return null;
10629
10804
  }
10630
10805
  }
10631
10806
  catch (error) {
10632
10807
  // Edge not found or other error
10633
- console.log(`Error getting edge for ${id}:`, error);
10808
+ this.logger.trace(`Edge not found for ${id}`);
10634
10809
  return null;
10635
10810
  }
10636
10811
  }
@@ -10640,7 +10815,7 @@ class S3CompatibleStorage extends BaseStorage {
10640
10815
  * It can cause memory issues with large datasets. Use getVerbsWithPagination() instead.
10641
10816
  */
10642
10817
  async getAllVerbs_internal() {
10643
- console.warn('WARNING: getAllVerbs_internal() is deprecated and will be removed in a future version. Use getVerbsWithPagination() instead.');
10818
+ this.logger.warn('getAllVerbs_internal() is deprecated and will be removed in a future version. Use getVerbsWithPagination() instead.');
10644
10819
  return this.getAllEdges();
10645
10820
  }
10646
10821
  /**
@@ -10650,7 +10825,7 @@ class S3CompatibleStorage extends BaseStorage {
10650
10825
  */
10651
10826
  async getAllEdges() {
10652
10827
  await this.ensureInitialized();
10653
- console.warn('WARNING: getAllEdges() is deprecated and will be removed in a future version. Use getEdgesWithPagination() instead.');
10828
+ this.logger.warn('getAllEdges() is deprecated and will be removed in a future version. Use getEdgesWithPagination() instead.');
10654
10829
  try {
10655
10830
  // Use the paginated method with a large limit to maintain backward compatibility
10656
10831
  // but warn about potential issues
@@ -10659,12 +10834,12 @@ class S3CompatibleStorage extends BaseStorage {
10659
10834
  useCache: true
10660
10835
  });
10661
10836
  if (result.hasMore) {
10662
- console.warn(`WARNING: Only returning the first 1000 edges. There are more edges available. Use getEdgesWithPagination() for proper pagination.`);
10837
+ this.logger.warn(`Only returning the first 1000 edges. There are more edges available. Use getEdgesWithPagination() for proper pagination.`);
10663
10838
  }
10664
10839
  return result.edges;
10665
10840
  }
10666
10841
  catch (error) {
10667
- console.error('Failed to get all edges:', error);
10842
+ this.logger.error('Failed to get all edges:', error);
10668
10843
  return [];
10669
10844
  }
10670
10845
  }
@@ -10761,7 +10936,7 @@ class S3CompatibleStorage extends BaseStorage {
10761
10936
  };
10762
10937
  }
10763
10938
  catch (error) {
10764
- console.error('Failed to get edges with pagination:', error);
10939
+ this.logger.error('Failed to get edges with pagination:', error);
10765
10940
  return {
10766
10941
  edges: [],
10767
10942
  hasMore: false
@@ -10777,7 +10952,7 @@ class S3CompatibleStorage extends BaseStorage {
10777
10952
  filterEdge(edge, filter) {
10778
10953
  // HNSWVerb filtering is not supported since metadata is stored separately
10779
10954
  // This method is deprecated and should not be used with the new storage pattern
10780
- console.warn('Edge filtering is deprecated and not supported with the new storage pattern');
10955
+ this.logger.trace('Edge filtering is deprecated and not supported with the new storage pattern');
10781
10956
  return true; // Return all edges since filtering requires metadata
10782
10957
  }
10783
10958
  /**
@@ -10842,7 +11017,7 @@ class S3CompatibleStorage extends BaseStorage {
10842
11017
  async getEdgesBySource(sourceId) {
10843
11018
  // This method is deprecated and would require loading metadata for each edge
10844
11019
  // For now, return empty array since this is not efficiently implementable with new storage pattern
10845
- console.warn('getEdgesBySource is deprecated and not efficiently supported in new storage pattern');
11020
+ this.logger.trace('getEdgesBySource is deprecated and not efficiently supported in new storage pattern');
10846
11021
  return [];
10847
11022
  }
10848
11023
  /**
@@ -10857,7 +11032,7 @@ class S3CompatibleStorage extends BaseStorage {
10857
11032
  async getEdgesByTarget(targetId) {
10858
11033
  // This method is deprecated and would require loading metadata for each edge
10859
11034
  // For now, return empty array since this is not efficiently implementable with new storage pattern
10860
- console.warn('getEdgesByTarget is deprecated and not efficiently supported in new storage pattern');
11035
+ this.logger.trace('getEdgesByTarget is deprecated and not efficiently supported in new storage pattern');
10861
11036
  return [];
10862
11037
  }
10863
11038
  /**
@@ -10872,7 +11047,7 @@ class S3CompatibleStorage extends BaseStorage {
10872
11047
  async getEdgesByType(type) {
10873
11048
  // This method is deprecated and would require loading metadata for each edge
10874
11049
  // For now, return empty array since this is not efficiently implementable with new storage pattern
10875
- console.warn('getEdgesByType is deprecated and not efficiently supported in new storage pattern');
11050
+ this.logger.trace('getEdgesByType is deprecated and not efficiently supported in new storage pattern');
10876
11051
  return [];
10877
11052
  }
10878
11053
  /**
@@ -10903,7 +11078,7 @@ class S3CompatibleStorage extends BaseStorage {
10903
11078
  });
10904
11079
  }
10905
11080
  catch (error) {
10906
- console.error(`Failed to delete edge ${id}:`, error);
11081
+ this.logger.error(`Failed to delete edge ${id}:`, error);
10907
11082
  throw new Error(`Failed to delete edge ${id}: ${error}`);
10908
11083
  }
10909
11084
  }
@@ -10913,13 +11088,11 @@ class S3CompatibleStorage extends BaseStorage {
10913
11088
  async saveMetadata(id, metadata) {
10914
11089
  await this.ensureInitialized();
10915
11090
  try {
10916
- console.log(`Saving metadata for ${id} to bucket ${this.bucketName}`);
10917
11091
  // Import the PutObjectCommand only when needed
10918
11092
  const { PutObjectCommand } = await import('@aws-sdk/client-s3');
10919
11093
  const key = `${this.metadataPrefix}${id}.json`;
10920
11094
  const body = JSON.stringify(metadata, null, 2);
10921
- console.log(`Saving metadata to key: ${key}`);
10922
- console.log(`Metadata: ${body}`);
11095
+ this.logger.trace(`Saving metadata for ${id} to key: ${key}`);
10923
11096
  // Save the metadata to S3-compatible storage
10924
11097
  const result = await this.s3Client.send(new PutObjectCommand({
10925
11098
  Bucket: this.bucketName,
@@ -10927,7 +11100,7 @@ class S3CompatibleStorage extends BaseStorage {
10927
11100
  Body: body,
10928
11101
  ContentType: 'application/json'
10929
11102
  }));
10930
- console.log(`Metadata for ${id} saved successfully:`, result);
11103
+ this.logger.debug(`Metadata for ${id} saved successfully`);
10931
11104
  // Log the change for efficient synchronization
10932
11105
  await this.appendToChangeLog({
10933
11106
  timestamp: Date.now(),
@@ -10944,19 +11117,18 @@ class S3CompatibleStorage extends BaseStorage {
10944
11117
  Key: key
10945
11118
  }));
10946
11119
  if (verifyResponse && verifyResponse.Body) {
10947
- const bodyContents = await verifyResponse.Body.transformToString();
10948
- console.log(`Verified metadata for ${id} was saved correctly: ${bodyContents}`);
11120
+ this.logger.trace(`Verified metadata for ${id} was saved correctly`);
10949
11121
  }
10950
11122
  else {
10951
- console.error(`Failed to verify metadata for ${id} was saved correctly: no response or body`);
11123
+ this.logger.warn(`Failed to verify metadata for ${id} was saved correctly: no response or body`);
10952
11124
  }
10953
11125
  }
10954
11126
  catch (verifyError) {
10955
- console.error(`Failed to verify metadata for ${id} was saved correctly:`, verifyError);
11127
+ this.logger.warn(`Failed to verify metadata for ${id} was saved correctly:`, verifyError);
10956
11128
  }
10957
11129
  }
10958
11130
  catch (error) {
10959
- console.error(`Failed to save metadata for ${id}:`, error);
11131
+ this.logger.error(`Failed to save metadata for ${id}:`, error);
10960
11132
  throw new Error(`Failed to save metadata for ${id}: ${error}`);
10961
11133
  }
10962
11134
  }
@@ -10966,13 +11138,11 @@ class S3CompatibleStorage extends BaseStorage {
10966
11138
  async saveVerbMetadata(id, metadata) {
10967
11139
  await this.ensureInitialized();
10968
11140
  try {
10969
- console.log(`Saving verb metadata for ${id} to bucket ${this.bucketName}`);
10970
11141
  // Import the PutObjectCommand only when needed
10971
11142
  const { PutObjectCommand } = await import('@aws-sdk/client-s3');
10972
11143
  const key = `verb-metadata/${id}.json`;
10973
11144
  const body = JSON.stringify(metadata, null, 2);
10974
- console.log(`Saving verb metadata to key: ${key}`);
10975
- console.log(`Verb Metadata: ${body}`);
11145
+ this.logger.trace(`Saving verb metadata for ${id} to key: ${key}`);
10976
11146
  // Save the verb metadata to S3-compatible storage
10977
11147
  const result = await this.s3Client.send(new PutObjectCommand({
10978
11148
  Bucket: this.bucketName,
@@ -10980,10 +11150,10 @@ class S3CompatibleStorage extends BaseStorage {
10980
11150
  Body: body,
10981
11151
  ContentType: 'application/json'
10982
11152
  }));
10983
- console.log(`Verb metadata for ${id} saved successfully:`, result);
11153
+ this.logger.debug(`Verb metadata for ${id} saved successfully`);
10984
11154
  }
10985
11155
  catch (error) {
10986
- console.error(`Failed to save verb metadata for ${id}:`, error);
11156
+ this.logger.error(`Failed to save verb metadata for ${id}:`, error);
10987
11157
  throw new Error(`Failed to save verb metadata for ${id}: ${error}`);
10988
11158
  }
10989
11159
  }
@@ -10995,9 +11165,8 @@ class S3CompatibleStorage extends BaseStorage {
10995
11165
  try {
10996
11166
  // Import the GetObjectCommand only when needed
10997
11167
  const { GetObjectCommand } = await import('@aws-sdk/client-s3');
10998
- console.log(`Getting verb metadata for ${id} from bucket ${this.bucketName}`);
10999
11168
  const key = `verb-metadata/${id}.json`;
11000
- console.log(`Looking for verb metadata at key: ${key}`);
11169
+ this.logger.trace(`Getting verb metadata for ${id} from key: ${key}`);
11001
11170
  // Try to get the verb metadata
11002
11171
  const response = await this.s3Client.send(new GetObjectCommand({
11003
11172
  Bucket: this.bucketName,
@@ -11005,20 +11174,20 @@ class S3CompatibleStorage extends BaseStorage {
11005
11174
  }));
11006
11175
  // Check if response is null or undefined
11007
11176
  if (!response || !response.Body) {
11008
- console.log(`No verb metadata found for ${id}`);
11177
+ this.logger.trace(`No verb metadata found for ${id}`);
11009
11178
  return null;
11010
11179
  }
11011
11180
  // Convert the response body to a string
11012
11181
  const bodyContents = await response.Body.transformToString();
11013
- console.log(`Retrieved verb metadata body: ${bodyContents}`);
11182
+ this.logger.trace(`Retrieved verb metadata body for ${id}`);
11014
11183
  // Parse the JSON string
11015
11184
  try {
11016
11185
  const parsedMetadata = JSON.parse(bodyContents);
11017
- console.log(`Successfully retrieved verb metadata for ${id}:`, parsedMetadata);
11186
+ this.logger.trace(`Successfully retrieved verb metadata for ${id}`);
11018
11187
  return parsedMetadata;
11019
11188
  }
11020
11189
  catch (parseError) {
11021
- console.error(`Failed to parse verb metadata for ${id}:`, parseError);
11190
+ this.logger.error(`Failed to parse verb metadata for ${id}:`, parseError);
11022
11191
  return null;
11023
11192
  }
11024
11193
  }
@@ -11029,7 +11198,7 @@ class S3CompatibleStorage extends BaseStorage {
11029
11198
  (error.message.includes('NoSuchKey') ||
11030
11199
  error.message.includes('not found') ||
11031
11200
  error.message.includes('does not exist')))) {
11032
- console.log(`Verb metadata not found for ${id}`);
11201
+ this.logger.trace(`Verb metadata not found for ${id}`);
11033
11202
  return null;
11034
11203
  }
11035
11204
  // For other types of errors, convert to BrainyError for better classification
@@ -11042,13 +11211,11 @@ class S3CompatibleStorage extends BaseStorage {
11042
11211
  async saveNounMetadata(id, metadata) {
11043
11212
  await this.ensureInitialized();
11044
11213
  try {
11045
- console.log(`Saving noun metadata for ${id} to bucket ${this.bucketName}`);
11046
11214
  // Import the PutObjectCommand only when needed
11047
11215
  const { PutObjectCommand } = await import('@aws-sdk/client-s3');
11048
11216
  const key = `noun-metadata/${id}.json`;
11049
11217
  const body = JSON.stringify(metadata, null, 2);
11050
- console.log(`Saving noun metadata to key: ${key}`);
11051
- console.log(`Noun Metadata: ${body}`);
11218
+ this.logger.trace(`Saving noun metadata for ${id} to key: ${key}`);
11052
11219
  // Save the noun metadata to S3-compatible storage
11053
11220
  const result = await this.s3Client.send(new PutObjectCommand({
11054
11221
  Bucket: this.bucketName,
@@ -11056,10 +11223,10 @@ class S3CompatibleStorage extends BaseStorage {
11056
11223
  Body: body,
11057
11224
  ContentType: 'application/json'
11058
11225
  }));
11059
- console.log(`Noun metadata for ${id} saved successfully:`, result);
11226
+ this.logger.debug(`Noun metadata for ${id} saved successfully`);
11060
11227
  }
11061
11228
  catch (error) {
11062
- console.error(`Failed to save noun metadata for ${id}:`, error);
11229
+ this.logger.error(`Failed to save noun metadata for ${id}:`, error);
11063
11230
  throw new Error(`Failed to save noun metadata for ${id}: ${error}`);
11064
11231
  }
11065
11232
  }
@@ -11071,9 +11238,8 @@ class S3CompatibleStorage extends BaseStorage {
11071
11238
  try {
11072
11239
  // Import the GetObjectCommand only when needed
11073
11240
  const { GetObjectCommand } = await import('@aws-sdk/client-s3');
11074
- console.log(`Getting noun metadata for ${id} from bucket ${this.bucketName}`);
11075
11241
  const key = `noun-metadata/${id}.json`;
11076
- console.log(`Looking for noun metadata at key: ${key}`);
11242
+ this.logger.trace(`Getting noun metadata for ${id} from key: ${key}`);
11077
11243
  // Try to get the noun metadata
11078
11244
  const response = await this.s3Client.send(new GetObjectCommand({
11079
11245
  Bucket: this.bucketName,
@@ -11081,20 +11247,20 @@ class S3CompatibleStorage extends BaseStorage {
11081
11247
  }));
11082
11248
  // Check if response is null or undefined
11083
11249
  if (!response || !response.Body) {
11084
- console.log(`No noun metadata found for ${id}`);
11250
+ this.logger.trace(`No noun metadata found for ${id}`);
11085
11251
  return null;
11086
11252
  }
11087
11253
  // Convert the response body to a string
11088
11254
  const bodyContents = await response.Body.transformToString();
11089
- console.log(`Retrieved noun metadata body: ${bodyContents}`);
11255
+ this.logger.trace(`Retrieved noun metadata body for ${id}`);
11090
11256
  // Parse the JSON string
11091
11257
  try {
11092
11258
  const parsedMetadata = JSON.parse(bodyContents);
11093
- console.log(`Successfully retrieved noun metadata for ${id}:`, parsedMetadata);
11259
+ this.logger.trace(`Successfully retrieved noun metadata for ${id}`);
11094
11260
  return parsedMetadata;
11095
11261
  }
11096
11262
  catch (parseError) {
11097
- console.error(`Failed to parse noun metadata for ${id}:`, parseError);
11263
+ this.logger.error(`Failed to parse noun metadata for ${id}:`, parseError);
11098
11264
  return null;
11099
11265
  }
11100
11266
  }
@@ -11105,7 +11271,7 @@ class S3CompatibleStorage extends BaseStorage {
11105
11271
  (error.message.includes('NoSuchKey') ||
11106
11272
  error.message.includes('not found') ||
11107
11273
  error.message.includes('does not exist')))) {
11108
- console.log(`Noun metadata not found for ${id}`);
11274
+ this.logger.trace(`Noun metadata not found for ${id}`);
11109
11275
  return null;
11110
11276
  }
11111
11277
  // For other types of errors, convert to BrainyError for better classification
@@ -11204,6 +11370,9 @@ class S3CompatibleStorage extends BaseStorage {
11204
11370
  await deleteObjectsWithPrefix(this.metadataPrefix);
11205
11371
  // Delete all objects in the index directory
11206
11372
  await deleteObjectsWithPrefix(this.indexPrefix);
11373
+ // Clear the statistics cache
11374
+ this.statisticsCache = null;
11375
+ this.statisticsModified = false;
11207
11376
  }
11208
11377
  catch (error) {
11209
11378
  console.error('Failed to clear storage:', error);
@@ -11319,7 +11488,7 @@ class S3CompatibleStorage extends BaseStorage {
11319
11488
  }
11320
11489
  }
11321
11490
  catch (error) {
11322
- console.error(`Error getting metadata from ${object.Key}:`, error);
11491
+ this.logger.warn(`Error getting metadata from ${object.Key}:`, error);
11323
11492
  }
11324
11493
  }
11325
11494
  }
@@ -11340,7 +11509,7 @@ class S3CompatibleStorage extends BaseStorage {
11340
11509
  };
11341
11510
  }
11342
11511
  catch (error) {
11343
- console.error('Failed to get storage status:', error);
11512
+ this.logger.error('Failed to get storage status:', error);
11344
11513
  return {
11345
11514
  type: this.serviceType,
11346
11515
  used: 0,
@@ -11380,6 +11549,11 @@ class S3CompatibleStorage extends BaseStorage {
11380
11549
  scheduleBatchUpdate() {
11381
11550
  // Mark statistics as modified
11382
11551
  this.statisticsModified = true;
11552
+ // If we're in read-only mode, don't update statistics
11553
+ if (this.readOnly) {
11554
+ this.logger.trace('Skipping statistics update in read-only mode');
11555
+ return;
11556
+ }
11383
11557
  // If a timer is already set, don't set another one
11384
11558
  if (this.statisticsBatchUpdateTimerId !== null) {
11385
11559
  return;
@@ -11416,7 +11590,7 @@ class S3CompatibleStorage extends BaseStorage {
11416
11590
  if (!lockAcquired) {
11417
11591
  // Another instance is updating statistics, skip this flush
11418
11592
  // but keep the modified flag so we'll try again later
11419
- console.log('Statistics flush skipped - another instance is updating');
11593
+ this.logger.debug('Statistics flush skipped - another instance is updating');
11420
11594
  return;
11421
11595
  }
11422
11596
  try {
@@ -11435,7 +11609,7 @@ class S3CompatibleStorage extends BaseStorage {
11435
11609
  }
11436
11610
  catch (error) {
11437
11611
  // If we can't read current stats, proceed with local cache
11438
- console.warn('Could not read current statistics from storage, using local cache:', error);
11612
+ this.logger.warn('Could not read current statistics from storage, using local cache:', error);
11439
11613
  }
11440
11614
  // Merge local statistics with storage statistics
11441
11615
  let mergedStats = this.statisticsCache;
@@ -11473,7 +11647,7 @@ class S3CompatibleStorage extends BaseStorage {
11473
11647
  }
11474
11648
  }
11475
11649
  catch (error) {
11476
- console.error('Failed to flush statistics data:', error);
11650
+ this.logger.error('Failed to flush statistics data:', error);
11477
11651
  // Mark as still modified so we'll try again later
11478
11652
  this.statisticsModified = true;
11479
11653
  // Don't throw the error to avoid disrupting the application
@@ -11538,7 +11712,7 @@ class S3CompatibleStorage extends BaseStorage {
11538
11712
  this.scheduleBatchUpdate();
11539
11713
  }
11540
11714
  catch (error) {
11541
- console.error('Failed to save statistics data:', error);
11715
+ this.logger.error('Failed to save statistics data:', error);
11542
11716
  throw new Error(`Failed to save statistics data: ${error}`);
11543
11717
  }
11544
11718
  }
@@ -11548,8 +11722,11 @@ class S3CompatibleStorage extends BaseStorage {
11548
11722
  */
11549
11723
  async getStatisticsData() {
11550
11724
  await this.ensureInitialized();
11551
- // If we have cached statistics, return a deep copy
11552
- if (this.statisticsCache) {
11725
+ // Always fetch fresh statistics from storage to avoid inconsistencies
11726
+ // Only use cache if explicitly in read-only mode
11727
+ const shouldUseCache = this.readOnly && this.statisticsCache &&
11728
+ (Date.now() - this.lastStatisticsFlushTime < this.MIN_FLUSH_INTERVAL_MS);
11729
+ if (shouldUseCache && this.statisticsCache) {
11553
11730
  return {
11554
11731
  nounCount: { ...this.statisticsCache.nounCount },
11555
11732
  verbCount: { ...this.statisticsCache.verbCount },
@@ -11590,7 +11767,7 @@ class S3CompatibleStorage extends BaseStorage {
11590
11767
  return statistics;
11591
11768
  }
11592
11769
  catch (error) {
11593
- console.error('Error getting statistics data:', error);
11770
+ this.logger.error('Error getting statistics data:', error);
11594
11771
  throw error;
11595
11772
  }
11596
11773
  }
@@ -11660,7 +11837,7 @@ class S3CompatibleStorage extends BaseStorage {
11660
11837
  }));
11661
11838
  }
11662
11839
  catch (error) {
11663
- console.warn('Failed to append to change log:', error);
11840
+ this.logger.warn('Failed to append to change log:', error);
11664
11841
  // Don't throw error to avoid disrupting main operations
11665
11842
  }
11666
11843
  }
@@ -11705,7 +11882,7 @@ class S3CompatibleStorage extends BaseStorage {
11705
11882
  }
11706
11883
  }
11707
11884
  catch (error) {
11708
- console.warn(`Failed to read change log entry ${object.Key}:`, error);
11885
+ this.logger.warn(`Failed to read change log entry ${object.Key}:`, error);
11709
11886
  // Continue processing other entries
11710
11887
  }
11711
11888
  }
@@ -11714,7 +11891,7 @@ class S3CompatibleStorage extends BaseStorage {
11714
11891
  return changes.slice(0, maxEntries);
11715
11892
  }
11716
11893
  catch (error) {
11717
- console.error('Failed to get changes from change log:', error);
11894
+ this.logger.error('Failed to get changes from change log:', error);
11718
11895
  return [];
11719
11896
  }
11720
11897
  }
@@ -11761,15 +11938,15 @@ class S3CompatibleStorage extends BaseStorage {
11761
11938
  }));
11762
11939
  }
11763
11940
  catch (error) {
11764
- console.warn(`Failed to delete old change log entry ${key}:`, error);
11941
+ this.logger.warn(`Failed to delete old change log entry ${key}:`, error);
11765
11942
  }
11766
11943
  }
11767
11944
  if (entriesToDelete.length > 0) {
11768
- console.log(`Cleaned up ${entriesToDelete.length} old change log entries`);
11945
+ this.logger.debug(`Cleaned up ${entriesToDelete.length} old change log entries`);
11769
11946
  }
11770
11947
  }
11771
11948
  catch (error) {
11772
- console.warn('Failed to cleanup old change logs:', error);
11949
+ this.logger.warn('Failed to cleanup old change logs:', error);
11773
11950
  }
11774
11951
  }
11775
11952
  /**
@@ -11824,13 +12001,13 @@ class S3CompatibleStorage extends BaseStorage {
11824
12001
  // Schedule automatic cleanup when lock expires
11825
12002
  setTimeout(() => {
11826
12003
  this.releaseLock(lockKey, lockValue).catch((error) => {
11827
- console.warn(`Failed to auto-release expired lock ${lockKey}:`, error);
12004
+ this.logger.warn(`Failed to auto-release expired lock ${lockKey}:`, error);
11828
12005
  });
11829
12006
  }, ttl);
11830
12007
  return true;
11831
12008
  }
11832
12009
  catch (error) {
11833
- console.warn(`Failed to acquire lock ${lockKey}:`, error);
12010
+ this.logger.warn(`Failed to acquire lock ${lockKey}:`, error);
11834
12011
  return false;
11835
12012
  }
11836
12013
  }
@@ -11879,7 +12056,7 @@ class S3CompatibleStorage extends BaseStorage {
11879
12056
  this.activeLocks.delete(lockKey);
11880
12057
  }
11881
12058
  catch (error) {
11882
- console.warn(`Failed to release lock ${lockKey}:`, error);
12059
+ this.logger.warn(`Failed to release lock ${lockKey}:`, error);
11883
12060
  }
11884
12061
  }
11885
12062
  /**
@@ -11930,16 +12107,84 @@ class S3CompatibleStorage extends BaseStorage {
11930
12107
  }));
11931
12108
  }
11932
12109
  catch (error) {
11933
- console.warn(`Failed to delete expired lock ${lockKey}:`, error);
12110
+ this.logger.warn(`Failed to delete expired lock ${lockKey}:`, error);
11934
12111
  }
11935
12112
  }
11936
12113
  if (expiredLocks.length > 0) {
11937
- console.log(`Cleaned up ${expiredLocks.length} expired locks`);
12114
+ this.logger.debug(`Cleaned up ${expiredLocks.length} expired locks`);
11938
12115
  }
11939
12116
  }
11940
12117
  catch (error) {
11941
- console.warn('Failed to cleanup expired locks:', error);
12118
+ this.logger.warn('Failed to cleanup expired locks:', error);
12119
+ }
12120
+ }
12121
+ /**
12122
+ * Get nouns with pagination support
12123
+ * @param options Pagination options
12124
+ * @returns Promise that resolves to a paginated result of nouns
12125
+ */
12126
+ async getNounsWithPagination(options = {}) {
12127
+ await this.ensureInitialized();
12128
+ const limit = options.limit || 100;
12129
+ const cursor = options.cursor;
12130
+ // Get paginated nodes
12131
+ const result = await this.getNodesWithPagination({
12132
+ limit,
12133
+ cursor,
12134
+ useCache: true
12135
+ });
12136
+ // Apply filters if provided
12137
+ let filteredNodes = result.nodes;
12138
+ if (options.filter) {
12139
+ // Filter by noun type
12140
+ if (options.filter.nounType) {
12141
+ const nounTypes = Array.isArray(options.filter.nounType)
12142
+ ? options.filter.nounType
12143
+ : [options.filter.nounType];
12144
+ const filteredByType = [];
12145
+ for (const node of filteredNodes) {
12146
+ const metadata = await this.getNounMetadata(node.id);
12147
+ if (metadata && nounTypes.includes(metadata.type || metadata.noun)) {
12148
+ filteredByType.push(node);
12149
+ }
12150
+ }
12151
+ filteredNodes = filteredByType;
12152
+ }
12153
+ // Filter by service
12154
+ if (options.filter.service) {
12155
+ const services = Array.isArray(options.filter.service)
12156
+ ? options.filter.service
12157
+ : [options.filter.service];
12158
+ const filteredByService = [];
12159
+ for (const node of filteredNodes) {
12160
+ const metadata = await this.getNounMetadata(node.id);
12161
+ if (metadata && services.includes(metadata.service)) {
12162
+ filteredByService.push(node);
12163
+ }
12164
+ }
12165
+ filteredNodes = filteredByService;
12166
+ }
12167
+ // Filter by metadata
12168
+ if (options.filter.metadata) {
12169
+ const metadataFilter = options.filter.metadata;
12170
+ const filteredByMetadata = [];
12171
+ for (const node of filteredNodes) {
12172
+ const metadata = await this.getNounMetadata(node.id);
12173
+ if (metadata) {
12174
+ const matches = Object.entries(metadataFilter).every(([key, value]) => metadata[key] === value);
12175
+ if (matches) {
12176
+ filteredByMetadata.push(node);
12177
+ }
12178
+ }
12179
+ }
12180
+ filteredNodes = filteredByMetadata;
12181
+ }
11942
12182
  }
12183
+ return {
12184
+ items: filteredNodes,
12185
+ hasMore: result.hasMore,
12186
+ nextCursor: result.nextCursor
12187
+ };
11943
12188
  }
11944
12189
  }
11945
12190
 
@@ -12080,7 +12325,8 @@ class FileSystemStorage extends BaseStorage {
12080
12325
  return {
12081
12326
  id: parsedNode.id,
12082
12327
  vector: parsedNode.vector,
12083
- connections
12328
+ connections,
12329
+ level: parsedNode.level || 0
12084
12330
  };
12085
12331
  }
12086
12332
  catch (error) {
@@ -12111,7 +12357,8 @@ class FileSystemStorage extends BaseStorage {
12111
12357
  allNodes.push({
12112
12358
  id: parsedNode.id,
12113
12359
  vector: parsedNode.vector,
12114
- connections
12360
+ connections,
12361
+ level: parsedNode.level || 0
12115
12362
  });
12116
12363
  }
12117
12364
  }
@@ -12150,7 +12397,8 @@ class FileSystemStorage extends BaseStorage {
12150
12397
  nouns.push({
12151
12398
  id: parsedNode.id,
12152
12399
  vector: parsedNode.vector,
12153
- connections
12400
+ connections,
12401
+ level: parsedNode.level || 0
12154
12402
  });
12155
12403
  }
12156
12404
  }
@@ -12415,6 +12663,9 @@ class FileSystemStorage extends BaseStorage {
12415
12663
  await removeDirectoryContents(this.verbMetadataDir);
12416
12664
  // Remove all files in the index directory
12417
12665
  await removeDirectoryContents(this.indexDir);
12666
+ // Clear the statistics cache
12667
+ this.statisticsCache = null;
12668
+ this.statisticsModified = false;
12418
12669
  }
12419
12670
  /**
12420
12671
  * Get information about storage usage and capacity
@@ -17110,6 +17361,230 @@ class CacheAutoConfigurator {
17110
17361
  }
17111
17362
  }
17112
17363
 
17364
+ /**
17365
+ * Lightweight statistics collector for Brainy
17366
+ * Designed to have minimal performance impact even with millions of entries
17367
+ */
17368
+ class StatisticsCollector {
17369
+ constructor() {
17370
+ // Content type tracking (lightweight counters)
17371
+ this.contentTypes = new Map();
17372
+ // Data freshness tracking (only track timestamps, not full data)
17373
+ this.oldestTimestamp = Date.now();
17374
+ this.newestTimestamp = Date.now();
17375
+ this.updateTimestamps = [];
17376
+ // Search performance tracking (rolling window)
17377
+ this.searchMetrics = {
17378
+ totalSearches: 0,
17379
+ totalSearchTimeMs: 0,
17380
+ searchTimestamps: [],
17381
+ topSearchTerms: new Map()
17382
+ };
17383
+ // Verb type tracking
17384
+ this.verbTypes = new Map();
17385
+ // Storage size estimates (updated periodically, not on every operation)
17386
+ this.storageSizeCache = {
17387
+ lastUpdated: 0,
17388
+ sizes: {
17389
+ nouns: 0,
17390
+ verbs: 0,
17391
+ metadata: 0,
17392
+ index: 0
17393
+ }
17394
+ };
17395
+ this.MAX_TIMESTAMPS = 1000; // Keep last 1000 timestamps
17396
+ this.MAX_SEARCH_TERMS = 100; // Track top 100 search terms
17397
+ this.SIZE_UPDATE_INTERVAL = 60000; // Update sizes every minute
17398
+ }
17399
+ /**
17400
+ * Track content type (very lightweight)
17401
+ */
17402
+ trackContentType(type) {
17403
+ this.contentTypes.set(type, (this.contentTypes.get(type) || 0) + 1);
17404
+ }
17405
+ /**
17406
+ * Track data update timestamp (lightweight)
17407
+ */
17408
+ trackUpdate(timestamp) {
17409
+ const ts = timestamp || Date.now();
17410
+ // Update oldest/newest
17411
+ if (ts < this.oldestTimestamp)
17412
+ this.oldestTimestamp = ts;
17413
+ if (ts > this.newestTimestamp)
17414
+ this.newestTimestamp = ts;
17415
+ // Add to rolling window
17416
+ this.updateTimestamps.push({ timestamp: ts, count: 1 });
17417
+ // Keep window size limited
17418
+ if (this.updateTimestamps.length > this.MAX_TIMESTAMPS) {
17419
+ this.updateTimestamps.shift();
17420
+ }
17421
+ }
17422
+ /**
17423
+ * Track search performance (lightweight)
17424
+ */
17425
+ trackSearch(searchTerm, durationMs) {
17426
+ this.searchMetrics.totalSearches++;
17427
+ this.searchMetrics.totalSearchTimeMs += durationMs;
17428
+ // Add to rolling window
17429
+ this.searchMetrics.searchTimestamps.push({
17430
+ timestamp: Date.now(),
17431
+ count: 1
17432
+ });
17433
+ // Keep window size limited
17434
+ if (this.searchMetrics.searchTimestamps.length > this.MAX_TIMESTAMPS) {
17435
+ this.searchMetrics.searchTimestamps.shift();
17436
+ }
17437
+ // Track search term (limit to top N)
17438
+ const termCount = (this.searchMetrics.topSearchTerms.get(searchTerm) || 0) + 1;
17439
+ this.searchMetrics.topSearchTerms.set(searchTerm, termCount);
17440
+ // Prune if too many terms
17441
+ if (this.searchMetrics.topSearchTerms.size > this.MAX_SEARCH_TERMS * 2) {
17442
+ this.pruneSearchTerms();
17443
+ }
17444
+ }
17445
+ /**
17446
+ * Track verb type (lightweight)
17447
+ */
17448
+ trackVerbType(type) {
17449
+ this.verbTypes.set(type, (this.verbTypes.get(type) || 0) + 1);
17450
+ }
17451
+ /**
17452
+ * Update storage size estimates (called periodically, not on every operation)
17453
+ */
17454
+ updateStorageSizes(sizes) {
17455
+ this.storageSizeCache = {
17456
+ lastUpdated: Date.now(),
17457
+ sizes
17458
+ };
17459
+ }
17460
+ /**
17461
+ * Get comprehensive statistics
17462
+ */
17463
+ getStatistics() {
17464
+ const now = Date.now();
17465
+ const hourAgo = now - 3600000;
17466
+ const dayAgo = now - 86400000;
17467
+ const weekAgo = now - 604800000;
17468
+ const monthAgo = now - 2592000000;
17469
+ // Calculate data freshness
17470
+ const updatesLastHour = this.updateTimestamps.filter(t => t.timestamp > hourAgo).length;
17471
+ const updatesLastDay = this.updateTimestamps.filter(t => t.timestamp > dayAgo).length;
17472
+ // Calculate age distribution
17473
+ const ageDistribution = {
17474
+ last24h: 0,
17475
+ last7d: 0,
17476
+ last30d: 0,
17477
+ older: 0
17478
+ };
17479
+ // Estimate based on update patterns (not scanning all data)
17480
+ const totalUpdates = this.updateTimestamps.length;
17481
+ if (totalUpdates > 0) {
17482
+ const recentUpdates = this.updateTimestamps.filter(t => t.timestamp > dayAgo).length;
17483
+ const weekUpdates = this.updateTimestamps.filter(t => t.timestamp > weekAgo).length;
17484
+ const monthUpdates = this.updateTimestamps.filter(t => t.timestamp > monthAgo).length;
17485
+ ageDistribution.last24h = Math.round((recentUpdates / totalUpdates) * 100);
17486
+ ageDistribution.last7d = Math.round(((weekUpdates - recentUpdates) / totalUpdates) * 100);
17487
+ ageDistribution.last30d = Math.round(((monthUpdates - weekUpdates) / totalUpdates) * 100);
17488
+ ageDistribution.older = 100 - ageDistribution.last24h - ageDistribution.last7d - ageDistribution.last30d;
17489
+ }
17490
+ // Calculate search metrics
17491
+ const searchesLastHour = this.searchMetrics.searchTimestamps.filter(t => t.timestamp > hourAgo).length;
17492
+ const searchesLastDay = this.searchMetrics.searchTimestamps.filter(t => t.timestamp > dayAgo).length;
17493
+ const avgSearchTime = this.searchMetrics.totalSearches > 0
17494
+ ? this.searchMetrics.totalSearchTimeMs / this.searchMetrics.totalSearches
17495
+ : 0;
17496
+ // Get top search terms
17497
+ const topSearchTerms = Array.from(this.searchMetrics.topSearchTerms.entries())
17498
+ .sort((a, b) => b[1] - a[1])
17499
+ .slice(0, 10)
17500
+ .map(([term]) => term);
17501
+ // Calculate storage metrics
17502
+ const totalSize = Object.values(this.storageSizeCache.sizes).reduce((a, b) => a + b, 0);
17503
+ return {
17504
+ contentTypes: Object.fromEntries(this.contentTypes),
17505
+ dataFreshness: {
17506
+ oldestEntry: new Date(this.oldestTimestamp).toISOString(),
17507
+ newestEntry: new Date(this.newestTimestamp).toISOString(),
17508
+ updatesLastHour,
17509
+ updatesLastDay,
17510
+ ageDistribution
17511
+ },
17512
+ storageMetrics: {
17513
+ totalSizeBytes: totalSize,
17514
+ nounsSizeBytes: this.storageSizeCache.sizes.nouns,
17515
+ verbsSizeBytes: this.storageSizeCache.sizes.verbs,
17516
+ metadataSizeBytes: this.storageSizeCache.sizes.metadata,
17517
+ indexSizeBytes: this.storageSizeCache.sizes.index
17518
+ },
17519
+ searchMetrics: {
17520
+ totalSearches: this.searchMetrics.totalSearches,
17521
+ averageSearchTimeMs: avgSearchTime,
17522
+ searchesLastHour,
17523
+ searchesLastDay,
17524
+ topSearchTerms
17525
+ },
17526
+ verbStatistics: {
17527
+ totalVerbs: Array.from(this.verbTypes.values()).reduce((a, b) => a + b, 0),
17528
+ verbTypes: Object.fromEntries(this.verbTypes),
17529
+ averageConnectionsPerVerb: 2 // Verbs connect 2 nouns
17530
+ }
17531
+ };
17532
+ }
17533
+ /**
17534
+ * Merge statistics from storage (for distributed systems)
17535
+ */
17536
+ mergeFromStorage(stored) {
17537
+ // Merge content types
17538
+ if (stored.contentTypes) {
17539
+ for (const [type, count] of Object.entries(stored.contentTypes)) {
17540
+ this.contentTypes.set(type, count);
17541
+ }
17542
+ }
17543
+ // Merge verb types
17544
+ if (stored.verbStatistics?.verbTypes) {
17545
+ for (const [type, count] of Object.entries(stored.verbStatistics.verbTypes)) {
17546
+ this.verbTypes.set(type, count);
17547
+ }
17548
+ }
17549
+ // Merge search metrics
17550
+ if (stored.searchMetrics) {
17551
+ this.searchMetrics.totalSearches = stored.searchMetrics.totalSearches || 0;
17552
+ this.searchMetrics.totalSearchTimeMs = (stored.searchMetrics.averageSearchTimeMs || 0) * this.searchMetrics.totalSearches;
17553
+ }
17554
+ // Merge data freshness
17555
+ if (stored.dataFreshness) {
17556
+ this.oldestTimestamp = new Date(stored.dataFreshness.oldestEntry).getTime();
17557
+ this.newestTimestamp = new Date(stored.dataFreshness.newestEntry).getTime();
17558
+ }
17559
+ }
17560
+ /**
17561
+ * Reset statistics (for testing)
17562
+ */
17563
+ reset() {
17564
+ this.contentTypes.clear();
17565
+ this.verbTypes.clear();
17566
+ this.updateTimestamps = [];
17567
+ this.searchMetrics = {
17568
+ totalSearches: 0,
17569
+ totalSearchTimeMs: 0,
17570
+ searchTimestamps: [],
17571
+ topSearchTerms: new Map()
17572
+ };
17573
+ this.oldestTimestamp = Date.now();
17574
+ this.newestTimestamp = Date.now();
17575
+ }
17576
+ pruneSearchTerms() {
17577
+ // Keep only top N search terms
17578
+ const sorted = Array.from(this.searchMetrics.topSearchTerms.entries())
17579
+ .sort((a, b) => b[1] - a[1])
17580
+ .slice(0, this.MAX_SEARCH_TERMS);
17581
+ this.searchMetrics.topSearchTerms.clear();
17582
+ for (const [term, count] of sorted) {
17583
+ this.searchMetrics.topSearchTerms.set(term, count);
17584
+ }
17585
+ }
17586
+ }
17587
+
17113
17588
  /**
17114
17589
  * BrainyData
17115
17590
  * Main class that provides the vector database functionality
@@ -17170,6 +17645,8 @@ class BrainyData {
17170
17645
  this.operationalMode = null;
17171
17646
  this.domainDetector = null;
17172
17647
  this.healthMonitor = null;
17648
+ // Statistics collector
17649
+ this.statisticsCollector = new StatisticsCollector();
17173
17650
  // Set dimensions to fixed value of 512 (Universal Sentence Encoder dimension)
17174
17651
  this._dimensions = 512;
17175
17652
  // Set distance function
@@ -17433,7 +17910,8 @@ class BrainyData {
17433
17910
  }
17434
17911
  // Adapt cache configuration based on performance (every few updates)
17435
17912
  // Only adapt every 5th update to avoid over-optimization
17436
- const updateCount = Math.floor((Date.now() - (this.lastUpdateTime || 0)) / this.realtimeUpdateConfig.interval);
17913
+ const updateCount = Math.floor((Date.now() - (this.lastUpdateTime || 0)) /
17914
+ this.realtimeUpdateConfig.interval);
17437
17915
  if (updateCount % 5 === 0) {
17438
17916
  this.adaptCacheConfiguration();
17439
17917
  }
@@ -17751,6 +18229,16 @@ class BrainyData {
17751
18229
  // Continue initialization even if remote connection fails
17752
18230
  }
17753
18231
  }
18232
+ // Initialize statistics collector with existing data
18233
+ try {
18234
+ const existingStats = await this.storage.getStatistics();
18235
+ if (existingStats) {
18236
+ this.statisticsCollector.mergeFromStorage(existingStats);
18237
+ }
18238
+ }
18239
+ catch (e) {
18240
+ // Ignore errors loading existing statistics
18241
+ }
17754
18242
  this.isInitialized = true;
17755
18243
  this.isInitializing = false;
17756
18244
  // Start real-time updates if enabled
@@ -17962,13 +18450,15 @@ class BrainyData {
17962
18450
  try {
17963
18451
  if (this.writeOnly) {
17964
18452
  // In write-only mode, check storage directly
17965
- existingNoun = await this.storage.getNoun(options.id) ?? undefined;
18453
+ existingNoun =
18454
+ (await this.storage.getNoun(options.id)) ?? undefined;
17966
18455
  }
17967
18456
  else {
17968
18457
  // In normal mode, check index first, then storage
17969
18458
  existingNoun = this.index.getNouns().get(options.id);
17970
18459
  if (!existingNoun) {
17971
- existingNoun = await this.storage.getNoun(options.id) ?? undefined;
18460
+ existingNoun =
18461
+ (await this.storage.getNoun(options.id)) ?? undefined;
17972
18462
  }
17973
18463
  }
17974
18464
  if (existingNoun) {
@@ -18003,6 +18493,7 @@ class BrainyData {
18003
18493
  id,
18004
18494
  vector,
18005
18495
  connections: new Map(),
18496
+ level: 0, // Default level for new nodes
18006
18497
  metadata: undefined // Will be set separately
18007
18498
  };
18008
18499
  }
@@ -18076,17 +18567,24 @@ class BrainyData {
18076
18567
  // Domain already specified, keep it
18077
18568
  const domainInfo = this.domainDetector.detectDomain(metadataToSave);
18078
18569
  if (domainInfo.domainMetadata) {
18079
- metadataToSave.domainMetadata = domainInfo.domainMetadata;
18570
+ ;
18571
+ metadataToSave.domainMetadata =
18572
+ domainInfo.domainMetadata;
18080
18573
  }
18081
18574
  }
18082
18575
  else {
18083
18576
  // Try to detect domain from the data
18084
- const dataToAnalyze = Array.isArray(vectorOrData) ? metadata : vectorOrData;
18577
+ const dataToAnalyze = Array.isArray(vectorOrData)
18578
+ ? metadata
18579
+ : vectorOrData;
18085
18580
  const domainInfo = this.domainDetector.detectDomain(dataToAnalyze);
18086
18581
  if (domainInfo.domain) {
18582
+ ;
18087
18583
  metadataToSave.domain = domainInfo.domain;
18088
18584
  if (domainInfo.domainMetadata) {
18089
- metadataToSave.domainMetadata = domainInfo.domainMetadata;
18585
+ ;
18586
+ metadataToSave.domainMetadata =
18587
+ domainInfo.domainMetadata;
18090
18588
  }
18091
18589
  }
18092
18590
  }
@@ -18101,10 +18599,19 @@ class BrainyData {
18101
18599
  // Track metadata statistics
18102
18600
  const metadataService = this.getServiceName(options);
18103
18601
  await this.storage.incrementStatistic('metadata', metadataService);
18602
+ // Track content type if it's a GraphNoun
18603
+ if (metadataToSave &&
18604
+ typeof metadataToSave === 'object' &&
18605
+ 'noun' in metadataToSave) {
18606
+ this.statisticsCollector.trackContentType(metadataToSave.noun);
18607
+ }
18608
+ // Track update timestamp
18609
+ this.statisticsCollector.trackUpdate();
18104
18610
  }
18105
18611
  }
18106
- // Update HNSW index size (excluding verbs)
18107
- await this.storage.updateHnswIndexSize(await this.getNounCount());
18612
+ // Update HNSW index size with actual index size
18613
+ const indexSize = this.index.size();
18614
+ await this.storage.updateHnswIndexSize(indexSize);
18108
18615
  // Update health metrics if in distributed mode
18109
18616
  if (this.healthMonitor) {
18110
18617
  const vectorCount = await this.getNounCount();
@@ -18578,7 +19085,7 @@ class BrainyData {
18578
19085
  let startIndex = 0;
18579
19086
  // If cursor provided, find starting position
18580
19087
  if (options.cursor) {
18581
- startIndex = allResults.findIndex(r => r.id === options.cursor.lastId &&
19088
+ startIndex = allResults.findIndex((r) => r.id === options.cursor.lastId &&
18582
19089
  Math.abs(r.score - options.cursor.lastScore) < 0.0001);
18583
19090
  if (startIndex >= 0) {
18584
19091
  startIndex += 1; // Start after the cursor position
@@ -18595,7 +19102,8 @@ class BrainyData {
18595
19102
  }
18596
19103
  // Create cursor for next page
18597
19104
  let nextCursor;
18598
- const hasMoreResults = (startIndex + results.length) < allResults.length || allResults.length >= searchK;
19105
+ const hasMoreResults = startIndex + results.length < allResults.length ||
19106
+ allResults.length >= searchK;
18599
19107
  if (results.length > 0 && hasMoreResults) {
18600
19108
  const lastResult = results[results.length - 1];
18601
19109
  nextCursor = {
@@ -18679,7 +19187,7 @@ class BrainyData {
18679
19187
  });
18680
19188
  }
18681
19189
  // Filter out placeholder nouns from search results
18682
- searchResults = searchResults.filter(result => {
19190
+ searchResults = searchResults.filter((result) => {
18683
19191
  if (result.metadata && typeof result.metadata === 'object') {
18684
19192
  const metadata = result.metadata;
18685
19193
  // Exclude placeholder nouns from search results
@@ -18787,7 +19295,7 @@ class BrainyData {
18787
19295
  // In write-only mode, query storage directly since index is not loaded
18788
19296
  if (this.writeOnly) {
18789
19297
  try {
18790
- noun = await this.storage.getNoun(id) ?? undefined;
19298
+ noun = (await this.storage.getNoun(id)) ?? undefined;
18791
19299
  }
18792
19300
  catch (storageError) {
18793
19301
  // If storage lookup fails, return null (noun doesn't exist)
@@ -18800,7 +19308,7 @@ class BrainyData {
18800
19308
  // If not found in index, fallback to storage (for race conditions)
18801
19309
  if (!noun && this.storage) {
18802
19310
  try {
18803
- noun = await this.storage.getNoun(id) ?? undefined;
19311
+ noun = (await this.storage.getNoun(id)) ?? undefined;
18804
19312
  }
18805
19313
  catch (storageError) {
18806
19314
  // Storage lookup failed, noun doesn't exist
@@ -19192,6 +19700,7 @@ class BrainyData {
19192
19700
  id: sourceId,
19193
19701
  vector: sourcePlaceholderVector,
19194
19702
  connections: new Map(),
19703
+ level: 0,
19195
19704
  metadata: sourceMetadata
19196
19705
  };
19197
19706
  // Create placeholder target noun
@@ -19212,6 +19721,7 @@ class BrainyData {
19212
19721
  id: targetId,
19213
19722
  vector: targetPlaceholderVector,
19214
19723
  connections: new Map(),
19724
+ level: 0,
19215
19725
  metadata: targetMetadata
19216
19726
  };
19217
19727
  // Save placeholder nouns to storage (but skip indexing for speed)
@@ -19448,8 +19958,11 @@ class BrainyData {
19448
19958
  // Track verb statistics
19449
19959
  const serviceForStats = this.getServiceName(options);
19450
19960
  await this.storage.incrementStatistic('verb', serviceForStats);
19451
- // Update HNSW index size (excluding verbs)
19452
- await this.storage.updateHnswIndexSize(await this.getNounCount());
19961
+ // Track verb type
19962
+ this.statisticsCollector.trackVerbType(verbMetadata.verb);
19963
+ // Update HNSW index size with actual index size
19964
+ const indexSize = this.index.size();
19965
+ await this.storage.updateHnswIndexSize(indexSize);
19453
19966
  // Invalidate search cache since verb data has changed
19454
19967
  this.searchCache.invalidateOnDataChange('add');
19455
19968
  return id;
@@ -19674,6 +20187,8 @@ class BrainyData {
19674
20187
  await this.index.clear();
19675
20188
  // Clear storage
19676
20189
  await this.storage.clear();
20190
+ // Reset statistics collector
20191
+ this.statisticsCollector = new StatisticsCollector();
19677
20192
  // Clear search cache since all data has been removed
19678
20193
  this.searchCache.invalidateOnDataChange('delete');
19679
20194
  }
@@ -19727,7 +20242,8 @@ class BrainyData {
19727
20242
  // Apply new cache configuration
19728
20243
  this.searchCache.updateConfig(newConfig.cacheConfig);
19729
20244
  // Apply new real-time update configuration if needed
19730
- if (newConfig.realtimeConfig.enabled !== this.realtimeUpdateConfig.enabled ||
20245
+ if (newConfig.realtimeConfig.enabled !==
20246
+ this.realtimeUpdateConfig.enabled ||
19731
20247
  newConfig.realtimeConfig.interval !== this.realtimeUpdateConfig.interval) {
19732
20248
  const wasEnabled = this.realtimeUpdateConfig.enabled;
19733
20249
  this.realtimeUpdateConfig = {
@@ -19817,6 +20333,41 @@ class BrainyData {
19817
20333
  // Call the flushStatisticsToStorage method on the storage adapter
19818
20334
  await this.storage.flushStatisticsToStorage();
19819
20335
  }
20336
+ /**
20337
+ * Update storage sizes if needed (called periodically for performance)
20338
+ */
20339
+ async updateStorageSizesIfNeeded() {
20340
+ // Only update every minute to avoid performance impact
20341
+ const now = Date.now();
20342
+ const lastUpdate = this.lastStorageSizeUpdate || 0;
20343
+ if (now - lastUpdate < 60000) {
20344
+ return; // Skip if updated recently
20345
+ }
20346
+ this.lastStorageSizeUpdate = now;
20347
+ try {
20348
+ // Estimate sizes based on counts and average sizes
20349
+ const stats = await this.storage.getStatistics();
20350
+ if (stats) {
20351
+ const avgNounSize = 2048; // ~2KB per noun (vector + metadata)
20352
+ const avgVerbSize = 512; // ~0.5KB per verb
20353
+ const avgMetadataSize = 256; // ~0.25KB per metadata entry
20354
+ const avgIndexEntrySize = 128; // ~128 bytes per index entry
20355
+ // Calculate total counts
20356
+ const totalNouns = Object.values(stats.nounCount).reduce((a, b) => a + b, 0);
20357
+ const totalVerbs = Object.values(stats.verbCount).reduce((a, b) => a + b, 0);
20358
+ const totalMetadata = Object.values(stats.metadataCount).reduce((a, b) => a + b, 0);
20359
+ this.statisticsCollector.updateStorageSizes({
20360
+ nouns: totalNouns * avgNounSize,
20361
+ verbs: totalVerbs * avgVerbSize,
20362
+ metadata: totalMetadata * avgMetadataSize,
20363
+ index: stats.hnswIndexSize * avgIndexEntrySize
20364
+ });
20365
+ }
20366
+ }
20367
+ catch (error) {
20368
+ // Ignore errors in size calculation
20369
+ }
20370
+ }
19820
20371
  /**
19821
20372
  * Get statistics about the current state of the database
19822
20373
  * @param options Additional options for retrieving statistics
@@ -19891,6 +20442,40 @@ class BrainyData {
19891
20442
  relate: result.verbCount,
19892
20443
  total: result.nounCount + result.verbCount + result.metadataCount
19893
20444
  };
20445
+ // Add extended statistics if requested
20446
+ if (true) {
20447
+ // Always include for now
20448
+ // Add index health metrics
20449
+ try {
20450
+ const indexHealth = this.index.getIndexHealth();
20451
+ result.indexHealth = indexHealth;
20452
+ }
20453
+ catch (e) {
20454
+ // Index health not available
20455
+ }
20456
+ // Add cache metrics
20457
+ try {
20458
+ const cacheStats = this.searchCache.getStats();
20459
+ result.cacheMetrics = cacheStats;
20460
+ }
20461
+ catch (e) {
20462
+ // Cache stats not available
20463
+ }
20464
+ // Add memory usage
20465
+ if (typeof process !== 'undefined' && process.memoryUsage) {
20466
+ ;
20467
+ result.memoryUsage = process.memoryUsage().heapUsed;
20468
+ }
20469
+ // Add last updated timestamp
20470
+ ;
20471
+ result.lastUpdated =
20472
+ stats.lastUpdated || new Date().toISOString();
20473
+ // Add enhanced statistics from collector
20474
+ const collectorStats = this.statisticsCollector.getStatistics();
20475
+ Object.assign(result, collectorStats);
20476
+ // Update storage sizes if needed (only periodically for performance)
20477
+ await this.updateStorageSizesIfNeeded();
20478
+ }
19894
20479
  return result;
19895
20480
  }
19896
20481
  // If statistics are not available, return zeros instead of calculating on-demand
@@ -20250,15 +20835,20 @@ class BrainyData {
20250
20835
  await this.ensureInitialized();
20251
20836
  // Check if database is in write-only mode
20252
20837
  this.checkWriteOnly();
20838
+ const searchStartTime = Date.now();
20253
20839
  try {
20254
20840
  // Embed the query text
20255
20841
  const queryVector = await this.embed(query);
20256
20842
  // Search using the embedded vector
20257
- return await this.search(queryVector, k, {
20843
+ const results = await this.search(queryVector, k, {
20258
20844
  nounTypes: options.nounTypes,
20259
20845
  includeVerbs: options.includeVerbs,
20260
20846
  searchMode: options.searchMode
20261
20847
  });
20848
+ // Track search performance
20849
+ const duration = Date.now() - searchStartTime;
20850
+ this.statisticsCollector.trackSearch(query, duration);
20851
+ return results;
20262
20852
  }
20263
20853
  catch (error) {
20264
20854
  console.error('Failed to search with text query:', error);
@@ -127662,5 +128252,5 @@ var _osShim$1 = /*#__PURE__*/Object.freeze({
127662
128252
  totalmem: totalmem
127663
128253
  });
127664
128254
 
127665
- export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, applyTensorFlowPatch, areWebWorkersAvailable, areWorkerThreadsAvailable, areWorkerThreadsAvailableSync, augmentationPipeline, availableAugmentations, cleanupWorkerPools, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, getNounTypeMap, getNounTypes, getStatistics, getVerbTypeMap, getVerbTypes, initializeAugmentationPipeline, isBrowser$1 as isBrowser, isNode, isThreadingAvailable, isThreadingAvailableAsync, isWebWorker, loadAugmentationModule, loadAugmentationsFromModules, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
128255
+ export { AugmentationType, BrainyData, BrainyMCPAdapter, BrainyMCPService, ExecutionMode, FileSystemStorage, FileSystemStorageAugmentation, HNSWIndex, HNSWIndexOptimized, LogLevel, MCPAugmentationToolset, MCPRequestType, MCP_VERSION, MemoryStorage, MemoryStorageAugmentation, NounType, OPFSStorage, OPFSStorageAugmentation, Pipeline, S3CompatibleStorage as R2Storage, S3CompatibleStorage, SequentialPipeline, ServerSearchActivationAugmentation, ServerSearchConduitAugmentation, StreamlinedExecutionMode, UniversalSentenceEncoder$1 as UniversalSentenceEncoder, VerbType, WebRTCConduitAugmentation, WebSocketConduitAugmentation, addWebSocketSupport, applyTensorFlowPatch, areWebWorkersAvailable, areWorkerThreadsAvailable, areWorkerThreadsAvailableSync, augmentationPipeline, availableAugmentations, cleanupWorkerPools, configureLogger, cosineDistance$1 as cosineDistance, createAugmentationRegistryPlugin, createAugmentationRegistryRollupPlugin, createConduitAugmentation, createEmbeddingFunction, createMemoryAugmentation, createModuleLogger, createPipeline, createSenseAugmentation, createServerSearchAugmentations, createStorage, createStreamingPipeline, createTensorFlowEmbeddingFunction, createThreadedEmbeddingFunction, defaultEmbeddingFunction, dotProductDistance, environment, euclideanDistance, executeAugmentation, executeByType, executeInThread, executeSingle, executeStreamlined, getAugmentationsByType, getNounTypeMap, getNounTypes, getStatistics, getVerbTypeMap, getVerbTypes, initializeAugmentationPipeline, isBrowser$1 as isBrowser, isNode, isThreadingAvailable, isThreadingAvailableAsync, isWebWorker, loadAugmentationModule, loadAugmentationsFromModules, logger, manhattanDistance, pipeline, processStaticData, processStreamingData, registerAugmentation, sequentialPipeline, setAugmentationEnabled };
127666
128256
  //# sourceMappingURL=unified.js.map