@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.
Files changed (48) hide show
  1. package/deno.json +2 -1
  2. package/dist/docloader-DnUMWHaJ.d.cts +202 -0
  3. package/dist/docloader-xRGn1azD.d.ts +202 -0
  4. package/dist/internal/jsonld-cache.cjs +279 -0
  5. package/dist/internal/jsonld-cache.d.cts +48 -0
  6. package/dist/internal/jsonld-cache.d.ts +48 -0
  7. package/dist/internal/jsonld-cache.js +275 -0
  8. package/dist/mod.cjs +178 -190
  9. package/dist/mod.d.cts +59 -190
  10. package/dist/mod.d.ts +59 -190
  11. package/dist/mod.js +165 -184
  12. package/dist/tests/decimal.test.cjs +3 -3
  13. package/dist/tests/decimal.test.mjs +3 -3
  14. package/dist/tests/{docloader-C76ldE5C.mjs → docloader-B5Upa3Ox.mjs} +97 -10
  15. package/dist/tests/{docloader-mvgIWKI7.cjs → docloader-C69O-D5T.cjs} +102 -9
  16. package/dist/tests/docloader.test.cjs +58 -6
  17. package/dist/tests/docloader.test.mjs +58 -6
  18. package/dist/tests/jsonld-cache.test.cjs +652 -0
  19. package/dist/tests/jsonld-cache.test.d.cts +1 -0
  20. package/dist/tests/jsonld-cache.test.d.mts +1 -0
  21. package/dist/tests/jsonld-cache.test.mjs +651 -0
  22. package/dist/tests/{key-CrrK9mYh.mjs → key-CDGDH_vC.mjs} +69 -1
  23. package/dist/tests/{key-pMmqUKuo.cjs → key-_wXwomh_.cjs} +86 -0
  24. package/dist/tests/key.test.cjs +48 -1
  25. package/dist/tests/key.test.mjs +48 -1
  26. package/dist/tests/{request-BEXkv1ul.mjs → request-BGoZTy5L.mjs} +1 -1
  27. package/dist/tests/{request-SworbvLc.cjs → request-DX4gki5x.cjs} +1 -1
  28. package/dist/tests/request.test.cjs +1 -1
  29. package/dist/tests/request.test.mjs +1 -1
  30. package/dist/tests/{url-C20FhC7p.cjs → url-2XwVbUS_.cjs} +108 -0
  31. package/dist/tests/{url-m9Qzxy-Y.mjs → url-YWJbnRlf.mjs} +85 -1
  32. package/dist/tests/url.test.cjs +104 -1
  33. package/dist/tests/url.test.mjs +105 -2
  34. package/dist/url-BAdyyqAa.cjs +315 -0
  35. package/dist/url-BuxPHxK2.js +261 -0
  36. package/package.json +11 -1
  37. package/src/contexts/fep-7aa9.json +24 -0
  38. package/src/contexts.ts +2 -0
  39. package/src/docloader.test.ts +102 -6
  40. package/src/docloader.ts +76 -8
  41. package/src/internal/jsonld-cache.ts +538 -0
  42. package/src/jsonld-cache.test.ts +554 -0
  43. package/src/key.test.ts +86 -0
  44. package/src/key.ts +125 -0
  45. package/src/mod.ts +8 -0
  46. package/src/url.test.ts +252 -1
  47. package/src/url.ts +141 -0
  48. 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.1570+f1b6aaa3";
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 MAX_HTML_SIZE = 1024 * 1024;
4837
- const html = await response.text();
4838
- if (html.length > MAX_HTML_SIZE) {
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.length
4749
+ size: html.size
4842
4750
  });
4843
- document = JSON.parse(html);
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
- document = JSON.parse(html);
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;