Files
indiekit-server/scripts/patch-webmention-sender-retry.mjs
Sven 711958b8a9 fix(patches): silence retry noise, tighten h-entry check, fix stale comment
- retry: silently skip when livefetch:v2 marker is present instead of
  logging a misleading "target snippet not found (package updated?)"
  warning on every startup
- livefetch: match `h-entry"` or `h-entry ` instead of bare `h-entry`
  to avoid false positives from body text containing the string
- reset-stale: update comment to reference livefetch v2 (not retry)
  as the patch that prevents recurrence

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 21:46:35 +01:00

114 lines
3.8 KiB
JavaScript

/**
* Patch @rmdes/indiekit-endpoint-webmention-sender controller to not silently
* mark posts as webmention-sent when the live page fetch fails.
*
* Root cause: when a post has no stored content (likes, bookmarks, reposts),
* the controller tries to fetch the published URL. If the fetch fails (page not
* yet deployed), it marks the post as webmention-sent with empty results — and
* it is never retried. This patch skips those posts instead so they are picked
* up on the next poll once the page is live.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const candidates = [
"node_modules/@rmdes/indiekit-endpoint-webmention-sender/lib/controllers/webmention-sender.js",
];
const marker = "Page not yet available";
const oldSnippet = ` // If no content, try fetching the published page
let contentToProcess = postContent;
if (!contentToProcess) {
try {
const pageResponse = await fetch(postUrl);
if (pageResponse.ok) {
contentToProcess = await pageResponse.text();
}
} catch (error) {
console.log(\`[webmention] Could not fetch \${postUrl}: \${error.message}\`);
}
}
if (!contentToProcess) {
console.log(\`[webmention] No content to process for \${postUrl}\`);
await markWebmentionsSent(postsCollection, postUrl, { sent: [], failed: [], skipped: [] });
continue;
}`;
const newSnippet = ` // If no content, try fetching the published page
let contentToProcess = postContent;
let fetchFailed = false;
if (!contentToProcess) {
try {
const pageResponse = await fetch(postUrl);
if (pageResponse.ok) {
contentToProcess = await pageResponse.text();
} else {
fetchFailed = true;
}
} catch (error) {
fetchFailed = true;
console.log(\`[webmention] Could not fetch \${postUrl}: \${error.message}\`);
}
}
if (!contentToProcess) {
if (fetchFailed) {
// Page not yet available — skip and retry on next poll rather than
// permanently marking this post as sent with zero webmentions.
console.log(\`[webmention] Page not yet available for \${postUrl}, will retry next poll\`);
continue;
}
console.log(\`[webmention] No content to process for \${postUrl}\`);
await markWebmentionsSent(postsCollection, postUrl, { sent: [], failed: [], skipped: [] });
continue;
}`;
async function exists(filePath) {
try {
await access(filePath);
return true;
} catch {
return false;
}
}
let filesChecked = 0;
let filesPatched = 0;
for (const filePath of candidates) {
if (!(await exists(filePath))) {
continue;
}
filesChecked += 1;
const source = await readFile(filePath, "utf8");
if (source.includes(marker)) {
continue;
}
if (!source.includes(oldSnippet)) {
// livefetch v2 replaces the same block — this patch is intentionally superseded.
if (source.includes("[patched:livefetch:v2]")) {
continue; // silently skip; livefetch v2 is a superset of this patch
}
console.log(`[patch] webmention-sender-retry: target snippet not found in ${filePath} (package updated?)`);
continue;
}
const updated = source.replace(oldSnippet, newSnippet);
await writeFile(filePath, updated, "utf8");
filesPatched += 1;
}
if (filesChecked === 0) {
console.log("[patch] webmention-sender-retry: package file not found");
} else if (filesPatched === 0) {
console.log("[patch] webmention-sender-retry: already applied");
} else {
console.log(`[patch] webmention-sender-retry: patched ${filesPatched}/${filesChecked} file(s)`);
}