npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

webfinger

v0.7.0

Published

Modern, zero-dependency WebFinger client (RFC 7033) for Node.js — promise-based JRD discovery for acct: URIs and URLs, built for ActivityPub

Readme

Webfinger

Webfinger client library for Node.js and browsers.

It supports RFC 7033.

Table of Contents

Security

This library does not provide protection against server-side request forgery (SSRF) out of the box. Its default transport is global fetch, which can connect to private or internal network addresses, including through redirects.

When discovering user-supplied addresses, use options.fetch to supply a transport that enforces your network policy, including checks on resolved IP addresses and redirect destinations. The example below shows how to supply an SSRF-protecting fetch implementation.

Install

For Node.js usage, requires Node.js 22.x, 24.x, or 26.x. For browser requirements and loading instructions, see Browser usage.

npm install webfinger

Usage

Import the named webfinger export and await discovery:

import { webfinger } from 'webfinger'

const jrd = await webfinger('[email protected]')
console.log(jrd.subject)
console.log(jrd.links)

See API for relation filtering, custom fetch functions, and JRD link selection. For user-supplied addresses, review Security.

Browser usage

The library uses native ES modules and the same promise-based webfinger() API in browsers. With a bundler, use the package import shown above. To load it directly from npm through jsDelivr, use a version-pinned URL in a module script:

<script type="module">
  import { webfinger } from 'https://cdn.jsdelivr.net/npm/[email protected]/lib/webfinger.js'

  const jrd = await webfinger('[email protected]')
  console.log(jrd.subject)
  console.log(jrd.links)
</script>

This example targets the 0.7.0 release and requires that version to be published to npm. Replace [email protected] with an account whose WebFinger endpoint allows requests from your site's origin. A bare webfinger import needs a bundler or an import map to resolve in a browser.

Browsers must support ES modules, private class fields, fetch, and URLSearchParams. Discovery for HTTP and HTTPS resource URLs also requires URL.parse(). Browser tests currently run in Chromium; Firefox and Safari have not been verified.

For cross-origin discovery, the remote WebFinger endpoint must return CORS headers allowing your site's origin, such as Access-Control-Allow-Origin. Without that permission, browser fetch cannot read the response and discovery rejects. Supplying options.fetch does not bypass browser CORS restrictions.

API

The webfinger() function returns a promise. Await the result; errors reject the promise. This replaces the previous callback API.

webfinger(address)

Resolves to a JRD instance containing discovery data for address.

The address argument accepts an acct: URI, a bare account identifier such as [email protected], or a URL with a hostname, such as an http: or https: URL. Bare account identifiers are prefixed with acct: before discovery.

Discovery requests https://<hostname>/.well-known/webfinger with the resource in the query string. The response must have status 200 and contain JSON. XRD conversion and host-meta/LRDD fallback are not supported.

webfinger(address, options)

The optional second argument is an object with rel and fetch properties. The previous positional rel argument and three-argument signature are no longer supported. Move the relation into options.rel and pass any custom fetch function in the same object.

options.rel

Supply a relation string or an array of relation strings to filter discovery results. An array sends repeated rel query parameters. Omitting rel, or passing undefined, null, an empty string, or an empty array, sends no relation filter.

const jrd = await webfinger('[email protected]', { rel: ['self', 'profile'] })

Servers may return additional links. Use the returned JRD's link() method or filter its links array to select the links you need.

options.fetch

Supply a custom fetch function for the discovery request. When the fetch property is absent, the module uses global fetch.

The function receives the discovery URL and a request-options object containing the Accept header. It should return a promise for a fetch-compatible response with a numeric status and an asynchronous json() method. The response must have status 200; its JSON is parsed into the returned JRD.

const jrd = await webfinger('[email protected]', { fetch: customFetch })

To also filter by relation, include rel in the same options object. Supply a callable function. Explicit values such as undefined or null do not select the default and cause the lookup to reject. If you pass an object method that depends on this, bind it to its instance first. Errors from the custom fetch function reject the lookup promise.

For example, after installing the optional guarded-fetch package in your application, you can supply its fetch-compatible function:

import { guardedFetch } from 'guarded-fetch'
import { webfinger } from 'webfinger'

const jrd = await webfinger('[email protected]', {
  fetch: guardedFetch
})
console.log(jrd.subject)

guarded-fetch checks destination IP addresses and redirects to help prevent SSRF. This is an integration example; guarded-fetch is not a dependency of this library. Its bare guardedFetch function does not limit response-body size; consult its documentation when choosing resource limits for your application.

The former httpsOnly and webfingerOnly options have no effect.

JRD

JRD represents a JSON Resource Descriptor returned by webfinger(). Obtain instances by awaiting webfinger(); the class is internal and is not exported for direct construction.

The class provides synchronous access to the document and its links. Reading its properties or selecting a link does not make a network request.

In general, malformed or mistyped response properties are ignored and filtered out. Missing or invalid top-level fields retain their defaults: subject is undefined, aliases and links are empty arrays, and properties is an empty object. A JSON response of null or another non-object value, including an array, produces an empty JRD.

Link titles and properties must be non-null, non-array objects. Invalid values are omitted, so reading those fields returns undefined; the rest of the link remains available. This handling applies to parsed response data. Network errors, non-200 responses, and invalid JSON syntax still reject the lookup promise.

Properties

All four properties are getter-only:

  • subject: the subject identifier from the document, or undefined when absent or mistyped.
  • aliases: a frozen array of alternative identifiers, defaulting to an empty array when absent or mistyped. Access an individual alias with jrd.aliases[i].
  • properties: a frozen object mapping property URI keys to string or null values, defaulting to an empty object when the field is absent or mistyped. Explicit null values within a valid properties object are preserved.
  • links: a frozen array of link objects in document order, defaulting to an empty array when absent or mistyped. Each link object and its titles and properties objects, when present and valid, are also frozen. Links retain server-supplied fields except metadata filtered out by validation.

The collections and standard link metadata are read-only. Attempting to change frozen contents or assign to these getter-only properties throws a TypeError in strict mode.

link(rel, type?)

Selects a link from the returned document by relation and, optionally, media type. This is local selection, independent of options.rel passed to webfinger() during discovery.

Returns the first link in document order whose rel exactly matches the requested relation and whose media type satisfies the optional filter:

  • Omit type or pass null to accept any media type, including a missing type.
  • Pass a nonempty string to require an exact type match.
  • Pass an array of strings to accept any listed type. Array order does not establish a preference; document order determines the first match.
  • Pass an empty array to match nothing.

Media type matching compares the complete string, including any parameters; it does not normalize media types.

The result is the same frozen link object exposed in links. Read its href property for the target URI. No match returns undefined. Callers needing multiple matches can filter the links array.

Example: finding an ActivityPub actor

With webfinger imported from this package, use the following expression inside an async function to find an account's ActivityPub actor URI:

(await webfinger('[email protected]')).link('self', ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'])?.href

This returns the first matching link's href, or undefined if no matching link has a target URI. Discovery errors still reject the promise.

Contributing

Questions, bug reports, and pull requests are welcome through the GitHub repository. Use the issue tracker for questions and bugs. Please follow the Code of Conduct and run lint and tests before submitting a pull request.

Testing

Use Node.js 22, 24, or 26 to run the tests with the built-in Node Test Runner. Nock intercepts HTTP and HTTPS requests; the tests call the real webfinger() function. No servers, certificates, network access, or elevated privileges are needed.

npm ci
npm run lint
npm test

Run the browser tests in headless Chromium with Playwright:

npx playwright install chromium
npm run test:browser

The browser tests import the library as a native ES module and exercise browser fetch with intercepted responses, including cross-origin discovery with CORS headers and repeated relation parameters. Installing Chromium requires network access; the tests themselves do not contact external services. CI runs both the Node.js and browser suites before publishing.

License

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

See LICENSE.md for the full license text.