@rmdes/indiekit-endpoint-activitypub 3.11.6 → 3.11.8
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.
|
@@ -44,7 +44,7 @@ export function setLocalIdentity(publicationUrl, handle) {
|
|
|
44
44
|
* @param {Set<string>} [options.pinnedIds] - UIDs the user has pinned
|
|
45
45
|
* @returns {object} Mastodon Status entity
|
|
46
46
|
*/
|
|
47
|
-
export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bookmarkedIds, pinnedIds, replyIdMap } = {}) {
|
|
47
|
+
export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bookmarkedIds, pinnedIds, replyIdMap, replyAccountIdMap } = {}) {
|
|
48
48
|
if (!item) return null;
|
|
49
49
|
|
|
50
50
|
// Use published-based cursor as the status ID so pagination cursors
|
|
@@ -205,7 +205,7 @@ export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bo
|
|
|
205
205
|
id,
|
|
206
206
|
created_at: published,
|
|
207
207
|
in_reply_to_id: replyIdMap?.get(item.inReplyTo) ?? null,
|
|
208
|
-
in_reply_to_account_id: null,
|
|
208
|
+
in_reply_to_account_id: replyAccountIdMap?.get(item.inReplyTo) ?? null,
|
|
209
209
|
sensitive,
|
|
210
210
|
spoiler_text: spoilerText,
|
|
211
211
|
visibility,
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Batch-resolve inReplyTo URLs to Mastodon cursor IDs.
|
|
2
|
+
* Batch-resolve inReplyTo URLs to Mastodon cursor IDs and account IDs.
|
|
3
|
+
*
|
|
4
|
+
* Looks up parent posts in ap_timeline by uid/url and returns two Maps:
|
|
5
|
+
* - replyIdMap: inReplyTo URL → cursor ID (status ID)
|
|
6
|
+
* - replyAccountIdMap: inReplyTo URL → author account ID
|
|
3
7
|
*
|
|
4
|
-
* Looks up parent posts in ap_timeline by uid/url and returns a Map
|
|
5
|
-
* of inReplyTo URL → cursor ID (milliseconds since epoch as string).
|
|
6
8
|
* Used by route handlers before calling serializeStatus().
|
|
7
9
|
*
|
|
8
10
|
* @param {object} collection - ap_timeline MongoDB collection
|
|
9
11
|
* @param {Array<object>} items - Timeline items with optional inReplyTo
|
|
10
|
-
* @returns {Promise<Map<string, string
|
|
12
|
+
* @returns {Promise<{replyIdMap: Map<string, string>, replyAccountIdMap: Map<string, string>}>}
|
|
11
13
|
*/
|
|
12
14
|
import { encodeCursor } from "./pagination.js";
|
|
15
|
+
import { remoteActorId } from "./id-mapping.js";
|
|
13
16
|
|
|
14
17
|
export async function resolveReplyIds(collection, items) {
|
|
15
|
-
const
|
|
16
|
-
|
|
18
|
+
const replyIdMap = new Map();
|
|
19
|
+
const replyAccountIdMap = new Map();
|
|
20
|
+
if (!collection || !items?.length) return { replyIdMap, replyAccountIdMap };
|
|
17
21
|
|
|
18
22
|
// Collect unique inReplyTo URLs
|
|
19
23
|
const urls = [
|
|
@@ -23,22 +27,27 @@ export async function resolveReplyIds(collection, items) {
|
|
|
23
27
|
.filter(Boolean),
|
|
24
28
|
),
|
|
25
29
|
];
|
|
26
|
-
if (urls.length === 0) return
|
|
30
|
+
if (urls.length === 0) return { replyIdMap, replyAccountIdMap };
|
|
27
31
|
|
|
28
32
|
// Batch lookup parents by uid or url
|
|
29
33
|
const parents = await collection
|
|
30
34
|
.find({ $or: [{ uid: { $in: urls } }, { url: { $in: urls } }] })
|
|
31
|
-
.project({ uid: 1, url: 1, published: 1 })
|
|
35
|
+
.project({ uid: 1, url: 1, published: 1, "author.url": 1 })
|
|
32
36
|
.toArray();
|
|
33
37
|
|
|
34
38
|
for (const parent of parents) {
|
|
35
39
|
const cursorId = encodeCursor(parent.published);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
const authorUrl = parent.author?.url;
|
|
41
|
+
const authorAccountId = authorUrl ? remoteActorId(authorUrl) : null;
|
|
42
|
+
|
|
43
|
+
const setMaps = (key) => {
|
|
44
|
+
if (cursorId && cursorId !== "0") replyIdMap.set(key, cursorId);
|
|
45
|
+
if (authorAccountId) replyAccountIdMap.set(key, authorAccountId);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
if (parent.uid) setMaps(parent.uid);
|
|
49
|
+
if (parent.url && parent.url !== parent.uid) setMaps(parent.url);
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
return
|
|
52
|
+
return { replyIdMap, replyAccountIdMap };
|
|
44
53
|
}
|
|
@@ -44,13 +44,14 @@ router.get("/api/v1/statuses/:id", tokenRequired, scopeRequired("read", "read:st
|
|
|
44
44
|
|
|
45
45
|
// Load interaction state if authenticated
|
|
46
46
|
const interactionState = await loadItemInteractions(collections, item);
|
|
47
|
-
const replyIdMap = await resolveReplyIds(collections.ap_timeline, [item]);
|
|
47
|
+
const { replyIdMap, replyAccountIdMap } = await resolveReplyIds(collections.ap_timeline, [item]);
|
|
48
48
|
|
|
49
49
|
const status = serializeStatus(item, {
|
|
50
50
|
baseUrl,
|
|
51
51
|
...interactionState,
|
|
52
52
|
pinnedIds: new Set(),
|
|
53
53
|
replyIdMap,
|
|
54
|
+
replyAccountIdMap,
|
|
54
55
|
});
|
|
55
56
|
|
|
56
57
|
res.json(status);
|
|
@@ -126,8 +127,8 @@ router.get("/api/v1/statuses/:id/context", tokenRequired, scopeRequired("read",
|
|
|
126
127
|
};
|
|
127
128
|
|
|
128
129
|
const allItems = [...ancestors, ...descendants];
|
|
129
|
-
const replyIdMap = await resolveReplyIds(collections.ap_timeline, allItems);
|
|
130
|
-
const serializeOpts = { baseUrl, ...emptyInteractions, replyIdMap };
|
|
130
|
+
const { replyIdMap, replyAccountIdMap } = await resolveReplyIds(collections.ap_timeline, allItems);
|
|
131
|
+
const serializeOpts = { baseUrl, ...emptyInteractions, replyIdMap, replyAccountIdMap };
|
|
131
132
|
|
|
132
133
|
res.json({
|
|
133
134
|
ancestors: ancestors.map((a) => serializeStatus(a, serializeOpts)),
|
|
@@ -65,7 +65,7 @@ router.get("/api/v1/timelines/home", tokenRequired, scopeRequired("read", "read:
|
|
|
65
65
|
);
|
|
66
66
|
|
|
67
67
|
// Resolve reply parent IDs for threading
|
|
68
|
-
const replyIdMap = await resolveReplyIds(collections.ap_timeline, items);
|
|
68
|
+
const { replyIdMap, replyAccountIdMap } = await resolveReplyIds(collections.ap_timeline, items);
|
|
69
69
|
|
|
70
70
|
// Serialize to Mastodon Status entities
|
|
71
71
|
const statuses = items.map((item) =>
|
|
@@ -76,6 +76,7 @@ router.get("/api/v1/timelines/home", tokenRequired, scopeRequired("read", "read:
|
|
|
76
76
|
bookmarkedIds,
|
|
77
77
|
pinnedIds: new Set(),
|
|
78
78
|
replyIdMap,
|
|
79
|
+
replyAccountIdMap,
|
|
79
80
|
}),
|
|
80
81
|
);
|
|
81
82
|
|
|
@@ -168,7 +169,7 @@ router.get("/api/v1/timelines/public", async (req, res, next) => {
|
|
|
168
169
|
));
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
const replyIdMap = await resolveReplyIds(collections.ap_timeline, items);
|
|
172
|
+
const { replyIdMap: rIdMap, replyAccountIdMap: rAcctMap } = await resolveReplyIds(collections.ap_timeline, items);
|
|
172
173
|
|
|
173
174
|
const statuses = items.map((item) =>
|
|
174
175
|
serializeStatus(item, {
|
|
@@ -177,7 +178,8 @@ router.get("/api/v1/timelines/public", async (req, res, next) => {
|
|
|
177
178
|
rebloggedIds,
|
|
178
179
|
bookmarkedIds,
|
|
179
180
|
pinnedIds: new Set(),
|
|
180
|
-
replyIdMap,
|
|
181
|
+
replyIdMap: rIdMap,
|
|
182
|
+
replyAccountIdMap: rAcctMap,
|
|
181
183
|
}),
|
|
182
184
|
);
|
|
183
185
|
|
|
@@ -234,7 +236,7 @@ router.get("/api/v1/timelines/tag/:hashtag", async (req, res, next) => {
|
|
|
234
236
|
));
|
|
235
237
|
}
|
|
236
238
|
|
|
237
|
-
const replyIdMap = await resolveReplyIds(collections.ap_timeline, items);
|
|
239
|
+
const { replyIdMap: rIdMap, replyAccountIdMap: rAcctMap } = await resolveReplyIds(collections.ap_timeline, items);
|
|
238
240
|
|
|
239
241
|
const statuses = items.map((item) =>
|
|
240
242
|
serializeStatus(item, {
|
|
@@ -243,7 +245,8 @@ router.get("/api/v1/timelines/tag/:hashtag", async (req, res, next) => {
|
|
|
243
245
|
rebloggedIds,
|
|
244
246
|
bookmarkedIds,
|
|
245
247
|
pinnedIds: new Set(),
|
|
246
|
-
replyIdMap,
|
|
248
|
+
replyIdMap: rIdMap,
|
|
249
|
+
replyAccountIdMap: rAcctMap,
|
|
247
250
|
}),
|
|
248
251
|
);
|
|
249
252
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "3.11.
|
|
3
|
+
"version": "3.11.8",
|
|
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",
|