git-cat-file
Version:
A pure-JavaScript implementation of `git cat-file -p` for Node.js.
80 lines (66 loc) • 2.3 kB
text/typescript
/**
* https://github.com/kawanet/git-cat-file
*/
import {promises as fs} from "fs"
import {inflateSync} from "zlib"
import type {GCF} from "../types/git-cat-file.d.ts"
import {shortCache} from "./cache.ts"
export class Loose {
protected readonly root: string
constructor(root: string) {
this.root = root
}
private readdir = shortCache((first: string) => fs.readdir(`${this.root}/objects/${first}`))
async findAll(object_id: string): Promise<string[]> {
const first = object_id.slice(0, 2)
const rest = object_id.slice(2)
const {length} = rest
const files: string[] = await this.readdir(first).catch((): null => null)
if (!files) return
return files.filter(name => name.slice(0, length) === rest).map(name => (first + name))
}
async getObject(oid: string): Promise<GCF.IObject> {
const first = oid.slice(0, 2)
const rest = oid.slice(2)
const obj = new LooseObject(`${this.root}/objects/${first}/${rest}`)
const type = await obj.getType()
const data = await obj.getData()
return {oid, type, data}
}
}
function findZero(buf: Buffer, offset?: number): number {
offset |= 0
while (buf[offset++]) {
// nop
}
return offset
}
class LooseObject {
private buf: Promise<Buffer>
private offset: Promise<number>
private type: Promise<GCF.ObjType>
private readonly path: string
constructor(path: string) {
this.path = path
}
private getRaw(): Promise<Buffer> {
return this.buf || (this.buf = fs.readFile(this.path).then(inflateSync))
}
private getOffset(): Promise<number> {
return this.offset || (this.offset = this.getRaw().then(findZero))
}
getType(): Promise<GCF.ObjType> {
return this.type || (this.type = this.parseType())
}
private async parseType(): Promise<GCF.ObjType> {
const raw = await this.getRaw()
const offset = await this.getOffset()
const head = raw.slice(0, offset - 2).toString()
return head.split(/\s+/).shift() as GCF.ObjType
}
async getData(): Promise<Buffer> {
const raw = await this.getRaw()
const offset = await this.getOffset()
return raw.slice(offset)
}
}