// The clipper's writes (server/clip.ts) against a throwaway vault: a page // becomes a note under Clips/ that never overwrites, a thought goes under // `## Captured` today, the token lives in the data directory at 0600 and a // rotation retires the old one. The HTTP door is exercised by the browser // harness; this is the write half, which is what the vault keeps. import assert from "node:assert/strict"; import { after, before, describe, it } from "node:test"; import { existsSync, readFileSync, statSync } from "node:fs"; import path from "node:path"; import { captureLine, clipToken, dailyNotePathToday, ensureClipToken, performClip, rotateClipToken } from "../server/clip.ts"; import { initIndexer } from "../server/indexer.ts"; import { initSite } from "../server/site.ts"; import { initVault, VaultError } from "../server/vault.ts"; import { makeDir, makeVault, removeVault } from "./helpers/vault.ts"; const data = makeDir(); const root = makeVault({ "Inbox.md": "# Inbox\n\nThings.\n", "Clips/Taken.md": "---\nclipped: 2026-01-01\n---\n\n# Taken\n", }); before(async () => { initSite({ ASTROLABE_DATA: data }); initVault(root); await initIndexer(); }); after(() => { removeVault(root); removeVault(data); }); describe("performClip", () => { it("files a page under Clips/ with its source, converted", async () => { const out = await performClip({ url: "https://ex.org/a/b?c=1", title: " A / Title ", html: "

A / Title

Hello there.

", selection: null, text: null, }); assert.equal(out.kind, "clipped"); assert.equal(out.path, "Clips/A Title.md"); const text = readFileSync(path.join(root, out.path), "utf8"); assert.match(text, /^---\nsource: "https:\/\/ex\.org\/a\/b\?c=1"\nclipped: \d{4}-\d{2}-\d{2}\n---\n\n# A \/ Title\n\nHello \[there\]\(https:\/\/ex\.org\/x\)\.\n$/); }); it("never overwrites: the same title lands as (2), (3)…", async () => { const a = await performClip({ url: "https://ex.org/t", title: "Taken", html: null, selection: null, text: null }); assert.equal(a.path, "Clips/Taken (2).md"); const b = await performClip({ url: "https://ex.org/t2", title: "Taken", html: null, selection: null, text: "Some words https://ex.org/t2 after" }); assert.equal(b.path, "Clips/Taken (3).md"); assert.match(readFileSync(path.join(root, b.path), "utf8"), /# Taken\n\nSome words after\n$/); assert.equal(readFileSync(path.join(root, "Clips/Taken.md"), "utf8"), "---\nclipped: 2026-01-01\n---\n\n# Taken\n"); }); it("takes the title from the page, then the host; refuses what is not a web address", async () => { const titled = await performClip({ url: "https://ex.org/p", title: null, html: "From <the> page

x

", selection: null, text: null }); assert.equal(titled.path, "Clips/From the page.md"); const host = await performClip({ url: "https://host.example/deep", title: "", html: null, selection: "picked words", text: null }); assert.equal(host.path, "Clips/host.example.md"); assert.match(readFileSync(path.join(root, host.path), "utf8"), /picked words/); await assert.rejects( performClip({ url: "javascript:alert(1)", title: "x", html: null, selection: null, text: null }), (err: unknown) => err instanceof VaultError && err.code === "clipBadUrl", ); }); it("a thought with no address is captured into today's note", async () => { const out = await performClip({ url: null, title: null, html: null, selection: null, text: "just a thought" }); assert.equal(out.kind, "captured"); assert.equal(out.path, dailyNotePathToday()); const text = readFileSync(path.join(root, out.path), "utf8"); assert.match(text, /^## Captured\n\n- \d\d:\d\d just a thought\n$/); // A second one joins the section. await performClip({ url: null, title: null, html: null, selection: null, text: "and another" }); assert.match(readFileSync(path.join(root, out.path), "utf8"), /- \d\d:\d\d just a thought\n- \d\d:\d\d and another\n$/); await assert.rejects( performClip({ url: null, title: null, html: null, selection: null, text: " " }), (err: unknown) => err instanceof VaultError && err.code === "captureEmpty", ); }); }); describe("captureLine", () => { it("appends into a named note with the caller's clock, and creates a missing one", async () => { await captureLine("Inbox.md", "into the inbox", "08:30"); assert.equal(readFileSync(path.join(root, "Inbox.md"), "utf8"), "# Inbox\n\nThings.\n\n## Captured\n\n- 08:30 into the inbox\n"); const made = await captureLine("New/Place.md", "first", "09:00"); assert.equal(made, "New/Place.md"); assert.equal(readFileSync(path.join(root, "New/Place.md"), "utf8"), "## Captured\n\n- 09:00 first\n"); }); it("lands every one of a burst of captures and clips: the writes are serialised", async () => { // Six sheets at once into one note. Without the queue each read the same // file, each appended to what it read, and the last write won — five // lines gone with a 200 for each of them. await Promise.all([1, 2, 3, 4, 5, 6].map((n) => captureLine("Burst.md", `line ${n}`, "10:00"))); const lines = readFileSync(path.join(root, "Burst.md"), "utf8").split("\n").filter((l) => l.startsWith("- 10:00 line ")); assert.equal(lines.length, 6); // Four clips of one title at once: four notes, not one overwritten thrice. const outs = await Promise.all( [1, 2, 3, 4].map((n) => performClip({ url: "https://ex.org/burst", title: "Burst clip", html: `

${n}

`, selection: null, text: null })), ); assert.deepEqual(new Set(outs.map((o) => o.path)).size, 4); }); }); describe("the token", () => { it("is made on first ask, kept at 0600 in the data directory, and retired by a rotation", () => { assert.equal(clipToken(), null); const first = ensureClipToken(); assert.match(first, /^[a-f0-9]{48}$/); assert.equal(ensureClipToken(), first); const file = path.join(data, "clip-token"); assert.ok(existsSync(file)); if (process.platform !== "win32") assert.equal(statSync(file).mode & 0o777, 0o600); const next = rotateClipToken(); assert.notEqual(next, first); assert.equal(clipToken(), next); }); });