@soulcraft/brainy 3.20.3 → 3.20.4

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.
@@ -897,26 +897,32 @@ export class FileSystemStorage extends BaseStorage {
897
897
  const limit = options.limit || 100;
898
898
  const startIndex = options.cursor ? parseInt(options.cursor, 10) : 0;
899
899
  try {
900
- // Production-scale optimization: Use persisted count for total instead of scanning
901
- const totalCount = this.totalVerbCount || 0;
900
+ // Get actual verb files first (critical for accuracy)
901
+ const verbFiles = await this.getAllShardedFiles(this.verbsDir);
902
+ verbFiles.sort(); // Consistent ordering for pagination
903
+ // Use actual file count - don't trust cached totalVerbCount
904
+ // This prevents accessing undefined array elements
905
+ const actualFileCount = verbFiles.length;
902
906
  // For large datasets, warn about performance
903
- if (totalCount > 1000000) {
904
- console.warn(`Very large verb dataset detected (${totalCount} verbs). Performance may be degraded. Consider database storage for optimal performance.`);
907
+ if (actualFileCount > 1000000) {
908
+ console.warn(`Very large verb dataset detected (${actualFileCount} verbs). Performance may be degraded. Consider database storage for optimal performance.`);
905
909
  }
906
- // Calculate pagination bounds
907
- const endIndex = Math.min(startIndex + limit, totalCount);
908
- const hasMore = endIndex < totalCount;
909
910
  // For production-scale datasets, use streaming approach
910
- if (totalCount > 50000) {
911
+ if (actualFileCount > 50000) {
911
912
  return await this.getVerbsWithPaginationStreaming(options, startIndex, limit);
912
913
  }
913
- // For smaller datasets, use the current approach (with optimizations)
914
- const verbFiles = await this.getAllShardedFiles(this.verbsDir);
915
- verbFiles.sort(); // This is still acceptable for <50k files
914
+ // Calculate pagination bounds using ACTUAL file count
915
+ const endIndex = Math.min(startIndex + limit, actualFileCount);
916
916
  // Load the requested page of verbs
917
917
  const verbs = [];
918
+ let successfullyLoaded = 0;
918
919
  for (let i = startIndex; i < endIndex; i++) {
919
920
  const file = verbFiles[i];
921
+ // CRITICAL: Null-safety check for undefined array elements
922
+ if (!file) {
923
+ console.warn(`Unexpected undefined file at index ${i}, skipping`);
924
+ continue;
925
+ }
920
926
  const id = file.replace('.json', '');
921
927
  try {
922
928
  // Read the verb data (HNSWVerb stored as edge) - use sharded path
@@ -1003,14 +1009,18 @@ export class FileSystemStorage extends BaseStorage {
1003
1009
  }
1004
1010
  }
1005
1011
  verbs.push(verb);
1012
+ successfullyLoaded++;
1006
1013
  }
1007
1014
  catch (error) {
1008
1015
  console.warn(`Failed to read verb ${id}:`, error);
1009
1016
  }
1010
1017
  }
1018
+ // CRITICAL FIX: hasMore based on actual file count, not cached totalVerbCount
1019
+ // Also verify we successfully loaded items (prevents infinite loops on corrupted storage)
1020
+ const hasMore = (endIndex < actualFileCount) && (successfullyLoaded > 0 || startIndex === 0);
1011
1021
  return {
1012
1022
  items: verbs,
1013
- totalCount,
1023
+ totalCount: actualFileCount, // Return actual count, not cached value
1014
1024
  hasMore,
1015
1025
  nextCursor: hasMore ? String(endIndex) : undefined
1016
1026
  };
@@ -1587,14 +1597,14 @@ export class FileSystemStorage extends BaseStorage {
1587
1597
  */
1588
1598
  async getVerbsWithPaginationStreaming(options, startIndex, limit) {
1589
1599
  const verbs = [];
1590
- const totalCount = this.totalVerbCount || 0;
1591
1600
  let processedCount = 0;
1592
1601
  let skippedCount = 0;
1593
1602
  let resultCount = 0;
1594
1603
  const depth = this.cachedShardingDepth ?? this.getOptimalShardingDepth();
1595
1604
  try {
1596
1605
  // Stream through sharded directories efficiently
1597
- const hasMore = await this.streamShardedFiles(this.verbsDir, depth, async (filename, filePath) => {
1606
+ // hasMore=false means we reached the end of files, hasMore=true means streaming stopped early
1607
+ const streamingHasMore = await this.streamShardedFiles(this.verbsDir, depth, async (filename, filePath) => {
1598
1608
  // Skip files until we reach start index
1599
1609
  if (skippedCount < startIndex) {
1600
1610
  skippedCount++;
@@ -1602,7 +1612,7 @@ export class FileSystemStorage extends BaseStorage {
1602
1612
  }
1603
1613
  // Stop if we have enough results
1604
1614
  if (resultCount >= limit) {
1605
- return false; // stop streaming
1615
+ return false; // stop streaming - more files exist
1606
1616
  }
1607
1617
  try {
1608
1618
  const id = filename.replace('.json', '');
@@ -1666,10 +1676,13 @@ export class FileSystemStorage extends BaseStorage {
1666
1676
  return true; // continue
1667
1677
  }
1668
1678
  });
1669
- const finalHasMore = (startIndex + resultCount) < totalCount;
1679
+ // CRITICAL FIX: Use streaming result for hasMore, not cached totalVerbCount
1680
+ // streamingHasMore=false means we exhausted all files
1681
+ // Also verify we loaded items to prevent infinite loops
1682
+ const finalHasMore = streamingHasMore && (resultCount > 0 || startIndex === 0);
1670
1683
  return {
1671
1684
  items: verbs,
1672
- totalCount,
1685
+ totalCount: this.totalVerbCount || undefined, // Return cached count as hint only
1673
1686
  hasMore: finalHasMore,
1674
1687
  nextCursor: finalHasMore ? String(startIndex + resultCount) : undefined
1675
1688
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soulcraft/brainy",
3
- "version": "3.20.3",
3
+ "version": "3.20.4",
4
4
  "description": "Universal Knowledge Protocol™ - World's first Triple Intelligence database unifying vector, graph, and document search in one API. 31 nouns × 40 verbs for infinite expressiveness.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",