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,391 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.QueryOptimizer = void 0;
4
+ exports.createQueryOptimizer = createQueryOptimizer;
5
+ const logger_js_1 = require("../logging/logger.js");
6
+ const query_parser_1 = require("./utils/query-parser");
7
+ const cache_service_1 = require("./services/cache-service");
8
+ const metrics_collector_1 = require("./services/metrics-collector");
9
+ const sql_1 = require("../raw-builder/sql");
10
+ /**
11
+ * Refactored query optimizer using focused services
12
+ */
13
+ class QueryOptimizer {
14
+ db;
15
+ cacheService;
16
+ metricsCollector;
17
+ options;
18
+ logger;
19
+ schemaInfo;
20
+ constructor(db, schemaInfo, options = {}, logger) {
21
+ this.db = db;
22
+ this.schemaInfo = schemaInfo;
23
+ this.logger = logger || new logger_js_1.Logger('QueryOptimizer');
24
+ this.options = {
25
+ enableQueryCache: true,
26
+ enableIndexRecommendations: true,
27
+ enableQueryRewriting: true,
28
+ slowQueryThreshold: 1000,
29
+ cacheSize: 1000,
30
+ maxCacheAge: 300000,
31
+ ...options
32
+ };
33
+ // Initialize services
34
+ this.cacheService = new cache_service_1.QueryCacheService({
35
+ maxSize: this.options.cacheSize,
36
+ defaultTtl: this.options.maxCacheAge,
37
+ enableMetrics: true
38
+ }, this.logger);
39
+ this.metricsCollector = new metrics_collector_1.MetricsCollector({
40
+ enabled: true,
41
+ slowQueryThreshold: this.options.slowQueryThreshold,
42
+ nPlusOneDetection: true,
43
+ missingIndexDetection: this.options.enableIndexRecommendations,
44
+ largeResultSetThreshold: 1000
45
+ }, this.logger);
46
+ }
47
+ /**
48
+ * Optimize and execute a query
49
+ */
50
+ async optimizeQuery(query, params = [], context) {
51
+ const startTime = performance.now();
52
+ try {
53
+ // Check cache first
54
+ if (this.options.enableQueryCache) {
55
+ const cachedResult = this.cacheService.getCachedQuery(query, params);
56
+ if (cachedResult) {
57
+ const executionTime = performance.now() - startTime;
58
+ return {
59
+ originalQuery: query,
60
+ optimizedQuery: query,
61
+ optimizationType: 'cache_hit',
62
+ executionTime,
63
+ improvement: 100,
64
+ suggestions: ['Query served from cache'],
65
+ result: cachedResult
66
+ };
67
+ }
68
+ }
69
+ // Parse and analyze query
70
+ const parsedQuery = query_parser_1.QueryParser.parseQuery(query);
71
+ const optimization = await this.analyzeQuery(parsedQuery, query, params);
72
+ const optimizedQuery = optimization.optimizedQuery || query;
73
+ // Execute the query
74
+ const result = await this.executeQuery(optimizedQuery, params);
75
+ const executionTime = performance.now() - startTime;
76
+ // Cache the result if appropriate
77
+ if (this.options.enableQueryCache &&
78
+ query_parser_1.QueryParser.shouldCache(query, executionTime)) {
79
+ this.cacheService.cacheQuery(query, params, result);
80
+ }
81
+ // Record metrics
82
+ this.metricsCollector.recordQuery(query, executionTime, {
83
+ table: context?.table,
84
+ operation: context?.operation,
85
+ resultCount: Array.isArray(result) ? result.length : 1
86
+ });
87
+ // Generate suggestions
88
+ const suggestions = this.generateSuggestions(parsedQuery, executionTime, context);
89
+ return {
90
+ originalQuery: query,
91
+ optimizedQuery,
92
+ optimizationType: optimization.type,
93
+ executionTime,
94
+ improvement: optimization.improvement,
95
+ suggestions,
96
+ result
97
+ };
98
+ }
99
+ catch (error) {
100
+ const executionTime = performance.now() - startTime;
101
+ // Record error metrics
102
+ const errorMessage = error instanceof Error ? error.message : String(error);
103
+ this.metricsCollector.recordQuery(query, executionTime, {
104
+ table: context?.table,
105
+ operation: context?.operation,
106
+ error: errorMessage
107
+ });
108
+ this.logger.error(`Query optimization failed: ${errorMessage}`, {
109
+ query,
110
+ executionTime
111
+ });
112
+ throw error;
113
+ }
114
+ }
115
+ /**
116
+ * Get index recommendations
117
+ */
118
+ async getIndexRecommendations() {
119
+ if (!this.options.enableIndexRecommendations) {
120
+ return [];
121
+ }
122
+ const slowQueries = this.metricsCollector.getSlowQueries();
123
+ const recommendations = [];
124
+ for (const queryInfo of slowQueries) {
125
+ const parsedQuery = query_parser_1.QueryParser.parseQuery(queryInfo.query);
126
+ const queryRecommendations = this.analyzeQueryForIndexes(parsedQuery, queryInfo.query);
127
+ recommendations.push(...queryRecommendations);
128
+ }
129
+ return this.deduplicateRecommendations(recommendations);
130
+ }
131
+ /**
132
+ * Get query performance statistics
133
+ */
134
+ getQueryStats() {
135
+ const metricsStats = this.metricsCollector.getPerformanceStats();
136
+ const cacheStats = this.cacheService.getStats();
137
+ return {
138
+ ...metricsStats,
139
+ cache: cacheStats,
140
+ indexRecommendations: 0 // Would be populated by recommendations
141
+ };
142
+ }
143
+ /**
144
+ * Clear query cache
145
+ */
146
+ clearCache() {
147
+ this.cacheService.clear();
148
+ }
149
+ /**
150
+ * Clear metrics
151
+ */
152
+ clearMetrics() {
153
+ this.metricsCollector.clear();
154
+ }
155
+ /**
156
+ * Get cache statistics
157
+ */
158
+ getCacheStats() {
159
+ return this.cacheService.getStats();
160
+ }
161
+ /**
162
+ * Get performance warnings
163
+ */
164
+ getPerformanceWarnings() {
165
+ return this.metricsCollector.getRecentWarnings();
166
+ }
167
+ /**
168
+ * Shutdown optimizer
169
+ */
170
+ shutdown() {
171
+ this.cacheService.shutdown();
172
+ this.metricsCollector.shutdown();
173
+ }
174
+ /**
175
+ * Analyze query for optimization opportunities
176
+ */
177
+ async analyzeQuery(parsedQuery, originalQuery, params) {
178
+ let optimizedQuery = originalQuery;
179
+ let improvement = 0;
180
+ let type = 'no_optimization';
181
+ // Apply query-specific optimizations
182
+ if (parsedQuery.type === 'SELECT') {
183
+ const selectOptimization = this.optimizeSelectQuery(parsedQuery, originalQuery);
184
+ if (selectOptimization.improvement > 0) {
185
+ optimizedQuery = selectOptimization.optimizedQuery;
186
+ improvement = selectOptimization.improvement;
187
+ type = 'query_rewrite';
188
+ }
189
+ }
190
+ if (parsedQuery.isComplex) {
191
+ const complexOptimization = this.optimizeComplexQuery(parsedQuery, originalQuery);
192
+ if (complexOptimization.improvement > improvement) {
193
+ optimizedQuery = complexOptimization.optimizedQuery;
194
+ improvement = complexOptimization.improvement;
195
+ type = 'query_rewrite';
196
+ }
197
+ }
198
+ return {
199
+ optimizedQuery,
200
+ type,
201
+ improvement
202
+ };
203
+ }
204
+ /**
205
+ * Optimize SELECT queries
206
+ */
207
+ optimizeSelectQuery(parsedQuery, originalQuery) {
208
+ let optimizedQuery = originalQuery;
209
+ let improvement = 0;
210
+ // Add LIMIT if missing and query could return many rows
211
+ if (!parsedQuery.hasLimit && query_parser_1.QueryParser.couldReturnManyRows(originalQuery)) {
212
+ optimizedQuery = `${originalQuery} LIMIT 1000`;
213
+ improvement = 10;
214
+ }
215
+ // Optimize ORDER BY with LIMIT
216
+ if (parsedQuery.hasLimit && parsedQuery.orderByColumns.length > 0) {
217
+ const orderByColumn = parsedQuery.orderByColumns[0];
218
+ const table = parsedQuery.tables[0];
219
+ if (table && this.hasIndexOnColumn(table, orderByColumn)) {
220
+ improvement = Math.max(improvement, 20);
221
+ }
222
+ }
223
+ return {
224
+ optimizedQuery,
225
+ improvement
226
+ };
227
+ }
228
+ /**
229
+ * Optimize complex queries (joins, aggregates)
230
+ */
231
+ optimizeComplexQuery(parsedQuery, originalQuery) {
232
+ let optimizedQuery = originalQuery;
233
+ let improvement = 0;
234
+ // Check for missing indexes on join columns
235
+ for (const { table, column } of parsedQuery.joinColumns) {
236
+ if (!this.hasIndexOnColumn(table, column)) {
237
+ improvement = Math.max(improvement, 15);
238
+ }
239
+ }
240
+ // Check for missing indexes on WHERE columns
241
+ for (const column of parsedQuery.whereColumns) {
242
+ const table = parsedQuery.tables[0];
243
+ if (table && !this.hasIndexOnColumn(table, column)) {
244
+ improvement = Math.max(improvement, 20);
245
+ }
246
+ }
247
+ // Check for missing indexes on GROUP BY columns
248
+ if (parsedQuery.hasAggregates && parsedQuery.groupByColumns.length > 0) {
249
+ const table = parsedQuery.tables[0];
250
+ if (table && parsedQuery.groupByColumns.every(col => this.hasIndexOnColumn(table, col))) {
251
+ improvement = Math.max(improvement, 25);
252
+ }
253
+ }
254
+ return {
255
+ optimizedQuery,
256
+ improvement
257
+ };
258
+ }
259
+ /**
260
+ * Analyze query for index recommendations
261
+ */
262
+ analyzeQueryForIndexes(parsedQuery, originalQuery) {
263
+ const recommendations = [];
264
+ const table = parsedQuery.tables[0];
265
+ if (!table) {
266
+ return recommendations;
267
+ }
268
+ // Analyze WHERE clauses
269
+ for (const column of parsedQuery.whereColumns) {
270
+ if (!this.hasIndexOnColumn(table, column)) {
271
+ recommendations.push({
272
+ table,
273
+ columns: [column],
274
+ type: 'btree',
275
+ reason: `WHERE clause on ${column}`,
276
+ estimatedImprovement: 20,
277
+ priority: 'medium'
278
+ });
279
+ }
280
+ }
281
+ // Analyze JOIN columns
282
+ for (const { table: joinTable, column } of parsedQuery.joinColumns) {
283
+ if (!this.hasIndexOnColumn(joinTable, column)) {
284
+ recommendations.push({
285
+ table: joinTable,
286
+ columns: [column],
287
+ type: 'btree',
288
+ reason: `JOIN condition on ${column}`,
289
+ estimatedImprovement: 30,
290
+ priority: 'high'
291
+ });
292
+ }
293
+ }
294
+ // Analyze ORDER BY columns
295
+ for (const column of parsedQuery.orderByColumns) {
296
+ if (!this.hasIndexOnColumn(table, column)) {
297
+ recommendations.push({
298
+ table,
299
+ columns: [column],
300
+ type: 'btree',
301
+ reason: `ORDER BY clause on ${column}`,
302
+ estimatedImprovement: 15,
303
+ priority: 'medium'
304
+ });
305
+ }
306
+ }
307
+ return recommendations;
308
+ }
309
+ /**
310
+ * Deduplicate index recommendations
311
+ */
312
+ deduplicateRecommendations(recommendations) {
313
+ const unique = new Map();
314
+ for (const rec of recommendations) {
315
+ const key = `${rec.table}:${rec.columns.join(',')}`;
316
+ if (!unique.has(key) || unique.get(key).priority === 'low') {
317
+ unique.set(key, rec);
318
+ }
319
+ }
320
+ return Array.from(unique.values()).sort((a, b) => {
321
+ const priorityOrder = { high: 3, medium: 2, low: 1 };
322
+ return priorityOrder[b.priority] - priorityOrder[a.priority];
323
+ });
324
+ }
325
+ /**
326
+ * Generate optimization suggestions
327
+ */
328
+ generateSuggestions(parsedQuery, executionTime, context) {
329
+ const suggestions = [];
330
+ if (executionTime > this.options.slowQueryThreshold) {
331
+ suggestions.push(`Query took ${executionTime.toFixed(2)}ms - consider adding indexes`);
332
+ }
333
+ if (parsedQuery.type === 'SELECT' && !parsedQuery.hasLimit) {
334
+ suggestions.push('Consider adding LIMIT clause to prevent large result sets');
335
+ }
336
+ if (parsedQuery.joinColumns.length > 0) {
337
+ for (const { table, column } of parsedQuery.joinColumns) {
338
+ if (!this.hasIndexOnColumn(table, column)) {
339
+ suggestions.push(`Consider adding index on ${table}.${column} for join optimization`);
340
+ }
341
+ }
342
+ }
343
+ if (parsedQuery.hasAggregates && parsedQuery.groupByColumns.length > 0) {
344
+ suggestions.push('Consider adding composite indexes for GROUP BY columns');
345
+ }
346
+ return suggestions;
347
+ }
348
+ /**
349
+ * Execute a query with error handling
350
+ * Note: This creates a proper SQL query with parameters
351
+ */
352
+ async executeQuery(query, params) {
353
+ try {
354
+ // Split query by '?' placeholders and create template string array
355
+ const parts = query.split('?');
356
+ const templateStrings = Object.assign([...parts], { raw: [...parts] });
357
+ // Create SQL query using template tag syntax
358
+ const sqlQuery = (0, sql_1.sql)(templateStrings, ...params);
359
+ // Compile the query first to match the expected interface
360
+ const compiledQuery = sqlQuery.compile(this.db);
361
+ const result = await this.db.executeQuery(compiledQuery);
362
+ return result.rows;
363
+ }
364
+ catch (error) {
365
+ const errorMessage = error instanceof Error ? error.message : String(error);
366
+ this.logger.error(`Query execution failed: ${errorMessage}`, { query, params });
367
+ throw error;
368
+ }
369
+ }
370
+ /**
371
+ * Check if column has an index
372
+ */
373
+ hasIndexOnColumn(table, column) {
374
+ const tableInfo = this.schemaInfo.tables.find(t => t.name === table);
375
+ if (!tableInfo)
376
+ return false;
377
+ // Check if column is primary key
378
+ const columnInfo = tableInfo.columns.find(col => col.name === column);
379
+ if (columnInfo?.isPrimaryKey)
380
+ return true;
381
+ // Check if column has an index
382
+ return tableInfo.indexes.some(idx => idx.columns.includes(column));
383
+ }
384
+ }
385
+ exports.QueryOptimizer = QueryOptimizer;
386
+ /**
387
+ * Factory function to create query optimizer
388
+ */
389
+ function createQueryOptimizer(db, schemaInfo, options, logger) {
390
+ return new QueryOptimizer(db, schemaInfo, options, logger);
391
+ }
@@ -0,0 +1,177 @@
1
+ import { Logger } from '../../logging/logger.js';
2
+ export interface CacheEntry<T = any> {
3
+ key: string;
4
+ value: T;
5
+ timestamp: number;
6
+ ttl: number;
7
+ hitCount: number;
8
+ size: number;
9
+ }
10
+ export interface CacheConfig {
11
+ maxSize: number;
12
+ defaultTtl: number;
13
+ cleanupInterval: number;
14
+ enableCompression: boolean;
15
+ enableMetrics: boolean;
16
+ }
17
+ export interface CacheStats {
18
+ size: number;
19
+ maxSize: number;
20
+ hitCount: number;
21
+ missCount: number;
22
+ hitRate: number;
23
+ totalSize: number;
24
+ evictions: number;
25
+ lastCleanup: Date;
26
+ }
27
+ export interface CacheMetrics {
28
+ operations: {
29
+ get: number;
30
+ set: number;
31
+ delete: number;
32
+ clear: number;
33
+ };
34
+ performance: {
35
+ averageGetTime: number;
36
+ averageSetTime: number;
37
+ slowestOperation: number;
38
+ };
39
+ memory: {
40
+ totalSize: number;
41
+ averageEntrySize: number;
42
+ largestEntry: number;
43
+ };
44
+ }
45
+ /**
46
+ * Generic cache service with TTL, size limits, and metrics
47
+ */
48
+ export declare class CacheService<T = any> {
49
+ private cache;
50
+ private config;
51
+ private stats;
52
+ private metrics;
53
+ private cleanupTimer?;
54
+ protected logger: Logger;
55
+ constructor(config?: Partial<CacheConfig>, logger?: Logger);
56
+ /**
57
+ * Get a value from cache
58
+ */
59
+ get(key: string): T | null;
60
+ /**
61
+ * Set a value in cache
62
+ */
63
+ set(key: string, value: T, ttl?: number): boolean;
64
+ /**
65
+ * Delete a value from cache
66
+ */
67
+ delete(key: string): boolean;
68
+ /**
69
+ * Check if key exists in cache
70
+ */
71
+ has(key: string): boolean;
72
+ /**
73
+ * Clear all entries from cache
74
+ */
75
+ clear(): void;
76
+ /**
77
+ * Get cache statistics
78
+ */
79
+ getStats(): CacheStats;
80
+ /**
81
+ * Get detailed metrics
82
+ */
83
+ getMetrics(): CacheMetrics;
84
+ /**
85
+ * Get cache size
86
+ */
87
+ size(): number;
88
+ /**
89
+ * Get all cache keys
90
+ */
91
+ keys(): string[];
92
+ /**
93
+ * Get cache entries (for debugging)
94
+ */
95
+ entries(): Array<{
96
+ key: string;
97
+ entry: CacheEntry<T>;
98
+ }>;
99
+ /**
100
+ * Warm cache with multiple entries
101
+ */
102
+ warm(entries: Array<{
103
+ key: string;
104
+ value: T;
105
+ ttl?: number;
106
+ }>): number;
107
+ /**
108
+ * Get cache health status
109
+ */
110
+ getHealth(): {
111
+ status: 'healthy' | 'warning' | 'critical';
112
+ issues: string[];
113
+ recommendations: string[];
114
+ };
115
+ /**
116
+ * Shutdown cache service
117
+ */
118
+ shutdown(): void;
119
+ /**
120
+ * Check if cache entry is expired
121
+ */
122
+ private isExpired;
123
+ /**
124
+ * Calculate size of a value (rough estimation)
125
+ */
126
+ private calculateSize;
127
+ /**
128
+ * Evict entries when cache is full
129
+ */
130
+ private evictEntries;
131
+ /**
132
+ * Update cache statistics
133
+ */
134
+ private updateStats;
135
+ /**
136
+ * Update hit rate
137
+ */
138
+ private updateHitRate;
139
+ /**
140
+ * Update performance metrics
141
+ */
142
+ private updatePerformanceMetrics;
143
+ /**
144
+ * Start cleanup timer
145
+ */
146
+ private startCleanupTimer;
147
+ /**
148
+ * Cleanup expired entries
149
+ */
150
+ private cleanup;
151
+ }
152
+ /**
153
+ * Factory function to create cache service
154
+ */
155
+ export declare function createCacheService<T = any>(config?: Partial<CacheConfig>, logger?: Logger): CacheService<T>;
156
+ /**
157
+ * Specialized query cache service
158
+ */
159
+ export declare class QueryCacheService extends CacheService<any> {
160
+ constructor(config?: Partial<CacheConfig>, logger?: Logger);
161
+ /**
162
+ * Cache query result with automatic key generation
163
+ */
164
+ cacheQuery(query: string, params: any[], result: any, ttl?: number): boolean;
165
+ /**
166
+ * Get cached query result
167
+ */
168
+ getCachedQuery(query: string, params: any[]): any | null;
169
+ /**
170
+ * Generate cache key for query
171
+ */
172
+ private generateQueryKey;
173
+ /**
174
+ * Invalidate cache entries for a specific table
175
+ */
176
+ invalidateTable(table: string): number;
177
+ }