@fedify/fedify 2.4.0-dev.2054 → 2.4.0-dev.2063

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 (83) hide show
  1. package/dist/{builder-C86Z3BKs.mjs → builder-BzS4brdy.mjs} +8 -2
  2. package/dist/compat/mod.d.cts +1 -1
  3. package/dist/compat/mod.d.ts +1 -1
  4. package/dist/compat/transformers.test.mjs +1 -1
  5. package/dist/{compound-proof-BcZqTCwy.mjs → compound-proof-BrSXP5so.mjs} +1 -1
  6. package/dist/{context-BJPHhBmD.d.ts → context-D_gg4M0n.d.ts} +102 -1
  7. package/dist/{context-Bh_mJfbD.d.cts → context-b36TpPoG.d.cts} +102 -1
  8. package/dist/{deno-Bz6KTcon.mjs → deno-CVZgG4sc.mjs} +1 -1
  9. package/dist/{docloader-DS54zLD1.mjs → docloader-DBb8z7_l.mjs} +2 -2
  10. package/dist/federation/builder.test.mjs +1 -1
  11. package/dist/federation/compound-fanout.test.mjs +3 -3
  12. package/dist/federation/handler-compound-proof.test.mjs +3 -3
  13. package/dist/federation/handler.test.mjs +5 -5
  14. package/dist/federation/hashlink-media.test.d.mts +2 -0
  15. package/dist/federation/hashlink-media.test.mjs +422 -0
  16. package/dist/federation/idempotency.test.mjs +2 -2
  17. package/dist/federation/metrics.test.mjs +1 -1
  18. package/dist/federation/middleware.test.mjs +7 -7
  19. package/dist/federation/mod.cjs +1 -1
  20. package/dist/federation/mod.d.cts +3 -3
  21. package/dist/federation/mod.d.ts +3 -3
  22. package/dist/federation/mod.js +1 -1
  23. package/dist/federation/outgoing-proof.test.mjs +5 -5
  24. package/dist/federation/portable.test.mjs +3 -3
  25. package/dist/federation/router.test.mjs +2 -152
  26. package/dist/federation/send.test.mjs +3 -3
  27. package/dist/federation/tasks/enqueue.test.mjs +1 -1
  28. package/dist/federation/tasks/tasks.test.mjs +2 -2
  29. package/dist/federation/temporal.test.mjs +1 -1
  30. package/dist/federation/webfinger.test.mjs +1 -1
  31. package/dist/{federation-su1OEWsM.cjs → federation-BrsIzNK5.cjs} +139 -21
  32. package/dist/{http-C3WGK1dL.js → http-99KVDtMr.js} +1 -1
  33. package/dist/{http-LmUsMjK_.cjs → http-CVjwUCCu.cjs} +1 -1
  34. package/dist/{http-Ty805y3A.mjs → http-znjq5j6a.mjs} +3 -3
  35. package/dist/{key-BMc-2eW0.mjs → key-BnMA6vpl.mjs} +2 -2
  36. package/dist/{kv-cache-BhPPtHg7.mjs → kv-cache--REpvHgG.mjs} +1 -1
  37. package/dist/{kv-cache-CuaZnk6r.cjs → kv-cache-D2O0JspA.cjs} +1 -1
  38. package/dist/{kv-cache-BcHCmrC8.js → kv-cache-aijDx_jN.js} +1 -1
  39. package/dist/{ld-OpXWt-80.mjs → ld-BBVP8ock.mjs} +3 -3
  40. package/dist/{metrics-CTAe8lYz.mjs → metrics-B5spFq82.mjs} +1 -1
  41. package/dist/{middleware-C-JLcqy7.mjs → middleware-ClrGS1xy.mjs} +1 -1
  42. package/dist/{middleware-BtwW3G41.mjs → middleware-Cw8EWwAT.mjs} +145 -33
  43. package/dist/{middleware-_CMav1o7.js → middleware-CzYM4cJI.js} +140 -22
  44. package/dist/{mod-DU8aVzQJ.d.cts → mod-Bfenkg_c.d.cts} +1 -1
  45. package/dist/{mod-BofpDIcU.d.ts → mod-BhMvsgdF.d.ts} +1 -1
  46. package/dist/{mod-CNRAxXEb.d.ts → mod-Dhm7SqyE.d.ts} +150 -2
  47. package/dist/{mod-CVQFWefn.d.cts → mod-pLbeNwcC.d.cts} +150 -2
  48. package/dist/mod.cjs +6 -5
  49. package/dist/mod.d.cts +5 -5
  50. package/dist/mod.d.ts +5 -5
  51. package/dist/mod.js +6 -6
  52. package/dist/nodeinfo/handler.test.mjs +1 -1
  53. package/dist/{owner-Bcm4pLXw.cjs → owner-CHBQ0sSj.cjs} +34 -2
  54. package/dist/{owner-B-TLr-1I.js → owner-Cu_KMvQN.js} +29 -3
  55. package/dist/{owner-RxB_eQpU.mjs → owner-Hc0hvbbX.mjs} +2 -2
  56. package/dist/{proof-CVf2XbEa.mjs → proof-B69bJaSn.mjs} +32 -6
  57. package/dist/router-u5O5KCQr.mjs +156 -0
  58. package/dist/{send-DF9KzhJH.mjs → send-BB5rBhvN.mjs} +3 -3
  59. package/dist/sig/compound-proof-discovery.test.mjs +1 -1
  60. package/dist/sig/compound-proof-producer.test.mjs +1 -1
  61. package/dist/sig/compound-proof-verification.test.mjs +2 -2
  62. package/dist/sig/compound-proof.test.mjs +2 -2
  63. package/dist/sig/http.test.mjs +2 -2
  64. package/dist/sig/key.test.mjs +1 -1
  65. package/dist/sig/ld.test.mjs +2 -2
  66. package/dist/sig/mod.cjs +4 -2
  67. package/dist/sig/mod.d.cts +2 -2
  68. package/dist/sig/mod.d.ts +2 -2
  69. package/dist/sig/mod.js +4 -3
  70. package/dist/sig/owner.test.mjs +1 -1
  71. package/dist/sig/portable-collection.test.d.mts +2 -0
  72. package/dist/sig/portable-collection.test.mjs +971 -0
  73. package/dist/sig/proof.test.mjs +3 -3
  74. package/dist/sig-B8WDBV-h.js +401 -0
  75. package/dist/sig-dut4BzEW.cjs +405 -0
  76. package/dist/{temporal-B6U3fEUW.mjs → temporal-CcRkOx3C.mjs} +1 -1
  77. package/dist/testing/kv.test.mjs +1 -1
  78. package/dist/testing/mod.d.mts +101 -0
  79. package/dist/utils/docloader.test.mjs +2 -2
  80. package/dist/utils/kv-cache.test.mjs +1 -1
  81. package/dist/utils/mod.cjs +1 -1
  82. package/dist/utils/mod.js +1 -1
  83. package/package.json +7 -7
@@ -1,27 +1,27 @@
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-Bz6KTcon.mjs";
5
- import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-CTAe8lYz.mjs";
4
+ import { n as version, t as name } from "./deno-CVZgG4sc.mjs";
5
+ import { a as instrumentDocumentLoader, b as recordWebFingerHandle, c as recordCircuitBreakerStateChange, d as recordCollectionRequest, f as recordCollectionTotalItems, g as recordInboxActivity, h as recordFanoutRecipients, i as getRemoteHost, l as recordCollectionDispatchDuration, n as getDurationMs, o as isAbortError, r as getFederationMetrics, u as recordCollectionPageItems, v as recordOutboxActivity, x as registerQueueDepthGauge, y as recordOutboxEnqueue } from "./metrics-B5spFq82.mjs";
6
6
  import { t as formatAcceptSignature } from "./accept-CPVqki78.mjs";
7
- import { o as importJwk, s as validateCryptoKey, t as exportJwk } from "./key-BMc-2eW0.mjs";
8
- import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-Ty805y3A.mjs";
9
- import { t as getAuthenticatedDocumentLoader } from "./docloader-DS54zLD1.mjs";
10
- import { n as kvCache } from "./kv-cache-BhPPtHg7.mjs";
7
+ import { o as importJwk, s as validateCryptoKey, t as exportJwk } from "./key-BnMA6vpl.mjs";
8
+ import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-znjq5j6a.mjs";
9
+ import { t as getAuthenticatedDocumentLoader } from "./docloader-DBb8z7_l.mjs";
10
+ import { n as kvCache } from "./kv-cache--REpvHgG.mjs";
11
11
  import { r as normalizeOutgoingActivityJsonLd } from "./outgoing-jsonld-4IJqPYos.mjs";
12
12
  import { d as readBoundedText, l as BodyTooLargeError, t as getNodeInfo, u as MAX_BODY_SIZE } from "./client-DpnpgjyX.mjs";
13
13
  import { t as nodeInfoToJson } from "./types-DE_LV1JB.mjs";
14
- import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-OpXWt-80.mjs";
15
- import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-RxB_eQpU.mjs";
16
- import { a as verifyObject, n as hasProofLike, o as verifyPortableObjectProof, r as signObject } from "./proof-CVf2XbEa.mjs";
17
- import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-DF9KzhJH.mjs";
18
- import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-C86Z3BKs.mjs";
14
+ import { _ as wrapContextLoaderForJsonLd, a as compactJsonLd, c as getNormalizationContextLoader, d as isClearlyMalformedContextReference, f as isInvalidUrlTypeError, l as hasSignature, m as verifyCompactJsonLd, p as signJsonLd, r as assertSafeJsonLd, s as detachSignature, t as InvalidContextReferenceError, u as hasSignatureLike } from "./ld-BBVP8ock.mjs";
15
+ import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-Hc0hvbbX.mjs";
16
+ import { a as verifyObject, n as hasProofLike, o as verifyPortableObjectProof, r as signObject } from "./proof-B69bJaSn.mjs";
17
+ import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-BB5rBhvN.mjs";
18
+ import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-BzS4brdy.mjs";
19
19
  import { t as CircuitBreaker } from "./circuit-breaker-C5NqoLBJ.mjs";
20
20
  import { t as buildCollectionSynchronizationHeader } from "./collection-CnnP1UAv.mjs";
21
- import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "./compound-proof-BcZqTCwy.mjs";
21
+ import { a as verifyCompoundPortableObjectProofs, i as inspectCompoundPortableObjectApplicability, r as findUnsupportedCompoundProofShape, t as containsCompoundPortableObject } from "./compound-proof-BrSXP5so.mjs";
22
22
  import { t as KvKeyCache } from "./keycache-BUDHB5rY.mjs";
23
23
  import { t as acceptsJsonLd } from "./negotiation-CCocWT_N.mjs";
24
- import { t as hasMalformedKnownTemporalLiteral } from "./temporal-B6U3fEUW.mjs";
24
+ import { t as hasMalformedKnownTemporalLiteral } from "./temporal-CcRkOx3C.mjs";
25
25
  import { t as createExponentialBackoffPolicy } from "./retry-zX5xw5e0.mjs";
26
26
  import { t as TaskCodec } from "./codec-C6rTrpIp.mjs";
27
27
  import { getLogger, withContext } from "@logtape/logtape";
@@ -30,7 +30,7 @@ import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey,
30
30
  import { lookupWebFinger } from "@fedify/webfinger";
31
31
  import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
32
32
  import { uniq } from "es-toolkit";
33
- import { FetchError, canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, haveSameFe34Origin, isGatewayUrl, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
33
+ import { FetchError, canonicalizePortableUri, decodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, haveSameFe34Origin, isGatewayUrl, parseDigestMultibase, parseHashlink, parseIri, toCompatibleEf61Id } from "@fedify/vocab-runtime";
34
34
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
35
35
  import jsonld from "@fedify/vocab-runtime/jsonld";
36
36
  import { DataPointType, MeterProvider as MeterProvider$1, MetricReader } from "@opentelemetry/sdk-metrics";
@@ -520,6 +520,12 @@ async function routeActivity({ context: ctx, json, originalJson, normalizedActiv
520
520
  const PORTABLE_OBJECT_CONTENT_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
521
521
  const GATEWAY_OBJECT_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
522
522
  const BARE_DID_PATTERN = /^did:[a-z0-9]+:[^/?#]+$/i;
523
+ const GATEWAY_PATH_PREFIX = "/.well-known/apgateway/";
524
+ const HASHLINK_SCHEME_PATTERN = /^hl(?::|%3A)/i;
525
+ /**
526
+ * The route template of hashlink media requests, used for metrics and traces.
527
+ */
528
+ const HASHLINK_MEDIA_ROUTE_TEMPLATE = "/.well-known/apgateway/hl:{digestMultibase}";
523
529
  /**
524
530
  * Recognizes an FEP-ef61 gateway request for a portable object, e.g.,
525
531
  * `GET /.well-known/apgateway/did:key:z6Mk.../notes/123`.
@@ -574,6 +580,55 @@ function buildPortableUri(authority, path) {
574
580
  getFe34Origin(authority);
575
581
  return parseIri(`ap+ef61://${authority}${path}`);
576
582
  }
583
+ /**
584
+ * Recognizes an FEP-ef61 gateway request for a resource addressed by
585
+ * a hashlink, e.g.,
586
+ * `GET /.well-known/apgateway/hl:zQmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n`.
587
+ *
588
+ * The rest of the path, including any slashes, is taken as the hashlink, as
589
+ * a digest in a multibase encoding like base64 may contain slashes. The query
590
+ * is not part of the hashlink, so it is ignored here.
591
+ *
592
+ * @param url The request URL.
593
+ * @returns The parsed request, or `null` if the URL is not a gateway request
594
+ * for a hashlink, e.g., a request for a portable object.
595
+ */
596
+ function parseHashlinkGatewayRequest(url) {
597
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
598
+ if (!url.pathname.startsWith(GATEWAY_PATH_PREFIX)) return null;
599
+ const encoded = url.pathname.slice(23);
600
+ if (!HASHLINK_SCHEME_PATTERN.test(encoded)) return null;
601
+ let digestMultibase;
602
+ let digest;
603
+ let multihash;
604
+ try {
605
+ let hashlink;
606
+ try {
607
+ hashlink = decodeURIComponent(encoded);
608
+ } catch (error) {
609
+ throw new TypeError("Invalid percent-encoding in the hashlink.", { cause: error });
610
+ }
611
+ ({digestMultibase} = parseHashlink(hashlink));
612
+ ({digest} = parseDigestMultibase(digestMultibase));
613
+ multihash = decodeMultibase(digestMultibase);
614
+ } catch (error) {
615
+ if (error instanceof TypeError) return {
616
+ type: "malformed",
617
+ error
618
+ };
619
+ throw error;
620
+ }
621
+ return {
622
+ type: "media",
623
+ media: Object.freeze({
624
+ hashlink: `hl:${digestMultibase}`,
625
+ digestMultibase,
626
+ algorithm: "sha2-256",
627
+ digest,
628
+ multihash
629
+ })
630
+ };
631
+ }
577
632
  //#endregion
578
633
  //#region src/federation/handler.ts
579
634
  const rawInboxContextFactorySymbol = Symbol("fedify.rawInboxContextFactory");
@@ -4441,7 +4496,7 @@ var FederationImpl = class extends FederationBuilderImpl {
4441
4496
  tracer,
4442
4497
  metricState
4443
4498
  });
4444
- if (acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
4499
+ if (metricState.endpoint !== "hashlink_media" && acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
4445
4500
  } catch (error) {
4446
4501
  this.metrics.recordHttpServerRequest(request.method, metricState.endpoint ?? "error", getDurationMs(metricStart), { routeTemplate: metricState.routeTemplate });
4447
4502
  span.setStatus({
@@ -4492,24 +4547,22 @@ var FederationImpl = class extends FederationBuilderImpl {
4492
4547
  const url = new URL(request.url);
4493
4548
  const route = this.router.route(url.pathname);
4494
4549
  if (route == null) {
4550
+ const media = this.hashlinkMediaDispatcher == null ? null : parseHashlinkGatewayRequest(url);
4551
+ if (media != null) return await this.#fetchHashlinkMedia(request, media, {
4552
+ onNotFound,
4553
+ contextData,
4554
+ span,
4555
+ metricState
4556
+ });
4495
4557
  const portable = request.method === "GET" || request.method === "HEAD" ? parsePortableGatewayRequest(url) : null;
4496
- if (portable != null) {
4497
- const response = await this.#fetchPortableObject(request, portable, {
4498
- onNotFound,
4499
- onNotAcceptable,
4500
- onUnauthorized,
4501
- contextData,
4502
- span,
4503
- metricState
4504
- });
4505
- if (request.method !== "HEAD" || response.body == null) return response;
4506
- await response.body.cancel();
4507
- return new Response(null, {
4508
- status: response.status,
4509
- statusText: response.statusText,
4510
- headers: response.headers
4511
- });
4512
- }
4558
+ if (portable != null) return await discardBodyForHead(request, await this.#fetchPortableObject(request, portable, {
4559
+ onNotFound,
4560
+ onNotAcceptable,
4561
+ onUnauthorized,
4562
+ contextData,
4563
+ span,
4564
+ metricState
4565
+ }));
4513
4566
  metricState.endpoint = "not_found";
4514
4567
  return await onNotFound(request);
4515
4568
  }
@@ -4833,8 +4886,67 @@ var FederationImpl = class extends FederationBuilderImpl {
4833
4886
  onNotFound
4834
4887
  });
4835
4888
  }
4889
+ async #fetchHashlinkMedia(request, media, { onNotFound, contextData, span, metricState }) {
4890
+ metricState.endpoint = "hashlink_media";
4891
+ metricState.routeTemplate = HASHLINK_MEDIA_ROUTE_TEMPLATE;
4892
+ span.updateName(`${request.method} ${HASHLINK_MEDIA_ROUTE_TEMPLATE}`);
4893
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response("Method not allowed.", {
4894
+ status: 405,
4895
+ headers: {
4896
+ Allow: "GET, HEAD",
4897
+ "Content-Type": "text/plain; charset=utf-8"
4898
+ }
4899
+ });
4900
+ if (media.type === "malformed") {
4901
+ getLogger([
4902
+ "fedify",
4903
+ "federation",
4904
+ "hashlinkMedia"
4905
+ ]).debug("Malformed FEP-ef61 hashlink media request {url}: {error}", {
4906
+ url: request.url,
4907
+ error: media.error
4908
+ });
4909
+ return new Response(request.method === "HEAD" ? null : "Malformed hashlink.", {
4910
+ status: 400,
4911
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
4912
+ });
4913
+ }
4914
+ const context = this.#createContext(request, contextData);
4915
+ const response = await this.hashlinkMediaDispatcher(context, media.media);
4916
+ if (response == null) return await onNotFound(request);
4917
+ return await discardBodyForHead(request, response, false);
4918
+ }
4836
4919
  };
4837
4920
  const PORTABLE_GATEWAY_ROUTE_PREFIX = "/.well-known/apgateway/{did}";
4921
+ /**
4922
+ * Removes the body of a response to a `HEAD` request.
4923
+ * @param request The request.
4924
+ * @param response The response to the request.
4925
+ * @param waitForCancel Whether to wait until the discarded body is cancelled.
4926
+ * @returns The response without a body if the request is a `HEAD` request,
4927
+ * or the response itself otherwise.
4928
+ */
4929
+ async function discardBodyForHead(request, response, waitForCancel = true) {
4930
+ if (request.method !== "HEAD" || response.body == null) return response;
4931
+ const cancelled = response.body.cancel();
4932
+ if (waitForCancel) await cancelled;
4933
+ else cancelled.catch((error) => {
4934
+ getLogger([
4935
+ "fedify",
4936
+ "federation",
4937
+ "http"
4938
+ ]).debug("Failed to cancel the body of a response to {method} {url}: {error}", {
4939
+ method: request.method,
4940
+ url: request.url,
4941
+ error
4942
+ });
4943
+ });
4944
+ return new Response(null, {
4945
+ status: response.status,
4946
+ statusText: response.statusText,
4947
+ headers: response.headers
4948
+ });
4949
+ }
4838
4950
  function getEndpointCategory(routeName) {
4839
4951
  if (routeName.startsWith("object:")) return "object";
4840
4952
  if (routeName.startsWith("collection:") || routeName.startsWith("orderedCollection:")) return "collection";
@@ -5974,4 +6086,4 @@ function getRequestId(request) {
5974
6086
  return `req_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
5975
6087
  }
5976
6088
  //#endregion
5977
- export { handleBenchmarkTrigger as _, OutboxContextImpl as a, actorDehydrator as b, handleActor as c, handleInbox as d, handleObject as f, PORTABLE_OBJECT_CONTENT_TYPE as g, respondWithObjectIfAcceptable as h, KvSpecDeterminer as i, handleCollection as l, respondWithObject as m, FederationImpl as n, createFederation as o, handleOutbox as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCustomCollection as u, handleNodeInfo as v, autoIdAssigner as x, handleNodeInfoJrd as y };
6089
+ export { autoIdAssigner as S, parseHashlinkGatewayRequest as _, OutboxContextImpl as a, handleNodeInfoJrd as b, handleActor as c, handleInbox as d, handleObject as f, PORTABLE_OBJECT_CONTENT_TYPE as g, respondWithObjectIfAcceptable as h, KvSpecDeterminer as i, handleCollection as l, respondWithObject as m, FederationImpl as n, createFederation as o, handleOutbox as p, InboxContextImpl as r, handleWebFinger as s, ContextImpl as t, handleCustomCollection as u, handleBenchmarkTrigger as v, actorDehydrator as x, handleNodeInfo as y };
@@ -2,16 +2,16 @@ import { Temporal } from "temporal-polyfill";
2
2
  import { URLPattern } from "urlpattern-polyfill";
3
3
  import { t as __exportAll } from "./rolldown-runtime-BC-Eo5Mj.js";
4
4
  import { r as getDefaultActivityTransformers } from "./transformers-DAtXl_Kc.js";
5
- import { A as recordWebFingerHandle, C as recordCollectionRequest, D as recordInboxActivity, E as recordFanoutRecipients, I as name, L as version, M as formatAcceptSignature, O as recordOutboxActivity, S as recordCollectionPageItems, _ as instrumentDocumentLoader, a as verifyRequestDetailed, b as recordCircuitBreakerStateChange, d as importJwk, f as validateCryptoKey, g as getRemoteHost, h as getFederationMetrics, i as verifyRequest, j as registerQueueDepthGauge, k as recordOutboxEnqueue, m as getDurationMs, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, v as isAbortError, w as recordCollectionTotalItems, x as recordCollectionDispatchDuration } from "./http-C3WGK1dL.js";
5
+ import { A as recordWebFingerHandle, C as recordCollectionRequest, D as recordInboxActivity, E as recordFanoutRecipients, I as name, L as version, M as formatAcceptSignature, O as recordOutboxActivity, S as recordCollectionPageItems, _ as instrumentDocumentLoader, a as verifyRequestDetailed, b as recordCircuitBreakerStateChange, d as importJwk, f as validateCryptoKey, g as getRemoteHost, h as getFederationMetrics, i as verifyRequest, j as registerQueueDepthGauge, k as recordOutboxEnqueue, m as getDurationMs, n as parseRfc9421SignatureInput, o as exportJwk, t as doubleKnock, v as isAbortError, w as recordCollectionTotalItems, x as recordCollectionDispatchDuration } from "./http-99KVDtMr.js";
6
6
  import { a as MAX_BODY_SIZE, i as BodyTooLargeError, n as getNodeInfo, o as readBoundedText, t as nodeInfoToJson } from "./types-C_cNlNVK.js";
7
- import { C as verifyCompactJsonLd, D as normalizeOutgoingActivityJsonLd, E as wrapContextLoaderForJsonLd, S as signJsonLd, _ as getNormalizationContextLoader, a as signObject, b as isClearlyMalformedContextReference, c as verifyPortableObjectProof, d as InvalidContextReferenceError, f as assertSafeJsonLd, g as detachSignature, i as hasProofLike, l as verifyPortableObjectProofPolicy, m as compactJsonLd, n as getKeyOwner, o as verifyMapLocalProof, s as verifyObject, t as doesActorOwnKey, v as hasSignature, x as isInvalidUrlTypeError, y as hasSignatureLike } from "./owner-B-TLr-1I.js";
8
- import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-BcHCmrC8.js";
7
+ import { C as signJsonLd, D as wrapContextLoaderForJsonLd, O as normalizeOutgoingActivityJsonLd, S as isInvalidUrlTypeError, _ as detachSignature, a as signObject, b as hasSignatureLike, c as verifyPortableObjectProof, f as InvalidContextReferenceError, h as compactJsonLd, i as hasProofLike, l as verifyPortableObjectProofPolicy, n as getKeyOwner, o as verifyMapLocalProof, p as assertSafeJsonLd, s as verifyObject, t as doesActorOwnKey, v as getNormalizationContextLoader, w as verifyCompactJsonLd, x as isClearlyMalformedContextReference, y as hasSignature } from "./owner-Cu_KMvQN.js";
8
+ import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-aijDx_jN.js";
9
9
  import { getLogger, withContext } from "@logtape/logtape";
10
10
  import { Router, RouterError, assertPath } from "@fedify/uri-template";
11
11
  import { Activity, Collection, CollectionPage, CryptographicKey, Link, Multikey, Object as Object$1, OrderedCollection, OrderedCollectionPage, Tombstone, getTypeId, lookupObject, traverseCollection } from "@fedify/vocab";
12
12
  import { SpanKind, SpanStatusCode, context, metrics, propagation, trace } from "@opentelemetry/api";
13
13
  import { encodeHex } from "byte-encodings/hex";
14
- import { FetchError, UrlError, canonicalizePortableUri, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, haveSameFe34Origin, isGatewayUrl, parseIri, toCompatibleEf61Id, validatePublicUrl } from "@fedify/vocab-runtime";
14
+ import { FetchError, UrlError, canonicalizePortableUri, decodeMultibase, formatIri, fromCompatibleEf61Id, getDocumentLoader, getFe34Origin, haveSameFe34Origin, isGatewayUrl, parseDigestMultibase, parseHashlink, parseIri, toCompatibleEf61Id, validatePublicUrl } from "@fedify/vocab-runtime";
15
15
  import jsonld from "@fedify/vocab-runtime/jsonld";
16
16
  import { uniq } from "es-toolkit";
17
17
  import { ATTR_HTTP_REQUEST_HEADER, ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_HEADER, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_URL_FULL } from "@opentelemetry/semantic-conventions";
@@ -105,6 +105,7 @@ var FederationBuilderImpl = class {
105
105
  mediaUploaderPath;
106
106
  mediaUploaderCallback;
107
107
  mediaUploaderAuthorizePredicate;
108
+ hashlinkMediaDispatcher;
108
109
  sharedInboxKeyDispatcher;
109
110
  unverifiedActivityHandler;
110
111
  outboxPermanentFailureHandler;
@@ -162,6 +163,7 @@ var FederationBuilderImpl = class {
162
163
  f.mediaUploaderPath = this.mediaUploaderPath;
163
164
  f.mediaUploaderCallback = this.mediaUploaderCallback;
164
165
  f.mediaUploaderAuthorizePredicate = this.mediaUploaderAuthorizePredicate;
166
+ f.hashlinkMediaDispatcher = this.hashlinkMediaDispatcher;
165
167
  f.sharedInboxKeyDispatcher = this.sharedInboxKeyDispatcher;
166
168
  f.unverifiedActivityHandler = this.unverifiedActivityHandler;
167
169
  f.outboxPermanentFailureHandler = this.outboxPermanentFailureHandler;
@@ -469,6 +471,10 @@ var FederationBuilderImpl = class {
469
471
  } };
470
472
  return setters;
471
473
  }
474
+ setHashlinkMediaDispatcher(dispatcher) {
475
+ if (this.hashlinkMediaDispatcher != null) throw new RouterError("Hashlink media dispatcher already set.");
476
+ this.hashlinkMediaDispatcher = dispatcher;
477
+ }
472
478
  setFollowingDispatcher(path, dispatcher) {
473
479
  if (this.router.has("following")) throw new RouterError("Following collection dispatcher already set.");
474
480
  assertPath(path);
@@ -2196,6 +2202,12 @@ function acceptsJsonLd(request) {
2196
2202
  const PORTABLE_OBJECT_CONTENT_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"";
2197
2203
  const GATEWAY_OBJECT_PATH_PATTERN = /^\/\.well-known\/apgateway\/did(?::|%3A)/i;
2198
2204
  const BARE_DID_PATTERN = /^did:[a-z0-9]+:[^/?#]+$/i;
2205
+ const GATEWAY_PATH_PREFIX = "/.well-known/apgateway/";
2206
+ const HASHLINK_SCHEME_PATTERN = /^hl(?::|%3A)/i;
2207
+ /**
2208
+ * The route template of hashlink media requests, used for metrics and traces.
2209
+ */
2210
+ const HASHLINK_MEDIA_ROUTE_TEMPLATE = "/.well-known/apgateway/hl:{digestMultibase}";
2199
2211
  /**
2200
2212
  * Recognizes an FEP-ef61 gateway request for a portable object, e.g.,
2201
2213
  * `GET /.well-known/apgateway/did:key:z6Mk.../notes/123`.
@@ -2250,6 +2262,55 @@ function buildPortableUri(authority, path) {
2250
2262
  getFe34Origin(authority);
2251
2263
  return parseIri(`ap+ef61://${authority}${path}`);
2252
2264
  }
2265
+ /**
2266
+ * Recognizes an FEP-ef61 gateway request for a resource addressed by
2267
+ * a hashlink, e.g.,
2268
+ * `GET /.well-known/apgateway/hl:zQmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n`.
2269
+ *
2270
+ * The rest of the path, including any slashes, is taken as the hashlink, as
2271
+ * a digest in a multibase encoding like base64 may contain slashes. The query
2272
+ * is not part of the hashlink, so it is ignored here.
2273
+ *
2274
+ * @param url The request URL.
2275
+ * @returns The parsed request, or `null` if the URL is not a gateway request
2276
+ * for a hashlink, e.g., a request for a portable object.
2277
+ */
2278
+ function parseHashlinkGatewayRequest(url) {
2279
+ if (url.protocol !== "http:" && url.protocol !== "https:") return null;
2280
+ if (!url.pathname.startsWith(GATEWAY_PATH_PREFIX)) return null;
2281
+ const encoded = url.pathname.slice(23);
2282
+ if (!HASHLINK_SCHEME_PATTERN.test(encoded)) return null;
2283
+ let digestMultibase;
2284
+ let digest;
2285
+ let multihash;
2286
+ try {
2287
+ let hashlink;
2288
+ try {
2289
+ hashlink = decodeURIComponent(encoded);
2290
+ } catch (error) {
2291
+ throw new TypeError("Invalid percent-encoding in the hashlink.", { cause: error });
2292
+ }
2293
+ ({digestMultibase} = parseHashlink(hashlink));
2294
+ ({digest} = parseDigestMultibase(digestMultibase));
2295
+ multihash = decodeMultibase(digestMultibase);
2296
+ } catch (error) {
2297
+ if (error instanceof TypeError) return {
2298
+ type: "malformed",
2299
+ error
2300
+ };
2301
+ throw error;
2302
+ }
2303
+ return {
2304
+ type: "media",
2305
+ media: Object.freeze({
2306
+ hashlink: `hl:${digestMultibase}`,
2307
+ digestMultibase,
2308
+ algorithm: "sha2-256",
2309
+ digest,
2310
+ multihash
2311
+ })
2312
+ };
2313
+ }
2253
2314
  //#endregion
2254
2315
  //#region src/federation/temporal.ts
2255
2316
  function isPlainObject$1(value) {
@@ -6901,7 +6962,7 @@ var FederationImpl = class extends FederationBuilderImpl {
6901
6962
  tracer,
6902
6963
  metricState
6903
6964
  });
6904
- if (acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
6965
+ if (metricState.endpoint !== "hashlink_media" && acceptsJsonLd(request)) response.headers.set("Vary", "Accept");
6905
6966
  } catch (error) {
6906
6967
  this.metrics.recordHttpServerRequest(request.method, metricState.endpoint ?? "error", getDurationMs(metricStart), { routeTemplate: metricState.routeTemplate });
6907
6968
  span.setStatus({
@@ -6952,24 +7013,22 @@ var FederationImpl = class extends FederationBuilderImpl {
6952
7013
  const url = new URL(request.url);
6953
7014
  const route = this.router.route(url.pathname);
6954
7015
  if (route == null) {
7016
+ const media = this.hashlinkMediaDispatcher == null ? null : parseHashlinkGatewayRequest(url);
7017
+ if (media != null) return await this.#fetchHashlinkMedia(request, media, {
7018
+ onNotFound,
7019
+ contextData,
7020
+ span,
7021
+ metricState
7022
+ });
6955
7023
  const portable = request.method === "GET" || request.method === "HEAD" ? parsePortableGatewayRequest(url) : null;
6956
- if (portable != null) {
6957
- const response = await this.#fetchPortableObject(request, portable, {
6958
- onNotFound,
6959
- onNotAcceptable,
6960
- onUnauthorized,
6961
- contextData,
6962
- span,
6963
- metricState
6964
- });
6965
- if (request.method !== "HEAD" || response.body == null) return response;
6966
- await response.body.cancel();
6967
- return new Response(null, {
6968
- status: response.status,
6969
- statusText: response.statusText,
6970
- headers: response.headers
6971
- });
6972
- }
7024
+ if (portable != null) return await discardBodyForHead(request, await this.#fetchPortableObject(request, portable, {
7025
+ onNotFound,
7026
+ onNotAcceptable,
7027
+ onUnauthorized,
7028
+ contextData,
7029
+ span,
7030
+ metricState
7031
+ }));
6973
7032
  metricState.endpoint = "not_found";
6974
7033
  return await onNotFound(request);
6975
7034
  }
@@ -7293,8 +7352,67 @@ var FederationImpl = class extends FederationBuilderImpl {
7293
7352
  onNotFound
7294
7353
  });
7295
7354
  }
7355
+ async #fetchHashlinkMedia(request, media, { onNotFound, contextData, span, metricState }) {
7356
+ metricState.endpoint = "hashlink_media";
7357
+ metricState.routeTemplate = HASHLINK_MEDIA_ROUTE_TEMPLATE;
7358
+ span.updateName(`${request.method} ${HASHLINK_MEDIA_ROUTE_TEMPLATE}`);
7359
+ if (request.method !== "GET" && request.method !== "HEAD") return new Response("Method not allowed.", {
7360
+ status: 405,
7361
+ headers: {
7362
+ Allow: "GET, HEAD",
7363
+ "Content-Type": "text/plain; charset=utf-8"
7364
+ }
7365
+ });
7366
+ if (media.type === "malformed") {
7367
+ getLogger([
7368
+ "fedify",
7369
+ "federation",
7370
+ "hashlinkMedia"
7371
+ ]).debug("Malformed FEP-ef61 hashlink media request {url}: {error}", {
7372
+ url: request.url,
7373
+ error: media.error
7374
+ });
7375
+ return new Response(request.method === "HEAD" ? null : "Malformed hashlink.", {
7376
+ status: 400,
7377
+ headers: { "Content-Type": "text/plain; charset=utf-8" }
7378
+ });
7379
+ }
7380
+ const context = this.#createContext(request, contextData);
7381
+ const response = await this.hashlinkMediaDispatcher(context, media.media);
7382
+ if (response == null) return await onNotFound(request);
7383
+ return await discardBodyForHead(request, response, false);
7384
+ }
7296
7385
  };
7297
7386
  const PORTABLE_GATEWAY_ROUTE_PREFIX = "/.well-known/apgateway/{did}";
7387
+ /**
7388
+ * Removes the body of a response to a `HEAD` request.
7389
+ * @param request The request.
7390
+ * @param response The response to the request.
7391
+ * @param waitForCancel Whether to wait until the discarded body is cancelled.
7392
+ * @returns The response without a body if the request is a `HEAD` request,
7393
+ * or the response itself otherwise.
7394
+ */
7395
+ async function discardBodyForHead(request, response, waitForCancel = true) {
7396
+ if (request.method !== "HEAD" || response.body == null) return response;
7397
+ const cancelled = response.body.cancel();
7398
+ if (waitForCancel) await cancelled;
7399
+ else cancelled.catch((error) => {
7400
+ getLogger([
7401
+ "fedify",
7402
+ "federation",
7403
+ "http"
7404
+ ]).debug("Failed to cancel the body of a response to {method} {url}: {error}", {
7405
+ method: request.method,
7406
+ url: request.url,
7407
+ error
7408
+ });
7409
+ });
7410
+ return new Response(null, {
7411
+ status: response.status,
7412
+ statusText: response.statusText,
7413
+ headers: response.headers
7414
+ });
7415
+ }
7298
7416
  function getEndpointCategory(routeName) {
7299
7417
  if (routeName.startsWith("object:")) return "object";
7300
7418
  if (routeName.startsWith("collection:") || routeName.startsWith("orderedCollection:")) return "collection";
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { Bt as WebFingerLinksDispatcher, _t as ActorDispatcher, gt as ActorAliasMapper, u as RequestContext, vt as ActorHandleMapper } from "./context-Bh_mJfbD.cjs";
2
+ import { Ht as WebFingerLinksDispatcher, _t as ActorDispatcher, gt as ActorAliasMapper, u as RequestContext, vt as ActorHandleMapper } from "./context-b36TpPoG.cjs";
3
3
  import { MeterProvider, Span, Tracer } from "@opentelemetry/api";
4
4
  import { RouterError, RouterOptions, RouterRouteResult } from "@fedify/uri-template";
5
5
 
@@ -1,5 +1,5 @@
1
1
  /// <reference lib="esnext.temporal" />
2
- import { Bt as WebFingerLinksDispatcher, _t as ActorDispatcher, gt as ActorAliasMapper, u as RequestContext, vt as ActorHandleMapper } from "./context-BJPHhBmD.js";
2
+ import { Ht as WebFingerLinksDispatcher, _t as ActorDispatcher, gt as ActorAliasMapper, u as RequestContext, vt as ActorHandleMapper } from "./context-D_gg4M0n.js";
3
3
  import { RouterError, RouterOptions, RouterRouteResult } from "@fedify/uri-template";
4
4
  import { MeterProvider, Span, Tracer } from "@opentelemetry/api";
5
5
 
@@ -2,7 +2,7 @@
2
2
  import { S as KeyCache } from "./http-65PYi9jY.js";
3
3
  import { CryptographicKey, DataIntegrityProof, Multikey, Object as Object$1 } from "@fedify/vocab";
4
4
  import { MeterProvider, TracerProvider } from "@opentelemetry/api";
5
- import { DocumentLoader } from "@fedify/vocab-runtime";
5
+ import { DocumentLoader, PortableObjectVerifierOptions } from "@fedify/vocab-runtime";
6
6
 
7
7
  //#region src/sig/ld.d.ts
8
8
  /**
@@ -382,4 +382,152 @@ declare function verifyObject<T extends Object$1>(cls: (new (...args: any[]) =>
382
382
  fromJsonLd(jsonLd: unknown, options: VerifyObjectOptions): Promise<T>;
383
383
  }, jsonLd: unknown, options?: VerifyObjectOptions): Promise<T | null>;
384
384
  //#endregion
385
- export { verifyJsonLd as C, signJsonLd as S, VerifySignatureOptions as _, VerifyPortableObjectProofOptions as a, detachSignature as b, createProof as c, verifyObject as d, verifyPortableObjectProof as f, VerifyJsonLdOptions as g, SignJsonLdOptions as h, VerifyPortableObjectProofFailureReason as i, hasProofLike as l, CreateSignatureOptions as m, SignObjectOptions as n, VerifyPortableObjectProofResult as o, verifyProof as p, VerifyObjectOptions as r, VerifyProofOptions as s, CreateProofOptions as t, signObject as u, attachSignature as v, verifySignature as w, hasSignatureLike as x, createSignature as y };
385
+ //#region src/sig/portable-collection.d.ts
386
+ /**
387
+ * Options for {@link verifyPortableObject}.
388
+ *
389
+ * Besides the options for {@link verifyPortableObjectProof}, it takes where
390
+ * the document came from, which property accessors pass automatically when
391
+ * `verifyPortableObject()` is used as their `verifyPortableObject` option.
392
+ * @since 2.4.0
393
+ */
394
+ interface VerifyPortableObjectOptions extends VerifyPortableObjectProofOptions, Omit<PortableObjectVerifierOptions, "documentLoader" | "contextLoader" | "tracerProvider"> {}
395
+ /**
396
+ * The reason why {@link verifyPortableObject} did not accept a document.
397
+ *
398
+ * Besides the reasons of {@link verifyPortableObjectProof}, it describes why
399
+ * an unsecured portable collection was not trusted. The
400
+ * `unsecuredCollection` reason itself never appears, since such collections
401
+ * are judged by the gateway trust policy instead.
402
+ * @since 2.4.0
403
+ */
404
+ type VerifyPortableObjectFailureReason = Exclude<VerifyPortableObjectProofFailureReason, {
405
+ readonly type: "unsecuredCollection";
406
+ }> | {
407
+ /**
408
+ * The unsecured collection was not retrieved from a gateway, i.e., the
409
+ * document's final URL is missing or is not a compatible identifier for
410
+ * the same portable collection.
411
+ */
412
+ readonly type: "unknownCollectionSource"; /** The final URL of the document, if known. */
413
+ readonly documentUrl: URL | null;
414
+ } | {
415
+ /**
416
+ * The actor that owns the unsecured collection cannot be determined
417
+ * unambiguously.
418
+ */
419
+ readonly type: "unknownCollectionOwner";
420
+ } | {
421
+ /** The collection, its pages, or its referrer disagree on its owner. */readonly type: "conflictingCollectionOwners"; /** The owner candidates that disagree. */
422
+ readonly candidates: readonly URL[];
423
+ } | {
424
+ /**
425
+ * A collection page does not belong to the collection it was reached
426
+ * from: its `partOf` names another collection, or its cryptographic
427
+ * origin differs.
428
+ */
429
+ readonly type: "collectionPageMismatch"; /** The page. */
430
+ readonly pageId: URL; /** The collection that the page was reached from. */
431
+ readonly collectionId: URL; /** The mismatching `partOf`, or `null` if the origin differs. */
432
+ readonly partOf: URL | null;
433
+ } | {
434
+ /**
435
+ * The owner's actor document could not be retrieved or verified, or it
436
+ * lists no gateways.
437
+ */
438
+ readonly type: "collectionOwnerUnavailable"; /** The owner candidate. */
439
+ readonly owner: URL;
440
+ } | {
441
+ /**
442
+ * The owner's actor document does not list the collection as its
443
+ * `inbox`, `outbox`, `followers`, `following`, or `liked`.
444
+ */
445
+ readonly type: "collectionNotListedByOwner"; /** The owner. */
446
+ readonly owner: URL; /** The collection. */
447
+ readonly collectionId: URL;
448
+ } | {
449
+ /**
450
+ * The unsecured collection was retrieved from a gateway that its owner
451
+ * does not list in its `gateways`.
452
+ */
453
+ readonly type: "untrustedGateway"; /** The owner. */
454
+ readonly owner: URL; /** The gateway that served the collection. */
455
+ readonly gateway: URL; /** The owner's gateways. */
456
+ readonly gateways: readonly URL[];
457
+ };
458
+ /**
459
+ * The result of {@link verifyPortableObject}.
460
+ * @since 2.4.0
461
+ */
462
+ type VerifyPortableObjectResult = {
463
+ /** Whether the document is accepted. */readonly verified: true; /** The document carries valid Object Integrity Proofs. */
464
+ readonly method: "proof"; /** The public keys used by the verified proofs, in proof order. */
465
+ readonly keys: readonly Multikey[];
466
+ /**
467
+ * Opaque state for verifying the pages of a portable collection. Do not
468
+ * inspect it; property accessors pass it back automatically.
469
+ */
470
+ readonly collectionContext?: unknown;
471
+ } | {
472
+ /** Whether the document is accepted. */readonly verified: true;
473
+ /**
474
+ * The document is an unsecured portable collection served by a gateway
475
+ * that its owner lists.
476
+ */
477
+ readonly method: "gateway"; /** The document has no integrity proof. */
478
+ readonly unsecured: true; /** The actor that owns the collection. */
479
+ readonly owner: URL; /** The gateway that served the collection. */
480
+ readonly gateway: URL;
481
+ /**
482
+ * Opaque state for verifying the pages of a portable collection. Do not
483
+ * inspect it; property accessors pass it back automatically.
484
+ */
485
+ readonly collectionContext: unknown;
486
+ } | {
487
+ /** Whether the document is accepted. */readonly verified: false; /** Why the document was not accepted. */
488
+ readonly reason: VerifyPortableObjectFailureReason;
489
+ };
490
+ /**
491
+ * Applies the [FEP-ef61] trust policy to a portable object fetched through
492
+ * a gateway.
493
+ *
494
+ * Portable actors, activities, objects, and collections with proofs are
495
+ * verified by {@link verifyPortableObjectProof}. A portable collection or
496
+ * collection page without a proof is accepted only if all of the following
497
+ * hold:
498
+ *
499
+ * - It was retrieved from a gateway: its final URL (`documentUrl`) is
500
+ * a compatible identifier for the same portable collection.
501
+ * - Its owner is determined unambiguously: the actor whose `inbox`,
502
+ * `outbox`, `followers`, `following`, or `liked` property referred to
503
+ * the collection, or the single actor in its `attributedTo`. A page
504
+ * inherits the owner of the collection it was reached from through
505
+ * `first`, `last`, `current`, `next`, or `prev`, and must not name
506
+ * another collection in `partOf`.
507
+ * - The owner's actor document is verified by its proof and lists the
508
+ * collection as its `inbox`, `outbox`, `followers`, `following`, or
509
+ * `liked`.
510
+ * - The gateway that served the collection is in the owner's `gateways`.
511
+ *
512
+ * Where the document came from and which object referred to it are passed
513
+ * by property accessors when this function is used as their
514
+ * `verifyPortableObject` option. Objects embedded in an unsecured
515
+ * collection accepted this way are not trusted: accessors dereference and
516
+ * verify them one by one.
517
+ *
518
+ * Note that acceptance through the gateway policy does not verify anything
519
+ * cryptographically; it only means that the owner trusts the gateway.
520
+ *
521
+ * [FEP-ef61]: https://w3id.org/fep/ef61
522
+ *
523
+ * @param jsonLd The JSON-LD document to verify.
524
+ * @param options Additional options. See also
525
+ * {@link VerifyPortableObjectOptions}.
526
+ * @returns The detailed result.
527
+ * @throws {TypeError} If the input is not a single JSON-LD object or has a
528
+ * malformed portable ID.
529
+ * @since 2.4.0
530
+ */
531
+ declare function verifyPortableObject(jsonLd: unknown, options?: VerifyPortableObjectOptions): Promise<VerifyPortableObjectResult>;
532
+ //#endregion
533
+ export { createSignature as C, verifyJsonLd as D, signJsonLd as E, verifySignature as O, attachSignature as S, hasSignatureLike as T, verifyProof as _, CreateProofOptions as a, VerifyJsonLdOptions as b, VerifyPortableObjectProofFailureReason as c, VerifyProofOptions as d, createProof as f, verifyPortableObjectProof as g, verifyObject as h, verifyPortableObject as i, VerifyPortableObjectProofOptions as l, signObject as m, VerifyPortableObjectOptions as n, SignObjectOptions as o, hasProofLike as p, VerifyPortableObjectResult as r, VerifyObjectOptions as s, VerifyPortableObjectFailureReason as t, VerifyPortableObjectProofResult as u, CreateSignatureOptions as v, detachSignature as w, VerifySignatureOptions as x, SignJsonLdOptions as y };