@fedify/fedify 2.2.6 → 2.2.8

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 (52) hide show
  1. package/README.md +5 -4
  2. package/dist/{builder-C-al99Vs.mjs → builder-Hr1c39Ha.mjs} +4 -2
  3. package/dist/{client-D_1QpnWt.mjs → client-dx126nMS.mjs} +63 -5
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{deno-B0LcALOm.mjs → deno-B_vGx2Ji.mjs} +1 -1
  6. package/dist/{docloader-DLAjdiZt.mjs → docloader-DixiMoIZ.mjs} +2 -2
  7. package/dist/federation/builder.test.mjs +13 -1
  8. package/dist/federation/handler.test.mjs +3 -3
  9. package/dist/federation/idempotency.test.mjs +2 -2
  10. package/dist/federation/middleware.test.mjs +6 -6
  11. package/dist/federation/mod.cjs +1 -1
  12. package/dist/federation/mod.js +1 -1
  13. package/dist/federation/send.test.mjs +3 -3
  14. package/dist/federation/temporal.test.mjs +1 -1
  15. package/dist/federation/webfinger.test.mjs +69 -7
  16. package/dist/{http-BkivuOe4.mjs → http-BlHW6_Oi.mjs} +2 -2
  17. package/dist/{http-C-9duWvR.js → http-C-FdpmAl.js} +1 -1
  18. package/dist/{http-NEN2KDMb.cjs → http-DLRpF855.cjs} +1 -1
  19. package/dist/{key-BcpJR7wT.mjs → key-6U1u5H_H.mjs} +1 -1
  20. package/dist/{kv-cache-8o7zAqEi.js → kv-cache-BjlL4Zk0.js} +1 -1
  21. package/dist/{kv-cache-9K6uVGgd.cjs → kv-cache-DKprbYof.cjs} +1 -1
  22. package/dist/{ld-DAZQsoKN.mjs → ld-DDBxn1rq.mjs} +2 -2
  23. package/dist/{middleware-C3JAusAj.mjs → middleware-BJM6vrlT.mjs} +46 -29
  24. package/dist/{middleware-BUiHyCQ9.cjs → middleware-Bj5UFOaT.cjs} +42 -23
  25. package/dist/{middleware-CeqmkLh1.cjs → middleware-DNJYfRV4.cjs} +1 -1
  26. package/dist/{middleware-Bk-ge16h.js → middleware-DOkcTiZ-.js} +41 -22
  27. package/dist/{middleware-BysI6Pqz.mjs → middleware-Ds3ujCKo.mjs} +1 -1
  28. package/dist/mod.cjs +5 -5
  29. package/dist/mod.js +5 -5
  30. package/dist/nodeinfo/client.test.mjs +61 -1
  31. package/dist/nodeinfo/handler.test.mjs +1 -1
  32. package/dist/nodeinfo/mod.cjs +1 -1
  33. package/dist/nodeinfo/mod.js +1 -1
  34. package/dist/{owner-CwVIBwY0.mjs → owner-vuyMTd_f.mjs} +2 -2
  35. package/dist/{proof-wrxzZi_u.mjs → proof-BGrdGhcb.mjs} +2 -2
  36. package/dist/{proof-C2en2Cmb.cjs → proof-BNGY2IU6.cjs} +1 -1
  37. package/dist/{proof-IyDe0jjt.js → proof-DA_wkPds.js} +1 -1
  38. package/dist/{send-DOlXF7S8.mjs → send-BvazsIXC.mjs} +2 -2
  39. package/dist/sig/http.test.mjs +2 -2
  40. package/dist/sig/key.test.mjs +1 -1
  41. package/dist/sig/ld.test.mjs +2 -2
  42. package/dist/sig/mod.cjs +2 -2
  43. package/dist/sig/mod.js +2 -2
  44. package/dist/sig/owner.test.mjs +1 -1
  45. package/dist/sig/proof.test.mjs +1 -1
  46. package/dist/{temporal-B13wkvUP.mjs → temporal-MdUFasAg.mjs} +1 -1
  47. package/dist/{types-CAY3OdLq.js → types-DTONwNqL.js} +63 -5
  48. package/dist/{types-KC4QAoxe.cjs → types-m8u9L86X.cjs} +62 -4
  49. package/dist/utils/docloader.test.mjs +2 -2
  50. package/dist/utils/mod.cjs +1 -1
  51. package/dist/utils/mod.js +1 -1
  52. package/package.json +6 -6
package/README.md CHANGED
@@ -193,7 +193,7 @@ This project exists thanks to all the people who contribute, donate, and sponsor
193
193
  it. We are grateful for their support. We would like to thank the following
194
194
  financial contributors:[^2]
195
195
 
196
- [^2]: Those lists are automatically updated every hour.
196
+ [^2]: Those lists are automatically updated twice a day.
197
197
 
198
198
  <!-- hongdown-disable -->
199
199
 
@@ -204,21 +204,22 @@ financial contributors:[^2]
204
204
  ### Corporate sponsors
205
205
 
206
206
  - [<img src="https://images.opencollective.com/ghost/avatar/128.png" width="64" height="64"> Ghost](https://ghost.org)
207
+ - [<img src="https://images.opencollective.com/altstore/avatar/128.png" width="64" height="64"> AltStore](https://altstore.io)
207
208
 
208
209
  ### Supporters
209
210
 
210
- - [Daniel Supernault](https://pixelfed.org/)
211
211
  - [tkgka](https://opencollective.com/tkgka)
212
212
  - [Blaine](https://opencollective.com/blaine)
213
213
  - [Erick González Aguilar](https://opencollective.com/erick-gonzalez-aguilar)
214
+ - [Daniel Supernault](https://pixelfed.org/)
214
215
 
215
216
  ### Backers
216
217
 
217
- Robin Riley, yamanoku, Encyclia, taye, okin, Andy Piper, box464, Evan Prodromou, Rafael Goulart, malte
218
+ Robin Riley, Encyclia, taye, box464, Evan Prodromou, okin, yamanoku, malte, Rafael Goulart, Andy Piper
218
219
 
219
220
  ### One-time donations
220
221
 
221
- Robin Riley, Markus P, Nils Bergmann, Rameez
222
+ Robin Riley, malte, Nils Bergmann, Markus P, Rameez
222
223
 
223
224
  <!-- /DO NOT EDIT -->
224
225
 
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-B0LcALOm.mjs";
5
+ import { n as version, t as name } from "./deno-B_vGx2Ji.mjs";
6
6
  import { t as ActivityListenerSet } from "./activity-listener-ell7W1s9.mjs";
7
7
  import { Tombstone, getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -58,7 +58,7 @@ var FederationBuilderImpl = class {
58
58
  this.collectionTypeIds = {};
59
59
  }
60
60
  async build(options) {
61
- const { FederationImpl } = await import("./middleware-BysI6Pqz.mjs");
61
+ const { FederationImpl } = await import("./middleware-Ds3ujCKo.mjs");
62
62
  const f = new FederationImpl(options);
63
63
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
64
64
  f.router = this.router.clone();
@@ -69,6 +69,8 @@ var FederationBuilderImpl = class {
69
69
  f.webFingerLinksDispatcher = this.webFingerLinksDispatcher;
70
70
  f.objectCallbacks = { ...this.objectCallbacks };
71
71
  f.objectTypeIds = { ...this.objectTypeIds };
72
+ f.collectionCallbacks = { ...this.collectionCallbacks };
73
+ f.collectionTypeIds = { ...this.collectionTypeIds };
72
74
  f.inboxPath = this.inboxPath;
73
75
  f.outboxPath = this.outboxPath;
74
76
  f.inboxCallbacks = this.inboxCallbacks == null ? void 0 : { ...this.inboxCallbacks };
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { getUserAgent } from "@fedify/vocab-runtime";
4
+ import { UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  //#region src/nodeinfo/client.ts
7
7
  const logger = getLogger([
@@ -9,15 +9,72 @@ const logger = getLogger([
9
9
  "nodeinfo",
10
10
  "client"
11
11
  ]);
12
+ const DEFAULT_MAX_REDIRECTION = 5;
13
+ /**
14
+ * Fetches a URL while guarding against SSRF. Before every request—including
15
+ * each redirect hop—the destination is validated with
16
+ * {@link validatePublicUrl}, so that loopback, link-local, and private
17
+ * addresses, as well as non-HTTP(S) schemes such as `data:`, are rejected.
18
+ * Redirects are followed manually with a cap, so that a public URL cannot
19
+ * bounce the request to an internal address.
20
+ * @param url The URL to fetch.
21
+ * @param headers The request headers.
22
+ * @returns The final response, or `null` if the URL is disallowed or there
23
+ * are too many redirects.
24
+ */
25
+ async function fetchPublicUrl(url, headers) {
26
+ let current = url instanceof URL ? url : new URL(url);
27
+ let redirected = 0;
28
+ while (true) {
29
+ try {
30
+ await validatePublicUrl(current.href);
31
+ } catch (e) {
32
+ if (e instanceof UrlError) {
33
+ logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
34
+ url: current.href,
35
+ error: e
36
+ });
37
+ return null;
38
+ }
39
+ throw e;
40
+ }
41
+ const response = await fetch(current, {
42
+ headers,
43
+ redirect: "manual"
44
+ });
45
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
46
+ redirected++;
47
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
48
+ logger.error("Too many redirections ({redirections}) while fetching {url}.", {
49
+ redirections: redirected,
50
+ url: current.href
51
+ });
52
+ return null;
53
+ }
54
+ const next = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? current : response.url);
55
+ if (next.protocol !== current.protocol) {
56
+ logger.error("Refused a redirect to a different protocol ({protocol} to {redirectedProtocol}) while fetching NodeInfo.", {
57
+ protocol: current.protocol,
58
+ redirectedProtocol: next.protocol
59
+ });
60
+ return null;
61
+ }
62
+ current = next;
63
+ continue;
64
+ }
65
+ return response;
66
+ }
67
+ }
12
68
  async function getNodeInfo(url, options = {}) {
13
69
  try {
14
70
  let nodeInfoUrl = url;
15
71
  if (!options.direct) {
16
72
  const wellKnownUrl = new URL("/.well-known/nodeinfo", url);
17
- const wellKnownResponse = await fetch(wellKnownUrl, { headers: {
73
+ const wellKnownResponse = await fetchPublicUrl(wellKnownUrl, {
18
74
  Accept: "application/json",
19
75
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
20
- } });
76
+ });
77
+ if (wellKnownResponse == null) return void 0;
21
78
  if (!wellKnownResponse.ok) {
22
79
  logger.error("Failed to fetch {url}: {status} {statusText}", {
23
80
  url: wellKnownUrl.href,
@@ -37,10 +94,11 @@ async function getNodeInfo(url, options = {}) {
37
94
  }
38
95
  nodeInfoUrl = link.href;
39
96
  }
40
- const response = await fetch(nodeInfoUrl, { headers: {
97
+ const response = await fetchPublicUrl(nodeInfoUrl, {
41
98
  Accept: "application/json",
42
99
  "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
43
- } });
100
+ });
101
+ if (response == null) return void 0;
44
102
  if (!response.ok) {
45
103
  logger.error("Failed to fetch NodeInfo document from {url}: {status} {statusText}", {
46
104
  url: nodeInfoUrl.toString(),
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
6
6
  import { t as assert } from "../assert-DikXweDx.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
8
- import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-C3JAusAj.mjs";
8
+ import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-BJM6vrlT.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Follow, Person } from "@fedify/vocab";
11
11
  //#region src/compat/transformers.test.ts
@@ -3,6 +3,6 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  //#region deno.json
5
5
  var name = "@fedify/fedify";
6
- var version = "2.2.6";
6
+ var version = "2.2.8";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { o as validateCryptoKey } from "./key-BcpJR7wT.mjs";
5
- import { n as doubleKnock } from "./http-BkivuOe4.mjs";
4
+ import { o as validateCryptoKey } from "./key-6U1u5H_H.mjs";
5
+ import { n as doubleKnock } from "./http-BlHW6_Oi.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -6,7 +6,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import { i as assertExists } from "../std__assert-CRDpx_HF.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
9
- import { n as createFederationBuilder } from "../builder-C-al99Vs.mjs";
9
+ import { n as createFederationBuilder } from "../builder-Hr1c39Ha.mjs";
10
10
  import { test } from "@fedify/fixture";
11
11
  import { Activity, Note, Person } from "@fedify/vocab";
12
12
  //#region src/federation/builder.test.ts
@@ -180,6 +180,18 @@ test("FederationBuilder", async (t) => {
180
180
  });
181
181
  builder.setCollectionDispatcher(strId, Note, "/string-id/{id}", dispatcher);
182
182
  });
183
+ await t.step("propagates custom collection dispatchers to the built federation", async () => {
184
+ const kv = new MemoryKvStore();
185
+ const builder = createFederationBuilder();
186
+ builder.setCollectionDispatcher("test", Note, "/c/{id}", () => ({ items: [] }));
187
+ const impl = await builder.build({
188
+ kv,
189
+ origin: "https://example.com"
190
+ });
191
+ assertExists(impl.collectionCallbacks["test"]);
192
+ assertEquals(impl.collectionTypeIds["test"], Note);
193
+ assertEquals((await impl.fetch(new Request("https://example.com/c/x", { headers: { accept: "application/activity+json" } }), { contextData: void 0 })).status, 200);
194
+ });
183
195
  });
184
196
  //#endregion
185
197
  export {};
@@ -8,11 +8,11 @@ import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
9
9
  import { t as assert } from "../assert-DikXweDx.mjs";
10
10
  import { r as parseAcceptSignature } from "../accept-CPkZzmGN.mjs";
11
- import { s as signRequest } from "../http-BkivuOe4.mjs";
11
+ import { s as signRequest } from "../http-BlHW6_Oi.mjs";
12
12
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-DGu1NFwu.mjs";
13
- import { a as compactJsonLd, p as signJsonLd } from "../ld-DAZQsoKN.mjs";
13
+ import { a as compactJsonLd, p as signJsonLd } from "../ld-DDBxn1rq.mjs";
14
14
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
15
- import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-C3JAusAj.mjs";
15
+ import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-BJM6vrlT.mjs";
16
16
  import { t as ActivityListenerSet } from "../activity-listener-ell7W1s9.mjs";
17
17
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
18
18
  import { Activity, Create, Note, Person, Tombstone } from "@fedify/vocab";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-CRDpx_HF.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
7
- import { r as signObject } from "../proof-wrxzZi_u.mjs";
7
+ import { r as signObject } from "../proof-BGrdGhcb.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
9
- import { o as createFederation } from "../middleware-C3JAusAj.mjs";
9
+ import { o as createFederation } from "../middleware-BJM6vrlT.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, Follow, Person } from "@fedify/vocab";
12
12
  //#region src/federation/idempotency.test.ts
@@ -11,14 +11,14 @@ import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
11
11
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
12
12
  import { t as assertStrictEquals } from "../assert_strict_equals-Dmjbg-bA.mjs";
13
13
  import { t as assert } from "../assert-DikXweDx.mjs";
14
- import { l as verifyRequest, s as signRequest } from "../http-BkivuOe4.mjs";
14
+ import { l as verifyRequest, s as signRequest } from "../http-BlHW6_Oi.mjs";
15
15
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
16
- import { t as getAuthenticatedDocumentLoader } from "../docloader-DLAjdiZt.mjs";
17
- import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-DAZQsoKN.mjs";
18
- import { t as doesActorOwnKey } from "../owner-CwVIBwY0.mjs";
19
- import { i as verifyObject, r as signObject } from "../proof-wrxzZi_u.mjs";
16
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-DixiMoIZ.mjs";
17
+ import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-DDBxn1rq.mjs";
18
+ import { t as doesActorOwnKey } from "../owner-vuyMTd_f.mjs";
19
+ import { i as verifyObject, r as signObject } from "../proof-BGrdGhcb.mjs";
20
20
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
21
- import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-C3JAusAj.mjs";
21
+ import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-BJM6vrlT.mjs";
22
22
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
23
23
  import * as vocab from "@fedify/vocab";
24
24
  import { Create, Offer, Person, getTypeId, lookupObject } from "@fedify/vocab";
@@ -2,7 +2,7 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("../chunk-DDcVe30Y.cjs");
5
- const require_middleware = require("../middleware-BUiHyCQ9.cjs");
5
+ const require_middleware = require("../middleware-Bj5UFOaT.cjs");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  //#region src/federation/kv.ts
8
8
  /**
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-Bk-ge16h.js";
3
+ import { a as createExponentialBackoffPolicy, c as buildCollectionSynchronizationHeader, d as Router, f as RouterError, i as SendActivityError, l as digest, o as respondWithObject, r as handleWebFinger, s as respondWithObjectIfAcceptable, t as createFederation, u as createFederationBuilder } from "../middleware-DOkcTiZ-.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -8,10 +8,10 @@ import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z
8
8
  import { t as assertInstanceOf } from "../assert_instance_of-C4Ri6VuN.mjs";
9
9
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
10
10
  import { t as assert } from "../assert-DikXweDx.mjs";
11
- import { l as verifyRequest } from "../http-BkivuOe4.mjs";
11
+ import { l as verifyRequest } from "../http-BlHW6_Oi.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DGu1NFwu.mjs";
13
- import { t as doesActorOwnKey } from "../owner-CwVIBwY0.mjs";
14
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-DOlXF7S8.mjs";
13
+ import { t as doesActorOwnKey } from "../owner-vuyMTd_f.mjs";
14
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-BvazsIXC.mjs";
15
15
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
17
17
  //#region src/federation/send.test.ts
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import "../std__assert-CRDpx_HF.mjs";
5
5
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
6
6
  import { t as assert } from "../assert-DikXweDx.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-B13wkvUP.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-MdUFasAg.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-Dk_tacqz.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-CRDpx_HF.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-rV3vodCc.mjs";
8
- import { o as createFederation, s as handleWebFinger } from "../middleware-C3JAusAj.mjs";
8
+ import { o as createFederation, s as handleWebFinger } from "../middleware-BJM6vrlT.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -257,7 +257,7 @@ test("handleWebFinger()", async (t) => {
257
257
  });
258
258
  onNotFoundCalled = null;
259
259
  const actorHandleMapper = (_ctx, username) => {
260
- return username === "foo" ? "someone" : username === "bar" ? "someone2" : null;
260
+ return username === "foo" ? "someone" : username === "bar" ? "someone2" : username === "qux" ? "someone2" : null;
261
261
  };
262
262
  await t.step("handle mapper", async () => {
263
263
  const u = new URL(url);
@@ -380,21 +380,83 @@ test("handleWebFinger()", async (t) => {
380
380
  assertEquals(response.status, 404);
381
381
  });
382
382
  await t.step("handleHost", async () => {
383
- const u = new URL(url);
384
- u.searchParams.set("resource", "acct:someone@example.com");
383
+ let u = new URL("https://ap.example.com/.well-known/webfinger");
384
+ u.searchParams.set("resource", "acct:someone@ap.example.com");
385
385
  let context = createContext(u);
386
386
  let request = context.request;
387
387
  let response = await handleWebFinger(request, {
388
388
  context,
389
- host: "handle.example.com",
389
+ host: "example.com",
390
390
  actorDispatcher,
391
391
  onNotFound
392
392
  });
393
393
  assertEquals(response.status, 200);
394
394
  assertEquals(await response.json(), {
395
- ...expected,
396
- aliases: [...expected.aliases, "acct:someone@handle.example.com"]
395
+ subject: "acct:someone@example.com",
396
+ aliases: ["https://ap.example.com/users/someone", "acct:someone@ap.example.com"],
397
+ links: [
398
+ {
399
+ href: "https://ap.example.com/users/someone",
400
+ rel: "self",
401
+ type: "application/activity+json"
402
+ },
403
+ {
404
+ href: "https://ap.example.com/@someone",
405
+ rel: "http://webfinger.net/rel/profile-page"
406
+ },
407
+ {
408
+ href: "https://ap.example.com/@someone",
409
+ rel: "alternate",
410
+ type: "text/html"
411
+ },
412
+ {
413
+ href: "https://ap.example.com/icon.jpg",
414
+ rel: "http://webfinger.net/rel/avatar",
415
+ type: "image/jpeg"
416
+ }
417
+ ]
418
+ });
419
+ u.searchParams.set("resource", "acct:qux@ap.example.com");
420
+ context = createContext(u);
421
+ request = context.request;
422
+ response = await handleWebFinger(request, {
423
+ context,
424
+ host: "example.com",
425
+ actorDispatcher,
426
+ actorHandleMapper,
427
+ onNotFound
397
428
  });
429
+ assertEquals(response.status, 200);
430
+ assertEquals(await response.json(), {
431
+ subject: "acct:bar@example.com",
432
+ aliases: [
433
+ "https://ap.example.com/users/someone2",
434
+ "acct:qux@ap.example.com",
435
+ "acct:bar@ap.example.com"
436
+ ],
437
+ links: [
438
+ {
439
+ href: "https://ap.example.com/users/someone2",
440
+ rel: "self",
441
+ type: "application/activity+json"
442
+ },
443
+ {
444
+ href: "https://ap.example.com/@someone2",
445
+ rel: "http://webfinger.net/rel/profile-page"
446
+ },
447
+ {
448
+ href: "https://ap.example.com/@someone2",
449
+ rel: "alternate",
450
+ type: "text/html"
451
+ },
452
+ {
453
+ href: "https://ap.example.com/icon.jpg",
454
+ rel: "http://webfinger.net/rel/avatar",
455
+ type: "image/jpeg"
456
+ }
457
+ ]
458
+ });
459
+ u = new URL(url);
398
460
  u.searchParams.set("resource", "acct:someone@handle.example.com");
399
461
  context = createContext(u);
400
462
  request = context.request;
@@ -1,9 +1,9 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-B0LcALOm.mjs";
4
+ import { n as version, t as name } from "./deno-B_vGx2Ji.mjs";
5
5
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPkZzmGN.mjs";
6
- import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-BcpJR7wT.mjs";
6
+ import { o as validateCryptoKey, r as fetchKeyDetailed } from "./key-6U1u5H_H.mjs";
7
7
  import { CryptographicKey } from "@fedify/vocab";
8
8
  import { SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { FetchError } from "@fedify/vocab-runtime";
@@ -10,7 +10,7 @@ import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } fro
10
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.2.6";
13
+ var version = "2.2.8";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
11
11
  let byte_encodings_base64 = require("byte-encodings/base64");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.2.6";
14
+ var version = "2.2.8";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -1,7 +1,7 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-B0LcALOm.mjs";
4
+ import { n as version, t as name } from "./deno-B_vGx2Ji.mjs";
5
5
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
7
  import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { d as validateCryptoKey, t as doubleKnock } from "./http-C-9duWvR.js";
3
+ import { d as validateCryptoKey, t as doubleKnock } from "./http-C-FdpmAl.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { curry } from "es-toolkit";
6
6
  import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-NEN2KDMb.cjs");
4
+ const require_http = require("./http-DLRpF855.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let es_toolkit = require("es-toolkit");
7
7
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-B0LcALOm.mjs";
5
- import { n as fetchKey, o as validateCryptoKey } from "./key-BcpJR7wT.mjs";
4
+ import { n as version, t as name } from "./deno-B_vGx2Ji.mjs";
5
+ import { n as fetchKey, o as validateCryptoKey } from "./key-6U1u5H_H.mjs";
6
6
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { getDocumentLoader } from "@fedify/vocab-runtime";
@@ -2,25 +2,25 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-B0LcALOm.mjs";
5
+ import { n as version, t as name } from "./deno-B_vGx2Ji.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-CPkZzmGN.mjs";
7
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-BcpJR7wT.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BkivuOe4.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-DLAjdiZt.mjs";
7
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-6U1u5H_H.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-BlHW6_Oi.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-DixiMoIZ.mjs";
10
10
  import { n as kvCache } from "./kv-cache-DYsF2MhP.mjs";
11
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-DAZQsoKN.mjs";
12
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-CwVIBwY0.mjs";
11
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-DDBxn1rq.mjs";
12
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-vuyMTd_f.mjs";
13
13
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-CNmZLixq.mjs";
14
- import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-wrxzZi_u.mjs";
15
- import { t as getNodeInfo } from "./client-D_1QpnWt.mjs";
14
+ import { i as verifyObject, n as hasProofLike, r as signObject } from "./proof-BGrdGhcb.mjs";
15
+ import { t as getNodeInfo } from "./client-dx126nMS.mjs";
16
16
  import { t as nodeInfoToJson } from "./types-J53Kw7so.mjs";
17
- import { t as FederationBuilderImpl } from "./builder-C-al99Vs.mjs";
17
+ import { t as FederationBuilderImpl } from "./builder-Hr1c39Ha.mjs";
18
18
  import { t as buildCollectionSynchronizationHeader } from "./collection-D-HqUuA2.mjs";
19
19
  import { t as KvKeyCache } from "./keycache-EGATflN-.mjs";
20
20
  import { t as acceptsJsonLd } from "./negotiation-SQvQgUqe.mjs";
21
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-B13wkvUP.mjs";
21
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-MdUFasAg.mjs";
22
22
  import { t as createExponentialBackoffPolicy } from "./retry-v_sGLH1d.mjs";
23
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-DOlXF7S8.mjs";
23
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-BvazsIXC.mjs";
24
24
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
25
25
  import { lookupWebFinger } from "@fedify/webfinger";
26
26
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -1697,6 +1697,24 @@ const logger = getLogger([
1697
1697
  "webfinger",
1698
1698
  "server"
1699
1699
  ]);
1700
+ function getWebFingerSubjectAndAliases({ resourceUrl, actorUri, preferredUsername, acctUsername, host, contextHost }) {
1701
+ const aliases = [];
1702
+ let subject = resourceUrl.href;
1703
+ if (resourceUrl.protocol != "acct:" && preferredUsername != null) {
1704
+ aliases.push(`acct:${preferredUsername}@${host ?? contextHost}`);
1705
+ if (host != null && host !== contextHost) aliases.push(`acct:${preferredUsername}@${contextHost}`);
1706
+ }
1707
+ if (resourceUrl.href !== actorUri.href) aliases.push(actorUri.href);
1708
+ if (resourceUrl.protocol === "acct:" && host != null && host !== contextHost && !resourceUrl.href.endsWith(`@${host}`)) {
1709
+ subject = `acct:${preferredUsername ?? acctUsername}@${host}`;
1710
+ aliases.push(resourceUrl.href);
1711
+ if (preferredUsername != null && preferredUsername !== "" && preferredUsername !== acctUsername) aliases.push(`acct:${preferredUsername}@${contextHost}`);
1712
+ }
1713
+ return {
1714
+ subject,
1715
+ aliases
1716
+ };
1717
+ }
1700
1718
  /**
1701
1719
  * Handles a WebFinger request. You would not typically call this function
1702
1720
  * directly, but instead use {@link Federation.fetch} method.
@@ -1751,6 +1769,7 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1751
1769
  return identifier;
1752
1770
  }
1753
1771
  let identifier = null;
1772
+ let acctUsername = null;
1754
1773
  const uriParsed = context.parseUri(resourceUrl);
1755
1774
  if (uriParsed?.type != "actor") {
1756
1775
  const match = /^acct:([^@]+)@([^@]+)$/.exec(resource);
@@ -1764,8 +1783,9 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1764
1783
  const normalizedHost = portMatch == null ? domainToASCII(match[2].toLowerCase()) : domainToASCII(match[2].substring(0, portMatch.index).toLowerCase()) + portMatch[0];
1765
1784
  if (normalizedHost != context.url.host && normalizedHost != host) return await onNotFound(request);
1766
1785
  else {
1767
- identifier = await mapUsernameToIdentifier(match[1]);
1768
- resourceUrl = new URL(`acct:${match[1]}@${normalizedHost}`);
1786
+ acctUsername = match[1];
1787
+ identifier = await mapUsernameToIdentifier(acctUsername);
1788
+ resourceUrl = new URL(`acct:${acctUsername}@${normalizedHost}`);
1769
1789
  }
1770
1790
  }
1771
1791
  } else identifier = uriParsed.identifier;
@@ -1779,9 +1799,10 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1779
1799
  status: 410,
1780
1800
  headers: { "Access-Control-Allow-Origin": "*" }
1781
1801
  });
1802
+ const actorUri = context.getActorUri(identifier);
1782
1803
  const links = [{
1783
1804
  rel: "self",
1784
- href: context.getActorUri(identifier).href,
1805
+ href: actorUri.href,
1785
1806
  type: "application/activity+json"
1786
1807
  }];
1787
1808
  for (const url of actor.urls) if (url instanceof Link && url.href != null) links.push({
@@ -1805,23 +1826,19 @@ async function handleWebFingerInternal(request, { context, host, actorDispatcher
1805
1826
  const customLinks = await webFingerLinksDispatcher(context, resourceUrl);
1806
1827
  if (customLinks != null) for (const link of customLinks) links.push(link);
1807
1828
  }
1808
- const aliases = [];
1809
- const preferredUsername = actor.preferredUsername;
1810
- if (resourceUrl.protocol != "acct:" && preferredUsername != null) {
1811
- aliases.push(`acct:${preferredUsername}@${host ?? context.url.host}`);
1812
- if (host != null && host !== context.url.host) aliases.push(`acct:${preferredUsername}@${context.url.host}`);
1813
- }
1814
- if (resourceUrl.href !== context.getActorUri(identifier).href) aliases.push(context.getActorUri(identifier).href);
1815
- if (resourceUrl.protocol === "acct:" && host != null && host !== context.url.host && !resourceUrl.href.endsWith(`@${host}`)) {
1816
- const username = resourceUrl.href.replace(/^acct:/, "").replace(/@.*$/, "");
1817
- aliases.push(`acct:${username}@${host}`);
1818
- }
1819
- const jrd = {
1820
- subject: resourceUrl.href,
1829
+ const { subject, aliases } = getWebFingerSubjectAndAliases({
1830
+ resourceUrl,
1831
+ actorUri,
1832
+ preferredUsername: actor.preferredUsername,
1833
+ acctUsername,
1834
+ host,
1835
+ contextHost: context.url.host
1836
+ });
1837
+ return new Response(JSON.stringify({
1838
+ subject,
1821
1839
  aliases,
1822
1840
  links
1823
- };
1824
- return new Response(JSON.stringify(jrd), { headers: {
1841
+ }), { headers: {
1825
1842
  "Content-Type": "application/jrd+json",
1826
1843
  "Access-Control-Allow-Origin": "*"
1827
1844
  } });