@aetherframework/database 1.1.0 → 1.1.2
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/examples/mysql-test-pressure.js +1530 -0
- package/examples/test-direct.js +116 -0
- package/examples/transaction_example.js +127 -0
- package/package.json +3 -1
- package/src/DatabaseManager.js +565 -0
- package/src/core/ConnectionManager.js +351 -0
- package/src/core/DatabaseFactory.js +188 -0
- package/src/core/MongoQueryBuilder.js +576 -0
- package/src/core/PluginManager.js +968 -0
- package/src/core/QueryBuilder.js +4394 -0
- package/src/core/TransactionManager.js +40 -0
- package/src/drivers/clickhouse-driver.js +272 -0
- package/src/drivers/index.js +273 -0
- package/src/drivers/mongodb-driver.js +87 -0
- package/src/drivers/mssql-driver.js +117 -0
- package/src/drivers/mysql-driver.js +169 -0
- package/src/drivers/oracle-driver.js +101 -0
- package/src/drivers/postgres-driver.js +234 -0
- package/src/drivers/redis-driver.js +52 -0
- package/src/drivers/sqlite-driver.js +67 -0
- package/src/middleware/connection-pool.js +455 -0
- package/src/middleware/performance-monitor.js +652 -0
- package/src/middleware/query-cache.js +500 -0
- package/src/middleware/query-logger.js +262 -0
- package/src/plugins/AuditPlugin.js +447 -0
- package/src/plugins/BasePlugin.js +418 -0
- package/src/plugins/BatchOperationPlugin.js +165 -0
- package/src/plugins/CachePlugin.js +407 -0
- package/src/plugins/CtePlugin.js +523 -0
- package/src/plugins/DistributedPlugin.js +543 -0
- package/src/plugins/EncryptionPlugin.js +211 -0
- package/src/plugins/FullTextSearchPlugin.js +164 -0
- package/src/plugins/GeospatialPlugin.js +219 -0
- package/src/plugins/GraphQLPlugin.js +162 -0
- package/src/plugins/HookPlugin.js +211 -0
- package/src/plugins/JsonPlugin.js +366 -0
- package/src/plugins/OptimisticLockPlugin.js +374 -0
- package/src/plugins/PerformancePlugin.js +175 -0
- package/src/plugins/ResiliencePlugin.js +114 -0
- package/src/plugins/ShardingPlugin.js +227 -0
- package/src/plugins/SoftDeletePlugin.js +258 -0
- package/src/plugins/SyncPlugin.js +373 -0
- package/src/plugins/VersioningPlugin.js +314 -0
- package/src/plugins/WindowFunctionPlugin.js +343 -0
- package/src/utils/config-loader.js +632 -0
- package/src/utils/error-handler.js +724 -0
- package/src/utils/migration-runner.js +1066 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/database/core/ConnectionManager
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { EventEmitter } from 'events';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Connection Manager - Manages database connections with pooling and retry logic
|
|
12
|
+
*/
|
|
13
|
+
class ConnectionManager extends EventEmitter {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new ConnectionManager instance
|
|
16
|
+
* @param {Object} driver - Database driver instance
|
|
17
|
+
* @param {Object} config - Connection configuration
|
|
18
|
+
*/
|
|
19
|
+
constructor(driver, config) {
|
|
20
|
+
super();
|
|
21
|
+
this.driver = driver;
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.connection = null;
|
|
24
|
+
this.pool = null;
|
|
25
|
+
this.isConnected = false;
|
|
26
|
+
this.retryCount = 0;
|
|
27
|
+
this.maxRetries = config.retry?.maxAttempts || 3;
|
|
28
|
+
this.retryDelay = config.retry?.delay || 1000;
|
|
29
|
+
this.retryBackoff = config.retry?.backoff !== false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Connect to database
|
|
34
|
+
* @returns {Promise<ConnectionManager>} Connected instance
|
|
35
|
+
*/
|
|
36
|
+
async connect() {
|
|
37
|
+
if (this.isConnected) {
|
|
38
|
+
return this;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Check if driver supports connection pooling
|
|
43
|
+
if (this.config.pool && this.driver.createPool) {
|
|
44
|
+
this.pool = await this.driver.createPool(this.config);
|
|
45
|
+
this.connection = this.pool;
|
|
46
|
+
} else {
|
|
47
|
+
this.connection = await this.driver.connect(this.config);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
this.isConnected = true;
|
|
51
|
+
this.retryCount = 0;
|
|
52
|
+
this.emit('connected', { type: this.config.type, config: this.config });
|
|
53
|
+
return this;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
this.emit('connection:error', { error, config: this.config });
|
|
56
|
+
|
|
57
|
+
// Retry logic
|
|
58
|
+
if (this.retryCount < this.maxRetries) {
|
|
59
|
+
this.retryCount++;
|
|
60
|
+
const delay = this.retryBackoff
|
|
61
|
+
? this.retryDelay * Math.pow(2, this.retryCount - 1)
|
|
62
|
+
: this.retryDelay;
|
|
63
|
+
|
|
64
|
+
console.warn(`⚠️ Connection failed, retrying in ${delay}ms (attempt ${this.retryCount}/${this.maxRetries})`);
|
|
65
|
+
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
67
|
+
return this.connect();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
throw new Error(`Failed to connect to ${this.config.type} database after ${this.maxRetries} attempts: ${error.message}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Execute query
|
|
76
|
+
* @param {string} sql - SQL statement
|
|
77
|
+
* @param {Array} params - Query parameters
|
|
78
|
+
* @param {Object} options - Query options
|
|
79
|
+
* @returns {Promise<Object>} Query result
|
|
80
|
+
*/
|
|
81
|
+
async query(sql, params = [], options = {}) {
|
|
82
|
+
if (!this.isConnected) {
|
|
83
|
+
await this.connect();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const startTime = Date.now();
|
|
88
|
+
const result = await this.driver.query(this.connection, sql, params, options);
|
|
89
|
+
const duration = Date.now() - startTime;
|
|
90
|
+
|
|
91
|
+
this.emit('query:executed', {
|
|
92
|
+
sql,
|
|
93
|
+
params,
|
|
94
|
+
duration,
|
|
95
|
+
result,
|
|
96
|
+
options
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return result;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
this.emit('query:error', { sql, params, error, options });
|
|
102
|
+
|
|
103
|
+
// Check if connection is still alive
|
|
104
|
+
if (error.code === 'ECONNRESET' || error.code === 'PROTOCOL_CONNECTION_LOST') {
|
|
105
|
+
console.warn('⚠️ Connection lost, attempting to reconnect...');
|
|
106
|
+
this.isConnected = false;
|
|
107
|
+
await this.connect();
|
|
108
|
+
return this.query(sql, params, options);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Execute transaction
|
|
117
|
+
* @param {Function} callback - Transaction callback
|
|
118
|
+
* @returns {Promise<*>} Transaction result
|
|
119
|
+
*/
|
|
120
|
+
async transaction(callback) {
|
|
121
|
+
if (!this.isConnected) {
|
|
122
|
+
await this.connect();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!this.driver.beginTransaction) {
|
|
126
|
+
throw new Error(`Transaction not supported for ${this.config.type} driver`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
await this.driver.beginTransaction(this.connection);
|
|
131
|
+
this.emit('transaction:begin');
|
|
132
|
+
|
|
133
|
+
const result = await callback({
|
|
134
|
+
query: (sql, params) => this.query(sql, params),
|
|
135
|
+
execute: (sql, params) => this.execute(sql, params)
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await this.driver.commitTransaction(this.connection);
|
|
139
|
+
this.emit('transaction:commit', { result });
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
await this.driver.rollbackTransaction(this.connection);
|
|
144
|
+
this.emit('transaction:rollback', { error });
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Execute SQL statement (for INSERT, UPDATE, DELETE)
|
|
151
|
+
* @param {string} sql - SQL statement
|
|
152
|
+
* @param {Array} params - Query parameters
|
|
153
|
+
* @param {Object} options - Query options
|
|
154
|
+
* @returns {Promise<Object>} Execution result
|
|
155
|
+
*/
|
|
156
|
+
async execute(sql, params = [], options = {}) {
|
|
157
|
+
if (!this.isConnected) {
|
|
158
|
+
await this.connect();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
try {
|
|
162
|
+
const startTime = Date.now();
|
|
163
|
+
const result = await this.driver.execute(this.connection, sql, params, options);
|
|
164
|
+
const duration = Date.now() - startTime;
|
|
165
|
+
|
|
166
|
+
this.emit('execute:completed', {
|
|
167
|
+
sql,
|
|
168
|
+
params,
|
|
169
|
+
duration,
|
|
170
|
+
result,
|
|
171
|
+
options
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return result;
|
|
175
|
+
} catch (error) {
|
|
176
|
+
this.emit('execute:error', { sql, params, error, options });
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Close connection
|
|
183
|
+
* @returns {Promise<void>}
|
|
184
|
+
*/
|
|
185
|
+
async close() {
|
|
186
|
+
if (!this.isConnected) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
if (this.pool && this.driver.closePool) {
|
|
192
|
+
await this.driver.closePool(this.pool);
|
|
193
|
+
} else if (this.driver.close) {
|
|
194
|
+
await this.driver.close(this.connection);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
this.isConnected = false;
|
|
198
|
+
this.connection = null;
|
|
199
|
+
this.pool = null;
|
|
200
|
+
this.emit('closed');
|
|
201
|
+
} catch (error) {
|
|
202
|
+
this.emit('close:error', { error });
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get connection status
|
|
209
|
+
* @returns {Object} Connection status
|
|
210
|
+
*/
|
|
211
|
+
getStatus() {
|
|
212
|
+
return {
|
|
213
|
+
type: this.config.type,
|
|
214
|
+
isConnected: this.isConnected,
|
|
215
|
+
retryCount: this.retryCount,
|
|
216
|
+
maxRetries: this.maxRetries,
|
|
217
|
+
hasPool: !!this.pool,
|
|
218
|
+
config: {
|
|
219
|
+
host: this.config.host,
|
|
220
|
+
port: this.config.port,
|
|
221
|
+
database: this.config.database,
|
|
222
|
+
user: this.config.user
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Health check
|
|
229
|
+
* @returns {Promise<Object>} Health check result
|
|
230
|
+
*/
|
|
231
|
+
async healthCheck() {
|
|
232
|
+
if (!this.isConnected) {
|
|
233
|
+
return {
|
|
234
|
+
status: 'disconnected',
|
|
235
|
+
message: 'Not connected to database',
|
|
236
|
+
timestamp: new Date().toISOString()
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
const startTime = Date.now();
|
|
242
|
+
|
|
243
|
+
// Try a simple query to check connection health
|
|
244
|
+
const healthQuery = this.getHealthQuery();
|
|
245
|
+
await this.driver.query(this.connection, healthQuery.sql, healthQuery.params);
|
|
246
|
+
const duration = Date.now() - startTime;
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
status: 'healthy',
|
|
250
|
+
type: this.config.type,
|
|
251
|
+
duration: `${duration}ms`,
|
|
252
|
+
timestamp: new Date().toISOString()
|
|
253
|
+
};
|
|
254
|
+
} catch (error) {
|
|
255
|
+
return {
|
|
256
|
+
status: 'unhealthy',
|
|
257
|
+
type: this.config.type,
|
|
258
|
+
error: error.message,
|
|
259
|
+
timestamp: new Date().toISOString()
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get health check query based on database type
|
|
266
|
+
* @returns {Object} Health check query
|
|
267
|
+
*/
|
|
268
|
+
getHealthQuery() {
|
|
269
|
+
const queries = {
|
|
270
|
+
mysql: { sql: 'SELECT 1', params: [] },
|
|
271
|
+
postgresql: { sql: 'SELECT 1', params: [] },
|
|
272
|
+
sqlite: { sql: 'SELECT 1', params: [] },
|
|
273
|
+
mongodb: { sql: 'db.stats()', params: [] },
|
|
274
|
+
redis: { sql: 'PING', params: [] },
|
|
275
|
+
mssql: { sql: 'SELECT 1', params: [] },
|
|
276
|
+
oracle: { sql: 'SELECT 1 FROM DUAL', params: [] }
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
return queries[this.config.type] || { sql: 'SELECT 1', params: [] };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Get connection pool stats (if using pooling)
|
|
284
|
+
* @returns {Object|null} Pool statistics
|
|
285
|
+
*/
|
|
286
|
+
getPoolStats() {
|
|
287
|
+
if (!this.pool || !this.driver.getPoolStats) {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return this.driver.getPoolStats(this.pool);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Reconnect to database
|
|
296
|
+
* @returns {Promise<ConnectionManager>} Reconnected instance
|
|
297
|
+
*/
|
|
298
|
+
async reconnect() {
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
await this.close();
|
|
302
|
+
await this.connect();
|
|
303
|
+
return this;
|
|
304
|
+
} catch (error) {
|
|
305
|
+
this.emit('reconnect:error', { error });
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Ping database connection
|
|
312
|
+
* @returns {Promise<boolean>} True if ping successful
|
|
313
|
+
*/
|
|
314
|
+
async ping() {
|
|
315
|
+
if (!this.isConnected) {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
try {
|
|
320
|
+
const health = await this.healthCheck();
|
|
321
|
+
return health.status === 'healthy';
|
|
322
|
+
} catch (error) {
|
|
323
|
+
return false;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
getDriver() {
|
|
327
|
+
return this.driver;
|
|
328
|
+
}
|
|
329
|
+
getType() {
|
|
330
|
+
return this.type;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Get connection metrics
|
|
334
|
+
* @returns {Object} Connection metrics
|
|
335
|
+
*/
|
|
336
|
+
getMetrics() {
|
|
337
|
+
return {
|
|
338
|
+
type: this.config.type,
|
|
339
|
+
isConnected: this.isConnected,
|
|
340
|
+
retryCount: this.retryCount,
|
|
341
|
+
connectionTime: this.connectionTime,
|
|
342
|
+
lastQueryTime: this.lastQueryTime,
|
|
343
|
+
totalQueries: this.totalQueries || 0,
|
|
344
|
+
failedQueries: this.failedQueries || 0,
|
|
345
|
+
poolStats: this.getPoolStats(),
|
|
346
|
+
timestamp: new Date().toISOString()
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
export default ConnectionManager;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license MIT
|
|
3
|
+
* Copyright (c) 2026-present AetherFramework Contributors.
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
* @module @aetherframework/database/core/DatabaseFactory
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import mysqlDriver from '../drivers/mysql-driver.js';
|
|
9
|
+
import postgresDriver from '../drivers/postgres-driver.js';
|
|
10
|
+
import sqliteDriver from '../drivers/sqlite-driver.js';
|
|
11
|
+
import mongodbDriver from '../drivers/mongodb-driver.js';
|
|
12
|
+
import redisDriver from '../drivers/redis-driver.js';
|
|
13
|
+
import mssqlDriver from '../drivers/mssql-driver.js';
|
|
14
|
+
import oracleDriver from '../drivers/oracle-driver.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Database Factory - Core factory for creating database driver instances
|
|
18
|
+
* Supports both built-in and external drivers with plugin architecture
|
|
19
|
+
*/
|
|
20
|
+
class DatabaseFactory {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.drivers = new Map();
|
|
23
|
+
this.externalDrivers = new Map();
|
|
24
|
+
this.initializeBuiltInDrivers();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Initialize built-in database drivers
|
|
29
|
+
*/
|
|
30
|
+
initializeBuiltInDrivers() {
|
|
31
|
+
const builtInDrivers = {
|
|
32
|
+
'mysql': mysqlDriver,
|
|
33
|
+
'mariadb': mysqlDriver,
|
|
34
|
+
'postgresql': postgresDriver,
|
|
35
|
+
'postgres': postgresDriver,
|
|
36
|
+
'pg': postgresDriver,
|
|
37
|
+
'sqlite': sqliteDriver,
|
|
38
|
+
'sqlite3': sqliteDriver,
|
|
39
|
+
'mongodb': mongodbDriver,
|
|
40
|
+
'mongo': mongodbDriver,
|
|
41
|
+
'redis': redisDriver,
|
|
42
|
+
'mssql': mssqlDriver,
|
|
43
|
+
'sqlserver': mssqlDriver,
|
|
44
|
+
'oracle': oracleDriver
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
for (const [name, DriverClass] of Object.entries(builtInDrivers)) {
|
|
48
|
+
this.registerDriver(name, DriverClass);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Register a new driver
|
|
54
|
+
* @param {string} name - Driver name
|
|
55
|
+
* @param {Class} DriverClass - Driver class
|
|
56
|
+
*/
|
|
57
|
+
registerDriver(name, DriverClass) {
|
|
58
|
+
this.drivers.set(name.toLowerCase(), DriverClass);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Register external driver
|
|
63
|
+
* @param {string} name - Driver name
|
|
64
|
+
* @param {Class} DriverClass - Driver class
|
|
65
|
+
*/
|
|
66
|
+
registerExternalDriver(name, DriverClass) {
|
|
67
|
+
this.externalDrivers.set(name.toLowerCase(), DriverClass);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Load driver dynamically
|
|
72
|
+
* @param {string} driverName - Driver name
|
|
73
|
+
* @returns {Promise<Class>} Driver class
|
|
74
|
+
*/
|
|
75
|
+
async loadDriver(driverName) {
|
|
76
|
+
const normalizedName = driverName.toLowerCase();
|
|
77
|
+
|
|
78
|
+
// Check if driver is already loaded
|
|
79
|
+
if (this.drivers.has(normalizedName)) {
|
|
80
|
+
return this.drivers.get(normalizedName);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check if it's an external driver
|
|
84
|
+
if (this.externalDrivers.has(normalizedName)) {
|
|
85
|
+
return this.externalDrivers.get(normalizedName);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Try to load from external drivers directory
|
|
89
|
+
try {
|
|
90
|
+
const driverModule = await import(`../drivers/${normalizedName}-driver.js`);
|
|
91
|
+
if (driverModule.default && typeof driverModule.default === 'function') {
|
|
92
|
+
this.registerDriver(normalizedName, driverModule.default);
|
|
93
|
+
return driverModule.default;
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
// Driver not found in drivers directory
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
throw new Error(`Unsupported database driver: ${driverName}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Create driver instance
|
|
104
|
+
* @param {string} driverName - Driver name
|
|
105
|
+
* @param {Object} config - Driver configuration
|
|
106
|
+
* @returns {Object} Driver instance
|
|
107
|
+
*/
|
|
108
|
+
createDriver(driverName, config) {
|
|
109
|
+
const normalizedName = driverName.toLowerCase();
|
|
110
|
+
|
|
111
|
+
// Check built-in drivers first
|
|
112
|
+
if (this.drivers.has(normalizedName)) {
|
|
113
|
+
const DriverClass = this.drivers.get(normalizedName);
|
|
114
|
+
return new DriverClass(config);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check external drivers
|
|
118
|
+
if (this.externalDrivers.has(normalizedName)) {
|
|
119
|
+
const DriverClass = this.externalDrivers.get(normalizedName);
|
|
120
|
+
return new DriverClass(config);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
throw new Error(`Driver not found: ${driverName}`);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Get available driver names
|
|
128
|
+
* @returns {Array} List of available driver names
|
|
129
|
+
*/
|
|
130
|
+
getAvailableDrivers() {
|
|
131
|
+
const builtInDrivers = Array.from(this.drivers.keys());
|
|
132
|
+
const externalDrivers = Array.from(this.externalDrivers.keys());
|
|
133
|
+
return [...builtInDrivers, ...externalDrivers];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Check if driver exists
|
|
138
|
+
* @param {string} driverName - Driver name
|
|
139
|
+
* @returns {boolean} True if driver exists
|
|
140
|
+
*/
|
|
141
|
+
hasDriver(driverName) {
|
|
142
|
+
const normalizedName = driverName.toLowerCase();
|
|
143
|
+
return this.drivers.has(normalizedName) || this.externalDrivers.has(normalizedName);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Remove driver
|
|
148
|
+
* @param {string} driverName - Driver name
|
|
149
|
+
*/
|
|
150
|
+
removeDriver(driverName) {
|
|
151
|
+
const normalizedName = driverName.toLowerCase();
|
|
152
|
+
if (this.drivers.has(normalizedName)) {
|
|
153
|
+
this.drivers.delete(normalizedName);
|
|
154
|
+
}
|
|
155
|
+
if (this.externalDrivers.has(normalizedName)) {
|
|
156
|
+
this.externalDrivers.delete(normalizedName);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Clear all drivers
|
|
162
|
+
*/
|
|
163
|
+
clearDrivers() {
|
|
164
|
+
this.drivers.clear();
|
|
165
|
+
this.externalDrivers.clear();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Get driver info
|
|
170
|
+
* @param {string} driverName - Driver name
|
|
171
|
+
* @returns {Object} Driver information
|
|
172
|
+
*/
|
|
173
|
+
getDriverInfo(driverName) {
|
|
174
|
+
const normalizedName = driverName.toLowerCase();
|
|
175
|
+
const isBuiltIn = this.drivers.has(normalizedName);
|
|
176
|
+
const isExternal = this.externalDrivers.has(normalizedName);
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
name: driverName,
|
|
180
|
+
isBuiltIn,
|
|
181
|
+
isExternal,
|
|
182
|
+
exists: isBuiltIn || isExternal,
|
|
183
|
+
type: isBuiltIn ? 'built-in' : isExternal ? 'external' : 'not-found'
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export default DatabaseFactory;
|