@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
package/dist/ai.d.cts ADDED
@@ -0,0 +1,97 @@
1
+ import { R as DecimalText, g as CurrencyCode, i as Money } from "./money-B-3aFjNJ.cjs";
2
+ //#region src/ai/mentions.d.ts
3
+ /** One price found in text. `code` is set when the mark names one currency; `amount` when the number reads (see the separator engine's 'auto' rule). */
4
+ interface MoneyMention {
5
+ /** The matched text, mark and number. */
6
+ readonly raw: string;
7
+ /** UTF-16 offset of `raw` in the text. */
8
+ readonly index: number;
9
+ /** The currency mark as written (`'$'`, `'AED'`, `'Rp'`). */
10
+ readonly symbol?: string;
11
+ readonly code?: CurrencyCode;
12
+ /** Major units as canonical decimal text. */
13
+ readonly amount?: DecimalText;
14
+ }
15
+ /**
16
+ * Every spelling a store may print for `currency` besides its ISO code, from the registry:
17
+ * symbol, narrow symbol and aliases (`['₹', 'Rs.', 'Rs']` for INR, `['$', 'US$']` for USD).
18
+ */
19
+ export declare function currencySymbolAliases(currency: string): readonly string[];
20
+ /**
21
+ * The money-literal regex as `{ source, flags }` (flags `'gu'`), a string so a browser script
22
+ * (`page.evaluate`) runs exactly what Node runs. It matches a currency mark next to a number,
23
+ * either order, at most one space between: the marks of `currency`, or of every active
24
+ * currency (single-letter marks such as `R` only when `currency` names them), plus any Unicode
25
+ * currency sign. Numbers group in threes or in Indian lakh (`1,00,000`) with `.`, `,`, space
26
+ * or apostrophe and carry up to `maxFractionDigits` (default 3) fraction digits. Groups: 1 mark and 2 number, or 3 number
27
+ * and 4 mark. A bare number never matches.
28
+ */
29
+ export declare function moneyLiteralPattern(options?: {
30
+ currency?: string;
31
+ maxFractionDigits?: number;
32
+ }): {
33
+ source: string;
34
+ flags: string;
35
+ };
36
+ /**
37
+ * Every price in `text`, in order (see `moneyLiteralPattern`). With `currency`, its marks are
38
+ * matched (single letters too) and claim the mentions they appear in; a mark only one active
39
+ * currency prints names that currency; `$`, `kr` and other shared marks leave `code` unset.
40
+ * `amount` is read with the separator engine's `'auto'` rule in the named currency (one
41
+ * separator before exactly three digits groups, except a KWD-style decimal mark).
42
+ */
43
+ export declare function moneyMentions(text: string, options?: {
44
+ currency?: string;
45
+ }): MoneyMention[];
46
+ //#endregion
47
+ //#region src/ai/model-money.d.ts
48
+ /** A stored amount as the model sees it: the major amount, the code and a display string. */
49
+ interface MoneyView {
50
+ readonly amount: number;
51
+ readonly currency: CurrencyCode;
52
+ readonly display: string;
53
+ }
54
+ /**
55
+ * `{ amount: 19.99, currency: 'USD', display: '$19.99' }`. `amount` is exact; `display` shows the
56
+ * display digits, widened to keep a stored value finer than them (JPY 550 is `'¥5.5'`, HUF 435050
57
+ * `'HUF 4,350.5'`). capability-kit's `moneyView` uses the storage digits as the minimum instead
58
+ * (HUF 435000 is `'HUF 4,350.00'` there and `'HUF 4,350'` here, IQD 1500 `'IQD 1.500'` against
59
+ * `'IQD 1.5'`): a deliberate change, listed in the README's behaviour changes.
60
+ */
61
+ export declare function moneyView(m: Money): MoneyView;
62
+ type ParseModelMoneyResult = {
63
+ readonly ok: true;
64
+ readonly money: Money;
65
+ } | {
66
+ readonly ok: false;
67
+ readonly error: string;
68
+ };
69
+ interface ParseModelMoneyOptions {
70
+ /** Default false. */
71
+ allowNegative?: boolean;
72
+ /** Default true. */
73
+ allowZero?: boolean;
74
+ }
75
+ /**
76
+ * An amount from a model tool call (major units, as the merchant says it) as Money, or an
77
+ * error to send back to the model ending with `MONEY_UNIT_SENTENCE`. Numbers go through
78
+ * `fromMajor` (float noise such as 0.1 + 0.2 allowed); strings through `/input`'s entry rule
79
+ * in English (`'1,299.00'`, `'$1,299'`, `'99.000'` VND, `'1.500'` JPY). More fraction digits
80
+ * than the currency stores and negative amounts are refused, never rounded or flipped. An
81
+ * unknown currency throws `currency_invalid` (it is the tool's, not the model's).
82
+ */
83
+ export declare function parseModelMoney(input: unknown, currency: string, options?: ParseModelMoneyOptions): ParseModelMoneyResult;
84
+ //#endregion
85
+ //#region src/ai/wording.d.ts
86
+ /** The one unit sentence every money field carries, for the model and for people. Verbatim. */
87
+ export declare const MONEY_UNIT_SENTENCE = "Amount in normal currency units as the merchant says it (99 means 99.00; 1.5 means 1.50). Never cents or minor units.";
88
+ /** JSON Schema keyword marking a money field. Verbatim. */
89
+ export declare const MONEY_META_KEY = "x-amboras-money";
90
+ /** How store API amounts are scaled, for tool descriptions that hand stored amounts to the model. */
91
+ export declare const STORAGE_UNIT_SENTENCE = "Store API amounts are storage units: major units x100, x1000 for KWD, BHD, OMR, TND, JOD, IQD and LYD, and x100 for zero-decimal currencies too. Render them with the money formatter; never divide or multiply by hand.";
92
+ /** What a manual exchange rate means, for rate fields. */
93
+ export declare const RATE_UNIT_SENTENCE = "1 unit of the store currency equals this many units of the other currency.";
94
+ /** A money field's description: `describe` (trimmed) followed by the unit sentence, or the sentence alone. */
95
+ export declare function moneyFieldDescription(describe?: string): string;
96
+ //#endregion
97
+ export type { MoneyMention, MoneyView, ParseModelMoneyOptions, ParseModelMoneyResult };
package/dist/ai.d.ts ADDED
@@ -0,0 +1,97 @@
1
+ import { R as DecimalText, g as CurrencyCode, i as Money } from "./money-B-3aFjNJ.js";
2
+ //#region src/ai/mentions.d.ts
3
+ /** One price found in text. `code` is set when the mark names one currency; `amount` when the number reads (see the separator engine's 'auto' rule). */
4
+ interface MoneyMention {
5
+ /** The matched text, mark and number. */
6
+ readonly raw: string;
7
+ /** UTF-16 offset of `raw` in the text. */
8
+ readonly index: number;
9
+ /** The currency mark as written (`'$'`, `'AED'`, `'Rp'`). */
10
+ readonly symbol?: string;
11
+ readonly code?: CurrencyCode;
12
+ /** Major units as canonical decimal text. */
13
+ readonly amount?: DecimalText;
14
+ }
15
+ /**
16
+ * Every spelling a store may print for `currency` besides its ISO code, from the registry:
17
+ * symbol, narrow symbol and aliases (`['₹', 'Rs.', 'Rs']` for INR, `['$', 'US$']` for USD).
18
+ */
19
+ export declare function currencySymbolAliases(currency: string): readonly string[];
20
+ /**
21
+ * The money-literal regex as `{ source, flags }` (flags `'gu'`), a string so a browser script
22
+ * (`page.evaluate`) runs exactly what Node runs. It matches a currency mark next to a number,
23
+ * either order, at most one space between: the marks of `currency`, or of every active
24
+ * currency (single-letter marks such as `R` only when `currency` names them), plus any Unicode
25
+ * currency sign. Numbers group in threes or in Indian lakh (`1,00,000`) with `.`, `,`, space
26
+ * or apostrophe and carry up to `maxFractionDigits` (default 3) fraction digits. Groups: 1 mark and 2 number, or 3 number
27
+ * and 4 mark. A bare number never matches.
28
+ */
29
+ export declare function moneyLiteralPattern(options?: {
30
+ currency?: string;
31
+ maxFractionDigits?: number;
32
+ }): {
33
+ source: string;
34
+ flags: string;
35
+ };
36
+ /**
37
+ * Every price in `text`, in order (see `moneyLiteralPattern`). With `currency`, its marks are
38
+ * matched (single letters too) and claim the mentions they appear in; a mark only one active
39
+ * currency prints names that currency; `$`, `kr` and other shared marks leave `code` unset.
40
+ * `amount` is read with the separator engine's `'auto'` rule in the named currency (one
41
+ * separator before exactly three digits groups, except a KWD-style decimal mark).
42
+ */
43
+ export declare function moneyMentions(text: string, options?: {
44
+ currency?: string;
45
+ }): MoneyMention[];
46
+ //#endregion
47
+ //#region src/ai/model-money.d.ts
48
+ /** A stored amount as the model sees it: the major amount, the code and a display string. */
49
+ interface MoneyView {
50
+ readonly amount: number;
51
+ readonly currency: CurrencyCode;
52
+ readonly display: string;
53
+ }
54
+ /**
55
+ * `{ amount: 19.99, currency: 'USD', display: '$19.99' }`. `amount` is exact; `display` shows the
56
+ * display digits, widened to keep a stored value finer than them (JPY 550 is `'¥5.5'`, HUF 435050
57
+ * `'HUF 4,350.5'`). capability-kit's `moneyView` uses the storage digits as the minimum instead
58
+ * (HUF 435000 is `'HUF 4,350.00'` there and `'HUF 4,350'` here, IQD 1500 `'IQD 1.500'` against
59
+ * `'IQD 1.5'`): a deliberate change, listed in the README's behaviour changes.
60
+ */
61
+ export declare function moneyView(m: Money): MoneyView;
62
+ type ParseModelMoneyResult = {
63
+ readonly ok: true;
64
+ readonly money: Money;
65
+ } | {
66
+ readonly ok: false;
67
+ readonly error: string;
68
+ };
69
+ interface ParseModelMoneyOptions {
70
+ /** Default false. */
71
+ allowNegative?: boolean;
72
+ /** Default true. */
73
+ allowZero?: boolean;
74
+ }
75
+ /**
76
+ * An amount from a model tool call (major units, as the merchant says it) as Money, or an
77
+ * error to send back to the model ending with `MONEY_UNIT_SENTENCE`. Numbers go through
78
+ * `fromMajor` (float noise such as 0.1 + 0.2 allowed); strings through `/input`'s entry rule
79
+ * in English (`'1,299.00'`, `'$1,299'`, `'99.000'` VND, `'1.500'` JPY). More fraction digits
80
+ * than the currency stores and negative amounts are refused, never rounded or flipped. An
81
+ * unknown currency throws `currency_invalid` (it is the tool's, not the model's).
82
+ */
83
+ export declare function parseModelMoney(input: unknown, currency: string, options?: ParseModelMoneyOptions): ParseModelMoneyResult;
84
+ //#endregion
85
+ //#region src/ai/wording.d.ts
86
+ /** The one unit sentence every money field carries, for the model and for people. Verbatim. */
87
+ export declare const MONEY_UNIT_SENTENCE = "Amount in normal currency units as the merchant says it (99 means 99.00; 1.5 means 1.50). Never cents or minor units.";
88
+ /** JSON Schema keyword marking a money field. Verbatim. */
89
+ export declare const MONEY_META_KEY = "x-amboras-money";
90
+ /** How store API amounts are scaled, for tool descriptions that hand stored amounts to the model. */
91
+ export declare const STORAGE_UNIT_SENTENCE = "Store API amounts are storage units: major units x100, x1000 for KWD, BHD, OMR, TND, JOD, IQD and LYD, and x100 for zero-decimal currencies too. Render them with the money formatter; never divide or multiply by hand.";
92
+ /** What a manual exchange rate means, for rate fields. */
93
+ export declare const RATE_UNIT_SENTENCE = "1 unit of the store currency equals this many units of the other currency.";
94
+ /** A money field's description: `describe` (trimmed) followed by the unit sentence, or the sentence alone. */
95
+ export declare function moneyFieldDescription(describe?: string): string;
96
+ //#endregion
97
+ export type { MoneyMention, MoneyView, ParseModelMoneyOptions, ParseModelMoneyResult };
package/dist/ai.js ADDED
@@ -0,0 +1,175 @@
1
+ import { $ as describeValue, E as resolveCurrency, Q as MoneyError, l as toMajorNumber, m as currencyMarks, n as fromMajor, pt as toDecimalText, s as storedDecimal, u as CURRENCIES } from "./money-WhnGiI00.js";
2
+ import { t as formatMoney } from "./format-BdPnijvn.js";
3
+ import { n as readNumberText, t as digitGroupingSource } from "./number-text-DE-q4jO5.js";
4
+ import { s as parseAmountInput } from "./amount-BoYBMgK9.js";
5
+ //#region src/ai/mentions.ts
6
+ const ACTIVE = CURRENCIES.filter((c) => c.status === "active");
7
+ const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8
+ const LETTER_START = /^\p{L}/u;
9
+ const LETTER_END = /\p{L}$/u;
10
+ const SINGLE_LETTER = /^\p{L}$/u;
11
+ const OWNERS = /* @__PURE__ */ new Map();
12
+ for (const info of ACTIVE) for (const mark of currencyMarks(info)) OWNERS.set(mark, [...OWNERS.get(mark) ?? [], info.code]);
13
+ /**
14
+ * Every spelling a store may print for `currency` besides its ISO code, from the registry:
15
+ * symbol, narrow symbol and aliases (`['₹', 'Rs.', 'Rs']` for INR, `['$', 'US$']` for USD).
16
+ */
17
+ function currencySymbolAliases(currency) {
18
+ const info = resolveCurrency(currency, { allowHistoric: true });
19
+ return Object.freeze(currencyMarks(info).filter((mark) => mark !== info.code));
20
+ }
21
+ /** A mark as a regex alternative; a mark that starts or ends with a letter must not touch another letter. */
22
+ function markSource(mark) {
23
+ const before = LETTER_START.test(mark) ? "(?<!\\p{L})" : "";
24
+ const after = LETTER_END.test(mark) ? "(?!\\p{L})" : "";
25
+ return `${before}${escapeRegExp(mark)}${after}`;
26
+ }
27
+ function numberSource(maxFractionDigits) {
28
+ const fraction = maxFractionDigits === 0 ? "" : `(?:[.,]\\d{1,${maxFractionDigits}})?`;
29
+ return `(?:${digitGroupingSource("[ \\u00a0\\u2009\\u202f.,'’]")})${fraction}|\\d+${fraction}`;
30
+ }
31
+ /**
32
+ * The money-literal regex as `{ source, flags }` (flags `'gu'`), a string so a browser script
33
+ * (`page.evaluate`) runs exactly what Node runs. It matches a currency mark next to a number,
34
+ * either order, at most one space between: the marks of `currency`, or of every active
35
+ * currency (single-letter marks such as `R` only when `currency` names them), plus any Unicode
36
+ * currency sign. Numbers group in threes or in Indian lakh (`1,00,000`) with `.`, `,`, space
37
+ * or apostrophe and carry up to `maxFractionDigits` (default 3) fraction digits. Groups: 1 mark and 2 number, or 3 number
38
+ * and 4 mark. A bare number never matches.
39
+ */
40
+ function moneyLiteralPattern(options = {}) {
41
+ const digits = options.maxFractionDigits ?? 3;
42
+ if (!Number.isInteger(digits) || digits < 0 || digits > 6) throw new MoneyError("amount_invalid", `maxFractionDigits must be an integer from 0 to 6, got ${describeValue(digits)}`);
43
+ const mark = `(${[...(options.currency === void 0 ? [...OWNERS.keys()].filter((mark) => !SINGLE_LETTER.test(mark)) : currencyMarks(resolveCurrency(options.currency, { allowHistoric: true }))).sort((a, b) => b.length - a.length || (a < b ? -1 : 1)).map(markSource), "\\p{Sc}"].join("|")})`;
44
+ const number = `(${numberSource(digits)})`;
45
+ return {
46
+ source: `${mark}\\s?${number}|${number}\\s?${mark}`,
47
+ flags: "gu"
48
+ };
49
+ }
50
+ function codeOfMark(mark, currency) {
51
+ if (currency && currencyMarks(currency).includes(mark)) return currency.code;
52
+ const owners = OWNERS.get(mark);
53
+ return owners?.length === 1 ? owners[0] : void 0;
54
+ }
55
+ /**
56
+ * Every price in `text`, in order (see `moneyLiteralPattern`). With `currency`, its marks are
57
+ * matched (single letters too) and claim the mentions they appear in; a mark only one active
58
+ * currency prints names that currency; `$`, `kr` and other shared marks leave `code` unset.
59
+ * `amount` is read with the separator engine's `'auto'` rule in the named currency (one
60
+ * separator before exactly three digits groups, except a KWD-style decimal mark).
61
+ */
62
+ function moneyMentions(text, options = {}) {
63
+ const currency = options.currency === void 0 ? null : resolveCurrency(options.currency, { allowHistoric: true });
64
+ const { source, flags } = moneyLiteralPattern({ currency: currency?.code });
65
+ const mentions = [];
66
+ for (const match of String(text).matchAll(new RegExp(source, flags))) {
67
+ const symbol = match[1] ?? match[4];
68
+ const code = codeOfMark(symbol, currency);
69
+ const read = readNumberText(match[2] ?? match[3], {
70
+ decimalSeparator: "auto",
71
+ currency: code
72
+ });
73
+ mentions.push({
74
+ raw: match[0],
75
+ index: match.index,
76
+ symbol,
77
+ ...code === void 0 ? {} : { code },
78
+ ..."reason" in read ? {} : { amount: toDecimalText({
79
+ units: read.units,
80
+ scale: read.scale
81
+ }) }
82
+ });
83
+ }
84
+ return mentions;
85
+ }
86
+ //#endregion
87
+ //#region src/ai/wording.ts
88
+ /** The one unit sentence every money field carries, for the model and for people. Verbatim. */
89
+ const MONEY_UNIT_SENTENCE = "Amount in normal currency units as the merchant says it (99 means 99.00; 1.5 means 1.50). Never cents or minor units.";
90
+ /** JSON Schema keyword marking a money field. Verbatim. */
91
+ const MONEY_META_KEY = "x-amboras-money";
92
+ /** How store API amounts are scaled, for tool descriptions that hand stored amounts to the model. */
93
+ const STORAGE_UNIT_SENTENCE = "Store API amounts are storage units: major units x100, x1000 for KWD, BHD, OMR, TND, JOD, IQD and LYD, and x100 for zero-decimal currencies too. Render them with the money formatter; never divide or multiply by hand.";
94
+ /** What a manual exchange rate means, for rate fields. */
95
+ const RATE_UNIT_SENTENCE = "1 unit of the store currency equals this many units of the other currency.";
96
+ /** A money field's description: `describe` (trimmed) followed by the unit sentence, or the sentence alone. */
97
+ function moneyFieldDescription(describe) {
98
+ const lead = typeof describe === "string" ? describe.trim() : "";
99
+ return lead ? `${lead} ${MONEY_UNIT_SENTENCE}` : MONEY_UNIT_SENTENCE;
100
+ }
101
+ //#endregion
102
+ //#region src/ai/model-money.ts
103
+ /**
104
+ * `{ amount: 19.99, currency: 'USD', display: '$19.99' }`. `amount` is exact; `display` shows the
105
+ * display digits, widened to keep a stored value finer than them (JPY 550 is `'¥5.5'`, HUF 435050
106
+ * `'HUF 4,350.5'`). capability-kit's `moneyView` uses the storage digits as the minimum instead
107
+ * (HUF 435000 is `'HUF 4,350.00'` there and `'HUF 4,350'` here, IQD 1500 `'IQD 1.500'` against
108
+ * `'IQD 1.5'`): a deliberate change, listed in the README's behaviour changes.
109
+ */
110
+ function moneyView(m) {
111
+ const { info } = storedDecimal(m);
112
+ return {
113
+ amount: toMajorNumber(m),
114
+ currency: info.code,
115
+ display: formatMoney(m, {
116
+ locale: "en",
117
+ digits: "widen"
118
+ })
119
+ };
120
+ }
121
+ function failure(reason, input, code, digits) {
122
+ const shown = describeValue(input);
123
+ return {
124
+ ok: false,
125
+ error: `${{
126
+ empty: `Not an amount: ${shown}.`,
127
+ unparseable: `Not an amount: ${shown}.`,
128
+ too_many_decimals: `${shown} has more decimal places than ${code} uses (at most ${digits}).`,
129
+ negative: "The amount must not be negative.",
130
+ zero: "The amount must be more than zero.",
131
+ below_min: `${shown} is out of range for ${code}.`,
132
+ above_max: `${shown} is out of range for ${code}.`
133
+ }[reason]} ${MONEY_UNIT_SENTENCE}`
134
+ };
135
+ }
136
+ function fromNumber(input, currency, options) {
137
+ if (!Number.isFinite(input)) return "unparseable";
138
+ if (input < 0 && !options.allowNegative) return "negative";
139
+ if (input === 0 && options.allowZero === false) return "zero";
140
+ try {
141
+ return fromMajor(input, currency);
142
+ } catch (error) {
143
+ return error.code === "precision" ? "too_many_decimals" : "above_max";
144
+ }
145
+ }
146
+ /**
147
+ * An amount from a model tool call (major units, as the merchant says it) as Money, or an
148
+ * error to send back to the model ending with `MONEY_UNIT_SENTENCE`. Numbers go through
149
+ * `fromMajor` (float noise such as 0.1 + 0.2 allowed); strings through `/input`'s entry rule
150
+ * in English (`'1,299.00'`, `'$1,299'`, `'99.000'` VND, `'1.500'` JPY). More fraction digits
151
+ * than the currency stores and negative amounts are refused, never rounded or flipped. An
152
+ * unknown currency throws `currency_invalid` (it is the tool's, not the model's).
153
+ */
154
+ function parseModelMoney(input, currency, options = {}) {
155
+ const info = resolveCurrency(currency, { allowHistoric: true });
156
+ if (typeof input === "number") {
157
+ const result = fromNumber(input, info.code, options);
158
+ return typeof result === "string" ? failure(result, input, info.code, info.storageDigits) : {
159
+ ok: true,
160
+ money: result
161
+ };
162
+ }
163
+ if (typeof input !== "string") return failure("unparseable", input, info.code, info.storageDigits);
164
+ const result = parseAmountInput(input, info.code, {
165
+ locale: "en",
166
+ allowNegative: options.allowNegative,
167
+ allowZero: options.allowZero
168
+ });
169
+ return result.ok ? {
170
+ ok: true,
171
+ money: result.money
172
+ } : failure(result.reason, input, info.code, info.storageDigits);
173
+ }
174
+ //#endregion
175
+ export { MONEY_META_KEY, MONEY_UNIT_SENTENCE, RATE_UNIT_SENTENCE, STORAGE_UNIT_SENTENCE, currencySymbolAliases, moneyFieldDescription, moneyLiteralPattern, moneyMentions, moneyView, parseModelMoney };
@@ -0,0 +1,222 @@
1
+ const require_money = require("./money-B3wH_Z4S.cjs");
2
+ const require_rounding = require("./rounding-DHBtNCHG.cjs");
3
+ //#region src/allocate.ts
4
+ const invalid = (message) => new require_money.MoneyError("amount_invalid", message);
5
+ const sumOf = (values) => values.reduce((a, b) => a + b, 0n);
6
+ const floorShares = (units, weights, total) => weights.map((w) => require_money.divRound(units * w, total, "floor"));
7
+ function largestRemainder(units, weights, total) {
8
+ const shares = floorShares(units, weights, total);
9
+ let rest = units - sumOf(shares);
10
+ const order = weights.map((w, i) => ({
11
+ i,
12
+ fraction: units * w % total
13
+ })).sort((a, b) => a.fraction === b.fraction ? a.i - b.i : a.fraction > b.fraction ? -1 : 1);
14
+ for (const { i } of order) {
15
+ if (rest === 0n) break;
16
+ shares[i] += 1n;
17
+ rest -= 1n;
18
+ }
19
+ return shares;
20
+ }
21
+ function largestWeight(units, weights, total) {
22
+ const top = weights.indexOf(weights.reduce((a, b) => b > a ? b : a));
23
+ const shares = weights.map((w, i) => i === top ? 0n : require_money.mulDiv(units, w, total, "half-up"));
24
+ let deficit = sumOf(shares) - units;
25
+ if (deficit <= 0n) {
26
+ shares[top] = -deficit;
27
+ return shares;
28
+ }
29
+ const byWeight = weights.map((w, i) => ({
30
+ w,
31
+ i
32
+ })).sort((a, b) => a.w === b.w ? a.i - b.i : a.w > b.w ? -1 : 1);
33
+ for (const { i } of byWeight) {
34
+ const take = shares[i] < deficit ? shares[i] : deficit;
35
+ shares[i] -= take;
36
+ deficit -= take;
37
+ }
38
+ return shares;
39
+ }
40
+ function lastAbsorbs(units, weights, total) {
41
+ const shares = floorShares(units, weights, total);
42
+ let last = weights.length - 1;
43
+ while (weights[last] === 0n) last -= 1;
44
+ shares[last] += units - sumOf(shares);
45
+ return shares;
46
+ }
47
+ function cumulative(units, weights, total) {
48
+ let running = 0n;
49
+ let previous = 0n;
50
+ return weights.map((w) => {
51
+ running += w;
52
+ const reached = require_money.mulDiv(units, running, total, "half-up");
53
+ const share = reached - previous;
54
+ previous = reached;
55
+ return share;
56
+ });
57
+ }
58
+ function lastFirst(amount, weights, total, step, caps) {
59
+ const capTotal = sumOf(caps);
60
+ const target = amount < capTotal ? amount : capTotal;
61
+ const shares = weights.map((w, i) => {
62
+ const share = require_money.mulDiv(target, w, total * step, "half-up") * step;
63
+ return share < caps[i] ? share : caps[i];
64
+ });
65
+ let rest = target - sumOf(shares);
66
+ for (let i = shares.length - 1; i >= 0 && rest !== 0n; i--) {
67
+ const wanted = shares[i] + rest;
68
+ const next = wanted < 0n ? 0n : wanted > caps[i] ? caps[i] : wanted;
69
+ rest -= next - shares[i];
70
+ shares[i] = next;
71
+ }
72
+ return shares;
73
+ }
74
+ /**
75
+ * Internal: `total` split across non-negative integer `weights` in integer units, exactly.
76
+ * Shares are multiples of `step`; a negative total is split by magnitude and negated;
77
+ * all-zero weights need a zero total. Throws `amount_invalid` (weights, caps, step) or
78
+ * `precision` (total not a multiple of step).
79
+ */
80
+ function allocateUnits(total, weights, options = {}) {
81
+ const strategy = options.strategy ?? "largest-remainder";
82
+ const step = options.step ?? 1n;
83
+ if (step <= 0n) throw invalid(`Allocation step must be positive, got ${step}`);
84
+ if (weights.some((w) => w < 0n)) throw invalid("Allocation weights must not be negative");
85
+ if (total % step !== 0n) throw new require_money.MoneyError("precision", `${total} is not a whole number of ${step}-unit steps; round it first`);
86
+ const weightTotal = sumOf(weights);
87
+ const negative = total < 0n;
88
+ const magnitude = negative ? -total : total;
89
+ let shares;
90
+ if (weightTotal === 0n) {
91
+ if (magnitude !== 0n) throw invalid("Cannot allocate a non-zero total across zero weights");
92
+ shares = weights.map(() => 0n);
93
+ } else if (strategy === "last-first") {
94
+ const caps = options.caps;
95
+ if (!caps || caps.length !== weights.length || caps.some((c) => c < 0n)) throw invalid("'last-first' needs one non-negative cap per weight");
96
+ shares = lastFirst(magnitude, weights, weightTotal, step, caps);
97
+ } else {
98
+ const units = magnitude / step;
99
+ const split = strategy === "largest-weight" ? largestWeight : strategy === "last" ? lastAbsorbs : strategy === "cumulative" ? cumulative : strategy === "largest-remainder" ? largestRemainder : void 0;
100
+ if (!split) throw invalid(`Unknown allocation strategy: ${require_money.describeValue(strategy)}`);
101
+ shares = split(units, weights, weightTotal).map((s) => s * step);
102
+ }
103
+ return negative ? shares.map((s) => s === 0n ? 0n : -s) : shares;
104
+ }
105
+ /** Weights as integers on one common scale. Money weights must be in `currency` (when given). */
106
+ function weightUnits(weights, info) {
107
+ const values = weights.map((w) => {
108
+ if (typeof w !== "object" || w === null) return require_money.decimalOf(w, true, "weight");
109
+ const own = require_money.storedDecimal(w).info;
110
+ if (info && own.code !== info.code) throw new require_money.MoneyError("currency_mismatch", `Weight in ${own.code}, total in ${info.code}`);
111
+ return {
112
+ units: BigInt(w.amount),
113
+ scale: 0
114
+ };
115
+ });
116
+ const scale = Math.max(0, ...values.map((v) => v.scale));
117
+ return values.map((v) => require_money.roundToScale(v, scale, "down").units);
118
+ }
119
+ function stepOf(info, unit) {
120
+ if (typeof unit === "object" && unit !== null) {
121
+ if (!Number.isSafeInteger(unit.step) || unit.step < 1) throw invalid(`unit.step must be a positive integer, got ${require_money.describeValue(unit.step)}`);
122
+ return BigInt(unit.step);
123
+ }
124
+ return require_rounding.unitSize(info, unit ?? "storage");
125
+ }
126
+ function allocate(total, weights, options = {}) {
127
+ if (typeof total === "object" && total !== null) {
128
+ const { info } = require_money.storedDecimal(total);
129
+ const units = weightUnits(weights, info);
130
+ let caps;
131
+ if (options.strategy === "last-first") caps = (options.caps ?? (weights.every((w) => typeof w === "object" && w !== null) ? weights : void 0))?.map((c) => weightUnits([c], info)[0]);
132
+ return allocateUnits(BigInt(total.amount), units, {
133
+ strategy: options.strategy,
134
+ step: stepOf(info, options.unit),
135
+ caps
136
+ }).map((s) => require_money.boundedMoney(s, info));
137
+ }
138
+ const scale = options.scale;
139
+ if (scale === void 0 || !Number.isSafeInteger(scale) || scale < 0 || scale > 100) throw invalid(`scale must be an integer from 0 to 100, got ${require_money.describeValue(scale)}`);
140
+ if (options.strategy === "last-first") throw invalid("'last-first' needs caps: allocate Money instead");
141
+ const value = require_money.decimalOf(total, true, "total");
142
+ if (require_money.fractionDigitsOf(value) > scale) throw new require_money.MoneyError("precision", `${total} has more than ${scale} fraction digits`);
143
+ return allocateUnits(require_money.roundToScale(value, scale, "down").units, weightUnits(weights, null), { strategy: options.strategy }).map((s) => require_money.toDecimalText({
144
+ units: s,
145
+ scale
146
+ }));
147
+ }
148
+ /**
149
+ * Greedy capped allocation in the given order: `take_i = min(left, cap_i)`; `shortfall` is
150
+ * what could not be placed. With `allOrNothing`, a shortfall allocates nothing (all zeros,
151
+ * `shortfall = amount`). Negative caps or amount throw `amount_invalid`; all one currency.
152
+ */
153
+ function allocateWaterfall(caps, amount, options = {}) {
154
+ const { info } = require_money.storedDecimal(amount);
155
+ const limits = caps.map((c) => weightUnits([c], info)[0]);
156
+ let left = BigInt(amount.amount);
157
+ if (left < 0n || limits.some((c) => c < 0n)) throw invalid("Waterfall caps and amount must not be negative");
158
+ const takes = limits.map((cap) => {
159
+ const take = cap < left ? cap : left;
160
+ left -= take;
161
+ return take;
162
+ });
163
+ if (left > 0n && options.allOrNothing) return {
164
+ allocations: takes.map(() => require_money.boundedMoney(0n, info)),
165
+ shortfall: require_money.boundedMoney(BigInt(amount.amount), info)
166
+ };
167
+ return {
168
+ allocations: takes.map((t) => require_money.boundedMoney(t, info)),
169
+ shortfall: require_money.boundedMoney(left, info)
170
+ };
171
+ }
172
+ /**
173
+ * A line total charged at one unit price, or split in two parts one `unit` apart when it does
174
+ * not divide by `quantity`, so `sum(quantity x unitPrice) === net` exactly (JPY 100000 x 3
175
+ * gives `2 @ 33333`, `1 @ 33334`). Zero-quantity parts are omitted. `quantity` must be a
176
+ * positive integer; `net` a whole `unit` (else `precision`).
177
+ */
178
+ function splitUnitPrice(net, quantity, options = {}) {
179
+ const { info } = require_money.storedDecimal(net);
180
+ if (!Number.isSafeInteger(quantity) || quantity < 1) throw invalid(`quantity must be a positive integer, got ${require_money.describeValue(quantity)}`);
181
+ const unit = require_rounding.unitSize(info, options.unit ?? "storage");
182
+ const amount = BigInt(net.amount);
183
+ if (amount % unit !== 0n) throw new require_money.MoneyError("precision", `${net.amount} is not a whole number of ${unit}-unit steps`);
184
+ const units = amount / unit;
185
+ const q = BigInt(quantity);
186
+ const per = require_money.divRound(units, q, "floor");
187
+ const extra = units - per * q;
188
+ const parts = [{
189
+ quantity: Number(q - extra),
190
+ unitPrice: require_money.boundedMoney(per * unit, info)
191
+ }];
192
+ if (extra > 0n) parts.push({
193
+ quantity: Number(extra),
194
+ unitPrice: require_money.boundedMoney((per + 1n) * unit, info)
195
+ });
196
+ return parts;
197
+ }
198
+ //#endregion
199
+ Object.defineProperty(exports, "allocate", {
200
+ enumerable: true,
201
+ get: function() {
202
+ return allocate;
203
+ }
204
+ });
205
+ Object.defineProperty(exports, "allocateUnits", {
206
+ enumerable: true,
207
+ get: function() {
208
+ return allocateUnits;
209
+ }
210
+ });
211
+ Object.defineProperty(exports, "allocateWaterfall", {
212
+ enumerable: true,
213
+ get: function() {
214
+ return allocateWaterfall;
215
+ }
216
+ });
217
+ Object.defineProperty(exports, "splitUnitPrice", {
218
+ enumerable: true,
219
+ get: function() {
220
+ return splitUnitPrice;
221
+ }
222
+ });