* Index a single file
(relativePath: string)
| 1703 | * Index a single file |
| 1704 | */ |
| 1705 | async indexFile(relativePath: string): Promise<ExtractionResult> { |
| 1706 | // Indexing read: follow in-root symlinks (the `../` guard still applies), #935. |
| 1707 | const fullPath = validatePathWithinRoot(this.rootDir, relativePath, { allowSymlinkEscape: true }); |
| 1708 | |
| 1709 | if (!fullPath) { |
| 1710 | return { |
| 1711 | nodes: [], |
| 1712 | edges: [], |
| 1713 | unresolvedReferences: [], |
| 1714 | errors: [{ message: `Path traversal blocked: ${relativePath}`, filePath: relativePath, severity: 'error', code: 'path_traversal' }], |
| 1715 | durationMs: 0, |
| 1716 | }; |
| 1717 | } |
| 1718 | |
| 1719 | // Read file content and stats |
| 1720 | let content: string; |
| 1721 | let stats: fs.Stats; |
| 1722 | try { |
| 1723 | stats = await fsp.stat(fullPath); |
| 1724 | content = await fsp.readFile(fullPath, 'utf-8'); |
| 1725 | } catch (error) { |
| 1726 | return { |
| 1727 | nodes: [], |
| 1728 | edges: [], |
| 1729 | unresolvedReferences: [], |
| 1730 | errors: [ |
| 1731 | { |
| 1732 | message: `Failed to read file: ${error instanceof Error ? error.message : String(error)}`, |
| 1733 | filePath: relativePath, |
| 1734 | severity: 'error', |
| 1735 | code: 'read_error', |
| 1736 | }, |
| 1737 | ], |
| 1738 | durationMs: 0, |
| 1739 | }; |
| 1740 | } |
| 1741 | |
| 1742 | return this.indexFileWithContent(relativePath, content, stats); |
| 1743 | } |
| 1744 | |
| 1745 | /** |
| 1746 | * Index a single file with pre-read content and stats. |
no test coverage detected