@aetherframework/database 1.0.9 → 1.1.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.
Files changed (44) hide show
  1. package/package.json +1 -2
  2. package/src/DatabaseManager.js +0 -565
  3. package/src/core/ConnectionManager.js +0 -351
  4. package/src/core/DatabaseFactory.js +0 -188
  5. package/src/core/MongoQueryBuilder.js +0 -576
  6. package/src/core/PluginManager.js +0 -968
  7. package/src/core/QueryBuilder.js +0 -4398
  8. package/src/core/TransactionManager.js +0 -40
  9. package/src/drivers/clickhouse-driver.js +0 -272
  10. package/src/drivers/index.js +0 -273
  11. package/src/drivers/mongodb-driver.js +0 -87
  12. package/src/drivers/mssql-driver.js +0 -117
  13. package/src/drivers/mysql-driver.js +0 -169
  14. package/src/drivers/oracle-driver.js +0 -101
  15. package/src/drivers/postgres-driver.js +0 -234
  16. package/src/drivers/redis-driver.js +0 -52
  17. package/src/drivers/sqlite-driver.js +0 -67
  18. package/src/middleware/connection-pool.js +0 -455
  19. package/src/middleware/performance-monitor.js +0 -652
  20. package/src/middleware/query-cache.js +0 -500
  21. package/src/middleware/query-logger.js +0 -262
  22. package/src/plugins/AuditPlugin.js +0 -447
  23. package/src/plugins/BasePlugin.js +0 -418
  24. package/src/plugins/BatchOperationPlugin.js +0 -165
  25. package/src/plugins/CachePlugin.js +0 -407
  26. package/src/plugins/CtePlugin.js +0 -523
  27. package/src/plugins/DistributedPlugin.js +0 -543
  28. package/src/plugins/EncryptionPlugin.js +0 -211
  29. package/src/plugins/FullTextSearchPlugin.js +0 -164
  30. package/src/plugins/GeospatialPlugin.js +0 -219
  31. package/src/plugins/GraphQLPlugin.js +0 -162
  32. package/src/plugins/HookPlugin.js +0 -211
  33. package/src/plugins/JsonPlugin.js +0 -366
  34. package/src/plugins/OptimisticLockPlugin.js +0 -374
  35. package/src/plugins/PerformancePlugin.js +0 -175
  36. package/src/plugins/ResiliencePlugin.js +0 -114
  37. package/src/plugins/ShardingPlugin.js +0 -227
  38. package/src/plugins/SoftDeletePlugin.js +0 -258
  39. package/src/plugins/SyncPlugin.js +0 -373
  40. package/src/plugins/VersioningPlugin.js +0 -314
  41. package/src/plugins/WindowFunctionPlugin.js +0 -343
  42. package/src/utils/config-loader.js +0 -632
  43. package/src/utils/error-handler.js +0 -724
  44. package/src/utils/migration-runner.js +0 -1066
@@ -1,374 +0,0 @@
1
- /**
2
- * @license MIT
3
- * Copyright (c) 2026-present AetherFramework Contributors.
4
- * SPDX-License-Identifier: MIT
5
- * @module @aetherframework/database/plugin/OptimisticLockPlugin
6
- */
7
- import { BasePlugin } from './BasePlugin.js';
8
-
9
- /**
10
- * Optimistic Lock Plugin - Provides optimistic locking and data versioning
11
- * Prevents concurrent updates and maintains version history
12
- */
13
- export class OptimisticLockPlugin extends BasePlugin {
14
- constructor(queryBuilder) {
15
- super(queryBuilder);
16
- this.versionColumn = 'version';
17
- this.versionHistoryEnabled = false;
18
- this.versionHistoryTable = null;
19
- this.keyField = 'id';
20
- }
21
-
22
- /**
23
- * Initialize the plugin
24
- * @returns {Promise<void>}
25
- */
26
- async init() {
27
- if (this.initialized) return;
28
- this.initialized = true;
29
- this._registerMethods();
30
- }
31
-
32
- /**
33
- * Register plugin methods to QueryBuilder
34
- * @protected
35
- */
36
- _registerMethods() {
37
- this.queryBuilder.withOptimisticLock = this.withOptimisticLock.bind(this);
38
- this.queryBuilder.updateWithVersion = this.updateWithVersion.bind(this);
39
- this.queryBuilder.createVersionHistory = this.createVersionHistory.bind(this);
40
- this.queryBuilder.getVersionHistory = this.getVersionHistory.bind(this);
41
- this.queryBuilder.restoreFromVersion = this.restoreFromVersion.bind(this);
42
-
43
- // Override update method to add version checking
44
- const originalUpdate = this.queryBuilder.update;
45
- this.queryBuilder.update = function(data) {
46
- // If optimistic lock is enabled and version column exists in data
47
- if (this.versionColumn && data && data[this.versionColumn] !== undefined) {
48
- const expectedVersion = data[this.versionColumn];
49
- delete data[this.versionColumn];
50
-
51
- // Add version check to WHERE clause
52
- this.where(this.versionColumn, '=', expectedVersion);
53
-
54
- // Increment version
55
- data[this.versionColumn] = expectedVersion + 1;
56
- }
57
-
58
- return originalUpdate.call(this, data);
59
- }.bind(this.queryBuilder);
60
-
61
- return this;
62
- }
63
-
64
- /**
65
- * Cleanup plugin resources
66
- * @returns {Promise<void>}
67
- */
68
- async cleanup() {
69
- delete this.queryBuilder.withOptimisticLock;
70
- delete this.queryBuilder.updateWithVersion;
71
- delete this.queryBuilder.createVersionHistory;
72
- delete this.queryBuilder.getVersionHistory;
73
- delete this.queryBuilder.restoreFromVersion;
74
-
75
- // Restore original update method
76
- // Note: This requires storing the original method reference
77
-
78
- this.initialized = false;
79
- }
80
-
81
- /**
82
- * Enable optimistic locking
83
- * @param {Object} options - Configuration options
84
- * @param {string} options.column - Version column name (default: 'version')
85
- * @param {boolean} options.autoIncrement - Auto increment version (default: true)
86
- * @returns {QueryBuilder} Query builder instance
87
- */
88
- withOptimisticLock(options = {}) {
89
- this.versionColumn = options.column || 'version';
90
- this.autoIncrement = options.autoIncrement !== false;
91
- this.queryBuilder.query.optimisticLock = true;
92
-
93
- return this.queryBuilder;
94
- }
95
-
96
- /**
97
- * Update with version check (optimistic locking)
98
- * @param {Object} data - Data to update
99
- * @param {number} expectedVersion - Expected version number
100
- * @param {Object} options - Update options
101
- * @param {string} options.changedBy - User who made the change
102
- * @param {string} options.changeReason - Reason for the change
103
- * @returns {Promise<Object>} Update result
104
- */
105
- async updateWithVersion(data, expectedVersion, options = {}) {
106
- if (!this.versionColumn) {
107
- throw new Error('Optimistic lock is not enabled. Call withOptimisticLock() first.');
108
- }
109
-
110
- // Store original data for version history
111
- const originalData = await this.queryBuilder.clone().first();
112
-
113
- if (!originalData) {
114
- throw new Error('Record not found');
115
- }
116
-
117
- // Check version
118
- if (originalData[this.versionColumn] !== expectedVersion) {
119
- throw new Error(
120
- 'Optimistic lock failed: record was modified by another transaction. ' +
121
- `Expected version: ${expectedVersion}, Actual version: ${originalData[this.versionColumn]}`
122
- );
123
- }
124
-
125
- // Increment version
126
- const updateData = { ...data };
127
- updateData[this.versionColumn] = expectedVersion + 1;
128
-
129
- // Add version check to WHERE clause
130
- this.queryBuilder.where(this.versionColumn, '=', expectedVersion);
131
-
132
- // Execute update
133
- const result = await this.queryBuilder.update(updateData).execute();
134
-
135
- if (result.affectedRows === 0) {
136
- throw new Error('Optimistic lock failed: record was modified by another transaction');
137
- }
138
-
139
- // Record version history if enabled
140
- if (this.versionHistoryEnabled && this.versionHistoryTable) {
141
- await this._recordVersionHistory(originalData, updateData, options);
142
- }
143
-
144
- return {
145
- ...result,
146
- newVersion: updateData[this.versionColumn],
147
- previousVersion: expectedVersion
148
- };
149
- }
150
-
151
- /**
152
- * Create version history table
153
- * @param {Object} options - Configuration options
154
- * @param {string} options.tableName - History table name (default: `${tableName}_history`)
155
- * @param {string} options.keyField - Primary key field name (default: 'id')
156
- * @param {boolean} options.autoCreate - Auto create table if not exists (default: true)
157
- * @returns {Promise<QueryBuilder>} Query builder instance
158
- */
159
- async createVersionHistory(options = {}) {
160
- const tableName = options.tableName || `${this.queryBuilder.tableName}_history`;
161
- const keyField = options.keyField || 'id';
162
- const autoCreate = options.autoCreate !== false;
163
-
164
- this.versionHistoryEnabled = true;
165
- this.versionHistoryTable = tableName;
166
- this.keyField = keyField;
167
-
168
- if (autoCreate) {
169
- await this._createHistoryTable(tableName, keyField);
170
- }
171
-
172
- // Add hook to record version history
173
- this.queryBuilder.addHook('beforeUpdate', async (data, query) => {
174
- await this._recordVersionHistoryOnUpdate(data, query);
175
- });
176
-
177
- return this.queryBuilder;
178
- }
179
-
180
- /**
181
- * Get version history for a record
182
- * @param {number|string} recordId - Record ID
183
- * @param {Object} options - Query options
184
- * @param {number} options.limit - Limit results
185
- * @param {number} options.offset - Offset for pagination
186
- * @param {string} options.orderBy - Order by column
187
- * @param {string} options.orderDirection - Order direction (ASC/DESC)
188
- * @returns {Promise<Array>} Version history
189
- */
190
- async getVersionHistory(recordId, options = {}) {
191
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) {
192
- throw new Error('Version history is not enabled. Call createVersionHistory() first.');
193
- }
194
-
195
- const {
196
- limit = 100,
197
- offset = 0,
198
- orderBy = 'changed_at',
199
- orderDirection = 'DESC'
200
- } = options;
201
-
202
- const historyQuery = new this.queryBuilder.constructor(
203
- this.versionHistoryTable,
204
- this.queryBuilder.connection,
205
- this.queryBuilder.dialect
206
- );
207
-
208
- return historyQuery
209
- .where(this.keyField, '=', recordId)
210
- .orderBy(orderBy, orderDirection)
211
- .limit(limit)
212
- .offset(offset)
213
- .get();
214
- }
215
-
216
- /**
217
- * Restore record from specific version
218
- * @param {number} historyId - History record ID
219
- * @param {Object} options - Restore options
220
- * @param {string} options.restoredBy - User who performed restoration
221
- * @param {string} options.restoreReason - Reason for restoration
222
- * @returns {Promise<Object>} Restore result
223
- */
224
- async restoreFromVersion(historyId, options = {}) {
225
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) {
226
- throw new Error('Version history is not enabled. Call createVersionHistory() first.');
227
- }
228
-
229
- // Get history record
230
- const historyQuery = new this.queryBuilder.constructor(
231
- this.versionHistoryTable,
232
- this.queryBuilder.connection,
233
- this.queryBuilder.dialect
234
- );
235
-
236
- const historyRecord = await historyQuery
237
- .where('history_id', '=', historyId)
238
- .first();
239
-
240
- if (!historyRecord) {
241
- throw new Error(`Version history record ${historyId} not found`);
242
- }
243
-
244
- // Parse historical data
245
- const historicalData = typeof historyRecord.data === 'string'
246
- ? JSON.parse(historyRecord.data)
247
- : historyRecord.data;
248
-
249
- // Update current record with historical data
250
- const updateData = {
251
- ...historicalData,
252
- [this.versionColumn]: historyRecord.version,
253
- restored_from_version: historyId,
254
- restored_at: new Date(),
255
- restored_by: options.restoredBy || null,
256
- restore_reason: options.restoreReason || null
257
- };
258
-
259
- // Remove history-specific fields
260
- delete updateData.history_id;
261
- delete updateData.changed_at;
262
- delete updateData.changed_by;
263
-
264
- return this.queryBuilder
265
- .where(this.keyField, '=', historyRecord[this.keyField])
266
- .update(updateData)
267
- .execute();
268
- }
269
-
270
- /**
271
- * Create history table
272
- * @private
273
- */
274
- async _createHistoryTable(tableName, keyField) {
275
- const sql = `
276
- CREATE TABLE IF NOT EXISTS ${tableName} (
277
- history_id BIGINT AUTO_INCREMENT PRIMARY KEY,
278
- ${keyField} BIGINT NOT NULL,
279
- version INT NOT NULL,
280
- data JSON NOT NULL,
281
- changed_by VARCHAR(255),
282
- changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
283
- change_type VARCHAR(50),
284
- change_reason TEXT,
285
- INDEX idx_${keyField}_version (${keyField}, version),
286
- INDEX idx_changed_at (changed_at)
287
- )
288
- `;
289
-
290
- await this.queryBuilder.connection.query(sql);
291
- }
292
-
293
- /**
294
- * Record version history on update
295
- * @private
296
- */
297
- async _recordVersionHistoryOnUpdate(data, query) {
298
- if (!this.versionHistoryEnabled || !this.versionHistoryTable) {
299
- return;
300
- }
301
-
302
- const current = await query.clone().first();
303
- if (current && current[this.versionColumn]) {
304
- await this._recordVersionHistory(current, data, {
305
- changed_by: data.changed_by,
306
- change_type: 'UPDATE',
307
- change_reason: data.change_reason
308
- });
309
- }
310
- }
311
-
312
- /**
313
- * Record version history
314
- * @private
315
- */
316
- async _recordVersionHistory(originalData, newData, options = {}) {
317
- const historyData = {
318
- [this.keyField]: originalData[this.keyField],
319
- version: originalData[this.versionColumn],
320
- data: JSON.stringify(originalData),
321
- changed_by: options.changedBy || null,
322
- change_type: options.changeType || 'UPDATE',
323
- change_reason: options.changeReason || null
324
- };
325
-
326
- const historyQuery = new this.queryBuilder.constructor(
327
- this.versionHistoryTable,
328
- this.queryBuilder.connection,
329
- this.queryBuilder.dialect
330
- );
331
-
332
- await historyQuery.insert(historyData);
333
- }
334
-
335
- /**
336
- * Get plugin configuration
337
- * @returns {Object} Configuration object
338
- */
339
- getConfig() {
340
- return {
341
- versionColumn: this.versionColumn,
342
- versionHistoryEnabled: this.versionHistoryEnabled,
343
- versionHistoryTable: this.versionHistoryTable,
344
- keyField: this.keyField,
345
- autoIncrement: this.autoIncrement
346
- };
347
- }
348
-
349
- /**
350
- * Get current version of record
351
- * @param {number|string} recordId - Record ID
352
- * @returns {Promise<number|null>} Current version number or null if not found
353
- */
354
- async getCurrentVersion(recordId) {
355
- const record = await this.queryBuilder
356
- .clone()
357
- .where(this.keyField, '=', recordId)
358
- .select(this.versionColumn)
359
- .first();
360
-
361
- return record ? record[this.versionColumn] : null;
362
- }
363
-
364
- /**
365
- * Compare versions
366
- * @param {number|string} recordId - Record ID
367
- * @param {number} expectedVersion - Expected version
368
- * @returns {Promise<boolean>} True if versions match
369
- */
370
- async checkVersion(recordId, expectedVersion) {
371
- const currentVersion = await this.getCurrentVersion(recordId);
372
- return currentVersion === expectedVersion;
373
- }
374
- }
@@ -1,175 +0,0 @@
1
- /**
2
- * @license MIT
3
- * Copyright (c) 2026-present AetherFramework Contributors.
4
- * SPDX-License-Identifier: MIT
5
- * @module @aetherframework/database/plugin/PerformancePlugin
6
- */
7
- import { BasePlugin } from "./BasePlugin.js";
8
-
9
- /**
10
- * Performance Plugin - Provides query analysis, cost estimation, and index usage suggestions
11
- */
12
- export class PerformancePlugin extends BasePlugin {
13
- constructor(queryBuilder) {
14
- super(queryBuilder);
15
- this.pluginName = "PerformancePlugin";
16
- }
17
-
18
- _registerMethods() {
19
- // Register performance analysis methods to QueryBuilder
20
- this.queryBuilder.explain = this.explain.bind(this);
21
- this.queryBuilder.analyze = this.analyze.bind(this);
22
- this.queryBuilder.getPerformanceMetrics = this.getPerformanceMetrics.bind(this);
23
- this.queryBuilder.estimateQueryCost = this.estimateQueryCost.bind(this);
24
- this.queryBuilder.getUsedIndexes = this.getUsedIndexes.bind(this);
25
- this.queryBuilder.getJoinStrategy = this.getJoinStrategy.bind(this);
26
- this.queryBuilder.getSortMethod = this.getSortMethod.bind(this);
27
- }
28
-
29
- /**
30
- * Explain query execution plan
31
- * @returns {Promise<Object>} Explain plan
32
- */
33
- async explain() {
34
- const { sql, bindings } = this.queryBuilder.toSQL();
35
- const explainSql = `EXPLAIN ${sql}`;
36
- return this.queryBuilder.executeQuery(explainSql, bindings);
37
- }
38
-
39
- /**
40
- * Analyze query performance
41
- * @returns {Promise<Object>} Analysis results
42
- */
43
- async analyze() {
44
- const { sql, bindings } = this.queryBuilder.toSQL();
45
- const analyzeSql = `ANALYZE ${sql}`;
46
- return this.queryBuilder.executeQuery(analyzeSql, bindings);
47
- }
48
-
49
- /**
50
- * Get query performance metrics
51
- * @returns {Object} Performance metrics
52
- */
53
- getPerformanceMetrics() {
54
- const { sql } = this.queryBuilder.toSQL();
55
-
56
- return {
57
- table: this.queryBuilder.tableName,
58
- type: this.queryBuilder.query.type,
59
- estimatedCost: this.estimateQueryCost(),
60
- indexesUsed: this.getUsedIndexes(),
61
- joinStrategy: this.getJoinStrategy(),
62
- sortMethod: this.getSortMethod(),
63
- filterConditions: this.queryBuilder.query.where.length,
64
- hasSubqueries: this.queryBuilder.subQueries.size > 0,
65
- hasAggregations: this.queryBuilder.query.groupBy.length > 0,
66
- sqlLength: sql.length,
67
- bindingsCount: this.queryBuilder.bindings.length,
68
- timestamp: new Date().toISOString(),
69
- };
70
- }
71
-
72
- /**
73
- * Estimate query cost
74
- * @returns {number} Estimated cost
75
- */
76
- estimateQueryCost() {
77
- let cost = 1; // Base cost
78
-
79
- // Add cost for joins
80
- cost += this.queryBuilder.query.joins.length * 10;
81
-
82
- // Add cost for WHERE conditions
83
- cost += this.queryBuilder.query.where.length * 2;
84
-
85
- // Add cost for GROUP BY
86
- cost += this.queryBuilder.query.groupBy.length * 5;
87
-
88
- // Add cost for ORDER BY
89
- cost += this.queryBuilder.query.orderBy.length * 3;
90
-
91
- // Add cost for subqueries
92
- cost += this.queryBuilder.subQueries.size * 20;
93
-
94
- // Add cost for UNION
95
- cost += this.queryBuilder.query.union.length * 15;
96
-
97
- // Add cost for DISTINCT
98
- if (this.queryBuilder.query.distinct) cost += 5;
99
-
100
- // Add cost for LIMIT/OFFSET
101
- if (this.queryBuilder.query.limit !== null) cost += 2;
102
- if (this.queryBuilder.query.offset !== null) cost += 3;
103
-
104
- return cost;
105
- }
106
-
107
- /**
108
- * Get used indexes
109
- * @returns {Array} List of used indexes
110
- */
111
- getUsedIndexes() {
112
- const indexes = new Set();
113
-
114
- // Analyze WHERE condition fields
115
- this.queryBuilder.query.where.forEach((condition) => {
116
- if (condition.column && condition.type === "basic") {
117
- indexes.add(`${this.queryBuilder.tableName}.${condition.column}`);
118
- }
119
- });
120
-
121
- // Analyze JOIN condition fields
122
- this.queryBuilder.query.joins.forEach((join) => {
123
- if (join.first)
124
- indexes.add(`${join.table}.${join.first.split(".") || join.first}`);
125
- if (join.second)
126
- indexes.add(
127
- `${this.queryBuilder.tableName}.${join.second.split(".") || join.second}`,
128
- );
129
- });
130
-
131
- // Analyze ORDER BY fields
132
- this.queryBuilder.query.orderBy.forEach((order) => {
133
- if (order.column && !order.raw) {
134
- indexes.add(`${this.queryBuilder.tableName}.${order.column}`);
135
- }
136
- });
137
-
138
- // Analyze GROUP BY fields
139
- this.queryBuilder.query.groupBy.forEach((column) => {
140
- indexes.add(`${this.queryBuilder.tableName}.${column}`);
141
- });
142
-
143
- return Array.from(indexes);
144
- }
145
-
146
- /**
147
- * Get join strategy
148
- * @returns {Array} Join strategies
149
- */
150
- getJoinStrategy() {
151
- if (this.queryBuilder.query.joins.length === 0) return ["none"];
152
-
153
- return this.queryBuilder.query.joins.map((join) => ({
154
- type: join.type,
155
- table: join.table,
156
- condition: `${join.first} ${join.operator} ${join.second}`,
157
- }));
158
- }
159
-
160
- /**
161
- * Get sort method
162
- * @returns {Array} Sort methods
163
- */
164
- getSortMethod() {
165
- if (this.queryBuilder.query.orderBy.length === 0) return ["none"];
166
-
167
- return this.queryBuilder.query.orderBy.map((order) => ({
168
- column: order.raw ? "raw" : order.column,
169
- direction: order.raw ? "custom" : order.direction,
170
- usingIndex: this.getUsedIndexes().some((idx) =>
171
- idx.includes(order.column),
172
- ),
173
- }));
174
- }
175
- }
@@ -1,114 +0,0 @@
1
- /**
2
- * @license MIT
3
- * Copyright (c) 2026-present AetherFramework Contributors.
4
- * SPDX-License-Identifier: MIT
5
- * @module @aetherframework/database/plugin/ResiliencePlugin
6
- */
7
- import { BasePlugin } from "./BasePlugin.js";
8
-
9
- /**
10
- * Resilience Plugin - Provides query retry, timeout control, and transaction management
11
- */
12
- export class ResiliencePlugin extends BasePlugin {
13
- constructor(queryBuilder) {
14
- super(queryBuilder);
15
- this.pluginName = "ResiliencePlugin";
16
- }
17
-
18
- _registerMethods() {
19
- // Register resilience methods to QueryBuilder
20
- this.queryBuilder.executeWithRetry = this.executeWithRetry.bind(this);
21
- this.queryBuilder.isRetryableError = this.isRetryableError.bind(this);
22
- this.queryBuilder.executeWithTimeout = this.executeWithTimeout.bind(this);
23
- this.queryBuilder.executeInTransaction = this.executeInTransaction.bind(this);
24
- }
25
-
26
- /**
27
- * Execute query with retry logic
28
- * @param {number} maxRetries - Maximum number of retries
29
- * @param {number} retryDelay - Delay between retries in milliseconds
30
- * @returns {Promise<Object>} Query result
31
- */
32
- async executeWithRetry(maxRetries = 3, retryDelay = 1000) {
33
- let lastError;
34
-
35
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
36
- try {
37
- return await this.queryBuilder.execute();
38
- } catch (error) {
39
- lastError = error;
40
-
41
- // Check if error is retryable
42
- if (!this.isRetryableError(error) || attempt === maxRetries) {
43
- throw error;
44
- }
45
-
46
-
47
-
48
- // Wait before retrying
49
- await new Promise((resolve) =>
50
- setTimeout(resolve, retryDelay * attempt),
51
- );
52
- }
53
- }
54
-
55
- throw lastError;
56
- }
57
-
58
- /**
59
- * Check if error is retryable
60
- * @param {Error} error - Error object
61
- * @returns {boolean} True if error is retryable
62
- */
63
- isRetryableError(error) {
64
- const retryableMessages = [
65
- "deadlock",
66
- "timeout",
67
- "connection",
68
- "lock",
69
- "busy",
70
- "try again",
71
- "retry",
72
- "temporary",
73
- ];
74
-
75
- const errorMessage = error.message.toLowerCase();
76
- return retryableMessages.some((msg) => errorMessage.includes(msg));
77
- }
78
-
79
- /**
80
- * Execute query with timeout
81
- * @param {number} timeout - Timeout in milliseconds
82
- * @returns {Promise<Object>} Query result
83
- */
84
- async executeWithTimeout(timeout = 30000) {
85
- return Promise.race([
86
- this.queryBuilder.execute(),
87
- new Promise((_, reject) => {
88
- setTimeout(
89
- () => reject(new Error(`Query timeout after ${timeout}ms`)),
90
- timeout,
91
- );
92
- }),
93
- ]);
94
- }
95
-
96
- /**
97
- * Execute query with transaction
98
- * @param {Function} callback - Transaction callback
99
- * @returns {Promise<Object>} Transaction result
100
- */
101
- async executeInTransaction(callback) {
102
- try {
103
- await this.queryBuilder.connection.beginTransaction();
104
-
105
- const result = await callback(this.queryBuilder);
106
-
107
- await this.queryBuilder.connection.commit();
108
- return result;
109
- } catch (error) {
110
- await this.queryBuilder.connection.rollback();
111
- throw error;
112
- }
113
- }
114
- }