app/engine/localBackend.ts
zzmq.dev@gmail.com/zz / app / engine / localBackend.ts
@ 493f33d9
Download
Ladder Summary
- file The module implements the **local** `Backend` used by zz – it resolves a repo’s on‑disk locations under `~/.zz/repos` and `~/.zz/state`, performs git operations (clone, list commits, diff, read file at a commit, commit a changeset) and reads/writes ladder notes and anchors via the existing note‑handling utilities. Helper functions (`repoDirOf`, `stateDirOf`, `everyLocalRepoSlug`, `collectSearchableRungs`) support locating repositories and gathering searchable rungs, while the exported factory `createLocalBackend()` returns an object that conforms to the `Backend` interface with methods such as `ensureCloned`, `listCommits`, `diffCommit`, `readFileAtCommit`, `commitChangeset`, `readAnchor`, `readLadderTree`, `writeLadder`, etc. No classes are exported – only the function **createLocalBackend**.
// Today's behavior, unchanged, behind the Backend interface -- a real
// local clone plus flat/SQLite files under app/state/, exactly as
// before. The only thing that moves is who resolves a repo slug into
// paths: that logic used to live in cli/lib/repo.ts and get threaded
// through every call site by the caller; it lives here now, since a
// path is something only a *local* backend has at all.
import { existsSync } from "node:fs";
import { execFileSync } from "node:child_process";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type { Backend } from "./backend.js";
import type { Lane } from "./types.js";
import type { Rung, RungKind } from "./ladder.js";
import type { EmbeddedRung } from "./ladderNotes.js";
import { writeLadder, appendRepoChildren as appendRepoChildrenImpl, readAnchor as readAnchorImpl } from "./ladderNotes.js";
import { listCommitsChronological, commitFileChanges, fileContentAtCommit, type CommitRow, type FileChangeRow } from "./ingest.js";
import { commitChangeset as commitChangesetImpl, type ChangesetFile, type CommitAuthor } from "./changeset.js";
import { readLane as readLaneImpl, writeLane as writeLaneImpl, listLanes as listLanesImpl, withLane as withLaneImpl } from "./store.js";
const here = dirname(fileURLToPath(import.meta.url));
const appRoot = join(here, "..");
function parseSlug(slug: string): { name: string } {
if (typeof slug !== "string" || !slug) {
throw new Error(`expected a repo slug shaped "owner/repo", got ${JSON.stringify(slug)}.`);
}
const parts = slug.split("/");
if (parts.length !== 2 || !parts[0] || !parts[1]) {
throw new Error(`expected "owner/repo", got "${slug}"`);
}
return { name: parts[1] };
}
function repoDirOf(slug: string): string {
return join(appRoot, "demo-repos", parseSlug(slug).name);
}
function stateDirOf(slug: string): string {
return join(appRoot, "state", parseSlug(slug).name);
}
export function createLocalBackend(): Backend {
return {
name: "local",
async ensureCloned(repo) {
const repoDir = repoDirOf(repo);
if (existsSync(repoDir)) return;
execFileSync("git", ["clone", "--quiet", `https://github.com/${repo}.git`, repoDir], { stdio: "inherit" });
},
async listCommits(repo): Promise<CommitRow[]> {
return listCommitsChronological(repoDirOf(repo));
},
async diffCommit(repo, commit): Promise<FileChangeRow[]> {
return commitFileChanges(repoDirOf(repo), commit);
},
async readFileAtCommit(repo, sha, path): Promise<string> {
return fileContentAtCommit(repoDirOf(repo), sha, path);
},
async commitChangeset(repo, input: { files: ChangesetFile[]; message: string; author: CommitAuthor }): Promise<string> {
return commitChangesetImpl(repoDirOf(repo), input);
},
async readAnchor(repo, path): Promise<EmbeddedRung | null> {
return readAnchorImpl(repoDirOf(repo), stateDirOf(repo), path);
},
async writeLadder(repo, rung: Rung | Rung[]): Promise<void> {
const rungs = Array.isArray(rung) ? rung : [rung];
for (const one of rungs) await writeLadder(repoDirOf(repo), stateDirOf(repo), one);
},
async appendRepoChildren(repo, children: { path: string; kind: RungKind }[]): Promise<void> {
await appendRepoChildrenImpl(repoDirOf(repo), stateDirOf(repo), children);
},
async readLane(repo, laneId): Promise<Lane | null> {
return readLaneImpl(stateDirOf(repo), laneId);
},
async writeLane(repo, lane: Lane): Promise<void> {
writeLaneImpl(stateDirOf(repo), lane);
},
async listLanes(repo): Promise<Lane[]> {
return listLanesImpl(stateDirOf(repo));
},
async withLane(repo, laneId, mutate): Promise<Lane> {
return withLaneImpl(stateDirOf(repo), laneId, mutate);
},
};
}