/** * Patch: propagate Micropub deletes to registered syndicators. * * Bug: * action.js case "delete" only calls postData.delete() + postContent.delete(). * It never notifies syndicators, so deleting a post from the web backend * leaves orphaned copies on ActivityPub (Fediverse) and Bluesky. * * Fix: * After postContent.delete() returns, iterate publication.syndicationTargets * and call syndicator.delete(url, syndication) on any syndicator that exposes * the method. The syndication URLs are read from data._deletedProperties * (preserved by postData.delete() before stripping the properties). * * Errors from individual syndicators are caught and logged — a failed remote * delete must never break the 200 response for the local delete. */ import { access, readFile, writeFile } from "node:fs/promises"; const MARKER = "// [patch] micropub-delete-propagation"; const candidates = [ "node_modules/@indiekit/endpoint-micropub/lib/controllers/action.js", ]; const OLD_DELETE_CASE = ` case "delete": { data = await postData.delete(application, publication, url); content = await postContent.delete(publication, data); break; }`; const NEW_DELETE_CASE = ` case "delete": { ${MARKER} data = await postData.delete(application, publication, url); content = await postContent.delete(publication, data); // Propagate delete to syndicators (AP, Bluesky, …) ${MARKER} const _deletedSyndication = data?._deletedProperties?.syndication ? [data._deletedProperties.syndication].flat() : []; if (_deletedSyndication.length > 0 && publication.syndicationTargets?.length > 0) { for (const _syndicator of publication.syndicationTargets) { if (typeof _syndicator.delete === "function") { _syndicator.delete(url, _deletedSyndication).catch((err) => console.warn(\`[Micropub] Syndicator delete failed (\${_syndicator.name}): \${err.message}\`), ); } } } break; }`; async function exists(p) { try { await access(p); return true; } catch { return false; } } let checked = 0; let patched = 0; for (const filePath of candidates) { if (!(await exists(filePath))) continue; checked++; const source = await readFile(filePath, "utf8"); if (source.includes(MARKER)) { console.log(`[postinstall] patch-micropub-delete-propagation: already applied to ${filePath}`); continue; } if (!source.includes(OLD_DELETE_CASE)) { console.warn(`[postinstall] patch-micropub-delete-propagation: snippet not found in ${filePath}`); continue; } await writeFile(filePath, source.replace(OLD_DELETE_CASE, NEW_DELETE_CASE), "utf8"); patched++; console.log(`[postinstall] Applied patch-micropub-delete-propagation to ${filePath}`); } if (checked === 0) { console.log("[postinstall] patch-micropub-delete-propagation: no target files found"); } else if (patched === 0) { console.log("[postinstall] patch-micropub-delete-propagation: already up to date"); } else { console.log(`[postinstall] patch-micropub-delete-propagation: patched ${patched} file(s)`); }