()
| 383 | } |
| 384 | |
| 385 | async index(): Promise<IndexingStats> { |
| 386 | const startTime = Date.now(); |
| 387 | const stats: IndexingStats = { |
| 388 | totalFiles: 0, |
| 389 | indexedFiles: 0, |
| 390 | skippedFiles: 0, |
| 391 | totalChunks: 0, |
| 392 | totalLines: 0, |
| 393 | duration: 0, |
| 394 | avgChunkSize: 0, |
| 395 | componentsByType: {}, |
| 396 | componentsByLayer: { |
| 397 | presentation: 0, |
| 398 | business: 0, |
| 399 | data: 0, |
| 400 | state: 0, |
| 401 | core: 0, |
| 402 | shared: 0, |
| 403 | feature: 0, |
| 404 | infrastructure: 0, |
| 405 | unknown: 0 |
| 406 | }, |
| 407 | errors: [], |
| 408 | startedAt: new Date() |
| 409 | }; |
| 410 | |
| 411 | let stagingDir: string | null = null; |
| 412 | |
| 413 | try { |
| 414 | // Ensure there is at least a generic fallback analyzer registered when the indexer |
| 415 | // is used directly (e.g. in tests or standalone scripts). |
| 416 | if (analyzerRegistry.getAll().length === 0) { |
| 417 | const { GenericAnalyzer } = await import('../analyzers/generic/index.js'); |
| 418 | analyzerRegistry.register(new GenericAnalyzer()); |
| 419 | } |
| 420 | |
| 421 | const resolvedProjectOptions = this.getProjectOptions(); |
| 422 | |
| 423 | const buildId = randomUUID(); |
| 424 | const generatedAt = new Date().toISOString(); |
| 425 | const toolVersion = await getToolVersion(); |
| 426 | |
| 427 | // Phase 1: Scanning |
| 428 | this.updateProgress('scanning', 0); |
| 429 | let files = await this.scanFiles(); |
| 430 | |
| 431 | // Memory safety: limit total files to prevent heap exhaustion |
| 432 | const MAX_FILES = 10000; |
| 433 | if (files.length > MAX_FILES) { |
| 434 | console.warn( |
| 435 | `WARNING: Found ${files.length} files, limiting to ${MAX_FILES} to prevent memory issues.` |
| 436 | ); |
| 437 | console.warn( |
| 438 | `Consider using more specific include patterns or excluding large directories.` |
| 439 | ); |
| 440 | files = files.slice(0, MAX_FILES); |
| 441 | } |
| 442 |
no test coverage detected