fixup! diigo-cache: RSS in diigo's export shape, from cached items

compare-export.ts does its file I/O asynchronously (node:fs/promises;
main is async), so no sync-I/O precedent. The one exception says why:
node:sqlite has only DatabaseSync.

Generated-by: Claude Opus 5.5
This commit is contained in:
dckc committed 2026-10-07 08:44:22 -05:00
1 parent dc4f23ad04
commit f3241e4985
1 file changed
+12 -11
+12 -11
View File
@@ -5,7 +5,7 @@
* *
* node scripts/compare-export.ts <cache.sqlite> <export.xml> <out-dir> * node scripts/compare-export.ts <cache.sqlite> <export.xml> <out-dir>
*/ */
import * as nodeFs from "node:fs"; import * as nodeFs from "node:fs/promises";
import { basename, join } from "node:path"; import { basename, join } from "node:path";
import { DatabaseSync } from "node:sqlite"; import { DatabaseSync } from "node:sqlite";
import { itemDescription, toRss } from "../src/rss.ts"; import { itemDescription, toRss } from "../src/rss.ts";
@@ -95,11 +95,11 @@ export function cacheItems(db: { prepare(sql: string): { all(): unknown[] } }) {
} }
/** write access to files directly inside one directory: attenuates fs */ /** write access to files directly inside one directory: attenuates fs */
export function outDir(fs: Pick<typeof nodeFs, "writeFileSync">, dir: string) { export function outDir(fs: Pick<typeof nodeFs, "writeFile">, dir: string) {
return { return {
write(name: string, text: string): void { async write(name: string, text: string): Promise<void> {
if (basename(name) !== name) throw new Error(`not a plain file name: ${name}`); if (basename(name) !== name) throw new Error(`not a plain file name: ${name}`);
fs.writeFileSync(join(dir, name), text); await fs.writeFile(join(dir, name), text);
}, },
}; };
} }
@@ -116,10 +116,11 @@ export function outDir(fs: Pick<typeof nodeFs, "writeFileSync">, dir: string) {
* {@link outDir} — before anything else uses them. Ends by reporting an * {@link outDir} — before anything else uses them. Ends by reporting an
* exit code (2: usage) through `exit`. * exit code (2: usage) through `exit`.
*/ */
export function main( export async function main(
argv = process.argv.slice(2), argv = process.argv.slice(2),
{ {
fs = nodeFs, fs = nodeFs,
// node:sqlite has only a synchronous API; one read of a local file
connect = (path: string) => new DatabaseSync(path, { readOnly: true }), connect = (path: string) => new DatabaseSync(path, { readOnly: true }),
now = () => new Date(), now = () => new Date(),
stdout = process.stdout, stdout = process.stdout,
@@ -128,14 +129,14 @@ export function main(
process.exitCode = code; process.exitCode = code;
}, },
}: { }: {
fs?: Pick<typeof nodeFs, "readFileSync" | "writeFileSync">; fs?: Pick<typeof nodeFs, "readFile" | "writeFile">;
connect?: (path: string) => { prepare(sql: string): { all(): unknown[] } }; connect?: (path: string) => { prepare(sql: string): { all(): unknown[] } };
now?: () => Date; now?: () => Date;
stdout?: { write(text: string): unknown }; stdout?: { write(text: string): unknown };
stderr?: { write(text: string): unknown }; stderr?: { write(text: string): unknown };
exit?: (code: number) => void; exit?: (code: number) => void;
} = {}, } = {},
): void { ): Promise<void> {
const [dbPath, exportPath, outPath] = argv; const [dbPath, exportPath, outPath] = argv;
if (!dbPath || !exportPath || !outPath) { if (!dbPath || !exportPath || !outPath) {
stderr.write("usage: node scripts/compare-export.ts <cache.sqlite> <export.xml> <out-dir>\n"); stderr.write("usage: node scripts/compare-export.ts <cache.sqlite> <export.xml> <out-dir>\n");
@@ -143,13 +144,13 @@ export function main(
} }
const cache = cacheItems(connect(dbPath)); const cache = cacheItems(connect(dbPath));
const out = outDir(fs, outPath); const out = outDir(fs, outPath);
const exportXml = fs.readFileSync(exportPath, "utf8"); const exportXml = await fs.readFile(exportPath, "utf8");
const ours = cache.all(); const ours = cache.all();
out.write("from-cache.rss", toRss(ours, { title: "from the cache", pubDate: now().toUTCString() })); await out.write("from-cache.rss", toRss(ours, { title: "from the cache", pubDate: now().toUTCString() }));
const theirs = parseExport(exportXml); const theirs = parseExport(exportXml);
const { complete, report } = compare(ours, theirs); const { complete, report } = compare(ours, theirs);
out.write("report.json", JSON.stringify(report, null, 1)); await out.write("report.json", JSON.stringify(report, null, 1));
const counts = { const counts = {
export: theirs.length, export: theirs.length,
cache: ours.length, cache: ours.length,
@@ -161,4 +162,4 @@ export function main(
} }
// run only as a program, not when imported (e.g. by a test) // run only as a program, not when imported (e.g. by a test)
if (import.meta.main) main(); if (import.meta.main) await main();