/// Abstract interface for reading/writing vault files on a git host. /// /// Both Gitea and GitHub expose a "repo contents" REST API that lets us /// read, write, and delete individual files without cloning the repo. /// This interface captures just the operations the vault needs. export interface GitHost { /// Read a single file from the repo, returning its raw bytes. readFile(path: string): Promise; /// Create or update a file in the repo with a commit message. writeFile(path: string, content: Uint8Array, message: string): Promise; /// Delete a file from the repo with a commit message. deleteFile(path: string, message: string): Promise; /// List file names in a directory (non-recursive). listDir(path: string): Promise; /// Best-effort: returns metadata for the most recent commit touching `path`. /// Returns null if the path has no commits, the API fails, or the host /// doesn't support the lookup. Callers must tolerate null. lastCommit(path: string): Promise<{ sha: string; author: string; date: string } | null>; /// Write an opaque binary blob to the repo. Optimized for large /// attachments — implementations switch from Contents API to Git /// Data API when content exceeds BLOB_THRESHOLD_BYTES. /// Returns the path that was written (same as input, for chaining). putBlob(path: string, content: Uint8Array, message: string): Promise; /// Read an opaque binary blob from the repo. Same semantics as /// readFile for current sizes. Distinct method so we can later add /// streaming/chunked reads for very large blobs. getBlob(path: string): Promise; /// Delete a blob from the repo. Currently identical to deleteFile; /// kept distinct for symmetry with putBlob. deleteBlob(path: string, message: string): Promise; } /// Pre-base64 byte size at which putBlob switches from Contents API to /// Git Data API. 900 KB leaves headroom for base64 inflation (1.33×) and /// JSON wrapping under both GitHub's and Gitea's Contents API soft-limits. export const BLOB_THRESHOLD_BYTES = 900 * 1024; /// Convert a Uint8Array to a base64 string (works in service worker context). export function uint8ArrayToBase64(bytes: Uint8Array): string { let binary = ''; for (let i = 0; i < bytes.length; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } /// Convert a base64 string to a Uint8Array. export function base64ToUint8Array(base64: string): Uint8Array { const binary = atob(base64); const bytes = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { bytes[i] = binary.charCodeAt(i); } return bytes; } /// Factory function that returns the appropriate GitHost implementation. import { GiteaHost } from './gitea'; import { GitHubHost } from './github'; export function createGitHost( hostType: 'gitea' | 'github', hostUrl: string, repoPath: string, apiToken: string, ): GitHost { if (hostType === 'gitea') { return new GiteaHost(hostUrl, repoPath, apiToken); } return new GitHubHost(repoPath, apiToken); }