@fedify/vocab-runtime 2.4.0-dev.2155 → 2.4.0-dev.2159

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 (57) hide show
  1. package/deno.json +1 -1
  2. package/dist/{docloader-oRpq1nQ8.d.cts → docloader-ClQraSWr.d.cts} +3 -0
  3. package/dist/{docloader-oRpq1nQ8.d.ts → docloader-ClQraSWr.d.ts} +3 -0
  4. package/dist/internal/jsonld-cache.cjs +1 -1
  5. package/dist/internal/jsonld-cache.d.cts +1 -1
  6. package/dist/internal/jsonld-cache.d.ts +1 -1
  7. package/dist/internal/jsonld-cache.js +1 -1
  8. package/dist/internal/portable-dereference.cjs +2 -2
  9. package/dist/internal/portable-dereference.d.cts +2 -2
  10. package/dist/internal/portable-dereference.d.ts +2 -2
  11. package/dist/internal/portable-dereference.js +2 -2
  12. package/dist/internal/signed-representation.cjs +1 -1
  13. package/dist/internal/signed-representation.js +1 -1
  14. package/dist/mod.cjs +18 -6
  15. package/dist/mod.d.cts +9 -2
  16. package/dist/mod.d.ts +9 -2
  17. package/dist/mod.js +18 -6
  18. package/dist/{portable-rwhiyMvY.d.ts → portable-DRtvOxTb.d.ts} +1 -1
  19. package/dist/{portable-Drfl02vZ.d.cts → portable-ilkFPcXa.d.cts} +1 -1
  20. package/dist/tests/{body-BJRBGr6n.mjs → body-BtHFjNgj.mjs} +1 -1
  21. package/dist/tests/{body-CLlfvGkr.cjs → body-qXqIp808.cjs} +1 -1
  22. package/dist/tests/body.test.cjs +1 -1
  23. package/dist/tests/body.test.mjs +1 -1
  24. package/dist/tests/decimal.test.cjs +2 -2
  25. package/dist/tests/decimal.test.mjs +2 -2
  26. package/dist/tests/{docloader-DsoAI6Tk.mjs → docloader-D6THKCoD.mjs} +19 -7
  27. package/dist/tests/{docloader-s_sA2gs3.cjs → docloader-DeoYwxVw.cjs} +19 -7
  28. package/dist/tests/docloader.test.cjs +241 -4
  29. package/dist/tests/docloader.test.mjs +240 -4
  30. package/dist/tests/internal/portable-dereference.test.cjs +3 -3
  31. package/dist/tests/internal/portable-dereference.test.mjs +3 -3
  32. package/dist/tests/{jsonld-cache-DnZS0z1G.mjs → jsonld-cache-B7PKo3Lu.mjs} +1 -1
  33. package/dist/tests/{jsonld-cache-9jap4yM0.cjs → jsonld-cache-BO-QXxIc.cjs} +1 -1
  34. package/dist/tests/jsonld-cache.test.cjs +2 -2
  35. package/dist/tests/jsonld-cache.test.mjs +2 -2
  36. package/dist/tests/{request-BzZ8Wn6I.mjs → request-1y3WTvsB.mjs} +1 -1
  37. package/dist/tests/{request-Dt_khD3j.cjs → request-nfOih0SA.cjs} +1 -1
  38. package/dist/tests/request.test.cjs +1 -1
  39. package/dist/tests/request.test.mjs +1 -1
  40. package/dist/tests/signed-representation.test.cjs +1 -1
  41. package/dist/tests/signed-representation.test.mjs +1 -1
  42. package/dist/tests/{url-DgJM9WJn.mjs → url-D7qRF-dF.mjs} +14 -4
  43. package/dist/tests/{url-BqlldzT8.cjs → url-gLKqAO1H.cjs} +15 -3
  44. package/dist/tests/url.test.cjs +36 -3
  45. package/dist/tests/url.test.mjs +37 -4
  46. package/dist/{url-CEhi4hb4.js → url-BHZYe8Vu.js} +14 -4
  47. package/dist/{url-Cke4irYD.cjs → url-P6l9s9dN.cjs} +15 -3
  48. package/package.json +1 -1
  49. package/src/contexts.ts +7 -1
  50. package/src/docloader.test.ts +307 -1
  51. package/src/docloader.ts +32 -7
  52. package/src/url.test.ts +29 -3
  53. package/src/url.ts +21 -5
  54. package/dist/{contexts-2hEbs3Qk.cjs → contexts-BK1CqBR5.cjs} +22 -22
  55. package/dist/{contexts-DoZUIW7y.js → contexts-DPuJ4UYL.js} +22 -22
  56. package/dist/tests/{contexts-BBX_qhBL.mjs → contexts-CIKsin4e.mjs} +9 -9
  57. package/dist/tests/{contexts-Ch3v2Cgu.cjs → contexts-DizzBjz4.cjs} +9 -9
package/src/docloader.ts CHANGED
@@ -324,6 +324,8 @@ export interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
324
324
 
325
325
  /**
326
326
  * Creates a JSON-LD document loader that utilizes the browser's `fetch` API.
327
+ * At most 20 HTTP redirects and alternate document links are followed in total
328
+ * per call. Revisiting a URL within that chain throws a {@link FetchError}.
327
329
  *
328
330
  * The created loader preloads the below frequently used contexts by default
329
331
  * (unless `options.skipPreloadedContexts` is set to `true`):
@@ -336,6 +338,7 @@ export interface GetDocumentLoaderOptions extends DocumentLoaderFactoryOptions {
336
338
  * - <https://www.w3.org/ns/cid/v1>
337
339
  * - <https://w3id.org/security/multikey/v1>
338
340
  * - <https://w3id.org/fep/ef61>
341
+ * - <https://w3id.org/fep/7aa9>
339
342
  * - <https://purl.archive.org/socialweb/webfinger>
340
343
  * - <http://schema.org/>
341
344
  * @param options Options for the document loader.
@@ -371,13 +374,20 @@ export function getDocumentLoader(
371
374
  await validatePublicUrl(currentUrl);
372
375
  } catch (error) {
373
376
  if (error instanceof UrlError) {
374
- logger[options?.suppressError ? "warn" : "error"](
375
- "Disallowed private URL: {url}",
376
- {
377
+ if (error.reason === "dns") {
378
+ logger.debug("DNS lookup failed for {url}", {
377
379
  url: currentUrl,
378
380
  error,
379
- },
380
- );
381
+ });
382
+ } else {
383
+ logger[options?.suppressError ? "warn" : "error"](
384
+ "Disallowed private URL: {url}",
385
+ {
386
+ url: currentUrl,
387
+ error,
388
+ },
389
+ );
390
+ }
381
391
  }
382
392
  throw error;
383
393
  }
@@ -442,7 +452,22 @@ export function getDocumentLoader(
442
452
  const result = await getRemoteDocument(
443
453
  currentUrl,
444
454
  response,
445
- load,
455
+ async (alternateUrl) => {
456
+ options?.signal?.throwIfAborted();
457
+ if (redirected >= DEFAULT_MAX_REDIRECTION) {
458
+ throw new FetchError(
459
+ currentUrl,
460
+ `Too many redirections (${redirected + 1})`,
461
+ );
462
+ }
463
+ if (visited.has(alternateUrl)) {
464
+ throw new FetchError(
465
+ currentUrl,
466
+ `Redirect loop detected: ${alternateUrl}`,
467
+ );
468
+ }
469
+ return await load(alternateUrl, options, redirected + 1, visited);
470
+ },
446
471
  options,
447
472
  );
448
473
  span.setAttribute("docloader.document_url", result.documentUrl);
@@ -463,5 +488,5 @@ export function getDocumentLoader(
463
488
  },
464
489
  );
465
490
  }
466
- return load;
491
+ return (url, options) => load(url, options);
467
492
  }
package/src/url.test.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { deepStrictEqual, ok, rejects, throws } from "node:assert";
1
+ import { deepStrictEqual, ok, rejects, strictEqual, throws } from "node:assert";
2
2
  import { test } from "node:test";
3
3
  import {
4
4
  arePortableUrisEqual,
@@ -1023,6 +1023,28 @@ test("compatible identifier conversion round-trips", () => {
1023
1023
  );
1024
1024
  });
1025
1025
 
1026
+ test("UrlError reason is independent of its cause", () => {
1027
+ const cause = new Error("Underlying failure");
1028
+ strictEqual(new UrlError("Rejected URL").reason, "disallowed");
1029
+ const disallowedError = new UrlError("Rejected URL", { cause });
1030
+ strictEqual(disallowedError.reason, "disallowed");
1031
+ strictEqual(disallowedError.cause, cause);
1032
+ const dnsError = new UrlError("Lookup failed", { reason: "dns", cause });
1033
+ strictEqual(dnsError.reason, "dns");
1034
+ strictEqual(dnsError.cause, cause);
1035
+ });
1036
+
1037
+ test("validatePublicUrl() classifies disallowed URLs", async () => {
1038
+ for (
1039
+ const url of ["ftp://example.com", "https://localhost", "https://127.0.0.1"]
1040
+ ) {
1041
+ await rejects(() => validatePublicUrl(url), {
1042
+ name: "UrlError",
1043
+ reason: "disallowed",
1044
+ });
1045
+ }
1046
+ });
1047
+
1026
1048
  test("validatePublicUrl()", async () => {
1027
1049
  await rejects(() => validatePublicUrl("ftp://localhost"), UrlError);
1028
1050
  await rejects(
@@ -1095,15 +1117,19 @@ test("validateLookupAddresses() rejects unsafe or CNAME-only Cloudflare Workers
1095
1117
  { address: "private-host.example.net.", family: 4 },
1096
1118
  { address: "127.0.0.1", family: 4 },
1097
1119
  ]),
1098
- UrlError,
1120
+ { name: "UrlError", reason: "disallowed" },
1099
1121
  );
1100
1122
  throws(
1101
1123
  () =>
1102
1124
  validateLookupAddresses([
1103
1125
  { address: "app-host.example.net.", family: 4 },
1104
1126
  ]),
1105
- UrlError,
1127
+ { name: "UrlError", reason: "dns" },
1106
1128
  );
1129
+ throws(() => validateLookupAddresses([]), {
1130
+ name: "UrlError",
1131
+ reason: "dns",
1132
+ });
1107
1133
  });
1108
1134
 
1109
1135
  test("isValidPublicIPv4Address()", () => {
package/src/url.ts CHANGED
@@ -1,11 +1,25 @@
1
1
  import type { LookupAddress } from "node:dns";
2
- import { lookup } from "node:dns/promises";
2
+ // FIXME: the default dns import exists since tests can stub `dns.lookup()`.
3
+ // This should be replaced by injecting the lookup function such as an internal
4
+ // option on `validatePublicUrl()`
5
+ import dns from "node:dns/promises";
3
6
  import { isIP } from "node:net";
4
7
 
5
8
  export class UrlError extends Error {
6
- constructor(message: string, options?: { cause?: unknown }) {
9
+ /**
10
+ * The failure category. Defaults to `"disallowed"`.
11
+ * `"dns"` includes lookup errors and results with no usable IP addresses.
12
+ * @since 2.0.29
13
+ */
14
+ readonly reason: "dns" | "disallowed";
15
+
16
+ constructor(
17
+ message: string,
18
+ options?: { cause?: unknown; reason?: "dns" | "disallowed" },
19
+ ) {
7
20
  super(message, options);
8
21
  this.name = "UrlError";
22
+ this.reason = options?.reason ?? "disallowed";
9
23
  }
10
24
  }
11
25
 
@@ -622,9 +636,9 @@ export async function validatePublicUrl(url: string): Promise<void> {
622
636
  // and ensure that they are all public:
623
637
  let addresses: LookupAddress[];
624
638
  try {
625
- addresses = await lookup(hostname, { all: true });
639
+ addresses = await dns.lookup(hostname, { all: true });
626
640
  } catch (error) {
627
- throw new UrlError("DNS lookup failed", { cause: error });
641
+ throw new UrlError("DNS lookup failed", { cause: error, reason: "dns" });
628
642
  }
629
643
  validateLookupAddresses(addresses);
630
644
  }
@@ -657,7 +671,9 @@ export function validateLookupAddresses(
657
671
  validatePublicIpAddress(address, family);
658
672
  }
659
673
  if (ipAddressCount === 0) {
660
- throw new UrlError("DNS lookup did not return any IP address");
674
+ throw new UrlError("DNS lookup did not return any IP address", {
675
+ reason: "dns",
676
+ });
661
677
  }
662
678
  }
663
679
 
@@ -4297,28 +4297,6 @@ const preloadedContexts = {
4297
4297
  "@type": "@id"
4298
4298
  }
4299
4299
  } },
4300
- "https://w3id.org/fep/7aa9": { "@context": {
4301
- "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4302
- "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
4303
- "FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
4304
- "FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
4305
- "topic": {
4306
- "@id": "https://w3id.org/fep/7aa9#topic",
4307
- "@type": "@id"
4308
- },
4309
- "featuredObject": {
4310
- "@id": "https://w3id.org/fep/7aa9#featuredObject",
4311
- "@type": "@id"
4312
- },
4313
- "canFeature": {
4314
- "@id": "https://w3id.org/fep/7aa9#canFeature",
4315
- "@type": "@id"
4316
- },
4317
- "featureAuthorization": {
4318
- "@id": "https://w3id.org/fep/7aa9#featureAuthorization",
4319
- "@type": "@id"
4320
- }
4321
- } },
4322
4300
  "https://www.w3.org/ns/cid/v1": { "@context": {
4323
4301
  "@protected": true,
4324
4302
  "id": "@id",
@@ -4438,6 +4416,28 @@ const preloadedContexts = {
4438
4416
  },
4439
4417
  "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4440
4418
  } },
4419
+ "https://w3id.org/fep/7aa9": { "@context": {
4420
+ "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4421
+ "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
4422
+ "FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
4423
+ "FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
4424
+ "topic": {
4425
+ "@id": "https://w3id.org/fep/7aa9#topic",
4426
+ "@type": "@id"
4427
+ },
4428
+ "featuredObject": {
4429
+ "@id": "https://w3id.org/fep/7aa9#featuredObject",
4430
+ "@type": "@id"
4431
+ },
4432
+ "canFeature": {
4433
+ "@id": "https://w3id.org/fep/7aa9#canFeature",
4434
+ "@type": "@id"
4435
+ },
4436
+ "featureAuthorization": {
4437
+ "@id": "https://w3id.org/fep/7aa9#featureAuthorization",
4438
+ "@type": "@id"
4439
+ }
4440
+ } },
4441
4441
  "https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
4442
4442
  "as": "https://www.w3.org/ns/activitystreams#",
4443
4443
  "lemmy": "https://join-lemmy.org/ns#",
@@ -4297,28 +4297,6 @@ const preloadedContexts = {
4297
4297
  "@type": "@id"
4298
4298
  }
4299
4299
  } },
4300
- "https://w3id.org/fep/7aa9": { "@context": {
4301
- "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4302
- "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
4303
- "FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
4304
- "FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
4305
- "topic": {
4306
- "@id": "https://w3id.org/fep/7aa9#topic",
4307
- "@type": "@id"
4308
- },
4309
- "featuredObject": {
4310
- "@id": "https://w3id.org/fep/7aa9#featuredObject",
4311
- "@type": "@id"
4312
- },
4313
- "canFeature": {
4314
- "@id": "https://w3id.org/fep/7aa9#canFeature",
4315
- "@type": "@id"
4316
- },
4317
- "featureAuthorization": {
4318
- "@id": "https://w3id.org/fep/7aa9#featureAuthorization",
4319
- "@type": "@id"
4320
- }
4321
- } },
4322
4300
  "https://www.w3.org/ns/cid/v1": { "@context": {
4323
4301
  "@protected": true,
4324
4302
  "id": "@id",
@@ -4438,6 +4416,28 @@ const preloadedContexts = {
4438
4416
  },
4439
4417
  "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4440
4418
  } },
4419
+ "https://w3id.org/fep/7aa9": { "@context": {
4420
+ "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4421
+ "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
4422
+ "FeatureRequest": "https://w3id.org/fep/7aa9#FeatureRequest",
4423
+ "FeatureAuthorization": "https://w3id.org/fep/7aa9#FeatureAuthorization",
4424
+ "topic": {
4425
+ "@id": "https://w3id.org/fep/7aa9#topic",
4426
+ "@type": "@id"
4427
+ },
4428
+ "featuredObject": {
4429
+ "@id": "https://w3id.org/fep/7aa9#featuredObject",
4430
+ "@type": "@id"
4431
+ },
4432
+ "canFeature": {
4433
+ "@id": "https://w3id.org/fep/7aa9#canFeature",
4434
+ "@type": "@id"
4435
+ },
4436
+ "featureAuthorization": {
4437
+ "@id": "https://w3id.org/fep/7aa9#featureAuthorization",
4438
+ "@type": "@id"
4439
+ }
4440
+ } },
4441
4441
  "https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
4442
4442
  "as": "https://www.w3.org/ns/activitystreams#",
4443
4443
  "lemmy": "https://join-lemmy.org/ns#",
@@ -4411,6 +4411,15 @@ const preloadedContexts = {
4411
4411
  "@type": "@id"
4412
4412
  }
4413
4413
  } },
4414
+ "https://www.w3.org/ns/cid/v1": cid_v1_default,
4415
+ "https://w3id.org/fep/ef61": { "@context": {
4416
+ "gateways": {
4417
+ "@id": "https://w3id.org/fep/ef61/gateways",
4418
+ "@type": "@id",
4419
+ "@container": "@list"
4420
+ },
4421
+ "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4422
+ } },
4414
4423
  "https://w3id.org/fep/7aa9": { "@context": {
4415
4424
  "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4416
4425
  "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
@@ -4433,15 +4442,6 @@ const preloadedContexts = {
4433
4442
  "@type": "@id"
4434
4443
  }
4435
4444
  } },
4436
- "https://www.w3.org/ns/cid/v1": cid_v1_default,
4437
- "https://w3id.org/fep/ef61": { "@context": {
4438
- "gateways": {
4439
- "@id": "https://w3id.org/fep/ef61/gateways",
4440
- "@type": "@id",
4441
- "@container": "@list"
4442
- },
4443
- "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4444
- } },
4445
4445
  "https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
4446
4446
  "as": "https://www.w3.org/ns/activitystreams#",
4447
4447
  "lemmy": "https://join-lemmy.org/ns#",
@@ -4411,6 +4411,15 @@ const preloadedContexts = {
4411
4411
  "@type": "@id"
4412
4412
  }
4413
4413
  } },
4414
+ "https://www.w3.org/ns/cid/v1": cid_v1_default,
4415
+ "https://w3id.org/fep/ef61": { "@context": {
4416
+ "gateways": {
4417
+ "@id": "https://w3id.org/fep/ef61/gateways",
4418
+ "@type": "@id",
4419
+ "@container": "@list"
4420
+ },
4421
+ "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4422
+ } },
4414
4423
  "https://w3id.org/fep/7aa9": { "@context": {
4415
4424
  "FeaturedCollection": "https://w3id.org/fep/7aa9#FeaturedCollection",
4416
4425
  "FeaturedItem": "https://w3id.org/fep/7aa9#FeaturedItem",
@@ -4433,15 +4442,6 @@ const preloadedContexts = {
4433
4442
  "@type": "@id"
4434
4443
  }
4435
4444
  } },
4436
- "https://www.w3.org/ns/cid/v1": cid_v1_default,
4437
- "https://w3id.org/fep/ef61": { "@context": {
4438
- "gateways": {
4439
- "@id": "https://w3id.org/fep/ef61/gateways",
4440
- "@type": "@id",
4441
- "@container": "@list"
4442
- },
4443
- "digestMultibase": "https://www.w3.org/ns/credentials/v2#digestMultibase"
4444
- } },
4445
4445
  "https://join-lemmy.org/context.json": { "@context": ["https://w3id.org/security/v1", {
4446
4446
  "as": "https://www.w3.org/ns/activitystreams#",
4447
4447
  "lemmy": "https://join-lemmy.org/ns#",