@fedify/fedify 2.2.0-pr.695.16 → 2.2.0-pr.697.17

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/activity-listener-Ck3JZ_hR.mjs +40 -0
  2. package/dist/{builder-7PVCiLiR.mjs → builder-0VkYL-Be.mjs} +57 -7
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{context-78ecvxf5.d.ts → context-BGrYMSTk.d.ts} +143 -1
  7. package/dist/{context-DYDPdoCb.d.cts → context-CMUd4wy0.d.cts} +143 -1
  8. package/dist/{context-Juj6bdHC.mjs → context-Dk_tacqz.mjs} +17 -2
  9. package/dist/{deno-vxcWcxQS.mjs → deno-ZTLy21O_.mjs} +1 -1
  10. package/dist/{docloader-D7q0-Xef.mjs → docloader-qLB9fFVV.mjs} +2 -2
  11. package/dist/federation/builder.test.mjs +25 -1
  12. package/dist/federation/handler.test.mjs +369 -8
  13. package/dist/federation/idempotency.test.mjs +2 -2
  14. package/dist/federation/inbox.test.mjs +3 -3
  15. package/dist/federation/middleware.test.mjs +510 -8
  16. package/dist/federation/mod.cjs +1 -1
  17. package/dist/federation/mod.d.cts +3 -3
  18. package/dist/federation/mod.d.ts +3 -3
  19. package/dist/federation/mod.js +1 -1
  20. package/dist/federation/send.test.mjs +3 -3
  21. package/dist/federation/webfinger.test.mjs +2 -2
  22. package/dist/{http-JxF7bG0o.cjs → http-BLuuf-Rt.cjs} +1 -1
  23. package/dist/{http-RZPxDWq5.mjs → http-CFmrJbuT.mjs} +2 -2
  24. package/dist/{http-D-MhhYUF.js → http-CqkZgsp_.js} +1 -1
  25. package/dist/{key-CGx_dDkX.mjs → key-DVFCI5om.mjs} +1 -1
  26. package/dist/{kv-cache-C2gdVgvb.cjs → kv-cache-BlXew67e.cjs} +1 -1
  27. package/dist/{kv-cache-D84Mk0fZ.js → kv-cache-Dgsvz3PC.js} +1 -1
  28. package/dist/{ld-wup-liFO.mjs → ld-N69KBAlI.mjs} +26 -3
  29. package/dist/{middleware-Bn75dPug.cjs → middleware-B0BqhA1u.cjs} +676 -323
  30. package/dist/{middleware-RF-sUfTr.js → middleware-BpSfJf2S.js} +670 -322
  31. package/dist/{middleware-CXOVT4Ph.cjs → middleware-CUe96Oxe.cjs} +1 -1
  32. package/dist/{middleware-wdfeWjRJ.mjs → middleware-DDXN5eBU.mjs} +1 -1
  33. package/dist/{middleware-BjVx-_bv.mjs → middleware-DbCHS-GH.mjs} +612 -180
  34. package/dist/{mod-CEohtXhV.d.cts → mod-BcJHeuv1.d.cts} +1 -1
  35. package/dist/{mod-CokIUYDr.d.ts → mod-CJXfyw7v.d.ts} +1 -1
  36. package/dist/{mod-DvxszxXC.d.ts → mod-CR8soWa9.d.ts} +18 -1
  37. package/dist/{mod-DoJBjjnO.d.cts → mod-Cr3f-ACa.d.cts} +18 -1
  38. package/dist/mod.cjs +6 -4
  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-q2mUMM9a.mjs → owner-DGdkXeF5.mjs} +2 -2
  44. package/dist/{proof-CirP9OSd.js → proof-BtFhWZuV.js} +54 -2
  45. package/dist/{proof--CpZsF_p.mjs → proof-C50gFNxj.mjs} +32 -3
  46. package/dist/{proof-_Zyfqyce.cjs → proof-DwRcU3OF.cjs} +61 -3
  47. package/dist/{send-CVJfx7bF.mjs → send-BlHiNsZI.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 +44 -2
  51. package/dist/sig/mod.cjs +4 -2
  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 +46 -2
  57. package/dist/testing/mod.d.mts +149 -1
  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 +5 -5
  63. package/dist/inbox-CmYvcSMM.mjs +0 -179
@@ -0,0 +1,40 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { Activity } from "@fedify/vocab";
5
+ //#region src/federation/activity-listener.ts
6
+ var ActivityListenerSet = class {
7
+ #listeners;
8
+ constructor() {
9
+ this.#listeners = /* @__PURE__ */ new Map();
10
+ }
11
+ clone() {
12
+ const Clone = this.constructor;
13
+ const clone = new Clone();
14
+ clone.#listeners = new Map(this.#listeners);
15
+ return clone;
16
+ }
17
+ add(type, listener) {
18
+ if (this.#listeners.has(type)) throw new TypeError("Listener already set for this type.");
19
+ this.#listeners.set(type, listener);
20
+ }
21
+ dispatchWithClass(activity) {
22
+ let cls = activity.constructor;
23
+ while (cls != null) {
24
+ if (this.#listeners.has(cls)) break;
25
+ if (cls === Activity) return null;
26
+ cls = globalThis.Object.getPrototypeOf(cls);
27
+ }
28
+ if (cls == null) return null;
29
+ const listener = this.#listeners.get(cls);
30
+ return {
31
+ class: cls,
32
+ listener
33
+ };
34
+ }
35
+ dispatch(activity) {
36
+ return this.dispatchWithClass(activity)?.listener ?? null;
37
+ }
38
+ };
39
+ //#endregion
40
+ export { ActivityListenerSet as t };
@@ -2,12 +2,23 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-vxcWcxQS.mjs";
6
- import { t as InboxListenerSet } from "./inbox-CmYvcSMM.mjs";
5
+ import { n as version, t as name } from "./deno-ZTLy21O_.mjs";
6
+ import { t as ActivityListenerSet } from "./activity-listener-Ck3JZ_hR.mjs";
7
7
  import { Tombstone, getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { getLogger } from "@logtape/logtape";
10
10
  //#region src/federation/builder.ts
11
+ function validateSingleIdentifierVariablePath(path, errorMessage) {
12
+ const operatorMatches = globalThis.Array.from(path.matchAll(/{([+#./;?&]?)([A-Za-z_][A-Za-z0-9_]*)}/g));
13
+ if (operatorMatches.length !== 1 || operatorMatches[0]?.[2] !== "identifier") throw new RouterError(errorMessage);
14
+ if (operatorMatches.some((match) => [
15
+ "?",
16
+ "&",
17
+ "#"
18
+ ].includes(match[1]) && match[2] === "identifier")) throw new RouterError(errorMessage);
19
+ const variables = new Router().add(path, "outbox");
20
+ if (variables.size !== 1 || !variables.has("identifier")) throw new RouterError(errorMessage);
21
+ }
11
22
  var FederationBuilderImpl = class {
12
23
  router;
13
24
  actorCallbacks;
@@ -16,6 +27,7 @@ var FederationBuilderImpl = class {
16
27
  objectCallbacks;
17
28
  objectTypeIds;
18
29
  inboxPath;
30
+ outboxPath;
19
31
  inboxCallbacks;
20
32
  outboxCallbacks;
21
33
  followingCallbacks;
@@ -24,7 +36,10 @@ var FederationBuilderImpl = class {
24
36
  featuredCallbacks;
25
37
  featuredTagsCallbacks;
26
38
  inboxListeners;
39
+ outboxListeners;
27
40
  inboxErrorHandler;
41
+ outboxListenerErrorHandler;
42
+ outboxAuthorizePredicate;
28
43
  sharedInboxKeyDispatcher;
29
44
  unverifiedActivityHandler;
30
45
  outboxPermanentFailureHandler;
@@ -43,7 +58,7 @@ var FederationBuilderImpl = class {
43
58
  this.collectionTypeIds = {};
44
59
  }
45
60
  async build(options) {
46
- const { FederationImpl } = await import("./middleware-wdfeWjRJ.mjs");
61
+ const { FederationImpl } = await import("./middleware-DDXN5eBU.mjs");
47
62
  const f = new FederationImpl(options);
48
63
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
49
64
  f.router = this.router.clone();
@@ -55,6 +70,7 @@ var FederationBuilderImpl = class {
55
70
  f.objectCallbacks = { ...this.objectCallbacks };
56
71
  f.objectTypeIds = { ...this.objectTypeIds };
57
72
  f.inboxPath = this.inboxPath;
73
+ f.outboxPath = this.outboxPath;
58
74
  f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
59
75
  f.outboxCallbacks = this.outboxCallbacks == null ? void 0 : { ...this.outboxCallbacks };
60
76
  f.followingCallbacks = this.followingCallbacks == null ? void 0 : { ...this.followingCallbacks };
@@ -63,7 +79,10 @@ var FederationBuilderImpl = class {
63
79
  f.featuredCallbacks = this.featuredCallbacks == null ? void 0 : { ...this.featuredCallbacks };
64
80
  f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
65
81
  f.inboxListeners = this.inboxListeners?.clone();
82
+ f.outboxListeners = this.outboxListeners?.clone();
66
83
  f.inboxErrorHandler = this.inboxErrorHandler;
84
+ f.outboxListenerErrorHandler = this.outboxListenerErrorHandler;
85
+ f.outboxAuthorizePredicate = this.outboxAuthorizePredicate;
67
86
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
68
87
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
69
88
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
@@ -263,9 +282,14 @@ var FederationBuilderImpl = class {
263
282
  return setters;
264
283
  }
265
284
  setOutboxDispatcher(path, dispatcher) {
266
- if (this.router.has("outbox")) throw new RouterError("Outbox dispatcher already set.");
267
- const variables = this.router.add(path, "outbox");
268
- if (variables.size !== 1 || !variables.has("identifier")) throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
285
+ if (this.outboxCallbacks != null) throw new RouterError("Outbox dispatcher already set.");
286
+ if (this.router.has("outbox")) {
287
+ if (this.outboxPath !== path) throw new RouterError("Outbox dispatcher path must match outbox listener path.");
288
+ } else {
289
+ validateSingleIdentifierVariablePath(path, "Path for outbox dispatcher must have one variable: {identifier}");
290
+ this.router.add(path, "outbox");
291
+ this.outboxPath = path;
292
+ }
269
293
  const callbacks = { dispatcher };
270
294
  this.outboxCallbacks = callbacks;
271
295
  const setters = {
@@ -288,6 +312,32 @@ var FederationBuilderImpl = class {
288
312
  };
289
313
  return setters;
290
314
  }
315
+ setOutboxListeners(outboxPath) {
316
+ if (this.outboxListeners != null) throw new RouterError("Outbox listeners already set.");
317
+ if (this.router.has("outbox")) {
318
+ if (this.outboxPath !== outboxPath) throw new RouterError("Outbox listener path must match outbox dispatcher path.");
319
+ } else {
320
+ validateSingleIdentifierVariablePath(outboxPath, "Path for outbox must have one variable: {identifier}");
321
+ this.router.add(outboxPath, "outbox");
322
+ this.outboxPath = outboxPath;
323
+ }
324
+ const listeners = this.outboxListeners = new ActivityListenerSet();
325
+ const setters = {
326
+ on(type, listener) {
327
+ listeners.add(type, listener);
328
+ return setters;
329
+ },
330
+ onError: (handler) => {
331
+ this.outboxListenerErrorHandler = handler;
332
+ return setters;
333
+ },
334
+ authorize: (predicate) => {
335
+ this.outboxAuthorizePredicate = predicate;
336
+ return setters;
337
+ }
338
+ };
339
+ return setters;
340
+ }
291
341
  setFollowingDispatcher(path, dispatcher) {
292
342
  if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
293
343
  const variables = this.router.add(path, "following");
@@ -430,7 +480,7 @@ var FederationBuilderImpl = class {
430
480
  if (sharedInboxPath != null) {
431
481
  if (this.router.add(sharedInboxPath, "sharedInbox").size !== 0) throw new RouterError("Path for shared inbox must have no variables.");
432
482
  }
433
- const listeners = this.inboxListeners = new InboxListenerSet();
483
+ const listeners = this.inboxListeners = new ActivityListenerSet();
434
484
  const setters = {
435
485
  on(type, listener) {
436
486
  listeners.add(type, listener);
@@ -1,4 +1,4 @@
1
- import { n as Context, wt as ActivityTransformer } from "../context-DYDPdoCb.cjs";
1
+ import { Ot as ActivityTransformer, n as Context } from "../context-CMUd4wy0.cjs";
2
2
  import { Activity } from "@fedify/vocab";
3
3
 
4
4
  //#region src/compat/transformers.d.ts
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as Context, wt as ActivityTransformer } from "../context-78ecvxf5.js";
3
+ import { Ot as ActivityTransformer, n as Context } from "../context-BGrYMSTk.js";
4
4
  import { Activity } from "@fedify/vocab";
5
5
 
6
6
  //#region src/compat/transformers.d.ts
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
6
6
  import { t as assert } from "../assert-ddO5KLpe.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-tL2TOE9X.mjs";
8
- import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-BjVx-_bv.mjs";
8
+ import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-DbCHS-GH.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Follow, Person } from "@fedify/vocab";
11
11
  //#region src/compat/transformers.test.ts
@@ -212,6 +212,16 @@ type CollectionCursor<TContext extends Context<TContextData>, TContextData, TFil
212
212
  */
213
213
  type InboxListener<TContextData, TActivity extends Activity> = (context: InboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
214
214
  /**
215
+ * A callback that listens for activities in an outbox.
216
+ *
217
+ * @template TContextData The context data to pass to the {@link Context}.
218
+ * @template TActivity The type of activity to listen for.
219
+ * @param context The outbox context.
220
+ * @param activity The activity that was received.
221
+ * @since 2.2.0
222
+ */
223
+ type OutboxListener<TContextData, TActivity extends Activity> = (context: OutboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
224
+ /**
215
225
  * The reason why an incoming activity could not be verified.
216
226
  *
217
227
  * Unlike inbox listeners registered through {@link InboxListenerSetters.on},
@@ -242,6 +252,15 @@ type UnverifiedActivityHandler<TContextData> = (context: RequestContext<TContext
242
252
  */
243
253
  type InboxErrorHandler<TContextData> = (context: Context<TContextData>, error: Error) => void | Promise<void>;
244
254
  /**
255
+ * A callback that handles errors in an outbox listener.
256
+ *
257
+ * @template TContextData The context data to pass to the {@link Context}.
258
+ * @param context The outbox context.
259
+ * @param error The error that occurred.
260
+ * @since 2.2.0
261
+ */
262
+ type OutboxListenerErrorHandler<TContextData> = (context: OutboxContext<TContextData>, error: Error) => void | Promise<void>;
263
+ /**
245
264
  * A callback that dispatches the key pair for the authenticated document loader
246
265
  * of the {@link Context} passed to the shared inbox listener.
247
266
  *
@@ -1051,6 +1070,32 @@ interface Federatable<TContextData> {
1051
1070
  */
1052
1071
  setOutboxDispatcher(path: `${string}${Rfc6570Expression<"identifier">}${string}`, dispatcher: CollectionDispatcher<Activity, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
1053
1072
  /**
1073
+ * Assigns the URL path for the outbox and starts setting outbox listeners.
1074
+ *
1075
+ * @example
1076
+ * ``` typescript
1077
+ * federation
1078
+ * .setOutboxListeners("/users/{identifier}/outbox")
1079
+ * .on(Activity, async (ctx, activity) => {
1080
+ * await ctx.sendActivity({ identifier: ctx.identifier }, "followers", activity);
1081
+ * })
1082
+ * .authorize(async (ctx, identifier) => {
1083
+ * return ctx.request.headers.get("authorization") === `Bearer ${identifier}`;
1084
+ * });
1085
+ * ```
1086
+ *
1087
+ * @param outboxPath The URI path pattern for the outbox. The syntax is based
1088
+ * on URI Template
1089
+ * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). The
1090
+ * path must have one variable: `{identifier}`. If an
1091
+ * outbox dispatcher is configured, this path must match
1092
+ * the outbox dispatcher path.
1093
+ * @returns An object to register outbox listeners.
1094
+ * @throws {RouterError} Thrown if the path pattern is invalid.
1095
+ * @since 2.2.0
1096
+ */
1097
+ setOutboxListeners(outboxPath: `${string}${Rfc6570Expression<"identifier">}${string}`): OutboxListenerSetters<TContextData>;
1098
+ /**
1054
1099
  * Registers a following collection dispatcher.
1055
1100
  * @param path The URI path pattern for the following collection. The syntax
1056
1101
  * is based on URI Template
@@ -1674,6 +1719,38 @@ type IdempotencyStrategy = "global" | "per-origin" | "per-inbox";
1674
1719
  */
1675
1720
  type IdempotencyKeyCallback<TContextData> = (ctx: InboxContext<TContextData>, activity: Activity) => string | null | Promise<string | null>;
1676
1721
  /**
1722
+ * Registry for outbox listeners for different activity types.
1723
+ * @since 2.2.0
1724
+ */
1725
+ interface OutboxListenerSetters<TContextData> {
1726
+ /**
1727
+ * Registers a listener for a specific incoming activity type.
1728
+ *
1729
+ * @param type A subclass of {@link Activity} to listen to.
1730
+ * @param listener A callback to handle an incoming activity.
1731
+ * @returns The setters object so that settings can be chained.
1732
+ * @since 2.2.0
1733
+ */
1734
+ on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: OutboxListener<TContextData, TActivity>): OutboxListenerSetters<TContextData>;
1735
+ /**
1736
+ * Registers an error handler for outbox listeners. Any exceptions thrown
1737
+ * from the listeners are caught and passed to this handler.
1738
+ *
1739
+ * @param handler A callback to handle an error.
1740
+ * @returns The setters object so that settings can be chained.
1741
+ * @since 2.2.0
1742
+ */
1743
+ onError(handler: OutboxListenerErrorHandler<TContextData>): OutboxListenerSetters<TContextData>;
1744
+ /**
1745
+ * Registers a callback to authorize POST requests to the outbox.
1746
+ *
1747
+ * @param predicate A callback to authorize the request.
1748
+ * @returns The setters object so that settings can be chained.
1749
+ * @since 2.2.0
1750
+ */
1751
+ authorize(predicate: AuthorizePredicate<TContextData>): OutboxListenerSetters<TContextData>;
1752
+ }
1753
+ /**
1677
1754
  * Registry for inbox listeners for different activity types.
1678
1755
  */
1679
1756
  interface InboxListenerSetters<TContextData> {
@@ -2445,6 +2522,71 @@ interface InboxContext<TContextData> extends Context<TContextData> {
2445
2522
  }, recipients: "followers", options?: ForwardActivityOptions): Promise<void>;
2446
2523
  }
2447
2524
  /**
2525
+ * A context for outbox listeners.
2526
+ * @since 2.2.0
2527
+ */
2528
+ interface OutboxContext<TContextData> extends Context<TContextData> {
2529
+ /**
2530
+ * The identifier of the actor whose outbox received the POST.
2531
+ * @since 2.2.0
2532
+ */
2533
+ readonly identifier: string;
2534
+ /**
2535
+ * Indicates whether the posted activity has been delivered during the
2536
+ * current outbox listener invocation.
2537
+ * @returns `true` if the posted activity has been delivered; `false`
2538
+ * otherwise.
2539
+ * @since 2.2.0
2540
+ */
2541
+ hasDeliveredActivity(): boolean;
2542
+ /**
2543
+ * Forwards a posted activity to the recipients' inboxes without
2544
+ * re-serializing the original payload. The forwarded activity will be
2545
+ * signed in HTTP Signatures by the forwarder, but its payload will not be
2546
+ * modified, i.e., Linked Data Signatures and Object Integrity Proofs will
2547
+ * not be added. Therefore, if the posted activity is not signed (i.e., it
2548
+ * has neither Linked Data Signatures nor Object Integrity Proofs), the
2549
+ * recipients probably will not trust the activity.
2550
+ * @param forwarder The forwarder's identifier or the forwarder's username
2551
+ * or the forwarder's key pair(s).
2552
+ * @param recipients The recipients of the activity.
2553
+ * @param options Options for forwarding the activity.
2554
+ * @since 2.2.0
2555
+ */
2556
+ forwardActivity(forwarder: SenderKeyPair | SenderKeyPair[] | {
2557
+ identifier: string;
2558
+ } | {
2559
+ username: string;
2560
+ }, recipients: Recipient | Recipient[], options?: ForwardActivityOptions): Promise<void>;
2561
+ /**
2562
+ * Forwards a posted activity to the recipients' inboxes without
2563
+ * re-serializing the original payload. The forwarded activity will be
2564
+ * signed in HTTP Signatures by the forwarder, but its payload will not be
2565
+ * modified, i.e., Linked Data Signatures and Object Integrity Proofs will
2566
+ * not be added. Therefore, if the posted activity is not signed (i.e., it
2567
+ * has neither Linked Data Signatures nor Object Integrity Proofs), the
2568
+ * recipients probably will not trust the activity.
2569
+ * @param forwarder The forwarder's identifier or the forwarder's username.
2570
+ * @param recipients In this case, it must be `"followers"`.
2571
+ * @param options Options for forwarding the activity.
2572
+ * @since 2.2.0
2573
+ */
2574
+ forwardActivity(forwarder: {
2575
+ identifier: string;
2576
+ } | {
2577
+ username: string;
2578
+ }, recipients: "followers", options?: ForwardActivityOptions): Promise<void>;
2579
+ /**
2580
+ * Creates a new context with the same properties as this one,
2581
+ * but with the given data.
2582
+ * @param data The new data to associate with the context.
2583
+ * @returns A new context with the same properties as this one,
2584
+ * but with the given data.
2585
+ * @since 2.2.0
2586
+ */
2587
+ clone(data: TContextData): OutboxContext<TContextData>;
2588
+ }
2589
+ /**
2448
2590
  * A result of parsing an URI.
2449
2591
  */
2450
2592
  type ParseUriResult = {
@@ -2644,4 +2786,4 @@ interface ActorKeyPair extends CryptoKeyPair {
2644
2786
  readonly multikey: Multikey;
2645
2787
  }
2646
2788
  //#endregion
2647
- export { ActorHandleMapper as $, FederationQueueOptions as A, RouterOptions as B, IdempotencyStrategy as C, digest as Ct, Rfc6570Expression as D, ObjectCallbackSetters as E, createExponentialBackoffPolicy as F, InProcessMessageQueue as G, RespondWithObjectOptions as H, Message as I, MessageQueueEnqueueOptions as J, InProcessMessageQueueOptions as K, createFederationBuilder as L, CreateExponentialBackoffPolicyOptions as M, RetryContext as N, FederationKvPrefixes as O, RetryPolicy as P, ActorDispatcher as Q, Router as R, IdempotencyKeyCallback as S, buildCollectionSynchronizationHeader as St, InboxListenerSetters as T, respondWithObject as U, RouterRouteResult as V, respondWithObjectIfAcceptable as W, ParallelMessageQueue as X, MessageQueueListenOptions as Y, ActorAliasMapper as Z, Federation as _, UnverifiedActivityReason as _t, GetSignedKeyOptions as a, CustomCollectionCounter as at, FederationOptions as b, SenderKeyPair as bt, RequestContext as c, InboxErrorHandler as ct, SendActivityOptionsForCollection as d, ObjectAuthorizePredicate as dt, ActorKeyPairsDispatcher as et, ActorCallbackSetters as f, ObjectDispatcher as ft, Federatable as g, UnverifiedActivityHandler as gt, CustomCollectionCallbackSetters as h, SharedInboxKeyDispatcher as ht, GetActorOptions as i, CollectionDispatcher as it, createFederation as j, FederationOrigin as k, RouteActivityOptions as l, InboxListener as lt, ConstructorWithTypeId as m, OutboxPermanentFailureHandler as mt, Context as n, CollectionCounter as nt, InboxContext as o, CustomCollectionCursor as ot, CollectionCallbackSetters as p, OutboxErrorHandler as pt, MessageQueue as q, ForwardActivityOptions as r, CollectionCursor as rt, ParseUriResult as s, CustomCollectionDispatcher as st, ActorKeyPair as t, AuthorizePredicate as tt, SendActivityOptions as u, NodeInfoDispatcher as ut, FederationBuilder as v, WebFingerLinksDispatcher as vt, InboxChallengePolicy as w, ActivityTransformer as wt, FederationStartQueueOptions as x, PageItems as xt, FederationFetchOptions as y, SendActivityError as yt, RouterError as z };
2789
+ export { ActorAliasMapper as $, FederationKvPrefixes as A, Router as B, IdempotencyKeyCallback as C, SendActivityError as Ct, ObjectCallbackSetters as D, digest as Dt, InboxListenerSetters as E, buildCollectionSynchronizationHeader as Et, RetryContext as F, respondWithObject as G, RouterOptions as H, RetryPolicy as I, InProcessMessageQueueOptions as J, respondWithObjectIfAcceptable as K, createExponentialBackoffPolicy as L, FederationQueueOptions as M, createFederation as N, OutboxListenerSetters as O, ActivityTransformer as Ot, CreateExponentialBackoffPolicyOptions as P, ParallelMessageQueue as Q, Message as R, FederationStartQueueOptions as S, WebFingerLinksDispatcher as St, InboxChallengePolicy as T, PageItems as Tt, RouterRouteResult as U, RouterError as V, RespondWithObjectOptions as W, MessageQueueEnqueueOptions as X, MessageQueue as Y, MessageQueueListenOptions as Z, Federatable as _, OutboxListenerErrorHandler as _t, GetSignedKeyOptions as a, CollectionCursor as at, FederationFetchOptions as b, UnverifiedActivityHandler as bt, ParseUriResult as c, CustomCollectionCursor as ct, SendActivityOptions as d, InboxListener as dt, ActorDispatcher as et, SendActivityOptionsForCollection as f, NodeInfoDispatcher as ft, CustomCollectionCallbackSetters as g, OutboxListener as gt, ConstructorWithTypeId as h, OutboxErrorHandler as ht, GetActorOptions as i, CollectionCounter as it, FederationOrigin as j, Rfc6570Expression as k, RequestContext as l, CustomCollectionDispatcher as lt, CollectionCallbackSetters as m, ObjectDispatcher as mt, Context as n, ActorKeyPairsDispatcher as nt, InboxContext as o, CollectionDispatcher as ot, ActorCallbackSetters as p, ObjectAuthorizePredicate as pt, InProcessMessageQueue as q, ForwardActivityOptions as r, AuthorizePredicate as rt, OutboxContext as s, CustomCollectionCounter as st, ActorKeyPair as t, ActorHandleMapper as tt, RouteActivityOptions as u, InboxErrorHandler as ut, Federation as v, OutboxPermanentFailureHandler as vt, IdempotencyStrategy as w, SenderKeyPair as wt, FederationOptions as x, UnverifiedActivityReason as xt, FederationBuilder as y, SharedInboxKeyDispatcher as yt, createFederationBuilder as z };
@@ -210,6 +210,16 @@ type CollectionCursor<TContext extends Context<TContextData>, TContextData, TFil
210
210
  */
211
211
  type InboxListener<TContextData, TActivity extends Activity> = (context: InboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
212
212
  /**
213
+ * A callback that listens for activities in an outbox.
214
+ *
215
+ * @template TContextData The context data to pass to the {@link Context}.
216
+ * @template TActivity The type of activity to listen for.
217
+ * @param context The outbox context.
218
+ * @param activity The activity that was received.
219
+ * @since 2.2.0
220
+ */
221
+ type OutboxListener<TContextData, TActivity extends Activity> = (context: OutboxContext<TContextData>, activity: TActivity) => void | Promise<void>;
222
+ /**
213
223
  * The reason why an incoming activity could not be verified.
214
224
  *
215
225
  * Unlike inbox listeners registered through {@link InboxListenerSetters.on},
@@ -240,6 +250,15 @@ type UnverifiedActivityHandler<TContextData> = (context: RequestContext<TContext
240
250
  */
241
251
  type InboxErrorHandler<TContextData> = (context: Context<TContextData>, error: Error) => void | Promise<void>;
242
252
  /**
253
+ * A callback that handles errors in an outbox listener.
254
+ *
255
+ * @template TContextData The context data to pass to the {@link Context}.
256
+ * @param context The outbox context.
257
+ * @param error The error that occurred.
258
+ * @since 2.2.0
259
+ */
260
+ type OutboxListenerErrorHandler<TContextData> = (context: OutboxContext<TContextData>, error: Error) => void | Promise<void>;
261
+ /**
243
262
  * A callback that dispatches the key pair for the authenticated document loader
244
263
  * of the {@link Context} passed to the shared inbox listener.
245
264
  *
@@ -1049,6 +1068,32 @@ interface Federatable<TContextData> {
1049
1068
  */
1050
1069
  setOutboxDispatcher(path: `${string}${Rfc6570Expression<"identifier">}${string}`, dispatcher: CollectionDispatcher<Activity, RequestContext<TContextData>, TContextData, void>): CollectionCallbackSetters<RequestContext<TContextData>, TContextData, void>;
1051
1070
  /**
1071
+ * Assigns the URL path for the outbox and starts setting outbox listeners.
1072
+ *
1073
+ * @example
1074
+ * ``` typescript
1075
+ * federation
1076
+ * .setOutboxListeners("/users/{identifier}/outbox")
1077
+ * .on(Activity, async (ctx, activity) => {
1078
+ * await ctx.sendActivity({ identifier: ctx.identifier }, "followers", activity);
1079
+ * })
1080
+ * .authorize(async (ctx, identifier) => {
1081
+ * return ctx.request.headers.get("authorization") === `Bearer ${identifier}`;
1082
+ * });
1083
+ * ```
1084
+ *
1085
+ * @param outboxPath The URI path pattern for the outbox. The syntax is based
1086
+ * on URI Template
1087
+ * ([RFC 6570](https://tools.ietf.org/html/rfc6570)). The
1088
+ * path must have one variable: `{identifier}`. If an
1089
+ * outbox dispatcher is configured, this path must match
1090
+ * the outbox dispatcher path.
1091
+ * @returns An object to register outbox listeners.
1092
+ * @throws {RouterError} Thrown if the path pattern is invalid.
1093
+ * @since 2.2.0
1094
+ */
1095
+ setOutboxListeners(outboxPath: `${string}${Rfc6570Expression<"identifier">}${string}`): OutboxListenerSetters<TContextData>;
1096
+ /**
1052
1097
  * Registers a following collection dispatcher.
1053
1098
  * @param path The URI path pattern for the following collection. The syntax
1054
1099
  * is based on URI Template
@@ -1672,6 +1717,38 @@ type IdempotencyStrategy = "global" | "per-origin" | "per-inbox";
1672
1717
  */
1673
1718
  type IdempotencyKeyCallback<TContextData> = (ctx: InboxContext<TContextData>, activity: Activity) => string | null | Promise<string | null>;
1674
1719
  /**
1720
+ * Registry for outbox listeners for different activity types.
1721
+ * @since 2.2.0
1722
+ */
1723
+ interface OutboxListenerSetters<TContextData> {
1724
+ /**
1725
+ * Registers a listener for a specific incoming activity type.
1726
+ *
1727
+ * @param type A subclass of {@link Activity} to listen to.
1728
+ * @param listener A callback to handle an incoming activity.
1729
+ * @returns The setters object so that settings can be chained.
1730
+ * @since 2.2.0
1731
+ */
1732
+ on<TActivity extends Activity>(type: new (...args: any[]) => TActivity, listener: OutboxListener<TContextData, TActivity>): OutboxListenerSetters<TContextData>;
1733
+ /**
1734
+ * Registers an error handler for outbox listeners. Any exceptions thrown
1735
+ * from the listeners are caught and passed to this handler.
1736
+ *
1737
+ * @param handler A callback to handle an error.
1738
+ * @returns The setters object so that settings can be chained.
1739
+ * @since 2.2.0
1740
+ */
1741
+ onError(handler: OutboxListenerErrorHandler<TContextData>): OutboxListenerSetters<TContextData>;
1742
+ /**
1743
+ * Registers a callback to authorize POST requests to the outbox.
1744
+ *
1745
+ * @param predicate A callback to authorize the request.
1746
+ * @returns The setters object so that settings can be chained.
1747
+ * @since 2.2.0
1748
+ */
1749
+ authorize(predicate: AuthorizePredicate<TContextData>): OutboxListenerSetters<TContextData>;
1750
+ }
1751
+ /**
1675
1752
  * Registry for inbox listeners for different activity types.
1676
1753
  */
1677
1754
  interface InboxListenerSetters<TContextData> {
@@ -2443,6 +2520,71 @@ interface InboxContext<TContextData> extends Context<TContextData> {
2443
2520
  }, recipients: "followers", options?: ForwardActivityOptions): Promise<void>;
2444
2521
  }
2445
2522
  /**
2523
+ * A context for outbox listeners.
2524
+ * @since 2.2.0
2525
+ */
2526
+ interface OutboxContext<TContextData> extends Context<TContextData> {
2527
+ /**
2528
+ * The identifier of the actor whose outbox received the POST.
2529
+ * @since 2.2.0
2530
+ */
2531
+ readonly identifier: string;
2532
+ /**
2533
+ * Indicates whether the posted activity has been delivered during the
2534
+ * current outbox listener invocation.
2535
+ * @returns `true` if the posted activity has been delivered; `false`
2536
+ * otherwise.
2537
+ * @since 2.2.0
2538
+ */
2539
+ hasDeliveredActivity(): boolean;
2540
+ /**
2541
+ * Forwards a posted activity to the recipients' inboxes without
2542
+ * re-serializing the original payload. The forwarded activity will be
2543
+ * signed in HTTP Signatures by the forwarder, but its payload will not be
2544
+ * modified, i.e., Linked Data Signatures and Object Integrity Proofs will
2545
+ * not be added. Therefore, if the posted activity is not signed (i.e., it
2546
+ * has neither Linked Data Signatures nor Object Integrity Proofs), the
2547
+ * recipients probably will not trust the activity.
2548
+ * @param forwarder The forwarder's identifier or the forwarder's username
2549
+ * or the forwarder's key pair(s).
2550
+ * @param recipients The recipients of the activity.
2551
+ * @param options Options for forwarding the activity.
2552
+ * @since 2.2.0
2553
+ */
2554
+ forwardActivity(forwarder: SenderKeyPair | SenderKeyPair[] | {
2555
+ identifier: string;
2556
+ } | {
2557
+ username: string;
2558
+ }, recipients: Recipient | Recipient[], options?: ForwardActivityOptions): Promise<void>;
2559
+ /**
2560
+ * Forwards a posted activity to the recipients' inboxes without
2561
+ * re-serializing the original payload. The forwarded activity will be
2562
+ * signed in HTTP Signatures by the forwarder, but its payload will not be
2563
+ * modified, i.e., Linked Data Signatures and Object Integrity Proofs will
2564
+ * not be added. Therefore, if the posted activity is not signed (i.e., it
2565
+ * has neither Linked Data Signatures nor Object Integrity Proofs), the
2566
+ * recipients probably will not trust the activity.
2567
+ * @param forwarder The forwarder's identifier or the forwarder's username.
2568
+ * @param recipients In this case, it must be `"followers"`.
2569
+ * @param options Options for forwarding the activity.
2570
+ * @since 2.2.0
2571
+ */
2572
+ forwardActivity(forwarder: {
2573
+ identifier: string;
2574
+ } | {
2575
+ username: string;
2576
+ }, recipients: "followers", options?: ForwardActivityOptions): Promise<void>;
2577
+ /**
2578
+ * Creates a new context with the same properties as this one,
2579
+ * but with the given data.
2580
+ * @param data The new data to associate with the context.
2581
+ * @returns A new context with the same properties as this one,
2582
+ * but with the given data.
2583
+ * @since 2.2.0
2584
+ */
2585
+ clone(data: TContextData): OutboxContext<TContextData>;
2586
+ }
2587
+ /**
2446
2588
  * A result of parsing an URI.
2447
2589
  */
2448
2590
  type ParseUriResult = {
@@ -2642,4 +2784,4 @@ interface ActorKeyPair extends CryptoKeyPair {
2642
2784
  readonly multikey: Multikey;
2643
2785
  }
2644
2786
  //#endregion
2645
- export { ActorHandleMapper as $, FederationQueueOptions as A, RouterOptions as B, IdempotencyStrategy as C, digest as Ct, Rfc6570Expression as D, ObjectCallbackSetters as E, createExponentialBackoffPolicy as F, InProcessMessageQueue as G, RespondWithObjectOptions as H, Message as I, MessageQueueEnqueueOptions as J, InProcessMessageQueueOptions as K, createFederationBuilder as L, CreateExponentialBackoffPolicyOptions as M, RetryContext as N, FederationKvPrefixes as O, RetryPolicy as P, ActorDispatcher as Q, Router as R, IdempotencyKeyCallback as S, buildCollectionSynchronizationHeader as St, InboxListenerSetters as T, respondWithObject as U, RouterRouteResult as V, respondWithObjectIfAcceptable as W, ParallelMessageQueue as X, MessageQueueListenOptions as Y, ActorAliasMapper as Z, Federation as _, UnverifiedActivityReason as _t, GetSignedKeyOptions as a, CustomCollectionCounter as at, FederationOptions as b, SenderKeyPair as bt, RequestContext as c, InboxErrorHandler as ct, SendActivityOptionsForCollection as d, ObjectAuthorizePredicate as dt, ActorKeyPairsDispatcher as et, ActorCallbackSetters as f, ObjectDispatcher as ft, Federatable as g, UnverifiedActivityHandler as gt, CustomCollectionCallbackSetters as h, SharedInboxKeyDispatcher as ht, GetActorOptions as i, CollectionDispatcher as it, createFederation as j, FederationOrigin as k, RouteActivityOptions as l, InboxListener as lt, ConstructorWithTypeId as m, OutboxPermanentFailureHandler as mt, Context as n, CollectionCounter as nt, InboxContext as o, CustomCollectionCursor as ot, CollectionCallbackSetters as p, OutboxErrorHandler as pt, MessageQueue as q, ForwardActivityOptions as r, CollectionCursor as rt, ParseUriResult as s, CustomCollectionDispatcher as st, ActorKeyPair as t, AuthorizePredicate as tt, SendActivityOptions as u, NodeInfoDispatcher as ut, FederationBuilder as v, WebFingerLinksDispatcher as vt, InboxChallengePolicy as w, ActivityTransformer as wt, FederationStartQueueOptions as x, PageItems as xt, FederationFetchOptions as y, SendActivityError as yt, RouterError as z };
2787
+ export { ActorAliasMapper as $, FederationKvPrefixes as A, Router as B, IdempotencyKeyCallback as C, SendActivityError as Ct, ObjectCallbackSetters as D, digest as Dt, InboxListenerSetters as E, buildCollectionSynchronizationHeader as Et, RetryContext as F, respondWithObject as G, RouterOptions as H, RetryPolicy as I, InProcessMessageQueueOptions as J, respondWithObjectIfAcceptable as K, createExponentialBackoffPolicy as L, FederationQueueOptions as M, createFederation as N, OutboxListenerSetters as O, ActivityTransformer as Ot, CreateExponentialBackoffPolicyOptions as P, ParallelMessageQueue as Q, Message as R, FederationStartQueueOptions as S, WebFingerLinksDispatcher as St, InboxChallengePolicy as T, PageItems as Tt, RouterRouteResult as U, RouterError as V, RespondWithObjectOptions as W, MessageQueueEnqueueOptions as X, MessageQueue as Y, MessageQueueListenOptions as Z, Federatable as _, OutboxListenerErrorHandler as _t, GetSignedKeyOptions as a, CollectionCursor as at, FederationFetchOptions as b, UnverifiedActivityHandler as bt, ParseUriResult as c, CustomCollectionCursor as ct, SendActivityOptions as d, InboxListener as dt, ActorDispatcher as et, SendActivityOptionsForCollection as f, NodeInfoDispatcher as ft, CustomCollectionCallbackSetters as g, OutboxListener as gt, ConstructorWithTypeId as h, OutboxErrorHandler as ht, GetActorOptions as i, CollectionCounter as it, FederationOrigin as j, Rfc6570Expression as k, RequestContext as l, CustomCollectionDispatcher as lt, CollectionCallbackSetters as m, ObjectDispatcher as mt, Context as n, ActorKeyPairsDispatcher as nt, InboxContext as o, CollectionDispatcher as ot, ActorCallbackSetters as p, ObjectAuthorizePredicate as pt, InProcessMessageQueue as q, ForwardActivityOptions as r, AuthorizePredicate as rt, OutboxContext as s, CustomCollectionCounter as st, ActorKeyPair as t, ActorHandleMapper as tt, RouteActivityOptions as u, InboxErrorHandler as ut, Federation as v, OutboxPermanentFailureHandler as vt, IdempotencyStrategy as w, SenderKeyPair as wt, FederationOptions as x, UnverifiedActivityReason as xt, FederationBuilder as y, SharedInboxKeyDispatcher as yt, createFederationBuilder as z };
@@ -96,10 +96,25 @@ function createInboxContext(args) {
96
96
  data
97
97
  })),
98
98
  recipient: args.recipient ?? null,
99
- forwardActivity: args.forwardActivity ?? ((_params) => {
99
+ forwardActivity: args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
100
100
  throw new Error("Not implemented");
101
101
  })
102
102
  };
103
103
  }
104
+ function createOutboxContext(args) {
105
+ const forwardActivity = args.forwardActivity ?? ((_forwarder, _recipients, _options) => {
106
+ throw new Error("Not implemented");
107
+ });
108
+ return {
109
+ ...createContext(args),
110
+ clone: args.clone ?? ((data) => createOutboxContext({
111
+ ...args,
112
+ data
113
+ })),
114
+ identifier: args.identifier,
115
+ hasDeliveredActivity: args.hasDeliveredActivity ?? (() => false),
116
+ forwardActivity
117
+ };
118
+ }
104
119
  //#endregion
105
- export { createRequestContext as n, createInboxContext as t };
120
+ export { createOutboxContext as n, createRequestContext as r, createInboxContext as t };
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.2.0-pr.695.16+7a782334";
6
+ var version = "2.2.0-pr.697.17+8d06dbd9";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { o as validateCryptoKey } from "./key-CGx_dDkX.mjs";
5
- import { n as doubleKnock } from "./http-RZPxDWq5.mjs";
4
+ import { o as validateCryptoKey } from "./key-DVFCI5om.mjs";
5
+ import { n as doubleKnock } from "./http-CFmrJbuT.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";