pkg.ts
1.8 kB · typescript · 54 lines
1import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";2import { dirname, join, resolve } from "node:path";3import { client, list, PROD_BUCKET } from "../../aws/s3.ts";45/* HASH */67function crawl(dir: string, at: string, out: string[]) {8 for (const entry of readdirSync(join(dir, at), { withFileTypes: true })) {9 const rel = at ? `${at}/${entry.name}` : entry.name;10 if (entry.isDirectory()) crawl(dir, rel, out);11 else out.push(rel);12 }13}1415export function pkgFiles(dir: string): string[] {16 const out: string[] = [];17 crawl(dir, "", out);18 return out.sort((a, b) => Buffer.compare(Buffer.from(a, "utf8"), Buffer.from(b, "utf8")));19}2021export function pkgHash(dir: string): string {22 const manifest = new Bun.CryptoHasher("sha256");23 for (const rel of pkgFiles(dir)) {24 const one = new Bun.CryptoHasher("sha256");25 one.update(readFileSync(join(dir, rel)));26 manifest.update(`${one.digest("hex")} ${rel}\n`);27 }28 return manifest.digest("hex");29}3031/* FETCH */3233export async function ensurePkg(root = resolve(import.meta.dir, "..")): Promise<string> {34 const dir = join(root, "pkg");35 if (existsSync(dir) && pkgFiles(dir).length > 0) return dir;36 const hash = readFileSync(join(root, "pkg.lock"), "utf8").trim();37 const prefix = `pkg/${hash}/`;38 const s3 = client(PROD_BUCKET);39 const keys = (await list(s3, prefix)).filter((key) => key.length > prefix.length);40 for (const key of keys) {41 const to = join(dir, key.slice(prefix.length));42 mkdirSync(dirname(to), { recursive: true });43 writeFileSync(to, Buffer.from(await s3.file(key).arrayBuffer()));44 }45 if (keys.length === 0) throw new Error(`no objects at s3://${PROD_BUCKET}/${prefix}`);46 return dir;47}4849/* MAIN */5051if (import.meta.main) {52 const dir = await ensurePkg();53 console.log(`${pkgHash(dir)} ${pkgFiles(dir).length} ${dir}`);54}