noormme 1.1.0 → 1.2.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.
- package/README.md +84 -65
- package/dist/cjs/agentic/ActionJournal.js +13 -9
- package/dist/cjs/agentic/CapabilityManager.js +35 -21
- package/dist/cjs/agentic/CognitiveRepository.js +19 -9
- package/dist/cjs/agentic/ContextBuffer.js +24 -12
- package/dist/cjs/agentic/Cortex.js +11 -4
- package/dist/cjs/agentic/EpisodicMemory.js +7 -5
- package/dist/cjs/agentic/PersonaManager.js +16 -8
- package/dist/cjs/agentic/PolicyEnforcer.js +31 -12
- package/dist/cjs/agentic/ResourceMonitor.js +4 -4
- package/dist/cjs/agentic/SessionCompressor.js +22 -14
- package/dist/cjs/agentic/SessionManager.js +36 -18
- package/dist/cjs/agentic/VectorIndexer.js +22 -18
- package/dist/cjs/agentic/improvement/AblationEngine.js +22 -15
- package/dist/cjs/agentic/improvement/ActionRefiner.js +12 -10
- package/dist/cjs/agentic/improvement/ConflictResolver.js +5 -5
- package/dist/cjs/agentic/improvement/CortexJanitor.js +30 -9
- package/dist/cjs/agentic/improvement/CuriosityEngine.js +27 -23
- package/dist/cjs/agentic/improvement/EvolutionRitual.js +4 -4
- package/dist/cjs/agentic/improvement/EvolutionaryPilot.js +16 -6
- package/dist/cjs/agentic/improvement/GoalArchitect.d.ts +2 -2
- package/dist/cjs/agentic/improvement/GoalArchitect.js +20 -18
- package/dist/cjs/agentic/improvement/GovernanceManager.js +19 -11
- package/dist/cjs/agentic/improvement/HiveLink.js +22 -15
- package/dist/cjs/agentic/improvement/KnowledgeDistiller.js +48 -32
- package/dist/cjs/agentic/improvement/RecursiveReasoner.js +40 -17
- package/dist/cjs/agentic/improvement/ReflectionEngine.js +10 -8
- package/dist/cjs/agentic/improvement/RitualOrchestrator.js +28 -22
- package/dist/cjs/agentic/improvement/RuleEngine.js +22 -17
- package/dist/cjs/agentic/improvement/SelfEvolution.js +24 -18
- package/dist/cjs/agentic/improvement/SelfTestRegistry.js +18 -15
- package/dist/cjs/agentic/improvement/SkillSynthesizer.js +42 -27
- package/dist/cjs/agentic/improvement/SovereignMetrics.js +19 -17
- package/dist/cjs/agentic/improvement/StrategicPlanner.js +120 -55
- package/dist/cjs/agentic/telemetry/CognitiveSynthesizer.js +26 -12
- package/dist/cjs/agentic/telemetry/EventHarvester.js +3 -2
- package/dist/cjs/agentic/telemetry/ResearchAlchemist.js +5 -2
- package/dist/cjs/cache/cache-manager.js +7 -4
- package/dist/cjs/cli/commands/analyze.js +5 -4
- package/dist/cjs/cli/commands/generate.js +81 -44
- package/dist/cjs/cli/commands/init.js +7 -3
- package/dist/cjs/cli/commands/inspect.js +139 -36
- package/dist/cjs/cli/commands/migrate.js +5 -4
- package/dist/cjs/cli/commands/optimize.js +4 -4
- package/dist/cjs/cli/commands/status.js +9 -7
- package/dist/cjs/cli/commands/watch.js +7 -6
- package/dist/cjs/cli/index.js +2 -2
- package/dist/cjs/cli/ui/spinner.d.ts +15 -0
- package/dist/cjs/cli/ui/spinner.js +76 -0
- package/dist/cjs/dialect/database-introspector.js +3 -1
- package/dist/cjs/dialect/postgresql/postgresql-driver.js +3 -1
- package/dist/cjs/dialect/postgresql/postgresql-features.js +18 -8
- package/dist/cjs/dialect/postgresql/postgresql-introspector.js +2 -2
- package/dist/cjs/dialect/sqlite/sqlite-auto-indexer.js +47 -33
- package/dist/cjs/dialect/sqlite/sqlite-auto-optimizer.js +8 -7
- package/dist/cjs/dialect/sqlite/sqlite-driver.js +2 -2
- package/dist/cjs/dialect/sqlite/sqlite-introspector.js +15 -12
- package/dist/cjs/edge-runtime/edge-config.js +21 -19
- package/dist/cjs/errors/NoormError.js +22 -20
- package/dist/cjs/helpers/agent-schema.js +2 -0
- package/dist/cjs/helpers/postgresql.js +7 -4
- package/dist/cjs/helpers/schema-evolution.js +31 -6
- package/dist/cjs/index.d.ts +3 -3
- package/dist/cjs/logging/logger.js +8 -4
- package/dist/cjs/migration/data_migrator.js +12 -11
- package/dist/cjs/migration/database_migration_manager.js +17 -13
- package/dist/cjs/migration/schema_differ.js +22 -14
- package/dist/cjs/migration/schema_introspector.js +8 -8
- package/dist/cjs/migration/type_mapper.js +68 -67
- package/dist/cjs/noormme.js +52 -37
- package/dist/cjs/performance/index.js +5 -5
- package/dist/cjs/performance/query-optimizer.js +26 -21
- package/dist/cjs/performance/services/cache-service.js +26 -16
- package/dist/cjs/performance/services/connection-factory.js +28 -23
- package/dist/cjs/performance/services/metrics-collector.js +41 -36
- package/dist/cjs/performance/utils/query-parser.js +15 -16
- package/dist/cjs/relationships/relationship-engine.js +10 -8
- package/dist/cjs/repository/repository-factory.js +97 -38
- package/dist/cjs/schema/core/coordinators/schema-discovery.coordinator.js +1 -3
- package/dist/cjs/schema/core/discovery/relationship-discovery.js +16 -16
- package/dist/cjs/schema/core/discovery/table-metadata-discovery.js +9 -9
- package/dist/cjs/schema/core/discovery/view-discovery.js +5 -4
- package/dist/cjs/schema/core/factories/discovery-factory.js +4 -4
- package/dist/cjs/schema/core/utils/name-generator.js +14 -5
- package/dist/cjs/schema/core/utils/type-mapper.js +24 -24
- package/dist/cjs/schema/dialects/postgresql/postgresql-discovery.coordinator.js +8 -7
- package/dist/cjs/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +17 -15
- package/dist/cjs/schema/dialects/sqlite/discovery/sqlite-index-discovery.js +8 -8
- package/dist/cjs/schema/dialects/sqlite/introspection/sqlite-schema-introspector.js +6 -11
- package/dist/cjs/schema/dialects/sqlite/sqlite-discovery.coordinator.js +14 -13
- package/dist/cjs/schema/test/basic-schema-test.js +11 -9
- package/dist/cjs/schema/test/dialect-capabilities.test.js +6 -6
- package/dist/cjs/schema/test/discovery-factory.test.js +2 -2
- package/dist/cjs/schema/test/error-handling.test.js +8 -6
- package/dist/cjs/schema/test/integration.test.js +24 -18
- package/dist/cjs/schema/test/schema-discovery-coordinator.test.js +9 -9
- package/dist/cjs/schema/test/simple-schema-test.js +9 -9
- package/dist/cjs/schema/test/sqlite-discovery-coordinator.test.js +64 -48
- package/dist/cjs/schema/test/test-runner.js +3 -3
- package/dist/cjs/sqlite-migration/index.d.ts +2 -2
- package/dist/cjs/sqlite-migration/sqlite-migration-manager.js +21 -17
- package/dist/cjs/sqlite-migration/sqlite-migration-provider.js +38 -34
- package/dist/cjs/testing/test-utils.js +36 -34
- package/dist/cjs/types/index.d.ts +5 -2
- package/dist/cjs/types/index.js +6 -3
- package/dist/cjs/types/type-generator.js +46 -42
- package/dist/cjs/util/safe-sql-helpers.js +1 -1
- package/dist/cjs/util/security-validator.js +20 -9
- package/dist/cjs/utils/errorHelpers.js +20 -10
- package/dist/cjs/watch/schema-watcher.js +22 -23
- package/dist/esm/agentic/ActionJournal.js +13 -9
- package/dist/esm/agentic/CapabilityManager.js +35 -21
- package/dist/esm/agentic/CognitiveRepository.js +19 -9
- package/dist/esm/agentic/ContextBuffer.js +24 -12
- package/dist/esm/agentic/Cortex.js +11 -4
- package/dist/esm/agentic/EpisodicMemory.js +7 -5
- package/dist/esm/agentic/PersonaManager.js +16 -8
- package/dist/esm/agentic/PolicyEnforcer.js +31 -12
- package/dist/esm/agentic/ResourceMonitor.js +4 -4
- package/dist/esm/agentic/SessionCompressor.js +22 -14
- package/dist/esm/agentic/SessionManager.js +36 -18
- package/dist/esm/agentic/VectorIndexer.js +22 -18
- package/dist/esm/agentic/improvement/AblationEngine.js +22 -15
- package/dist/esm/agentic/improvement/ActionRefiner.js +12 -10
- package/dist/esm/agentic/improvement/ConflictResolver.js +5 -5
- package/dist/esm/agentic/improvement/CortexJanitor.js +30 -9
- package/dist/esm/agentic/improvement/CuriosityEngine.js +27 -23
- package/dist/esm/agentic/improvement/EvolutionRitual.js +4 -4
- package/dist/esm/agentic/improvement/EvolutionaryPilot.js +16 -6
- package/dist/esm/agentic/improvement/GoalArchitect.d.ts +2 -2
- package/dist/esm/agentic/improvement/GoalArchitect.js +20 -18
- package/dist/esm/agentic/improvement/GovernanceManager.js +19 -11
- package/dist/esm/agentic/improvement/HiveLink.js +22 -15
- package/dist/esm/agentic/improvement/KnowledgeDistiller.js +48 -32
- package/dist/esm/agentic/improvement/RecursiveReasoner.js +40 -17
- package/dist/esm/agentic/improvement/ReflectionEngine.js +10 -8
- package/dist/esm/agentic/improvement/RitualOrchestrator.js +28 -22
- package/dist/esm/agentic/improvement/RuleEngine.js +22 -17
- package/dist/esm/agentic/improvement/SelfEvolution.js +24 -18
- package/dist/esm/agentic/improvement/SelfTestRegistry.js +18 -15
- package/dist/esm/agentic/improvement/SkillSynthesizer.js +42 -27
- package/dist/esm/agentic/improvement/SovereignMetrics.js +19 -17
- package/dist/esm/agentic/improvement/StrategicPlanner.js +120 -55
- package/dist/esm/agentic/telemetry/CognitiveSynthesizer.js +26 -12
- package/dist/esm/agentic/telemetry/EventHarvester.js +3 -2
- package/dist/esm/agentic/telemetry/ResearchAlchemist.js +5 -2
- package/dist/esm/cache/cache-manager.js +7 -4
- package/dist/esm/cli/commands/analyze.js +5 -4
- package/dist/esm/cli/commands/generate.js +82 -45
- package/dist/esm/cli/commands/init.js +8 -4
- package/dist/esm/cli/commands/inspect.js +140 -37
- package/dist/esm/cli/commands/migrate.js +5 -4
- package/dist/esm/cli/commands/optimize.js +4 -4
- package/dist/esm/cli/commands/status.js +9 -7
- package/dist/esm/cli/commands/watch.js +7 -6
- package/dist/esm/cli/index.js +2 -2
- package/dist/esm/cli/ui/spinner.d.ts +15 -0
- package/dist/esm/cli/ui/spinner.js +70 -0
- package/dist/esm/dialect/database-introspector.js +3 -1
- package/dist/esm/dialect/postgresql/postgresql-driver.js +3 -1
- package/dist/esm/dialect/postgresql/postgresql-features.js +18 -8
- package/dist/esm/dialect/postgresql/postgresql-introspector.js +2 -2
- package/dist/esm/dialect/sqlite/sqlite-auto-indexer.js +47 -33
- package/dist/esm/dialect/sqlite/sqlite-auto-optimizer.js +8 -7
- package/dist/esm/dialect/sqlite/sqlite-driver.js +2 -2
- package/dist/esm/dialect/sqlite/sqlite-introspector.js +15 -12
- package/dist/esm/dynamic/dynamic.js +1 -1
- package/dist/esm/edge-runtime/edge-config.js +21 -19
- package/dist/esm/errors/NoormError.js +22 -20
- package/dist/esm/helpers/agent-schema.js +2 -0
- package/dist/esm/helpers/postgresql.js +7 -4
- package/dist/esm/helpers/schema-evolution.js +31 -6
- package/dist/esm/index.d.ts +3 -3
- package/dist/esm/index.js +2 -2
- package/dist/esm/logging/logger.js +8 -4
- package/dist/esm/migration/data_migrator.js +12 -11
- package/dist/esm/migration/database_migration_manager.js +18 -14
- package/dist/esm/migration/schema_differ.js +22 -14
- package/dist/esm/migration/schema_introspector.js +8 -8
- package/dist/esm/migration/type_mapper.js +68 -67
- package/dist/esm/noormme.js +52 -37
- package/dist/esm/performance/index.js +5 -5
- package/dist/esm/performance/query-optimizer.js +26 -21
- package/dist/esm/performance/services/cache-service.js +26 -16
- package/dist/esm/performance/services/connection-factory.js +28 -23
- package/dist/esm/performance/services/metrics-collector.js +41 -36
- package/dist/esm/performance/utils/query-parser.js +15 -16
- package/dist/esm/raw-builder/sql.js +1 -1
- package/dist/esm/relationships/relationship-engine.js +10 -8
- package/dist/esm/repository/repository-factory.js +98 -39
- package/dist/esm/schema/builders/alter-table-add-index-builder.js +1 -1
- package/dist/esm/schema/builders/create-index-builder.js +2 -2
- package/dist/esm/schema/core/coordinators/schema-discovery.coordinator.js +1 -3
- package/dist/esm/schema/core/discovery/relationship-discovery.js +16 -16
- package/dist/esm/schema/core/discovery/table-metadata-discovery.js +9 -9
- package/dist/esm/schema/core/discovery/view-discovery.js +5 -4
- package/dist/esm/schema/core/factories/discovery-factory.js +4 -4
- package/dist/esm/schema/core/utils/name-generator.js +14 -5
- package/dist/esm/schema/core/utils/type-mapper.js +24 -24
- package/dist/esm/schema/dialects/postgresql/postgresql-discovery.coordinator.js +8 -7
- package/dist/esm/schema/dialects/sqlite/discovery/sqlite-constraint-discovery.js +17 -15
- package/dist/esm/schema/dialects/sqlite/discovery/sqlite-index-discovery.js +8 -8
- package/dist/esm/schema/dialects/sqlite/introspection/sqlite-schema-introspector.js +6 -11
- package/dist/esm/schema/dialects/sqlite/sqlite-discovery.coordinator.js +14 -13
- package/dist/esm/schema/test/basic-schema-test.js +11 -9
- package/dist/esm/schema/test/dialect-capabilities.test.js +6 -6
- package/dist/esm/schema/test/discovery-factory.test.js +2 -2
- package/dist/esm/schema/test/error-handling.test.js +8 -6
- package/dist/esm/schema/test/integration.test.js +24 -18
- package/dist/esm/schema/test/schema-discovery-coordinator.test.js +9 -9
- package/dist/esm/schema/test/simple-schema-test.js +9 -9
- package/dist/esm/schema/test/sqlite-discovery-coordinator.test.js +64 -48
- package/dist/esm/schema/test/test-runner.js +3 -3
- package/dist/esm/sqlite-migration/index.d.ts +2 -2
- package/dist/esm/sqlite-migration/sqlite-migration-manager.js +21 -17
- package/dist/esm/sqlite-migration/sqlite-migration-provider.js +38 -34
- package/dist/esm/testing/test-utils.js +36 -34
- package/dist/esm/types/index.d.ts +5 -2
- package/dist/esm/types/index.js +6 -3
- package/dist/esm/types/type-generator.js +46 -42
- package/dist/esm/util/safe-sql-helpers.js +1 -1
- package/dist/esm/util/security-validator.js +20 -9
- package/dist/esm/utils/errorHelpers.js +21 -11
- package/dist/esm/watch/schema-watcher.js +22 -23
- package/package.json +40 -44
|
@@ -59,11 +59,11 @@ class MetricsCollector {
|
|
|
59
59
|
maxHistorySize: 10000,
|
|
60
60
|
warningRetentionDays: 7,
|
|
61
61
|
persistPath: config.persistPath,
|
|
62
|
-
...config
|
|
62
|
+
...config,
|
|
63
63
|
};
|
|
64
64
|
this.startCleanupTimer();
|
|
65
65
|
// Attempt to load metrics on startup (fire and forget)
|
|
66
|
-
this.load().catch(err => this.logger.warn('Failed to load metrics:', err));
|
|
66
|
+
this.load().catch((err) => this.logger.warn('Failed to load metrics:', err));
|
|
67
67
|
}
|
|
68
68
|
/**
|
|
69
69
|
* Load metrics from disk
|
|
@@ -101,7 +101,7 @@ class MetricsCollector {
|
|
|
101
101
|
const data = JSON.stringify({
|
|
102
102
|
queryHistory: this.queryHistory,
|
|
103
103
|
warnings: this.warnings,
|
|
104
|
-
savedAt: Date.now()
|
|
104
|
+
savedAt: Date.now(),
|
|
105
105
|
}, null, 2);
|
|
106
106
|
await fs.writeFile(this.config.persistPath, data, 'utf-8');
|
|
107
107
|
this.logger.debug(`Saved metrics to ${this.config.persistPath}`);
|
|
@@ -123,7 +123,7 @@ class MetricsCollector {
|
|
|
123
123
|
table: options.table,
|
|
124
124
|
operation: options.operation,
|
|
125
125
|
resultCount: options.resultCount,
|
|
126
|
-
error: options.error
|
|
126
|
+
error: options.error,
|
|
127
127
|
};
|
|
128
128
|
this.queryHistory.push(metrics);
|
|
129
129
|
// Keep history size under control
|
|
@@ -140,12 +140,13 @@ class MetricsCollector {
|
|
|
140
140
|
*/
|
|
141
141
|
getPerformanceStats() {
|
|
142
142
|
const totalQueries = this.queryHistory.length;
|
|
143
|
-
const slowQueries = this.queryHistory.filter(q => q.executionTime > this.config.slowQueryThreshold).length;
|
|
143
|
+
const slowQueries = this.queryHistory.filter((q) => q.executionTime > this.config.slowQueryThreshold).length;
|
|
144
144
|
const averageExecutionTime = totalQueries > 0
|
|
145
|
-
? this.queryHistory.reduce((sum, q) => sum + q.executionTime, 0) /
|
|
145
|
+
? this.queryHistory.reduce((sum, q) => sum + q.executionTime, 0) /
|
|
146
|
+
totalQueries
|
|
146
147
|
: 0;
|
|
147
148
|
const errorRate = totalQueries > 0
|
|
148
|
-
? this.queryHistory.filter(q => q.error).length / totalQueries
|
|
149
|
+
? this.queryHistory.filter((q) => q.error).length / totalQueries
|
|
149
150
|
: 0;
|
|
150
151
|
const warningCount = {};
|
|
151
152
|
for (const warning of this.warnings) {
|
|
@@ -160,7 +161,7 @@ class MetricsCollector {
|
|
|
160
161
|
errorRate,
|
|
161
162
|
warningCount,
|
|
162
163
|
topSlowQueries,
|
|
163
|
-
queryPatterns
|
|
164
|
+
queryPatterns,
|
|
164
165
|
};
|
|
165
166
|
}
|
|
166
167
|
/**
|
|
@@ -175,14 +176,13 @@ class MetricsCollector {
|
|
|
175
176
|
* Get warnings by type
|
|
176
177
|
*/
|
|
177
178
|
getWarningsByType(type) {
|
|
178
|
-
return this.warnings.filter(w => w.type === type);
|
|
179
|
+
return this.warnings.filter((w) => w.type === type);
|
|
179
180
|
}
|
|
180
181
|
/**
|
|
181
182
|
* Get query history
|
|
182
183
|
*/
|
|
183
184
|
getQueryHistory(limit) {
|
|
184
|
-
const history = this.queryHistory
|
|
185
|
-
.sort((a, b) => b.timestamp - a.timestamp);
|
|
185
|
+
const history = this.queryHistory.sort((a, b) => b.timestamp - a.timestamp);
|
|
186
186
|
return limit ? history.slice(0, limit) : history;
|
|
187
187
|
}
|
|
188
188
|
/**
|
|
@@ -191,7 +191,7 @@ class MetricsCollector {
|
|
|
191
191
|
getSlowQueries(threshold) {
|
|
192
192
|
const actualThreshold = threshold || this.config.slowQueryThreshold;
|
|
193
193
|
return this.queryHistory
|
|
194
|
-
.filter(q => q.executionTime > actualThreshold)
|
|
194
|
+
.filter((q) => q.executionTime > actualThreshold)
|
|
195
195
|
.sort((a, b) => b.executionTime - a.executionTime);
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
@@ -216,7 +216,7 @@ class MetricsCollector {
|
|
|
216
216
|
let maxCountInWindow = 0;
|
|
217
217
|
for (const query of queries) {
|
|
218
218
|
const windowStart = query.timestamp - windowMs;
|
|
219
|
-
const countInWindow = queries.filter(q => q.timestamp >= windowStart && q.timestamp <= query.timestamp).length;
|
|
219
|
+
const countInWindow = queries.filter((q) => q.timestamp >= windowStart && q.timestamp <= query.timestamp).length;
|
|
220
220
|
maxCountInWindow = Math.max(maxCountInWindow, countInWindow);
|
|
221
221
|
}
|
|
222
222
|
if (maxCountInWindow >= 5) {
|
|
@@ -229,7 +229,7 @@ class MetricsCollector {
|
|
|
229
229
|
pattern,
|
|
230
230
|
count: maxCountInWindow,
|
|
231
231
|
timeWindow: windowMs,
|
|
232
|
-
severity
|
|
232
|
+
severity,
|
|
233
233
|
});
|
|
234
234
|
break;
|
|
235
235
|
}
|
|
@@ -255,7 +255,7 @@ class MetricsCollector {
|
|
|
255
255
|
config: this.config,
|
|
256
256
|
stats: this.getPerformanceStats(),
|
|
257
257
|
warnings: this.warnings,
|
|
258
|
-
queries: this.queryHistory
|
|
258
|
+
queries: this.queryHistory,
|
|
259
259
|
};
|
|
260
260
|
}
|
|
261
261
|
/**
|
|
@@ -280,7 +280,7 @@ class MetricsCollector {
|
|
|
280
280
|
// Clean up old recent queries (keep last 10 seconds)
|
|
281
281
|
const cutoff = Date.now() - 10000;
|
|
282
282
|
for (const [query, queries] of this.recentQueries.entries()) {
|
|
283
|
-
const recent = queries.filter(q => q.timestamp >= cutoff);
|
|
283
|
+
const recent = queries.filter((q) => q.timestamp >= cutoff);
|
|
284
284
|
if (recent.length === 0) {
|
|
285
285
|
this.recentQueries.delete(query);
|
|
286
286
|
}
|
|
@@ -302,8 +302,10 @@ class MetricsCollector {
|
|
|
302
302
|
suggestion: 'Consider adding indexes or optimizing the query',
|
|
303
303
|
query: metrics.query,
|
|
304
304
|
table: metrics.table,
|
|
305
|
-
severity: metrics.executionTime > this.config.slowQueryThreshold * 3
|
|
306
|
-
|
|
305
|
+
severity: metrics.executionTime > this.config.slowQueryThreshold * 3
|
|
306
|
+
? 'high'
|
|
307
|
+
: 'medium',
|
|
308
|
+
timestamp: Date.now(),
|
|
307
309
|
});
|
|
308
310
|
}
|
|
309
311
|
// Check for N+1 queries
|
|
@@ -314,15 +316,18 @@ class MetricsCollector {
|
|
|
314
316
|
}
|
|
315
317
|
}
|
|
316
318
|
// Check for large result sets
|
|
317
|
-
if (metrics.resultCount &&
|
|
319
|
+
if (metrics.resultCount &&
|
|
320
|
+
metrics.resultCount > this.config.largeResultSetThreshold) {
|
|
318
321
|
warnings.push({
|
|
319
322
|
type: 'large_result_set',
|
|
320
323
|
message: `Large result set: ${metrics.resultCount} rows returned`,
|
|
321
324
|
suggestion: 'Consider using pagination or filtering to reduce result size',
|
|
322
325
|
query: metrics.query,
|
|
323
326
|
table: metrics.table,
|
|
324
|
-
severity: metrics.resultCount > this.config.largeResultSetThreshold * 5
|
|
325
|
-
|
|
327
|
+
severity: metrics.resultCount > this.config.largeResultSetThreshold * 5
|
|
328
|
+
? 'high'
|
|
329
|
+
: 'medium',
|
|
330
|
+
timestamp: Date.now(),
|
|
326
331
|
});
|
|
327
332
|
}
|
|
328
333
|
// Log warnings
|
|
@@ -338,7 +343,7 @@ class MetricsCollector {
|
|
|
338
343
|
// Look for the same query executed multiple times in quick succession
|
|
339
344
|
if (recentQueries.length >= 5) {
|
|
340
345
|
const timeWindow = 5000; // 5 seconds
|
|
341
|
-
const recentInWindow = recentQueries.filter(q => Date.now() - q.timestamp < timeWindow);
|
|
346
|
+
const recentInWindow = recentQueries.filter((q) => Date.now() - q.timestamp < timeWindow);
|
|
342
347
|
if (recentInWindow.length >= 5) {
|
|
343
348
|
return {
|
|
344
349
|
type: 'n_plus_one',
|
|
@@ -347,7 +352,7 @@ class MetricsCollector {
|
|
|
347
352
|
query: metrics.query,
|
|
348
353
|
table: metrics.table,
|
|
349
354
|
severity: 'high',
|
|
350
|
-
timestamp: Date.now()
|
|
355
|
+
timestamp: Date.now(),
|
|
351
356
|
};
|
|
352
357
|
}
|
|
353
358
|
}
|
|
@@ -387,11 +392,11 @@ class MetricsCollector {
|
|
|
387
392
|
*/
|
|
388
393
|
getWarningEmoji(type) {
|
|
389
394
|
const emojis = {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
+
n_plus_one: '🔄',
|
|
396
|
+
missing_index: '📇',
|
|
397
|
+
slow_query: '🐌',
|
|
398
|
+
full_table_scan: '🔍',
|
|
399
|
+
large_result_set: '📊',
|
|
395
400
|
};
|
|
396
401
|
return emojis[type] || '⚠️';
|
|
397
402
|
}
|
|
@@ -425,7 +430,7 @@ class MetricsCollector {
|
|
|
425
430
|
.map(([query, { totalTime, count }]) => ({
|
|
426
431
|
query,
|
|
427
432
|
averageTime: totalTime / count,
|
|
428
|
-
count
|
|
433
|
+
count,
|
|
429
434
|
}))
|
|
430
435
|
.sort((a, b) => b.averageTime - a.averageTime)
|
|
431
436
|
.slice(0, 10);
|
|
@@ -447,7 +452,7 @@ class MetricsCollector {
|
|
|
447
452
|
.map(([pattern, { totalTime, count }]) => ({
|
|
448
453
|
pattern,
|
|
449
454
|
frequency: count,
|
|
450
|
-
averageTime: totalTime / count
|
|
455
|
+
averageTime: totalTime / count,
|
|
451
456
|
}))
|
|
452
457
|
.sort((a, b) => b.frequency - a.frequency)
|
|
453
458
|
.slice(0, 20);
|
|
@@ -458,22 +463,22 @@ class MetricsCollector {
|
|
|
458
463
|
startCleanupTimer() {
|
|
459
464
|
this.cleanupTimer = setInterval(() => {
|
|
460
465
|
this.cleanup();
|
|
461
|
-
this.save().catch(err => this.logger.warn('Failed to auto-save metrics:', err));
|
|
466
|
+
this.save().catch((err) => this.logger.warn('Failed to auto-save metrics:', err));
|
|
462
467
|
}, 60000); // Cleanup and save every minute
|
|
463
468
|
}
|
|
464
469
|
/**
|
|
465
470
|
* Cleanup old data
|
|
466
471
|
*/
|
|
467
472
|
cleanup() {
|
|
468
|
-
const cutoffTime = Date.now() -
|
|
473
|
+
const cutoffTime = Date.now() - this.config.warningRetentionDays * 24 * 60 * 60 * 1000;
|
|
469
474
|
// Remove old warnings
|
|
470
|
-
this.warnings = this.warnings.filter(w => w.timestamp > cutoffTime);
|
|
475
|
+
this.warnings = this.warnings.filter((w) => w.timestamp > cutoffTime);
|
|
471
476
|
// Remove old queries (keep last 24 hours)
|
|
472
|
-
const queryCutoff = Date.now() -
|
|
473
|
-
this.queryHistory = this.queryHistory.filter(q => q.timestamp > queryCutoff);
|
|
477
|
+
const queryCutoff = Date.now() - 24 * 60 * 60 * 1000;
|
|
478
|
+
this.queryHistory = this.queryHistory.filter((q) => q.timestamp > queryCutoff);
|
|
474
479
|
// Clean up recent queries
|
|
475
480
|
for (const [query, queries] of this.recentQueries.entries()) {
|
|
476
|
-
const recent = queries.filter(q => q.timestamp > queryCutoff);
|
|
481
|
+
const recent = queries.filter((q) => q.timestamp > queryCutoff);
|
|
477
482
|
if (recent.length === 0) {
|
|
478
483
|
this.recentQueries.delete(query);
|
|
479
484
|
}
|
|
@@ -23,7 +23,7 @@ class QueryParser {
|
|
|
23
23
|
groupByColumns: this.extractGroupByColumns(normalized),
|
|
24
24
|
hasLimit: lowerQuery.includes('limit'),
|
|
25
25
|
hasAggregates: this.hasAggregates(normalized),
|
|
26
|
-
isComplex: this.isComplexQuery(normalized)
|
|
26
|
+
isComplex: this.isComplexQuery(normalized),
|
|
27
27
|
};
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
@@ -60,7 +60,7 @@ class QueryParser {
|
|
|
60
60
|
// FROM clause
|
|
61
61
|
const fromMatches = query.match(/from\s+(\w+)/gi);
|
|
62
62
|
if (fromMatches) {
|
|
63
|
-
fromMatches.forEach(match => {
|
|
63
|
+
fromMatches.forEach((match) => {
|
|
64
64
|
const table = match.split(/\s+/)[1];
|
|
65
65
|
if (table)
|
|
66
66
|
tables.add(table);
|
|
@@ -69,7 +69,7 @@ class QueryParser {
|
|
|
69
69
|
// JOIN clauses
|
|
70
70
|
const joinMatches = query.match(/join\s+(\w+)/gi);
|
|
71
71
|
if (joinMatches) {
|
|
72
|
-
joinMatches.forEach(match => {
|
|
72
|
+
joinMatches.forEach((match) => {
|
|
73
73
|
const table = match.split(/\s+/)[1];
|
|
74
74
|
if (table)
|
|
75
75
|
tables.add(table);
|
|
@@ -87,7 +87,7 @@ class QueryParser {
|
|
|
87
87
|
const whereClause = whereMatch[1];
|
|
88
88
|
const columnMatches = whereClause.match(/(\w+)\s*[=<>!]/g);
|
|
89
89
|
return columnMatches
|
|
90
|
-
? columnMatches.map(match => match.split(/\s+/)[0]).filter(Boolean)
|
|
90
|
+
? columnMatches.map((match) => match.split(/\s+/)[0]).filter(Boolean)
|
|
91
91
|
: [];
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
@@ -116,7 +116,7 @@ class QueryParser {
|
|
|
116
116
|
return [];
|
|
117
117
|
return orderByMatch[1]
|
|
118
118
|
.split(',')
|
|
119
|
-
.map(col => col.trim().split(/\s+/)[0])
|
|
119
|
+
.map((col) => col.trim().split(/\s+/)[0])
|
|
120
120
|
.filter(Boolean);
|
|
121
121
|
}
|
|
122
122
|
/**
|
|
@@ -128,7 +128,7 @@ class QueryParser {
|
|
|
128
128
|
return [];
|
|
129
129
|
return groupByMatch[1]
|
|
130
130
|
.split(',')
|
|
131
|
-
.map(col => col.trim())
|
|
131
|
+
.map((col) => col.trim())
|
|
132
132
|
.filter(Boolean);
|
|
133
133
|
}
|
|
134
134
|
/**
|
|
@@ -136,25 +136,25 @@ class QueryParser {
|
|
|
136
136
|
*/
|
|
137
137
|
static hasAggregates(query) {
|
|
138
138
|
const lowerQuery = query.toLowerCase();
|
|
139
|
-
return lowerQuery.includes('group by') ||
|
|
139
|
+
return (lowerQuery.includes('group by') ||
|
|
140
140
|
lowerQuery.includes('having') ||
|
|
141
141
|
lowerQuery.includes('count(') ||
|
|
142
142
|
lowerQuery.includes('sum(') ||
|
|
143
143
|
lowerQuery.includes('avg(') ||
|
|
144
144
|
lowerQuery.includes('max(') ||
|
|
145
|
-
lowerQuery.includes('min(');
|
|
145
|
+
lowerQuery.includes('min('));
|
|
146
146
|
}
|
|
147
147
|
/**
|
|
148
148
|
* Check if query is complex (joins, subqueries, etc.)
|
|
149
149
|
*/
|
|
150
150
|
static isComplexQuery(query) {
|
|
151
151
|
const lowerQuery = query.toLowerCase();
|
|
152
|
-
return lowerQuery.includes('join') ||
|
|
152
|
+
return (lowerQuery.includes('join') ||
|
|
153
153
|
lowerQuery.includes('subquery') ||
|
|
154
154
|
lowerQuery.includes('union') ||
|
|
155
155
|
lowerQuery.includes('except') ||
|
|
156
156
|
lowerQuery.includes('intersect') ||
|
|
157
|
-
(lowerQuery.includes('select') && lowerQuery.includes('('));
|
|
157
|
+
(lowerQuery.includes('select') && lowerQuery.includes('(')));
|
|
158
158
|
}
|
|
159
159
|
/**
|
|
160
160
|
* Check if query could return many rows
|
|
@@ -193,7 +193,7 @@ class QueryParser {
|
|
|
193
193
|
*/
|
|
194
194
|
static generateCacheKey(query, params = []) {
|
|
195
195
|
const normalized = this.normalizeQuery(query);
|
|
196
|
-
const paramsKey = params.map(p => String(p)).join('|');
|
|
196
|
+
const paramsKey = params.map((p) => String(p)).join('|');
|
|
197
197
|
return `${normalized}:${paramsKey}`;
|
|
198
198
|
}
|
|
199
199
|
/**
|
|
@@ -239,12 +239,11 @@ class QueryParser {
|
|
|
239
239
|
original: query,
|
|
240
240
|
frequency: 1,
|
|
241
241
|
averageTime: executionTime,
|
|
242
|
-
tables: parsed.tables
|
|
242
|
+
tables: parsed.tables,
|
|
243
243
|
});
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
|
-
return Array.from(patternMap.values())
|
|
247
|
-
.sort((a, b) => b.frequency - a.frequency);
|
|
246
|
+
return Array.from(patternMap.values()).sort((a, b) => b.frequency - a.frequency);
|
|
248
247
|
}
|
|
249
248
|
/**
|
|
250
249
|
* Detect potential N+1 query patterns
|
|
@@ -270,7 +269,7 @@ class QueryParser {
|
|
|
270
269
|
let maxCountInWindow = 0;
|
|
271
270
|
for (const query of queries) {
|
|
272
271
|
const windowStart = query.timestamp - windowMs;
|
|
273
|
-
const countInWindow = queries.filter(q => q.timestamp >= windowStart && q.timestamp <= query.timestamp).length;
|
|
272
|
+
const countInWindow = queries.filter((q) => q.timestamp >= windowStart && q.timestamp <= query.timestamp).length;
|
|
274
273
|
maxCountInWindow = Math.max(maxCountInWindow, countInWindow);
|
|
275
274
|
}
|
|
276
275
|
if (maxCountInWindow >= 5) {
|
|
@@ -283,7 +282,7 @@ class QueryParser {
|
|
|
283
282
|
pattern,
|
|
284
283
|
count: maxCountInWindow,
|
|
285
284
|
timeWindow: windowMs,
|
|
286
|
-
severity
|
|
285
|
+
severity,
|
|
287
286
|
});
|
|
288
287
|
break; // Only report the most severe pattern
|
|
289
288
|
}
|
|
@@ -41,7 +41,7 @@ class RelationshipEngine {
|
|
|
41
41
|
*/
|
|
42
42
|
async loadEntityRelationships(entity, relations) {
|
|
43
43
|
for (const relationName of relations) {
|
|
44
|
-
const relationship = this.relationships.find(r => r.name === relationName);
|
|
44
|
+
const relationship = this.relationships.find((r) => r.name === relationName);
|
|
45
45
|
if (!relationship)
|
|
46
46
|
continue;
|
|
47
47
|
await this.loadSingleRelationship(entity, relationship);
|
|
@@ -52,7 +52,7 @@ class RelationshipEngine {
|
|
|
52
52
|
*/
|
|
53
53
|
async batchLoadRelationships(entities, relations) {
|
|
54
54
|
for (const relationName of relations) {
|
|
55
|
-
const relationship = this.relationships.find(r => r.name === relationName);
|
|
55
|
+
const relationship = this.relationships.find((r) => r.name === relationName);
|
|
56
56
|
if (!relationship)
|
|
57
57
|
continue;
|
|
58
58
|
await this.batchLoadSingleRelationship(entities, relationship);
|
|
@@ -85,6 +85,7 @@ class RelationshipEngine {
|
|
|
85
85
|
relatedData = await this.loadManyToManyRelationship(entity, relationship);
|
|
86
86
|
break;
|
|
87
87
|
}
|
|
88
|
+
;
|
|
88
89
|
entity[relationship.name] = relatedData;
|
|
89
90
|
}
|
|
90
91
|
/**
|
|
@@ -92,8 +93,8 @@ class RelationshipEngine {
|
|
|
92
93
|
*/
|
|
93
94
|
async batchLoadSingleRelationship(entities, relationship) {
|
|
94
95
|
const entityValues = entities
|
|
95
|
-
.map(e => e[relationship.fromColumn])
|
|
96
|
-
.filter(v => v !== undefined && v !== null);
|
|
96
|
+
.map((e) => e[relationship.fromColumn])
|
|
97
|
+
.filter((v) => v !== undefined && v !== null);
|
|
97
98
|
if (entityValues.length === 0)
|
|
98
99
|
return;
|
|
99
100
|
let relatedData;
|
|
@@ -138,6 +139,7 @@ class RelationshipEngine {
|
|
|
138
139
|
else {
|
|
139
140
|
entityRelatedData = groupedData.get(entityValue) || [];
|
|
140
141
|
}
|
|
142
|
+
;
|
|
141
143
|
entity[relationship.name] = entityRelatedData;
|
|
142
144
|
}
|
|
143
145
|
}
|
|
@@ -167,8 +169,8 @@ class RelationshipEngine {
|
|
|
167
169
|
throw new Error('Many-to-many relationship requires throughTable');
|
|
168
170
|
}
|
|
169
171
|
const entityValues = entities
|
|
170
|
-
.map(e => e[relationship.fromColumn])
|
|
171
|
-
.filter(v => v !== undefined && v !== null);
|
|
172
|
+
.map((e) => e[relationship.fromColumn])
|
|
173
|
+
.filter((v) => v !== undefined && v !== null);
|
|
172
174
|
if (entityValues.length === 0)
|
|
173
175
|
return [];
|
|
174
176
|
return await this.db
|
|
@@ -182,7 +184,7 @@ class RelationshipEngine {
|
|
|
182
184
|
* Get relationships for a specific table
|
|
183
185
|
*/
|
|
184
186
|
getRelationshipsForTable(tableName) {
|
|
185
|
-
return this.relationships.filter(r => r.fromTable === tableName);
|
|
187
|
+
return this.relationships.filter((r) => r.fromTable === tableName);
|
|
186
188
|
}
|
|
187
189
|
/**
|
|
188
190
|
* Get all relationships
|
|
@@ -200,7 +202,7 @@ class RelationshipEngine {
|
|
|
200
202
|
* Remove a relationship
|
|
201
203
|
*/
|
|
202
204
|
removeRelationship(relationshipName) {
|
|
203
|
-
this.relationships = this.relationships.filter(r => r.name !== relationshipName);
|
|
205
|
+
this.relationships = this.relationships.filter((r) => r.name !== relationshipName);
|
|
204
206
|
}
|
|
205
207
|
}
|
|
206
208
|
exports.RelationshipEngine = RelationshipEngine;
|