@fedify/fedify 2.0.29-dev.2121 → 2.0.29-dev.2143

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 (54) hide show
  1. package/dist/{builder-CcdhtqZA.mjs → builder-CuFZFNTx.mjs} +3 -3
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{deno-t1Hq3o4g.mjs → deno-gzW01cMG.mjs} +1 -1
  4. package/dist/docloader-DW-UgR6o.mjs +78 -0
  5. package/dist/federation/builder.test.mjs +1 -1
  6. package/dist/federation/handler.test.mjs +6 -6
  7. package/dist/federation/idempotency.test.mjs +2 -2
  8. package/dist/federation/inbox.test.mjs +1 -1
  9. package/dist/federation/middleware.test.mjs +6 -6
  10. package/dist/federation/mod.cjs +1 -1
  11. package/dist/federation/mod.js +1 -1
  12. package/dist/federation/send.test.mjs +3 -3
  13. package/dist/federation/temporal.test.mjs +1 -1
  14. package/dist/federation/webfinger.test.mjs +1 -1
  15. package/dist/{http-6cUjmZau.cjs → http-BjfKj845.cjs} +1 -1
  16. package/dist/{http-CBZxo485.js → http-DCMS1UsO.js} +1 -1
  17. package/dist/{http-DscVc8UW.mjs → http-KxgJKEhF.mjs} +2 -2
  18. package/dist/{inbox-CKKOBg0X.mjs → inbox-ByhUuvHp.mjs} +1 -1
  19. package/dist/{key-BMxcDQ7Z.mjs → key-DK65oVQg.mjs} +1 -1
  20. package/dist/{kv-cache-BV20L2x4.cjs → kv-cache-4PJQF26S.cjs} +29 -7
  21. package/dist/{kv-cache-OZi_bDmv.js → kv-cache-CnJnTcyv.js} +30 -8
  22. package/dist/{ld-uaq9Uc9i.mjs → ld-CkhxuQKE.mjs} +2 -2
  23. package/dist/{middleware-DUALf6gS.cjs → middleware-BrhLLYbZ.cjs} +1 -1
  24. package/dist/{middleware-D5XEd8l9.mjs → middleware-CT46Re-o.mjs} +11 -11
  25. package/dist/{middleware-Cf7qPU7x.mjs → middleware-Kd08zJtj.mjs} +1 -1
  26. package/dist/{middleware-w_QMTb52.js → middleware-fe8Y1tqE.js} +3 -3
  27. package/dist/{middleware-Bhbzx0XA.cjs → middleware-s7WxQ4ac.cjs} +4 -4
  28. package/dist/{mod-Bt0eg_Lv.d.cts → mod-D3xVy8Yy.d.cts} +2 -0
  29. package/dist/{mod-XucBiDNd.d.ts → mod-PD3bZZ5j.d.ts} +2 -0
  30. package/dist/mod.cjs +4 -4
  31. package/dist/mod.d.cts +1 -1
  32. package/dist/mod.d.ts +1 -1
  33. package/dist/mod.js +4 -4
  34. package/dist/nodeinfo/handler.test.mjs +1 -1
  35. package/dist/{owner-Ds5LSYBk.mjs → owner-DOeeThGK.mjs} +2 -2
  36. package/dist/{proof-zSRThRNk.js → proof-CIp6SzLJ.js} +1 -1
  37. package/dist/{proof-2v-RNn_n.mjs → proof-CX04YEdf.mjs} +2 -2
  38. package/dist/{proof-C5gSPzjd.cjs → proof-dP19tIYO.cjs} +1 -1
  39. package/dist/{send-Dvu09tKp.mjs → send-Ce-4x3da.mjs} +2 -2
  40. package/dist/sig/http.test.mjs +2 -2
  41. package/dist/sig/key.test.mjs +1 -1
  42. package/dist/sig/ld.test.mjs +2 -2
  43. package/dist/sig/mod.cjs +2 -2
  44. package/dist/sig/mod.js +2 -2
  45. package/dist/sig/owner.test.mjs +1 -1
  46. package/dist/sig/proof.test.mjs +1 -1
  47. package/dist/{temporal-BAJ8SH-_.mjs → temporal-DFEjA29B.mjs} +1 -1
  48. package/dist/utils/docloader.test.mjs +244 -2
  49. package/dist/utils/mod.cjs +1 -1
  50. package/dist/utils/mod.d.cts +1 -1
  51. package/dist/utils/mod.d.ts +1 -1
  52. package/dist/utils/mod.js +1 -1
  53. package/package.json +5 -5
  54. package/dist/docloader-DmksBzVa.mjs +0 -56
@@ -2,8 +2,8 @@ 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-t1Hq3o4g.mjs";
6
- import { t as InboxListenerSet } from "./inbox-CKKOBg0X.mjs";
5
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
6
+ import { t as InboxListenerSet } from "./inbox-ByhUuvHp.mjs";
7
7
  import { getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { getLogger } from "@logtape/logtape";
@@ -42,7 +42,7 @@ var FederationBuilderImpl = class {
42
42
  this.collectionTypeIds = {};
43
43
  }
44
44
  async build(options) {
45
- const { FederationImpl } = await import("./middleware-Cf7qPU7x.mjs");
45
+ const { FederationImpl } = await import("./middleware-Kd08zJtj.mjs");
46
46
  const f = new FederationImpl(options);
47
47
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
48
48
  f.router = this.router.clone();
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
6
6
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
7
- import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-D5XEd8l9.mjs";
7
+ import { _ as autoIdAssigner, g as actorDehydrator, n as FederationImpl } from "../middleware-CT46Re-o.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  import { Follow, Person } from "@fedify/vocab";
10
10
  //#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.0.29-dev.2121+c46fb9dd";
6
+ var version = "2.0.29-dev.2143+bbbfea55";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -0,0 +1,78 @@
1
+ import "@js-temporal/polyfill";
2
+ import "urlpattern-polyfill";
3
+ globalThis.addEventListener = () => {};
4
+ import { o as validateCryptoKey } from "./key-DK65oVQg.mjs";
5
+ import { n as doubleKnock } from "./http-KxgJKEhF.mjs";
6
+ import { curry } from "es-toolkit";
7
+ import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
+ import { getLogger } from "@logtape/logtape";
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, 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);
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
+ specDeterminer,
45
+ log: curry(logRequest)(logger),
46
+ tracerProvider,
47
+ signal: options?.signal,
48
+ validateRedirect: async (nextUrl) => {
49
+ follow(nextUrl);
50
+ await validateUrl(nextUrl);
51
+ visited.add(nextUrl);
52
+ currentUrl = nextUrl;
53
+ }
54
+ });
55
+ return getRemoteDocument(currentUrl, response, (alternateUrl) => {
56
+ follow(alternateUrl);
57
+ return load(alternateUrl, options, redirected, visited);
58
+ });
59
+ }
60
+ async function validateUrl(url) {
61
+ if (!allowPrivateAddress) try {
62
+ await validatePublicUrl(url);
63
+ } catch (error) {
64
+ if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
65
+ url,
66
+ error
67
+ });
68
+ else logger.error("Disallowed private URL: {url}", {
69
+ url,
70
+ error
71
+ });
72
+ throw error;
73
+ }
74
+ }
75
+ return (url, options) => load(url, options);
76
+ }
77
+ //#endregion
78
+ export { getAuthenticatedDocumentLoader as t };
@@ -5,7 +5,7 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { i as assertExists } from "../std__assert-BUOJXi-Z.mjs";
6
6
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { n as createFederationBuilder } from "../builder-CcdhtqZA.mjs";
8
+ import { n as createFederationBuilder } from "../builder-CuFZFNTx.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Activity, Note, Person } from "@fedify/vocab";
11
11
  //#region src/federation/builder.test.ts
@@ -6,14 +6,14 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-BUOJXi-Z.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { t as assert } from "../assert-BhQd8QHP.mjs";
9
- import { i as generateCryptoKeyPair } from "../key-BMxcDQ7Z.mjs";
10
- import { s as signRequest } from "../http-DscVc8UW.mjs";
9
+ import { i as generateCryptoKeyPair } from "../key-DK65oVQg.mjs";
10
+ import { s as signRequest } from "../http-KxgJKEhF.mjs";
11
11
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-CCxRJhWa.mjs";
12
- import { a as compactJsonLd, f as signJsonLd } from "../ld-uaq9Uc9i.mjs";
13
- import { n as signObject } from "../proof-2v-RNn_n.mjs";
12
+ import { a as compactJsonLd, f as signJsonLd } from "../ld-CkhxuQKE.mjs";
13
+ import { n as signObject } from "../proof-CX04YEdf.mjs";
14
14
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
15
- import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-D5XEd8l9.mjs";
16
- import { t as InboxListenerSet } from "../inbox-CKKOBg0X.mjs";
15
+ import { a as createFederation, c as handleCollection, d as handleObject, f as respondWithObject, l as handleCustomCollection, p as respondWithObjectIfAcceptable, s as handleActor, u as handleInbox } from "../middleware-CT46Re-o.mjs";
16
+ import { t as InboxListenerSet } from "../inbox-ByhUuvHp.mjs";
17
17
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
18
18
  import { Create, CryptographicKey, Multikey, Note, Person } from "@fedify/vocab";
19
19
  //#region src/federation/handler.test.ts
@@ -4,9 +4,9 @@ globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-BUOJXi-Z.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
7
- import { n as signObject } from "../proof-2v-RNn_n.mjs";
7
+ import { n as signObject } from "../proof-CX04YEdf.mjs";
8
8
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
9
- import { a as createFederation } from "../middleware-D5XEd8l9.mjs";
9
+ import { a as createFederation } from "../middleware-CT46Re-o.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
@@ -3,7 +3,7 @@ import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
6
- import { t as InboxListenerSet } from "../inbox-CKKOBg0X.mjs";
6
+ import { t as InboxListenerSet } from "../inbox-ByhUuvHp.mjs";
7
7
  import { test } from "@fedify/fixture";
8
8
  import { Activity, Create, Invite, Offer, Update } from "@fedify/vocab";
9
9
  //#region src/federation/inbox.test.ts
@@ -9,14 +9,14 @@ import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z
9
9
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
10
10
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
11
11
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
12
- import { l as verifyRequest, s as signRequest } from "../http-DscVc8UW.mjs";
12
+ import { l as verifyRequest, s as signRequest } from "../http-KxgJKEhF.mjs";
13
13
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
14
- import { t as getAuthenticatedDocumentLoader } from "../docloader-DmksBzVa.mjs";
15
- import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-uaq9Uc9i.mjs";
16
- import { t as doesActorOwnKey } from "../owner-Ds5LSYBk.mjs";
17
- import { n as signObject, r as verifyObject } from "../proof-2v-RNn_n.mjs";
14
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-DW-UgR6o.mjs";
15
+ import { a as compactJsonLd, f as signJsonLd, m as verifyJsonLd, s as detachSignature } from "../ld-CkhxuQKE.mjs";
16
+ import { t as doesActorOwnKey } from "../owner-DOeeThGK.mjs";
17
+ import { n as signObject, r as verifyObject } from "../proof-CX04YEdf.mjs";
18
18
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
19
- import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-D5XEd8l9.mjs";
19
+ import { a as createFederation, i as KvSpecDeterminer, n as FederationImpl, r as InboxContextImpl, t as ContextImpl } from "../middleware-CT46Re-o.mjs";
20
20
  import { mockDocumentLoader, test } from "@fedify/fixture";
21
21
  import * as vocab from "@fedify/vocab";
22
22
  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-Bhbzx0XA.cjs");
5
+ const require_middleware = require("../middleware-s7WxQ4ac.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-w_QMTb52.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-fe8Y1tqE.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -7,10 +7,10 @@ import "../std__assert-BUOJXi-Z.mjs";
7
7
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
9
9
  import { t as assertNotEquals } from "../assert_not_equals--wG9hV7u.mjs";
10
- import { l as verifyRequest } from "../http-DscVc8UW.mjs";
10
+ import { l as verifyRequest } from "../http-KxgJKEhF.mjs";
11
11
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
12
- import { t as doesActorOwnKey } from "../owner-Ds5LSYBk.mjs";
13
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-Dvu09tKp.mjs";
12
+ import { t as doesActorOwnKey } from "../owner-DOeeThGK.mjs";
13
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-Ce-4x3da.mjs";
14
14
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
15
15
  import { Activity, Application, Endpoints, Group, Person, Service } from "@fedify/vocab";
16
16
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
@@ -4,7 +4,7 @@ globalThis.addEventListener = () => {};
4
4
  import "../std__assert-BUOJXi-Z.mjs";
5
5
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
6
6
  import { t as assert } from "../assert-BhQd8QHP.mjs";
7
- import { t as hasMalformedKnownTemporalLiteral } from "../temporal-BAJ8SH-_.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-DFEjA29B.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 { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-BUOJXi-Z.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { a as createFederation, o as handleWebFinger } from "../middleware-D5XEd8l9.mjs";
8
+ import { a as createFederation, o as handleWebFinger } from "../middleware-CT46Re-o.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -11,7 +11,7 @@ let byte_encodings_base64 = require("byte-encodings/base64");
11
11
  let structured_field_values = require("structured-field-values");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.0.29-dev.2121+c46fb9dd";
14
+ var version = "2.0.29-dev.2143+bbbfea55";
15
15
  //#endregion
16
16
  //#region src/sig/key.ts
17
17
  /**
@@ -10,7 +10,7 @@ import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
10
10
  import { Item, decodeDict, encodeItem } from "structured-field-values";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.0.29-dev.2121+c46fb9dd";
13
+ var version = "2.0.29-dev.2143+bbbfea55";
14
14
  //#endregion
15
15
  //#region src/sig/key.ts
16
16
  /**
@@ -1,8 +1,8 @@
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-t1Hq3o4g.mjs";
5
- import { o as validateCryptoKey, r as fetchKey } from "./key-BMxcDQ7Z.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
+ import { o as validateCryptoKey, r as fetchKey } from "./key-DK65oVQg.mjs";
6
6
  import { CryptographicKey } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { FetchError } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
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-t1Hq3o4g.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
5
  import { Activity, getTypeId } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
7
7
  import { getLogger } from "@logtape/logtape";
@@ -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-t1Hq3o4g.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.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 { getDocumentLoader } 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-6cUjmZau.cjs");
4
+ const require_http = require("./http-BjfKj845.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");
@@ -11,10 +11,13 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
11
11
  "utils",
12
12
  "docloader"
13
13
  ]);
14
+ const DEFAULT_MAX_REDIRECTION = 20;
14
15
  /**
15
16
  * Gets an authenticated {@link DocumentLoader} for the given identity.
16
17
  * Note that an authenticated document loader intentionally does not cache
17
18
  * the fetched documents.
19
+ * At most 20 HTTP redirects and alternate document links are followed in total
20
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
18
21
  * @param identity The identity to get the document loader for.
19
22
  * The actor's key pair.
20
23
  * @param options The options for the document loader.
@@ -24,15 +27,34 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
24
27
  */
25
28
  function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent, specDeterminer, tracerProvider } = {}) {
26
29
  require_http.validateCryptoKey(identity.privateKey);
27
- async function load(url, options) {
28
- await validateUrl(url);
29
- return (0, _fedify_vocab_runtime.getRemoteDocument)(url, await require_http.doubleKnock((0, _fedify_vocab_runtime.createActivityPubRequest)(url, { userAgent }), identity, {
30
+ async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
31
+ options?.signal?.throwIfAborted();
32
+ let currentUrl = new URL(url).href;
33
+ await validateUrl(currentUrl);
34
+ visited.add(currentUrl);
35
+ const originalRequest = (0, _fedify_vocab_runtime.createActivityPubRequest)(currentUrl, { userAgent });
36
+ function follow(nextUrl) {
37
+ options?.signal?.throwIfAborted();
38
+ if (redirected >= DEFAULT_MAX_REDIRECTION) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
39
+ if (visited.has(nextUrl)) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
40
+ redirected++;
41
+ }
42
+ const response = await require_http.doubleKnock(originalRequest, identity, {
30
43
  specDeterminer,
31
44
  log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
32
45
  tracerProvider,
33
46
  signal: options?.signal,
34
- validateRedirect: validateUrl
35
- }), load);
47
+ validateRedirect: async (nextUrl) => {
48
+ follow(nextUrl);
49
+ await validateUrl(nextUrl);
50
+ visited.add(nextUrl);
51
+ currentUrl = nextUrl;
52
+ }
53
+ });
54
+ return (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (alternateUrl) => {
55
+ follow(alternateUrl);
56
+ return load(alternateUrl, options, redirected, visited);
57
+ });
36
58
  }
37
59
  async function validateUrl(url) {
38
60
  if (!allowPrivateAddress) try {
@@ -49,7 +71,7 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
49
71
  throw error;
50
72
  }
51
73
  }
52
- return load;
74
+ return (url, options) => load(url, options);
53
75
  }
54
76
  //#endregion
55
77
  //#region src/utils/kv-cache.ts
@@ -1,19 +1,22 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { l as validateCryptoKey, t as doubleKnock } from "./http-CBZxo485.js";
3
+ import { l as validateCryptoKey, t as doubleKnock } from "./http-DCMS1UsO.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { curry } from "es-toolkit";
6
- import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
6
+ import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/docloader.ts
8
8
  const logger$1 = getLogger([
9
9
  "fedify",
10
10
  "utils",
11
11
  "docloader"
12
12
  ]);
13
+ const DEFAULT_MAX_REDIRECTION = 20;
13
14
  /**
14
15
  * Gets an authenticated {@link DocumentLoader} for the given identity.
15
16
  * Note that an authenticated document loader intentionally does not cache
16
17
  * the fetched documents.
18
+ * At most 20 HTTP redirects and alternate document links are followed in total
19
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
17
20
  * @param identity The identity to get the document loader for.
18
21
  * The actor's key pair.
19
22
  * @param options The options for the document loader.
@@ -23,15 +26,34 @@ const logger$1 = getLogger([
23
26
  */
24
27
  function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent, specDeterminer, tracerProvider } = {}) {
25
28
  validateCryptoKey(identity.privateKey);
26
- async function load(url, options) {
27
- await validateUrl(url);
28
- return getRemoteDocument(url, await doubleKnock(createActivityPubRequest(url, { userAgent }), identity, {
29
+ async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
30
+ options?.signal?.throwIfAborted();
31
+ let currentUrl = new URL(url).href;
32
+ await validateUrl(currentUrl);
33
+ visited.add(currentUrl);
34
+ const originalRequest = createActivityPubRequest(currentUrl, { userAgent });
35
+ function follow(nextUrl) {
36
+ options?.signal?.throwIfAborted();
37
+ if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
38
+ if (visited.has(nextUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
39
+ redirected++;
40
+ }
41
+ const response = await doubleKnock(originalRequest, identity, {
29
42
  specDeterminer,
30
43
  log: curry(logRequest)(logger$1),
31
44
  tracerProvider,
32
45
  signal: options?.signal,
33
- validateRedirect: validateUrl
34
- }), load);
46
+ validateRedirect: async (nextUrl) => {
47
+ follow(nextUrl);
48
+ await validateUrl(nextUrl);
49
+ visited.add(nextUrl);
50
+ currentUrl = nextUrl;
51
+ }
52
+ });
53
+ return getRemoteDocument(currentUrl, response, (alternateUrl) => {
54
+ follow(alternateUrl);
55
+ return load(alternateUrl, options, redirected, visited);
56
+ });
35
57
  }
36
58
  async function validateUrl(url) {
37
59
  if (!allowPrivateAddress) try {
@@ -48,7 +70,7 @@ function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAge
48
70
  throw error;
49
71
  }
50
72
  }
51
- return load;
73
+ return (url, options) => load(url, options);
52
74
  }
53
75
  //#endregion
54
76
  //#region src/utils/kv-cache.ts
@@ -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-t1Hq3o4g.mjs";
5
- import { o as validateCryptoKey, r as fetchKey } from "./key-BMxcDQ7Z.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
+ import { o as validateCryptoKey, r as fetchKey } from "./key-DK65oVQg.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";
@@ -1,4 +1,4 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
- const require_middleware = require("./middleware-Bhbzx0XA.cjs");
3
+ const require_middleware = require("./middleware-s7WxQ4ac.cjs");
4
4
  exports.FederationImpl = require_middleware.FederationImpl;
@@ -2,24 +2,24 @@ 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-t1Hq3o4g.mjs";
6
- import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-BMxcDQ7Z.mjs";
7
- import { l as verifyRequest } from "./http-DscVc8UW.mjs";
8
- import { t as getAuthenticatedDocumentLoader } from "./docloader-DmksBzVa.mjs";
5
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
6
+ import { a as importJwk, o as validateCryptoKey, t as exportJwk } from "./key-DK65oVQg.mjs";
7
+ import { l as verifyRequest } from "./http-KxgJKEhF.mjs";
8
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-DW-UgR6o.mjs";
9
9
  import { n as kvCache } from "./kv-cache-BrjuJue8.mjs";
10
- import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-uaq9Uc9i.mjs";
11
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-Ds5LSYBk.mjs";
12
- import { n as signObject, r as verifyObject } from "./proof-2v-RNn_n.mjs";
10
+ import { a as compactJsonLd, c as getNormalizationContextLoader, d as isInvalidUrlTypeError, f as signJsonLd, g as wrapContextLoaderForJsonLd, l as hasSignature, p as verifyCompactJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as isClearlyMalformedContextReference } from "./ld-CkhxuQKE.mjs";
11
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-DOeeThGK.mjs";
12
+ import { n as signObject, r as verifyObject } from "./proof-CX04YEdf.mjs";
13
13
  import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-BUqbea1d.mjs";
14
14
  import { t as nodeInfoToJson } from "./types-MNlFfxjv.mjs";
15
- import { n as routeActivity } from "./inbox-CKKOBg0X.mjs";
16
- import { t as FederationBuilderImpl } from "./builder-CcdhtqZA.mjs";
15
+ import { n as routeActivity } from "./inbox-ByhUuvHp.mjs";
16
+ import { t as FederationBuilderImpl } from "./builder-CuFZFNTx.mjs";
17
17
  import { t as buildCollectionSynchronizationHeader } from "./collection-DI6YzYpd.mjs";
18
18
  import { t as KvKeyCache } from "./keycache-D239uGT9.mjs";
19
19
  import { t as acceptsJsonLd } from "./negotiation-TWEs6Cl8.mjs";
20
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-BAJ8SH-_.mjs";
20
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-DFEjA29B.mjs";
21
21
  import { t as createExponentialBackoffPolicy } from "./retry-Ddbq3AcK.mjs";
22
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-Dvu09tKp.mjs";
22
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-Ce-4x3da.mjs";
23
23
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
24
24
  import { lookupWebFinger } from "@fedify/webfinger";
25
25
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -1,5 +1,5 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as FederationImpl } from "./middleware-D5XEd8l9.mjs";
4
+ import { n as FederationImpl } from "./middleware-CT46Re-o.mjs";
5
5
  export { FederationImpl };
@@ -2,10 +2,10 @@ import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./chunk-CRNNMoPX.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-BGMIq1cs.js";
5
- import { c as importJwk, d as name, f as version, i as exportJwk, l as validateCryptoKey, r as verifyRequest, t as doubleKnock } from "./http-CBZxo485.js";
5
+ import { c as importJwk, d as name, f as version, i as exportJwk, l as validateCryptoKey, r as verifyRequest, t as doubleKnock } from "./http-DCMS1UsO.js";
6
6
  import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-miAYfiGO.js";
7
- import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-zSRThRNk.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-OZi_bDmv.js";
7
+ import { _ as signJsonLd, a as doesActorOwnKey, c as assertSafeJsonLd, f as detachSignature, g as isInvalidUrlTypeError, h as isClearlyMalformedContextReference, m as hasSignature, n as signObject, o as getKeyOwner, p as getNormalizationContextLoader, r as verifyObject, s as InvalidContextReferenceError, u as compactJsonLd, v as verifyCompactJsonLd, x as wrapContextLoaderForJsonLd } from "./proof-CIp6SzLJ.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-CnJnTcyv.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
11
11
  import { SpanKind, SpanStatusCode, context, propagation, trace } from "@opentelemetry/api";
@@ -2,10 +2,10 @@ const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
4
  const require_transformers = require("./transformers-NeAONrAq.cjs");
5
- const require_http = require("./http-6cUjmZau.cjs");
5
+ const require_http = require("./http-BjfKj845.cjs");
6
6
  const require_types = require("./types-DRPjmHVV.cjs");
7
- const require_proof = require("./proof-C5gSPzjd.cjs");
8
- const require_kv_cache = require("./kv-cache-BV20L2x4.cjs");
7
+ const require_proof = require("./proof-dP19tIYO.cjs");
8
+ const require_kv_cache = require("./kv-cache-4PJQF26S.cjs");
9
9
  let _logtape_logtape = require("@logtape/logtape");
10
10
  let _fedify_vocab = require("@fedify/vocab");
11
11
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -335,7 +335,7 @@ var FederationBuilderImpl = class {
335
335
  this.collectionTypeIds = {};
336
336
  }
337
337
  async build(options) {
338
- const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-DUALf6gS.cjs"));
338
+ const { FederationImpl } = await Promise.resolve().then(() => require("./middleware-BrhLLYbZ.cjs"));
339
339
  const f = new FederationImpl(options);
340
340
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
341
341
  f.router = this.router.clone();
@@ -30,6 +30,8 @@ interface GetAuthenticatedDocumentLoaderOptions extends DocumentLoaderFactoryOpt
30
30
  * Gets an authenticated {@link DocumentLoader} for the given identity.
31
31
  * Note that an authenticated document loader intentionally does not cache
32
32
  * the fetched documents.
33
+ * At most 20 HTTP redirects and alternate document links are followed in total
34
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
33
35
  * @param identity The identity to get the document loader for.
34
36
  * The actor's key pair.
35
37
  * @param options The options for the document loader.
@@ -30,6 +30,8 @@ interface GetAuthenticatedDocumentLoaderOptions extends DocumentLoaderFactoryOpt
30
30
  * Gets an authenticated {@link DocumentLoader} for the given identity.
31
31
  * Note that an authenticated document loader intentionally does not cache
32
32
  * the fetched documents.
33
+ * At most 20 HTTP redirects and alternate document links are followed in total
34
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
33
35
  * @param identity The identity to get the document loader for.
34
36
  * The actor's key pair.
35
37
  * @param options The options for the document loader.
package/dist/mod.cjs CHANGED
@@ -4,11 +4,11 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  require("./chunk-DDcVe30Y.cjs");
5
5
  const require_transformers = require("./transformers-NeAONrAq.cjs");
6
6
  require("./compat/mod.cjs");
7
- const require_http = require("./http-6cUjmZau.cjs");
8
- const require_middleware = require("./middleware-Bhbzx0XA.cjs");
7
+ const require_http = require("./http-BjfKj845.cjs");
8
+ const require_middleware = require("./middleware-s7WxQ4ac.cjs");
9
9
  const require_types = require("./types-DRPjmHVV.cjs");
10
- const require_proof = require("./proof-C5gSPzjd.cjs");
11
- const require_kv_cache = require("./kv-cache-BV20L2x4.cjs");
10
+ const require_proof = require("./proof-dP19tIYO.cjs");
11
+ const require_kv_cache = require("./kv-cache-4PJQF26S.cjs");
12
12
  const require_federation_mod = require("./federation/mod.cjs");
13
13
  require("./nodeinfo/mod.cjs");
14
14
  require("./runtime/mod.cjs");
package/dist/mod.d.cts CHANGED
@@ -7,7 +7,7 @@ import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreL
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.cjs";
8
8
  import { n as handleWebFinger, t as WebFingerHandlerParameters } from "./mod-Dp5dfH-i.cjs";
9
9
  import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "./mod-IOWUF5Yr.cjs";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-Bt0eg_Lv.cjs";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-D3xVy8Yy.cjs";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
13
13
  //#region src/mod.d.ts
package/dist/mod.d.ts CHANGED
@@ -7,7 +7,7 @@ import { a as MemoryKvStore, i as KvStoreSetOptions, n as KvStore, r as KvStoreL
7
7
  import { actorDehydrator, autoIdAssigner, getDefaultActivityTransformers } from "./compat/mod.js";
8
8
  import { n as handleWebFinger, t as WebFingerHandlerParameters } from "./mod-D24V_w-K.js";
9
9
  import { _ as verifyJsonLd, a as createProof, c as verifyProof, d as VerifyJsonLdOptions, f as VerifySignatureOptions, g as signJsonLd, h as detachSignature, i as VerifyProofOptions, l as CreateSignatureOptions, m as createSignature, n as SignObjectOptions, o as signObject, p as attachSignature, r as VerifyObjectOptions, s as verifyObject, t as CreateProofOptions, u as SignJsonLdOptions, v as verifySignature } from "./mod-E7YM72JZ.js";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-XucBiDNd.js";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./mod-PD3bZZ5j.js";
11
11
  export * from "@fedify/vocab-runtime";
12
12
 
13
13
  //#region src/mod.d.ts
package/dist/mod.js CHANGED
@@ -3,11 +3,11 @@ import { URLPattern } from "urlpattern-polyfill";
3
3
  import "./chunk-CRNNMoPX.js";
4
4
  import { n as autoIdAssigner, r as getDefaultActivityTransformers, t as actorDehydrator } from "./transformers-BGMIq1cs.js";
5
5
  import "./compat/mod.js";
6
- import { c as importJwk, i as exportJwk, n as signRequest, o as fetchKey, r as verifyRequest, s as generateCryptoKeyPair } from "./http-CBZxo485.js";
7
- 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-w_QMTb52.js";
6
+ import { c as importJwk, i as exportJwk, n as signRequest, o as fetchKey, r as verifyRequest, s as generateCryptoKeyPair } from "./http-DCMS1UsO.js";
7
+ 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-fe8Y1tqE.js";
8
8
  import { n as getNodeInfo, r as parseNodeInfo, t as nodeInfoToJson } from "./types-miAYfiGO.js";
9
- import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "./proof-zSRThRNk.js";
10
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-OZi_bDmv.js";
9
+ import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "./proof-CIp6SzLJ.js";
10
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-CnJnTcyv.js";
11
11
  import { InProcessMessageQueue, MemoryKvStore, ParallelMessageQueue } from "./federation/mod.js";
12
12
  import "./nodeinfo/mod.js";
13
13
  import "./runtime/mod.js";
@@ -5,7 +5,7 @@ import { n as createRequestContext } from "../context-Juj6bdHC.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-BUOJXi-Z.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { a as createFederation, h as handleNodeInfoJrd, m as handleNodeInfo } from "../middleware-D5XEd8l9.mjs";
8
+ import { a as createFederation, h as handleNodeInfoJrd, m as handleNodeInfo } from "../middleware-CT46Re-o.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  //#region src/nodeinfo/handler.test.ts
11
11
  test("handleNodeInfo()", async () => {
@@ -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-t1Hq3o4g.mjs";
5
- import { n as fetchActorDocument, s as verifyKeyOwnership, t as exportJwk } from "./key-BMxcDQ7Z.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
+ import { n as fetchActorDocument, s as verifyKeyOwnership, t as exportJwk } from "./key-DK65oVQg.mjs";
6
6
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
7
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { 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 { a as fetchActorDocument, d as name, f as version, i as exportJwk, l as validateCryptoKey, o as fetchKey, u as verifyKeyOwnership } from "./http-CBZxo485.js";
3
+ import { a as fetchActorDocument, d as name, f as version, i as exportJwk, l as validateCryptoKey, o as fetchKey, u as verifyKeyOwnership } from "./http-DCMS1UsO.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
5
  import { Activity, CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1, getTypeId, isActor } from "@fedify/vocab";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -1,8 +1,8 @@
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-t1Hq3o4g.mjs";
5
- import { o as validateCryptoKey, r as fetchKey } from "./key-BMxcDQ7Z.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
+ import { o as validateCryptoKey, r as fetchKey } from "./key-DK65oVQg.mjs";
6
6
  import { Activity, DataIntegrityProof, Multikey, getTypeId } from "@fedify/vocab";
7
7
  import { SpanStatusCode, trace } from "@opentelemetry/api";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -1,7 +1,7 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  const require_chunk = require("./chunk-DDcVe30Y.cjs");
4
- const require_http = require("./http-6cUjmZau.cjs");
4
+ const require_http = require("./http-BjfKj845.cjs");
5
5
  let _logtape_logtape = require("@logtape/logtape");
6
6
  let _fedify_vocab = require("@fedify/vocab");
7
7
  let _opentelemetry_api = require("@opentelemetry/api");
@@ -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-t1Hq3o4g.mjs";
5
- import { n as doubleKnock } from "./http-DscVc8UW.mjs";
4
+ import { n as version, t as name } from "./deno-gzW01cMG.mjs";
5
+ import { n as doubleKnock } from "./http-KxgJKEhF.mjs";
6
6
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
7
  import { FetchError, UrlError, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -7,8 +7,8 @@ import { i as assertExists, t as assertStringIncludes } from "../std__assert-BUO
7
7
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
8
  import { t as assert } from "../assert-BhQd8QHP.mjs";
9
9
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
10
- import { t as exportJwk } from "../key-BMxcDQ7Z.mjs";
11
- import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase } from "../http-DscVc8UW.mjs";
10
+ import { t as exportJwk } from "../key-DK65oVQg.mjs";
11
+ import { a as parseRfc9421Signature, c as timingSafeEqual, i as formatRfc9421SignatureParameters, l as verifyRequest, n as doubleKnock, o as parseRfc9421SignatureInput, r as formatRfc9421Signature, s as signRequest, t as createRfc9421SignatureBase } from "../http-KxgJKEhF.mjs";
12
12
  import { i as rsaPrivateKey2, l as rsaPublicKey5, o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CCxRJhWa.mjs";
13
13
  import { mockDocumentLoader, test } from "@fedify/fixture";
14
14
  import { FetchError, exportSpki } from "@fedify/vocab-runtime";
@@ -6,7 +6,7 @@ import "../std__assert-BUOJXi-Z.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
9
- import { a as importJwk, i as generateCryptoKeyPair, o as validateCryptoKey, r as fetchKey, t as exportJwk } from "../key-BMxcDQ7Z.mjs";
9
+ import { a as importJwk, i as generateCryptoKeyPair, o as validateCryptoKey, r as fetchKey, t as exportJwk } from "../key-DK65oVQg.mjs";
10
10
  import { c as rsaPublicKey3, i as rsaPrivateKey2, o as rsaPublicKey1, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { CryptographicKey, Multikey } from "@fedify/vocab";
@@ -5,9 +5,9 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import { n as assertFalse, t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
6
6
  import { n as assertInstanceOf, t as assert } from "../assert-BhQd8QHP.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
8
- import { i as generateCryptoKeyPair } from "../key-BMxcDQ7Z.mjs";
8
+ import { i as generateCryptoKeyPair } from "../key-DK65oVQg.mjs";
9
9
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
10
- import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-uaq9Uc9i.mjs";
10
+ import { a as compactJsonLd, d as isInvalidUrlTypeError, f as signJsonLd, h as verifySignature, i as attachSignature, m as verifyJsonLd, n as UnsafeJsonLdError, o as createSignature, s as detachSignature } from "../ld-CkhxuQKE.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { CryptographicKey } from "@fedify/vocab";
13
13
  import { encodeBase64 } from "byte-encodings/base64";
package/dist/sig/mod.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_http = require("../http-6cUjmZau.cjs");
5
- const require_proof = require("../proof-C5gSPzjd.cjs");
4
+ const require_http = require("../http-BjfKj845.cjs");
5
+ const require_proof = require("../proof-dP19tIYO.cjs");
6
6
  exports.attachSignature = require_proof.attachSignature;
7
7
  exports.createProof = require_proof.createProof;
8
8
  exports.createSignature = require_proof.createSignature;
package/dist/sig/mod.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { c as importJwk, i as exportJwk, n as signRequest, o as fetchKey, r as verifyRequest, s as generateCryptoKeyPair } from "../http-CBZxo485.js";
4
- import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-zSRThRNk.js";
3
+ import { c as importJwk, i as exportJwk, n as signRequest, o as fetchKey, r as verifyRequest, s as generateCryptoKeyPair } from "../http-DCMS1UsO.js";
4
+ import { _ as signJsonLd, a as doesActorOwnKey, b as verifySignature, d as createSignature, f as detachSignature, i as verifyProof, l as attachSignature, n as signObject, o as getKeyOwner, r as verifyObject, t as createProof, y as verifyJsonLd } from "../proof-CIp6SzLJ.js";
5
5
  export { attachSignature, createProof, createSignature, detachSignature, doesActorOwnKey, exportJwk, fetchKey, generateCryptoKeyPair, getKeyOwner, importJwk, signJsonLd, signObject, signRequest, verifyJsonLd, verifyObject, verifyProof, verifyRequest, verifySignature };
@@ -6,7 +6,7 @@ import "../std__assert-BUOJXi-Z.mjs";
6
6
  import { n as assertFalse } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { t as assert } from "../assert-BhQd8QHP.mjs";
8
8
  import { o as rsaPublicKey1, s as rsaPublicKey2 } from "../keys-CCxRJhWa.mjs";
9
- import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-Ds5LSYBk.mjs";
9
+ import { n as getKeyOwner, t as doesActorOwnKey } from "../owner-DOeeThGK.mjs";
10
10
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, CryptographicKey, lookupObject } from "@fedify/vocab";
12
12
  //#region src/sig/owner.test.ts
@@ -6,7 +6,7 @@ import "../std__assert-BUOJXi-Z.mjs";
6
6
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
7
7
  import { n as assertInstanceOf } from "../assert-BhQd8QHP.mjs";
8
8
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-CCxRJhWa.mjs";
9
- import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-2v-RNn_n.mjs";
9
+ import { i as verifyProof, n as signObject, r as verifyObject, t as createProof } from "../proof-CX04YEdf.mjs";
10
10
  import { mockDocumentLoader, test } from "@fedify/fixture";
11
11
  import { Create, DataIntegrityProof, Multikey, Note, Place } from "@fedify/vocab";
12
12
  import { decodeMultibase, importMultibaseKey } from "@fedify/vocab-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { c as getNormalizationContextLoader } from "./ld-uaq9Uc9i.mjs";
4
+ import { c as getNormalizationContextLoader } from "./ld-CkhxuQKE.mjs";
5
5
  import jsonld from "@fedify/vocab-runtime/jsonld";
6
6
  //#region src/federation/temporal.ts
7
7
  function isPlainObject(value) {
@@ -5,13 +5,14 @@ import { t as esm_default } from "../esm-DVILvP5e.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-BUOJXi-Z.mjs";
7
7
  import { t as assertRejects } from "../assert_rejects-B-qJtC9Z.mjs";
8
- import { l as verifyRequest } from "../http-DscVc8UW.mjs";
8
+ import { l as verifyRequest } from "../http-KxgJKEhF.mjs";
9
9
  import { i as rsaPrivateKey2 } from "../keys-CCxRJhWa.mjs";
10
- import { t as getAuthenticatedDocumentLoader } from "../docloader-DmksBzVa.mjs";
10
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-DW-UgR6o.mjs";
11
11
  import { mockDocumentLoader, test } from "@fedify/fixture";
12
12
  import { FetchError, UrlError } from "@fedify/vocab-runtime";
13
13
  import { configure, reset } from "@logtape/logtape";
14
14
  import dns from "node:dns/promises";
15
+ import { deepStrictEqual, ok, rejects } from "node:assert/strict";
15
16
  //#region src/utils/docloader.test.ts
16
17
  test("getAuthenticatedDocumentLoader()", async (t) => {
17
18
  esm_default.spyGlobal();
@@ -198,5 +199,246 @@ test("getAuthenticatedDocumentLoader() logs DNS failures as such", { ignore: "De
198
199
  }
199
200
  });
200
201
  });
202
+ test("getAuthenticatedDocumentLoader() bounds alternate document chains", async (t) => {
203
+ const base = "https://example.com/alternate-chain/";
204
+ const loader = getAuthenticatedDocumentLoader({
205
+ keyId: new URL("https://example.com/key2"),
206
+ privateKey: rsaPrivateKey2
207
+ }, { allowPrivateAddress: true });
208
+ for (const mode of [
209
+ "header",
210
+ "html",
211
+ "mixed"
212
+ ]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
213
+ esm_default.mockGlobal();
214
+ let requests = 0;
215
+ esm_default.get(`begin:${base}`, ({ url }) => {
216
+ requests++;
217
+ const index = Number(new URL(url).pathname.split("/").at(-1));
218
+ if (index === hops || requests > 30) return Response.json({ done: true });
219
+ const next = `${base}${index + 1}`;
220
+ if (mode === "mixed" && index % 2 === 1) return Response.redirect(next, 302);
221
+ return alternate(next, mode === "html");
222
+ });
223
+ try {
224
+ if (hops === 20) deepStrictEqual((await loader(`${base}0`)).document, { done: true });
225
+ else await rejects(loader(`${base}0`), (error) => {
226
+ ok(error instanceof FetchError);
227
+ ok(error.message.includes("Too many redirections (21)"));
228
+ return true;
229
+ });
230
+ deepStrictEqual(requests, 21);
231
+ } finally {
232
+ esm_default.hardReset();
233
+ }
234
+ });
235
+ for (const mode of [
236
+ "header",
237
+ "html",
238
+ "alternate-redirect",
239
+ "redirect-alternate",
240
+ "redirect-intermediate"
241
+ ]) await t.step(`${mode}: cycle`, async () => {
242
+ esm_default.mockGlobal();
243
+ let requests = 0;
244
+ esm_default.get(`begin:${base}`, ({ url }) => {
245
+ requests++;
246
+ if (requests > 30) return Response.json({ stopped: true });
247
+ const index = Number(new URL(url).pathname.split("/").at(-1));
248
+ const next = `${base}${mode === "redirect-intermediate" ? index === 0 ? 1 : index === 1 ? 2 : 1 : 1 - index}`;
249
+ if (mode === "alternate-redirect" && index === 1 || (mode === "redirect-alternate" || mode === "redirect-intermediate") && index === 0) return Response.redirect(next, 302);
250
+ return alternate(next, mode === "html");
251
+ });
252
+ try {
253
+ await rejects(loader(`${base}0`), (error) => {
254
+ ok(error instanceof FetchError);
255
+ ok(error.message.includes("Redirect loop detected:"));
256
+ return true;
257
+ });
258
+ deepStrictEqual(requests, mode === "redirect-intermediate" ? 3 : 2);
259
+ } finally {
260
+ esm_default.hardReset();
261
+ }
262
+ });
263
+ await t.step("alternate followed by 20 redirects shares the limit", async () => {
264
+ esm_default.mockGlobal();
265
+ let requests = 0;
266
+ esm_default.get(`begin:${base}`, ({ url }) => {
267
+ requests++;
268
+ const index = Number(new URL(url).pathname.split("/").at(-1));
269
+ if (index === 21) return Response.json({ done: true });
270
+ return index === 0 ? alternate(`${base}1`, false) : Response.redirect(`${base}${index + 1}`, 302);
271
+ });
272
+ try {
273
+ await rejects(loader(`${base}0`), (error) => {
274
+ ok(error instanceof FetchError);
275
+ ok(error.message.includes("Too many redirections (21)"));
276
+ return true;
277
+ });
278
+ deepStrictEqual(requests, 21);
279
+ } finally {
280
+ esm_default.hardReset();
281
+ }
282
+ });
283
+ await t.step("relative alternate after redirect and isolated calls", async () => {
284
+ esm_default.mockGlobal();
285
+ esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
286
+ esm_default.get(`${base}html`, alternate("./document", true));
287
+ esm_default.get(`${base}document`, Response.json({ done: true }));
288
+ try {
289
+ for (let i = 0; i < 2; i++) {
290
+ const results = await Promise.all([loader(`${base}start`), loader(`${base}start`)]);
291
+ for (const result of results) {
292
+ deepStrictEqual(result.document, { done: true });
293
+ deepStrictEqual(result.documentUrl, `${base}document`);
294
+ }
295
+ }
296
+ } finally {
297
+ esm_default.hardReset();
298
+ }
299
+ });
300
+ function alternate(next, html) {
301
+ return html ? new Response(`<link rel="alternate" type="application/activity+json" href="${next}">`, { headers: { "Content-Type": "text/html" } }) : new Response("not JSON", { headers: {
302
+ "Content-Type": "text/plain",
303
+ Link: `<${next}>; rel="alternate"; type="application/activity+json"`
304
+ } });
305
+ }
306
+ });
307
+ test("getAuthenticatedDocumentLoader() preserves cancellation across alternates", async (t) => {
308
+ const base = "https://example.com/alternate-abort/";
309
+ const loader = getAuthenticatedDocumentLoader({
310
+ keyId: new URL("https://example.com/key2"),
311
+ privateKey: rsaPrivateKey2
312
+ }, { allowPrivateAddress: true });
313
+ for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
314
+ esm_default.mockGlobal();
315
+ const controller = new AbortController();
316
+ const reason = /* @__PURE__ */ new Error("Alternate document loading cancelled");
317
+ let requests = 0;
318
+ esm_default.get(`begin:${base}`, ({ url }) => {
319
+ requests++;
320
+ const index = Number(new URL(url).pathname.split("/").at(-1));
321
+ if (index === 2) return Response.json({ done: true });
322
+ if (index === 1) controller.abort(reason);
323
+ const next = `${base}${index + 1}`;
324
+ return new Response(html ? `<link rel="alternate" type="application/activity+json" href="${next}">` : "not JSON", { headers: html ? { "Content-Type": "text/html" } : {
325
+ "Content-Type": "text/plain",
326
+ Link: `<${next}>; rel="alternate"; type="application/activity+json"`
327
+ } });
328
+ });
329
+ try {
330
+ await rejects(loader(`${base}0`, { signal: controller.signal }), (error) => {
331
+ ok(error === reason);
332
+ return true;
333
+ });
334
+ deepStrictEqual(requests, 2);
335
+ } finally {
336
+ esm_default.hardReset();
337
+ }
338
+ });
339
+ });
340
+ test("getAuthenticatedDocumentLoader() tracks fallback redirects after alternates", async () => {
341
+ esm_default.mockGlobal();
342
+ let requests = 0;
343
+ let knocks = 0;
344
+ const first = "https://example.com/fallback-alternate/a";
345
+ const second = "https://example.com/fallback-alternate/b";
346
+ esm_default.get(first, () => {
347
+ requests++;
348
+ if (requests > 30) return Response.json({ stopped: true });
349
+ return new Response("", { headers: {
350
+ "Content-Type": "text/plain",
351
+ Link: `<${second}>; rel="alternate"; type="application/activity+json"`
352
+ } });
353
+ });
354
+ esm_default.get(second, () => {
355
+ requests++;
356
+ return ++knocks % 2 === 1 ? new Response("", { status: 401 }) : Response.redirect(first, 302);
357
+ });
358
+ try {
359
+ const loader = getAuthenticatedDocumentLoader({
360
+ keyId: new URL("https://example.com/key2"),
361
+ privateKey: rsaPrivateKey2
362
+ }, { allowPrivateAddress: true });
363
+ await assertRejects(() => loader(first), FetchError, "Redirect loop detected:");
364
+ assertEquals(requests, 3);
365
+ } finally {
366
+ esm_default.hardReset();
367
+ }
368
+ });
369
+ test("getAuthenticatedDocumentLoader() resolves relative alternates without response URLs", async () => {
370
+ const originalFetch = globalThis.fetch;
371
+ const base = "https://example.com/empty-response-url/";
372
+ const urls = [];
373
+ globalThis.fetch = (input) => {
374
+ const url = input instanceof Request ? input.url : String(input);
375
+ urls.push(url);
376
+ if (url === `${base}start`) return Promise.resolve(Response.redirect(`${base}nested/page`, 302));
377
+ if (url === `${base}nested/page`) return Promise.resolve(new Response("<link rel=\"alternate\" type=\"application/activity+json\" href=\"document\">", { headers: { "Content-Type": "text/html" } }));
378
+ assertEquals(url, `${base}nested/document`);
379
+ return Promise.resolve(Response.json({ done: true }));
380
+ };
381
+ try {
382
+ assertEquals(await getAuthenticatedDocumentLoader({
383
+ keyId: new URL("https://example.com/key2"),
384
+ privateKey: rsaPrivateKey2
385
+ }, { allowPrivateAddress: true })(`${base}start`), {
386
+ contextUrl: null,
387
+ document: { done: true },
388
+ documentUrl: `${base}nested/document`
389
+ });
390
+ assertEquals(urls, [
391
+ `${base}start`,
392
+ `${base}nested/page`,
393
+ `${base}nested/document`
394
+ ]);
395
+ } finally {
396
+ globalThis.fetch = originalFetch;
397
+ }
398
+ });
399
+ test("getAuthenticatedDocumentLoader() validates alternate addresses", async () => {
400
+ esm_default.mockGlobal();
401
+ let privateRequests = 0;
402
+ const url = "https://8.8.8.8/alternate";
403
+ esm_default.get(url, new Response("", { headers: {
404
+ "Content-Type": "text/plain",
405
+ Link: "<http://127.0.0.1/private>; rel=\"alternate\"; type=\"application/activity+json\""
406
+ } }));
407
+ esm_default.get("http://127.0.0.1/private", () => {
408
+ privateRequests++;
409
+ return Response.json({ private: true });
410
+ });
411
+ try {
412
+ const loader = getAuthenticatedDocumentLoader({
413
+ keyId: new URL("https://example.com/key2"),
414
+ privateKey: rsaPrivateKey2
415
+ });
416
+ await assertRejects(() => loader(url), UrlError);
417
+ assertEquals(privateRequests, 0);
418
+ } finally {
419
+ esm_default.hardReset();
420
+ }
421
+ });
422
+ test("getAuthenticatedDocumentLoader() rejects cancellation before fetching", async () => {
423
+ esm_default.mockGlobal();
424
+ let requests = 0;
425
+ const url = "https://example.com/pre-aborted-alternate";
426
+ esm_default.get(url, () => {
427
+ requests++;
428
+ return Response.json({ done: true });
429
+ });
430
+ try {
431
+ const loader = getAuthenticatedDocumentLoader({
432
+ keyId: new URL("https://example.com/key2"),
433
+ privateKey: rsaPrivateKey2
434
+ }, { allowPrivateAddress: true });
435
+ const controller = new AbortController();
436
+ controller.abort();
437
+ await rejects(loader(url, { signal: controller.signal }), { name: "AbortError" });
438
+ deepStrictEqual(requests, 0);
439
+ } finally {
440
+ esm_default.hardReset();
441
+ }
442
+ });
201
443
  //#endregion
202
444
  export {};
@@ -1,6 +1,6 @@
1
1
  const { Temporal } = require("@js-temporal/polyfill");
2
2
  const { URLPattern } = require("urlpattern-polyfill");
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
- const require_kv_cache = require("../kv-cache-BV20L2x4.cjs");
4
+ const require_kv_cache = require("../kv-cache-4PJQF26S.cjs");
5
5
  exports.getAuthenticatedDocumentLoader = require_kv_cache.getAuthenticatedDocumentLoader;
6
6
  exports.kvCache = require_kv_cache.kvCache;
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-Bt0eg_Lv.cjs";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-D3xVy8Yy.cjs";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
@@ -1,3 +1,3 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-XucBiDNd.js";
2
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../mod-PD3bZZ5j.js";
3
3
  export { getAuthenticatedDocumentLoader, kvCache };
package/dist/utils/mod.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-OZi_bDmv.js";
3
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "../kv-cache-CnJnTcyv.js";
4
4
  export { getAuthenticatedDocumentLoader, kvCache };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/fedify",
3
- "version": "2.0.29-dev.2121+c46fb9dd",
3
+ "version": "2.0.29-dev.2143+bbbfea55",
4
4
  "description": "An ActivityPub server framework",
5
5
  "keywords": [
6
6
  "ActivityPub",
@@ -144,9 +144,9 @@
144
144
  "uri-template-router": "^1.0.0",
145
145
  "url-template": "^3.1.1",
146
146
  "urlpattern-polyfill": "^10.1.0",
147
- "@fedify/vocab-runtime": "2.0.29-dev.2121+c46fb9dd",
148
- "@fedify/vocab": "2.0.29-dev.2121+c46fb9dd",
149
- "@fedify/webfinger": "2.0.29-dev.2121+c46fb9dd"
147
+ "@fedify/vocab-runtime": "2.0.29-dev.2143+bbbfea55",
148
+ "@fedify/webfinger": "2.0.29-dev.2143+bbbfea55",
149
+ "@fedify/vocab": "2.0.29-dev.2143+bbbfea55"
150
150
  },
151
151
  "devDependencies": {
152
152
  "@std/assert": "npm:@jsr/std__assert@^0.226.0",
@@ -159,7 +159,7 @@
159
159
  "typescript": "^6.0.0",
160
160
  "wrangler": "^4.17.0",
161
161
  "@fedify/fixture": "2.0.0",
162
- "@fedify/vocab-tools": "^2.0.29-dev.2121+c46fb9dd"
162
+ "@fedify/vocab-tools": "^2.0.29-dev.2143+bbbfea55"
163
163
  },
164
164
  "scripts": {
165
165
  "build:self": "tsdown",
@@ -1,56 +0,0 @@
1
- import "@js-temporal/polyfill";
2
- import "urlpattern-polyfill";
3
- globalThis.addEventListener = () => {};
4
- import { o as validateCryptoKey } from "./key-BMxcDQ7Z.mjs";
5
- import { n as doubleKnock } from "./http-DscVc8UW.mjs";
6
- import { curry } from "es-toolkit";
7
- import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
- import { getLogger } from "@logtape/logtape";
9
- //#region src/utils/docloader.ts
10
- const logger = getLogger([
11
- "fedify",
12
- "utils",
13
- "docloader"
14
- ]);
15
- /**
16
- * Gets an authenticated {@link DocumentLoader} for the given identity.
17
- * Note that an authenticated document loader intentionally does not cache
18
- * the fetched documents.
19
- * @param identity The identity to get the document loader for.
20
- * The actor's key pair.
21
- * @param options The options for the document loader.
22
- * @returns The authenticated document loader.
23
- * @throws {TypeError} If the key is invalid or unsupported.
24
- * @since 0.4.0
25
- */
26
- function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, userAgent, specDeterminer, tracerProvider } = {}) {
27
- validateCryptoKey(identity.privateKey);
28
- async function load(url, options) {
29
- await validateUrl(url);
30
- return getRemoteDocument(url, await doubleKnock(createActivityPubRequest(url, { userAgent }), identity, {
31
- specDeterminer,
32
- log: curry(logRequest)(logger),
33
- tracerProvider,
34
- signal: options?.signal,
35
- validateRedirect: validateUrl
36
- }), load);
37
- }
38
- async function validateUrl(url) {
39
- if (!allowPrivateAddress) try {
40
- await validatePublicUrl(url);
41
- } catch (error) {
42
- if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
43
- url,
44
- error
45
- });
46
- else logger.error("Disallowed private URL: {url}", {
47
- url,
48
- error
49
- });
50
- throw error;
51
- }
52
- }
53
- return load;
54
- }
55
- //#endregion
56
- export { getAuthenticatedDocumentLoader as t };