isogit-lfs/src/download.ts

105 lines
2.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import path from "path-browserify";
import { Buffer } from "buffer";
import { bodyToBuffer, getAuthHeader, isWriteable } from "./util";
import { Pointer } from "./pointers";
import { HTTPRequest, BasicAuth } from "./types";
import { PromiseFsClient } from "isomorphic-git";
interface LFSInfoResponse {
objects: {
actions: {
download: {
href: string;
header?: Record<string, string>;
};
};
}[];
}
function isValidLFSInfoResponseData(
val: Record<string, any>
): val is LFSInfoResponse {
return val.objects?.[0]?.actions?.download?.href?.trim !== undefined;
}
/**
* Downloads, caches and returns a blob corresponding to given LFS pointer.
* Uses already cached object, if size matches.
*/
export default async function downloadBlobFromPointer(
fs: PromiseFsClient | null,
url: string,
auth: BasicAuth | {},
{ info, objectPath }: Pointer
): Promise<Blob> {
if (fs) {
try {
const cached = await fs?.promises.readFile(objectPath);
if (cached.byteLength === info.size) {
return cached;
}
} catch (e) {
// Silence file not found errors (implies cache miss)
if ((e as any).code !== "ENOENT") {
throw e;
}
}
}
const authHeaders: Record<string, string> =
"username" in auth ? getAuthHeader(auth) : {};
// Request LFS transfer
const lfsInfoRequestData = {
operation: "download",
transfers: ["basic"],
objects: [info],
};
const lfsInfoRes = await fetch(`${url}/info/lfs/objects/batch`, {
method: "POST",
headers: {
// Github LFS doesnt seem to accept this UA, but works fine without any
// 'User-Agent': `git/isomorphic-git@${git.version()}`,
...authHeaders,
Accept: "application/vnd.git-lfs+json",
"Content-Type": "application/vnd.git-lfs+json",
},
body: JSON.stringify(lfsInfoRequestData),
});
const lfsInfoResponseData = await lfsInfoRes.json();
if (isValidLFSInfoResponseData(lfsInfoResponseData)) {
// Request the actual blob
const downloadAction = lfsInfoResponseData.objects[0].actions.download;
const lfsObjectDownloadURL = downloadAction.href;
const lfsObjectDownloadHeaders = downloadAction.header ?? {};
const lfsObjectRes = await fetch(lfsObjectDownloadURL, {
method: "GET",
headers: {
...authHeaders,
...lfsObjectDownloadHeaders,
},
});
const blob = await lfsObjectRes.blob();
if (fs) {
// Write LFS cache for this object, if cache path is accessible.
if (await isWriteable(fs, objectPath)) {
await fs.promises.mkdir(path.dirname(objectPath), { recursive: true });
await fs.promises.writeFile(objectPath, blob);
}
}
return blob;
} else {
throw new Error(
"Unexpected JSON structure received for LFS download request"
);
}
}