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

@wentools/money

v0.1.2

Published

TypeScript money handling library

Readme

Money Library

Type-safe money handling for financial applications. No more floating-point errors.

Why You Need This

Working with money in JavaScript is dangerous:

// ❌ JavaScript's problems with money
0.1 + 0.2 // 0.30000000000000004 (precision error!)
const price = 10.5 // 10.5 what? Kronor? Ören? Ambiguous!
const total = price + '50' // "10.550" (silent string concatenation!)

This library makes money handling safe and precise:

// ✅ With this library
const sek = money.for(SEK)
const price = sek.create.fromMinor(1050) // Explicitly 1050 ören = 10.50 kr
const total = sek.calculate.add(price, tax) // Precise calculation, no float errors
sek.format.standard(total) // "12,50 kr" - properly formatted

Quick Start

import { money } from '@lib/money'
import { SEK } from '@lib/money/config/registry'

// Create a money API for your currency
const sek = money.for(SEK)

// Create money values
const price = sek.create.fromMinor(9999) // 99.99 kr (from ören)
const salary = sek.create.fromMajor(25000) // 25,000 kr (from kronor)

// Safe calculations - no floating-point errors!
const total = sek.calculate.add(price, salary)
if (total.isOk()) {
  console.log(sek.format.standard(total.value)) // "25 099,99 kr"
}

// Format for display
sek.format.standard(price) // "99,99 kr"
sek.format.accounting(price) // "SEK 99.99"
sek.format.input(price) // "99.99" (for input fields)

Core Concepts

1. Minor Units (The Secret to Precision)

We store money as integers in the smallest unit (ören, cents) to avoid floating-point errors:

// API sends/receives minor units
const apiResponse = { amountInMinorUnits: 50000 } // 500.00 kr
const money = sek.create.fromMinor(apiResponse.amountInMinorUnits)

// Convert back for API
const payload = {
  amountInMinorUnits: sek.transform.toMinor(money.value),
}

2. Type Safety

Branded types prevent mixing different units:

const minor: AmountInMinorUnits = 1050
const major: AmountInMajorUnits = 10.5
// minor + major  // ❌ TypeScript error - can't mix units!

3. Result Pattern (No Exceptions)

All operations return Result types for explicit error handling:

const result = sek.calculate.divide(amount, 0)
if (result.isErr()) {
  console.log(result.error.type) // "DIVISION_BY_ZERO"
}

Common Use Cases

Processing API Responses

// Backend sends amounts in minor units (ören)
const apiData = {
  loanAmount: 50000000, // 500,000 kr
  processingFee: 195000, // 1,950 kr
}

// Convert to Money objects
const loan = sek.create.fromMinor(apiData.loanAmount)
const fee = sek.create.fromMinor(apiData.processingFee)

// Calculate and display
if (loan.isOk() && fee.isOk()) {
  const total = sek.calculate.add(loan.value, fee.value)
  if (total.isOk()) {
    console.log(sek.format.standard(total.value)) // "501 950,00 kr"
  }
}

Handling User Input

// User enters amount in a form
const userInput = '5000.50'
const amount = sek.create.fromMajor(parseFloat(userInput))

if (amount.isOk()) {
  // Validate minimum amount
  const minimum = sek.create.fromMajor(100)
  if (minimum.isOk()) {
    const isValid = sek.compare.isGreaterOrEqual(amount.value, minimum.value)
    if (isValid.isOk() && isValid.value) {
      // Send to API in minor units
      const payload = {
        amountInMinorUnits: sek.transform.toMinor(amount.value),
        currency: 'SEK',
      }
    }
  }
}

Parsing Different Formats

import { PARSE_RULES } from '@lib/money'

// Swedish format: "1 234,56"
const swedishAmount = sek.parse.withRules(PARSE_RULES['sv-SE'])('1 234,56')

// Numeric format: "1234.56"
const numericAmount = sek.parse.numeric('1234.56')

Available Operations

Create

  • fromMinor(1050) - Create from minor units (ören/cents)
  • fromMajor(10.50) - Create from major units (kronor/dollars)
  • zero() - Create zero value

Calculate

  • add(a, b) - Addition
  • subtract(a, b) - Subtraction
  • multiply(money, factor) - Multiplication
  • divide(money, divisor) - Division
  • percentage(money, percent) - Calculate percentage
  • sum(moneys[]) - Sum array of money

Format

  • standard(money) - Locale-specific format: "1 234,56 kr"
  • accounting(money) - Accounting format: "SEK 1,234.56"
  • plain(money) - Plain number: "1234.56"
  • input(money) - For input fields: "1234.56"
  • compact(money) - Compact format: "1.2k kr"
  • words(money) - Words: "one thousand two hundred kronor"

Compare

  • isEqual(a, b) - Check equality
  • isGreater(a, b) - Check if a > b
  • isGreaterOrEqual(a, b) - Check if a ≥ b
  • isLess(a, b) - Check if a < b
  • isLessOrEqual(a, b) - Check if a ≤ b
  • isNegative(money) - Check if negative
  • isZero(money) - Check if zero

Transform

  • toMinor(money) - Get minor units value
  • toMajor(money) - Get major units value
  • abs(money) - Absolute value
  • negate(money) - Negate value
  • roundToMajor(money) - Round to nearest major unit

What This Library Does NOT Do

This is a frontend-focused library. It does NOT handle:

  • ❌ Currency conversion (backend provides rates)
  • ❌ Interest calculations (backend business logic)
  • ❌ Payment processing (backend handles transactions)
  • ❌ Complex financial calculations (backend's domain)

This separation keeps the frontend focused on presentation and user interaction while the backend handles business logic.

Supported Currencies

import { SEK, USD, EUR, GBP, JPY, NOK, DKK } from '@lib/money/config/registry'

Or define your own:

const customCurrency: CurrencyConfig = {
  code: 'XXX',
  decimals: 2,
  minorToMajor: 100,
  symbol: '¤',
  // ... other config
}

const xxx = money.for(customCurrency)

Error Handling

All operations return Result types. Always check for errors:

const result = sek.calculate.add(amount1, amount2)
if (result.isOk()) {
  console.log('Success:', result.value)
} else {
  console.error('Error:', result.error.type)
  // Error types: CURRENCY_MISMATCH, DIVISION_BY_ZERO,
  // CALCULATION_OVERFLOW, INVALID_FORMAT, etc.
}

Installation

The library is already included in this project at @lib/money.

License

Internal library for this project.