import { isRecord } from '@/lib/guards' import type { RemoteFile } from '@/lib/types' import { decodeBase64Content, encodePath, getRepoApiPath, isHttpError, requestGitJson } from './http' import { isLfsFile, parseLfsPointer } from './lfs' import type { GitRemoteConfig } from './types' interface GitContentEntry { content?: string name: string path?: string sha?: string size?: number type?: string } function isGitContentEntry(value: unknown): value is GitContentEntry { return isRecord(value) && typeof value.name === 'string' && (value.path === undefined || typeof value.path === 'string') && (value.sha === undefined || typeof value.sha === 'string') && (value.size === undefined || typeof value.size === 'number') && (value.content === undefined || typeof value.content === 'string') && (value.type === undefined || typeof value.type === 'string') } function getContentEntrySize(entry: GitContentEntry) { return entry.size ?? 0 } async function getRemoteContent(remote: GitRemoteConfig, path: string, branch: string) { const query = new URLSearchParams({ ref: branch }) return requestGitJson(remote, `${getRepoApiPath(remote)}/contents/${encodePath(path)}?${query.toString()}`) } export function getRequiredContentEntrySha(entry: GitContentEntry, path: string) { if (!entry.sha) { throw new Error(`SHA Git manquant pour ${path}`) } return entry.sha } export async function getRemoteFileEntry(remote: GitRemoteConfig, path: string, branch: string): Promise { try { const data = await getRemoteContent(remote, path, branch) return isGitContentEntry(data) ? data : null } catch (err) { if (isHttpError(err) && err.status === 404) return null throw err } } export async function readRemoteFolder( remote: GitRemoteConfig, branch: string, folderPath: string, ): Promise<{ exists: boolean; files: RemoteFile[] }> { try { const data = await getRemoteContent(remote, folderPath, branch) if (!Array.isArray(data)) { throw new Error(`Le chemin distant ${folderPath} existe mais ce n'est pas un dossier`) } const files: RemoteFile[] = await Promise.all( data.map(async (entry: unknown): Promise => { if (!isGitContentEntry(entry)) { throw new Error(`Reponse Git invalide pour ${folderPath}`) } const size = getContentEntrySize(entry) if (!isLfsFile(entry.name) || size > 1024) { return { name: entry.name, size } } try { const fileData = await getRemoteFileEntry(remote, `${folderPath}/${entry.name}`, branch) if (fileData?.content) { const content = decodeBase64Content(fileData.content) const pointer = parseLfsPointer(content) if (pointer) { return { name: entry.name, size: pointer.size } } } } catch (err) { if (!isHttpError(err) || err.status !== 404) throw err } return { name: entry.name, size } }), ) return { exists: true, files } } catch (err: unknown) { if (isHttpError(err) && err.status === 404) { return { exists: false, files: [] } } throw err } }