@amboras-dev/money 0.0.0-stage → 0.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 (78) hide show
  1. package/README.md +401 -2
  2. package/dist/ai.cjs +185 -0
  3. package/dist/ai.d.cts +97 -0
  4. package/dist/ai.d.ts +97 -0
  5. package/dist/ai.js +175 -0
  6. package/dist/allocate-CZRg4TOP.cjs +222 -0
  7. package/dist/allocate-X7_jAtOj.js +199 -0
  8. package/dist/amount-BV0O-aN8.cjs +241 -0
  9. package/dist/amount-BoYBMgK9.js +200 -0
  10. package/dist/arithmetic-BhNwisjb.js +132 -0
  11. package/dist/arithmetic-DjT4Kzgt.cjs +233 -0
  12. package/dist/billing.cjs +184 -0
  13. package/dist/billing.d.cts +81 -0
  14. package/dist/billing.d.ts +81 -0
  15. package/dist/billing.js +175 -0
  16. package/dist/commerce.cjs +536 -0
  17. package/dist/commerce.d.cts +293 -0
  18. package/dist/commerce.d.ts +293 -0
  19. package/dist/commerce.js +516 -0
  20. package/dist/format-BdPnijvn.js +99 -0
  21. package/dist/format-DLhno7eX.cjs +128 -0
  22. package/dist/fx.cjs +407 -0
  23. package/dist/fx.d.cts +157 -0
  24. package/dist/fx.d.ts +157 -0
  25. package/dist/fx.js +374 -0
  26. package/dist/index.cjs +352 -0
  27. package/dist/index.d.cts +300 -0
  28. package/dist/index.d.ts +300 -0
  29. package/dist/index.js +276 -0
  30. package/dist/input.cjs +163 -0
  31. package/dist/input.d.cts +203 -0
  32. package/dist/input.d.ts +203 -0
  33. package/dist/input.js +153 -0
  34. package/dist/iso-minor-CqkWDxHS.js +48 -0
  35. package/dist/iso-minor-wSwJN57T.cjs +65 -0
  36. package/dist/medusa-4K8N1pUc.d.cts +87 -0
  37. package/dist/medusa-DBXJXXvF.d.ts +87 -0
  38. package/dist/medusa-DqJY1qpj.js +116 -0
  39. package/dist/medusa-lRY9xlsf.cjs +175 -0
  40. package/dist/money-B-3aFjNJ.d.cts +248 -0
  41. package/dist/money-B-3aFjNJ.d.ts +248 -0
  42. package/dist/money-B3wH_Z4S.cjs +3990 -0
  43. package/dist/money-WhnGiI00.js +3583 -0
  44. package/dist/number-text-BWu67jUq.cjs +145 -0
  45. package/dist/number-text-DE-q4jO5.js +134 -0
  46. package/dist/own-D7544D3k.js +4 -0
  47. package/dist/own-oP9DXFWv.cjs +9 -0
  48. package/dist/percent-C0InC2t2.cjs +43 -0
  49. package/dist/percent-D1VB9xXG.js +20 -0
  50. package/dist/rate-CVeMii4a.cjs +217 -0
  51. package/dist/rate-DBPiYgT1.d.ts +63 -0
  52. package/dist/rate-DicStBue.js +152 -0
  53. package/dist/rate-tqK8ijG0.d.cts +63 -0
  54. package/dist/reporting-CG-ai2sM.d.cts +31 -0
  55. package/dist/reporting-tpGJbqYp.d.ts +31 -0
  56. package/dist/retail-1mu888J1.js +69 -0
  57. package/dist/retail-C_BPLvMj.d.cts +31 -0
  58. package/dist/retail-Cml8YSKg.cjs +86 -0
  59. package/dist/retail-DRUTTQoJ.d.ts +31 -0
  60. package/dist/rounding-DHBtNCHG.cjs +79 -0
  61. package/dist/rounding-g9rvye0k.js +50 -0
  62. package/dist/sql.cjs +290 -0
  63. package/dist/sql.d.cts +147 -0
  64. package/dist/sql.d.ts +147 -0
  65. package/dist/sql.js +271 -0
  66. package/dist/testing.cjs +61995 -0
  67. package/dist/testing.d.cts +125 -0
  68. package/dist/testing.d.ts +125 -0
  69. package/dist/testing.js +61986 -0
  70. package/dist/wire-Bhuut8cW.cjs +2097 -0
  71. package/dist/wire-CaUN9Vf_.d.ts +309 -0
  72. package/dist/wire-sW0yzqth.js +1888 -0
  73. package/dist/wire-xjYXv4go.d.cts +309 -0
  74. package/dist/wire.cjs +37 -0
  75. package/dist/wire.d.cts +2 -0
  76. package/dist/wire.d.ts +2 -0
  77. package/dist/wire.js +2 -0
  78. package/package.json +158 -3
@@ -0,0 +1,175 @@
1
+ const require_money = require("./money-B3wH_Z4S.cjs");
2
+ const require_own = require("./own-oP9DXFWv.cjs");
3
+ //#region src/medusa.ts
4
+ const isObject = (value) => typeof value === "object" && value !== null;
5
+ /** Internal: `row['raw_' + key]` when it is not null/undefined, else `row[key]` (undefined for a non-object row). */
6
+ function storedFieldValue(row, key) {
7
+ if (!isObject(row)) return void 0;
8
+ const raw = row[`raw_${key}`];
9
+ return raw !== null && raw !== void 0 ? raw : row[key];
10
+ }
11
+ function readStoredField(row, key, currency, options = {}) {
12
+ return require_money.readStored(storedFieldValue(row, key), currency, {
13
+ ...options,
14
+ field: options.field ?? key
15
+ });
16
+ }
17
+ function readStoredDecimal(raw, options = {}) {
18
+ const value = require_money.medusaAmountDecimal(raw);
19
+ if (value && (options.signed !== false || value.units >= 0n)) return require_money.toDecimalText(value);
20
+ if (options.mode === "lenient") return null;
21
+ throw new require_money.MoneyError("amount_invalid", `Invalid stored amount: ${require_money.describeValue(raw)}`, { field: options.field });
22
+ }
23
+ /**
24
+ * A Medusa amount's literal, verbatim (`'4300.00'` stays `'4300.00'`), for SQL and ledger columns:
25
+ * `.value ?? .numeric ?? raw` when it is an unsigned decimal literal without leading zeros
26
+ * (`isDecimalLiteral(text, { leadingZeros: false })`), else null. A number counts only while all
27
+ * its digits are there (finite; a safe integer when whole) and is spelled `String(n)`. Unlike
28
+ * `readStoredDecimal` it neither canonicalizes nor reads `.raw.value`, a bigint or a BigNumber's
29
+ * `valueOf`: the literal is passed on as Medusa holds it.
30
+ */
31
+ function readStoredLiteral(raw) {
32
+ const picked = isObject(raw) ? raw.value ?? raw.numeric ?? raw : raw;
33
+ let literal = null;
34
+ if (typeof picked === "string") literal = picked;
35
+ else if (typeof picked === "number" && require_money.strictNumberDecimal(picked) !== null) literal = String(picked);
36
+ return literal !== null && require_money.isDecimalLiteral(literal, { leadingZeros: false }) ? literal : null;
37
+ }
38
+ /**
39
+ * A variant's `calculated_price` (a number, or an object read through
40
+ * `raw_calculated_amount` / `calculated_amount`) as Money in
41
+ * `firstCurrency(calculated_price.currency_code, fallbackCurrency)`. Null on anything
42
+ * missing or unreadable.
43
+ */
44
+ function readCalculatedPrice(variant, fallbackCurrency) {
45
+ const price = isObject(variant) ? variant.calculated_price : void 0;
46
+ const priced = isObject(price) ? price : void 0;
47
+ let currency;
48
+ try {
49
+ currency = require_money.firstCurrency(priced?.currency_code, fallbackCurrency);
50
+ } catch {
51
+ return null;
52
+ }
53
+ return priced ? readStoredField(priced, "calculated_amount", currency, { mode: "lenient" }) : require_money.readStored(price, currency, { mode: "lenient" });
54
+ }
55
+ /**
56
+ * `{ value, precision }` for a Medusa raw column: `value` is plain decimal text, never an
57
+ * exponent (`1e21` gives `'1000000000000000000000'`). NaN and Infinity throw `amount_invalid`.
58
+ */
59
+ function toMedusaRaw(value, precision = 20) {
60
+ if (!Number.isSafeInteger(precision) || precision < 1) throw new require_money.MoneyError("amount_invalid", `precision must be a positive integer, got ${require_money.describeValue(precision)}`);
61
+ let decimal;
62
+ if (isObject(value)) {
63
+ require_money.storedDecimal(value);
64
+ decimal = {
65
+ units: BigInt(value.amount),
66
+ scale: 0
67
+ };
68
+ } else decimal = require_money.decimalOf(value);
69
+ return {
70
+ value: require_money.toDecimalText(decimal),
71
+ precision
72
+ };
73
+ }
74
+ /** A Money for a Medusa amount input: `'0'` for zero (Medusa drops numeric 0 on order-edit updates), else the number. */
75
+ function toMedusaAmountInput(m) {
76
+ require_money.storedDecimal(m);
77
+ return m.amount === 0 ? "0" : m.amount;
78
+ }
79
+ /**
80
+ * Validates an object of `code -> stored amount` (keys through `currencyCode`, values
81
+ * non-negative safe integers), upper-cases the keys and freezes it. A key given twice in
82
+ * different cases throws `currency_invalid`.
83
+ */
84
+ function moneyByCurrency(input) {
85
+ if (!isObject(input) || Array.isArray(input)) throw new require_money.MoneyError("amount_invalid", `Expected an object of currency code to stored amount, got ${require_money.describeValue(input)}`);
86
+ const out = {};
87
+ for (const key of Object.keys(input)) {
88
+ const code = require_money.currencyCode(key, { field: key });
89
+ if (require_own.hasOwnKey(out, code)) throw new require_money.MoneyError("currency_invalid", `${code} is given more than once`, { field: key });
90
+ const amount = input[key];
91
+ if (!require_money.isStoredAmount(amount)) throw new require_money.MoneyError("amount_invalid", `Stored amount must be a non-negative safe integer, got ${require_money.describeValue(amount)}`, { field: key });
92
+ out[code] = amount === 0 ? 0 : amount;
93
+ }
94
+ return Object.freeze(out);
95
+ }
96
+ /**
97
+ * The amount for `currency` from a MoneyByCurrency or a Medusa price list (base prices only:
98
+ * rows with a `price_list_id` are skipped). Exact currency match, any case; the amount is read
99
+ * strictly. Null when there is none.
100
+ */
101
+ function moneyFor(source, currency) {
102
+ const info = require_money.resolveCurrency(currency, { allowHistoric: true });
103
+ if (Array.isArray(source)) {
104
+ const row = source.find((p) => isObject(p) && (p.price_list_id === null || p.price_list_id === void 0) && require_money.sameCurrency(p.currency_code, info.code));
105
+ return row ? require_money.readStored(row.amount, info.code, { field: "amount" }) : null;
106
+ }
107
+ const map = source;
108
+ return require_own.hasOwnKey(map, info.code) ? require_money.money(map[info.code], info.code) : null;
109
+ }
110
+ /** Whether unknown Medusa input holds an amount above zero. `''`, null and garbage are false, never a throw. */
111
+ function isPositiveAmount(raw) {
112
+ const value = require_money.medusaAmountDecimal(raw);
113
+ return value !== null && require_money.isPositiveDecimal(value);
114
+ }
115
+ //#endregion
116
+ Object.defineProperty(exports, "isPositiveAmount", {
117
+ enumerable: true,
118
+ get: function() {
119
+ return isPositiveAmount;
120
+ }
121
+ });
122
+ Object.defineProperty(exports, "moneyByCurrency", {
123
+ enumerable: true,
124
+ get: function() {
125
+ return moneyByCurrency;
126
+ }
127
+ });
128
+ Object.defineProperty(exports, "moneyFor", {
129
+ enumerable: true,
130
+ get: function() {
131
+ return moneyFor;
132
+ }
133
+ });
134
+ Object.defineProperty(exports, "readCalculatedPrice", {
135
+ enumerable: true,
136
+ get: function() {
137
+ return readCalculatedPrice;
138
+ }
139
+ });
140
+ Object.defineProperty(exports, "readStoredDecimal", {
141
+ enumerable: true,
142
+ get: function() {
143
+ return readStoredDecimal;
144
+ }
145
+ });
146
+ Object.defineProperty(exports, "readStoredField", {
147
+ enumerable: true,
148
+ get: function() {
149
+ return readStoredField;
150
+ }
151
+ });
152
+ Object.defineProperty(exports, "readStoredLiteral", {
153
+ enumerable: true,
154
+ get: function() {
155
+ return readStoredLiteral;
156
+ }
157
+ });
158
+ Object.defineProperty(exports, "storedFieldValue", {
159
+ enumerable: true,
160
+ get: function() {
161
+ return storedFieldValue;
162
+ }
163
+ });
164
+ Object.defineProperty(exports, "toMedusaAmountInput", {
165
+ enumerable: true,
166
+ get: function() {
167
+ return toMedusaAmountInput;
168
+ }
169
+ });
170
+ Object.defineProperty(exports, "toMedusaRaw", {
171
+ enumerable: true,
172
+ get: function() {
173
+ return toMedusaRaw;
174
+ }
175
+ });
@@ -0,0 +1,248 @@
1
+ //#region src/decimal.d.ts
2
+ /**
3
+ * How a value between two representable amounts is resolved. `half-up` rounds a tie away from zero.
4
+ * `floor-after-tenth` is Shopify's discount truncation: round to a tenth of the unit (half-up), then
5
+ * floor (14.99995 gives 15.00, 1.899 gives 1.89).
6
+ */
7
+ type RoundingMode = 'half-up' | 'half-even' | 'down' | 'up' | 'floor' | 'ceil' | 'floor-after-tenth';
8
+ declare const DECIMAL_TEXT_BRAND: unique symbol;
9
+ /** Canonical decimal text: optional `-`, no leading zeros, no exponent, no trailing fraction zeros, `'0'` for zero. */
10
+ type DecimalText = string & {
11
+ readonly [DECIMAL_TEXT_BRAND]: true;
12
+ };
13
+ //#endregion
14
+ //#region src/registry.d.ts
15
+ declare const CURRENCY_CODE: unique symbol;
16
+ /** An upper-case code this package knows. Obtain one from `currencyCode` or `isCurrencyCode`. */
17
+ type CurrencyCode = string & {
18
+ readonly [CURRENCY_CODE]: true;
19
+ };
20
+ type CurrencyStatus = 'active' | 'historic';
21
+ /** Amboras' storage multiplier: x100 for every currency (zero-decimal ones too), x1000 for KWD, BHD, OMR, TND, JOD, IQD, LYD. */
22
+ type StorageScale = 100 | 1000;
23
+ type FractionDigits = 0 | 2 | 3;
24
+ interface CurrencyInfo {
25
+ readonly code: CurrencyCode;
26
+ /** English name (CLDR). */
27
+ readonly name: string;
28
+ /** English symbol (CLDR): `$`, `CA$`, `¥`; the code itself when CLDR has none. */
29
+ readonly symbol: string;
30
+ /** Narrow symbol (CLDR): `$` for CAD too. */
31
+ readonly narrowSymbol: string;
32
+ /** True when another active currency uses the same narrow symbol (`$`, `¥`, `kr`). */
33
+ readonly symbolIsAmbiguous: boolean;
34
+ /** ISO 4217 numeric code from CLDR, three digits; null where CLDR has none. */
35
+ readonly numericCode: string | null;
36
+ readonly status: CurrencyStatus;
37
+ /** The code that replaced a historic currency; null for active ones. */
38
+ readonly replacedBy: CurrencyCode | null;
39
+ readonly storageScale: StorageScale;
40
+ /** Fraction digits a stored amount is rounded to on the way in (0 for JPY, 3 for IQD). */
41
+ readonly storageDigits: FractionDigits;
42
+ /** Fraction digits shown to people (0 for HUF and IQD, 3 for KWD). */
43
+ readonly displayDigits: FractionDigits;
44
+ /**
45
+ * Medusa core's `decimal_digits` (pinned `@medusajs/utils` defaults; 2 where Medusa lists
46
+ * none). Only for agreeing with Medusa's payment-status epsilon: see `medusaEpsilon`.
47
+ */
48
+ readonly medusaDigits: FractionDigits;
49
+ /** Other spellings stores print for the currency (`['₹', 'Rs.', 'Rs']` for INR); empty where none. */
50
+ readonly symbolAliases: readonly string[];
51
+ /** The locale storefronts format this currency in (`'de-DE'` for EUR), or null. */
52
+ readonly localeHint: string | null;
53
+ /**
54
+ * A three-decimal currency written with a decimal comma: text reading takes a lone comma
55
+ * before three digits as its decimal (`'12,500'` TND is 12.5) and a lone dot as grouping,
56
+ * the reverse of KWD. True for TND only.
57
+ */
58
+ readonly commaDecimal: boolean;
59
+ }
60
+ interface CurrencyCodeOptions {
61
+ /**
62
+ * Accept retired codes such as BGN or HRK. Default true: an existing store's currency (a
63
+ * region, a price, a tool input) keeps validating after the code is retired. Pass false only
64
+ * where a currency is being picked for something new (a new store, a new region).
65
+ */
66
+ allowHistoric?: boolean;
67
+ /** Names the input field in the error. */
68
+ field?: string;
69
+ }
70
+ /** Every currency this package knows, active and historic, sorted by code. */
71
+ declare const CURRENCIES: readonly CurrencyInfo[];
72
+ /** `input` as a CurrencyCode (`'usd'` gives `'USD'`, `'bgn'` gives `'BGN'`). Throws `currency_invalid`, and for a historic code with `allowHistoric: false`. */
73
+ declare function currencyCode(input: string, options?: CurrencyCodeOptions): CurrencyCode;
74
+ /** Whether `value` already is a CurrencyCode: an upper-case known code (historic ones too, unless `allowHistoric: false`). */
75
+ declare function isCurrencyCode(value: unknown, options?: Pick<CurrencyCodeOptions, 'allowHistoric'>): value is CurrencyCode;
76
+ /** Everything known about a currency, historic ones included. Throws `currency_invalid` for an unknown code. */
77
+ declare function currencyInfo(code: string): CurrencyInfo;
78
+ /** 100, or 1000 for KWD, BHD, OMR, TND, JOD, IQD, LYD. Stored amount = major amount x storageScale. */
79
+ declare function storageScale(code: string): StorageScale;
80
+ /** Fraction digits a stored amount is rounded to: 0 for JPY, KRW, VND...; 3 for the x1000 seven; else 2. */
81
+ declare function storageDigits(code: string): FractionDigits;
82
+ /** Fraction digits to show: 0 for JPY, HUF, IDR, IQD...; 3 for KWD, BHD, OMR, TND, JOD, LYD; else 2. */
83
+ declare function displayDigits(code: string): FractionDigits;
84
+ /** `input` as a CurrencyCode, or null (see `findCurrency`). For optional input: "null while unknown". */
85
+ declare function parseCurrencyCode(input: unknown, options?: CurrencyCodeOptions): CurrencyCode | null;
86
+ /**
87
+ * The first candidate that is a known currency (historic codes included). Empty candidates
88
+ * (null, undefined, blank) are skipped; a non-empty one that is not a known code throws
89
+ * `currency_invalid` (a corrupt code is never replaced by a fallback); all empty throws
90
+ * `currency_missing`. No implicit default: pass `'USD'` last to make one visible.
91
+ */
92
+ declare function firstCurrency(...candidates: unknown[]): CurrencyCode;
93
+ /** Whether both parse (historic allowed) to the same code. Empty or unknown never matches. */
94
+ declare function sameCurrency(a: unknown, b: unknown): boolean;
95
+ /** The lower-case code for Medusa columns, Medusa API payloads and SQL on lower-case columns. Historic codes allowed. */
96
+ declare function medusaCurrencyCode(code: string): string;
97
+ /** CLDR English symbol (`'CA$'`, `'A$'`), or the narrow one (`'$'`) with `display: 'narrow'`. From the registry, never ICU. */
98
+ declare function currencySymbol(code: string, options?: {
99
+ display?: 'symbol' | 'narrow';
100
+ }): string;
101
+ /**
102
+ * The currency's name: English (`locale` absent or `'en'`) from the registry; other locales
103
+ * from `Intl.DisplayNames`, falling back to the English name when Intl throws or knows none.
104
+ */
105
+ declare function currencyName(code: string, locale?: string): string;
106
+ /** `'Canadian Dollar (CAD $)'`, `'Euro (EUR €)'`, `'UAE Dirham (AED)'`: name, code, and the narrow symbol when it is not the code. */
107
+ declare function currencyLabel(code: string, options?: {
108
+ locale?: string;
109
+ }): string;
110
+ /**
111
+ * Currencies for a picker that chooses a currency for something new: active ones only
112
+ * (`includeHistoric: true` adds the retired ones), `preferred` first in the given order
113
+ * (unknown codes, and retired ones unless included, ignored), the rest by code.
114
+ */
115
+ declare function currencyOptions(options?: {
116
+ preferred?: readonly string[];
117
+ locale?: string;
118
+ includeHistoric?: boolean;
119
+ }): ReadonlyArray<{
120
+ code: CurrencyCode;
121
+ label: string;
122
+ }>;
123
+ /** The locale storefronts format `code` in (`'de-DE'` for EUR), else `'en-US'`. Opt-in: formatMoney never calls it. */
124
+ declare function localeForCurrency(code: string): string;
125
+ /**
126
+ * Medusa core's payment-status epsilon for `code`, as text: `'1'` (JPY, HUF, IQD...),
127
+ * `'0.001'` (KWD, BHD, JOD, LYD, OMR, TND), else `'0.01'`. Compared against storage-scale
128
+ * numbers, as Medusa does, so these sites agree with Medusa's own `payment_status`.
129
+ */
130
+ declare function medusaEpsilon(code: string): DecimalText;
131
+ //#endregion
132
+ //#region src/money.d.ts
133
+ declare const STORED_AMOUNT: unique symbol;
134
+ declare const MAJOR_DECIMAL: unique symbol;
135
+ /** An amount in Amboras storage units (major x storageScale): a safe integer. */
136
+ type StoredAmount = number & {
137
+ readonly [STORED_AMOUNT]: true;
138
+ };
139
+ /** A major-unit amount as canonical decimal text: `-12.50`, `1200`, `1.234`. */
140
+ type MajorDecimal = string & {
141
+ readonly [MAJOR_DECIMAL]: true;
142
+ };
143
+ /** A stored amount and its currency. Build one with `money`, `fromMajor` or `readStored`. */
144
+ interface Money {
145
+ readonly amount: StoredAmount;
146
+ readonly currency: CurrencyCode;
147
+ }
148
+ /**
149
+ * A stored amount that may hold a fraction of a storage unit, as Medusa keeps unrounded totals (a
150
+ * tax line can leave 107.8175): a finite number (a safe integer when whole) or plain decimal text
151
+ * (`readStoredDecimal`'s result). `Money` is always whole; the PSP codecs and comparators take
152
+ * either, and `formatStored` formats one.
153
+ */
154
+ interface FractionalMoney {
155
+ readonly amount: number | DecimalText;
156
+ readonly currency: string;
157
+ }
158
+ interface FromMajorOptions {
159
+ /**
160
+ * More fraction digits than the currency stores: `'reject'` (default) throws
161
+ * `precision`, `'round'` rounds to the currency's storage digits with `rounding`.
162
+ */
163
+ precision?: 'reject' | 'round';
164
+ /** Used with `precision: 'round'`. Default `'half-up'` (a tie rounds away from zero). */
165
+ rounding?: RoundingMode;
166
+ /**
167
+ * The finest amount kept: `'currency'` (default) the currency's storage digits (whole yen,
168
+ * a cent); `'storage'` one storage unit (1/100 yen), Medusa `toMinorUnits`
169
+ * (`Math.round(major x divisor)`): JPY 1200.5 is 120050 and with `precision: 'round'`
170
+ * JPY 1200.555 is 120056.
171
+ */
172
+ unit?: 'currency' | 'storage';
173
+ /** Names the input field in errors. */
174
+ field?: string;
175
+ }
176
+ interface ToMajorDecimalOptions {
177
+ /**
178
+ * `'exact'` (default): the stored value exactly, with the currency's storage digits,
179
+ * widened to the storage scale's digits only when the value needs them (JPY 1200.50).
180
+ * `'display'`: rounded to the currency's display digits with `rounding`.
181
+ */
182
+ digits?: 'exact' | 'display';
183
+ /** Used with `digits: 'display'`. Default `'half-up'`. */
184
+ rounding?: RoundingMode;
185
+ }
186
+ interface ReadStoredOptions {
187
+ /** `'strict'` (default) throws `amount_invalid` on a bad amount; `'lenient'` returns null. Never 0. */
188
+ mode?: 'strict' | 'lenient';
189
+ /**
190
+ * A fractional stored amount (Medusa's unrounded totals, 107.8175): `'reject'` (default) reads it
191
+ * as invalid; `'round'` rounds it half-up, exactly, to a whole storage unit (107.8175 gives 108).
192
+ */
193
+ precision?: 'reject' | 'round';
194
+ /** Names the input field in errors. */
195
+ field?: string;
196
+ }
197
+ interface StoredAmountOptions {
198
+ /** Inclusive, in stored units. */
199
+ min?: number;
200
+ /** Inclusive, in stored units. */
201
+ max?: number;
202
+ /** Default false. */
203
+ allowNegative?: boolean;
204
+ /** Default true. */
205
+ allowZero?: boolean;
206
+ }
207
+ /** Whether `value` is a stored amount: a safe integer number within the given sign and range rules. */
208
+ declare function isStoredAmount(value: unknown, options?: StoredAmountOptions): value is number;
209
+ /** A stored amount (already x100 / x1000) as Money. Throws `amount_invalid` unless it is a safe integer. */
210
+ declare function money(stored: number, currency: string): Money;
211
+ /**
212
+ * A major-unit amount (19.99 or '19.99') as Money: x100, or x1000 for the seven
213
+ * three-decimal currencies. JPY 1500 is stored as 150000.
214
+ *
215
+ * `precision: 'reject'` (default) is capability-kit `toStorageAmount` exactly: more
216
+ * fraction digits than the `unit` keeps (default the currency's storage digits) throws
217
+ * `precision` (19.999 USD, 5.5 JPY) unless a number is only float noise away from an
218
+ * allowed amount. Strings are exact, so they get no noise allowance. `precision: 'round'`
219
+ * rounds to the unit instead (JPY 1500.4 gives 150000; with `unit: 'storage'` 150040).
220
+ * Beyond 2^46 units of the storage digits throws `overflow`.
221
+ */
222
+ declare function fromMajor(major: number | string, currency: string, options?: FromMajorOptions): Money;
223
+ /** Major units as exact decimal text (USD 1999 gives '19.99', JPY 120000 gives '1200', KWD 1005 gives '1.005'). */
224
+ declare function toMajorDecimal(m: Money, options?: ToMajorDecimalOptions): MajorDecimal;
225
+ /**
226
+ * Major units as a number: the double nearest the exact amount (`digits: 'exact'`, default),
227
+ * or rounded to the display digits first (`digits: 'display'`, the pixel value). For
228
+ * arithmetic, stay in Money.
229
+ */
230
+ declare function toMajorNumber(m: Money, options?: ToMajorDecimalOptions): number;
231
+ /**
232
+ * A stored amount as Medusa hands it over (a number, a numeric string, a raw
233
+ * `{ value: '4300', precision: 20 }`, a live BigNumber) as Money. Precedence
234
+ * `.value ?? .raw.value ?? .raw_.value`, then `.numeric ?? .numeric_`, then the
235
+ * primitive. `'4300.00'` is accepted; a fractional amount is rounded half-up to a whole
236
+ * storage unit with `precision: 'round'`; otherwise a fractional, non-finite, unsafe or
237
+ * unreadable amount throws `amount_invalid`, or gives null with `mode: 'lenient'` (never 0).
238
+ * An unknown currency always throws.
239
+ */
240
+ declare function readStored(raw: unknown, currency: string, options: ReadStoredOptions & {
241
+ mode: 'lenient';
242
+ }): Money | null;
243
+ declare function readStored(raw: unknown, currency: string, options?: ReadStoredOptions & {
244
+ mode?: 'strict';
245
+ }): Money;
246
+ declare function readStored(raw: unknown, currency: string, options?: ReadStoredOptions): Money | null;
247
+ //#endregion
248
+ export { isCurrencyCode as A, currencyInfo as C, currencySymbol as D, currencyOptions as E, sameCurrency as F, storageDigits as I, storageScale as L, medusaCurrencyCode as M, medusaEpsilon as N, displayDigits as O, parseCurrencyCode as P, DecimalText as R, currencyCode as S, currencyName as T, CurrencyCodeOptions as _, ReadStoredOptions as a, FractionDigits as b, ToMajorDecimalOptions as c, money as d, readStored as f, CurrencyCode as g, CURRENCIES as h, Money as i, localeForCurrency as j, firstCurrency as k, fromMajor as l, toMajorNumber as m, FromMajorOptions as n, StoredAmount as o, toMajorDecimal as p, MajorDecimal as r, StoredAmountOptions as s, FractionalMoney as t, isStoredAmount as u, CurrencyInfo as v, currencyLabel as w, StorageScale as x, CurrencyStatus as y, RoundingMode as z };
@@ -0,0 +1,248 @@
1
+ //#region src/decimal.d.ts
2
+ /**
3
+ * How a value between two representable amounts is resolved. `half-up` rounds a tie away from zero.
4
+ * `floor-after-tenth` is Shopify's discount truncation: round to a tenth of the unit (half-up), then
5
+ * floor (14.99995 gives 15.00, 1.899 gives 1.89).
6
+ */
7
+ type RoundingMode = 'half-up' | 'half-even' | 'down' | 'up' | 'floor' | 'ceil' | 'floor-after-tenth';
8
+ declare const DECIMAL_TEXT_BRAND: unique symbol;
9
+ /** Canonical decimal text: optional `-`, no leading zeros, no exponent, no trailing fraction zeros, `'0'` for zero. */
10
+ type DecimalText = string & {
11
+ readonly [DECIMAL_TEXT_BRAND]: true;
12
+ };
13
+ //#endregion
14
+ //#region src/registry.d.ts
15
+ declare const CURRENCY_CODE: unique symbol;
16
+ /** An upper-case code this package knows. Obtain one from `currencyCode` or `isCurrencyCode`. */
17
+ type CurrencyCode = string & {
18
+ readonly [CURRENCY_CODE]: true;
19
+ };
20
+ type CurrencyStatus = 'active' | 'historic';
21
+ /** Amboras' storage multiplier: x100 for every currency (zero-decimal ones too), x1000 for KWD, BHD, OMR, TND, JOD, IQD, LYD. */
22
+ type StorageScale = 100 | 1000;
23
+ type FractionDigits = 0 | 2 | 3;
24
+ interface CurrencyInfo {
25
+ readonly code: CurrencyCode;
26
+ /** English name (CLDR). */
27
+ readonly name: string;
28
+ /** English symbol (CLDR): `$`, `CA$`, `¥`; the code itself when CLDR has none. */
29
+ readonly symbol: string;
30
+ /** Narrow symbol (CLDR): `$` for CAD too. */
31
+ readonly narrowSymbol: string;
32
+ /** True when another active currency uses the same narrow symbol (`$`, `¥`, `kr`). */
33
+ readonly symbolIsAmbiguous: boolean;
34
+ /** ISO 4217 numeric code from CLDR, three digits; null where CLDR has none. */
35
+ readonly numericCode: string | null;
36
+ readonly status: CurrencyStatus;
37
+ /** The code that replaced a historic currency; null for active ones. */
38
+ readonly replacedBy: CurrencyCode | null;
39
+ readonly storageScale: StorageScale;
40
+ /** Fraction digits a stored amount is rounded to on the way in (0 for JPY, 3 for IQD). */
41
+ readonly storageDigits: FractionDigits;
42
+ /** Fraction digits shown to people (0 for HUF and IQD, 3 for KWD). */
43
+ readonly displayDigits: FractionDigits;
44
+ /**
45
+ * Medusa core's `decimal_digits` (pinned `@medusajs/utils` defaults; 2 where Medusa lists
46
+ * none). Only for agreeing with Medusa's payment-status epsilon: see `medusaEpsilon`.
47
+ */
48
+ readonly medusaDigits: FractionDigits;
49
+ /** Other spellings stores print for the currency (`['₹', 'Rs.', 'Rs']` for INR); empty where none. */
50
+ readonly symbolAliases: readonly string[];
51
+ /** The locale storefronts format this currency in (`'de-DE'` for EUR), or null. */
52
+ readonly localeHint: string | null;
53
+ /**
54
+ * A three-decimal currency written with a decimal comma: text reading takes a lone comma
55
+ * before three digits as its decimal (`'12,500'` TND is 12.5) and a lone dot as grouping,
56
+ * the reverse of KWD. True for TND only.
57
+ */
58
+ readonly commaDecimal: boolean;
59
+ }
60
+ interface CurrencyCodeOptions {
61
+ /**
62
+ * Accept retired codes such as BGN or HRK. Default true: an existing store's currency (a
63
+ * region, a price, a tool input) keeps validating after the code is retired. Pass false only
64
+ * where a currency is being picked for something new (a new store, a new region).
65
+ */
66
+ allowHistoric?: boolean;
67
+ /** Names the input field in the error. */
68
+ field?: string;
69
+ }
70
+ /** Every currency this package knows, active and historic, sorted by code. */
71
+ declare const CURRENCIES: readonly CurrencyInfo[];
72
+ /** `input` as a CurrencyCode (`'usd'` gives `'USD'`, `'bgn'` gives `'BGN'`). Throws `currency_invalid`, and for a historic code with `allowHistoric: false`. */
73
+ declare function currencyCode(input: string, options?: CurrencyCodeOptions): CurrencyCode;
74
+ /** Whether `value` already is a CurrencyCode: an upper-case known code (historic ones too, unless `allowHistoric: false`). */
75
+ declare function isCurrencyCode(value: unknown, options?: Pick<CurrencyCodeOptions, 'allowHistoric'>): value is CurrencyCode;
76
+ /** Everything known about a currency, historic ones included. Throws `currency_invalid` for an unknown code. */
77
+ declare function currencyInfo(code: string): CurrencyInfo;
78
+ /** 100, or 1000 for KWD, BHD, OMR, TND, JOD, IQD, LYD. Stored amount = major amount x storageScale. */
79
+ declare function storageScale(code: string): StorageScale;
80
+ /** Fraction digits a stored amount is rounded to: 0 for JPY, KRW, VND...; 3 for the x1000 seven; else 2. */
81
+ declare function storageDigits(code: string): FractionDigits;
82
+ /** Fraction digits to show: 0 for JPY, HUF, IDR, IQD...; 3 for KWD, BHD, OMR, TND, JOD, LYD; else 2. */
83
+ declare function displayDigits(code: string): FractionDigits;
84
+ /** `input` as a CurrencyCode, or null (see `findCurrency`). For optional input: "null while unknown". */
85
+ declare function parseCurrencyCode(input: unknown, options?: CurrencyCodeOptions): CurrencyCode | null;
86
+ /**
87
+ * The first candidate that is a known currency (historic codes included). Empty candidates
88
+ * (null, undefined, blank) are skipped; a non-empty one that is not a known code throws
89
+ * `currency_invalid` (a corrupt code is never replaced by a fallback); all empty throws
90
+ * `currency_missing`. No implicit default: pass `'USD'` last to make one visible.
91
+ */
92
+ declare function firstCurrency(...candidates: unknown[]): CurrencyCode;
93
+ /** Whether both parse (historic allowed) to the same code. Empty or unknown never matches. */
94
+ declare function sameCurrency(a: unknown, b: unknown): boolean;
95
+ /** The lower-case code for Medusa columns, Medusa API payloads and SQL on lower-case columns. Historic codes allowed. */
96
+ declare function medusaCurrencyCode(code: string): string;
97
+ /** CLDR English symbol (`'CA$'`, `'A$'`), or the narrow one (`'$'`) with `display: 'narrow'`. From the registry, never ICU. */
98
+ declare function currencySymbol(code: string, options?: {
99
+ display?: 'symbol' | 'narrow';
100
+ }): string;
101
+ /**
102
+ * The currency's name: English (`locale` absent or `'en'`) from the registry; other locales
103
+ * from `Intl.DisplayNames`, falling back to the English name when Intl throws or knows none.
104
+ */
105
+ declare function currencyName(code: string, locale?: string): string;
106
+ /** `'Canadian Dollar (CAD $)'`, `'Euro (EUR €)'`, `'UAE Dirham (AED)'`: name, code, and the narrow symbol when it is not the code. */
107
+ declare function currencyLabel(code: string, options?: {
108
+ locale?: string;
109
+ }): string;
110
+ /**
111
+ * Currencies for a picker that chooses a currency for something new: active ones only
112
+ * (`includeHistoric: true` adds the retired ones), `preferred` first in the given order
113
+ * (unknown codes, and retired ones unless included, ignored), the rest by code.
114
+ */
115
+ declare function currencyOptions(options?: {
116
+ preferred?: readonly string[];
117
+ locale?: string;
118
+ includeHistoric?: boolean;
119
+ }): ReadonlyArray<{
120
+ code: CurrencyCode;
121
+ label: string;
122
+ }>;
123
+ /** The locale storefronts format `code` in (`'de-DE'` for EUR), else `'en-US'`. Opt-in: formatMoney never calls it. */
124
+ declare function localeForCurrency(code: string): string;
125
+ /**
126
+ * Medusa core's payment-status epsilon for `code`, as text: `'1'` (JPY, HUF, IQD...),
127
+ * `'0.001'` (KWD, BHD, JOD, LYD, OMR, TND), else `'0.01'`. Compared against storage-scale
128
+ * numbers, as Medusa does, so these sites agree with Medusa's own `payment_status`.
129
+ */
130
+ declare function medusaEpsilon(code: string): DecimalText;
131
+ //#endregion
132
+ //#region src/money.d.ts
133
+ declare const STORED_AMOUNT: unique symbol;
134
+ declare const MAJOR_DECIMAL: unique symbol;
135
+ /** An amount in Amboras storage units (major x storageScale): a safe integer. */
136
+ type StoredAmount = number & {
137
+ readonly [STORED_AMOUNT]: true;
138
+ };
139
+ /** A major-unit amount as canonical decimal text: `-12.50`, `1200`, `1.234`. */
140
+ type MajorDecimal = string & {
141
+ readonly [MAJOR_DECIMAL]: true;
142
+ };
143
+ /** A stored amount and its currency. Build one with `money`, `fromMajor` or `readStored`. */
144
+ interface Money {
145
+ readonly amount: StoredAmount;
146
+ readonly currency: CurrencyCode;
147
+ }
148
+ /**
149
+ * A stored amount that may hold a fraction of a storage unit, as Medusa keeps unrounded totals (a
150
+ * tax line can leave 107.8175): a finite number (a safe integer when whole) or plain decimal text
151
+ * (`readStoredDecimal`'s result). `Money` is always whole; the PSP codecs and comparators take
152
+ * either, and `formatStored` formats one.
153
+ */
154
+ interface FractionalMoney {
155
+ readonly amount: number | DecimalText;
156
+ readonly currency: string;
157
+ }
158
+ interface FromMajorOptions {
159
+ /**
160
+ * More fraction digits than the currency stores: `'reject'` (default) throws
161
+ * `precision`, `'round'` rounds to the currency's storage digits with `rounding`.
162
+ */
163
+ precision?: 'reject' | 'round';
164
+ /** Used with `precision: 'round'`. Default `'half-up'` (a tie rounds away from zero). */
165
+ rounding?: RoundingMode;
166
+ /**
167
+ * The finest amount kept: `'currency'` (default) the currency's storage digits (whole yen,
168
+ * a cent); `'storage'` one storage unit (1/100 yen), Medusa `toMinorUnits`
169
+ * (`Math.round(major x divisor)`): JPY 1200.5 is 120050 and with `precision: 'round'`
170
+ * JPY 1200.555 is 120056.
171
+ */
172
+ unit?: 'currency' | 'storage';
173
+ /** Names the input field in errors. */
174
+ field?: string;
175
+ }
176
+ interface ToMajorDecimalOptions {
177
+ /**
178
+ * `'exact'` (default): the stored value exactly, with the currency's storage digits,
179
+ * widened to the storage scale's digits only when the value needs them (JPY 1200.50).
180
+ * `'display'`: rounded to the currency's display digits with `rounding`.
181
+ */
182
+ digits?: 'exact' | 'display';
183
+ /** Used with `digits: 'display'`. Default `'half-up'`. */
184
+ rounding?: RoundingMode;
185
+ }
186
+ interface ReadStoredOptions {
187
+ /** `'strict'` (default) throws `amount_invalid` on a bad amount; `'lenient'` returns null. Never 0. */
188
+ mode?: 'strict' | 'lenient';
189
+ /**
190
+ * A fractional stored amount (Medusa's unrounded totals, 107.8175): `'reject'` (default) reads it
191
+ * as invalid; `'round'` rounds it half-up, exactly, to a whole storage unit (107.8175 gives 108).
192
+ */
193
+ precision?: 'reject' | 'round';
194
+ /** Names the input field in errors. */
195
+ field?: string;
196
+ }
197
+ interface StoredAmountOptions {
198
+ /** Inclusive, in stored units. */
199
+ min?: number;
200
+ /** Inclusive, in stored units. */
201
+ max?: number;
202
+ /** Default false. */
203
+ allowNegative?: boolean;
204
+ /** Default true. */
205
+ allowZero?: boolean;
206
+ }
207
+ /** Whether `value` is a stored amount: a safe integer number within the given sign and range rules. */
208
+ declare function isStoredAmount(value: unknown, options?: StoredAmountOptions): value is number;
209
+ /** A stored amount (already x100 / x1000) as Money. Throws `amount_invalid` unless it is a safe integer. */
210
+ declare function money(stored: number, currency: string): Money;
211
+ /**
212
+ * A major-unit amount (19.99 or '19.99') as Money: x100, or x1000 for the seven
213
+ * three-decimal currencies. JPY 1500 is stored as 150000.
214
+ *
215
+ * `precision: 'reject'` (default) is capability-kit `toStorageAmount` exactly: more
216
+ * fraction digits than the `unit` keeps (default the currency's storage digits) throws
217
+ * `precision` (19.999 USD, 5.5 JPY) unless a number is only float noise away from an
218
+ * allowed amount. Strings are exact, so they get no noise allowance. `precision: 'round'`
219
+ * rounds to the unit instead (JPY 1500.4 gives 150000; with `unit: 'storage'` 150040).
220
+ * Beyond 2^46 units of the storage digits throws `overflow`.
221
+ */
222
+ declare function fromMajor(major: number | string, currency: string, options?: FromMajorOptions): Money;
223
+ /** Major units as exact decimal text (USD 1999 gives '19.99', JPY 120000 gives '1200', KWD 1005 gives '1.005'). */
224
+ declare function toMajorDecimal(m: Money, options?: ToMajorDecimalOptions): MajorDecimal;
225
+ /**
226
+ * Major units as a number: the double nearest the exact amount (`digits: 'exact'`, default),
227
+ * or rounded to the display digits first (`digits: 'display'`, the pixel value). For
228
+ * arithmetic, stay in Money.
229
+ */
230
+ declare function toMajorNumber(m: Money, options?: ToMajorDecimalOptions): number;
231
+ /**
232
+ * A stored amount as Medusa hands it over (a number, a numeric string, a raw
233
+ * `{ value: '4300', precision: 20 }`, a live BigNumber) as Money. Precedence
234
+ * `.value ?? .raw.value ?? .raw_.value`, then `.numeric ?? .numeric_`, then the
235
+ * primitive. `'4300.00'` is accepted; a fractional amount is rounded half-up to a whole
236
+ * storage unit with `precision: 'round'`; otherwise a fractional, non-finite, unsafe or
237
+ * unreadable amount throws `amount_invalid`, or gives null with `mode: 'lenient'` (never 0).
238
+ * An unknown currency always throws.
239
+ */
240
+ declare function readStored(raw: unknown, currency: string, options: ReadStoredOptions & {
241
+ mode: 'lenient';
242
+ }): Money | null;
243
+ declare function readStored(raw: unknown, currency: string, options?: ReadStoredOptions & {
244
+ mode?: 'strict';
245
+ }): Money;
246
+ declare function readStored(raw: unknown, currency: string, options?: ReadStoredOptions): Money | null;
247
+ //#endregion
248
+ export { isCurrencyCode as A, currencyInfo as C, currencySymbol as D, currencyOptions as E, sameCurrency as F, storageDigits as I, storageScale as L, medusaCurrencyCode as M, medusaEpsilon as N, displayDigits as O, parseCurrencyCode as P, DecimalText as R, currencyCode as S, currencyName as T, CurrencyCodeOptions as _, ReadStoredOptions as a, FractionDigits as b, ToMajorDecimalOptions as c, money as d, readStored as f, CurrencyCode as g, CURRENCIES as h, Money as i, localeForCurrency as j, firstCurrency as k, fromMajor as l, toMajorNumber as m, FromMajorOptions as n, StoredAmount as o, toMajorDecimal as p, MajorDecimal as r, StoredAmountOptions as s, FractionalMoney as t, isStoredAmount as u, CurrencyInfo as v, currencyLabel as w, StorageScale as x, CurrencyStatus as y, RoundingMode as z };