refactor: split git provider adapters

This commit is contained in:
Tom Boullay
2026-05-17 14:12:09 +02:00
parent 377ed7cfb3
commit 81c513ee1f
13 changed files with 779 additions and 644 deletions
+102
View File
@@ -0,0 +1,102 @@
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<GitContentEntry | null> {
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<RemoteFile> => {
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
}
}