@fedify/relay 2.0.0-pr.471.1 → 2.0.0-pr.471.1920

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 (3) hide show
  1. package/dist/mod.cjs +222 -7
  2. package/dist/mod.js +223 -8
  3. package/package.json +7 -5
package/dist/mod.cjs CHANGED
@@ -120,21 +120,21 @@ var MastodonRelay = class {
120
120
  const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
121
121
  if (!isPublicFollow && parsed?.type !== "actor") return;
122
122
  const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
123
- const recipient = await follow.getActor(ctx);
124
- if (recipient == null || recipient.id == null || recipient.preferredUsername == null || recipient.inboxId == null) return;
123
+ const follower = await follow.getActor(ctx);
124
+ if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
125
125
  let approved = false;
126
- if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, recipient);
126
+ if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, follower);
127
127
  if (approved) {
128
128
  const followers = await options.kv.get(["followers"]) ?? [];
129
129
  followers.push(follow.id.href);
130
130
  await options.kv.set(["followers"], followers);
131
- await options.kv.set(["follower", follow.id.href], await recipient.toJsonLd());
132
- await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, recipient, new __fedify_fedify_vocab.Accept({
131
+ await options.kv.set(["follower", follow.id.href], await follower.toJsonLd());
132
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Accept({
133
133
  id: new URL(`#accepts`, relayActorUri),
134
134
  actor: relayActorUri,
135
135
  object: follow
136
136
  }));
137
- } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, recipient, new __fedify_fedify_vocab.Reject({
137
+ } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Reject({
138
138
  id: new URL(`#rejects`, relayActorUri),
139
139
  actor: relayActorUri,
140
140
  object: follow
@@ -210,7 +210,222 @@ var LitePubRelay = class {
210
210
  #subscriptionHandler;
211
211
  constructor(options) {
212
212
  this.#options = options;
213
- this.#federation = (0, __fedify_fedify.createFederation)({ kv: options.kv });
213
+ this.#federation = options.federation ?? (0, __fedify_fedify.createFederation)({
214
+ kv: options.kv,
215
+ queue: options.queue,
216
+ documentLoaderFactory: options.documentLoaderFactory,
217
+ authenticatedDocumentLoaderFactory: options.authenticatedDocumentLoaderFactory
218
+ });
219
+ this.#federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
220
+ if (identifier !== RELAY_SERVER_ACTOR) return null;
221
+ const keys = await ctx.getActorKeyPairs(identifier);
222
+ return new __fedify_fedify_vocab.Application({
223
+ id: ctx.getActorUri(identifier),
224
+ preferredUsername: identifier,
225
+ name: "ActivityPub Relay",
226
+ summary: "LitePub-compatible ActivityPub relay server",
227
+ inbox: ctx.getInboxUri(),
228
+ followers: ctx.getFollowersUri(identifier),
229
+ following: ctx.getFollowingUri(identifier),
230
+ url: ctx.getActorUri(identifier),
231
+ publicKey: keys[0].cryptographicKey,
232
+ assertionMethods: keys.map((k) => k.multikey)
233
+ });
234
+ }).setKeyPairsDispatcher(async (_ctx, identifier) => {
235
+ if (identifier !== RELAY_SERVER_ACTOR) return [];
236
+ const rsaPairJson = await options.kv.get([
237
+ "keypair",
238
+ "rsa",
239
+ identifier
240
+ ]);
241
+ const ed25519PairJson = await options.kv.get([
242
+ "keypair",
243
+ "ed25519",
244
+ identifier
245
+ ]);
246
+ if (rsaPairJson == null || ed25519PairJson == null) {
247
+ const rsaPair$1 = await (0, __fedify_fedify.generateCryptoKeyPair)("RSASSA-PKCS1-v1_5");
248
+ const ed25519Pair$1 = await (0, __fedify_fedify.generateCryptoKeyPair)("Ed25519");
249
+ await options.kv.set([
250
+ "keypair",
251
+ "rsa",
252
+ identifier
253
+ ], {
254
+ privateKey: await (0, __fedify_fedify.exportJwk)(rsaPair$1.privateKey),
255
+ publicKey: await (0, __fedify_fedify.exportJwk)(rsaPair$1.publicKey)
256
+ });
257
+ await options.kv.set([
258
+ "keypair",
259
+ "ed25519",
260
+ identifier
261
+ ], {
262
+ privateKey: await (0, __fedify_fedify.exportJwk)(ed25519Pair$1.privateKey),
263
+ publicKey: await (0, __fedify_fedify.exportJwk)(ed25519Pair$1.publicKey)
264
+ });
265
+ return [rsaPair$1, ed25519Pair$1];
266
+ }
267
+ const rsaPair = {
268
+ privateKey: await (0, __fedify_fedify.importJwk)(rsaPairJson.privateKey, "private"),
269
+ publicKey: await (0, __fedify_fedify.importJwk)(rsaPairJson.publicKey, "public")
270
+ };
271
+ const ed25519Pair = {
272
+ privateKey: await (0, __fedify_fedify.importJwk)(ed25519PairJson.privateKey, "private"),
273
+ publicKey: await (0, __fedify_fedify.importJwk)(ed25519PairJson.publicKey, "public")
274
+ };
275
+ return [rsaPair, ed25519Pair];
276
+ });
277
+ this.#federation.setFollowingDispatcher("/users/{identifier}/following", (_ctx, _identifier) => {
278
+ return { items: [] };
279
+ }).setCounter((_ctx, _identifier) => 0);
280
+ this.#federation.setFollowersDispatcher("/users/{identifier}/followers", async (_ctx, identifier) => {
281
+ if (identifier !== RELAY_SERVER_ACTOR) return null;
282
+ const followers = await options.kv.get(["followers"]) ?? [];
283
+ const actors = [];
284
+ for (const followerId of followers) {
285
+ const follower = await options.kv.get(["follower", followerId]);
286
+ if (!follower) continue;
287
+ const actor = await __fedify_fedify_vocab.Object.fromJsonLd(follower.actor);
288
+ if (!(0, __fedify_fedify_vocab.isActor)(actor)) continue;
289
+ actors.push(actor);
290
+ }
291
+ return { items: actors };
292
+ });
293
+ this.#federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(__fedify_fedify_vocab.Follow, async (ctx, follow) => {
294
+ if (follow.id == null || follow.objectId == null) return;
295
+ const parsed = ctx.parseUri(follow.objectId);
296
+ const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
297
+ if (!isPublicFollow && parsed?.type !== "actor") return;
298
+ const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
299
+ const follower = await follow.getActor(ctx);
300
+ if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
301
+ const existingFollow = await options.kv.get(["follower", follower.id.href]);
302
+ if (existingFollow?.state === "pending") return;
303
+ let subscriptionApproved = false;
304
+ if (this.#subscriptionHandler) subscriptionApproved = await this.#subscriptionHandler(ctx, follower);
305
+ if (subscriptionApproved) {
306
+ await options.kv.set(["follower", follower.id.href], {
307
+ "actor": await follower.toJsonLd(),
308
+ "state": "pending"
309
+ });
310
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Accept({
311
+ id: new URL(`#accepts`, relayActorUri),
312
+ actor: relayActorUri,
313
+ object: follow
314
+ }));
315
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Follow({
316
+ actor: relayActorUri,
317
+ object: follower.id,
318
+ to: follower.id
319
+ }));
320
+ } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new __fedify_fedify_vocab.Reject({
321
+ id: new URL(`#rejects`, relayActorUri),
322
+ actor: relayActorUri,
323
+ object: follow
324
+ }));
325
+ }).on(__fedify_fedify_vocab.Accept, async (ctx, accept) => {
326
+ const follow = await accept.getObject({
327
+ crossOrigin: "trust",
328
+ ...ctx
329
+ });
330
+ if (!(follow instanceof __fedify_fedify_vocab.Follow)) return;
331
+ const follower = follow.actorId;
332
+ if (follower == null) return;
333
+ const following = await accept.getActor();
334
+ if (!(0, __fedify_fedify_vocab.isActor)(following) || !following.id) return;
335
+ const parsed = ctx.parseUri(follower);
336
+ if (parsed == null || parsed.type !== "actor") return;
337
+ const followerData = await options.kv.get(["follower", following.id.href]);
338
+ if (followerData == null) return;
339
+ const updatedFollowerData = {
340
+ ...followerData,
341
+ status: "accepted"
342
+ };
343
+ await options.kv.set(["follower", following.id.href], updatedFollowerData);
344
+ const followers = await options.kv.get(["followers"]) ?? [];
345
+ followers.push(following.id.href);
346
+ await options.kv.set(["followers"], followers);
347
+ }).on(__fedify_fedify_vocab.Undo, async (ctx, undo) => {
348
+ const activity = await undo.getObject({
349
+ crossOrigin: "trust",
350
+ ...ctx
351
+ });
352
+ if (activity instanceof __fedify_fedify_vocab.Follow) {
353
+ if (activity.id == null || activity.actorId == null) return;
354
+ const followers = await options.kv.get(["followers"]) ?? [];
355
+ const updatedFollowers = followers.filter((id) => id !== activity.actorId?.href);
356
+ await options.kv.set(["followers"], updatedFollowers);
357
+ options.kv.delete(["follower", activity.actorId?.href]);
358
+ } else console.warn("Unsupported object type ({type}) for Undo activity: {object}", {
359
+ type: activity?.constructor.name,
360
+ object: activity
361
+ });
362
+ }).on(__fedify_fedify_vocab.Create, async (ctx, create) => {
363
+ const sender = await create.getActor(ctx);
364
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
365
+ const announce = new __fedify_fedify_vocab.Announce({
366
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
367
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
368
+ object: create.objectId,
369
+ to: __fedify_fedify_vocab.PUBLIC_COLLECTION,
370
+ published: Temporal.Now.instant()
371
+ });
372
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
373
+ excludeBaseUris,
374
+ preferSharedInbox: true
375
+ });
376
+ }).on(__fedify_fedify_vocab.Update, async (ctx, update) => {
377
+ const sender = await update.getActor(ctx);
378
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
379
+ const announce = new __fedify_fedify_vocab.Announce({
380
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
381
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
382
+ object: update.objectId,
383
+ to: __fedify_fedify_vocab.PUBLIC_COLLECTION
384
+ });
385
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
386
+ excludeBaseUris,
387
+ preferSharedInbox: true
388
+ });
389
+ }).on(__fedify_fedify_vocab.Move, async (ctx, move) => {
390
+ const sender = await move.getActor(ctx);
391
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
392
+ const announce = new __fedify_fedify_vocab.Announce({
393
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
394
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
395
+ object: move.objectId,
396
+ to: __fedify_fedify_vocab.PUBLIC_COLLECTION
397
+ });
398
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
399
+ excludeBaseUris,
400
+ preferSharedInbox: true
401
+ });
402
+ }).on(__fedify_fedify_vocab.Delete, async (ctx, deleteActivity) => {
403
+ const sender = await deleteActivity.getActor(ctx);
404
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
405
+ const announce = new __fedify_fedify_vocab.Announce({
406
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
407
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
408
+ object: deleteActivity.objectId,
409
+ to: __fedify_fedify_vocab.PUBLIC_COLLECTION
410
+ });
411
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
412
+ excludeBaseUris,
413
+ preferSharedInbox: true
414
+ });
415
+ }).on(__fedify_fedify_vocab.Announce, async (ctx, announceActivity) => {
416
+ const sender = await announceActivity.getActor(ctx);
417
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
418
+ const announce = new __fedify_fedify_vocab.Announce({
419
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
420
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
421
+ object: announceActivity.objectId,
422
+ to: __fedify_fedify_vocab.PUBLIC_COLLECTION
423
+ });
424
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
425
+ excludeBaseUris,
426
+ preferSharedInbox: true
427
+ });
428
+ });
214
429
  }
215
430
  get domain() {
216
431
  return this.#options.domain || "localhost";
package/dist/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createFederation, exportJwk, generateCryptoKeyPair, importJwk } from "@fedify/fedify";
2
- import { Accept, Create, Delete, Follow, Move, Object as Object$1, Reject, Service, Undo, Update, isActor } from "@fedify/fedify/vocab";
2
+ import { Accept, Announce, Application, Create, Delete, Follow, Move, Object as Object$1, PUBLIC_COLLECTION, Reject, Service, Undo, Update, isActor } from "@fedify/fedify/vocab";
3
3
 
4
4
  //#region src/relay.ts
5
5
  const RELAY_SERVER_ACTOR = "relay";
@@ -97,21 +97,21 @@ var MastodonRelay = class {
97
97
  const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
98
98
  if (!isPublicFollow && parsed?.type !== "actor") return;
99
99
  const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
100
- const recipient = await follow.getActor(ctx);
101
- if (recipient == null || recipient.id == null || recipient.preferredUsername == null || recipient.inboxId == null) return;
100
+ const follower = await follow.getActor(ctx);
101
+ if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
102
102
  let approved = false;
103
- if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, recipient);
103
+ if (this.#subscriptionHandler) approved = await this.#subscriptionHandler(ctx, follower);
104
104
  if (approved) {
105
105
  const followers = await options.kv.get(["followers"]) ?? [];
106
106
  followers.push(follow.id.href);
107
107
  await options.kv.set(["followers"], followers);
108
- await options.kv.set(["follower", follow.id.href], await recipient.toJsonLd());
109
- await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, recipient, new Accept({
108
+ await options.kv.set(["follower", follow.id.href], await follower.toJsonLd());
109
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Accept({
110
110
  id: new URL(`#accepts`, relayActorUri),
111
111
  actor: relayActorUri,
112
112
  object: follow
113
113
  }));
114
- } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, recipient, new Reject({
114
+ } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Reject({
115
115
  id: new URL(`#rejects`, relayActorUri),
116
116
  actor: relayActorUri,
117
117
  object: follow
@@ -187,7 +187,222 @@ var LitePubRelay = class {
187
187
  #subscriptionHandler;
188
188
  constructor(options) {
189
189
  this.#options = options;
190
- this.#federation = createFederation({ kv: options.kv });
190
+ this.#federation = options.federation ?? createFederation({
191
+ kv: options.kv,
192
+ queue: options.queue,
193
+ documentLoaderFactory: options.documentLoaderFactory,
194
+ authenticatedDocumentLoaderFactory: options.authenticatedDocumentLoaderFactory
195
+ });
196
+ this.#federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
197
+ if (identifier !== RELAY_SERVER_ACTOR) return null;
198
+ const keys = await ctx.getActorKeyPairs(identifier);
199
+ return new Application({
200
+ id: ctx.getActorUri(identifier),
201
+ preferredUsername: identifier,
202
+ name: "ActivityPub Relay",
203
+ summary: "LitePub-compatible ActivityPub relay server",
204
+ inbox: ctx.getInboxUri(),
205
+ followers: ctx.getFollowersUri(identifier),
206
+ following: ctx.getFollowingUri(identifier),
207
+ url: ctx.getActorUri(identifier),
208
+ publicKey: keys[0].cryptographicKey,
209
+ assertionMethods: keys.map((k) => k.multikey)
210
+ });
211
+ }).setKeyPairsDispatcher(async (_ctx, identifier) => {
212
+ if (identifier !== RELAY_SERVER_ACTOR) return [];
213
+ const rsaPairJson = await options.kv.get([
214
+ "keypair",
215
+ "rsa",
216
+ identifier
217
+ ]);
218
+ const ed25519PairJson = await options.kv.get([
219
+ "keypair",
220
+ "ed25519",
221
+ identifier
222
+ ]);
223
+ if (rsaPairJson == null || ed25519PairJson == null) {
224
+ const rsaPair$1 = await generateCryptoKeyPair("RSASSA-PKCS1-v1_5");
225
+ const ed25519Pair$1 = await generateCryptoKeyPair("Ed25519");
226
+ await options.kv.set([
227
+ "keypair",
228
+ "rsa",
229
+ identifier
230
+ ], {
231
+ privateKey: await exportJwk(rsaPair$1.privateKey),
232
+ publicKey: await exportJwk(rsaPair$1.publicKey)
233
+ });
234
+ await options.kv.set([
235
+ "keypair",
236
+ "ed25519",
237
+ identifier
238
+ ], {
239
+ privateKey: await exportJwk(ed25519Pair$1.privateKey),
240
+ publicKey: await exportJwk(ed25519Pair$1.publicKey)
241
+ });
242
+ return [rsaPair$1, ed25519Pair$1];
243
+ }
244
+ const rsaPair = {
245
+ privateKey: await importJwk(rsaPairJson.privateKey, "private"),
246
+ publicKey: await importJwk(rsaPairJson.publicKey, "public")
247
+ };
248
+ const ed25519Pair = {
249
+ privateKey: await importJwk(ed25519PairJson.privateKey, "private"),
250
+ publicKey: await importJwk(ed25519PairJson.publicKey, "public")
251
+ };
252
+ return [rsaPair, ed25519Pair];
253
+ });
254
+ this.#federation.setFollowingDispatcher("/users/{identifier}/following", (_ctx, _identifier) => {
255
+ return { items: [] };
256
+ }).setCounter((_ctx, _identifier) => 0);
257
+ this.#federation.setFollowersDispatcher("/users/{identifier}/followers", async (_ctx, identifier) => {
258
+ if (identifier !== RELAY_SERVER_ACTOR) return null;
259
+ const followers = await options.kv.get(["followers"]) ?? [];
260
+ const actors = [];
261
+ for (const followerId of followers) {
262
+ const follower = await options.kv.get(["follower", followerId]);
263
+ if (!follower) continue;
264
+ const actor = await Object$1.fromJsonLd(follower.actor);
265
+ if (!isActor(actor)) continue;
266
+ actors.push(actor);
267
+ }
268
+ return { items: actors };
269
+ });
270
+ this.#federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(Follow, async (ctx, follow) => {
271
+ if (follow.id == null || follow.objectId == null) return;
272
+ const parsed = ctx.parseUri(follow.objectId);
273
+ const isPublicFollow = follow.objectId.href === "https://www.w3.org/ns/activitystreams#Public";
274
+ if (!isPublicFollow && parsed?.type !== "actor") return;
275
+ const relayActorUri = ctx.getActorUri(RELAY_SERVER_ACTOR);
276
+ const follower = await follow.getActor(ctx);
277
+ if (follower == null || follower.id == null || follower.preferredUsername == null || follower.inboxId == null) return;
278
+ const existingFollow = await options.kv.get(["follower", follower.id.href]);
279
+ if (existingFollow?.state === "pending") return;
280
+ let subscriptionApproved = false;
281
+ if (this.#subscriptionHandler) subscriptionApproved = await this.#subscriptionHandler(ctx, follower);
282
+ if (subscriptionApproved) {
283
+ await options.kv.set(["follower", follower.id.href], {
284
+ "actor": await follower.toJsonLd(),
285
+ "state": "pending"
286
+ });
287
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Accept({
288
+ id: new URL(`#accepts`, relayActorUri),
289
+ actor: relayActorUri,
290
+ object: follow
291
+ }));
292
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Follow({
293
+ actor: relayActorUri,
294
+ object: follower.id,
295
+ to: follower.id
296
+ }));
297
+ } else await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, follower, new Reject({
298
+ id: new URL(`#rejects`, relayActorUri),
299
+ actor: relayActorUri,
300
+ object: follow
301
+ }));
302
+ }).on(Accept, async (ctx, accept) => {
303
+ const follow = await accept.getObject({
304
+ crossOrigin: "trust",
305
+ ...ctx
306
+ });
307
+ if (!(follow instanceof Follow)) return;
308
+ const follower = follow.actorId;
309
+ if (follower == null) return;
310
+ const following = await accept.getActor();
311
+ if (!isActor(following) || !following.id) return;
312
+ const parsed = ctx.parseUri(follower);
313
+ if (parsed == null || parsed.type !== "actor") return;
314
+ const followerData = await options.kv.get(["follower", following.id.href]);
315
+ if (followerData == null) return;
316
+ const updatedFollowerData = {
317
+ ...followerData,
318
+ status: "accepted"
319
+ };
320
+ await options.kv.set(["follower", following.id.href], updatedFollowerData);
321
+ const followers = await options.kv.get(["followers"]) ?? [];
322
+ followers.push(following.id.href);
323
+ await options.kv.set(["followers"], followers);
324
+ }).on(Undo, async (ctx, undo) => {
325
+ const activity = await undo.getObject({
326
+ crossOrigin: "trust",
327
+ ...ctx
328
+ });
329
+ if (activity instanceof Follow) {
330
+ if (activity.id == null || activity.actorId == null) return;
331
+ const followers = await options.kv.get(["followers"]) ?? [];
332
+ const updatedFollowers = followers.filter((id) => id !== activity.actorId?.href);
333
+ await options.kv.set(["followers"], updatedFollowers);
334
+ options.kv.delete(["follower", activity.actorId?.href]);
335
+ } else console.warn("Unsupported object type ({type}) for Undo activity: {object}", {
336
+ type: activity?.constructor.name,
337
+ object: activity
338
+ });
339
+ }).on(Create, async (ctx, create) => {
340
+ const sender = await create.getActor(ctx);
341
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
342
+ const announce = new Announce({
343
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
344
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
345
+ object: create.objectId,
346
+ to: PUBLIC_COLLECTION,
347
+ published: Temporal.Now.instant()
348
+ });
349
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
350
+ excludeBaseUris,
351
+ preferSharedInbox: true
352
+ });
353
+ }).on(Update, async (ctx, update) => {
354
+ const sender = await update.getActor(ctx);
355
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
356
+ const announce = new Announce({
357
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
358
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
359
+ object: update.objectId,
360
+ to: PUBLIC_COLLECTION
361
+ });
362
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
363
+ excludeBaseUris,
364
+ preferSharedInbox: true
365
+ });
366
+ }).on(Move, async (ctx, move) => {
367
+ const sender = await move.getActor(ctx);
368
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
369
+ const announce = new Announce({
370
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
371
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
372
+ object: move.objectId,
373
+ to: PUBLIC_COLLECTION
374
+ });
375
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
376
+ excludeBaseUris,
377
+ preferSharedInbox: true
378
+ });
379
+ }).on(Delete, async (ctx, deleteActivity) => {
380
+ const sender = await deleteActivity.getActor(ctx);
381
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
382
+ const announce = new Announce({
383
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
384
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
385
+ object: deleteActivity.objectId,
386
+ to: PUBLIC_COLLECTION
387
+ });
388
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
389
+ excludeBaseUris,
390
+ preferSharedInbox: true
391
+ });
392
+ }).on(Announce, async (ctx, announceActivity) => {
393
+ const sender = await announceActivity.getActor(ctx);
394
+ const excludeBaseUris = sender?.id ? [new URL(sender.id)] : [];
395
+ const announce = new Announce({
396
+ id: new URL(`/announce#${crypto.randomUUID()}`, ctx.origin),
397
+ actor: ctx.getActorUri(RELAY_SERVER_ACTOR),
398
+ object: announceActivity.objectId,
399
+ to: PUBLIC_COLLECTION
400
+ });
401
+ await ctx.sendActivity({ identifier: RELAY_SERVER_ACTOR }, "followers", announce, {
402
+ excludeBaseUris,
403
+ preferSharedInbox: true
404
+ });
405
+ });
191
406
  }
192
407
  get domain() {
193
408
  return this.#options.domain || "localhost";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/relay",
3
- "version": "2.0.0-pr.471.1",
3
+ "version": "2.0.0-pr.471.1920+38258210",
4
4
  "description": "ActivityPub relay support for Fedify",
5
5
  "keywords": [
6
6
  "Fedify",
@@ -48,17 +48,19 @@
48
48
  "package.json"
49
49
  ],
50
50
  "peerDependencies": {
51
- "@fedify/fedify": "^2.0.0-pr.471.1"
51
+ "@fedify/fedify": "^2.0.0-pr.471.1920+38258210"
52
52
  },
53
53
  "devDependencies": {
54
54
  "tsdown": "^0.12.9",
55
- "typescript": "^5.9.3"
55
+ "typescript": "^5.9.3",
56
+ "@fedify/testing": "^2.0.0-pr.471.1920+38258210",
57
+ "@fedify/vocab-runtime": "^2.0.0-pr.471.1920+38258210"
56
58
  },
57
59
  "scripts": {
58
60
  "build": "deno task codegen && tsdown",
59
61
  "prepublish": "deno task codegen && tsdown",
60
- "test": "deno task codegen && tsdown && cd dist/ && node --test",
61
- "test:bun": "deno task codegen && deno task pnpm:build-vocab && tsdown && cd dist/ && bun test --timeout 60000",
62
+ "test": "deno task codegen && tsdown && node --test",
63
+ "test:bun": "deno task codegen && tsdown && bun test --timeout 60000",
62
64
  "test:cfworkers": "deno task codegen && wrangler deploy --dry-run --outdir src/cfworkers && node --import=tsx src/cfworkers/client.ts"
63
65
  }
64
66
  }