@fedify/fedify 2.2.16 → 2.2.17

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 (45) hide show
  1. package/dist/{builder-2WicIXl5.mjs → builder-a-kl29PE.mjs} +2 -2
  2. package/dist/compat/transformers.test.mjs +1 -1
  3. package/dist/{deno-BCQztXLl.mjs → deno-Cya0rbEr.mjs} +1 -1
  4. package/dist/{docloader-NG6Mmr-J.mjs → docloader-Dz0Z1Y07.mjs} +2 -2
  5. package/dist/federation/builder.test.mjs +1 -1
  6. package/dist/federation/handler.test.mjs +5 -5
  7. package/dist/federation/idempotency.test.mjs +2 -2
  8. package/dist/federation/middleware.test.mjs +6 -6
  9. package/dist/federation/mod.cjs +1 -1
  10. package/dist/federation/mod.js +1 -1
  11. package/dist/federation/send.test.mjs +3 -3
  12. package/dist/federation/temporal.test.mjs +1 -1
  13. package/dist/federation/webfinger.test.mjs +1 -1
  14. package/dist/{http-B7mxZjxT.cjs → http-C_aA2Zx1.cjs} +103 -9
  15. package/dist/{http-DK5sOqk3.js → http-DkzPSl5o.js} +104 -10
  16. package/dist/{http-B72sZOJB.mjs → http-dobCLGG2.mjs} +2 -2
  17. package/dist/{key-DCQAjTMo.mjs → key-BMHNd0gP.mjs} +104 -10
  18. package/dist/{kv-cache-DCIQW7aM.cjs → kv-cache-Btp4kC_U.cjs} +1 -1
  19. package/dist/{kv-cache-BatvhvQ8.js → kv-cache-DVhfMtqE.js} +1 -1
  20. package/dist/{ld-37J55dno.mjs → ld-B_m0qw8j.mjs} +2 -2
  21. package/dist/{middleware-DOTXcNS-.mjs → middleware-5qLdPLro.mjs} +10 -10
  22. package/dist/{middleware-CPmKbXgW.mjs → middleware-BYxKsDHj.mjs} +1 -1
  23. package/dist/{middleware-Cqza2chY.js → middleware-BchkzbpG.js} +3 -3
  24. package/dist/{middleware-O_yyvaOf.cjs → middleware-C7mI18WE.cjs} +4 -4
  25. package/dist/{middleware-BZO3vnP1.cjs → middleware-ZKHfZ7zj.cjs} +1 -1
  26. package/dist/mod.cjs +4 -4
  27. package/dist/mod.js +4 -4
  28. package/dist/nodeinfo/handler.test.mjs +1 -1
  29. package/dist/{owner-Bou1yabn.mjs → owner-Dk85kXZz.mjs} +2 -2
  30. package/dist/{proof-BdrKA26Y.js → proof-Bz6IdT2p.js} +1 -1
  31. package/dist/{proof-ZUtbM4a6.cjs → proof-ClpE9zhD.cjs} +1 -1
  32. package/dist/{proof-BFSV3hJj.mjs → proof-DrGrnZVE.mjs} +2 -2
  33. package/dist/{send-DwQAYND-.mjs → send-Co-E7yJn.mjs} +2 -2
  34. package/dist/sig/http.test.mjs +2 -2
  35. package/dist/sig/key.test.mjs +198 -3
  36. package/dist/sig/ld.test.mjs +2 -2
  37. package/dist/sig/mod.cjs +2 -2
  38. package/dist/sig/mod.js +2 -2
  39. package/dist/sig/owner.test.mjs +1 -1
  40. package/dist/sig/proof.test.mjs +1 -1
  41. package/dist/{temporal-BWuGBFaX.mjs → temporal-Bnwa_2NC.mjs} +1 -1
  42. package/dist/utils/docloader.test.mjs +2 -2
  43. package/dist/utils/mod.cjs +1 -1
  44. package/dist/utils/mod.js +1 -1
  45. package/package.json +5 -5
@@ -2,7 +2,7 @@ import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
4
  import { n as RouterError, t as Router } from "./router-CrMLXoOr.mjs";
5
- import { n as version, t as name } from "./deno-BCQztXLl.mjs";
5
+ import { n as version, t as name } from "./deno-Cya0rbEr.mjs";
6
6
  import { t as ActivityListenerSet } from "./activity-listener-ell7W1s9.mjs";
7
7
  import { Tombstone, getTypeId } from "@fedify/vocab";
8
8
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
@@ -59,7 +59,7 @@ var FederationBuilderImpl = class {
59
59
  this.collectionTypeIds = {};
60
60
  }
61
61
  async build(options) {
62
- const { FederationImpl } = await import("./middleware-CPmKbXgW.mjs");
62
+ const { FederationImpl } = await import("./middleware-BYxKsDHj.mjs");
63
63
  const f = new FederationImpl(options);
64
64
  const trailingSlashInsensitiveValue = f.router.trailingSlashInsensitive;
65
65
  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 { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-DOTXcNS-.mjs";
7
+ import { n as FederationImpl, v as actorDehydrator, y as autoIdAssigner } from "../middleware-5qLdPLro.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.2.16";
6
+ var version = "2.2.17";
7
7
  //#endregion
8
8
  export { version as n, name as t };
@@ -1,8 +1,8 @@
1
1
  import "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { s as validateCryptoKey } from "./key-DCQAjTMo.mjs";
5
- import { n as doubleKnock } from "./http-B72sZOJB.mjs";
4
+ import { s as validateCryptoKey } from "./key-BMHNd0gP.mjs";
5
+ import { n as doubleKnock } from "./http-dobCLGG2.mjs";
6
6
  import { curry } from "es-toolkit";
7
7
  import { FetchError, UrlError, createActivityPubRequest, getRemoteDocument, logRequest, validatePublicUrl } from "@fedify/vocab-runtime";
8
8
  import { getLogger } from "@logtape/logtape";
@@ -7,7 +7,7 @@ import { r as assertExists } from "../std__assert-BVFj8-_V.mjs";
7
7
  import { t as assertThrows } from "../assert_throws-4NwKEy2q.mjs";
8
8
  import { t as assertStrictEquals } from "../assert_strict_equals-Dmjbg-bA.mjs";
9
9
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
10
- import { n as createFederationBuilder } from "../builder-2WicIXl5.mjs";
10
+ import { n as createFederationBuilder } from "../builder-a-kl29PE.mjs";
11
11
  import { test } from "@fedify/fixture";
12
12
  import { Activity, Note, Person } from "@fedify/vocab";
13
13
  //#region src/federation/builder.test.ts
@@ -7,13 +7,13 @@ import "../std__assert-BVFj8-_V.mjs";
7
7
  import { 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 { r as parseAcceptSignature } from "../accept-Dd__NiUL.mjs";
10
- import { a as generateCryptoKeyPair } from "../key-DCQAjTMo.mjs";
11
- import { s as signRequest } from "../http-B72sZOJB.mjs";
10
+ import { a as generateCryptoKeyPair } from "../key-BMHNd0gP.mjs";
11
+ import { s as signRequest } from "../http-dobCLGG2.mjs";
12
12
  import { a as rsaPrivateKey3, c as rsaPublicKey3, s as rsaPublicKey2 } from "../keys-BAK-tUlf.mjs";
13
13
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
14
- import { a as compactJsonLd, p as signJsonLd } from "../ld-37J55dno.mjs";
15
- import { r as signObject } from "../proof-BFSV3hJj.mjs";
16
- import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-DOTXcNS-.mjs";
14
+ import { a as compactJsonLd, p as signJsonLd } from "../ld-B_m0qw8j.mjs";
15
+ import { r as signObject } from "../proof-DrGrnZVE.mjs";
16
+ import { c as handleActor, d as handleInbox, f as handleObject, h as respondWithObjectIfAcceptable, l as handleCollection, m as respondWithObject, o as createFederation, p as handleOutbox, u as handleCustomCollection } from "../middleware-5qLdPLro.mjs";
17
17
  import { t as ActivityListenerSet } from "../activity-listener-ell7W1s9.mjs";
18
18
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
19
19
  import { Activity, Create, CryptographicKey, Multikey, Note, Person, Tombstone } from "@fedify/vocab";
@@ -5,8 +5,8 @@ import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
5
5
  import "../std__assert-BVFj8-_V.mjs";
6
6
  import { n as ed25519PrivateKey, r as ed25519PublicKey, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { r as signObject } from "../proof-BFSV3hJj.mjs";
9
- import { o as createFederation } from "../middleware-DOTXcNS-.mjs";
8
+ import { r as signObject } from "../proof-DrGrnZVE.mjs";
9
+ import { o as createFederation } from "../middleware-5qLdPLro.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
@@ -10,14 +10,14 @@ 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
12
  import { t as assertStrictEquals } from "../assert_strict_equals-Dmjbg-bA.mjs";
13
- import { l as verifyRequest, s as signRequest } from "../http-B72sZOJB.mjs";
13
+ import { l as verifyRequest, s as signRequest } from "../http-dobCLGG2.mjs";
14
14
  import { a as rsaPrivateKey3, c as rsaPublicKey3, i as rsaPrivateKey2, n as ed25519PrivateKey, r as ed25519PublicKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
15
- import { t as getAuthenticatedDocumentLoader } from "../docloader-NG6Mmr-J.mjs";
15
+ import { t as getAuthenticatedDocumentLoader } from "../docloader-Dz0Z1Y07.mjs";
16
16
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
17
- import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-37J55dno.mjs";
18
- import { t as doesActorOwnKey } from "../owner-Bou1yabn.mjs";
19
- import { i as verifyObject, r as signObject } from "../proof-BFSV3hJj.mjs";
20
- import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-DOTXcNS-.mjs";
17
+ import { a as compactJsonLd, h as verifyJsonLd, p as signJsonLd, s as detachSignature } from "../ld-B_m0qw8j.mjs";
18
+ import { t as doesActorOwnKey } from "../owner-Dk85kXZz.mjs";
19
+ import { i as verifyObject, r as signObject } from "../proof-DrGrnZVE.mjs";
20
+ import { i as KvSpecDeterminer, n as FederationImpl, o as createFederation, r as InboxContextImpl, t as ContextImpl } from "../middleware-5qLdPLro.mjs";
21
21
  import { createTestTracerProvider, mockDocumentLoader, test } from "@fedify/fixture";
22
22
  import * as vocab from "@fedify/vocab";
23
23
  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-O_yyvaOf.cjs");
5
+ const require_middleware = require("../middleware-C7mI18WE.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-Cqza2chY.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-BchkzbpG.js";
4
4
  import { isEqual } from "es-toolkit";
5
5
  //#region src/federation/kv.ts
6
6
  /**
@@ -7,10 +7,10 @@ import "../std__assert-BVFj8-_V.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-B72sZOJB.mjs";
10
+ import { l as verifyRequest } from "../http-dobCLGG2.mjs";
11
11
  import { i as rsaPrivateKey2, n as ed25519PrivateKey, s as rsaPublicKey2, t as ed25519Multikey } from "../keys-BAK-tUlf.mjs";
12
- import { t as doesActorOwnKey } from "../owner-Bou1yabn.mjs";
13
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-DwQAYND-.mjs";
12
+ import { t as doesActorOwnKey } from "../owner-Dk85kXZz.mjs";
13
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "../send-Co-E7yJn.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-BVFj8-_V.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-BWuGBFaX.mjs";
7
+ import { t as hasMalformedKnownTemporalLiteral } from "../temporal-Bnwa_2NC.mjs";
8
8
  import { test } from "@fedify/fixture";
9
9
  //#region src/federation/temporal.test.ts
10
10
  test("hasMalformedKnownTemporalLiteral() detects expanded proof timestamps", async () => {
@@ -5,7 +5,7 @@ import { r as createRequestContext } from "../context-Dk_tacqz.mjs";
5
5
  import { t as assertEquals } from "../assert_equals-Ew3jOFa3.mjs";
6
6
  import "../std__assert-BVFj8-_V.mjs";
7
7
  import { t as MemoryKvStore } from "../kv-mI4Ix2EU.mjs";
8
- import { o as createFederation, s as handleWebFinger } from "../middleware-DOTXcNS-.mjs";
8
+ import { o as createFederation, s as handleWebFinger } from "../middleware-5qLdPLro.mjs";
9
9
  import { test } from "@fedify/fixture";
10
10
  import { Image, Link, Person, Tombstone } from "@fedify/vocab";
11
11
  //#region src/federation/webfinger.test.ts
@@ -11,7 +11,7 @@ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conve
11
11
  let byte_encodings_base64 = require("byte-encodings/base64");
12
12
  //#region deno.json
13
13
  var name = "@fedify/fedify";
14
- var version = "2.2.16";
14
+ var version = "2.2.17";
15
15
  //#endregion
16
16
  //#region src/sig/accept.ts
17
17
  /**
@@ -153,6 +153,64 @@ function fulfillAcceptSignature(entry, localKeyId, localAlg) {
153
153
  }
154
154
  //#endregion
155
155
  //#region src/sig/key.ts
156
+ /** Classifies decoding failures without hiding unexpected loader errors. */
157
+ function classifyKeyJsonLdError(error, seen = /* @__PURE__ */ new Set()) {
158
+ if (!(error instanceof Error) || !error.name.startsWith("jsonld.")) return null;
159
+ if (seen.has(error)) throw error;
160
+ seen.add(error);
161
+ const details = error.details;
162
+ if (typeof details?.code !== "string") return null;
163
+ if (details.code !== "loading remote context failed" || !("cause" in details) && !("cause" in error)) return "invalid";
164
+ const cause = "cause" in details ? details.cause : error.cause;
165
+ const nestedFailure = classifyKeyJsonLdError(cause, seen);
166
+ if (nestedFailure != null) return nestedFailure;
167
+ if (cause instanceof SyntaxError) return "invalid";
168
+ if (cause instanceof _fedify_vocab_runtime.FetchError || cause instanceof _fedify_vocab_runtime.UrlError) return "fetch";
169
+ if (cause instanceof Error && [
170
+ "AbortError",
171
+ "TimeoutError",
172
+ "NetworkError"
173
+ ].includes(cause.name)) return "fetch";
174
+ if (cause instanceof TypeError) {
175
+ const urlCode = cause.code;
176
+ if (cause.name === "InvalidContextReferenceError") return "invalid";
177
+ if (urlCode === "ERR_INVALID_URL" || cause.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(cause.message) || / cannot be parsed as a URL\.?$/.test(cause.message)) return typeof details.url === "string" && /^[A-Za-z][A-Za-z0-9+.-]*:/.test(details.url) && !URL.canParse(details.url) ? "invalid" : "fetch";
178
+ const networkCause = cause.cause;
179
+ const code = cause.code ?? networkCause?.code;
180
+ if (typeof code === "string" && [
181
+ "ECONNREFUSED",
182
+ "ECONNRESET",
183
+ "ECONNABORTED",
184
+ "ETIMEDOUT",
185
+ "EHOSTUNREACH",
186
+ "ENETUNREACH",
187
+ "ENOTFOUND",
188
+ "EAI_AGAIN",
189
+ "UND_ERR_CONNECT_TIMEOUT",
190
+ "UND_ERR_HEADERS_TIMEOUT",
191
+ "UND_ERR_BODY_TIMEOUT",
192
+ "UND_ERR_SOCKET"
193
+ ].includes(code) || /^(fetch failed|Failed to fetch|error sending request|error reading a body from connection|Unable to connect|ConnectionRefused|Network connection lost|The socket connection was closed unexpectedly)/.test(cause.message)) return "fetch";
194
+ }
195
+ throw cause;
196
+ }
197
+ /** Carries a context transport failure through the key decoding boundary. */
198
+ var KeyContextFetchError = class extends Error {
199
+ constructor(error) {
200
+ super("Failed to fetch a key's JSON-LD context.", { cause: error });
201
+ this.error = error;
202
+ }
203
+ };
204
+ function contextFetchError(error) {
205
+ const seen = /* @__PURE__ */ new Set();
206
+ while (error instanceof Error && !seen.has(error)) {
207
+ seen.add(error);
208
+ const details = error.details;
209
+ if (!error.name.startsWith("jsonld.") || details?.code !== "loading remote context failed") break;
210
+ error = "cause" in details ? details.cause : error.cause;
211
+ }
212
+ return new KeyContextFetchError(error);
213
+ }
156
214
  /**
157
215
  * Checks if the given key is valid and supported. No-op if the key is valid,
158
216
  * otherwise throws an error.
@@ -247,6 +305,9 @@ async function importJwk(jwk, type) {
247
305
  * @internal
248
306
  */
249
307
  async function fetchActorDocument(actorId, options = {}) {
308
+ return await fetchActorDocumentInternal(actorId, options, false);
309
+ }
310
+ async function fetchActorDocumentInternal(actorId, options, propagateContextFetchErrors) {
250
311
  const logger = (0, _logtape_logtape.getLogger)([
251
312
  "fedify",
252
313
  "sig",
@@ -277,9 +338,12 @@ async function fetchActorDocument(actorId, options = {}) {
277
338
  baseUrl: documentUrl
278
339
  });
279
340
  } catch (error) {
280
- if (!(error instanceof TypeError)) throw error;
281
- logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
341
+ const failure = classifyKeyJsonLdError(error);
342
+ if (failure === "fetch" && propagateContextFetchErrors) throw contextFetchError(error);
343
+ if (failure == null && !(error instanceof TypeError)) throw error;
344
+ logger.debug("Failed to decode the actor document at {documentUrl}: {error}", {
282
345
  documentUrl: documentUrl.href,
346
+ failure: failure ?? "invalid",
283
347
  error
284
348
  });
285
349
  return null;
@@ -318,6 +382,9 @@ async function fetchActorDocument(actorId, options = {}) {
318
382
  * @internal
319
383
  */
320
384
  async function verifyKeyOwnership(key, options = {}) {
385
+ return await verifyKeyOwnershipInternal(key, options, false);
386
+ }
387
+ async function verifyKeyOwnershipInternal(key, options, propagateContextFetchErrors) {
321
388
  const logger = (0, _logtape_logtape.getLogger)([
322
389
  "fedify",
323
390
  "sig",
@@ -327,7 +394,7 @@ async function verifyKeyOwnership(key, options = {}) {
327
394
  if (keyId == null) return null;
328
395
  const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
329
396
  if (claimedOwnerId == null) return null;
330
- const owner = await fetchActorDocument(claimedOwnerId, options);
397
+ const owner = await fetchActorDocumentInternal(claimedOwnerId, options, propagateContextFetchErrors);
331
398
  if (owner == null) {
332
399
  logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
333
400
  keyId: keyId.href,
@@ -479,6 +546,21 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
479
546
  tracerProvider
480
547
  });
481
548
  } catch (e) {
549
+ const failure = classifyKeyJsonLdError(e);
550
+ if (failure === "fetch") throw contextFetchError(e);
551
+ if (failure != null) {
552
+ logger.debug("Failed to decode key {keyId}: {error}", {
553
+ keyId,
554
+ failure,
555
+ error: e
556
+ });
557
+ await keyCache?.set(cacheKey, null);
558
+ await clearFetchErrorMetadata(cacheKey, keyCache);
559
+ return {
560
+ key: null,
561
+ cached: false
562
+ };
563
+ }
482
564
  if (!(e instanceof TypeError)) throw e;
483
565
  try {
484
566
  object = await cls.fromJsonLd(document, {
@@ -487,8 +569,14 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
487
569
  tracerProvider
488
570
  });
489
571
  } catch (e) {
490
- if (e instanceof TypeError) {
491
- logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
572
+ const failure = classifyKeyJsonLdError(e);
573
+ if (failure === "fetch") throw contextFetchError(e);
574
+ if (failure != null || e instanceof TypeError) {
575
+ logger.debug("Failed to decode key {keyId}: {error}", {
576
+ keyId,
577
+ failure: failure ?? "invalid",
578
+ error: e
579
+ });
492
580
  await keyCache?.set(cacheKey, null);
493
581
  await clearFetchErrorMetadata(cacheKey, keyCache);
494
582
  return {
@@ -580,16 +668,17 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
580
668
  const claimedOwnerId = key instanceof _fedify_vocab.CryptographicKey ? key.ownerId : key.controllerId;
581
669
  if (ownerDocument != null && claimedOwnerId == null) key = key instanceof _fedify_vocab.CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
582
670
  else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
583
- if (await verifyKeyOwnership(key, {
671
+ if (await verifyKeyOwnershipInternal(key, {
584
672
  documentLoader,
585
673
  contextLoader,
586
674
  tracerProvider
587
- }) == null) {
675
+ }, true) == null) {
588
676
  logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
589
677
  keyId,
590
678
  claimedOwnerId: claimedOwnerId.href
591
679
  });
592
680
  await keyCache?.set(cacheKey, null);
681
+ await clearFetchErrorMetadata(cacheKey, keyCache);
593
682
  return {
594
683
  key: null,
595
684
  cached: false
@@ -627,7 +716,12 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
627
716
  } catch (error) {
628
717
  return await onFetchError(error, cacheKey, keyId, keyCache, logger);
629
718
  }
630
- return await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
719
+ try {
720
+ return await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
721
+ } catch (error) {
722
+ if (!(error instanceof KeyContextFetchError)) throw error;
723
+ return await onFetchError(error.error, cacheKey, keyId, keyCache, logger);
724
+ }
631
725
  }
632
726
  async function fetchKeyInternal(keyId, cls, options = {}) {
633
727
  return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
@@ -4,13 +4,13 @@ import { getLogger } from "@logtape/logtape";
4
4
  import { CryptographicKey, Object as Object$1, isActor } from "@fedify/vocab";
5
5
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
6
6
  import { encodeHex } from "byte-encodings/hex";
7
- import { FetchError, getDocumentLoader } from "@fedify/vocab-runtime";
7
+ import { FetchError, UrlError, getDocumentLoader } from "@fedify/vocab-runtime";
8
8
  import { Item, decodeDict, encodeDict, encodeItem } from "structured-field-values";
9
9
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
10
10
  import { decodeBase64, encodeBase64 } from "byte-encodings/base64";
11
11
  //#region deno.json
12
12
  var name = "@fedify/fedify";
13
- var version = "2.2.16";
13
+ var version = "2.2.17";
14
14
  //#endregion
15
15
  //#region src/sig/accept.ts
16
16
  /**
@@ -152,6 +152,64 @@ function fulfillAcceptSignature(entry, localKeyId, localAlg) {
152
152
  }
153
153
  //#endregion
154
154
  //#region src/sig/key.ts
155
+ /** Classifies decoding failures without hiding unexpected loader errors. */
156
+ function classifyKeyJsonLdError(error, seen = /* @__PURE__ */ new Set()) {
157
+ if (!(error instanceof Error) || !error.name.startsWith("jsonld.")) return null;
158
+ if (seen.has(error)) throw error;
159
+ seen.add(error);
160
+ const details = error.details;
161
+ if (typeof details?.code !== "string") return null;
162
+ if (details.code !== "loading remote context failed" || !("cause" in details) && !("cause" in error)) return "invalid";
163
+ const cause = "cause" in details ? details.cause : error.cause;
164
+ const nestedFailure = classifyKeyJsonLdError(cause, seen);
165
+ if (nestedFailure != null) return nestedFailure;
166
+ if (cause instanceof SyntaxError) return "invalid";
167
+ if (cause instanceof FetchError || cause instanceof UrlError) return "fetch";
168
+ if (cause instanceof Error && [
169
+ "AbortError",
170
+ "TimeoutError",
171
+ "NetworkError"
172
+ ].includes(cause.name)) return "fetch";
173
+ if (cause instanceof TypeError) {
174
+ const urlCode = cause.code;
175
+ if (cause.name === "InvalidContextReferenceError") return "invalid";
176
+ if (urlCode === "ERR_INVALID_URL" || cause.message === "Invalid URL string." || /^Invalid URL(?::|$)/.test(cause.message) || / cannot be parsed as a URL\.?$/.test(cause.message)) return typeof details.url === "string" && /^[A-Za-z][A-Za-z0-9+.-]*:/.test(details.url) && !URL.canParse(details.url) ? "invalid" : "fetch";
177
+ const networkCause = cause.cause;
178
+ const code = cause.code ?? networkCause?.code;
179
+ if (typeof code === "string" && [
180
+ "ECONNREFUSED",
181
+ "ECONNRESET",
182
+ "ECONNABORTED",
183
+ "ETIMEDOUT",
184
+ "EHOSTUNREACH",
185
+ "ENETUNREACH",
186
+ "ENOTFOUND",
187
+ "EAI_AGAIN",
188
+ "UND_ERR_CONNECT_TIMEOUT",
189
+ "UND_ERR_HEADERS_TIMEOUT",
190
+ "UND_ERR_BODY_TIMEOUT",
191
+ "UND_ERR_SOCKET"
192
+ ].includes(code) || /^(fetch failed|Failed to fetch|error sending request|error reading a body from connection|Unable to connect|ConnectionRefused|Network connection lost|The socket connection was closed unexpectedly)/.test(cause.message)) return "fetch";
193
+ }
194
+ throw cause;
195
+ }
196
+ /** Carries a context transport failure through the key decoding boundary. */
197
+ var KeyContextFetchError = class extends Error {
198
+ constructor(error) {
199
+ super("Failed to fetch a key's JSON-LD context.", { cause: error });
200
+ this.error = error;
201
+ }
202
+ };
203
+ function contextFetchError(error) {
204
+ const seen = /* @__PURE__ */ new Set();
205
+ while (error instanceof Error && !seen.has(error)) {
206
+ seen.add(error);
207
+ const details = error.details;
208
+ if (!error.name.startsWith("jsonld.") || details?.code !== "loading remote context failed") break;
209
+ error = "cause" in details ? details.cause : error.cause;
210
+ }
211
+ return new KeyContextFetchError(error);
212
+ }
155
213
  /**
156
214
  * Checks if the given key is valid and supported. No-op if the key is valid,
157
215
  * otherwise throws an error.
@@ -246,6 +304,9 @@ async function importJwk(jwk, type) {
246
304
  * @internal
247
305
  */
248
306
  async function fetchActorDocument(actorId, options = {}) {
307
+ return await fetchActorDocumentInternal(actorId, options, false);
308
+ }
309
+ async function fetchActorDocumentInternal(actorId, options, propagateContextFetchErrors) {
249
310
  const logger = getLogger([
250
311
  "fedify",
251
312
  "sig",
@@ -276,9 +337,12 @@ async function fetchActorDocument(actorId, options = {}) {
276
337
  baseUrl: documentUrl
277
338
  });
278
339
  } catch (error) {
279
- if (!(error instanceof TypeError)) throw error;
280
- logger.debug("The document served at {documentUrl} is not a valid object: {error}", {
340
+ const failure = classifyKeyJsonLdError(error);
341
+ if (failure === "fetch" && propagateContextFetchErrors) throw contextFetchError(error);
342
+ if (failure == null && !(error instanceof TypeError)) throw error;
343
+ logger.debug("Failed to decode the actor document at {documentUrl}: {error}", {
281
344
  documentUrl: documentUrl.href,
345
+ failure: failure ?? "invalid",
282
346
  error
283
347
  });
284
348
  return null;
@@ -317,6 +381,9 @@ async function fetchActorDocument(actorId, options = {}) {
317
381
  * @internal
318
382
  */
319
383
  async function verifyKeyOwnership(key, options = {}) {
384
+ return await verifyKeyOwnershipInternal(key, options, false);
385
+ }
386
+ async function verifyKeyOwnershipInternal(key, options, propagateContextFetchErrors) {
320
387
  const logger = getLogger([
321
388
  "fedify",
322
389
  "sig",
@@ -326,7 +393,7 @@ async function verifyKeyOwnership(key, options = {}) {
326
393
  if (keyId == null) return null;
327
394
  const claimedOwnerId = key instanceof CryptographicKey ? key.ownerId : key.controllerId;
328
395
  if (claimedOwnerId == null) return null;
329
- const owner = await fetchActorDocument(claimedOwnerId, options);
396
+ const owner = await fetchActorDocumentInternal(claimedOwnerId, options, propagateContextFetchErrors);
330
397
  if (owner == null) {
331
398
  logger.debug("The owner ({claimedOwnerId}) that key {keyId} claims could not be resolved.", {
332
399
  keyId: keyId.href,
@@ -478,6 +545,21 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
478
545
  tracerProvider
479
546
  });
480
547
  } catch (e) {
548
+ const failure = classifyKeyJsonLdError(e);
549
+ if (failure === "fetch") throw contextFetchError(e);
550
+ if (failure != null) {
551
+ logger.debug("Failed to decode key {keyId}: {error}", {
552
+ keyId,
553
+ failure,
554
+ error: e
555
+ });
556
+ await keyCache?.set(cacheKey, null);
557
+ await clearFetchErrorMetadata(cacheKey, keyCache);
558
+ return {
559
+ key: null,
560
+ cached: false
561
+ };
562
+ }
481
563
  if (!(e instanceof TypeError)) throw e;
482
564
  try {
483
565
  object = await cls.fromJsonLd(document, {
@@ -486,8 +568,14 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
486
568
  tracerProvider
487
569
  });
488
570
  } catch (e) {
489
- if (e instanceof TypeError) {
490
- logger.debug("Failed to verify; key {keyId} returned an invalid object.", { keyId });
571
+ const failure = classifyKeyJsonLdError(e);
572
+ if (failure === "fetch") throw contextFetchError(e);
573
+ if (failure != null || e instanceof TypeError) {
574
+ logger.debug("Failed to decode key {keyId}: {error}", {
575
+ keyId,
576
+ failure: failure ?? "invalid",
577
+ error: e
578
+ });
491
579
  await keyCache?.set(cacheKey, null);
492
580
  await clearFetchErrorMetadata(cacheKey, keyCache);
493
581
  return {
@@ -579,16 +667,17 @@ async function resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, {
579
667
  const claimedOwnerId = key instanceof CryptographicKey ? key.ownerId : key.controllerId;
580
668
  if (ownerDocument != null && claimedOwnerId == null) key = key instanceof CryptographicKey ? key.clone({ owner: ownerDocument.id }) : key.clone({ controller: ownerDocument.id });
581
669
  else if (claimedOwnerId != null && claimedOwnerId.href !== ownerDocument?.id?.href) {
582
- if (await verifyKeyOwnership(key, {
670
+ if (await verifyKeyOwnershipInternal(key, {
583
671
  documentLoader,
584
672
  contextLoader,
585
673
  tracerProvider
586
- }) == null) {
674
+ }, true) == null) {
587
675
  logger.debug("Failed to verify; the owner {claimedOwnerId} that key {keyId} claims does not list the key as its own.", {
588
676
  keyId,
589
677
  claimedOwnerId: claimedOwnerId.href
590
678
  });
591
679
  await keyCache?.set(cacheKey, null);
680
+ await clearFetchErrorMetadata(cacheKey, keyCache);
592
681
  return {
593
682
  key: null,
594
683
  cached: false
@@ -626,7 +715,12 @@ async function fetchKeyWithResult(cacheKey, cls, options, onCachedUnavailable, o
626
715
  } catch (error) {
627
716
  return await onFetchError(error, cacheKey, keyId, keyCache, logger);
628
717
  }
629
- return await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
718
+ try {
719
+ return await resolveFetchedKey(document, cacheKey, documentUrl, keyId, cls, options, logger);
720
+ } catch (error) {
721
+ if (!(error instanceof KeyContextFetchError)) throw error;
722
+ return await onFetchError(error.error, cacheKey, keyId, keyCache, logger);
723
+ }
630
724
  }
631
725
  async function fetchKeyInternal(keyId, cls, options = {}) {
632
726
  return await fetchKeyWithResult(typeof keyId === "string" ? new URL(keyId) : keyId, cls, options, (_cacheKey, _keyId, _keyCache, _logger) => {
@@ -1,9 +1,9 @@
1
1
  import { Temporal } from "@js-temporal/polyfill";
2
2
  import "urlpattern-polyfill";
3
3
  globalThis.addEventListener = () => {};
4
- import { n as version, t as name } from "./deno-BCQztXLl.mjs";
4
+ import { n as version, t as name } from "./deno-Cya0rbEr.mjs";
5
5
  import { i as validateAcceptSignature, n as fulfillAcceptSignature, r as parseAcceptSignature } from "./accept-Dd__NiUL.mjs";
6
- import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-DCQAjTMo.mjs";
6
+ import { i as fetchKeyDetailed, s as validateCryptoKey } from "./key-BMHNd0gP.mjs";
7
7
  import { CryptographicKey } from "@fedify/vocab";
8
8
  import { SpanStatusCode, trace } from "@opentelemetry/api";
9
9
  import { FetchError } from "@fedify/vocab-runtime";