noormme 1.0.0 → 1.0.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.
Files changed (137) hide show
  1. package/LICENSE +89 -21
  2. package/README.md +195 -578
  3. package/dist/cjs/cli/commands/analyze.js +4 -1
  4. package/dist/cjs/cli/commands/generate.js +48 -8
  5. package/dist/cjs/cli/commands/init.js +54 -14
  6. package/dist/cjs/cli/commands/inspect.js +10 -3
  7. package/dist/cjs/cli/commands/migrate.js +38 -2
  8. package/dist/cjs/cli/commands/optimize.js +4 -1
  9. package/dist/cjs/cli/commands/status.js +41 -5
  10. package/dist/cjs/cli/commands/watch.js +4 -1
  11. package/dist/cjs/cli/index.js +4 -1
  12. package/dist/cjs/dialect/database-introspector.js +16 -21
  13. package/dist/cjs/dialect/sqlite/sqlite-introspector.js +13 -24
  14. package/dist/cjs/dynamic/dynamic.d.ts +20 -0
  15. package/dist/cjs/dynamic/dynamic.js +25 -0
  16. package/dist/cjs/edge-runtime/edge-config.d.ts +125 -0
  17. package/dist/cjs/edge-runtime/edge-config.js +323 -0
  18. package/dist/cjs/errors/NoormError.d.ts +27 -4
  19. package/dist/cjs/errors/NoormError.js +142 -21
  20. package/dist/cjs/logging/logger.d.ts +7 -2
  21. package/dist/cjs/logging/logger.js +21 -7
  22. package/dist/cjs/noormme.d.ts +12 -8
  23. package/dist/cjs/noormme.js +133 -61
  24. package/dist/cjs/operation-node/column-node.js +4 -0
  25. package/dist/cjs/operation-node/identifier-node.js +4 -0
  26. package/dist/cjs/operation-node/table-node.js +7 -0
  27. package/dist/cjs/parser/reference-parser.js +5 -0
  28. package/dist/cjs/parser/table-parser.js +2 -0
  29. package/dist/cjs/performance/index.d.ts +44 -0
  30. package/dist/cjs/performance/index.js +64 -0
  31. package/dist/cjs/performance/query-optimizer.d.ts +134 -0
  32. package/dist/cjs/performance/query-optimizer.js +391 -0
  33. package/dist/cjs/performance/services/cache-service.d.ts +177 -0
  34. package/dist/cjs/performance/services/cache-service.js +415 -0
  35. package/dist/cjs/performance/services/connection-factory.d.ts +198 -0
  36. package/dist/cjs/performance/services/connection-factory.js +498 -0
  37. package/dist/cjs/performance/services/metrics-collector.d.ts +162 -0
  38. package/dist/cjs/performance/services/metrics-collector.js +406 -0
  39. package/dist/cjs/performance/utils/query-parser.d.ts +123 -0
  40. package/dist/cjs/performance/utils/query-parser.js +295 -0
  41. package/dist/cjs/raw-builder/sql.d.ts +73 -26
  42. package/dist/cjs/raw-builder/sql.js +9 -0
  43. package/dist/cjs/repository/repository-factory.d.ts +10 -42
  44. package/dist/cjs/repository/repository-factory.js +276 -394
  45. package/dist/cjs/schema/core/coordinators/schema-discovery.coordinator.js +6 -4
  46. package/dist/cjs/schema/core/factories/discovery-factory.js +5 -5
  47. package/dist/cjs/schema/core/utils/name-generator.js +34 -2
  48. package/dist/cjs/schema/core/utils/type-mapper.d.ts +19 -14
  49. package/dist/cjs/schema/core/utils/type-mapper.js +4 -7
  50. package/dist/cjs/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +3 -2
  51. package/dist/cjs/schema/dialects/sqlite/sqlite-discovery.coordinator.d.ts +2 -0
  52. package/dist/cjs/schema/dialects/sqlite/sqlite-discovery.coordinator.js +19 -5
  53. package/dist/cjs/schema/test/dialect-capabilities.test.js +6 -0
  54. package/dist/cjs/schema/test/error-handling.test.js +52 -33
  55. package/dist/cjs/schema/test/integration.test.js +51 -5
  56. package/dist/cjs/schema/test/sqlite-discovery-coordinator.test.js +8 -4
  57. package/dist/cjs/sqlite-migration/index.js +35 -2
  58. package/dist/cjs/testing/test-utils.d.ts +5 -0
  59. package/dist/cjs/testing/test-utils.js +66 -6
  60. package/dist/cjs/types/index.d.ts +78 -13
  61. package/dist/cjs/types/index.js +23 -0
  62. package/dist/cjs/types/type-generator.d.ts +8 -0
  63. package/dist/cjs/types/type-generator.js +86 -17
  64. package/dist/cjs/util/safe-sql-helpers.d.ts +124 -0
  65. package/dist/cjs/util/safe-sql-helpers.js +221 -0
  66. package/dist/cjs/util/security-validator.d.ts +44 -0
  67. package/dist/cjs/util/security-validator.js +256 -0
  68. package/dist/cjs/util/security.d.ts +60 -0
  69. package/dist/cjs/util/security.js +137 -0
  70. package/dist/cjs/watch/schema-watcher.js +26 -7
  71. package/dist/esm/cli/commands/generate.js +10 -6
  72. package/dist/esm/cli/commands/init.js +15 -11
  73. package/dist/esm/cli/commands/inspect.js +6 -2
  74. package/dist/esm/cli/commands/status.js +3 -3
  75. package/dist/esm/dialect/database-introspector.js +16 -21
  76. package/dist/esm/dialect/sqlite/sqlite-introspector.js +13 -24
  77. package/dist/esm/dynamic/dynamic.d.ts +20 -0
  78. package/dist/esm/dynamic/dynamic.js +25 -0
  79. package/dist/esm/edge-runtime/edge-config.d.ts +125 -0
  80. package/dist/esm/edge-runtime/edge-config.js +281 -0
  81. package/dist/esm/errors/NoormError.d.ts +27 -4
  82. package/dist/esm/errors/NoormError.js +134 -18
  83. package/dist/esm/logging/logger.d.ts +7 -2
  84. package/dist/esm/logging/logger.js +21 -7
  85. package/dist/esm/noormme.d.ts +12 -8
  86. package/dist/esm/noormme.js +130 -61
  87. package/dist/esm/operation-node/column-node.js +4 -0
  88. package/dist/esm/operation-node/identifier-node.js +4 -0
  89. package/dist/esm/operation-node/table-node.js +7 -0
  90. package/dist/esm/parser/reference-parser.js +5 -0
  91. package/dist/esm/parser/table-parser.js +2 -0
  92. package/dist/esm/performance/index.d.ts +44 -0
  93. package/dist/esm/performance/index.js +48 -0
  94. package/dist/esm/performance/query-optimizer.d.ts +134 -0
  95. package/dist/esm/performance/query-optimizer.js +387 -0
  96. package/dist/esm/performance/services/cache-service.d.ts +177 -0
  97. package/dist/esm/performance/services/cache-service.js +410 -0
  98. package/dist/esm/performance/services/connection-factory.d.ts +198 -0
  99. package/dist/esm/performance/services/connection-factory.js +493 -0
  100. package/dist/esm/performance/services/metrics-collector.d.ts +162 -0
  101. package/dist/esm/performance/services/metrics-collector.js +402 -0
  102. package/dist/esm/performance/utils/query-parser.d.ts +123 -0
  103. package/dist/esm/performance/utils/query-parser.js +292 -0
  104. package/dist/esm/raw-builder/sql.d.ts +73 -26
  105. package/dist/esm/raw-builder/sql.js +9 -0
  106. package/dist/esm/repository/repository-factory.d.ts +10 -42
  107. package/dist/esm/repository/repository-factory.js +277 -395
  108. package/dist/esm/schema/core/coordinators/schema-discovery.coordinator.js +6 -4
  109. package/dist/esm/schema/core/factories/discovery-factory.js +5 -5
  110. package/dist/esm/schema/core/utils/name-generator.js +34 -2
  111. package/dist/esm/schema/core/utils/type-mapper.d.ts +19 -14
  112. package/dist/esm/schema/core/utils/type-mapper.js +4 -7
  113. package/dist/esm/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +3 -2
  114. package/dist/esm/schema/dialects/sqlite/sqlite-discovery.coordinator.d.ts +2 -0
  115. package/dist/esm/schema/dialects/sqlite/sqlite-discovery.coordinator.js +19 -5
  116. package/dist/esm/schema/test/dialect-capabilities.test.js +6 -0
  117. package/dist/esm/schema/test/error-handling.test.js +52 -33
  118. package/dist/esm/schema/test/integration.test.js +18 -5
  119. package/dist/esm/schema/test/sqlite-discovery-coordinator.test.js +8 -4
  120. package/dist/esm/testing/test-utils.d.ts +5 -0
  121. package/dist/esm/testing/test-utils.js +66 -6
  122. package/dist/esm/types/index.d.ts +78 -13
  123. package/dist/esm/types/index.js +22 -1
  124. package/dist/esm/types/type-generator.d.ts +8 -0
  125. package/dist/esm/types/type-generator.js +86 -17
  126. package/dist/esm/util/safe-sql-helpers.d.ts +124 -0
  127. package/dist/esm/util/safe-sql-helpers.js +209 -0
  128. package/dist/esm/util/security-validator.d.ts +44 -0
  129. package/dist/esm/util/security-validator.js +246 -0
  130. package/dist/esm/util/security.d.ts +60 -0
  131. package/dist/esm/util/security.js +114 -0
  132. package/dist/esm/watch/schema-watcher.js +26 -7
  133. package/package.json +1 -1
  134. package/dist/cjs/performance/query-analyzer.d.ts +0 -89
  135. package/dist/cjs/performance/query-analyzer.js +0 -263
  136. package/dist/esm/performance/query-analyzer.d.ts +0 -89
  137. package/dist/esm/performance/query-analyzer.js +0 -260
@@ -0,0 +1,498 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionPoolManager = exports.ConnectionFactory = void 0;
4
+ exports.createConnectionPool = createConnectionPool;
5
+ const noormme_1 = require("../../noormme");
6
+ const logger_js_1 = require("../../logging/logger.js");
7
+ /**
8
+ * Factory for creating and managing database connections
9
+ */
10
+ class ConnectionFactory {
11
+ connections = new Map();
12
+ stats;
13
+ logger;
14
+ constructor(logger) {
15
+ this.logger = logger || new logger_js_1.Logger('ConnectionFactory');
16
+ this.stats = {
17
+ totalCreated: 0,
18
+ totalDestroyed: 0,
19
+ activeConnections: 0,
20
+ failedCreations: 0,
21
+ averageCreationTime: 0,
22
+ averageValidationTime: 0
23
+ };
24
+ }
25
+ /**
26
+ * Create a new database connection
27
+ */
28
+ async createConnection(config) {
29
+ const startTime = performance.now();
30
+ const id = config.id || this.generateConnectionId();
31
+ try {
32
+ this.logger.debug(`Creating connection ${id}`);
33
+ const db = new noormme_1.NOORMME(config);
34
+ await db.initialize();
35
+ const connection = {
36
+ id,
37
+ db,
38
+ createdAt: new Date(),
39
+ lastUsed: new Date(),
40
+ isActive: true,
41
+ inUse: false,
42
+ config
43
+ };
44
+ this.connections.set(id, connection);
45
+ this.updateStats('created', performance.now() - startTime);
46
+ this.logger.debug(`Created connection ${id} successfully`);
47
+ return connection;
48
+ }
49
+ catch (error) {
50
+ this.updateStats('failed', performance.now() - startTime);
51
+ this.logger.error(`Failed to create connection ${id}:`, error);
52
+ throw error;
53
+ }
54
+ }
55
+ /**
56
+ * Validate a connection
57
+ */
58
+ async validateConnection(connection) {
59
+ const startTime = performance.now();
60
+ try {
61
+ await connection.db.execute('SELECT 1');
62
+ const responseTime = performance.now() - startTime;
63
+ this.updateStats('validated', responseTime);
64
+ return {
65
+ isValid: true,
66
+ responseTime
67
+ };
68
+ }
69
+ catch (error) {
70
+ const responseTime = performance.now() - startTime;
71
+ this.updateStats('validated', responseTime);
72
+ const errorMessage = error instanceof Error ? error.message : String(error);
73
+ return {
74
+ isValid: false,
75
+ error: errorMessage,
76
+ responseTime
77
+ };
78
+ }
79
+ }
80
+ /**
81
+ * Get connection by ID
82
+ */
83
+ getConnection(id) {
84
+ return this.connections.get(id) || null;
85
+ }
86
+ /**
87
+ * Get all connections
88
+ */
89
+ getAllConnections() {
90
+ return Array.from(this.connections.values());
91
+ }
92
+ /**
93
+ * Get active connections
94
+ */
95
+ getActiveConnections() {
96
+ return Array.from(this.connections.values()).filter(conn => conn.isActive);
97
+ }
98
+ /**
99
+ * Get idle connections
100
+ */
101
+ getIdleConnections() {
102
+ return Array.from(this.connections.values()).filter(conn => conn.isActive && !conn.inUse);
103
+ }
104
+ /**
105
+ * Mark connection as in use
106
+ */
107
+ markInUse(connection) {
108
+ connection.inUse = true;
109
+ connection.lastUsed = new Date();
110
+ }
111
+ /**
112
+ * Mark connection as idle
113
+ */
114
+ markIdle(connection) {
115
+ connection.inUse = false;
116
+ connection.lastUsed = new Date();
117
+ }
118
+ /**
119
+ * Destroy a connection
120
+ */
121
+ async destroyConnection(id) {
122
+ const connection = this.connections.get(id);
123
+ if (!connection) {
124
+ return false;
125
+ }
126
+ try {
127
+ await connection.db.destroy();
128
+ connection.isActive = false;
129
+ this.connections.delete(id);
130
+ this.updateStats('destroyed', 0);
131
+ this.logger.debug(`Destroyed connection ${id}`);
132
+ return true;
133
+ }
134
+ catch (error) {
135
+ this.logger.error(`Failed to destroy connection ${id}:`, error);
136
+ return false;
137
+ }
138
+ }
139
+ /**
140
+ * Destroy all connections
141
+ */
142
+ async destroyAllConnections() {
143
+ const destroyPromises = Array.from(this.connections.keys()).map(id => this.destroyConnection(id));
144
+ await Promise.all(destroyPromises);
145
+ this.logger.info('Destroyed all connections');
146
+ }
147
+ /**
148
+ * Cleanup inactive connections
149
+ */
150
+ async cleanupInactiveConnections(maxAge = 300000) {
151
+ const now = Date.now();
152
+ let cleaned = 0;
153
+ for (const [id, connection] of this.connections.entries()) {
154
+ if (!connection.inUse &&
155
+ now - connection.lastUsed.getTime() > maxAge) {
156
+ if (await this.destroyConnection(id)) {
157
+ cleaned++;
158
+ }
159
+ }
160
+ }
161
+ if (cleaned > 0) {
162
+ this.logger.info(`Cleaned up ${cleaned} inactive connections`);
163
+ }
164
+ return cleaned;
165
+ }
166
+ /**
167
+ * Validate all connections
168
+ */
169
+ async validateAllConnections() {
170
+ const validationPromises = Array.from(this.connections.values()).map(async (connection) => {
171
+ const result = await this.validateConnection(connection);
172
+ return {
173
+ id: connection.id,
174
+ isValid: result.isValid,
175
+ error: result.error,
176
+ responseTime: result.responseTime
177
+ };
178
+ });
179
+ return Promise.all(validationPromises);
180
+ }
181
+ /**
182
+ * Get connection statistics
183
+ */
184
+ getStats() {
185
+ return { ...this.stats };
186
+ }
187
+ /**
188
+ * Get connection health status
189
+ */
190
+ async getHealthStatus() {
191
+ const connections = this.getAllConnections();
192
+ const activeConnections = this.getActiveConnections();
193
+ const idleConnections = this.getIdleConnections();
194
+ const inUseConnections = connections.filter(c => c.inUse);
195
+ const issues = [];
196
+ // Check for failed creations
197
+ if (this.stats.failedCreations > 0) {
198
+ issues.push(`${this.stats.failedCreations} connection creation failures`);
199
+ }
200
+ // Check for slow validation times
201
+ if (this.stats.averageValidationTime > 1000) {
202
+ issues.push(`Slow connection validation: ${this.stats.averageValidationTime.toFixed(2)}ms average`);
203
+ }
204
+ // Check for high connection count
205
+ if (connections.length > 50) {
206
+ issues.push(`High connection count: ${connections.length}`);
207
+ }
208
+ let status = 'healthy';
209
+ if (issues.length > 0) {
210
+ status = issues.some(issue => issue.includes('failures')) ? 'critical' : 'warning';
211
+ }
212
+ return {
213
+ status,
214
+ issues,
215
+ connections: {
216
+ total: connections.length,
217
+ active: activeConnections.length,
218
+ idle: idleConnections.length,
219
+ inUse: inUseConnections.length
220
+ }
221
+ };
222
+ }
223
+ /**
224
+ * Generate unique connection ID
225
+ */
226
+ generateConnectionId() {
227
+ return `conn_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
228
+ }
229
+ /**
230
+ * Update statistics
231
+ */
232
+ updateStats(operation, time) {
233
+ switch (operation) {
234
+ case 'created':
235
+ this.stats.totalCreated++;
236
+ this.stats.activeConnections++;
237
+ this.updateAverageTime('creation', time);
238
+ break;
239
+ case 'destroyed':
240
+ this.stats.totalDestroyed++;
241
+ this.stats.activeConnections = Math.max(0, this.stats.activeConnections - 1);
242
+ break;
243
+ case 'failed':
244
+ this.stats.failedCreations++;
245
+ break;
246
+ case 'validated':
247
+ this.updateAverageTime('validation', time);
248
+ break;
249
+ }
250
+ }
251
+ /**
252
+ * Update average time for operations
253
+ */
254
+ updateAverageTime(operation, time) {
255
+ if (operation === 'creation') {
256
+ const total = this.stats.totalCreated;
257
+ this.stats.averageCreationTime =
258
+ (this.stats.averageCreationTime * (total - 1) + time) / total;
259
+ }
260
+ else if (operation === 'validation') {
261
+ // This is a simplified approach - in practice you'd track validation count separately
262
+ this.stats.averageValidationTime =
263
+ (this.stats.averageValidationTime + time) / 2;
264
+ }
265
+ }
266
+ }
267
+ exports.ConnectionFactory = ConnectionFactory;
268
+ /**
269
+ * Connection pool manager using the factory
270
+ */
271
+ class ConnectionPoolManager {
272
+ factory;
273
+ config;
274
+ poolConfig;
275
+ waitingQueue = [];
276
+ validationTimer;
277
+ isShuttingDown = false;
278
+ logger;
279
+ constructor(config, poolConfig = {}, logger) {
280
+ this.config = config;
281
+ this.logger = logger || new logger_js_1.Logger('ConnectionPoolManager');
282
+ this.factory = new ConnectionFactory(this.logger);
283
+ this.poolConfig = {
284
+ minConnections: 2,
285
+ maxConnections: 10,
286
+ acquireTimeout: 30000,
287
+ idleTimeout: 300000,
288
+ validationInterval: 60000,
289
+ ...poolConfig
290
+ };
291
+ this.startValidationTimer();
292
+ }
293
+ /**
294
+ * Initialize the connection pool
295
+ */
296
+ async initialize() {
297
+ this.logger.info(`Initializing connection pool with ${this.poolConfig.minConnections} minimum connections`);
298
+ const initPromises = Array.from({ length: this.poolConfig.minConnections }, (_, i) => this.factory.createConnection({
299
+ ...this.config,
300
+ id: `pool_conn_${i}`
301
+ }));
302
+ try {
303
+ await Promise.all(initPromises);
304
+ this.logger.info(`Connection pool initialized with ${this.factory.getAllConnections().length} connections`);
305
+ }
306
+ catch (error) {
307
+ this.logger.error('Failed to initialize connection pool:', error);
308
+ throw error;
309
+ }
310
+ }
311
+ /**
312
+ * Acquire a connection from the pool
313
+ */
314
+ async acquire() {
315
+ if (this.isShuttingDown) {
316
+ throw new Error('Connection pool is shutting down');
317
+ }
318
+ const startTime = performance.now();
319
+ try {
320
+ // Try to get an idle connection
321
+ const idleConnection = this.getIdleConnection();
322
+ if (idleConnection) {
323
+ this.factory.markInUse(idleConnection);
324
+ this.logger.debug(`Acquired connection ${idleConnection.id} from pool`);
325
+ return idleConnection;
326
+ }
327
+ // Try to create a new connection if under max limit
328
+ const currentConnections = this.factory.getAllConnections();
329
+ if (currentConnections.length < this.poolConfig.maxConnections) {
330
+ const newConnection = await this.factory.createConnection({
331
+ ...this.config,
332
+ id: `pool_conn_${Date.now()}`
333
+ });
334
+ this.factory.markInUse(newConnection);
335
+ this.logger.debug(`Created and acquired new connection ${newConnection.id}`);
336
+ return newConnection;
337
+ }
338
+ // Wait for a connection to become available
339
+ return this.waitForConnection(startTime);
340
+ }
341
+ catch (error) {
342
+ this.logger.error('Failed to acquire connection:', error);
343
+ throw error;
344
+ }
345
+ }
346
+ /**
347
+ * Release a connection back to the pool
348
+ */
349
+ async release(connection) {
350
+ try {
351
+ // Validate connection before returning to pool
352
+ const validation = await this.factory.validateConnection(connection);
353
+ if (!validation.isValid) {
354
+ this.logger.warn(`Connection ${connection.id} failed validation, removing from pool`);
355
+ await this.factory.destroyConnection(connection.id);
356
+ return;
357
+ }
358
+ this.factory.markIdle(connection);
359
+ this.processWaitingQueue();
360
+ this.logger.debug(`Released connection ${connection.id} back to pool`);
361
+ }
362
+ catch (error) {
363
+ this.logger.error(`Failed to release connection ${connection.id}:`, error);
364
+ }
365
+ }
366
+ /**
367
+ * Execute a function with a pooled connection
368
+ */
369
+ async withConnection(fn) {
370
+ const connection = await this.acquire();
371
+ try {
372
+ return await fn(connection.db);
373
+ }
374
+ finally {
375
+ await this.release(connection);
376
+ }
377
+ }
378
+ /**
379
+ * Get pool statistics
380
+ */
381
+ getStats() {
382
+ const factoryStats = this.factory.getStats();
383
+ const connections = this.factory.getAllConnections();
384
+ return {
385
+ ...factoryStats,
386
+ poolSize: connections.length,
387
+ idleConnections: this.factory.getIdleConnections().length,
388
+ inUseConnections: connections.filter(c => c.inUse).length,
389
+ waitingQueue: this.waitingQueue.length
390
+ };
391
+ }
392
+ /**
393
+ * Shutdown the connection pool
394
+ */
395
+ async shutdown() {
396
+ this.logger.info('Shutting down connection pool...');
397
+ this.isShuttingDown = true;
398
+ // Stop validation timer
399
+ if (this.validationTimer) {
400
+ clearInterval(this.validationTimer);
401
+ }
402
+ // Reject all waiting requests
403
+ this.waitingQueue.forEach(({ reject }) => {
404
+ reject(new Error('Connection pool is shutting down'));
405
+ });
406
+ this.waitingQueue = [];
407
+ // Destroy all connections
408
+ await this.factory.destroyAllConnections();
409
+ this.logger.info('Connection pool shutdown complete');
410
+ }
411
+ /**
412
+ * Get idle connection from pool
413
+ */
414
+ getIdleConnection() {
415
+ const idleConnections = this.factory.getIdleConnections();
416
+ return idleConnections.length > 0 ? idleConnections[0] : null;
417
+ }
418
+ /**
419
+ * Wait for a connection to become available
420
+ */
421
+ async waitForConnection(startTime) {
422
+ return new Promise((resolve, reject) => {
423
+ const timeout = setTimeout(() => {
424
+ const index = this.waitingQueue.findIndex(item => item.reject === reject);
425
+ if (index !== -1) {
426
+ this.waitingQueue.splice(index, 1);
427
+ }
428
+ reject(new Error(`Connection acquisition timeout after ${this.poolConfig.acquireTimeout}ms`));
429
+ }, this.poolConfig.acquireTimeout);
430
+ this.waitingQueue.push({
431
+ resolve: (connection) => {
432
+ clearTimeout(timeout);
433
+ resolve(connection);
434
+ },
435
+ reject: (error) => {
436
+ clearTimeout(timeout);
437
+ reject(error);
438
+ },
439
+ timestamp: Date.now()
440
+ });
441
+ });
442
+ }
443
+ /**
444
+ * Process waiting queue when connections become available
445
+ */
446
+ processWaitingQueue() {
447
+ while (this.waitingQueue.length > 0) {
448
+ const idleConnection = this.getIdleConnection();
449
+ if (!idleConnection)
450
+ break;
451
+ const waiter = this.waitingQueue.shift();
452
+ this.factory.markInUse(idleConnection);
453
+ waiter.resolve(idleConnection);
454
+ }
455
+ }
456
+ /**
457
+ * Start validation timer
458
+ */
459
+ startValidationTimer() {
460
+ this.validationTimer = setInterval(async () => {
461
+ await this.validateConnections();
462
+ await this.cleanupIdleConnections();
463
+ }, this.poolConfig.validationInterval);
464
+ }
465
+ /**
466
+ * Validate all connections in the pool
467
+ */
468
+ async validateConnections() {
469
+ const validationPromises = this.factory.getIdleConnections().map(async (connection) => {
470
+ const validation = await this.factory.validateConnection(connection);
471
+ if (!validation.isValid) {
472
+ await this.factory.destroyConnection(connection.id);
473
+ }
474
+ });
475
+ await Promise.all(validationPromises);
476
+ }
477
+ /**
478
+ * Cleanup idle connections that exceed the idle timeout
479
+ */
480
+ async cleanupIdleConnections() {
481
+ const now = Date.now();
482
+ const idleConnections = this.factory.getIdleConnections();
483
+ const excessConnections = idleConnections.filter(conn => now - conn.lastUsed.getTime() > this.poolConfig.idleTimeout).slice(this.poolConfig.minConnections);
484
+ for (const connection of excessConnections) {
485
+ await this.factory.destroyConnection(connection.id);
486
+ }
487
+ if (excessConnections.length > 0) {
488
+ this.logger.debug(`Cleaned up ${excessConnections.length} idle connections`);
489
+ }
490
+ }
491
+ }
492
+ exports.ConnectionPoolManager = ConnectionPoolManager;
493
+ /**
494
+ * Factory function to create connection pool manager
495
+ */
496
+ function createConnectionPool(config, poolConfig, logger) {
497
+ return new ConnectionPoolManager(config, poolConfig, logger);
498
+ }
@@ -0,0 +1,162 @@
1
+ import { Logger } from '../../logging/logger.js';
2
+ export interface QueryMetrics {
3
+ query: string;
4
+ executionTime: number;
5
+ timestamp: number;
6
+ table?: string;
7
+ operation?: string;
8
+ resultCount?: number;
9
+ error?: string;
10
+ }
11
+ export interface PerformanceWarning {
12
+ type: 'n_plus_one' | 'missing_index' | 'slow_query' | 'full_table_scan' | 'large_result_set';
13
+ message: string;
14
+ suggestion: string;
15
+ query?: string;
16
+ table?: string;
17
+ severity: 'low' | 'medium' | 'high';
18
+ timestamp: number;
19
+ }
20
+ export interface MetricsConfig {
21
+ enabled: boolean;
22
+ slowQueryThreshold: number;
23
+ nPlusOneDetection: boolean;
24
+ missingIndexDetection: boolean;
25
+ largeResultSetThreshold: number;
26
+ maxHistorySize: number;
27
+ warningRetentionDays: number;
28
+ }
29
+ export interface PerformanceStats {
30
+ totalQueries: number;
31
+ averageExecutionTime: number;
32
+ slowQueries: number;
33
+ errorRate: number;
34
+ warningCount: {
35
+ [key: string]: number;
36
+ };
37
+ topSlowQueries: Array<{
38
+ query: string;
39
+ averageTime: number;
40
+ count: number;
41
+ }>;
42
+ queryPatterns: Array<{
43
+ pattern: string;
44
+ frequency: number;
45
+ averageTime: number;
46
+ }>;
47
+ }
48
+ /**
49
+ * Service for collecting and analyzing performance metrics
50
+ */
51
+ export declare class MetricsCollector {
52
+ private queryHistory;
53
+ private warnings;
54
+ private recentQueries;
55
+ private config;
56
+ private logger;
57
+ private cleanupTimer?;
58
+ constructor(config?: Partial<MetricsConfig>, logger?: Logger);
59
+ /**
60
+ * Record a query execution
61
+ */
62
+ recordQuery(query: string, executionTime: number, options?: {
63
+ table?: string;
64
+ operation?: string;
65
+ resultCount?: number;
66
+ error?: string;
67
+ }): void;
68
+ /**
69
+ * Get performance statistics
70
+ */
71
+ getPerformanceStats(): PerformanceStats;
72
+ /**
73
+ * Get recent warnings
74
+ */
75
+ getRecentWarnings(limit?: number): PerformanceWarning[];
76
+ /**
77
+ * Get warnings by type
78
+ */
79
+ getWarningsByType(type: PerformanceWarning['type']): PerformanceWarning[];
80
+ /**
81
+ * Get query history
82
+ */
83
+ getQueryHistory(limit?: number): QueryMetrics[];
84
+ /**
85
+ * Get slow queries
86
+ */
87
+ getSlowQueries(threshold?: number): QueryMetrics[];
88
+ /**
89
+ * Get N+1 query patterns
90
+ */
91
+ getNPlusOnePatterns(): Array<{
92
+ pattern: string;
93
+ count: number;
94
+ timeWindow: number;
95
+ severity: 'low' | 'medium' | 'high';
96
+ }>;
97
+ /**
98
+ * Clear all metrics
99
+ */
100
+ clear(): void;
101
+ /**
102
+ * Export metrics data
103
+ */
104
+ exportMetrics(): {
105
+ config: MetricsConfig;
106
+ stats: PerformanceStats;
107
+ warnings: PerformanceWarning[];
108
+ queries: QueryMetrics[];
109
+ };
110
+ /**
111
+ * Shutdown metrics collector
112
+ */
113
+ shutdown(): void;
114
+ /**
115
+ * Track recent query for pattern detection
116
+ */
117
+ private trackRecentQuery;
118
+ /**
119
+ * Analyze query for performance issues
120
+ */
121
+ private analyzeQuery;
122
+ /**
123
+ * Detect N+1 query patterns
124
+ */
125
+ private detectNPlusOne;
126
+ /**
127
+ * Add warning to collection
128
+ */
129
+ private addWarning;
130
+ /**
131
+ * Log performance warning
132
+ */
133
+ private logWarning;
134
+ /**
135
+ * Get emoji for warning type
136
+ */
137
+ private getWarningEmoji;
138
+ /**
139
+ * Normalize query for comparison
140
+ */
141
+ private normalizeQuery;
142
+ /**
143
+ * Get top slow queries
144
+ */
145
+ private getTopSlowQueries;
146
+ /**
147
+ * Get query patterns
148
+ */
149
+ private getQueryPatterns;
150
+ /**
151
+ * Start cleanup timer
152
+ */
153
+ private startCleanupTimer;
154
+ /**
155
+ * Cleanup old data
156
+ */
157
+ private cleanup;
158
+ }
159
+ /**
160
+ * Factory function to create metrics collector
161
+ */
162
+ export declare function createMetricsCollector(config?: Partial<MetricsConfig>, logger?: Logger): MetricsCollector;