Returns cached ahead/behind/lastSyncAt from the GitHost plus a live count of active (non-trashed) manifest items. No network call — sync is user-initiated; the sync handler records lastSyncAt (unix seconds). ahead/behind stay 0 in the extension (writes go straight to the host, no local commit graph) and exist for parity with relicario status. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
3.8 KiB
TypeScript
94 lines
3.8 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>;
|
||
|
||
/// Like writeFile, but throws if the file already exists. Used by setup
|
||
/// wizard to refuse to clobber existing vault state. Implementation must
|
||
/// pre-check existence and only POST/PUT-create — never include a sha.
|
||
writeFileCreateOnly(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>;
|
||
|
||
/// Cached sync metadata, populated by the `sync` handler — get_vault_status
|
||
/// reads these without any network call. lastSyncAt is unix SECONDS (or null
|
||
/// until the first sync). ahead/behind exist for parity with `relicario
|
||
/// status`; the extension writes straight to the host (no local commit
|
||
/// graph), so in practice they stay 0.
|
||
lastSyncAt: number | null;
|
||
ahead: number;
|
||
behind: number;
|
||
}
|
||
|
||
/// 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);
|
||
}
|