feat: consolidated patch-ap-mastodon-notifications (status-lookup)

Absorbs patch-ap-notifications-status-lookup into the shared helper
pattern used by the AP patch consolidation series.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sven
2026-04-12 13:55:32 +02:00
parent 55944ea895
commit fd0375d5c5
@@ -0,0 +1,86 @@
/**
* Patch: include notif.url in batchFetchStatuses so mention notifications
* resolve their associated status correctly.
*
* Root cause:
* batchFetchStatuses() only collected notif.targetUrl for the batch lookup.
* serializeNotification() looks up mentions by notif.url (the incoming
* reply URL), not notif.targetUrl (the own post being replied to). These
* are different URLs, so the statusMap never has an entry for the mention →
* fallback fires → status.id = notif._id.toString() (a notification ObjectId,
* not a timeline ObjectId) → Phanpy uses that ID for subsequent requests →
* GET /api/v1/statuses/:id/context returns 404 because ap_timeline has no
* document with that _id.
*
* Fix:
* Collect both notif.url and notif.targetUrl so the batch covers all URL
* shapes used by any notification type.
*/
import { access, readFile, writeFile } from "node:fs/promises";
const AP_BASE = "@rmdes/indiekit-endpoint-activitypub";
const AP_ROOTS = [
`node_modules/${AP_BASE}`,
`node_modules/@indiekit/indiekit/node_modules/${AP_BASE}`,
];
function apPath(rel) {
return AP_ROOTS.map(r => `${r}/${rel}`);
}
async function fileExists(p) {
try { await access(p); return true; } catch { return false; }
}
async function applyPatch(filePath, marker, oldSnippet, newSnippet) {
if (!(await fileExists(filePath))) return "file_not_found";
const src = await readFile(filePath, "utf8");
if (src.includes(marker)) return "already_applied";
if (!src.includes(oldSnippet)) return "snippet_not_found";
await writeFile(filePath, src.replace(oldSnippet, newSnippet), "utf8");
return "applied";
}
const SCRIPT = "patch-ap-mastodon-notifications";
const PATCHES = [
{
name: "ap-notifications-status-lookup",
files: apPath("lib/mastodon/routes/notifications.js"),
marker: "// [patch] ap-notifications-status-lookup",
oldSnippet: ` const targetUrls = [
...new Set(
notifications
.map((n) => n.targetUrl)
.filter(Boolean),
),
];`,
newSnippet: ` const targetUrls = [ // [patch] ap-notifications-status-lookup
...new Set(
notifications
.flatMap((n) => [n.targetUrl, n.url])
.filter(Boolean),
),
];`,
},
];
let total = 0;
for (const p of PATCHES) {
let done = false;
for (const f of p.files) {
const r = await applyPatch(f, p.marker, p.oldSnippet, p.newSnippet);
if (r === "applied") {
console.log(`[postinstall] ${SCRIPT}: applied ${p.name} to ${f}`);
total++; done = true; break;
} else if (r === "already_applied") {
console.log(`[postinstall] ${SCRIPT}: ${p.name} already applied in ${f}`);
done = true; break;
} else if (r === "snippet_not_found") {
console.warn(`[postinstall] ${SCRIPT}: ${p.name} — snippet not found in ${f}, skipping`);
}
}
if (!done) console.log(`[postinstall] ${SCRIPT}: ${p.name} — no target file found`);
}
console.log(`[postinstall] ${SCRIPT}: done (${total} patch(es) applied)`);