OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
 
master @ 267 LINES
 
[ HISTORY ]  [ UP ]
 

// Build-time git readers for the /git/ section. Pages are generated
// from real repositories at build time - stagit-style, but rendered
// through the site's own components. Anything that can land in a
// frame passes toGrid() so history written before the og-ascii rule
// can't fail the build. Every reader takes a ref so branch pages are
// first-class; urls follow the github shape (tree/<branch>/<path>).
import { execFileSync } from "node:child_process";
import { existsSync } from "node:fs";
import { toGrid } from "./ascii";
import type { TimelineEntry } from "../components/Timeline/Timeline";

export interface GitRepo {
  /** Url segment. */
  slug: string;
  /** Display name. */
  name: string;
  /** Local checkout or bare repo path, resolved from ooknet-design/. */
  path: string;
  desc: string;
  clone?: string;
  /** Path prefixes that get no blob/history pages - committed data
   *  dumps would explode the page count. Listed but unlinked. */
  exclude?: string[];
}

const MAX_BLOB = 200 * 1024;

const git = (path: string, args: string[]): string =>
  execFileSync("git", ["-C", path, ...args], {
    encoding: "utf8",
    maxBuffer: 32 * 1024 * 1024,
  });

export const repoAvailable = (r: GitRepo): boolean => existsSync(r.path);

/** True when a path sits under one of the repo's excluded prefixes. */
export const excluded = (r: GitRepo, path: string): boolean =>
  (r.exclude ?? []).some((p) => path === p || path.startsWith(p + "/"));

/** Local branches, default branch first. */
export function branches(path: string): string[] {
  const head = defaultBranch(path);
  const all = git(path, ["for-each-ref", "refs/heads", "--format=%(refname:short)"
])
    .split("\n")
    .filter(Boolean);
  return [head, ...all.filter((b) => b !== head)];
}

export const defaultBranch = (path: string): string =>
  git(path, ["symbolic-ref", "--short", "HEAD"]).trim();

export interface RepoInfo {
  branch: string;
  commits: number;
  lastHash: string;
  lastDate: string;
  lastSubject: string;
}

export function repoInfo(path: string, ref?: string): RepoInfo {
  const branch = ref ?? defaultBranch(path);
  const [lastHash, lastDate, ...subject] = git(path, ["log", "-1", "--format=%h|%a
s|%s", branch])
    .trim()
    .split("|");
  return {
    branch,
    commits: parseInt(git(path, ["rev-list", "--count", branch]).trim(), 10),
    lastHash,
    lastDate,
    lastSubject: toGrid(subject.join("|")),
  };
}

export function repoFiles(path: string, ref = "HEAD"): string[] {
  return git(path, ["ls-tree", "-r", "--name-only", ref])
    .split("\n")
    .filter(Boolean);
}

export function repoLog(path: string, limit: number, base: string, ref = "HEAD"): 
TimelineEntry[] {
  return git(path, ["log", `-${limit}`, "--format=%h|%as|%s", ref])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [hash, date, ...subject] = line.split("|");
      return {
        date,
        title: toGrid(hash),
        desc: toGrid(subject.join("|")),
        href: `${base}commit/${hash}/`,
      };
    });
}

/** Commits touching one path - the file history page. */
export function pathLog(
  path: string,
  file: string,
  limit: number,
  base: string,
  ref = "HEAD",
): TimelineEntry[] {
  return git(path, ["log", `-${limit}`, "--format=%h|%as|%s", ref, "--", file])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [hash, date, ...subject] = line.split("|");
      return {
        date,
        title: toGrid(hash),
        desc: toGrid(subject.join("|")),
        href: `${base}commit/${hash}/`,
      };
    });
}

export interface DirEntry {
  name: string;
  path: string;
  kind: "tree" | "blob";
  /** Last commit touching this path. */
  hash: string;
  date: string;
  subject: string;
}

/** Immediate children of a directory, github-style: dirs first, each
 *  with the last commit that touched it. */
export function dirEntries(path: string, dir = "", ref = "HEAD"): DirEntry[] {
  const spec = dir ? `${ref}:${dir}` : ref;
  const rows = git(path, ["ls-tree", spec])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [meta, name] = line.split("\t");
      const kind = meta.split(" ")[1] as "tree" | "blob" | "commit";
      return { name, kind };
    })
    .filter((e): e is { name: string; kind: "tree" | "blob" } => e.kind !== "commi
t");
  return rows
    .sort((a, b) => (a.kind === b.kind ? a.name.localeCompare(b.name) : a.kind ===
 "tree" ? -1 : 1))
    .map(({ name, kind }) => {
      const full = dir ? `${dir}/${name}` : name;
      const [hash, date, ...subject] = git(path, ["log", "-1", "--format=%h|%as|%s
", ref, "--", full])
        .trim()
        .split("|");
      return { name: toGrid(name), path: full, kind, hash, date, subject: toGrid(s
ubject.join("|")) };
    });
}

/** Every directory path in the repo, for tree page generation. */
export function repoDirs(files: string[]): string[] {
  const dirs = new Set<string>();
  for (const f of files) {
    const segments = f.split("/");
    for (let i = 1; i < segments.length; i++) {
      dirs.add(segments.slice(0, i).join("/"));
    }
  }
  return [...dirs].sort();
}

/** Repo README content, if present. */
export function readReadme(path: string, ref = "HEAD"): string | null {
  for (const name of ["README.md", "readme.md", "README"]) {
    try {
      return git(path, ["show", `${ref}:${name}`]);
    } catch {
      continue;
    }
  }
  return null;
}

export interface Blob {
  text: string;
  binary: boolean;
  truncated: boolean;
  lines: number;
}

export function readBlob(path: string, file: string, ref = "HEAD"): Blob {
  const raw = execFileSync("git", ["-C", path, "show", `${ref}:${file}`], {
    maxBuffer: 32 * 1024 * 1024,
  });
  if (raw.subarray(0, 8000).includes(0)) {
    return { text: "", binary: true, truncated: false, lines: 0 };
  }
  const truncated = raw.length > MAX_BLOB;
  const text = raw.subarray(0, MAX_BLOB).toString("utf8");
  return {
    text,
    binary: false,
    truncated,
    lines: text.split("\n").length,
  };
}

export interface CommitFile {
  path: string;
  diff: string;
}

export interface Commit {
  hash: string;
  date: string;
  subject: string;
  body: string;
  files: CommitFile[];
  /** Set instead of files when the patch is too large to render. */
  stat?: string;
}

/** True when a numstat says the patch is too big for a page. Data
 *  regen commits produced 5MB pages at looser thresholds - a human
 *  reads neither a thousand changed lines nor fifty files. */
export function oversizedNumstat(numstat: string): boolean {
  const rows = numstat.split("\n").filter(Boolean);
  let lines = 0;
  for (const r of rows) {
    const [a, d] = r.split("\t");
    lines += (parseInt(a, 10) || 0) + (parseInt(d, 10) || 0);
  }
  return rows.length > 50 || lines > 1000;
}

/** One commit with its patch split per file, github-style. Oversized
 *  commits carry a diffstat summary instead of patches. */
export function readCommit(path: string, hash: string): Commit {
  const meta = git(path, ["show", "-s", "--format=%H|%as|%s|%b", hash]).trim();
  const [full, date, subject, ...body] = meta.split("|");
  const base = {
    hash: full,
    date,
    subject: toGrid(subject),
    body: toGrid(body.join("|").trim()),
  };
  const numstat = git(path, ["show", "--numstat", "--format=", hash]);
  if (oversizedNumstat(numstat)) {
    const stat = git(path, ["show", "--stat", "--format=", hash]);
    return { ...base, files: [], stat: stat.length > MAX_BLOB ? stat.slice(0, MAX_
BLOB) : stat };
  }
  const patch = git(path, ["show", "--patch", "--format=", hash]);
  const files: CommitFile[] = [];
  for (const chunk of patch.split(/^(?=diff --git )/m)) {
    if (!chunk.trim()) continue;
    const m = chunk.match(/^diff --git a\/.+? b\/(.+)$/m);
    const file = m ? m[1] : "?";
    const MAX_FILE_DIFF = 64 * 1024;
    const trimmed =
      chunk.length > MAX_FILE_DIFF
        ? chunk.slice(0, MAX_FILE_DIFF) + "\n[diff truncated]"
        : chunk;
    files.push({ path: file, diff: trimmed });
  }
  return { ...base, files };
}

/** Frame label for a file: the uppercased extension, same convention
 *  as the pipeline's code fences (label = uppercase of the token you
 *  have). No language table to maintain. */
export function langOf(file: string): string {
  const name = file.split("/").pop() ?? file;
  const ext = name.includes(".") ? name.split(".").pop()! : "";
  return ext ? ext.toUpperCase().slice(0, 8) : "TXT";
}

// Build-time git readers for the /git/ sect
ion. Pages are generated
// from real repositories at build time - st
agit-style, but rendered
// through the site's own components. Anythi
ng that can land in a
// frame passes toGrid() so history written 
before the og-ascii rule
// can't fail the build. Every reader takes 
a ref so branch pages are
// first-class; urls follow the github shape
 (tree/<branch>/<path>).
import { execFileSync } from "node:child_pro
cess";
import { existsSync } from "node:fs";
import { toGrid } from "./ascii";
import type { TimelineEntry } from "../compo
nents/Timeline/Timeline";

export interface GitRepo {
  /** Url segment. */
  slug: string;
  /** Display name. */
  name: string;
  /** Local checkout or bare repo path, reso
lved from ooknet-design/. */
  path: string;
  desc: string;
  clone?: string;
  /** Path prefixes that get no blob/history
 pages - committed data
   *  dumps would explode the page count. Li
sted but unlinked. */
  exclude?: string[];
}

const MAX_BLOB = 200 * 1024;

const git = (path: string, args: string[]): 
string =>
  execFileSync("git", ["-C", path, ...args],
 {
    encoding: "utf8",
    maxBuffer: 32 * 1024 * 1024,
  });

export const repoAvailable = (r: GitRepo): b
oolean => existsSync(r.path);

/** True when a path sits under one of the r
epo's excluded prefixes. */
export const excluded = (r: GitRepo, path: s
tring): boolean =>
  (r.exclude ?? []).some((p) => path === p |
| path.startsWith(p + "/"));

/** Local branches, default branch first. */
export function branches(path: string): stri
ng[] {
  const head = defaultBranch(path);
  const all = git(path, ["for-each-ref", "re
fs/heads", "--format=%(refname:short)"])
    .split("\n")
    .filter(Boolean);
  return [head, ...all.filter((b) => b !== h
ead)];
}

export const defaultBranch = (path: string):
 string =>
  git(path, ["symbolic-ref", "--short", "HEA
D"]).trim();

export interface RepoInfo {
  branch: string;
  commits: number;
  lastHash: string;
  lastDate: string;
  lastSubject: string;
}

export function repoInfo(path: string, ref?:
 string): RepoInfo {
  const branch = ref ?? defaultBranch(path);
  const [lastHash, lastDate, ...subject] = g
it(path, ["log", "-1", "--format=%h|%as|%s",
 branch])
    .trim()
    .split("|");
  return {
    branch,
    commits: parseInt(git(path, ["rev-list",
 "--count", branch]).trim(), 10),
    lastHash,
    lastDate,
    lastSubject: toGrid(subject.join("|")),
  };
}

export function repoFiles(path: string, ref 
= "HEAD"): string[] {
  return git(path, ["ls-tree", "-r", "--name
-only", ref])
    .split("\n")
    .filter(Boolean);
}

export function repoLog(path: string, limit:
 number, base: string, ref = "HEAD"): Timeli
neEntry[] {
  return git(path, ["log", `-${limit}`, "--f
ormat=%h|%as|%s", ref])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [hash, date, ...subject] = line.
split("|");
      return {
        date,
        title: toGrid(hash),
        desc: toGrid(subject.join("|")),
        href: `${base}commit/${hash}/`,
      };
    });
}

/** Commits touching one path - the file his
tory page. */
export function pathLog(
  path: string,
  file: string,
  limit: number,
  base: string,
  ref = "HEAD",
): TimelineEntry[] {
  return git(path, ["log", `-${limit}`, "--f
ormat=%h|%as|%s", ref, "--", file])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [hash, date, ...subject] = line.
split("|");
      return {
        date,
        title: toGrid(hash),
        desc: toGrid(subject.join("|")),
        href: `${base}commit/${hash}/`,
      };
    });
}

export interface DirEntry {
  name: string;
  path: string;
  kind: "tree" | "blob";
  /** Last commit touching this path. */
  hash: string;
  date: string;
  subject: string;
}

/** Immediate children of a directory, githu
b-style: dirs first, each
 *  with the last commit that touched it. */
export function dirEntries(path: string, dir
 = "", ref = "HEAD"): DirEntry[] {
  const spec = dir ? `${ref}:${dir}` : ref;
  const rows = git(path, ["ls-tree", spec])
    .split("\n")
    .filter(Boolean)
    .map((line) => {
      const [meta, name] = line.split("\t");
      const kind = meta.split(" ")[1] as "tr
ee" | "blob" | "commit";
      return { name, kind };
    })
    .filter((e): e is { name: string; kind: 
"tree" | "blob" } => e.kind !== "commit");
  return rows
    .sort((a, b) => (a.kind === b.kind ? a.n
ame.localeCompare(b.name) : a.kind === "tree
" ? -1 : 1))
    .map(({ name, kind }) => {
      const full = dir ? `${dir}/${name}` : 
name;
      const [hash, date, ...subject] = git(p
ath, ["log", "-1", "--format=%h|%as|%s", ref
, "--", full])
        .trim()
        .split("|");
      return { name: toGrid(name), path: ful
l, kind, hash, date, subject: toGrid(subject
.join("|")) };
    });
}

/** Every directory path in the repo, for tr
ee page generation. */
export function repoDirs(files: string[]): s
tring[] {
  const dirs = new Set<string>();
  for (const f of files) {
    const segments = f.split("/");
    for (let i = 1; i < segments.length; i++
) {
      dirs.add(segments.slice(0, i).join("/"
));
    }
  }
  return [...dirs].sort();
}

/** Repo README content, if present. */
export function readReadme(path: string, ref
 = "HEAD"): string | null {
  for (const name of ["README.md", "readme.m
d", "README"]) {
    try {
      return git(path, ["show", `${ref}:${na
me}`]);
    } catch {
      continue;
    }
  }
  return null;
}

export interface Blob {
  text: string;
  binary: boolean;
  truncated: boolean;
  lines: number;
}

export function readBlob(path: string, file:
 string, ref = "HEAD"): Blob {
  const raw = execFileSync("git", ["-C", pat
h, "show", `${ref}:${file}`], {
    maxBuffer: 32 * 1024 * 1024,
  });
  if (raw.subarray(0, 8000).includes(0)) {
    return { text: "", binary: true, truncat
ed: false, lines: 0 };
  }
  const truncated = raw.length > MAX_BLOB;
  const text = raw.subarray(0, MAX_BLOB).toS
tring("utf8");
  return {
    text,
    binary: false,
    truncated,
    lines: text.split("\n").length,
  };
}

export interface CommitFile {
  path: string;
  diff: string;
}

export interface Commit {
  hash: string;
  date: string;
  subject: string;
  body: string;
  files: CommitFile[];
  /** Set instead of files when the patch is
 too large to render. */
  stat?: string;
}

/** True when a numstat says the patch is to
o big for a page. Data
 *  regen commits produced 5MB pages at loos
er thresholds - a human
 *  reads neither a thousand changed lines n
or fifty files. */
export function oversizedNumstat(numstat: st
ring): boolean {
  const rows = numstat.split("\n").filter(Bo
olean);
  let lines = 0;
  for (const r of rows) {
    const [a, d] = r.split("\t");
    lines += (parseInt(a, 10) || 0) + (parse
Int(d, 10) || 0);
  }
  return rows.length > 50 || lines > 1000;
}

/** One commit with its patch split per file
, github-style. Oversized
 *  commits carry a diffstat summary instead
 of patches. */
export function readCommit(path: string, has
h: string): Commit {
  const meta = git(path, ["show", "-s", "--f
ormat=%H|%as|%s|%b", hash]).trim();
  const [full, date, subject, ...body] = met
a.split("|");
  const base = {
    hash: full,
    date,
    subject: toGrid(subject),
    body: toGrid(body.join("|").trim()),
  };
  const numstat = git(path, ["show", "--nums
tat", "--format=", hash]);
  if (oversizedNumstat(numstat)) {
    const stat = git(path, ["show", "--stat"
, "--format=", hash]);
    return { ...base, files: [], stat: stat.
length > MAX_BLOB ? stat.slice(0, MAX_BLOB) 
: stat };
  }
  const patch = git(path, ["show", "--patch"
, "--format=", hash]);
  const files: CommitFile[] = [];
  for (const chunk of patch.split(/^(?=diff 
--git )/m)) {
    if (!chunk.trim()) continue;
    const m = chunk.match(/^diff --git a\/.+
? b\/(.+)$/m);
    const file = m ? m[1] : "?";
    const MAX_FILE_DIFF = 64 * 1024;
    const trimmed =
      chunk.length > MAX_FILE_DIFF
        ? chunk.slice(0, MAX_FILE_DIFF) + "\
n[diff truncated]"
        : chunk;
    files.push({ path: file, diff: trimmed }
);
  }
  return { ...base, files };
}

/** Frame label for a file: the uppercased e
xtension, same convention
 *  as the pipeline's code fences (label = u
ppercase of the token you
 *  have). No language table to maintain. */
export function langOf(file: string): string
 {
  const name = file.split("/").pop() ?? file
;
  const ext = name.includes(".") ? name.spli
t(".").pop()! : "";
  return ext ? ext.toUpperCase().slice(0, 8)
 : "TXT";
}
 
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET