80 lines
3.0 KiB
TypeScript
80 lines
3.0 KiB
TypeScript
/// 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<Uint8Array>;
|
||
|
||
/// Create or update a file in the repo with a commit message.
|
||
writeFile(path: string, content: Uint8Array, message: string): Promise<void>;
|
||
|
||
/// Delete a file from the repo with a commit message.
|
||
deleteFile(path: string, message: string): Promise<void>;
|
||
|
||
/// List file names in a directory (non-recursive).
|
||
listDir(path: string): Promise<string[]>;
|
||
|
||
/// 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<string>;
|
||
|
||
/// 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<Uint8Array>;
|
||
|
||
/// Delete a blob from the repo. Currently identical to deleteFile;
|
||
/// kept distinct for symmetry with putBlob.
|
||
deleteBlob(path: string, message: string): Promise<void>;
|
||
}
|
||
|
||
/// 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);
|
||
}
|