@vielzeug/coins 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +3 -5
  2. package/dist/aggregate.cjs +1 -1
  3. package/dist/aggregate.cjs.map +1 -1
  4. package/dist/aggregate.d.ts +1 -4
  5. package/dist/aggregate.d.ts.map +1 -1
  6. package/dist/aggregate.js +22 -24
  7. package/dist/aggregate.js.map +1 -1
  8. package/dist/coins.cjs +1 -1
  9. package/dist/coins.cjs.map +1 -1
  10. package/dist/coins.iife.js +1 -1
  11. package/dist/coins.iife.js.map +1 -1
  12. package/dist/coins.js +1 -1
  13. package/dist/coins.js.map +1 -1
  14. package/dist/currency.cjs.map +1 -1
  15. package/dist/currency.d.ts.map +1 -1
  16. package/dist/currency.js.map +1 -1
  17. package/dist/decimal.cjs.map +1 -1
  18. package/dist/decimal.d.ts.map +1 -1
  19. package/dist/decimal.js.map +1 -1
  20. package/dist/errors.cjs +1 -1
  21. package/dist/errors.cjs.map +1 -1
  22. package/dist/errors.d.ts +1 -2
  23. package/dist/errors.d.ts.map +1 -1
  24. package/dist/errors.js +1 -4
  25. package/dist/errors.js.map +1 -1
  26. package/dist/exchange.cjs.map +1 -1
  27. package/dist/exchange.d.ts.map +1 -1
  28. package/dist/exchange.js.map +1 -1
  29. package/dist/format.cjs +1 -1
  30. package/dist/format.cjs.map +1 -1
  31. package/dist/format.d.ts.map +1 -1
  32. package/dist/format.js +26 -26
  33. package/dist/format.js.map +1 -1
  34. package/dist/index.cjs +1 -1
  35. package/dist/index.d.ts +3 -4
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +7 -8
  38. package/dist/money.cjs +1 -1
  39. package/dist/money.cjs.map +1 -1
  40. package/dist/money.d.ts +4 -0
  41. package/dist/money.d.ts.map +1 -1
  42. package/dist/money.js +36 -31
  43. package/dist/money.js.map +1 -1
  44. package/dist/serialization.cjs +1 -1
  45. package/dist/serialization.cjs.map +1 -1
  46. package/dist/serialization.d.ts.map +1 -1
  47. package/dist/serialization.js +1 -1
  48. package/dist/serialization.js.map +1 -1
  49. package/dist/types.d.ts +1 -0
  50. package/dist/types.d.ts.map +1 -1
  51. package/package.json +21 -17
@@ -1 +1 @@
1
- {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_MONEY'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n static is(error: unknown): error is CoinsError {\n return error instanceof CoinsError;\n }\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: ${expected} and ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${String(value)}\"`);\n this.value = value;\n }\n}\n"],"mappings":"AASA,IAAa,EAAb,MAAa,UAAmB,KAAM,CACpC,KAEA,OAAO,GAAG,EAAqC,CAC7C,OAAO,aAAiB,CAC1B,CAEA,YAAY,EAAsB,EAAiB,EAAwB,CACzE,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,EACZ,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA2C,CAAW,CACpD,SACA,SAEA,YAAY,EAAkB,EAAkB,CAC9C,MAAM,oBAAqB,sBAAsB,EAAS,OAAO,GAAU,EAC3E,KAAK,SAAW,EAChB,KAAK,SAAW,CAClB,CACF,EAEa,EAAb,cAA0C,CAAW,CACnD,MAEA,YAAY,EAAgB,CAC1B,MAAM,mBAAoB,0BAA0B,OAAO,CAAK,EAAE,EAAE,EACpE,KAAK,MAAQ,CACf,CACF"}
1
+ {"version":3,"file":"errors.cjs","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'FORMAT_ERROR'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_MONEY'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: ${expected} and ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${String(value)}\"`);\n this.value = value;\n }\n}\n"],"mappings":"AAUA,IAAa,EAAb,cAAgC,KAAM,CACpC,KAEA,YAAY,EAAsB,EAAiB,EAAwB,CACzE,MAAM,EAAS,CAAO,EACtB,KAAK,KAAO,EACZ,KAAK,KAAO,WAAW,KACvB,OAAO,eAAe,KAAM,WAAW,SAAS,CAClD,CACF,EAEa,EAAb,cAA2C,CAAW,CACpD,SACA,SAEA,YAAY,EAAkB,EAAkB,CAC9C,MAAM,oBAAqB,sBAAsB,EAAS,OAAO,GAAU,EAC3E,KAAK,SAAW,EAChB,KAAK,SAAW,CAClB,CACF,EAEa,EAAb,cAA0C,CAAW,CACnD,MAEA,YAAY,EAAgB,CAC1B,MAAM,mBAAoB,0BAA0B,OAAO,CAAK,EAAE,EAAE,EACpE,KAAK,MAAQ,CACf,CACF"}
package/dist/errors.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- export type CoinsErrorCode = 'CURRENCY_MISMATCH' | 'DIVISION_BY_ZERO' | 'INVALID_ALLOCATION' | 'INVALID_CURRENCY' | 'INVALID_DECIMAL' | 'INVALID_MONEY' | 'INVALID_ROUNDING';
1
+ export type CoinsErrorCode = 'CURRENCY_MISMATCH' | 'DIVISION_BY_ZERO' | 'FORMAT_ERROR' | 'INVALID_ALLOCATION' | 'INVALID_CURRENCY' | 'INVALID_DECIMAL' | 'INVALID_MONEY' | 'INVALID_ROUNDING';
2
2
  export declare class CoinsError extends Error {
3
3
  readonly code: CoinsErrorCode;
4
- static is(error: unknown): error is CoinsError;
5
4
  constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions);
6
5
  }
7
6
  export declare class CurrencyMismatchError extends CoinsError {
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,eAAe,GACf,kBAAkB,CAAC;AAEvB,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAE9B,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,UAAU;gBAIlC,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAM1E;AAED,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAK/C;AAED,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAI3B"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GACtB,mBAAmB,GACnB,kBAAkB,GAClB,cAAc,GACd,oBAAoB,GACpB,kBAAkB,GAClB,iBAAiB,GACjB,eAAe,GACf,kBAAkB,CAAC;AAEvB,qBAAa,UAAW,SAAQ,KAAK;IACnC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;gBAElB,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY;CAM1E;AAED,qBAAa,qBAAsB,SAAQ,UAAU;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;gBAEd,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAK/C;AAED,qBAAa,oBAAqB,SAAQ,UAAU;IAClD,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBAEZ,KAAK,EAAE,OAAO;CAI3B"}
package/dist/errors.js CHANGED
@@ -1,9 +1,6 @@
1
1
  //#region src/errors.ts
2
- var e = class e extends Error {
2
+ var e = class extends Error {
3
3
  code;
4
- static is(t) {
5
- return t instanceof e;
6
- }
7
4
  constructor(e, t, n) {
8
5
  super(t, n), this.code = e, this.name = new.target.name, Object.setPrototypeOf(this, new.target.prototype);
9
6
  }
@@ -1 +1 @@
1
- {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_MONEY'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n static is(error: unknown): error is CoinsError {\n return error instanceof CoinsError;\n }\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: ${expected} and ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${String(value)}\"`);\n this.value = value;\n }\n}\n"],"mappings":";AASA,IAAa,IAAb,MAAa,UAAmB,MAAM;CACpC;CAEA,OAAO,GAAG,GAAqC;EAC7C,OAAO,aAAiB;CAC1B;CAEA,YAAY,GAAsB,GAAiB,GAAwB;EAIzE,AAHA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO,GACZ,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAEa,IAAb,cAA2C,EAAW;CACpD;CACA;CAEA,YAAY,GAAkB,GAAkB;EAG9C,AAFA,MAAM,qBAAqB,sBAAsB,EAAS,OAAO,GAAU,GAC3E,KAAK,WAAW,GAChB,KAAK,WAAW;CAClB;AACF,GAEa,IAAb,cAA0C,EAAW;CACnD;CAEA,YAAY,GAAgB;EAE1B,AADA,MAAM,oBAAoB,0BAA0B,OAAO,CAAK,EAAE,EAAE,GACpE,KAAK,QAAQ;CACf;AACF"}
1
+ {"version":3,"file":"errors.js","names":[],"sources":["../src/errors.ts"],"sourcesContent":["export type CoinsErrorCode =\n | 'CURRENCY_MISMATCH'\n | 'DIVISION_BY_ZERO'\n | 'FORMAT_ERROR'\n | 'INVALID_ALLOCATION'\n | 'INVALID_CURRENCY'\n | 'INVALID_DECIMAL'\n | 'INVALID_MONEY'\n | 'INVALID_ROUNDING';\n\nexport class CoinsError extends Error {\n readonly code: CoinsErrorCode;\n\n constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions) {\n super(message, options);\n this.code = code;\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class CurrencyMismatchError extends CoinsError {\n readonly expected: string;\n readonly received: string;\n\n constructor(expected: string, received: string) {\n super('CURRENCY_MISMATCH', `Currency mismatch: ${expected} and ${received}`);\n this.expected = expected;\n this.received = received;\n }\n}\n\nexport class InvalidCurrencyError extends CoinsError {\n readonly value: unknown;\n\n constructor(value: unknown) {\n super('INVALID_CURRENCY', `Unsupported currency: \"${String(value)}\"`);\n this.value = value;\n }\n}\n"],"mappings":";AAUA,IAAa,IAAb,cAAgC,MAAM;CACpC;CAEA,YAAY,GAAsB,GAAiB,GAAwB;EAIzE,AAHA,MAAM,GAAS,CAAO,GACtB,KAAK,OAAO,GACZ,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAEa,IAAb,cAA2C,EAAW;CACpD;CACA;CAEA,YAAY,GAAkB,GAAkB;EAG9C,AAFA,MAAM,qBAAqB,sBAAsB,EAAS,OAAO,GAAU,GAC3E,KAAK,WAAW,GAChB,KAAK,WAAW;CAClB;AACF,GAEa,IAAb,cAA0C,EAAW;CACnD;CAEA,YAAY,GAAgB;EAE1B,AADA,MAAM,oBAAoB,0BAA0B,OAAO,CAAK,EAAE,EAAE,GACpE,KAAK,QAAQ;CACf;AACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.cjs","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nimport { isCurrency } from './currency';\nimport { decimal, roundDivision } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { isMoney, withMinor } from './money';\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator < 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates cannot be negative');\n\n return Object.freeze({ from, to, value: parsed });\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n if (!isMoney(value) || !isValidRate(rate))\n throw new CoinsError('INVALID_MONEY', 'Exchange requires canonical money and rate values');\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return withMinor(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n\nfunction isValidRate(value: unknown): value is ExchangeRate {\n if (typeof value !== 'object' || value === null || !Object.isFrozen(value)) return false;\n\n const rate = value as Partial<ExchangeRate>;\n\n return (\n isCurrency(rate.from) &&\n isCurrency(rate.to) &&\n typeof rate.value === 'object' &&\n rate.value !== null &&\n Object.isFrozen(rate.value) &&\n typeof rate.value.numerator === 'bigint' &&\n rate.value.numerator >= 0n &&\n typeof rate.value.denominator === 'bigint' &&\n rate.value.denominator > 0n\n );\n}\n"],"mappings":"gHAOA,SAAgB,EAAyD,CACvE,OACA,KACA,SAKyB,CACzB,GAAI,CAAC,EAAA,WAAW,CAAI,GAAK,CAAC,EAAA,WAAW,CAAE,EACrC,MAAM,IAAI,EAAA,WAAW,mBAAoB,8CAA8C,EAEzF,IAAM,EAAS,EAAA,QAAQ,CAAK,EAE5B,GAAI,EAAO,UAAY,GAAI,MAAM,IAAI,EAAA,WAAW,kBAAmB,mCAAmC,EAEtG,OAAO,OAAO,OAAO,CAAE,OAAM,KAAI,MAAO,CAAO,CAAC,CAClD,CAEA,SAAgB,EACd,EACA,EACA,EAAuC,CAAC,EAC7B,CACX,GAAI,CAAC,EAAA,QAAQ,CAAK,GAAK,CAAC,EAAY,CAAI,EACtC,MAAM,IAAI,EAAA,WAAW,gBAAiB,mDAAmD,EAE3F,GAAI,EAAM,WAAa,EAAK,KAAM,MAAM,IAAI,EAAA,sBAAsB,EAAM,SAAS,KAAM,EAAK,KAAK,IAAI,EAErG,IAAM,EAAY,EAAM,OAAS,EAAK,MAAM,UAAY,KAAO,OAAO,EAAK,GAAG,SAAS,EACjF,EAAc,EAAK,MAAM,YAAc,KAAO,OAAO,EAAK,KAAK,SAAS,EAE9E,OAAO,EAAA,UAAU,EAAA,cAAc,EAAW,EAAa,EAAQ,UAAY,kBAAkB,EAAG,EAAK,EAAE,CACzG,CAEA,SAAS,EAAY,EAAuC,CAC1D,GAAI,OAAO,GAAU,WAAY,GAAkB,CAAC,OAAO,SAAS,CAAK,EAAG,MAAO,GAEnF,IAAM,EAAO,EAEb,OACE,EAAA,WAAW,EAAK,IAAI,GACpB,EAAA,WAAW,EAAK,EAAE,GAClB,OAAO,EAAK,OAAU,UACtB,EAAK,QAAU,MACf,OAAO,SAAS,EAAK,KAAK,GAC1B,OAAO,EAAK,MAAM,WAAc,UAChC,EAAK,MAAM,WAAa,IACxB,OAAO,EAAK,MAAM,aAAgB,UAClC,EAAK,MAAM,YAAc,EAE7B"}
1
+ {"version":3,"file":"exchange.cjs","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import { isCurrency } from './currency';\nimport { decimal, roundDivision } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { isMoney, withMinor } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator < 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates cannot be negative');\n\n return Object.freeze({ from, to, value: parsed });\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n if (!isMoney(value) || !isValidRate(rate))\n throw new CoinsError('INVALID_MONEY', 'Exchange requires canonical money and rate values');\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return withMinor(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n\nfunction isValidRate(value: unknown): value is ExchangeRate {\n if (typeof value !== 'object' || value === null || !Object.isFrozen(value)) return false;\n\n const rate = value as Partial<ExchangeRate>;\n\n return (\n isCurrency(rate.from) &&\n isCurrency(rate.to) &&\n typeof rate.value === 'object' &&\n rate.value !== null &&\n Object.isFrozen(rate.value) &&\n typeof rate.value.numerator === 'bigint' &&\n rate.value.numerator >= 0n &&\n typeof rate.value.denominator === 'bigint' &&\n rate.value.denominator > 0n\n );\n}\n"],"mappings":"gHAMA,SAAgB,EAAyD,CACvE,OACA,KACA,SAKyB,CACzB,GAAI,CAAC,EAAA,WAAW,CAAI,GAAK,CAAC,EAAA,WAAW,CAAE,EACrC,MAAM,IAAI,EAAA,WAAW,mBAAoB,8CAA8C,EAEzF,IAAM,EAAS,EAAA,QAAQ,CAAK,EAE5B,GAAI,EAAO,UAAY,GAAI,MAAM,IAAI,EAAA,WAAW,kBAAmB,mCAAmC,EAEtG,OAAO,OAAO,OAAO,CAAE,OAAM,KAAI,MAAO,CAAO,CAAC,CAClD,CAEA,SAAgB,EACd,EACA,EACA,EAAuC,CAAC,EAC7B,CACX,GAAI,CAAC,EAAA,QAAQ,CAAK,GAAK,CAAC,EAAY,CAAI,EACtC,MAAM,IAAI,EAAA,WAAW,gBAAiB,mDAAmD,EAE3F,GAAI,EAAM,WAAa,EAAK,KAAM,MAAM,IAAI,EAAA,sBAAsB,EAAM,SAAS,KAAM,EAAK,KAAK,IAAI,EAErG,IAAM,EAAY,EAAM,OAAS,EAAK,MAAM,UAAY,KAAO,OAAO,EAAK,GAAG,SAAS,EACjF,EAAc,EAAK,MAAM,YAAc,KAAO,OAAO,EAAK,KAAK,SAAS,EAE9E,OAAO,EAAA,UAAU,EAAA,cAAc,EAAW,EAAa,EAAQ,UAAY,kBAAkB,EAAG,EAAK,EAAE,CACzG,CAEA,SAAS,EAAY,EAAuC,CAC1D,GAAI,OAAO,GAAU,WAAY,GAAkB,CAAC,OAAO,SAAS,CAAK,EAAG,MAAO,GAEnF,IAAM,EAAO,EAEb,OACE,EAAA,WAAW,EAAK,IAAI,GACpB,EAAA,WAAW,EAAK,EAAE,GAClB,OAAO,EAAK,OAAU,UACtB,EAAK,QAAU,MACf,OAAO,SAAS,EAAK,KAAK,GAC1B,OAAO,EAAK,MAAM,WAAc,UAChC,EAAK,MAAM,WAAa,IACxB,OAAO,EAAK,MAAM,aAAgB,UAClC,EAAK,MAAM,YAAc,EAE7B"}
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../src/exchange.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAO3E,wBAAgB,YAAY,CAAC,IAAI,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EAAE,EACvE,IAAI,EACJ,EAAE,EACF,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CASzB;AAED,wBAAgB,QAAQ,CAAC,IAAI,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EACjE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,EAC5B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GACxC,KAAK,CAAC,EAAE,CAAC,CAUX"}
1
+ {"version":3,"file":"exchange.d.ts","sourceRoot":"","sources":["../src/exchange.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE3E,wBAAgB,YAAY,CAAC,IAAI,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EAAE,EACvE,IAAI,EACJ,EAAE,EACF,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,EAAE,EAAE,EAAE,CAAC;IACP,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,CASzB;AAED,wBAAgB,QAAQ,CAAC,IAAI,SAAS,QAAQ,EAAE,EAAE,SAAS,QAAQ,EACjE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC,EAClB,IAAI,EAAE,YAAY,CAAC,IAAI,EAAE,EAAE,CAAC,EAC5B,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GACxC,KAAK,CAAC,EAAE,CAAC,CAUX"}
@@ -1 +1 @@
1
- {"version":3,"file":"exchange.js","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nimport { isCurrency } from './currency';\nimport { decimal, roundDivision } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { isMoney, withMinor } from './money';\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator < 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates cannot be negative');\n\n return Object.freeze({ from, to, value: parsed });\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n if (!isMoney(value) || !isValidRate(rate))\n throw new CoinsError('INVALID_MONEY', 'Exchange requires canonical money and rate values');\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return withMinor(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n\nfunction isValidRate(value: unknown): value is ExchangeRate {\n if (typeof value !== 'object' || value === null || !Object.isFrozen(value)) return false;\n\n const rate = value as Partial<ExchangeRate>;\n\n return (\n isCurrency(rate.from) &&\n isCurrency(rate.to) &&\n typeof rate.value === 'object' &&\n rate.value !== null &&\n Object.isFrozen(rate.value) &&\n typeof rate.value.numerator === 'bigint' &&\n rate.value.numerator >= 0n &&\n typeof rate.value.denominator === 'bigint' &&\n rate.value.denominator > 0n\n );\n}\n"],"mappings":";;;;;AAOA,SAAgB,EAAyD,EACvE,SACA,OACA,YAKyB;CACzB,IAAI,CAAC,EAAW,CAAI,KAAK,CAAC,EAAW,CAAE,GACrC,MAAM,IAAI,EAAW,oBAAoB,8CAA8C;CAEzF,IAAM,IAAS,EAAQ,CAAK;CAE5B,IAAI,EAAO,YAAY,IAAI,MAAM,IAAI,EAAW,mBAAmB,mCAAmC;CAEtG,OAAO,OAAO,OAAO;EAAE;EAAM;EAAI,OAAO;CAAO,CAAC;AAClD;AAEA,SAAgB,EACd,GACA,GACA,IAAuC,CAAC,GAC7B;CACX,IAAI,CAAC,EAAQ,CAAK,KAAK,CAAC,EAAY,CAAI,GACtC,MAAM,IAAI,EAAW,iBAAiB,mDAAmD;CAE3F,IAAI,EAAM,aAAa,EAAK,MAAM,MAAM,IAAI,EAAsB,EAAM,SAAS,MAAM,EAAK,KAAK,IAAI;CAErG,IAAM,IAAY,EAAM,SAAS,EAAK,MAAM,YAAY,OAAO,OAAO,EAAK,GAAG,SAAS,GACjF,IAAc,EAAK,MAAM,cAAc,OAAO,OAAO,EAAK,KAAK,SAAS;CAE9E,OAAO,EAAU,EAAc,GAAW,GAAa,EAAQ,YAAY,kBAAkB,GAAG,EAAK,EAAE;AACzG;AAEA,SAAS,EAAY,GAAuC;CAC1D,IAAI,OAAO,KAAU,aAAY,KAAkB,CAAC,OAAO,SAAS,CAAK,GAAG,OAAO;CAEnF,IAAM,IAAO;CAEb,OACE,EAAW,EAAK,IAAI,KACpB,EAAW,EAAK,EAAE,KAClB,OAAO,EAAK,SAAU,YACtB,EAAK,UAAU,QACf,OAAO,SAAS,EAAK,KAAK,KAC1B,OAAO,EAAK,MAAM,aAAc,YAChC,EAAK,MAAM,aAAa,MACxB,OAAO,EAAK,MAAM,eAAgB,YAClC,EAAK,MAAM,cAAc;AAE7B"}
1
+ {"version":3,"file":"exchange.js","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import { isCurrency } from './currency';\nimport { decimal, roundDivision } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { isMoney, withMinor } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nexport function exchangeRate<From extends Currency, To extends Currency>({\n from,\n to,\n value,\n}: {\n from: From;\n to: To;\n value: string;\n}): ExchangeRate<From, To> {\n if (!isCurrency(from) || !isCurrency(to))\n throw new CoinsError('INVALID_CURRENCY', 'Exchange rate requires registered currencies');\n\n const parsed = decimal(value);\n\n if (parsed.numerator < 0n) throw new CoinsError('INVALID_DECIMAL', 'Exchange rates cannot be negative');\n\n return Object.freeze({ from, to, value: parsed });\n}\n\nexport function exchange<From extends Currency, To extends Currency>(\n value: Money<From>,\n rate: ExchangeRate<From, To>,\n options: { rounding?: RoundingMode } = {},\n): Money<To> {\n if (!isMoney(value) || !isValidRate(rate))\n throw new CoinsError('INVALID_MONEY', 'Exchange requires canonical money and rate values');\n\n if (value.currency !== rate.from) throw new CurrencyMismatchError(value.currency.code, rate.from.code);\n\n const numerator = value.amount * rate.value.numerator * 10n ** BigInt(rate.to.minorUnit);\n const denominator = rate.value.denominator * 10n ** BigInt(rate.from.minorUnit);\n\n return withMinor(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n\nfunction isValidRate(value: unknown): value is ExchangeRate {\n if (typeof value !== 'object' || value === null || !Object.isFrozen(value)) return false;\n\n const rate = value as Partial<ExchangeRate>;\n\n return (\n isCurrency(rate.from) &&\n isCurrency(rate.to) &&\n typeof rate.value === 'object' &&\n rate.value !== null &&\n Object.isFrozen(rate.value) &&\n typeof rate.value.numerator === 'bigint' &&\n rate.value.numerator >= 0n &&\n typeof rate.value.denominator === 'bigint' &&\n rate.value.denominator > 0n\n );\n}\n"],"mappings":";;;;;AAMA,SAAgB,EAAyD,EACvE,SACA,OACA,YAKyB;CACzB,IAAI,CAAC,EAAW,CAAI,KAAK,CAAC,EAAW,CAAE,GACrC,MAAM,IAAI,EAAW,oBAAoB,8CAA8C;CAEzF,IAAM,IAAS,EAAQ,CAAK;CAE5B,IAAI,EAAO,YAAY,IAAI,MAAM,IAAI,EAAW,mBAAmB,mCAAmC;CAEtG,OAAO,OAAO,OAAO;EAAE;EAAM;EAAI,OAAO;CAAO,CAAC;AAClD;AAEA,SAAgB,EACd,GACA,GACA,IAAuC,CAAC,GAC7B;CACX,IAAI,CAAC,EAAQ,CAAK,KAAK,CAAC,EAAY,CAAI,GACtC,MAAM,IAAI,EAAW,iBAAiB,mDAAmD;CAE3F,IAAI,EAAM,aAAa,EAAK,MAAM,MAAM,IAAI,EAAsB,EAAM,SAAS,MAAM,EAAK,KAAK,IAAI;CAErG,IAAM,IAAY,EAAM,SAAS,EAAK,MAAM,YAAY,OAAO,OAAO,EAAK,GAAG,SAAS,GACjF,IAAc,EAAK,MAAM,cAAc,OAAO,OAAO,EAAK,KAAK,SAAS;CAE9E,OAAO,EAAU,EAAc,GAAW,GAAa,EAAQ,YAAY,kBAAkB,GAAG,EAAK,EAAE;AACzG;AAEA,SAAS,EAAY,GAAuC;CAC1D,IAAI,OAAO,KAAU,aAAY,KAAkB,CAAC,OAAO,SAAS,CAAK,GAAG,OAAO;CAEnF,IAAM,IAAO;CAEb,OACE,EAAW,EAAK,IAAI,KACpB,EAAW,EAAK,EAAE,KAClB,OAAO,EAAK,SAAU,YACtB,EAAK,UAAU,QACf,OAAO,SAAS,EAAK,KAAK,KAC1B,OAAO,EAAK,MAAM,aAAc,YAChC,EAAK,MAAM,aAAa,MACxB,OAAO,EAAK,MAAM,eAAgB,YAClC,EAAK,MAAM,cAAc;AAE7B"}
package/dist/format.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./money.cjs");var r=20,i=new Map,a=new Map;function o(e,t={}){return s(e,t).map(e=>e.value).join(``)}function s(r,i={}){if(!n.isMoney(r))throw new e.CoinsError(`INVALID_MONEY`,`format() requires canonical money`);let{locale:a=`en-US`,maximumFractionDigits:o=r.currency.minorUnit,minimumFractionDigits:s=r.currency.minorUnit,style:d=`symbol`}=i;c(s,o);let f=10n**BigInt(o),p=o>=r.currency.minorUnit?r.amount*10n**BigInt(o-r.currency.minorUnit):t.roundDivision(r.amount,10n**BigInt(r.currency.minorUnit-o),`halfAwayFromZero`),m=p<0n,h=m?-p:p,g=h/f,_=(o===0?``:(h%f).toString().padStart(o,`0`)).replace(RegExp(`0{0,${Math.max(0,o-s)}}$`),``),v=l(a,r.currency.code,d,m),y=[],b=!1;for(let e of v)e.type!==`group`&&(e.type===`integer`?b||=(y.push({type:`integer`,value:u(a).format(g)}),!0):e.type===`decimal`?_&&y.push({type:`decimal`,value:e.value}):e.type===`fraction`?_&&y.push({type:`fraction`,value:_}):e.type===`currency`||e.type===`minusSign`||e.type===`plusSign`?y.push({type:e.type,value:e.value}):y.push({type:`literal`,value:e.value}));return y}function c(t,n){if(!Number.isInteger(n)||!Number.isInteger(t)||t<0||n<t||n>r)throw new e.CoinsError(`INVALID_ROUNDING`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r}`)}function l(t,n,r,i){let o=`${t}\0${n}\0${r}\0${i}`,s=a.get(o);if(s)return s;try{let e=new Intl.NumberFormat(t,{currency:n,currencyDisplay:r,maximumFractionDigits:1,minimumFractionDigits:1,style:`currency`}).formatToParts(i?-1.1:1.1);return a.set(o,e),e}catch(r){throw new e.CoinsError(`INVALID_CURRENCY`,`Cannot format currency "${n}" for locale "${t}"`,{cause:r})}}function u(t){let n=i.get(t);if(n)return n;try{let e=new Intl.NumberFormat(t,{maximumFractionDigits:0,useGrouping:!0});return i.set(t,e),e}catch(n){throw new e.CoinsError(`INVALID_ROUNDING`,`Cannot create number formatter for locale "${t}"`,{cause:n})}}exports.format=o,exports.formatParts=s;
1
+ const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./money.cjs");var r=20,i=`halfAwayFromZero`,a=new Map,o=new Map;function s(e,t={}){return c(e,t).map(e=>e.value).join(``)}function c(r,a={}){if(!n.isMoney(r))throw new e.CoinsError(`INVALID_MONEY`,`format() requires canonical money`);let{locale:o=`en-US`,maximumFractionDigits:s=r.currency.minorUnit,minimumFractionDigits:c=r.currency.minorUnit,rounding:f=i,style:p=`symbol`}=a;l(c,s);let m=10n**BigInt(s),h=s>=r.currency.minorUnit?r.amount*10n**BigInt(s-r.currency.minorUnit):t.roundDivision(r.amount,10n**BigInt(r.currency.minorUnit-s),f),g=h<0n,_=g?-h:h,v=_/m,y=(s===0?``:(_%m).toString().padStart(s,`0`)).replace(RegExp(`0{0,${Math.max(0,s-c)}}$`),``),b=u(o,r.currency.code,p,g),x=[],S=!1;for(let e of b)e.type!==`group`&&(e.type===`integer`?S||=(x.push({type:`integer`,value:d(o).format(v)}),!0):e.type===`decimal`?y&&x.push({type:`decimal`,value:e.value}):e.type===`fraction`?y&&x.push({type:`fraction`,value:y}):e.type===`currency`||e.type===`minusSign`||e.type===`plusSign`?x.push({type:e.type,value:e.value}):x.push({type:`literal`,value:e.value}));return x}function l(t,n){if(!Number.isInteger(n)||!Number.isInteger(t)||t<0||n<t||n>r)throw new e.CoinsError(`FORMAT_ERROR`,`Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r}`)}function u(t,n,r,i){let a=`${t}\0${n}\0${r}\0${i}`,s=o.get(a);if(s)return s;try{let e=new Intl.NumberFormat(t,{currency:n,currencyDisplay:r,maximumFractionDigits:1,minimumFractionDigits:1,style:`currency`}).formatToParts(i?-1.1:1.1);return o.set(a,e),e}catch(r){throw new e.CoinsError(`FORMAT_ERROR`,`Cannot format currency "${n}" for locale "${t}"`,{cause:r})}}function d(t){let n=a.get(t);if(n)return n;try{let e=new Intl.NumberFormat(t,{maximumFractionDigits:0,useGrouping:!0});return a.set(t,e),e}catch(n){throw new e.CoinsError(`FORMAT_ERROR`,`Cannot create number formatter for locale "${t}"`,{cause:n})}}exports.format=s,exports.formatParts=c;
2
2
  //# sourceMappingURL=format.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import type { FormatOptions, Money, MoneyFormatPart } from './types';\n\nimport { roundDivision } from './decimal';\nimport { CoinsError } from './errors';\nimport { isMoney } from './money';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst integerFormatters = new Map<string, Intl.NumberFormat>();\nconst templates = new Map<string, Intl.NumberFormatPart[]>();\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n if (!isMoney(value)) throw new CoinsError('INVALID_MONEY', 'format() requires canonical money');\n\n const {\n locale = 'en-US',\n maximumFractionDigits = value.currency.minorUnit,\n minimumFractionDigits = value.currency.minorUnit,\n style = 'symbol',\n } = options;\n\n validateFractionDigits(minimumFractionDigits, maximumFractionDigits);\n\n const targetScale = 10n ** BigInt(maximumFractionDigits);\n const scaled =\n maximumFractionDigits >= value.currency.minorUnit\n ? value.amount * 10n ** BigInt(maximumFractionDigits - value.currency.minorUnit)\n : roundDivision(\n value.amount,\n 10n ** BigInt(value.currency.minorUnit - maximumFractionDigits),\n 'halfAwayFromZero',\n );\n const negative = scaled < 0n;\n const absolute = negative ? -scaled : scaled;\n const integer = absolute / targetScale;\n const rawFraction =\n maximumFractionDigits === 0 ? '' : (absolute % targetScale).toString().padStart(maximumFractionDigits, '0');\n const fraction = rawFraction.replace(\n new RegExp(`0{0,${Math.max(0, maximumFractionDigits - minimumFractionDigits)}}$`),\n '',\n );\n const template = getTemplate(locale, value.currency.code, style, negative);\n const parts: MoneyFormatPart[] = [];\n let insertedInteger = false;\n\n for (const part of template) {\n if (part.type === 'group') continue;\n\n if (part.type === 'integer') {\n if (!insertedInteger) {\n parts.push({ type: 'integer', value: getIntegerFormatter(locale).format(integer) });\n insertedInteger = true;\n }\n } else if (part.type === 'decimal') {\n if (fraction) parts.push({ type: 'decimal', value: part.value });\n } else if (part.type === 'fraction') {\n if (fraction) parts.push({ type: 'fraction', value: fraction });\n } else if (part.type === 'currency' || part.type === 'minusSign' || part.type === 'plusSign') {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n\nfunction validateFractionDigits(minimum: number, maximum: number): void {\n if (\n !Number.isInteger(maximum) ||\n !Number.isInteger(minimum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > MAX_FRACTION_DIGITS\n ) {\n throw new CoinsError(\n 'INVALID_ROUNDING',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS}`,\n );\n }\n}\n\nfunction getTemplate(\n locale: string,\n currency: string,\n style: NonNullable<FormatOptions['style']>,\n negative: boolean,\n): Intl.NumberFormatPart[] {\n const key = `${locale}\\0${currency}\\0${style}\\0${negative}`;\n const cached = templates.get(key);\n\n if (cached) return cached;\n\n try {\n const template = new Intl.NumberFormat(locale, {\n currency,\n currencyDisplay: style,\n maximumFractionDigits: 1,\n minimumFractionDigits: 1,\n style: 'currency',\n }).formatToParts(negative ? -1.1 : 1.1);\n\n templates.set(key, template);\n\n return template;\n } catch (error) {\n throw new CoinsError('INVALID_CURRENCY', `Cannot format currency \"${currency}\" for locale \"${locale}\"`, {\n cause: error,\n });\n }\n}\n\nfunction getIntegerFormatter(locale: string): Intl.NumberFormat {\n const cached = integerFormatters.get(locale);\n\n if (cached) return cached;\n\n try {\n const formatter = new Intl.NumberFormat(locale, { maximumFractionDigits: 0, useGrouping: true });\n\n integerFormatters.set(locale, formatter);\n\n return formatter;\n } catch (error) {\n throw new CoinsError('INVALID_ROUNDING', `Cannot create number formatter for locale \"${locale}\"`, { cause: error });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { roundDivision } from './decimal';\nimport { CoinsError } from './errors';\nimport { isMoney } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst integerFormatters = new Map<string, Intl.NumberFormat>();\nconst templates = new Map<string, Intl.NumberFormatPart[]>();\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n if (!isMoney(value)) throw new CoinsError('INVALID_MONEY', 'format() requires canonical money');\n\n const {\n locale = 'en-US',\n maximumFractionDigits = value.currency.minorUnit,\n minimumFractionDigits = value.currency.minorUnit,\n rounding = defaultFormatRounding,\n style = 'symbol',\n } = options;\n\n validateFractionDigits(minimumFractionDigits, maximumFractionDigits);\n\n const targetScale = 10n ** BigInt(maximumFractionDigits);\n const scaled =\n maximumFractionDigits >= value.currency.minorUnit\n ? value.amount * 10n ** BigInt(maximumFractionDigits - value.currency.minorUnit)\n : roundDivision(value.amount, 10n ** BigInt(value.currency.minorUnit - maximumFractionDigits), rounding);\n const negative = scaled < 0n;\n const absolute = negative ? -scaled : scaled;\n const integer = absolute / targetScale;\n const rawFraction =\n maximumFractionDigits === 0 ? '' : (absolute % targetScale).toString().padStart(maximumFractionDigits, '0');\n const fraction = rawFraction.replace(\n new RegExp(`0{0,${Math.max(0, maximumFractionDigits - minimumFractionDigits)}}$`),\n '',\n );\n const template = getTemplate(locale, value.currency.code, style, negative);\n const parts: MoneyFormatPart[] = [];\n let insertedInteger = false;\n\n for (const part of template) {\n if (part.type === 'group') continue;\n\n if (part.type === 'integer') {\n if (!insertedInteger) {\n parts.push({ type: 'integer', value: getIntegerFormatter(locale).format(integer) });\n insertedInteger = true;\n }\n } else if (part.type === 'decimal') {\n if (fraction) parts.push({ type: 'decimal', value: part.value });\n } else if (part.type === 'fraction') {\n if (fraction) parts.push({ type: 'fraction', value: fraction });\n } else if (part.type === 'currency' || part.type === 'minusSign' || part.type === 'plusSign') {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n\nfunction validateFractionDigits(minimum: number, maximum: number): void {\n if (\n !Number.isInteger(maximum) ||\n !Number.isInteger(minimum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > MAX_FRACTION_DIGITS\n ) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS}`,\n );\n }\n}\n\nfunction getTemplate(\n locale: string,\n currency: string,\n style: NonNullable<FormatOptions['style']>,\n negative: boolean,\n): Intl.NumberFormatPart[] {\n const key = `${locale}\\0${currency}\\0${style}\\0${negative}`;\n const cached = templates.get(key);\n\n if (cached) return cached;\n\n try {\n const template = new Intl.NumberFormat(locale, {\n currency,\n currencyDisplay: style,\n maximumFractionDigits: 1,\n minimumFractionDigits: 1,\n style: 'currency',\n }).formatToParts(negative ? -1.1 : 1.1);\n\n templates.set(key, template);\n\n return template;\n } catch (error) {\n throw new CoinsError('FORMAT_ERROR', `Cannot format currency \"${currency}\" for locale \"${locale}\"`, {\n cause: error,\n });\n }\n}\n\nfunction getIntegerFormatter(locale: string): Intl.NumberFormat {\n const cached = integerFormatters.get(locale);\n\n if (cached) return cached;\n\n try {\n const formatter = new Intl.NumberFormat(locale, { maximumFractionDigits: 0, useGrouping: true });\n\n integerFormatters.set(locale, formatter);\n\n return formatter;\n } catch (error) {\n throw new CoinsError('FORMAT_ERROR', `Cannot create number formatter for locale \"${locale}\"`, { cause: error });\n }\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAUrE,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAIxE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,aAAkB,GAAG,eAAe,EAAE,CAsDxF"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,eAAe,EAAgB,MAAM,SAAS,CAAC;AAOnF,wBAAgB,MAAM,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAIxE;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,GAAE,aAAkB,GAAG,eAAe,EAAE,CAmDxF"}
package/dist/format.js CHANGED
@@ -2,38 +2,38 @@ import { CoinsError as e } from "./errors.js";
2
2
  import { roundDivision as t } from "./decimal.js";
3
3
  import { isMoney as n } from "./money.js";
4
4
  //#region src/format.ts
5
- var r = 20, i = /* @__PURE__ */ new Map(), a = /* @__PURE__ */ new Map();
6
- function o(e, t = {}) {
7
- return s(e, t).map((e) => e.value).join("");
5
+ var r = 20, i = "halfAwayFromZero", a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
6
+ function s(e, t = {}) {
7
+ return c(e, t).map((e) => e.value).join("");
8
8
  }
9
- function s(r, i = {}) {
9
+ function c(r, a = {}) {
10
10
  if (!n(r)) throw new e("INVALID_MONEY", "format() requires canonical money");
11
- let { locale: a = "en-US", maximumFractionDigits: o = r.currency.minorUnit, minimumFractionDigits: s = r.currency.minorUnit, style: d = "symbol" } = i;
12
- c(s, o);
13
- let f = 10n ** BigInt(o), p = o >= r.currency.minorUnit ? r.amount * 10n ** BigInt(o - r.currency.minorUnit) : t(r.amount, 10n ** BigInt(r.currency.minorUnit - o), "halfAwayFromZero"), m = p < 0n, h = m ? -p : p, g = h / f, _ = (o === 0 ? "" : (h % f).toString().padStart(o, "0")).replace(RegExp(`0{0,${Math.max(0, o - s)}}$`), ""), v = l(a, r.currency.code, d, m), y = [], b = !1;
14
- for (let e of v) e.type !== "group" && (e.type === "integer" ? b ||= (y.push({
11
+ let { locale: o = "en-US", maximumFractionDigits: s = r.currency.minorUnit, minimumFractionDigits: c = r.currency.minorUnit, rounding: f = i, style: p = "symbol" } = a;
12
+ l(c, s);
13
+ let m = 10n ** BigInt(s), h = s >= r.currency.minorUnit ? r.amount * 10n ** BigInt(s - r.currency.minorUnit) : t(r.amount, 10n ** BigInt(r.currency.minorUnit - s), f), g = h < 0n, _ = g ? -h : h, v = _ / m, y = (s === 0 ? "" : (_ % m).toString().padStart(s, "0")).replace(RegExp(`0{0,${Math.max(0, s - c)}}$`), ""), b = u(o, r.currency.code, p, g), x = [], S = !1;
14
+ for (let e of b) e.type !== "group" && (e.type === "integer" ? S ||= (x.push({
15
15
  type: "integer",
16
- value: u(a).format(g)
17
- }), !0) : e.type === "decimal" ? _ && y.push({
16
+ value: d(o).format(v)
17
+ }), !0) : e.type === "decimal" ? y && x.push({
18
18
  type: "decimal",
19
19
  value: e.value
20
- }) : e.type === "fraction" ? _ && y.push({
20
+ }) : e.type === "fraction" ? y && x.push({
21
21
  type: "fraction",
22
- value: _
23
- }) : e.type === "currency" || e.type === "minusSign" || e.type === "plusSign" ? y.push({
22
+ value: y
23
+ }) : e.type === "currency" || e.type === "minusSign" || e.type === "plusSign" ? x.push({
24
24
  type: e.type,
25
25
  value: e.value
26
- }) : y.push({
26
+ }) : x.push({
27
27
  type: "literal",
28
28
  value: e.value
29
29
  }));
30
- return y;
30
+ return x;
31
31
  }
32
- function c(t, n) {
33
- if (!Number.isInteger(n) || !Number.isInteger(t) || t < 0 || n < t || n > r) throw new e("INVALID_ROUNDING", `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r}`);
32
+ function l(t, n) {
33
+ if (!Number.isInteger(n) || !Number.isInteger(t) || t < 0 || n < t || n > r) throw new e("FORMAT_ERROR", `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${r}`);
34
34
  }
35
- function l(t, n, r, i) {
36
- let o = `${t}\0${n}\0${r}\0${i}`, s = a.get(o);
35
+ function u(t, n, r, i) {
36
+ let a = `${t}\0${n}\0${r}\0${i}`, s = o.get(a);
37
37
  if (s) return s;
38
38
  try {
39
39
  let e = new Intl.NumberFormat(t, {
@@ -43,25 +43,25 @@ function l(t, n, r, i) {
43
43
  minimumFractionDigits: 1,
44
44
  style: "currency"
45
45
  }).formatToParts(i ? -1.1 : 1.1);
46
- return a.set(o, e), e;
46
+ return o.set(a, e), e;
47
47
  } catch (r) {
48
- throw new e("INVALID_CURRENCY", `Cannot format currency "${n}" for locale "${t}"`, { cause: r });
48
+ throw new e("FORMAT_ERROR", `Cannot format currency "${n}" for locale "${t}"`, { cause: r });
49
49
  }
50
50
  }
51
- function u(t) {
52
- let n = i.get(t);
51
+ function d(t) {
52
+ let n = a.get(t);
53
53
  if (n) return n;
54
54
  try {
55
55
  let e = new Intl.NumberFormat(t, {
56
56
  maximumFractionDigits: 0,
57
57
  useGrouping: !0
58
58
  });
59
- return i.set(t, e), e;
59
+ return a.set(t, e), e;
60
60
  } catch (n) {
61
- throw new e("INVALID_ROUNDING", `Cannot create number formatter for locale "${t}"`, { cause: n });
61
+ throw new e("FORMAT_ERROR", `Cannot create number formatter for locale "${t}"`, { cause: n });
62
62
  }
63
63
  }
64
64
  //#endregion
65
- export { o as format, s as formatParts };
65
+ export { s as format, c as formatParts };
66
66
 
67
67
  //# sourceMappingURL=format.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["import type { FormatOptions, Money, MoneyFormatPart } from './types';\n\nimport { roundDivision } from './decimal';\nimport { CoinsError } from './errors';\nimport { isMoney } from './money';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst integerFormatters = new Map<string, Intl.NumberFormat>();\nconst templates = new Map<string, Intl.NumberFormatPart[]>();\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n if (!isMoney(value)) throw new CoinsError('INVALID_MONEY', 'format() requires canonical money');\n\n const {\n locale = 'en-US',\n maximumFractionDigits = value.currency.minorUnit,\n minimumFractionDigits = value.currency.minorUnit,\n style = 'symbol',\n } = options;\n\n validateFractionDigits(minimumFractionDigits, maximumFractionDigits);\n\n const targetScale = 10n ** BigInt(maximumFractionDigits);\n const scaled =\n maximumFractionDigits >= value.currency.minorUnit\n ? value.amount * 10n ** BigInt(maximumFractionDigits - value.currency.minorUnit)\n : roundDivision(\n value.amount,\n 10n ** BigInt(value.currency.minorUnit - maximumFractionDigits),\n 'halfAwayFromZero',\n );\n const negative = scaled < 0n;\n const absolute = negative ? -scaled : scaled;\n const integer = absolute / targetScale;\n const rawFraction =\n maximumFractionDigits === 0 ? '' : (absolute % targetScale).toString().padStart(maximumFractionDigits, '0');\n const fraction = rawFraction.replace(\n new RegExp(`0{0,${Math.max(0, maximumFractionDigits - minimumFractionDigits)}}$`),\n '',\n );\n const template = getTemplate(locale, value.currency.code, style, negative);\n const parts: MoneyFormatPart[] = [];\n let insertedInteger = false;\n\n for (const part of template) {\n if (part.type === 'group') continue;\n\n if (part.type === 'integer') {\n if (!insertedInteger) {\n parts.push({ type: 'integer', value: getIntegerFormatter(locale).format(integer) });\n insertedInteger = true;\n }\n } else if (part.type === 'decimal') {\n if (fraction) parts.push({ type: 'decimal', value: part.value });\n } else if (part.type === 'fraction') {\n if (fraction) parts.push({ type: 'fraction', value: fraction });\n } else if (part.type === 'currency' || part.type === 'minusSign' || part.type === 'plusSign') {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n\nfunction validateFractionDigits(minimum: number, maximum: number): void {\n if (\n !Number.isInteger(maximum) ||\n !Number.isInteger(minimum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > MAX_FRACTION_DIGITS\n ) {\n throw new CoinsError(\n 'INVALID_ROUNDING',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS}`,\n );\n }\n}\n\nfunction getTemplate(\n locale: string,\n currency: string,\n style: NonNullable<FormatOptions['style']>,\n negative: boolean,\n): Intl.NumberFormatPart[] {\n const key = `${locale}\\0${currency}\\0${style}\\0${negative}`;\n const cached = templates.get(key);\n\n if (cached) return cached;\n\n try {\n const template = new Intl.NumberFormat(locale, {\n currency,\n currencyDisplay: style,\n maximumFractionDigits: 1,\n minimumFractionDigits: 1,\n style: 'currency',\n }).formatToParts(negative ? -1.1 : 1.1);\n\n templates.set(key, template);\n\n return template;\n } catch (error) {\n throw new CoinsError('INVALID_CURRENCY', `Cannot format currency \"${currency}\" for locale \"${locale}\"`, {\n cause: error,\n });\n }\n}\n\nfunction getIntegerFormatter(locale: string): Intl.NumberFormat {\n const cached = integerFormatters.get(locale);\n\n if (cached) return cached;\n\n try {\n const formatter = new Intl.NumberFormat(locale, { maximumFractionDigits: 0, useGrouping: true });\n\n integerFormatters.set(locale, formatter);\n\n return formatter;\n } catch (error) {\n throw new CoinsError('INVALID_ROUNDING', `Cannot create number formatter for locale \"${locale}\"`, { cause: error });\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { roundDivision } from './decimal';\nimport { CoinsError } from './errors';\nimport { isMoney } from './money';\nimport type { FormatOptions, Money, MoneyFormatPart, RoundingMode } from './types';\n\nconst MAX_FRACTION_DIGITS = 20;\nconst defaultFormatRounding: RoundingMode = 'halfAwayFromZero';\nconst integerFormatters = new Map<string, Intl.NumberFormat>();\nconst templates = new Map<string, Intl.NumberFormatPart[]>();\n\nexport function format(value: Money, options: FormatOptions = {}): string {\n return formatParts(value, options)\n .map((part) => part.value)\n .join('');\n}\n\nexport function formatParts(value: Money, options: FormatOptions = {}): MoneyFormatPart[] {\n if (!isMoney(value)) throw new CoinsError('INVALID_MONEY', 'format() requires canonical money');\n\n const {\n locale = 'en-US',\n maximumFractionDigits = value.currency.minorUnit,\n minimumFractionDigits = value.currency.minorUnit,\n rounding = defaultFormatRounding,\n style = 'symbol',\n } = options;\n\n validateFractionDigits(minimumFractionDigits, maximumFractionDigits);\n\n const targetScale = 10n ** BigInt(maximumFractionDigits);\n const scaled =\n maximumFractionDigits >= value.currency.minorUnit\n ? value.amount * 10n ** BigInt(maximumFractionDigits - value.currency.minorUnit)\n : roundDivision(value.amount, 10n ** BigInt(value.currency.minorUnit - maximumFractionDigits), rounding);\n const negative = scaled < 0n;\n const absolute = negative ? -scaled : scaled;\n const integer = absolute / targetScale;\n const rawFraction =\n maximumFractionDigits === 0 ? '' : (absolute % targetScale).toString().padStart(maximumFractionDigits, '0');\n const fraction = rawFraction.replace(\n new RegExp(`0{0,${Math.max(0, maximumFractionDigits - minimumFractionDigits)}}$`),\n '',\n );\n const template = getTemplate(locale, value.currency.code, style, negative);\n const parts: MoneyFormatPart[] = [];\n let insertedInteger = false;\n\n for (const part of template) {\n if (part.type === 'group') continue;\n\n if (part.type === 'integer') {\n if (!insertedInteger) {\n parts.push({ type: 'integer', value: getIntegerFormatter(locale).format(integer) });\n insertedInteger = true;\n }\n } else if (part.type === 'decimal') {\n if (fraction) parts.push({ type: 'decimal', value: part.value });\n } else if (part.type === 'fraction') {\n if (fraction) parts.push({ type: 'fraction', value: fraction });\n } else if (part.type === 'currency' || part.type === 'minusSign' || part.type === 'plusSign') {\n parts.push({ type: part.type, value: part.value });\n } else {\n parts.push({ type: 'literal', value: part.value });\n }\n }\n\n return parts;\n}\n\nfunction validateFractionDigits(minimum: number, maximum: number): void {\n if (\n !Number.isInteger(maximum) ||\n !Number.isInteger(minimum) ||\n minimum < 0 ||\n maximum < minimum ||\n maximum > MAX_FRACTION_DIGITS\n ) {\n throw new CoinsError(\n 'FORMAT_ERROR',\n `Fraction digits must be integers satisfying 0 ≤ minimum ≤ maximum ≤ ${MAX_FRACTION_DIGITS}`,\n );\n }\n}\n\nfunction getTemplate(\n locale: string,\n currency: string,\n style: NonNullable<FormatOptions['style']>,\n negative: boolean,\n): Intl.NumberFormatPart[] {\n const key = `${locale}\\0${currency}\\0${style}\\0${negative}`;\n const cached = templates.get(key);\n\n if (cached) return cached;\n\n try {\n const template = new Intl.NumberFormat(locale, {\n currency,\n currencyDisplay: style,\n maximumFractionDigits: 1,\n minimumFractionDigits: 1,\n style: 'currency',\n }).formatToParts(negative ? -1.1 : 1.1);\n\n templates.set(key, template);\n\n return template;\n } catch (error) {\n throw new CoinsError('FORMAT_ERROR', `Cannot format currency \"${currency}\" for locale \"${locale}\"`, {\n cause: error,\n });\n }\n}\n\nfunction getIntegerFormatter(locale: string): Intl.NumberFormat {\n const cached = integerFormatters.get(locale);\n\n if (cached) return cached;\n\n try {\n const formatter = new Intl.NumberFormat(locale, { maximumFractionDigits: 0, useGrouping: true });\n\n integerFormatters.set(locale, formatter);\n\n return formatter;\n } catch (error) {\n throw new CoinsError('FORMAT_ERROR', `Cannot create number formatter for locale \"${locale}\"`, { cause: error });\n }\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./currency.cjs"),r=require("./money.cjs"),i=require("./aggregate.cjs"),a=require("./exchange.cjs"),o=require("./format.cjs"),s=require("./serialization.cjs");exports.BHD=n.BHD,exports.CoinsError=e.CoinsError,exports.CurrencyMismatchError=e.CurrencyMismatchError,exports.EUR=n.EUR,exports.GBP=n.GBP,exports.InvalidCurrencyError=e.InvalidCurrencyError,exports.JPY=n.JPY,exports.KRW=n.KRW,exports.KWD=n.KWD,exports.USD=n.USD,exports.abs=r.abs,exports.add=r.add,exports.allocate=i.allocate,exports.clamp=i.clamp,exports.compare=r.compare,exports.currency=n.currency,exports.decimal=t.decimal,exports.defineCurrency=n.defineCurrency,exports.divide=r.divide,exports.exchange=a.exchange,exports.exchangeRate=a.exchangeRate,exports.format=o.format,exports.formatParts=o.formatParts,exports.isCurrency=n.isCurrency,exports.isMoney=r.isMoney,exports.money=r.money,exports.multiply=r.multiply,exports.negate=r.negate,exports.parseMoney=r.parseMoney,exports.parseMoneyJSON=s.parseMoneyJSON,exports.round=r.round,exports.subtract=r.subtract,exports.sum=i.sum,exports.toDecimal=r.toDecimal,exports.toJSON=s.toJSON;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./errors.cjs"),t=require("./currency.cjs"),n=require("./money.cjs"),r=require("./aggregate.cjs"),i=require("./exchange.cjs"),a=require("./format.cjs"),o=require("./serialization.cjs");exports.BHD=t.BHD,exports.CoinsError=e.CoinsError,exports.CurrencyMismatchError=e.CurrencyMismatchError,exports.EUR=t.EUR,exports.GBP=t.GBP,exports.InvalidCurrencyError=e.InvalidCurrencyError,exports.JPY=t.JPY,exports.KRW=t.KRW,exports.KWD=t.KWD,exports.USD=t.USD,exports.abs=n.abs,exports.add=n.add,exports.allocate=r.allocate,exports.clamp=n.clamp,exports.compare=n.compare,exports.currency=t.currency,exports.defineCurrency=t.defineCurrency,exports.divide=n.divide,exports.exchange=i.exchange,exports.exchangeRate=i.exchangeRate,exports.format=a.format,exports.formatParts=a.formatParts,exports.isCurrency=t.isCurrency,exports.isMoney=n.isMoney,exports.money=n.money,exports.multiply=n.multiply,exports.negate=n.negate,exports.parseMoney=n.parseMoney,exports.parseMoneyJSON=o.parseMoneyJSON,exports.round=n.round,exports.subtract=n.subtract,exports.sum=r.sum,exports.toDecimal=n.toDecimal,exports.toJSON=o.toJSON;
package/dist/index.d.ts CHANGED
@@ -1,11 +1,10 @@
1
- export { clamp, allocate, sum } from './aggregate';
1
+ export { allocate, sum } from './aggregate';
2
2
  export { BHD, currency, defineCurrency, EUR, GBP, isCurrency, JPY, KRW, KWD, USD } from './currency';
3
- export { decimal } from './decimal';
4
- export { CoinsError, CurrencyMismatchError, InvalidCurrencyError } from './errors';
5
3
  export type { CoinsErrorCode } from './errors';
4
+ export { CoinsError, CurrencyMismatchError, InvalidCurrencyError } from './errors';
6
5
  export { exchange, exchangeRate } from './exchange';
7
6
  export { format, formatParts } from './format';
8
- export { abs, add, compare, divide, isMoney, money, multiply, negate, parseMoney, round, subtract, toDecimal, } from './money';
7
+ export { abs, add, clamp, compare, divide, isMoney, money, multiply, negate, parseMoney, round, subtract, toDecimal, } from './money';
9
8
  export { parseMoneyJSON, toJSON } from './serialization';
10
9
  export type { Currency, CurrencyCode, Decimal, ExchangeRate, FormatOptions, Money, MoneyFormatPart, MoneyJSON, RoundingMode, } from './types';
11
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACrG,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACnF,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EACL,GAAG,EACH,GAAG,EACH,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,KAAK,EACL,QAAQ,EACR,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,KAAK,EACL,eAAe,EACf,SAAS,EACT,YAAY,GACb,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACrG,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACnF,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EACL,GAAG,EACH,GAAG,EACH,KAAK,EACL,OAAO,EACP,MAAM,EACN,OAAO,EACP,KAAK,EACL,QAAQ,EACR,MAAM,EACN,UAAU,EACV,KAAK,EACL,QAAQ,EACR,SAAS,GACV,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AACzD,YAAY,EACV,QAAQ,EACR,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,aAAa,EACb,KAAK,EACL,eAAe,EACf,SAAS,EACT,YAAY,GACb,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  import { CoinsError as e, CurrencyMismatchError as t, InvalidCurrencyError as n } from "./errors.js";
2
- import { decimal as r } from "./decimal.js";
3
- import { BHD as i, EUR as a, GBP as o, JPY as s, KRW as c, KWD as l, USD as u, currency as d, defineCurrency as f, isCurrency as p } from "./currency.js";
4
- import { abs as m, add as h, compare as g, divide as _, isMoney as v, money as y, multiply as b, negate as x, parseMoney as S, round as C, subtract as w, toDecimal as T } from "./money.js";
5
- import { allocate as E, clamp as D, sum as O } from "./aggregate.js";
6
- import { exchange as k, exchangeRate as A } from "./exchange.js";
7
- import { format as j, formatParts as M } from "./format.js";
8
- import { parseMoneyJSON as N, toJSON as P } from "./serialization.js";
9
- export { i as BHD, e as CoinsError, t as CurrencyMismatchError, a as EUR, o as GBP, n as InvalidCurrencyError, s as JPY, c as KRW, l as KWD, u as USD, m as abs, h as add, E as allocate, D as clamp, g as compare, d as currency, r as decimal, f as defineCurrency, _ as divide, k as exchange, A as exchangeRate, j as format, M as formatParts, p as isCurrency, v as isMoney, y as money, b as multiply, x as negate, S as parseMoney, N as parseMoneyJSON, C as round, w as subtract, O as sum, T as toDecimal, P as toJSON };
2
+ import { BHD as r, EUR as i, GBP as a, JPY as o, KRW as s, KWD as c, USD as l, currency as u, defineCurrency as d, isCurrency as f } from "./currency.js";
3
+ import { abs as p, add as m, clamp as h, compare as g, divide as _, isMoney as v, money as y, multiply as b, negate as x, parseMoney as S, round as C, subtract as w, toDecimal as T } from "./money.js";
4
+ import { allocate as E, sum as D } from "./aggregate.js";
5
+ import { exchange as O, exchangeRate as k } from "./exchange.js";
6
+ import { format as A, formatParts as j } from "./format.js";
7
+ import { parseMoneyJSON as M, toJSON as N } from "./serialization.js";
8
+ export { r as BHD, e as CoinsError, t as CurrencyMismatchError, i as EUR, a as GBP, n as InvalidCurrencyError, o as JPY, s as KRW, c as KWD, l as USD, p as abs, m as add, E as allocate, h as clamp, g as compare, u as currency, d as defineCurrency, _ as divide, O as exchange, k as exchangeRate, A as format, j as formatParts, f as isCurrency, v as isMoney, y as money, b as multiply, x as negate, S as parseMoney, M as parseMoneyJSON, C as round, w as subtract, D as sum, T as toDecimal, N as toJSON };
package/dist/money.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./currency.cjs");var r=`halfAwayFromZero`;function i(n,i,a){if(b(i),typeof n==`bigint`){if(!a||!(`unit`in a)||a.unit!==`minor`)throw new e.CoinsError(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return _(n,i)}if(a&&`unit`in a)throw new e.CoinsError(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let o=t.decimal(n),s=o.numerator*10n**BigInt(i.minorUnit);if(s%o.denominator!==0n&&!a)throw new e.CoinsError(`INVALID_MONEY`,`Amount "${n}" exceeds ${i.code} precision; provide rounding`);return _(t.roundDivision(s,o.denominator,a?.rounding??r),i)}function a(t){if(!x(t))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a plain data object`);let r=Object.getOwnPropertyDescriptors(t),i=r.amount,a=r.currency;if(!S(i)||!S(a))throw new e.CoinsError(`INVALID_MONEY`,`Money properties must be plain data values`);if(typeof i.value!=`bigint`||!n.isCurrency(a.value))throw new e.CoinsError(`INVALID_MONEY`,`Money must contain bigint amount and a registered currency`);return _(i.value,a.value)}function o(e){try{return a(e),!0}catch{return!1}}function s(e,t){return y(e,t),_(e.amount+t.amount,e.currency)}function c(e,t){return y(e,t),_(e.amount-t.amount,e.currency)}function l(e,n,i={}){v(e);let a=t.decimal(n);return _(t.roundDivision(e.amount*a.numerator,a.denominator,i.rounding??r),e.currency)}function u(n,i,a={}){v(n);let o=t.decimal(i);if(o.numerator===0n)throw new e.CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);return _(t.roundDivision(n.amount*o.denominator,o.numerator<0n?-o.numerator:o.numerator,a.rounding??r)*(o.numerator<0n?-1n:1n),n.currency)}function d(e,t){return y(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function f(e){return v(e),_(e.amount<0n?-e.amount:e.amount,e.currency)}function p(e){return v(e),_(-e.amount,e.currency)}function m(n,i){v(n);let{fractionDigits:a,rounding:o=r}=i;if(!Number.isInteger(a)||a<0||a>n.currency.minorUnit)throw new e.CoinsError(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${n.currency.minorUnit}`);let s=10n**BigInt(n.currency.minorUnit-a);return _(t.roundDivision(n.amount,s,o)*s,n.currency)}function h(e){return v(e),t.toDecimalString(e.amount,e.currency.minorUnit)}function g(e,t){return b(t),_(e,t)}function _(e,t){return Object.freeze({amount:e,currency:t})}function v(t){if(!Object.isFrozen(t)||!n.isCurrency(t.currency))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a canonical Coins value`)}function y(t,n){if(v(t),v(n),t.currency!==n.currency)throw new e.CurrencyMismatchError(t.currency.code,n.currency.code)}function b(t){if(!n.isCurrency(t))throw new e.CoinsError(`INVALID_CURRENCY`,`Money requires a registered currency`)}function x(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function S(e){return e!==void 0&&`value`in e&&e.get===void 0&&e.set===void 0}exports.abs=f,exports.add=s,exports.compare=d,exports.divide=u,exports.isMoney=o,exports.money=i,exports.multiply=l,exports.negate=p,exports.parseMoney=a,exports.round=m,exports.subtract=c,exports.toDecimal=h,exports.withMinor=g;
1
+ const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./currency.cjs");var r=`halfAwayFromZero`;function i(n,i,a){if(x(i),typeof n==`bigint`){if(!a||!(`unit`in a)||a.unit!==`minor`)throw new e.CoinsError(`INVALID_MONEY`,`Bigint amounts require { unit: 'minor' }`);return v(n,i)}if(a&&`unit`in a)throw new e.CoinsError(`INVALID_MONEY`,`Decimal strings do not accept a unit option`);let o=t.decimal(n),s=o.numerator*10n**BigInt(i.minorUnit);if(s%o.denominator!==0n&&!a)throw new e.CoinsError(`INVALID_MONEY`,`Amount "${n}" exceeds ${i.code} precision; provide rounding`);return v(t.roundDivision(s,o.denominator,a?.rounding??r),i)}function a(t){if(!S(t))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a plain data object`);let r=Object.getOwnPropertyDescriptors(t),i=r.amount,a=r.currency;if(!C(i)||!C(a))throw new e.CoinsError(`INVALID_MONEY`,`Money properties must be plain data values`);if(typeof i.value!=`bigint`||!n.isCurrency(a.value))throw new e.CoinsError(`INVALID_MONEY`,`Money must contain bigint amount and a registered currency`);return v(i.value,a.value)}function o(e){try{return a(e),!0}catch{return!1}}function s(e,t){return b(e,t),v(e.amount+t.amount,e.currency)}function c(e,t){return b(e,t),v(e.amount-t.amount,e.currency)}function l(e,n,i={}){y(e);let a=t.decimal(n);return v(t.roundDivision(e.amount*a.numerator,a.denominator,i.rounding??r),e.currency)}function u(n,i,a={}){y(n);let o=t.decimal(i);if(o.numerator===0n)throw new e.CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let s=o.numerator<0n,c=s?-o.numerator:o.numerator,l=t.roundDivision(n.amount*o.denominator,c,a.rounding??r);return v(s?-l:l,n.currency)}function d(e,t){return b(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function f(t,n){if(d(n.min,n.max)===1)throw new e.CoinsError(`INVALID_MONEY`,`Clamp minimum cannot exceed maximum`);return d(t,n.min)===-1?n.min:d(t,n.max)===1?n.max:t}function p(e){return y(e),v(e.amount<0n?-e.amount:e.amount,e.currency)}function m(e){return y(e),v(-e.amount,e.currency)}function h(n,i){y(n);let{fractionDigits:a,rounding:o=r}=i;if(!Number.isInteger(a)||a<0||a>n.currency.minorUnit)throw new e.CoinsError(`INVALID_ROUNDING`,`fractionDigits must be an integer from 0 to ${n.currency.minorUnit}`);let s=10n**BigInt(n.currency.minorUnit-a);return v(t.roundDivision(n.amount,s,o)*s,n.currency)}function g(e){return y(e),t.toDecimalString(e.amount,e.currency.minorUnit)}function _(e,t){return x(t),v(e,t)}function v(e,t){return Object.freeze({amount:e,currency:t})}function y(t){if(!Object.isFrozen(t)||!n.isCurrency(t.currency))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a canonical Coins value`)}function b(t,n){if(y(t),y(n),t.currency!==n.currency)throw new e.CurrencyMismatchError(t.currency.code,n.currency.code)}function x(t){if(!n.isCurrency(t))throw new e.CoinsError(`INVALID_CURRENCY`,`Money requires a registered currency`)}function S(e){if(typeof e!=`object`||!e)return!1;let t=Object.getPrototypeOf(e);return t===Object.prototype||t===null}function C(e){return e!==void 0&&`value`in e&&e.get===void 0&&e.set===void 0}exports.abs=p,exports.add=s,exports.clamp=f,exports.compare=d,exports.divide=u,exports.isMoney=o,exports.money=i,exports.multiply=l,exports.negate=m,exports.parseMoney=a,exports.round=h,exports.subtract=c,exports.toDecimal=g,exports.withMinor=_;
2
2
  //# sourceMappingURL=money.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"money.cjs","names":[],"sources":["../src/money.ts"],"sourcesContent":["import type { Currency, Money, RoundingMode } from './types';\n\nimport { isCurrency } from './currency';\nimport { decimal, roundDivision, toDecimalString } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nexport function money<C extends Currency>(amount: string, currency: C): Money<C>;\nexport function money<C extends Currency>(amount: string, currency: C, options: { rounding: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && !options) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\nexport function parseMoney(value: unknown): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n if (typeof amountDescriptor.value !== 'bigint' || !isCurrency(currencyDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must contain bigint amount and a registered currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n}\n\nexport function isMoney(value: unknown): value is Money {\n try {\n parseMoney(value);\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n return createMoney(\n roundDivision(\n value.amount * scalar.denominator,\n scalar.numerator < 0n ? -scalar.numerator : scalar.numerator,\n options.rounding ?? defaultRounding,\n ) * (scalar.numerator < 0n ? -1n : 1n),\n value.currency,\n );\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function withMinor<C extends Currency>(amount: bigint, currency: C): Money<C> {\n assertCurrency(currency);\n\n return createMoney(amount, currency);\n}\n\nfunction createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n return Object.freeze({ amount, currency }) as Money<C>;\n}\n\nfunction assertMoney(value: Money): void {\n if (!Object.isFrozen(value) || !isCurrency(value.currency)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical Coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: Currency): void {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"money.cjs","names":[],"sources":["../src/money.ts"],"sourcesContent":["import { isCurrency } from './currency';\nimport { decimal, roundDivision, toDecimalString } from './decimal';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\nexport function money<C extends Currency>(amount: string, currency: C): Money<C>;\nexport function money<C extends Currency>(amount: string, currency: C, options: { rounding: RoundingMode }): Money<C>;\nexport function money<C extends Currency>(amount: bigint, currency: C, options: { unit: 'minor' }): Money<C>;\nexport function money<C extends Currency>(\n amount: bigint | string,\n currency: C,\n options?: { rounding: RoundingMode } | { unit: 'minor' },\n): Money<C> {\n assertCurrency(currency);\n\n if (typeof amount === 'bigint') {\n if (!options || !('unit' in options) || options.unit !== 'minor') {\n throw new CoinsError('INVALID_MONEY', \"Bigint amounts require { unit: 'minor' }\");\n }\n\n return createMoney(amount, currency);\n }\n\n if (options && 'unit' in options)\n throw new CoinsError('INVALID_MONEY', 'Decimal strings do not accept a unit option');\n\n const value = decimal(amount);\n const scaled = value.numerator * 10n ** BigInt(currency.minorUnit);\n const remainder = scaled % value.denominator;\n\n if (remainder !== 0n && !options) {\n throw new CoinsError('INVALID_MONEY', `Amount \"${amount}\" exceeds ${currency.code} precision; provide rounding`);\n }\n\n return createMoney(roundDivision(scaled, value.denominator, options?.rounding ?? defaultRounding), currency);\n}\n\nexport function parseMoney(value: unknown): Money {\n if (!isPlainDataObject(value)) throw new CoinsError('INVALID_MONEY', 'Money must be a plain data object');\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n const amountDescriptor = descriptors.amount;\n const currencyDescriptor = descriptors.currency;\n\n if (!isDataProperty(amountDescriptor) || !isDataProperty(currencyDescriptor)) {\n throw new CoinsError('INVALID_MONEY', 'Money properties must be plain data values');\n }\n\n if (typeof amountDescriptor.value !== 'bigint' || !isCurrency(currencyDescriptor.value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must contain bigint amount and a registered currency');\n }\n\n return createMoney(amountDescriptor.value, currencyDescriptor.value);\n}\n\nexport function isMoney(value: unknown): value is Money {\n try {\n parseMoney(value);\n\n return true;\n } catch {\n return false;\n }\n}\n\nexport function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount + right.amount, left.currency);\n}\n\nexport function subtract<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C> {\n assertSameCurrency(left, right);\n\n return createMoney(left.amount - right.amount, left.currency);\n}\n\nexport function multiply<C extends Currency>(\n value: Money<C>,\n factor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(factor);\n\n return createMoney(\n roundDivision(value.amount * scalar.numerator, scalar.denominator, options.rounding ?? defaultRounding),\n value.currency,\n );\n}\n\nexport function divide<C extends Currency>(\n value: Money<C>,\n divisor: string,\n options: { rounding?: RoundingMode } = {},\n): Money<C> {\n assertMoney(value);\n\n const scalar = decimal(divisor);\n\n if (scalar.numerator === 0n) throw new CoinsError('DIVISION_BY_ZERO', 'Cannot divide money by zero');\n\n const negative = scalar.numerator < 0n;\n const absoluteDivisor = negative ? -scalar.numerator : scalar.numerator;\n const quotient = roundDivision(\n value.amount * scalar.denominator,\n absoluteDivisor,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(negative ? -quotient : quotient, value.currency);\n}\n\nexport function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1 {\n assertSameCurrency(left, right);\n\n return left.amount === right.amount ? 0 : left.amount < right.amount ? -1 : 1;\n}\n\nexport function clamp<C extends Currency>(\n value: Money<C>,\n options: { max: Money<NoInfer<C>>; min: Money<NoInfer<C>> },\n): Money<C> {\n if (compare(options.min, options.max) === 1) {\n throw new CoinsError('INVALID_MONEY', 'Clamp minimum cannot exceed maximum');\n }\n\n return compare(value, options.min) === -1 ? options.min : compare(value, options.max) === 1 ? options.max : value;\n}\n\nexport function abs<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(value.amount < 0n ? -value.amount : value.amount, value.currency);\n}\n\nexport function negate<C extends Currency>(value: Money<C>): Money<C> {\n assertMoney(value);\n\n return createMoney(-value.amount, value.currency);\n}\n\nexport function round<C extends Currency>(\n value: Money<C>,\n options: { fractionDigits: number; rounding?: RoundingMode },\n): Money<C> {\n assertMoney(value);\n\n const { fractionDigits, rounding = defaultRounding } = options;\n\n if (!Number.isInteger(fractionDigits) || fractionDigits < 0 || fractionDigits > value.currency.minorUnit) {\n throw new CoinsError('INVALID_ROUNDING', `fractionDigits must be an integer from 0 to ${value.currency.minorUnit}`);\n }\n\n const factor = 10n ** BigInt(value.currency.minorUnit - fractionDigits);\n\n return createMoney(roundDivision(value.amount, factor, rounding) * factor, value.currency);\n}\n\nexport function toDecimal(value: Money): string {\n assertMoney(value);\n\n return toDecimalString(value.amount, value.currency.minorUnit);\n}\n\nexport function withMinor<C extends Currency>(amount: bigint, currency: C): Money<C> {\n assertCurrency(currency);\n\n return createMoney(amount, currency);\n}\n\nfunction createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n return Object.freeze({ amount, currency }) as Money<C>;\n}\n\nfunction assertMoney(value: Money): void {\n if (!Object.isFrozen(value) || !isCurrency(value.currency)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical Coins value');\n }\n}\n\nfunction assertSameCurrency(left: Money, right: Money): void {\n assertMoney(left);\n assertMoney(right);\n\n if (left.currency !== right.currency) {\n throw new CurrencyMismatchError(left.currency.code, right.currency.code);\n }\n}\n\nfunction assertCurrency(value: Currency): void {\n if (!isCurrency(value)) throw new CoinsError('INVALID_CURRENCY', 'Money requires a registered currency');\n}\n\nfunction isPlainDataObject(value: unknown): value is Record<PropertyKey, unknown> {\n if (typeof value !== 'object' || value === null) return false;\n\n const prototype = Object.getPrototypeOf(value);\n\n return prototype === Object.prototype || prototype === null;\n}\n\nfunction isDataProperty(\n descriptor: PropertyDescriptor | undefined,\n): descriptor is PropertyDescriptor & { value: unknown } {\n return (\n descriptor !== undefined && 'value' in descriptor && descriptor.get === undefined && descriptor.set === undefined\n );\n}\n"],"mappings":"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"}
package/dist/money.d.ts CHANGED
@@ -17,6 +17,10 @@ export declare function divide<C extends Currency>(value: Money<C>, divisor: str
17
17
  rounding?: RoundingMode;
18
18
  }): Money<C>;
19
19
  export declare function compare<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): -1 | 0 | 1;
20
+ export declare function clamp<C extends Currency>(value: Money<C>, options: {
21
+ max: Money<NoInfer<C>>;
22
+ min: Money<NoInfer<C>>;
23
+ }): Money<C>;
20
24
  export declare function abs<C extends Currency>(value: Money<C>): Money<C>;
21
25
  export declare function negate<C extends Currency>(value: Money<C>): Money<C>;
22
26
  export declare function round<C extends Currency>(value: Money<C>, options: {
@@ -1 +1 @@
1
- {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAQ7D,wBAAgB,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AACjF,wBAAgB,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE;IAAE,QAAQ,EAAE,YAAY,CAAA;CAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AACtH,wBAAgB,KAAK,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AA8B7G,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAgBhD;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,KAAK,CAQtD;AAED,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAI1F;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAI/F;AAED,wBAAgB,QAAQ,CAAC,CAAC,SAAS,QAAQ,EACzC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GACxC,KAAK,CAAC,CAAC,CAAC,CASV;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,QAAQ,EACvC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAO,GACxC,KAAK,CAAC,CAAC,CAAC,CAeV;AAED,wBAAgB,OAAO,CAAC,CAAC,SAAS,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAIhG;AAED,wBAAgB,GAAG,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAIjE;AAED,wBAAgB,MAAM,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAIpE;AAED,wBAAgB,KAAK,CAAC,CAAC,SAAS,QAAQ,EACtC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EACf,OAAO,EAAE;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,YAAY,CAAA;CAAE,GAC3D,KAAK,CAAC,CAAC,CAAC,CAYV;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAI9C;AAED,wBAAgB,SAAS,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAInF"}
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}
package/dist/money.js CHANGED
@@ -4,21 +4,21 @@ import { isCurrency as a } from "./currency.js";
4
4
  //#region src/money.ts
5
5
  var o = "halfAwayFromZero";
6
6
  function s(t, i, a) {
7
- if (C(i), typeof t == "bigint") {
7
+ if (w(i), typeof t == "bigint") {
8
8
  if (!a || !("unit" in a) || a.unit !== "minor") throw new e("INVALID_MONEY", "Bigint amounts require { unit: 'minor' }");
9
- return b(t, i);
9
+ return x(t, i);
10
10
  }
11
11
  if (a && "unit" in a) throw new e("INVALID_MONEY", "Decimal strings do not accept a unit option");
12
12
  let s = n(t), c = s.numerator * 10n ** BigInt(i.minorUnit);
13
13
  if (c % s.denominator !== 0n && !a) throw new e("INVALID_MONEY", `Amount "${t}" exceeds ${i.code} precision; provide rounding`);
14
- return b(r(c, s.denominator, a?.rounding ?? o), i);
14
+ return x(r(c, s.denominator, a?.rounding ?? o), i);
15
15
  }
16
16
  function c(t) {
17
- if (!w(t)) throw new e("INVALID_MONEY", "Money must be a plain data object");
17
+ if (!T(t)) throw new e("INVALID_MONEY", "Money must be a plain data object");
18
18
  let n = Object.getOwnPropertyDescriptors(t), r = n.amount, i = n.currency;
19
- if (!T(r) || !T(i)) throw new e("INVALID_MONEY", "Money properties must be plain data values");
19
+ if (!E(r) || !E(i)) throw new e("INVALID_MONEY", "Money properties must be plain data values");
20
20
  if (typeof r.value != "bigint" || !a(i.value)) throw new e("INVALID_MONEY", "Money must contain bigint amount and a registered currency");
21
- return b(r.value, i.value);
21
+ return x(r.value, i.value);
22
22
  }
23
23
  function l(e) {
24
24
  try {
@@ -28,68 +28,73 @@ function l(e) {
28
28
  }
29
29
  }
30
30
  function u(e, t) {
31
- return S(e, t), b(e.amount + t.amount, e.currency);
31
+ return C(e, t), x(e.amount + t.amount, e.currency);
32
32
  }
33
33
  function d(e, t) {
34
- return S(e, t), b(e.amount - t.amount, e.currency);
34
+ return C(e, t), x(e.amount - t.amount, e.currency);
35
35
  }
36
36
  function f(e, t, i = {}) {
37
- x(e);
37
+ S(e);
38
38
  let a = n(t);
39
- return b(r(e.amount * a.numerator, a.denominator, i.rounding ?? o), e.currency);
39
+ return x(r(e.amount * a.numerator, a.denominator, i.rounding ?? o), e.currency);
40
40
  }
41
41
  function p(t, i, a = {}) {
42
- x(t);
42
+ S(t);
43
43
  let s = n(i);
44
44
  if (s.numerator === 0n) throw new e("DIVISION_BY_ZERO", "Cannot divide money by zero");
45
- return b(r(t.amount * s.denominator, s.numerator < 0n ? -s.numerator : s.numerator, a.rounding ?? o) * (s.numerator < 0n ? -1n : 1n), t.currency);
45
+ let c = s.numerator < 0n, l = c ? -s.numerator : s.numerator, u = r(t.amount * s.denominator, l, a.rounding ?? o);
46
+ return x(c ? -u : u, t.currency);
46
47
  }
47
48
  function m(e, t) {
48
- return S(e, t), e.amount === t.amount ? 0 : e.amount < t.amount ? -1 : 1;
49
+ return C(e, t), e.amount === t.amount ? 0 : e.amount < t.amount ? -1 : 1;
49
50
  }
50
- function h(e) {
51
- return x(e), b(e.amount < 0n ? -e.amount : e.amount, e.currency);
51
+ function h(t, n) {
52
+ if (m(n.min, n.max) === 1) throw new e("INVALID_MONEY", "Clamp minimum cannot exceed maximum");
53
+ return m(t, n.min) === -1 ? n.min : m(t, n.max) === 1 ? n.max : t;
52
54
  }
53
55
  function g(e) {
54
- return x(e), b(-e.amount, e.currency);
56
+ return S(e), x(e.amount < 0n ? -e.amount : e.amount, e.currency);
55
57
  }
56
- function _(t, n) {
57
- x(t);
58
+ function _(e) {
59
+ return S(e), x(-e.amount, e.currency);
60
+ }
61
+ function v(t, n) {
62
+ S(t);
58
63
  let { fractionDigits: i, rounding: a = o } = n;
59
64
  if (!Number.isInteger(i) || i < 0 || i > t.currency.minorUnit) throw new e("INVALID_ROUNDING", `fractionDigits must be an integer from 0 to ${t.currency.minorUnit}`);
60
65
  let s = 10n ** BigInt(t.currency.minorUnit - i);
61
- return b(r(t.amount, s, a) * s, t.currency);
62
- }
63
- function v(e) {
64
- return x(e), i(e.amount, e.currency.minorUnit);
66
+ return x(r(t.amount, s, a) * s, t.currency);
65
67
  }
66
- function y(e, t) {
67
- return C(t), b(e, t);
68
+ function y(e) {
69
+ return S(e), i(e.amount, e.currency.minorUnit);
68
70
  }
69
71
  function b(e, t) {
72
+ return w(t), x(e, t);
73
+ }
74
+ function x(e, t) {
70
75
  return Object.freeze({
71
76
  amount: e,
72
77
  currency: t
73
78
  });
74
79
  }
75
- function x(t) {
80
+ function S(t) {
76
81
  if (!Object.isFrozen(t) || !a(t.currency)) throw new e("INVALID_MONEY", "Money must be a canonical Coins value");
77
82
  }
78
- function S(e, n) {
79
- if (x(e), x(n), e.currency !== n.currency) throw new t(e.currency.code, n.currency.code);
83
+ function C(e, n) {
84
+ if (S(e), S(n), e.currency !== n.currency) throw new t(e.currency.code, n.currency.code);
80
85
  }
81
- function C(t) {
86
+ function w(t) {
82
87
  if (!a(t)) throw new e("INVALID_CURRENCY", "Money requires a registered currency");
83
88
  }
84
- function w(e) {
89
+ function T(e) {
85
90
  if (typeof e != "object" || !e) return !1;
86
91
  let t = Object.getPrototypeOf(e);
87
92
  return t === Object.prototype || t === null;
88
93
  }
89
- function T(e) {
94
+ function E(e) {
90
95
  return e !== void 0 && "value" in e && e.get === void 0 && e.set === void 0;
91
96
  }
92
97
  //#endregion
93
- export { h as abs, u as add, m as compare, p as divide, l as isMoney, s as money, f as multiply, g as negate, c as parseMoney, _ as round, d as subtract, v as toDecimal, y as withMinor };
98
+ export { g as abs, u as add, h as clamp, m as compare, p as divide, l as isMoney, s as money, f as multiply, _ as negate, c as parseMoney, v as round, d as subtract, y as toDecimal, b as withMinor };
94
99
 
95
100
  //# sourceMappingURL=money.js.map