@fedify/vocab-runtime 2.4.0-dev.1570 → 2.4.0-dev.1573
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/docloader-DnUMWHaJ.d.cts +202 -0
- package/dist/docloader-xRGn1azD.d.ts +202 -0
- package/dist/internal/jsonld-cache.cjs +279 -0
- package/dist/internal/jsonld-cache.d.cts +48 -0
- package/dist/internal/jsonld-cache.d.ts +48 -0
- package/dist/internal/jsonld-cache.js +275 -0
- package/dist/mod.cjs +178 -190
- package/dist/mod.d.cts +59 -190
- package/dist/mod.d.ts +59 -190
- package/dist/mod.js +165 -184
- package/dist/tests/decimal.test.cjs +3 -3
- package/dist/tests/decimal.test.mjs +3 -3
- package/dist/tests/{docloader-C76ldE5C.mjs → docloader-B5Upa3Ox.mjs} +97 -10
- package/dist/tests/{docloader-mvgIWKI7.cjs → docloader-C69O-D5T.cjs} +102 -9
- package/dist/tests/docloader.test.cjs +58 -6
- package/dist/tests/docloader.test.mjs +58 -6
- package/dist/tests/jsonld-cache.test.cjs +652 -0
- package/dist/tests/jsonld-cache.test.d.cts +1 -0
- package/dist/tests/jsonld-cache.test.d.mts +1 -0
- package/dist/tests/jsonld-cache.test.mjs +651 -0
- package/dist/tests/{key-CrrK9mYh.mjs → key-CDGDH_vC.mjs} +69 -1
- package/dist/tests/{key-pMmqUKuo.cjs → key-_wXwomh_.cjs} +86 -0
- package/dist/tests/key.test.cjs +48 -1
- package/dist/tests/key.test.mjs +48 -1
- package/dist/tests/{request-BEXkv1ul.mjs → request-BGoZTy5L.mjs} +1 -1
- package/dist/tests/{request-SworbvLc.cjs → request-DX4gki5x.cjs} +1 -1
- package/dist/tests/request.test.cjs +1 -1
- package/dist/tests/request.test.mjs +1 -1
- package/dist/tests/{url-C20FhC7p.cjs → url-2XwVbUS_.cjs} +108 -0
- package/dist/tests/{url-m9Qzxy-Y.mjs → url-YWJbnRlf.mjs} +85 -1
- package/dist/tests/url.test.cjs +104 -1
- package/dist/tests/url.test.mjs +105 -2
- package/dist/url-BAdyyqAa.cjs +315 -0
- package/dist/url-BuxPHxK2.js +261 -0
- package/package.json +11 -1
- package/src/contexts/fep-7aa9.json +24 -0
- package/src/contexts.ts +2 -0
- package/src/docloader.test.ts +102 -6
- package/src/docloader.ts +76 -8
- package/src/internal/jsonld-cache.ts +538 -0
- package/src/jsonld-cache.test.ts +554 -0
- package/src/key.test.ts +86 -0
- package/src/key.ts +125 -0
- package/src/mod.ts +8 -0
- package/src/url.test.ts +252 -1
- package/src/url.ts +141 -0
- package/tsdown.config.ts +6 -1
package/dist/mod.cjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const require_chunk = require("./chunk-M78iaK0I.cjs");
|
|
4
|
+
const require_url = require("./url-BAdyyqAa.cjs");
|
|
4
5
|
let _logtape_logtape = require("@logtape/logtape");
|
|
5
6
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
6
7
|
let node_process = require("node:process");
|
|
7
8
|
node_process = require_chunk.__toESM(node_process, 1);
|
|
8
|
-
let node_dns_promises = require("node:dns/promises");
|
|
9
|
-
let node_net = require("node:net");
|
|
10
9
|
let asn1js = require("asn1js");
|
|
11
10
|
let byte_encodings_base64 = require("byte-encodings/base64");
|
|
12
11
|
let byte_encodings_base64url = require("byte-encodings/base64url");
|
|
@@ -4288,6 +4287,28 @@ const preloadedContexts = {
|
|
|
4288
4287
|
"@type": "@id"
|
|
4289
4288
|
}
|
|
4290
4289
|
} },
|
|
4290
|
+
"https://w3id.org/fep/7aa9": { "@context": {
|
|
4291
|
+
"FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
|
|
4292
|
+
"FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
|
|
4293
|
+
"FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
|
|
4294
|
+
"FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
|
|
4295
|
+
"topic": {
|
|
4296
|
+
"@id": "https://w3id.org/fep/7aa9#topic",
|
|
4297
|
+
"@type": "@id"
|
|
4298
|
+
},
|
|
4299
|
+
"featuredObject": {
|
|
4300
|
+
"@id": "https://w3id.org/fep/7aa9#featuredObject",
|
|
4301
|
+
"@type": "@id"
|
|
4302
|
+
},
|
|
4303
|
+
"canFeature": {
|
|
4304
|
+
"@id": "https://w3id.org/fep/7aa9#canFeature",
|
|
4305
|
+
"@type": "@id"
|
|
4306
|
+
},
|
|
4307
|
+
"featureAuthorization": {
|
|
4308
|
+
"@id": "https://w3id.org/fep/7aa9#featureAuthorization",
|
|
4309
|
+
"@type": "@id"
|
|
4310
|
+
}
|
|
4311
|
+
} },
|
|
4291
4312
|
"https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
|
|
4292
4313
|
"as": "https://www.w3.org/ns/activitystreams#",
|
|
4293
4314
|
"lemmy": "https://join-lemmy.org/ns#",
|
|
@@ -4346,7 +4367,7 @@ const preloadedContexts = {
|
|
|
4346
4367
|
//#endregion
|
|
4347
4368
|
//#region deno.json
|
|
4348
4369
|
var name = "@fedify/vocab-runtime";
|
|
4349
|
-
var version = "2.4.0-dev.
|
|
4370
|
+
var version = "2.4.0-dev.1573+246bd0b2";
|
|
4350
4371
|
//#endregion
|
|
4351
4372
|
//#region src/link.ts
|
|
4352
4373
|
const parametersNeedLowerCase = ["rel", "type"];
|
|
@@ -4599,179 +4620,6 @@ function logRequest(logger, request) {
|
|
|
4599
4620
|
});
|
|
4600
4621
|
}
|
|
4601
4622
|
//#endregion
|
|
4602
|
-
//#region src/url.ts
|
|
4603
|
-
var UrlError = class extends Error {
|
|
4604
|
-
constructor(message) {
|
|
4605
|
-
super(message);
|
|
4606
|
-
this.name = "UrlError";
|
|
4607
|
-
}
|
|
4608
|
-
};
|
|
4609
|
-
/**
|
|
4610
|
-
* Validates a URL to prevent SSRF attacks.
|
|
4611
|
-
*/
|
|
4612
|
-
async function validatePublicUrl(url) {
|
|
4613
|
-
const parsed = new URL(url);
|
|
4614
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") throw new UrlError(`Unsupported protocol: ${parsed.protocol}`);
|
|
4615
|
-
let hostname = parsed.hostname;
|
|
4616
|
-
if (hostname.startsWith("[") && hostname.endsWith("]")) hostname = hostname.slice(1, -1);
|
|
4617
|
-
if (hostname === "localhost") throw new UrlError("Localhost is not allowed");
|
|
4618
|
-
const hostnameFamily = (0, node_net.isIP)(hostname);
|
|
4619
|
-
if (hostnameFamily !== 0) {
|
|
4620
|
-
validatePublicIpAddress(hostname, hostnameFamily);
|
|
4621
|
-
return;
|
|
4622
|
-
}
|
|
4623
|
-
if ("Deno" in globalThis && !(0, node_net.isIP)(hostname)) {
|
|
4624
|
-
if ((await Deno.permissions.query({ name: "net" })).state !== "granted") return;
|
|
4625
|
-
}
|
|
4626
|
-
if ("Bun" in globalThis) {
|
|
4627
|
-
if (hostname === "example.com" || hostname.endsWith(".example.com")) return;
|
|
4628
|
-
else if (hostname === "fedify-test.internal") throw new UrlError("Invalid or private address: fedify-test.internal");
|
|
4629
|
-
}
|
|
4630
|
-
let addresses;
|
|
4631
|
-
try {
|
|
4632
|
-
addresses = await (0, node_dns_promises.lookup)(hostname, { all: true });
|
|
4633
|
-
} catch {
|
|
4634
|
-
addresses = [];
|
|
4635
|
-
}
|
|
4636
|
-
for (const { address, family } of addresses) validatePublicIpAddress(address, family);
|
|
4637
|
-
}
|
|
4638
|
-
function validatePublicIpAddress(address, family) {
|
|
4639
|
-
if (family === 4 && isValidPublicIPv4Address(address) || family === 6 && isValidPublicIPv6Address(address)) return;
|
|
4640
|
-
throw new UrlError(`Invalid or private address: ${address}`);
|
|
4641
|
-
}
|
|
4642
|
-
function isValidPublicIPv4Address(address) {
|
|
4643
|
-
const parts = parseIPv4Address(address);
|
|
4644
|
-
if (parts == null) return false;
|
|
4645
|
-
const value = ipv4PartsToNumber(parts);
|
|
4646
|
-
return !nonPublicIPv4Prefixes.some(({ base, prefix }) => matchesIPv4Prefix(value, base, prefix));
|
|
4647
|
-
}
|
|
4648
|
-
function isValidPublicIPv6Address(address) {
|
|
4649
|
-
const words = parseIPv6Address(address);
|
|
4650
|
-
if (words == null) return false;
|
|
4651
|
-
if (nonPublicIPv6Prefixes.some(({ words: prefixWords, prefix }) => matchesIPv6Prefix(words, prefixWords, prefix))) return false;
|
|
4652
|
-
for (const { extractIPv4, prefix, words: prefixWords } of ipv6WithIPv4Prefixes) {
|
|
4653
|
-
if (!matchesIPv6Prefix(words, prefixWords, prefix)) continue;
|
|
4654
|
-
const ipv4Address = extractIPv4(words);
|
|
4655
|
-
if (ipv4Address != null && !isValidPublicIPv4Address(ipv4Address)) return false;
|
|
4656
|
-
}
|
|
4657
|
-
return true;
|
|
4658
|
-
}
|
|
4659
|
-
function expandIPv6Address(address) {
|
|
4660
|
-
address = address.toLowerCase();
|
|
4661
|
-
const ipv4Delimiter = address.lastIndexOf(":");
|
|
4662
|
-
if (address.includes(".") && ipv4Delimiter >= 0) {
|
|
4663
|
-
const ipv4Parts = parseIPv4Address(address.substring(ipv4Delimiter + 1));
|
|
4664
|
-
if (ipv4Parts == null) return address;
|
|
4665
|
-
const high = (ipv4Parts[0] << 8) + ipv4Parts[1];
|
|
4666
|
-
const low = (ipv4Parts[2] << 8) + ipv4Parts[3];
|
|
4667
|
-
address = address.substring(0, ipv4Delimiter + 1) + high.toString(16) + ":" + low.toString(16);
|
|
4668
|
-
}
|
|
4669
|
-
if (address === "::") return "0000:0000:0000:0000:0000:0000:0000:0000";
|
|
4670
|
-
if (address.startsWith("::")) address = "0000" + address;
|
|
4671
|
-
if (address.endsWith("::")) address = address + "0000";
|
|
4672
|
-
address = address.replace("::", ":0000".repeat(8 - (address.match(/:/g) || []).length) + ":");
|
|
4673
|
-
return address.split(":").map((part) => part.padStart(4, "0")).join(":");
|
|
4674
|
-
}
|
|
4675
|
-
const nonPublicIPv4Prefixes = [
|
|
4676
|
-
ipv4Prefix("0.0.0.0/8", "RFC 6890"),
|
|
4677
|
-
ipv4Prefix("10.0.0.0/8", "RFC 1918"),
|
|
4678
|
-
ipv4Prefix("100.64.0.0/10", "RFC 6598"),
|
|
4679
|
-
ipv4Prefix("127.0.0.0/8", "RFC 1122"),
|
|
4680
|
-
ipv4Prefix("169.254.0.0/16", "RFC 3927"),
|
|
4681
|
-
ipv4Prefix("172.16.0.0/12", "RFC 1918"),
|
|
4682
|
-
ipv4Prefix("192.0.0.0/24", "RFC 6890"),
|
|
4683
|
-
ipv4Prefix("192.0.2.0/24", "RFC 5737"),
|
|
4684
|
-
ipv4Prefix("192.88.99.0/24", "RFC 7526"),
|
|
4685
|
-
ipv4Prefix("192.168.0.0/16", "RFC 1918"),
|
|
4686
|
-
ipv4Prefix("198.18.0.0/15", "RFC 2544"),
|
|
4687
|
-
ipv4Prefix("198.51.100.0/24", "RFC 5737"),
|
|
4688
|
-
ipv4Prefix("203.0.113.0/24", "RFC 5737"),
|
|
4689
|
-
ipv4Prefix("224.0.0.0/4", "RFC 5771"),
|
|
4690
|
-
ipv4Prefix("240.0.0.0/4", "RFC 1112")
|
|
4691
|
-
];
|
|
4692
|
-
const nonPublicIPv6Prefixes = [
|
|
4693
|
-
ipv6Prefix("::/16", "RFC 4291"),
|
|
4694
|
-
ipv6Prefix("2001::/32", "RFC 4380"),
|
|
4695
|
-
ipv6Prefix("2002::/16", "RFC 3056"),
|
|
4696
|
-
ipv6Prefix("64:ff9b:1::/48", "RFC 8215"),
|
|
4697
|
-
ipv6Prefix("fc00::/7", "RFC 4193"),
|
|
4698
|
-
ipv6Prefix("fe80::/10", "RFC 4291"),
|
|
4699
|
-
ipv6Prefix("ff00::/8", "RFC 4291")
|
|
4700
|
-
];
|
|
4701
|
-
const ipv6WithIPv4Prefixes = [{
|
|
4702
|
-
...ipv6Prefix("64:ff9b::/96", "RFC 6052"),
|
|
4703
|
-
extractIPv4: (words) => ipv4FromWords(words[6], words[7])
|
|
4704
|
-
}];
|
|
4705
|
-
function ipv4Prefix(cidr, rfc) {
|
|
4706
|
-
const [address, prefixText] = cidr.split("/");
|
|
4707
|
-
const prefix = parseInt(prefixText, 10);
|
|
4708
|
-
const parts = parseIPv4Address(address);
|
|
4709
|
-
if (parts == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 32) throw new Error(`Invalid IPv4 prefix: ${cidr}`);
|
|
4710
|
-
return {
|
|
4711
|
-
cidr,
|
|
4712
|
-
base: ipv4PartsToNumber(parts),
|
|
4713
|
-
prefix,
|
|
4714
|
-
rfc
|
|
4715
|
-
};
|
|
4716
|
-
}
|
|
4717
|
-
function ipv6Prefix(cidr, rfc) {
|
|
4718
|
-
const [address, prefixText] = cidr.split("/");
|
|
4719
|
-
const prefix = parseInt(prefixText, 10);
|
|
4720
|
-
const words = parseIPv6Address(address);
|
|
4721
|
-
if (words == null || !Number.isInteger(prefix) || prefix < 0 || prefix > 128) throw new Error(`Invalid IPv6 prefix: ${cidr}`);
|
|
4722
|
-
return {
|
|
4723
|
-
cidr,
|
|
4724
|
-
words,
|
|
4725
|
-
prefix,
|
|
4726
|
-
rfc
|
|
4727
|
-
};
|
|
4728
|
-
}
|
|
4729
|
-
function parseIPv4Address(address) {
|
|
4730
|
-
const parts = address.split(".").map((part) => {
|
|
4731
|
-
if (!/^\d+$/.test(part)) return NaN;
|
|
4732
|
-
return parseInt(part, 10);
|
|
4733
|
-
});
|
|
4734
|
-
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part) || part < 0 || part > 255)) return null;
|
|
4735
|
-
return parts;
|
|
4736
|
-
}
|
|
4737
|
-
function parseIPv6Address(address) {
|
|
4738
|
-
const parts = expandIPv6Address(address).split(":");
|
|
4739
|
-
if (parts.length !== 8) return null;
|
|
4740
|
-
const words = parts.map((part) => {
|
|
4741
|
-
if (!/^[0-9a-f]{1,4}$/i.test(part)) return NaN;
|
|
4742
|
-
return parseInt(part, 16);
|
|
4743
|
-
});
|
|
4744
|
-
if (words.some((word) => !Number.isInteger(word) || word < 0 || word > 65535)) return null;
|
|
4745
|
-
return words;
|
|
4746
|
-
}
|
|
4747
|
-
function ipv4PartsToNumber(parts) {
|
|
4748
|
-
return parts[0] * 2 ** 24 + parts[1] * 2 ** 16 + parts[2] * 2 ** 8 + parts[3];
|
|
4749
|
-
}
|
|
4750
|
-
function ipv4FromWords(highWord, lowWord) {
|
|
4751
|
-
return [
|
|
4752
|
-
highWord >> 8,
|
|
4753
|
-
highWord & 255,
|
|
4754
|
-
lowWord >> 8,
|
|
4755
|
-
lowWord & 255
|
|
4756
|
-
].join(".");
|
|
4757
|
-
}
|
|
4758
|
-
function matchesIPv4Prefix(address, prefixBase, prefixLength) {
|
|
4759
|
-
const blockSize = 2 ** (32 - prefixLength);
|
|
4760
|
-
return Math.floor(address / blockSize) === Math.floor(prefixBase / blockSize);
|
|
4761
|
-
}
|
|
4762
|
-
function matchesIPv6Prefix(address, prefixWords, prefixLength) {
|
|
4763
|
-
let remaining = prefixLength;
|
|
4764
|
-
for (let i = 0; i < 8 && remaining > 0; i++) if (remaining >= 16) {
|
|
4765
|
-
if (address[i] !== prefixWords[i]) return false;
|
|
4766
|
-
remaining -= 16;
|
|
4767
|
-
} else {
|
|
4768
|
-
const mask = 65535 << 16 - remaining & 65535;
|
|
4769
|
-
if ((address[i] & mask) !== (prefixWords[i] & mask)) return false;
|
|
4770
|
-
remaining = 0;
|
|
4771
|
-
}
|
|
4772
|
-
return true;
|
|
4773
|
-
}
|
|
4774
|
-
//#endregion
|
|
4775
4623
|
//#region src/docloader.ts
|
|
4776
4624
|
const logger = (0, _logtape_logtape.getLogger)([
|
|
4777
4625
|
"fedify",
|
|
@@ -4779,6 +4627,66 @@ const logger = (0, _logtape_logtape.getLogger)([
|
|
|
4779
4627
|
"docloader"
|
|
4780
4628
|
]);
|
|
4781
4629
|
const DEFAULT_MAX_REDIRECTION = 20;
|
|
4630
|
+
const MAX_HTML_SIZE = 1024 * 1024;
|
|
4631
|
+
function createResponseMetadata(response) {
|
|
4632
|
+
return new Response(null, {
|
|
4633
|
+
headers: response.headers,
|
|
4634
|
+
status: response.status,
|
|
4635
|
+
statusText: response.statusText
|
|
4636
|
+
});
|
|
4637
|
+
}
|
|
4638
|
+
async function cancelResponseBody(response) {
|
|
4639
|
+
if (response.body != null) await response.body.cancel();
|
|
4640
|
+
}
|
|
4641
|
+
async function readBoundedText(response, maxBytes) {
|
|
4642
|
+
const contentLength = response.headers.get("Content-Length");
|
|
4643
|
+
if (contentLength != null) {
|
|
4644
|
+
const size = Number(contentLength);
|
|
4645
|
+
if (size > maxBytes) {
|
|
4646
|
+
await cancelResponseBody(response);
|
|
4647
|
+
return {
|
|
4648
|
+
text: "",
|
|
4649
|
+
size,
|
|
4650
|
+
tooLarge: true
|
|
4651
|
+
};
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
if (response.body == null) return {
|
|
4655
|
+
text: "",
|
|
4656
|
+
size: 0,
|
|
4657
|
+
tooLarge: false
|
|
4658
|
+
};
|
|
4659
|
+
const reader = response.body.getReader();
|
|
4660
|
+
const decoder = new TextDecoder();
|
|
4661
|
+
let text = "";
|
|
4662
|
+
let size = 0;
|
|
4663
|
+
try {
|
|
4664
|
+
while (true) {
|
|
4665
|
+
const result = await reader.read();
|
|
4666
|
+
if (result.done) break;
|
|
4667
|
+
const chunkSize = result.value.byteLength;
|
|
4668
|
+
if (size + chunkSize > maxBytes) {
|
|
4669
|
+
size += chunkSize;
|
|
4670
|
+
await reader.cancel();
|
|
4671
|
+
return {
|
|
4672
|
+
text: "",
|
|
4673
|
+
size,
|
|
4674
|
+
tooLarge: true
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
size += chunkSize;
|
|
4678
|
+
text += decoder.decode(result.value, { stream: true });
|
|
4679
|
+
}
|
|
4680
|
+
text += decoder.decode();
|
|
4681
|
+
return {
|
|
4682
|
+
text,
|
|
4683
|
+
size,
|
|
4684
|
+
tooLarge: false
|
|
4685
|
+
};
|
|
4686
|
+
} finally {
|
|
4687
|
+
reader.releaseLock();
|
|
4688
|
+
}
|
|
4689
|
+
}
|
|
4782
4690
|
/**
|
|
4783
4691
|
* Gets a {@link RemoteDocument} from the given response.
|
|
4784
4692
|
* @param url The URL of the document to load.
|
|
@@ -4833,19 +4741,19 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4833
4741
|
}
|
|
4834
4742
|
let document;
|
|
4835
4743
|
if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
|
|
4836
|
-
const
|
|
4837
|
-
const html = await response
|
|
4838
|
-
if (html.
|
|
4744
|
+
const errorResponse = createResponseMetadata(response);
|
|
4745
|
+
const html = await readBoundedText(response, MAX_HTML_SIZE);
|
|
4746
|
+
if (html.tooLarge) {
|
|
4839
4747
|
logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
|
|
4840
4748
|
url: documentUrl,
|
|
4841
|
-
size: html.
|
|
4749
|
+
size: html.size
|
|
4842
4750
|
});
|
|
4843
|
-
document
|
|
4751
|
+
throw new FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4844
4752
|
} else {
|
|
4845
4753
|
const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
|
|
4846
4754
|
const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
|
|
4847
4755
|
let tagMatch;
|
|
4848
|
-
while ((tagMatch = tagPattern.exec(html)) !== null) {
|
|
4756
|
+
while ((tagMatch = tagPattern.exec(html.text)) !== null) {
|
|
4849
4757
|
const tagContent = tagMatch[2];
|
|
4850
4758
|
let attrMatch;
|
|
4851
4759
|
const attribs = {};
|
|
@@ -4862,7 +4770,12 @@ async function getRemoteDocument(url, response, fetch) {
|
|
|
4862
4770
|
return await fetch(new URL(attribs.href, docUrl).href);
|
|
4863
4771
|
}
|
|
4864
4772
|
}
|
|
4865
|
-
|
|
4773
|
+
try {
|
|
4774
|
+
document = JSON.parse(html.text);
|
|
4775
|
+
} catch (error) {
|
|
4776
|
+
if (!(error instanceof SyntaxError)) throw error;
|
|
4777
|
+
throw new FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
|
|
4778
|
+
}
|
|
4866
4779
|
}
|
|
4867
4780
|
} else document = await response.json();
|
|
4868
4781
|
logger.debug("Fetched document: {status} {url} {headers}", {
|
|
@@ -4908,9 +4821,9 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
|
|
|
4908
4821
|
};
|
|
4909
4822
|
}
|
|
4910
4823
|
if (!allowPrivateAddress) try {
|
|
4911
|
-
await validatePublicUrl(currentUrl);
|
|
4824
|
+
await require_url.validatePublicUrl(currentUrl);
|
|
4912
4825
|
} catch (error) {
|
|
4913
|
-
if (error instanceof UrlError) logger.error("Disallowed private URL: {url}", {
|
|
4826
|
+
if (error instanceof require_url.UrlError) logger.error("Disallowed private URL: {url}", {
|
|
4914
4827
|
url: currentUrl,
|
|
4915
4828
|
error
|
|
4916
4829
|
});
|
|
@@ -5340,6 +5253,11 @@ const algorithms = {
|
|
|
5340
5253
|
},
|
|
5341
5254
|
"1.3.101.112": "Ed25519"
|
|
5342
5255
|
};
|
|
5256
|
+
const DID_KEY_PREFIX = "did:key:";
|
|
5257
|
+
const ED25519_PUBLIC_KEY_MULTICODEC = 237;
|
|
5258
|
+
const ED25519_PUBLIC_KEY_LENGTH = 32;
|
|
5259
|
+
const DID_KEY_PATTERN = /^did:key:([^/?#]+)$/;
|
|
5260
|
+
const DID_KEY_VERIFICATION_METHOD_PATTERN = /^did:key:([^/?#]+)#([^/?#]+)$/;
|
|
5343
5261
|
/**
|
|
5344
5262
|
* Imports a PEM-SPKI formatted public key.
|
|
5345
5263
|
* @param pem The PEM-SPKI formatted public key.
|
|
@@ -5401,6 +5319,69 @@ const PKCS1_HEADER = /^\s*-----BEGIN\s+RSA\s+PUBLIC\s+KEY-----\s*\n/;
|
|
|
5401
5319
|
function importPem(pem) {
|
|
5402
5320
|
return PKCS1_HEADER.test(pem) ? importPkcs1(pem) : importSpki(pem);
|
|
5403
5321
|
}
|
|
5322
|
+
function decodeEd25519DidKeyMultibase(multibaseKey) {
|
|
5323
|
+
if (!multibaseKey.startsWith("z")) throw new TypeError("did:key must use base58-btc Multibase encoding.");
|
|
5324
|
+
let decoded;
|
|
5325
|
+
try {
|
|
5326
|
+
decoded = decodeMultibase(multibaseKey);
|
|
5327
|
+
} catch (error) {
|
|
5328
|
+
throw new TypeError("Invalid did:key Multibase encoding.", { cause: error });
|
|
5329
|
+
}
|
|
5330
|
+
const { code } = getMulticodecPrefix(decoded);
|
|
5331
|
+
if (code !== ED25519_PUBLIC_KEY_MULTICODEC) throw new TypeError("Unsupported did:key type: 0x" + code.toString(16));
|
|
5332
|
+
const content = removeMulticodecPrefix(decoded);
|
|
5333
|
+
if (content.length !== ED25519_PUBLIC_KEY_LENGTH) throw new TypeError("Invalid Ed25519 did:key length.");
|
|
5334
|
+
return content;
|
|
5335
|
+
}
|
|
5336
|
+
/**
|
|
5337
|
+
* Imports an Ed25519 `did:key` DID.
|
|
5338
|
+
*
|
|
5339
|
+
* @param did The `did:key` DID.
|
|
5340
|
+
* @returns The imported Ed25519 public key.
|
|
5341
|
+
* @throws {TypeError} If the DID is malformed or uses an unsupported key type.
|
|
5342
|
+
* @since 2.4.0
|
|
5343
|
+
*/
|
|
5344
|
+
async function importDidKey(did) {
|
|
5345
|
+
const match = (did instanceof URL ? did.href : did).match(DID_KEY_PATTERN);
|
|
5346
|
+
if (match == null) throw new TypeError("Invalid did:key DID.");
|
|
5347
|
+
const content = decodeEd25519DidKeyMultibase(match[1]);
|
|
5348
|
+
return await crypto.subtle.importKey("raw", content.slice(), "Ed25519", true, ["verify"]);
|
|
5349
|
+
}
|
|
5350
|
+
/**
|
|
5351
|
+
* Exports an Ed25519 public key as a `did:key` DID.
|
|
5352
|
+
*
|
|
5353
|
+
* @param key The Ed25519 public key.
|
|
5354
|
+
* @returns The `did:key` DID.
|
|
5355
|
+
* @throws {TypeError} If the key is invalid or unsupported.
|
|
5356
|
+
* @since 2.4.0
|
|
5357
|
+
*/
|
|
5358
|
+
async function exportDidKey(key) {
|
|
5359
|
+
if (key.algorithm.name !== "Ed25519") throw new TypeError("Unsupported key type: " + JSON.stringify(key.algorithm));
|
|
5360
|
+
return DID_KEY_PREFIX + await exportMultibaseKey(key);
|
|
5361
|
+
}
|
|
5362
|
+
/**
|
|
5363
|
+
* Parses an Ed25519 `did:key` verification method DID URL.
|
|
5364
|
+
*
|
|
5365
|
+
* @param didUrl The `did:key` DID URL.
|
|
5366
|
+
* @returns The parsed verification method.
|
|
5367
|
+
* @throws {TypeError} If the DID URL is malformed, unsupported, or its
|
|
5368
|
+
* fragment does not identify the same key as the DID.
|
|
5369
|
+
* @since 2.4.0
|
|
5370
|
+
*/
|
|
5371
|
+
async function parseDidKeyVerificationMethod(didUrl) {
|
|
5372
|
+
const didUrlString = didUrl instanceof URL ? didUrl.href : didUrl;
|
|
5373
|
+
const match = didUrlString.match(DID_KEY_VERIFICATION_METHOD_PATTERN);
|
|
5374
|
+
if (match == null) throw new TypeError("Invalid did:key verification method.");
|
|
5375
|
+
const [, publicKeyMultibase, fragment] = match;
|
|
5376
|
+
if (publicKeyMultibase !== fragment) throw new TypeError("Invalid did:key verification method fragment.");
|
|
5377
|
+
const publicKey = await importDidKey(DID_KEY_PREFIX + publicKeyMultibase);
|
|
5378
|
+
return {
|
|
5379
|
+
id: new URL(didUrlString),
|
|
5380
|
+
controller: new URL(DID_KEY_PREFIX + publicKeyMultibase),
|
|
5381
|
+
publicKeyMultibase,
|
|
5382
|
+
publicKey
|
|
5383
|
+
};
|
|
5384
|
+
}
|
|
5404
5385
|
/**
|
|
5405
5386
|
* Imports a [Multibase]-encoded public key.
|
|
5406
5387
|
*
|
|
@@ -5569,26 +5550,33 @@ LanguageString.prototype[Symbol.for("nodejs.util.inspect.custom")] = function(_d
|
|
|
5569
5550
|
//#endregion
|
|
5570
5551
|
exports.FetchError = FetchError;
|
|
5571
5552
|
exports.LanguageString = LanguageString;
|
|
5572
|
-
exports.UrlError = UrlError;
|
|
5553
|
+
exports.UrlError = require_url.UrlError;
|
|
5573
5554
|
exports.canParseDecimal = canParseDecimal;
|
|
5574
5555
|
exports.createActivityPubRequest = createActivityPubRequest;
|
|
5575
5556
|
exports.decodeMultibase = decodeMultibase;
|
|
5576
5557
|
exports.encodeMultibase = encodeMultibase;
|
|
5577
5558
|
exports.encodingFromBaseData = encodingFromBaseData;
|
|
5578
|
-
exports.expandIPv6Address = expandIPv6Address;
|
|
5559
|
+
exports.expandIPv6Address = require_url.expandIPv6Address;
|
|
5560
|
+
exports.exportDidKey = exportDidKey;
|
|
5579
5561
|
exports.exportMultibaseKey = exportMultibaseKey;
|
|
5580
5562
|
exports.exportSpki = exportSpki;
|
|
5563
|
+
exports.formatIri = require_url.formatIri;
|
|
5581
5564
|
exports.getDocumentLoader = getDocumentLoader;
|
|
5582
5565
|
exports.getRemoteDocument = getRemoteDocument;
|
|
5583
5566
|
exports.getUserAgent = getUserAgent;
|
|
5567
|
+
exports.haveSameIriOrigin = require_url.haveSameIriOrigin;
|
|
5568
|
+
exports.importDidKey = importDidKey;
|
|
5584
5569
|
exports.importMultibaseKey = importMultibaseKey;
|
|
5585
5570
|
exports.importPem = importPem;
|
|
5586
5571
|
exports.importPkcs1 = importPkcs1;
|
|
5587
5572
|
exports.importSpki = importSpki;
|
|
5588
5573
|
exports.isDecimal = isDecimal;
|
|
5589
|
-
exports.isValidPublicIPv4Address = isValidPublicIPv4Address;
|
|
5590
|
-
exports.isValidPublicIPv6Address = isValidPublicIPv6Address;
|
|
5574
|
+
exports.isValidPublicIPv4Address = require_url.isValidPublicIPv4Address;
|
|
5575
|
+
exports.isValidPublicIPv6Address = require_url.isValidPublicIPv6Address;
|
|
5591
5576
|
exports.logRequest = logRequest;
|
|
5592
5577
|
exports.parseDecimal = parseDecimal;
|
|
5578
|
+
exports.parseDidKeyVerificationMethod = parseDidKeyVerificationMethod;
|
|
5579
|
+
exports.parseIri = require_url.parseIri;
|
|
5580
|
+
exports.parseJsonLdId = require_url.parseJsonLdId;
|
|
5593
5581
|
exports.preloadedContexts = preloadedContexts;
|
|
5594
|
-
exports.validatePublicUrl = validatePublicUrl;
|
|
5582
|
+
exports.validatePublicUrl = require_url.validatePublicUrl;
|