@danielsimonjr/memoryjs 1.0.0 → 1.2.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/README.md +385 -113
- package/README.md.backup-1768084780988 +266 -0
- package/dist/index.cjs +24156 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +16967 -0
- package/dist/index.d.ts +16963 -11
- package/dist/index.js +23887 -19
- package/dist/index.js.map +1 -1
- package/dist/workers/levenshteinWorker.cjs +102 -0
- package/dist/workers/levenshteinWorker.cjs.map +1 -0
- package/dist/workers/levenshteinWorker.js +57 -91
- package/dist/workers/levenshteinWorker.js.map +1 -1
- package/package.json +75 -69
- package/dist/core/EntityManager.d.ts +0 -268
- package/dist/core/EntityManager.d.ts.map +0 -1
- package/dist/core/EntityManager.js +0 -512
- package/dist/core/EntityManager.js.map +0 -1
- package/dist/core/GraphEventEmitter.d.ts +0 -202
- package/dist/core/GraphEventEmitter.d.ts.map +0 -1
- package/dist/core/GraphEventEmitter.js +0 -347
- package/dist/core/GraphEventEmitter.js.map +0 -1
- package/dist/core/GraphStorage.d.ts +0 -395
- package/dist/core/GraphStorage.d.ts.map +0 -1
- package/dist/core/GraphStorage.js +0 -786
- package/dist/core/GraphStorage.js.map +0 -1
- package/dist/core/GraphTraversal.d.ts +0 -141
- package/dist/core/GraphTraversal.d.ts.map +0 -1
- package/dist/core/GraphTraversal.js +0 -574
- package/dist/core/GraphTraversal.js.map +0 -1
- package/dist/core/HierarchyManager.d.ts +0 -111
- package/dist/core/HierarchyManager.d.ts.map +0 -1
- package/dist/core/HierarchyManager.js +0 -225
- package/dist/core/HierarchyManager.js.map +0 -1
- package/dist/core/ManagerContext.d.ts +0 -76
- package/dist/core/ManagerContext.d.ts.map +0 -1
- package/dist/core/ManagerContext.js +0 -129
- package/dist/core/ManagerContext.js.map +0 -1
- package/dist/core/ObservationManager.d.ts +0 -85
- package/dist/core/ObservationManager.d.ts.map +0 -1
- package/dist/core/ObservationManager.js +0 -124
- package/dist/core/ObservationManager.js.map +0 -1
- package/dist/core/RelationManager.d.ts +0 -131
- package/dist/core/RelationManager.d.ts.map +0 -1
- package/dist/core/RelationManager.js +0 -212
- package/dist/core/RelationManager.js.map +0 -1
- package/dist/core/SQLiteStorage.d.ts +0 -354
- package/dist/core/SQLiteStorage.d.ts.map +0 -1
- package/dist/core/SQLiteStorage.js +0 -919
- package/dist/core/SQLiteStorage.js.map +0 -1
- package/dist/core/StorageFactory.d.ts +0 -45
- package/dist/core/StorageFactory.d.ts.map +0 -1
- package/dist/core/StorageFactory.js +0 -65
- package/dist/core/StorageFactory.js.map +0 -1
- package/dist/core/TransactionManager.d.ts +0 -464
- package/dist/core/TransactionManager.d.ts.map +0 -1
- package/dist/core/TransactionManager.js +0 -869
- package/dist/core/TransactionManager.js.map +0 -1
- package/dist/core/index.d.ts +0 -17
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -20
- package/dist/core/index.js.map +0 -1
- package/dist/features/AnalyticsManager.d.ts +0 -44
- package/dist/features/AnalyticsManager.d.ts.map +0 -1
- package/dist/features/AnalyticsManager.js +0 -224
- package/dist/features/AnalyticsManager.js.map +0 -1
- package/dist/features/ArchiveManager.d.ts +0 -133
- package/dist/features/ArchiveManager.d.ts.map +0 -1
- package/dist/features/ArchiveManager.js +0 -282
- package/dist/features/ArchiveManager.js.map +0 -1
- package/dist/features/CompressionManager.d.ts +0 -119
- package/dist/features/CompressionManager.d.ts.map +0 -1
- package/dist/features/CompressionManager.js +0 -470
- package/dist/features/CompressionManager.js.map +0 -1
- package/dist/features/IOManager.d.ts +0 -225
- package/dist/features/IOManager.d.ts.map +0 -1
- package/dist/features/IOManager.js +0 -1093
- package/dist/features/IOManager.js.map +0 -1
- package/dist/features/KeywordExtractor.d.ts +0 -61
- package/dist/features/KeywordExtractor.d.ts.map +0 -1
- package/dist/features/KeywordExtractor.js +0 -127
- package/dist/features/KeywordExtractor.js.map +0 -1
- package/dist/features/ObservationNormalizer.d.ts +0 -90
- package/dist/features/ObservationNormalizer.d.ts.map +0 -1
- package/dist/features/ObservationNormalizer.js +0 -194
- package/dist/features/ObservationNormalizer.js.map +0 -1
- package/dist/features/StreamingExporter.d.ts +0 -128
- package/dist/features/StreamingExporter.d.ts.map +0 -1
- package/dist/features/StreamingExporter.js +0 -212
- package/dist/features/StreamingExporter.js.map +0 -1
- package/dist/features/TagManager.d.ts +0 -147
- package/dist/features/TagManager.d.ts.map +0 -1
- package/dist/features/TagManager.js +0 -211
- package/dist/features/TagManager.js.map +0 -1
- package/dist/features/index.d.ts +0 -14
- package/dist/features/index.d.ts.map +0 -1
- package/dist/features/index.js +0 -15
- package/dist/features/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/search/BM25Search.d.ts +0 -148
- package/dist/search/BM25Search.d.ts.map +0 -1
- package/dist/search/BM25Search.js +0 -340
- package/dist/search/BM25Search.js.map +0 -1
- package/dist/search/BasicSearch.d.ts +0 -51
- package/dist/search/BasicSearch.d.ts.map +0 -1
- package/dist/search/BasicSearch.js +0 -138
- package/dist/search/BasicSearch.js.map +0 -1
- package/dist/search/BooleanSearch.d.ts +0 -98
- package/dist/search/BooleanSearch.d.ts.map +0 -1
- package/dist/search/BooleanSearch.js +0 -431
- package/dist/search/BooleanSearch.js.map +0 -1
- package/dist/search/EarlyTerminationManager.d.ts +0 -140
- package/dist/search/EarlyTerminationManager.d.ts.map +0 -1
- package/dist/search/EarlyTerminationManager.js +0 -280
- package/dist/search/EarlyTerminationManager.js.map +0 -1
- package/dist/search/EmbeddingCache.d.ts +0 -175
- package/dist/search/EmbeddingCache.d.ts.map +0 -1
- package/dist/search/EmbeddingCache.js +0 -247
- package/dist/search/EmbeddingCache.js.map +0 -1
- package/dist/search/EmbeddingService.d.ts +0 -277
- package/dist/search/EmbeddingService.d.ts.map +0 -1
- package/dist/search/EmbeddingService.js +0 -531
- package/dist/search/EmbeddingService.js.map +0 -1
- package/dist/search/FuzzySearch.d.ts +0 -118
- package/dist/search/FuzzySearch.d.ts.map +0 -1
- package/dist/search/FuzzySearch.js +0 -313
- package/dist/search/FuzzySearch.js.map +0 -1
- package/dist/search/HybridScorer.d.ts +0 -181
- package/dist/search/HybridScorer.d.ts.map +0 -1
- package/dist/search/HybridScorer.js +0 -258
- package/dist/search/HybridScorer.js.map +0 -1
- package/dist/search/HybridSearchManager.d.ts +0 -80
- package/dist/search/HybridSearchManager.d.ts.map +0 -1
- package/dist/search/HybridSearchManager.js +0 -188
- package/dist/search/HybridSearchManager.js.map +0 -1
- package/dist/search/IncrementalIndexer.d.ts +0 -201
- package/dist/search/IncrementalIndexer.d.ts.map +0 -1
- package/dist/search/IncrementalIndexer.js +0 -343
- package/dist/search/IncrementalIndexer.js.map +0 -1
- package/dist/search/OptimizedInvertedIndex.d.ts +0 -163
- package/dist/search/OptimizedInvertedIndex.d.ts.map +0 -1
- package/dist/search/OptimizedInvertedIndex.js +0 -359
- package/dist/search/OptimizedInvertedIndex.js.map +0 -1
- package/dist/search/ParallelSearchExecutor.d.ts +0 -172
- package/dist/search/ParallelSearchExecutor.d.ts.map +0 -1
- package/dist/search/ParallelSearchExecutor.js +0 -310
- package/dist/search/ParallelSearchExecutor.js.map +0 -1
- package/dist/search/QuantizedVectorStore.d.ts +0 -171
- package/dist/search/QuantizedVectorStore.d.ts.map +0 -1
- package/dist/search/QuantizedVectorStore.js +0 -308
- package/dist/search/QuantizedVectorStore.js.map +0 -1
- package/dist/search/QueryAnalyzer.d.ts +0 -76
- package/dist/search/QueryAnalyzer.d.ts.map +0 -1
- package/dist/search/QueryAnalyzer.js +0 -228
- package/dist/search/QueryAnalyzer.js.map +0 -1
- package/dist/search/QueryCostEstimator.d.ts +0 -244
- package/dist/search/QueryCostEstimator.d.ts.map +0 -1
- package/dist/search/QueryCostEstimator.js +0 -653
- package/dist/search/QueryCostEstimator.js.map +0 -1
- package/dist/search/QueryPlanCache.d.ts +0 -220
- package/dist/search/QueryPlanCache.d.ts.map +0 -1
- package/dist/search/QueryPlanCache.js +0 -380
- package/dist/search/QueryPlanCache.js.map +0 -1
- package/dist/search/QueryPlanner.d.ts +0 -58
- package/dist/search/QueryPlanner.d.ts.map +0 -1
- package/dist/search/QueryPlanner.js +0 -138
- package/dist/search/QueryPlanner.js.map +0 -1
- package/dist/search/RankedSearch.d.ts +0 -71
- package/dist/search/RankedSearch.d.ts.map +0 -1
- package/dist/search/RankedSearch.js +0 -239
- package/dist/search/RankedSearch.js.map +0 -1
- package/dist/search/ReflectionManager.d.ts +0 -120
- package/dist/search/ReflectionManager.d.ts.map +0 -1
- package/dist/search/ReflectionManager.js +0 -232
- package/dist/search/ReflectionManager.js.map +0 -1
- package/dist/search/SavedSearchManager.d.ts +0 -79
- package/dist/search/SavedSearchManager.d.ts.map +0 -1
- package/dist/search/SavedSearchManager.js +0 -147
- package/dist/search/SavedSearchManager.js.map +0 -1
- package/dist/search/SearchFilterChain.d.ts +0 -120
- package/dist/search/SearchFilterChain.d.ts.map +0 -1
- package/dist/search/SearchFilterChain.js +0 -186
- package/dist/search/SearchFilterChain.js.map +0 -1
- package/dist/search/SearchManager.d.ts +0 -326
- package/dist/search/SearchManager.d.ts.map +0 -1
- package/dist/search/SearchManager.js +0 -454
- package/dist/search/SearchManager.js.map +0 -1
- package/dist/search/SearchSuggestions.d.ts +0 -27
- package/dist/search/SearchSuggestions.d.ts.map +0 -1
- package/dist/search/SearchSuggestions.js +0 -58
- package/dist/search/SearchSuggestions.js.map +0 -1
- package/dist/search/SemanticSearch.d.ts +0 -149
- package/dist/search/SemanticSearch.d.ts.map +0 -1
- package/dist/search/SemanticSearch.js +0 -324
- package/dist/search/SemanticSearch.js.map +0 -1
- package/dist/search/SymbolicSearch.d.ts +0 -61
- package/dist/search/SymbolicSearch.d.ts.map +0 -1
- package/dist/search/SymbolicSearch.js +0 -164
- package/dist/search/SymbolicSearch.js.map +0 -1
- package/dist/search/TFIDFEventSync.d.ts +0 -85
- package/dist/search/TFIDFEventSync.d.ts.map +0 -1
- package/dist/search/TFIDFEventSync.js +0 -134
- package/dist/search/TFIDFEventSync.js.map +0 -1
- package/dist/search/TFIDFIndexManager.d.ts +0 -151
- package/dist/search/TFIDFIndexManager.d.ts.map +0 -1
- package/dist/search/TFIDFIndexManager.js +0 -433
- package/dist/search/TFIDFIndexManager.js.map +0 -1
- package/dist/search/VectorStore.d.ts +0 -235
- package/dist/search/VectorStore.d.ts.map +0 -1
- package/dist/search/VectorStore.js +0 -312
- package/dist/search/VectorStore.js.map +0 -1
- package/dist/search/index.d.ts +0 -35
- package/dist/search/index.d.ts.map +0 -1
- package/dist/search/index.js +0 -53
- package/dist/search/index.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -13
- package/dist/types/index.js.map +0 -1
- package/dist/types/types.d.ts +0 -1811
- package/dist/types/types.d.ts.map +0 -1
- package/dist/types/types.js +0 -10
- package/dist/types/types.js.map +0 -1
- package/dist/utils/BatchProcessor.d.ts +0 -271
- package/dist/utils/BatchProcessor.d.ts.map +0 -1
- package/dist/utils/BatchProcessor.js +0 -377
- package/dist/utils/BatchProcessor.js.map +0 -1
- package/dist/utils/MemoryMonitor.d.ts +0 -176
- package/dist/utils/MemoryMonitor.d.ts.map +0 -1
- package/dist/utils/MemoryMonitor.js +0 -306
- package/dist/utils/MemoryMonitor.js.map +0 -1
- package/dist/utils/WorkerPoolManager.d.ts +0 -233
- package/dist/utils/WorkerPoolManager.d.ts.map +0 -1
- package/dist/utils/WorkerPoolManager.js +0 -421
- package/dist/utils/WorkerPoolManager.js.map +0 -1
- package/dist/utils/compressedCache.d.ts +0 -221
- package/dist/utils/compressedCache.d.ts.map +0 -1
- package/dist/utils/compressedCache.js +0 -349
- package/dist/utils/compressedCache.js.map +0 -1
- package/dist/utils/compressionUtil.d.ts +0 -214
- package/dist/utils/compressionUtil.d.ts.map +0 -1
- package/dist/utils/compressionUtil.js +0 -248
- package/dist/utils/compressionUtil.js.map +0 -1
- package/dist/utils/constants.d.ts +0 -245
- package/dist/utils/constants.d.ts.map +0 -1
- package/dist/utils/constants.js +0 -253
- package/dist/utils/constants.js.map +0 -1
- package/dist/utils/entityUtils.d.ts +0 -379
- package/dist/utils/entityUtils.d.ts.map +0 -1
- package/dist/utils/entityUtils.js +0 -649
- package/dist/utils/entityUtils.js.map +0 -1
- package/dist/utils/errors.d.ts +0 -95
- package/dist/utils/errors.d.ts.map +0 -1
- package/dist/utils/errors.js +0 -146
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/formatters.d.ts +0 -145
- package/dist/utils/formatters.d.ts.map +0 -1
- package/dist/utils/formatters.js +0 -133
- package/dist/utils/formatters.js.map +0 -1
- package/dist/utils/index.d.ts +0 -26
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -88
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/indexes.d.ts +0 -270
- package/dist/utils/indexes.d.ts.map +0 -1
- package/dist/utils/indexes.js +0 -527
- package/dist/utils/indexes.js.map +0 -1
- package/dist/utils/logger.d.ts +0 -31
- package/dist/utils/logger.d.ts.map +0 -1
- package/dist/utils/logger.js +0 -41
- package/dist/utils/logger.js.map +0 -1
- package/dist/utils/operationUtils.d.ts +0 -124
- package/dist/utils/operationUtils.d.ts.map +0 -1
- package/dist/utils/operationUtils.js +0 -176
- package/dist/utils/operationUtils.js.map +0 -1
- package/dist/utils/parallelUtils.d.ts +0 -76
- package/dist/utils/parallelUtils.d.ts.map +0 -1
- package/dist/utils/parallelUtils.js +0 -192
- package/dist/utils/parallelUtils.js.map +0 -1
- package/dist/utils/schemas.d.ts +0 -556
- package/dist/utils/schemas.d.ts.map +0 -1
- package/dist/utils/schemas.js +0 -485
- package/dist/utils/schemas.js.map +0 -1
- package/dist/utils/searchAlgorithms.d.ts +0 -99
- package/dist/utils/searchAlgorithms.d.ts.map +0 -1
- package/dist/utils/searchAlgorithms.js +0 -168
- package/dist/utils/searchAlgorithms.js.map +0 -1
- package/dist/utils/searchCache.d.ts +0 -108
- package/dist/utils/searchCache.d.ts.map +0 -1
- package/dist/utils/searchCache.js +0 -210
- package/dist/utils/searchCache.js.map +0 -1
- package/dist/utils/taskScheduler.d.ts +0 -294
- package/dist/utils/taskScheduler.d.ts.map +0 -1
- package/dist/utils/taskScheduler.js +0 -487
- package/dist/utils/taskScheduler.js.map +0 -1
- package/dist/workers/index.d.ts +0 -12
- package/dist/workers/index.d.ts.map +0 -1
- package/dist/workers/index.js +0 -10
- package/dist/workers/index.js.map +0 -1
- package/dist/workers/levenshteinWorker.d.ts +0 -60
- package/dist/workers/levenshteinWorker.d.ts.map +0 -1
|
@@ -1,869 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Transaction Manager
|
|
3
|
-
*
|
|
4
|
-
* Provides atomic transaction support for knowledge graph operations.
|
|
5
|
-
* Ensures data consistency by allowing multiple operations to be
|
|
6
|
-
* grouped together and committed atomically, with automatic rollback on failure.
|
|
7
|
-
*
|
|
8
|
-
* @module core/TransactionManager
|
|
9
|
-
*/
|
|
10
|
-
import { IOManager } from '../features/IOManager.js';
|
|
11
|
-
import { KnowledgeGraphError } from '../utils/errors.js';
|
|
12
|
-
import { checkCancellation, createProgressReporter, createProgress, sanitizeObject } from '../utils/index.js';
|
|
13
|
-
/**
|
|
14
|
-
* Types of operations that can be performed in a transaction.
|
|
15
|
-
*/
|
|
16
|
-
export var OperationType;
|
|
17
|
-
(function (OperationType) {
|
|
18
|
-
OperationType["CREATE_ENTITY"] = "CREATE_ENTITY";
|
|
19
|
-
OperationType["UPDATE_ENTITY"] = "UPDATE_ENTITY";
|
|
20
|
-
OperationType["DELETE_ENTITY"] = "DELETE_ENTITY";
|
|
21
|
-
OperationType["CREATE_RELATION"] = "CREATE_RELATION";
|
|
22
|
-
OperationType["DELETE_RELATION"] = "DELETE_RELATION";
|
|
23
|
-
})(OperationType || (OperationType = {}));
|
|
24
|
-
/**
|
|
25
|
-
* Manages atomic transactions for knowledge graph operations.
|
|
26
|
-
*
|
|
27
|
-
* Provides ACID-like guarantees:
|
|
28
|
-
* - Atomicity: All operations succeed or all fail
|
|
29
|
-
* - Consistency: Graph is always in a valid state
|
|
30
|
-
* - Isolation: Each transaction operates on a snapshot
|
|
31
|
-
* - Durability: Changes are persisted to disk
|
|
32
|
-
*
|
|
33
|
-
* @example
|
|
34
|
-
* ```typescript
|
|
35
|
-
* const storage = new GraphStorage('/data/memory.jsonl');
|
|
36
|
-
* const txManager = new TransactionManager(storage);
|
|
37
|
-
*
|
|
38
|
-
* // Begin transaction
|
|
39
|
-
* txManager.begin();
|
|
40
|
-
*
|
|
41
|
-
* // Stage operations
|
|
42
|
-
* txManager.createEntity({ name: 'Alice', entityType: 'person', observations: [] });
|
|
43
|
-
* txManager.createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' });
|
|
44
|
-
*
|
|
45
|
-
* // Commit atomically (or rollback on error)
|
|
46
|
-
* const result = await txManager.commit();
|
|
47
|
-
* if (result.success) {
|
|
48
|
-
* console.log(`Transaction completed: ${result.operationsExecuted} operations`);
|
|
49
|
-
* }
|
|
50
|
-
* ```
|
|
51
|
-
*/
|
|
52
|
-
export class TransactionManager {
|
|
53
|
-
storage;
|
|
54
|
-
operations = [];
|
|
55
|
-
inTransaction = false;
|
|
56
|
-
ioManager;
|
|
57
|
-
transactionBackup;
|
|
58
|
-
constructor(storage) {
|
|
59
|
-
this.storage = storage;
|
|
60
|
-
this.ioManager = new IOManager(storage);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Begin a new transaction.
|
|
64
|
-
*
|
|
65
|
-
* Creates a backup of the current state for rollback purposes.
|
|
66
|
-
* Only one transaction can be active at a time.
|
|
67
|
-
*
|
|
68
|
-
* @throws {KnowledgeGraphError} If a transaction is already in progress
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```typescript
|
|
72
|
-
* txManager.begin();
|
|
73
|
-
* // ... stage operations ...
|
|
74
|
-
* await txManager.commit();
|
|
75
|
-
* ```
|
|
76
|
-
*/
|
|
77
|
-
begin() {
|
|
78
|
-
if (this.inTransaction) {
|
|
79
|
-
throw new KnowledgeGraphError('Transaction already in progress', 'TRANSACTION_ACTIVE');
|
|
80
|
-
}
|
|
81
|
-
this.operations = [];
|
|
82
|
-
this.inTransaction = true;
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Stage a create entity operation.
|
|
86
|
-
*
|
|
87
|
-
* @param entity - Entity to create (without timestamps)
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```typescript
|
|
91
|
-
* txManager.begin();
|
|
92
|
-
* txManager.createEntity({
|
|
93
|
-
* name: 'Alice',
|
|
94
|
-
* entityType: 'person',
|
|
95
|
-
* observations: ['Software engineer'],
|
|
96
|
-
* importance: 8
|
|
97
|
-
* });
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
createEntity(entity) {
|
|
101
|
-
this.ensureInTransaction();
|
|
102
|
-
this.operations.push({
|
|
103
|
-
type: OperationType.CREATE_ENTITY,
|
|
104
|
-
data: entity,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Stage an update entity operation.
|
|
109
|
-
*
|
|
110
|
-
* @param name - Name of entity to update
|
|
111
|
-
* @param updates - Partial entity updates
|
|
112
|
-
*
|
|
113
|
-
* @example
|
|
114
|
-
* ```typescript
|
|
115
|
-
* txManager.begin();
|
|
116
|
-
* txManager.updateEntity('Alice', {
|
|
117
|
-
* importance: 9,
|
|
118
|
-
* observations: ['Lead software engineer']
|
|
119
|
-
* });
|
|
120
|
-
* ```
|
|
121
|
-
*/
|
|
122
|
-
updateEntity(name, updates) {
|
|
123
|
-
this.ensureInTransaction();
|
|
124
|
-
this.operations.push({
|
|
125
|
-
type: OperationType.UPDATE_ENTITY,
|
|
126
|
-
data: { name, updates },
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Stage a delete entity operation.
|
|
131
|
-
*
|
|
132
|
-
* @param name - Name of entity to delete
|
|
133
|
-
*
|
|
134
|
-
* @example
|
|
135
|
-
* ```typescript
|
|
136
|
-
* txManager.begin();
|
|
137
|
-
* txManager.deleteEntity('OldEntity');
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
deleteEntity(name) {
|
|
141
|
-
this.ensureInTransaction();
|
|
142
|
-
this.operations.push({
|
|
143
|
-
type: OperationType.DELETE_ENTITY,
|
|
144
|
-
data: { name },
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* Stage a create relation operation.
|
|
149
|
-
*
|
|
150
|
-
* @param relation - Relation to create (without timestamps)
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* ```typescript
|
|
154
|
-
* txManager.begin();
|
|
155
|
-
* txManager.createRelation({
|
|
156
|
-
* from: 'Alice',
|
|
157
|
-
* to: 'Bob',
|
|
158
|
-
* relationType: 'mentors'
|
|
159
|
-
* });
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
createRelation(relation) {
|
|
163
|
-
this.ensureInTransaction();
|
|
164
|
-
this.operations.push({
|
|
165
|
-
type: OperationType.CREATE_RELATION,
|
|
166
|
-
data: relation,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
/**
|
|
170
|
-
* Stage a delete relation operation.
|
|
171
|
-
*
|
|
172
|
-
* @param from - Source entity name
|
|
173
|
-
* @param to - Target entity name
|
|
174
|
-
* @param relationType - Type of relation
|
|
175
|
-
*
|
|
176
|
-
* @example
|
|
177
|
-
* ```typescript
|
|
178
|
-
* txManager.begin();
|
|
179
|
-
* txManager.deleteRelation('Alice', 'Bob', 'mentors');
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
deleteRelation(from, to, relationType) {
|
|
183
|
-
this.ensureInTransaction();
|
|
184
|
-
this.operations.push({
|
|
185
|
-
type: OperationType.DELETE_RELATION,
|
|
186
|
-
data: { from, to, relationType },
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* Commit the transaction, applying all staged operations atomically.
|
|
191
|
-
*
|
|
192
|
-
* Creates a backup before applying changes. If any operation fails,
|
|
193
|
-
* automatically rolls back to the pre-transaction state.
|
|
194
|
-
*
|
|
195
|
-
* Phase 9B: Supports progress tracking and cancellation via LongRunningOperationOptions.
|
|
196
|
-
*
|
|
197
|
-
* @param options - Optional progress/cancellation options (Phase 9B)
|
|
198
|
-
* @returns Promise resolving to transaction result
|
|
199
|
-
* @throws {OperationCancelledError} If operation is cancelled via signal (Phase 9B)
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* ```typescript
|
|
203
|
-
* txManager.begin();
|
|
204
|
-
* txManager.createEntity({ name: 'Alice', entityType: 'person', observations: [] });
|
|
205
|
-
* txManager.createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' });
|
|
206
|
-
*
|
|
207
|
-
* const result = await txManager.commit();
|
|
208
|
-
* if (result.success) {
|
|
209
|
-
* console.log(`Committed ${result.operationsExecuted} operations`);
|
|
210
|
-
* } else {
|
|
211
|
-
* console.error(`Transaction failed: ${result.error}`);
|
|
212
|
-
* }
|
|
213
|
-
*
|
|
214
|
-
* // With progress tracking (Phase 9B)
|
|
215
|
-
* const result = await txManager.commit({
|
|
216
|
-
* onProgress: (p) => console.log(`${p.percentage}% complete`),
|
|
217
|
-
* });
|
|
218
|
-
* ```
|
|
219
|
-
*/
|
|
220
|
-
async commit(options) {
|
|
221
|
-
this.ensureInTransaction();
|
|
222
|
-
// Setup progress reporter
|
|
223
|
-
const reportProgress = createProgressReporter(options?.onProgress);
|
|
224
|
-
const totalOperations = this.operations.length;
|
|
225
|
-
reportProgress?.(createProgress(0, 100, 'commit'));
|
|
226
|
-
try {
|
|
227
|
-
// Check for early cancellation (inside try block to handle gracefully)
|
|
228
|
-
checkCancellation(options?.signal, 'commit');
|
|
229
|
-
// Phase 1: Create backup for rollback (0-20% progress)
|
|
230
|
-
reportProgress?.(createProgress(5, 100, 'creating backup'));
|
|
231
|
-
const backupResult = await this.ioManager.createBackup({
|
|
232
|
-
description: 'Transaction backup (auto-created)',
|
|
233
|
-
});
|
|
234
|
-
this.transactionBackup = backupResult.path;
|
|
235
|
-
// Check for cancellation after backup
|
|
236
|
-
checkCancellation(options?.signal, 'commit');
|
|
237
|
-
reportProgress?.(createProgress(20, 100, 'backup created'));
|
|
238
|
-
// Phase 2: Load graph (20-30% progress)
|
|
239
|
-
reportProgress?.(createProgress(25, 100, 'loading graph'));
|
|
240
|
-
const graph = await this.storage.getGraphForMutation();
|
|
241
|
-
const timestamp = new Date().toISOString();
|
|
242
|
-
reportProgress?.(createProgress(30, 100, 'graph loaded'));
|
|
243
|
-
// Phase 3: Apply all operations (30-80% progress)
|
|
244
|
-
let operationsExecuted = 0;
|
|
245
|
-
for (const operation of this.operations) {
|
|
246
|
-
// Check for cancellation between operations
|
|
247
|
-
checkCancellation(options?.signal, 'commit');
|
|
248
|
-
this.applyOperation(graph, operation, timestamp);
|
|
249
|
-
operationsExecuted++;
|
|
250
|
-
// Map operation progress (0-100%) to overall progress (30-80%)
|
|
251
|
-
const opProgress = totalOperations > 0 ? Math.round(30 + (operationsExecuted / totalOperations) * 50) : 80;
|
|
252
|
-
reportProgress?.(createProgress(opProgress, 100, 'applying operations'));
|
|
253
|
-
}
|
|
254
|
-
// Check for cancellation before save
|
|
255
|
-
checkCancellation(options?.signal, 'commit');
|
|
256
|
-
// Phase 4: Save the modified graph (80-95% progress)
|
|
257
|
-
reportProgress?.(createProgress(85, 100, 'saving graph'));
|
|
258
|
-
await this.storage.saveGraph(graph);
|
|
259
|
-
reportProgress?.(createProgress(95, 100, 'graph saved'));
|
|
260
|
-
// Clean up transaction state
|
|
261
|
-
this.inTransaction = false;
|
|
262
|
-
this.operations = [];
|
|
263
|
-
// Delete the transaction backup (no longer needed)
|
|
264
|
-
if (this.transactionBackup) {
|
|
265
|
-
await this.ioManager.deleteBackup(this.transactionBackup);
|
|
266
|
-
this.transactionBackup = undefined;
|
|
267
|
-
}
|
|
268
|
-
// Report completion
|
|
269
|
-
reportProgress?.(createProgress(100, 100, 'commit'));
|
|
270
|
-
return {
|
|
271
|
-
success: true,
|
|
272
|
-
operationsExecuted,
|
|
273
|
-
};
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
// Rollback on error
|
|
277
|
-
const rollbackResult = await this.rollback();
|
|
278
|
-
return {
|
|
279
|
-
success: false,
|
|
280
|
-
operationsExecuted: 0,
|
|
281
|
-
error: error instanceof Error ? error.message : String(error),
|
|
282
|
-
rollbackBackup: rollbackResult.backupUsed,
|
|
283
|
-
};
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Rollback the current transaction.
|
|
288
|
-
*
|
|
289
|
-
* Restores the graph to the pre-transaction state using the backup.
|
|
290
|
-
* Automatically called by commit() on failure.
|
|
291
|
-
*
|
|
292
|
-
* @returns Promise resolving to rollback result
|
|
293
|
-
*
|
|
294
|
-
* @example
|
|
295
|
-
* ```typescript
|
|
296
|
-
* txManager.begin();
|
|
297
|
-
* txManager.createEntity({ name: 'Test', entityType: 'temp', observations: [] });
|
|
298
|
-
*
|
|
299
|
-
* // Explicit rollback (e.g., user cancellation)
|
|
300
|
-
* const result = await txManager.rollback();
|
|
301
|
-
* console.log(`Rolled back, restored from: ${result.backupUsed}`);
|
|
302
|
-
* ```
|
|
303
|
-
*/
|
|
304
|
-
async rollback() {
|
|
305
|
-
if (!this.transactionBackup) {
|
|
306
|
-
this.inTransaction = false;
|
|
307
|
-
this.operations = [];
|
|
308
|
-
return { success: false };
|
|
309
|
-
}
|
|
310
|
-
try {
|
|
311
|
-
// Restore from backup
|
|
312
|
-
await this.ioManager.restoreFromBackup(this.transactionBackup);
|
|
313
|
-
// Clean up
|
|
314
|
-
const backupUsed = this.transactionBackup;
|
|
315
|
-
await this.ioManager.deleteBackup(this.transactionBackup);
|
|
316
|
-
this.inTransaction = false;
|
|
317
|
-
this.operations = [];
|
|
318
|
-
this.transactionBackup = undefined;
|
|
319
|
-
return { success: true, backupUsed };
|
|
320
|
-
}
|
|
321
|
-
catch (error) {
|
|
322
|
-
// Rollback failed - keep backup for manual recovery
|
|
323
|
-
this.inTransaction = false;
|
|
324
|
-
this.operations = [];
|
|
325
|
-
return { success: false, backupUsed: this.transactionBackup };
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
/**
|
|
329
|
-
* Check if a transaction is currently in progress.
|
|
330
|
-
*
|
|
331
|
-
* @returns True if transaction is active
|
|
332
|
-
*/
|
|
333
|
-
isInTransaction() {
|
|
334
|
-
return this.inTransaction;
|
|
335
|
-
}
|
|
336
|
-
/**
|
|
337
|
-
* Get the number of staged operations in the current transaction.
|
|
338
|
-
*
|
|
339
|
-
* @returns Number of operations staged
|
|
340
|
-
*/
|
|
341
|
-
getOperationCount() {
|
|
342
|
-
return this.operations.length;
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Ensure a transaction is in progress, or throw an error.
|
|
346
|
-
*
|
|
347
|
-
* @private
|
|
348
|
-
*/
|
|
349
|
-
ensureInTransaction() {
|
|
350
|
-
if (!this.inTransaction) {
|
|
351
|
-
throw new KnowledgeGraphError('No transaction in progress. Call begin() first.', 'NO_TRANSACTION');
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
/**
|
|
355
|
-
* Apply a single operation to the graph.
|
|
356
|
-
*
|
|
357
|
-
* @private
|
|
358
|
-
*/
|
|
359
|
-
applyOperation(graph, operation, timestamp) {
|
|
360
|
-
switch (operation.type) {
|
|
361
|
-
case OperationType.CREATE_ENTITY: {
|
|
362
|
-
const entity = {
|
|
363
|
-
...operation.data,
|
|
364
|
-
createdAt: timestamp,
|
|
365
|
-
lastModified: timestamp,
|
|
366
|
-
};
|
|
367
|
-
// Check for duplicates
|
|
368
|
-
if (graph.entities.some(e => e.name === entity.name)) {
|
|
369
|
-
throw new KnowledgeGraphError(`Entity "${entity.name}" already exists`, 'DUPLICATE_ENTITY');
|
|
370
|
-
}
|
|
371
|
-
graph.entities.push(entity);
|
|
372
|
-
break;
|
|
373
|
-
}
|
|
374
|
-
case OperationType.UPDATE_ENTITY: {
|
|
375
|
-
const { name, updates } = operation.data;
|
|
376
|
-
const entity = graph.entities.find(e => e.name === name);
|
|
377
|
-
if (!entity) {
|
|
378
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
379
|
-
}
|
|
380
|
-
// Sanitize updates to prevent prototype pollution
|
|
381
|
-
Object.assign(entity, sanitizeObject(updates));
|
|
382
|
-
entity.lastModified = timestamp;
|
|
383
|
-
break;
|
|
384
|
-
}
|
|
385
|
-
case OperationType.DELETE_ENTITY: {
|
|
386
|
-
const { name } = operation.data;
|
|
387
|
-
const index = graph.entities.findIndex(e => e.name === name);
|
|
388
|
-
if (index === -1) {
|
|
389
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
390
|
-
}
|
|
391
|
-
graph.entities.splice(index, 1);
|
|
392
|
-
// Delete related relations
|
|
393
|
-
graph.relations = graph.relations.filter(r => r.from !== name && r.to !== name);
|
|
394
|
-
break;
|
|
395
|
-
}
|
|
396
|
-
case OperationType.CREATE_RELATION: {
|
|
397
|
-
const relation = {
|
|
398
|
-
...operation.data,
|
|
399
|
-
createdAt: timestamp,
|
|
400
|
-
lastModified: timestamp,
|
|
401
|
-
};
|
|
402
|
-
// Check for duplicates
|
|
403
|
-
const exists = graph.relations.some(r => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType);
|
|
404
|
-
if (exists) {
|
|
405
|
-
throw new KnowledgeGraphError(`Relation "${relation.from}" -> "${relation.to}" (${relation.relationType}) already exists`, 'DUPLICATE_RELATION');
|
|
406
|
-
}
|
|
407
|
-
graph.relations.push(relation);
|
|
408
|
-
break;
|
|
409
|
-
}
|
|
410
|
-
case OperationType.DELETE_RELATION: {
|
|
411
|
-
const { from, to, relationType } = operation.data;
|
|
412
|
-
const index = graph.relations.findIndex(r => r.from === from && r.to === to && r.relationType === relationType);
|
|
413
|
-
if (index === -1) {
|
|
414
|
-
throw new KnowledgeGraphError(`Relation "${from}" -> "${to}" (${relationType}) not found`, 'RELATION_NOT_FOUND');
|
|
415
|
-
}
|
|
416
|
-
graph.relations.splice(index, 1);
|
|
417
|
-
break;
|
|
418
|
-
}
|
|
419
|
-
default: {
|
|
420
|
-
// Exhaustiveness check - TypeScript will error if we miss a case
|
|
421
|
-
const _exhaustiveCheck = operation;
|
|
422
|
-
throw new KnowledgeGraphError(`Unknown operation type: ${_exhaustiveCheck.type}`, 'UNKNOWN_OPERATION');
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// ==================== Phase 10 Sprint 1: BatchTransaction ====================
|
|
428
|
-
/**
|
|
429
|
-
* Phase 10 Sprint 1: Fluent API for building and executing batch transactions.
|
|
430
|
-
*
|
|
431
|
-
* BatchTransaction provides a builder pattern for accumulating multiple
|
|
432
|
-
* graph operations and executing them atomically in a single transaction.
|
|
433
|
-
* This reduces I/O overhead and ensures consistency across related changes.
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* ```typescript
|
|
437
|
-
* const storage = new GraphStorage('/data/memory.jsonl');
|
|
438
|
-
* const batch = new BatchTransaction(storage);
|
|
439
|
-
*
|
|
440
|
-
* // Build the batch with fluent API
|
|
441
|
-
* const result = await batch
|
|
442
|
-
* .createEntity({ name: 'Alice', entityType: 'person', observations: ['Developer'] })
|
|
443
|
-
* .createEntity({ name: 'Bob', entityType: 'person', observations: ['Designer'] })
|
|
444
|
-
* .createRelation({ from: 'Alice', to: 'Bob', relationType: 'knows' })
|
|
445
|
-
* .updateEntity('Alice', { importance: 8 })
|
|
446
|
-
* .execute();
|
|
447
|
-
*
|
|
448
|
-
* if (result.success) {
|
|
449
|
-
* console.log(`Batch completed: ${result.operationsExecuted} operations in ${result.executionTimeMs}ms`);
|
|
450
|
-
* }
|
|
451
|
-
* ```
|
|
452
|
-
*/
|
|
453
|
-
export class BatchTransaction {
|
|
454
|
-
operations = [];
|
|
455
|
-
storage;
|
|
456
|
-
/**
|
|
457
|
-
* Create a new BatchTransaction instance.
|
|
458
|
-
*
|
|
459
|
-
* @param storage - GraphStorage instance to execute operations against
|
|
460
|
-
*/
|
|
461
|
-
constructor(storage) {
|
|
462
|
-
this.storage = storage;
|
|
463
|
-
}
|
|
464
|
-
/**
|
|
465
|
-
* Add a create entity operation to the batch.
|
|
466
|
-
*
|
|
467
|
-
* @param entity - Entity to create (without timestamps)
|
|
468
|
-
* @returns This BatchTransaction for chaining
|
|
469
|
-
*
|
|
470
|
-
* @example
|
|
471
|
-
* ```typescript
|
|
472
|
-
* batch.createEntity({
|
|
473
|
-
* name: 'Alice',
|
|
474
|
-
* entityType: 'person',
|
|
475
|
-
* observations: ['Software engineer'],
|
|
476
|
-
* importance: 8
|
|
477
|
-
* });
|
|
478
|
-
* ```
|
|
479
|
-
*/
|
|
480
|
-
createEntity(entity) {
|
|
481
|
-
this.operations.push({ type: 'createEntity', data: entity });
|
|
482
|
-
return this;
|
|
483
|
-
}
|
|
484
|
-
/**
|
|
485
|
-
* Add an update entity operation to the batch.
|
|
486
|
-
*
|
|
487
|
-
* @param name - Name of entity to update
|
|
488
|
-
* @param updates - Partial entity updates
|
|
489
|
-
* @returns This BatchTransaction for chaining
|
|
490
|
-
*
|
|
491
|
-
* @example
|
|
492
|
-
* ```typescript
|
|
493
|
-
* batch.updateEntity('Alice', { importance: 9 });
|
|
494
|
-
* ```
|
|
495
|
-
*/
|
|
496
|
-
updateEntity(name, updates) {
|
|
497
|
-
this.operations.push({ type: 'updateEntity', data: { name, updates } });
|
|
498
|
-
return this;
|
|
499
|
-
}
|
|
500
|
-
/**
|
|
501
|
-
* Add a delete entity operation to the batch.
|
|
502
|
-
*
|
|
503
|
-
* @param name - Name of entity to delete
|
|
504
|
-
* @returns This BatchTransaction for chaining
|
|
505
|
-
*
|
|
506
|
-
* @example
|
|
507
|
-
* ```typescript
|
|
508
|
-
* batch.deleteEntity('OldEntity');
|
|
509
|
-
* ```
|
|
510
|
-
*/
|
|
511
|
-
deleteEntity(name) {
|
|
512
|
-
this.operations.push({ type: 'deleteEntity', data: { name } });
|
|
513
|
-
return this;
|
|
514
|
-
}
|
|
515
|
-
/**
|
|
516
|
-
* Add a create relation operation to the batch.
|
|
517
|
-
*
|
|
518
|
-
* @param relation - Relation to create (without timestamps)
|
|
519
|
-
* @returns This BatchTransaction for chaining
|
|
520
|
-
*
|
|
521
|
-
* @example
|
|
522
|
-
* ```typescript
|
|
523
|
-
* batch.createRelation({
|
|
524
|
-
* from: 'Alice',
|
|
525
|
-
* to: 'Bob',
|
|
526
|
-
* relationType: 'mentors'
|
|
527
|
-
* });
|
|
528
|
-
* ```
|
|
529
|
-
*/
|
|
530
|
-
createRelation(relation) {
|
|
531
|
-
this.operations.push({ type: 'createRelation', data: relation });
|
|
532
|
-
return this;
|
|
533
|
-
}
|
|
534
|
-
/**
|
|
535
|
-
* Add a delete relation operation to the batch.
|
|
536
|
-
*
|
|
537
|
-
* @param from - Source entity name
|
|
538
|
-
* @param to - Target entity name
|
|
539
|
-
* @param relationType - Type of relation
|
|
540
|
-
* @returns This BatchTransaction for chaining
|
|
541
|
-
*
|
|
542
|
-
* @example
|
|
543
|
-
* ```typescript
|
|
544
|
-
* batch.deleteRelation('Alice', 'Bob', 'mentors');
|
|
545
|
-
* ```
|
|
546
|
-
*/
|
|
547
|
-
deleteRelation(from, to, relationType) {
|
|
548
|
-
this.operations.push({ type: 'deleteRelation', data: { from, to, relationType } });
|
|
549
|
-
return this;
|
|
550
|
-
}
|
|
551
|
-
/**
|
|
552
|
-
* Add observations to an existing entity.
|
|
553
|
-
*
|
|
554
|
-
* @param name - Name of entity to add observations to
|
|
555
|
-
* @param observations - Observations to add
|
|
556
|
-
* @returns This BatchTransaction for chaining
|
|
557
|
-
*
|
|
558
|
-
* @example
|
|
559
|
-
* ```typescript
|
|
560
|
-
* batch.addObservations('Alice', ['Knows TypeScript', 'Leads team']);
|
|
561
|
-
* ```
|
|
562
|
-
*/
|
|
563
|
-
addObservations(name, observations) {
|
|
564
|
-
this.operations.push({ type: 'addObservations', data: { name, observations } });
|
|
565
|
-
return this;
|
|
566
|
-
}
|
|
567
|
-
/**
|
|
568
|
-
* Delete observations from an existing entity.
|
|
569
|
-
*
|
|
570
|
-
* @param name - Name of entity to delete observations from
|
|
571
|
-
* @param observations - Observations to delete
|
|
572
|
-
* @returns This BatchTransaction for chaining
|
|
573
|
-
*
|
|
574
|
-
* @example
|
|
575
|
-
* ```typescript
|
|
576
|
-
* batch.deleteObservations('Alice', ['Old fact']);
|
|
577
|
-
* ```
|
|
578
|
-
*/
|
|
579
|
-
deleteObservations(name, observations) {
|
|
580
|
-
this.operations.push({ type: 'deleteObservations', data: { name, observations } });
|
|
581
|
-
return this;
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Add multiple operations from an array.
|
|
585
|
-
*
|
|
586
|
-
* @param operations - Array of batch operations
|
|
587
|
-
* @returns This BatchTransaction for chaining
|
|
588
|
-
*
|
|
589
|
-
* @example
|
|
590
|
-
* ```typescript
|
|
591
|
-
* batch.addOperations([
|
|
592
|
-
* { type: 'createEntity', data: { name: 'A', entityType: 'x', observations: [] } },
|
|
593
|
-
* { type: 'createEntity', data: { name: 'B', entityType: 'x', observations: [] } }
|
|
594
|
-
* ]);
|
|
595
|
-
* ```
|
|
596
|
-
*/
|
|
597
|
-
addOperations(operations) {
|
|
598
|
-
this.operations.push(...operations);
|
|
599
|
-
return this;
|
|
600
|
-
}
|
|
601
|
-
/**
|
|
602
|
-
* Get the number of operations in this batch.
|
|
603
|
-
*
|
|
604
|
-
* @returns Number of operations queued
|
|
605
|
-
*/
|
|
606
|
-
size() {
|
|
607
|
-
return this.operations.length;
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Clear all operations from the batch.
|
|
611
|
-
*
|
|
612
|
-
* @returns This BatchTransaction for chaining
|
|
613
|
-
*/
|
|
614
|
-
clear() {
|
|
615
|
-
this.operations = [];
|
|
616
|
-
return this;
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Get a copy of the operations in this batch.
|
|
620
|
-
*
|
|
621
|
-
* @returns Array of batch operations
|
|
622
|
-
*/
|
|
623
|
-
getOperations() {
|
|
624
|
-
return [...this.operations];
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Execute all operations in the batch atomically.
|
|
628
|
-
*
|
|
629
|
-
* All operations are applied within a single transaction. If any
|
|
630
|
-
* operation fails, all changes are rolled back (when stopOnError is true).
|
|
631
|
-
*
|
|
632
|
-
* @param options - Batch execution options
|
|
633
|
-
* @returns Promise resolving to batch result
|
|
634
|
-
*
|
|
635
|
-
* @example
|
|
636
|
-
* ```typescript
|
|
637
|
-
* const result = await batch.execute();
|
|
638
|
-
* if (result.success) {
|
|
639
|
-
* console.log(`Created ${result.entitiesCreated} entities`);
|
|
640
|
-
* } else {
|
|
641
|
-
* console.error(`Failed at operation ${result.failedOperationIndex}: ${result.error}`);
|
|
642
|
-
* }
|
|
643
|
-
* ```
|
|
644
|
-
*/
|
|
645
|
-
async execute(options = {}) {
|
|
646
|
-
const startTime = Date.now();
|
|
647
|
-
const { stopOnError = true, validateBeforeExecute = true } = options;
|
|
648
|
-
const result = {
|
|
649
|
-
success: true,
|
|
650
|
-
operationsExecuted: 0,
|
|
651
|
-
entitiesCreated: 0,
|
|
652
|
-
entitiesUpdated: 0,
|
|
653
|
-
entitiesDeleted: 0,
|
|
654
|
-
relationsCreated: 0,
|
|
655
|
-
relationsDeleted: 0,
|
|
656
|
-
executionTimeMs: 0,
|
|
657
|
-
};
|
|
658
|
-
if (this.operations.length === 0) {
|
|
659
|
-
result.executionTimeMs = Date.now() - startTime;
|
|
660
|
-
return result;
|
|
661
|
-
}
|
|
662
|
-
// Load graph for mutation
|
|
663
|
-
const graph = await this.storage.getGraphForMutation();
|
|
664
|
-
const timestamp = new Date().toISOString();
|
|
665
|
-
// Optional: Validate all operations before executing
|
|
666
|
-
if (validateBeforeExecute) {
|
|
667
|
-
const validationError = this.validateOperations(graph);
|
|
668
|
-
if (validationError) {
|
|
669
|
-
return {
|
|
670
|
-
...result,
|
|
671
|
-
success: false,
|
|
672
|
-
error: validationError.message,
|
|
673
|
-
failedOperationIndex: validationError.index,
|
|
674
|
-
executionTimeMs: Date.now() - startTime,
|
|
675
|
-
};
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
// Execute operations
|
|
679
|
-
for (let i = 0; i < this.operations.length; i++) {
|
|
680
|
-
const operation = this.operations[i];
|
|
681
|
-
try {
|
|
682
|
-
this.applyBatchOperation(graph, operation, timestamp, result);
|
|
683
|
-
result.operationsExecuted++;
|
|
684
|
-
}
|
|
685
|
-
catch (error) {
|
|
686
|
-
result.success = false;
|
|
687
|
-
result.error = error instanceof Error ? error.message : String(error);
|
|
688
|
-
result.failedOperationIndex = i;
|
|
689
|
-
if (stopOnError) {
|
|
690
|
-
result.executionTimeMs = Date.now() - startTime;
|
|
691
|
-
return result;
|
|
692
|
-
}
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
// Save the modified graph if successful (or if stopOnError is false)
|
|
696
|
-
if (result.success || !stopOnError) {
|
|
697
|
-
try {
|
|
698
|
-
await this.storage.saveGraph(graph);
|
|
699
|
-
}
|
|
700
|
-
catch (error) {
|
|
701
|
-
result.success = false;
|
|
702
|
-
result.error = `Failed to save graph: ${error instanceof Error ? error.message : String(error)}`;
|
|
703
|
-
}
|
|
704
|
-
}
|
|
705
|
-
result.executionTimeMs = Date.now() - startTime;
|
|
706
|
-
return result;
|
|
707
|
-
}
|
|
708
|
-
/**
|
|
709
|
-
* Validate all operations before executing.
|
|
710
|
-
* @private
|
|
711
|
-
*/
|
|
712
|
-
validateOperations(graph) {
|
|
713
|
-
const entityNames = new Set(graph.entities.map(e => e.name));
|
|
714
|
-
const pendingCreates = new Set();
|
|
715
|
-
const pendingDeletes = new Set();
|
|
716
|
-
for (let i = 0; i < this.operations.length; i++) {
|
|
717
|
-
const op = this.operations[i];
|
|
718
|
-
switch (op.type) {
|
|
719
|
-
case 'createEntity': {
|
|
720
|
-
const name = op.data.name;
|
|
721
|
-
if (entityNames.has(name) && !pendingDeletes.has(name)) {
|
|
722
|
-
return { message: `Entity "${name}" already exists`, index: i };
|
|
723
|
-
}
|
|
724
|
-
if (pendingCreates.has(name)) {
|
|
725
|
-
return { message: `Duplicate create for entity "${name}" in batch`, index: i };
|
|
726
|
-
}
|
|
727
|
-
pendingCreates.add(name);
|
|
728
|
-
break;
|
|
729
|
-
}
|
|
730
|
-
case 'updateEntity':
|
|
731
|
-
case 'addObservations':
|
|
732
|
-
case 'deleteObservations': {
|
|
733
|
-
const name = op.data.name;
|
|
734
|
-
const exists = (entityNames.has(name) || pendingCreates.has(name)) && !pendingDeletes.has(name);
|
|
735
|
-
if (!exists) {
|
|
736
|
-
return { message: `Entity "${name}" not found`, index: i };
|
|
737
|
-
}
|
|
738
|
-
break;
|
|
739
|
-
}
|
|
740
|
-
case 'deleteEntity': {
|
|
741
|
-
const name = op.data.name;
|
|
742
|
-
const exists = (entityNames.has(name) || pendingCreates.has(name)) && !pendingDeletes.has(name);
|
|
743
|
-
if (!exists) {
|
|
744
|
-
return { message: `Entity "${name}" not found for deletion`, index: i };
|
|
745
|
-
}
|
|
746
|
-
pendingDeletes.add(name);
|
|
747
|
-
break;
|
|
748
|
-
}
|
|
749
|
-
case 'createRelation': {
|
|
750
|
-
const { from, to } = op.data;
|
|
751
|
-
const fromExists = (entityNames.has(from) || pendingCreates.has(from)) && !pendingDeletes.has(from);
|
|
752
|
-
const toExists = (entityNames.has(to) || pendingCreates.has(to)) && !pendingDeletes.has(to);
|
|
753
|
-
if (!fromExists) {
|
|
754
|
-
return { message: `Source entity "${from}" not found for relation`, index: i };
|
|
755
|
-
}
|
|
756
|
-
if (!toExists) {
|
|
757
|
-
return { message: `Target entity "${to}" not found for relation`, index: i };
|
|
758
|
-
}
|
|
759
|
-
break;
|
|
760
|
-
}
|
|
761
|
-
case 'deleteRelation': {
|
|
762
|
-
// Relations are validated at execution time
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
return null;
|
|
768
|
-
}
|
|
769
|
-
/**
|
|
770
|
-
* Apply a single batch operation to the graph.
|
|
771
|
-
* @private
|
|
772
|
-
*/
|
|
773
|
-
applyBatchOperation(graph, operation, timestamp, result) {
|
|
774
|
-
switch (operation.type) {
|
|
775
|
-
case 'createEntity': {
|
|
776
|
-
const entity = {
|
|
777
|
-
...operation.data,
|
|
778
|
-
createdAt: timestamp,
|
|
779
|
-
lastModified: timestamp,
|
|
780
|
-
};
|
|
781
|
-
if (graph.entities.some(e => e.name === entity.name)) {
|
|
782
|
-
throw new KnowledgeGraphError(`Entity "${entity.name}" already exists`, 'DUPLICATE_ENTITY');
|
|
783
|
-
}
|
|
784
|
-
graph.entities.push(entity);
|
|
785
|
-
result.entitiesCreated++;
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
case 'updateEntity': {
|
|
789
|
-
const { name, updates } = operation.data;
|
|
790
|
-
const entity = graph.entities.find(e => e.name === name);
|
|
791
|
-
if (!entity) {
|
|
792
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
793
|
-
}
|
|
794
|
-
// Sanitize updates to prevent prototype pollution
|
|
795
|
-
Object.assign(entity, sanitizeObject(updates));
|
|
796
|
-
entity.lastModified = timestamp;
|
|
797
|
-
result.entitiesUpdated++;
|
|
798
|
-
break;
|
|
799
|
-
}
|
|
800
|
-
case 'deleteEntity': {
|
|
801
|
-
const { name } = operation.data;
|
|
802
|
-
const index = graph.entities.findIndex(e => e.name === name);
|
|
803
|
-
if (index === -1) {
|
|
804
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
805
|
-
}
|
|
806
|
-
graph.entities.splice(index, 1);
|
|
807
|
-
// Delete related relations
|
|
808
|
-
graph.relations = graph.relations.filter(r => r.from !== name && r.to !== name);
|
|
809
|
-
result.entitiesDeleted++;
|
|
810
|
-
break;
|
|
811
|
-
}
|
|
812
|
-
case 'createRelation': {
|
|
813
|
-
const relation = {
|
|
814
|
-
...operation.data,
|
|
815
|
-
createdAt: timestamp,
|
|
816
|
-
lastModified: timestamp,
|
|
817
|
-
};
|
|
818
|
-
const exists = graph.relations.some(r => r.from === relation.from && r.to === relation.to && r.relationType === relation.relationType);
|
|
819
|
-
if (exists) {
|
|
820
|
-
throw new KnowledgeGraphError(`Relation "${relation.from}" -> "${relation.to}" (${relation.relationType}) already exists`, 'DUPLICATE_RELATION');
|
|
821
|
-
}
|
|
822
|
-
graph.relations.push(relation);
|
|
823
|
-
result.relationsCreated++;
|
|
824
|
-
break;
|
|
825
|
-
}
|
|
826
|
-
case 'deleteRelation': {
|
|
827
|
-
const { from, to, relationType } = operation.data;
|
|
828
|
-
const index = graph.relations.findIndex(r => r.from === from && r.to === to && r.relationType === relationType);
|
|
829
|
-
if (index === -1) {
|
|
830
|
-
throw new KnowledgeGraphError(`Relation "${from}" -> "${to}" (${relationType}) not found`, 'RELATION_NOT_FOUND');
|
|
831
|
-
}
|
|
832
|
-
graph.relations.splice(index, 1);
|
|
833
|
-
result.relationsDeleted++;
|
|
834
|
-
break;
|
|
835
|
-
}
|
|
836
|
-
case 'addObservations': {
|
|
837
|
-
const { name, observations } = operation.data;
|
|
838
|
-
const entity = graph.entities.find(e => e.name === name);
|
|
839
|
-
if (!entity) {
|
|
840
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
841
|
-
}
|
|
842
|
-
// Add only new observations
|
|
843
|
-
const existingSet = new Set(entity.observations);
|
|
844
|
-
const newObs = observations.filter((o) => !existingSet.has(o));
|
|
845
|
-
entity.observations.push(...newObs);
|
|
846
|
-
entity.lastModified = timestamp;
|
|
847
|
-
result.entitiesUpdated++;
|
|
848
|
-
break;
|
|
849
|
-
}
|
|
850
|
-
case 'deleteObservations': {
|
|
851
|
-
const { name, observations } = operation.data;
|
|
852
|
-
const entity = graph.entities.find(e => e.name === name);
|
|
853
|
-
if (!entity) {
|
|
854
|
-
throw new KnowledgeGraphError(`Entity "${name}" not found`, 'ENTITY_NOT_FOUND');
|
|
855
|
-
}
|
|
856
|
-
const toDelete = new Set(observations);
|
|
857
|
-
entity.observations = entity.observations.filter((o) => !toDelete.has(o));
|
|
858
|
-
entity.lastModified = timestamp;
|
|
859
|
-
result.entitiesUpdated++;
|
|
860
|
-
break;
|
|
861
|
-
}
|
|
862
|
-
default: {
|
|
863
|
-
const _exhaustiveCheck = operation;
|
|
864
|
-
throw new KnowledgeGraphError(`Unknown batch operation type: ${_exhaustiveCheck.type}`, 'UNKNOWN_OPERATION');
|
|
865
|
-
}
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
//# sourceMappingURL=TransactionManager.js.map
|