@fedify/vocab-runtime 2.4.0-dev.1964 → 2.4.0-dev.1988

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 (42) hide show
  1. package/deno.json +1 -1
  2. package/dist/mod.cjs +176 -152
  3. package/dist/mod.js +176 -152
  4. package/dist/tests/body-Cg5W_IdS.cjs +94 -0
  5. package/dist/tests/body-DRVriaJO.mjs +77 -0
  6. package/dist/tests/body.test.cjs +89 -0
  7. package/dist/tests/body.test.d.cts +1 -0
  8. package/dist/tests/body.test.d.mts +1 -0
  9. package/dist/tests/body.test.mjs +90 -0
  10. package/dist/tests/decimal.test.cjs +2 -2
  11. package/dist/tests/decimal.test.mjs +2 -2
  12. package/dist/tests/digest.test.cjs +1 -1
  13. package/dist/tests/digest.test.mjs +1 -1
  14. package/dist/tests/{docloader-BexVAGvr.mjs → docloader-ZSduXDDg.mjs} +33 -82
  15. package/dist/tests/{docloader-B3auVdRI.cjs → docloader-j_N6ZEZx.cjs} +33 -82
  16. package/dist/tests/docloader.test.cjs +84 -5
  17. package/dist/tests/docloader.test.mjs +84 -5
  18. package/dist/tests/internal/multicodec.test.cjs +1 -1
  19. package/dist/tests/internal/multicodec.test.mjs +1 -1
  20. package/dist/tests/jsonld-cache.test.cjs +1 -1
  21. package/dist/tests/jsonld-cache.test.mjs +1 -1
  22. package/dist/tests/key.test.cjs +1 -1
  23. package/dist/tests/key.test.mjs +1 -1
  24. package/dist/tests/langstr.test.cjs +1 -1
  25. package/dist/tests/langstr.test.mjs +1 -1
  26. package/dist/tests/link.test.cjs +1 -1
  27. package/dist/tests/link.test.mjs +1 -1
  28. package/dist/tests/multibase/multibase.test.cjs +1 -1
  29. package/dist/tests/multibase/multibase.test.mjs +1 -1
  30. package/dist/tests/{request-CRO9h2AO.cjs → request-B3YkikPO.cjs} +1 -1
  31. package/dist/tests/{request-C8BUtPj4.mjs → request-Bo1iqGDW.mjs} +1 -1
  32. package/dist/tests/request.test.cjs +2 -2
  33. package/dist/tests/request.test.mjs +2 -2
  34. package/dist/tests/temporal.test.cjs +1 -1
  35. package/dist/tests/temporal.test.mjs +1 -1
  36. package/dist/tests/url.test.cjs +1 -1
  37. package/dist/tests/url.test.mjs +1 -1
  38. package/package.json +1 -1
  39. package/src/body.test.ts +125 -0
  40. package/src/body.ts +95 -0
  41. package/src/docloader.test.ts +128 -3
  42. package/src/docloader.ts +52 -93
@@ -0,0 +1,90 @@
1
+ import { r as readBoundedText, t as BodyTooLargeError } from "./body-DRVriaJO.mjs";
2
+ import { deepStrictEqual, ok, rejects } from "node:assert/strict";
3
+ import { test } from "node:test";
4
+ import { gunzipSync, gzipSync } from "node:zlib";
5
+ //#region src/body.test.ts
6
+ const url = "https://example.com/document";
7
+ test("readBoundedText handles empty bodies and flushes incomplete UTF-8", async () => {
8
+ deepStrictEqual(await readBoundedText(new Response(null), 1, url), "");
9
+ deepStrictEqual(await readBoundedText(new Response(""), 1, url), "");
10
+ deepStrictEqual(await readBoundedText(new Response(new Uint8Array([226, 130])), 2, url), "�");
11
+ });
12
+ test("readBoundedText counts bytes and preserves split UTF-8", async () => {
13
+ const bytes = new TextEncoder().encode("\"안녕\"");
14
+ const stream = new ReadableStream({ start(controller) {
15
+ for (const byte of bytes) controller.enqueue(new Uint8Array([byte]));
16
+ controller.close();
17
+ } });
18
+ deepStrictEqual(await readBoundedText(new Response(stream), bytes.length, url), "\"안녕\"");
19
+ await rejects(readBoundedText(new Response(bytes), bytes.length - 1, url), BodyTooLargeError);
20
+ });
21
+ test("readBoundedText rejects a declared oversized body without pulling", async () => {
22
+ let pulled = false;
23
+ let canceled = false;
24
+ const response = new Response(new ReadableStream({
25
+ pull() {
26
+ pulled = true;
27
+ },
28
+ cancel() {
29
+ canceled = true;
30
+ }
31
+ }, { highWaterMark: 0 }), { headers: { "Content-Length": "100" } });
32
+ await rejects(readBoundedText(response, 10, url), BodyTooLargeError);
33
+ deepStrictEqual(pulled, false);
34
+ deepStrictEqual(canceled, true);
35
+ deepStrictEqual(response.body?.locked, false);
36
+ });
37
+ test("readBoundedText cancels chunked bodies despite absent or false lengths", async () => {
38
+ for (const length of [
39
+ void 0,
40
+ "1",
41
+ "invalid"
42
+ ]) {
43
+ let pulls = 0;
44
+ let canceled = false;
45
+ const stream = new ReadableStream({
46
+ pull(controller) {
47
+ pulls++;
48
+ controller.enqueue(/* @__PURE__ */ new Uint8Array(4));
49
+ },
50
+ cancel() {
51
+ canceled = true;
52
+ }
53
+ }, { highWaterMark: 0 });
54
+ const response = new Response(stream, { headers: length == null ? {} : { "Content-Length": length } });
55
+ await rejects(readBoundedText(response, 8, url), BodyTooLargeError);
56
+ deepStrictEqual(pulls, 3);
57
+ deepStrictEqual(canceled, true);
58
+ deepStrictEqual(response.body?.locked, false);
59
+ }
60
+ });
61
+ test("readBoundedText limits decoded gzip bytes, not the compressed length", async () => {
62
+ const compressed = gzipSync("\"" + "a".repeat(4096) + "\"");
63
+ ok(compressed.length < 128);
64
+ const decoded = gunzipSync(compressed);
65
+ await rejects(readBoundedText(new Response(decoded, { headers: {
66
+ "Content-Encoding": "gzip",
67
+ "Content-Length": String(compressed.length)
68
+ } }), 128, url), BodyTooLargeError);
69
+ deepStrictEqual(await readBoundedText(new Response("{}", { headers: {
70
+ "Content-Encoding": "gzip",
71
+ "Content-Length": "22"
72
+ } }), 2, url), "{}");
73
+ });
74
+ test("readBoundedText rejects invalid limits and releases failed streams", async () => {
75
+ for (const limit of [
76
+ 0,
77
+ -1,
78
+ NaN,
79
+ Infinity,
80
+ 1.5
81
+ ]) await rejects(readBoundedText(new Response("{}"), limit, url), RangeError);
82
+ const failure = /* @__PURE__ */ new Error("broken stream");
83
+ const response = new Response(new ReadableStream({ pull(controller) {
84
+ controller.error(failure);
85
+ } }));
86
+ await rejects(readBoundedText(response, 10, url), (error) => error === failure);
87
+ deepStrictEqual(response.body?.locked, false);
88
+ });
89
+ //#endregion
90
+ export {};
@@ -1,11 +1,11 @@
1
- require("./docloader-B3auVdRI.cjs");
1
+ require("./docloader-j_N6ZEZx.cjs");
2
2
  require("./url-CPfMatUL.cjs");
3
3
  require("./key-8Qg2SbZi.cjs");
4
4
  require("./multibase-B5Mea7Ip.cjs");
5
5
  require("./digest-3FeH2Y-Q.cjs");
6
6
  require("./langstr-C4Fl80ae.cjs");
7
- let node_assert = require("node:assert");
8
7
  let node_test = require("node:test");
8
+ let node_assert = require("node:assert");
9
9
  //#region src/decimal.ts
10
10
  const DECIMAL_PATTERN = /^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)$/;
11
11
  const XML_SCHEMA_WHITESPACE_PATTERN = /[\t\n\r ]+/g;
@@ -1,11 +1,11 @@
1
- import "./docloader-BexVAGvr.mjs";
1
+ import "./docloader-ZSduXDDg.mjs";
2
2
  import "./url-Basm1L_c.mjs";
3
3
  import "./key-C7MNhlkO.mjs";
4
4
  import "./multibase-BPnF_L4e.mjs";
5
5
  import "./digest-COC7xDiQ.mjs";
6
6
  import "./langstr-CQ26J_L7.mjs";
7
- import { deepStrictEqual, throws } from "node:assert";
8
7
  import { test } from "node:test";
8
+ import { deepStrictEqual, throws } from "node:assert";
9
9
  //#region src/decimal.ts
10
10
  const DECIMAL_PATTERN = /^(\+|-)?([0-9]+(\.[0-9]*)?|\.[0-9]+)$/;
11
11
  const XML_SCHEMA_WHITESPACE_PATTERN = /[\t\n\r ]+/g;
@@ -1,8 +1,8 @@
1
1
  const require_multicodec = require("./multicodec-CLRPeW4N.cjs");
2
2
  const require_multibase = require("./multibase-B5Mea7Ip.cjs");
3
3
  const require_digest = require("./digest-3FeH2Y-Q.cjs");
4
- let node_test = require("node:test");
5
4
  let node_assert_strict = require("node:assert/strict");
5
+ let node_test = require("node:test");
6
6
  //#region src/digest.test.ts
7
7
  const encoder = new TextEncoder();
8
8
  const decoder = new TextDecoder();
@@ -1,8 +1,8 @@
1
1
  import { t as addMulticodecPrefix } from "./multicodec-CRIj_05H.mjs";
2
2
  import { n as encodeMultibase } from "./multibase-BPnF_L4e.mjs";
3
3
  import { a as verifyDigestMultibase, i as parseHashlink, n as createHashlink, o as verifyHashlink, r as parseDigestMultibase, t as computeDigestMultibase } from "./digest-COC7xDiQ.mjs";
4
- import { test } from "node:test";
5
4
  import { deepStrictEqual, equal, rejects, throws } from "node:assert/strict";
5
+ import { test } from "node:test";
6
6
  //#region src/digest.test.ts
7
7
  const encoder = new TextEncoder();
8
8
  const decoder = new TextDecoder();
@@ -1,4 +1,5 @@
1
- import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-C8BUtPj4.mjs";
1
+ import { a as name, i as logRequest, n as createActivityPubRequest, o as version, t as FetchError } from "./request-Bo1iqGDW.mjs";
2
+ import { n as MAX_BODY_SIZE, r as readBoundedText, t as BodyTooLargeError } from "./body-DRVriaJO.mjs";
2
3
  import { t as HttpHeaderLink } from "./link-Cevmc87v.mjs";
3
4
  import { g as validatePublicUrl, t as UrlError } from "./url-Basm1L_c.mjs";
4
5
  import { getLogger } from "@logtape/logtape";
@@ -4529,58 +4530,6 @@ function createResponseMetadata(response) {
4529
4530
  statusText: response.statusText
4530
4531
  });
4531
4532
  }
4532
- async function cancelResponseBody(response) {
4533
- if (response.body != null) await response.body.cancel();
4534
- }
4535
- async function readBoundedText(response, maxBytes) {
4536
- const contentLength = response.headers.get("Content-Length");
4537
- if (contentLength != null) {
4538
- const size = Number(contentLength);
4539
- if (size > maxBytes) {
4540
- await cancelResponseBody(response);
4541
- return {
4542
- text: "",
4543
- size,
4544
- tooLarge: true
4545
- };
4546
- }
4547
- }
4548
- if (response.body == null) return {
4549
- text: "",
4550
- size: 0,
4551
- tooLarge: false
4552
- };
4553
- const reader = response.body.getReader();
4554
- const decoder = new TextDecoder();
4555
- let text = "";
4556
- let size = 0;
4557
- try {
4558
- while (true) {
4559
- const result = await reader.read();
4560
- if (result.done) break;
4561
- const chunkSize = result.value.byteLength;
4562
- if (size + chunkSize > maxBytes) {
4563
- size += chunkSize;
4564
- await reader.cancel();
4565
- return {
4566
- text: "",
4567
- size,
4568
- tooLarge: true
4569
- };
4570
- }
4571
- size += chunkSize;
4572
- text += decoder.decode(result.value, { stream: true });
4573
- }
4574
- text += decoder.decode();
4575
- return {
4576
- text,
4577
- size,
4578
- tooLarge: false
4579
- };
4580
- } finally {
4581
- reader.releaseLock();
4582
- }
4583
- }
4584
4533
  /**
4585
4534
  * Gets a {@link RemoteDocument} from the given response.
4586
4535
  * @param url The URL of the document to load.
@@ -4636,42 +4585,44 @@ async function getRemoteDocument(url, response, fetch) {
4636
4585
  let document;
4637
4586
  if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
4638
4587
  const errorResponse = createResponseMetadata(response);
4639
- const html = await readBoundedText(response, MAX_HTML_SIZE);
4640
- if (html.tooLarge) {
4588
+ let html;
4589
+ try {
4590
+ html = await readBoundedText(response, MAX_HTML_SIZE, documentUrl);
4591
+ } catch (error) {
4592
+ if (!(error instanceof BodyTooLargeError)) throw error;
4641
4593
  logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
4642
4594
  url: documentUrl,
4643
- size: html.size
4595
+ maxBytes: MAX_HTML_SIZE
4644
4596
  });
4645
4597
  throw new FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
4646
- } else {
4647
- const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
4648
- const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
4649
- let tagMatch;
4650
- while ((tagMatch = tagPattern.exec(html.text)) !== null) {
4651
- const tagContent = tagMatch[2];
4652
- let attrMatch;
4653
- const attribs = {};
4654
- attrPattern.lastIndex = 0;
4655
- while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
4656
- const key = attrMatch[1].toLowerCase();
4657
- attribs[key] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
4658
- }
4659
- 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) {
4660
- logger.debug("Found alternate document: {alternateUrl} from {url}", {
4661
- alternateUrl: attribs.href,
4662
- url: documentUrl
4663
- });
4664
- return await fetch(new URL(attribs.href, docUrl).href);
4665
- }
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] ?? "";
4666
4610
  }
4667
- try {
4668
- document = JSON.parse(html.text);
4669
- } catch (error) {
4670
- if (!(error instanceof SyntaxError)) throw error;
4671
- throw new FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
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);
4672
4617
  }
4673
4618
  }
4674
- } else document = await response.json();
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));
4675
4626
  logger.debug("Fetched document: {status} {url} {headers}", {
4676
4627
  status: response.status,
4677
4628
  url: documentUrl,
@@ -1,4 +1,5 @@
1
- const require_request = require("./request-CRO9h2AO.cjs");
1
+ const require_request = require("./request-B3YkikPO.cjs");
2
+ const require_body = require("./body-Cg5W_IdS.cjs");
2
3
  const require_link = require("./link-DlKm8bEr.cjs");
3
4
  const require_url = require("./url-CPfMatUL.cjs");
4
5
  let _logtape_logtape = require("@logtape/logtape");
@@ -4529,58 +4530,6 @@ function createResponseMetadata(response) {
4529
4530
  statusText: response.statusText
4530
4531
  });
4531
4532
  }
4532
- async function cancelResponseBody(response) {
4533
- if (response.body != null) await response.body.cancel();
4534
- }
4535
- async function readBoundedText(response, maxBytes) {
4536
- const contentLength = response.headers.get("Content-Length");
4537
- if (contentLength != null) {
4538
- const size = Number(contentLength);
4539
- if (size > maxBytes) {
4540
- await cancelResponseBody(response);
4541
- return {
4542
- text: "",
4543
- size,
4544
- tooLarge: true
4545
- };
4546
- }
4547
- }
4548
- if (response.body == null) return {
4549
- text: "",
4550
- size: 0,
4551
- tooLarge: false
4552
- };
4553
- const reader = response.body.getReader();
4554
- const decoder = new TextDecoder();
4555
- let text = "";
4556
- let size = 0;
4557
- try {
4558
- while (true) {
4559
- const result = await reader.read();
4560
- if (result.done) break;
4561
- const chunkSize = result.value.byteLength;
4562
- if (size + chunkSize > maxBytes) {
4563
- size += chunkSize;
4564
- await reader.cancel();
4565
- return {
4566
- text: "",
4567
- size,
4568
- tooLarge: true
4569
- };
4570
- }
4571
- size += chunkSize;
4572
- text += decoder.decode(result.value, { stream: true });
4573
- }
4574
- text += decoder.decode();
4575
- return {
4576
- text,
4577
- size,
4578
- tooLarge: false
4579
- };
4580
- } finally {
4581
- reader.releaseLock();
4582
- }
4583
- }
4584
4533
  /**
4585
4534
  * Gets a {@link RemoteDocument} from the given response.
4586
4535
  * @param url The URL of the document to load.
@@ -4636,42 +4585,44 @@ async function getRemoteDocument(url, response, fetch) {
4636
4585
  let document;
4637
4586
  if (!jsonLd && (contentType === "text/html" || contentType?.startsWith("text/html;") || contentType === "application/xhtml+xml" || contentType?.startsWith("application/xhtml+xml;"))) {
4638
4587
  const errorResponse = createResponseMetadata(response);
4639
- const html = await readBoundedText(response, MAX_HTML_SIZE);
4640
- if (html.tooLarge) {
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;
4641
4593
  logger.warn("HTML response too large, skipping alternate link discovery: {url}", {
4642
4594
  url: documentUrl,
4643
- size: html.size
4595
+ maxBytes: MAX_HTML_SIZE
4644
4596
  });
4645
4597
  throw new require_request.FetchError(documentUrl, `HTML document is too large to scan for an ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
4646
- } else {
4647
- const tagPattern = /<(a|link)\s+([^>]*?)\s*\/?>/gi;
4648
- const attrPattern = /([a-z][a-z:_-]*)=(?:"([^"]*)"|'([^']*)'|([^\s>]+))/gi;
4649
- let tagMatch;
4650
- while ((tagMatch = tagPattern.exec(html.text)) !== null) {
4651
- const tagContent = tagMatch[2];
4652
- let attrMatch;
4653
- const attribs = {};
4654
- attrPattern.lastIndex = 0;
4655
- while ((attrMatch = attrPattern.exec(tagContent)) !== null) {
4656
- const key = attrMatch[1].toLowerCase();
4657
- attribs[key] = attrMatch[2] ?? attrMatch[3] ?? attrMatch[4] ?? "";
4658
- }
4659
- 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) {
4660
- logger.debug("Found alternate document: {alternateUrl} from {url}", {
4661
- alternateUrl: attribs.href,
4662
- url: documentUrl
4663
- });
4664
- return await fetch(new URL(attribs.href, docUrl).href);
4665
- }
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] ?? "";
4666
4610
  }
4667
- try {
4668
- document = JSON.parse(html.text);
4669
- } catch (error) {
4670
- if (!(error instanceof SyntaxError)) throw error;
4671
- throw new require_request.FetchError(documentUrl, `HTML document has no ActivityPub alternate link (Content-Type: ${contentType})`, errorResponse);
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);
4672
4617
  }
4673
4618
  }
4674
- } else document = await response.json();
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));
4675
4626
  logger.debug("Fetched document: {status} {url} {headers}", {
4676
4627
  status: response.status,
4677
4628
  url: documentUrl,
@@ -1,9 +1,11 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
- const require_docloader = require("./docloader-B3auVdRI.cjs");
3
- const require_request = require("./request-CRO9h2AO.cjs");
2
+ const require_request = require("./request-B3YkikPO.cjs");
3
+ const require_docloader = require("./docloader-j_N6ZEZx.cjs");
4
4
  const require_url = require("./url-CPfMatUL.cjs");
5
- let node_assert = require("node:assert");
6
5
  let node_test = require("node:test");
6
+ let node_zlib = require("node:zlib");
7
+ let node_assert = require("node:assert");
8
+ let node_http = require("node:http");
7
9
  //#region ../../node_modules/.pnpm/glob-to-regexp@0.4.1/node_modules/glob-to-regexp/index.js
8
10
  var require_glob_to_regexp = /* @__PURE__ */ require_rolldown_runtime.__commonJSMin(((exports, module) => {
9
11
  module.exports = function(glob, opts) {
@@ -1471,9 +1473,9 @@ var esm_default = new class FetchMock {
1471
1473
  "Content-Length": String(1048577),
1472
1474
  "Content-Type": "text/html; charset=utf-8"
1473
1475
  } });
1474
- Object.defineProperty(response, "body", { value: { cancel: () => {
1476
+ Object.defineProperty(response, "body", { value: new ReadableStream({ cancel() {
1475
1477
  canceled = true;
1476
- } } });
1478
+ } }) });
1477
1479
  await (0, node_assert.rejects)(() => require_docloader.getRemoteDocument("https://example.com/large-html-cancel", response, () => {
1478
1480
  throw new Error("unexpected alternate fetch");
1479
1481
  }), require_request.FetchError);
@@ -1626,4 +1628,81 @@ var esm_default = new class FetchMock {
1626
1628
  });
1627
1629
  esm_default.hardReset();
1628
1630
  });
1631
+ (0, node_test.test)("getDocumentLoader() bounds JSON, HTML, and alternate documents", async () => {
1632
+ const url = "https://example.com/bounded";
1633
+ esm_default.mockGlobal();
1634
+ let oversized = true;
1635
+ try {
1636
+ esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
1637
+ "Content-Type": "application/activity+json",
1638
+ ...oversized ? { "Content-Length": String(16777217) } : {}
1639
+ } }));
1640
+ const loader = require_docloader.getDocumentLoader({ allowPrivateAddress: true });
1641
+ await (0, node_assert.rejects)(loader(url), require_request.FetchError);
1642
+ oversized = false;
1643
+ (0, node_assert.deepStrictEqual)((await loader(url)).document, { name: "hello" });
1644
+ oversized = true;
1645
+ esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
1646
+ await (0, node_assert.rejects)(require_docloader.getDocumentLoader({ allowPrivateAddress: true })(`${url}/html`), require_request.FetchError);
1647
+ esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
1648
+ "Content-Type": "text/html",
1649
+ Link: `<${url}>; rel="alternate"; type="application/activity+json"`
1650
+ } }));
1651
+ await (0, node_assert.rejects)(loader(`${url}/alternate`), require_request.FetchError);
1652
+ esm_default.get(`${url}/redirect`, {
1653
+ status: 302,
1654
+ headers: { Location: url }
1655
+ });
1656
+ await (0, node_assert.rejects)(loader(`${url}/redirect`), require_request.FetchError);
1657
+ } finally {
1658
+ esm_default.hardReset();
1659
+ }
1660
+ });
1661
+ (0, node_test.test)("getDocumentLoader() rejects oversized gzip responses from fetch", async () => {
1662
+ const document = { name: "a".repeat(16 * 1024 * 1024) };
1663
+ const compressed = (0, node_zlib.gzipSync)(JSON.stringify(document));
1664
+ (0, node_assert.ok)(compressed.byteLength < 64 * 1024);
1665
+ const server = (0, node_http.createServer)((request, response) => {
1666
+ const body = request.url === "/small" ? (0, node_zlib.gzipSync)("{\"name\":\"hello\"}") : compressed;
1667
+ response.writeHead(200, {
1668
+ "Content-Type": "application/activity+json",
1669
+ "Content-Encoding": "gzip",
1670
+ "Content-Length": body.byteLength
1671
+ });
1672
+ response.end(body);
1673
+ });
1674
+ await new Promise((resolve, reject) => {
1675
+ server.once("error", reject);
1676
+ server.listen(0, "127.0.0.1", resolve);
1677
+ });
1678
+ try {
1679
+ const address = server.address();
1680
+ (0, node_assert.ok)(address != null && typeof address !== "string");
1681
+ const url = `http://127.0.0.1:${address.port}/document`;
1682
+ await (0, node_assert.rejects)(require_docloader.getDocumentLoader({ allowPrivateAddress: true })(url), require_request.FetchError);
1683
+ (0, node_assert.deepStrictEqual)((await require_docloader.getDocumentLoader({ allowPrivateAddress: true })(new URL("/small", url).href)).document, { name: "hello" });
1684
+ } finally {
1685
+ await new Promise((resolve, reject) => {
1686
+ server.close((error) => error == null ? resolve() : reject(error));
1687
+ server.closeAllConnections();
1688
+ });
1689
+ }
1690
+ });
1691
+ (0, node_test.test)("getRemoteDocument() bounds JSON by default", async () => {
1692
+ let canceled = false;
1693
+ let pulls = 0;
1694
+ await (0, node_assert.rejects)(require_docloader.getRemoteDocument("https://example.com/document", new Response(new ReadableStream({
1695
+ pull(controller) {
1696
+ pulls++;
1697
+ controller.enqueue(new Uint8Array(64 * 1024).fill(32));
1698
+ },
1699
+ cancel() {
1700
+ canceled = true;
1701
+ }
1702
+ }, { highWaterMark: 0 })), () => {
1703
+ throw new Error("Unexpected alternate document");
1704
+ }), require_request.FetchError);
1705
+ (0, node_assert.deepStrictEqual)(canceled, true);
1706
+ (0, node_assert.deepStrictEqual)(pulls, 257);
1707
+ });
1629
1708
  //#endregion
@@ -1,8 +1,10 @@
1
- import { i as cid_v1_default, n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-BexVAGvr.mjs";
2
- import { t as FetchError } from "./request-C8BUtPj4.mjs";
1
+ import { t as FetchError } from "./request-Bo1iqGDW.mjs";
2
+ import { i as cid_v1_default, n as getRemoteDocument, r as preloadedContexts, t as getDocumentLoader } from "./docloader-ZSduXDDg.mjs";
3
3
  import { t as UrlError } from "./url-Basm1L_c.mjs";
4
- import { deepStrictEqual, ok, rejects } from "node:assert";
5
4
  import { test } from "node:test";
5
+ import { gzipSync } from "node:zlib";
6
+ import { deepStrictEqual, ok, rejects } from "node:assert";
7
+ import { createServer } from "node:http";
6
8
  //#region \0rolldown/runtime.js
7
9
  var __create = Object.create;
8
10
  var __defProp = Object.defineProperty;
@@ -1490,9 +1492,9 @@ test("getDocumentLoader()", async (t) => {
1490
1492
  "Content-Length": String(1048577),
1491
1493
  "Content-Type": "text/html; charset=utf-8"
1492
1494
  } });
1493
- Object.defineProperty(response, "body", { value: { cancel: () => {
1495
+ Object.defineProperty(response, "body", { value: new ReadableStream({ cancel() {
1494
1496
  canceled = true;
1495
- } } });
1497
+ } }) });
1496
1498
  await rejects(() => getRemoteDocument("https://example.com/large-html-cancel", response, () => {
1497
1499
  throw new Error("unexpected alternate fetch");
1498
1500
  }), FetchError);
@@ -1645,5 +1647,82 @@ test("getDocumentLoader()", async (t) => {
1645
1647
  });
1646
1648
  esm_default.hardReset();
1647
1649
  });
1650
+ test("getDocumentLoader() bounds JSON, HTML, and alternate documents", async () => {
1651
+ const url = "https://example.com/bounded";
1652
+ esm_default.mockGlobal();
1653
+ let oversized = true;
1654
+ try {
1655
+ esm_default.get(url, () => new Response("{\"name\":\"hello\"}", { headers: {
1656
+ "Content-Type": "application/activity+json",
1657
+ ...oversized ? { "Content-Length": String(16777217) } : {}
1658
+ } }));
1659
+ const loader = getDocumentLoader({ allowPrivateAddress: true });
1660
+ await rejects(loader(url), FetchError);
1661
+ oversized = false;
1662
+ deepStrictEqual((await loader(url)).document, { name: "hello" });
1663
+ oversized = true;
1664
+ esm_default.get(`${url}/html`, () => new Response(" ".repeat(1048577), { headers: { "Content-Type": "text/html" } }));
1665
+ await rejects(getDocumentLoader({ allowPrivateAddress: true })(`${url}/html`), FetchError);
1666
+ esm_default.get(`${url}/alternate`, () => new Response("", { headers: {
1667
+ "Content-Type": "text/html",
1668
+ Link: `<${url}>; rel="alternate"; type="application/activity+json"`
1669
+ } }));
1670
+ await rejects(loader(`${url}/alternate`), FetchError);
1671
+ esm_default.get(`${url}/redirect`, {
1672
+ status: 302,
1673
+ headers: { Location: url }
1674
+ });
1675
+ await rejects(loader(`${url}/redirect`), FetchError);
1676
+ } finally {
1677
+ esm_default.hardReset();
1678
+ }
1679
+ });
1680
+ test("getDocumentLoader() rejects oversized gzip responses from fetch", async () => {
1681
+ const document = { name: "a".repeat(16 * 1024 * 1024) };
1682
+ const compressed = gzipSync(JSON.stringify(document));
1683
+ ok(compressed.byteLength < 64 * 1024);
1684
+ const server = createServer((request, response) => {
1685
+ const body = request.url === "/small" ? gzipSync("{\"name\":\"hello\"}") : compressed;
1686
+ response.writeHead(200, {
1687
+ "Content-Type": "application/activity+json",
1688
+ "Content-Encoding": "gzip",
1689
+ "Content-Length": body.byteLength
1690
+ });
1691
+ response.end(body);
1692
+ });
1693
+ await new Promise((resolve, reject) => {
1694
+ server.once("error", reject);
1695
+ server.listen(0, "127.0.0.1", resolve);
1696
+ });
1697
+ try {
1698
+ const address = server.address();
1699
+ ok(address != null && typeof address !== "string");
1700
+ const url = `http://127.0.0.1:${address.port}/document`;
1701
+ await rejects(getDocumentLoader({ allowPrivateAddress: true })(url), FetchError);
1702
+ deepStrictEqual((await getDocumentLoader({ allowPrivateAddress: true })(new URL("/small", url).href)).document, { name: "hello" });
1703
+ } finally {
1704
+ await new Promise((resolve, reject) => {
1705
+ server.close((error) => error == null ? resolve() : reject(error));
1706
+ server.closeAllConnections();
1707
+ });
1708
+ }
1709
+ });
1710
+ test("getRemoteDocument() bounds JSON by default", async () => {
1711
+ let canceled = false;
1712
+ let pulls = 0;
1713
+ await rejects(getRemoteDocument("https://example.com/document", new Response(new ReadableStream({
1714
+ pull(controller) {
1715
+ pulls++;
1716
+ controller.enqueue(new Uint8Array(64 * 1024).fill(32));
1717
+ },
1718
+ cancel() {
1719
+ canceled = true;
1720
+ }
1721
+ }, { highWaterMark: 0 })), () => {
1722
+ throw new Error("Unexpected alternate document");
1723
+ }), FetchError);
1724
+ deepStrictEqual(canceled, true);
1725
+ deepStrictEqual(pulls, 257);
1726
+ });
1648
1727
  //#endregion
1649
1728
  export {};
@@ -1,6 +1,6 @@
1
1
  const require_multicodec = require("../multicodec-CLRPeW4N.cjs");
2
- let node_assert = require("node:assert");
3
2
  let node_test = require("node:test");
3
+ let node_assert = require("node:assert");
4
4
  //#region src/internal/multicodec.test.ts
5
5
  (0, node_test.test)("getMulticodecPrefix() decodes supported multicodec prefixes", () => {
6
6
  (0, node_assert.deepStrictEqual)(require_multicodec.getMulticodecPrefix(Uint8Array.from([
@@ -1,6 +1,6 @@
1
1
  import { n as getMulticodecPrefix, r as removeMulticodecPrefix, t as addMulticodecPrefix } from "../multicodec-CRIj_05H.mjs";
2
- import { deepStrictEqual, throws } from "node:assert";
3
2
  import { test } from "node:test";
3
+ import { deepStrictEqual, throws } from "node:assert";
4
4
  //#region src/internal/multicodec.test.ts
5
5
  test("getMulticodecPrefix() decodes supported multicodec prefixes", () => {
6
6
  deepStrictEqual(getMulticodecPrefix(Uint8Array.from([
@@ -1,7 +1,7 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-emK7D4bc.cjs");
2
2
  const require_url = require("./url-CPfMatUL.cjs");
3
- let node_assert = require("node:assert");
4
3
  let node_test = require("node:test");
4
+ let node_assert = require("node:assert");
5
5
  let jsonld_dist_jsonld_esm_js = require("jsonld/dist/jsonld.esm.js");
6
6
  jsonld_dist_jsonld_esm_js = require_rolldown_runtime.__toESM(jsonld_dist_jsonld_esm_js, 1);
7
7
  //#region src/jsonld.ts