@vielzeug/coins 2.1.1 → 2.2.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 (56) hide show
  1. package/README.md +2 -2
  2. package/dist/{decimal.cjs → _decimal.cjs} +2 -2
  3. package/dist/_decimal.cjs.map +1 -0
  4. package/dist/{decimal.d.ts → _decimal.d.ts} +1 -1
  5. package/dist/_decimal.d.ts.map +1 -0
  6. package/dist/{decimal.js → _decimal.js} +3 -2
  7. package/dist/_decimal.js.map +1 -0
  8. package/dist/aggregate.cjs +1 -1
  9. package/dist/aggregate.cjs.map +1 -1
  10. package/dist/aggregate.js +7 -9
  11. package/dist/aggregate.js.map +1 -1
  12. package/dist/coins.cjs +1 -1
  13. package/dist/coins.cjs.map +1 -1
  14. package/dist/coins.iife.js +1 -1
  15. package/dist/coins.iife.js.map +1 -1
  16. package/dist/coins.js +1 -1
  17. package/dist/coins.js.map +1 -1
  18. package/dist/currency.cjs +1 -1
  19. package/dist/currency.cjs.map +1 -1
  20. package/dist/currency.d.ts +3 -4
  21. package/dist/currency.d.ts.map +1 -1
  22. package/dist/currency.js +20 -40
  23. package/dist/currency.js.map +1 -1
  24. package/dist/errors.cjs.map +1 -1
  25. package/dist/errors.d.ts +1 -1
  26. package/dist/errors.d.ts.map +1 -1
  27. package/dist/errors.js.map +1 -1
  28. package/dist/exchange.cjs +1 -1
  29. package/dist/exchange.cjs.map +1 -1
  30. package/dist/exchange.d.ts +1 -0
  31. package/dist/exchange.d.ts.map +1 -1
  32. package/dist/exchange.js +12 -12
  33. package/dist/exchange.js.map +1 -1
  34. package/dist/format.cjs +1 -1
  35. package/dist/format.cjs.map +1 -1
  36. package/dist/format.js +15 -15
  37. package/dist/format.js.map +1 -1
  38. package/dist/index.cjs +1 -1
  39. package/dist/index.d.ts +2 -2
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +5 -5
  42. package/dist/money.cjs +1 -1
  43. package/dist/money.cjs.map +1 -1
  44. package/dist/money.d.ts +5 -4
  45. package/dist/money.d.ts.map +1 -1
  46. package/dist/money.js +33 -39
  47. package/dist/money.js.map +1 -1
  48. package/dist/serialization.cjs +1 -1
  49. package/dist/serialization.cjs.map +1 -1
  50. package/dist/serialization.d.ts.map +1 -1
  51. package/dist/serialization.js +15 -15
  52. package/dist/serialization.js.map +1 -1
  53. package/package.json +1 -1
  54. package/dist/decimal.cjs.map +0 -1
  55. package/dist/decimal.d.ts.map +0 -1
  56. package/dist/decimal.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"currency.js","names":[],"sources":["../src/currency.ts"],"sourcesContent":["import { CoinsError, InvalidCurrencyError } from './errors';\nimport type { Currency, CurrencyCode } from './types';\n\nconst builtins = new Map<string, Currency>();\nconst custom = new Map<string, Currency>();\n\nfunction builtin<C extends string>(code: C, minorUnit: number): Currency<C> {\n const definition = createDefinition(code, minorUnit);\n\n builtins.set(code, definition);\n\n return definition;\n}\n\nexport const USD = builtin('USD', 2);\nexport const EUR = builtin('EUR', 2);\nexport const GBP = builtin('GBP', 2);\nexport const JPY = builtin('JPY', 0);\nexport const KRW = builtin('KRW', 0);\nexport const BHD = builtin('BHD', 3);\nexport const KWD = builtin('KWD', 3);\n\n/** Custom definitions are explicit process configuration; built-ins remain immutable and separate. */\nexport function defineCurrency<C extends string>({ code, minorUnit }: { code: C; minorUnit: number }): Currency<C> {\n validateDefinition(code, minorUnit);\n\n const builtinDefinition = builtins.get(code);\n\n if (builtinDefinition) {\n if (builtinDefinition.minorUnit !== minorUnit) throw duplicateScaleError(code, builtinDefinition.minorUnit);\n\n return builtinDefinition as Currency<C>;\n }\n\n const existing = custom.get(code);\n\n if (existing) {\n if (existing.minorUnit !== minorUnit) throw duplicateScaleError(code, existing.minorUnit);\n\n return existing as Currency<C>;\n }\n\n const definition = createDefinition(code, minorUnit);\n\n custom.set(code, definition);\n\n return definition;\n}\n\nexport function currency(code: string): Currency {\n const definition = builtins.get(code) ?? custom.get(code);\n\n if (!definition) throw new InvalidCurrencyError(code);\n\n return definition;\n}\n\nexport function isCurrency(value: unknown): value is Currency {\n if (typeof value !== 'object' || value === null) return false;\n\n const candidate = value as Partial<Currency>;\n\n return (\n typeof candidate.code === 'string' &&\n (builtins.get(candidate.code) === value || custom.get(candidate.code) === value)\n );\n}\n\nexport function resolveBuiltinCurrency(code: string): Currency {\n const definition = builtins.get(code);\n\n if (!definition) throw new InvalidCurrencyError(code);\n\n return definition;\n}\n\nfunction createDefinition<C extends string>(code: C, minorUnit: number): Currency<C> {\n validateDefinition(code, minorUnit);\n\n return Object.freeze({ code: code as CurrencyCode<C>, minorUnit }) as Currency<C>;\n}\n\nfunction validateDefinition(code: string, minorUnit: number): void {\n if (!/^[A-Z]{3}$/.test(code)) {\n throw new CoinsError('INVALID_CURRENCY', `Currency code must be three uppercase letters: \"${code}\"`);\n }\n\n if (!Number.isInteger(minorUnit) || minorUnit < 0 || minorUnit > 6) {\n throw new CoinsError('INVALID_CURRENCY', `Currency \"${code}\" must have 0–6 minor-unit digits`);\n }\n}\n\nfunction duplicateScaleError(code: string, minorUnit: number): CoinsError {\n return new CoinsError('INVALID_CURRENCY', `Currency \"${code}\" already has ${minorUnit} minor-unit digits`);\n}\n"],"mappings":";;AAGA,IAAM,oBAAW,IAAI,IAAsB,GACrC,oBAAS,IAAI,IAAsB;AAEzC,SAAS,EAA0B,GAAS,GAAgC;CAC1E,IAAM,IAAa,EAAiB,GAAM,CAAS;CAInD,OAFA,EAAS,IAAI,GAAM,CAAU,GAEtB;AACT;AAEA,IAAa,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC;AAGnC,SAAgB,EAAiC,EAAE,SAAM,gBAA0D;CACjH,EAAmB,GAAM,CAAS;CAElC,IAAM,IAAoB,EAAS,IAAI,CAAI;CAE3C,IAAI,GAAmB;EACrB,IAAI,EAAkB,cAAc,GAAW,MAAM,EAAoB,GAAM,EAAkB,SAAS;EAE1G,OAAO;CACT;CAEA,IAAM,IAAW,EAAO,IAAI,CAAI;CAEhC,IAAI,GAAU;EACZ,IAAI,EAAS,cAAc,GAAW,MAAM,EAAoB,GAAM,EAAS,SAAS;EAExF,OAAO;CACT;CAEA,IAAM,IAAa,EAAiB,GAAM,CAAS;CAInD,OAFA,EAAO,IAAI,GAAM,CAAU,GAEpB;AACT;AAEA,SAAgB,EAAS,GAAwB;CAC/C,IAAM,IAAa,EAAS,IAAI,CAAI,KAAK,EAAO,IAAI,CAAI;CAExD,IAAI,CAAC,GAAY,MAAM,IAAI,EAAqB,CAAI;CAEpD,OAAO;AACT;AAEA,SAAgB,EAAW,GAAmC;CAC5D,IAAI,OAAO,KAAU,aAAY,GAAgB,OAAO;CAExD,IAAM,IAAY;CAElB,OACE,OAAO,EAAU,QAAS,aACzB,EAAS,IAAI,EAAU,IAAI,MAAM,KAAS,EAAO,IAAI,EAAU,IAAI,MAAM;AAE9E;AAEA,SAAgB,EAAuB,GAAwB;CAC7D,IAAM,IAAa,EAAS,IAAI,CAAI;CAEpC,IAAI,CAAC,GAAY,MAAM,IAAI,EAAqB,CAAI;CAEpD,OAAO;AACT;AAEA,SAAS,EAAmC,GAAS,GAAgC;CAGnF,OAFA,EAAmB,GAAM,CAAS,GAE3B,OAAO,OAAO;EAAQ;EAAyB;CAAU,CAAC;AACnE;AAEA,SAAS,EAAmB,GAAc,GAAyB;CACjE,IAAI,CAAC,aAAa,KAAK,CAAI,GACzB,MAAM,IAAI,EAAW,oBAAoB,mDAAmD,EAAK,EAAE;CAGrG,IAAI,CAAC,OAAO,UAAU,CAAS,KAAK,IAAY,KAAK,IAAY,GAC/D,MAAM,IAAI,EAAW,oBAAoB,aAAa,EAAK,kCAAkC;AAEjG;AAEA,SAAS,EAAoB,GAAc,GAA+B;CACxE,OAAO,IAAI,EAAW,oBAAoB,aAAa,EAAK,gBAAgB,EAAU,mBAAmB;AAC3G"}
1
+ {"version":3,"file":"currency.js","names":[],"sources":["../src/currency.ts"],"sourcesContent":["import { CoinsError, InvalidCurrencyError } from './errors';\nimport type { Currency, CurrencyCode } from './types';\n\nconst builtins = new Map<string, Currency>();\nconst canonicalCurrencies = new WeakSet<object>();\n\nfunction register<C extends string>(code: C, minorUnit: number): Currency<C> {\n validateDefinition(code, minorUnit);\n\n const definition = Object.freeze({ code: code as CurrencyCode<C>, minorUnit }) as Currency<C>;\n\n canonicalCurrencies.add(definition);\n\n return definition;\n}\n\nfunction builtin<C extends string>(code: C, minorUnit: number): Currency<C> {\n const definition = register(code, minorUnit);\n\n builtins.set(code, definition);\n\n return definition;\n}\n\nexport const USD = builtin('USD', 2);\nexport const EUR = builtin('EUR', 2);\nexport const GBP = builtin('GBP', 2);\nexport const JPY = builtin('JPY', 0);\nexport const KRW = builtin('KRW', 0);\nexport const BHD = builtin('BHD', 3);\nexport const KWD = builtin('KWD', 3);\n\n/** Resolve a built-in currency by code, or construct an immutable custom currency from a definition. */\nexport function currency<C extends string>(code: C): Currency<C>;\nexport function currency<C extends string>(definition: { code: C; minorUnit: number }): Currency<C>;\nexport function currency<C extends string>(input: C | { code: C; minorUnit: number }): Currency<C> {\n if (typeof input === 'string') {\n const definition = builtins.get(input);\n\n if (!definition) throw new InvalidCurrencyError(input);\n\n return definition as Currency<C>;\n }\n\n return register(input.code, input.minorUnit);\n}\n\nexport function isCurrency(value: unknown): value is Currency {\n return typeof value === 'object' && value !== null && canonicalCurrencies.has(value);\n}\n\nfunction validateDefinition(code: string, minorUnit: number): void {\n if (!/^[A-Z]{3}$/.test(code)) {\n throw new CoinsError('INVALID_CURRENCY', `Currency code must be three uppercase letters: \"${code}\"`);\n }\n\n if (!Number.isInteger(minorUnit) || minorUnit < 0 || minorUnit > 6) {\n throw new CoinsError('INVALID_CURRENCY', `Currency \"${code}\" must have 0–6 minor-unit digits`);\n }\n}\n"],"mappings":";;AAGA,IAAM,oBAAW,IAAI,IAAsB,GACrC,oBAAsB,IAAI,QAAgB;AAEhD,SAAS,EAA2B,GAAS,GAAgC;CAC3E,EAAmB,GAAM,CAAS;CAElC,IAAM,IAAa,OAAO,OAAO;EAAQ;EAAyB;CAAU,CAAC;CAI7E,OAFA,EAAoB,IAAI,CAAU,GAE3B;AACT;AAEA,SAAS,EAA0B,GAAS,GAAgC;CAC1E,IAAM,IAAa,EAAS,GAAM,CAAS;CAI3C,OAFA,EAAS,IAAI,GAAM,CAAU,GAEtB;AACT;AAEA,IAAa,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC,GACtB,IAAM,EAAQ,OAAO,CAAC;AAKnC,SAAgB,EAA2B,GAAwD;CACjG,IAAI,OAAO,KAAU,UAAU;EAC7B,IAAM,IAAa,EAAS,IAAI,CAAK;EAErC,IAAI,CAAC,GAAY,MAAM,IAAI,EAAqB,CAAK;EAErD,OAAO;CACT;CAEA,OAAO,EAAS,EAAM,MAAM,EAAM,SAAS;AAC7C;AAEA,SAAgB,EAAW,GAAmC;CAC5D,OAAO,OAAO,KAAU,cAAY,KAAkB,EAAoB,IAAI,CAAK;AACrF;AAEA,SAAS,EAAmB,GAAc,GAAyB;CACjE,IAAI,CAAC,aAAa,KAAK,CAAI,GACzB,MAAM,IAAI,EAAW,oBAAoB,mDAAmD,EAAK,EAAE;CAGrG,IAAI,CAAC,OAAO,UAAU,CAAS,KAAK,IAAY,KAAK,IAAY,GAC/D,MAAM,IAAI,EAAW,oBAAoB,aAAa,EAAK,kCAAkC;AAEjG"}
@@ -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 | '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"}
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_EXCHANGE_RATE'\n | 'INVALID_MONEY'\n | 'INVALID_RANGE'\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":"AAYA,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,4 +1,4 @@
1
- export type CoinsErrorCode = 'CURRENCY_MISMATCH' | 'DIVISION_BY_ZERO' | 'FORMAT_ERROR' | '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_EXCHANGE_RATE' | 'INVALID_MONEY' | 'INVALID_RANGE' | 'INVALID_ROUNDING';
2
2
  export declare class CoinsError extends Error {
3
3
  readonly code: CoinsErrorCode;
4
4
  constructor(code: CoinsErrorCode, message: string, options?: ErrorOptions);
@@ -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,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"}
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,uBAAuB,GACvB,eAAe,GACf,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"}
@@ -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 | '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
+ {"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_EXCHANGE_RATE'\n | 'INVALID_MONEY'\n | 'INVALID_RANGE'\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":";AAYA,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"}
package/dist/exchange.cjs CHANGED
@@ -1,2 +1,2 @@
1
- const e=require("./errors.cjs"),t=require("./decimal.cjs"),n=require("./currency.cjs"),r=require("./money.cjs");function i({from:r,to:i,value:a}){if(!n.isCurrency(r)||!n.isCurrency(i))throw new e.CoinsError(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let o=t.decimal(a);if(o.numerator<0n)throw new e.CoinsError(`INVALID_DECIMAL`,`Exchange rates cannot be negative`);return Object.freeze({from:r,to:i,value:o})}function a(n,i,a={}){if(!r.isMoney(n)||!o(i))throw new e.CoinsError(`INVALID_MONEY`,`Exchange requires canonical money and rate values`);if(n.currency!==i.from)throw new e.CurrencyMismatchError(n.currency.code,i.from.code);let s=n.amount*i.value.numerator*10n**BigInt(i.to.minorUnit),c=i.value.denominator*10n**BigInt(i.from.minorUnit);return r.withMinor(t.roundDivision(s,c,a.rounding??`halfAwayFromZero`),i.to)}function o(e){if(typeof e!=`object`||!e||!Object.isFrozen(e))return!1;let t=e;return n.isCurrency(t.from)&&n.isCurrency(t.to)&&typeof t.value==`object`&&t.value!==null&&Object.isFrozen(t.value)&&typeof t.value.numerator==`bigint`&&t.value.numerator>=0n&&typeof t.value.denominator==`bigint`&&t.value.denominator>0n}exports.exchange=a,exports.exchangeRate=i;
1
+ const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./currency.cjs"),r=require("./money.cjs");var i=new WeakSet;function a({from:r,to:a,value:o}){if(!n.isCurrency(r)||!n.isCurrency(a))throw new e.CoinsError(`INVALID_CURRENCY`,`Exchange rate requires registered currencies`);let s=t.decimal(o);if(s.numerator<0n)throw new e.CoinsError(`INVALID_DECIMAL`,`Exchange rates cannot be negative`);let c=Object.freeze({from:r,to:a,value:s});return i.add(c),c}function o(e){return typeof e==`object`&&!!e&&i.has(e)}function s(n,i,a={}){if(r.assertMoney(n),!o(i))throw new e.CoinsError(`INVALID_EXCHANGE_RATE`,`Exchange requires a canonical exchange rate`);if(n.currency!==i.from)throw new e.CurrencyMismatchError(n.currency.code,i.from.code);let s=n.amount*i.value.numerator*10n**BigInt(i.to.minorUnit),c=i.value.denominator*10n**BigInt(i.from.minorUnit);return r.createMoney(t.roundDivision(s,c,a.rounding??`halfAwayFromZero`),i.to)}exports.exchange=s,exports.exchangeRate=a,exports.isExchangeRate=o;
2
2
  //# sourceMappingURL=exchange.cjs.map
@@ -1 +1 @@
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
+ {"version":3,"file":"exchange.cjs","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\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 const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\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 assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\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 createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n"],"mappings":"iHAMA,IAAM,EAAiB,IAAI,QAE3B,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,IAAM,EAAO,OAAO,OAAO,CAAE,OAAM,KAAI,MAAO,CAAO,CAAC,EAItD,OAFA,EAAe,IAAI,CAAI,EAEhB,CACT,CAEA,SAAgB,EAAe,EAAuC,CACpE,OAAO,OAAO,GAAU,YAAY,GAAkB,EAAe,IAAI,CAAK,CAChF,CAEA,SAAgB,EACd,EACA,EACA,EAAuC,CAAC,EAC7B,CAGX,GAFA,EAAA,YAAY,CAAK,EAEb,CAAC,EAAe,CAAI,EACtB,MAAM,IAAI,EAAA,WAAW,wBAAyB,6CAA6C,EAG7F,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,YAAY,EAAA,cAAc,EAAW,EAAa,EAAQ,UAAY,kBAAkB,EAAG,EAAK,EAAE,CAC3G"}
@@ -4,6 +4,7 @@ export declare function exchangeRate<From extends Currency, To extends Currency>
4
4
  to: To;
5
5
  value: string;
6
6
  }): ExchangeRate<From, To>;
7
+ export declare function isExchangeRate(value: unknown): value is ExchangeRate;
7
8
  export declare function exchange<From extends Currency, To extends Currency>(value: Money<From>, rate: ExchangeRate<From, To>, options?: {
8
9
  rounding?: RoundingMode;
9
10
  }): Money<To>;
@@ -1 +1 @@
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
+ {"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;AAI3E,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,CAazB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAEpE;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,CAaX"}
package/dist/exchange.js CHANGED
@@ -1,30 +1,30 @@
1
1
  import { CoinsError as e, CurrencyMismatchError as t } from "./errors.js";
2
- import { decimal as n, roundDivision as r } from "./decimal.js";
2
+ import { decimal as n, roundDivision as r } from "./_decimal.js";
3
3
  import { isCurrency as i } from "./currency.js";
4
- import { isMoney as a, withMinor as o } from "./money.js";
4
+ import { assertMoney as a, createMoney as o } from "./money.js";
5
5
  //#region src/exchange.ts
6
- function s({ from: t, to: r, value: a }) {
6
+ var s = /* @__PURE__ */ new WeakSet();
7
+ function c({ from: t, to: r, value: a }) {
7
8
  if (!i(t) || !i(r)) throw new e("INVALID_CURRENCY", "Exchange rate requires registered currencies");
8
9
  let o = n(a);
9
10
  if (o.numerator < 0n) throw new e("INVALID_DECIMAL", "Exchange rates cannot be negative");
10
- return Object.freeze({
11
+ let c = Object.freeze({
11
12
  from: t,
12
13
  to: r,
13
14
  value: o
14
15
  });
16
+ return s.add(c), c;
15
17
  }
16
- function c(n, i, s = {}) {
17
- if (!a(n) || !l(i)) throw new e("INVALID_MONEY", "Exchange requires canonical money and rate values");
18
+ function l(e) {
19
+ return typeof e == "object" && !!e && s.has(e);
20
+ }
21
+ function u(n, i, s = {}) {
22
+ if (a(n), !l(i)) throw new e("INVALID_EXCHANGE_RATE", "Exchange requires a canonical exchange rate");
18
23
  if (n.currency !== i.from) throw new t(n.currency.code, i.from.code);
19
24
  let c = n.amount * i.value.numerator * 10n ** BigInt(i.to.minorUnit), u = i.value.denominator * 10n ** BigInt(i.from.minorUnit);
20
25
  return o(r(c, u, s.rounding ?? "halfAwayFromZero"), i.to);
21
26
  }
22
- function l(e) {
23
- if (typeof e != "object" || !e || !Object.isFrozen(e)) return !1;
24
- let t = e;
25
- return i(t.from) && i(t.to) && typeof t.value == "object" && t.value !== null && Object.isFrozen(t.value) && typeof t.value.numerator == "bigint" && t.value.numerator >= 0n && typeof t.value.denominator == "bigint" && t.value.denominator > 0n;
26
- }
27
27
  //#endregion
28
- export { c as exchange, s as exchangeRate };
28
+ export { u as exchange, c as exchangeRate, l as isExchangeRate };
29
29
 
30
30
  //# sourceMappingURL=exchange.js.map
@@ -1 +1 @@
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"}
1
+ {"version":3,"file":"exchange.js","names":[],"sources":["../src/exchange.ts"],"sourcesContent":["import { decimal, roundDivision } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport { assertMoney, createMoney } from './money';\nimport type { Currency, ExchangeRate, Money, RoundingMode } from './types';\n\nconst canonicalRates = new WeakSet<object>();\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 const rate = Object.freeze({ from, to, value: parsed }) as ExchangeRate<From, To>;\n\n canonicalRates.add(rate);\n\n return rate;\n}\n\nexport function isExchangeRate(value: unknown): value is ExchangeRate {\n return typeof value === 'object' && value !== null && canonicalRates.has(value);\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 assertMoney(value);\n\n if (!isExchangeRate(rate)) {\n throw new CoinsError('INVALID_EXCHANGE_RATE', 'Exchange requires a canonical exchange rate');\n }\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 createMoney(roundDivision(numerator, denominator, options.rounding ?? 'halfAwayFromZero'), rate.to);\n}\n"],"mappings":";;;;;AAMA,IAAM,oBAAiB,IAAI,QAAgB;AAE3C,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,IAAM,IAAO,OAAO,OAAO;EAAE;EAAM;EAAI,OAAO;CAAO,CAAC;CAItD,OAFA,EAAe,IAAI,CAAI,GAEhB;AACT;AAEA,SAAgB,EAAe,GAAuC;CACpE,OAAO,OAAO,KAAU,cAAY,KAAkB,EAAe,IAAI,CAAK;AAChF;AAEA,SAAgB,EACd,GACA,GACA,IAAuC,CAAC,GAC7B;CAGX,IAFA,EAAY,CAAK,GAEb,CAAC,EAAe,CAAI,GACtB,MAAM,IAAI,EAAW,yBAAyB,6CAA6C;CAG7F,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,EAAY,EAAc,GAAW,GAAa,EAAQ,YAAY,kBAAkB,GAAG,EAAK,EAAE;AAC3G"}
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=`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;
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(e,r={}){n.assertMoney(e);let{locale:a=`en-US`,maximumFractionDigits:o=e.currency.minorUnit,minimumFractionDigits:s=e.currency.minorUnit,rounding:c=i,style:f=`symbol`}=r;l(s,o);let p=10n**BigInt(o),m=o>=e.currency.minorUnit?e.amount*10n**BigInt(o-e.currency.minorUnit):t.roundDivision(e.amount,10n**BigInt(e.currency.minorUnit-o),c),h=m<0n,g=h?-m:m,_=g/p,v=(o===0?``:(g%p).toString().padStart(o,`0`)).replace(RegExp(`0{0,${Math.max(0,o-s)}}$`),``),y=u(a,e.currency.code,f,h),b=[],x=!1;for(let e of y)e.type!==`group`&&(e.type===`integer`?x||=(b.push({type:`integer`,value:d(a).format(_)}),!0):e.type===`decimal`?v&&b.push({type:`decimal`,value:e.value}):e.type===`fraction`?v&&b.push({type:`fraction`,value:v}):e.type===`currency`||e.type===`minusSign`||e.type===`plusSign`?b.push({type:e.type,value:e.value}):b.push({type:`literal`,value:e.value}));return b}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 { 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
+ {"version":3,"file":"format.cjs","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { roundDivision } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney } 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 assertMoney(value);\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/format.js CHANGED
@@ -1,33 +1,33 @@
1
1
  import { CoinsError as e } from "./errors.js";
2
- import { roundDivision as t } from "./decimal.js";
3
- import { isMoney as n } from "./money.js";
2
+ import { roundDivision as t } from "./_decimal.js";
3
+ import { assertMoney as n } from "./money.js";
4
4
  //#region src/format.ts
5
5
  var r = 20, i = "halfAwayFromZero", a = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Map();
6
6
  function s(e, t = {}) {
7
7
  return c(e, t).map((e) => e.value).join("");
8
8
  }
9
- function c(r, a = {}) {
10
- if (!n(r)) throw new e("INVALID_MONEY", "format() requires canonical money");
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({
9
+ function c(e, r = {}) {
10
+ n(e);
11
+ let { locale: a = "en-US", maximumFractionDigits: o = e.currency.minorUnit, minimumFractionDigits: s = e.currency.minorUnit, rounding: c = i, style: f = "symbol" } = r;
12
+ l(s, o);
13
+ let p = 10n ** BigInt(o), m = o >= e.currency.minorUnit ? e.amount * 10n ** BigInt(o - e.currency.minorUnit) : t(e.amount, 10n ** BigInt(e.currency.minorUnit - o), c), h = m < 0n, g = h ? -m : m, _ = g / p, v = (o === 0 ? "" : (g % p).toString().padStart(o, "0")).replace(RegExp(`0{0,${Math.max(0, o - s)}}$`), ""), y = u(a, e.currency.code, f, h), b = [], x = !1;
14
+ for (let e of y) e.type !== "group" && (e.type === "integer" ? x ||= (b.push({
15
15
  type: "integer",
16
- value: d(o).format(v)
17
- }), !0) : e.type === "decimal" ? y && x.push({
16
+ value: d(a).format(_)
17
+ }), !0) : e.type === "decimal" ? v && b.push({
18
18
  type: "decimal",
19
19
  value: e.value
20
- }) : e.type === "fraction" ? y && x.push({
20
+ }) : e.type === "fraction" ? v && b.push({
21
21
  type: "fraction",
22
- value: y
23
- }) : e.type === "currency" || e.type === "minusSign" || e.type === "plusSign" ? x.push({
22
+ value: v
23
+ }) : e.type === "currency" || e.type === "minusSign" || e.type === "plusSign" ? b.push({
24
24
  type: e.type,
25
25
  value: e.value
26
- }) : x.push({
26
+ }) : b.push({
27
27
  type: "literal",
28
28
  value: e.value
29
29
  }));
30
- return x;
30
+ return b;
31
31
  }
32
32
  function l(t, n) {
33
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}`);
@@ -1 +1 @@
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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"}
1
+ {"version":3,"file":"format.js","names":[],"sources":["../src/format.ts"],"sourcesContent":["import { roundDivision } from './_decimal';\nimport { CoinsError } from './errors';\nimport { assertMoney } 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 assertMoney(value);\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("./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;
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.divide=n.divide,exports.exchange=i.exchange,exports.exchangeRate=i.exchangeRate,exports.format=a.format,exports.formatParts=a.formatParts,exports.isCurrency=t.isCurrency,exports.isExchangeRate=i.isExchangeRate,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,8 +1,8 @@
1
1
  export { allocate, sum } from './aggregate';
2
- export { BHD, currency, defineCurrency, EUR, GBP, isCurrency, JPY, KRW, KWD, USD } from './currency';
2
+ export { BHD, currency, EUR, GBP, isCurrency, JPY, KRW, KWD, USD } from './currency';
3
3
  export type { CoinsErrorCode } from './errors';
4
4
  export { CoinsError, CurrencyMismatchError, InvalidCurrencyError } from './errors';
5
- export { exchange, exchangeRate } from './exchange';
5
+ export { exchange, exchangeRate, isExchangeRate } from './exchange';
6
6
  export { format, formatParts } from './format';
7
7
  export { abs, add, clamp, compare, divide, isMoney, money, multiply, negate, parseMoney, round, subtract, toDecimal, } from './money';
8
8
  export { parseMoneyJSON, toJSON } from './serialization';
@@ -1 +1 @@
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"}
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,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACrF,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,cAAc,EAAE,MAAM,YAAY,CAAC;AACpE,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,8 +1,8 @@
1
1
  import { CoinsError as e, CurrencyMismatchError as t, InvalidCurrencyError as n } from "./errors.js";
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";
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, isCurrency as d } from "./currency.js";
3
+ import { abs as f, add as p, clamp as m, compare as h, divide as g, isMoney as _, money as v, multiply as y, negate as b, parseMoney as x, round as S, subtract as C, toDecimal as w } from "./money.js";
4
+ import { allocate as T, sum as E } from "./aggregate.js";
5
+ import { exchange as D, exchangeRate as O, isExchangeRate as k } from "./exchange.js";
6
6
  import { format as A, formatParts as j } from "./format.js";
7
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 };
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, f as abs, p as add, T as allocate, m as clamp, h as compare, u as currency, g as divide, D as exchange, O as exchangeRate, A as format, j as formatParts, d as isCurrency, k as isExchangeRate, _ as isMoney, v as money, y as multiply, b as negate, x as parseMoney, M as parseMoneyJSON, S as round, C as subtract, E as sum, w 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(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=_;
1
+ const e=require("./errors.cjs"),t=require("./_decimal.cjs"),n=require("./currency.cjs");var r=new WeakSet,i=`halfAwayFromZero`;function a(n,r,a){if(x(r),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,r)}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(r.minorUnit);if(s%o.denominator!==0n&&a?.rounding===void 0)throw new e.CoinsError(`INVALID_MONEY`,`Amount "${n}" exceeds ${r.code} precision; provide rounding`);return v(t.roundDivision(s,o.denominator,a?.rounding??i),r)}function o(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 s(e){return typeof e==`object`&&!!e&&r.has(e)}function c(e,t){return b(e,t),v(e.amount+t.amount,e.currency)}function l(e,t){return b(e,t),v(e.amount-t.amount,e.currency)}function u(e,n,r={}){y(e);let a=t.decimal(n);return v(t.roundDivision(e.amount*a.numerator,a.denominator,r.rounding??i),e.currency)}function d(n,r,a={}){y(n);let o=t.decimal(r);if(o.numerator===0n)throw new e.CoinsError(`DIVISION_BY_ZERO`,`Cannot divide money by zero`);let s=o.numerator<0n?-o.numerator:o.numerator,c=n.amount*o.denominator;return v(t.roundDivision(o.numerator<0n?-c:c,s,a.rounding??i),n.currency)}function f(e,t){return b(e,t),e.amount===t.amount?0:e.amount<t.amount?-1:1}function p(t,n){if(f(n.min,n.max)===1)throw new e.CoinsError(`INVALID_RANGE`,`Clamp minimum cannot exceed maximum`);return f(t,n.min)===-1?n.min:f(t,n.max)===1?n.max:t}function m(e){return y(e),v(e.amount<0n?-e.amount:e.amount,e.currency)}function h(e){return y(e),v(-e.amount,e.currency)}function g(n,r){y(n);let{fractionDigits:a,rounding:o=i}=r;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 _(e){return y(e),t.toDecimalString(e.amount,e.currency.minorUnit)}function v(e,t){let n=Object.freeze({amount:e,currency:t});return r.add(n),n}function y(t){if(!s(t))throw new e.CoinsError(`INVALID_MONEY`,`Money must be a canonical @vielzeug/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=m,exports.add=c,exports.assertMoney=y,exports.clamp=p,exports.compare=f,exports.createMoney=v,exports.divide=d,exports.isMoney=s,exports.money=a,exports.multiply=u,exports.negate=h,exports.parseMoney=o,exports.round=g,exports.subtract=l,exports.toDecimal=_;
2
2
  //# sourceMappingURL=money.cjs.map
@@ -1 +1 @@
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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"}
1
+ {"version":3,"file":"money.cjs","names":[],"sources":["../src/money.ts"],"sourcesContent":["import { decimal, roundDivision, toDecimalString } from './_decimal';\nimport { isCurrency } from './currency';\nimport { CoinsError, CurrencyMismatchError } from './errors';\nimport type { Currency, Money, RoundingMode } from './types';\n\nconst canonicalMoney = new WeakSet<object>();\n\nconst defaultRounding: RoundingMode = 'halfAwayFromZero';\n\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?.rounding === undefined) {\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\n/** Validates a plain data object and returns canonical money. Use for untrusted input; use `isMoney()` for trusted values. */\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 return typeof value === 'object' && value !== null && canonicalMoney.has(value);\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 divisorMagnitude = scalar.numerator < 0n ? -scalar.numerator : scalar.numerator;\n const dividend = value.amount * scalar.denominator;\n const quotient = roundDivision(\n scalar.numerator < 0n ? -dividend : dividend,\n divisorMagnitude,\n options.rounding ?? defaultRounding,\n );\n\n return createMoney(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_RANGE', '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 createMoney<C extends Currency>(amount: bigint, currency: C): Money<C> {\n const value = Object.freeze({ amount, currency }) as Money<C>;\n\n canonicalMoney.add(value);\n\n return value;\n}\n\nexport function assertMoney(value: unknown): asserts value is Money {\n if (!isMoney(value)) {\n throw new CoinsError('INVALID_MONEY', 'Money must be a canonical @vielzeug/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: unknown): asserts value is Currency {\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
@@ -1,11 +1,11 @@
1
1
  import type { Currency, Money, RoundingMode } from './types';
2
- export declare function money<C extends Currency>(amount: string, currency: C): Money<C>;
3
- export declare function money<C extends Currency>(amount: string, currency: C, options: {
4
- rounding: RoundingMode;
2
+ export declare function money<C extends Currency>(amount: string, currency: C, options?: {
3
+ rounding?: RoundingMode;
5
4
  }): Money<C>;
6
5
  export declare function money<C extends Currency>(amount: bigint, currency: C, options: {
7
6
  unit: 'minor';
8
7
  }): Money<C>;
8
+ /** Validates a plain data object and returns canonical money. Use for untrusted input; use `isMoney()` for trusted values. */
9
9
  export declare function parseMoney(value: unknown): Money;
10
10
  export declare function isMoney(value: unknown): value is Money;
11
11
  export declare function add<C extends Currency>(left: Money<C>, right: Money<NoInfer<C>>): Money<C>;
@@ -28,5 +28,6 @@ export declare function round<C extends Currency>(value: Money<C>, options: {
28
28
  rounding?: RoundingMode;
29
29
  }): Money<C>;
30
30
  export declare function toDecimal(value: Money): string;
31
- export declare function withMinor<C extends Currency>(amount: bigint, currency: C): Money<C>;
31
+ export declare function createMoney<C extends Currency>(amount: bigint, currency: C): Money<C>;
32
+ export declare function assertMoney(value: unknown): asserts value is Money;
32
33
  //# sourceMappingURL=money.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"money.d.ts","sourceRoot":"","sources":["../src/money.ts"],"names":[],"mappings":"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"}