| 81 | } |
| 82 | |
| 83 | export async function fetchGitlabRepoFiles(url: string, onProgress?: (msg: string) => void) { |
| 84 | const match = url.match(/gitlab\.com\/([^?#]+)/i); |
| 85 | if (!match) throw new Error("Invalid GitLab URL"); |
| 86 | const path = match[1].replace(/\.git$/, "").replace(/\/-\/.*$/, "").replace(/\/$/, ""); |
| 87 | const parts = path.split("/").filter(Boolean); |
| 88 | if (parts.length < 2) throw new Error("Invalid GitLab project path"); |
| 89 | const projectPath = encodeURIComponent(parts.join("/")); |
| 90 | |
| 91 | let branch = "main"; |
| 92 | const projectRes = await fetch(`https://gitlab.com/api/v4/projects/${projectPath}`); |
| 93 | if (projectRes.ok) { |
| 94 | const projectData = await projectRes.json(); |
| 95 | if (projectData?.default_branch) branch = projectData.default_branch; |
| 96 | } |
| 97 | |
| 98 | const treeRes = await fetch( |
| 99 | `https://gitlab.com/api/v4/projects/${projectPath}/repository/tree?recursive=true&per_page=100&ref=${encodeURIComponent(branch)}` |
| 100 | ); |
| 101 | if (!treeRes.ok) throw new Error("Could not fetch GitLab repository tree."); |
| 102 | const treeData = await treeRes.json(); |
| 103 | const filePaths = (Array.isArray(treeData) ? treeData : []) |
| 104 | .filter((t: { type: string }) => t.type === "blob") |
| 105 | .map((t: { path: string }) => t.path) |
| 106 | .filter((p: string) => p.match(/\.(js|ts|jsx|tsx|py|c|h|cpp|hpp|cc|cs|go|rs|rb|php|swift|kt|kts|dart)$/) && !p.includes("node_modules") && !p.includes(".git")); |
| 107 | |
| 108 | if (onProgress) onProgress(`Found ${filePaths.length} files...`); |
| 109 | |
| 110 | const files: { path: string; content: string }[] = []; |
| 111 | for (let i = 0; i < Math.min(filePaths.length, 150); i += 10) { |
| 112 | const batch = filePaths.slice(i, i + 10); |
| 113 | await Promise.all(batch.map(async (filePath: string) => { |
| 114 | try { |
| 115 | const r = await fetch(`https://gitlab.com/${parts.join("/")}/-/raw/${branch}/${filePath}`); |
| 116 | if (r.ok) files.push({ path: filePath, content: await r.text() }); |
| 117 | } catch { |
| 118 | // skip failed files |
| 119 | } |
| 120 | })); |
| 121 | } |
| 122 | return files; |
| 123 | } |
| 124 | |
| 125 | export async function fetchGithubRepoFiles(url: string, onProgress?: (msg: string) => void) { |
| 126 | const match = url.match(/github\.com\/([^/]+)\/([^/]+)/); |