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

@ipfy/homoglyph

v1.0.0

Published

Detect visually confusable domain names using Unicode UTS #39.

Readme

Homoglyph

Unicode-aware detection of visually confusable domain names.

Homoglyph is a multi-language, standards-based library for spotting Unicode homoglyph / homograph structure in domain names, using Unicode UTS #39 17.0.0.

paypal.com
раypal.com

Those strings are not the same domain. The second mixes Cyrillic letters that UTS #39 maps as confusable with Latin.

It is for developers who accept domains through email fields, URL fields, registration forms, and similar inputs.

Implementations

One GitHub repository, ipfy/Homoglyph, ships native packages:

| Language | Package | Install | | --- | --- | --- | | JavaScript | npm @ipfy/homoglyph | npm install @ipfy/homoglyph | | Python | PyPI ipfy-homoglyph | pip install ipfy-homoglyph | | PHP | Packagist ipfy/homoglyph | composer require ipfy/homoglyph |

All three implement the same Homoglyph specification (see docs/DESIGN.md and docs/ARCHITECTURE.md), the same Unicode 17.0.0 confusable data, and the shared corpus in conformance/cases/.

JavaScript additionally provides browser warn() / reject() helpers. Python and PHP provide the core detect() API only.

What it is — and is not

The security boundary is the domain. Email local-parts and URL paths, queries, and fragments are not treated as hostname evidence.

| Observation | Meaning | | --- | --- | | Unicode in a domain | Not malicious. IDNs are legitimate. | | Non-Latin | Not malicious. | | Punycode / xn-- | Not malicious. Valid A-labels are decoded, then analyzed. | | Mixed script | Not automatically malicious. | | detected: true | A UTS #39-relevant issue worth reporting | | rejectable: true | High-confidence finding suitable for hard enforcement |

Homoglyph does not decide whether a domain is a phishing site. It is not a blacklist, reputation service, DNS client, or TLD policy engine.

JavaScript (npm)

npm install @ipfy/homoglyph
import Homoglyph from '@ipfy/homoglyph';

const result = Homoglyph.detect('раypal.com');
if (result.detected) {
  console.log(result.reason, result.domain, result.rejectable);
}

CDN (IIFE, globalThis.Homoglyph):

<script src="https://cdn.jsdelivr.net/npm/@ipfy/homoglyph/dist/homoglyph.min.js"></script>

Browser-only helpers: Homoglyph.warn(input) and Homoglyph.reject(input). See below. There is no init().

Python (PyPI)

pip install ipfy-homoglyph
import homoglyph

result = homoglyph.detect("раypal.com")
if result.detected:
    print(result.reason, result.domain, result.rejectable)

PHP (Packagist)

Requires PHP 8.1+ and ext-intl (Unicode NFC/NFD via Normalizer).

composer require ipfy/homoglyph
use IPfy\Homoglyph\Homoglyph;

$result = Homoglyph::detect('раypal.com');
if ($result->detected) {
    echo $result->reason, ' ', $result->domain;
}

Email and URL behavior

user@раypal.com                 → analyze раypal.com
mаnο[email protected]               → domain paypal.com; not rejectable
https://раypal.com/login        → analyze раypal.com
https://example.com/раypal     → path ignored
https://example.com/?q=раypal  → query ignored
https://example.com/#раypal    → fragment ignored
пример.com                     → labels analyzed independently (not mixed-script)

Punycode / IDNA

Valid xn-- A-labels are decoded and then analyzed. Punycode itself is not evidence of malice. Successful Punycode decoding is not full IDNA validation.

Unicode

| Item | Value | | --- | --- | | Unicode / UTS #39 | 17.0.0 | | Source of truth | unicode/ in this repository | | Runtime download | none |

Refresh generated maps with npm run generate (writes JavaScript, Python, and PHP runtime data from the same files).

JavaScript warn() / reject()

These exist only in the JavaScript browser build. They attach form metadata or constraint validation from detect(). Visitor-facing reject() messages are ordinary validation text, not “phishing” warnings.

Development

npm install
npm run generate
npm test
npm run build

cd python && python -m build && pytest
cd php && composer install && vendor/bin/phpunit

License

MIT. Unicode data: Unicode License V3 (see NOTICE).