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

@extenshi/cli

v0.30.0

Published

Pre-publish security scanner for browser extensions

Readme

@extenshi/cli

See the CLI documentation for scanning, project setup and publishing.

Localizing an extension

For an unpacked extension with literal English text, localize init ./extension --output ./extension-localized creates a new copy with localized manifest fields and static HTML text/display attributes (CLI 0.27.0+). The output must be new and outside the input, with an existing parent directory. A packaged external extenshi-i18n.js applies messages without replacing markup. JavaScript, form submission values and unsupported templates/CSP/encoding/sandbox pages remain unchanged and are reported in LOCALIZATION-REPORT.md. ZIP and JS/AST extraction are not included.

The free localize workflow operates on an unpacked, internationalized extension directory. It requires manifest.json with default_locale and _locales/<default_locale>/messages.json. It prepares requests for your own translator or coding agent; it does not call a translation service. The normal CLI telemetry preference still applies.

extenshi localize prepare ./extension --lang fr,de,es --output ./translation-request
# Translate messages in translation-request/localization-request.json.
# Review every requested message and add "reviewed": true to each locale object.
extenshi localize apply ./extension --translations ./translation-request/localization-request.json
extenshi localize check ./extension

Optional localization.json in the extension root:

{
  "version": 1,
  "sourceLocale": "en",
  "targetLocales": ["fr", "de", "es"],
  "protectedTerms": ["Acme"]
}

With this configuration, --lang is optional. All three commands accept --protect Acme,Product to add exact terms that must remain when they occur in the original message. Prepared requests include the configured terms. Configure durable terms in localization.json, or repeat --protect when applying/checking.

A reviewed request has this shape (the actual source hashes come from prepare):

{
  "version": 1,
  "sourceLocale": "en",
  "locales": {
    "fr": {
      "reviewed": true,
      "sourceHashes": { "greeting": "sha256-from-prepare" },
      "messages": { "greeting": { "message": "Bonjour" } }
    }
  }
}

Only missing, untracked, source-changed or target-edited keys appear in the request. A locale may also include removeKeys for keys deleted from the source; review these removals before setting reviewed: true. Apply removes them from the target catalog and tracking state, and refuses removal if the key has returned in the source. Existing translations remain intact when other keys are applied. .extenshi-localization.json records source and reviewed target hashes only after successful apply; keep it in source control. Direct target edits require another review. Older source-only state also requires re-review before check passes. An unchanged second prepare contains no locale requests. Use a fresh output directory each time: prepare refuses to overwrite translator work.

Apply requires a reviewed bundle and rejects unknown keys, empty messages, changed placeholders, changed positional substitutions or dollar escapes, missing protected terms, stale source hashes, invalid locale paths and symlinked workspace files. It validates all requested locales before writing and checks localized manifest lengths plus new findings from the existing listing risk rules against the default locale. Manifest checks resolve case-insensitive message names, static named placeholders and dollar escapes before measuring visible text. Manifest strings that require runtime positional arguments are rejected; use static content there. Checks are heuristic and cannot certify translation accuracy or store acceptance. Complete translations are required for each applied locale; sparse browser fallback catalogs need their missing keys translated first.

check prints JSON and exits with status 1 for missing, stale, unreviewed or invalid translations, including target locales configured but not created. A project that keeps its own translations (for example a script that writes every messages.json) has no .extenshi-localization.json, so nothing there was ever reviewed through prepare/apply. Without that file check runs in parity-only mode, or run check ./extension --parity-only yourself: it compares every locale with the default locale for missing or extra keys, changed $NAME$, $1 and $$ tokens and placeholders definitions, empty or malformed messages, reserved @@ names, case-only duplicates, a missing default_locale directory and locale directories Chrome ignores, plus the same listing-risk and manifest length findings review mode reports; it ignores review state. See the localization guide for the rule list. The JSON reports "mode" and, when the mode was chosen automatically, a note. This workflow covers extension message catalogs, including localized manifest text. Full store descriptions and screenshots need separate localization. RTL text is accepted for supported locales, but visual direction, overflow and interaction need testing in the target browser.

Standalone Pay

extenshi pay works without a local repository, project binding, or Extenshi development project. It requires a backend with standalone Pay support and explicit Pay permissions on your API key, granted at API keys. Existing keys have no Pay access by default.

extenshi pay list
extenshi pay create --input '{"name":"My extension"}' --apply
extenshi pay readiness --input '{"appId":"<application-uuid>"}'
extenshi pay connect --input '{"appId":"<application-uuid>"}' --apply
extenshi pay seller --input '{"appId":"<application-uuid>"}'
extenshi pay profile --input-file seller-profile.json --apply
extenshi pay offer-upsert --input-file offer.json --apply
extenshi pay enable --input '{"appId":"<application-uuid>","enabled":true}' --apply
extenshi pay export --input '{"appId":"<application-uuid>","resource":"payments"}' --output payments.jsonl

Every mutation requires --apply; this is an explicit operation, not a dry-run endpoint. connect returns a URL for the author to complete Stripe KYC. Agreements must be accepted in the browser. Readiness and actual SDK registry availability must be checked before launch. The CLI never accepts agreements or creates platform-owned sellers.

Use link with { "appId": "...", "projectId": "..." } to attach an optional development project, and unlink to remove the link. archive preserves existing payment data. offer-archive takes appId and sku. rotate-key generates a new publishable SDK key; prior public keys remain accepted for installed builds. This is not secret revocation. Do not blindly retry create, connect, or rotation after a timeout.

Profile JSON: { "appId": "...", "profile": { "displayName": "Author-confirmed name", "supportUrl": "https://example.com/support", "termsUrl": "https://example.com/terms" } }. Offer JSON: { "appId": "...", "sku": "pro", "kind": "subscription", "interval": "month", "amountMinor": 499, "currency": "usd", "title": "Pro", "features": ["premium"] }. A one-time or lifetime offer uses interval: null.

extenshi pay payment-evidence --input '{"appId":"..."}' prints what Extenshi's payment ledger proves about the application, per mode: checkout completed, license issued, an installation received the signed license, restored on a second installation, refund revoked. It is derived from provider-signed webhooks, carries opaque IDs and timestamps only, and is the same evidence extenshi release status uses for the payment check of a linked project. Only a live-mode purchase satisfies a release's payment requirement.

extenshi pay test-cycle --app <appId> [--sku pro] [--refund-key-env NAME] runs one Stripe TEST-mode purchase against an application whose seller is in test mode and prints that evidence. It refuses a live seller, a non-test Checkout session and a non-test refund key. The CLI acts as the installation: it opens a checkout, prints the hosted URL for payment with card 4242 4242 4242 4242, waits for the webhook, verifies the signed license, optionally refunds with the Stripe TEST secret named by --refund-key-env and checks the signed revocation. Test-mode purchases are recorded and shown but do not satisfy a release's payment requirement.

Without --output, export returns one page with nextCursor; pass that cursor to retrieve the next page. With --output, all pages are streamed to a new private (0600) JSONL file; existing files are never overwritten, and failures remove incomplete files. Resources: offers, payments, subscriptions, entitlements, customers. Keep exports private. Only the publishable SDK key belongs in extension code.