@fedify/vocab-runtime 2.4.0-dev.2233 → 2.4.0-dev.2245

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 (55) hide show
  1. package/deno.json +2 -2
  2. package/dist/internal/jsonld-cache.cjs +1 -1
  3. package/dist/internal/jsonld-cache.js +1 -1
  4. package/dist/internal/portable-dereference.cjs +3 -6
  5. package/dist/internal/portable-dereference.js +3 -6
  6. package/dist/mod.cjs +2 -2
  7. package/dist/mod.d.cts +14 -3
  8. package/dist/mod.d.ts +14 -3
  9. package/dist/mod.js +2 -2
  10. package/dist/tests/{body-DQKTFMsH.cjs → body-C7LfsQJ3.cjs} +1 -1
  11. package/dist/tests/{body-BEYUpKvX.mjs → body-D50XSh7s.mjs} +1 -1
  12. package/dist/tests/body.test.cjs +1 -1
  13. package/dist/tests/body.test.mjs +1 -1
  14. package/dist/tests/decimal.test.cjs +3 -3
  15. package/dist/tests/decimal.test.mjs +3 -3
  16. package/dist/tests/{docloader-ra1lbBfP.mjs → docloader-BdcRw8S9.mjs} +3 -3
  17. package/dist/tests/{docloader-DHCYdBXf.cjs → docloader-oUrWeeUz.cjs} +3 -3
  18. package/dist/tests/docloader.test.cjs +59 -65
  19. package/dist/tests/docloader.test.mjs +38 -44
  20. package/dist/tests/internal/portable-dereference.test.cjs +21 -149
  21. package/dist/tests/internal/portable-dereference.test.mjs +3 -131
  22. package/dist/tests/{jsonld-cache-BKrsWPCQ.mjs → jsonld-cache-CKfgtxPB.mjs} +1 -1
  23. package/dist/tests/{jsonld-cache-BTgizq9A.cjs → jsonld-cache-DzxT9IXH.cjs} +1 -1
  24. package/dist/tests/jsonld-cache.test.cjs +2 -2
  25. package/dist/tests/jsonld-cache.test.mjs +2 -2
  26. package/dist/tests/multibase/multibase.test.cjs +7 -7
  27. package/dist/tests/multibase/multibase.test.mjs +5 -5
  28. package/dist/tests/portable-dereference-C_cwGjz6.mjs +132 -0
  29. package/dist/tests/portable-dereference-CyFYTBnr.cjs +149 -0
  30. package/dist/tests/{portable-media-DHaAEpaQ.cjs → portable-media-Bj5FXpUr.cjs} +3 -3
  31. package/dist/tests/{portable-media-1NjUQari.mjs → portable-media-D4NJcqkI.mjs} +3 -3
  32. package/dist/tests/portable-media.test.cjs +1 -1
  33. package/dist/tests/portable-media.test.mjs +1 -1
  34. package/dist/tests/portable-workers.test.cjs +36 -0
  35. package/dist/tests/portable-workers.test.d.cts +2 -0
  36. package/dist/tests/portable-workers.test.d.mts +2 -0
  37. package/dist/tests/portable-workers.test.mjs +35 -0
  38. package/dist/tests/{request-BQlkB8du.cjs → request-CBCk-e0R.cjs} +1 -1
  39. package/dist/tests/{request-D3elVRyB.mjs → request-wKyc91L5.mjs} +1 -1
  40. package/dist/tests/request.test.cjs +1 -1
  41. package/dist/tests/request.test.mjs +1 -1
  42. package/dist/tests/{url-Cdu4ASgJ.mjs → url-CWC-NI3a.mjs} +63 -7
  43. package/dist/tests/{url-CtHyzMR4.cjs → url-DsMBGqCc.cjs} +63 -7
  44. package/dist/tests/url.test.cjs +74 -1
  45. package/dist/tests/url.test.mjs +74 -1
  46. package/dist/{url-BHfQuRdy.cjs → url-BJMXv2kE.cjs} +63 -7
  47. package/dist/{url-DqqDZY9i.js → url-CZm2-7ZO.js} +63 -7
  48. package/package.json +6 -3
  49. package/scripts/test-bun.mjs +17 -0
  50. package/src/docloader.test.ts +82 -74
  51. package/src/internal/portable-dereference.ts +2 -5
  52. package/src/multibase/multibase.test.ts +5 -5
  53. package/src/portable-workers.test.ts +81 -0
  54. package/src/url.test.ts +90 -0
  55. package/src/url.ts +115 -13
@@ -1,12 +1,12 @@
1
- import { t as FetchError } from "./request-D3elVRyB.mjs";
1
+ import { t as FetchError } from "./request-wKyc91L5.mjs";
2
2
  import { n as cid_v1_default, t as preloadedContexts } from "./contexts-CIKsin4e.mjs";
3
- import { n as UrlError } from "./url-Cdu4ASgJ.mjs";
4
- import { i as withDocumentLoaderTimeout, n as getRemoteDocument, r as resolveDocumentLoaderTimeout, t as getDocumentLoader } from "./docloader-ra1lbBfP.mjs";
5
- import { test } from "node:test";
3
+ import { n as UrlError } from "./url-CWC-NI3a.mjs";
4
+ import { i as withDocumentLoaderTimeout, n as getRemoteDocument, r as resolveDocumentLoaderTimeout, t as getDocumentLoader } from "./docloader-BdcRw8S9.mjs";
6
5
  import { gzipSync } from "node:zlib";
7
6
  import { configure, reset } from "@logtape/logtape";
8
7
  import { deepStrictEqual, ok, rejects, throws } from "node:assert";
9
8
  import dns from "node:dns/promises";
9
+ import { test } from "@fedify/fixture";
10
10
  import { createServer } from "node:http";
11
11
  //#region \0rolldown/runtime.js
12
12
  var __create = Object.create;
@@ -1305,7 +1305,7 @@ test("getDocumentLoader()", async (t) => {
1305
1305
  name: "Fetched object",
1306
1306
  type: "Object"
1307
1307
  } });
1308
- await t.test("ok", async () => {
1308
+ await t.step("ok", async () => {
1309
1309
  deepStrictEqual(await fetchDocumentLoader("https://example.com/object"), {
1310
1310
  contextUrl: null,
1311
1311
  documentUrl: "https://example.com/object",
@@ -1348,7 +1348,7 @@ test("getDocumentLoader()", async (t) => {
1348
1348
  Link: "<https://example.com/object>; rel=\"alternate\"; type=\"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"\""
1349
1349
  }
1350
1350
  });
1351
- await t.test("Link header", async () => {
1351
+ await t.step("Link header", async () => {
1352
1352
  deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
1353
1353
  contextUrl: "https://www.w3.org/ns/activitystreams",
1354
1354
  documentUrl: "https://example.com/link-ctx",
@@ -1369,7 +1369,7 @@ test("getDocumentLoader()", async (t) => {
1369
1369
  }
1370
1370
  });
1371
1371
  });
1372
- await t.test("Link header relative url", async () => {
1372
+ await t.step("Link header relative url", async () => {
1373
1373
  deepStrictEqual(await fetchDocumentLoader("https://example.com/link-ctx"), {
1374
1374
  contextUrl: "https://www.w3.org/ns/activitystreams",
1375
1375
  documentUrl: "https://example.com/link-ctx",
@@ -1390,7 +1390,7 @@ test("getDocumentLoader()", async (t) => {
1390
1390
  }
1391
1391
  });
1392
1392
  });
1393
- await t.test("wrong Link header syntax", async () => {
1393
+ await t.step("wrong Link header syntax", async () => {
1394
1394
  deepStrictEqual(await fetchDocumentLoader("https://example.com/obj-w-wrong-link"), {
1395
1395
  contextUrl: null,
1396
1396
  documentUrl: "https://example.com/obj-w-wrong-link",
@@ -1414,7 +1414,7 @@ test("getDocumentLoader()", async (t) => {
1414
1414
  </html>`,
1415
1415
  headers: { "Content-Type": "text/html; charset=utf-8" }
1416
1416
  });
1417
- await t.test("HTML <link>", async () => {
1417
+ await t.step("HTML <link>", async () => {
1418
1418
  deepStrictEqual(await fetchDocumentLoader("https://example.com/html-link"), {
1419
1419
  contextUrl: null,
1420
1420
  documentUrl: "https://example.com/object",
@@ -1438,7 +1438,7 @@ test("getDocumentLoader()", async (t) => {
1438
1438
  </html>`,
1439
1439
  headers: { "Content-Type": "application/xhtml+xml; charset=utf-8" }
1440
1440
  });
1441
- await t.test("XHTML <link>", async () => {
1441
+ await t.step("XHTML <link>", async () => {
1442
1442
  deepStrictEqual(await fetchDocumentLoader("https://example.com/xhtml-link"), {
1443
1443
  contextUrl: null,
1444
1444
  documentUrl: "https://example.com/object",
@@ -1464,7 +1464,7 @@ test("getDocumentLoader()", async (t) => {
1464
1464
  </html>`,
1465
1465
  headers: { "Content-Type": "text/html; charset=utf-8" }
1466
1466
  });
1467
- await t.test("HTML <a>", async () => {
1467
+ await t.step("HTML <a>", async () => {
1468
1468
  deepStrictEqual(await fetchDocumentLoader("https://example.com/html-a"), {
1469
1469
  contextUrl: null,
1470
1470
  documentUrl: "https://example.com/object",
@@ -1486,7 +1486,7 @@ test("getDocumentLoader()", async (t) => {
1486
1486
  </html>`,
1487
1487
  headers: { "Content-Type": "text/html; charset=utf-8" }
1488
1488
  });
1489
- await t.test("HTML without ActivityPub alternate link", async () => {
1489
+ await t.step("HTML without ActivityPub alternate link", async () => {
1490
1490
  await rejects(() => fetchDocumentLoader("https://example.com/html-no-alternate"), (error) => {
1491
1491
  ok(error instanceof FetchError);
1492
1492
  ok(error.message.includes("HTML document has no ActivityPub alternate link"));
@@ -1506,7 +1506,7 @@ test("getDocumentLoader()", async (t) => {
1506
1506
  },
1507
1507
  headers: { "Content-Type": "text/html; charset=utf-8" }
1508
1508
  });
1509
- await t.test("wrong Content-Type with JSON body", async () => {
1509
+ await t.step("wrong Content-Type with JSON body", async () => {
1510
1510
  deepStrictEqual(await fetchDocumentLoader("https://example.com/wrong-content-type"), {
1511
1511
  contextUrl: null,
1512
1512
  documentUrl: "https://example.com/wrong-content-type",
@@ -1525,7 +1525,7 @@ test("getDocumentLoader()", async (t) => {
1525
1525
  "Content-Type": "text/html; charset=utf-8"
1526
1526
  }
1527
1527
  });
1528
- await t.test("HTML Content-Length over limit", async () => {
1528
+ await t.step("HTML Content-Length over limit", async () => {
1529
1529
  await rejects(() => fetchDocumentLoader("https://example.com/large-html"), (error) => {
1530
1530
  ok(error instanceof FetchError);
1531
1531
  ok(error.message.includes("HTML document is too large to scan for an ActivityPub alternate link"));
@@ -1535,7 +1535,7 @@ test("getDocumentLoader()", async (t) => {
1535
1535
  return true;
1536
1536
  });
1537
1537
  });
1538
- await t.test("HTML Content-Length over limit cancels body", async () => {
1538
+ await t.step("HTML Content-Length over limit cancels body", async () => {
1539
1539
  let canceled = false;
1540
1540
  const response = new Response("<!DOCTYPE html>", { headers: {
1541
1541
  "Content-Length": String(1048577),
@@ -1550,10 +1550,10 @@ test("getDocumentLoader()", async (t) => {
1550
1550
  deepStrictEqual(canceled, true);
1551
1551
  });
1552
1552
  esm_default.get("https://example.com/404", { status: 404 });
1553
- await t.test("not ok", async () => {
1553
+ await t.step("not ok", async () => {
1554
1554
  await rejects(() => fetchDocumentLoader("https://example.com/404"), FetchError, "HTTP 404: https://example.com/404");
1555
1555
  });
1556
- await t.test("preloaded contexts", async () => {
1556
+ await t.step("preloaded contexts", async () => {
1557
1557
  for (const [url, document] of Object.entries(preloadedContexts)) deepStrictEqual(await fetchDocumentLoader(url), {
1558
1558
  contextUrl: null,
1559
1559
  documentUrl: url,
@@ -1561,7 +1561,7 @@ test("getDocumentLoader()", async (t) => {
1561
1561
  });
1562
1562
  });
1563
1563
  esm_default.get("https://www.w3.org/ns/cid/v1", { status: 503 });
1564
- await t.test("preloaded CID v1 context", async () => {
1564
+ await t.step("preloaded CID v1 context", async () => {
1565
1565
  const url = "https://www.w3.org/ns/cid/v1";
1566
1566
  deepStrictEqual(await fetchDocumentLoader(url), {
1567
1567
  contextUrl: null,
@@ -1569,7 +1569,7 @@ test("getDocumentLoader()", async (t) => {
1569
1569
  document: cid_v1_default
1570
1570
  });
1571
1571
  });
1572
- await t.test("preloaded FEP-ef61 context", async () => {
1572
+ await t.step("preloaded FEP-ef61 context", async () => {
1573
1573
  const url = "https://w3id.org/fep/ef61";
1574
1574
  ok(url in preloadedContexts);
1575
1575
  deepStrictEqual(await fetchDocumentLoader(url), {
@@ -1586,7 +1586,7 @@ test("getDocumentLoader()", async (t) => {
1586
1586
  });
1587
1587
  });
1588
1588
  esm_default.get("https://w3id.org/fep/7aa9", { status: 502 });
1589
- await t.test("preloaded FEP-7aa9 context", async () => {
1589
+ await t.step("preloaded FEP-7aa9 context", async () => {
1590
1590
  const url = "https://w3id.org/fep/7aa9";
1591
1591
  deepStrictEqual(await fetchDocumentLoader(url), {
1592
1592
  contextUrl: null,
@@ -1616,7 +1616,7 @@ test("getDocumentLoader()", async (t) => {
1616
1616
  });
1617
1617
  deepStrictEqual(esm_default.callHistory.calls(url).length, 0);
1618
1618
  });
1619
- await t.test("deny non-HTTP/HTTPS", async () => {
1619
+ await t.step("deny non-HTTP/HTTPS", async () => {
1620
1620
  await rejects(() => fetchDocumentLoader("ftp://localhost"), UrlError);
1621
1621
  });
1622
1622
  esm_default.get("https://example.com/localhost-redirect", {
@@ -1641,13 +1641,13 @@ test("getDocumentLoader()", async (t) => {
1641
1641
  name: "Fetched object",
1642
1642
  type: "Object"
1643
1643
  } });
1644
- await t.test("allowPrivateAddress: false", async () => {
1644
+ await t.step("allowPrivateAddress: false", async () => {
1645
1645
  await rejects(() => fetchDocumentLoader("https://localhost/object"), UrlError);
1646
1646
  await rejects(() => fetchDocumentLoader("https://example.com/localhost-redirect"), UrlError);
1647
1647
  await rejects(() => fetchDocumentLoader("https://example.com/localhost-link"), UrlError);
1648
1648
  });
1649
1649
  const fetchDocumentLoader2 = getDocumentLoader({ allowPrivateAddress: true });
1650
- await t.test("allowPrivateAddress: true", async () => {
1650
+ await t.step("allowPrivateAddress: true", async () => {
1651
1651
  const expected = {
1652
1652
  contextUrl: null,
1653
1653
  documentUrl: "https://localhost/object",
@@ -1670,12 +1670,12 @@ test("getDocumentLoader()", async (t) => {
1670
1670
  headers: { Location: `https://example.com/too-many-redirects/${Number(cl.url.split("/").at(-1)) + 1}` }
1671
1671
  };
1672
1672
  });
1673
- await t.test("too many redirects", async () => {
1673
+ await t.step("too many redirects", async () => {
1674
1674
  redirectAttempts = 0;
1675
1675
  await rejects(() => fetchDocumentLoader("https://example.com/too-many-redirects/0"), FetchError, "Too many redirections");
1676
1676
  deepStrictEqual(redirectAttempts, 21);
1677
1677
  });
1678
- await t.test("custom max redirection", async () => {
1678
+ await t.step("custom max redirection", async () => {
1679
1679
  redirectAttempts = 0;
1680
1680
  const loader = getDocumentLoader({ maxRedirection: 1 });
1681
1681
  await rejects(() => loader("https://example.com/too-many-redirects/0"), FetchError, "Too many redirections");
@@ -1696,7 +1696,7 @@ test("getDocumentLoader()", async (t) => {
1696
1696
  headers: { Location: "https://example.com/redirect-loop-a" }
1697
1697
  };
1698
1698
  });
1699
- await t.test("redirect loop", async () => {
1699
+ await t.step("redirect loop", async () => {
1700
1700
  loopAttempts = 0;
1701
1701
  await rejects(() => fetchDocumentLoader("https://example.com/redirect-loop-a"), FetchError, "Redirect loop detected");
1702
1702
  deepStrictEqual(loopAttempts, 2);
@@ -1709,7 +1709,7 @@ test("getDocumentLoader()", async (t) => {
1709
1709
  headers: { Location: "/redirect-loop-relative" }
1710
1710
  };
1711
1711
  });
1712
- await t.test("redirect loop with relative location", async () => {
1712
+ await t.step("redirect loop with relative location", async () => {
1713
1713
  relativeLoopAttempts = 0;
1714
1714
  await rejects(() => fetchDocumentLoader("https://example.com/redirect-loop-relative"), FetchError, "Redirect loop detected");
1715
1715
  deepStrictEqual(relativeLoopAttempts, 1);
@@ -1719,7 +1719,7 @@ test("getDocumentLoader()", async (t) => {
1719
1719
  body: maliciousPayload,
1720
1720
  headers: { "Content-Type": "text/html; charset=utf-8" }
1721
1721
  });
1722
- await t.test("ReDoS resistance (CVE-2025-68475)", async () => {
1722
+ await t.step("ReDoS resistance (CVE-2025-68475)", async () => {
1723
1723
  const start = performance.now();
1724
1724
  await rejects(() => fetchDocumentLoader("https://example.com/redos"), FetchError);
1725
1725
  const elapsed = performance.now() - start;
@@ -1805,16 +1805,13 @@ test("getRemoteDocument() bounds JSON by default", async () => {
1805
1805
  deepStrictEqual(pulls, 257);
1806
1806
  });
1807
1807
  test("getDocumentLoader() logs DNS failures as such", async (t) => {
1808
- if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") {
1809
- t.skip("requires the net permission");
1810
- return;
1811
- }
1808
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
1812
1809
  const loader = getDocumentLoader();
1813
1810
  for (const result of [
1814
1811
  "throws",
1815
1812
  "empty",
1816
1813
  "private"
1817
- ]) await t.test(result, async () => {
1814
+ ]) await t.step(result, async () => {
1818
1815
  const originalLookup = dns.lookup;
1819
1816
  dns.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
1820
1817
  address: "127.0.0.1",
@@ -1869,7 +1866,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
1869
1866
  "header",
1870
1867
  "html",
1871
1868
  "mixed"
1872
- ]) for (const hops of [20, 21]) await t.test(`${mode}: ${hops} hops`, async () => {
1869
+ ]) for (const hops of [20, 21]) await t.step(`${mode}: ${hops} hops`, async () => {
1873
1870
  esm_default.mockGlobal();
1874
1871
  let requests = 0;
1875
1872
  esm_default.get(`begin:${base}`, ({ url }) => {
@@ -1898,7 +1895,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
1898
1895
  "alternate-redirect",
1899
1896
  "redirect-alternate",
1900
1897
  "redirect-intermediate"
1901
- ]) await t.test(`${mode}: cycle`, async () => {
1898
+ ]) await t.step(`${mode}: cycle`, async () => {
1902
1899
  esm_default.mockGlobal();
1903
1900
  let requests = 0;
1904
1901
  esm_default.get(`begin:${base}`, ({ url }) => {
@@ -1920,7 +1917,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
1920
1917
  esm_default.hardReset();
1921
1918
  }
1922
1919
  });
1923
- await t.test("alternate followed by 20 redirects shares the limit", async () => {
1920
+ await t.step("alternate followed by 20 redirects shares the limit", async () => {
1924
1921
  esm_default.mockGlobal();
1925
1922
  let requests = 0;
1926
1923
  esm_default.get(`begin:${base}`, ({ url }) => {
@@ -1940,7 +1937,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
1940
1937
  esm_default.hardReset();
1941
1938
  }
1942
1939
  });
1943
- await t.test("relative alternate after redirect and isolated calls", async () => {
1940
+ await t.step("relative alternate after redirect and isolated calls", async () => {
1944
1941
  esm_default.mockGlobal();
1945
1942
  esm_default.get(`${base}start`, Response.redirect(`${base}html`, 302));
1946
1943
  esm_default.get(`${base}html`, alternate("./document", true));
@@ -1967,7 +1964,7 @@ test("getDocumentLoader() bounds alternate document chains", async (t) => {
1967
1964
  test("getDocumentLoader() preserves cancellation across alternates", async (t) => {
1968
1965
  const base = "https://example.com/alternate-abort/";
1969
1966
  const loader = getDocumentLoader({ allowPrivateAddress: true });
1970
- for (const html of [false, true]) await t.test(html ? "HTML" : "Link header", async () => {
1967
+ for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
1971
1968
  esm_default.mockGlobal();
1972
1969
  const controller = new AbortController();
1973
1970
  let requests = 0;
@@ -2097,7 +2094,7 @@ test("getDocumentLoader() shares the timeout across redirects", async () => {
2097
2094
  });
2098
2095
  });
2099
2096
  test("getDocumentLoader() shares the timeout across alternates", async (t) => {
2100
- for (const html of [false, true]) await t.test(html ? "HTML" : "Link header", async () => {
2097
+ for (const html of [false, true]) await t.step(html ? "HTML" : "Link header", async () => {
2101
2098
  await withServer((request, response) => {
2102
2099
  const index = Number(request.url?.split("/").at(-1));
2103
2100
  setTimeout(() => {
@@ -2155,11 +2152,8 @@ test("getDocumentLoader() lets the caller's signal abort a request", async () =>
2155
2152
  });
2156
2153
  });
2157
2154
  });
2158
- test("getDocumentLoader() bounds DNS lookups that ignore the timeout", async (t) => {
2159
- if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") {
2160
- t.skip("requires the net permission");
2161
- return;
2162
- }
2155
+ test("getDocumentLoader() bounds DNS lookups that ignore the timeout", async () => {
2156
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") throw new Error("This DNS test requires Deno network permission (--allow-net).");
2163
2157
  esm_default.mockGlobal();
2164
2158
  let requests = 0;
2165
2159
  const url = "https://slow-dns.example/object";
@@ -1,148 +1,20 @@
1
- const require_contexts = require("../contexts-DizzBjz4.cjs");
2
- const require_url = require("../url-CtHyzMR4.cjs");
3
- const require_jsonld_cache = require("../jsonld-cache-BTgizq9A.cjs");
1
+ const require_url = require("../url-DsMBGqCc.cjs");
2
+ const require_jsonld_cache = require("../jsonld-cache-DzxT9IXH.cjs");
3
+ const require_portable_dereference = require("../portable-dereference-CyFYTBnr.cjs");
4
4
  let node_assert_strict = require("node:assert/strict");
5
5
  let node_test = require("node:test");
6
- let _logtape_logtape = require("@logtape/logtape");
7
- require("@opentelemetry/api");
8
- //#region src/internal/portable-dereference.ts
9
- const logger = (0, _logtape_logtape.getLogger)([
10
- "fedify",
11
- "vocab",
12
- "gateway"
13
- ]);
14
- /**
15
- * The maximum number of `@gateway` location hints to try for one reference.
16
- * Hints come from possibly untrusted documents, so they are bounded to keep
17
- * a single accessor call from fanning out to many servers.
18
- */
19
- const MAX_GATEWAY_HINTS = 5;
20
- const BASELINE_CONTEXT_URLS = /* @__PURE__ */ new Set([
21
- "https://w3id.org/identity/v1",
22
- "https://www.w3.org/ns/activitystreams",
23
- "https://w3id.org/security/v1",
24
- "https://w3id.org/security/data-integrity/v1"
25
- ]);
26
- /**
27
- * Checks whether a URL is an FEP-ef61 portable ActivityPub IRI.
28
- *
29
- * @internal Technically exported for generated vocabulary classes, but not
30
- * part of the public API contract. This is not considered public API for
31
- * Semantic Versioning decisions.
32
- */
33
- function isPortableIri(url) {
34
- return url.protocol === "ap+ef61:" || url.protocol === "ap:";
35
- }
36
- /**
37
- * Picks the ordered list of FEP-ef61 gateways to fetch a portable IRI from.
38
- *
39
- * If `gateways` is given, it is used as is (even when empty), and `@gateway`
40
- * location hints in the IRI are ignored. Otherwise, up to
41
- * {@link MAX_GATEWAY_HINTS} valid `@gateway` hints are used. Duplicate
42
- * gateways are dropped in both cases.
43
- *
44
- * @throws {TypeError} If an explicit gateway is not an HTTP(S) origin.
45
- * @internal
46
- */
47
- function getPortableGatewayCandidates(url, gateways) {
48
- const candidates = [];
49
- const seen = /* @__PURE__ */ new Set();
50
- const add = (gateway) => {
51
- if (seen.has(gateway.href)) return;
52
- seen.add(gateway.href);
53
- candidates.push(gateway);
54
- };
55
- if (gateways != null) {
56
- for (const gateway of gateways) {
57
- const parsed = require_url.parseGatewayOrigin(gateway);
58
- if (parsed == null) throw new TypeError("FEP-ef61 gateways must be HTTP(S) origins with no credentials, path, query, or fragment: " + String(gateway));
59
- add(parsed);
60
- }
61
- return candidates;
62
- }
63
- for (const hint of new URLSearchParams(url.search).getAll(require_url.GATEWAY_HINT_PARAMETER)) {
64
- if (candidates.length >= MAX_GATEWAY_HINTS) break;
65
- const parsed = require_url.parseGatewayOrigin(hint);
66
- if (parsed == null) {
67
- logger.debug("Ignoring an invalid FEP-ef61 gateway hint {hint} in {url}.", {
68
- hint,
69
- url: require_url.formatIri(url)
70
- });
71
- continue;
72
- }
73
- add(parsed);
74
- }
75
- return candidates;
76
- }
77
- /**
78
- * Creates a context loader that returns the same context documents for the
79
- * whole dereference operation, so that the identity check, the proof
80
- * verifier, and the parser interpret the fetched document identically even
81
- * if the underlying loader is nondeterministic. Failed loads are not
82
- * remembered, so a transient failure does not affect the next gateway.
83
- *
84
- * The parsed object keeps the loader for its own later dereferences, so
85
- * `release()` turns it into a plain pass-through to the underlying loader
86
- * once the operation is over.
87
- *
88
- * @internal Technically exported for generated vocabulary classes, but not
89
- * part of the public API contract. This is not considered public API for
90
- * Semantic Versioning decisions.
91
- */
92
- function createSnapshotContextLoader(contextLoader, suppressError) {
93
- contextLoader = require_jsonld_cache.unwrapReleasedDocumentLoader(contextLoader);
94
- const cache = /* @__PURE__ */ new Map();
95
- let released = false;
96
- const release = () => {
97
- released = true;
98
- state.released = true;
99
- cache.clear();
100
- };
101
- const loader = async (url, options) => {
102
- if (released) return await contextLoader(url, options);
103
- const key = URL.canParse(url) ? new URL(url).href : url;
104
- if (BASELINE_CONTEXT_URLS.has(key)) return {
105
- contextUrl: null,
106
- document: structuredClone(require_contexts.preloadedContexts[key]),
107
- documentUrl: key
108
- };
109
- let promise = cache.get(key);
110
- if (promise == null) {
111
- const loading = contextLoader(url, suppressError ? {
112
- ...options,
113
- suppressError: true
114
- } : options).then((document) => structuredClone(document));
115
- promise = loading;
116
- cache.set(key, loading);
117
- loading.catch(() => {
118
- if (cache.get(key) === loading) cache.delete(key);
119
- });
120
- }
121
- return structuredClone(await promise);
122
- };
123
- const state = {
124
- base: contextLoader,
125
- released: false
126
- };
127
- require_jsonld_cache.registerDocumentLoaderWrapper(loader, state);
128
- return {
129
- loader,
130
- release
131
- };
132
- }
133
- //#endregion
134
6
  //#region src/internal/portable-dereference.test.ts
135
7
  const hrefs = (urls) => urls.map((url) => url.href);
136
8
  (0, node_test.test)("isPortableIri()", () => {
137
- (0, node_assert_strict.ok)(isPortableIri(require_url.parseIri("ap://did:key:z6Mkabc/actor")));
138
- (0, node_assert_strict.ok)(isPortableIri(require_url.parseIri("ap+ef61://did:key:z6Mkabc/actor")));
139
- (0, node_assert_strict.ok)(isPortableIri(new URL("ap://did%3Akey%3Az6Mkabc/actor")));
140
- (0, node_assert_strict.ok)(!isPortableIri(new URL("https://example.com/actor")));
141
- (0, node_assert_strict.ok)(!isPortableIri(new URL("did:key:z6Mkabc")));
9
+ (0, node_assert_strict.ok)(require_portable_dereference.isPortableIri(require_url.parseIri("ap://did:key:z6Mkabc/actor")));
10
+ (0, node_assert_strict.ok)(require_portable_dereference.isPortableIri(require_url.parseIri("ap+ef61://did:key:z6Mkabc/actor")));
11
+ (0, node_assert_strict.ok)(require_portable_dereference.isPortableIri(new URL("ap://did%3Akey%3Az6Mkabc/actor")));
12
+ (0, node_assert_strict.ok)(!require_portable_dereference.isPortableIri(new URL("https://example.com/actor")));
13
+ (0, node_assert_strict.ok)(!require_portable_dereference.isPortableIri(new URL("did:key:z6Mkabc")));
142
14
  });
143
15
  (0, node_test.test)("getPortableGatewayCandidates() uses explicit gateways", () => {
144
16
  const url = require_url.parseIri("ap://did:key:z6Mkabc/actor?@gateway=https%3A%2F%2Fhint.example");
145
- (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(url, [
17
+ (0, node_assert_strict.deepStrictEqual)(hrefs(require_portable_dereference.getPortableGatewayCandidates(url, [
146
18
  "https://b.example",
147
19
  new URL("https://a.example/"),
148
20
  "https://b.example/",
@@ -152,7 +24,7 @@ const hrefs = (urls) => urls.map((url) => url.href);
152
24
  "https://a.example/",
153
25
  "http://c.example:8080/"
154
26
  ]);
155
- (0, node_assert_strict.deepStrictEqual)(getPortableGatewayCandidates(url, []), []);
27
+ (0, node_assert_strict.deepStrictEqual)(require_portable_dereference.getPortableGatewayCandidates(url, []), []);
156
28
  for (const gateway of [
157
29
  "https://a.example/path",
158
30
  "https://a.example/?",
@@ -160,13 +32,13 @@ const hrefs = (urls) => urls.map((url) => url.href);
160
32
  "https://user:pass@a.example",
161
33
  "ftp://a.example",
162
34
  "a.example"
163
- ]) (0, node_assert_strict.throws)(() => getPortableGatewayCandidates(url, [gateway]), TypeError);
35
+ ]) (0, node_assert_strict.throws)(() => require_portable_dereference.getPortableGatewayCandidates(url, [gateway]), TypeError);
164
36
  });
165
37
  (0, node_test.test)("getPortableGatewayCandidates() reads @gateway hints", () => {
166
- (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor?page=1&@gateway=https%3A%2F%2Fa.example&gateways=https%3A%2F%2Flegacy.example&@gateway=not%20a%20URL&@gateway=https%3A%2F%2Fb.example%2Fpath&@gateway=https%3A%2F%2Fquery.example%2F%3F&@gateway=https%3A%2F%2Ffragment.example%2F%23&%40gateway=https%3A%2F%2Fb.example&@gateway=https%3A%2F%2Fa.example%2F"))), ["https://a.example/", "https://b.example/"]);
167
- (0, node_assert_strict.deepStrictEqual)(getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor")), []);
38
+ (0, node_assert_strict.deepStrictEqual)(hrefs(require_portable_dereference.getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor?page=1&@gateway=https%3A%2F%2Fa.example&gateways=https%3A%2F%2Flegacy.example&@gateway=not%20a%20URL&@gateway=https%3A%2F%2Fb.example%2Fpath&@gateway=https%3A%2F%2Fquery.example%2F%3F&@gateway=https%3A%2F%2Ffragment.example%2F%23&%40gateway=https%3A%2F%2Fb.example&@gateway=https%3A%2F%2Fa.example%2F"))), ["https://a.example/", "https://b.example/"]);
39
+ (0, node_assert_strict.deepStrictEqual)(require_portable_dereference.getPortableGatewayCandidates(require_url.parseIri("ap://did:key:z6Mkabc/actor")), []);
168
40
  const hints = Array.from({ length: 7 }, (_, i) => `@gateway=https%3A%2F%2Fg${i}.example`);
169
- (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(require_url.parseIri(`ap://did:key:z6Mkabc/actor?${hints.join("&")}`))), [
41
+ (0, node_assert_strict.deepStrictEqual)(hrefs(require_portable_dereference.getPortableGatewayCandidates(require_url.parseIri(`ap://did:key:z6Mkabc/actor?${hints.join("&")}`))), [
170
42
  0,
171
43
  1,
172
44
  2,
@@ -176,7 +48,7 @@ const hrefs = (urls) => urls.map((url) => url.href);
176
48
  });
177
49
  (0, node_test.test)("getPortableGatewayCandidates() reads hints from withGatewayHints()", () => {
178
50
  const gateways = Array.from({ length: 7 }, (_, i) => `https://g${i}.example`);
179
- (0, node_assert_strict.deepStrictEqual)(hrefs(getPortableGatewayCandidates(require_url.withGatewayHints("ap://did:key:z6Mkabc/actor?page=1", gateways))), [
51
+ (0, node_assert_strict.deepStrictEqual)(hrefs(require_portable_dereference.getPortableGatewayCandidates(require_url.withGatewayHints("ap://did:key:z6Mkabc/actor?page=1", gateways))), [
180
52
  0,
181
53
  1,
182
54
  2,
@@ -196,15 +68,15 @@ const hrefs = (urls) => urls.map((url) => url.href);
196
68
  };
197
69
  let loader = base;
198
70
  for (let i = 0; i < 1e4; i++) {
199
- const snapshot = createSnapshotContextLoader(loader);
71
+ const snapshot = require_portable_dereference.createSnapshotContextLoader(loader);
200
72
  snapshot.release();
201
73
  loader = snapshot.loader;
202
74
  }
203
75
  await loader("https://example.com/context");
204
76
  (0, node_assert_strict.deepStrictEqual)(calls, ["https://example.com/context"]);
205
- const outer = createSnapshotContextLoader(base);
77
+ const outer = require_portable_dereference.createSnapshotContextLoader(base);
206
78
  await outer.loader("https://example.com/context");
207
- const inner = createSnapshotContextLoader(outer.loader);
79
+ const inner = require_portable_dereference.createSnapshotContextLoader(outer.loader);
208
80
  await inner.loader("https://example.com/context");
209
81
  (0, node_assert_strict.deepStrictEqual)(calls.length, 2);
210
82
  inner.release();
@@ -223,7 +95,7 @@ const hrefs = (urls) => urls.map((url) => url.href);
223
95
  for (const snapshotFirst of [false, true]) {
224
96
  let loader = base;
225
97
  for (let i = 0; i < 1e4; i++) {
226
- const wrapper = i % 2 === 0 === snapshotFirst ? createSnapshotContextLoader(loader, true) : require_jsonld_cache.createScopedContextLoader(loader, true);
98
+ const wrapper = i % 2 === 0 === snapshotFirst ? require_portable_dereference.createSnapshotContextLoader(loader, true) : require_jsonld_cache.createScopedContextLoader(loader, true);
227
99
  wrapper.release();
228
100
  loader = wrapper.loader;
229
101
  }
@@ -244,10 +116,10 @@ const hrefs = (urls) => urls.map((url) => url.href);
244
116
  document: { "@context": {} }
245
117
  });
246
118
  };
247
- const active = activeSnapshot ? createSnapshotContextLoader(base, true) : require_jsonld_cache.createScopedContextLoader(base, true);
119
+ const active = activeSnapshot ? require_portable_dereference.createSnapshotContextLoader(base, true) : require_jsonld_cache.createScopedContextLoader(base, true);
248
120
  let loader = active.loader;
249
121
  for (let i = 0; i < 4; i++) {
250
- const wrapper = i % 2 === 0 ? require_jsonld_cache.createScopedContextLoader(loader, true) : createSnapshotContextLoader(loader, true);
122
+ const wrapper = i % 2 === 0 ? require_jsonld_cache.createScopedContextLoader(loader, true) : require_portable_dereference.createSnapshotContextLoader(loader, true);
251
123
  wrapper.release();
252
124
  loader = wrapper.loader;
253
125
  }