@fedify/vocab-runtime 2.3.15-dev.2482 → 2.3.15

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 (114) hide show
  1. package/deno.json +5 -2
  2. package/dist/internal.cjs +6 -0
  3. package/dist/internal.d.cts +3 -0
  4. package/dist/internal.d.ts +3 -0
  5. package/dist/internal.js +3 -0
  6. package/dist/mod.cjs +105 -253
  7. package/dist/mod.d.cts +5 -28
  8. package/dist/mod.d.ts +6 -28
  9. package/dist/mod.js +98 -245
  10. package/dist/public-fetch-BC-nWJNM.d.ts +143 -0
  11. package/dist/public-fetch-Bk32IaYB.cjs +821 -0
  12. package/dist/public-fetch-DBpt3xxf.d.cts +143 -0
  13. package/dist/public-fetch-Dfmc_Kfy.js +772 -0
  14. package/dist/tests/{body-BmS3KJE1.cjs → body-BtW9TZGZ.cjs} +1 -1
  15. package/dist/tests/{body-xrlsEUPK.mjs → body-CmdrGt1w.mjs} +1 -1
  16. package/dist/tests/body.test.cjs +1 -1
  17. package/dist/tests/body.test.mjs +1 -1
  18. package/dist/tests/decimal.test.cjs +6 -6
  19. package/dist/tests/decimal.test.mjs +6 -6
  20. package/dist/tests/{docloader-CETMi5su.cjs → docloader-D34pSxKd.cjs} +106 -35
  21. package/dist/tests/{docloader-BJXlwNXs.mjs → docloader-zoX3z1bj.mjs} +102 -37
  22. package/dist/tests/docloader.test.cjs +523 -1273
  23. package/dist/tests/docloader.test.mjs +469 -1238
  24. package/dist/tests/esm-C_ftyt-G.cjs +1208 -0
  25. package/dist/tests/esm-LaZ1p80h.mjs +1222 -0
  26. package/dist/tests/internal/multicodec.test.cjs +1 -1
  27. package/dist/tests/internal/multicodec.test.mjs +1 -1
  28. package/dist/tests/{key-Qj7u1qmH.mjs → key-2lZR0Cht.mjs} +2 -2
  29. package/dist/tests/{key-CWDxavIR.cjs → key-CnsHrQSU.cjs} +2 -2
  30. package/dist/tests/key.test.cjs +2 -2
  31. package/dist/tests/key.test.mjs +2 -2
  32. package/dist/tests/{langstr-BsqFJupv.cjs → langstr-CZpWmpo5.cjs} +1 -1
  33. package/dist/tests/{langstr-ABdAOKlF.mjs → langstr-DTB4sl0a.mjs} +1 -1
  34. package/dist/tests/langstr.test.cjs +1 -1
  35. package/dist/tests/langstr.test.mjs +1 -1
  36. package/dist/tests/langtag.test.cjs +1 -1
  37. package/dist/tests/langtag.test.mjs +1 -1
  38. package/dist/tests/link.test.mjs +1 -1
  39. package/dist/tests/multibase/multibase.test.cjs +1 -1
  40. package/dist/tests/multibase/multibase.test.mjs +1 -1
  41. package/dist/tests/node-C0iftNaR.cjs +90 -0
  42. package/dist/tests/node-C3bjFjL1.mjs +84 -0
  43. package/dist/tests/pin-grant-CU-o367k.cjs +70 -0
  44. package/dist/tests/pin-grant-HW3B6SKv.mjs +53 -0
  45. package/dist/tests/pin-grant.test.cjs +88 -0
  46. package/dist/tests/pin-grant.test.d.cts +1 -0
  47. package/dist/tests/pin-grant.test.d.mts +1 -0
  48. package/dist/tests/pin-grant.test.mjs +88 -0
  49. package/dist/tests/pinners/bun.test.cjs +167 -0
  50. package/dist/tests/pinners/bun.test.d.cts +1 -0
  51. package/dist/tests/pinners/bun.test.d.mts +1 -0
  52. package/dist/tests/pinners/bun.test.mjs +167 -0
  53. package/dist/tests/pinners/node.test.cjs +269 -0
  54. package/dist/tests/pinners/node.test.d.cts +1 -0
  55. package/dist/tests/pinners/node.test.d.mts +1 -0
  56. package/dist/tests/pinners/node.test.mjs +268 -0
  57. package/dist/tests/pinners/pinners.test.cjs +197 -0
  58. package/dist/tests/pinners/pinners.test.d.cts +1 -0
  59. package/dist/tests/pinners/pinners.test.d.mts +1 -0
  60. package/dist/tests/pinners/pinners.test.mjs +197 -0
  61. package/dist/tests/pinners/testdata/node-child.cjs +135 -0
  62. package/dist/tests/pinners/testdata/node-child.d.cts +1 -0
  63. package/dist/tests/pinners/testdata/node-child.d.mts +1 -0
  64. package/dist/tests/pinners/testdata/node-child.mjs +134 -0
  65. package/dist/tests/pinners-D4UQ-HXm.cjs +221 -0
  66. package/dist/tests/pinners-ZgZDx9y6.mjs +203 -0
  67. package/dist/tests/public-fetch-BtiBjQPL.cjs +187 -0
  68. package/dist/tests/public-fetch-Yi7Vks9s.mjs +170 -0
  69. package/dist/tests/public-fetch.test.cjs +1074 -0
  70. package/dist/tests/public-fetch.test.d.cts +1 -0
  71. package/dist/tests/public-fetch.test.d.mts +1 -0
  72. package/dist/tests/public-fetch.test.mjs +1074 -0
  73. package/dist/tests/{request-rtQTKXfB.mjs → request-BKFirRrO.mjs} +1 -1
  74. package/dist/tests/{request-pbEw07NE.cjs → request-CqKju-Ll.cjs} +1 -1
  75. package/dist/tests/request.test.cjs +1 -1
  76. package/dist/tests/request.test.mjs +1 -1
  77. package/dist/tests/support-Cj0T6TP_.cjs +363 -0
  78. package/dist/tests/support-M5ClN-no.mjs +272 -0
  79. package/dist/tests/{url-4PAkYhZC.cjs → url-BR7UVTkW.cjs} +88 -12
  80. package/dist/tests/{url-3fyJuqC0.mjs → url-DQWrMGhu.mjs} +65 -13
  81. package/dist/tests/url.test.cjs +4 -1
  82. package/dist/tests/url.test.mjs +4 -1
  83. package/package.json +14 -3
  84. package/src/docloader.test.ts +700 -32
  85. package/src/docloader.ts +174 -67
  86. package/src/internal.ts +13 -0
  87. package/src/pin-grant.test.ts +118 -0
  88. package/src/pin-grant.ts +99 -0
  89. package/src/pinners/bun.test.ts +278 -0
  90. package/src/pinners/bun.ts +223 -0
  91. package/src/pinners/mod.ts +59 -0
  92. package/src/pinners/node.test.ts +415 -0
  93. package/src/pinners/node.ts +144 -0
  94. package/src/pinners/pinners.test.ts +316 -0
  95. package/src/pinners/testdata/node-child.ts +147 -0
  96. package/src/pinners/testdata/proxy-child.ts +82 -0
  97. package/src/pinners/testdata/support.ts +343 -0
  98. package/src/pinners/testdata/tls-cert.pem +12 -0
  99. package/src/pinners/testdata/tls-child.ts +88 -0
  100. package/src/pinners/testdata/tls-key.pem +5 -0
  101. package/src/pinners/types.ts +48 -0
  102. package/src/public-fetch.test.ts +1286 -0
  103. package/src/public-fetch.ts +338 -0
  104. package/src/testdata/public-fetch-deno-worker.ts +74 -0
  105. package/src/url.test.ts +11 -0
  106. package/src/url.ts +113 -22
  107. package/tsdown.config.ts +12 -3
  108. /package/dist/tests/{langtag-LDBqlxHv.mjs → langtag-D8FWxjv8.mjs} +0 -0
  109. /package/dist/tests/{langtag-DgySESmk.cjs → langtag-a2-s9uyi.cjs} +0 -0
  110. /package/dist/tests/{link-CYszIegt.mjs → link-CHMtH2-k.mjs} +0 -0
  111. /package/dist/tests/{multibase-BL45eko3.cjs → multibase-CPwWBzL3.cjs} +0 -0
  112. /package/dist/tests/{multibase-Da9VJCw8.mjs → multibase-D85wf3ZR.mjs} +0 -0
  113. /package/dist/tests/{multicodec-ByS9XLsw.cjs → multicodec-B-_CSt1M.cjs} +0 -0
  114. /package/dist/tests/{multicodec-BBPZjRfg.mjs → multicodec-caWdG98t.mjs} +0 -0
@@ -1,5 +1,5 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- const require_request = require("./request-pbEw07NE.cjs");
2
+ const require_request = require("./request-CqKju-Ll.cjs");
3
3
  let _logtape_logtape = require("@logtape/logtape");
4
4
  //#region src/body.ts
5
5
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1,4 +1,4 @@
1
- import { t as FetchError } from "./request-rtQTKXfB.mjs";
1
+ import { t as FetchError } from "./request-BKFirRrO.mjs";
2
2
  import { getLogger } from "@logtape/logtape";
3
3
  //#region src/body.ts
4
4
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1,5 +1,5 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- const require_body = require("./body-BmS3KJE1.cjs");
2
+ const require_body = require("./body-BtW9TZGZ.cjs");
3
3
  let node_assert_strict = require("node:assert/strict");
4
4
  let node_test = require("node:test");
5
5
  let node_zlib = require("node:zlib");
@@ -1,4 +1,4 @@
1
- import { r as readBoundedText, t as BodyTooLargeError } from "./body-xrlsEUPK.mjs";
1
+ import { r as readBoundedText, t as BodyTooLargeError } from "./body-CmdrGt1w.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,10 +1,10 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- require("./docloader-CETMi5su.cjs");
3
- require("./url-4PAkYhZC.cjs");
4
- require("./key-CWDxavIR.cjs");
5
- require("./multibase-BL45eko3.cjs");
6
- require("./langtag-DgySESmk.cjs");
7
- require("./langstr-BsqFJupv.cjs");
2
+ require("./docloader-D34pSxKd.cjs");
3
+ require("./url-BR7UVTkW.cjs");
4
+ require("./key-CnsHrQSU.cjs");
5
+ require("./multibase-CPwWBzL3.cjs");
6
+ require("./langtag-a2-s9uyi.cjs");
7
+ require("./langstr-CZpWmpo5.cjs");
8
8
  let node_test = require("node:test");
9
9
  let node_assert = require("node:assert");
10
10
  //#region src/decimal.ts
@@ -1,9 +1,9 @@
1
- import "./docloader-BJXlwNXs.mjs";
2
- import "./url-3fyJuqC0.mjs";
3
- import "./key-Qj7u1qmH.mjs";
4
- import "./multibase-Da9VJCw8.mjs";
5
- import "./langtag-LDBqlxHv.mjs";
6
- import "./langstr-ABdAOKlF.mjs";
1
+ import "./docloader-zoX3z1bj.mjs";
2
+ import "./url-DQWrMGhu.mjs";
3
+ import "./key-2lZR0Cht.mjs";
4
+ import "./multibase-D85wf3ZR.mjs";
5
+ import "./langtag-D8FWxjv8.mjs";
6
+ import "./langstr-DTB4sl0a.mjs";
7
7
  import { test } from "node:test";
8
8
  import { deepStrictEqual, throws } from "node:assert";
9
9
  //#region src/decimal.ts
@@ -1,8 +1,9 @@
1
1
  require("./chunk-C2EiDwsr.cjs");
2
- const require_request = require("./request-pbEw07NE.cjs");
3
- const require_body = require("./body-BmS3KJE1.cjs");
2
+ const require_request = require("./request-CqKju-Ll.cjs");
3
+ const require_body = require("./body-BtW9TZGZ.cjs");
4
4
  const require_link = require("./link-qDTmjPw_.cjs");
5
- const require_url = require("./url-4PAkYhZC.cjs");
5
+ const require_url = require("./url-BR7UVTkW.cjs");
6
+ const require_public_fetch = require("./public-fetch-BtiBjQPL.cjs");
6
7
  let _logtape_logtape = require("@logtape/logtape");
7
8
  let _opentelemetry_api = require("@opentelemetry/api");
8
9
  //#region src/contexts/activitystreams.json
@@ -4541,13 +4542,47 @@ const DEFAULT_MAX_REDIRECTION = 20;
4541
4542
  * Gets a {@link RemoteDocument} from the given response.
4542
4543
  * @param url The URL of the document to load.
4543
4544
  * @param response The response to get the document from.
4544
- * @param fetch The function to fetch the document.
4545
+ * @param fetch The function to fetch the document. It is called with the URL
4546
+ * of an alternate document, if any, after the response body has
4547
+ * been either read or its cancellation has been started.
4545
4548
  * @returns The loaded remote document.
4546
4549
  * @throws {FetchError} If the response is not OK.
4547
4550
  * @internal
4548
4551
  */
4549
4552
  async function getRemoteDocument(url, response, fetch) {
4550
- const documentUrl = response.url === "" ? url : response.url;
4553
+ const result = await parseRemoteDocument(url, response);
4554
+ if ("alternate" in result) return await fetch(result.alternate);
4555
+ return result.document;
4556
+ }
4557
+ /**
4558
+ * Starts cancelling the body of a response without waiting for it to settle.
4559
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
4560
+ * settles only after the other branch is cancelled too, per the Streams
4561
+ * standard, so awaiting it could block forever. Failures are only logged.
4562
+ * @param response The response whose body is cancelled.
4563
+ * @param url The URL of the response, for logging.
4564
+ */
4565
+ function cancelResponseBody(response, url) {
4566
+ response.body?.cancel().catch((error) => {
4567
+ logger.debug("Failed to cancel the response body from {url}: {error}", {
4568
+ url,
4569
+ error
4570
+ });
4571
+ });
4572
+ }
4573
+ /**
4574
+ * Gets a {@link RemoteDocument} from the given response, or the URL of an
4575
+ * alternate document to load instead. When an alternate document is found,
4576
+ * the response body has been either read or its cancellation has been started
4577
+ * (without waiting for it), so that the alternate document can be loaded after
4578
+ * the response's session is closed.
4579
+ * @param url The URL of the document to load.
4580
+ * @param response The response to get the document from.
4581
+ * @returns The loaded remote document, or the alternate document URL.
4582
+ * @throws {FetchError} If the response is not OK.
4583
+ */
4584
+ async function parseRemoteDocument(url, response) {
4585
+ const documentUrl = url;
4551
4586
  const docUrl = new URL(documentUrl);
4552
4587
  if (!response.ok) {
4553
4588
  logger.error("Failed to fetch document: {status} {url} {headers}", {
@@ -4584,7 +4619,8 @@ async function getRemoteDocument(url, response, fetch) {
4584
4619
  alternateUrl: altUri.href,
4585
4620
  url: documentUrl
4586
4621
  });
4587
- return await fetch(altUri.href);
4622
+ cancelResponseBody(response, documentUrl);
4623
+ return { alternate: altUri.href };
4588
4624
  }
4589
4625
  }
4590
4626
  }
@@ -4609,7 +4645,7 @@ async function getRemoteDocument(url, response, fetch) {
4609
4645
  alternateUrl: attribs.href,
4610
4646
  url: documentUrl
4611
4647
  });
4612
- return await fetch(new URL(attribs.href, docUrl).href);
4648
+ return { alternate: new URL(attribs.href, docUrl).href };
4613
4649
  }
4614
4650
  }
4615
4651
  document = JSON.parse(html);
@@ -4619,11 +4655,11 @@ async function getRemoteDocument(url, response, fetch) {
4619
4655
  url: documentUrl,
4620
4656
  headers: Object.fromEntries(response.headers.entries())
4621
4657
  });
4622
- return {
4658
+ return { document: {
4623
4659
  contextUrl,
4624
4660
  document,
4625
4661
  documentUrl
4626
- };
4662
+ } };
4627
4663
  }
4628
4664
  /**
4629
4665
  * Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
@@ -4649,9 +4685,54 @@ async function getRemoteDocument(url, response, fetch) {
4649
4685
  * @returns The document loader.
4650
4686
  * @since 1.3.0
4651
4687
  */
4652
- function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}) {
4688
+ function getDocumentLoader(options = {}) {
4689
+ return createDocumentLoader(options);
4690
+ }
4691
+ /**
4692
+ * The implementation of {@link getDocumentLoader}, with injectable
4693
+ * dependencies. This is not exported from any entry point.
4694
+ * @param options Options for the document loader.
4695
+ * @param deps The dependencies.
4696
+ * @returns The document loader.
4697
+ */
4698
+ function createDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}, deps = {}) {
4653
4699
  const tracer = _opentelemetry_api.trace.getTracerProvider().getTracer(require_request.name, require_request.version);
4654
4700
  const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
4701
+ async function openHop(url, signal) {
4702
+ try {
4703
+ if (deps.openHop != null) return await deps.openHop(url);
4704
+ return allowPrivateAddress ? await require_public_fetch.openDirectFetch(url) : await require_public_fetch.openPublicFetch(url, { signal });
4705
+ } catch (error) {
4706
+ if (error instanceof require_url.UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
4707
+ url,
4708
+ error
4709
+ });
4710
+ else logger.error("Disallowed private URL: {url}", {
4711
+ url,
4712
+ error
4713
+ });
4714
+ throw error;
4715
+ }
4716
+ }
4717
+ async function fetchHop(session, url, span, options) {
4718
+ try {
4719
+ const request = require_request.createActivityPubRequest(url, { userAgent });
4720
+ require_request.logRequest(logger, request);
4721
+ const response = await session.fetch(request, {
4722
+ redirect: "manual",
4723
+ signal: options?.signal
4724
+ });
4725
+ span.setAttribute("http.response.status_code", response.status);
4726
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
4727
+ const location = response.headers.get("Location");
4728
+ cancelResponseBody(response, url);
4729
+ return { location };
4730
+ }
4731
+ return await parseRemoteDocument(url, response);
4732
+ } finally {
4733
+ await session.close();
4734
+ }
4735
+ }
4655
4736
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
4656
4737
  options?.signal?.throwIfAborted();
4657
4738
  const parsedUrl = new URL(url);
@@ -4666,33 +4747,15 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4666
4747
  documentUrl: currentUrl
4667
4748
  };
4668
4749
  }
4669
- if (!allowPrivateAddress) try {
4670
- await require_url.validatePublicUrl(currentUrl);
4671
- } catch (error) {
4672
- if (error instanceof require_url.UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
4673
- url: currentUrl,
4674
- error
4675
- });
4676
- else logger.error("Disallowed private URL: {url}", {
4677
- url: currentUrl,
4678
- error
4679
- });
4680
- throw error;
4681
- }
4750
+ const session = await openHop(currentUrl, options?.signal);
4682
4751
  visited.add(currentUrl);
4683
4752
  return await tracer.startActiveSpan("activitypub.fetch_document", {
4684
4753
  kind: _opentelemetry_api.SpanKind.CLIENT,
4685
4754
  attributes: { "url.full": currentUrl }
4686
4755
  }, async (span) => {
4687
4756
  try {
4688
- const request = require_request.createActivityPubRequest(currentUrl, { userAgent });
4689
- require_request.logRequest(logger, request);
4690
- const response = await fetch(request, {
4691
- redirect: "manual",
4692
- signal: options?.signal
4693
- });
4694
- span.setAttribute("http.response.status_code", response.status);
4695
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
4757
+ const outcome = await fetchHop(session, currentUrl, span, options);
4758
+ if ("location" in outcome) {
4696
4759
  if (redirected >= maximumRedirection) {
4697
4760
  logger.error("Too many redirections ({redirections}) while fetching document.", {
4698
4761
  redirections: redirected + 1,
@@ -4700,7 +4763,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4700
4763
  });
4701
4764
  throw new require_request.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
4702
4765
  }
4703
- const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
4766
+ const redirectUrl = new URL(outcome.location, currentUrl).href;
4704
4767
  span.setAttribute("http.redirect.url", redirectUrl);
4705
4768
  if (visited.has(redirectUrl)) {
4706
4769
  logger.error("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
@@ -4711,12 +4774,14 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4711
4774
  }
4712
4775
  return await load(redirectUrl, options, redirected + 1, visited);
4713
4776
  }
4714
- const result = await getRemoteDocument(currentUrl, response, async (alternateUrl) => {
4777
+ let result;
4778
+ if ("alternate" in outcome) {
4779
+ const alternateUrl = outcome.alternate;
4715
4780
  options?.signal?.throwIfAborted();
4716
4781
  if (redirected >= DEFAULT_MAX_REDIRECTION) throw new require_request.FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
4717
4782
  if (visited.has(alternateUrl)) throw new require_request.FetchError(currentUrl, `Redirect loop detected: ${alternateUrl}`);
4718
- return await load(alternateUrl, options, redirected + 1, visited);
4719
- });
4783
+ result = await load(alternateUrl, options, redirected + 1, visited);
4784
+ } else result = outcome.document;
4720
4785
  span.setAttribute("docloader.document_url", result.documentUrl);
4721
4786
  if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
4722
4787
  return result;
@@ -4741,6 +4806,12 @@ Object.defineProperty(exports, "cid_v1_default", {
4741
4806
  return cid_v1_default;
4742
4807
  }
4743
4808
  });
4809
+ Object.defineProperty(exports, "createDocumentLoader", {
4810
+ enumerable: true,
4811
+ get: function() {
4812
+ return createDocumentLoader;
4813
+ }
4814
+ });
4744
4815
  Object.defineProperty(exports, "getDocumentLoader", {
4745
4816
  enumerable: true,
4746
4817
  get: function() {
@@ -1,7 +1,8 @@
1
- import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-rtQTKXfB.mjs";
2
- import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-xrlsEUPK.mjs";
3
- import { t as HttpHeaderLink } from "./link-CYszIegt.mjs";
4
- import { o as validatePublicUrl, t as UrlError } from "./url-3fyJuqC0.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-BKFirRrO.mjs";
2
+ import { n as MAX_BODY_SIZE, r as readBoundedText } from "./body-CmdrGt1w.mjs";
3
+ import { t as HttpHeaderLink } from "./link-CHMtH2-k.mjs";
4
+ import { t as UrlError } from "./url-DQWrMGhu.mjs";
5
+ import { n as openDirectFetch, r as openPublicFetch } from "./public-fetch-Yi7Vks9s.mjs";
5
6
  import { getLogger } from "@logtape/logtape";
6
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
8
  //#region src/contexts/activitystreams.json
@@ -4540,13 +4541,47 @@ const DEFAULT_MAX_REDIRECTION = 20;
4540
4541
  * Gets a {@link RemoteDocument} from the given response.
4541
4542
  * @param url The URL of the document to load.
4542
4543
  * @param response The response to get the document from.
4543
- * @param fetch The function to fetch the document.
4544
+ * @param fetch The function to fetch the document. It is called with the URL
4545
+ * of an alternate document, if any, after the response body has
4546
+ * been either read or its cancellation has been started.
4544
4547
  * @returns The loaded remote document.
4545
4548
  * @throws {FetchError} If the response is not OK.
4546
4549
  * @internal
4547
4550
  */
4548
4551
  async function getRemoteDocument(url, response, fetch) {
4549
- const documentUrl = response.url === "" ? url : response.url;
4552
+ const result = await parseRemoteDocument(url, response);
4553
+ if ("alternate" in result) return await fetch(result.alternate);
4554
+ return result.document;
4555
+ }
4556
+ /**
4557
+ * Starts cancelling the body of a response without waiting for it to settle.
4558
+ * The cancellation of a branch of a tee (e.g., a body cloned by a fetch mock)
4559
+ * settles only after the other branch is cancelled too, per the Streams
4560
+ * standard, so awaiting it could block forever. Failures are only logged.
4561
+ * @param response The response whose body is cancelled.
4562
+ * @param url The URL of the response, for logging.
4563
+ */
4564
+ function cancelResponseBody(response, url) {
4565
+ response.body?.cancel().catch((error) => {
4566
+ logger.debug("Failed to cancel the response body from {url}: {error}", {
4567
+ url,
4568
+ error
4569
+ });
4570
+ });
4571
+ }
4572
+ /**
4573
+ * Gets a {@link RemoteDocument} from the given response, or the URL of an
4574
+ * alternate document to load instead. When an alternate document is found,
4575
+ * the response body has been either read or its cancellation has been started
4576
+ * (without waiting for it), so that the alternate document can be loaded after
4577
+ * the response's session is closed.
4578
+ * @param url The URL of the document to load.
4579
+ * @param response The response to get the document from.
4580
+ * @returns The loaded remote document, or the alternate document URL.
4581
+ * @throws {FetchError} If the response is not OK.
4582
+ */
4583
+ async function parseRemoteDocument(url, response) {
4584
+ const documentUrl = url;
4550
4585
  const docUrl = new URL(documentUrl);
4551
4586
  if (!response.ok) {
4552
4587
  logger.error("Failed to fetch document: {status} {url} {headers}", {
@@ -4583,7 +4618,8 @@ async function getRemoteDocument(url, response, fetch) {
4583
4618
  alternateUrl: altUri.href,
4584
4619
  url: documentUrl
4585
4620
  });
4586
- return await fetch(altUri.href);
4621
+ cancelResponseBody(response, documentUrl);
4622
+ return { alternate: altUri.href };
4587
4623
  }
4588
4624
  }
4589
4625
  }
@@ -4608,7 +4644,7 @@ async function getRemoteDocument(url, response, fetch) {
4608
4644
  alternateUrl: attribs.href,
4609
4645
  url: documentUrl
4610
4646
  });
4611
- return await fetch(new URL(attribs.href, docUrl).href);
4647
+ return { alternate: new URL(attribs.href, docUrl).href };
4612
4648
  }
4613
4649
  }
4614
4650
  document = JSON.parse(html);
@@ -4618,11 +4654,11 @@ async function getRemoteDocument(url, response, fetch) {
4618
4654
  url: documentUrl,
4619
4655
  headers: Object.fromEntries(response.headers.entries())
4620
4656
  });
4621
- return {
4657
+ return { document: {
4622
4658
  contextUrl,
4623
4659
  document,
4624
4660
  documentUrl
4625
- };
4661
+ } };
4626
4662
  }
4627
4663
  /**
4628
4664
  * Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
@@ -4648,9 +4684,54 @@ async function getRemoteDocument(url, response, fetch) {
4648
4684
  * @returns The document loader.
4649
4685
  * @since 1.3.0
4650
4686
  */
4651
- function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}) {
4687
+ function getDocumentLoader(options = {}) {
4688
+ return createDocumentLoader(options);
4689
+ }
4690
+ /**
4691
+ * The implementation of {@link getDocumentLoader}, with injectable
4692
+ * dependencies. This is not exported from any entry point.
4693
+ * @param options Options for the document loader.
4694
+ * @param deps The dependencies.
4695
+ * @returns The document loader.
4696
+ */
4697
+ function createDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedContexts, userAgent } = {}, deps = {}) {
4652
4698
  const tracer = trace.getTracerProvider().getTracer(name, version);
4653
4699
  const maximumRedirection = maxRedirection ?? DEFAULT_MAX_REDIRECTION;
4700
+ async function openHop(url, signal) {
4701
+ try {
4702
+ if (deps.openHop != null) return await deps.openHop(url);
4703
+ return allowPrivateAddress ? await openDirectFetch(url) : await openPublicFetch(url, { signal });
4704
+ } catch (error) {
4705
+ if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
4706
+ url,
4707
+ error
4708
+ });
4709
+ else logger.error("Disallowed private URL: {url}", {
4710
+ url,
4711
+ error
4712
+ });
4713
+ throw error;
4714
+ }
4715
+ }
4716
+ async function fetchHop(session, url, span, options) {
4717
+ try {
4718
+ const request = createActivityPubRequest(url, { userAgent });
4719
+ logRequest(logger, request);
4720
+ const response = await session.fetch(request, {
4721
+ redirect: "manual",
4722
+ signal: options?.signal
4723
+ });
4724
+ span.setAttribute("http.response.status_code", response.status);
4725
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
4726
+ const location = response.headers.get("Location");
4727
+ cancelResponseBody(response, url);
4728
+ return { location };
4729
+ }
4730
+ return await parseRemoteDocument(url, response);
4731
+ } finally {
4732
+ await session.close();
4733
+ }
4734
+ }
4654
4735
  async function load(url, options, redirected = 0, visited = /* @__PURE__ */ new Set()) {
4655
4736
  options?.signal?.throwIfAborted();
4656
4737
  const parsedUrl = new URL(url);
@@ -4665,33 +4746,15 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4665
4746
  documentUrl: currentUrl
4666
4747
  };
4667
4748
  }
4668
- if (!allowPrivateAddress) try {
4669
- await validatePublicUrl(currentUrl);
4670
- } catch (error) {
4671
- if (error instanceof UrlError) if (error.reason === "dns") logger.debug("DNS lookup failed for {url}", {
4672
- url: currentUrl,
4673
- error
4674
- });
4675
- else logger.error("Disallowed private URL: {url}", {
4676
- url: currentUrl,
4677
- error
4678
- });
4679
- throw error;
4680
- }
4749
+ const session = await openHop(currentUrl, options?.signal);
4681
4750
  visited.add(currentUrl);
4682
4751
  return await tracer.startActiveSpan("activitypub.fetch_document", {
4683
4752
  kind: SpanKind.CLIENT,
4684
4753
  attributes: { "url.full": currentUrl }
4685
4754
  }, async (span) => {
4686
4755
  try {
4687
- const request = createActivityPubRequest(currentUrl, { userAgent });
4688
- logRequest(logger, request);
4689
- const response = await fetch(request, {
4690
- redirect: "manual",
4691
- signal: options?.signal
4692
- });
4693
- span.setAttribute("http.response.status_code", response.status);
4694
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
4756
+ const outcome = await fetchHop(session, currentUrl, span, options);
4757
+ if ("location" in outcome) {
4695
4758
  if (redirected >= maximumRedirection) {
4696
4759
  logger.error("Too many redirections ({redirections}) while fetching document.", {
4697
4760
  redirections: redirected + 1,
@@ -4699,7 +4762,7 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4699
4762
  });
4700
4763
  throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
4701
4764
  }
4702
- const redirectUrl = new URL(response.headers.get("Location"), response.url === "" ? currentUrl : response.url).href;
4765
+ const redirectUrl = new URL(outcome.location, currentUrl).href;
4703
4766
  span.setAttribute("http.redirect.url", redirectUrl);
4704
4767
  if (visited.has(redirectUrl)) {
4705
4768
  logger.error("Detected a redirect loop while fetching document: {url} -> {redirectUrl}", {
@@ -4710,12 +4773,14 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4710
4773
  }
4711
4774
  return await load(redirectUrl, options, redirected + 1, visited);
4712
4775
  }
4713
- const result = await getRemoteDocument(currentUrl, response, async (alternateUrl) => {
4776
+ let result;
4777
+ if ("alternate" in outcome) {
4778
+ const alternateUrl = outcome.alternate;
4714
4779
  options?.signal?.throwIfAborted();
4715
4780
  if (redirected >= DEFAULT_MAX_REDIRECTION) throw new FetchError(currentUrl, `Too many redirections (${redirected + 1})`);
4716
4781
  if (visited.has(alternateUrl)) throw new FetchError(currentUrl, `Redirect loop detected: ${alternateUrl}`);
4717
- return await load(alternateUrl, options, redirected + 1, visited);
4718
- });
4782
+ result = await load(alternateUrl, options, redirected + 1, visited);
4783
+ } else result = outcome.document;
4719
4784
  span.setAttribute("docloader.document_url", result.documentUrl);
4720
4785
  if (result.contextUrl != null) span.setAttribute("docloader.context_url", result.contextUrl);
4721
4786
  return result;
@@ -4734,4 +4799,4 @@ function getDocumentLoader({ allowPrivateAddress, maxRedirection, skipPreloadedC
4734
4799
  return (url, options) => load(url, options);
4735
4800
  }
4736
4801
  //#endregion
4737
- export { cid_v1_default as i, getRemoteDocument as n, preloadedContexts as r, getDocumentLoader as t };
4802
+ export { cid_v1_default as a, preloadedContexts as i, getDocumentLoader as n, getRemoteDocument as r, createDocumentLoader as t };