F6: diff-view toggle (Feature #17) #18
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* BaselineStore — load/save one diff-view baseline JSON per document (F6 §6.3).
|
||||
* The CoauthorStore shape (src/store.ts): vscode-free (Node fs only, unit-
|
||||
* testable), one file per docPath. INV-19: the storage dir is VS Code's
|
||||
* per-workspace extension storage, NEVER the repo — so the baseline can never
|
||||
* be committed, merged, or read by another rung, and the sidecar / cross-rung
|
||||
* contract (INV-14..17) are untouched by construction.
|
||||
*/
|
||||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
export type BaselineReason = "opened" | "machine-landing" | "pinned";
|
||||
|
||||
export interface Baseline {
|
||||
docPath: string;
|
||||
/** full document text captured at the epoch (from the buffer, not disk — §6.3). */
|
||||
text: string;
|
||||
capturedAt: string;
|
||||
reason: BaselineReason;
|
||||
}
|
||||
|
||||
export class BaselineStore {
|
||||
/** @param storageDir absolute VS Code workspace-storage dir (context.storageUri.fsPath). */
|
||||
constructor(private readonly storageDir: string) {}
|
||||
|
||||
/** `<storageDir>/baselines/<repo-relative-docPath>.json` (§6.3). */
|
||||
baselinePath(docPath: string): string {
|
||||
return path.join(this.storageDir, "baselines", `${docPath}.json`);
|
||||
}
|
||||
|
||||
load(docPath: string): Baseline | null {
|
||||
const p = this.baselinePath(docPath);
|
||||
if (!fs.existsSync(p)) return null;
|
||||
return JSON.parse(fs.readFileSync(p, "utf8")) as Baseline;
|
||||
}
|
||||
|
||||
/** Newest epoch wins — overwrite in place, no history (state-not-history, the F3/F4 precedent). */
|
||||
save(docPath: string, baseline: Baseline): void {
|
||||
const p = this.baselinePath(docPath);
|
||||
fs.mkdirSync(path.dirname(p), { recursive: true });
|
||||
fs.writeFileSync(p, JSON.stringify(baseline, null, 2) + "\n", "utf8");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { BaselineStore, type Baseline } from "../src/baselineStore";
|
||||
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = fs.mkdtempSync(path.join(os.tmpdir(), "baseline-store-"));
|
||||
});
|
||||
afterEach(() => {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
function sample(docPath = "notes/chapter-1.md"): Baseline {
|
||||
return { docPath, text: "hello\nworld\n", capturedAt: "2026-06-11T00:00:00.000Z", reason: "opened" };
|
||||
}
|
||||
|
||||
describe("BaselineStore", () => {
|
||||
it("returns null for a document with no baseline", () => {
|
||||
expect(new BaselineStore(dir).load("notes/chapter-1.md")).toBeNull();
|
||||
});
|
||||
|
||||
it("computes the baseline path as baselines/<docPath>.json", () => {
|
||||
const store = new BaselineStore(dir);
|
||||
expect(store.baselinePath("notes/chapter-1.md")).toBe(
|
||||
path.join(dir, "baselines", "notes", "chapter-1.md.json"),
|
||||
);
|
||||
});
|
||||
|
||||
it("save then load round-trips the baseline (including nested docPaths)", () => {
|
||||
const store = new BaselineStore(dir);
|
||||
const b = sample();
|
||||
store.save(b.docPath, b);
|
||||
expect(store.load(b.docPath)).toEqual(b);
|
||||
});
|
||||
|
||||
it("overwrites in place: the newest epoch wins, no history kept", () => {
|
||||
const store = new BaselineStore(dir);
|
||||
store.save("d.md", { docPath: "d.md", text: "v1", capturedAt: "2026-06-11T00:00:00.000Z", reason: "opened" });
|
||||
store.save("d.md", { docPath: "d.md", text: "v2", capturedAt: "2026-06-11T00:01:00.000Z", reason: "pinned" });
|
||||
expect(store.load("d.md")).toEqual({ docPath: "d.md", text: "v2", capturedAt: "2026-06-11T00:01:00.000Z", reason: "pinned" });
|
||||
});
|
||||
|
||||
it("writes pretty JSON with a trailing newline", () => {
|
||||
const store = new BaselineStore(dir);
|
||||
const b = sample("d.md");
|
||||
store.save("d.md", b);
|
||||
const raw = fs.readFileSync(store.baselinePath("d.md"), "utf8");
|
||||
expect(raw).toBe(JSON.stringify(b, null, 2) + "\n");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user