@fedify/webfinger 2.2.18 → 2.2.20

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/dist/mod.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _opentelemetry_api = require("@opentelemetry/api");
2
3
  let _fedify_vocab_runtime = require("@fedify/vocab-runtime");
4
+ let _fedify_vocab_runtime_internal = require("@fedify/vocab-runtime/internal");
3
5
  let _logtape_logtape = require("@logtape/logtape");
4
- let _opentelemetry_api = require("@opentelemetry/api");
5
6
  //#region deno.json
6
7
  var name = "@fedify/webfinger";
7
- var version = "2.2.18";
8
+ var version = "2.2.20";
8
9
  //#endregion
9
10
  //#region src/body.ts
10
11
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -80,7 +81,7 @@ async function readBoundedText(message, maxBytes, url) {
80
81
  }
81
82
  }
82
83
  //#endregion
83
- //#region src/lookup.ts
84
+ //#region src/lookup-impl.ts
84
85
  const logger = (0, _logtape_logtape.getLogger)([
85
86
  "fedify",
86
87
  "webfinger",
@@ -88,36 +89,16 @@ const logger = (0, _logtape_logtape.getLogger)([
88
89
  ]);
89
90
  const DEFAULT_MAX_REDIRECTION = 5;
90
91
  /**
91
- * Looks up a WebFinger resource.
92
+ * Looks up a WebFinger resource, opening a fetch session per hop. This is
93
+ * the implementation of {@link lookupWebFinger}, which is not exported from
94
+ * the package entry point.
92
95
  * @param resource The resource URL to look up.
93
96
  * @param options Extra options for looking up the resource.
97
+ * @param deps The dependencies.
94
98
  * @returns The resource descriptor, or `null` if not found.
95
- * @since 0.2.0
99
+ * @internal
96
100
  */
97
- async function lookupWebFinger(resource, options = {}) {
98
- return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(name, version).startActiveSpan("webfinger.lookup", {
99
- kind: _opentelemetry_api.SpanKind.CLIENT,
100
- attributes: {
101
- "webfinger.resource": resource.toString(),
102
- "webfinger.resource.scheme": typeof resource === "string" ? resource.replace(/:.*$/, "") : resource.protocol.replace(/:$/, "")
103
- }
104
- }, async (span) => {
105
- try {
106
- const result = await lookupWebFingerInternal(resource, options);
107
- span.setStatus({ code: result === null ? _opentelemetry_api.SpanStatusCode.ERROR : _opentelemetry_api.SpanStatusCode.OK });
108
- return result;
109
- } catch (error) {
110
- span.setStatus({
111
- code: _opentelemetry_api.SpanStatusCode.ERROR,
112
- message: String(error)
113
- });
114
- throw error;
115
- } finally {
116
- span.end();
117
- }
118
- });
119
- }
120
- async function lookupWebFingerInternal(resource, options = {}) {
101
+ async function lookupWebFingerInternal(resource, options = {}, deps = {}) {
121
102
  if (typeof resource === "string") resource = new URL(resource);
122
103
  let protocol = "https:";
123
104
  let server;
@@ -132,76 +113,154 @@ async function lookupWebFingerInternal(resource, options = {}) {
132
113
  }
133
114
  let url = new URL(`${protocol}//${server}/.well-known/webfinger`);
134
115
  url.searchParams.set("resource", resource.href);
116
+ const openHop = deps.openHop ?? ((hopUrl) => options.allowPrivateAddress === true ? (0, _fedify_vocab_runtime_internal.openDirectFetch)(hopUrl) : (0, _fedify_vocab_runtime_internal.openPublicFetch)(hopUrl, { signal: options.signal }));
135
117
  let redirected = 0;
136
118
  while (true) {
137
119
  logger.debug("Fetching WebFinger resource descriptor from {url}...", { url: url.href });
138
- let response;
139
- if (options.allowPrivateAddress !== true) try {
140
- await (0, _fedify_vocab_runtime.validatePublicUrl)(url.href);
141
- } catch (e) {
142
- if (e instanceof _fedify_vocab_runtime.UrlError) {
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 });
148
- return null;
149
- }
150
- throw e;
151
- }
120
+ const session = await openSession(url, openHop, options.signal);
121
+ if (session == null) return null;
122
+ let hop;
152
123
  try {
153
- response = await fetch(url, {
154
- headers: {
155
- Accept: "application/jrd+json",
156
- "User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
157
- },
158
- redirect: "manual",
159
- signal: options.signal
124
+ hop = await fetchHop(session, url, options);
125
+ } finally {
126
+ await session.close();
127
+ }
128
+ if ("result" in hop) return hop.result;
129
+ redirected++;
130
+ const maxRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
131
+ if (redirected >= maxRedirection) {
132
+ logger.error("Too many redirections ({redirections}) while fetching WebFinger resource descriptor.", { redirections: redirected });
133
+ return null;
134
+ }
135
+ const redirectedUrl = new URL(hop.redirect, url);
136
+ if (redirectedUrl.protocol !== url.protocol) {
137
+ logger.error("Redirected to a different protocol ({protocol} to {redirectedProtocol}) while fetching WebFinger resource descriptor.", {
138
+ protocol: url.protocol,
139
+ redirectedProtocol: redirectedUrl.protocol
160
140
  });
161
- } catch (error) {
162
- logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
141
+ return null;
142
+ }
143
+ url = redirectedUrl;
144
+ }
145
+ }
146
+ /**
147
+ * Opens a fetch session for a hop. A disallowed URL, a failed DNS lookup,
148
+ * or an aborted lookup yields `null` instead of an error.
149
+ */
150
+ async function openSession(url, openHop, signal) {
151
+ try {
152
+ return await openHop(url.href);
153
+ } catch (e) {
154
+ if (e instanceof _fedify_vocab_runtime.UrlError) {
155
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
163
156
  url: url.href,
164
- error
157
+ error: e
165
158
  });
159
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
166
160
  return null;
167
161
  }
168
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
169
- redirected++;
170
- const maxRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
171
- if (redirected >= maxRedirection) {
172
- logger.error("Too many redirections ({redirections}) while fetching WebFinger resource descriptor.", { redirections: redirected });
173
- return null;
174
- }
175
- const redirectedUrl = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? url : response.url);
176
- if (redirectedUrl.protocol !== url.protocol) {
177
- logger.error("Redirected to a different protocol ({protocol} to {redirectedProtocol}) while fetching WebFinger resource descriptor.", {
178
- protocol: url.protocol,
179
- redirectedProtocol: redirectedUrl.protocol
180
- });
181
- return null;
182
- }
183
- url = redirectedUrl;
184
- continue;
185
- }
186
- if (!response.ok) {
187
- logger.debug("Failed to fetch WebFinger resource descriptor: {status} {statusText}.", {
162
+ if (signal?.aborted) {
163
+ logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
188
164
  url: url.href,
189
- status: response.status,
190
- statusText: response.statusText
165
+ error: e
191
166
  });
192
167
  return null;
193
168
  }
194
- try {
195
- return JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, url));
196
- } catch (e) {
197
- if (e instanceof BodyTooLargeError) return null;
198
- if (e instanceof SyntaxError) {
199
- logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
200
- return null;
201
- }
202
- throw e;
169
+ throw e;
170
+ }
171
+ }
172
+ /**
173
+ * Fetches a hop with the given session, and consumes the response body or
174
+ * starts cancelling it before returning. The caller closes the session.
175
+ */
176
+ async function fetchHop(session, url, options) {
177
+ let response;
178
+ try {
179
+ response = await session.fetch(url, {
180
+ headers: {
181
+ Accept: "application/jrd+json",
182
+ "User-Agent": typeof options.userAgent === "string" ? options.userAgent : (0, _fedify_vocab_runtime.getUserAgent)(options.userAgent)
183
+ },
184
+ redirect: "manual",
185
+ signal: options.signal
186
+ });
187
+ } catch (error) {
188
+ logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
189
+ url: url.href,
190
+ error
191
+ });
192
+ return { result: null };
193
+ }
194
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
195
+ cancelBody(response, url);
196
+ return { redirect: response.headers.get("Location") };
197
+ }
198
+ if (!response.ok) {
199
+ logger.debug("Failed to fetch WebFinger resource descriptor: {status} {statusText}.", {
200
+ url: url.href,
201
+ status: response.status,
202
+ statusText: response.statusText
203
+ });
204
+ cancelBody(response, url);
205
+ return { result: null };
206
+ }
207
+ try {
208
+ return { result: JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, url)) };
209
+ } catch (e) {
210
+ if (e instanceof BodyTooLargeError) return { result: null };
211
+ if (e instanceof SyntaxError) {
212
+ logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
213
+ return { result: null };
203
214
  }
215
+ throw e;
204
216
  }
205
217
  }
218
+ /**
219
+ * Starts cancelling a response body that is not read, without waiting for it
220
+ * to settle. The cancellation of a branch of a tee (e.g., a body cloned by
221
+ * a fetch mock) settles only after the other branch is cancelled too, per
222
+ * the Streams standard, so awaiting it could block forever. A failure is
223
+ * only logged.
224
+ */
225
+ function cancelBody(response, url) {
226
+ response.body?.cancel().catch((error) => {
227
+ logger.debug("Failed to cancel the response body from {url}: {error}", {
228
+ url: url.href,
229
+ error
230
+ });
231
+ });
232
+ }
233
+ //#endregion
234
+ //#region src/lookup.ts
235
+ /**
236
+ * Looks up a WebFinger resource.
237
+ * @param resource The resource URL to look up.
238
+ * @param options Extra options for looking up the resource.
239
+ * @returns The resource descriptor, or `null` if not found.
240
+ * @since 0.2.0
241
+ */
242
+ async function lookupWebFinger(resource, options = {}) {
243
+ return await (options.tracerProvider ?? _opentelemetry_api.trace.getTracerProvider()).getTracer(name, version).startActiveSpan("webfinger.lookup", {
244
+ kind: _opentelemetry_api.SpanKind.CLIENT,
245
+ attributes: {
246
+ "webfinger.resource": resource.toString(),
247
+ "webfinger.resource.scheme": typeof resource === "string" ? resource.replace(/:.*$/, "") : resource.protocol.replace(/:$/, "")
248
+ }
249
+ }, async (span) => {
250
+ try {
251
+ const result = await lookupWebFingerInternal(resource, options);
252
+ span.setStatus({ code: result === null ? _opentelemetry_api.SpanStatusCode.ERROR : _opentelemetry_api.SpanStatusCode.OK });
253
+ return result;
254
+ } catch (error) {
255
+ span.setStatus({
256
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
257
+ message: String(error)
258
+ });
259
+ throw error;
260
+ } finally {
261
+ span.end();
262
+ }
263
+ });
264
+ }
206
265
  //#endregion
207
266
  exports.lookupWebFinger = lookupWebFinger;
package/dist/mod.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { GetUserAgentOptions } from "@fedify/vocab-runtime";
2
1
  import { TracerProvider } from "@opentelemetry/api";
2
+ import { GetUserAgentOptions } from "@fedify/vocab-runtime";
3
3
 
4
4
  //#region src/jrd.d.ts
5
5
  /**
package/dist/mod.js CHANGED
@@ -1,9 +1,10 @@
1
- import { FetchError, UrlError, getUserAgent, validatePublicUrl } from "@fedify/vocab-runtime";
2
- import { getLogger } from "@logtape/logtape";
3
1
  import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
2
+ import { FetchError, UrlError, getUserAgent } from "@fedify/vocab-runtime";
3
+ import { openDirectFetch, openPublicFetch } from "@fedify/vocab-runtime/internal";
4
+ import { getLogger } from "@logtape/logtape";
4
5
  //#region deno.json
5
6
  var name = "@fedify/webfinger";
6
- var version = "2.2.18";
7
+ var version = "2.2.20";
7
8
  //#endregion
8
9
  //#region src/body.ts
9
10
  /** Decoded JSON body limit: 16 MiB. @internal */
@@ -79,7 +80,7 @@ async function readBoundedText(message, maxBytes, url) {
79
80
  }
80
81
  }
81
82
  //#endregion
82
- //#region src/lookup.ts
83
+ //#region src/lookup-impl.ts
83
84
  const logger = getLogger([
84
85
  "fedify",
85
86
  "webfinger",
@@ -87,36 +88,16 @@ const logger = getLogger([
87
88
  ]);
88
89
  const DEFAULT_MAX_REDIRECTION = 5;
89
90
  /**
90
- * Looks up a WebFinger resource.
91
+ * Looks up a WebFinger resource, opening a fetch session per hop. This is
92
+ * the implementation of {@link lookupWebFinger}, which is not exported from
93
+ * the package entry point.
91
94
  * @param resource The resource URL to look up.
92
95
  * @param options Extra options for looking up the resource.
96
+ * @param deps The dependencies.
93
97
  * @returns The resource descriptor, or `null` if not found.
94
- * @since 0.2.0
98
+ * @internal
95
99
  */
96
- async function lookupWebFinger(resource, options = {}) {
97
- return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("webfinger.lookup", {
98
- kind: SpanKind.CLIENT,
99
- attributes: {
100
- "webfinger.resource": resource.toString(),
101
- "webfinger.resource.scheme": typeof resource === "string" ? resource.replace(/:.*$/, "") : resource.protocol.replace(/:$/, "")
102
- }
103
- }, async (span) => {
104
- try {
105
- const result = await lookupWebFingerInternal(resource, options);
106
- span.setStatus({ code: result === null ? SpanStatusCode.ERROR : SpanStatusCode.OK });
107
- return result;
108
- } catch (error) {
109
- span.setStatus({
110
- code: SpanStatusCode.ERROR,
111
- message: String(error)
112
- });
113
- throw error;
114
- } finally {
115
- span.end();
116
- }
117
- });
118
- }
119
- async function lookupWebFingerInternal(resource, options = {}) {
100
+ async function lookupWebFingerInternal(resource, options = {}, deps = {}) {
120
101
  if (typeof resource === "string") resource = new URL(resource);
121
102
  let protocol = "https:";
122
103
  let server;
@@ -131,76 +112,154 @@ async function lookupWebFingerInternal(resource, options = {}) {
131
112
  }
132
113
  let url = new URL(`${protocol}//${server}/.well-known/webfinger`);
133
114
  url.searchParams.set("resource", resource.href);
115
+ const openHop = deps.openHop ?? ((hopUrl) => options.allowPrivateAddress === true ? openDirectFetch(hopUrl) : openPublicFetch(hopUrl, { signal: options.signal }));
134
116
  let redirected = 0;
135
117
  while (true) {
136
118
  logger.debug("Fetching WebFinger resource descriptor from {url}...", { url: url.href });
137
- let response;
138
- if (options.allowPrivateAddress !== true) try {
139
- await validatePublicUrl(url.href);
140
- } catch (e) {
141
- if (e instanceof UrlError) {
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 });
147
- return null;
148
- }
149
- throw e;
150
- }
119
+ const session = await openSession(url, openHop, options.signal);
120
+ if (session == null) return null;
121
+ let hop;
151
122
  try {
152
- response = await fetch(url, {
153
- headers: {
154
- Accept: "application/jrd+json",
155
- "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
156
- },
157
- redirect: "manual",
158
- signal: options.signal
123
+ hop = await fetchHop(session, url, options);
124
+ } finally {
125
+ await session.close();
126
+ }
127
+ if ("result" in hop) return hop.result;
128
+ redirected++;
129
+ const maxRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
130
+ if (redirected >= maxRedirection) {
131
+ logger.error("Too many redirections ({redirections}) while fetching WebFinger resource descriptor.", { redirections: redirected });
132
+ return null;
133
+ }
134
+ const redirectedUrl = new URL(hop.redirect, url);
135
+ if (redirectedUrl.protocol !== url.protocol) {
136
+ logger.error("Redirected to a different protocol ({protocol} to {redirectedProtocol}) while fetching WebFinger resource descriptor.", {
137
+ protocol: url.protocol,
138
+ redirectedProtocol: redirectedUrl.protocol
159
139
  });
160
- } catch (error) {
161
- logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
140
+ return null;
141
+ }
142
+ url = redirectedUrl;
143
+ }
144
+ }
145
+ /**
146
+ * Opens a fetch session for a hop. A disallowed URL, a failed DNS lookup,
147
+ * or an aborted lookup yields `null` instead of an error.
148
+ */
149
+ async function openSession(url, openHop, signal) {
150
+ try {
151
+ return await openHop(url.href);
152
+ } catch (e) {
153
+ if (e instanceof UrlError) {
154
+ if (e.reason === "dns") logger.debug("DNS lookup failed for {url}", {
162
155
  url: url.href,
163
- error
156
+ error: e
164
157
  });
158
+ else logger.error("Invalid URL for WebFinger resource descriptor: {error}", { error: e });
165
159
  return null;
166
160
  }
167
- if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
168
- redirected++;
169
- const maxRedirection = options.maxRedirection ?? DEFAULT_MAX_REDIRECTION;
170
- if (redirected >= maxRedirection) {
171
- logger.error("Too many redirections ({redirections}) while fetching WebFinger resource descriptor.", { redirections: redirected });
172
- return null;
173
- }
174
- const redirectedUrl = new URL(response.headers.get("Location"), response.url == null || response.url === "" ? url : response.url);
175
- if (redirectedUrl.protocol !== url.protocol) {
176
- logger.error("Redirected to a different protocol ({protocol} to {redirectedProtocol}) while fetching WebFinger resource descriptor.", {
177
- protocol: url.protocol,
178
- redirectedProtocol: redirectedUrl.protocol
179
- });
180
- return null;
181
- }
182
- url = redirectedUrl;
183
- continue;
184
- }
185
- if (!response.ok) {
186
- logger.debug("Failed to fetch WebFinger resource descriptor: {status} {statusText}.", {
161
+ if (signal?.aborted) {
162
+ logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
187
163
  url: url.href,
188
- status: response.status,
189
- statusText: response.statusText
164
+ error: e
190
165
  });
191
166
  return null;
192
167
  }
193
- try {
194
- return JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, url));
195
- } catch (e) {
196
- if (e instanceof BodyTooLargeError) return null;
197
- if (e instanceof SyntaxError) {
198
- logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
199
- return null;
200
- }
201
- throw e;
168
+ throw e;
169
+ }
170
+ }
171
+ /**
172
+ * Fetches a hop with the given session, and consumes the response body or
173
+ * starts cancelling it before returning. The caller closes the session.
174
+ */
175
+ async function fetchHop(session, url, options) {
176
+ let response;
177
+ try {
178
+ response = await session.fetch(url, {
179
+ headers: {
180
+ Accept: "application/jrd+json",
181
+ "User-Agent": typeof options.userAgent === "string" ? options.userAgent : getUserAgent(options.userAgent)
182
+ },
183
+ redirect: "manual",
184
+ signal: options.signal
185
+ });
186
+ } catch (error) {
187
+ logger.debug("Failed to fetch WebFinger resource descriptor: {error}", {
188
+ url: url.href,
189
+ error
190
+ });
191
+ return { result: null };
192
+ }
193
+ if (response.status >= 300 && response.status < 400 && response.headers.has("Location")) {
194
+ cancelBody(response, url);
195
+ return { redirect: response.headers.get("Location") };
196
+ }
197
+ if (!response.ok) {
198
+ logger.debug("Failed to fetch WebFinger resource descriptor: {status} {statusText}.", {
199
+ url: url.href,
200
+ status: response.status,
201
+ statusText: response.statusText
202
+ });
203
+ cancelBody(response, url);
204
+ return { result: null };
205
+ }
206
+ try {
207
+ return { result: JSON.parse(await readBoundedText(response, MAX_BODY_SIZE, url)) };
208
+ } catch (e) {
209
+ if (e instanceof BodyTooLargeError) return { result: null };
210
+ if (e instanceof SyntaxError) {
211
+ logger.debug("Failed to parse WebFinger resource descriptor as JSON: {error}", { error: e });
212
+ return { result: null };
202
213
  }
214
+ throw e;
203
215
  }
204
216
  }
217
+ /**
218
+ * Starts cancelling a response body that is not read, without waiting for it
219
+ * to settle. The cancellation of a branch of a tee (e.g., a body cloned by
220
+ * a fetch mock) settles only after the other branch is cancelled too, per
221
+ * the Streams standard, so awaiting it could block forever. A failure is
222
+ * only logged.
223
+ */
224
+ function cancelBody(response, url) {
225
+ response.body?.cancel().catch((error) => {
226
+ logger.debug("Failed to cancel the response body from {url}: {error}", {
227
+ url: url.href,
228
+ error
229
+ });
230
+ });
231
+ }
232
+ //#endregion
233
+ //#region src/lookup.ts
234
+ /**
235
+ * Looks up a WebFinger resource.
236
+ * @param resource The resource URL to look up.
237
+ * @param options Extra options for looking up the resource.
238
+ * @returns The resource descriptor, or `null` if not found.
239
+ * @since 0.2.0
240
+ */
241
+ async function lookupWebFinger(resource, options = {}) {
242
+ return await (options.tracerProvider ?? trace.getTracerProvider()).getTracer(name, version).startActiveSpan("webfinger.lookup", {
243
+ kind: SpanKind.CLIENT,
244
+ attributes: {
245
+ "webfinger.resource": resource.toString(),
246
+ "webfinger.resource.scheme": typeof resource === "string" ? resource.replace(/:.*$/, "") : resource.protocol.replace(/:$/, "")
247
+ }
248
+ }, async (span) => {
249
+ try {
250
+ const result = await lookupWebFingerInternal(resource, options);
251
+ span.setStatus({ code: result === null ? SpanStatusCode.ERROR : SpanStatusCode.OK });
252
+ return result;
253
+ } catch (error) {
254
+ span.setStatus({
255
+ code: SpanStatusCode.ERROR,
256
+ message: String(error)
257
+ });
258
+ throw error;
259
+ } finally {
260
+ span.end();
261
+ }
262
+ });
263
+ }
205
264
  //#endregion
206
265
  export { lookupWebFinger };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fedify/webfinger",
3
- "version": "2.2.18",
3
+ "version": "2.2.20",
4
4
  "homepage": "https://fedify.dev/",
5
5
  "repository": {
6
6
  "type": "git",
@@ -58,9 +58,9 @@
58
58
  },
59
59
  "dependencies": {
60
60
  "@logtape/logtape": "^2.0.5",
61
- "@opentelemetry/api": "^1.9.0",
61
+ "@opentelemetry/api": "~1.9.0",
62
62
  "es-toolkit": "1.43.0",
63
- "@fedify/vocab-runtime": "2.2.18"
63
+ "@fedify/vocab-runtime": "2.2.20"
64
64
  },
65
65
  "scripts": {
66
66
  "build:self": "tsdown",