magector 1.0.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,355 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Comprehensive benchmark suite for Magector
4
+ */
5
+
6
+ import { MagentoIndexer } from '../indexer.js';
7
+ import { generateCompleteMockModule, MOCK_MODULES } from './test-data-generator.js';
8
+ import { TEST_QUERIES } from './test-queries.js';
9
+ import { writeFile, mkdir, rm } from 'fs/promises';
10
+ import { existsSync } from 'fs';
11
+ import path from 'path';
12
+
13
+ const BENCHMARK_DIR = '/private/tmp/magector-benchmark';
14
+
15
+ class BenchmarkSuite {
16
+ constructor() {
17
+ this.results = {
18
+ indexing: {},
19
+ search: {},
20
+ memory: {},
21
+ gnn: {}
22
+ };
23
+ }
24
+
25
+ async setup(moduleCount = 5) {
26
+ console.log(`\nSetting up benchmark with ${moduleCount} modules...`);
27
+
28
+ if (existsSync(BENCHMARK_DIR)) {
29
+ await rm(BENCHMARK_DIR, { recursive: true });
30
+ }
31
+ await mkdir(`${BENCHMARK_DIR}/app/code`, { recursive: true });
32
+
33
+ let totalFiles = 0;
34
+ const modules = MOCK_MODULES.slice(0, moduleCount);
35
+
36
+ for (const moduleName of modules) {
37
+ const files = generateCompleteMockModule(moduleName);
38
+ totalFiles += files.length;
39
+
40
+ for (const file of files) {
41
+ const filePath = path.join(BENCHMARK_DIR, file.path);
42
+ await mkdir(path.dirname(filePath), { recursive: true });
43
+ await writeFile(filePath, file.content);
44
+ }
45
+ }
46
+
47
+ console.log(` Generated ${totalFiles} files in ${moduleCount} modules`);
48
+ return { modules: moduleCount, files: totalFiles };
49
+ }
50
+
51
+ async benchmarkIndexing() {
52
+ console.log('\nšŸ“Š Benchmarking Indexing Performance...');
53
+
54
+ const configurations = [
55
+ { name: 'Basic (no GNN)', enableGNN: false },
56
+ { name: 'Full GNN', enableGNN: true }
57
+ ];
58
+
59
+ for (const config of configurations) {
60
+ const dbPath = `${BENCHMARK_DIR}/bench-${config.name.replace(/\s/g, '-')}.db`;
61
+ const graphPath = `${BENCHMARK_DIR}/bench-${config.name.replace(/\s/g, '-')}-graph.json`;
62
+
63
+ const indexer = new MagentoIndexer({
64
+ dbPath,
65
+ graphPath,
66
+ magentoRoot: BENCHMARK_DIR,
67
+ enableGNN: config.enableGNN
68
+ });
69
+
70
+ await indexer.init();
71
+
72
+ const startTime = Date.now();
73
+ const startMemory = process.memoryUsage().heapUsed;
74
+
75
+ const stats = await indexer.indexDirectory();
76
+
77
+ const endTime = Date.now();
78
+ const endMemory = process.memoryUsage().heapUsed;
79
+
80
+ this.results.indexing[config.name] = {
81
+ duration: endTime - startTime,
82
+ filesIndexed: stats.indexed,
83
+ filesPerSecond: stats.indexed / ((endTime - startTime) / 1000),
84
+ memoryUsed: (endMemory - startMemory) / 1024 / 1024,
85
+ graphNodes: stats.graphNodes || 0,
86
+ graphEdges: stats.graphEdges || 0
87
+ };
88
+
89
+ console.log(` ${config.name}: ${endTime - startTime}ms (${stats.indexed} files)`);
90
+ }
91
+
92
+ return this.results.indexing;
93
+ }
94
+
95
+ async benchmarkSearch() {
96
+ console.log('\nšŸ“Š Benchmarking Search Performance...');
97
+
98
+ const indexer = new MagentoIndexer({
99
+ dbPath: `${BENCHMARK_DIR}/bench-Full-GNN.db`,
100
+ graphPath: `${BENCHMARK_DIR}/bench-Full-GNN-graph.json`,
101
+ magentoRoot: BENCHMARK_DIR,
102
+ enableGNN: true
103
+ });
104
+ await indexer.init();
105
+
106
+ // Warm-up
107
+ for (let i = 0; i < 10; i++) {
108
+ await indexer.search('test query', { limit: 10 });
109
+ }
110
+
111
+ const searchTypes = [
112
+ { name: 'Simple search', method: 'search', iterations: 100 },
113
+ { name: 'Graph-enhanced', method: 'searchWithGraph', iterations: 100 }
114
+ ];
115
+
116
+ for (const searchType of searchTypes) {
117
+ const latencies = [];
118
+
119
+ for (let i = 0; i < searchType.iterations; i++) {
120
+ const query = TEST_QUERIES[i % TEST_QUERIES.length];
121
+ const start = process.hrtime.bigint();
122
+
123
+ if (searchType.method === 'search') {
124
+ await indexer.search(query.query, { limit: 10 });
125
+ } else {
126
+ await indexer.searchWithGraph(query.query, { limit: 10 });
127
+ }
128
+
129
+ const end = process.hrtime.bigint();
130
+ latencies.push(Number(end - start) / 1e6); // Convert to ms
131
+ }
132
+
133
+ latencies.sort((a, b) => a - b);
134
+
135
+ this.results.search[searchType.name] = {
136
+ iterations: searchType.iterations,
137
+ min: latencies[0].toFixed(2),
138
+ max: latencies[latencies.length - 1].toFixed(2),
139
+ avg: (latencies.reduce((a, b) => a + b, 0) / latencies.length).toFixed(2),
140
+ p50: latencies[Math.floor(latencies.length * 0.5)].toFixed(2),
141
+ p90: latencies[Math.floor(latencies.length * 0.9)].toFixed(2),
142
+ p99: latencies[Math.floor(latencies.length * 0.99)].toFixed(2),
143
+ throughput: (1000 / (latencies.reduce((a, b) => a + b, 0) / latencies.length)).toFixed(1)
144
+ };
145
+
146
+ console.log(` ${searchType.name}: avg ${this.results.search[searchType.name].avg}ms, p99 ${this.results.search[searchType.name].p99}ms`);
147
+ }
148
+
149
+ return this.results.search;
150
+ }
151
+
152
+ async benchmarkMemory() {
153
+ console.log('\nšŸ“Š Benchmarking Memory Usage...');
154
+
155
+ global.gc && global.gc();
156
+ const baseline = process.memoryUsage();
157
+
158
+ const indexer = new MagentoIndexer({
159
+ dbPath: `${BENCHMARK_DIR}/bench-memory.db`,
160
+ graphPath: `${BENCHMARK_DIR}/bench-memory-graph.json`,
161
+ magentoRoot: BENCHMARK_DIR,
162
+ enableGNN: true
163
+ });
164
+
165
+ await indexer.init();
166
+ const afterInit = process.memoryUsage();
167
+
168
+ await indexer.indexDirectory();
169
+ const afterIndex = process.memoryUsage();
170
+
171
+ // Run searches
172
+ for (let i = 0; i < 100; i++) {
173
+ await indexer.searchWithGraph(TEST_QUERIES[i % TEST_QUERIES.length].query, { limit: 10 });
174
+ }
175
+ const afterSearch = process.memoryUsage();
176
+
177
+ this.results.memory = {
178
+ baseline: {
179
+ heapUsed: (baseline.heapUsed / 1024 / 1024).toFixed(2),
180
+ heapTotal: (baseline.heapTotal / 1024 / 1024).toFixed(2),
181
+ rss: (baseline.rss / 1024 / 1024).toFixed(2)
182
+ },
183
+ afterInit: {
184
+ heapUsed: (afterInit.heapUsed / 1024 / 1024).toFixed(2),
185
+ heapTotal: (afterInit.heapTotal / 1024 / 1024).toFixed(2),
186
+ delta: ((afterInit.heapUsed - baseline.heapUsed) / 1024 / 1024).toFixed(2)
187
+ },
188
+ afterIndex: {
189
+ heapUsed: (afterIndex.heapUsed / 1024 / 1024).toFixed(2),
190
+ heapTotal: (afterIndex.heapTotal / 1024 / 1024).toFixed(2),
191
+ delta: ((afterIndex.heapUsed - afterInit.heapUsed) / 1024 / 1024).toFixed(2)
192
+ },
193
+ afterSearch: {
194
+ heapUsed: (afterSearch.heapUsed / 1024 / 1024).toFixed(2),
195
+ heapTotal: (afterSearch.heapTotal / 1024 / 1024).toFixed(2),
196
+ delta: ((afterSearch.heapUsed - afterIndex.heapUsed) / 1024 / 1024).toFixed(2)
197
+ }
198
+ };
199
+
200
+ console.log(` Heap after indexing: ${this.results.memory.afterIndex.heapUsed}MB`);
201
+ console.log(` Heap after 100 searches: ${this.results.memory.afterSearch.heapUsed}MB`);
202
+
203
+ return this.results.memory;
204
+ }
205
+
206
+ async benchmarkGNN() {
207
+ console.log('\nšŸ“Š Benchmarking GNN Features...');
208
+
209
+ const indexer = new MagentoIndexer({
210
+ dbPath: `${BENCHMARK_DIR}/bench-Full-GNN.db`,
211
+ graphPath: `${BENCHMARK_DIR}/bench-Full-GNN-graph.json`,
212
+ magentoRoot: BENCHMARK_DIR,
213
+ enableGNN: true
214
+ });
215
+ await indexer.init();
216
+
217
+ // Test graph loading
218
+ const startLoad = Date.now();
219
+ const graph = await indexer.loadGraph();
220
+ const loadTime = Date.now() - startLoad;
221
+
222
+ // Test dependency finding
223
+ const depLatencies = [];
224
+ const testClasses = ['Index', 'ItemRepository', 'ProductSaveObserver', 'ItemList'];
225
+
226
+ for (const className of testClasses) {
227
+ const start = Date.now();
228
+ await indexer.findDependencies(className);
229
+ depLatencies.push(Date.now() - start);
230
+ }
231
+
232
+ // Test graph traversal
233
+ const traversalStart = Date.now();
234
+ const edges = graph.edges.filter(e => e.type === 'extends').slice(0, 100);
235
+ const traversalTime = Date.now() - traversalStart;
236
+
237
+ this.results.gnn = {
238
+ graphStats: {
239
+ nodes: graph.nodes.length,
240
+ edges: graph.edges.length,
241
+ nodeTypes: [...new Set(graph.nodes.map(n => n.type))],
242
+ edgeTypes: [...new Set(graph.edges.map(e => e.type))]
243
+ },
244
+ performance: {
245
+ graphLoadTime: loadTime,
246
+ avgDependencyLookup: (depLatencies.reduce((a, b) => a + b, 0) / depLatencies.length).toFixed(2),
247
+ graphTraversalTime: traversalTime
248
+ }
249
+ };
250
+
251
+ console.log(` Graph nodes: ${graph.nodes.length}, edges: ${graph.edges.length}`);
252
+ console.log(` Graph load: ${loadTime}ms, dependency lookup: ${this.results.gnn.performance.avgDependencyLookup}ms`);
253
+
254
+ return this.results.gnn;
255
+ }
256
+
257
+ async generateReport() {
258
+ const report = `
259
+ ╔══════════════════════════════════════════════════════════════════════════════╗
260
+ ā•‘ MAGECTOR BENCHMARK REPORT ā•‘
261
+ ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•
262
+
263
+ Generated: ${new Date().toISOString()}
264
+
265
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
266
+ INDEXING PERFORMANCE
267
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
268
+ ${Object.entries(this.results.indexing).map(([name, data]) => `
269
+ ${name}:
270
+ Duration: ${data.duration}ms
271
+ Files indexed: ${data.filesIndexed}
272
+ Files/second: ${data.filesPerSecond.toFixed(1)}
273
+ Memory delta: ${data.memoryUsed.toFixed(2)}MB
274
+ Graph nodes: ${data.graphNodes}
275
+ Graph edges: ${data.graphEdges}
276
+ `).join('')}
277
+
278
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
279
+ SEARCH PERFORMANCE
280
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
281
+ ${Object.entries(this.results.search).map(([name, data]) => `
282
+ ${name} (${data.iterations} iterations):
283
+ Min: ${data.min}ms
284
+ Max: ${data.max}ms
285
+ Average: ${data.avg}ms
286
+ P50: ${data.p50}ms
287
+ P90: ${data.p90}ms
288
+ P99: ${data.p99}ms
289
+ Throughput: ${data.throughput} queries/sec
290
+ `).join('')}
291
+
292
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
293
+ MEMORY USAGE
294
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
295
+ Baseline heap: ${this.results.memory.baseline.heapUsed}MB
296
+ After init: ${this.results.memory.afterInit.heapUsed}MB (+${this.results.memory.afterInit.delta}MB)
297
+ After indexing: ${this.results.memory.afterIndex.heapUsed}MB (+${this.results.memory.afterIndex.delta}MB)
298
+ After 100 searches: ${this.results.memory.afterSearch.heapUsed}MB (+${this.results.memory.afterSearch.delta}MB)
299
+
300
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
301
+ GNN FEATURES
302
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
303
+ Graph nodes: ${this.results.gnn.graphStats.nodes}
304
+ Graph edges: ${this.results.gnn.graphStats.edges}
305
+ Node types: ${this.results.gnn.graphStats.nodeTypes.join(', ')}
306
+ Edge types: ${this.results.gnn.graphStats.edgeTypes.join(', ')}
307
+
308
+ Graph load time: ${this.results.gnn.performance.graphLoadTime}ms
309
+ Avg dependency lookup: ${this.results.gnn.performance.avgDependencyLookup}ms
310
+ Graph traversal: ${this.results.gnn.performance.graphTraversalTime}ms
311
+
312
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
313
+ `;
314
+
315
+ await mkdir('./validation-results', { recursive: true });
316
+ await writeFile('./validation-results/benchmark-report.txt', report);
317
+ await writeFile('./validation-results/benchmark-results.json', JSON.stringify(this.results, null, 2));
318
+
319
+ return report;
320
+ }
321
+
322
+ async cleanup() {
323
+ if (existsSync(BENCHMARK_DIR)) {
324
+ await rm(BENCHMARK_DIR, { recursive: true });
325
+ }
326
+ }
327
+ }
328
+
329
+ async function runBenchmarks() {
330
+ const suite = new BenchmarkSuite();
331
+
332
+ try {
333
+ console.log('╔════════════════════════════════════════════════════════════════╗');
334
+ console.log('ā•‘ MAGECTOR BENCHMARK SUITE ā•‘');
335
+ console.log('ā•šā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•ā•');
336
+
337
+ await suite.setup(5);
338
+ await suite.benchmarkIndexing();
339
+ await suite.benchmarkSearch();
340
+ await suite.benchmarkMemory();
341
+ await suite.benchmarkGNN();
342
+
343
+ const report = await suite.generateReport();
344
+ console.log(report);
345
+
346
+ console.log('Results saved to:');
347
+ console.log(' - validation-results/benchmark-report.txt');
348
+ console.log(' - validation-results/benchmark-results.json\n');
349
+
350
+ } finally {
351
+ await suite.cleanup();
352
+ }
353
+ }
354
+
355
+ runBenchmarks().catch(console.error);