@fedify/fedify 2.2.0-dev.802 → 2.2.0-pr.695.16

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 (63) hide show
  1. package/dist/{builder-DI1wKPDM.mjs → builder-7PVCiLiR.mjs} +7 -57
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{context-BGrYMSTk.d.ts → context-78ecvxf5.d.ts} +1 -143
  6. package/dist/{context-CMUd4wy0.d.cts → context-DYDPdoCb.d.cts} +1 -143
  7. package/dist/{context-Dk_tacqz.mjs → context-Juj6bdHC.mjs} +2 -17
  8. package/dist/{deno-Cw2dzrO3.mjs → deno-vxcWcxQS.mjs} +1 -1
  9. package/dist/{docloader-DgqPtQaA.mjs → docloader-D7q0-Xef.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -25
  11. package/dist/federation/handler.test.mjs +8 -369
  12. package/dist/federation/idempotency.test.mjs +2 -2
  13. package/dist/federation/inbox.test.mjs +3 -3
  14. package/dist/federation/middleware.test.mjs +8 -510
  15. package/dist/federation/mod.cjs +1 -1
  16. package/dist/federation/mod.d.cts +3 -3
  17. package/dist/federation/mod.d.ts +3 -3
  18. package/dist/federation/mod.js +1 -1
  19. package/dist/federation/send.test.mjs +3 -3
  20. package/dist/federation/webfinger.test.mjs +2 -2
  21. package/dist/{http-sBAA3RBV.js → http-D-MhhYUF.js} +1 -1
  22. package/dist/{http-B8Eh-Nuv.cjs → http-JxF7bG0o.cjs} +1 -1
  23. package/dist/{http-iFvJsgX5.mjs → http-RZPxDWq5.mjs} +2 -2
  24. package/dist/inbox-CmYvcSMM.mjs +179 -0
  25. package/dist/{key-hslhDRFM.mjs → key-CGx_dDkX.mjs} +1 -1
  26. package/dist/{kv-cache-BJGwpmdO.cjs → kv-cache-C2gdVgvb.cjs} +1 -1
  27. package/dist/{kv-cache-CPthE3cU.js → kv-cache-D84Mk0fZ.js} +1 -1
  28. package/dist/{ld-BDJhDECh.mjs → ld-wup-liFO.mjs} +3 -26
  29. package/dist/{middleware-CERbaJNL.mjs → middleware-BjVx-_bv.mjs} +180 -612
  30. package/dist/{middleware-gIhAvTk3.cjs → middleware-Bn75dPug.cjs} +365 -718
  31. package/dist/{middleware-uOgNgCF7.cjs → middleware-CXOVT4Ph.cjs} +1 -1
  32. package/dist/{middleware-D4erN-QW.js → middleware-RF-sUfTr.js} +368 -716
  33. package/dist/{middleware-_UvHRcH-.mjs → middleware-wdfeWjRJ.mjs} +1 -1
  34. package/dist/{mod-BcJHeuv1.d.cts → mod-CEohtXhV.d.cts} +1 -1
  35. package/dist/{mod-CJXfyw7v.d.ts → mod-CokIUYDr.d.ts} +1 -1
  36. package/dist/{mod-Cr3f-ACa.d.cts → mod-DoJBjjnO.d.cts} +1 -18
  37. package/dist/{mod-CR8soWa9.d.ts → mod-DvxszxXC.d.ts} +1 -18
  38. package/dist/mod.cjs +4 -6
  39. package/dist/mod.d.cts +5 -5
  40. package/dist/mod.d.ts +5 -5
  41. package/dist/mod.js +5 -5
  42. package/dist/nodeinfo/handler.test.mjs +2 -2
  43. package/dist/{owner-B4-VE_g8.mjs → owner-q2mUMM9a.mjs} +2 -2
  44. package/dist/{proof-B6PfWGbc.mjs → proof--CpZsF_p.mjs} +3 -32
  45. package/dist/{proof-BqB_7rN2.js → proof-CirP9OSd.js} +2 -54
  46. package/dist/{proof-4H8fV7OE.cjs → proof-_Zyfqyce.cjs} +3 -61
  47. package/dist/{send-BIh8F_6u.mjs → send-CVJfx7bF.mjs} +2 -2
  48. package/dist/sig/http.test.mjs +2 -2
  49. package/dist/sig/key.test.mjs +1 -1
  50. package/dist/sig/ld.test.mjs +2 -44
  51. package/dist/sig/mod.cjs +2 -4
  52. package/dist/sig/mod.d.cts +2 -2
  53. package/dist/sig/mod.d.ts +2 -2
  54. package/dist/sig/mod.js +3 -3
  55. package/dist/sig/owner.test.mjs +1 -1
  56. package/dist/sig/proof.test.mjs +2 -46
  57. package/dist/testing/mod.d.mts +1 -149
  58. package/dist/testing/mod.mjs +2 -2
  59. package/dist/utils/docloader.test.mjs +2 -2
  60. package/dist/utils/mod.cjs +1 -1
  61. package/dist/utils/mod.js +1 -1
  62. package/package.json +6 -6
  63. package/dist/activity-listener-Ck3JZ_hR.mjs +0 -40
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-B8Eh-Nuv.cjs");
6
- const require_proof = require("./proof-4H8fV7OE.cjs");
5
+ const require_http = require("./http-JxF7bG0o.cjs");
6
+ const require_proof = require("./proof-_Zyfqyce.cjs");
7
7
  const require_types = require("./types-KC4QAoxe.cjs");
8
- const require_kv_cache = require("./kv-cache-BJGwpmdO.cjs");
8
+ const require_kv_cache = require("./kv-cache-C2gdVgvb.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_vocab = require("@fedify/vocab");
11
11
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -17,15 +17,14 @@ let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
17
17
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
18
18
  let _fedify_webfinger = require("@fedify/webfinger");
19
19
  let node_url = require("node:url");
20
- //#region src/federation/activity-listener.ts
21
- var ActivityListenerSet = class {
20
+ //#region src/federation/inbox.ts
21
+ var InboxListenerSet = class InboxListenerSet {
22
22
  #listeners;
23
23
  constructor() {
24
24
  this.#listeners = /* @__PURE__ */ new Map();
25
25
  }
26
26
  clone() {
27
- const Clone = this.constructor;
28
- const clone = new Clone();
27
+ const clone = new InboxListenerSet();
29
28
  clone.#listeners = new Map(this.#listeners);
30
29
  return clone;
31
30
  }
@@ -35,13 +34,14 @@ var ActivityListenerSet = class {
35
34
  }
36
35
  dispatchWithClass(activity) {
37
36
  let cls = activity.constructor;
38
- while (cls != null) {
39
- if (this.#listeners.has(cls)) break;
37
+ const inboxListeners = this.#listeners;
38
+ if (inboxListeners == null) return null;
39
+ while (true) {
40
+ if (inboxListeners.has(cls)) break;
40
41
  if (cls === _fedify_vocab.Activity) return null;
41
42
  cls = globalThis.Object.getPrototypeOf(cls);
42
43
  }
43
- if (cls == null) return null;
44
- const listener = this.#listeners.get(cls);
44
+ const listener = inboxListeners.get(cls);
45
45
  return {
46
46
  class: cls,
47
47
  listener
@@ -51,6 +51,142 @@ var ActivityListenerSet = class {
51
51
  return this.dispatchWithClass(activity)?.listener ?? null;
52
52
  }
53
53
  };
54
+ async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
55
+ const logger = (0, _logtape_logtape.getLogger)([
56
+ "fedify",
57
+ "federation",
58
+ "inbox"
59
+ ]);
60
+ let cacheKey = null;
61
+ if (activity.id != null) {
62
+ const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
63
+ const strategy = idempotencyStrategy ?? "per-inbox";
64
+ let keyString;
65
+ if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
66
+ else switch (strategy) {
67
+ case "global":
68
+ keyString = activity.id.href;
69
+ break;
70
+ case "per-origin":
71
+ keyString = `${ctx.origin}\n${activity.id.href}`;
72
+ break;
73
+ case "per-inbox":
74
+ keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
75
+ break;
76
+ default: keyString = `${ctx.origin}\n${activity.id.href}`;
77
+ }
78
+ if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
79
+ }
80
+ if (cacheKey != null) {
81
+ if (await kv.get(cacheKey) === true) {
82
+ logger.debug("Activity {activityId} has already been processed.", {
83
+ activityId: activity.id?.href,
84
+ activity: json,
85
+ recipient
86
+ });
87
+ span.setStatus({
88
+ code: _opentelemetry_api.SpanStatusCode.UNSET,
89
+ message: `Activity ${activity.id?.href} has already been processed.`
90
+ });
91
+ return "alreadyProcessed";
92
+ }
93
+ }
94
+ if (activity.actorId == null) {
95
+ logger.error("Missing actor.", { activity: json });
96
+ span.setStatus({
97
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
98
+ message: "Missing actor."
99
+ });
100
+ return "missingActor";
101
+ }
102
+ span.setAttribute("activitypub.actor.id", activity.actorId.href);
103
+ if (queue != null) {
104
+ const carrier = {};
105
+ _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
106
+ try {
107
+ await queue.enqueue({
108
+ type: "inbox",
109
+ id: crypto.randomUUID(),
110
+ baseUrl: ctx.origin,
111
+ activity: json,
112
+ identifier: recipient,
113
+ attempt: 0,
114
+ started: (/* @__PURE__ */ new Date()).toISOString(),
115
+ traceContext: carrier
116
+ });
117
+ } catch (error) {
118
+ logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
119
+ error,
120
+ activityId: activity.id?.href,
121
+ activity: json,
122
+ recipient
123
+ });
124
+ span.setStatus({
125
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
126
+ message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
127
+ });
128
+ throw error;
129
+ }
130
+ logger.info("Activity {activityId} is enqueued.", {
131
+ activityId: activity.id?.href,
132
+ activity: json,
133
+ recipient
134
+ });
135
+ return "enqueued";
136
+ }
137
+ tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
138
+ return await tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: _opentelemetry_api.SpanKind.INTERNAL }, async (span) => {
139
+ const dispatched = inboxListeners?.dispatchWithClass(activity);
140
+ if (dispatched == null) {
141
+ logger.error("Unsupported activity type:\n{activity}", {
142
+ activity: json,
143
+ recipient
144
+ });
145
+ span.setStatus({
146
+ code: _opentelemetry_api.SpanStatusCode.UNSET,
147
+ message: `Unsupported activity type: ${(0, _fedify_vocab.getTypeId)(activity).href}`
148
+ });
149
+ span.end();
150
+ return "unsupportedActivity";
151
+ }
152
+ const { class: cls, listener } = dispatched;
153
+ span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
154
+ try {
155
+ await listener(inboxContextFactory(recipient, json, activity?.id?.href, (0, _fedify_vocab.getTypeId)(activity).href), activity);
156
+ } catch (error) {
157
+ try {
158
+ await inboxErrorHandler?.(ctx, error);
159
+ } catch (error) {
160
+ logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
161
+ error,
162
+ activityId: activity.id?.href,
163
+ activity: json,
164
+ recipient
165
+ });
166
+ }
167
+ logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
168
+ error,
169
+ activityId: activity.id?.href,
170
+ activity: json,
171
+ recipient
172
+ });
173
+ span.setStatus({
174
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
175
+ message: String(error)
176
+ });
177
+ span.end();
178
+ return "error";
179
+ }
180
+ if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
181
+ logger.info("Activity {activityId} has been processed.", {
182
+ activityId: activity.id?.href,
183
+ activity: json,
184
+ recipient
185
+ });
186
+ span.end();
187
+ return "success";
188
+ });
189
+ }
54
190
  //#endregion
55
191
  //#region src/federation/router.ts
56
192
  function cloneInnerRouter(router) {
@@ -160,17 +296,6 @@ var RouterError = class extends Error {
160
296
  };
161
297
  //#endregion
162
298
  //#region src/federation/builder.ts
163
- function validateSingleIdentifierVariablePath(path, errorMessage) {
164
- const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
165
- if (operatorMatches.length !== 1 || operatorMatches[0]?.[2] !== "identifier") throw new RouterError(errorMessage);
166
- if (operatorMatches.some((match) => [
167
- "?",
168
- "&",
169
- "#"
170
- ].includes(match[1]) && match[2] === "identifier")) throw new RouterError(errorMessage);
171
- const variables = new Router().add(path, "outbox");
172
- if (variables.size !== 1 || !variables.has("identifier")) throw new RouterError(errorMessage);
173
- }
174
299
  var FederationBuilderImpl = class {
175
300
  router;
176
301
  actorCallbacks;
@@ -179,7 +304,6 @@ var FederationBuilderImpl = class {
179
304
  objectCallbacks;
180
305
  objectTypeIds;
181
306
  inboxPath;
182
- outboxPath;
183
307
  inboxCallbacks;
184
308
  outboxCallbacks;
185
309
  followingCallbacks;
@@ -188,10 +312,7 @@ var FederationBuilderImpl = class {
188
312
  featuredCallbacks;
189
313
  featuredTagsCallbacks;
190
314
  inboxListeners;
191
- outboxListeners;
192
315
  inboxErrorHandler;
193
- outboxListenerErrorHandler;
194
- outboxAuthorizePredicate;
195
316
  sharedInboxKeyDispatcher;
196
317
  unverifiedActivityHandler;
197
318
  outboxPermanentFailureHandler;
@@ -210,7 +331,7 @@ var FederationBuilderImpl = class {
210
331
  this.collectionTypeIds = {};
211
332
  }
212
333
  async build(options) {
213
- const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-uOgNgCF7.cjs"));
334
+ const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-CXOVT4Ph.cjs"));
214
335
  const f = new FederationImpl(options);
215
336
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
216
337
  f.router = this.router.clone();
@@ -222,7 +343,6 @@ var FederationBuilderImpl = class {
222
343
  f.objectCallbacks = { ...this.objectCallbacks };
223
344
  f.objectTypeIds = { ...this.objectTypeIds };
224
345
  f.inboxPath = this.inboxPath;
225
- f.outboxPath = this.outboxPath;
226
346
  f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
227
347
  f.outboxCallbacks = this.outboxCallbacks == null ? void 0 : { ...this.outboxCallbacks };
228
348
  f.followingCallbacks = this.followingCallbacks == null ? void 0 : { ...this.followingCallbacks };
@@ -231,10 +351,7 @@ var FederationBuilderImpl = class {
231
351
  f.featuredCallbacks = this.featuredCallbacks == null ? void 0 : { ...this.featuredCallbacks };
232
352
  f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
233
353
  f.inboxListeners = this.inboxListeners?.clone();
234
- f.outboxListeners = this.outboxListeners?.clone();
235
354
  f.inboxErrorHandler = this.inboxErrorHandler;
236
- f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
237
- f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
238
355
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
239
356
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
240
357
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
@@ -434,14 +551,9 @@ var FederationBuilderImpl = class {
434
551
  return setters;
435
552
  }
436
553
  setOutboxDispatcher(path, dispatcher) {
437
- if (this.outboxCallbacks != null) throw new RouterError("Outbox dispatcher already set.");
438
- if (this.router.has("outbox")) {
439
- if (this.outboxPath !== path) throw new RouterError("Outbox dispatcher path must match outbox listener path.");
440
- } else {
441
- validateSingleIdentifierVariablePath(path, "Path for outbox dispatcher must have one variable: {identifier}");
442
- this.router.add(path, "outbox");
443
- this.outboxPath = path;
444
- }
554
+ if (this.router.has("outbox")) throw new RouterError("Outbox dispatcher already set.");
555
+ const variables = this.router.add(path, "outbox");
556
+ if (variables.size !== 1 || !variables.has("identifier")) throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
445
557
  const callbacks = { dispatcher };
446
558
  this.outboxCallbacks = callbacks;
447
559
  const setters = {
@@ -464,32 +576,6 @@ var FederationBuilderImpl = class {
464
576
  };
465
577
  return setters;
466
578
  }
467
- setOutboxListeners(outboxPath) {
468
- if (this.outboxListeners != null) throw new RouterError("Outbox listeners already set.");
469
- if (this.router.has("outbox")) {
470
- if (this.outboxPath !== outboxPath) throw new RouterError("Outbox listener path must match outbox dispatcher path.");
471
- } else {
472
- validateSingleIdentifierVariablePath(outboxPath, "Path for outbox must have one variable: {identifier}");
473
- this.router.add(outboxPath, "outbox");
474
- this.outboxPath = outboxPath;
475
- }
476
- const listeners = this.outboxListeners = new ActivityListenerSet();
477
- const setters = {
478
- on(type, listener) {
479
- listeners.add(type, listener);
480
- return setters;
481
- },
482
- onError: (handler) => {
483
- this.outboxListenerErrorHandler = handler;
484
- return setters;
485
- },
486
- authorize: (predicate) => {
487
- this.outboxAuthorizePredicate = predicate;
488
- return setters;
489
- }
490
- };
491
- return setters;
492
- }
493
579
  setFollowingDispatcher(path, dispatcher) {
494
580
  if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
495
581
  const variables = this.router.add(path, "following");
@@ -632,7 +718,7 @@ var FederationBuilderImpl = class {
632
718
  if (sharedInboxPath != null) {
633
719
  if (this.router.add(sharedInboxPath, "sharedInbox").size !== 0) throw new RouterError("Path for shared inbox must have no variables.");
634
720
  }
635
- const listeners = this.inboxListeners = new ActivityListenerSet();
721
+ const listeners = this.inboxListeners = new InboxListenerSet();
636
722
  const setters = {
637
723
  on(type, listener) {
638
724
  listeners.add(type, listener);
@@ -769,144 +855,6 @@ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
769
855
  return `collectionId="${collectionId}", url="${url}", digest="${(0, byte_encodings_hex.encodeHex)(await digest(actorIds))}"`;
770
856
  }
771
857
  //#endregion
772
- //#region src/federation/inbox.ts
773
- async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider, idempotencyStrategy }) {
774
- const logger = (0, _logtape_logtape.getLogger)([
775
- "fedify",
776
- "federation",
777
- "inbox"
778
- ]);
779
- let cacheKey = null;
780
- if (activity.id != null) {
781
- const inboxContext = inboxContextFactory(recipient, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
782
- const strategy = idempotencyStrategy ?? "per-inbox";
783
- let keyString;
784
- if (typeof strategy === "function") keyString = await strategy(inboxContext, activity);
785
- else switch (strategy) {
786
- case "global":
787
- keyString = activity.id.href;
788
- break;
789
- case "per-origin":
790
- keyString = `${ctx.origin}\n${activity.id.href}`;
791
- break;
792
- case "per-inbox":
793
- keyString = `${ctx.origin}\n${activity.id.href}\n${recipient == null ? "sharedInbox" : `inbox\n${recipient}`}`;
794
- break;
795
- default: keyString = `${ctx.origin}\n${activity.id.href}`;
796
- }
797
- if (keyString != null) cacheKey = [...kvPrefixes.activityIdempotence, keyString];
798
- }
799
- if (cacheKey != null) {
800
- if (await kv.get(cacheKey) === true) {
801
- logger.debug("Activity {activityId} has already been processed.", {
802
- activityId: activity.id?.href,
803
- activity: json,
804
- recipient
805
- });
806
- span.setStatus({
807
- code: _opentelemetry_api.SpanStatusCode.UNSET,
808
- message: `Activity ${activity.id?.href} has already been processed.`
809
- });
810
- return "alreadyProcessed";
811
- }
812
- }
813
- if (activity.actorId == null) {
814
- logger.error("Missing actor.", { activity: json });
815
- span.setStatus({
816
- code: _opentelemetry_api.SpanStatusCode.ERROR,
817
- message: "Missing actor."
818
- });
819
- return "missingActor";
820
- }
821
- span.setAttribute("activitypub.actor.id", activity.actorId.href);
822
- if (queue != null) {
823
- const carrier = {};
824
- _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
825
- try {
826
- await queue.enqueue({
827
- type: "inbox",
828
- id: crypto.randomUUID(),
829
- baseUrl: ctx.origin,
830
- activity: json,
831
- identifier: recipient,
832
- attempt: 0,
833
- started: (/* @__PURE__ */ new Date()).toISOString(),
834
- traceContext: carrier
835
- });
836
- } catch (error) {
837
- logger.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
838
- error,
839
- activityId: activity.id?.href,
840
- activity: json,
841
- recipient
842
- });
843
- span.setStatus({
844
- code: _opentelemetry_api.SpanStatusCode.ERROR,
845
- message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
846
- });
847
- throw error;
848
- }
849
- logger.info("Activity {activityId} is enqueued.", {
850
- activityId: activity.id?.href,
851
- activity: json,
852
- recipient
853
- });
854
- return "enqueued";
855
- }
856
- tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
857
- return await tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: _opentelemetry_api.SpanKind.INTERNAL }, async (span) => {
858
- const dispatched = inboxListeners?.dispatchWithClass(activity);
859
- if (dispatched == null) {
860
- logger.error("Unsupported activity type:\n{activity}", {
861
- activity: json,
862
- recipient
863
- });
864
- span.setStatus({
865
- code: _opentelemetry_api.SpanStatusCode.UNSET,
866
- message: `Unsupported activity type: ${(0, _fedify_vocab.getTypeId)(activity).href}`
867
- });
868
- span.end();
869
- return "unsupportedActivity";
870
- }
871
- const { class: cls, listener } = dispatched;
872
- span.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
873
- try {
874
- await listener(inboxContextFactory(recipient, json, activity?.id?.href, (0, _fedify_vocab.getTypeId)(activity).href), activity);
875
- } catch (error) {
876
- try {
877
- await inboxErrorHandler?.(ctx, error);
878
- } catch (error) {
879
- logger.error("An unexpected error occurred in inbox error handler:\n{error}", {
880
- error,
881
- activityId: activity.id?.href,
882
- activity: json,
883
- recipient
884
- });
885
- }
886
- logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
887
- error,
888
- activityId: activity.id?.href,
889
- activity: json,
890
- recipient
891
- });
892
- span.setStatus({
893
- code: _opentelemetry_api.SpanStatusCode.ERROR,
894
- message: String(error)
895
- });
896
- span.end();
897
- return "error";
898
- }
899
- if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
900
- logger.info("Activity {activityId} has been processed.", {
901
- activityId: activity.id?.href,
902
- activity: json,
903
- recipient
904
- });
905
- span.end();
906
- return "success";
907
- });
908
- }
909
- //#endregion
910
858
  //#region src/federation/keycache.ts
911
859
  var KvKeyCache = class {
912
860
  kv;
@@ -1134,8 +1082,8 @@ async function handleObject(request, { values, context, objectDispatcher, author
1134
1082
  * @param parameters The parameters for handling the collection.
1135
1083
  * @returns A promise that resolves to an HTTP response.
1136
1084
  */
1137
- async function handleCollection(request, { name: name$1, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
1138
- const spanName = name$1.trim().replace(/\s+/g, "_");
1085
+ async function handleCollection(request, { name: name$2, identifier, uriGetter, filter, filterPredicate, context, collectionCallbacks, tracerProvider, onUnauthorized, onNotFound }) {
1086
+ const spanName = name$2.trim().replace(/\s+/g, "_");
1139
1087
  tracerProvider = tracerProvider ?? _opentelemetry_api.trace.getTracerProvider();
1140
1088
  const tracer = tracerProvider.getTracer(require_http.name, require_http.version);
1141
1089
  const cursor = new URL(request.url).searchParams.get("cursor");
@@ -1177,7 +1125,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1177
1125
  collection = new _fedify_vocab.OrderedCollection({
1178
1126
  id: baseUri,
1179
1127
  totalItems: totalItems == null ? null : Number(totalItems),
1180
- items: filterCollectionItems(itemsOrResponse, name$1, filterPredicate)
1128
+ items: filterCollectionItems(itemsOrResponse, name$2, filterPredicate)
1181
1129
  });
1182
1130
  } else {
1183
1131
  const lastCursor = await collectionCallbacks.lastCursor?.(context, identifier);
@@ -1198,7 +1146,7 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1198
1146
  } else {
1199
1147
  const uri = new URL(baseUri);
1200
1148
  uri.searchParams.set("cursor", cursor);
1201
- const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$1}`, {
1149
+ const pageOrResponse = await tracer.startActiveSpan(`activitypub.dispatch_collection_page ${name$2}`, {
1202
1150
  kind: _opentelemetry_api.SpanKind.SERVER,
1203
1151
  attributes: {
1204
1152
  "activitypub.collection.id": uri.href,
@@ -1238,249 +1186,53 @@ async function handleCollection(request, { name: name$1, identifier, uriGetter,
1238
1186
  }
1239
1187
  const partOf = new URL(context.url);
1240
1188
  partOf.searchParams.delete("cursor");
1241
- collection = new _fedify_vocab.OrderedCollectionPage({
1242
- id: uri,
1243
- prev,
1244
- next,
1245
- items: filterCollectionItems(items, name$1, filterPredicate),
1246
- partOf
1247
- });
1248
- }
1249
- if (collectionCallbacks.authorizePredicate != null) {
1250
- if (!await collectionCallbacks.authorizePredicate(context, identifier)) return await onUnauthorized(request);
1251
- }
1252
- const jsonLd = await collection.toJsonLd(context);
1253
- return new Response(JSON.stringify(jsonLd), { headers: {
1254
- "Content-Type": "application/activity+json",
1255
- Vary: "Accept"
1256
- } });
1257
- }
1258
- /**
1259
- * Filters collection items based on the provided predicate.
1260
- * @template TItem The type of items to filter.
1261
- * @param items The items to filter.
1262
- * @param collectionName The name of the collection for logging purposes.
1263
- * @param filterPredicate Optional predicate function to filter items.
1264
- * @returns The filtered items as Objects, Links, or URLs.
1265
- */
1266
- function filterCollectionItems(items, collectionName, filterPredicate) {
1267
- const result = [];
1268
- let logged = false;
1269
- for (const item of items) {
1270
- let mappedItem;
1271
- if (item instanceof _fedify_vocab.Object || item instanceof _fedify_vocab.Link || item instanceof URL) mappedItem = item;
1272
- else if (item.id == null) continue;
1273
- else mappedItem = item.id;
1274
- if (filterPredicate != null && !filterPredicate(item)) {
1275
- if (!logged) {
1276
- (0, _logtape_logtape.getLogger)([
1277
- "fedify",
1278
- "federation",
1279
- "collection"
1280
- ]).warn(`The ${collectionName} collection apparently does not implement filtering. This may result in a large response payload. Please consider implementing filtering for the collection. See also: https://fedify.dev/manual/collections#filtering-by-server`);
1281
- logged = true;
1282
- }
1283
- continue;
1284
- }
1285
- result.push(mappedItem);
1286
- }
1287
- return result;
1288
- }
1289
- function summarizeJsonActivity(json) {
1290
- if (json == null || typeof json !== "object") return {};
1291
- const activity = json;
1292
- return {
1293
- activityId: typeof activity.id === "string" ? activity.id : void 0,
1294
- activityType: typeof activity.type === "string" ? activity.type : void 0
1295
- };
1296
- }
1297
- /**
1298
- * Handles an outbox POST request.
1299
- * @template TContextData The context data to pass to the context.
1300
- * @param request The HTTP request.
1301
- * @param parameters The parameters for handling the request.
1302
- * @returns A promise that resolves to an HTTP response.
1303
- * @since 2.2.0
1304
- */
1305
- async function handleOutbox(request, { identifier, context: ctx, outboxContextFactory, actorDispatcher, authorizePredicate, outboxListeners, outboxErrorHandler, onUnauthorized, onNotFound }) {
1306
- const logger = (0, _logtape_logtape.getLogger)([
1307
- "fedify",
1308
- "federation",
1309
- "outbox"
1310
- ]);
1311
- if (request.bodyUsed) {
1312
- logger.error("Request body has already been read.", { identifier });
1313
- return new Response("Internal server error.", {
1314
- status: 500,
1315
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1316
- });
1317
- } else if (request.body?.locked) {
1318
- logger.error("Request body is locked.", { identifier });
1319
- return new Response("Internal server error.", {
1320
- status: 500,
1321
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1322
- });
1323
- }
1324
- if (actorDispatcher == null) {
1325
- logger.error("Actor dispatcher is not set.", { identifier });
1326
- return await onNotFound(request);
1327
- }
1328
- if (authorizePredicate != null) {
1329
- const authorizeContext = ctx.clone(ctx.data);
1330
- authorizeContext.request = request.clone();
1331
- const requestForUnauthorized = authorizeContext.request.clone();
1332
- if (!await authorizePredicate(authorizeContext, identifier)) return await onUnauthorized(requestForUnauthorized);
1333
- }
1334
- const actor = await actorDispatcher(ctx, identifier);
1335
- if (actor == null || actor instanceof _fedify_vocab.Tombstone) {
1336
- logger.error("Actor {identifier} not found.", { identifier });
1337
- return await onNotFound(request);
1338
- }
1339
- const requestForParsing = request.clone();
1340
- let json;
1341
- try {
1342
- json = await requestForParsing.json();
1343
- } catch (error) {
1344
- logger.error("Failed to parse JSON:\n{error}", {
1345
- identifier,
1346
- error
1347
- });
1348
- const outboxContext = outboxContextFactory(identifier, null, void 0, "");
1349
- try {
1350
- await outboxErrorHandler?.(outboxContext, error);
1351
- } catch (error) {
1352
- logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
1353
- error,
1354
- identifier
1355
- });
1356
- }
1357
- return new Response("Invalid JSON.", {
1358
- status: 400,
1359
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1360
- });
1361
- }
1362
- let activity;
1363
- try {
1364
- activity = await _fedify_vocab.Activity.fromJsonLd(json, ctx);
1365
- } catch (error) {
1366
- const summary = summarizeJsonActivity(json);
1367
- logger.error("Failed to parse activity:\n{error}", {
1368
- identifier,
1369
- ...summary,
1370
- error
1371
- });
1372
- const outboxContext = outboxContextFactory(identifier, json, summary.activityId, summary.activityType ?? "");
1373
- try {
1374
- await outboxErrorHandler?.(outboxContext, error);
1375
- } catch (error) {
1376
- logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
1377
- error,
1378
- identifier,
1379
- ...summary
1380
- });
1381
- }
1382
- return new Response("Invalid activity.", {
1383
- status: 400,
1384
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1385
- });
1386
- }
1387
- const outboxContext = outboxContextFactory(identifier, json, activity.id?.href, (0, _fedify_vocab.getTypeId)(activity).href);
1388
- const expectedActorId = actor.id ?? ctx.getActorUri(identifier);
1389
- if (activity.actorIds.length < 1) {
1390
- const error = /* @__PURE__ */ new Error("The posted activity has no actor.");
1391
- logger.error("The posted activity has no actor for outbox {identifier}.", {
1392
- identifier,
1393
- activityId: activity.id?.href,
1394
- expectedActorId: expectedActorId.href
1395
- });
1396
- try {
1397
- await outboxErrorHandler?.(outboxContext, error);
1398
- } catch (error) {
1399
- logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
1400
- error,
1401
- activityId: activity.id?.href,
1402
- activityType: (0, _fedify_vocab.getTypeId)(activity).href,
1403
- identifier
1404
- });
1405
- }
1406
- return new Response(error.message, {
1407
- status: 400,
1408
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1409
- });
1410
- }
1411
- if (!activity.actorIds.every((actorId) => actorId.href === expectedActorId.href)) {
1412
- const error = /* @__PURE__ */ new Error("The activity actor does not match the outbox owner.");
1413
- logger.error("The posted activity actor does not match outbox owner {identifier}.", {
1414
- identifier,
1415
- activityId: activity.id?.href,
1416
- expectedActorId: expectedActorId.href,
1417
- actorIds: activity.actorIds.map((actorId) => actorId.href)
1418
- });
1419
- try {
1420
- await outboxErrorHandler?.(outboxContext, error);
1421
- } catch (error) {
1422
- logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
1423
- error,
1424
- activityId: activity.id?.href,
1425
- activityType: (0, _fedify_vocab.getTypeId)(activity).href,
1426
- identifier
1427
- });
1428
- }
1429
- return new Response(error.message, {
1430
- status: 400,
1431
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1432
- });
1433
- }
1434
- const dispatched = outboxListeners?.dispatchWithClass(activity);
1435
- if (dispatched == null) {
1436
- logger.debug("Unsupported activity type {activityType}.", {
1437
- identifier,
1438
- activityId: activity.id?.href,
1439
- activityType: (0, _fedify_vocab.getTypeId)(activity).href
1440
- });
1441
- return new Response("", {
1442
- status: 202,
1443
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1444
- });
1445
- }
1446
- try {
1447
- await dispatched.listener(outboxContext, activity);
1448
- } catch (error) {
1449
- try {
1450
- await outboxErrorHandler?.(outboxContext, error);
1451
- } catch (error) {
1452
- logger.error("An unexpected error occurred in outbox error handler:\n{error}", {
1453
- error,
1454
- activityId: activity.id?.href,
1455
- activityType: (0, _fedify_vocab.getTypeId)(activity).href,
1456
- identifier
1457
- });
1458
- }
1459
- logger.error("Failed to process the incoming activity {activityId}:\n{error}", {
1460
- error,
1461
- activityId: activity.id?.href,
1462
- activityType: (0, _fedify_vocab.getTypeId)(activity).href,
1463
- identifier
1464
- });
1465
- return new Response("Internal server error.", {
1466
- status: 500,
1467
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1189
+ collection = new _fedify_vocab.OrderedCollectionPage({
1190
+ id: uri,
1191
+ prev,
1192
+ next,
1193
+ items: filterCollectionItems(items, name$2, filterPredicate),
1194
+ partOf
1468
1195
  });
1469
1196
  }
1470
- if (!outboxContext.hasDeliveredActivity()) logger.warn("Outbox listener for {identifier} returned without delivering the posted activity; ctx.sendActivity() or ctx.forwardActivity() may have been skipped or resulted in no delivery.", {
1471
- identifier,
1472
- activityId: activity.id?.href,
1473
- activityType: (0, _fedify_vocab.getTypeId)(activity).href
1474
- });
1475
- logger.info("Activity {activityId} has been processed in outbox listener.", {
1476
- activityId: activity.id?.href,
1477
- activityType: (0, _fedify_vocab.getTypeId)(activity).href,
1478
- identifier
1479
- });
1480
- return new Response("", {
1481
- status: 202,
1482
- headers: { "Content-Type": "text/plain; charset=utf-8" }
1483
- });
1197
+ if (collectionCallbacks.authorizePredicate != null) {
1198
+ if (!await collectionCallbacks.authorizePredicate(context, identifier)) return await onUnauthorized(request);
1199
+ }
1200
+ const jsonLd = await collection.toJsonLd(context);
1201
+ return new Response(JSON.stringify(jsonLd), { headers: {
1202
+ "Content-Type": "application/activity+json",
1203
+ Vary: "Accept"
1204
+ } });
1205
+ }
1206
+ /**
1207
+ * Filters collection items based on the provided predicate.
1208
+ * @template TItem The type of items to filter.
1209
+ * @param items The items to filter.
1210
+ * @param collectionName The name of the collection for logging purposes.
1211
+ * @param filterPredicate Optional predicate function to filter items.
1212
+ * @returns The filtered items as Objects, Links, or URLs.
1213
+ */
1214
+ function filterCollectionItems(items, collectionName, filterPredicate) {
1215
+ const result = [];
1216
+ let logged = false;
1217
+ for (const item of items) {
1218
+ let mappedItem;
1219
+ if (item instanceof _fedify_vocab.Object || item instanceof _fedify_vocab.Link || item instanceof URL) mappedItem = item;
1220
+ else if (item.id == null) continue;
1221
+ else mappedItem = item.id;
1222
+ if (filterPredicate != null && !filterPredicate(item)) {
1223
+ if (!logged) {
1224
+ (0, _logtape_logtape.getLogger)([
1225
+ "fedify",
1226
+ "federation",
1227
+ "collection"
1228
+ ]).warn(`The ${collectionName} collection apparently does not implement filtering. This may result in a large response payload. Please consider implementing filtering for the collection. See also: https://fedify.dev/manual/collections#filtering-by-server`);
1229
+ logged = true;
1230
+ }
1231
+ continue;
1232
+ }
1233
+ result.push(mappedItem);
1234
+ }
1235
+ return result;
1484
1236
  }
1485
1237
  /**
1486
1238
  * Handles an inbox request for ActivityPub activities.
@@ -1912,8 +1664,8 @@ var CustomCollectionHandler = class {
1912
1664
  * @param CollectionPage The CollectionPage constructor.
1913
1665
  * @param filterPredicate Optional filter predicate for items.
1914
1666
  */
1915
- constructor(name$2, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
1916
- this.name = name$2;
1667
+ constructor(name$1, values, context, callbacks, tracerProvider = _opentelemetry_api.trace.getTracerProvider(), Collection, CollectionPage, filterPredicate) {
1668
+ this.name = name$1;
1917
1669
  this.values = values;
1918
1670
  this.context = context;
1919
1671
  this.callbacks = callbacks;
@@ -3522,37 +3274,16 @@ var FederationImpl = class extends FederationBuilderImpl {
3522
3274
  onNotFound
3523
3275
  });
3524
3276
  }
3525
- case "outbox":
3526
- if (request.method === "POST") {
3527
- if (this.outboxListeners == null) return new Response("Method not allowed.", {
3528
- status: 405,
3529
- headers: {
3530
- Allow: "GET, HEAD",
3531
- "Content-Type": "text/plain; charset=utf-8"
3532
- }
3533
- });
3534
- return await handleOutbox(request, {
3535
- identifier: route.values.identifier,
3536
- context,
3537
- outboxContextFactory: context.toOutboxContext.bind(context),
3538
- actorDispatcher: this.actorCallbacks?.dispatcher,
3539
- authorizePredicate: this.outboxAuthorizePredicate ?? this.outboxCallbacks?.authorizePredicate,
3540
- outboxListeners: this.outboxListeners,
3541
- outboxErrorHandler: this.outboxListenerErrorHandler,
3542
- onUnauthorized,
3543
- onNotFound
3544
- });
3545
- }
3546
- return await handleCollection(request, {
3547
- name: "outbox",
3548
- identifier: route.values.identifier,
3549
- uriGetter: context.getOutboxUri.bind(context),
3550
- context,
3551
- collectionCallbacks: this.outboxCallbacks,
3552
- tracerProvider: this.tracerProvider,
3553
- onUnauthorized,
3554
- onNotFound
3555
- });
3277
+ case "outbox": return await handleCollection(request, {
3278
+ name: "outbox",
3279
+ identifier: route.values.identifier,
3280
+ uriGetter: context.getOutboxUri.bind(context),
3281
+ context,
3282
+ collectionCallbacks: this.outboxCallbacks,
3283
+ tracerProvider: this.tracerProvider,
3284
+ onUnauthorized,
3285
+ onNotFound
3286
+ });
3556
3287
  case "inbox":
3557
3288
  if (request.method !== "POST") return await handleCollection(request, {
3558
3289
  name: "inbox",
@@ -3722,16 +3453,6 @@ var ContextImpl = class ContextImpl {
3722
3453
  invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
3723
3454
  });
3724
3455
  }
3725
- toOutboxContext(identifier, activity, activityId, activityType) {
3726
- return new OutboxContextImpl(identifier, activity, activityId, activityType, {
3727
- url: this.url,
3728
- federation: this.federation,
3729
- data: this.data,
3730
- documentLoader: this.documentLoader,
3731
- contextLoader: this.contextLoader,
3732
- invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
3733
- });
3734
- }
3735
3456
  get hostname() {
3736
3457
  return this.url.hostname;
3737
3458
  }
@@ -4021,9 +3742,9 @@ var ContextImpl = class ContextImpl {
4021
3742
  attributes: {
4022
3743
  "activitypub.activity.type": (0, _fedify_vocab.getTypeId)(activity).href,
4023
3744
  "activitypub.activity.to": activity.toIds.map((to) => to.href),
4024
- "activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
3745
+ "activitypub.activity.cc": activity.toIds.map((cc) => cc.href),
4025
3746
  "activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
4026
- "activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
3747
+ "activitypub.activity.bcc": activity.toIds.map((bcc) => bcc.href)
4027
3748
  }
4028
3749
  }, async (span) => {
4029
3750
  try {
@@ -4118,13 +3839,6 @@ var ContextImpl = class ContextImpl {
4118
3839
  preferSharedInbox: options.preferSharedInbox,
4119
3840
  excludeBaseUris: options.excludeBaseUris
4120
3841
  });
4121
- if (globalThis.Object.keys(inboxes).length < 1) {
4122
- logger.debug("No inboxes found for activity {activityId}.", {
4123
- activityId: activity.id?.href,
4124
- activity
4125
- });
4126
- return false;
4127
- }
4128
3842
  logger.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
4129
3843
  inboxes: globalThis.Object.keys(inboxes),
4130
3844
  activityId: activity.id?.href,
@@ -4132,7 +3846,7 @@ var ContextImpl = class ContextImpl {
4132
3846
  });
4133
3847
  if (this.federation.fanoutQueue == null || options.immediate || options.fanout === "skip" || (options.fanout ?? "auto") === "auto" && globalThis.Object.keys(inboxes).length < FANOUT_THRESHOLD) {
4134
3848
  await this.federation.sendActivity(keys, inboxes, activity, opts);
4135
- return true;
3849
+ return;
4136
3850
  }
4137
3851
  const keyJwkPairs = await Promise.all(keys.map(async ({ keyId, privateKey }) => ({
4138
3852
  keyId: keyId.href,
@@ -4161,7 +3875,6 @@ var ContextImpl = class ContextImpl {
4161
3875
  };
4162
3876
  if (!this.federation.manuallyStartQueue) this.federation._startQueueInternal(this.data);
4163
3877
  await this.federation.fanoutQueue.enqueue(message, { orderingKey: options.orderingKey });
4164
- return true;
4165
3878
  }
4166
3879
  async *getFollowers(identifier) {
4167
3880
  if (this.federation.followersCallbacks == null) throw new Error("No followers collection dispatcher registered.");
@@ -4396,170 +4109,6 @@ var RequestContextImpl = class RequestContextImpl extends ContextImpl {
4396
4109
  }
4397
4110
  }
4398
4111
  };
4399
- function forwardActivity(ctx, loggerCategory, forwarder, recipients, options) {
4400
- return ctx.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan(ctx.federation.outboxQueue == null || options?.immediate ? `activitypub.${loggerCategory}` : "activitypub.fanout", {
4401
- kind: ctx.federation.outboxQueue == null || options?.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
4402
- attributes: { "activitypub.activity.type": ctx.activityType }
4403
- }, async (span) => {
4404
- try {
4405
- if (ctx.activityId != null) span.setAttribute("activitypub.activity.id", ctx.activityId);
4406
- return await forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options);
4407
- } catch (e) {
4408
- span.setStatus({
4409
- code: _opentelemetry_api.SpanStatusCode.ERROR,
4410
- message: String(e)
4411
- });
4412
- throw e;
4413
- } finally {
4414
- span.end();
4415
- }
4416
- });
4417
- }
4418
- async function forwardActivityInternal(ctx, loggerCategory, forwarder, recipients, options) {
4419
- const logger = (0, _logtape_logtape.getLogger)([
4420
- "fedify",
4421
- "federation",
4422
- loggerCategory
4423
- ]);
4424
- let keys;
4425
- let identifier = null;
4426
- if ("identifier" in forwarder || "username" in forwarder) {
4427
- if ("identifier" in forwarder) identifier = forwarder.identifier;
4428
- else {
4429
- const username = forwarder.username;
4430
- if (ctx.federation.actorCallbacks?.handleMapper == null) identifier = username;
4431
- else {
4432
- const mapped = await ctx.federation.actorCallbacks.handleMapper(ctx, username);
4433
- if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
4434
- identifier = mapped;
4435
- }
4436
- }
4437
- const actorKeyPairs = await ctx.getActorKeyPairs(identifier);
4438
- if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
4439
- keys = actorKeyPairs.map((kp) => ({
4440
- keyId: kp.keyId,
4441
- privateKey: kp.privateKey
4442
- }));
4443
- } else if (Array.isArray(forwarder)) {
4444
- if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
4445
- keys = forwarder;
4446
- } else keys = [forwarder];
4447
- if (!require_proof.hasSignatureLike(ctx.activity)) {
4448
- if (!require_proof.hasProofLike(ctx.activity)) {
4449
- if (options?.skipIfUnsigned) return false;
4450
- logger.warn("The activity {activityId} is not signed; even if it is forwarded to other servers as is, it may not be accepted by them due to the lack of a signature/proof.", {
4451
- activityId: ctx.activityId,
4452
- activityType: ctx.activityType,
4453
- identifier: identifier ?? void 0
4454
- });
4455
- }
4456
- }
4457
- if (recipients === "followers") {
4458
- if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
4459
- const followers = [];
4460
- for await (const recipient of ctx.getFollowers(identifier)) followers.push(recipient);
4461
- recipients = followers;
4462
- }
4463
- const inboxes = extractInboxes({
4464
- recipients: Array.isArray(recipients) ? recipients : [recipients],
4465
- preferSharedInbox: options?.preferSharedInbox,
4466
- excludeBaseUris: options?.excludeBaseUris
4467
- });
4468
- if (globalThis.Object.keys(inboxes).length < 1) {
4469
- logger.debug("No inboxes found for activity {activityId}.", {
4470
- activityId: ctx.activityId,
4471
- activityType: ctx.activityType,
4472
- identifier: identifier ?? void 0
4473
- });
4474
- return false;
4475
- }
4476
- logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
4477
- inboxes: globalThis.Object.keys(inboxes),
4478
- activityId: ctx.activityId,
4479
- activity: ctx.activity
4480
- });
4481
- if (options?.immediate || ctx.federation.outboxQueue == null) {
4482
- if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
4483
- else logger.debug("Forwarding activity immediately without queue since queue is not set.");
4484
- const promises = [];
4485
- for (const inbox in inboxes) promises.push(sendActivity({
4486
- keys,
4487
- activity: ctx.activity,
4488
- activityId: ctx.activityId,
4489
- activityType: ctx.activityType,
4490
- inbox: new URL(inbox),
4491
- sharedInbox: inboxes[inbox].sharedInbox,
4492
- tracerProvider: ctx.tracerProvider,
4493
- specDeterminer: new KvSpecDeterminer(ctx.federation.kv, ctx.federation.kvPrefixes.httpMessageSignaturesSpec, ctx.federation.firstKnock)
4494
- }));
4495
- await Promise.all(promises);
4496
- return true;
4497
- }
4498
- logger.debug("Enqueuing activity {activityId} to forward later.", {
4499
- activityId: ctx.activityId,
4500
- activity: ctx.activity
4501
- });
4502
- if (!ctx.federation.manuallyStartQueue) ctx.federation._startQueueInternal(ctx.data);
4503
- const keyJwkPairs = [];
4504
- for (const { keyId, privateKey } of keys) {
4505
- const privateKeyJwk = await require_http.exportJwk(privateKey);
4506
- keyJwkPairs.push({
4507
- keyId: keyId.href,
4508
- privateKey: privateKeyJwk
4509
- });
4510
- }
4511
- const carrier = {};
4512
- _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
4513
- const orderingKey = options?.orderingKey;
4514
- const started = (/* @__PURE__ */ new Date()).toISOString();
4515
- const messages = [];
4516
- for (const inbox in inboxes) {
4517
- const inboxUrl = new URL(inbox);
4518
- const message = {
4519
- type: "outbox",
4520
- id: crypto.randomUUID(),
4521
- baseUrl: ctx.origin,
4522
- keys: keyJwkPairs,
4523
- activity: ctx.activity,
4524
- activityId: ctx.activityId,
4525
- activityType: ctx.activityType,
4526
- inbox,
4527
- sharedInbox: inboxes[inbox].sharedInbox,
4528
- actorIds: [...inboxes[inbox].actorIds],
4529
- started,
4530
- attempt: 0,
4531
- headers: {},
4532
- orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
4533
- traceContext: carrier
4534
- };
4535
- messages.push({
4536
- message,
4537
- orderingKey: message.orderingKey
4538
- });
4539
- }
4540
- const { outboxQueue } = ctx.federation;
4541
- if (outboxQueue.enqueueMany == null || orderingKey != null) {
4542
- const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4543
- const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
4544
- if (errors.length > 0) {
4545
- logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
4546
- activityId: ctx.activityId,
4547
- errors
4548
- });
4549
- if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${ctx.activityId} to forward later.`);
4550
- throw errors[0];
4551
- }
4552
- } else try {
4553
- await outboxQueue.enqueueMany(messages.map((m) => m.message));
4554
- } catch (error) {
4555
- logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
4556
- activityId: ctx.activityId,
4557
- error
4558
- });
4559
- throw error;
4560
- }
4561
- return true;
4562
- }
4563
4112
  var InboxContextImpl = class InboxContextImpl extends ContextImpl {
4564
4113
  recipient;
4565
4114
  activity;
@@ -4583,40 +4132,13 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
4583
4132
  });
4584
4133
  }
4585
4134
  forwardActivity(forwarder, recipients, options) {
4586
- return forwardActivity(this, "inbox", forwarder, recipients, options).then(() => void 0);
4587
- }
4588
- };
4589
- var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
4590
- #deliveryState;
4591
- identifier;
4592
- activity;
4593
- activityId;
4594
- activityType;
4595
- constructor(identifier, activity, activityId, activityType, options, deliveryState = { delivered: false }) {
4596
- super(options);
4597
- this.#deliveryState = deliveryState;
4598
- this.identifier = identifier;
4599
- this.activity = activity;
4600
- this.activityId = activityId;
4601
- this.activityType = activityType;
4602
- }
4603
- hasDeliveredActivity() {
4604
- return this.#deliveryState.delivered;
4605
- }
4606
- sendActivity(sender, recipients, activity, options = {}) {
4607
- return this.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan(this.federation.outboxQueue == null || options.immediate ? "activitypub.outbox" : "activitypub.fanout", {
4608
- kind: this.federation.outboxQueue == null || options.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
4609
- attributes: {
4610
- "activitypub.activity.type": (0, _fedify_vocab.getTypeId)(activity).href,
4611
- "activitypub.activity.to": activity.toIds.map((to) => to.href),
4612
- "activitypub.activity.cc": activity.ccIds.map((cc) => cc.href),
4613
- "activitypub.activity.bto": activity.btoIds.map((bto) => bto.href),
4614
- "activitypub.activity.bcc": activity.bccIds.map((bcc) => bcc.href)
4615
- }
4135
+ return this.tracerProvider.getTracer(require_http.name, require_http.version).startActiveSpan("activitypub.outbox", {
4136
+ kind: this.federation.outboxQueue == null || options?.immediate ? _opentelemetry_api.SpanKind.CLIENT : _opentelemetry_api.SpanKind.PRODUCER,
4137
+ attributes: { "activitypub.activity.type": this.activityType }
4616
4138
  }, async (span) => {
4617
4139
  try {
4618
- if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
4619
- if (await this.sendActivityInternal(sender, recipients, activity, options, span)) this.#deliveryState.delivered = true;
4140
+ if (this.activityId != null) span.setAttribute("activitypub.activity.id", this.activityId);
4141
+ await this.forwardActivityInternal(forwarder, recipients, options);
4620
4142
  } catch (e) {
4621
4143
  span.setStatus({
4622
4144
  code: _opentelemetry_api.SpanStatusCode.ERROR,
@@ -4628,20 +4150,151 @@ var OutboxContextImpl = class OutboxContextImpl extends ContextImpl {
4628
4150
  }
4629
4151
  });
4630
4152
  }
4631
- forwardActivity(forwarder, recipients, options) {
4632
- return forwardActivity(this, "outbox", forwarder, recipients, options).then((delivered) => {
4633
- if (delivered) this.#deliveryState.delivered = true;
4153
+ async forwardActivityInternal(forwarder, recipients, options) {
4154
+ const logger = (0, _logtape_logtape.getLogger)([
4155
+ "fedify",
4156
+ "federation",
4157
+ "inbox"
4158
+ ]);
4159
+ let keys;
4160
+ let identifier = null;
4161
+ if ("identifier" in forwarder || "username" in forwarder) {
4162
+ if ("identifier" in forwarder) identifier = forwarder.identifier;
4163
+ else {
4164
+ const username = forwarder.username;
4165
+ if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
4166
+ else {
4167
+ const mapped = await this.federation.actorCallbacks.handleMapper(this, username);
4168
+ if (mapped == null) throw new Error(`No actor found for the given username ${JSON.stringify(username)}.`);
4169
+ identifier = mapped;
4170
+ }
4171
+ }
4172
+ const actorKeyPairs = await this.getActorKeyPairs(identifier);
4173
+ if (actorKeyPairs.length < 1) throw new Error(`No key pair found for actor ${JSON.stringify(identifier)}.`);
4174
+ keys = actorKeyPairs.map((kp) => ({
4175
+ keyId: kp.keyId,
4176
+ privateKey: kp.privateKey
4177
+ }));
4178
+ } else if (Array.isArray(forwarder)) {
4179
+ if (forwarder.length < 1) throw new Error("The forwarder's key pairs are empty.");
4180
+ keys = forwarder;
4181
+ } else keys = [forwarder];
4182
+ if (!require_proof.hasSignature(this.activity)) {
4183
+ let hasProof;
4184
+ try {
4185
+ hasProof = await (await _fedify_vocab.Activity.fromJsonLd(this.activity, this)).getProof() != null;
4186
+ } catch {
4187
+ hasProof = false;
4188
+ }
4189
+ if (!hasProof) {
4190
+ if (options?.skipIfUnsigned) return;
4191
+ logger.warn("The received activity {activityId} is not signed; even if it is forwarded to other servers as is, it may not be accepted by them due to the lack of a signature/proof.");
4192
+ }
4193
+ }
4194
+ if (recipients === "followers") {
4195
+ if (identifier == null) throw new Error("If recipients is \"followers\", forwarder must be an actor identifier or username.");
4196
+ const followers = [];
4197
+ for await (const recipient of this.getFollowers(identifier)) followers.push(recipient);
4198
+ recipients = followers;
4199
+ }
4200
+ const inboxes = extractInboxes({
4201
+ recipients: Array.isArray(recipients) ? recipients : [recipients],
4202
+ preferSharedInbox: options?.preferSharedInbox,
4203
+ excludeBaseUris: options?.excludeBaseUris
4634
4204
  });
4635
- }
4636
- clone(data) {
4637
- return new OutboxContextImpl(this.identifier, this.activity, this.activityId, this.activityType, {
4638
- url: this.url,
4639
- federation: this.federation,
4640
- data,
4641
- documentLoader: this.documentLoader,
4642
- contextLoader: this.contextLoader,
4643
- invokedFromActorKeyPairsDispatcher: this.invokedFromActorKeyPairsDispatcher
4644
- }, this.#deliveryState);
4205
+ logger.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
4206
+ inboxes: globalThis.Object.keys(inboxes),
4207
+ activityId: this.activityId,
4208
+ activity: this.activity
4209
+ });
4210
+ if (options?.immediate || this.federation.outboxQueue == null) {
4211
+ if (options?.immediate) logger.debug("Forwarding activity immediately without queue since immediate option is set.");
4212
+ else logger.debug("Forwarding activity immediately without queue since queue is not set.");
4213
+ const promises = [];
4214
+ for (const inbox in inboxes) promises.push(sendActivity({
4215
+ keys,
4216
+ activity: this.activity,
4217
+ activityId: this.activityId,
4218
+ activityType: this.activityType,
4219
+ inbox: new URL(inbox),
4220
+ sharedInbox: inboxes[inbox].sharedInbox,
4221
+ tracerProvider: this.tracerProvider,
4222
+ specDeterminer: new KvSpecDeterminer(this.federation.kv, this.federation.kvPrefixes.httpMessageSignaturesSpec, this.federation.firstKnock)
4223
+ }));
4224
+ await Promise.all(promises);
4225
+ return;
4226
+ }
4227
+ logger.debug("Enqueuing activity {activityId} to forward later.", {
4228
+ activityId: this.activityId,
4229
+ activity: this.activity
4230
+ });
4231
+ const keyJwkPairs = [];
4232
+ for (const { keyId, privateKey } of keys) {
4233
+ const privateKeyJwk = await require_http.exportJwk(privateKey);
4234
+ keyJwkPairs.push({
4235
+ keyId: keyId.href,
4236
+ privateKey: privateKeyJwk
4237
+ });
4238
+ }
4239
+ const carrier = {};
4240
+ _opentelemetry_api.propagation.inject(_opentelemetry_api.context.active(), carrier);
4241
+ const orderingKey = options?.orderingKey;
4242
+ const messages = [];
4243
+ for (const inbox in inboxes) {
4244
+ const inboxUrl = new URL(inbox);
4245
+ const message = {
4246
+ type: "outbox",
4247
+ id: crypto.randomUUID(),
4248
+ baseUrl: this.origin,
4249
+ keys: keyJwkPairs,
4250
+ activity: this.activity,
4251
+ activityId: this.activityId,
4252
+ activityType: this.activityType,
4253
+ inbox,
4254
+ sharedInbox: inboxes[inbox].sharedInbox,
4255
+ started: (/* @__PURE__ */ new Date()).toISOString(),
4256
+ attempt: 0,
4257
+ headers: {},
4258
+ orderingKey: orderingKey == null ? void 0 : `${orderingKey}\n${inboxUrl.origin}`,
4259
+ traceContext: carrier
4260
+ };
4261
+ messages.push({
4262
+ message,
4263
+ orderingKey: message.orderingKey
4264
+ });
4265
+ }
4266
+ const { outboxQueue } = this.federation;
4267
+ if (outboxQueue.enqueueMany == null) {
4268
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4269
+ const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
4270
+ if (errors.length > 0) {
4271
+ logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
4272
+ activityId: this.activityId,
4273
+ errors
4274
+ });
4275
+ if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
4276
+ throw errors[0];
4277
+ }
4278
+ } else if (orderingKey != null) {
4279
+ const promises = messages.map((m) => outboxQueue.enqueue(m.message, { orderingKey: m.orderingKey }));
4280
+ const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
4281
+ if (errors.length > 0) {
4282
+ logger.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
4283
+ activityId: this.activityId,
4284
+ errors
4285
+ });
4286
+ if (errors.length > 1) throw new AggregateError(errors, `Failed to enqueue activity ${this.activityId} to forward later.`);
4287
+ throw errors[0];
4288
+ }
4289
+ } else try {
4290
+ await outboxQueue.enqueueMany(messages.map((m) => m.message));
4291
+ } catch (error) {
4292
+ logger.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
4293
+ activityId: this.activityId,
4294
+ error
4295
+ });
4296
+ throw error;
4297
+ }
4645
4298
  }
4646
4299
  };
4647
4300
  var KvSpecDeterminer = class {
@@ -4718,12 +4371,6 @@ Object.defineProperty(exports, "KvSpecDeterminer", {
4718
4371
  return KvSpecDeterminer;
4719
4372
  }
4720
4373
  });
4721
- Object.defineProperty(exports, "OutboxContextImpl", {
4722
- enumerable: true,
4723
- get: function() {
4724
- return OutboxContextImpl;
4725
- }
4726
- });
4727
4374
  Object.defineProperty(exports, "Router", {
4728
4375
  enumerable: true,
4729
4376
  get: function() {