@soulcraft/brainy 5.7.13 → 5.8.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.
@@ -87,11 +87,39 @@ export class GraphAdjacencyIndex {
87
87
  }
88
88
  /**
89
89
  * Core API - Neighbor lookup with LSM-tree storage
90
- * Now O(log n) with bloom filter optimization (90% of queries skip disk I/O)
90
+ *
91
+ * O(log n) with bloom filter optimization (90% of queries skip disk I/O)
92
+ * v5.8.0: Added pagination support for high-degree nodes
93
+ *
94
+ * @param id Entity ID to get neighbors for
95
+ * @param optionsOrDirection Optional: direction string OR options object
96
+ * @returns Array of neighbor IDs (paginated if limit/offset specified)
97
+ *
98
+ * @example
99
+ * // Get all neighbors (backward compatible)
100
+ * const all = await graphIndex.getNeighbors(id)
101
+ *
102
+ * @example
103
+ * // Get outgoing neighbors (backward compatible)
104
+ * const out = await graphIndex.getNeighbors(id, 'out')
105
+ *
106
+ * @example
107
+ * // Get first 50 outgoing neighbors (new API)
108
+ * const page1 = await graphIndex.getNeighbors(id, { direction: 'out', limit: 50 })
109
+ *
110
+ * @example
111
+ * // Paginate through neighbors
112
+ * const page1 = await graphIndex.getNeighbors(id, { limit: 100, offset: 0 })
113
+ * const page2 = await graphIndex.getNeighbors(id, { limit: 100, offset: 100 })
91
114
  */
92
- async getNeighbors(id, direction) {
115
+ async getNeighbors(id, optionsOrDirection) {
93
116
  await this.ensureInitialized();
117
+ // Normalize old API (direction string) to new API (options object)
118
+ const options = typeof optionsOrDirection === 'string'
119
+ ? { direction: optionsOrDirection }
120
+ : (optionsOrDirection || {});
94
121
  const startTime = performance.now();
122
+ const direction = options.direction || 'both';
95
123
  const neighbors = new Set();
96
124
  // Query LSM-trees with bloom filter optimization
97
125
  if (direction !== 'in') {
@@ -106,7 +134,14 @@ export class GraphAdjacencyIndex {
106
134
  incoming.forEach(neighborId => neighbors.add(neighborId));
107
135
  }
108
136
  }
109
- const result = Array.from(neighbors);
137
+ // Convert to array for pagination
138
+ let result = Array.from(neighbors);
139
+ // Apply pagination if requested (v5.8.0)
140
+ if (options?.limit !== undefined || options?.offset !== undefined) {
141
+ const offset = options.offset || 0;
142
+ const limit = options.limit !== undefined ? options.limit : result.length;
143
+ result = result.slice(offset, offset + limit);
144
+ }
110
145
  const elapsed = performance.now() - startTime;
111
146
  // Performance assertion - should be sub-5ms with LSM-tree
112
147
  if (elapsed > 5.0) {
@@ -116,13 +151,31 @@ export class GraphAdjacencyIndex {
116
151
  }
117
152
  /**
118
153
  * Get verb IDs by source - Billion-scale optimization for getVerbsBySource
154
+ *
119
155
  * O(log n) LSM-tree lookup with bloom filter optimization
120
156
  * v5.7.1: Filters out deleted verb IDs (tombstone deletion workaround)
157
+ * v5.8.0: Added pagination support for entities with many relationships
121
158
  *
122
159
  * @param sourceId Source entity ID
123
- * @returns Array of verb IDs originating from this source (excluding deleted)
160
+ * @param options Optional configuration
161
+ * @param options.limit Maximum number of verb IDs to return (default: all)
162
+ * @param options.offset Number of verb IDs to skip (default: 0)
163
+ * @returns Array of verb IDs originating from this source (excluding deleted, paginated if requested)
164
+ *
165
+ * @example
166
+ * // Get all verb IDs (backward compatible)
167
+ * const all = await graphIndex.getVerbIdsBySource(sourceId)
168
+ *
169
+ * @example
170
+ * // Get first 50 verb IDs
171
+ * const page1 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 50 })
172
+ *
173
+ * @example
174
+ * // Paginate through verb IDs
175
+ * const page1 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 100, offset: 0 })
176
+ * const page2 = await graphIndex.getVerbIdsBySource(sourceId, { limit: 100, offset: 100 })
124
177
  */
125
- async getVerbIdsBySource(sourceId) {
178
+ async getVerbIdsBySource(sourceId, options) {
126
179
  await this.ensureInitialized();
127
180
  const startTime = performance.now();
128
181
  const verbIds = await this.lsmTreeVerbsBySource.get(sourceId);
@@ -134,17 +187,42 @@ export class GraphAdjacencyIndex {
134
187
  // Filter out deleted verb IDs (tombstone deletion workaround)
135
188
  // LSM-tree retains all IDs, but verbIdSet tracks deletions
136
189
  const allIds = verbIds || [];
137
- return allIds.filter(id => this.verbIdSet.has(id));
190
+ let result = allIds.filter(id => this.verbIdSet.has(id));
191
+ // Apply pagination if requested (v5.8.0)
192
+ if (options?.limit !== undefined || options?.offset !== undefined) {
193
+ const offset = options.offset || 0;
194
+ const limit = options.limit !== undefined ? options.limit : result.length;
195
+ result = result.slice(offset, offset + limit);
196
+ }
197
+ return result;
138
198
  }
139
199
  /**
140
200
  * Get verb IDs by target - Billion-scale optimization for getVerbsByTarget
201
+ *
141
202
  * O(log n) LSM-tree lookup with bloom filter optimization
142
203
  * v5.7.1: Filters out deleted verb IDs (tombstone deletion workaround)
204
+ * v5.8.0: Added pagination support for popular target entities
143
205
  *
144
206
  * @param targetId Target entity ID
145
- * @returns Array of verb IDs pointing to this target (excluding deleted)
207
+ * @param options Optional configuration
208
+ * @param options.limit Maximum number of verb IDs to return (default: all)
209
+ * @param options.offset Number of verb IDs to skip (default: 0)
210
+ * @returns Array of verb IDs pointing to this target (excluding deleted, paginated if requested)
211
+ *
212
+ * @example
213
+ * // Get all verb IDs (backward compatible)
214
+ * const all = await graphIndex.getVerbIdsByTarget(targetId)
215
+ *
216
+ * @example
217
+ * // Get first 50 verb IDs
218
+ * const page1 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 50 })
219
+ *
220
+ * @example
221
+ * // Paginate through verb IDs
222
+ * const page1 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 100, offset: 0 })
223
+ * const page2 = await graphIndex.getVerbIdsByTarget(targetId, { limit: 100, offset: 100 })
146
224
  */
147
- async getVerbIdsByTarget(targetId) {
225
+ async getVerbIdsByTarget(targetId, options) {
148
226
  await this.ensureInitialized();
149
227
  const startTime = performance.now();
150
228
  const verbIds = await this.lsmTreeVerbsByTarget.get(targetId);
@@ -156,7 +234,14 @@ export class GraphAdjacencyIndex {
156
234
  // Filter out deleted verb IDs (tombstone deletion workaround)
157
235
  // LSM-tree retains all IDs, but verbIdSet tracks deletions
158
236
  const allIds = verbIds || [];
159
- return allIds.filter(id => this.verbIdSet.has(id));
237
+ let result = allIds.filter(id => this.verbIdSet.has(id));
238
+ // Apply pagination if requested (v5.8.0)
239
+ if (options?.limit !== undefined || options?.offset !== undefined) {
240
+ const offset = options.offset || 0;
241
+ const limit = options.limit !== undefined ? options.limit : result.length;
242
+ result = result.slice(offset, offset + limit);
243
+ }
244
+ return result;
160
245
  }
161
246
  /**
162
247
  * Get verb from cache or storage - Billion-scale memory optimization
@@ -2,8 +2,8 @@
2
2
  * Type-Aware HNSW Index - Phase 2 Billion-Scale Optimization
3
3
  *
4
4
  * Maintains separate HNSW graphs per entity type for massive memory savings:
5
- * - Memory @ 1B scale: 384GB → 50GB (-87%)
6
- * - Query speed: 10x faster for single-type queries
5
+ * - Memory @ 1B scale: PROJECTED 384GB → 50GB (-87% from architectural analysis, not yet benchmarked)
6
+ * - Query speed: PROJECTED 10x faster for single-type queries (not yet benchmarked)
7
7
  * - Storage: Already type-first from Phase 1a
8
8
  *
9
9
  * Architecture:
@@ -35,7 +35,7 @@ export interface TypeAwareHNSWStats {
35
35
  * TypeAwareHNSWIndex - Separate HNSW graphs per entity type
36
36
  *
37
37
  * Phase 2 of billion-scale optimization roadmap.
38
- * Reduces HNSW memory by 87% @ billion scale.
38
+ * PROJECTED: Reduces HNSW memory by 87% @ billion scale (calculated from architecture, not yet benchmarked).
39
39
  */
40
40
  export declare class TypeAwareHNSWIndex {
41
41
  private indexes;
@@ -2,8 +2,8 @@
2
2
  * Type-Aware HNSW Index - Phase 2 Billion-Scale Optimization
3
3
  *
4
4
  * Maintains separate HNSW graphs per entity type for massive memory savings:
5
- * - Memory @ 1B scale: 384GB → 50GB (-87%)
6
- * - Query speed: 10x faster for single-type queries
5
+ * - Memory @ 1B scale: PROJECTED 384GB → 50GB (-87% from architectural analysis, not yet benchmarked)
6
+ * - Query speed: PROJECTED 10x faster for single-type queries (not yet benchmarked)
7
7
  * - Storage: Already type-first from Phase 1a
8
8
  *
9
9
  * Architecture:
@@ -27,7 +27,7 @@ const DEFAULT_CONFIG = {
27
27
  * TypeAwareHNSWIndex - Separate HNSW graphs per entity type
28
28
  *
29
29
  * Phase 2 of billion-scale optimization roadmap.
30
- * Reduces HNSW memory by 87% @ billion scale.
30
+ * PROJECTED: Reduces HNSW memory by 87% @ billion scale (calculated from architecture, not yet benchmarked).
31
31
  */
32
32
  export class TypeAwareHNSWIndex {
33
33
  /**
@@ -5,14 +5,14 @@
5
5
  * natural language queries using semantic similarity and routing to specific
6
6
  * TypeAwareHNSWIndex graphs.
7
7
  *
8
- * Performance Impact:
8
+ * Performance Impact (PROJECTED - not yet benchmarked):
9
9
  * - Single-type queries: 42x speedup (search 1/42 graphs)
10
10
  * - Multi-type queries: 8-21x speedup (search 2-5/42 graphs)
11
- * - Overall: 40% latency reduction @ 1B scale
11
+ * - Overall: PROJECTED 40% latency reduction @ 1B scale (calculated from graph reduction, not measured)
12
12
  *
13
13
  * Examples:
14
- * - "Find engineers" → single-type → [Person] → 42x speedup
15
- * - "People at Tesla" → multi-type → [Person, Organization] → 21x speedup
14
+ * - "Find engineers" → single-type → [Person] → PROJECTED 42x speedup
15
+ * - "People at Tesla" → multi-type → [Person, Organization] → PROJECTED 21x speedup
16
16
  * - "Everything about AI" → all-types → [all 42 types] → no speedup
17
17
  */
18
18
  import { NounType } from '../types/graphTypes.js';
@@ -5,14 +5,14 @@
5
5
  * natural language queries using semantic similarity and routing to specific
6
6
  * TypeAwareHNSWIndex graphs.
7
7
  *
8
- * Performance Impact:
8
+ * Performance Impact (PROJECTED - not yet benchmarked):
9
9
  * - Single-type queries: 42x speedup (search 1/42 graphs)
10
10
  * - Multi-type queries: 8-21x speedup (search 2-5/42 graphs)
11
- * - Overall: 40% latency reduction @ 1B scale
11
+ * - Overall: PROJECTED 40% latency reduction @ 1B scale (calculated from graph reduction, not measured)
12
12
  *
13
13
  * Examples:
14
- * - "Find engineers" → single-type → [Person] → 42x speedup
15
- * - "People at Tesla" → multi-type → [Person, Organization] → 21x speedup
14
+ * - "Find engineers" → single-type → [Person] → PROJECTED 42x speedup
15
+ * - "People at Tesla" → multi-type → [Person, Organization] → PROJECTED 21x speedup
16
16
  * - "Everything about AI" → all-types → [all 42 types] → no speedup
17
17
  */
18
18
  import { NounType, NOUN_TYPE_COUNT } from '../types/graphTypes.js';
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Transaction - Atomic Unit of Work
3
+ *
4
+ * Executes operations atomically: all succeed or all rollback.
5
+ * Prevents partial failures that leave system in inconsistent state.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * const tx = new Transaction()
10
+ * tx.addOperation(operation1)
11
+ * tx.addOperation(operation2)
12
+ * await tx.execute() // Both succeed or both rollback
13
+ * ```
14
+ */
15
+ import { Operation, TransactionState, TransactionContext, TransactionOptions } from './types.js';
16
+ /**
17
+ * Transaction class
18
+ */
19
+ export declare class Transaction implements TransactionContext {
20
+ private operations;
21
+ private rollbackActions;
22
+ private state;
23
+ private readonly options;
24
+ private startTime?;
25
+ private endTime?;
26
+ constructor(options?: TransactionOptions);
27
+ /**
28
+ * Add an operation to the transaction
29
+ */
30
+ addOperation(operation: Operation): void;
31
+ /**
32
+ * Get current transaction state
33
+ */
34
+ getState(): TransactionState;
35
+ /**
36
+ * Get number of operations in transaction
37
+ */
38
+ getOperationCount(): number;
39
+ /**
40
+ * Execute all operations atomically
41
+ */
42
+ execute(): Promise<void>;
43
+ /**
44
+ * Commit the transaction
45
+ */
46
+ private commit;
47
+ /**
48
+ * Rollback all executed operations in reverse order
49
+ */
50
+ private rollback;
51
+ /**
52
+ * Get transaction execution time in milliseconds
53
+ */
54
+ getExecutionTimeMs(): number | undefined;
55
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * Transaction - Atomic Unit of Work
3
+ *
4
+ * Executes operations atomically: all succeed or all rollback.
5
+ * Prevents partial failures that leave system in inconsistent state.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * const tx = new Transaction()
10
+ * tx.addOperation(operation1)
11
+ * tx.addOperation(operation2)
12
+ * await tx.execute() // Both succeed or both rollback
13
+ * ```
14
+ */
15
+ import { InvalidTransactionStateError, TransactionExecutionError, TransactionRollbackError, TransactionTimeoutError } from './errors.js';
16
+ import { prodLog } from '../utils/logger.js';
17
+ /**
18
+ * Default transaction options
19
+ */
20
+ const DEFAULT_OPTIONS = {
21
+ timeout: 30000, // 30 seconds
22
+ logging: false,
23
+ maxRollbackRetries: 3
24
+ };
25
+ /**
26
+ * Transaction class
27
+ */
28
+ export class Transaction {
29
+ constructor(options = {}) {
30
+ this.operations = [];
31
+ this.rollbackActions = [];
32
+ this.state = 'pending';
33
+ this.options = { ...DEFAULT_OPTIONS, ...options };
34
+ }
35
+ /**
36
+ * Add an operation to the transaction
37
+ */
38
+ addOperation(operation) {
39
+ if (this.state !== 'pending') {
40
+ throw new InvalidTransactionStateError(this.state, 'add operation');
41
+ }
42
+ this.operations.push(operation);
43
+ }
44
+ /**
45
+ * Get current transaction state
46
+ */
47
+ getState() {
48
+ return this.state;
49
+ }
50
+ /**
51
+ * Get number of operations in transaction
52
+ */
53
+ getOperationCount() {
54
+ return this.operations.length;
55
+ }
56
+ /**
57
+ * Execute all operations atomically
58
+ */
59
+ async execute() {
60
+ if (this.state !== 'pending') {
61
+ throw new InvalidTransactionStateError(this.state, 'execute');
62
+ }
63
+ this.state = 'executing';
64
+ this.startTime = Date.now();
65
+ if (this.options.logging) {
66
+ prodLog.info(`[Transaction] Executing ${this.operations.length} operations`);
67
+ }
68
+ try {
69
+ // Execute each operation in order
70
+ for (let i = 0; i < this.operations.length; i++) {
71
+ // Check timeout
72
+ if (Date.now() - this.startTime > this.options.timeout) {
73
+ throw new TransactionTimeoutError(this.options.timeout, i);
74
+ }
75
+ const operation = this.operations[i];
76
+ try {
77
+ if (this.options.logging) {
78
+ prodLog.info(`[Transaction] Executing operation ${i}: ${operation.name || 'unnamed'}`);
79
+ }
80
+ // Execute operation
81
+ const rollbackAction = await operation.execute();
82
+ // Record rollback action (if provided)
83
+ if (rollbackAction) {
84
+ this.rollbackActions.push(rollbackAction);
85
+ }
86
+ }
87
+ catch (error) {
88
+ // Operation failed - rollback and re-throw
89
+ const executionError = new TransactionExecutionError(`Operation ${i} failed: ${error.message}`, i, operation.name, error);
90
+ await this.rollback(executionError);
91
+ throw executionError;
92
+ }
93
+ }
94
+ // All operations succeeded - commit
95
+ this.commit();
96
+ }
97
+ catch (error) {
98
+ // Error already handled in rollback
99
+ throw error;
100
+ }
101
+ }
102
+ /**
103
+ * Commit the transaction
104
+ */
105
+ commit() {
106
+ this.state = 'committed';
107
+ this.endTime = Date.now();
108
+ if (this.options.logging) {
109
+ const duration = this.endTime - (this.startTime || this.endTime);
110
+ prodLog.info(`[Transaction] Committed successfully in ${duration}ms`);
111
+ }
112
+ // Clear rollback actions - no longer needed
113
+ this.rollbackActions = [];
114
+ }
115
+ /**
116
+ * Rollback all executed operations in reverse order
117
+ */
118
+ async rollback(originalError) {
119
+ this.state = 'rolling_back';
120
+ if (this.options.logging) {
121
+ prodLog.info(`[Transaction] Rolling back ${this.rollbackActions.length} operations`);
122
+ }
123
+ const rollbackErrors = [];
124
+ // Execute rollback actions in REVERSE order
125
+ for (let i = this.rollbackActions.length - 1; i >= 0; i--) {
126
+ const action = this.rollbackActions[i];
127
+ // Retry rollback with exponential backoff
128
+ let attempts = 0;
129
+ let success = false;
130
+ while (attempts < this.options.maxRollbackRetries && !success) {
131
+ try {
132
+ await action();
133
+ success = true;
134
+ if (this.options.logging) {
135
+ prodLog.info(`[Transaction] Rolled back operation ${i}`);
136
+ }
137
+ }
138
+ catch (error) {
139
+ attempts++;
140
+ if (attempts >= this.options.maxRollbackRetries) {
141
+ // Max retries exceeded - log error and continue
142
+ const rollbackError = error;
143
+ rollbackErrors.push(rollbackError);
144
+ prodLog.error(`[Transaction] Rollback failed for operation ${i} after ${attempts} attempts: ${rollbackError.message}`);
145
+ }
146
+ else {
147
+ // Retry with exponential backoff
148
+ const delayMs = Math.pow(2, attempts) * 100;
149
+ await new Promise(resolve => setTimeout(resolve, delayMs));
150
+ }
151
+ }
152
+ }
153
+ }
154
+ this.state = 'rolled_back';
155
+ this.endTime = Date.now();
156
+ if (this.options.logging) {
157
+ const duration = this.endTime - (this.startTime || this.endTime);
158
+ prodLog.info(`[Transaction] Rolled back in ${duration}ms`);
159
+ }
160
+ // If rollback encountered errors, wrap them with original error
161
+ if (rollbackErrors.length > 0) {
162
+ throw new TransactionRollbackError(`Transaction rollback encountered ${rollbackErrors.length} errors during cleanup`, originalError, rollbackErrors);
163
+ }
164
+ }
165
+ /**
166
+ * Get transaction execution time in milliseconds
167
+ */
168
+ getExecutionTimeMs() {
169
+ if (this.startTime && this.endTime) {
170
+ return this.endTime - this.startTime;
171
+ }
172
+ return undefined;
173
+ }
174
+ }
175
+ //# sourceMappingURL=Transaction.js.map
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Transaction Manager
3
+ *
4
+ * Manages transaction execution across the system.
5
+ * Provides high-level API for executing atomic operations.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * const txManager = new TransactionManager()
10
+ *
11
+ * const result = await txManager.executeTransaction(async (tx) => {
12
+ * tx.addOperation(operation1)
13
+ * tx.addOperation(operation2)
14
+ * return someValue
15
+ * })
16
+ * ```
17
+ */
18
+ import { TransactionFunction, TransactionResult, TransactionOptions } from './types.js';
19
+ /**
20
+ * Transaction Manager Statistics
21
+ */
22
+ export interface TransactionStats {
23
+ totalTransactions: number;
24
+ successfulTransactions: number;
25
+ failedTransactions: number;
26
+ rolledBackTransactions: number;
27
+ averageExecutionTimeMs: number;
28
+ averageOperationsPerTransaction: number;
29
+ }
30
+ /**
31
+ * Transaction Manager
32
+ */
33
+ export declare class TransactionManager {
34
+ private stats;
35
+ private totalExecutionTime;
36
+ private totalOperations;
37
+ /**
38
+ * Execute a function within a transaction
39
+ * All operations succeed atomically or all rollback
40
+ *
41
+ * @param fn Function that builds and executes transaction
42
+ * @param options Transaction execution options
43
+ * @returns Result from user function
44
+ * @throws TransactionError if transaction fails
45
+ */
46
+ executeTransaction<T>(fn: TransactionFunction<T>, options?: TransactionOptions): Promise<T>;
47
+ /**
48
+ * Execute a transaction and return detailed result
49
+ */
50
+ executeTransactionWithResult<T>(fn: TransactionFunction<T>, options?: TransactionOptions): Promise<TransactionResult<T>>;
51
+ /**
52
+ * Get transaction statistics
53
+ */
54
+ getStats(): Readonly<TransactionStats>;
55
+ /**
56
+ * Reset transaction statistics
57
+ */
58
+ resetStats(): void;
59
+ /**
60
+ * Update running statistics
61
+ */
62
+ private updateStats;
63
+ /**
64
+ * Log current statistics
65
+ */
66
+ logStats(): void;
67
+ }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Transaction Manager
3
+ *
4
+ * Manages transaction execution across the system.
5
+ * Provides high-level API for executing atomic operations.
6
+ *
7
+ * Usage:
8
+ * ```typescript
9
+ * const txManager = new TransactionManager()
10
+ *
11
+ * const result = await txManager.executeTransaction(async (tx) => {
12
+ * tx.addOperation(operation1)
13
+ * tx.addOperation(operation2)
14
+ * return someValue
15
+ * })
16
+ * ```
17
+ */
18
+ import { Transaction } from './Transaction.js';
19
+ import { TransactionError } from './errors.js';
20
+ import { prodLog } from '../utils/logger.js';
21
+ /**
22
+ * Transaction Manager
23
+ */
24
+ export class TransactionManager {
25
+ constructor() {
26
+ this.stats = {
27
+ totalTransactions: 0,
28
+ successfulTransactions: 0,
29
+ failedTransactions: 0,
30
+ rolledBackTransactions: 0,
31
+ averageExecutionTimeMs: 0,
32
+ averageOperationsPerTransaction: 0
33
+ };
34
+ this.totalExecutionTime = 0;
35
+ this.totalOperations = 0;
36
+ }
37
+ /**
38
+ * Execute a function within a transaction
39
+ * All operations succeed atomically or all rollback
40
+ *
41
+ * @param fn Function that builds and executes transaction
42
+ * @param options Transaction execution options
43
+ * @returns Result from user function
44
+ * @throws TransactionError if transaction fails
45
+ */
46
+ async executeTransaction(fn, options) {
47
+ const transaction = new Transaction(options);
48
+ this.stats.totalTransactions++;
49
+ try {
50
+ // Execute user function (builds operations list)
51
+ const result = await fn(transaction);
52
+ // Execute all operations atomically
53
+ await transaction.execute();
54
+ // Update statistics
55
+ this.stats.successfulTransactions++;
56
+ this.updateStats(transaction);
57
+ return result;
58
+ }
59
+ catch (error) {
60
+ // Transaction failed and rolled back
61
+ this.stats.failedTransactions++;
62
+ if (transaction.getState() === 'rolled_back') {
63
+ this.stats.rolledBackTransactions++;
64
+ }
65
+ this.updateStats(transaction);
66
+ // Re-throw with context
67
+ if (error instanceof TransactionError) {
68
+ throw error;
69
+ }
70
+ else {
71
+ throw new TransactionError(`Transaction failed: ${error.message}`, { cause: error });
72
+ }
73
+ }
74
+ }
75
+ /**
76
+ * Execute a transaction and return detailed result
77
+ */
78
+ async executeTransactionWithResult(fn, options) {
79
+ const startTime = Date.now();
80
+ const transaction = new Transaction(options);
81
+ try {
82
+ const value = await fn(transaction);
83
+ await transaction.execute();
84
+ const executionTimeMs = Date.now() - startTime;
85
+ return {
86
+ value,
87
+ operationCount: transaction.getOperationCount(),
88
+ executionTimeMs
89
+ };
90
+ }
91
+ catch (error) {
92
+ // Transaction failed
93
+ throw error;
94
+ }
95
+ }
96
+ /**
97
+ * Get transaction statistics
98
+ */
99
+ getStats() {
100
+ return { ...this.stats };
101
+ }
102
+ /**
103
+ * Reset transaction statistics
104
+ */
105
+ resetStats() {
106
+ this.stats = {
107
+ totalTransactions: 0,
108
+ successfulTransactions: 0,
109
+ failedTransactions: 0,
110
+ rolledBackTransactions: 0,
111
+ averageExecutionTimeMs: 0,
112
+ averageOperationsPerTransaction: 0
113
+ };
114
+ this.totalExecutionTime = 0;
115
+ this.totalOperations = 0;
116
+ }
117
+ /**
118
+ * Update running statistics
119
+ */
120
+ updateStats(transaction) {
121
+ const executionTime = transaction.getExecutionTimeMs();
122
+ const operationCount = transaction.getOperationCount();
123
+ if (executionTime !== undefined) {
124
+ this.totalExecutionTime += executionTime;
125
+ this.stats.averageExecutionTimeMs =
126
+ this.totalExecutionTime / this.stats.totalTransactions;
127
+ }
128
+ this.totalOperations += operationCount;
129
+ this.stats.averageOperationsPerTransaction =
130
+ this.totalOperations / this.stats.totalTransactions;
131
+ }
132
+ /**
133
+ * Log current statistics
134
+ */
135
+ logStats() {
136
+ prodLog.info('[TransactionManager] Statistics:');
137
+ prodLog.info(` Total: ${this.stats.totalTransactions}`);
138
+ prodLog.info(` Successful: ${this.stats.successfulTransactions}`);
139
+ prodLog.info(` Failed: ${this.stats.failedTransactions}`);
140
+ prodLog.info(` Rolled back: ${this.stats.rolledBackTransactions}`);
141
+ prodLog.info(` Avg execution time: ${this.stats.averageExecutionTimeMs.toFixed(2)}ms`);
142
+ prodLog.info(` Avg operations/tx: ${this.stats.averageOperationsPerTransaction.toFixed(2)}`);
143
+ }
144
+ }
145
+ //# sourceMappingURL=TransactionManager.js.map