@fedify/fedify 1.6.14 → 1.6.15

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 (129) hide show
  1. package/dist/actor-C92GiVtr.js +146 -0
  2. package/dist/actor.js +34648 -5
  3. package/dist/{assert.js → assert-C-mZuSQl.js} +1 -1
  4. package/dist/{assert_instance_of.js → assert_instance_of-lS0Jr2iu.js} +1 -1
  5. package/dist/{assert_is_error.js → assert_is_error-CIYFACrT.js} +1 -1
  6. package/dist/{assert_not_equals.js → assert_not_equals-C1azCAB0.js} +1 -1
  7. package/dist/{assert_rejects.js → assert_rejects-Bkh5lA1a.js} +2 -2
  8. package/dist/{assert_throws.js → assert_throws-CmpfkWEM.js} +2 -2
  9. package/dist/authdocloader-D6yaLhIk.js +51 -0
  10. package/dist/authdocloader.js +2 -4
  11. package/dist/{builder.js → builder-BxA2MvN4.js} +5 -5
  12. package/dist/{client.js → client-CwG-8F8w.js} +2 -2
  13. package/dist/compat/transformers.test.js +30 -30
  14. package/dist/{context.js → context-p_zzA2HO.js} +4 -4
  15. package/dist/docloader-BHWMnBPo.js +4413 -0
  16. package/dist/docloader.js +61 -4
  17. package/dist/{esm.js → esm-BRXvTSrx.js} +1 -1
  18. package/dist/federation/builder.test.js +17 -17
  19. package/dist/federation/collection.test.js +8 -8
  20. package/dist/federation/handler.test.js +36 -36
  21. package/dist/federation/inbox.test.js +11 -11
  22. package/dist/federation/keycache.test.js +12 -12
  23. package/dist/federation/kv.test.js +8 -8
  24. package/dist/federation/middleware.test.js +39 -39
  25. package/dist/federation/mq.test.js +8 -8
  26. package/dist/federation/retry.test.js +5 -5
  27. package/dist/federation/router.test.js +9 -9
  28. package/dist/federation/send.test.js +23 -23
  29. package/dist/http-CDlvJGv-.js +780 -0
  30. package/dist/http.js +4 -5
  31. package/dist/{inbox.js → inbox-ABTFAUzE.js} +3 -3
  32. package/dist/key-B3u1GQO0.js +259 -0
  33. package/dist/key-CFRooZ-5.js +16 -0
  34. package/dist/key.js +1 -7
  35. package/dist/key2.js +2 -4
  36. package/dist/{keycache.js → keycache-cqA-vzFY.js} +1 -1
  37. package/dist/{keys.js → keys-Bf_ChvLf.js} +1 -1
  38. package/dist/{ld.js → ld-BqMJ-pvL.js} +4 -4
  39. package/dist/{lookup2.js → lookup-BjvH6B6r.js} +4 -4
  40. package/dist/lookup-DYr8tISP.js +129 -0
  41. package/dist/lookup.js +1 -3
  42. package/dist/middleware-BqxvDftt.js +32 -0
  43. package/dist/middleware-DrkMfeD5.js +2638 -0
  44. package/dist/middleware.js +1220 -362
  45. package/dist/middleware2.js +6 -21
  46. package/dist/nodeinfo/client.test.js +12 -12
  47. package/dist/nodeinfo/handler.test.js +34 -34
  48. package/dist/nodeinfo/semver.test.js +8 -8
  49. package/dist/nodeinfo/types.test.js +9 -9
  50. package/dist/{owner.js → owner-DmM37H3z.js} +3 -3
  51. package/dist/proof-DAyY3Emw.js +255 -0
  52. package/dist/proof.js +330 -8
  53. package/dist/runtime/authdocloader.test.js +20 -20
  54. package/dist/runtime/docloader.test.js +13 -13
  55. package/dist/runtime/key.test.js +15 -15
  56. package/dist/runtime/langstr.test.js +8 -8
  57. package/dist/runtime/multibase/multibase.test.js +8 -8
  58. package/dist/runtime/url.test.js +7 -7
  59. package/dist/{send.js → send-D8zkuKY4.js} +2 -2
  60. package/dist/sig/http.test.js +20 -20
  61. package/dist/sig/key.test.js +17 -17
  62. package/dist/sig/ld.test.js +18 -18
  63. package/dist/sig/owner.test.js +20 -20
  64. package/dist/sig/proof.test.js +19 -19
  65. package/dist/{std__assert.js → std__assert-BdP_WkD-.js} +1 -1
  66. package/dist/testing/docloader.test.js +8 -8
  67. package/dist/testing/mod.js +2 -2
  68. package/dist/{testing.js → testing-qaAD4B0t.js} +1 -1
  69. package/dist/types-CB_2uuCA.js +51 -0
  70. package/dist/types.js +397 -3
  71. package/dist/vocab/actor.test.js +16 -16
  72. package/dist/vocab/lookup.test.js +17 -17
  73. package/dist/vocab/type.test.js +9 -9
  74. package/dist/vocab/vocab.test.js +17 -17
  75. package/dist/vocab-Cj-MrEWI.js +34386 -0
  76. package/dist/vocab.js +133 -34351
  77. package/dist/webfinger/handler.test.js +34 -34
  78. package/dist/webfinger/lookup.test.js +11 -11
  79. package/dist/x/cfworkers.test.js +7 -7
  80. package/package.json +1 -1
  81. /package/dist/{assert_equals.js → assert_equals-Dy0MG_Zw.js} +0 -0
  82. /package/dist/{chunk.js → chunk-DvTpRkcT.js} +0 -0
  83. /package/dist/{collection.js → collection-XNLQhehO.js} +0 -0
  84. /package/dist/compat/{transformers.test.d.ts → transformers.test-DnJbd34u.d.ts} +0 -0
  85. /package/dist/{denokv.js → denokv-XeyH3cFs.js} +0 -0
  86. /package/dist/{docloader2.js → docloader-BDSHZfTJ.js} +0 -0
  87. /package/dist/federation/{builder.test.d.ts → builder.test-Bpt6NOZ6.d.ts} +0 -0
  88. /package/dist/federation/{collection.test.d.ts → collection.test-DKJ6JOZz.d.ts} +0 -0
  89. /package/dist/federation/{handler.test.d.ts → handler.test-BMT7uLC0.d.ts} +0 -0
  90. /package/dist/federation/{inbox.test.d.ts → inbox.test-Do6i02Qp.d.ts} +0 -0
  91. /package/dist/federation/{keycache.test.d.ts → keycache.test-BT83IPZY.d.ts} +0 -0
  92. /package/dist/federation/{kv.test.d.ts → kv.test-kFzzF2VN.d.ts} +0 -0
  93. /package/dist/federation/{middleware.test.d.ts → middleware.test-B1R4_e3-.d.ts} +0 -0
  94. /package/dist/federation/{mq.test.d.ts → mq.test-l79EQQOe.d.ts} +0 -0
  95. /package/dist/federation/{retry.test.d.ts → retry.test-BqS50VCX.d.ts} +0 -0
  96. /package/dist/federation/{router.test.d.ts → router.test-CYQl4po-.d.ts} +0 -0
  97. /package/dist/federation/{send.test.d.ts → send.test-COUnNUzv.d.ts} +0 -0
  98. /package/dist/{kv.js → kv-DosGYGwV.js} +0 -0
  99. /package/dist/{langstr.js → langstr-pFHBDU4y.js} +0 -0
  100. /package/dist/{multibase.js → multibase-DBcKTV2a.js} +0 -0
  101. /package/dist/nodeinfo/{client.test.d.ts → client.test-CZLe79hL.d.ts} +0 -0
  102. /package/dist/nodeinfo/{handler.test.d.ts → handler.test-B-EDZ_hK.d.ts} +0 -0
  103. /package/dist/nodeinfo/{semver.test.d.ts → semver.test-BEuuQSEM.d.ts} +0 -0
  104. /package/dist/nodeinfo/{types.test.d.ts → types.test-B5AT89WV.d.ts} +0 -0
  105. /package/dist/{retry.js → retry-BQet39_l.js} +0 -0
  106. /package/dist/{router.js → router-BuDkN4RQ.js} +0 -0
  107. /package/dist/runtime/{authdocloader.test.d.ts → authdocloader.test-hCRKzn9v.d.ts} +0 -0
  108. /package/dist/runtime/{docloader.test.d.ts → docloader.test-CVd7i_5h.d.ts} +0 -0
  109. /package/dist/runtime/{key.test.d.ts → key.test-DBsILYSD.d.ts} +0 -0
  110. /package/dist/runtime/{langstr.test.d.ts → langstr.test-CiKxuuRY.d.ts} +0 -0
  111. /package/dist/runtime/multibase/{multibase.test.d.ts → multibase.test-Brh6gPBP.d.ts} +0 -0
  112. /package/dist/runtime/{url.test.d.ts → url.test-DlRqkU2j.d.ts} +0 -0
  113. /package/dist/{semver.js → semver-D9d-VO-_.js} +0 -0
  114. /package/dist/sig/{http.test.d.ts → http.test-BpXNAWNI.d.ts} +0 -0
  115. /package/dist/sig/{key.test.d.ts → key.test-B2iLIugy.d.ts} +0 -0
  116. /package/dist/sig/{ld.test.d.ts → ld.test-D-cI70Gw.d.ts} +0 -0
  117. /package/dist/sig/{owner.test.d.ts → owner.test-B_YRjMPj.d.ts} +0 -0
  118. /package/dist/sig/{proof.test.d.ts → proof.test-BagEM_-4.d.ts} +0 -0
  119. /package/dist/testing/{docloader.test.d.ts → docloader.test-lrzf6sDZ.d.ts} +0 -0
  120. /package/dist/testing/{mod.d.ts → mod-3uM8ZvS7.d.ts} +0 -0
  121. /package/dist/{type.js → type-DFsmi-p1.js} +0 -0
  122. /package/dist/{url.js → url-BdNvnK9P.js} +0 -0
  123. /package/dist/vocab/{actor.test.d.ts → actor.test-ClC-iVWk.d.ts} +0 -0
  124. /package/dist/vocab/{lookup.test.d.ts → lookup.test-Cq1I-27w.d.ts} +0 -0
  125. /package/dist/vocab/{type.test.d.ts → type.test-bfFiYGcs.d.ts} +0 -0
  126. /package/dist/vocab/{vocab.test.d.ts → vocab.test-h-ZTisfu.d.ts} +0 -0
  127. /package/dist/webfinger/{handler.test.d.ts → handler.test-DiUeEDDD.d.ts} +0 -0
  128. /package/dist/webfinger/{lookup.test.d.ts → lookup.test-D9onm3U3.d.ts} +0 -0
  129. /package/dist/x/{cfworkers.test.d.ts → cfworkers.test-KXHlJ29z.d.ts} +0 -0
@@ -1,301 +1,845 @@
1
1
 
2
2
  import { Temporal } from "@js-temporal/polyfill";
3
3
  import { URLPattern } from "urlpattern-polyfill";
4
- globalThis.addEventListener = () => {};
5
4
 
6
- import { d as version, i as getDocumentLoader, s as kvCache, u as name } from "./docloader.js";
7
- import { t as getNodeInfo } from "./client.js";
8
- import { n as RouterError } from "./router.js";
9
- import { t as nodeInfoToJson } from "./types.js";
10
- import { _ as Object$1, b as OrderedCollectionPage, h as Multikey, m as Link, o as CryptographicKey, t as Activity, y as OrderedCollection } from "./vocab.js";
5
+ import { r as getDefaultActivityTransformers } from "./transformers.js";
6
+ import { d as name, f as version, i as getDocumentLoader, s as kvCache } from "./docloader.js";
7
+ import { G as Multikey, Tt as getTypeId, V as Link, X as OrderedCollectionPage, Y as OrderedCollection, q as Object$1, s as Activity, y as CryptographicKey } from "./actor.js";
11
8
  import { t as lookupWebFinger } from "./lookup.js";
12
- import { t as getTypeId } from "./type.js";
13
9
  import { a as validateCryptoKey, i as importJwk, t as exportJwk } from "./key2.js";
14
- import { l as verifyRequest } from "./http.js";
10
+ import { r as verifyRequest, t as doubleKnock } from "./http.js";
11
+ import { a as doesActorOwnKey, d as signJsonLd, f as verifyJsonLd, l as detachSignature, n as signObject, o as getKeyOwner, r as verifyObject, u as hasSignature } from "./proof.js";
12
+ import { n as getNodeInfo, t as nodeInfoToJson } from "./types.js";
15
13
  import { t as getAuthenticatedDocumentLoader } from "./authdocloader.js";
16
- import { a as signJsonLd, i as hasSignature, o as verifyJsonLd, r as detachSignature } from "./ld.js";
17
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner.js";
18
- import { n as signObject, r as verifyObject } from "./proof.js";
19
- import { n as traverseCollection, t as lookupObject } from "./lookup2.js";
20
- import { n as routeActivity } from "./inbox.js";
21
- import { t as FederationBuilderImpl } from "./builder.js";
22
- import { t as buildCollectionSynchronizationHeader } from "./collection.js";
23
- import { t as KvKeyCache } from "./keycache.js";
24
- import { t as createExponentialBackoffPolicy } from "./retry.js";
25
- import { n as sendActivity, t as extractInboxes } from "./send.js";
14
+ import { n as lookupObject, r as traverseCollection } from "./vocab.js";
26
15
  import { getLogger, withContext } from "@logtape/logtape";
27
16
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
17
+ import { encodeHex } from "byte-encodings/hex";
18
+ import { cloneDeep } from "@es-toolkit/es-toolkit";
19
+ import { Router } from "uri-template-router";
20
+ import { parseTemplate } from "url-template";
28
21
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
29
22
  import { domainToASCII } from "node:url";
30
23
 
31
- //#region compat/transformers.ts
32
- const logger$1 = getLogger([
33
- "fedify",
34
- "compat",
35
- "transformers"
36
- ]);
37
- /**
38
- * An activity transformer that assigns a new random ID to an activity if it
39
- * does not already have one. This is useful for ensuring that activities
40
- * have an ID before they are sent to other servers.
41
- *
42
- * The generated ID is an origin URI with a fragment which contains an activity
43
- * type name with a random UUID:
44
- *
45
- * ```
46
- * https://example.com/#Follow/12345678-1234-5678-1234-567812345678
47
- * ```
48
- *
49
- * @typeParam TContextData The type of the context data.
50
- * @param activity The activity to assign an ID to.
51
- * @param context The context of the activity.
52
- * @return The activity with an ID assigned.
53
- * @since 1.4.0
54
- */
55
- function autoIdAssigner(activity, context$1) {
56
- if (activity.id != null) return activity;
57
- const id = new URL(`/#${activity.constructor.name}/${crypto.randomUUID()}`, context$1.origin);
58
- logger$1.warn("As the activity to send does not have an id, a new id {id} has been generated for it. However, it is recommended to explicitly set the id for the activity.", { id: id.href });
59
- return activity.clone({ id });
60
- }
61
- /**
62
- * An activity transformer that dehydrates the actor property of an activity
63
- * so that it only contains the actor's URI. For example, suppose we have an
64
- * activity like this:
65
- *
66
- * ```typescript
67
- * import { Follow, Person } from "@fedify/fedify/vocab";
68
- * const input = new Follow({
69
- * id: new URL("http://example.com/activities/1"),
70
- * actor: new Person({
71
- * id: new URL("http://example.com/actors/1"),
72
- * name: "Alice",
73
- * preferredUsername: "alice",
74
- * }),
75
- * object: new Person({
76
- * id: new URL("http://example.com/actors/2"),
77
- * name: "Bob",
78
- * preferredUsername: "bob",
79
- * }),
80
- * });
81
- * ```
82
- *
83
- * The result of applying this transformer would be:
84
- *
85
- * ```typescript
86
- * import { Follow, Person } from "@fedify/fedify/vocab";
87
- * const output = new Follow({
88
- * id: new URL("http://example.com/activities/1"),
89
- * actor: new URL("http://example.com/actors/1"),
90
- * object: new Person({
91
- * id: new URL("http://example.com/actors/2"),
92
- * name: "Bob",
93
- * preferredUsername: "bob",
94
- * }),
95
- * });
96
- * ```
97
- *
98
- * As some ActivityPub implementations like Threads fail to deal with inlined
99
- * actor objects, this transformer can be used to work around this issue.
100
- * @typeParam TContextData The type of the context data.
101
- * @param activity The activity to dehydrate the actor property of.
102
- * @param context The context of the activity.
103
- * @returns The dehydrated activity.
104
- * @since 1.4.0
105
- */
106
- function actorDehydrator(activity, _context) {
107
- if (activity.actorIds.length < 1) return activity;
108
- return activity.clone({ actors: activity.actorIds });
109
- }
110
- /**
111
- * Gets the default activity transformers that are applied to all outgoing
112
- * activities.
113
- * @typeParam TContextData The type of the context data.
114
- * @returns The default activity transformers.
115
- * @since 1.4.0
116
- */
117
- function getDefaultActivityTransformers() {
118
- return [autoIdAssigner, actorDehydrator];
24
+ //#region federation/inbox.ts
25
+ var InboxListenerSet = class InboxListenerSet {
26
+ #listeners;
27
+ constructor() {
28
+ this.#listeners = /* @__PURE__ */ new Map();
29
+ }
30
+ clone() {
31
+ const clone = new InboxListenerSet();
32
+ clone.#listeners = new Map(this.#listeners);
33
+ return clone;
34
+ }
35
+ add(type, listener) {
36
+ if (this.#listeners.has(type)) throw new TypeError("Listener already set for this type.");
37
+ this.#listeners.set(type, listener);
38
+ }
39
+ dispatchWithClass(activity) {
40
+ let cls = activity.constructor;
41
+ const inboxListeners = this.#listeners;
42
+ if (inboxListeners == null) return null;
43
+ while (true) {
44
+ if (inboxListeners.has(cls)) break;
45
+ if (cls === Activity) return null;
46
+ cls = globalThis.Object.getPrototypeOf(cls);
47
+ }
48
+ const listener = inboxListeners.get(cls);
49
+ return {
50
+ class: cls,
51
+ listener
52
+ };
53
+ }
54
+ dispatch(activity) {
55
+ return this.dispatchWithClass(activity)?.listener ?? null;
56
+ }
57
+ };
58
+ async function routeActivity({ context: ctx, json, activity, recipient, inboxListeners, inboxContextFactory, inboxErrorHandler, kv, kvPrefixes, queue, span, tracerProvider }) {
59
+ const logger$1 = getLogger([
60
+ "fedify",
61
+ "federation",
62
+ "inbox"
63
+ ]);
64
+ const cacheKey = activity.id == null ? null : [
65
+ ...kvPrefixes.activityIdempotence,
66
+ ctx.origin,
67
+ activity.id.href
68
+ ];
69
+ if (cacheKey != null) {
70
+ if (await kv.get(cacheKey) === true) {
71
+ logger$1.debug("Activity {activityId} has already been processed.", {
72
+ activityId: activity.id?.href,
73
+ activity: json,
74
+ recipient
75
+ });
76
+ span.setStatus({
77
+ code: SpanStatusCode.UNSET,
78
+ message: `Activity ${activity.id?.href} has already been processed.`
79
+ });
80
+ return "alreadyProcessed";
81
+ }
82
+ }
83
+ if (activity.actorId == null) {
84
+ logger$1.error("Missing actor.", { activity: json });
85
+ span.setStatus({
86
+ code: SpanStatusCode.ERROR,
87
+ message: "Missing actor."
88
+ });
89
+ return "missingActor";
90
+ }
91
+ span.setAttribute("activitypub.actor.id", activity.actorId.href);
92
+ if (queue != null) {
93
+ const carrier = {};
94
+ propagation.inject(context.active(), carrier);
95
+ try {
96
+ await queue.enqueue({
97
+ type: "inbox",
98
+ id: crypto.randomUUID(),
99
+ baseUrl: ctx.origin,
100
+ activity: json,
101
+ identifier: recipient,
102
+ attempt: 0,
103
+ started: (/* @__PURE__ */ new Date()).toISOString(),
104
+ traceContext: carrier
105
+ });
106
+ } catch (error) {
107
+ logger$1.error("Failed to enqueue the incoming activity {activityId}:\n{error}", {
108
+ error,
109
+ activityId: activity.id?.href,
110
+ activity: json,
111
+ recipient
112
+ });
113
+ span.setStatus({
114
+ code: SpanStatusCode.ERROR,
115
+ message: `Failed to enqueue the incoming activity ${activity.id?.href}.`
116
+ });
117
+ throw error;
118
+ }
119
+ logger$1.info("Activity {activityId} is enqueued.", {
120
+ activityId: activity.id?.href,
121
+ activity: json,
122
+ recipient
123
+ });
124
+ return "enqueued";
125
+ }
126
+ tracerProvider = tracerProvider ?? trace.getTracerProvider();
127
+ return await tracerProvider.getTracer(name, version).startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
128
+ const dispatched = inboxListeners?.dispatchWithClass(activity);
129
+ if (dispatched == null) {
130
+ logger$1.error("Unsupported activity type:\n{activity}", {
131
+ activity: json,
132
+ recipient
133
+ });
134
+ span$1.setStatus({
135
+ code: SpanStatusCode.UNSET,
136
+ message: `Unsupported activity type: ${getTypeId(activity).href}`
137
+ });
138
+ span$1.end();
139
+ return "unsupportedActivity";
140
+ }
141
+ const { class: cls, listener } = dispatched;
142
+ span$1.updateName(`activitypub.dispatch_inbox_listener ${cls.name}`);
143
+ try {
144
+ await listener(inboxContextFactory(recipient, json, activity?.id?.href, getTypeId(activity).href), activity);
145
+ } catch (error) {
146
+ try {
147
+ await inboxErrorHandler?.(ctx, error);
148
+ } catch (error$1) {
149
+ logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
150
+ error: error$1,
151
+ activityId: activity.id?.href,
152
+ activity: json,
153
+ recipient
154
+ });
155
+ }
156
+ logger$1.error("Failed to process the incoming activity {activityId}:\n{error}", {
157
+ error,
158
+ activityId: activity.id?.href,
159
+ activity: json,
160
+ recipient
161
+ });
162
+ span$1.setStatus({
163
+ code: SpanStatusCode.ERROR,
164
+ message: String(error)
165
+ });
166
+ span$1.end();
167
+ return "error";
168
+ }
169
+ if (cacheKey != null) await kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
170
+ logger$1.info("Activity {activityId} has been processed.", {
171
+ activityId: activity.id?.href,
172
+ activity: json,
173
+ recipient
174
+ });
175
+ span$1.end();
176
+ return "success";
177
+ });
119
178
  }
120
179
 
121
180
  //#endregion
122
- //#region nodeinfo/handler.ts
123
- /**
124
- * Handles a NodeInfo request. You would not typically call this function
125
- * directly, but instead use {@link Federation.handle} method.
126
- * @param request The NodeInfo request to handle.
127
- * @param parameters The parameters for handling the request.
128
- * @returns The response to the request.
129
- */
130
- async function handleNodeInfo(_request, { context: context$1, nodeInfoDispatcher }) {
131
- const promise = nodeInfoDispatcher(context$1);
132
- const json = nodeInfoToJson(promise instanceof Promise ? await promise : promise);
133
- return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\"" } });
181
+ //#region federation/router.ts
182
+ function cloneInnerRouter(router) {
183
+ const clone = new Router();
184
+ clone.nid = router.nid;
185
+ clone.fsm = cloneDeep(router.fsm);
186
+ clone.routeSet = new Set(router.routeSet);
187
+ clone.templateRouteMap = new Map(router.templateRouteMap);
188
+ clone.valueRouteMap = new Map(router.valueRouteMap);
189
+ clone.hierarchy = cloneDeep(router.hierarchy);
190
+ return clone;
134
191
  }
135
192
  /**
136
- * Handles a request to `/.well-known/nodeinfo`. You would not typically call
137
- * this function directly, but instead use {@link Federation.handle} method.
138
- * @param request The request to handle.
139
- * @param context The request context.
140
- * @returns The response to the request.
193
+ * URL router and constructor based on URI Template
194
+ * ([RFC 6570](https://tools.ietf.org/html/rfc6570)).
141
195
  */
142
- function handleNodeInfoJrd(_request, context$1) {
143
- const links = [];
144
- try {
145
- links.push({
146
- rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
147
- href: context$1.getNodeInfoUri().href,
148
- type: "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\""
149
- });
150
- } catch (e) {
151
- if (!(e instanceof RouterError)) throw e;
196
+ var Router$1 = class Router$1 {
197
+ #router;
198
+ #templates;
199
+ #templateStrings;
200
+ /**
201
+ * Whether to ignore trailing slashes when matching paths.
202
+ * @since 1.6.0
203
+ */
204
+ trailingSlashInsensitive;
205
+ /**
206
+ * Create a new {@link Router}.
207
+ * @param options Options for the router.
208
+ */
209
+ constructor(options = {}) {
210
+ this.#router = new Router();
211
+ this.#templates = {};
212
+ this.#templateStrings = {};
213
+ this.trailingSlashInsensitive = options.trailingSlashInsensitive ?? false;
214
+ }
215
+ clone() {
216
+ const clone = new Router$1({ trailingSlashInsensitive: this.trailingSlashInsensitive });
217
+ clone.#router = cloneInnerRouter(this.#router);
218
+ clone.#templates = { ...this.#templates };
219
+ clone.#templateStrings = { ...this.#templateStrings };
220
+ return clone;
221
+ }
222
+ /**
223
+ * Checks if a path name exists in the router.
224
+ * @param name The name of the path.
225
+ * @returns `true` if the path name exists, otherwise `false`.
226
+ */
227
+ has(name$1) {
228
+ return name$1 in this.#templates;
229
+ }
230
+ /**
231
+ * Adds a new path rule to the router.
232
+ * @param template The path pattern.
233
+ * @param name The name of the path.
234
+ * @returns The names of the variables in the path pattern.
235
+ */
236
+ add(template, name$1) {
237
+ if (!template.startsWith("/")) throw new RouterError("Path must start with a slash.");
238
+ const rule = this.#router.addTemplate(template, {}, name$1);
239
+ this.#templates[name$1] = parseTemplate(template);
240
+ this.#templateStrings[name$1] = template;
241
+ return new Set(rule.variables.map((v) => v.varname));
242
+ }
243
+ /**
244
+ * Resolves a path name and values from a URL, if any match.
245
+ * @param url The URL to resolve.
246
+ * @returns The name of the path and its values, if any match. Otherwise,
247
+ * `null`.
248
+ */
249
+ route(url) {
250
+ let match = this.#router.resolveURI(url);
251
+ if (match == null) {
252
+ if (!this.trailingSlashInsensitive) return null;
253
+ url = url.endsWith("/") ? url.replace(/\/+$/, "") : `${url}/`;
254
+ match = this.#router.resolveURI(url);
255
+ if (match == null) return null;
256
+ }
257
+ return {
258
+ name: match.matchValue,
259
+ template: this.#templateStrings[match.matchValue],
260
+ values: match.params
261
+ };
152
262
  }
153
- const jrd = { links };
154
- const response = new Response(JSON.stringify(jrd), { headers: { "Content-Type": "application/jrd+json" } });
155
- return Promise.resolve(response);
156
- }
157
-
158
- //#endregion
159
- //#region vocab/constants.ts
263
+ /**
264
+ * Constructs a URL/path from a path name and values.
265
+ * @param name The name of the path.
266
+ * @param values The values to expand the path with.
267
+ * @returns The URL/path, if the name exists. Otherwise, `null`.
268
+ */
269
+ build(name$1, values) {
270
+ if (name$1 in this.#templates) return this.#templates[name$1].expand(values);
271
+ return null;
272
+ }
273
+ };
160
274
  /**
161
- * The special public collection for [public addressing]. *Do not mutate this
162
- * object.*
163
- *
164
- * [public addressing]: https://www.w3.org/TR/activitypub/#public-addressing
165
- *
166
- * @since 0.7.0
275
+ * An error thrown by the {@link Router}.
167
276
  */
168
- const PUBLIC_COLLECTION = new URL("https://www.w3.org/ns/activitystreams#Public");
277
+ var RouterError = class extends Error {
278
+ /**
279
+ * Create a new {@link RouterError}.
280
+ * @param message The error message.
281
+ */
282
+ constructor(message) {
283
+ super(message);
284
+ this.name = "RouterError";
285
+ }
286
+ };
169
287
 
170
288
  //#endregion
171
- //#region webfinger/handler.ts
172
- const logger = getLogger([
173
- "fedify",
174
- "webfinger",
175
- "server"
176
- ]);
177
- /**
178
- * Handles a WebFinger request. You would not typically call this function
179
- * directly, but instead use {@link Federation.fetch} method.
180
- * @param request The WebFinger request to handle.
181
- * @param parameters The parameters for handling the request.
182
- * @returns The response to the request.
183
- */
184
- async function handleWebFinger(request, options) {
185
- if (options.tracer == null) return await handleWebFingerInternal(request, options);
186
- return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
187
- try {
188
- const response = await handleWebFingerInternal(request, options);
189
- span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
190
- return response;
191
- } catch (error) {
192
- span.setStatus({
193
- code: SpanStatusCode.ERROR,
194
- message: String(error)
289
+ //#region federation/builder.ts
290
+ var FederationBuilderImpl = class {
291
+ router;
292
+ actorCallbacks;
293
+ nodeInfoDispatcher;
294
+ objectCallbacks;
295
+ objectTypeIds;
296
+ inboxPath;
297
+ inboxCallbacks;
298
+ outboxCallbacks;
299
+ followingCallbacks;
300
+ followersCallbacks;
301
+ likedCallbacks;
302
+ featuredCallbacks;
303
+ featuredTagsCallbacks;
304
+ inboxListeners;
305
+ inboxErrorHandler;
306
+ sharedInboxKeyDispatcher;
307
+ constructor() {
308
+ this.router = new Router$1();
309
+ this.objectCallbacks = {};
310
+ this.objectTypeIds = {};
311
+ }
312
+ async build(options) {
313
+ const { FederationImpl: FederationImpl$1 } = await import("./middleware2.js");
314
+ const f = new FederationImpl$1(options);
315
+ const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
316
+ f.router = this.router.clone();
317
+ f.router.trailingSlashInsensitive = trailingSlashInsensitiveValue;
318
+ f._initializeRouter();
319
+ f.actorCallbacks = this.actorCallbacks == null ? void 0 : { ...this.actorCallbacks };
320
+ f.nodeInfoDispatcher = this.nodeInfoDispatcher;
321
+ f.objectCallbacks = { ...this.objectCallbacks };
322
+ f.objectTypeIds = { ...this.objectTypeIds };
323
+ f.inboxPath = this.inboxPath;
324
+ f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
325
+ f.outboxCallbacks = this.outboxCallbacks == null ? void 0 : { ...this.outboxCallbacks };
326
+ f.followingCallbacks = this.followingCallbacks == null ? void 0 : { ...this.followingCallbacks };
327
+ f.followersCallbacks = this.followersCallbacks == null ? void 0 : { ...this.followersCallbacks };
328
+ f.likedCallbacks = this.likedCallbacks == null ? void 0 : { ...this.likedCallbacks };
329
+ f.featuredCallbacks = this.featuredCallbacks == null ? void 0 : { ...this.featuredCallbacks };
330
+ f.featuredTagsCallbacks = this.featuredTagsCallbacks == null ? void 0 : { ...this.featuredTagsCallbacks };
331
+ f.inboxListeners = this.inboxListeners?.clone();
332
+ f.inboxErrorHandler = this.inboxErrorHandler;
333
+ f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
334
+ return f;
335
+ }
336
+ _getTracer() {
337
+ return trace.getTracer(name, version);
338
+ }
339
+ setActorDispatcher(path, dispatcher) {
340
+ if (this.router.has("actor")) throw new RouterError("Actor dispatcher already set.");
341
+ const variables = this.router.add(path, "actor");
342
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for actor dispatcher must have one variable: {identifier}");
343
+ if (variables.has("handle")) getLogger([
344
+ "fedify",
345
+ "federation",
346
+ "actor"
347
+ ]).warn("The {{handle}} variable in the actor dispatcher path is deprecated. Use {{identifier}} instead.");
348
+ const callbacks = { dispatcher: async (context$1, identifier) => {
349
+ const actor = await this._getTracer().startActiveSpan("activitypub.dispatch_actor", {
350
+ kind: SpanKind.SERVER,
351
+ attributes: { "fedify.actor.identifier": identifier }
352
+ }, async (span) => {
353
+ try {
354
+ const actor$1 = await dispatcher(context$1, identifier);
355
+ span.setAttribute("activitypub.actor.id", (actor$1?.id ?? context$1.getActorUri(identifier)).href);
356
+ if (actor$1 == null) span.setStatus({ code: SpanStatusCode.ERROR });
357
+ else span.setAttribute("activitypub.actor.type", getTypeId(actor$1).href);
358
+ return actor$1;
359
+ } catch (error) {
360
+ span.setStatus({
361
+ code: SpanStatusCode.ERROR,
362
+ message: String(error)
363
+ });
364
+ throw error;
365
+ } finally {
366
+ span.end();
367
+ }
195
368
  });
196
- throw error;
197
- } finally {
198
- span.end();
369
+ if (actor == null) return null;
370
+ const logger$1 = getLogger([
371
+ "fedify",
372
+ "federation",
373
+ "actor"
374
+ ]);
375
+ if (actor.id == null) logger$1.warn("Actor dispatcher returned an actor without an id property. Set the property with Context.getActorUri(identifier).");
376
+ else if (actor.id.href != context$1.getActorUri(identifier).href) logger$1.warn("Actor dispatcher returned an actor with an id property that does not match the actor URI. Set the property with Context.getActorUri(identifier).");
377
+ if (this.followingCallbacks != null && this.followingCallbacks.dispatcher != null) {
378
+ if (actor.followingId == null) logger$1.warn("You configured a following collection dispatcher, but the actor does not have a following property. Set the property with Context.getFollowingUri(identifier).");
379
+ else if (actor.followingId.href != context$1.getFollowingUri(identifier).href) logger$1.warn("You configured a following collection dispatcher, but the actor's following property does not match the following collection URI. Set the property with Context.getFollowingUri(identifier).");
380
+ }
381
+ if (this.followersCallbacks != null && this.followersCallbacks.dispatcher != null) {
382
+ if (actor.followersId == null) logger$1.warn("You configured a followers collection dispatcher, but the actor does not have a followers property. Set the property with Context.getFollowersUri(identifier).");
383
+ else if (actor.followersId.href != context$1.getFollowersUri(identifier).href) logger$1.warn("You configured a followers collection dispatcher, but the actor's followers property does not match the followers collection URI. Set the property with Context.getFollowersUri(identifier).");
384
+ }
385
+ if (this.outboxCallbacks != null && this.outboxCallbacks.dispatcher != null) {
386
+ if (actor?.outboxId == null) logger$1.warn("You configured an outbox collection dispatcher, but the actor does not have an outbox property. Set the property with Context.getOutboxUri(identifier).");
387
+ else if (actor.outboxId.href != context$1.getOutboxUri(identifier).href) logger$1.warn("You configured an outbox collection dispatcher, but the actor's outbox property does not match the outbox collection URI. Set the property with Context.getOutboxUri(identifier).");
388
+ }
389
+ if (this.likedCallbacks != null && this.likedCallbacks.dispatcher != null) {
390
+ if (actor?.likedId == null) logger$1.warn("You configured a liked collection dispatcher, but the actor does not have a liked property. Set the property with Context.getLikedUri(identifier).");
391
+ else if (actor.likedId.href != context$1.getLikedUri(identifier).href) logger$1.warn("You configured a liked collection dispatcher, but the actor's liked property does not match the liked collection URI. Set the property with Context.getLikedUri(identifier).");
392
+ }
393
+ if (this.featuredCallbacks != null && this.featuredCallbacks.dispatcher != null) {
394
+ if (actor?.featuredId == null) logger$1.warn("You configured a featured collection dispatcher, but the actor does not have a featured property. Set the property with Context.getFeaturedUri(identifier).");
395
+ else if (actor.featuredId.href != context$1.getFeaturedUri(identifier).href) logger$1.warn("You configured a featured collection dispatcher, but the actor's featured property does not match the featured collection URI. Set the property with Context.getFeaturedUri(identifier).");
396
+ }
397
+ if (this.featuredTagsCallbacks != null && this.featuredTagsCallbacks.dispatcher != null) {
398
+ if (actor?.featuredTagsId == null) logger$1.warn("You configured a featured tags collection dispatcher, but the actor does not have a featuredTags property. Set the property with Context.getFeaturedTagsUri(identifier).");
399
+ else if (actor.featuredTagsId.href != context$1.getFeaturedTagsUri(identifier).href) logger$1.warn("You configured a featured tags collection dispatcher, but the actor's featuredTags property does not match the featured tags collection URI. Set the property with Context.getFeaturedTagsUri(identifier).");
400
+ }
401
+ if (this.router.has("inbox")) {
402
+ if (actor.inboxId == null) logger$1.warn("You configured inbox listeners, but the actor does not have an inbox property. Set the property with Context.getInboxUri(identifier).");
403
+ else if (actor.inboxId.href != context$1.getInboxUri(identifier).href) logger$1.warn("You configured inbox listeners, but the actor's inbox property does not match the inbox URI. Set the property with Context.getInboxUri(identifier).");
404
+ if (actor.endpoints == null || actor.endpoints.sharedInbox == null) logger$1.warn("You configured inbox listeners, but the actor does not have a endpoints.sharedInbox property. Set the property with Context.getInboxUri().");
405
+ else if (actor.endpoints.sharedInbox.href != context$1.getInboxUri().href) logger$1.warn("You configured inbox listeners, but the actor's endpoints.sharedInbox property does not match the shared inbox URI. Set the property with Context.getInboxUri().");
406
+ }
407
+ if (callbacks.keyPairsDispatcher != null) {
408
+ if (actor.publicKeyId == null) logger$1.warn("You configured a key pairs dispatcher, but the actor does not have a publicKey property. Set the property with Context.getActorKeyPairs(identifier).");
409
+ if (actor.assertionMethodId == null) logger$1.warn("You configured a key pairs dispatcher, but the actor does not have an assertionMethod property. Set the property with Context.getActorKeyPairs(identifier).");
410
+ }
411
+ return actor;
412
+ } };
413
+ this.actorCallbacks = callbacks;
414
+ const setters = {
415
+ setKeyPairsDispatcher: (dispatcher$1) => {
416
+ callbacks.keyPairsDispatcher = (ctx, identifier) => this._getTracer().startActiveSpan("activitypub.dispatch_actor_key_pairs", {
417
+ kind: SpanKind.SERVER,
418
+ attributes: {
419
+ "activitypub.actor.id": ctx.getActorUri(identifier).href,
420
+ "fedify.actor.identifier": identifier
421
+ }
422
+ }, async (span) => {
423
+ try {
424
+ return await dispatcher$1(ctx, identifier);
425
+ } catch (e) {
426
+ span.setStatus({
427
+ code: SpanStatusCode.ERROR,
428
+ message: String(e)
429
+ });
430
+ throw e;
431
+ } finally {
432
+ span.end();
433
+ }
434
+ });
435
+ return setters;
436
+ },
437
+ mapHandle(mapper) {
438
+ callbacks.handleMapper = mapper;
439
+ return setters;
440
+ },
441
+ mapAlias(mapper) {
442
+ callbacks.aliasMapper = mapper;
443
+ return setters;
444
+ },
445
+ authorize(predicate) {
446
+ callbacks.authorizePredicate = predicate;
447
+ return setters;
448
+ }
449
+ };
450
+ return setters;
451
+ }
452
+ setNodeInfoDispatcher(path, dispatcher) {
453
+ if (this.router.has("nodeInfo")) throw new RouterError("NodeInfo dispatcher already set.");
454
+ if (this.router.add(path, "nodeInfo").size !== 0) throw new RouterError("Path for NodeInfo dispatcher must have no variables.");
455
+ this.nodeInfoDispatcher = dispatcher;
456
+ }
457
+ setObjectDispatcher(cls, path, dispatcher) {
458
+ const routeName = `object:${cls.typeId.href}`;
459
+ if (this.router.has(routeName)) throw new RouterError(`Object dispatcher for ${cls.name} already set.`);
460
+ const variables = this.router.add(path, routeName);
461
+ if (variables.size < 1) throw new RouterError("Path for object dispatcher must have at least one variable.");
462
+ const callbacks = {
463
+ dispatcher: (ctx, values) => {
464
+ return this._getTracer().startActiveSpan("activitypub.dispatch_object", {
465
+ kind: SpanKind.SERVER,
466
+ attributes: {
467
+ "fedify.object.type": cls.typeId.href,
468
+ ...globalThis.Object.fromEntries(globalThis.Object.entries(values).map(([k, v]) => [`fedify.object.values.${k}`, v]))
469
+ }
470
+ }, async (span) => {
471
+ try {
472
+ const object = await dispatcher(ctx, values);
473
+ span.setAttribute("activitypub.object.id", (object?.id ?? ctx.getObjectUri(cls, values)).href);
474
+ if (object == null) span.setStatus({ code: SpanStatusCode.ERROR });
475
+ else span.setAttribute("activitypub.object.type", getTypeId(object).href);
476
+ return object;
477
+ } catch (e) {
478
+ span.setStatus({
479
+ code: SpanStatusCode.ERROR,
480
+ message: String(e)
481
+ });
482
+ throw e;
483
+ } finally {
484
+ span.end();
485
+ }
486
+ });
487
+ },
488
+ parameters: variables
489
+ };
490
+ this.objectCallbacks[cls.typeId.href] = callbacks;
491
+ this.objectTypeIds[cls.typeId.href] = cls;
492
+ const setters = { authorize(predicate) {
493
+ callbacks.authorizePredicate = predicate;
494
+ return setters;
495
+ } };
496
+ return setters;
497
+ }
498
+ setInboxDispatcher(path, dispatcher) {
499
+ if (this.inboxCallbacks != null) throw new RouterError("Inbox dispatcher already set.");
500
+ if (this.router.has("inbox")) {
501
+ if (this.inboxPath !== path) throw new RouterError("Inbox dispatcher path must match inbox listener path.");
502
+ } else {
503
+ const variables = this.router.add(path, "inbox");
504
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for inbox dispatcher must have one variable: {identifier}");
505
+ if (variables.has("handle")) getLogger([
506
+ "fedify",
507
+ "federation",
508
+ "inbox"
509
+ ]).warn("The {{handle}} variable in the inbox dispatcher path is deprecated. Use {{identifier}} instead.");
510
+ this.inboxPath = path;
199
511
  }
200
- });
201
- }
202
- async function handleWebFingerInternal(request, { context: context$1, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span }) {
203
- if (actorDispatcher == null) return await onNotFound(request);
204
- const resource = context$1.url.searchParams.get("resource");
205
- if (resource == null) return new Response("Missing resource parameter.", { status: 400 });
206
- span?.setAttribute("webfinger.resource", resource);
207
- let resourceUrl;
208
- try {
209
- resourceUrl = new URL(resource);
210
- } catch (e) {
211
- if (e instanceof TypeError) return new Response("Invalid resource URL.", { status: 400 });
212
- throw e;
512
+ const callbacks = { dispatcher };
513
+ this.inboxCallbacks = callbacks;
514
+ const setters = {
515
+ setCounter(counter) {
516
+ callbacks.counter = counter;
517
+ return setters;
518
+ },
519
+ setFirstCursor(cursor) {
520
+ callbacks.firstCursor = cursor;
521
+ return setters;
522
+ },
523
+ setLastCursor(cursor) {
524
+ callbacks.lastCursor = cursor;
525
+ return setters;
526
+ },
527
+ authorize(predicate) {
528
+ callbacks.authorizePredicate = predicate;
529
+ return setters;
530
+ }
531
+ };
532
+ return setters;
213
533
  }
214
- span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
215
- if (actorDispatcher == null) {
216
- logger.error("Actor dispatcher is not set.");
217
- return await onNotFound(request);
534
+ setOutboxDispatcher(path, dispatcher) {
535
+ if (this.router.has("outbox")) throw new RouterError("Outbox dispatcher already set.");
536
+ const variables = this.router.add(path, "outbox");
537
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for outbox dispatcher must have one variable: {identifier}");
538
+ if (variables.has("handle")) getLogger([
539
+ "fedify",
540
+ "federation",
541
+ "outbox"
542
+ ]).warn("The {{handle}} variable in the outbox dispatcher path is deprecated. Use {{identifier}} instead.");
543
+ const callbacks = { dispatcher };
544
+ this.outboxCallbacks = callbacks;
545
+ const setters = {
546
+ setCounter(counter) {
547
+ callbacks.counter = counter;
548
+ return setters;
549
+ },
550
+ setFirstCursor(cursor) {
551
+ callbacks.firstCursor = cursor;
552
+ return setters;
553
+ },
554
+ setLastCursor(cursor) {
555
+ callbacks.lastCursor = cursor;
556
+ return setters;
557
+ },
558
+ authorize(predicate) {
559
+ callbacks.authorizePredicate = predicate;
560
+ return setters;
561
+ }
562
+ };
563
+ return setters;
218
564
  }
219
- async function mapUsernameToIdentifier(username) {
220
- if (actorHandleMapper == null) {
221
- logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
222
- return username;
223
- }
224
- const identifier$1 = await actorHandleMapper(context$1, username);
225
- if (identifier$1 == null) {
226
- logger.error("Actor {username} not found.", { username });
227
- return null;
228
- }
229
- return identifier$1;
565
+ setFollowingDispatcher(path, dispatcher) {
566
+ if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
567
+ const variables = this.router.add(path, "following");
568
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for following collection dispatcher must have one variable: {identifier}");
569
+ if (variables.has("handle")) getLogger([
570
+ "fedify",
571
+ "federation",
572
+ "collection"
573
+ ]).warn("The {{handle}} variable in the following collection dispatcher path is deprecated. Use {{identifier}} instead.");
574
+ const callbacks = { dispatcher };
575
+ this.followingCallbacks = callbacks;
576
+ const setters = {
577
+ setCounter(counter) {
578
+ callbacks.counter = counter;
579
+ return setters;
580
+ },
581
+ setFirstCursor(cursor) {
582
+ callbacks.firstCursor = cursor;
583
+ return setters;
584
+ },
585
+ setLastCursor(cursor) {
586
+ callbacks.lastCursor = cursor;
587
+ return setters;
588
+ },
589
+ authorize(predicate) {
590
+ callbacks.authorizePredicate = predicate;
591
+ return setters;
592
+ }
593
+ };
594
+ return setters;
230
595
  }
231
- let identifier = null;
232
- const uriParsed = context$1.parseUri(resourceUrl);
233
- if (uriParsed?.type != "actor") {
234
- const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
235
- if (match == null) {
236
- const result = await actorAliasMapper?.(context$1, resourceUrl);
237
- if (result == null) return await onNotFound(request);
238
- if ("identifier" in result) identifier = result.identifier;
239
- else identifier = await mapUsernameToIdentifier(result.username);
240
- } else {
241
- const portMatch = /:\d+$/.exec(match[2]);
242
- const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
243
- if (normalizedHost != context$1.url.host && normalizedHost != host) return await onNotFound(request);
244
- else {
245
- identifier = await mapUsernameToIdentifier(match[1]);
246
- resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
596
+ setFollowersDispatcher(path, dispatcher) {
597
+ if (this.router.has("followers")) throw new RouterError("Followers collection dispatcher already set.");
598
+ const variables = this.router.add(path, "followers");
599
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for followers collection dispatcher must have one variable: {identifier}");
600
+ if (variables.has("handle")) getLogger([
601
+ "fedify",
602
+ "federation",
603
+ "collection"
604
+ ]).warn("The {{handle}} variable in the followers collection dispatcher path is deprecated. Use {{identifier}} instead.");
605
+ const callbacks = { dispatcher };
606
+ this.followersCallbacks = callbacks;
607
+ const setters = {
608
+ setCounter(counter) {
609
+ callbacks.counter = counter;
610
+ return setters;
611
+ },
612
+ setFirstCursor(cursor) {
613
+ callbacks.firstCursor = cursor;
614
+ return setters;
615
+ },
616
+ setLastCursor(cursor) {
617
+ callbacks.lastCursor = cursor;
618
+ return setters;
619
+ },
620
+ authorize(predicate) {
621
+ callbacks.authorizePredicate = predicate;
622
+ return setters;
247
623
  }
248
- }
249
- } else identifier = uriParsed.identifier;
250
- if (identifier == null) return await onNotFound(request);
251
- const actor = await actorDispatcher(context$1, identifier);
252
- if (actor == null) {
253
- logger.error("Actor {identifier} not found.", { identifier });
254
- return await onNotFound(request);
624
+ };
625
+ return setters;
255
626
  }
256
- const links = [{
257
- rel: "self",
258
- href: context$1.getActorUri(identifier).href,
259
- type: "application/activity+json"
260
- }];
261
- for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
262
- rel: url.rel ?? "http://webfinger.net/rel/profile-page",
263
- href: url.href.href,
264
- type: url.mediaType == null ? void 0 : url.mediaType
265
- });
266
- else if (url instanceof URL) links.push({
267
- rel: "http://webfinger.net/rel/profile-page",
268
- href: url.href
269
- });
270
- for await (const image of actor.getIcons()) {
271
- if (image.url?.href == null) continue;
272
- const link = {
273
- rel: "http://webfinger.net/rel/avatar",
274
- href: image.url.href.toString()
627
+ setLikedDispatcher(path, dispatcher) {
628
+ if (this.router.has("liked")) throw new RouterError("Liked collection dispatcher already set.");
629
+ const variables = this.router.add(path, "liked");
630
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for liked collection dispatcher must have one variable: {identifier}");
631
+ if (variables.has("handle")) getLogger([
632
+ "fedify",
633
+ "federation",
634
+ "collection"
635
+ ]).warn("The {{handle}} variable in the liked collection dispatcher path is deprecated. Use {{identifier}} instead.");
636
+ const callbacks = { dispatcher };
637
+ this.likedCallbacks = callbacks;
638
+ const setters = {
639
+ setCounter(counter) {
640
+ callbacks.counter = counter;
641
+ return setters;
642
+ },
643
+ setFirstCursor(cursor) {
644
+ callbacks.firstCursor = cursor;
645
+ return setters;
646
+ },
647
+ setLastCursor(cursor) {
648
+ callbacks.lastCursor = cursor;
649
+ return setters;
650
+ },
651
+ authorize(predicate) {
652
+ callbacks.authorizePredicate = predicate;
653
+ return setters;
654
+ }
275
655
  };
276
- if (image.mediaType != null) link.type = image.mediaType;
277
- links.push(link);
656
+ return setters;
657
+ }
658
+ setFeaturedDispatcher(path, dispatcher) {
659
+ if (this.router.has("featured")) throw new RouterError("Featured collection dispatcher already set.");
660
+ const variables = this.router.add(path, "featured");
661
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for featured collection dispatcher must have one variable: {identifier}");
662
+ if (variables.has("handle")) getLogger([
663
+ "fedify",
664
+ "federation",
665
+ "collection"
666
+ ]).warn("The {{handle}} variable in the featured collection dispatcher path is deprecated. Use {{identifier}} instead.");
667
+ const callbacks = { dispatcher };
668
+ this.featuredCallbacks = callbacks;
669
+ const setters = {
670
+ setCounter(counter) {
671
+ callbacks.counter = counter;
672
+ return setters;
673
+ },
674
+ setFirstCursor(cursor) {
675
+ callbacks.firstCursor = cursor;
676
+ return setters;
677
+ },
678
+ setLastCursor(cursor) {
679
+ callbacks.lastCursor = cursor;
680
+ return setters;
681
+ },
682
+ authorize(predicate) {
683
+ callbacks.authorizePredicate = predicate;
684
+ return setters;
685
+ }
686
+ };
687
+ return setters;
688
+ }
689
+ setFeaturedTagsDispatcher(path, dispatcher) {
690
+ if (this.router.has("featuredTags")) throw new RouterError("Featured tags collection dispatcher already set.");
691
+ const variables = this.router.add(path, "featuredTags");
692
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for featured tags collection dispatcher must have one variable: {identifier}");
693
+ if (variables.has("handle")) getLogger([
694
+ "fedify",
695
+ "federation",
696
+ "collection"
697
+ ]).warn("The {{handle}} variable in the featured tags collection dispatcher path is deprecated. Use {{identifier}} instead.");
698
+ const callbacks = { dispatcher };
699
+ this.featuredTagsCallbacks = callbacks;
700
+ const setters = {
701
+ setCounter(counter) {
702
+ callbacks.counter = counter;
703
+ return setters;
704
+ },
705
+ setFirstCursor(cursor) {
706
+ callbacks.firstCursor = cursor;
707
+ return setters;
708
+ },
709
+ setLastCursor(cursor) {
710
+ callbacks.lastCursor = cursor;
711
+ return setters;
712
+ },
713
+ authorize(predicate) {
714
+ callbacks.authorizePredicate = predicate;
715
+ return setters;
716
+ }
717
+ };
718
+ return setters;
719
+ }
720
+ setInboxListeners(inboxPath, sharedInboxPath) {
721
+ if (this.inboxListeners != null) throw new RouterError("Inbox listeners already set.");
722
+ if (this.router.has("inbox")) {
723
+ if (this.inboxPath !== inboxPath) throw new RouterError("Inbox listener path must match inbox dispatcher path.");
724
+ } else {
725
+ const variables = this.router.add(inboxPath, "inbox");
726
+ if (variables.size !== 1 || !(variables.has("identifier") || variables.has("handle"))) throw new RouterError("Path for inbox must have one variable: {identifier}");
727
+ this.inboxPath = inboxPath;
728
+ if (variables.has("handle")) getLogger([
729
+ "fedify",
730
+ "federation",
731
+ "inbox"
732
+ ]).warn("The {{handle}} variable in the inbox path is deprecated. Use {{identifier}} instead.");
733
+ }
734
+ if (sharedInboxPath != null) {
735
+ if (this.router.add(sharedInboxPath, "sharedInbox").size !== 0) throw new RouterError("Path for shared inbox must have no variables.");
736
+ }
737
+ const listeners = this.inboxListeners = new InboxListenerSet();
738
+ const setters = {
739
+ on(type, listener) {
740
+ listeners.add(type, listener);
741
+ return setters;
742
+ },
743
+ onError: (handler) => {
744
+ this.inboxErrorHandler = handler;
745
+ return setters;
746
+ },
747
+ setSharedKeyDispatcher: (dispatcher) => {
748
+ this.sharedInboxKeyDispatcher = dispatcher;
749
+ return setters;
750
+ }
751
+ };
752
+ return setters;
753
+ }
754
+ };
755
+ /**
756
+ * Creates a new {@link FederationBuilder} instance.
757
+ * @returns A new {@link FederationBuilder} instance.
758
+ * @since 1.6.0
759
+ */
760
+ function createFederationBuilder() {
761
+ return new FederationBuilderImpl();
762
+ }
763
+
764
+ //#endregion
765
+ //#region federation/collection.ts
766
+ /**
767
+ * Calculates the [partial follower collection digest][1].
768
+ *
769
+ * [1]: https://w3id.org/fep/8fcf#partial-follower-collection-digest
770
+ * @param uris The URIs to calculate the digest. Duplicate URIs are ignored.
771
+ * @returns The digest.
772
+ */
773
+ async function digest(uris) {
774
+ const processed = /* @__PURE__ */ new Set();
775
+ const encoder = new TextEncoder();
776
+ const result = new Uint8Array(32);
777
+ for (const uri of uris) {
778
+ const u = uri instanceof URL ? uri.href : uri;
779
+ if (processed.has(u)) continue;
780
+ processed.add(u);
781
+ const encoded = encoder.encode(u);
782
+ const digest$1 = new Uint8Array(await crypto.subtle.digest("SHA-256", encoded));
783
+ for (let i = 0; i < 32; i++) result[i] ^= digest$1[i];
784
+ }
785
+ return result;
786
+ }
787
+ /**
788
+ * Builds [`Collection-Synchronization`][1] header content.
789
+ *
790
+ * [1]: https://w3id.org/fep/8fcf#the-collection-synchronization-http-header
791
+ *
792
+ * @param collectionId The sender's followers collection URI.
793
+ * @param actorIds The actor URIs to digest.
794
+ * @returns The header content.
795
+ */
796
+ async function buildCollectionSynchronizationHeader(collectionId, actorIds) {
797
+ const [anyActorId] = actorIds;
798
+ const baseUrl = new URL(anyActorId);
799
+ const url = new URL(collectionId);
800
+ url.searchParams.set("base-url", `${baseUrl.origin}/`);
801
+ return `collectionId="${collectionId}", url="${url}", digest="${encodeHex(await digest(actorIds))}"`;
802
+ }
803
+
804
+ //#endregion
805
+ //#region federation/keycache.ts
806
+ var KvKeyCache = class {
807
+ kv;
808
+ prefix;
809
+ options;
810
+ nullKeys;
811
+ constructor(kv, prefix, options = {}) {
812
+ this.kv = kv;
813
+ this.prefix = prefix;
814
+ this.nullKeys = /* @__PURE__ */ new Set();
815
+ this.options = options;
278
816
  }
279
- const aliases = [];
280
- if (resourceUrl.protocol != "acct:" && actor.preferredUsername != null) {
281
- aliases.push(`acct:${actor.preferredUsername}@${host ?? context$1.url.host}`);
282
- if (host != null && host !== context$1.url.host) aliases.push(`acct:${actor.preferredUsername}@${context$1.url.host}`);
817
+ async get(keyId) {
818
+ if (this.nullKeys.has(keyId.href)) return null;
819
+ const serialized = await this.kv.get([...this.prefix, keyId.href]);
820
+ if (serialized == null) return void 0;
821
+ try {
822
+ return await CryptographicKey.fromJsonLd(serialized, this.options);
823
+ } catch {
824
+ try {
825
+ return await Multikey.fromJsonLd(serialized, this.options);
826
+ } catch {
827
+ await this.kv.delete([...this.prefix, keyId.href]);
828
+ return;
829
+ }
830
+ }
283
831
  }
284
- if (resourceUrl.href !== context$1.getActorUri(identifier).href) aliases.push(context$1.getActorUri(identifier).href);
285
- if (resourceUrl.protocol === "acct:" && host != null && host !== context$1.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
286
- const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
287
- aliases.push(`acct:${username}@${host}`);
832
+ async set(keyId, key) {
833
+ if (key == null) {
834
+ this.nullKeys.add(keyId.href);
835
+ await this.kv.delete([...this.prefix, keyId.href]);
836
+ return;
837
+ }
838
+ this.nullKeys.delete(keyId.href);
839
+ const serialized = await key.toJsonLd(this.options);
840
+ await this.kv.set([...this.prefix, keyId.href], serialized);
288
841
  }
289
- const jrd = {
290
- subject: resourceUrl.href,
291
- aliases,
292
- links
293
- };
294
- return new Response(JSON.stringify(jrd), { headers: {
295
- "Content-Type": "application/jrd+json",
296
- "Access-Control-Allow-Origin": "*"
297
- } });
298
- }
842
+ };
299
843
 
300
844
  //#endregion
301
845
  //#region federation/negotiation.ts
@@ -364,18 +908,18 @@ function acceptsJsonLd(request) {
364
908
  return types.includes("application/activity+json") || types.includes("application/ld+json") || types.includes("application/json");
365
909
  }
366
910
  async function handleActor(request, { identifier, context: context$1, actorDispatcher, authorizePredicate, onNotFound, onNotAcceptable, onUnauthorized }) {
367
- const logger$2 = getLogger([
911
+ const logger$1 = getLogger([
368
912
  "fedify",
369
913
  "federation",
370
914
  "actor"
371
915
  ]);
372
916
  if (actorDispatcher == null) {
373
- logger$2.debug("Actor dispatcher is not set.", { identifier });
917
+ logger$1.debug("Actor dispatcher is not set.", { identifier });
374
918
  return await onNotFound(request);
375
919
  }
376
920
  const actor = await actorDispatcher(context$1, identifier);
377
921
  if (actor == null) {
378
- logger$2.debug("Actor {identifier} not found.", { identifier });
922
+ logger$1.debug("Actor {identifier} not found.", { identifier });
379
923
  return await onNotFound(request);
380
924
  }
381
925
  if (!acceptsJsonLd(request)) return await onNotAcceptable(request);
@@ -621,13 +1165,13 @@ async function handleInbox(request, options) {
621
1165
  });
622
1166
  }
623
1167
  async function handleInboxInternal(request, { recipient, context: ctx, inboxContextFactory, kv, kvPrefixes, queue, actorDispatcher, inboxListeners, inboxErrorHandler, onNotFound, signatureTimeWindow, skipSignatureVerification, tracerProvider }, span) {
624
- const logger$2 = getLogger([
1168
+ const logger$1 = getLogger([
625
1169
  "fedify",
626
1170
  "federation",
627
1171
  "inbox"
628
1172
  ]);
629
1173
  if (actorDispatcher == null) {
630
- logger$2.error("Actor dispatcher is not set.", { recipient });
1174
+ logger$1.error("Actor dispatcher is not set.", { recipient });
631
1175
  span.setStatus({
632
1176
  code: SpanStatusCode.ERROR,
633
1177
  message: "Actor dispatcher is not set."
@@ -635,7 +1179,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
635
1179
  return await onNotFound(request);
636
1180
  } else if (recipient != null) {
637
1181
  if (await actorDispatcher(ctx, recipient) == null) {
638
- logger$2.error("Actor {recipient} not found.", { recipient });
1182
+ logger$1.error("Actor {recipient} not found.", { recipient });
639
1183
  span.setStatus({
640
1184
  code: SpanStatusCode.ERROR,
641
1185
  message: `Actor ${recipient} not found.`
@@ -644,7 +1188,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
644
1188
  }
645
1189
  }
646
1190
  if (request.bodyUsed) {
647
- logger$2.error("Request body has already been read.", { recipient });
1191
+ logger$1.error("Request body has already been read.", { recipient });
648
1192
  span.setStatus({
649
1193
  code: SpanStatusCode.ERROR,
650
1194
  message: "Request body has already been read."
@@ -654,7 +1198,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
654
1198
  headers: { "Content-Type": "text/plain; charset=utf-8" }
655
1199
  });
656
1200
  } else if (request.body?.locked) {
657
- logger$2.error("Request body is locked.", { recipient });
1201
+ logger$1.error("Request body is locked.", { recipient });
658
1202
  span.setStatus({
659
1203
  code: SpanStatusCode.ERROR,
660
1204
  message: "Request body is locked."
@@ -668,14 +1212,14 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
668
1212
  try {
669
1213
  json = await request.clone().json();
670
1214
  } catch (error) {
671
- logger$2.error("Failed to parse JSON:\n{error}", {
1215
+ logger$1.error("Failed to parse JSON:\n{error}", {
672
1216
  recipient,
673
1217
  error
674
1218
  });
675
1219
  try {
676
1220
  await inboxErrorHandler?.(ctx, error);
677
1221
  } catch (error$1) {
678
- logger$2.error("An unexpected error occurred in inbox error handler:\n{error}", {
1222
+ logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
679
1223
  error: error$1,
680
1224
  activity: json,
681
1225
  recipient
@@ -701,7 +1245,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
701
1245
  });
702
1246
  } catch (error) {
703
1247
  if (error instanceof Error && error.name === "jsonld.SyntaxError") {
704
- logger$2.error("Failed to parse JSON-LD:\n{error}", {
1248
+ logger$1.error("Failed to parse JSON-LD:\n{error}", {
705
1249
  recipient,
706
1250
  error
707
1251
  });
@@ -715,13 +1259,13 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
715
1259
  const jsonWithoutSig = detachSignature(json);
716
1260
  let activity = null;
717
1261
  if (ldSigVerified) {
718
- logger$2.debug("Linked Data Signatures are verified.", {
1262
+ logger$1.debug("Linked Data Signatures are verified.", {
719
1263
  recipient,
720
1264
  json
721
1265
  });
722
1266
  activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
723
1267
  } else {
724
- logger$2.debug("Linked Data Signatures are not verified.", {
1268
+ logger$1.debug("Linked Data Signatures are not verified.", {
725
1269
  recipient,
726
1270
  json
727
1271
  });
@@ -733,7 +1277,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
733
1277
  tracerProvider
734
1278
  });
735
1279
  } catch (error) {
736
- logger$2.error("Failed to parse activity:\n{error}", {
1280
+ logger$1.error("Failed to parse activity:\n{error}", {
737
1281
  recipient,
738
1282
  activity: json,
739
1283
  error
@@ -741,7 +1285,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
741
1285
  try {
742
1286
  await inboxErrorHandler?.(ctx, error);
743
1287
  } catch (error$1) {
744
- logger$2.error("An unexpected error occurred in inbox error handler:\n{error}", {
1288
+ logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
745
1289
  error: error$1,
746
1290
  activity: json,
747
1291
  recipient
@@ -756,11 +1300,11 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
756
1300
  headers: { "Content-Type": "text/plain; charset=utf-8" }
757
1301
  });
758
1302
  }
759
- if (activity == null) logger$2.debug("Object Integrity Proofs are not verified.", {
1303
+ if (activity == null) logger$1.debug("Object Integrity Proofs are not verified.", {
760
1304
  recipient,
761
1305
  activity: json
762
1306
  });
763
- else logger$2.debug("Object Integrity Proofs are verified.", {
1307
+ else logger$1.debug("Object Integrity Proofs are verified.", {
764
1308
  recipient,
765
1309
  activity: json
766
1310
  });
@@ -776,7 +1320,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
776
1320
  tracerProvider
777
1321
  });
778
1322
  if (key == null) {
779
- logger$2.error("Failed to verify the request's HTTP Signatures.", { recipient });
1323
+ logger$1.error("Failed to verify the request's HTTP Signatures.", { recipient });
780
1324
  span.setStatus({
781
1325
  code: SpanStatusCode.ERROR,
782
1326
  message: `Failed to verify the request's HTTP Signatures.`
@@ -785,7 +1329,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
785
1329
  status: 401,
786
1330
  headers: { "Content-Type": "text/plain; charset=utf-8" }
787
1331
  });
788
- } else logger$2.debug("HTTP Signatures are verified.", { recipient });
1332
+ } else logger$1.debug("HTTP Signatures are verified.", { recipient });
789
1333
  httpSigKey = key;
790
1334
  }
791
1335
  activity = await Activity.fromJsonLd(jsonWithoutSig, ctx);
@@ -793,7 +1337,7 @@ async function handleInboxInternal(request, { recipient, context: ctx, inboxCont
793
1337
  if (activity.id != null) span.setAttribute("activitypub.activity.id", activity.id.href);
794
1338
  span.setAttribute("activitypub.activity.type", getTypeId(activity).href);
795
1339
  if (httpSigKey != null && !await doesActorOwnKey(activity, httpSigKey, ctx)) {
796
- logger$2.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
1340
+ logger$1.error("The signer ({keyId}) and the actor ({actorId}) do not match.", {
797
1341
  activity: json,
798
1342
  recipient,
799
1343
  keyId: httpSigKey.id?.href,
@@ -874,6 +1418,320 @@ async function respondWithObjectIfAcceptable(object, request, options) {
874
1418
  return response;
875
1419
  }
876
1420
 
1421
+ //#endregion
1422
+ //#region nodeinfo/handler.ts
1423
+ /**
1424
+ * Handles a NodeInfo request. You would not typically call this function
1425
+ * directly, but instead use {@link Federation.handle} method.
1426
+ * @param request The NodeInfo request to handle.
1427
+ * @param parameters The parameters for handling the request.
1428
+ * @returns The response to the request.
1429
+ */
1430
+ async function handleNodeInfo(_request, { context: context$1, nodeInfoDispatcher }) {
1431
+ const promise = nodeInfoDispatcher(context$1);
1432
+ const json = nodeInfoToJson(promise instanceof Promise ? await promise : promise);
1433
+ return new Response(JSON.stringify(json), { headers: { "Content-Type": "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\"" } });
1434
+ }
1435
+ /**
1436
+ * Handles a request to `/.well-known/nodeinfo`. You would not typically call
1437
+ * this function directly, but instead use {@link Federation.handle} method.
1438
+ * @param request The request to handle.
1439
+ * @param context The request context.
1440
+ * @returns The response to the request.
1441
+ */
1442
+ function handleNodeInfoJrd(_request, context$1) {
1443
+ const links = [];
1444
+ try {
1445
+ links.push({
1446
+ rel: "http://nodeinfo.diaspora.software/ns/schema/2.1",
1447
+ href: context$1.getNodeInfoUri().href,
1448
+ type: "application/json; profile=\"http://nodeinfo.diaspora.software/ns/schema/2.1#\""
1449
+ });
1450
+ } catch (e) {
1451
+ if (!(e instanceof RouterError)) throw e;
1452
+ }
1453
+ const jrd = { links };
1454
+ const response = new Response(JSON.stringify(jrd), { headers: { "Content-Type": "application/jrd+json" } });
1455
+ return Promise.resolve(response);
1456
+ }
1457
+
1458
+ //#endregion
1459
+ //#region webfinger/handler.ts
1460
+ const logger = getLogger([
1461
+ "fedify",
1462
+ "webfinger",
1463
+ "server"
1464
+ ]);
1465
+ /**
1466
+ * Handles a WebFinger request. You would not typically call this function
1467
+ * directly, but instead use {@link Federation.fetch} method.
1468
+ * @param request The WebFinger request to handle.
1469
+ * @param parameters The parameters for handling the request.
1470
+ * @returns The response to the request.
1471
+ */
1472
+ async function handleWebFinger(request, options) {
1473
+ if (options.tracer == null) return await handleWebFingerInternal(request, options);
1474
+ return await options.tracer.startActiveSpan("webfinger.handle", { kind: SpanKind.SERVER }, async (span) => {
1475
+ try {
1476
+ const response = await handleWebFingerInternal(request, options);
1477
+ span.setStatus({ code: response.ok ? SpanStatusCode.UNSET : SpanStatusCode.ERROR });
1478
+ return response;
1479
+ } catch (error) {
1480
+ span.setStatus({
1481
+ code: SpanStatusCode.ERROR,
1482
+ message: String(error)
1483
+ });
1484
+ throw error;
1485
+ } finally {
1486
+ span.end();
1487
+ }
1488
+ });
1489
+ }
1490
+ async function handleWebFingerInternal(request, { context: context$1, host, actorDispatcher, actorHandleMapper, actorAliasMapper, onNotFound, span }) {
1491
+ if (actorDispatcher == null) return await onNotFound(request);
1492
+ const resource = context$1.url.searchParams.get("resource");
1493
+ if (resource == null) return new Response("Missing resource parameter.", { status: 400 });
1494
+ span?.setAttribute("webfinger.resource", resource);
1495
+ let resourceUrl;
1496
+ try {
1497
+ resourceUrl = new URL(resource);
1498
+ } catch (e) {
1499
+ if (e instanceof TypeError) return new Response("Invalid resource URL.", { status: 400 });
1500
+ throw e;
1501
+ }
1502
+ span?.setAttribute("webfinger.resource.scheme", resourceUrl.protocol.replace(/:$/, ""));
1503
+ if (actorDispatcher == null) {
1504
+ logger.error("Actor dispatcher is not set.");
1505
+ return await onNotFound(request);
1506
+ }
1507
+ async function mapUsernameToIdentifier(username) {
1508
+ if (actorHandleMapper == null) {
1509
+ logger.error("No actor handle mapper is set; use the WebFinger username {username} as the actor's internal identifier.", { username });
1510
+ return username;
1511
+ }
1512
+ const identifier$1 = await actorHandleMapper(context$1, username);
1513
+ if (identifier$1 == null) {
1514
+ logger.error("Actor {username} not found.", { username });
1515
+ return null;
1516
+ }
1517
+ return identifier$1;
1518
+ }
1519
+ let identifier = null;
1520
+ const uriParsed = context$1.parseUri(resourceUrl);
1521
+ if (uriParsed?.type != "actor") {
1522
+ const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
1523
+ if (match == null) {
1524
+ const result = await actorAliasMapper?.(context$1, resourceUrl);
1525
+ if (result == null) return await onNotFound(request);
1526
+ if ("identifier" in result) identifier = result.identifier;
1527
+ else identifier = await mapUsernameToIdentifier(result.username);
1528
+ } else {
1529
+ const portMatch = /:\d+$/.exec(match[2]);
1530
+ const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
1531
+ if (normalizedHost != context$1.url.host && normalizedHost != host) return await onNotFound(request);
1532
+ else {
1533
+ identifier = await mapUsernameToIdentifier(match[1]);
1534
+ resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
1535
+ }
1536
+ }
1537
+ } else identifier = uriParsed.identifier;
1538
+ if (identifier == null) return await onNotFound(request);
1539
+ const actor = await actorDispatcher(context$1, identifier);
1540
+ if (actor == null) {
1541
+ logger.error("Actor {identifier} not found.", { identifier });
1542
+ return await onNotFound(request);
1543
+ }
1544
+ const links = [{
1545
+ rel: "self",
1546
+ href: context$1.getActorUri(identifier).href,
1547
+ type: "application/activity+json"
1548
+ }];
1549
+ for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
1550
+ rel: url.rel ?? "http://webfinger.net/rel/profile-page",
1551
+ href: url.href.href,
1552
+ type: url.mediaType == null ? void 0 : url.mediaType
1553
+ });
1554
+ else if (url instanceof URL) links.push({
1555
+ rel: "http://webfinger.net/rel/profile-page",
1556
+ href: url.href
1557
+ });
1558
+ for await (const image of actor.getIcons()) {
1559
+ if (image.url?.href == null) continue;
1560
+ const link = {
1561
+ rel: "http://webfinger.net/rel/avatar",
1562
+ href: image.url.href.toString()
1563
+ };
1564
+ if (image.mediaType != null) link.type = image.mediaType;
1565
+ links.push(link);
1566
+ }
1567
+ const aliases = [];
1568
+ if (resourceUrl.protocol != "acct:" && actor.preferredUsername != null) {
1569
+ aliases.push(`acct:${actor.preferredUsername}@${host ?? context$1.url.host}`);
1570
+ if (host != null && host !== context$1.url.host) aliases.push(`acct:${actor.preferredUsername}@${context$1.url.host}`);
1571
+ }
1572
+ if (resourceUrl.href !== context$1.getActorUri(identifier).href) aliases.push(context$1.getActorUri(identifier).href);
1573
+ if (resourceUrl.protocol === "acct:" && host != null && host !== context$1.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
1574
+ const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
1575
+ aliases.push(`acct:${username}@${host}`);
1576
+ }
1577
+ const jrd = {
1578
+ subject: resourceUrl.href,
1579
+ aliases,
1580
+ links
1581
+ };
1582
+ return new Response(JSON.stringify(jrd), { headers: {
1583
+ "Content-Type": "application/jrd+json",
1584
+ "Access-Control-Allow-Origin": "*"
1585
+ } });
1586
+ }
1587
+
1588
+ //#endregion
1589
+ //#region federation/retry.ts
1590
+ /**
1591
+ * Creates an exponential backoff retry policy. The delay between retries
1592
+ * starts at the `initialDelay` and is multiplied by the `factor` for each
1593
+ * subsequent retry, up to the `maxDelay`. The policy will give up after
1594
+ * `maxAttempts` attempts. The actual delay is randomized to avoid
1595
+ * synchronization (jitter).
1596
+ * @param options The options for the policy.
1597
+ * @returns The retry policy.
1598
+ * @since 0.12.0
1599
+ */
1600
+ function createExponentialBackoffPolicy(options = {}) {
1601
+ const initialDelay = Temporal.Duration.from(options.initialDelay ?? { seconds: 1 });
1602
+ const maxDelay = Temporal.Duration.from(options.maxDelay ?? { hours: 12 });
1603
+ const maxAttempts = options.maxAttempts ?? 10;
1604
+ const factor = options.factor ?? 2;
1605
+ const jitter = options.jitter ?? true;
1606
+ return ({ attempts }) => {
1607
+ if (attempts >= maxAttempts) return null;
1608
+ let milliseconds = initialDelay.total("millisecond");
1609
+ milliseconds *= factor ** attempts;
1610
+ if (jitter) {
1611
+ milliseconds *= 1 + Math.random();
1612
+ milliseconds = Math.round(milliseconds);
1613
+ }
1614
+ const delay$1 = Temporal.Duration.from({ milliseconds });
1615
+ return Temporal.Duration.compare(delay$1, maxDelay) > 0 ? maxDelay : delay$1;
1616
+ };
1617
+ }
1618
+
1619
+ //#endregion
1620
+ //#region federation/send.ts
1621
+ /**
1622
+ * Extracts the inbox URLs from recipients.
1623
+ * @param parameters The parameters to extract the inboxes.
1624
+ * See also {@link ExtractInboxesParameters}.
1625
+ * @returns The inboxes as a map of inbox URL to actor URIs.
1626
+ */
1627
+ function extractInboxes({ recipients, preferSharedInbox, excludeBaseUris }) {
1628
+ const inboxes = {};
1629
+ for (const recipient of recipients) {
1630
+ let inbox;
1631
+ let sharedInbox = false;
1632
+ if (preferSharedInbox && recipient.endpoints?.sharedInbox != null) {
1633
+ inbox = recipient.endpoints.sharedInbox;
1634
+ sharedInbox = true;
1635
+ } else inbox = recipient.inboxId;
1636
+ if (inbox != null && recipient.id != null) {
1637
+ if (excludeBaseUris != null && excludeBaseUris.some((u) => u.origin === inbox?.origin)) continue;
1638
+ inboxes[inbox.href] ??= {
1639
+ actorIds: /* @__PURE__ */ new Set(),
1640
+ sharedInbox
1641
+ };
1642
+ inboxes[inbox.href].actorIds.add(recipient.id.href);
1643
+ }
1644
+ }
1645
+ return inboxes;
1646
+ }
1647
+ /**
1648
+ * Sends an {@link Activity} to an inbox.
1649
+ *
1650
+ * @param parameters The parameters for sending the activity.
1651
+ * See also {@link SendActivityParameters}.
1652
+ * @throws {Error} If the activity fails to send.
1653
+ */
1654
+ function sendActivity(options) {
1655
+ const tracerProvider = options.tracerProvider ?? trace.getTracerProvider();
1656
+ return tracerProvider.getTracer(name, version).startActiveSpan("activitypub.send_activity", {
1657
+ kind: SpanKind.CLIENT,
1658
+ attributes: { "activitypub.shared_inbox": options.sharedInbox ?? false }
1659
+ }, async (span) => {
1660
+ if (options.activityId != null) span.setAttribute("activitypub.activity.id", options.activityId);
1661
+ if (options.activityType != null) span.setAttribute("activitypub.activity.type", options.activityType);
1662
+ try {
1663
+ await sendActivityInternal({
1664
+ ...options,
1665
+ tracerProvider
1666
+ });
1667
+ } catch (e) {
1668
+ span.setStatus({
1669
+ code: SpanStatusCode.ERROR,
1670
+ message: String(e)
1671
+ });
1672
+ throw e;
1673
+ } finally {
1674
+ span.end();
1675
+ }
1676
+ });
1677
+ }
1678
+ async function sendActivityInternal({ activity, activityId, keys, inbox, headers, specDeterminer, tracerProvider }) {
1679
+ const logger$1 = getLogger([
1680
+ "fedify",
1681
+ "federation",
1682
+ "outbox"
1683
+ ]);
1684
+ headers = new Headers(headers);
1685
+ headers.set("Content-Type", "application/activity+json");
1686
+ const request = new Request(inbox, {
1687
+ method: "POST",
1688
+ headers,
1689
+ body: JSON.stringify(activity)
1690
+ });
1691
+ let rsaKey = null;
1692
+ for (const key of keys) if (key.privateKey.algorithm.name === "RSASSA-PKCS1-v1_5") {
1693
+ rsaKey = key;
1694
+ break;
1695
+ }
1696
+ if (rsaKey == null) logger$1.warn("No supported key found to sign the request to {inbox}. The request will be sent without a signature. In order to sign the request, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
1697
+ inbox: inbox.href,
1698
+ keys: keys.map((pair) => ({
1699
+ keyId: pair.keyId.href,
1700
+ privateKey: pair.privateKey
1701
+ }))
1702
+ });
1703
+ let response;
1704
+ try {
1705
+ response = rsaKey == null ? await fetch(request) : await doubleKnock(request, rsaKey, {
1706
+ tracerProvider,
1707
+ specDeterminer
1708
+ });
1709
+ } catch (error) {
1710
+ logger$1.error("Failed to send activity {activityId} to {inbox}:\n{error}", {
1711
+ activityId,
1712
+ inbox: inbox.href,
1713
+ error
1714
+ });
1715
+ throw error;
1716
+ }
1717
+ if (!response.ok) {
1718
+ let error;
1719
+ try {
1720
+ error = await response.text();
1721
+ } catch (_) {
1722
+ error = "";
1723
+ }
1724
+ logger$1.error("Failed to send activity {activityId} to {inbox} ({status} {statusText}):\n{error}", {
1725
+ activityId,
1726
+ inbox: inbox.href,
1727
+ status: response.status,
1728
+ statusText: response.statusText,
1729
+ error
1730
+ });
1731
+ throw new Error(`Failed to send activity ${activityId} to ${inbox.href} (${response.status} ${response.statusText}):\n${error}`);
1732
+ }
1733
+ }
1734
+
877
1735
  //#endregion
878
1736
  //#region federation/middleware.ts
879
1737
  /**
@@ -910,7 +1768,7 @@ var FederationImpl = class extends FederationBuilderImpl {
910
1768
  tracerProvider;
911
1769
  constructor(options) {
912
1770
  super();
913
- const logger$2 = getLogger(["fedify", "federation"]);
1771
+ const logger$1 = getLogger(["fedify", "federation"]);
914
1772
  this.kv = options.kv;
915
1773
  this.kvPrefixes = {
916
1774
  activityIdempotence: ["_fedify", "activityIdempotence"],
@@ -967,7 +1825,7 @@ var FederationImpl = class extends FederationBuilderImpl {
967
1825
  if (options.documentLoader != null) {
968
1826
  if (options.documentLoaderFactory != null) throw new TypeError("Cannot set both documentLoader and documentLoaderFactory options at a time; use documentLoaderFactory only.");
969
1827
  this.documentLoaderFactory = () => options.documentLoader;
970
- logger$2.warn("The documentLoader option is deprecated; use documentLoaderFactory option instead.");
1828
+ logger$1.warn("The documentLoader option is deprecated; use documentLoaderFactory option instead.");
971
1829
  } else this.documentLoaderFactory = options.documentLoaderFactory ?? ((opts) => {
972
1830
  return kvCache({
973
1831
  loader: getDocumentLoader({
@@ -981,7 +1839,7 @@ var FederationImpl = class extends FederationBuilderImpl {
981
1839
  if (options.contextLoader != null) {
982
1840
  if (options.contextLoaderFactory != null) throw new TypeError("Cannot set both contextLoader and contextLoaderFactory options at a time; use contextLoaderFactory only.");
983
1841
  this.contextLoaderFactory = () => options.contextLoader;
984
- logger$2.warn("The contextLoader option is deprecated; use contextLoaderFactory option instead.");
1842
+ logger$1.warn("The contextLoader option is deprecated; use contextLoaderFactory option instead.");
985
1843
  } else this.contextLoaderFactory = options.contextLoaderFactory ?? this.documentLoaderFactory;
986
1844
  this.authenticatedDocumentLoaderFactory = options.authenticatedDocumentLoaderFactory ?? ((identity) => getAuthenticatedDocumentLoader(identity, {
987
1845
  allowPrivateAddress,
@@ -1007,24 +1865,24 @@ var FederationImpl = class extends FederationBuilderImpl {
1007
1865
  }
1008
1866
  async _startQueueInternal(ctxData, signal, queue) {
1009
1867
  if (this.inboxQueue == null && this.outboxQueue == null) return;
1010
- const logger$2 = getLogger([
1868
+ const logger$1 = getLogger([
1011
1869
  "fedify",
1012
1870
  "federation",
1013
1871
  "queue"
1014
1872
  ]);
1015
1873
  const promises = [];
1016
1874
  if (this.inboxQueue != null && (queue == null || queue === "inbox") && !this.inboxQueueStarted) {
1017
- logger$2.debug("Starting an inbox task worker.");
1875
+ logger$1.debug("Starting an inbox task worker.");
1018
1876
  this.inboxQueueStarted = true;
1019
1877
  promises.push(this.inboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
1020
1878
  }
1021
1879
  if (this.outboxQueue != null && this.outboxQueue !== this.inboxQueue && (queue == null || queue === "outbox") && !this.outboxQueueStarted) {
1022
- logger$2.debug("Starting an outbox task worker.");
1880
+ logger$1.debug("Starting an outbox task worker.");
1023
1881
  this.outboxQueueStarted = true;
1024
1882
  promises.push(this.outboxQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
1025
1883
  }
1026
1884
  if (this.fanoutQueue != null && this.fanoutQueue !== this.inboxQueue && this.fanoutQueue !== this.outboxQueue && (queue == null || queue === "fanout") && !this.fanoutQueueStarted) {
1027
- logger$2.debug("Starting a fanout task worker.");
1885
+ logger$1.debug("Starting a fanout task worker.");
1028
1886
  this.fanoutQueueStarted = true;
1029
1887
  promises.push(this.fanoutQueue.listen((msg) => this.processQueuedTask(ctxData, msg), { signal }));
1030
1888
  }
@@ -1123,7 +1981,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1123
1981
  });
1124
1982
  }
1125
1983
  async #listenOutboxMessage(_, message, span) {
1126
- const logger$2 = getLogger([
1984
+ const logger$1 = getLogger([
1127
1985
  "fedify",
1128
1986
  "federation",
1129
1987
  "outbox"
@@ -1172,34 +2030,34 @@ var FederationImpl = class extends FederationBuilderImpl {
1172
2030
  try {
1173
2031
  this.onOutboxError?.(error, activity);
1174
2032
  } catch (error$1) {
1175
- logger$2.error("An unexpected error occurred in onError handler:\n{error}", {
2033
+ logger$1.error("An unexpected error occurred in onError handler:\n{error}", {
1176
2034
  ...logData,
1177
2035
  error: error$1
1178
2036
  });
1179
2037
  }
1180
- const delay = this.outboxRetryPolicy({
2038
+ const delay$1 = this.outboxRetryPolicy({
1181
2039
  elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
1182
2040
  attempts: message.attempt
1183
2041
  });
1184
- if (delay != null) {
1185
- logger$2.error("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); retry...:\n{error}", {
2042
+ if (delay$1 != null) {
2043
+ logger$1.error("Failed to send activity {activityId} to {inbox} (attempt #{attempt}); retry...:\n{error}", {
1186
2044
  ...logData,
1187
2045
  error
1188
2046
  });
1189
2047
  await this.outboxQueue?.enqueue({
1190
2048
  ...message,
1191
2049
  attempt: message.attempt + 1
1192
- }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
1193
- } else logger$2.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
2050
+ }, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
2051
+ } else logger$1.error("Failed to send activity {activityId} to {inbox} after {attempt} attempts; giving up:\n{error}", {
1194
2052
  ...logData,
1195
2053
  error
1196
2054
  });
1197
2055
  return;
1198
2056
  }
1199
- logger$2.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
2057
+ logger$1.info("Successfully sent activity {activityId} to {inbox}.", { ...logData });
1200
2058
  }
1201
2059
  async #listenInboxMessage(ctxData, message, span) {
1202
- const logger$2 = getLogger([
2060
+ const logger$1 = getLogger([
1203
2061
  "fedify",
1204
2062
  "federation",
1205
2063
  "inbox"
@@ -1221,7 +2079,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1221
2079
  ];
1222
2080
  if (cacheKey != null) {
1223
2081
  if (await this.kv.get(cacheKey) === true) {
1224
- logger$2.debug("Activity {activityId} has already been processed.", {
2082
+ logger$1.debug("Activity {activityId} has already been processed.", {
1225
2083
  activityId: activity.id?.href,
1226
2084
  activity: message.activity,
1227
2085
  recipient: message.identifier
@@ -1232,7 +2090,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1232
2090
  await this._getTracer().startActiveSpan("activitypub.dispatch_inbox_listener", { kind: SpanKind.INTERNAL }, async (span$1) => {
1233
2091
  const dispatched = this.inboxListeners?.dispatchWithClass(activity);
1234
2092
  if (dispatched == null) {
1235
- logger$2.error("Unsupported activity type:\n{activity}", {
2093
+ logger$1.error("Unsupported activity type:\n{activity}", {
1236
2094
  activityId: activity.id?.href,
1237
2095
  activity: message.activity,
1238
2096
  recipient: message.identifier,
@@ -1253,7 +2111,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1253
2111
  try {
1254
2112
  await this.inboxErrorHandler?.(context$1, error);
1255
2113
  } catch (error$1) {
1256
- logger$2.error("An unexpected error occurred in inbox error handler:\n{error}", {
2114
+ logger$1.error("An unexpected error occurred in inbox error handler:\n{error}", {
1257
2115
  error: error$1,
1258
2116
  trial: message.attempt,
1259
2117
  activityId: activity.id?.href,
@@ -1261,12 +2119,12 @@ var FederationImpl = class extends FederationBuilderImpl {
1261
2119
  recipient: message.identifier
1262
2120
  });
1263
2121
  }
1264
- const delay = this.inboxRetryPolicy({
2122
+ const delay$1 = this.inboxRetryPolicy({
1265
2123
  elapsedTime: Temporal.Instant.from(message.started).until(Temporal.Now.instant()),
1266
2124
  attempts: message.attempt
1267
2125
  });
1268
- if (delay != null) {
1269
- logger$2.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
2126
+ if (delay$1 != null) {
2127
+ logger$1.error("Failed to process the incoming activity {activityId} (attempt #{attempt}); retry...:\n{error}", {
1270
2128
  error,
1271
2129
  attempt: message.attempt,
1272
2130
  activityId: activity.id?.href,
@@ -1276,8 +2134,8 @@ var FederationImpl = class extends FederationBuilderImpl {
1276
2134
  await this.inboxQueue?.enqueue({
1277
2135
  ...message,
1278
2136
  attempt: message.attempt + 1
1279
- }, { delay: Temporal.Duration.compare(delay, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay });
1280
- } else logger$2.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
2137
+ }, { delay: Temporal.Duration.compare(delay$1, { seconds: 0 }) < 0 ? Temporal.Duration.from({ seconds: 0 }) : delay$1 });
2138
+ } else logger$1.error("Failed to process the incoming activity {activityId} after {trial} attempts; giving up:\n{error}", {
1281
2139
  error,
1282
2140
  activityId: activity.id?.href,
1283
2141
  activity: message.activity,
@@ -1291,7 +2149,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1291
2149
  return;
1292
2150
  }
1293
2151
  if (cacheKey != null) await this.kv.set(cacheKey, true, { ttl: Temporal.Duration.from({ days: 1 }) });
1294
- logger$2.info("Activity {activityId} has been processed.", {
2152
+ logger$1.info("Activity {activityId} has been processed.", {
1295
2153
  activityId: activity.id?.href,
1296
2154
  activity: message.activity,
1297
2155
  recipient: message.identifier
@@ -1340,7 +2198,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1340
2198
  };
1341
2199
  }
1342
2200
  async sendActivity(keys, inboxes, activity, options) {
1343
- const logger$2 = getLogger([
2201
+ const logger$1 = getLogger([
1344
2202
  "fedify",
1345
2203
  "federation",
1346
2204
  "outbox"
@@ -1374,7 +2232,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1374
2232
  format: "compact",
1375
2233
  contextLoader
1376
2234
  });
1377
- if (rsaKey == null) logger$2.warn("No supported key found to create a Linked Data signature for the activity {activityId}. The activity will be sent without a Linked Data signature. In order to create a Linked Data signature, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
2235
+ if (rsaKey == null) logger$1.warn("No supported key found to create a Linked Data signature for the activity {activityId}. The activity will be sent without a Linked Data signature. In order to create a Linked Data signature, at least one RSASSA-PKCS1-v1_5 key must be provided.", {
1378
2236
  activityId,
1379
2237
  keys: keys.map((pair) => ({
1380
2238
  keyId: pair.keyId.href,
@@ -1385,7 +2243,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1385
2243
  contextLoader,
1386
2244
  tracerProvider: this.tracerProvider
1387
2245
  });
1388
- if (!proofCreated) logger$2.warn("No supported key found to create a proof for the activity {activityId}. The activity will be sent without a proof. In order to create a proof, at least one Ed25519 key must be provided.", {
2246
+ if (!proofCreated) logger$1.warn("No supported key found to create a proof for the activity {activityId}. The activity will be sent without a proof. In order to create a proof, at least one Ed25519 key must be provided.", {
1389
2247
  activityId,
1390
2248
  keys: keys.map((pair) => ({
1391
2249
  keyId: pair.keyId.href,
@@ -1393,11 +2251,11 @@ var FederationImpl = class extends FederationBuilderImpl {
1393
2251
  }))
1394
2252
  });
1395
2253
  if (immediate || this.outboxQueue == null) {
1396
- if (immediate) logger$2.debug("Sending activity immediately without queue since immediate option is set.", {
2254
+ if (immediate) logger$1.debug("Sending activity immediately without queue since immediate option is set.", {
1397
2255
  activityId: activity.id.href,
1398
2256
  activity: jsonLd
1399
2257
  });
1400
- else logger$2.debug("Sending activity immediately without queue since queue is not set.", {
2258
+ else logger$1.debug("Sending activity immediately without queue since queue is not set.", {
1401
2259
  activityId: activity.id.href,
1402
2260
  activity: jsonLd
1403
2261
  });
@@ -1416,7 +2274,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1416
2274
  await Promise.all(promises);
1417
2275
  return;
1418
2276
  }
1419
- logger$2.debug("Enqueuing activity {activityId} to send later.", {
2277
+ logger$1.debug("Enqueuing activity {activityId} to send later.", {
1420
2278
  activityId: activity.id.href,
1421
2279
  activity: jsonLd
1422
2280
  });
@@ -1455,7 +2313,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1455
2313
  const promises = messages.map((m) => outboxQueue.enqueue(m));
1456
2314
  const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
1457
2315
  if (errors.length > 0) {
1458
- logger$2.error("Failed to enqueue activity {activityId} to send later: {errors}", {
2316
+ logger$1.error("Failed to enqueue activity {activityId} to send later: {errors}", {
1459
2317
  activityId: activity.id.href,
1460
2318
  errors
1461
2319
  });
@@ -1465,7 +2323,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1465
2323
  } else try {
1466
2324
  await outboxQueue.enqueueMany(messages);
1467
2325
  } catch (error) {
1468
- logger$2.error("Failed to enqueue activity {activityId} to send later: {error}", {
2326
+ logger$1.error("Failed to enqueue activity {activityId} to send later: {error}", {
1469
2327
  activityId: activity.id.href,
1470
2328
  error
1471
2329
  });
@@ -1482,7 +2340,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1482
2340
  [ATTR_URL_FULL]: request.url
1483
2341
  }
1484
2342
  }, async (span) => {
1485
- const logger$2 = getLogger([
2343
+ const logger$1 = getLogger([
1486
2344
  "fedify",
1487
2345
  "federation",
1488
2346
  "http"
@@ -1501,7 +2359,7 @@ var FederationImpl = class extends FederationBuilderImpl {
1501
2359
  message: `${error}`
1502
2360
  });
1503
2361
  span.end();
1504
- logger$2.error("An error occurred while serving request {method} {url}: {error}", {
2362
+ logger$1.error("An error occurred while serving request {method} {url}: {error}", {
1505
2363
  method: request.method,
1506
2364
  url: request.url,
1507
2365
  error
@@ -1525,9 +2383,9 @@ var FederationImpl = class extends FederationBuilderImpl {
1525
2383
  url: request.url,
1526
2384
  status: response.status
1527
2385
  };
1528
- if (response.status >= 500) logger$2.error(logTpl, values);
1529
- else if (response.status >= 400) logger$2.warn(logTpl, values);
1530
- else logger$2.info(logTpl, values);
2386
+ if (response.status >= 500) logger$1.error(logTpl, values);
2387
+ else if (response.status >= 400) logger$1.warn(logTpl, values);
2388
+ else logger$1.info(logTpl, values);
1531
2389
  return response;
1532
2390
  });
1533
2391
  });
@@ -1844,13 +2702,13 @@ var ContextImpl = class ContextImpl {
1844
2702
  if (uri == null) return null;
1845
2703
  if (uri.origin !== this.origin && uri.origin !== this.canonicalOrigin) return null;
1846
2704
  const route = this.federation.router.route(uri.pathname);
1847
- const logger$2 = getLogger(["fedify", "federation"]);
2705
+ const logger$1 = getLogger(["fedify", "federation"]);
1848
2706
  if (route == null) return null;
1849
2707
  else if (route.name === "sharedInbox") return {
1850
2708
  type: "inbox",
1851
2709
  identifier: void 0,
1852
2710
  get handle() {
1853
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2711
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1854
2712
  }
1855
2713
  };
1856
2714
  const identifier = "identifier" in route.values ? route.values.identifier : route.values.handle;
@@ -1858,7 +2716,7 @@ var ContextImpl = class ContextImpl {
1858
2716
  type: "actor",
1859
2717
  identifier,
1860
2718
  get handle() {
1861
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2719
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1862
2720
  return identifier;
1863
2721
  }
1864
2722
  };
@@ -1874,7 +2732,7 @@ var ContextImpl = class ContextImpl {
1874
2732
  type: "inbox",
1875
2733
  identifier,
1876
2734
  get handle() {
1877
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2735
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1878
2736
  return identifier;
1879
2737
  }
1880
2738
  };
@@ -1882,7 +2740,7 @@ var ContextImpl = class ContextImpl {
1882
2740
  type: "outbox",
1883
2741
  identifier,
1884
2742
  get handle() {
1885
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2743
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1886
2744
  return identifier;
1887
2745
  }
1888
2746
  };
@@ -1890,7 +2748,7 @@ var ContextImpl = class ContextImpl {
1890
2748
  type: "following",
1891
2749
  identifier,
1892
2750
  get handle() {
1893
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2751
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1894
2752
  return identifier;
1895
2753
  }
1896
2754
  };
@@ -1898,7 +2756,7 @@ var ContextImpl = class ContextImpl {
1898
2756
  type: "followers",
1899
2757
  identifier,
1900
2758
  get handle() {
1901
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2759
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1902
2760
  return identifier;
1903
2761
  }
1904
2762
  };
@@ -1906,7 +2764,7 @@ var ContextImpl = class ContextImpl {
1906
2764
  type: "liked",
1907
2765
  identifier,
1908
2766
  get handle() {
1909
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2767
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1910
2768
  return identifier;
1911
2769
  }
1912
2770
  };
@@ -1914,7 +2772,7 @@ var ContextImpl = class ContextImpl {
1914
2772
  type: "featured",
1915
2773
  identifier,
1916
2774
  get handle() {
1917
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2775
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1918
2776
  return identifier;
1919
2777
  }
1920
2778
  };
@@ -1922,19 +2780,19 @@ var ContextImpl = class ContextImpl {
1922
2780
  type: "featuredTags",
1923
2781
  identifier,
1924
2782
  get handle() {
1925
- logger$2.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
2783
+ logger$1.warn("The ParseUriResult.handle property is deprecated; use ParseUriResult.identifier instead.");
1926
2784
  return identifier;
1927
2785
  }
1928
2786
  };
1929
2787
  return null;
1930
2788
  }
1931
2789
  async getActorKeyPairs(identifier) {
1932
- const logger$2 = getLogger([
2790
+ const logger$1 = getLogger([
1933
2791
  "fedify",
1934
2792
  "federation",
1935
2793
  "actor"
1936
2794
  ]);
1937
- if (this.invokedFromActorKeyPairsDispatcher != null) logger$2.warn("Context.getActorKeyPairs({getActorKeyPairsIdentifier}) method is invoked from the actor key pairs dispatcher ({actorKeyPairsDispatcherIdentifier}); this may cause an infinite loop.", {
2795
+ if (this.invokedFromActorKeyPairsDispatcher != null) logger$1.warn("Context.getActorKeyPairs({getActorKeyPairsIdentifier}) method is invoked from the actor key pairs dispatcher ({actorKeyPairsDispatcherIdentifier}); this may cause an infinite loop.", {
1938
2796
  getActorKeyPairsIdentifier: identifier,
1939
2797
  actorKeyPairsDispatcherIdentifier: this.invokedFromActorKeyPairsDispatcher.identifier
1940
2798
  });
@@ -1942,7 +2800,7 @@ var ContextImpl = class ContextImpl {
1942
2800
  try {
1943
2801
  keyPairs = await this.getKeyPairsFromIdentifier(identifier);
1944
2802
  } catch (_) {
1945
- logger$2.warn("No actor key pairs dispatcher registered.");
2803
+ logger$1.warn("No actor key pairs dispatcher registered.");
1946
2804
  return [];
1947
2805
  }
1948
2806
  const owner = this.getActorUri(identifier);
@@ -1966,7 +2824,7 @@ var ContextImpl = class ContextImpl {
1966
2824
  return result;
1967
2825
  }
1968
2826
  async getKeyPairsFromIdentifier(identifier) {
1969
- const logger$2 = getLogger([
2827
+ const logger$1 = getLogger([
1970
2828
  "fedify",
1971
2829
  "federation",
1972
2830
  "actor"
@@ -1977,7 +2835,7 @@ var ContextImpl = class ContextImpl {
1977
2835
  handle: identifier
1978
2836
  });
1979
2837
  if (path == null) {
1980
- logger$2.warn("No actor dispatcher registered.");
2838
+ logger$1.warn("No actor dispatcher registered.");
1981
2839
  return [];
1982
2840
  }
1983
2841
  const actorUri = new URL(path, this.canonicalOrigin);
@@ -1985,7 +2843,7 @@ var ContextImpl = class ContextImpl {
1985
2843
  ...this,
1986
2844
  invokedFromActorKeyPairsDispatcher: { identifier }
1987
2845
  }), identifier);
1988
- if (keyPairs.length < 1) logger$2.warn("No key pairs found for actor {identifier}.", { identifier });
2846
+ if (keyPairs.length < 1) logger$1.warn("No key pairs found for actor {identifier}.", { identifier });
1989
2847
  let i = 0;
1990
2848
  const result = [];
1991
2849
  for (const keyPair of keyPairs) {
@@ -2100,7 +2958,7 @@ var ContextImpl = class ContextImpl {
2100
2958
  });
2101
2959
  }
2102
2960
  async sendActivityInternal(sender, recipients, activity, options, span) {
2103
- const logger$2 = getLogger([
2961
+ const logger$1 = getLogger([
2104
2962
  "fedify",
2105
2963
  "federation",
2106
2964
  "outbox"
@@ -2114,7 +2972,7 @@ var ContextImpl = class ContextImpl {
2114
2972
  if ("username" in sender) username = sender.username;
2115
2973
  else {
2116
2974
  username = sender.handle;
2117
- logger$2.warn("The \"handle\" property for the sender parameter is deprecated; use \"identifier\" or \"username\" instead.", { sender });
2975
+ logger$1.warn("The \"handle\" property for the sender parameter is deprecated; use \"identifier\" or \"username\" instead.", { sender });
2118
2976
  }
2119
2977
  if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
2120
2978
  else {
@@ -2151,7 +3009,7 @@ var ContextImpl = class ContextImpl {
2151
3009
  for (const activityTransformer of this.federation.activityTransformers) activity = activityTransformer(activity, this);
2152
3010
  span?.setAttribute("activitypub.activity.id", activity?.id?.href ?? "");
2153
3011
  if (activity.actorId == null) {
2154
- logger$2.error("Activity {activityId} to send does not have an actor.", {
3012
+ logger$1.error("Activity {activityId} to send does not have an actor.", {
2155
3013
  activity,
2156
3014
  activityId: activity?.id?.href
2157
3015
  });
@@ -2162,7 +3020,7 @@ var ContextImpl = class ContextImpl {
2162
3020
  preferSharedInbox: options.preferSharedInbox,
2163
3021
  excludeBaseUris: options.excludeBaseUris
2164
3022
  });
2165
- logger$2.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
3023
+ logger$1.debug("Sending activity {activityId} to inboxes:\n{inboxes}", {
2166
3024
  inboxes: globalThis.Object.keys(inboxes),
2167
3025
  activityId: activity.id?.href,
2168
3026
  activity
@@ -2248,7 +3106,7 @@ var ContextImpl = class ContextImpl {
2248
3106
  });
2249
3107
  }
2250
3108
  async routeActivityInternal(recipient, activity, options = {}, span) {
2251
- const logger$2 = getLogger([
3109
+ const logger$1 = getLogger([
2252
3110
  "fedify",
2253
3111
  "federation",
2254
3112
  "inbox"
@@ -2262,12 +3120,12 @@ var ContextImpl = class ContextImpl {
2262
3120
  tracerProvider: options.tracerProvider ?? this.tracerProvider,
2263
3121
  keyCache
2264
3122
  }) == null) {
2265
- logger$2.debug("Object Integrity Proofs are not verified.", {
3123
+ logger$1.debug("Object Integrity Proofs are not verified.", {
2266
3124
  recipient,
2267
3125
  activity: json
2268
3126
  });
2269
3127
  if (activity.id == null) {
2270
- logger$2.debug("Activity is missing an ID; unable to fetch.", {
3128
+ logger$1.debug("Activity is missing an ID; unable to fetch.", {
2271
3129
  recipient,
2272
3130
  activity: json
2273
3131
  });
@@ -2275,26 +3133,26 @@ var ContextImpl = class ContextImpl {
2275
3133
  }
2276
3134
  const fetched = await this.lookupObject(activity.id, options);
2277
3135
  if (fetched == null) {
2278
- logger$2.debug("Failed to fetch the remote activity object {activityId}.", {
3136
+ logger$1.debug("Failed to fetch the remote activity object {activityId}.", {
2279
3137
  recipient,
2280
3138
  activity: json,
2281
3139
  activityId: activity.id.href
2282
3140
  });
2283
3141
  return false;
2284
3142
  } else if (!(fetched instanceof Activity)) {
2285
- logger$2.debug("Fetched object is not an Activity.", {
3143
+ logger$1.debug("Fetched object is not an Activity.", {
2286
3144
  recipient,
2287
3145
  activity: await fetched.toJsonLd({ contextLoader })
2288
3146
  });
2289
3147
  return false;
2290
3148
  } else if (fetched.id?.href !== activity.id.href) {
2291
- logger$2.debug("Fetched activity object has a different ID; failed to verify.", {
3149
+ logger$1.debug("Fetched activity object has a different ID; failed to verify.", {
2292
3150
  recipient,
2293
3151
  activity: await fetched.toJsonLd({ contextLoader })
2294
3152
  });
2295
3153
  return false;
2296
3154
  } else if (fetched.actorIds.length < 1) {
2297
- logger$2.debug("Fetched activity object is missing an actor; unable to verify.", {
3155
+ logger$1.debug("Fetched activity object is missing an actor; unable to verify.", {
2298
3156
  recipient,
2299
3157
  activity: await fetched.toJsonLd({ contextLoader })
2300
3158
  });
@@ -2302,15 +3160,15 @@ var ContextImpl = class ContextImpl {
2302
3160
  }
2303
3161
  const activityId = fetched.id;
2304
3162
  if (!fetched.actorIds.every((actor) => actor.origin === activityId.origin)) {
2305
- logger$2.debug("Fetched activity object has actors from different origins; unable to verify.", {
3163
+ logger$1.debug("Fetched activity object has actors from different origins; unable to verify.", {
2306
3164
  recipient,
2307
3165
  activity: await fetched.toJsonLd({ contextLoader })
2308
3166
  });
2309
3167
  return false;
2310
3168
  }
2311
- logger$2.debug("Successfully fetched the remote activity object {activityId}; ignore the original activity and use the fetched one, which is trustworthy.");
3169
+ logger$1.debug("Successfully fetched the remote activity object {activityId}; ignore the original activity and use the fetched one, which is trustworthy.");
2312
3170
  activity = fetched;
2313
- } else logger$2.debug("Object Integrity Proofs are verified.", {
3171
+ } else logger$1.debug("Object Integrity Proofs are verified.", {
2314
3172
  recipient,
2315
3173
  activity: json
2316
3174
  });
@@ -2454,7 +3312,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2454
3312
  });
2455
3313
  }
2456
3314
  async forwardActivityInternal(forwarder, recipients, options) {
2457
- const logger$2 = getLogger([
3315
+ const logger$1 = getLogger([
2458
3316
  "fedify",
2459
3317
  "federation",
2460
3318
  "inbox"
@@ -2468,7 +3326,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2468
3326
  if ("username" in forwarder) username = forwarder.username;
2469
3327
  else {
2470
3328
  username = forwarder.handle;
2471
- logger$2.warn("The \"handle\" property for the forwarder parameter is deprecated; use \"identifier\" or \"username\" instead.", { forwarder });
3329
+ logger$1.warn("The \"handle\" property for the forwarder parameter is deprecated; use \"identifier\" or \"username\" instead.", { forwarder });
2472
3330
  }
2473
3331
  if (this.federation.actorCallbacks?.handleMapper == null) identifier = username;
2474
3332
  else {
@@ -2492,7 +3350,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2492
3350
  }
2493
3351
  if (!hasProof) {
2494
3352
  if (options?.skipIfUnsigned) return;
2495
- logger$2.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.");
3353
+ logger$1.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.");
2496
3354
  }
2497
3355
  }
2498
3356
  if (recipients === "followers") {
@@ -2506,14 +3364,14 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2506
3364
  preferSharedInbox: options?.preferSharedInbox,
2507
3365
  excludeBaseUris: options?.excludeBaseUris
2508
3366
  });
2509
- logger$2.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
3367
+ logger$1.debug("Forwarding activity {activityId} to inboxes:\n{inboxes}", {
2510
3368
  inboxes: globalThis.Object.keys(inboxes),
2511
3369
  activityId: this.activityId,
2512
3370
  activity: this.activity
2513
3371
  });
2514
3372
  if (options?.immediate || this.federation.outboxQueue == null) {
2515
- if (options?.immediate) logger$2.debug("Forwarding activity immediately without queue since immediate option is set.");
2516
- else logger$2.debug("Forwarding activity immediately without queue since queue is not set.");
3373
+ if (options?.immediate) logger$1.debug("Forwarding activity immediately without queue since immediate option is set.");
3374
+ else logger$1.debug("Forwarding activity immediately without queue since queue is not set.");
2517
3375
  const promises = [];
2518
3376
  for (const inbox in inboxes) promises.push(sendActivity({
2519
3377
  keys,
@@ -2528,7 +3386,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2528
3386
  await Promise.all(promises);
2529
3387
  return;
2530
3388
  }
2531
- logger$2.debug("Enqueuing activity {activityId} to forward later.", {
3389
+ logger$1.debug("Enqueuing activity {activityId} to forward later.", {
2532
3390
  activityId: this.activityId,
2533
3391
  activity: this.activity
2534
3392
  });
@@ -2566,7 +3424,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2566
3424
  const promises = messages.map((m) => outboxQueue.enqueue(m));
2567
3425
  const errors = (await Promise.allSettled(promises)).filter((r) => r.status === "rejected").map((r) => r.reason);
2568
3426
  if (errors.length > 0) {
2569
- logger$2.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
3427
+ logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{errors}", {
2570
3428
  activityId: this.activityId,
2571
3429
  errors
2572
3430
  });
@@ -2576,7 +3434,7 @@ var InboxContextImpl = class InboxContextImpl extends ContextImpl {
2576
3434
  } else try {
2577
3435
  await outboxQueue.enqueueMany(messages);
2578
3436
  } catch (error) {
2579
- logger$2.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
3437
+ logger$1.error("Failed to enqueue activity {activityId} to forward later:\n{error}", {
2580
3438
  activityId: this.activityId,
2581
3439
  error
2582
3440
  });
@@ -2635,4 +3493,4 @@ function getRequestId(request) {
2635
3493
  }
2636
3494
 
2637
3495
  //#endregion
2638
- export { autoIdAssigner as _, createFederation as a, handleCollection as c, respondWithObject as d, respondWithObjectIfAcceptable as f, actorDehydrator as g, handleNodeInfoJrd as h, KvSpecDeterminer as i, handleInbox as l, handleNodeInfo as m, FederationImpl as n, acceptsJsonLd as o, handleWebFinger as p, InboxContextImpl as r, handleActor as s, ContextImpl as t, handleObject as u };
3496
+ export { createFederation as a, respondWithObjectIfAcceptable as c, createFederationBuilder as d, Router$1 as f, KvSpecDeterminer as i, buildCollectionSynchronizationHeader as l, FederationImpl as n, createExponentialBackoffPolicy as o, RouterError as p, InboxContextImpl as r, respondWithObject as s, ContextImpl as t, digest as u };