TypeScript 55.4%
Python 43.2%
SQL 1.2%
1import { mkdir, readFile, rename, stat, writeFile } from "node:fs/promises";2import { dirname, join, resolve } from "node:path";3import { zstdCompressSync, zstdDecompressSync } from "node:zlib";4import { sha256 } from "@websensor/core";56/**7 * Content-addressed blob store for snapshots and diffs. Identical content is stored once8 * (`sha256/ab/cd/<hash>.zst`). The interface is storage-agnostic so an S3/MinIO driver can9 * be added without touching callers.10 */11export interface BlobStore {12 put(data: Buffer | string, meta?: { contentType?: string }): Promise<{ key: string; bytes: number; deduplicated: boolean }>;13 get(key: string): Promise<Buffer>;14 getText(key: string): Promise<string>;15 exists(key: string): Promise<boolean>;16}1718export class FsBlobStore implements BlobStore {19 constructor(private readonly root: string) {}2021 private pathFor(key: string): string {22 return join(this.root, key + ".zst");23 }2425 static keyFor(hash: string): string {26 return `sha256/${hash.slice(0, 2)}/${hash.slice(2, 4)}/${hash}`;27 }2829 async put(data: Buffer | string): Promise<{ key: string; bytes: number; deduplicated: boolean }> {30 const buf = typeof data === "string" ? Buffer.from(data, "utf8") : data;31 const key = FsBlobStore.keyFor(sha256(buf));32 const p = this.pathFor(key);33 try {34 const s = await stat(p);35 if (s.size > 0) return { key, bytes: buf.length, deduplicated: true };36 } catch {37 // not present38 }39 await mkdir(dirname(p), { recursive: true });40 const tmp = `${p}.${process.pid}.${Date.now()}.tmp`;41 await writeFile(tmp, zstdCompressSync(buf));42 await rename(tmp, p);43 return { key, bytes: buf.length, deduplicated: false };44 }4546 async get(key: string): Promise<Buffer> {47 const raw = await readFile(this.pathFor(key));48 return zstdDecompressSync(raw);49 }5051 async getText(key: string): Promise<string> {52 return (await this.get(key)).toString("utf8");53 }5455 async exists(key: string): Promise<boolean> {56 try {57 await stat(this.pathFor(key));58 return true;59 } catch {60 return false;61 }62 }63}6465let store: BlobStore | null = null;66export function getBlobStore(): BlobStore {67 if (!store) store = new FsBlobStore(resolve(process.env.BLOB_STORE_DIR ?? "./data/blobs"));68 return store;69}70