@fedify/vocab-runtime 2.4.0-dev.2010 → 2.4.0-dev.2027
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/deno.json +2 -1
- package/dist/contexts-2hEbs3Qk.cjs +4513 -0
- package/dist/contexts-DoZUIW7y.js +4508 -0
- package/dist/internal/signed-representation.cjs +378 -0
- package/dist/internal/signed-representation.d.cts +144 -0
- package/dist/internal/signed-representation.d.ts +144 -0
- package/dist/internal/signed-representation.js +371 -0
- package/dist/mod.cjs +5 -4510
- package/dist/mod.js +2 -4507
- package/dist/tests/{body-CI3XB0P0.mjs → body-C8W6PG-f.mjs} +1 -1
- package/dist/tests/{body-CfLJw2_P.cjs → body-CjqMU6be.cjs} +1 -1
- package/dist/tests/body.test.cjs +1 -1
- package/dist/tests/body.test.mjs +1 -1
- package/dist/tests/{docloader-D9VClviZ.cjs → contexts-BBX_qhBL.mjs} +1 -243
- package/dist/tests/{docloader-BhIci12h.mjs → contexts-Ch3v2Cgu.cjs} +10 -218
- package/dist/tests/decimal.test.cjs +1 -1
- package/dist/tests/decimal.test.mjs +1 -1
- package/dist/tests/docloader-BcthkJvx.mjs +221 -0
- package/dist/tests/docloader-DDwe-rFE.cjs +232 -0
- package/dist/tests/docloader.test.cjs +6 -5
- package/dist/tests/docloader.test.mjs +3 -2
- package/dist/tests/{request-Dq_yJVr_.mjs → request-CeEwiwiU.mjs} +1 -1
- package/dist/tests/{request-zC7c6dLm.cjs → request-DV2_yz3V.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/signed-representation.test.cjs +588 -0
- package/dist/tests/signed-representation.test.d.cts +1 -0
- package/dist/tests/signed-representation.test.d.mts +1 -0
- package/dist/tests/signed-representation.test.mjs +589 -0
- package/package.json +11 -1
- package/src/internal/signed-representation.ts +565 -0
- package/src/signed-representation.test.ts +338 -0
- package/tsdown.config.ts +1 -0
package/dist/tests/body.test.cjs
CHANGED
package/dist/tests/body.test.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { r as readBoundedText, t as BodyTooLargeError } from "./body-
|
|
1
|
+
import { r as readBoundedText, t as BodyTooLargeError } from "./body-C8W6PG-f.mjs";
|
|
2
2
|
import { deepStrictEqual, ok, rejects } from "node:assert/strict";
|
|
3
3
|
import { test } from "node:test";
|
|
4
4
|
import { gunzipSync, gzipSync } from "node:zlib";
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
const require_request = require("./request-zC7c6dLm.cjs");
|
|
2
|
-
const require_body = require("./body-CfLJw2_P.cjs");
|
|
3
|
-
const require_link = require("./link-DlKm8bEr.cjs");
|
|
4
|
-
const require_url = require("./url-CPfMatUL.cjs");
|
|
5
|
-
let _logtape_logtape = require("@logtape/logtape");
|
|
6
|
-
let _opentelemetry_api = require("@opentelemetry/api");
|
|
7
1
|
//#region src/contexts/activitystreams.json
|
|
8
2
|
var activitystreams_default = { "@context": {
|
|
9
3
|
"@vocab": "_:",
|
|
@@ -4515,240 +4509,4 @@ const preloadedContexts = {
|
|
|
4515
4509
|
} }
|
|
4516
4510
|
};
|
|
4517
4511
|
//#endregion
|
|
4518
|
-
|
|
4519
|
-
const logger = (0, _logtape_logtape.getLogger)([
|
|
4520
|
-
"fedify",
|
|
4521
|
-
"runtime",
|
|
4522
|
-
"docloader"
|
|
4523
|
-
]);
|
|
4524
|
-
const DEFAULT_MAX_REDIRECTION = 20;
|
|
4525
|
-
const MAX_HTML_SIZE = 1024 * 1024;
|
|
4526
|
-
function createResponseMetadata(response) {
|
|
4527
|
-
return new Response(null, {
|
|
4528
|
-
headers: response.headers,
|
|
4529
|
-
status: response.status,
|
|
4530
|
-
statusText: response.statusText
|
|
4531
|
-
});
|
|
4532
|
-
}
|
|
4533
|
-
/**
|
|
4534
|
-
* Gets a {@link RemoteDocument} from the given response.
|
|
4535
|
-
* @param url The URL of the document to load.
|
|
4536
|
-
* @param response The response to get the document from.
|
|
4537
|
-
* @param fetch The function to fetch the document.
|
|
4538
|
-
* @returns The loaded remote document.
|
|
4539
|
-
* @throws {FetchError} If the response is not OK.
|
|
4540
|
-
* @internal
|
|
4541
|
-
*/
|
|
4542
|
-
async function getRemoteDocument(url, response, fetch) {
|
|
4543
|
-
const documentUrl = response.url === "" ? url : response.url;
|
|
4544
|
-
const docUrl = new URL(documentUrl);
|
|
4545
|
-
if (!response.ok) {
|
|
4546
|
-
logger.error("Failed to fetch document: {status} {url} {headers}", {
|
|
4547
|
-
status: response.status,
|
|
4548
|
-
url: documentUrl,
|
|
4549
|
-
headers: Object.fromEntries(response.headers.entries())
|
|
4550
|
-
});
|
|
4551
|
-
throw new require_request.FetchError(documentUrl, `HTTP ${response.status}: ${documentUrl}`, response.clone());
|
|
4552
|
-
}
|
|
4553
|
-
const contentType = response.headers.get("Content-Type");
|
|
4554
|
-
const jsonLd = contentType == null || contentType === "application/activity+json" || contentType.startsWith("application/activity+json;") || contentType === "application/ld+json" || contentType.startsWith("application/ld+json;");
|
|
4555
|
-
const linkHeader = response.headers.get("Link");
|
|
4556
|
-
let contextUrl = null;
|
|
4557
|
-
if (linkHeader != null) {
|
|
4558
|
-
let link;
|
|
4559
|
-
try {
|
|
4560
|
-
link = new require_link.HttpHeaderLink(linkHeader);
|
|
4561
|
-
} catch (e) {
|
|
4562
|
-
if (e instanceof SyntaxError) link = new require_link.HttpHeaderLink();
|
|
4563
|
-
else throw e;
|
|
4564
|
-
}
|
|
4565
|
-
if (jsonLd) {
|
|
4566
|
-
const entries = link.getByRel("http://www.w3.org/ns/json-ld#context");
|
|
4567
|
-
for (const [uri, params] of entries) if ("type" in params && params.type === "application/ld+json") {
|
|
4568
|
-
contextUrl = uri;
|
|
4569
|
-
break;
|
|
4570
|
-
}
|
|
4571
|
-
} else {
|
|
4572
|
-
const entries = link.getByRel("alternate");
|
|
4573
|
-
for (const [uri, params] of entries) {
|
|
4574
|
-
const altUri = new URL(uri, docUrl);
|
|
4575
|
-
if ("type" in params && (params.type === "application/activity+json" || params.type === "application/ld+json" || params.type.startsWith("application/ld+json;")) && altUri.href !== docUrl.href) {
|
|
4576
|
-
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
4577
|
-
alternateUrl: altUri.href,
|
|
4578
|
-
url: documentUrl
|
|
4579
|
-
});
|
|
4580
|
-
return await fetch(altUri.href);
|
|
4581
|
-
}
|
|
4582
|
-
}
|
|
4583
|
-
}
|
|
4584
|
-
}
|
|
4585
|
-
let document;
|
|
4586
|
-
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
4587
|
-
const errorResponse = createResponseMetadata(response);
|
|
4588
|
-
let html;
|
|
4589
|
-
try {
|
|
4590
|
-
html = await require_body.readBoundedText(response, MAX_HTML_SIZE, documentUrl);
|
|
4591
|
-
} catch (error) {
|
|
4592
|
-
if (!(error instanceof require_body.BodyTooLargeError)) throw error;
|
|
4593
|
-
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
4594
|
-
url: documentUrl,
|
|
4595
|
-
maxBytes: MAX_HTML_SIZE
|
|
4596
|
-
});
|
|
4597
|
-
throw new require_request.FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4598
|
-
}
|
|
4599
|
-
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
4600
|
-
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
4601
|
-
let tagMatch;
|
|
4602
|
-
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
4603
|
-
const tagContent = tagMatch[2];
|
|
4604
|
-
let attrMatch;
|
|
4605
|
-
const attribs = {};
|
|
4606
|
-
attrPattern.lastIndex = 0;
|
|
4607
|
-
while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
|
|
4608
|
-
const key = attrMatch[1].toLowerCase();
|
|
4609
|
-
attribs[key] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
|
|
4610
|
-
}
|
|
4611
|
-
if (attribs.rel === "alternate" && "type" in attribs && (attribs.type === "application/activity+json" || attribs.type === "application/ld+json" || attribs.type.startsWith("application/ld+json;")) && "href" in attribs && new URL(attribs.href, docUrl).href !== docUrl.href) {
|
|
4612
|
-
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
4613
|
-
alternateUrl: attribs.href,
|
|
4614
|
-
url: documentUrl
|
|
4615
|
-
});
|
|
4616
|
-
return await fetch(new URL(attribs.href, docUrl).href);
|
|
4617
|
-
}
|
|
4618
|
-
}
|
|
4619
|
-
try {
|
|
4620
|
-
document = JSON.parse(html);
|
|
4621
|
-
} catch (error) {
|
|
4622
|
-
if (!(error instanceof SyntaxError)) throw error;
|
|
4623
|
-
throw new require_request.FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4624
|
-
}
|
|
4625
|
-
} else document = JSON.parse(await require_body.readBoundedText(response, require_body.MAX_BODY_SIZE, documentUrl));
|
|
4626
|
-
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
4627
|
-
status: response.status,
|
|
4628
|
-
url: documentUrl,
|
|
4629
|
-
headers: Object.fromEntries(response.headers.entries())
|
|
4630
|
-
});
|
|
4631
|
-
return {
|
|
4632
|
-
contextUrl,
|
|
4633
|
-
document,
|
|
4634
|
-
documentUrl
|
|
4635
|
-
};
|
|
4636
|
-
}
|
|
4637
|
-
/**
|
|
4638
|
-
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
4639
|
-
*
|
|
4640
|
-
* The created loader preloads the below frequently used contexts by default
|
|
4641
|
-
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
4642
|
-
*
|
|
4643
|
-
* - <https://www.w3.org/ns/activitystreams>
|
|
4644
|
-
* - <https://w3id.org/security/v1>
|
|
4645
|
-
* - <https://w3id.org/security/data-integrity/v1>
|
|
4646
|
-
* - <https://w3id.org/security/data-integrity/v2>
|
|
4647
|
-
* - <https://www.w3.org/ns/did/v1>
|
|
4648
|
-
* - <https://www.w3.org/ns/cid/v1>
|
|
4649
|
-
* - <https://w3id.org/security/multikey/v1>
|
|
4650
|
-
* - <https://w3id.org/fep/ef61>
|
|
4651
|
-
* - <https://purl.archive.org/socialweb/webfinger>
|
|
4652
|
-
* - <http://schema.org/>
|
|
4653
|
-
* @param options Options for the document loader.
|
|
4654
|
-
* @returns The document loader.
|
|
4655
|
-
* @since 1.3.0
|
|
4656
|
-
*/
|
|
4657
|
-
function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}) {
|
|
4658
|
-
const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(require_request.name, require_request.version);
|
|
4659
|
-
const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
4660
|
-
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
4661
|
-
options?.signal?.throwIfAborted();
|
|
4662
|
-
const currentUrl = new URL(url).href;
|
|
4663
|
-
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
4664
|
-
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
4665
|
-
return {
|
|
4666
|
-
contextUrl: null,
|
|
4667
|
-
document: preloadedContexts[currentUrl],
|
|
4668
|
-
documentUrl: currentUrl
|
|
4669
|
-
};
|
|
4670
|
-
}
|
|
4671
|
-
if (!allowPrivateAddress) try {
|
|
4672
|
-
await require_url.validatePublicUrl(currentUrl);
|
|
4673
|
-
} catch (error) {
|
|
4674
|
-
if (error instanceof require_url.UrlError) logger.error("Disallowed private URL: {url}", {
|
|
4675
|
-
url: currentUrl,
|
|
4676
|
-
error
|
|
4677
|
-
});
|
|
4678
|
-
throw error;
|
|
4679
|
-
}
|
|
4680
|
-
visited.add(currentUrl);
|
|
4681
|
-
return await tracer.startActiveSpan("activitypub.fetch_document", {
|
|
4682
|
-
kind: _opentelemetry_api.SpanKind.CLIENT,
|
|
4683
|
-
attributes: { "url.full": currentUrl }
|
|
4684
|
-
}, async (span) => {
|
|
4685
|
-
try {
|
|
4686
|
-
const request = require_request.createActivityPubRequest(currentUrl, { userAgent });
|
|
4687
|
-
require_request.logRequest(logger, request);
|
|
4688
|
-
const response = await fetch(request, {
|
|
4689
|
-
redirect: "manual",
|
|
4690
|
-
signal: options?.signal
|
|
4691
|
-
});
|
|
4692
|
-
span.setAttribute("http.response.status_code", response.status);
|
|
4693
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
4694
|
-
if (redirected >= maximumRedirection) {
|
|
4695
|
-
logger.error("Too many redirections ({redirections}) while fetching document.", {
|
|
4696
|
-
redirections: redirected + 1,
|
|
4697
|
-
url: currentUrl
|
|
4698
|
-
});
|
|
4699
|
-
throw new require_request.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
4700
|
-
}
|
|
4701
|
-
const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
|
|
4702
|
-
span.setAttribute("http.redirect.url", redirectUrl);
|
|
4703
|
-
if (visited.has(redirectUrl)) {
|
|
4704
|
-
logger.error("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
|
|
4705
|
-
url: currentUrl,
|
|
4706
|
-
redirectUrl
|
|
4707
|
-
});
|
|
4708
|
-
throw new require_request.FetchError(currentUrl, `Redirect loop detected: ${redirectUrl}`);
|
|
4709
|
-
}
|
|
4710
|
-
return await load(redirectUrl, options, redirected + 1, visited);
|
|
4711
|
-
}
|
|
4712
|
-
const result = await getRemoteDocument(currentUrl, response, load);
|
|
4713
|
-
span.setAttribute("docloader.document_url", result.documentUrl);
|
|
4714
|
-
if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
|
|
4715
|
-
return result;
|
|
4716
|
-
} catch (error) {
|
|
4717
|
-
span.recordException(error);
|
|
4718
|
-
span.setStatus({
|
|
4719
|
-
code: _opentelemetry_api.SpanStatusCode.ERROR,
|
|
4720
|
-
message: String(error)
|
|
4721
|
-
});
|
|
4722
|
-
throw error;
|
|
4723
|
-
} finally {
|
|
4724
|
-
span.end();
|
|
4725
|
-
}
|
|
4726
|
-
});
|
|
4727
|
-
}
|
|
4728
|
-
return load;
|
|
4729
|
-
}
|
|
4730
|
-
//#endregion
|
|
4731
|
-
Object.defineProperty(exports, "cid_v1_default", {
|
|
4732
|
-
enumerable: true,
|
|
4733
|
-
get: function() {
|
|
4734
|
-
return cid_v1_default;
|
|
4735
|
-
}
|
|
4736
|
-
});
|
|
4737
|
-
Object.defineProperty(exports, "getDocumentLoader", {
|
|
4738
|
-
enumerable: true,
|
|
4739
|
-
get: function() {
|
|
4740
|
-
return getDocumentLoader;
|
|
4741
|
-
}
|
|
4742
|
-
});
|
|
4743
|
-
Object.defineProperty(exports, "getRemoteDocument", {
|
|
4744
|
-
enumerable: true,
|
|
4745
|
-
get: function() {
|
|
4746
|
-
return getRemoteDocument;
|
|
4747
|
-
}
|
|
4748
|
-
});
|
|
4749
|
-
Object.defineProperty(exports, "preloadedContexts", {
|
|
4750
|
-
enumerable: true,
|
|
4751
|
-
get: function() {
|
|
4752
|
-
return preloadedContexts;
|
|
4753
|
-
}
|
|
4754
|
-
});
|
|
4512
|
+
export { cid_v1_default as n, preloadedContexts as t };
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-Dq_yJVr_.mjs";
|
|
2
|
-
import { n as MAX_BODY_SIZE, r as readBoundedText, t as BodyTooLargeError } from "./body-CI3XB0P0.mjs";
|
|
3
|
-
import { t as HttpHeaderLink } from "./link-Cevmc87v.mjs";
|
|
4
|
-
import { g as validatePublicUrl, t as UrlError } from "./url-Basm1L_c.mjs";
|
|
5
|
-
import { getLogger } from "@logtape/logtape";
|
|
6
|
-
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
7
1
|
//#region src/contexts/activitystreams.json
|
|
8
2
|
var activitystreams_default = { "@context": {
|
|
9
3
|
"@vocab": "_:",
|
|
@@ -4515,217 +4509,15 @@ const preloadedContexts = {
|
|
|
4515
4509
|
} }
|
|
4516
4510
|
};
|
|
4517
4511
|
//#endregion
|
|
4518
|
-
|
|
4519
|
-
|
|
4520
|
-
|
|
4521
|
-
|
|
4522
|
-
"docloader"
|
|
4523
|
-
]);
|
|
4524
|
-
const DEFAULT_MAX_REDIRECTION = 20;
|
|
4525
|
-
const MAX_HTML_SIZE = 1024 * 1024;
|
|
4526
|
-
function createResponseMetadata(response) {
|
|
4527
|
-
return new Response(null, {
|
|
4528
|
-
headers: response.headers,
|
|
4529
|
-
status: response.status,
|
|
4530
|
-
statusText: response.statusText
|
|
4531
|
-
});
|
|
4532
|
-
}
|
|
4533
|
-
/**
|
|
4534
|
-
* Gets a {@link RemoteDocument} from the given response.
|
|
4535
|
-
* @param url The URL of the document to load.
|
|
4536
|
-
* @param response The response to get the document from.
|
|
4537
|
-
* @param fetch The function to fetch the document.
|
|
4538
|
-
* @returns The loaded remote document.
|
|
4539
|
-
* @throws {FetchError} If the response is not OK.
|
|
4540
|
-
* @internal
|
|
4541
|
-
*/
|
|
4542
|
-
async function getRemoteDocument(url, response, fetch) {
|
|
4543
|
-
const documentUrl = response.url === "" ? url : response.url;
|
|
4544
|
-
const docUrl = new URL(documentUrl);
|
|
4545
|
-
if (!response.ok) {
|
|
4546
|
-
logger.error("Failed to fetch document: {status} {url} {headers}", {
|
|
4547
|
-
status: response.status,
|
|
4548
|
-
url: documentUrl,
|
|
4549
|
-
headers: Object.fromEntries(response.headers.entries())
|
|
4550
|
-
});
|
|
4551
|
-
throw new FetchError(documentUrl, `HTTP ${response.status}: ${documentUrl}`, response.clone());
|
|
4512
|
+
Object.defineProperty(exports, "cid_v1_default", {
|
|
4513
|
+
enumerable: true,
|
|
4514
|
+
get: function() {
|
|
4515
|
+
return cid_v1_default;
|
|
4552
4516
|
}
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
let link;
|
|
4559
|
-
try {
|
|
4560
|
-
link = new HttpHeaderLink(linkHeader);
|
|
4561
|
-
} catch (e) {
|
|
4562
|
-
if (e instanceof SyntaxError) link = new HttpHeaderLink();
|
|
4563
|
-
else throw e;
|
|
4564
|
-
}
|
|
4565
|
-
if (jsonLd) {
|
|
4566
|
-
const entries = link.getByRel("http://www.w3.org/ns/json-ld#context");
|
|
4567
|
-
for (const [uri, params] of entries) if ("type" in params && params.type === "application/ld+json") {
|
|
4568
|
-
contextUrl = uri;
|
|
4569
|
-
break;
|
|
4570
|
-
}
|
|
4571
|
-
} else {
|
|
4572
|
-
const entries = link.getByRel("alternate");
|
|
4573
|
-
for (const [uri, params] of entries) {
|
|
4574
|
-
const altUri = new URL(uri, docUrl);
|
|
4575
|
-
if ("type" in params && (params.type === "application/activity+json" || params.type === "application/ld+json" || params.type.startsWith("application/ld+json;")) && altUri.href !== docUrl.href) {
|
|
4576
|
-
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
4577
|
-
alternateUrl: altUri.href,
|
|
4578
|
-
url: documentUrl
|
|
4579
|
-
});
|
|
4580
|
-
return await fetch(altUri.href);
|
|
4581
|
-
}
|
|
4582
|
-
}
|
|
4583
|
-
}
|
|
4584
|
-
}
|
|
4585
|
-
let document;
|
|
4586
|
-
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
4587
|
-
const errorResponse = createResponseMetadata(response);
|
|
4588
|
-
let html;
|
|
4589
|
-
try {
|
|
4590
|
-
html = await readBoundedText(response, MAX_HTML_SIZE, documentUrl);
|
|
4591
|
-
} catch (error) {
|
|
4592
|
-
if (!(error instanceof BodyTooLargeError)) throw error;
|
|
4593
|
-
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
4594
|
-
url: documentUrl,
|
|
4595
|
-
maxBytes: MAX_HTML_SIZE
|
|
4596
|
-
});
|
|
4597
|
-
throw new FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4598
|
-
}
|
|
4599
|
-
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
4600
|
-
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
4601
|
-
let tagMatch;
|
|
4602
|
-
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
4603
|
-
const tagContent = tagMatch[2];
|
|
4604
|
-
let attrMatch;
|
|
4605
|
-
const attribs = {};
|
|
4606
|
-
attrPattern.lastIndex = 0;
|
|
4607
|
-
while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
|
|
4608
|
-
const key = attrMatch[1].toLowerCase();
|
|
4609
|
-
attribs[key] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
|
|
4610
|
-
}
|
|
4611
|
-
if (attribs.rel === "alternate" && "type" in attribs && (attribs.type === "application/activity+json" || attribs.type === "application/ld+json" || attribs.type.startsWith("application/ld+json;")) && "href" in attribs && new URL(attribs.href, docUrl).href !== docUrl.href) {
|
|
4612
|
-
logger.debug("Found alternate document: {alternateUrl} from {url}", {
|
|
4613
|
-
alternateUrl: attribs.href,
|
|
4614
|
-
url: documentUrl
|
|
4615
|
-
});
|
|
4616
|
-
return await fetch(new URL(attribs.href, docUrl).href);
|
|
4617
|
-
}
|
|
4618
|
-
}
|
|
4619
|
-
try {
|
|
4620
|
-
document = JSON.parse(html);
|
|
4621
|
-
} catch (error) {
|
|
4622
|
-
if (!(error instanceof SyntaxError)) throw error;
|
|
4623
|
-
throw new FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4624
|
-
}
|
|
4625
|
-
} else document = JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, documentUrl));
|
|
4626
|
-
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
4627
|
-
status: response.status,
|
|
4628
|
-
url: documentUrl,
|
|
4629
|
-
headers: Object.fromEntries(response.headers.entries())
|
|
4630
|
-
});
|
|
4631
|
-
return {
|
|
4632
|
-
contextUrl,
|
|
4633
|
-
document,
|
|
4634
|
-
documentUrl
|
|
4635
|
-
};
|
|
4636
|
-
}
|
|
4637
|
-
/**
|
|
4638
|
-
* Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
|
|
4639
|
-
*
|
|
4640
|
-
* The created loader preloads the below frequently used contexts by default
|
|
4641
|
-
* (unless `options.skipPreloadedContexts` is set to `true`):
|
|
4642
|
-
*
|
|
4643
|
-
* - <https://www.w3.org/ns/activitystreams>
|
|
4644
|
-
* - <https://w3id.org/security/v1>
|
|
4645
|
-
* - <https://w3id.org/security/data-integrity/v1>
|
|
4646
|
-
* - <https://w3id.org/security/data-integrity/v2>
|
|
4647
|
-
* - <https://www.w3.org/ns/did/v1>
|
|
4648
|
-
* - <https://www.w3.org/ns/cid/v1>
|
|
4649
|
-
* - <https://w3id.org/security/multikey/v1>
|
|
4650
|
-
* - <https://w3id.org/fep/ef61>
|
|
4651
|
-
* - <https://purl.archive.org/socialweb/webfinger>
|
|
4652
|
-
* - <http://schema.org/>
|
|
4653
|
-
* @param options Options for the document loader.
|
|
4654
|
-
* @returns The document loader.
|
|
4655
|
-
* @since 1.3.0
|
|
4656
|
-
*/
|
|
4657
|
-
function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}) {
|
|
4658
|
-
const tracer = trace.getTracerProvider().getTracer(name, version);
|
|
4659
|
-
const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
|
|
4660
|
-
async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
|
|
4661
|
-
options?.signal?.throwIfAborted();
|
|
4662
|
-
const currentUrl = new URL(url).href;
|
|
4663
|
-
if (!skipPreloadedContexts && currentUrl in preloadedContexts) {
|
|
4664
|
-
logger.debug("Using preloaded context: {url}.", { url: currentUrl });
|
|
4665
|
-
return {
|
|
4666
|
-
contextUrl: null,
|
|
4667
|
-
document: preloadedContexts[currentUrl],
|
|
4668
|
-
documentUrl: currentUrl
|
|
4669
|
-
};
|
|
4670
|
-
}
|
|
4671
|
-
if (!allowPrivateAddress) try {
|
|
4672
|
-
await validatePublicUrl(currentUrl);
|
|
4673
|
-
} catch (error) {
|
|
4674
|
-
if (error instanceof UrlError) logger.error("Disallowed private URL: {url}", {
|
|
4675
|
-
url: currentUrl,
|
|
4676
|
-
error
|
|
4677
|
-
});
|
|
4678
|
-
throw error;
|
|
4679
|
-
}
|
|
4680
|
-
visited.add(currentUrl);
|
|
4681
|
-
return await tracer.startActiveSpan("activitypub.fetch_document", {
|
|
4682
|
-
kind: SpanKind.CLIENT,
|
|
4683
|
-
attributes: { "url.full": currentUrl }
|
|
4684
|
-
}, async (span) => {
|
|
4685
|
-
try {
|
|
4686
|
-
const request = createActivityPubRequest(currentUrl, { userAgent });
|
|
4687
|
-
logRequest(logger, request);
|
|
4688
|
-
const response = await fetch(request, {
|
|
4689
|
-
redirect: "manual",
|
|
4690
|
-
signal: options?.signal
|
|
4691
|
-
});
|
|
4692
|
-
span.setAttribute("http.response.status_code", response.status);
|
|
4693
|
-
if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
|
|
4694
|
-
if (redirected >= maximumRedirection) {
|
|
4695
|
-
logger.error("Too many redirections ({redirections}) while fetching document.", {
|
|
4696
|
-
redirections: redirected + 1,
|
|
4697
|
-
url: currentUrl
|
|
4698
|
-
});
|
|
4699
|
-
throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
|
|
4700
|
-
}
|
|
4701
|
-
const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
|
|
4702
|
-
span.setAttribute("http.redirect.url", redirectUrl);
|
|
4703
|
-
if (visited.has(redirectUrl)) {
|
|
4704
|
-
logger.error("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
|
|
4705
|
-
url: currentUrl,
|
|
4706
|
-
redirectUrl
|
|
4707
|
-
});
|
|
4708
|
-
throw new FetchError(currentUrl, `Redirect loop detected: ${redirectUrl}`);
|
|
4709
|
-
}
|
|
4710
|
-
return await load(redirectUrl, options, redirected + 1, visited);
|
|
4711
|
-
}
|
|
4712
|
-
const result = await getRemoteDocument(currentUrl, response, load);
|
|
4713
|
-
span.setAttribute("docloader.document_url", result.documentUrl);
|
|
4714
|
-
if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
|
|
4715
|
-
return result;
|
|
4716
|
-
} catch (error) {
|
|
4717
|
-
span.recordException(error);
|
|
4718
|
-
span.setStatus({
|
|
4719
|
-
code: SpanStatusCode.ERROR,
|
|
4720
|
-
message: String(error)
|
|
4721
|
-
});
|
|
4722
|
-
throw error;
|
|
4723
|
-
} finally {
|
|
4724
|
-
span.end();
|
|
4725
|
-
}
|
|
4726
|
-
});
|
|
4517
|
+
});
|
|
4518
|
+
Object.defineProperty(exports, "preloadedContexts", {
|
|
4519
|
+
enumerable: true,
|
|
4520
|
+
get: function() {
|
|
4521
|
+
return preloadedContexts;
|
|
4727
4522
|
}
|
|
4728
|
-
|
|
4729
|
-
}
|
|
4730
|
-
//#endregion
|
|
4731
|
-
export { cid_v1_default as i, getRemoteDocument as n, preloadedContexts as r, getDocumentLoader as t };
|
|
4523
|
+
});
|