@rmdes/indiekit-endpoint-activitypub 3.10.1 → 3.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/lib/syndicator.js +67 -8
  2. package/package.json +1 -1
package/lib/syndicator.js CHANGED
@@ -224,7 +224,7 @@ export function createSyndicator(plugin) {
224
224
  // timelines (Phanpy/Moshidon). Uses $setOnInsert — idempotent.
225
225
  try {
226
226
  const profile = await plugin._collections.ap_profile?.findOne({});
227
- const content = normalizeContent(properties.content);
227
+ const content = buildTimelineContent(properties);
228
228
  const timelineItem = {
229
229
  uid: properties.url,
230
230
  url: properties.url,
@@ -289,13 +289,72 @@ export function createSyndicator(plugin) {
289
289
 
290
290
  // ─── Timeline helpers ───────────────────────────────────────────────────────
291
291
 
292
- function normalizeContent(content) {
293
- if (!content) return { text: "", html: "" };
294
- if (typeof content === "string") return { text: content, html: `<p>${content}</p>` };
295
- return {
296
- text: content.text || content.value || "",
297
- html: content.html || content.text || content.value || "",
298
- };
292
+ /**
293
+ * Build content from JF2 properties for the ap_timeline entry.
294
+ * For interaction types (likes, bookmarks, reposts), always includes
295
+ * the target URL — even when there's comment text alongside it.
296
+ */
297
+ function buildTimelineContent(properties) {
298
+ const esc = (s) => String(s).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
299
+
300
+ // Extract any existing body content
301
+ const raw = properties.content;
302
+ let bodyText = "";
303
+ let bodyHtml = "";
304
+ if (raw) {
305
+ if (typeof raw === "string") {
306
+ bodyText = raw;
307
+ bodyHtml = `<p>${raw}</p>`;
308
+ } else {
309
+ bodyText = raw.text || raw.value || "";
310
+ bodyHtml = raw.html || raw.text || raw.value || "";
311
+ }
312
+ }
313
+
314
+ // Interaction types: prepend label + target URL, append any comment
315
+ const likeOf = properties["like-of"];
316
+ if (likeOf) {
317
+ const prefix = `Liked: ${likeOf}`;
318
+ const prefixHtml = `<p>Liked: <a href="${esc(likeOf)}">${esc(likeOf)}</a></p>`;
319
+ return {
320
+ text: bodyText ? `${prefix}\n\n${bodyText}` : prefix,
321
+ html: bodyText ? `${prefixHtml}\n${bodyHtml}` : prefixHtml,
322
+ };
323
+ }
324
+
325
+ const bookmarkOf = properties["bookmark-of"];
326
+ if (bookmarkOf) {
327
+ const label = properties.name || bookmarkOf;
328
+ const prefix = `Bookmarked: ${label}`;
329
+ const prefixHtml = `<p>Bookmarked: <a href="${esc(bookmarkOf)}">${esc(label)}</a></p>`;
330
+ return {
331
+ text: bodyText ? `${prefix}\n\n${bodyText}` : prefix,
332
+ html: bodyText ? `${prefixHtml}\n${bodyHtml}` : prefixHtml,
333
+ };
334
+ }
335
+
336
+ const repostOf = properties["repost-of"];
337
+ if (repostOf) {
338
+ const label = properties.name || repostOf;
339
+ const prefix = `Reposted: ${label}`;
340
+ const prefixHtml = `<p>Reposted: <a href="${esc(repostOf)}">${esc(label)}</a></p>`;
341
+ return {
342
+ text: bodyText ? `${prefix}\n\n${bodyText}` : prefix,
343
+ html: bodyText ? `${prefixHtml}\n${bodyHtml}` : prefixHtml,
344
+ };
345
+ }
346
+
347
+ // Regular post — return body content as-is
348
+ if (bodyText || bodyHtml) {
349
+ return { text: bodyText, html: bodyHtml };
350
+ }
351
+
352
+ // Article with title but no body
353
+ if (properties.name) {
354
+ return { text: properties.name, html: `<p>${esc(properties.name)}</p>` };
355
+ }
356
+
357
+ return { text: "", html: "" };
299
358
  }
300
359
 
301
360
  function mapPostType(postType) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "3.10.1",
3
+ "version": "3.10.3",
4
4
  "description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
5
5
  "keywords": [
6
6
  "indiekit",