@rmdes/indiekit-endpoint-activitypub 3.10.0 → 3.10.1

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.
package/README.md CHANGED
@@ -110,12 +110,33 @@ ActivityPub federation endpoint for [Indiekit](https://getindiekit.com), built o
110
110
  - Federation management page with moderation overview (blocked servers, blocked accounts, muted)
111
111
 
112
112
  **Standards Compliance**
113
- - FEP-5feb: Search Indexing Consent — actor advertises `indexable` and `discoverable` properties
114
- - FEP-f1d5/0151: Enhanced NodeInfo 2.1 rich metadata including software repository, node name, staff accounts
115
- - FEP-4f05: Soft Delete with Tombstone — deleted posts return 410 with Tombstone JSON-LD including `formerType` and timestamps
116
- - FEP-3b86: Activity Intents WebFinger links for Follow, Create, Like, Announce intents with authorize_interaction routing
117
- - FEP-8fcf: Collection Synchronization — outbound follower digest headers via Fedify `syncCollection`
118
- - FEP-044f: Quote Posts rendered as embedded cards (via Fedify's `quoteUrl` support)
113
+
114
+ Core protocols and Fediverse Enhancement Proposals (FEPs) supported:
115
+
116
+ | Standard | Name | Status | Provider |
117
+ |----------|------|--------|----------|
118
+ | [ActivityPub](https://www.w3.org/TR/activitypub/) | W3C ActivityPub | Full (server-to-server) | Fedify 2.1 |
119
+ | [ActivityStreams 2.0](https://www.w3.org/TR/activitystreams-core/) | W3C Activity Streams | Full | Fedify 2.1 |
120
+ | [HTTP Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures) | draft-cavage HTTP Signatures | Full | Fedify 2.1 |
121
+ | [RFC 9421](https://www.rfc-editor.org/rfc/rfc9421) | HTTP Message Signatures | Full (with Accept-Signature negotiation) | Fedify 2.1 |
122
+ | [WebFinger](https://www.rfc-editor.org/rfc/rfc7033) | RFC 7033 WebFinger | Full | Fedify 2.1 |
123
+ | [NodeInfo 2.1](https://nodeinfo.diaspora.software/) | Server metadata discovery | Full (enriched) | Plugin |
124
+ | [FEP-8b32](https://w3id.org/fep/8b32) | Object Integrity Proofs (Ed25519) | Full | Fedify 2.1 |
125
+ | [FEP-521a](https://w3id.org/fep/521a) | Multiple key pairs (Multikey) | Full | Fedify 2.1 |
126
+ | [FEP-fe34](https://w3id.org/fep/fe34) | Origin-based security model | Full | Fedify 2.1 + Plugin |
127
+ | [FEP-8fcf](https://w3id.org/fep/8fcf) | Followers collection synchronization | Outbound only | Fedify 2.1 |
128
+ | [FEP-5feb](https://w3id.org/fep/5feb) | Search indexing consent | Full (`indexable`, `discoverable`) | Plugin |
129
+ | [FEP-f1d5](https://w3id.org/fep/f1d5) | Enhanced NodeInfo 2.1 | Full (metadata, staff accounts) | Plugin |
130
+ | [FEP-4f05](https://w3id.org/fep/4f05) | Soft delete with Tombstone | Full (410 + Tombstone JSON-LD) | Plugin |
131
+ | [FEP-3b86](https://w3id.org/fep/3b86) | Activity Intents | Full (Follow, Create, Like, Announce) | Plugin |
132
+ | [FEP-044f](https://w3id.org/fep/044f) | Quote posts | Full (Mastodon, Misskey, Fedibird formats) | Fedify 2.1 + Plugin |
133
+ | [FEP-c0e0](https://w3id.org/fep/c0e0) | Emoji reactions (EmojiReact) | Vocab support (no UI) | Fedify 2.1 |
134
+ | [FEP-5711](https://w3id.org/fep/5711) | Conversation threads | Vocab support | Fedify 2.1 |
135
+ | [Linked Data Signatures](https://w3c-dvcg.github.io/ld-signatures/) | RsaSignature2017 (legacy) | Full (outbound signing) | Fedify 2.1 |
136
+
137
+ **Status key:** *Full* = complete implementation, *Outbound only* = sending side only, *Vocab support* = types available but no dedicated UI/logic.
138
+
139
+ **Provider key:** *Fedify 2.1* = handled by the Fedify framework, *Plugin* = implemented in this plugin, *Fedify 2.1 + Plugin* = framework provides primitives, plugin wires them together.
119
140
 
120
141
  ## Requirements
121
142
 
package/lib/syndicator.js CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  } from "./jf2-to-as2.js";
9
9
  import { lookupWithSecurity } from "./lookup-helpers.js";
10
10
  import { logActivity } from "./activity-log.js";
11
+ import { addTimelineItem } from "./storage/timeline.js";
11
12
 
12
13
  /**
13
14
  * Create the ActivityPub syndicator object.
@@ -219,6 +220,54 @@ export function createSyndicator(plugin) {
219
220
  `[ActivityPub] Syndication queued: ${typeName} for ${properties.url}${replyNote}`,
220
221
  );
221
222
 
223
+ // Add own post to ap_timeline so it appears in Mastodon Client API
224
+ // timelines (Phanpy/Moshidon). Uses $setOnInsert — idempotent.
225
+ try {
226
+ const profile = await plugin._collections.ap_profile?.findOne({});
227
+ const content = normalizeContent(properties.content);
228
+ const timelineItem = {
229
+ uid: properties.url,
230
+ url: properties.url,
231
+ type: mapPostType(properties["post-type"]),
232
+ content,
233
+ author: {
234
+ name: profile?.name || handle,
235
+ url: profile?.url || plugin._publicationUrl,
236
+ photo: profile?.icon || "",
237
+ handle: `@${handle}`,
238
+ emojis: [],
239
+ bot: false,
240
+ },
241
+ published: properties.published || new Date().toISOString(),
242
+ createdAt: new Date().toISOString(),
243
+ visibility: properties.visibility || "public",
244
+ sensitive: properties.sensitive === "true",
245
+ category: Array.isArray(properties.category)
246
+ ? properties.category
247
+ : properties.category ? [properties.category] : [],
248
+ photo: normalizeMedia(properties.photo, plugin._publicationUrl),
249
+ video: normalizeMedia(properties.video, plugin._publicationUrl),
250
+ audio: normalizeMedia(properties.audio, plugin._publicationUrl),
251
+ counts: { replies: 0, boosts: 0, likes: 0 },
252
+ };
253
+ if (properties.name) timelineItem.name = properties.name;
254
+ if (properties.summary) timelineItem.summary = properties.summary;
255
+ if (properties["content-warning"]) {
256
+ timelineItem.summary = properties["content-warning"];
257
+ timelineItem.sensitive = true;
258
+ }
259
+ if (properties["in-reply-to"]) {
260
+ timelineItem.inReplyTo = Array.isArray(properties["in-reply-to"])
261
+ ? properties["in-reply-to"][0]
262
+ : properties["in-reply-to"];
263
+ }
264
+ await addTimelineItem(plugin._collections, timelineItem);
265
+ } catch (tlError) {
266
+ console.warn(
267
+ `[ActivityPub] Failed to add own post to timeline: ${tlError.message}`,
268
+ );
269
+ }
270
+
222
271
  return properties.url || undefined;
223
272
  } catch (error) {
224
273
  console.error("[ActivityPub] Syndication failed:", error.message);
@@ -237,3 +286,35 @@ export function createSyndicator(plugin) {
237
286
  update: async (properties) => plugin.update(properties),
238
287
  };
239
288
  }
289
+
290
+ // ─── Timeline helpers ───────────────────────────────────────────────────────
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
+ };
299
+ }
300
+
301
+ function mapPostType(postType) {
302
+ if (postType === "article") return "article";
303
+ if (postType === "repost") return "boost";
304
+ return "note";
305
+ }
306
+
307
+ function normalizeMedia(value, siteUrl) {
308
+ if (!value) return [];
309
+ const base = siteUrl?.replace(/\/$/, "") || "";
310
+ const arr = Array.isArray(value) ? value : [value];
311
+ return arr.map((item) => {
312
+ if (typeof item === "string") {
313
+ return item.startsWith("http") ? item : `${base}/${item.replace(/^\//, "")}`;
314
+ }
315
+ if (item?.url && !item.url.startsWith("http")) {
316
+ return { ...item, url: `${base}/${item.url.replace(/^\//, "")}` };
317
+ }
318
+ return item;
319
+ }).filter(Boolean);
320
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "3.10.0",
3
+ "version": "3.10.1",
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",