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

@smartput/currency

v0.2.0

Published

Currency recognition and formatting, with no rate table.

Readme

@smartput/currency

What a currency is, with nothing about what it is worth.

import { Currency } from "@smartput/currency/class";

const usd = Currency.for("dollars");
usd?.code;                        // "usd"
usd?.parse("1,250.50 usd");       // { ok: true, amount: 1250.5, raw: "1250.50", currency: "usd" }
usd?.format(new Decimal("30"));   // "$30.00"

The table, the vocabulary, the parser and the formatter. @smartput/rate holds the money kind, the snapshots and the providers, and depends on this package for the half of money that a date cannot change.

Why it is a package

Every ratio kind ships a micro path — parseLength, parseMass — and money could not, because UnitTable needs a ratio per unit and money's ratios are a live rate table. That exemption is right about conversion and wrong about everything else. Which currency a word names, how much "30 usd" is, and how to write it back are all answerable without a rate, a date or a registry, and a form field validating an amount should not have to link a provider to do it.

So this package parses and formats, and it cannot convert. The moment you want 30 usd in gbp you want a snapshot, and that is @smartput/rate.

Three doors

| Import | What you get | | --- | --- | | @smartput/currency | CURRENCIES, currencyLexicon() — what a money kind is built out of | | @smartput/currency/validate | parseCurrency, parseAmount, symbolOf, minorUnitsOf — 2.6 KB, no Decimal | | @smartput/currency/class | Currency, the two above with a code already in hand |

formatAmount is deliberately not on the /validate path. It needs a Decimal, core configures decimal.js's precision in a module-load side effect, and a side effect is the one thing a bundler may not drop — one re-export of it takes that entry from 2.6 KB to 35 KB. symbolOf and minorUnitsOf are the facts you need to render an amount yourself; check-size holds the line.

It agrees with the engine

parseAmount reads what evaluate reads and refuses what it refuses: "usd 30" fails on both, because a unit is written after its quantity. parseCurrency knows the aliases the kind registers and not quid, which arrives with @smartput/rate/locale/en — a pack is something a consumer chose, and a parser that knew a word the engine had not been given would be the more confusing of the two disagreements.

The one deliberate divergence is opt-in. evaluate("$30") is the number 30, so symbols are off by default here too; parseAmount("$30", { symbols: true }) reads them, because a form field has no number kind to lose the token to.

packages/rate/src/currency-agreement.test.ts asserts all of that from the side of the boundary that can see both.