@soulcraft/brainy 6.2.9 → 6.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/dist/brainy.d.ts +55 -0
- package/dist/brainy.js +104 -7
- package/dist/graph/graphAdjacencyIndex.d.ts +7 -0
- package/dist/graph/graphAdjacencyIndex.js +42 -0
- package/dist/storage/baseStorage.d.ts +7 -0
- package/dist/storage/baseStorage.js +31 -38
- package/dist/versioning/VersionIndex.d.ts +42 -47
- package/dist/versioning/VersionIndex.js +141 -166
- package/dist/versioning/VersionManager.d.ts +12 -6
- package/dist/versioning/VersionManager.js +26 -8
- package/dist/versioning/VersionStorage.d.ts +25 -15
- package/dist/versioning/VersionStorage.js +49 -65
- package/dist/vfs/PathResolver.d.ts +6 -0
- package/dist/vfs/PathResolver.js +20 -0
- package/dist/vfs/semantic/SemanticPathResolver.d.ts +6 -0
- package/dist/vfs/semantic/SemanticPathResolver.js +11 -0
- package/package.json +1 -1
- package/dist/augmentations/KnowledgeAugmentation.d.ts +0 -40
- package/dist/augmentations/KnowledgeAugmentation.js +0 -251
- package/dist/importManager.d.ts +0 -78
- package/dist/importManager.js +0 -267
- package/dist/query/typeInference.d.ts +0 -158
- package/dist/query/typeInference.js +0 -760
- package/dist/storage/adapters/typeAwareStorageAdapter.d.ts +0 -252
- package/dist/storage/adapters/typeAwareStorageAdapter.js +0 -814
- package/dist/types/brainyDataInterface.d.ts +0 -52
- package/dist/types/brainyDataInterface.js +0 -10
- package/dist/vfs/ConceptSystem.d.ts +0 -203
- package/dist/vfs/ConceptSystem.js +0 -545
- package/dist/vfs/EntityManager.d.ts +0 -75
- package/dist/vfs/EntityManager.js +0 -216
- package/dist/vfs/EventRecorder.d.ts +0 -84
- package/dist/vfs/EventRecorder.js +0 -269
- package/dist/vfs/GitBridge.d.ts +0 -167
- package/dist/vfs/GitBridge.js +0 -537
- package/dist/vfs/KnowledgeLayer.d.ts +0 -35
- package/dist/vfs/KnowledgeLayer.js +0 -443
- package/dist/vfs/PersistentEntitySystem.d.ts +0 -165
- package/dist/vfs/PersistentEntitySystem.js +0 -503
- package/dist/vfs/SemanticVersioning.d.ts +0 -105
- package/dist/vfs/SemanticVersioning.js +0 -309
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
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
|
+
### [6.3.1](https://github.com/soulcraftlabs/brainy/compare/v6.3.0...v6.3.1) (2025-12-09)
|
|
6
|
+
|
|
7
|
+
- fix(versioning): clean architecture with index pollution prevention (f145fa1)
|
|
8
|
+
- chore(release): 6.3.0 - singleton GraphAdjacencyIndex architecture fix (292be1b)
|
|
9
|
+
- fix(architecture): singleton GraphAdjacencyIndex via storage.getGraphIndex() (v6.3.0) (c15892e)
|
|
10
|
+
- chore(release): 6.2.9 - fix critical VFS bugs (directory corruption) (810b756)
|
|
11
|
+
- fix(vfs): resolve two critical VFS bugs causing directory listing corruption (2ba69ec)
|
|
12
|
+
- chore(release): 6.2.8 - deferred HNSW persistence for 30-50× faster cloud adds (1da6048)
|
|
13
|
+
- perf(hnsw): deferred persistence mode for 30-50× faster cloud storage adds (4d1d567)
|
|
14
|
+
- chore(release): 6.2.7 - simplify cloud storage to always-on write buffering (a33b759)
|
|
15
|
+
- perf(storage): simplify cloud adapters to always-on write buffering (26510ce)
|
|
16
|
+
- chore(release): 6.2.6 - fix cloud storage read-after-write consistency (6449bb1)
|
|
17
|
+
- fix(storage): populate cache before write buffer for read-after-write consistency (2d27bd0)
|
|
18
|
+
- chore(release): 6.2.5 - fix counts.byType() accumulation bug (e4bbd7f)
|
|
19
|
+
- fix(counts): counts.byType() returns inflated values due to accumulation bug (9456c2c)
|
|
20
|
+
- chore(release): 6.2.4 - fix asOf() COW property name mismatch (ea53c11)
|
|
21
|
+
- fix(cow): asOf() fails with "COW not enabled" due to property name mismatch (b3ae18b)
|
|
22
|
+
- chore(release): 6.2.3 - fix counts.byType({ excludeVFS: true }) returning empty (0ba6da4)
|
|
23
|
+
- fix(counts): counts.byType({ excludeVFS: true }) now returns correct type counts (9b2ff2d)
|
|
24
|
+
|
|
25
|
+
|
|
5
26
|
### [6.2.2](https://github.com/soulcraftlabs/brainy/compare/v6.2.1...v6.2.2) (2025-11-25)
|
|
6
27
|
|
|
7
28
|
- refactor: remove 3,700+ LOC of unused HNSW implementations (e3146ce)
|
package/dist/brainy.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Beautiful, Professional, Planet-Scale, Fun to Use
|
|
5
5
|
* NO STUBS, NO MOCKS, REAL IMPLEMENTATION
|
|
6
6
|
*/
|
|
7
|
+
import { BaseStorage } from './storage/baseStorage.js';
|
|
7
8
|
import { Vector } from './coreTypes.js';
|
|
8
9
|
import { ImprovedNeuralAPI } from './neural/improvedNeuralAPI.js';
|
|
9
10
|
import { NaturalLanguageProcessor } from './neural/naturalLanguageProcessor.js';
|
|
@@ -1629,6 +1630,60 @@ export declare class Brainy<T = any> implements BrainyInterface<T> {
|
|
|
1629
1630
|
};
|
|
1630
1631
|
density: number;
|
|
1631
1632
|
}>;
|
|
1633
|
+
/**
|
|
1634
|
+
* Search entities by metadata filters (internal API)
|
|
1635
|
+
* Used by versioning system for querying version metadata
|
|
1636
|
+
*
|
|
1637
|
+
* @param filters - Metadata filter object (same format as `where` in find())
|
|
1638
|
+
* @returns Array of matching entities
|
|
1639
|
+
* @internal
|
|
1640
|
+
*/
|
|
1641
|
+
searchByMetadata(filters: Record<string, any>): Promise<any[]>;
|
|
1642
|
+
/**
|
|
1643
|
+
* Get raw noun metadata (internal API)
|
|
1644
|
+
* Used by versioning system for reading entity state
|
|
1645
|
+
*
|
|
1646
|
+
* @param id - Entity ID
|
|
1647
|
+
* @returns Noun metadata or null if not found
|
|
1648
|
+
* @internal
|
|
1649
|
+
*/
|
|
1650
|
+
getNounMetadata(id: string): Promise<any | null>;
|
|
1651
|
+
/**
|
|
1652
|
+
* Save raw noun metadata (internal API)
|
|
1653
|
+
* Used by versioning system for storing version index entries
|
|
1654
|
+
*
|
|
1655
|
+
* @param id - Entity ID
|
|
1656
|
+
* @param data - Metadata to save
|
|
1657
|
+
* @internal
|
|
1658
|
+
*/
|
|
1659
|
+
saveNounMetadata(id: string, data: any): Promise<void>;
|
|
1660
|
+
/**
|
|
1661
|
+
* Delete noun metadata (internal API)
|
|
1662
|
+
* Used by versioning system for removing version index entries
|
|
1663
|
+
*
|
|
1664
|
+
* @param id - Entity ID
|
|
1665
|
+
* @internal
|
|
1666
|
+
*/
|
|
1667
|
+
deleteNounMetadata(id: string): Promise<void>;
|
|
1668
|
+
/**
|
|
1669
|
+
* Current branch name (internal API)
|
|
1670
|
+
* Used by versioning system for branch-aware operations
|
|
1671
|
+
* @internal
|
|
1672
|
+
*/
|
|
1673
|
+
get currentBranch(): string;
|
|
1674
|
+
/**
|
|
1675
|
+
* Reference manager for COW commits (internal API)
|
|
1676
|
+
* Used by versioning system for commit operations
|
|
1677
|
+
* @internal
|
|
1678
|
+
*/
|
|
1679
|
+
get refManager(): any;
|
|
1680
|
+
/**
|
|
1681
|
+
* Storage adapter (internal API)
|
|
1682
|
+
* Used by versioning system for direct storage access
|
|
1683
|
+
* Returns BaseStorage which has saveMetadata/getMetadata for key-value storage
|
|
1684
|
+
* @internal
|
|
1685
|
+
*/
|
|
1686
|
+
get storageAdapter(): BaseStorage;
|
|
1632
1687
|
/**
|
|
1633
1688
|
* Parse natural language query using advanced NLP with 220+ patterns
|
|
1634
1689
|
* The embedding model is always available as it's core to Brainy's functionality
|
package/dist/brainy.js
CHANGED
|
@@ -18,7 +18,6 @@ import { TripleIntelligenceSystem } from './triple/TripleIntelligenceSystem.js';
|
|
|
18
18
|
import { VirtualFileSystem } from './vfs/VirtualFileSystem.js';
|
|
19
19
|
import { VersioningAPI } from './versioning/VersioningAPI.js';
|
|
20
20
|
import { MetadataIndexManager } from './utils/metadataIndex.js';
|
|
21
|
-
import { GraphAdjacencyIndex } from './graph/graphAdjacencyIndex.js';
|
|
22
21
|
import { CommitBuilder } from './storage/cow/CommitObject.js';
|
|
23
22
|
import { NULL_HASH } from './storage/cow/constants.js';
|
|
24
23
|
import { createPipeline } from './streaming/pipeline.js';
|
|
@@ -127,8 +126,11 @@ export class Brainy {
|
|
|
127
126
|
// Initialize core metadata index
|
|
128
127
|
this.metadataIndex = new MetadataIndexManager(this.storage);
|
|
129
128
|
await this.metadataIndex.init();
|
|
130
|
-
//
|
|
131
|
-
|
|
129
|
+
// v6.3.0: Get GraphAdjacencyIndex from storage (SINGLETON pattern)
|
|
130
|
+
// Storage owns the single instance, Brainy accesses it via getGraphIndex()
|
|
131
|
+
// This fixes the dual-ownership bug where Brainy and Storage had separate instances
|
|
132
|
+
// causing verbIdSet to be out of sync and VFS tree queries to fail
|
|
133
|
+
this.graphIndex = await this.storage.getGraphIndex();
|
|
132
134
|
// Rebuild indexes if needed for existing data
|
|
133
135
|
await this.rebuildIndexesIfNeeded();
|
|
134
136
|
// Initialize augmentations
|
|
@@ -2198,8 +2200,10 @@ export class Brainy {
|
|
|
2198
2200
|
// Fast rebuild for small indexes from COW storage (Metadata/Graph are fast)
|
|
2199
2201
|
clone.metadataIndex = new MetadataIndexManager(clone.storage);
|
|
2200
2202
|
await clone.metadataIndex.init();
|
|
2201
|
-
clone.graphIndex =
|
|
2202
|
-
|
|
2203
|
+
clone.storage.graphIndex = undefined;
|
|
2204
|
+
clone.storage.graphIndexPromise = undefined;
|
|
2205
|
+
clone.graphIndex = await clone.storage.getGraphIndex();
|
|
2206
|
+
// getGraphIndex() will rebuild automatically if data exists (via _initializeGraphIndex)
|
|
2203
2207
|
// Setup augmentations
|
|
2204
2208
|
clone.augmentationRegistry = this.setupAugmentations();
|
|
2205
2209
|
await clone.augmentationRegistry.initializeAll({
|
|
@@ -2289,14 +2293,21 @@ export class Brainy {
|
|
|
2289
2293
|
this.index = this.setupIndex();
|
|
2290
2294
|
this.metadataIndex = new MetadataIndexManager(this.storage);
|
|
2291
2295
|
await this.metadataIndex.init();
|
|
2292
|
-
this.
|
|
2296
|
+
this.storage.invalidateGraphIndex();
|
|
2297
|
+
this.graphIndex = await this.storage.getGraphIndex();
|
|
2293
2298
|
// v5.7.7: Reset lazy loading state when switching branches
|
|
2294
2299
|
// Indexes contain data from previous branch, must rebuild for new branch
|
|
2295
2300
|
this.lazyRebuildCompleted = false;
|
|
2296
2301
|
// Rebuild indexes from new branch data (force=true to override disableAutoRebuild)
|
|
2297
2302
|
await this.rebuildIndexesIfNeeded(true);
|
|
2298
|
-
//
|
|
2303
|
+
// v6.3.0: Clear VFS caches before recreating VFS for new branch
|
|
2304
|
+
// UnifiedCache is global, so old branch's VFS path cache entries would persist
|
|
2299
2305
|
if (this._vfs) {
|
|
2306
|
+
// Clear old PathResolver's caches including UnifiedCache entries
|
|
2307
|
+
if (this._vfs.pathResolver?.invalidateAllCaches) {
|
|
2308
|
+
this._vfs.pathResolver.invalidateAllCaches();
|
|
2309
|
+
}
|
|
2310
|
+
// Recreate VFS for new branch
|
|
2300
2311
|
this._vfs = new VirtualFileSystem(this);
|
|
2301
2312
|
await this._vfs.init();
|
|
2302
2313
|
}
|
|
@@ -3543,6 +3554,92 @@ export class Brainy {
|
|
|
3543
3554
|
async getStats(options) {
|
|
3544
3555
|
return this.counts.getStats(options);
|
|
3545
3556
|
}
|
|
3557
|
+
// ============= INTERNAL VERSIONING API =============
|
|
3558
|
+
// These methods are used by the versioning system (brain.versions.*)
|
|
3559
|
+
// They expose internal storage and index operations needed for entity versioning
|
|
3560
|
+
/**
|
|
3561
|
+
* Search entities by metadata filters (internal API)
|
|
3562
|
+
* Used by versioning system for querying version metadata
|
|
3563
|
+
*
|
|
3564
|
+
* @param filters - Metadata filter object (same format as `where` in find())
|
|
3565
|
+
* @returns Array of matching entities
|
|
3566
|
+
* @internal
|
|
3567
|
+
*/
|
|
3568
|
+
async searchByMetadata(filters) {
|
|
3569
|
+
await this.ensureInitialized();
|
|
3570
|
+
const ids = await this.metadataIndex.getIdsForFilter(filters);
|
|
3571
|
+
if (ids.length === 0)
|
|
3572
|
+
return [];
|
|
3573
|
+
const results = [];
|
|
3574
|
+
const entitiesMap = await this.batchGet(ids);
|
|
3575
|
+
for (const id of ids) {
|
|
3576
|
+
const entity = entitiesMap.get(id);
|
|
3577
|
+
if (entity) {
|
|
3578
|
+
results.push(entity);
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
return results;
|
|
3582
|
+
}
|
|
3583
|
+
/**
|
|
3584
|
+
* Get raw noun metadata (internal API)
|
|
3585
|
+
* Used by versioning system for reading entity state
|
|
3586
|
+
*
|
|
3587
|
+
* @param id - Entity ID
|
|
3588
|
+
* @returns Noun metadata or null if not found
|
|
3589
|
+
* @internal
|
|
3590
|
+
*/
|
|
3591
|
+
async getNounMetadata(id) {
|
|
3592
|
+
await this.ensureInitialized();
|
|
3593
|
+
return this.storage.getNounMetadata(id);
|
|
3594
|
+
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Save raw noun metadata (internal API)
|
|
3597
|
+
* Used by versioning system for storing version index entries
|
|
3598
|
+
*
|
|
3599
|
+
* @param id - Entity ID
|
|
3600
|
+
* @param data - Metadata to save
|
|
3601
|
+
* @internal
|
|
3602
|
+
*/
|
|
3603
|
+
async saveNounMetadata(id, data) {
|
|
3604
|
+
await this.ensureInitialized();
|
|
3605
|
+
await this.storage.saveNounMetadata(id, data);
|
|
3606
|
+
}
|
|
3607
|
+
/**
|
|
3608
|
+
* Delete noun metadata (internal API)
|
|
3609
|
+
* Used by versioning system for removing version index entries
|
|
3610
|
+
*
|
|
3611
|
+
* @param id - Entity ID
|
|
3612
|
+
* @internal
|
|
3613
|
+
*/
|
|
3614
|
+
async deleteNounMetadata(id) {
|
|
3615
|
+
await this.ensureInitialized();
|
|
3616
|
+
await this.storage.deleteNounMetadata(id);
|
|
3617
|
+
}
|
|
3618
|
+
/**
|
|
3619
|
+
* Current branch name (internal API)
|
|
3620
|
+
* Used by versioning system for branch-aware operations
|
|
3621
|
+
* @internal
|
|
3622
|
+
*/
|
|
3623
|
+
get currentBranch() {
|
|
3624
|
+
return this.storage.currentBranch || 'main';
|
|
3625
|
+
}
|
|
3626
|
+
/**
|
|
3627
|
+
* Reference manager for COW commits (internal API)
|
|
3628
|
+
* Used by versioning system for commit operations
|
|
3629
|
+
* @internal
|
|
3630
|
+
*/
|
|
3631
|
+
get refManager() {
|
|
3632
|
+
return this.storage.refManager;
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Storage adapter (internal API)
|
|
3636
|
+
* Used by versioning system for direct storage access
|
|
3637
|
+
* Returns BaseStorage which has saveMetadata/getMetadata for key-value storage
|
|
3638
|
+
* @internal
|
|
3639
|
+
*/
|
|
3640
|
+
get storageAdapter() {
|
|
3641
|
+
return this.storage;
|
|
3642
|
+
}
|
|
3546
3643
|
// ============= HELPER METHODS =============
|
|
3547
3644
|
/**
|
|
3548
3645
|
* Parse natural language query using advanced NLP with 220+ patterns
|
|
@@ -51,8 +51,15 @@ export declare class GraphAdjacencyIndex {
|
|
|
51
51
|
constructor(storage: StorageAdapter, config?: GraphIndexConfig);
|
|
52
52
|
/**
|
|
53
53
|
* Initialize the graph index (lazy initialization)
|
|
54
|
+
* v6.3.0: Added defensive auto-rebuild check for verbIdSet consistency
|
|
54
55
|
*/
|
|
55
56
|
private ensureInitialized;
|
|
57
|
+
/**
|
|
58
|
+
* Populate verbIdSet from storage without full rebuild (v6.3.0)
|
|
59
|
+
* Lighter weight than full rebuild - only loads verb IDs, not all verb data
|
|
60
|
+
* @private
|
|
61
|
+
*/
|
|
62
|
+
private populateVerbIdSetFromStorage;
|
|
56
63
|
/**
|
|
57
64
|
* Core API - Neighbor lookup with LSM-tree storage
|
|
58
65
|
*
|
|
@@ -72,6 +72,7 @@ export class GraphAdjacencyIndex {
|
|
|
72
72
|
}
|
|
73
73
|
/**
|
|
74
74
|
* Initialize the graph index (lazy initialization)
|
|
75
|
+
* v6.3.0: Added defensive auto-rebuild check for verbIdSet consistency
|
|
75
76
|
*/
|
|
76
77
|
async ensureInitialized() {
|
|
77
78
|
if (this.initialized) {
|
|
@@ -81,10 +82,51 @@ export class GraphAdjacencyIndex {
|
|
|
81
82
|
await this.lsmTreeTarget.init();
|
|
82
83
|
await this.lsmTreeVerbsBySource.init();
|
|
83
84
|
await this.lsmTreeVerbsByTarget.init();
|
|
85
|
+
// v6.3.0: Defensive check - if LSM-trees have data but verbIdSet is empty,
|
|
86
|
+
// the index was created without proper rebuild (shouldn't happen with singleton
|
|
87
|
+
// pattern but protects against edge cases and future refactoring)
|
|
88
|
+
const lsmTreeSize = this.lsmTreeVerbsBySource.size();
|
|
89
|
+
if (lsmTreeSize > 0 && this.verbIdSet.size === 0) {
|
|
90
|
+
prodLog.warn(`GraphAdjacencyIndex: LSM-trees have ${lsmTreeSize} relationships but verbIdSet is empty. ` +
|
|
91
|
+
`Triggering auto-rebuild to restore consistency.`);
|
|
92
|
+
// Note: We don't await rebuild() here to avoid infinite loop
|
|
93
|
+
// (rebuild calls ensureInitialized). Instead, we'll populate verbIdSet
|
|
94
|
+
// by loading all verb IDs from storage.
|
|
95
|
+
await this.populateVerbIdSetFromStorage();
|
|
96
|
+
}
|
|
84
97
|
// Start auto-flush timer after initialization
|
|
85
98
|
this.startAutoFlush();
|
|
86
99
|
this.initialized = true;
|
|
87
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Populate verbIdSet from storage without full rebuild (v6.3.0)
|
|
103
|
+
* Lighter weight than full rebuild - only loads verb IDs, not all verb data
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
async populateVerbIdSetFromStorage() {
|
|
107
|
+
prodLog.info('GraphAdjacencyIndex: Populating verbIdSet from storage...');
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
// Use pagination to load all verb IDs
|
|
110
|
+
let hasMore = true;
|
|
111
|
+
let cursor = undefined;
|
|
112
|
+
let count = 0;
|
|
113
|
+
while (hasMore) {
|
|
114
|
+
const result = await this.storage.getVerbs({
|
|
115
|
+
pagination: { limit: 10000, cursor }
|
|
116
|
+
});
|
|
117
|
+
for (const verb of result.items) {
|
|
118
|
+
this.verbIdSet.add(verb.id);
|
|
119
|
+
// Also update counts
|
|
120
|
+
const verbType = verb.verb || 'unknown';
|
|
121
|
+
this.relationshipCountsByType.set(verbType, (this.relationshipCountsByType.get(verbType) || 0) + 1);
|
|
122
|
+
count++;
|
|
123
|
+
}
|
|
124
|
+
hasMore = result.hasMore;
|
|
125
|
+
cursor = result.nextCursor;
|
|
126
|
+
}
|
|
127
|
+
const elapsed = Date.now() - startTime;
|
|
128
|
+
prodLog.info(`GraphAdjacencyIndex: Populated verbIdSet with ${count} verb IDs in ${elapsed}ms`);
|
|
129
|
+
}
|
|
88
130
|
/**
|
|
89
131
|
* Core API - Neighbor lookup with LSM-tree storage
|
|
90
132
|
*
|
|
@@ -82,6 +82,13 @@ export declare abstract class BaseStorage extends BaseStorageAdapter {
|
|
|
82
82
|
* @public
|
|
83
83
|
*/
|
|
84
84
|
rebuildGraphIndex(): Promise<void>;
|
|
85
|
+
/**
|
|
86
|
+
* Invalidate GraphAdjacencyIndex (v6.3.0)
|
|
87
|
+
* Call this when switching branches or clearing data to force re-creation
|
|
88
|
+
* The next getGraphIndex() call will create a fresh instance and rebuild
|
|
89
|
+
* @public
|
|
90
|
+
*/
|
|
91
|
+
invalidateGraphIndex(): void;
|
|
85
92
|
/**
|
|
86
93
|
* Ensure the storage adapter is initialized
|
|
87
94
|
*/
|
|
@@ -185,18 +185,12 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
185
185
|
try {
|
|
186
186
|
// Load type statistics from storage (if they exist)
|
|
187
187
|
await this.loadTypeStatistics();
|
|
188
|
-
// v6.
|
|
189
|
-
//
|
|
190
|
-
//
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
prodLog.debug(`[BaseStorage] GraphAdjacencyIndex instantiated (lazy-loaded), graphIndex=${!!this.graphIndex}`);
|
|
195
|
-
}
|
|
196
|
-
catch (error) {
|
|
197
|
-
prodLog.error('[BaseStorage] Failed to create GraphAdjacencyIndex:', error);
|
|
198
|
-
throw error;
|
|
199
|
-
}
|
|
188
|
+
// v6.3.0: GraphAdjacencyIndex is now SINGLETON via getGraphIndex()
|
|
189
|
+
// - Removed direct creation here to fix dual-ownership bug
|
|
190
|
+
// - GraphAdjacencyIndex will be created lazily on first getGraphIndex() call
|
|
191
|
+
// - This ensures there's only ONE instance per storage adapter
|
|
192
|
+
// - See: https://github.com/soulcraftlabs/brainy/issues/vfs-corruption
|
|
193
|
+
prodLog.debug('[BaseStorage] init() complete - GraphAdjacencyIndex will be created via getGraphIndex()');
|
|
200
194
|
}
|
|
201
195
|
catch (error) {
|
|
202
196
|
// Reset flag on failure to allow retry
|
|
@@ -210,13 +204,28 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
210
204
|
* @public
|
|
211
205
|
*/
|
|
212
206
|
async rebuildGraphIndex() {
|
|
213
|
-
|
|
214
|
-
throw new Error('GraphAdjacencyIndex not initialized');
|
|
215
|
-
}
|
|
207
|
+
const index = await this.getGraphIndex();
|
|
216
208
|
prodLog.info('[BaseStorage] Rebuilding graph index from existing data...');
|
|
217
|
-
await
|
|
209
|
+
await index.rebuild();
|
|
218
210
|
prodLog.info('[BaseStorage] Graph index rebuild complete');
|
|
219
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Invalidate GraphAdjacencyIndex (v6.3.0)
|
|
214
|
+
* Call this when switching branches or clearing data to force re-creation
|
|
215
|
+
* The next getGraphIndex() call will create a fresh instance and rebuild
|
|
216
|
+
* @public
|
|
217
|
+
*/
|
|
218
|
+
invalidateGraphIndex() {
|
|
219
|
+
if (this.graphIndex) {
|
|
220
|
+
prodLog.info('[BaseStorage] Invalidating GraphAdjacencyIndex for branch switch/clear');
|
|
221
|
+
// Stop any pending operations
|
|
222
|
+
if (typeof this.graphIndex.stopAutoFlush === 'function') {
|
|
223
|
+
this.graphIndex.stopAutoFlush();
|
|
224
|
+
}
|
|
225
|
+
this.graphIndex = undefined;
|
|
226
|
+
this.graphIndexPromise = undefined;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
220
229
|
/**
|
|
221
230
|
* Ensure the storage adapter is initialized
|
|
222
231
|
*/
|
|
@@ -2311,28 +2320,12 @@ export class BaseStorage extends BaseStorageAdapter {
|
|
|
2311
2320
|
this.verbCountsByType[typeIndex]++;
|
|
2312
2321
|
// COW-aware write (v5.0.1): Use COW helper for branch isolation
|
|
2313
2322
|
await this.writeObjectToBranch(path, verb);
|
|
2314
|
-
// v6.
|
|
2315
|
-
//
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
sourceId: verb.sourceId,
|
|
2321
|
-
targetId: verb.targetId,
|
|
2322
|
-
vector: verb.vector,
|
|
2323
|
-
source: verb.sourceId,
|
|
2324
|
-
target: verb.targetId,
|
|
2325
|
-
verb: verb.verb,
|
|
2326
|
-
type: verb.verb,
|
|
2327
|
-
createdAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
|
|
2328
|
-
updatedAt: { seconds: Math.floor(Date.now() / 1000), nanoseconds: 0 },
|
|
2329
|
-
createdBy: { augmentation: 'storage', version: '6.0.0' }
|
|
2330
|
-
});
|
|
2331
|
-
prodLog.debug(`[BaseStorage] GraphAdjacencyIndex updated successfully`);
|
|
2332
|
-
}
|
|
2333
|
-
else {
|
|
2334
|
-
prodLog.warn(`[BaseStorage] graphIndex is null, cannot update index for verb ${verb.id}`);
|
|
2335
|
-
}
|
|
2323
|
+
// v6.3.0: GraphAdjacencyIndex updates are now handled EXCLUSIVELY by Brainy.relate()
|
|
2324
|
+
// via AddToGraphIndexOperation in the transaction system. This provides:
|
|
2325
|
+
// 1. Singleton pattern - only one graphIndex instance exists (via getGraphIndex())
|
|
2326
|
+
// 2. Transaction rollback - if relate() fails, index update is rolled back
|
|
2327
|
+
// 3. No double-counting - prevents duplicate addVerb() calls
|
|
2328
|
+
// REMOVED: Direct graphIndex.addVerb() call that caused dual-ownership bugs
|
|
2336
2329
|
// Periodically save statistics
|
|
2337
2330
|
// v6.2.9: Also save on first verb of each type to ensure low-count types are tracked
|
|
2338
2331
|
// This prevents stale statistics after restart for types with < 100 verbs (common for VFS)
|
|
@@ -1,33 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* VersionIndex -
|
|
2
|
+
* VersionIndex - Pure Key-Value Version Storage (v6.3.0)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* - Uses EntityIdMapper for efficient ID handling
|
|
8
|
-
* - Uses ChunkManager for adaptive chunking
|
|
9
|
-
* - Leverages Roaring Bitmaps for fast set operations
|
|
4
|
+
* Stores version metadata using simple key-value storage:
|
|
5
|
+
* - Version list per entity: __version_meta_{entityId}_{branch}
|
|
6
|
+
* - Content stored separately by VersionStorage
|
|
10
7
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* -
|
|
16
|
-
* - versionBranch: Branch version was created on
|
|
17
|
-
* - versionNumber: Version number
|
|
18
|
-
* - versionTag: Optional user tag
|
|
19
|
-
* - versionTimestamp: Creation timestamp
|
|
20
|
-
* - versionCommitHash: Commit hash
|
|
8
|
+
* Key Design Decisions:
|
|
9
|
+
* - Versions are NOT entities (no brain.add())
|
|
10
|
+
* - Versions do NOT pollute find() results
|
|
11
|
+
* - Simple O(1) lookups per entity
|
|
12
|
+
* - Versions per entity is typically small (10-1000)
|
|
21
13
|
*
|
|
22
14
|
* NO MOCKS - Production implementation
|
|
23
15
|
*/
|
|
24
|
-
import type { EntityVersion } from './VersionManager.js';
|
|
25
|
-
import type { VersionQuery } from './VersionManager.js';
|
|
16
|
+
import type { EntityVersion, VersionQuery } from './VersionManager.js';
|
|
26
17
|
/**
|
|
27
|
-
* VersionIndex -
|
|
18
|
+
* VersionIndex - Pure key-value version metadata storage
|
|
28
19
|
*
|
|
29
|
-
*
|
|
30
|
-
* This
|
|
20
|
+
* Uses simple JSON storage instead of creating entities.
|
|
21
|
+
* This ensures versions never appear in find() results.
|
|
31
22
|
*/
|
|
32
23
|
export declare class VersionIndex {
|
|
33
24
|
private brain;
|
|
@@ -35,25 +26,21 @@ export declare class VersionIndex {
|
|
|
35
26
|
constructor(brain: any);
|
|
36
27
|
/**
|
|
37
28
|
* Initialize version index
|
|
38
|
-
*
|
|
39
|
-
* No special setup needed - we use existing entity storage and indexes!
|
|
40
29
|
*/
|
|
41
30
|
initialize(): Promise<void>;
|
|
42
31
|
/**
|
|
43
32
|
* Add version to index
|
|
44
33
|
*
|
|
45
|
-
* Stores version
|
|
46
|
-
*
|
|
34
|
+
* Stores version entry in key-value storage.
|
|
35
|
+
* Handles deduplication by content hash.
|
|
47
36
|
*
|
|
48
|
-
* @param version Version metadata
|
|
37
|
+
* @param version Version metadata to store
|
|
49
38
|
*/
|
|
50
39
|
addVersion(version: EntityVersion): Promise<void>;
|
|
51
40
|
/**
|
|
52
41
|
* Get versions for an entity
|
|
53
42
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
* @param query Version query
|
|
43
|
+
* @param query Version query with filters
|
|
57
44
|
* @returns List of versions (newest first)
|
|
58
45
|
*/
|
|
59
46
|
getVersions(query: VersionQuery): Promise<EntityVersion[]>;
|
|
@@ -92,35 +79,43 @@ export declare class VersionIndex {
|
|
|
92
79
|
*/
|
|
93
80
|
removeVersion(entityId: string, version: number, branch: string): Promise<void>;
|
|
94
81
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* @param entity Entity from storage
|
|
98
|
-
* @returns EntityVersion or null if invalid
|
|
99
|
-
*/
|
|
100
|
-
private entityToVersion;
|
|
101
|
-
/**
|
|
102
|
-
* Generate unique ID for version entity
|
|
103
|
-
*
|
|
104
|
-
* Format: _version:{entityId}:{version}:{branch}
|
|
82
|
+
* Clear all versions for an entity
|
|
105
83
|
*
|
|
106
84
|
* @param entityId Entity ID
|
|
107
|
-
* @param version Version number
|
|
108
85
|
* @param branch Branch name
|
|
109
|
-
* @returns
|
|
86
|
+
* @returns Number of versions deleted
|
|
110
87
|
*/
|
|
111
|
-
|
|
88
|
+
clearVersions(entityId: string, branch: string): Promise<number>;
|
|
112
89
|
/**
|
|
113
90
|
* Get all versioned entities (for cleanup/debugging)
|
|
114
91
|
*
|
|
115
|
-
*
|
|
92
|
+
* Note: This is an expensive operation that requires scanning.
|
|
93
|
+
* In the simple key-value approach, we don't maintain a global index.
|
|
94
|
+
* This method returns an empty array - use storage-level scanning if needed.
|
|
95
|
+
*
|
|
96
|
+
* @returns Empty array (not supported in simple approach)
|
|
116
97
|
*/
|
|
117
98
|
getVersionedEntities(): Promise<string[]>;
|
|
118
99
|
/**
|
|
119
|
-
*
|
|
100
|
+
* Generate storage key for version metadata
|
|
120
101
|
*
|
|
121
102
|
* @param entityId Entity ID
|
|
122
103
|
* @param branch Branch name
|
|
123
|
-
* @returns
|
|
104
|
+
* @returns Storage key
|
|
124
105
|
*/
|
|
125
|
-
|
|
106
|
+
private getMetaKey;
|
|
107
|
+
/**
|
|
108
|
+
* Load version store from storage
|
|
109
|
+
*
|
|
110
|
+
* @param key Storage key
|
|
111
|
+
* @returns Version store or null
|
|
112
|
+
*/
|
|
113
|
+
private loadStore;
|
|
114
|
+
/**
|
|
115
|
+
* Save version store to storage
|
|
116
|
+
*
|
|
117
|
+
* @param key Storage key
|
|
118
|
+
* @param store Version store
|
|
119
|
+
*/
|
|
120
|
+
private saveStore;
|
|
126
121
|
}
|