@rmdes/indiekit-endpoint-activitypub 2.0.31 → 2.0.32

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/fedidb.js +75 -28
  2. package/package.json +1 -1
package/lib/fedidb.js CHANGED
@@ -2,10 +2,13 @@
2
2
  * FediDB API client with MongoDB caching.
3
3
  *
4
4
  * Wraps https://api.fedidb.org/v1/ endpoints:
5
- * - /servers?q=...search known fediverse instances
5
+ * - /servers — cursor-paginated list of known fediverse instances (ranked by size)
6
6
  * - /popular-accounts — top accounts by follower count
7
7
  *
8
- * Responses are cached in ap_kv to avoid hitting the API on every keystroke.
8
+ * NOTE: The /servers endpoint ignores query params (q, search, name) and always
9
+ * returns the same ranked list. We paginate through ~500 servers, cache the full
10
+ * corpus for 24 hours, and filter locally when the user searches.
11
+ *
9
12
  * Cache TTL: 24 hours for both datasets.
10
13
  */
11
14
 
@@ -71,46 +74,90 @@ async function writeToCache(kvCollection, cacheKey, data) {
71
74
  }
72
75
 
73
76
  /**
74
- * Search FediDB for instances matching a query.
75
- * Returns a flat array of { domain, software, description, mau, openRegistration }.
77
+ * Fetch the FediDB server catalogue by paginating through cursor-based results.
78
+ * Cached for 24 hours as a single entry. The API ignores the `q` param and
79
+ * always returns a ranked list, so we collect a large corpus and filter locally.
76
80
  *
77
- * Results are cached per normalized query for 24 hours.
81
+ * Paginates up to MAX_PAGES (13 pages × 40 = ~520 servers), which covers
82
+ * all well-known instances. Results are cached in ap_kv for 24 hours.
78
83
  *
79
84
  * @param {object} kvCollection - MongoDB ap_kv collection
80
- * @param {string} query - Search term (e.g. "mast")
81
- * @param {number} [limit=10] - Max results
82
85
  * @returns {Promise<Array>}
83
86
  */
84
- export async function searchInstances(kvCollection, query, limit = 10) {
85
- const q = (query || "").trim().toLowerCase();
86
- if (!q) return [];
87
+ const MAX_PAGES = 13;
87
88
 
88
- const cacheKey = `fedidb:instances:${q}:${limit}`;
89
+ async function getAllServers(kvCollection) {
90
+ const cacheKey = "fedidb:servers-all";
89
91
  const cached = await getFromCache(kvCollection, cacheKey);
90
92
  if (cached) return cached;
91
93
 
94
+ const results = [];
95
+
92
96
  try {
93
- const url = `${API_BASE}/servers?q=${encodeURIComponent(q)}&limit=${limit}`;
94
- const res = await fetchWithTimeout(url);
95
- if (!res.ok) return [];
97
+ let cursor = null;
98
+
99
+ for (let page = 0; page < MAX_PAGES; page++) {
100
+ let url = `${API_BASE}/servers?limit=40`;
101
+ if (cursor) url += `&cursor=${cursor}`;
102
+
103
+ const res = await fetchWithTimeout(url);
104
+ if (!res.ok) break;
105
+
106
+ const json = await res.json();
107
+ const servers = json.data || [];
108
+ if (servers.length === 0) break;
109
+
110
+ for (const s of servers) {
111
+ results.push({
112
+ domain: s.domain,
113
+ software: s.software?.name || "Unknown",
114
+ description: s.description || "",
115
+ mau: s.stats?.monthly_active_users || 0,
116
+ userCount: s.stats?.user_count || 0,
117
+ openRegistration: s.open_registration || false,
118
+ });
119
+ }
96
120
 
97
- const json = await res.json();
98
- const servers = json.data || [];
99
-
100
- const results = servers.map((s) => ({
101
- domain: s.domain,
102
- software: s.software?.name || "Unknown",
103
- description: s.description || "",
104
- mau: s.stats?.monthly_active_users || 0,
105
- userCount: s.stats?.user_count || 0,
106
- openRegistration: s.open_registration || false,
107
- }));
121
+ cursor = json.meta?.next_cursor;
122
+ if (!cursor) break;
123
+ }
108
124
 
109
- await writeToCache(kvCollection, cacheKey, results);
110
- return results;
125
+ if (results.length > 0) {
126
+ await writeToCache(kvCollection, cacheKey, results);
127
+ }
111
128
  } catch {
112
- return [];
129
+ // Return whatever we collected so far
113
130
  }
131
+
132
+ return results;
133
+ }
134
+
135
+ /**
136
+ * Search FediDB for instances matching a query.
137
+ * Returns a flat array of { domain, software, description, mau, openRegistration }.
138
+ *
139
+ * Fetches the full server list once (cached 24h) and filters by domain/software match.
140
+ * FediDB's /v1/servers endpoint ignores the `q` param and always returns a static
141
+ * ranked list, so server-side filtering is the only way to get relevant results.
142
+ *
143
+ * @param {object} kvCollection - MongoDB ap_kv collection
144
+ * @param {string} query - Search term (e.g. "mast")
145
+ * @param {number} [limit=10] - Max results
146
+ * @returns {Promise<Array>}
147
+ */
148
+ export async function searchInstances(kvCollection, query, limit = 10) {
149
+ const q = (query || "").trim().toLowerCase();
150
+ if (!q) return [];
151
+
152
+ const allServers = await getAllServers(kvCollection);
153
+
154
+ return allServers
155
+ .filter(
156
+ (s) =>
157
+ s.domain.toLowerCase().includes(q) ||
158
+ s.software.toLowerCase().includes(q),
159
+ )
160
+ .slice(0, limit);
114
161
  }
115
162
 
116
163
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rmdes/indiekit-endpoint-activitypub",
3
- "version": "2.0.31",
3
+ "version": "2.0.32",
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",