@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.
- package/dist/{builder-C86Z3BKs.mjs → builder-BzS4brdy.mjs} +8 -2
- package/dist/compat/mod.d.cts +1 -1
- package/dist/compat/mod.d.ts +1 -1
- package/dist/compat/transformers.test.mjs +1 -1
- package/dist/{compound-proof-BcZqTCwy.mjs → compound-proof-BrSXP5so.mjs} +1 -1
- package/dist/{context-BJPHhBmD.d.ts → context-D_gg4M0n.d.ts} +102 -1
- package/dist/{context-Bh_mJfbD.d.cts → context-b36TpPoG.d.cts} +102 -1
- package/dist/{deno-Bz6KTcon.mjs → deno-CVZgG4sc.mjs} +1 -1
- package/dist/{docloader-DS54zLD1.mjs → docloader-DBb8z7_l.mjs} +2 -2
- package/dist/federation/builder.test.mjs +1 -1
- package/dist/federation/compound-fanout.test.mjs +3 -3
- package/dist/federation/handler-compound-proof.test.mjs +3 -3
- package/dist/federation/handler.test.mjs +5 -5
- package/dist/federation/hashlink-media.test.d.mts +2 -0
- package/dist/federation/hashlink-media.test.mjs +422 -0
- package/dist/federation/idempotency.test.mjs +2 -2
- package/dist/federation/metrics.test.mjs +1 -1
- package/dist/federation/middleware.test.mjs +7 -7
- package/dist/federation/mod.cjs +1 -1
- package/dist/federation/mod.d.cts +3 -3
- package/dist/federation/mod.d.ts +3 -3
- package/dist/federation/mod.js +1 -1
- package/dist/federation/outgoing-proof.test.mjs +5 -5
- package/dist/federation/portable.test.mjs +3 -3
- package/dist/federation/router.test.mjs +2 -152
- package/dist/federation/send.test.mjs +3 -3
- package/dist/federation/tasks/enqueue.test.mjs +1 -1
- package/dist/federation/tasks/tasks.test.mjs +2 -2
- package/dist/federation/temporal.test.mjs +1 -1
- package/dist/federation/webfinger.test.mjs +1 -1
- package/dist/{federation-su1OEWsM.cjs → federation-BrsIzNK5.cjs} +139 -21
- package/dist/{http-C3WGK1dL.js → http-99KVDtMr.js} +1 -1
- package/dist/{http-LmUsMjK_.cjs → http-CVjwUCCu.cjs} +1 -1
- package/dist/{http-Ty805y3A.mjs → http-znjq5j6a.mjs} +3 -3
- package/dist/{key-BMc-2eW0.mjs → key-BnMA6vpl.mjs} +2 -2
- package/dist/{kv-cache-BhPPtHg7.mjs → kv-cache--REpvHgG.mjs} +1 -1
- package/dist/{kv-cache-CuaZnk6r.cjs → kv-cache-D2O0JspA.cjs} +1 -1
- package/dist/{kv-cache-BcHCmrC8.js → kv-cache-aijDx_jN.js} +1 -1
- package/dist/{ld-OpXWt-80.mjs → ld-BBVP8ock.mjs} +3 -3
- package/dist/{metrics-CTAe8lYz.mjs → metrics-B5spFq82.mjs} +1 -1
- package/dist/{middleware-C-JLcqy7.mjs → middleware-ClrGS1xy.mjs} +1 -1
- package/dist/{middleware-BtwW3G41.mjs → middleware-Cw8EWwAT.mjs} +145 -33
- package/dist/{middleware-_CMav1o7.js → middleware-CzYM4cJI.js} +140 -22
- package/dist/{mod-DU8aVzQJ.d.cts → mod-Bfenkg_c.d.cts} +1 -1
- package/dist/{mod-BofpDIcU.d.ts → mod-BhMvsgdF.d.ts} +1 -1
- package/dist/{mod-CNRAxXEb.d.ts → mod-Dhm7SqyE.d.ts} +150 -2
- package/dist/{mod-CVQFWefn.d.cts → mod-pLbeNwcC.d.cts} +150 -2
- package/dist/mod.cjs +6 -5
- package/dist/mod.d.cts +5 -5
- package/dist/mod.d.ts +5 -5
- package/dist/mod.js +6 -6
- package/dist/nodeinfo/handler.test.mjs +1 -1
- package/dist/{owner-Bcm4pLXw.cjs → owner-CHBQ0sSj.cjs} +34 -2
- package/dist/{owner-B-TLr-1I.js → owner-Cu_KMvQN.js} +29 -3
- package/dist/{owner-RxB_eQpU.mjs → owner-Hc0hvbbX.mjs} +2 -2
- package/dist/{proof-CVf2XbEa.mjs → proof-B69bJaSn.mjs} +32 -6
- package/dist/router-u5O5KCQr.mjs +156 -0
- package/dist/{send-DF9KzhJH.mjs → send-BB5rBhvN.mjs} +3 -3
- package/dist/sig/compound-proof-discovery.test.mjs +1 -1
- package/dist/sig/compound-proof-producer.test.mjs +1 -1
- package/dist/sig/compound-proof-verification.test.mjs +2 -2
- package/dist/sig/compound-proof.test.mjs +2 -2
- package/dist/sig/http.test.mjs +2 -2
- package/dist/sig/key.test.mjs +1 -1
- package/dist/sig/ld.test.mjs +2 -2
- package/dist/sig/mod.cjs +4 -2
- package/dist/sig/mod.d.cts +2 -2
- package/dist/sig/mod.d.ts +2 -2
- package/dist/sig/mod.js +4 -3
- package/dist/sig/owner.test.mjs +1 -1
- package/dist/sig/portable-collection.test.d.mts +2 -0
- package/dist/sig/portable-collection.test.mjs +971 -0
- package/dist/sig/proof.test.mjs +3 -3
- package/dist/sig-B8WDBV-h.js +401 -0
- package/dist/sig-dut4BzEW.cjs +405 -0
- package/dist/{temporal-B6U3fEUW.mjs → temporal-CcRkOx3C.mjs} +1 -1
- package/dist/testing/kv.test.mjs +1 -1
- package/dist/testing/mod.d.mts +101 -0
- package/dist/utils/docloader.test.mjs +2 -2
- package/dist/utils/kv-cache.test.mjs +1 -1
- package/dist/utils/mod.cjs +1 -1
- package/dist/utils/mod.js +1 -1
- 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-
|
|
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-
|
|
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-
|
|
8
|
-
import { l as verifyRequest, o as parseRfc9421SignatureInput, u as verifyRequestDetailed } from "./http-
|
|
9
|
-
import { t as getAuthenticatedDocumentLoader } from "./docloader-
|
|
10
|
-
import { n as kvCache } from "./kv-cache
|
|
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-
|
|
15
|
-
import { n as getKeyOwner, t as doesActorOwnKey } from "./owner-
|
|
16
|
-
import { a as verifyObject, n as hasProofLike, o as verifyPortableObjectProof, r as signObject } from "./proof-
|
|
17
|
-
import { n as extractInboxes, r as sendActivity, t as SendActivityError } from "./send-
|
|
18
|
-
import { n as FederationBuilderImpl, t as ACTOR_ALIAS_PREFIX } from "./builder-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4502
|
-
|
|
4503
|
-
|
|
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 {
|
|
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-
|
|
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
|
|
8
|
-
import { n as getAuthenticatedDocumentLoader, t as kvCache } from "./kv-cache-
|
|
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
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
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 };
|