@fedify/webfinger 2.2.13 → 2.2.14

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.
package/deno.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/webfinger",
3
- "version": "2.2.13",
3
+ "version": "2.2.14",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./src/mod.ts"
@@ -22,10 +22,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
22
22
  }) : target, mod));
23
23
  //#endregion
24
24
  let _fedify_fixture = require("@fedify/fixture");
25
- let es_toolkit = require("es-toolkit");
26
- let node_assert_strict = require("node:assert/strict");
27
25
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
28
26
  let _logtape_logtape = require("@logtape/logtape");
27
+ let es_toolkit = require("es-toolkit");
28
+ let node_assert_strict = require("node:assert/strict");
29
+ let node_dns_promises = require("node:dns/promises");
30
+ node_dns_promises = __toESM(node_dns_promises);
29
31
  let _opentelemetry_api = require("@opentelemetry/api");
30
32
  //#endregion
31
33
  //#region ../../node_modules/.pnpm/regexparam@3.0.0/node_modules/regexparam/dist/index.mjs
@@ -1228,7 +1230,7 @@ var esm_default = new class FetchMock {
1228
1230
  //#endregion
1229
1231
  //#region deno.json
1230
1232
  var name = "@fedify/webfinger";
1231
- var version = "2.2.13";
1233
+ var version = "2.2.14";
1232
1234
  //#endregion
1233
1235
  //#region src/body.ts
1234
1236
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1364,7 +1366,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
1364
1366
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url.href);
1365
1367
  } catch (e) {
1366
1368
  if (e instanceof _fedify_vocab_runtime.UrlError) {
1367
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1369
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
1370
+ url: url.href,
1371
+ error: e
1372
+ });
1373
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1368
1374
  return null;
1369
1375
  }
1370
1376
  throw e;
@@ -1609,4 +1615,42 @@ async function lookupWebFingerInternal(resource, options = {}) {
1609
1615
  esm_default.hardReset();
1610
1616
  }
1611
1617
  });
1618
+ (0, _fedify_fixture.test)("lookupWebFinger() logs DNS failures as such", async (t) => {
1619
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") return;
1620
+ for (const result of [
1621
+ "throws",
1622
+ "empty",
1623
+ "private"
1624
+ ]) await t.step(result, async () => {
1625
+ const originalLookup = node_dns_promises.default.lookup;
1626
+ node_dns_promises.default.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
1627
+ address: "127.0.0.1",
1628
+ family: 4
1629
+ }]));
1630
+ const records = [];
1631
+ await (0, _logtape_logtape.configure)({
1632
+ sinks: { buffer: (record) => records.push(record) },
1633
+ loggers: [{
1634
+ category: "fedify",
1635
+ sinks: ["buffer"],
1636
+ lowestLevel: "debug"
1637
+ }, {
1638
+ category: ["logtape", "meta"],
1639
+ sinks: []
1640
+ }],
1641
+ reset: true
1642
+ });
1643
+ try {
1644
+ (0, node_assert_strict.deepStrictEqual)(await lookupWebFinger("acct:alice@dns-failure.invalid"), null);
1645
+ const failures = records.filter((r) => r.rawMessage !== "Fetching WebFinger resource descriptor from {url}...");
1646
+ (0, node_assert_strict.deepStrictEqual)(failures.map((r) => [r.level, r.rawMessage]), [result === "private" ? ["error", "Invalid URL for WebFinger resource descriptor: {error}"] : ["debug", "DNS lookup failed for {url}"]]);
1647
+ const { error } = failures[0].properties;
1648
+ (0, node_assert_strict.ok)(error instanceof _fedify_vocab_runtime.UrlError);
1649
+ (0, node_assert_strict.deepStrictEqual)(error.reason, result === "private" ? "disallowed" : "dns");
1650
+ } finally {
1651
+ await (0, _logtape_logtape.reset)();
1652
+ node_dns_promises.default.lookup = originalLookup;
1653
+ }
1654
+ });
1655
+ });
1612
1656
  //#endregion
@@ -1,8 +1,9 @@
1
1
  import { test } from "@fedify/fixture";
2
- import { withTimeout } from "es-toolkit";
3
- import { deepStrictEqual } from "node:assert/strict";
4
2
  import { FetchError, UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
5
- import { getLogger } from "@logtape/logtape";
3
+ import { configure, getLogger, reset } from "@logtape/logtape";
4
+ import { withTimeout } from "es-toolkit";
5
+ import { deepStrictEqual, ok } from "node:assert/strict";
6
+ import dns from "node:dns/promises";
6
7
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
7
8
  //#region \0rolldown/runtime.js
8
9
  var __create = Object.create;
@@ -1227,7 +1228,7 @@ var esm_default = new class FetchMock {
1227
1228
  //#endregion
1228
1229
  //#region deno.json
1229
1230
  var name = "@fedify/webfinger";
1230
- var version = "2.2.13";
1231
+ var version = "2.2.14";
1231
1232
  //#endregion
1232
1233
  //#region src/body.ts
1233
1234
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -1363,7 +1364,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
1363
1364
  await validatePublicUrl(url.href);
1364
1365
  } catch (e) {
1365
1366
  if (e instanceof UrlError) {
1366
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1367
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
1368
+ url: url.href,
1369
+ error: e
1370
+ });
1371
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
1367
1372
  return null;
1368
1373
  }
1369
1374
  throw e;
@@ -1608,5 +1613,43 @@ test("lookupWebFinger() bounds resource descriptors", async () => {
1608
1613
  esm_default.hardReset();
1609
1614
  }
1610
1615
  });
1616
+ test("lookupWebFinger() logs DNS failures as such", async (t) => {
1617
+ if ("Deno" in globalThis && (await Deno.permissions.query({ name: "net" })).state !== "granted") return;
1618
+ for (const result of [
1619
+ "throws",
1620
+ "empty",
1621
+ "private"
1622
+ ]) await t.step(result, async () => {
1623
+ const originalLookup = dns.lookup;
1624
+ dns.lookup = (() => result === "throws" ? Promise.reject(/* @__PURE__ */ new Error("Resolver unavailable")) : Promise.resolve(result === "empty" ? [] : [{
1625
+ address: "127.0.0.1",
1626
+ family: 4
1627
+ }]));
1628
+ const records = [];
1629
+ await configure({
1630
+ sinks: { buffer: (record) => records.push(record) },
1631
+ loggers: [{
1632
+ category: "fedify",
1633
+ sinks: ["buffer"],
1634
+ lowestLevel: "debug"
1635
+ }, {
1636
+ category: ["logtape", "meta"],
1637
+ sinks: []
1638
+ }],
1639
+ reset: true
1640
+ });
1641
+ try {
1642
+ deepStrictEqual(await lookupWebFinger("acct:alice@dns-failure.invalid"), null);
1643
+ const failures = records.filter((r) => r.rawMessage !== "Fetching WebFinger resource descriptor from {url}...");
1644
+ deepStrictEqual(failures.map((r) => [r.level, r.rawMessage]), [result === "private" ? ["error", "Invalid URL for WebFinger resource descriptor: {error}"] : ["debug", "DNS lookup failed for {url}"]]);
1645
+ const { error } = failures[0].properties;
1646
+ ok(error instanceof UrlError);
1647
+ deepStrictEqual(error.reason, result === "private" ? "disallowed" : "dns");
1648
+ } finally {
1649
+ await reset();
1650
+ dns.lookup = originalLookup;
1651
+ }
1652
+ });
1653
+ });
1611
1654
  //#endregion
1612
1655
  export {};
package/dist/mod.cjs CHANGED
@@ -4,7 +4,7 @@ let _logtape_logtape = require("@logtape/logtape");
4
4
  let _opentelemetry_api = require("@opentelemetry/api");
5
5
  //#region deno.json
6
6
  var name = "@fedify/webfinger";
7
- var version = "2.2.13";
7
+ var version = "2.2.14";
8
8
  //#endregion
9
9
  //#region src/body.ts
10
10
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -140,7 +140,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
140
140
  await (0, _fedify_vocab_runtime.validatePublicUrl)(url.href);
141
141
  } catch (e) {
142
142
  if (e instanceof _fedify_vocab_runtime.UrlError) {
143
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
143
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
144
+ url: url.href,
145
+ error: e
146
+ });
147
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
144
148
  return null;
145
149
  }
146
150
  throw e;
package/dist/mod.js CHANGED
@@ -3,7 +3,7 @@ import { getLogger } from "@logtape/logtape";
3
3
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
4
4
  //#region deno.json
5
5
  var name = "@fedify/webfinger";
6
- var version = "2.2.13";
6
+ var version = "2.2.14";
7
7
  //#endregion
8
8
  //#region src/body.ts
9
9
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -139,7 +139,11 @@ async function lookupWebFingerInternal(resource, options = {}) {
139
139
  await validatePublicUrl(url.href);
140
140
  } catch (e) {
141
141
  if (e instanceof UrlError) {
142
- logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
142
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
143
+ url: url.href,
144
+ error: e
145
+ });
146
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
143
147
  return null;
144
148
  }
145
149
  throw e;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/webfinger",
3
- "version": "2.2.13",
3
+ "version": "2.2.14",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -60,7 +60,7 @@
60
60
  "@logtape/logtape": "^2.0.5",
61
61
  "@opentelemetry/api": "^1.9.0",
62
62
  "es-toolkit": "1.43.0",
63
- "@fedify/vocab-runtime": "2.2.13"
63
+ "@fedify/vocab-runtime": "2.2.14"
64
64
  },
65
65
  "scripts": {
66
66
  "build:self": "tsdown",
@@ -1,7 +1,10 @@
1
1
  import { test } from "@fedify/fixture";
2
+ import { UrlError } from "@fedify/vocab-runtime";
3
+ import { configure, type LogRecord, reset } from "@logtape/logtape";
2
4
  import { withTimeout } from "es-toolkit";
3
5
  import fetchMock from "fetch-mock";
4
- import { deepStrictEqual } from "node:assert/strict";
6
+ import { deepStrictEqual, ok } from "node:assert/strict";
7
+ import dns from "node:dns/promises";
5
8
  import type { ResourceDescriptor } from "./jrd.ts";
6
9
  import { lookupWebFinger } from "./lookup.ts";
7
10
 
@@ -365,3 +368,67 @@ test("lookupWebFinger() bounds resource descriptors", async () => {
365
368
  fetchMock.hardReset();
366
369
  }
367
370
  });
371
+
372
+ test("lookupWebFinger() logs DNS failures as such", async (t) => {
373
+ // The validator skips DNS when Deno has no network permission. Checked
374
+ // here rather than with top-level await, which the CommonJS build rejects.
375
+ if (
376
+ "Deno" in globalThis &&
377
+ (await Deno.permissions.query({ name: "net" })).state !== "granted"
378
+ ) return;
379
+ for (const result of ["throws", "empty", "private"] as const) {
380
+ await t.step(result, async () => {
381
+ // Stubbing works only because vocab-runtime's url.ts uses the default
382
+ // node:dns/promises import; see the FIXME there.
383
+ const originalLookup = dns.lookup;
384
+ dns.lookup = (() =>
385
+ result === "throws"
386
+ ? Promise.reject(new Error("Resolver unavailable"))
387
+ : Promise.resolve(
388
+ result === "empty" ? [] : [{ address: "127.0.0.1", family: 4 }],
389
+ )) as typeof dns.lookup;
390
+ const records: LogRecord[] = [];
391
+ await configure({
392
+ sinks: {
393
+ buffer: (record) =>
394
+ records.push(record),
395
+ },
396
+ loggers: [
397
+ { category: "fedify", sinks: ["buffer"], lowestLevel: "debug" },
398
+ { category: ["logtape", "meta"], sinks: [] },
399
+ ],
400
+ reset: true,
401
+ });
402
+ try {
403
+ deepStrictEqual(
404
+ await lookupWebFinger("acct:alice@dns-failure.invalid"),
405
+ null,
406
+ );
407
+ const failures = records.filter((r) =>
408
+ r.rawMessage !==
409
+ "Fetching WebFinger resource descriptor from {url}..."
410
+ );
411
+ deepStrictEqual(
412
+ failures.map((r) => [r.level, r.rawMessage]),
413
+ [
414
+ result === "private"
415
+ ? [
416
+ "error",
417
+ "Invalid URL for WebFinger resource descriptor: {error}",
418
+ ]
419
+ : ["debug", "DNS lookup failed for {url}"],
420
+ ],
421
+ );
422
+ const { error } = failures[0].properties;
423
+ ok(error instanceof UrlError);
424
+ deepStrictEqual(
425
+ error.reason,
426
+ result === "private" ? "disallowed" : "dns",
427
+ );
428
+ } finally {
429
+ await reset();
430
+ dns.lookup = originalLookup;
431
+ }
432
+ });
433
+ }
434
+ });
package/src/lookup.ts CHANGED
@@ -139,10 +139,20 @@ async function lookupWebFingerInternal(
139
139
  await validatePublicUrl(url.href);
140
140
  } catch (e) {
141
141
  if (e instanceof UrlError) {
142
- logger.error(
143
- "Invalid URL for WebFinger resource descriptor: {error}",
144
- { error: e },
145
- );
142
+ if (e.reason === "dns") {
143
+ logger.debug("DNS lookup failed for {url}", {
144
+ url: url.href,
145
+ error: e,
146
+ });
147
+ } else {
148
+ logger.error(
149
+ "Invalid URL for WebFinger resource descriptor: {error}",
150
+ { error: e },
151
+ );
152
+ }
153
+ // TODO: When merging forward into 2.3-maintenance and later, where
154
+ // this returns { resource: null, result: "network_error", remoteHost }
155
+ // instead of null, keep that return value for both reasons.
146
156
  return null;
147
157
  }
148
158
  throw e;