* Sync with current file state (incremental update) * * Uses a mutex to prevent concurrent indexing operations.
(options: IndexOptions = {})
| 539 | * Uses a mutex to prevent concurrent indexing operations. |
| 540 | */ |
| 541 | async sync(options: IndexOptions = {}): Promise<SyncResult> { |
| 542 | return this.indexMutex.withLock(async () => { |
| 543 | try { |
| 544 | this.fileLock.acquire(); |
| 545 | } catch { |
| 546 | return { filesChecked: 0, filesAdded: 0, filesModified: 0, filesRemoved: 0, nodesUpdated: 0, durationMs: 0 }; |
| 547 | } |
| 548 | try { |
| 549 | const result = await this.orchestrator.sync(options.onProgress); |
| 550 | |
| 551 | // Cross-file finalization (e.g. NestJS RouterModule prefixes). Run on |
| 552 | // every sync that touched files so edits to `app.module.ts` propagate |
| 553 | // to controllers in unchanged files. The pass is idempotent and cheap |
| 554 | // (regex over *.module.ts only). |
| 555 | if (result.filesAdded > 0 || result.filesModified > 0) { |
| 556 | this.resolver.runPostExtract(); |
| 557 | } |
| 558 | |
| 559 | // Resolve references if files were updated |
| 560 | if (result.filesAdded > 0 || result.filesModified > 0) { |
| 561 | if (result.changedFilePaths) { |
| 562 | // Scope resolution to changed files (git fast path — bounded set) |
| 563 | const unresolvedRefs = this.queries.getUnresolvedReferencesByFiles(result.changedFilePaths); |
| 564 | |
| 565 | options.onProgress?.({ |
| 566 | phase: 'resolving', |
| 567 | current: 0, |
| 568 | total: unresolvedRefs.length, |
| 569 | }); |
| 570 | |
| 571 | this.resolver.resolveAndPersist(unresolvedRefs, (current, total) => { |
| 572 | options.onProgress?.({ |
| 573 | phase: 'resolving', |
| 574 | current, |
| 575 | total, |
| 576 | }); |
| 577 | }); |
| 578 | } else { |
| 579 | // No git info — use batched resolution to avoid OOM |
| 580 | const unresolvedCount = this.queries.getUnresolvedReferencesCount(); |
| 581 | |
| 582 | options.onProgress?.({ |
| 583 | phase: 'resolving', |
| 584 | current: 0, |
| 585 | total: unresolvedCount, |
| 586 | }); |
| 587 | |
| 588 | await this.resolveReferencesBatched((current, total) => { |
| 589 | options.onProgress?.({ |
| 590 | phase: 'resolving', |
| 591 | current, |
| 592 | total, |
| 593 | }); |
| 594 | }); |
| 595 | } |
| 596 | |
| 597 | // Second pass: chained calls whose method lives on a supertype the |
| 598 | // receiver conforms to (protocol-extension / inherited). Needs the |
no test coverage detected