@fedify/fedify 2.4.0-dev.2155 → 2.4.0-dev.2157

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 (91) hide show
  1. package/dist/{builder-CxHALSwA.mjs → builder-CLaS4-yA.mjs} +2 -2
  2. package/dist/{client-DpnpgjyX.mjs → client-CEnNz8O7.mjs} +7 -3
  3. package/dist/compat/transformers.test.mjs +1 -1
  4. package/dist/{compound-proof-nhrXTRQO.mjs → compound-proof-D7K94KPz.mjs} +2 -2
  5. package/dist/{context-OrnjnuMq.mjs → context-B2rlkkyJ.mjs} +1 -1
  6. package/dist/{deno-YLLbRq06.mjs → deno-D1gcWWqB.mjs} +1 -1
  7. package/dist/{dist-BQvArojP.mjs → dist-DJQM4u7i.mjs} +2 -1
  8. package/dist/docloader-BMqr3hji.mjs +79 -0
  9. package/dist/federation/builder.test.mjs +1 -1
  10. package/dist/federation/compound-fanout.test.mjs +3 -3
  11. package/dist/federation/handler-compound-proof.test.mjs +4 -4
  12. package/dist/federation/handler.test.mjs +6 -6
  13. package/dist/federation/hashlink-media.test.mjs +2 -2
  14. package/dist/federation/idempotency.test.mjs +2 -2
  15. package/dist/federation/metrics.test.mjs +1 -1
  16. package/dist/federation/middleware.test.mjs +187 -10
  17. package/dist/federation/mod.cjs +1 -1
  18. package/dist/federation/mod.js +1 -1
  19. package/dist/federation/mq.test.mjs +98 -69
  20. package/dist/federation/outgoing-proof.test.mjs +5 -5
  21. package/dist/federation/portable-actor.test.mjs +3 -3
  22. package/dist/federation/portable-chain.test.mjs +3 -3
  23. package/dist/federation/portable-collection.test.mjs +4 -4
  24. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  25. package/dist/federation/portable-inbox.test.mjs +4 -4
  26. package/dist/federation/portable.test.mjs +3 -3
  27. package/dist/federation/send.test.mjs +84 -3
  28. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  29. package/dist/federation/tasks/tasks.test.mjs +4 -4
  30. package/dist/federation/temporal.test.mjs +1 -1
  31. package/dist/federation/webfinger.test.mjs +2 -2
  32. package/dist/{federation-D9TufmIO.cjs → federation-2e8XrN7q.cjs} +19 -5
  33. package/dist/{http-eqYXHRkz.cjs → http-BObQoB0j.cjs} +7 -3
  34. package/dist/{http-CXnNbRe_.mjs → http-CGcLv8rh.mjs} +9 -5
  35. package/dist/{http-Dk4O3WM0.js → http-Dgtt45-K.js} +7 -3
  36. package/dist/{key-BZ-Z4uez.mjs → key-CPM4r6tP.mjs} +2 -2
  37. package/dist/{kv-cache-3uKc0G76.js → kv-cache-BweyIZTb.js} +35 -9
  38. package/dist/{kv-cache-DzClEalR.cjs → kv-cache-D2fnB8qu.cjs} +34 -8
  39. package/dist/{kv-cache-DZr8HTel.mjs → kv-cache-bdPqTR1k.mjs} +1 -1
  40. package/dist/{ld-Djxsq6JR.mjs → ld-lU_ZqzsQ.mjs} +4 -4
  41. package/dist/{metrics-CBHhbHgG.mjs → metrics-B41pRPfd.mjs} +1 -1
  42. package/dist/{middleware-BDUR99k3.js → middleware-BYWyYjmX.js} +19 -5
  43. package/dist/{middleware-BahNnLfD.mjs → middleware-TrCVULgs.mjs} +1 -1
  44. package/dist/{middleware-DDa8pCUa.mjs → middleware-xdukzV3x.mjs} +15 -15
  45. package/dist/{mod-CZetqT5h.d.ts → mod-DaiBrWDW.d.ts} +2 -0
  46. package/dist/{mod-D4g9ffEn.d.cts → mod-Mt4aO3DX.d.cts} +2 -0
  47. package/dist/mod.cjs +5 -5
  48. package/dist/mod.d.cts +1 -1
  49. package/dist/mod.d.ts +1 -1
  50. package/dist/mod.js +5 -5
  51. package/dist/nodeinfo/client.test.mjs +49 -1
  52. package/dist/nodeinfo/handler.test.mjs +2 -2
  53. package/dist/nodeinfo/mod.cjs +1 -1
  54. package/dist/nodeinfo/mod.js +1 -1
  55. package/dist/{owner-OdWSE8am.mjs → owner-D63yPo_i.mjs} +3 -3
  56. package/dist/{portable-collection-wBWbuHOH.cjs → portable-collection-B8ojoQ50.cjs} +1 -1
  57. package/dist/{portable-collection-BCy06GiS.js → portable-collection-C3xEzYri.js} +1 -1
  58. package/dist/{portable-collection-DAV7Ho3m.mjs → portable-collection-_lZg-osX.mjs} +2 -2
  59. package/dist/{portable-key-BL6K88p2.mjs → portable-key-D8uWZF7b.mjs} +1 -1
  60. package/dist/{proof-vZ04IAjq.mjs → proof-C2YwD8a_.mjs} +4 -4
  61. package/dist/{send-ylV8KCLx.mjs → send-D9SGhxTB.mjs} +18 -4
  62. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  63. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  64. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  65. package/dist/sig/compound-proof.test.mjs +2 -2
  66. package/dist/sig/http.test.mjs +42 -2
  67. package/dist/sig/key.test.mjs +1 -1
  68. package/dist/sig/ld.test.mjs +5 -2
  69. package/dist/sig/mod.cjs +2 -2
  70. package/dist/sig/mod.js +2 -2
  71. package/dist/sig/owner.test.mjs +1 -1
  72. package/dist/sig/portable-collection.test.mjs +2 -2
  73. package/dist/sig/portable-key.test.mjs +6 -6
  74. package/dist/sig/proof.test.mjs +3 -3
  75. package/dist/{tasks-dbCShlCb.mjs → tasks-IG3pHfvZ.mjs} +1 -1
  76. package/dist/{temporal-BTD4f_mZ.mjs → temporal-q6FSekcN.mjs} +1 -1
  77. package/dist/testing/context.test.mjs +1 -1
  78. package/dist/testing/kv.test.mjs +3 -3
  79. package/dist/testing/mod.mjs +4 -4
  80. package/dist/{testing-CKaJhcA0.mjs → testing-C1itIjqW.mjs} +3 -3
  81. package/dist/{types-C_cNlNVK.js → types-_S2_MeLF.js} +7 -3
  82. package/dist/{types-Be2zbyeG.cjs → types-poIao40f.cjs} +7 -3
  83. package/dist/utils/docloader.test.mjs +297 -2
  84. package/dist/utils/kv-cache.test.mjs +1 -1
  85. package/dist/utils/mod.cjs +1 -1
  86. package/dist/utils/mod.d.cts +1 -1
  87. package/dist/utils/mod.d.ts +1 -1
  88. package/dist/utils/mod.js +1 -1
  89. package/dist/vocab/cjs.test.mjs +1 -1
  90. package/package.json +7 -7
  91. package/dist/docloader-vc-pSu9-.mjs +0 -53
@@ -1,7 +1,7 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-YLLbRq06.mjs";
4
+ import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
5
5
  import { c as isPortableId, r as getPortableDid, t as getCanonicalPortableId } from "./portable-key-id-Bg259c4Z.mjs";
6
6
  import { t as ActivityListenerSet } from "./activity-listener-C-BSbKI7.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
@@ -89,7 +89,7 @@ var FederationBuilderImpl = class {
89
89
  * specified.
90
90
  */
91
91
  async build(options) {
92
- const { FederationImpl } = await import("./middleware-BahNnLfD.mjs");
92
+ const { FederationImpl } = await import("./middleware-TrCVULgs.mjs");
93
93
  const f = new FederationImpl(options);
94
94
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
95
95
  f.router = this.router.clone();
@@ -93,8 +93,8 @@ const DEFAULT_MAX_REDIRECTION = 5;
93
93
  * bounce the request to an internal address.
94
94
  * @param url The URL to fetch.
95
95
  * @param headers The request headers.
96
- * @returns The final response, or `null` if the URL is disallowed or there
97
- * are too many redirects.
96
+ * @returns The final response, or `null` if the URL is disallowed, its
97
+ * hostname cannot be resolved, or there are too many redirects.
98
98
  */
99
99
  async function fetchPublicUrl(url, headers) {
100
100
  let current = url instanceof URL ? url : new URL(url);
@@ -104,7 +104,11 @@ async function fetchPublicUrl(url, headers) {
104
104
  await validatePublicUrl(current.href);
105
105
  } catch (e) {
106
106
  if (e instanceof UrlError) {
107
- logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
107
+ if (e.reason === "dns") logger.error("DNS lookup failed for {url}: {error}", {
108
+ url: current.href,
109
+ error: e
110
+ });
111
+ else logger.error("Refused to fetch a private or invalid URL {url}: {error}", {
108
112
  url: current.href,
109
113
  error: e
110
114
  });
@@ -5,7 +5,7 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertInstanceOf } from "../assert_instance_of-mrXWWNY-.mjs";
7
7
  import { t as assert } from "../assert-D1Eafmd_.mjs";
8
- import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-DDa8pCUa.mjs";
8
+ import { E as autoIdAssigner, T as actorDehydrator, n as FederationImpl } from "../middleware-xdukzV3x.mjs";
9
9
  import { Follow, Person } from "@fedify/vocab";
10
10
  import { test } from "@fedify/fixture";
11
11
  //#region src/compat/transformers.test.ts
@@ -3,8 +3,8 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { c as isPortableId, i as getRawCanonicalPortableId, t as getCanonicalPortableId } from "./portable-key-id-Bg259c4Z.mjs";
5
5
  import { n as preloadedOnlyDocumentLoader } from "./public-audience--klBIOk1.mjs";
6
- import { c as getNormalizationContextLoader } from "./ld-Djxsq6JR.mjs";
7
- import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-vZ04IAjq.mjs";
6
+ import { c as getNormalizationContextLoader } from "./ld-lU_ZqzsQ.mjs";
7
+ import { l as verifyPortableObjectProofPolicy, n as classifyFep2277CoreType, o as verifyMapLocalProof } from "./proof-C2YwD8a_.mjs";
8
8
  import { parseIri, preloadedContexts } from "@fedify/vocab-runtime";
9
9
  import jsonld from "@fedify/vocab-runtime/jsonld";
10
10
  //#region src/sig/compound-proof.ts
@@ -1,7 +1,7 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { t as mockDocumentLoader } from "./dist-BQvArojP.mjs";
4
+ import { t as mockDocumentLoader } from "./dist-DJQM4u7i.mjs";
5
5
  import { RouterError } from "@fedify/uri-template";
6
6
  import { lookupObject, traverseCollection } from "@fedify/vocab";
7
7
  import { lookupWebFinger } from "@fedify/webfinger";
@@ -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.4.0-dev.2155+2ccc5085";
6
+ var version = "2.4.0-dev.2157+b76e5888";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -6,6 +6,7 @@ import { configure, getConsoleSink, getLogger, reset } from "@logtape/logtape";
6
6
  import "@opentelemetry/sdk-trace-base";
7
7
  import "@opentelemetry/core";
8
8
  //#region ../fixture/dist/mod.js
9
+ const fixtureFetch = globalThis.fetch.bind(globalThis);
9
10
  const logger = getLogger(["fixture", "docloader"]);
10
11
  /**
11
12
  * A mock of the document loader. This does not make any actual HTTP requests
@@ -20,7 +21,7 @@ async function mockDocumentLoader(resource, _options) {
20
21
  if ("navigator" in globalThis && navigator.userAgent === "Cloudflare-Workers") {
21
22
  const testUrl = new URL(url);
22
23
  testUrl.hostname += ".test";
23
- const resp = await fetch(testUrl);
24
+ const resp = await fixtureFetch(testUrl);
24
25
  if (resp.ok) {
25
26
  const document = await resp.json();
26
27
  logger.debug("Successfully fetched fixture {resource}: {status} {statusText}\n{body}", {
@@ -0,0 +1,79 @@
1
+ import "temporal-polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { c as validateCryptoKey } from "./key-CPM4r6tP.mjs";
5
+ import { n as doubleKnock } from "./http-CGcLv8rh.mjs";
6
+ import { getLogger } from "@logtape/logtape";
7
+ import { curry } from "es-toolkit";
8
+ import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
9
+ //#region src/utils/docloader.ts
10
+ const logger = getLogger([
11
+ "fedify",
12
+ "utils",
13
+ "docloader"
14
+ ]);
15
+ const DEFAULT_MAX_REDIRECTION = 20;
16
+ /**
17
+ * Gets an authenticated {@link DocumentLoader} for the given identity.
18
+ * Note that an authenticated document loader intentionally does not cache
19
+ * the fetched documents.
20
+ * At most 20 HTTP redirects and alternate document links are followed in total
21
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
22
+ * @param identity The identity to get the document loader for.
23
+ * The actor's key pair.
24
+ * @param options The options for the document loader.
25
+ * @returns The authenticated document loader.
26
+ * @throws {TypeError} If the key is invalid or unsupported.
27
+ * @since 0.4.0
28
+ */
29
+ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
30
+ validateCryptoKey(identity.privateKey);
31
+ async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
32
+ options?.signal?.throwIfAborted();
33
+ let currentUrl = new URL(url).href;
34
+ await validateUrl(currentUrl, options);
35
+ visited.add(currentUrl);
36
+ const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
37
+ function follow(nextUrl) {
38
+ options?.signal?.throwIfAborted();
39
+ if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
40
+ if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
41
+ redirected++;
42
+ }
43
+ const response = await doubleKnock(originalRequest, identity, {
44
+ maxRedirection,
45
+ specDeterminer,
46
+ log: curry(logRequest)(logger),
47
+ tracerProvider,
48
+ signal: options?.signal,
49
+ validateRedirect: async (nextUrl) => {
50
+ follow(nextUrl);
51
+ await validateUrl(nextUrl, options);
52
+ visited.add(nextUrl);
53
+ currentUrl = nextUrl;
54
+ }
55
+ });
56
+ return getRemoteDocument(currentUrl, response, (alternateUrl) => {
57
+ follow(alternateUrl);
58
+ return load(alternateUrl, options, redirected, visited);
59
+ }, options);
60
+ }
61
+ async function validateUrl(url, options) {
62
+ if (!allowPrivateAddress) try {
63
+ await validatePublicUrl(url);
64
+ } catch (error) {
65
+ if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
66
+ url,
67
+ error
68
+ });
69
+ else logger[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
70
+ url,
71
+ error
72
+ });
73
+ throw error;
74
+ }
75
+ }
76
+ return (url, options) => load(url, options);
77
+ }
78
+ //#endregion
79
+ export { getAuthenticatedDocumentLoader as t };
@@ -6,7 +6,7 @@ import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import { t as assertExists } from "../assert_exists-cffSpJ7X.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-BYITOvPO.mjs";
8
8
  import "../std__assert-DvMDRPes.mjs";
9
- import { r as createFederationBuilder } from "../builder-CxHALSwA.mjs";
9
+ import { r as createFederationBuilder } from "../builder-CLaS4-yA.mjs";
10
10
  import { DisallowedOperatorError, DisallowedVarSpecModifierError, DuplicateRouteVariableError, RouteTemplateOptionsNotMatchedError, RouterError } from "@fedify/uri-template";
11
11
  import { Activity, Note, Person } from "@fedify/vocab";
12
12
  import { test } from "@fedify/fixture";
@@ -6,11 +6,11 @@ import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
8
  import { t as assert } from "../assert-D1Eafmd_.mjs";
9
- import { n as exportJwk } from "../key-BZ-Z4uez.mjs";
9
+ import { n as exportJwk } from "../key-CPM4r6tP.mjs";
10
10
  import { r as normalizeOutgoingActivityJsonLd } from "../outgoing-jsonld-4IJqPYos.mjs";
11
- import { a as signObject, d as verifyProof } from "../proof-vZ04IAjq.mjs";
11
+ import { a as signObject, d as verifyProof } from "../proof-C2YwD8a_.mjs";
12
12
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
13
- import { n as FederationImpl } from "../middleware-DDa8pCUa.mjs";
13
+ import { n as FederationImpl } from "../middleware-xdukzV3x.mjs";
14
14
  import { Create, DataIntegrityProof, Note } from "@fedify/vocab";
15
15
  import { mockDocumentLoader, test } from "@fedify/fixture";
16
16
  import { exportDidKey, parseIri } from "@fedify/vocab-runtime";
@@ -1,14 +1,14 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { r as createRequestContext, t as createInboxContext } from "../context-OrnjnuMq.mjs";
4
+ import { r as createRequestContext, t as createInboxContext } from "../context-B2rlkkyJ.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import "../std__assert-DvMDRPes.mjs";
8
- import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-Djxsq6JR.mjs";
9
- import { l as signRequest } from "../http-CXnNbRe_.mjs";
8
+ import { a as compactJsonLd, m as verifyCompactJsonLd, p as signJsonLd } from "../ld-lU_ZqzsQ.mjs";
9
+ import { l as signRequest } from "../http-CGcLv8rh.mjs";
10
10
  import { a as rsaPrivateKey3, c as rsaPublicKey3, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
11
- import { f as handleInbox, o as createFederation } from "../middleware-DDa8pCUa.mjs";
11
+ import { f as handleInbox, o as createFederation } from "../middleware-xdukzV3x.mjs";
12
12
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
13
13
  import { t as map_local_create_note_default } from "../map-local-create-note-B0SeXJms.mjs";
14
14
  import { Create, CryptographicKey, Multikey, Person, Update } from "@fedify/vocab";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-OrnjnuMq.mjs";
4
+ import { n as createOutboxContext, r as createRequestContext, t as createInboxContext } from "../context-B2rlkkyJ.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
7
7
  import { t as assertGreaterOrEqual } from "../assert_greater_or_equal-BlafOF4-.mjs";
@@ -10,12 +10,12 @@ import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
12
  import { r as parseAcceptSignature } from "../accept-CPVqki78.mjs";
13
- import { o as generateCryptoKeyPair } from "../key-BZ-Z4uez.mjs";
14
- import { a as compactJsonLd, p as signJsonLd } from "../ld-Djxsq6JR.mjs";
15
- import { a as signObject } from "../proof-vZ04IAjq.mjs";
16
- import { l as signRequest } from "../http-CXnNbRe_.mjs";
13
+ import { o as generateCryptoKeyPair } from "../key-CPM4r6tP.mjs";
14
+ import { a as compactJsonLd, p as signJsonLd } from "../ld-lU_ZqzsQ.mjs";
15
+ import { a as signObject } from "../proof-C2YwD8a_.mjs";
16
+ import { l as signRequest } from "../http-CGcLv8rh.mjs";
17
17
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-DIJNHW-Y.mjs";
18
- import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-DDa8pCUa.mjs";
18
+ import { d as handleCustomCollection, f as handleInbox, g as respondWithObjectIfAcceptable, h as respondWithObject, l as handleActor, m as handleOutbox, o as createFederation, p as handleObject, u as handleCollection } from "../middleware-xdukzV3x.mjs";
19
19
  import { t as ActivityListenerSet } from "../activity-listener-C-BSbKI7.mjs";
20
20
  import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
21
21
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
@@ -10,8 +10,8 @@ import "../std__assert-DvMDRPes.mjs";
10
10
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
11
11
  import { t as assert } from "../assert-D1Eafmd_.mjs";
12
12
  import { r as ed25519PublicKey } from "../keys-DIJNHW-Y.mjs";
13
- import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-DDa8pCUa.mjs";
14
- import { r as createFederationBuilder } from "../builder-CxHALSwA.mjs";
13
+ import { o as createFederation, x as parseHashlinkGatewayRequest } from "../middleware-xdukzV3x.mjs";
14
+ import { r as createFederationBuilder } from "../builder-CLaS4-yA.mjs";
15
15
  import { n as RouterError } from "../router-u5O5KCQr.mjs";
16
16
  import { Note } from "@fedify/vocab";
17
17
  import { createTestMeterProvider, mockDocumentLoader, test } from "@fedify/fixture";
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
6
6
  import "../std__assert-DvMDRPes.mjs";
7
- import { a as signObject } from "../proof-vZ04IAjq.mjs";
7
+ import { a as signObject } from "../proof-C2YwD8a_.mjs";
8
8
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
9
- import { o as createFederation } from "../middleware-DDa8pCUa.mjs";
9
+ import { o as createFederation } from "../middleware-xdukzV3x.mjs";
10
10
  import { Create, Follow, Person } from "@fedify/vocab";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  //#region src/federation/idempotency.test.ts
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
5
5
  import "../std__assert-DvMDRPes.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
7
- import { _ as recordKeyLookup, a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-CBHhbHgG.mjs";
7
+ import { _ as recordKeyLookup, a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, m as recordDocumentFetch, p as recordDocumentCache, r as getFederationMetrics, t as classifyFetchError, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "../metrics-B41pRPfd.mjs";
8
8
  import { createTestMeterProvider, test } from "@fedify/fixture";
9
9
  import { FetchError } from "@fedify/vocab-runtime";
10
10
  import { DataPointType, MeterProvider, MetricReader } from "@opentelemetry/sdk-metrics";
@@ -4,8 +4,8 @@ globalThis.addEventListener = () => {};
4
4
  import { t as esm_default } from "../esm-BHXXx4Wx.mjs";
5
5
  import { t as MemoryKvStore } from "../kv-DCHkaJ9-.mjs";
6
6
  import { t as ManualClockKvStore } from "../kv-DBRWVx3Q.mjs";
7
- import { a as numberSchema, r as envelopeSchema, t as MockQueue } from "../tasks-dbCShlCb.mjs";
8
- import "../testing-CKaJhcA0.mjs";
7
+ import { a as numberSchema, r as envelopeSchema, t as MockQueue } from "../tasks-IG3pHfvZ.mjs";
8
+ import "../testing-C1itIjqW.mjs";
9
9
  import { t as assertEquals } from "../assert_equals-Z6IcEbWU.mjs";
10
10
  import { t as assertExists } from "../assert_exists-cffSpJ7X.mjs";
11
11
  import { t as assertFalse } from "../assert_false-CPdRnc3E.mjs";
@@ -16,14 +16,14 @@ import "../std__assert-DvMDRPes.mjs";
16
16
  import { t as assertRejects } from "../assert_rejects-CTptdYck.mjs";
17
17
  import { t as assertStrictEquals } from "../assert_strict_equals-CYvBmkpC.mjs";
18
18
  import { t as assert } from "../assert-D1Eafmd_.mjs";
19
- import { g as recordInboxActivity } from "../metrics-CBHhbHgG.mjs";
20
- import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-Djxsq6JR.mjs";
21
- import { a as signObject, s as verifyObject } from "../proof-vZ04IAjq.mjs";
22
- import { d as verifyRequest, l as signRequest } from "../http-CXnNbRe_.mjs";
19
+ import { g as recordInboxActivity } from "../metrics-B41pRPfd.mjs";
20
+ import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-lU_ZqzsQ.mjs";
21
+ import { a as signObject, s as verifyObject } from "../proof-C2YwD8a_.mjs";
22
+ import { d as verifyRequest, l as signRequest } from "../http-CGcLv8rh.mjs";
23
23
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-DIJNHW-Y.mjs";
24
- import { t as getAuthenticatedDocumentLoader } from "../docloader-vc-pSu9-.mjs";
25
- import { S as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-DDa8pCUa.mjs";
26
- import { t as doesActorOwnKey } from "../owner-OdWSE8am.mjs";
24
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-BMqr3hji.mjs";
25
+ import { S as handleBenchmarkTrigger, i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-xdukzV3x.mjs";
26
+ import { t as doesActorOwnKey } from "../owner-D63yPo_i.mjs";
27
27
  import { t as CircuitBreaker } from "../circuit-breaker-C5NqoLBJ.mjs";
28
28
  import { t as TaskCodec } from "../codec-C6rTrpIp.mjs";
29
29
  import { t as markCircuitBreakerLegacySweepDone } from "../circuit-breaker-test-utils-DW6YC_Pe.mjs";
@@ -34,8 +34,9 @@ import { Create, Note, Offer, Person, getTypeId, lookupObject } from "@fedify/vo
34
34
  import { SpanStatusCode, metrics } from "@opentelemetry/api";
35
35
  import { createTestMeterProvider, createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
36
36
  import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
37
- import serialize from "json-canon";
37
+ import dns from "node:dns/promises";
38
38
  import { deepStrictEqual, ok, rejects, strictEqual } from "node:assert/strict";
39
+ import serialize from "json-canon";
39
40
  import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
40
41
  //#region ../fixture/src/fixtures/example.com/create.json
41
42
  var create_default = {
@@ -1175,6 +1176,31 @@ test({
1175
1176
  esm_default.hardReset();
1176
1177
  }
1177
1178
  });
1179
+ test("Federation.fetch() [rfc9421] multiple POST signatures", async (t) => {
1180
+ for (const validSecond of [true, false]) await t.step(validSecond ? "valid second signature" : "both signatures invalid", async () => {
1181
+ const inbox = [];
1182
+ const federation = createFederation({
1183
+ kv: new MemoryKvStore(),
1184
+ documentLoaderFactory: () => mockDocumentLoader,
1185
+ contextLoaderFactory: () => mockDocumentLoader
1186
+ });
1187
+ federation.setActorDispatcher("/users/{identifier}", (ctx, identifier) => new vocab.Person({ id: ctx.getActorUri(identifier) }));
1188
+ federation.setInboxListeners("/users/{identifier}/inbox", "/inbox").on(vocab.Create, (_ctx, activity) => {
1189
+ inbox.push(activity.id.href);
1190
+ });
1191
+ const signed = await signRequest(new Request("https://example.com/inbox", {
1192
+ method: "POST",
1193
+ headers: { "Content-Type": "application/activity+json" },
1194
+ body: JSON.stringify(create_default)
1195
+ }), rsaPrivateKey2, rsaPublicKey2.id, { spec: "rfc9421" });
1196
+ const input = signed.headers.get("Signature-Input");
1197
+ const signature = signed.headers.get("Signature");
1198
+ signed.headers.set("Signature-Input", `${input}, ${input.replace(/^sig1=/, "sig2=")}`);
1199
+ signed.headers.set("Signature", `sig1=:AAAAAA==:, ${validSecond ? signature.replace(/^sig1=/, "sig2=") : "sig2=:AAAAAA==:"}`);
1200
+ assertEquals((await federation.fetch(signed, { contextData: void 0 })).status, validSecond ? 202 : 401);
1201
+ assertEquals(inbox, validSecond ? [create_default.id] : []);
1202
+ });
1203
+ });
1178
1204
  test("Federation.fetch()", async (t) => {
1179
1205
  esm_default.spyGlobal();
1180
1206
  esm_default.get("https://example.com/key2", {
@@ -4943,6 +4969,157 @@ test("FederationImpl.processQueuedTask()", async (t) => {
4943
4969
  assertEquals(queuedMessages, []);
4944
4970
  });
4945
4971
  });
4972
+ test({
4973
+ name: "FederationImpl.processQueuedTask() accounts for destination validation failures",
4974
+ ignore: "Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted",
4975
+ async fn(t) {
4976
+ for (const result of [
4977
+ "throws",
4978
+ "empty",
4979
+ "private"
4980
+ ]) for (const failureThreshold of [1, 5]) for (const redirected of [false, true]) await t.step(`${result}, threshold: ${failureThreshold}, redirected: ${redirected}`, async () => {
4981
+ const destination = "https://delivery.invalid/inbox";
4982
+ const publicInbox = "https://8.8.8.8/inbox";
4983
+ const queuedMessages = [];
4984
+ const delays = [];
4985
+ const errors = [];
4986
+ const activities = [];
4987
+ const attempts = [];
4988
+ const delay = Temporal.Duration.from({ seconds: 5 });
4989
+ const kv = new MemoryKvStore();
4990
+ await markCircuitBreakerLegacySweepDone(kv);
4991
+ const [meterProvider, recorder] = createTestMeterProvider();
4992
+ const recoveryDelay = Temporal.Duration.from({ minutes: 30 });
4993
+ const federation = new FederationImpl({
4994
+ kv,
4995
+ meterProvider,
4996
+ circuitBreaker: {
4997
+ failureThreshold,
4998
+ recoveryDelay
4999
+ },
5000
+ queue: {
5001
+ enqueue(message, options) {
5002
+ queuedMessages.push(message);
5003
+ delays.push(options?.delay);
5004
+ return Promise.resolve();
5005
+ },
5006
+ listen() {
5007
+ return Promise.resolve();
5008
+ }
5009
+ },
5010
+ documentLoaderFactory: () => mockDocumentLoader,
5011
+ contextLoaderFactory: () => mockDocumentLoader,
5012
+ onOutboxError(error, activity) {
5013
+ errors.push(error);
5014
+ activities.push(activity);
5015
+ },
5016
+ outboxRetryPolicy(options) {
5017
+ attempts.push(options.attempts);
5018
+ return delay;
5019
+ }
5020
+ });
5021
+ const message = {
5022
+ type: "outbox",
5023
+ id: crypto.randomUUID(),
5024
+ baseUrl: "https://example.com",
5025
+ keys: [],
5026
+ activity: {
5027
+ "@context": "https://www.w3.org/ns/activitystreams",
5028
+ type: "Create",
5029
+ id: "https://example.com/activities/dns-failure",
5030
+ actor: "https://example.com/users/alice",
5031
+ object: {
5032
+ type: "Note",
5033
+ content: "test"
5034
+ }
5035
+ },
5036
+ activityType: "https://www.w3.org/ns/activitystreams#Create",
5037
+ inbox: redirected ? publicInbox : destination,
5038
+ sharedInbox: false,
5039
+ started: (/* @__PURE__ */ new Date()).toISOString(),
5040
+ attempt: 0,
5041
+ headers: {},
5042
+ traceContext: {}
5043
+ };
5044
+ const originalLookup = dns.lookup;
5045
+ const resolverError = /* @__PURE__ */ new Error("Resolver unavailable");
5046
+ const lookups = [];
5047
+ dns.lookup = ((hostname) => {
5048
+ lookups.push(hostname);
5049
+ assertEquals(hostname, "delivery.invalid");
5050
+ if (result === "throws") return Promise.reject(resolverError);
5051
+ return Promise.resolve(result === "empty" ? [] : [{
5052
+ address: "127.0.0.1",
5053
+ family: 4
5054
+ }]);
5055
+ });
5056
+ try {
5057
+ esm_default.mockGlobal().catch(202);
5058
+ if (redirected) esm_default.route(publicInbox, {
5059
+ status: 307,
5060
+ headers: { Location: destination }
5061
+ });
5062
+ await federation.processQueuedTask(void 0, message);
5063
+ assertEquals(errors.length, 1);
5064
+ const error = errors[0];
5065
+ if (result === "private") {
5066
+ assertInstanceOf(error, UrlError);
5067
+ assertEquals(error.reason, "disallowed");
5068
+ } else {
5069
+ assertInstanceOf(error, FetchError);
5070
+ assertEquals(error.url.href, destination);
5071
+ assertInstanceOf(error.cause, UrlError);
5072
+ assertEquals(error.cause.reason, "dns");
5073
+ assertStrictEquals(error.cause.cause, result === "throws" ? resolverError : void 0);
5074
+ }
5075
+ assertEquals(activities.length, 1);
5076
+ assertInstanceOf(activities[0], Create);
5077
+ assertEquals(activities[0].id?.href, "https://example.com/activities/dns-failure");
5078
+ assertEquals(attempts, [0]);
5079
+ const host = new URL(message.inbox).host;
5080
+ const state = await kv.get([
5081
+ "_fedify",
5082
+ "circuit",
5083
+ host
5084
+ ]);
5085
+ if (result === "private") assertEquals(state, void 0);
5086
+ else {
5087
+ assertExists(state);
5088
+ assertEquals(state.failures.length, 1);
5089
+ assertEquals(state.state, failureThreshold === 1 ? "open" : "closed");
5090
+ }
5091
+ if (result !== "private" && failureThreshold === 1) {
5092
+ assertEquals(queuedMessages.length, 1);
5093
+ const held = queuedMessages[0];
5094
+ assertEquals(held.attempt, 0);
5095
+ assertEquals(held.circuitHeld, true);
5096
+ assertExists(held.circuitHeldSince);
5097
+ assertEquals(delays, [recoveryDelay]);
5098
+ } else {
5099
+ assertEquals(queuedMessages, [{
5100
+ ...message,
5101
+ attempt: 1
5102
+ }]);
5103
+ assertEquals(delays, [delay]);
5104
+ }
5105
+ const expectedCount = result === "private" && !redirected ? 0 : 1;
5106
+ const sent = recorder.getMeasurements("activitypub.delivery.sent");
5107
+ assertEquals(sent.length, expectedCount);
5108
+ if (expectedCount === 1) {
5109
+ assertEquals(sent[0].value, 1);
5110
+ assertEquals(sent[0].attributes["activitypub.delivery.success"], false);
5111
+ assertEquals(sent[0].attributes["activitypub.remote.host"], host);
5112
+ }
5113
+ assertEquals(lookups, ["delivery.invalid"]);
5114
+ assertEquals(esm_default.callHistory.calls(destination).length, 0);
5115
+ assertEquals(esm_default.callHistory.calls().length, redirected ? 1 : 0);
5116
+ } finally {
5117
+ dns.lookup = originalLookup;
5118
+ esm_default.hardReset();
5119
+ }
5120
+ });
5121
+ }
5122
+ });
4946
5123
  test("FederationImpl.processQueuedTask() permanent failure", async (t) => {
4947
5124
  esm_default.spyGlobal();
4948
5125
  esm_default.post("https://gone.example/inbox", {
@@ -1,6 +1,6 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_federation = require("../federation-D9TufmIO.cjs");
3
+ const require_federation = require("../federation-2e8XrN7q.cjs");
4
4
  exports.CircuitBreaker = require_federation.CircuitBreaker;
5
5
  exports.InProcessMessageQueue = require_federation.InProcessMessageQueue;
6
6
  exports.MemoryKvStore = require_federation.MemoryKvStore;
@@ -1,6 +1,6 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { a as respondWithObjectIfAcceptable, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as respondWithObject, l as CircuitBreaker, n as handleWebFinger, o as SendActivityError, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "../middleware-BDUR99k3.js";
3
+ import { a as respondWithObjectIfAcceptable, c as digest, d as parseCircuitBreakerKvState, f as createFederationBuilder, i as respondWithObject, l as CircuitBreaker, n as handleWebFinger, o as SendActivityError, r as createExponentialBackoffPolicy, s as buildCollectionSynchronizationHeader, t as createFederation, u as normalizeCircuitBreakerOptions } from "../middleware-BYWyYjmX.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { Router as Router$1, RouterError as RouterError$1, assertPath, isPath } from "@fedify/uri-template";
6
6
  import { isEqual } from "es-toolkit";