2021-11-25 12:27:33 +00:00
|
|
|
import path from 'path';
|
2021-12-01 17:06:30 +00:00
|
|
|
import { SPEC_URL, toHex } from './util';
|
2021-11-25 12:27:33 +00:00
|
|
|
|
|
|
|
|
2021-11-30 21:18:47 +00:00
|
|
|
export interface PointerInfo {
|
2021-11-30 21:30:33 +00:00
|
|
|
/** OID (currently, SHA256 hash) of actual blob contents. */
|
2021-11-25 12:27:33 +00:00
|
|
|
oid: string;
|
2021-11-30 21:18:27 +00:00
|
|
|
|
|
|
|
/** Actual blob size in bytes. */
|
2021-11-29 01:47:47 +00:00
|
|
|
size: number;
|
2021-11-25 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface Pointer {
|
|
|
|
info: PointerInfo;
|
|
|
|
|
2021-11-30 21:18:27 +00:00
|
|
|
/** Absolute path to actual blob in LFS cache. */
|
2021-11-25 12:27:33 +00:00
|
|
|
objectPath: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
function isValidPointerInfo(val: Record<string, any>): val is PointerInfo {
|
2021-11-29 01:47:47 +00:00
|
|
|
return val.oid.trim !== undefined && typeof val.size === 'number';
|
2021-11-25 12:27:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-11-30 21:19:35 +00:00
|
|
|
function readPointerInfo(content: Buffer): PointerInfo {
|
2021-11-25 12:27:33 +00:00
|
|
|
const info = content.toString().trim().split('\n').reduce((accum, line) => {
|
|
|
|
const [k, v] = line.split(' ', 2);
|
|
|
|
if (k === 'oid') {
|
|
|
|
accum[k] = v.split(':', 2)[1];
|
|
|
|
} else if (k === 'size') {
|
2021-11-29 01:47:47 +00:00
|
|
|
accum[k] = parseInt(v, 10);
|
2021-11-25 12:27:33 +00:00
|
|
|
}
|
|
|
|
return accum;
|
|
|
|
}, {} as Record<string, any>);
|
|
|
|
|
|
|
|
if (isValidPointerInfo(info)) {
|
2021-11-30 21:19:35 +00:00
|
|
|
return info;
|
2021-11-25 12:27:33 +00:00
|
|
|
} else {
|
|
|
|
throw new Error("LFS pointer is incomplete or cannot be read");
|
|
|
|
}
|
|
|
|
}
|
2021-11-30 21:19:35 +00:00
|
|
|
|
|
|
|
|
|
|
|
interface PointerRequest {
|
|
|
|
dir: string;
|
|
|
|
gitdir?: string;
|
|
|
|
content: Buffer;
|
|
|
|
}
|
|
|
|
export function readPointer({ dir, gitdir = path.join(dir, '.git'), content }: PointerRequest): Pointer {
|
|
|
|
const info = readPointerInfo(content);
|
|
|
|
|
|
|
|
const objectPath = path.join(
|
|
|
|
gitdir,
|
|
|
|
'lfs',
|
|
|
|
'objects',
|
|
|
|
info.oid.substr(0, 2),
|
|
|
|
info.oid.substr(2, 2),
|
|
|
|
info.oid);
|
|
|
|
|
|
|
|
return { info, objectPath };
|
|
|
|
}
|
2021-11-30 21:20:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
/** Formats given PointerInfo for writing in Git tree. */
|
|
|
|
export function formatPointerInfo(info: PointerInfo): Buffer {
|
|
|
|
const lines = [
|
|
|
|
`version ${SPEC_URL}`,
|
|
|
|
`oid sha256:${info.oid}`,
|
|
|
|
`size ${info.size}`,
|
|
|
|
];
|
|
|
|
return Buffer.from(lines.join('\n'));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export async function buildPointerInfo(content: Buffer): Promise<PointerInfo> {
|
|
|
|
const size = Buffer.byteLength(content);
|
|
|
|
const hash = await crypto.subtle.digest('SHA-1', content);
|
|
|
|
return { oid: toHex(hash), size };
|
|
|
|
}
|