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

@wisemen/monetary

v0.2.35

Published

✔ Accurate Financial Calculations – `Monetary` provides the tools to accurately perform moneraty operations \ ✔ Persistence Support – Seamlessly store and retrieve monetary values with extenstions for `TypeORM`. \ ✔ DTO Validation – Ensure data integrity

Readme

Wisemen Monetary Package for TypeScript

Features

✔ Accurate Financial Calculations – Monetary provides the tools to accurately perform moneraty operations
✔ Persistence Support – Seamlessly store and retrieve monetary values with extenstions for TypeORM.
✔ DTO Validation – Ensure data integrity with built-in validation for monetary objects.
✔ Immutability – Monetary is fully immutable, meaning no state bugs.
✔ TypeScript Support – Fully typed for a smooth developer experience.

Philosophy

Wisemen Monetary places the responsibility of accurately dealing with monetary values with the developer, but helps where needed by providing the necessary tools.

Example

// 10 euros with precision 4
import { Monetary } from './monetary'

const price = new Monetary({
  amount: 10_0000,
  currency: Currency.EUR,
  precision: 4
})

// 2 euros discount with precision 0
const flatDiscount = new Monetary({
  amount: 2,
  currency: Currency.EUR,
  precision: 0
})

const bonusDiscountRate = 0.90 // 90 percent of discounted price

const discountedPrice = price
  .subtract(flatDiscount)        // subtract 2 euros
  .multiply(bonusDiscountRate)   // take 90 percent of price
  .toPrecision(2)                // only store up to cents
  .floor()

discountedPrice.toString() // -> "7,20 euros"

Deep Dive

Monetary operations

Rounding

Monetary stores the current amount internally as a regular javascript number which means it can be both an integer or a float. Monetary objects will never perform any type of rounding on the internal value automatically. The point at which rounding of the monetary value takes place and the specific rounding operation is determined by the developer. Rounding always respects the set precision.

Monetary supports 3 rounding operations:

  • round(): rounds half up to the nearest integer
  • ceil(): rounds up to the nearest integer
  • floor(): rounds down to the nearest integer
// 10 euros with precsion 2
const price = new Monetary({
  amount: 1000,
  currency: Currency.EUR,
  precision: 2
})

price.isRounded() // -> true

const discountedPrice =  price.multiply(.9999) // -> 9.999 euros
discountedPrice.isRounded() // -> false

discountedPrice.round() // -> 10 euros
discountedPrice.floor() // -> 9.99 euros
discountedPrice.ceil() // -> 10 euros

Adding and subtracting

Monetary only allows values with the same currency to be added or subtracted from each other.

// 10 euros with precsion 2
const euros = new Monetary({
  amount: 1000,
  currency: Currency.EUR,
  precision: 2
})

// 10 USD with precsion 2
const dollars = new Monetary({
  amount: 1000,
  currency: Currency.USD,
  precision: 2
})

euros.add(dollars) // -> throws IllegalMonetaryOperationError
euros.subtract(dollars) // -> throws IllegalMonetaryOperationError

Adding or subtracting values with different precisions will use the highest precision of the two in the resulting value. The amount of the value with the lowest precision is increased to the higher precision before the operation is performed.

// 10 euros with precsion 2
const euros = new Monetary({
  amount: 1000,
  currency: Currency.EUR,
  precision: 2
})

// 10 euros with precsion 4
const morePreciseEuros = new Monetary({
  amount: 1000000,
  currency: Currency.EUR,
  precision: 4
})

euros.add(morePreciseEuros) // -> 20 euros with precision 4
euros.subtract(morePreciseEuros) // -> 0 euros with precision 4

Changing precision

Changing the precision of a monetary value adjusts the internal amount. The value is multiplied by 10(newPrecision - oldPrecision). A decrease in precision can result in an unrounded value.

// 9,50 euros with precsion 2
const euros = new Monetary({
  amount: 950,
  currency: Currency.EUR,
  precision: 2
})

euros.toPrecision(4) // -> amount of 95000
euros.toPrecision(0) // -> amount of 9,50 (unrounded)

Persistence

Storing arbitrary precision is not possible in Monetary. Monetary storing mechanisms require a set precision. All stored values will have the set precision. Values with a lower precision are normalized to the set precision. Values with a higher precision will trigger an error when stored.

class Entity {
  @MonetaryColumn({defaultPrecision: 4})
  amount: Monetary
}

// 9,50 euros with precsion 2
const euros = new Monetary({
  amount: 950,
  currency: Currency.EUR,
  precision: 2
})

// 9,50 euros with precsion 5
const highPrecisionEuros = new Monetary({
  amount: 950000,
  currency: Currency.EUR,
  precision: 5
})

await entityRepository.insert({amount: euros}) // -> stored as 95000
await entityRepository.insert({amount: highPrecisionEuros}) // -> throws PrecisionLossError

Monetary provides 2 storing mechanisms for TypeORM.

Storing only the amount

When the currency of a monetary value is static (i.e. only EUR is supported) monetary values can be stored as just the amount. The amount is stored as an int4 column.

class Entity {
  @MonetaryAmountColumn({
    monetaryPrecision: 4,  
    currency: Currency.EUR // the static currency
  })
  amount: Monetary
}

// 9,50 dollars with precision 2
const dollars = new Monetary({
  amount: 950,
  currency: Currency.USD,
  precision: 2
})

await entityRepository.insert({amount: dollars}) // -> throws UnsupportedCurrencyError

Storing the amount and currency

Storing multiple currencies in a single field will store the monetary values as jsonb. It's possible to define a precision per currency. If a precision is not set for a currency, the default precision is used.

class Entity {
  @MonetaryColumn({
    defaultPrecision: 2,
    currencyPrecisions: {
      [Currency.EUR]: 4
    }
  })
  amount: Monetary
}

// 9,50 dollars with precsion 2
const dollars = new Monetary({
  amount: 950,
  currency: Currency.USD,
  precision: 2
})

// 9,50 euros with precsion 2
const euros = new Monetary({
  amount: 950,
  currency: Currency.EUR,
  precision: 2
})


await entityRepository.insert({amount: dollars}) // -> stored with precision 2 as 950
await entityRepository.insert({amount: euros}) // -> stored with precision 4 as 95000

DTO

Monetary provides a DTO MonetaryDto which defines ApiPropertys for open api documentation in Nestjs and validation for class-validator.

{
  "amount": 950,
  "currency": "EUR",
  "precision": 2
}

Validation

The DTO validates it's internal properties. When you need to validate the DTO as a nested property you can use IsMonetary. This validator applies IsObject, ValidateNested and Type internally. You can optionally define allowed currencies and a maximum precision.

class EntityDto {
  @IsMonetary({
    maxPrecision: 4, // optional max precision
    allowedCurrencies: new Set([Currency.EUR]) // optional allowed currencies
  })
  amount: MonetaryDto
}

Creating DTOs

A Monetary DTO can be made through a static method:

const euros = new Monetary({
  amount: 950,
  currency: Currency.EUR,
  precision: 2
})

const dto = MonetaryDto.from(euros)

Or with a builder which allows invalid values for testing:

const dto = new MonetaryDtoBuilder()
  .withAmount(950)
  .withCurrency(Currency.EUR)
  .withPrecision(2)
  .build()

Parsing DTOs

A Monetary DTO can be parsed by passing it to the constructor:

const dto = new MonetaryDtoBuilder()
  .withAmount(950)
  .withCurrency(Currency.EUR)
  .withPrecision(2)
  .build()

const amount = new Monetary(dto)

or with a convenience method on the dto:

const dto = new MonetaryDtoBuilder()
  .withAmount(950)
  .withCurrency(Currency.EUR)
  .withPrecision(2)
  .build()

const amount = dto.parse()