mysql-orm-lite 1.0.1 → 2.1.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.
@@ -0,0 +1,435 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.remove = exports.deleteWhere = exports.updateQuery = exports.updateWhere = exports.query = exports.findWhere = exports.select = exports.buildAndExecuteDeleteQuery = exports.buildAndExecuteUpdateQuery = exports.buildAndExecuteSelectQuery = exports.bulkUpsert = exports.upsert = exports.bulkInsert = exports.delete = exports._delete = exports.update = exports.insert = exports.findCount = exports.find = exports.utils = void 0;
7
+ const connectionManager_1 = __importDefault(require("./connectionManager"));
8
+ const performanceMonitor_1 = __importDefault(require("./performanceMonitor"));
9
+ exports.utils = {
10
+ handleConnectionError(err) {
11
+ const errorMessages = {
12
+ PROTOCOL_CONNECTION_LOST: 'Database connection was closed.',
13
+ ER_CON_COUNT_ERROR: 'Database has too many connections.',
14
+ ECONNREFUSED: 'Database connection was refused.',
15
+ POOL_CLOSED: 'Connection pool was closed'
16
+ };
17
+ return new Error(errorMessages[err.code] || err.message);
18
+ },
19
+ logQueryPerformance(query, startTime, params = []) {
20
+ const logger = connectionManager_1.default.getLogger();
21
+ const duration = Date.now() - startTime;
22
+ if (duration > 1000) {
23
+ logger.warn(' Slow Query ================================================================== ');
24
+ logger.warn(` Execution Time: ${duration}ms`);
25
+ logger.warn(` Query: ${query}`);
26
+ if (params.length)
27
+ logger.warn(` Parameters: ${JSON.stringify(params)}`);
28
+ logger.warn(' =========================================================================== ');
29
+ }
30
+ return duration;
31
+ },
32
+ async executeQuery({ query, params = [], dbConfig, operation = 'query' }) {
33
+ const startTime = Date.now();
34
+ const logger = connectionManager_1.default.getLogger();
35
+ try {
36
+ const pool = await connectionManager_1.default.getPool(dbConfig);
37
+ const [results] = await pool.query(query, params);
38
+ const duration = this.logQueryPerformance(query, startTime, params);
39
+ // Record in performance monitor if enabled
40
+ performanceMonitor_1.default.recordQuery(query, duration, params);
41
+ return results;
42
+ }
43
+ catch (error) {
44
+ logger.error(`${operation} Error: ${error.message}`);
45
+ logger.error(`Query: ${query}`);
46
+ if (params.length)
47
+ logger.error('Parameters:', JSON.stringify(params));
48
+ throw error;
49
+ }
50
+ },
51
+ validateUpdateParams(table, data, query) {
52
+ const errors = [];
53
+ if (!table || typeof table !== 'string')
54
+ errors.push('Invalid table name');
55
+ else if (!/^[a-zA-Z0-9_]+$/.test(table))
56
+ errors.push('Table name contains invalid characters');
57
+ if (!data || typeof data !== 'object' || Array.isArray(data))
58
+ errors.push('Data must be a non-null object');
59
+ else if (Object.keys(data).length === 0)
60
+ errors.push('Data object cannot be empty');
61
+ if (!query || typeof query !== 'string')
62
+ errors.push('Invalid WHERE clause');
63
+ else if (!query.toLowerCase().includes('where'))
64
+ errors.push('WHERE clause is required for security');
65
+ return errors;
66
+ },
67
+ prepareUpdateParams(data) {
68
+ const setFields = [];
69
+ const params = [];
70
+ for (const [key, value] of Object.entries(data)) {
71
+ if (value === undefined)
72
+ continue;
73
+ if (value && typeof value === 'object' && value.__raw) {
74
+ setFields.push(`${key} = ${value.value}`);
75
+ continue;
76
+ }
77
+ if (value === null) {
78
+ setFields.push(`${key} = NULL`);
79
+ }
80
+ else {
81
+ setFields.push(`${key} = ?`);
82
+ params.push(value);
83
+ }
84
+ }
85
+ return { setFields, params };
86
+ },
87
+ _buildWhereClause(conditions) {
88
+ const params = [];
89
+ const buildCondition = (key, value) => {
90
+ const column = key;
91
+ if (value === null)
92
+ return `${column} IS NULL`;
93
+ if (typeof value === 'object' && !Array.isArray(value)) {
94
+ return Object.entries(value).map(([op, val]) => {
95
+ switch (op) {
96
+ case '$eq':
97
+ params.push(val);
98
+ return `${column} = ?`;
99
+ case '$ne':
100
+ params.push(val);
101
+ return `${column} != ?`;
102
+ case '$gt':
103
+ params.push(val);
104
+ return `${column} > ?`;
105
+ case '$gte':
106
+ params.push(val);
107
+ return `${column} >= ?`;
108
+ case '$lt':
109
+ params.push(val);
110
+ return `${column} < ?`;
111
+ case '$lte':
112
+ params.push(val);
113
+ return `${column} <= ?`;
114
+ case '$in':
115
+ if (!val.length)
116
+ return 'FALSE';
117
+ params.push(...val);
118
+ return `${column} IN (${val.map(() => '?').join(', ')})`;
119
+ case '$notIn':
120
+ case '$nIn':
121
+ case '$nin':
122
+ if (!val.length)
123
+ return 'TRUE';
124
+ params.push(...val);
125
+ return `${column} NOT IN (${val.map(() => '?').join(', ')})`;
126
+ case '$like':
127
+ params.push(val);
128
+ return `${column} LIKE ?`;
129
+ case '$between':
130
+ if (val.length !== 2)
131
+ throw new Error('$between requires [min, max]');
132
+ params.push(val[0], val[1]);
133
+ return `${column} BETWEEN ? AND ?`;
134
+ default: throw new Error(`Unsupported operator: ${op}`);
135
+ }
136
+ }).join(' AND ');
137
+ }
138
+ params.push(value);
139
+ return `${column} = ?`;
140
+ };
141
+ const walk = (cond) => {
142
+ if (!cond || typeof cond !== 'object')
143
+ return '';
144
+ if (Array.isArray(cond))
145
+ return cond.map(walk).join(' AND ');
146
+ if ('$and' in cond)
147
+ return `(${cond.$and.map(walk).join(' AND ')})`;
148
+ if ('$or' in cond)
149
+ return `(${cond.$or.map(walk).join(' OR ')})`;
150
+ if ('$not' in cond)
151
+ return `(NOT ${walk(cond.$not)})`;
152
+ return Object.entries(cond).map(([k, v]) => buildCondition(k, v)).join(' AND ');
153
+ };
154
+ const clause = walk(conditions);
155
+ return { clause, params };
156
+ },
157
+ _buildSelectQuery(options) {
158
+ let { table, fields = '*', joins = [], where, orderBy, limit, offset, groupBy, having, alias, forUpdate = false } = options;
159
+ if (fields && Array.isArray(fields) && fields.length > 0) {
160
+ fields = fields.filter(field => field && field.trim() !== '').join(', ');
161
+ }
162
+ else if (fields && Array.isArray(fields)) {
163
+ fields = '*';
164
+ }
165
+ let query = `SELECT ${fields} FROM ${table}${alias ? ' ' + alias : ''}`;
166
+ const allParams = [];
167
+ for (const join of joins) {
168
+ let { type = 'INNER', table: joinTable, alias, on } = join;
169
+ const onClause = Array.isArray(on) ? on.join(' AND ') : on;
170
+ query += ` ${type.toUpperCase()} JOIN ${joinTable}${alias ? ' ' + alias : ''} ON ${onClause}`;
171
+ }
172
+ if (where) {
173
+ const { clause, params } = this._buildWhereClause(where);
174
+ if (clause) {
175
+ query += ` WHERE ${clause}`;
176
+ allParams.push(...params);
177
+ }
178
+ }
179
+ if (groupBy)
180
+ query += ` GROUP BY ${groupBy}`;
181
+ if (having)
182
+ query += ` HAVING ${having}`;
183
+ if (orderBy)
184
+ query += ` ORDER BY ${orderBy}`;
185
+ if (typeof limit === 'number' && limit && limit > 0) {
186
+ query += ` LIMIT ${limit}`;
187
+ if (typeof offset === 'number')
188
+ query += ` OFFSET ${offset}`;
189
+ }
190
+ if (forUpdate) {
191
+ query += ' FOR UPDATE';
192
+ }
193
+ return { query, params: allParams };
194
+ },
195
+ _buildUpdateQuery(options) {
196
+ const { table, data, where } = options;
197
+ const { setFields, params } = this.prepareUpdateParams(data);
198
+ if (setFields.length === 0)
199
+ throw new Error('No valid fields to update');
200
+ let query = `UPDATE ${table} SET ${setFields.join(', ')}`;
201
+ const allParams = [...params];
202
+ if (where) {
203
+ const { clause, params: whereParams } = this._buildWhereClause(where);
204
+ if (clause) {
205
+ query += ` WHERE ${clause}`;
206
+ allParams.push(...whereParams);
207
+ }
208
+ }
209
+ return { query, params: allParams };
210
+ },
211
+ _buildDeleteQuery(options) {
212
+ const { table, where } = options;
213
+ if (!table)
214
+ throw new Error('Table name is required for delete');
215
+ let query = `DELETE FROM ${table}`;
216
+ const allParams = [];
217
+ if (where) {
218
+ const { clause, params: whereParams } = this._buildWhereClause(where);
219
+ if (clause) {
220
+ query += ` WHERE ${clause}`;
221
+ allParams.push(...whereParams);
222
+ }
223
+ else {
224
+ throw new Error('DELETE requires a valid WHERE clause for safety');
225
+ }
226
+ }
227
+ else {
228
+ throw new Error('DELETE requires a WHERE clause for safety');
229
+ }
230
+ return { query, params: allParams };
231
+ }
232
+ };
233
+ // Public API
234
+ const find = async function (query, params = [], dbConfig) {
235
+ if (!query)
236
+ return [];
237
+ return await exports.utils.executeQuery({ query, params, dbConfig, operation: 'find' });
238
+ };
239
+ exports.find = find;
240
+ const findCount = async function (query, params = [], dbConfig) {
241
+ if (!query)
242
+ return 0;
243
+ const results = await exports.utils.executeQuery({ query, params, dbConfig, operation: 'findCount' });
244
+ return results && results[0] ? results[0].count : 0;
245
+ };
246
+ exports.findCount = findCount;
247
+ const insert = async function (table, data, dbConfig, debug = false, isIgnore = false) {
248
+ if (!table || !data || typeof data !== 'object')
249
+ throw new Error('Invalid table or data');
250
+ const logger = connectionManager_1.default.getLogger();
251
+ const fields = Object.keys(data);
252
+ const values = Object.values(data);
253
+ const placeholders = fields.map(() => '?').join(', ');
254
+ const sql = isIgnore
255
+ ? `INSERT IGNORE INTO ${table} (${fields.join(', ')}) VALUES (${placeholders})`
256
+ : `INSERT INTO ${table} (${fields.join(', ')}) VALUES (${placeholders})`;
257
+ const result = await exports.utils.executeQuery({ query: sql, params: values, dbConfig, operation: 'insert' });
258
+ if (debug) {
259
+ logger.info({ operation: 'INSERT', table, insertId: result.insertId });
260
+ }
261
+ return result.insertId;
262
+ };
263
+ exports.insert = insert;
264
+ const update = async function (table, data, query, dbConfig, debug = false) {
265
+ const errors = exports.utils.validateUpdateParams(table, data, query);
266
+ if (errors.length > 0)
267
+ throw new Error(`Validation failed: ${errors.join(', ')}`);
268
+ const logger = connectionManager_1.default.getLogger();
269
+ const { setFields, params } = exports.utils.prepareUpdateParams(data);
270
+ if (setFields.length === 0)
271
+ throw new Error('No valid fields to update');
272
+ const sql = `UPDATE ${table} SET ${setFields.join(', ')} ${query}`;
273
+ const result = await exports.utils.executeQuery({ query: sql, params, dbConfig, operation: 'update' });
274
+ if (debug) {
275
+ logger.info({ operation: 'UPDATE', table, affectedRows: result.affectedRows });
276
+ }
277
+ return result.affectedRows;
278
+ };
279
+ exports.update = update;
280
+ const _delete = async function (query, table, dbConfig) {
281
+ if (!query || !query.toLowerCase().includes('where'))
282
+ throw new Error('Invalid query or missing WHERE clause');
283
+ const logger = connectionManager_1.default.getLogger();
284
+ const sql = table ? `DELETE FROM ${table} ${query}` : query;
285
+ const result = await exports.utils.executeQuery({ query: sql, params: [], dbConfig, operation: 'delete' });
286
+ logger.info(`Deleted ${result.affectedRows} records from ${table || 'custom query'}`);
287
+ return result.affectedRows;
288
+ };
289
+ exports._delete = _delete;
290
+ exports.delete = exports._delete;
291
+ // Bulk Operations
292
+ const bulkInsert = async function (table, records, options, dbConfig) {
293
+ if (!table || !records || !Array.isArray(records) || records.length === 0) {
294
+ throw new Error('Table name and records are required for bulk insert');
295
+ }
296
+ const batchSize = options?.batchSize || 1000;
297
+ const isIgnore = options?.ignore || false;
298
+ const logger = connectionManager_1.default.getLogger();
299
+ // Validate all records have the same fields
300
+ const fields = Object.keys(records[0]);
301
+ for (const record of records) {
302
+ const recordFields = Object.keys(record);
303
+ if (recordFields.length !== fields.length || !recordFields.every(f => fields.includes(f))) {
304
+ throw new Error('All records must have the same fields for bulk insert');
305
+ }
306
+ }
307
+ let totalInserted = 0;
308
+ let firstInsertId;
309
+ let lastInsertId;
310
+ const batches = Math.ceil(records.length / batchSize);
311
+ for (let i = 0; i < records.length; i += batchSize) {
312
+ const batch = records.slice(i, i + batchSize);
313
+ const values = [];
314
+ const placeholders = [];
315
+ for (const record of batch) {
316
+ const recordValues = fields.map(field => record[field]);
317
+ values.push(...recordValues);
318
+ placeholders.push(`(${fields.map(() => '?').join(', ')})`);
319
+ }
320
+ const sql = isIgnore
321
+ ? `INSERT IGNORE INTO ${table} (${fields.join(', ')}) VALUES ${placeholders.join(', ')}`
322
+ : `INSERT INTO ${table} (${fields.join(', ')}) VALUES ${placeholders.join(', ')}`;
323
+ const result = await exports.utils.executeQuery({ query: sql, params: values, dbConfig, operation: 'bulkInsert' });
324
+ totalInserted += result.affectedRows || 0;
325
+ if (i === 0 && result.insertId) {
326
+ firstInsertId = result.insertId;
327
+ }
328
+ if (result.insertId) {
329
+ lastInsertId = result.insertId + (result.affectedRows || 1) - 1;
330
+ }
331
+ }
332
+ logger.info(`Bulk inserted ${totalInserted} records into ${table} in ${batches} batches`);
333
+ return {
334
+ totalInserted,
335
+ batches,
336
+ firstInsertId,
337
+ lastInsertId
338
+ };
339
+ };
340
+ exports.bulkInsert = bulkInsert;
341
+ const upsert = async function (table, data, options, dbConfig) {
342
+ if (!table || !data || !options.conflictKey) {
343
+ throw new Error('Table, data, and conflictKey are required for upsert');
344
+ }
345
+ const fields = Object.keys(data);
346
+ const values = Object.values(data);
347
+ const placeholders = fields.map(() => '?').join(', ');
348
+ // Determine which fields to update on duplicate key
349
+ const conflictKeys = Array.isArray(options.conflictKey) ? options.conflictKey : [options.conflictKey];
350
+ const updateFields = options.updateFields || fields.filter(f => !conflictKeys.includes(f));
351
+ if (updateFields.length === 0) {
352
+ throw new Error('No fields to update on duplicate key');
353
+ }
354
+ // Build UPDATE clause
355
+ const updateClause = updateFields.map(field => `${field} = VALUES(${field})`).join(', ');
356
+ const sql = `INSERT INTO ${table} (${fields.join(', ')}) VALUES (${placeholders}) ON DUPLICATE KEY UPDATE ${updateClause}`;
357
+ const result = await exports.utils.executeQuery({ query: sql, params: values, dbConfig, operation: 'upsert' });
358
+ // affectedRows = 1 means inserted, 2 means updated, 0 means no change
359
+ const action = result.affectedRows === 1 ? 'inserted' : 'updated';
360
+ return {
361
+ action,
362
+ affectedRows: result.affectedRows,
363
+ insertId: result.insertId
364
+ };
365
+ };
366
+ exports.upsert = upsert;
367
+ const bulkUpsert = async function (table, records, options, dbConfig) {
368
+ if (!table || !records || records.length === 0) {
369
+ throw new Error('Table name and records are required for bulk upsert');
370
+ }
371
+ const batchSize = options?.batchSize || 1000;
372
+ const logger = connectionManager_1.default.getLogger();
373
+ // Validate all records have the same fields
374
+ const fields = Object.keys(records[0]);
375
+ for (const record of records) {
376
+ const recordFields = Object.keys(record);
377
+ if (recordFields.length !== fields.length || !recordFields.every(f => fields.includes(f))) {
378
+ throw new Error('All records must have the same fields for bulk upsert');
379
+ }
380
+ }
381
+ // Determine which fields to update on duplicate key
382
+ const conflictKeys = Array.isArray(options.conflictKey) ? options.conflictKey : [options.conflictKey];
383
+ const updateFields = options.updateFields || fields.filter(f => !conflictKeys.includes(f));
384
+ if (updateFields.length === 0) {
385
+ throw new Error('No fields to update on duplicate key');
386
+ }
387
+ const updateClause = updateFields.map(field => `${field} = VALUES(${field})`).join(', ');
388
+ let totalAffected = 0;
389
+ const batches = Math.ceil(records.length / batchSize);
390
+ for (let i = 0; i < records.length; i += batchSize) {
391
+ const batch = records.slice(i, i + batchSize);
392
+ const values = [];
393
+ const placeholders = [];
394
+ for (const record of batch) {
395
+ const recordValues = fields.map(field => record[field]);
396
+ values.push(...recordValues);
397
+ placeholders.push(`(${fields.map(() => '?').join(', ')})`);
398
+ }
399
+ const sql = `INSERT INTO ${table} (${fields.join(', ')}) VALUES ${placeholders.join(', ')} ON DUPLICATE KEY UPDATE ${updateClause}`;
400
+ const result = await exports.utils.executeQuery({ query: sql, params: values, dbConfig, operation: 'bulkUpsert' });
401
+ totalAffected += result.affectedRows || 0;
402
+ }
403
+ logger.info(`Bulk upsert affected ${totalAffected} records in ${table} in ${batches} batches`);
404
+ return {
405
+ totalAffected,
406
+ batches
407
+ };
408
+ };
409
+ exports.bulkUpsert = bulkUpsert;
410
+ // Query Builder Methods
411
+ const buildAndExecuteSelectQuery = async function (options, dbConfig) {
412
+ const { query, params } = exports.utils._buildSelectQuery(options);
413
+ return await exports.utils.executeQuery({ query, params, dbConfig, operation: 'buildAndExecuteSelectQuery' });
414
+ };
415
+ exports.buildAndExecuteSelectQuery = buildAndExecuteSelectQuery;
416
+ const buildAndExecuteUpdateQuery = async function (options, dbConfig) {
417
+ const { query, params } = exports.utils._buildUpdateQuery(options);
418
+ const result = await exports.utils.executeQuery({ query, params, dbConfig, operation: 'buildAndExecuteUpdateQuery' });
419
+ return result.affectedRows;
420
+ };
421
+ exports.buildAndExecuteUpdateQuery = buildAndExecuteUpdateQuery;
422
+ const buildAndExecuteDeleteQuery = async function (options, dbConfig) {
423
+ const { query, params } = exports.utils._buildDeleteQuery(options);
424
+ const result = await exports.utils.executeQuery({ query, params, dbConfig, operation: 'buildAndExecuteDeleteQuery' });
425
+ return result.affectedRows;
426
+ };
427
+ exports.buildAndExecuteDeleteQuery = buildAndExecuteDeleteQuery;
428
+ // Aliases
429
+ exports.select = exports.buildAndExecuteSelectQuery;
430
+ exports.findWhere = exports.buildAndExecuteSelectQuery;
431
+ exports.query = exports.buildAndExecuteSelectQuery;
432
+ exports.updateWhere = exports.buildAndExecuteUpdateQuery;
433
+ exports.updateQuery = exports.buildAndExecuteUpdateQuery;
434
+ exports.deleteWhere = exports.buildAndExecuteDeleteQuery;
435
+ exports.remove = exports.buildAndExecuteDeleteQuery;
@@ -0,0 +1,50 @@
1
+ import { QueryMetric, PerformanceStats } from '../types';
2
+ declare class PerformanceMonitor {
3
+ private enabled;
4
+ private queries;
5
+ private startTime;
6
+ private queryCountByType;
7
+ /**
8
+ * Enable performance monitoring
9
+ */
10
+ enable(): void;
11
+ /**
12
+ * Disable performance monitoring
13
+ */
14
+ disable(): void;
15
+ /**
16
+ * Check if monitoring is enabled
17
+ */
18
+ isEnabled(): boolean;
19
+ /**
20
+ * Record a query execution
21
+ */
22
+ recordQuery(query: string, duration: number, params?: any[]): void;
23
+ /**
24
+ * Detect query type from SQL string
25
+ */
26
+ private detectQueryType;
27
+ /**
28
+ * Get current performance statistics
29
+ */
30
+ getStats(): PerformanceStats;
31
+ /**
32
+ * Get slowest queries
33
+ */
34
+ getSlowQueries(limit?: number): QueryMetric[];
35
+ /**
36
+ * Reset all collected metrics
37
+ */
38
+ reset(): void;
39
+ /**
40
+ * Get queries executed in the last N milliseconds
41
+ */
42
+ getRecentQueries(milliseconds?: number): QueryMetric[];
43
+ /**
44
+ * Get queries per second
45
+ */
46
+ getQueriesPerSecond(): number;
47
+ }
48
+ declare const performanceMonitor: PerformanceMonitor;
49
+ export default performanceMonitor;
50
+ export { performanceMonitor, PerformanceMonitor };
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PerformanceMonitor = exports.performanceMonitor = void 0;
4
+ class PerformanceMonitor {
5
+ enabled = false;
6
+ queries = [];
7
+ startTime = 0;
8
+ queryCountByType = {
9
+ SELECT: 0,
10
+ INSERT: 0,
11
+ UPDATE: 0,
12
+ DELETE: 0,
13
+ OTHER: 0
14
+ };
15
+ /**
16
+ * Enable performance monitoring
17
+ */
18
+ enable() {
19
+ this.enabled = true;
20
+ this.startTime = Date.now();
21
+ console.log('[Performance Monitor] Enabled');
22
+ }
23
+ /**
24
+ * Disable performance monitoring
25
+ */
26
+ disable() {
27
+ this.enabled = false;
28
+ console.log('[Performance Monitor] Disabled');
29
+ }
30
+ /**
31
+ * Check if monitoring is enabled
32
+ */
33
+ isEnabled() {
34
+ return this.enabled;
35
+ }
36
+ /**
37
+ * Record a query execution
38
+ */
39
+ recordQuery(query, duration, params) {
40
+ if (!this.enabled)
41
+ return;
42
+ const type = this.detectQueryType(query);
43
+ const metric = {
44
+ query,
45
+ duration,
46
+ timestamp: Date.now(),
47
+ type,
48
+ params
49
+ };
50
+ this.queries.push(metric);
51
+ this.queryCountByType[type]++;
52
+ // Keep only last 1000 queries to prevent memory issues
53
+ if (this.queries.length > 1000) {
54
+ this.queries.shift();
55
+ }
56
+ }
57
+ /**
58
+ * Detect query type from SQL string
59
+ */
60
+ detectQueryType(query) {
61
+ const normalized = query.trim().toUpperCase();
62
+ if (normalized.startsWith('SELECT'))
63
+ return 'SELECT';
64
+ if (normalized.startsWith('INSERT'))
65
+ return 'INSERT';
66
+ if (normalized.startsWith('UPDATE'))
67
+ return 'UPDATE';
68
+ if (normalized.startsWith('DELETE'))
69
+ return 'DELETE';
70
+ return 'OTHER';
71
+ }
72
+ /**
73
+ * Get current performance statistics
74
+ */
75
+ getStats() {
76
+ const totalQueries = this.queries.length;
77
+ const totalDuration = this.queries.reduce((sum, q) => sum + q.duration, 0);
78
+ const averageQueryTime = totalQueries > 0 ? totalDuration / totalQueries : 0;
79
+ const slowestQueries = [...this.queries]
80
+ .sort((a, b) => b.duration - a.duration)
81
+ .slice(0, 10);
82
+ return {
83
+ enabled: this.enabled,
84
+ totalQueries,
85
+ averageQueryTime: Math.round(averageQueryTime * 100) / 100,
86
+ slowestQueries,
87
+ queryCountByType: { ...this.queryCountByType },
88
+ startTime: this.startTime,
89
+ uptime: this.startTime > 0 ? Date.now() - this.startTime : 0
90
+ };
91
+ }
92
+ /**
93
+ * Get slowest queries
94
+ */
95
+ getSlowQueries(limit = 10) {
96
+ return [...this.queries]
97
+ .sort((a, b) => b.duration - a.duration)
98
+ .slice(0, limit);
99
+ }
100
+ /**
101
+ * Reset all collected metrics
102
+ */
103
+ reset() {
104
+ this.queries = [];
105
+ this.queryCountByType = {
106
+ SELECT: 0,
107
+ INSERT: 0,
108
+ UPDATE: 0,
109
+ DELETE: 0,
110
+ OTHER: 0
111
+ };
112
+ this.startTime = Date.now();
113
+ console.log('[Performance Monitor] Reset');
114
+ }
115
+ /**
116
+ * Get queries executed in the last N milliseconds
117
+ */
118
+ getRecentQueries(milliseconds = 60000) {
119
+ const cutoff = Date.now() - milliseconds;
120
+ return this.queries.filter(q => q.timestamp >= cutoff);
121
+ }
122
+ /**
123
+ * Get queries per second
124
+ */
125
+ getQueriesPerSecond() {
126
+ const uptime = this.startTime > 0 ? (Date.now() - this.startTime) / 1000 : 0;
127
+ return uptime > 0 ? this.queries.length / uptime : 0;
128
+ }
129
+ }
130
+ exports.PerformanceMonitor = PerformanceMonitor;
131
+ // Singleton instance
132
+ const performanceMonitor = new PerformanceMonitor();
133
+ exports.performanceMonitor = performanceMonitor;
134
+ exports.default = performanceMonitor;
@@ -0,0 +1,39 @@
1
+ import { DbConfig, ColumnInfo, IndexInfo, ForeignKeyInfo } from '../types';
2
+ declare const schema: {
3
+ /**
4
+ * Get list of all tables in the database
5
+ * @param dbConfig - Optional database configuration
6
+ * @returns Array of table names
7
+ */
8
+ tables(dbConfig?: DbConfig): Promise<string[]>;
9
+ /**
10
+ * Get column information for a specific table
11
+ * @param tableName - Name of the table
12
+ * @param dbConfig - Optional database configuration
13
+ * @returns Array of column information
14
+ */
15
+ columns(tableName: string, dbConfig?: DbConfig): Promise<ColumnInfo[]>;
16
+ /**
17
+ * Get index information for a specific table
18
+ * @param tableName - Name of the table
19
+ * @param dbConfig - Optional database configuration
20
+ * @returns Array of index information
21
+ */
22
+ indexes(tableName: string, dbConfig?: DbConfig): Promise<IndexInfo[]>;
23
+ /**
24
+ * Get foreign key relationships for a specific table or all tables
25
+ * @param tableName - Name of the table (optional, if not provided returns all foreign keys)
26
+ * @param dbConfig - Optional database configuration
27
+ * @returns Array of foreign key information
28
+ */
29
+ foreignKeys(tableName?: string, dbConfig?: DbConfig): Promise<ForeignKeyInfo[]>;
30
+ /**
31
+ * Get the correct order to delete/truncate tables based on foreign key dependencies
32
+ * Uses topological sort to ensure child tables are deleted before parent tables
33
+ * @param dbConfig - Optional database configuration
34
+ * @returns Array of table names in deletion order (children first)
35
+ */
36
+ deleteOrder(dbConfig?: DbConfig): Promise<string[]>;
37
+ };
38
+ export default schema;
39
+ export { schema };