@azghr/specie
v0.1.2
Published
Tiny integer-cents money type — safe arithmetic, currency-aware rounding, Intl formatting. No bignum, no dependencies.
Maintainers
Readme
specie
Tiny integer-cents money type — safe arithmetic, currency-aware rounding, Intl formatting. No bignum, no dependencies.
The Problem
0.1 + 0.2 !== 0.3. Storing money as floats produces rounding errors and audit nightmares. Correct money is integer minor units + a currency + explicit rounding + locale formatting.
This is competitive space: dinero.js, big.js, and decimal.js cover arbitrary decimals. specie is worth building only because it stays radically smaller: integer minor units only, currency-aware default fractions via Intl, and a few explicit operations.
Install
npm install @azghr/specie
# or
pnpm add @azghr/specie
# or
yarn add @azghr/specieUse
import { money, fromDecimal } from "@azghr/specie";
// 10-second copy-paste
const price = money(1999, "USD"); // $19.99 as 1999 cents
price.add(money(100, "USD")).format(); // "$20.99"
// Realistic usage
const subtotal = fromDecimal("19.99", "USD");
const tax = subtotal.multiply(0.0825); // 8.25% tax
const total = subtotal.add(tax);
total.format(); // "$21.64"API
money(amount: number, currency: string): Money
Create Money from integer minor units. amount must be a safe integer.
money(1999, "USD") // $19.99
money(500, "JPY") // ¥500fromDecimal(value: string, currency: string): Money
Parse decimal string to Money. Never uses float parsing to avoid rounding errors.
fromDecimal("19.99", "USD") // 1999 cents
fromDecimal("-10.50", "USD") // -1050 centsMoney.add(other: Money): Money
Add another Money value (same currency only). Throws CurrencyMismatch on currency mismatch.
Money.subtract(other: Money): Money
Subtract another Money value (same currency only).
Money.multiply(factor: number, rounding?: Rounding): Money
Multiply by a factor with rounding. Default: "half-even" (banker's rounding).
money(1000, "USD").multiply(1.0825) // Apply 8.25% tax
money(100, "USD").multiply(1.5, "half-up") // Force ties upMoney.allocate(ratios: readonly number[]): Money[]
Distribute by ratios using largest-remainder method. Parts sum exactly to the original amount.
money(100, "USD").allocate([3, 2]) // [$60, $40] (not $59.99/$40.01)Money.format(locale?: string): string
Format using Intl.NumberFormat.
money(1999, "USD").format() // "$19.99"
money(123456, "JPY").format("ja-JP") // "¥123,456" (no decimals)Money.compare(other: Money): -1 | 0 | 1
Compare with another Money. Throws CurrencyMismatch on currency mismatch.
Money.equals(other: Money): boolean
Check equality.
Money.isZero(), Money.isNegative(), Money.isPositive()
Check amount sign.
Money.toJSON(): { amount: number; currency: string }
Serialize to plain object.
Type: Rounding
"half-up" | "half-even" | "down" | "up"Class: CurrencyMismatch
Error thrown when operations mix different currencies.
Non-Goals
Use dinero.js or decimal.js for:
- Arbitrary precision / values beyond
Number.MAX_SAFE_INTEGER - Multi-currency conversion / forex
- Historical rates
- Crypto amounts with 18 decimals
TypeScript Note
import { money, fromDecimal } from "@azghr/specie";
// Full type safety
const price: Money = money(1999, "USD");
const total: Money = price.add(money(500, "USD"));
// Currency mismatch caught at compile time
const usd = money(100, "USD");
const eur = money(100, "EUR");
// usd.add(eur) // TypeScript: this works, runtime throws CurrencyMismatchRelated Packages
Caching & Concurrency:
- @azghr/filterkit — Framework-agnostic, type-safe filtering for TypeScript
- @azghr/singlet — Deduplicate concurrent async calls
- staleness — Stale-while-revalidate caching for async functions
Text Processing:
- @azghr/shorn — Truncate strings by byte budget without breaking graphemes
- seriatim — Sequential processing utilities
HTTP & Network:
- forbear — Read server rate-limit instructions from HTTP responses
- forestall — Delay execution until a condition is met
- obviate — Render operations unnecessary through caching
System & Process:
- quiesce — Ordered, timeboxed graceful shutdown for Node
- sortition — Deterministic percentage rollouts and A/B bucketing
- stanch — Stop flows or operations based on conditions
Utilities:
- expunge — Remove or exclude items from collections
- occlude — Hide or mask data and functionality
- placemark — Geographic location and mapping utilities
License
MIT
