@soulcraft/brainy 3.20.3 → 3.20.5
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/CHANGELOG.md +8 -1
- package/dist/brainy.js +9 -0
- package/dist/storage/adapters/fileSystemStorage.js +30 -17
- package/dist/storage/baseStorage.js +11 -6
- package/package.json +10 -6
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
+
### [3.20.5](https://github.com/soulcraftlabs/brainy/compare/v3.20.4...v3.20.5) (2025-10-01)
|
|
6
|
+
|
|
7
|
+
- feat: add --skip-tests flag to release script (0614171)
|
|
8
|
+
- fix: resolve critical bugs in delete operations and fix flaky tests (8476047)
|
|
9
|
+
- feat: implement simpler, more reliable release workflow (386fd2c)
|
|
10
|
+
|
|
11
|
+
|
|
5
12
|
### [3.20.2](https://github.com/soulcraftlabs/brainy/compare/v3.20.1...v3.20.2) (2025-09-30)
|
|
6
13
|
|
|
7
14
|
### Bug Fixes
|
|
@@ -276,4 +283,4 @@ See [MIGRATION.md](MIGRATION.md) for detailed migration instructions including:
|
|
|
276
283
|
- API changes and new patterns
|
|
277
284
|
- Storage format updates
|
|
278
285
|
- Configuration changes
|
|
279
|
-
- New features and capabilities
|
|
286
|
+
- New features and capabilities
|
package/dist/brainy.js
CHANGED
|
@@ -424,6 +424,15 @@ export class Brainy {
|
|
|
424
424
|
await this.graphIndex.removeVerb(verb.id);
|
|
425
425
|
// Then delete from storage
|
|
426
426
|
await this.storage.deleteVerb(verb.id);
|
|
427
|
+
// Delete verb metadata if exists
|
|
428
|
+
try {
|
|
429
|
+
if (typeof this.storage.deleteVerbMetadata === 'function') {
|
|
430
|
+
await this.storage.deleteVerbMetadata(verb.id);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
catch {
|
|
434
|
+
// Ignore if not supported
|
|
435
|
+
}
|
|
427
436
|
}
|
|
428
437
|
});
|
|
429
438
|
}
|
|
@@ -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
|
-
//
|
|
901
|
-
const
|
|
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 (
|
|
904
|
-
console.warn(`Very large verb dataset detected (${
|
|
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 (
|
|
911
|
+
if (actualFileCount > 50000) {
|
|
911
912
|
return await this.getVerbsWithPaginationStreaming(options, startIndex, limit);
|
|
912
913
|
}
|
|
913
|
-
//
|
|
914
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
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
|
};
|
|
@@ -240,9 +240,11 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
240
240
|
*/
|
|
241
241
|
async getVerbsBySource(sourceId) {
|
|
242
242
|
await this.ensureInitialized();
|
|
243
|
-
//
|
|
243
|
+
// CRITICAL: Fetch ALL verbs for this source, not just first page
|
|
244
|
+
// This is needed for delete operations to clean up all relationships
|
|
244
245
|
const result = await this.getVerbs({
|
|
245
|
-
filter: { sourceId }
|
|
246
|
+
filter: { sourceId },
|
|
247
|
+
pagination: { limit: Number.MAX_SAFE_INTEGER }
|
|
246
248
|
});
|
|
247
249
|
return result.items;
|
|
248
250
|
}
|
|
@@ -251,9 +253,11 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
251
253
|
*/
|
|
252
254
|
async getVerbsByTarget(targetId) {
|
|
253
255
|
await this.ensureInitialized();
|
|
254
|
-
//
|
|
256
|
+
// CRITICAL: Fetch ALL verbs for this target, not just first page
|
|
257
|
+
// This is needed for delete operations to clean up all relationships
|
|
255
258
|
const result = await this.getVerbs({
|
|
256
|
-
filter: { targetId }
|
|
259
|
+
filter: { targetId },
|
|
260
|
+
pagination: { limit: Number.MAX_SAFE_INTEGER }
|
|
257
261
|
});
|
|
258
262
|
return result.items;
|
|
259
263
|
}
|
|
@@ -262,9 +266,10 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
262
266
|
*/
|
|
263
267
|
async getVerbsByType(type) {
|
|
264
268
|
await this.ensureInitialized();
|
|
265
|
-
//
|
|
269
|
+
// Fetch ALL verbs of this type (no pagination limit)
|
|
266
270
|
const result = await this.getVerbs({
|
|
267
|
-
filter: { verbType: type }
|
|
271
|
+
filter: { verbType: type },
|
|
272
|
+
pagination: { limit: Number.MAX_SAFE_INTEGER }
|
|
268
273
|
});
|
|
269
274
|
return result.items;
|
|
270
275
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soulcraft/brainy",
|
|
3
|
-
"version": "3.20.
|
|
3
|
+
"version": "3.20.5",
|
|
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",
|
|
@@ -81,11 +81,15 @@
|
|
|
81
81
|
"format:check": "prettier --check \"src/**/*.{ts,js}\"",
|
|
82
82
|
"migrate:logger": "tsx scripts/migrate-to-structured-logger.ts",
|
|
83
83
|
"migrate:logger:dry": "tsx scripts/migrate-to-structured-logger.ts --dry-run",
|
|
84
|
-
"release": "
|
|
85
|
-
"release:patch": "
|
|
86
|
-
"release:minor": "
|
|
87
|
-
"release:major": "
|
|
88
|
-
"release:dry": "
|
|
84
|
+
"release": "./scripts/release.sh patch",
|
|
85
|
+
"release:patch": "./scripts/release.sh patch",
|
|
86
|
+
"release:minor": "./scripts/release.sh minor",
|
|
87
|
+
"release:major": "./scripts/release.sh major",
|
|
88
|
+
"release:dry": "./scripts/release.sh patch --dry-run",
|
|
89
|
+
"release:standard-version": "standard-version",
|
|
90
|
+
"release:standard-version:patch": "standard-version --release-as patch",
|
|
91
|
+
"release:standard-version:minor": "standard-version --release-as minor",
|
|
92
|
+
"release:standard-version:major": "standard-version --release-as major"
|
|
89
93
|
},
|
|
90
94
|
"keywords": [
|
|
91
95
|
"ai-database",
|