/** * GitHub API Service * Fetches files from a public GitHub repository directly from the browser. * No proxy or token required for public repos. */ const GITHUB_API = 'https://api.github.com'; const GITHUB_RAW = 'https://raw.githubusercontent.com'; /** * List markdown/text files in a GitHub repository folder. * @param {string} owner - e.g. "respellion" * @param {string} repo - e.g. "employee-handbook" * @param {string} folder - e.g. "docs" or "" for root * @param {string} branch - e.g. "main" * @returns {Promise>} */ export async function getRepoFolder(owner, repo, folder = '', branch = 'main') { // Use the GitHub Git Trees API to fetch all files recursively in one request const url = `${GITHUB_API}/repos/${owner}/${repo}/git/trees/${branch}?recursive=1`; const res = await fetch(url, { headers: { Accept: 'application/vnd.github+json' }, }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error(`GitHub API ${res.status}: ${err.message || 'Could not list folder'}`); } const data = await res.json(); // If folder is 'docs', we only want paths starting with 'docs/' const folderPrefix = folder ? folder.replace(/^\/|\/$/g, '') + '/' : ''; // Return only files (blobs) that match the folder prefix and file extensions return data.tree .filter(item => item.type === 'blob' && item.path.startsWith(folderPrefix) && (item.path.endsWith('.md') || item.path.endsWith('.txt')) ) .map(item => ({ name: item.path.split('/').pop(), path: item.path, sha: item.sha })); } /** * Fetch the raw text content of a file. * Uses raw.githubusercontent.com — no rate limits for public content. * @param {string} owner * @param {string} repo * @param {string} path - e.g. "docs/ROLES.md" * @param {string} branch * @returns {Promise} */ export async function getFileContent(owner, repo, path, branch = 'main') { const url = `${GITHUB_RAW}/${owner}/${repo}/${branch}/${path}`; const res = await fetch(url); if (!res.ok) { throw new Error(`Could not fetch "${path}" from GitHub (${res.status})`); } return res.text(); } /** * Parse a GitHub tree URL into its parts. * e.g. "https://github.com/respellion/employee-handbook/tree/main/docs" * → { owner: 'respellion', repo: 'employee-handbook', branch: 'main', folder: 'docs' } */ export function parseGitHubUrl(url) { try { const match = url.match(/github\.com\/([^/]+)\/([^/]+)(?:\/tree\/([^/]+)(?:\/(.+))?)?/); if (!match) return null; return { owner: match[1], repo: match[2], branch: match[3] || 'main', folder: match[4] || '', }; } catch { return null; } }