@fedify/fedify 2.4.0-dev.2149 → 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 (101) hide show
  1. package/dist/{builder-W2fIPRjv.mjs → builder-CLaS4-yA.mjs} +56 -3
  2. package/dist/{client-DpnpgjyX.mjs → client-CEnNz8O7.mjs} +7 -3
  3. package/dist/compat/mod.d.cts +1 -1
  4. package/dist/compat/mod.d.ts +1 -1
  5. package/dist/compat/transformers.test.mjs +1 -1
  6. package/dist/{compound-proof-B1QH25S6.mjs → compound-proof-D7K94KPz.mjs} +216 -4
  7. package/dist/{context-DfSVPcJ8.mjs → context-B2rlkkyJ.mjs} +9 -2
  8. package/dist/{context-CrDDVrbF.d.cts → context-BMggDR-l.d.cts} +394 -5
  9. package/dist/{context-B2C8nuN3.d.ts → context-exYu7gDU.d.ts} +394 -5
  10. package/dist/{deno-yXpYZm4K.mjs → deno-D1gcWWqB.mjs} +1 -1
  11. package/dist/{dist-BQvArojP.mjs → dist-DJQM4u7i.mjs} +2 -1
  12. package/dist/docloader-BMqr3hji.mjs +79 -0
  13. package/dist/federation/builder.test.mjs +1 -1
  14. package/dist/federation/compound-fanout.test.mjs +3 -3
  15. package/dist/federation/handler-compound-proof.test.mjs +4 -4
  16. package/dist/federation/handler.test.mjs +6 -6
  17. package/dist/federation/hashlink-media.test.mjs +2 -2
  18. package/dist/federation/idempotency.test.mjs +2 -2
  19. package/dist/federation/metrics.test.mjs +1 -1
  20. package/dist/federation/middleware.test.mjs +187 -10
  21. package/dist/federation/mod.cjs +1 -1
  22. package/dist/federation/mod.d.cts +3 -3
  23. package/dist/federation/mod.d.ts +3 -3
  24. package/dist/federation/mod.js +1 -1
  25. package/dist/federation/mq.test.mjs +98 -69
  26. package/dist/federation/outgoing-proof.test.mjs +5 -5
  27. package/dist/federation/portable-actor.test.mjs +3 -3
  28. package/dist/federation/portable-chain.test.mjs +3 -3
  29. package/dist/federation/portable-collection.test.d.mts +2 -0
  30. package/dist/federation/portable-collection.test.mjs +768 -0
  31. package/dist/federation/portable-gateway-keys.test.mjs +4 -4
  32. package/dist/federation/portable-inbox.test.mjs +4 -4
  33. package/dist/federation/portable.test.mjs +4 -4
  34. package/dist/federation/send.test.mjs +84 -3
  35. package/dist/federation/tasks/enqueue.test.mjs +3 -3
  36. package/dist/federation/tasks/tasks.test.mjs +4 -4
  37. package/dist/federation/temporal.test.mjs +1 -1
  38. package/dist/federation/webfinger.test.mjs +2 -2
  39. package/dist/{federation-D4j-ZXUN.cjs → federation-2e8XrN7q.cjs} +700 -70
  40. package/dist/{http-C_jobbLz.cjs → http-BObQoB0j.cjs} +18 -3
  41. package/dist/{http-BoH-e8sR.mjs → http-CGcLv8rh.mjs} +9 -5
  42. package/dist/{http-CBRurnlR.js → http-Dgtt45-K.js} +13 -4
  43. package/dist/{key-BtO7WCfP.mjs → key-CPM4r6tP.mjs} +2 -2
  44. package/dist/{kv-cache-E0es0w4R.js → kv-cache-BweyIZTb.js} +35 -9
  45. package/dist/{kv-cache-BxUqVENn.cjs → kv-cache-D2fnB8qu.cjs} +34 -8
  46. package/dist/{kv-cache-CMuTXrkC.mjs → kv-cache-bdPqTR1k.mjs} +1 -1
  47. package/dist/{ld-YYBfDbe_.mjs → ld-lU_ZqzsQ.mjs} +4 -4
  48. package/dist/{metrics-DMqdDud_.mjs → metrics-B41pRPfd.mjs} +1 -1
  49. package/dist/{middleware-D6uiL1Tw.js → middleware-BYWyYjmX.js} +702 -72
  50. package/dist/{middleware-C97UJpkA.mjs → middleware-TrCVULgs.mjs} +1 -1
  51. package/dist/{middleware-CBGR92fz.mjs → middleware-xdukzV3x.mjs} +432 -81
  52. package/dist/{mod-C06GoL5Z.d.ts → mod-CnGtADvO.d.ts} +1 -1
  53. package/dist/{mod-CZetqT5h.d.ts → mod-DaiBrWDW.d.ts} +2 -0
  54. package/dist/{mod-B7SfEqIP.d.cts → mod-Decmur1U.d.cts} +1 -1
  55. package/dist/{mod-D4g9ffEn.d.cts → mod-Mt4aO3DX.d.cts} +2 -0
  56. package/dist/mod.cjs +5 -5
  57. package/dist/mod.d.cts +5 -5
  58. package/dist/mod.d.ts +5 -5
  59. package/dist/mod.js +5 -5
  60. package/dist/nodeinfo/client.test.mjs +49 -1
  61. package/dist/nodeinfo/handler.test.mjs +2 -2
  62. package/dist/nodeinfo/mod.cjs +1 -1
  63. package/dist/nodeinfo/mod.js +1 -1
  64. package/dist/{owner-Cn6CFYBy.mjs → owner-D63yPo_i.mjs} +3 -3
  65. package/dist/{portable-collection-CWgXwOdR.cjs → portable-collection-B8ojoQ50.cjs} +1 -1
  66. package/dist/{portable-collection-CyhFWEB-.js → portable-collection-C3xEzYri.js} +1 -1
  67. package/dist/{portable-collection-B3swTTja.mjs → portable-collection-_lZg-osX.mjs} +2 -2
  68. package/dist/{portable-key-HM5Siym0.mjs → portable-key-D8uWZF7b.mjs} +1 -1
  69. package/dist/{proof-DWkXfZIU.mjs → proof-C2YwD8a_.mjs} +10 -5
  70. package/dist/{send-BQDT_8bc.mjs → send-D9SGhxTB.mjs} +18 -4
  71. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  72. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  73. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  74. package/dist/sig/compound-proof.test.mjs +2 -2
  75. package/dist/sig/http.test.mjs +42 -2
  76. package/dist/sig/key.test.mjs +1 -1
  77. package/dist/sig/ld.test.mjs +5 -2
  78. package/dist/sig/mod.cjs +2 -2
  79. package/dist/sig/mod.js +2 -2
  80. package/dist/sig/owner.test.mjs +1 -1
  81. package/dist/sig/portable-collection.test.mjs +2 -2
  82. package/dist/sig/portable-key.test.mjs +6 -6
  83. package/dist/sig/proof.test.mjs +3 -3
  84. package/dist/{tasks-dbCShlCb.mjs → tasks-IG3pHfvZ.mjs} +1 -1
  85. package/dist/{temporal-CFI__wjQ.mjs → temporal-q6FSekcN.mjs} +1 -1
  86. package/dist/testing/context.test.mjs +1 -1
  87. package/dist/testing/kv.test.mjs +3 -3
  88. package/dist/testing/mod.d.mts +393 -4
  89. package/dist/testing/mod.mjs +4 -4
  90. package/dist/{testing-otQhu4aD.mjs → testing-C1itIjqW.mjs} +3 -3
  91. package/dist/{types-C_cNlNVK.js → types-_S2_MeLF.js} +7 -3
  92. package/dist/{types-Be2zbyeG.cjs → types-poIao40f.cjs} +7 -3
  93. package/dist/utils/docloader.test.mjs +297 -2
  94. package/dist/utils/kv-cache.test.mjs +1 -1
  95. package/dist/utils/mod.cjs +1 -1
  96. package/dist/utils/mod.d.cts +1 -1
  97. package/dist/utils/mod.d.ts +1 -1
  98. package/dist/utils/mod.js +1 -1
  99. package/dist/vocab/cjs.test.mjs +1 -1
  100. package/package.json +7 -7
  101. package/dist/docloader-C4h6PL7m.mjs +0 -53
@@ -16,7 +16,7 @@ json_canon = require_rolldown_runtime.__toESM(json_canon, 1);
16
16
  let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
17
17
  //#region deno.json
18
18
  var name = "@fedify/fedify";
19
- var version = "2.4.0-dev.2149+e2302034";
19
+ var version = "2.4.0-dev.2157+b76e5888";
20
20
  //#endregion
21
21
  //#region src/sig/portable-key-id.ts
22
22
  const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
@@ -5804,7 +5804,7 @@ function normalizeContextReference(reference, baseUrl) {
5804
5804
  /** @internal */
5805
5805
  function isInvalidUrlTypeError(error) {
5806
5806
  const code = error.code;
5807
- return error instanceof TypeError && (code === "ERR_INVALID_URL" || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
5807
+ return error instanceof TypeError && (code === "ERR_INVALID_URL" || error.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
5808
5808
  }
5809
5809
  async function applyGraphAliasContext(state, context, documentLoader, remoteContextCache, baseUrl = null, processingContexts = /* @__PURE__ */ new Set()) {
5810
5810
  if (context === null) return createGraphAliasContextState();
@@ -7300,6 +7300,11 @@ function hasValidPortableProofShape(proofValue) {
7300
7300
  return Array.isArray(values) && values.length === 1 && !(isJsonLdNode(values[0]) && "@list" in values[0]);
7301
7301
  });
7302
7302
  }
7303
+ /**
7304
+ * Classifies an expanded JSON-LD node into an FEP-2277 core type by the
7305
+ * properties it has.
7306
+ * @internal
7307
+ */
7303
7308
  function classifyFep2277CoreType(node) {
7304
7309
  if (FEP_2277_ACTOR_PROPERTIES.every((property) => property in node)) return "actor";
7305
7310
  if (`${SECURITY_NAMESPACE}publicKeyMultibase` in node) return "verificationMethod";
@@ -8903,6 +8908,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
8903
8908
  failure = result;
8904
8909
  failureAlgorithm = algorithm;
8905
8910
  };
8911
+ let body = null;
8912
+ let digestValid = null;
8906
8913
  for (const sigName of signatureNames) {
8907
8914
  if (!signatures[sigName]) {
8908
8915
  setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
@@ -8954,7 +8961,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
8954
8961
  setFailure(invalidSignatureResult(keyId));
8955
8962
  continue;
8956
8963
  }
8957
- if (!await verifyRfc9421ContentDigest(contentDigestHeader, await request.arrayBuffer())) {
8964
+ body ??= await request.arrayBuffer();
8965
+ digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
8966
+ if (!digestValid) {
8958
8967
  logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
8959
8968
  setFailure(invalidSignatureResult(keyId));
8960
8969
  continue;
@@ -9310,6 +9319,12 @@ Object.defineProperty(exports, "attachSignature", {
9310
9319
  return attachSignature;
9311
9320
  }
9312
9321
  });
9322
+ Object.defineProperty(exports, "classifyFep2277CoreType", {
9323
+ enumerable: true,
9324
+ get: function() {
9325
+ return classifyFep2277CoreType;
9326
+ }
9327
+ });
9313
9328
  Object.defineProperty(exports, "compactJsonLd", {
9314
9329
  enumerable: true,
9315
9330
  get: function() {
@@ -1,12 +1,12 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-yXpYZm4K.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DMqdDud_.mjs";
4
+ import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B41pRPfd.mjs";
6
6
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-CPVqki78.mjs";
7
7
  import { f as parseKeyIdString } from "./portable-key-id-Bg259c4Z.mjs";
8
- import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-BtO7WCfP.mjs";
9
- import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-HM5Siym0.mjs";
8
+ import { a as fetchKeyDetailed, c as validateCryptoKey, t as bypassKeyCacheReads } from "./key-CPM4r6tP.mjs";
9
+ import { n as resolvePortableActorKey, r as verifyPortableGatewayKeyDocument } from "./portable-key-D8uWZF7b.mjs";
10
10
  import { getLogger } from "@logtape/logtape";
11
11
  import { CryptographicKey } from "@fedify/vocab";
12
12
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -804,6 +804,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
804
804
  failure = result;
805
805
  failureAlgorithm = algorithm;
806
806
  };
807
+ let body = null;
808
+ let digestValid = null;
807
809
  for (const sigName of signatureNames) {
808
810
  if (!signatures[sigName]) {
809
811
  setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
@@ -855,7 +857,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
855
857
  setFailure(invalidSignatureResult(keyId));
856
858
  continue;
857
859
  }
858
- if (!await verifyRfc9421ContentDigest(contentDigestHeader, await request.arrayBuffer())) {
860
+ body ??= await request.arrayBuffer();
861
+ digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
862
+ if (!digestValid) {
859
863
  logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
860
864
  setFailure(invalidSignatureResult(keyId));
861
865
  continue;
@@ -14,7 +14,7 @@ import serialize from "json-canon";
14
14
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
15
15
  //#region deno.json
16
16
  var name = "@fedify/fedify";
17
- var version = "2.4.0-dev.2149+e2302034";
17
+ var version = "2.4.0-dev.2157+b76e5888";
18
18
  //#endregion
19
19
  //#region src/sig/portable-key-id.ts
20
20
  const COMPATIBLE_KEY_ID_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
@@ -2238,7 +2238,7 @@ function normalizeContextReference(reference, baseUrl) {
2238
2238
  /** @internal */
2239
2239
  function isInvalidUrlTypeError(error) {
2240
2240
  const code = error.code;
2241
- return error instanceof TypeError && (code === "ERR_INVALID_URL" || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
2241
+ return error instanceof TypeError && (code === "ERR_INVALID_URL" || error.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
2242
2242
  }
2243
2243
  async function applyGraphAliasContext(state, context, documentLoader, remoteContextCache, baseUrl = null, processingContexts = /* @__PURE__ */ new Set()) {
2244
2244
  if (context === null) return createGraphAliasContextState();
@@ -3733,6 +3733,11 @@ function hasValidPortableProofShape(proofValue) {
3733
3733
  return Array.isArray(values) && values.length === 1 && !(isJsonLdNode(values[0]) && "@list" in values[0]);
3734
3734
  });
3735
3735
  }
3736
+ /**
3737
+ * Classifies an expanded JSON-LD node into an FEP-2277 core type by the
3738
+ * properties it has.
3739
+ * @internal
3740
+ */
3736
3741
  function classifyFep2277CoreType(node) {
3737
3742
  if (FEP_2277_ACTOR_PROPERTIES.every((property) => property in node)) return "actor";
3738
3743
  if (`${SECURITY_NAMESPACE}publicKeyMultibase` in node) return "verificationMethod";
@@ -5335,6 +5340,8 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
5335
5340
  failure = result;
5336
5341
  failureAlgorithm = algorithm;
5337
5342
  };
5343
+ let body = null;
5344
+ let digestValid = null;
5338
5345
  for (const sigName of signatureNames) {
5339
5346
  if (!signatures[sigName]) {
5340
5347
  setFailure(invalidSignatureResult(parseKeyId(signatureInputs[sigName]?.keyId)));
@@ -5386,7 +5393,9 @@ async function verifyRequestRfc9421(request, span, metricsContext, { documentLoa
5386
5393
  setFailure(invalidSignatureResult(keyId));
5387
5394
  continue;
5388
5395
  }
5389
- if (!await verifyRfc9421ContentDigest(contentDigestHeader, await request.arrayBuffer())) {
5396
+ body ??= await request.arrayBuffer();
5397
+ digestValid ??= await verifyRfc9421ContentDigest(contentDigestHeader, body);
5398
+ if (!digestValid) {
5390
5399
  logger.debug("Failed to verify; Content-Digest verification failed.", { contentDigest: contentDigestHeader });
5391
5400
  setFailure(invalidSignatureResult(keyId));
5392
5401
  continue;
@@ -5712,4 +5721,4 @@ function timingSafeEqual(a, b) {
5712
5721
  return result === 0;
5713
5722
  }
5714
5723
  //#endregion
5715
- export { recordCollectionRequest as $, isClearlyMalformedContextReference as A, fetchKeyDetailed as B, attachSignature as C, name as Ct, getNormalizationContextLoader as D, detachSignature as E, verifySignature as F, getDurationMs as G, importJwk as H, wrapContextLoaderForJsonLd as I, instrumentDocumentLoader as J, getFederationMetrics as K, exportJwk as L, signJsonLd as M, verifyCompactJsonLd as N, hasSignature as O, verifyJsonLd as P, recordCollectionPageItems as Q, fetchActorDocument as R, assertSafeJsonLd as S, isSamePublicKey as St, createSignature as T, validateCryptoKey as U, generateCryptoKeyPair as V, verifyKeyOwnership as W, recordCircuitBreakerStateChange as X, isAbortError$1 as Y, recordCollectionDispatchDuration as Z, verifyPortableObjectProofPolicy as _, hasPortableActor as _t, signRequest as a, recordOutboxEnqueue as at, normalizeOutgoingActivityJsonLd as b, isPortableKeyId as bt, fetchPortableGatewayKey as c, preloadedOnlyDocumentLoader as ct, createProof as d, parseAcceptSignature as dt, recordCollectionTotalItems as et, hasProofLike as f, validateAcceptSignature as ft, verifyPortableObjectProof as g, getRawCanonicalPortableId as gt, verifyObject as h, getPortableDid as ht, selectRequestSignature as i, recordOutboxActivity as it, isInvalidUrlTypeError as j, hasSignatureLike as k, resolvePortableActorKey as l, formatAcceptSignature as lt, verifyMapLocalProof as m, getGatewayKeyBase as mt, listRequestSignatures as n, recordFanoutRecipients as nt, verifyRequest as o, recordWebFingerHandle as ot, signObject as p, getCanonicalPortableId as pt, getRemoteHost as q, parseRfc9421SignatureInput as r, recordInboxActivity as rt, verifyRequestDetailed as s, registerQueueDepthGauge as st, doubleKnock as t, recordDocumentCache as tt, InvalidPortableObjectIdError as u, fulfillAcceptSignature as ut, verifyPortableObjectProofWithRoot as v, isCompatibleKeyId as vt, compactJsonLd as w, version as wt, InvalidContextReferenceError as x, isPortableUri as xt, verifyProof as y, isPortableId as yt, fetchKey as z };
5724
+ export { recordCollectionPageItems as $, hasSignatureLike as A, fetchKey as B, assertSafeJsonLd as C, isSamePublicKey as Ct, detachSignature as D, createSignature as E, verifyJsonLd as F, verifyKeyOwnership as G, generateCryptoKeyPair as H, verifySignature as I, getRemoteHost as J, getDurationMs as K, wrapContextLoaderForJsonLd as L, isInvalidUrlTypeError as M, signJsonLd as N, getNormalizationContextLoader as O, verifyCompactJsonLd as P, recordCollectionDispatchDuration as Q, exportJwk as R, InvalidContextReferenceError as S, isPortableUri as St, compactJsonLd as T, version as Tt, importJwk as U, fetchKeyDetailed as V, validateCryptoKey as W, isAbortError$1 as X, instrumentDocumentLoader as Y, recordCircuitBreakerStateChange as Z, verifyPortableObjectProof as _, getRawCanonicalPortableId as _t, signRequest as a, recordOutboxActivity as at, verifyProof as b, isPortableId as bt, fetchPortableGatewayKey as c, registerQueueDepthGauge as ct, classifyFep2277CoreType as d, fulfillAcceptSignature as dt, recordCollectionRequest as et, createProof as f, parseAcceptSignature as ft, verifyObject as g, getPortableDid as gt, verifyMapLocalProof as h, getGatewayKeyBase as ht, selectRequestSignature as i, recordInboxActivity as it, isClearlyMalformedContextReference as j, hasSignature as k, resolvePortableActorKey as l, preloadedOnlyDocumentLoader as lt, signObject as m, getCanonicalPortableId as mt, listRequestSignatures as n, recordDocumentCache as nt, verifyRequest as o, recordOutboxEnqueue as ot, hasProofLike as p, validateAcceptSignature as pt, getFederationMetrics as q, parseRfc9421SignatureInput as r, recordFanoutRecipients as rt, verifyRequestDetailed as s, recordWebFingerHandle as st, doubleKnock as t, recordCollectionTotalItems as tt, InvalidPortableObjectIdError as u, formatAcceptSignature as ut, verifyPortableObjectProofPolicy as v, hasPortableActor as vt, attachSignature as w, name as wt, normalizeOutgoingActivityJsonLd as x, isPortableKeyId as xt, verifyPortableObjectProofWithRoot as y, isCompatibleKeyId as yt, fetchActorDocument as z };
@@ -1,8 +1,8 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-yXpYZm4K.mjs";
5
- import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-DMqdDud_.mjs";
4
+ import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
5
+ import { _ as recordKeyLookup, n as getDurationMs, t as classifyFetchError } from "./metrics-B41pRPfd.mjs";
6
6
  import { c as isPortableId, f as parseKeyIdString, o as isCompatibleKeyId, s as isPortableActorDocument, u as isPortableUri } from "./portable-key-id-Bg259c4Z.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { CryptographicKey, Multikey, Object as Object$1, isActor } from "@fedify/vocab";
@@ -1,8 +1,8 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
- import { U as validateCryptoKey, t as doubleKnock, tt as recordDocumentCache } from "./http-CBRurnlR.js";
3
+ import { W as validateCryptoKey, nt as recordDocumentCache, t as doubleKnock } from "./http-Dgtt45-K.js";
4
4
  import { getLogger } from "@logtape/logtape";
5
- import { UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
5
+ import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, preloadedContexts, validatePublicUrl } from "@fedify/vocab-runtime";
6
6
  import { curry } from "es-toolkit";
7
7
  //#region src/utils/docloader.ts
8
8
  const logger$1 = getLogger([
@@ -10,10 +10,13 @@ const logger$1 = getLogger([
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,29 +26,52 @@ const logger$1 = getLogger([
23
26
  */
24
27
  function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
25
28
  validateCryptoKey(identity.privateKey);
26
- async function load(url, options) {
27
- await validateUrl(url, options);
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, options);
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
  maxRedirection,
30
43
  specDeterminer,
31
44
  log: curry(logRequest)(logger$1),
32
45
  tracerProvider,
33
46
  signal: options?.signal,
34
- validateRedirect: (url) => validateUrl(url, options)
35
- }), load, options);
47
+ validateRedirect: async (nextUrl) => {
48
+ follow(nextUrl);
49
+ await validateUrl(nextUrl, options);
50
+ visited.add(nextUrl);
51
+ currentUrl = nextUrl;
52
+ }
53
+ });
54
+ return getRemoteDocument(currentUrl, response, (alternateUrl) => {
55
+ follow(alternateUrl);
56
+ return load(alternateUrl, options, redirected, visited);
57
+ }, options);
36
58
  }
37
59
  async function validateUrl(url, options) {
38
60
  if (!allowPrivateAddress) try {
39
61
  await validatePublicUrl(url);
40
62
  } catch (error) {
41
- if (error instanceof UrlError) logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
63
+ if (error instanceof UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
64
+ url,
65
+ error
66
+ });
67
+ else logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
42
68
  url,
43
69
  error
44
70
  });
45
71
  throw error;
46
72
  }
47
73
  }
48
- return load;
74
+ return (url, options) => load(url, options);
49
75
  }
50
76
  //#endregion
51
77
  //#region src/utils/kv-cache.ts
@@ -1,5 +1,5 @@
1
1
  const { URLPattern } = require("urlpattern-polyfill");
2
- const require_http = require("./http-C_jobbLz.cjs");
2
+ const require_http = require("./http-BObQoB0j.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
5
5
  let es_toolkit = require("es-toolkit");
@@ -9,10 +9,13 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
9
9
  "utils",
10
10
  "docloader"
11
11
  ]);
12
+ const DEFAULT_MAX_REDIRECTION = 20;
12
13
  /**
13
14
  * Gets an authenticated {@link DocumentLoader} for the given identity.
14
15
  * Note that an authenticated document loader intentionally does not cache
15
16
  * the fetched documents.
17
+ * At most 20 HTTP redirects and alternate document links are followed in total
18
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
16
19
  * @param identity The identity to get the document loader for.
17
20
  * The actor's key pair.
18
21
  * @param options The options for the document loader.
@@ -22,29 +25,52 @@ const logger$1 = (0, _logtape_logtape.getLogger)([
22
25
  */
23
26
  function getAuthenticatedDocumentLoader(identity, { allowPrivateAddress, maxRedirection, userAgent, specDeterminer, tracerProvider } = {}) {
24
27
  require_http.validateCryptoKey(identity.privateKey);
25
- async function load(url, options) {
26
- await validateUrl(url, options);
27
- return (0, _fedify_vocab_runtime.getRemoteDocument)(url, await require_http.doubleKnock((0, _fedify_vocab_runtime.createActivityPubRequest)(url, { userAgent }), identity, {
28
+ async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
29
+ options?.signal?.throwIfAborted();
30
+ let currentUrl = new URL(url).href;
31
+ await validateUrl(currentUrl, options);
32
+ visited.add(currentUrl);
33
+ const originalRequest = (0, _fedify_vocab_runtime.createActivityPubRequest)(currentUrl, { userAgent });
34
+ function follow(nextUrl) {
35
+ options?.signal?.throwIfAborted();
36
+ if (redirected >= DEFAULT_MAX_REDIRECTION) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
37
+ if (visited.has(nextUrl)) throw new _fedify_vocab_runtime.FetchError(currentUrl, `Redirect loop detected: ${nextUrl}`);
38
+ redirected++;
39
+ }
40
+ const response = await require_http.doubleKnock(originalRequest, identity, {
28
41
  maxRedirection,
29
42
  specDeterminer,
30
43
  log: (0, es_toolkit.curry)(_fedify_vocab_runtime.logRequest)(logger$1),
31
44
  tracerProvider,
32
45
  signal: options?.signal,
33
- validateRedirect: (url) => validateUrl(url, options)
34
- }), load, options);
46
+ validateRedirect: async (nextUrl) => {
47
+ follow(nextUrl);
48
+ await validateUrl(nextUrl, options);
49
+ visited.add(nextUrl);
50
+ currentUrl = nextUrl;
51
+ }
52
+ });
53
+ return (0, _fedify_vocab_runtime.getRemoteDocument)(currentUrl, response, (alternateUrl) => {
54
+ follow(alternateUrl);
55
+ return load(alternateUrl, options, redirected, visited);
56
+ }, options);
35
57
  }
36
58
  async function validateUrl(url, options) {
37
59
  if (!allowPrivateAddress) try {
38
60
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url);
39
61
  } catch (error) {
40
- if (error instanceof _fedify_vocab_runtime.UrlError) logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
62
+ if (error instanceof _fedify_vocab_runtime.UrlError) if (error.reason === "dns") logger$1.debug("DNS lookup failed for {url}", {
63
+ url,
64
+ error
65
+ });
66
+ else logger$1[options?.suppressError ? "warn" : "error"]("Disallowed private URL: {url}", {
41
67
  url,
42
68
  error
43
69
  });
44
70
  throw error;
45
71
  }
46
72
  }
47
- return load;
73
+ return (url, options) => load(url, options);
48
74
  }
49
75
  //#endregion
50
76
  //#region src/utils/kv-cache.ts
@@ -1,7 +1,7 @@
1
1
  import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { p as recordDocumentCache } from "./metrics-DMqdDud_.mjs";
4
+ import { p as recordDocumentCache } from "./metrics-B41pRPfd.mjs";
5
5
  import { getLogger } from "@logtape/logtape";
6
6
  import { preloadedContexts } from "@fedify/vocab-runtime";
7
7
  //#region src/utils/kv-cache.ts
@@ -1,9 +1,9 @@
1
1
  import "temporal-polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-yXpYZm4K.mjs";
5
- import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-DMqdDud_.mjs";
6
- import { c as validateCryptoKey, i as fetchKey, t as bypassKeyCacheReads } from "./key-BtO7WCfP.mjs";
4
+ import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
5
+ import { n as getDurationMs, r as getFederationMetrics, s as measureSignatureKeyFetch } from "./metrics-B41pRPfd.mjs";
6
+ import { c as validateCryptoKey, i as fetchKey, t as bypassKeyCacheReads } from "./key-CPM4r6tP.mjs";
7
7
  import { getLogger } from "@logtape/logtape";
8
8
  import { Activity, CryptographicKey, Object as Object$1, getTypeId } from "@fedify/vocab";
9
9
  import { SpanStatusCode, trace } from "@opentelemetry/api";
@@ -206,7 +206,7 @@ function normalizeContextReference(reference, baseUrl) {
206
206
  /** @internal */
207
207
  function isInvalidUrlTypeError(error) {
208
208
  const code = error.code;
209
- return error instanceof TypeError && (code === "ERR_INVALID_URL" || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
209
+ return error instanceof TypeError && (code === "ERR_INVALID_URL" || error.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(error.message) || / cannot be parsed as a URL\.?$/.test(error.message));
210
210
  }
211
211
  async function applyGraphAliasContext(state, context, documentLoader, remoteContextCache, baseUrl = null, processingContexts = /* @__PURE__ */ new Set()) {
212
212
  if (context === null) return createGraphAliasContextState();
@@ -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-yXpYZm4K.mjs";
4
+ import { n as version, t as name } from "./deno-D1gcWWqB.mjs";
5
5
  import { metrics } from "@opentelemetry/api";
6
6
  import { FetchError } from "@fedify/vocab-runtime";
7
7
  //#region src/federation/metrics.ts