soff-money 0.1.0 โ 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.
- package/README.md +192 -36
- package/dist/core/index.cjs +99 -0
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +51 -0
- package/dist/core/index.d.ts +51 -0
- package/dist/core/index.js +99 -0
- package/dist/core/index.js.map +1 -1
- package/dist/index.cjs +152 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +148 -1
- package/dist/index.js.map +1 -1
- package/dist/locales/cl.cjs +21 -0
- package/dist/locales/cl.cjs.map +1 -0
- package/dist/locales/cl.d.cts +15 -0
- package/dist/locales/cl.d.ts +15 -0
- package/dist/locales/cl.js +18 -0
- package/dist/locales/cl.js.map +1 -0
- package/dist/locales/eu.cjs +16 -0
- package/dist/locales/eu.cjs.map +1 -0
- package/dist/locales/eu.d.cts +9 -0
- package/dist/locales/eu.d.ts +9 -0
- package/dist/locales/eu.js +14 -0
- package/dist/locales/eu.js.map +1 -0
- package/dist/locales/pe.cjs +16 -0
- package/dist/locales/pe.cjs.map +1 -0
- package/dist/locales/pe.d.cts +9 -0
- package/dist/locales/pe.d.ts +9 -0
- package/dist/locales/pe.js +14 -0
- package/dist/locales/pe.js.map +1 -0
- package/dist/locales/uy.cjs +16 -0
- package/dist/locales/uy.cjs.map +1 -0
- package/dist/locales/uy.d.cts +9 -0
- package/dist/locales/uy.d.ts +9 -0
- package/dist/locales/uy.js +14 -0
- package/dist/locales/uy.js.map +1 -0
- package/package.json +42 -2
package/README.md
CHANGED
|
@@ -1,25 +1,45 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Soff Money
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/soff-money)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](https://github.com/bledxs/soff-monorepo/actions)
|
|
6
|
+
[](https://codecov.io/gh/bledxs/soff-monorepo)
|
|
7
|
+
[](https://bundlephobia.com/package/soff-money)
|
|
2
8
|
|
|
3
9
|
Safe money handling for JavaScript with integer-based arithmetic and LATAM locale formatting.
|
|
4
10
|
|
|
5
|
-
|
|
11
|
+
**Zero dependencies** ยท **TypeScript** ยท **~8KB core**
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Soff Money](#soff-money)
|
|
16
|
+
- [Table of Contents](#table-of-contents)
|
|
17
|
+
- [Why?](#why)
|
|
18
|
+
- [Install](#install)
|
|
19
|
+
- [Quick Start](#quick-start)
|
|
20
|
+
- [Fair Distribution](#fair-distribution)
|
|
21
|
+
- [Proportional Distribution](#proportional-distribution)
|
|
22
|
+
- [Available Locales](#available-locales)
|
|
23
|
+
- [API Reference](#api-reference)
|
|
24
|
+
- [Creating Money](#creating-money)
|
|
25
|
+
- [Arithmetic Operations](#arithmetic-operations)
|
|
26
|
+
- [Percentage Operations](#percentage-operations)
|
|
27
|
+
- [Min/Max Operations](#minmax-operations)
|
|
28
|
+
- [Comparisons](#comparisons)
|
|
29
|
+
- [Formatting](#formatting)
|
|
30
|
+
- [Static Methods](#static-methods)
|
|
31
|
+
- [Bundle Size](#bundle-size)
|
|
32
|
+
- [Contributing](#contributing)
|
|
33
|
+
- [License](#license)
|
|
34
|
+
- [Documentation](#documentation)
|
|
35
|
+
|
|
36
|
+
## Why?
|
|
6
37
|
|
|
7
38
|
In JavaScript, `0.1 + 0.2 === 0.30000000000000004`. This is fatal for e-commerce or financial applications. Additionally, formatting currencies in Latin America is painful - does the symbol go before or after? Dots or commas for thousands?
|
|
8
39
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
A library that handles money using integers (Safe Money pattern) and formats according to the country's locale.
|
|
12
|
-
|
|
13
|
-
## Features
|
|
14
|
-
|
|
15
|
-
- ๐ข **Integer-based arithmetic** - No floating point errors
|
|
16
|
-
- ๐ **LATAM-first locales** - CO, MX, AR, BR, US
|
|
17
|
-
- ๐ณ **Tree-shakeable** - Import only what you need
|
|
18
|
-
- ๐ฆ **Tiny bundle** - Core is < 2KB gzipped
|
|
19
|
-
- ๐ฏ **TypeScript** - Full type safety
|
|
20
|
-
- โก **Zero dependencies** - Pure JavaScript
|
|
40
|
+
This library handles money using integers (Safe Money pattern) and formats according to the country's locale.
|
|
21
41
|
|
|
22
|
-
##
|
|
42
|
+
## Install
|
|
23
43
|
|
|
24
44
|
```bash
|
|
25
45
|
npm install soff-money
|
|
@@ -28,30 +48,30 @@ npm install soff-money
|
|
|
28
48
|
## Quick Start
|
|
29
49
|
|
|
30
50
|
```typescript
|
|
31
|
-
import { Money } from 'soff-money';
|
|
32
|
-
import { COP } from 'soff-money/locales/co';
|
|
51
|
+
import { Money, COP, USD } from 'soff-money';
|
|
33
52
|
|
|
34
|
-
// Create money from
|
|
35
|
-
const price = Money.
|
|
53
|
+
// Create money from decimal (safe - converted to cents internally)
|
|
54
|
+
const price = Money.fromDecimal(1500000, COP);
|
|
36
55
|
|
|
37
|
-
// Arithmetic operations
|
|
38
|
-
const
|
|
39
|
-
const discounted =
|
|
56
|
+
// Arithmetic operations (all return new Money instances)
|
|
57
|
+
const withTax = price.addPercentage(19); // Add 19% tax
|
|
58
|
+
const discounted = withTax.subtractPercentage(10); // 10% discount
|
|
40
59
|
|
|
41
60
|
// Format for display
|
|
42
|
-
console.log(
|
|
61
|
+
console.log(price.format()); // "$ 1.500.000,00"
|
|
62
|
+
console.log(discounted.format()); // "$ 1.606.500,00"
|
|
43
63
|
|
|
44
|
-
// Safe
|
|
45
|
-
|
|
46
|
-
|
|
64
|
+
// Safe comparisons
|
|
65
|
+
price.equals(Money.fromDecimal(1500000, COP)); // true
|
|
66
|
+
price.greaterThan(discounted); // false
|
|
47
67
|
```
|
|
48
68
|
|
|
49
|
-
##
|
|
69
|
+
## Fair Distribution
|
|
50
70
|
|
|
51
71
|
When splitting money, you never lose cents:
|
|
52
72
|
|
|
53
73
|
```typescript
|
|
54
|
-
const bill = Money.
|
|
74
|
+
const bill = Money.fromDecimal(100, USD);
|
|
55
75
|
const [alice, bob, charlie] = bill.distribute(3);
|
|
56
76
|
|
|
57
77
|
// alice: $33.34
|
|
@@ -60,16 +80,152 @@ const [alice, bob, charlie] = bill.distribute(3);
|
|
|
60
80
|
// Total: $100.00 โ (not $99.99!)
|
|
61
81
|
```
|
|
62
82
|
|
|
83
|
+
The extra cent goes to the first person - no money is lost!
|
|
84
|
+
|
|
85
|
+
### Proportional Distribution
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const total = Money.fromDecimal(100, USD);
|
|
89
|
+
const [share1, share2, share3] = total.distributeByRatios([1, 2, 2]);
|
|
90
|
+
|
|
91
|
+
// share1: $20.00 (20%)
|
|
92
|
+
// share2: $40.00 (40%)
|
|
93
|
+
// share3: $40.00 (40%)
|
|
94
|
+
```
|
|
95
|
+
|
|
63
96
|
## Available Locales
|
|
64
97
|
|
|
65
|
-
| Locale | Currency | Symbol | Format |
|
|
66
|
-
|
|
|
67
|
-
| `co`
|
|
68
|
-
| `mx`
|
|
69
|
-
| `ar`
|
|
70
|
-
| `br`
|
|
71
|
-
| `us`
|
|
98
|
+
| Locale | Import | Currency | Symbol | Format |
|
|
99
|
+
| ------------ | ----------------------- | -------- | ------ | ----------- |
|
|
100
|
+
| ๐จ๐ด Colombia | `soff-money/locales/co` | COP | $ | $ 1.500.000 |
|
|
101
|
+
| ๐ฒ๐ฝ Mรฉxico | `soff-money/locales/mx` | MXN | $ | $1,500.00 |
|
|
102
|
+
| ๐ฆ๐ท Argentina | `soff-money/locales/ar` | ARS | $ | $ 1.500,00 |
|
|
103
|
+
| ๐ง๐ท Brasil | `soff-money/locales/br` | BRL | R$ | R$ 1.500,00 |
|
|
104
|
+
| ๐บ๐ธ USA | `soff-money/locales/us` | USD | $ | $1,500.00 |
|
|
105
|
+
| ๐จ๐ฑ Chile | `soff-money/locales/cl` | CLP | $ | $ 1.500 |
|
|
106
|
+
| ๐ต๐ช Perรบ | `soff-money/locales/pe` | PEN | S/ | S/ 1,500.00 |
|
|
107
|
+
| ๐บ๐พ Uruguay | `soff-money/locales/uy` | UYU | $ | $ 1.500,00 |
|
|
108
|
+
| ๐ช๐บ Euro | `soff-money/locales/eu` | EUR | โฌ | 1.500,00 โฌ |
|
|
109
|
+
|
|
110
|
+
## API Reference
|
|
111
|
+
|
|
112
|
+
### Creating Money
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
// From decimal (recommended)
|
|
116
|
+
Money.fromDecimal(1500.5, COP);
|
|
117
|
+
|
|
118
|
+
// From cents (when you already have cents)
|
|
119
|
+
Money.fromCents(150050, COP);
|
|
120
|
+
|
|
121
|
+
// Zero
|
|
122
|
+
Money.zero(COP);
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Arithmetic Operations
|
|
126
|
+
|
|
127
|
+
All operations return new Money instances (immutable):
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const a = Money.fromDecimal(100, USD);
|
|
131
|
+
const b = Money.fromDecimal(50, USD);
|
|
132
|
+
|
|
133
|
+
a.add(b); // $150
|
|
134
|
+
a.subtract(b); // $50
|
|
135
|
+
a.multiply(2); // $200
|
|
136
|
+
a.multiply(0.5); // $50
|
|
137
|
+
a.divide(2); // $50
|
|
138
|
+
a.negate(); // -$100
|
|
139
|
+
a.abs(); // $100 (absolute value)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### Percentage Operations
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
const price = Money.fromDecimal(100, USD);
|
|
146
|
+
|
|
147
|
+
price.percentage(10); // $10.00 (10% of price)
|
|
148
|
+
price.addPercentage(19); // $119.00 (price + 19% tax)
|
|
149
|
+
price.subtractPercentage(10); // $90.00 (price - 10% discount)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Min/Max Operations
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
const a = Money.fromDecimal(100, USD);
|
|
156
|
+
const b = Money.fromDecimal(50, USD);
|
|
157
|
+
|
|
158
|
+
a.min(b); // $50 (minimum of a and b)
|
|
159
|
+
a.max(b); // $100 (maximum of a and b)
|
|
160
|
+
|
|
161
|
+
const min = Money.fromDecimal(10, USD);
|
|
162
|
+
const max = Money.fromDecimal(100, USD);
|
|
163
|
+
a.clamp(min, max); // $100 (clamp a between min and max)
|
|
164
|
+
|
|
165
|
+
a.isBetween(min, max); // true (check if a is in range)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Comparisons
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
a.equals(b); // false
|
|
172
|
+
a.greaterThan(b); // true
|
|
173
|
+
a.greaterThanOrEqual(b); // true
|
|
174
|
+
a.lessThan(b); // false
|
|
175
|
+
a.lessThanOrEqual(b); // false
|
|
176
|
+
a.isZero(); // false
|
|
177
|
+
a.isPositive(); // true
|
|
178
|
+
a.isNegative(); // false
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Formatting
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
const price = Money.fromDecimal(1500.5, USD);
|
|
185
|
+
|
|
186
|
+
price.format(); // "$1,500.50"
|
|
187
|
+
price.format({ showSymbol: false }); // "1,500.50"
|
|
188
|
+
price.format({ showDecimals: false }); // "$1,501"
|
|
189
|
+
price.format({ symbolPosition: 'after' }); // "1,500.50 $"
|
|
190
|
+
price.toDecimal(); // 1500.50
|
|
191
|
+
price.toCents(); // 150050
|
|
192
|
+
price.toJSON(); // { cents: 150050, currency: 'USD' }
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Static Methods
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Sum multiple values
|
|
199
|
+
const items = [Money.fromDecimal(100, USD), Money.fromDecimal(50, USD), Money.fromDecimal(25, USD)];
|
|
200
|
+
|
|
201
|
+
Money.sum(items); // $175.00
|
|
202
|
+
|
|
203
|
+
// Get min/max from array
|
|
204
|
+
Money.minimum(items); // $25.00
|
|
205
|
+
Money.maximum(items); // $100.00
|
|
206
|
+
|
|
207
|
+
// Calculate average
|
|
208
|
+
Money.average(items); // $58.33
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Bundle Size
|
|
212
|
+
|
|
213
|
+
| Import | Size (minified) |
|
|
214
|
+
| ------------ | --------------- |
|
|
215
|
+
| `core` | ~8.8KB |
|
|
216
|
+
| `locales/*` | ~0.3KB each |
|
|
217
|
+
| Full package | ~10.6KB |
|
|
218
|
+
|
|
219
|
+
Tree-shaking ensures you only ship what you import.
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
Please read [CONTRIBUTING.md](../../CONTRIBUTING.md) for details on our code of conduct, and the process for submitting pull requests.
|
|
72
224
|
|
|
73
225
|
## License
|
|
74
226
|
|
|
75
|
-
MIT
|
|
227
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
228
|
+
|
|
229
|
+
## Documentation
|
|
230
|
+
|
|
231
|
+
- [Espaรฑol](docs/README.es.md)
|
package/dist/core/index.cjs
CHANGED
|
@@ -203,6 +203,105 @@ var Money = class _Money {
|
|
|
203
203
|
negate() {
|
|
204
204
|
return new _Money(-this.cents, this.currency);
|
|
205
205
|
}
|
|
206
|
+
/**
|
|
207
|
+
* Calculate a percentage of this money
|
|
208
|
+
* @example money.percentage(10) returns 10% of the amount
|
|
209
|
+
*/
|
|
210
|
+
percentage(percent) {
|
|
211
|
+
return new _Money(Math.round(this.cents * percent / 100), this.currency);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Add a percentage to this money
|
|
215
|
+
* @example money.addPercentage(19) adds 19% (like tax)
|
|
216
|
+
*/
|
|
217
|
+
addPercentage(percent) {
|
|
218
|
+
return this.add(this.percentage(percent));
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Subtract a percentage from this money
|
|
222
|
+
* @example money.subtractPercentage(10) subtracts 10% (like discount)
|
|
223
|
+
*/
|
|
224
|
+
subtractPercentage(percent) {
|
|
225
|
+
return this.subtract(this.percentage(percent));
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Get the minimum of this and another money value
|
|
229
|
+
*/
|
|
230
|
+
min(other) {
|
|
231
|
+
this.assertSameCurrency(other);
|
|
232
|
+
return this.cents <= other.cents ? this : new _Money(other.cents, this.currency);
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Get the maximum of this and another money value
|
|
236
|
+
*/
|
|
237
|
+
max(other) {
|
|
238
|
+
this.assertSameCurrency(other);
|
|
239
|
+
return this.cents >= other.cents ? this : new _Money(other.cents, this.currency);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Clamp this money between a minimum and maximum
|
|
243
|
+
*/
|
|
244
|
+
clamp(minValue, maxValue) {
|
|
245
|
+
return this.max(minValue).min(maxValue);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Get cents (smallest unit)
|
|
249
|
+
*/
|
|
250
|
+
toCents() {
|
|
251
|
+
return this.cents;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Check if within a range (inclusive)
|
|
255
|
+
*/
|
|
256
|
+
isBetween(min, max) {
|
|
257
|
+
this.assertSameCurrency(min);
|
|
258
|
+
this.assertSameCurrency(max);
|
|
259
|
+
return this.cents >= min.cents && this.cents <= max.cents;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Sum multiple money values
|
|
263
|
+
*/
|
|
264
|
+
static sum(values) {
|
|
265
|
+
if (values.length === 0) {
|
|
266
|
+
throw new Error("Cannot sum empty array");
|
|
267
|
+
}
|
|
268
|
+
const currency = values[0].currency;
|
|
269
|
+
const total = values.reduce((sum, m) => {
|
|
270
|
+
if (m.currency.code !== currency.code) {
|
|
271
|
+
throw new Error("Cannot sum different currencies");
|
|
272
|
+
}
|
|
273
|
+
return sum + m.cents;
|
|
274
|
+
}, 0);
|
|
275
|
+
return new _Money(total, currency);
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get the minimum from an array of money values
|
|
279
|
+
*/
|
|
280
|
+
static minimum(values) {
|
|
281
|
+
if (values.length === 0) {
|
|
282
|
+
throw new Error("Cannot get minimum of empty array");
|
|
283
|
+
}
|
|
284
|
+
return values.reduce((min, m) => m.cents < min.cents ? m : min);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get the maximum from an array of money values
|
|
288
|
+
*/
|
|
289
|
+
static maximum(values) {
|
|
290
|
+
if (values.length === 0) {
|
|
291
|
+
throw new Error("Cannot get maximum of empty array");
|
|
292
|
+
}
|
|
293
|
+
return values.reduce((max, m) => m.cents > max.cents ? m : max);
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Calculate the average of money values
|
|
297
|
+
*/
|
|
298
|
+
static average(values) {
|
|
299
|
+
if (values.length === 0) {
|
|
300
|
+
throw new Error("Cannot calculate average of empty array");
|
|
301
|
+
}
|
|
302
|
+
const total = _Money.sum(values);
|
|
303
|
+
return total.divide(values.length);
|
|
304
|
+
}
|
|
206
305
|
/**
|
|
207
306
|
* Convert to JSON-serializable object
|
|
208
307
|
*/
|
package/dist/core/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/core/money.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Currency, FormatOptions, IMoney } from './types.js';\n\n/**\n * Immutable Money class that uses integer arithmetic for precision\n */\nexport class Money implements IMoney {\n readonly cents: number;\n readonly currency: Currency;\n\n private constructor(cents: number, currency: Currency) {\n if (!Number.isInteger(cents)) {\n throw new Error('Money must be created with an integer number of cents');\n }\n this.cents = cents;\n this.currency = currency;\n Object.freeze(this);\n }\n\n /**\n * Create money from cents (smallest unit)\n */\n static fromCents(cents: number, currency: Currency): Money {\n return new Money(Math.round(cents), currency);\n }\n\n /**\n * Create money from a decimal amount\n * @example Money.fromDecimal(100.50, USD) creates $100.50\n */\n static fromDecimal(amount: number, currency: Currency): Money {\n const multiplier = Math.pow(10, currency.decimals);\n const cents = Math.round(amount * multiplier);\n return new Money(cents, currency);\n }\n\n /**\n * Create zero money\n */\n static zero(currency: Currency): Money {\n return new Money(0, currency);\n }\n\n /**\n * Add another money value (must be same currency)\n */\n add(other: IMoney): Money {\n this.assertSameCurrency(other);\n return new Money(this.cents + other.cents, this.currency);\n }\n\n /**\n * Subtract another money value (must be same currency)\n */\n subtract(other: IMoney): Money {\n this.assertSameCurrency(other);\n return new Money(this.cents - other.cents, this.currency);\n }\n\n /**\n * Multiply by a factor (rounds to nearest cent)\n */\n multiply(factor: number): Money {\n return new Money(Math.round(this.cents * factor), this.currency);\n }\n\n /**\n * Divide by a divisor (rounds to nearest cent)\n */\n divide(divisor: number): Money {\n if (divisor === 0) {\n throw new Error('Cannot divide by zero');\n }\n return new Money(Math.round(this.cents / divisor), this.currency);\n }\n\n /**\n * Distribute money evenly without losing cents\n * The remainder is distributed to the first parts\n * @example $100 / 3 = [$33.34, $33.33, $33.33]\n */\n distribute(parts: number): Money[] {\n if (parts <= 0 || !Number.isInteger(parts)) {\n throw new Error('Parts must be a positive integer');\n }\n\n const quotient = Math.floor(this.cents / parts);\n const remainder = this.cents % parts;\n\n const result: Money[] = [];\n for (let i = 0; i < parts; i++) {\n // Add 1 cent to the first 'remainder' parts\n const extra = i < remainder ? 1 : 0;\n result.push(new Money(quotient + extra, this.currency));\n }\n\n return result;\n }\n\n /**\n * Distribute money according to ratios\n * @example $100 with ratios [1, 2, 2] = [$20, $40, $40]\n */\n distributeByRatios(ratios: number[]): Money[] {\n if (ratios.length === 0) {\n throw new Error('Ratios array cannot be empty');\n }\n\n const total = ratios.reduce((sum, r) => sum + r, 0);\n if (total <= 0) {\n throw new Error('Sum of ratios must be positive');\n }\n\n let remaining = this.cents;\n const result: Money[] = [];\n\n for (let i = 0; i < ratios.length; i++) {\n if (i === ratios.length - 1) {\n // Last part gets whatever is remaining to avoid rounding errors\n result.push(new Money(remaining, this.currency));\n } else {\n const share = Math.round((this.cents * ratios[i]) / total);\n result.push(new Money(share, this.currency));\n remaining -= share;\n }\n }\n\n return result;\n }\n\n /**\n * Format money for display\n */\n format(options: FormatOptions = {}): string {\n const { showSymbol = true, showDecimals = true, symbolPosition } = options;\n\n const decimal = this.toDecimal();\n const absValue = Math.abs(decimal);\n const isNegative = decimal < 0;\n\n // Format the number\n let formatted: string;\n if (showDecimals && this.currency.decimals > 0) {\n const [intPart, decPart] = absValue.toFixed(this.currency.decimals).split('.');\n const intFormatted = this.formatInteger(intPart);\n formatted = `${intFormatted}${this.currency.decimalSeparator}${decPart}`;\n } else {\n formatted = this.formatInteger(Math.round(absValue).toString());\n }\n\n // Add negative sign\n if (isNegative) {\n formatted = `-${formatted}`;\n }\n\n // Add symbol\n if (showSymbol) {\n const pos = symbolPosition ?? this.currency.symbolPosition;\n const space = this.currency.symbolSpacing ? ' ' : '';\n\n if (pos === 'before') {\n formatted = `${this.currency.symbol}${space}${formatted}`;\n } else {\n formatted = `${formatted}${space}${this.currency.symbol}`;\n }\n }\n\n return formatted;\n }\n\n /**\n * Get the decimal representation\n */\n toDecimal(): number {\n return this.cents / Math.pow(10, this.currency.decimals);\n }\n\n /**\n * Check equality with another money value\n */\n equals(other: IMoney): boolean {\n return this.cents === other.cents && this.currency.code === other.currency.code;\n }\n\n /**\n * Check if greater than another money value\n */\n greaterThan(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents > other.cents;\n }\n\n /**\n * Check if less than another money value\n */\n lessThan(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents < other.cents;\n }\n\n /**\n * Check if greater than or equal\n */\n greaterThanOrEqual(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents >= other.cents;\n }\n\n /**\n * Check if less than or equal\n */\n lessThanOrEqual(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents <= other.cents;\n }\n\n /**\n * Check if zero\n */\n isZero(): boolean {\n return this.cents === 0;\n }\n\n /**\n * Check if positive\n */\n isPositive(): boolean {\n return this.cents > 0;\n }\n\n /**\n * Check if negative\n */\n isNegative(): boolean {\n return this.cents < 0;\n }\n\n /**\n * Get absolute value\n */\n abs(): Money {\n return new Money(Math.abs(this.cents), this.currency);\n }\n\n /**\n * Negate the value\n */\n negate(): Money {\n return new Money(-this.cents, this.currency);\n }\n\n /**\n * Convert to JSON-serializable object\n */\n toJSON(): { cents: number; currency: string } {\n return {\n cents: this.cents,\n currency: this.currency.code,\n };\n }\n\n /**\n * String representation\n */\n toString(): string {\n return this.format();\n }\n\n private formatInteger(intStr: string): string {\n const parts: string[] = [];\n let remaining = intStr;\n\n while (remaining.length > 3) {\n parts.unshift(remaining.slice(-3));\n remaining = remaining.slice(0, -3);\n }\n\n if (remaining) {\n parts.unshift(remaining);\n }\n\n return parts.join(this.currency.thousandsSeparator);\n }\n\n private assertSameCurrency(other: IMoney): void {\n if (this.currency.code !== other.currency.code) {\n throw new Error(\n `Cannot perform operation with different currencies: ${this.currency.code} and ${other.currency.code}`\n );\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/core/money.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Currency, FormatOptions, IMoney } from './types.js';\n\n/**\n * Immutable Money class that uses integer arithmetic for precision\n */\nexport class Money implements IMoney {\n readonly cents: number;\n readonly currency: Currency;\n\n private constructor(cents: number, currency: Currency) {\n if (!Number.isInteger(cents)) {\n throw new Error('Money must be created with an integer number of cents');\n }\n this.cents = cents;\n this.currency = currency;\n Object.freeze(this);\n }\n\n /**\n * Create money from cents (smallest unit)\n */\n static fromCents(cents: number, currency: Currency): Money {\n return new Money(Math.round(cents), currency);\n }\n\n /**\n * Create money from a decimal amount\n * @example Money.fromDecimal(100.50, USD) creates $100.50\n */\n static fromDecimal(amount: number, currency: Currency): Money {\n const multiplier = Math.pow(10, currency.decimals);\n const cents = Math.round(amount * multiplier);\n return new Money(cents, currency);\n }\n\n /**\n * Create zero money\n */\n static zero(currency: Currency): Money {\n return new Money(0, currency);\n }\n\n /**\n * Add another money value (must be same currency)\n */\n add(other: IMoney): Money {\n this.assertSameCurrency(other);\n return new Money(this.cents + other.cents, this.currency);\n }\n\n /**\n * Subtract another money value (must be same currency)\n */\n subtract(other: IMoney): Money {\n this.assertSameCurrency(other);\n return new Money(this.cents - other.cents, this.currency);\n }\n\n /**\n * Multiply by a factor (rounds to nearest cent)\n */\n multiply(factor: number): Money {\n return new Money(Math.round(this.cents * factor), this.currency);\n }\n\n /**\n * Divide by a divisor (rounds to nearest cent)\n */\n divide(divisor: number): Money {\n if (divisor === 0) {\n throw new Error('Cannot divide by zero');\n }\n return new Money(Math.round(this.cents / divisor), this.currency);\n }\n\n /**\n * Distribute money evenly without losing cents\n * The remainder is distributed to the first parts\n * @example $100 / 3 = [$33.34, $33.33, $33.33]\n */\n distribute(parts: number): Money[] {\n if (parts <= 0 || !Number.isInteger(parts)) {\n throw new Error('Parts must be a positive integer');\n }\n\n const quotient = Math.floor(this.cents / parts);\n const remainder = this.cents % parts;\n\n const result: Money[] = [];\n for (let i = 0; i < parts; i++) {\n // Add 1 cent to the first 'remainder' parts\n const extra = i < remainder ? 1 : 0;\n result.push(new Money(quotient + extra, this.currency));\n }\n\n return result;\n }\n\n /**\n * Distribute money according to ratios\n * @example $100 with ratios [1, 2, 2] = [$20, $40, $40]\n */\n distributeByRatios(ratios: number[]): Money[] {\n if (ratios.length === 0) {\n throw new Error('Ratios array cannot be empty');\n }\n\n const total = ratios.reduce((sum, r) => sum + r, 0);\n if (total <= 0) {\n throw new Error('Sum of ratios must be positive');\n }\n\n let remaining = this.cents;\n const result: Money[] = [];\n\n for (let i = 0; i < ratios.length; i++) {\n if (i === ratios.length - 1) {\n // Last part gets whatever is remaining to avoid rounding errors\n result.push(new Money(remaining, this.currency));\n } else {\n const share = Math.round((this.cents * ratios[i]) / total);\n result.push(new Money(share, this.currency));\n remaining -= share;\n }\n }\n\n return result;\n }\n\n /**\n * Format money for display\n */\n format(options: FormatOptions = {}): string {\n const { showSymbol = true, showDecimals = true, symbolPosition } = options;\n\n const decimal = this.toDecimal();\n const absValue = Math.abs(decimal);\n const isNegative = decimal < 0;\n\n // Format the number\n let formatted: string;\n if (showDecimals && this.currency.decimals > 0) {\n const [intPart, decPart] = absValue.toFixed(this.currency.decimals).split('.');\n const intFormatted = this.formatInteger(intPart);\n formatted = `${intFormatted}${this.currency.decimalSeparator}${decPart}`;\n } else {\n formatted = this.formatInteger(Math.round(absValue).toString());\n }\n\n // Add negative sign\n if (isNegative) {\n formatted = `-${formatted}`;\n }\n\n // Add symbol\n if (showSymbol) {\n const pos = symbolPosition ?? this.currency.symbolPosition;\n const space = this.currency.symbolSpacing ? ' ' : '';\n\n if (pos === 'before') {\n formatted = `${this.currency.symbol}${space}${formatted}`;\n } else {\n formatted = `${formatted}${space}${this.currency.symbol}`;\n }\n }\n\n return formatted;\n }\n\n /**\n * Get the decimal representation\n */\n toDecimal(): number {\n return this.cents / Math.pow(10, this.currency.decimals);\n }\n\n /**\n * Check equality with another money value\n */\n equals(other: IMoney): boolean {\n return this.cents === other.cents && this.currency.code === other.currency.code;\n }\n\n /**\n * Check if greater than another money value\n */\n greaterThan(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents > other.cents;\n }\n\n /**\n * Check if less than another money value\n */\n lessThan(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents < other.cents;\n }\n\n /**\n * Check if greater than or equal\n */\n greaterThanOrEqual(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents >= other.cents;\n }\n\n /**\n * Check if less than or equal\n */\n lessThanOrEqual(other: IMoney): boolean {\n this.assertSameCurrency(other);\n return this.cents <= other.cents;\n }\n\n /**\n * Check if zero\n */\n isZero(): boolean {\n return this.cents === 0;\n }\n\n /**\n * Check if positive\n */\n isPositive(): boolean {\n return this.cents > 0;\n }\n\n /**\n * Check if negative\n */\n isNegative(): boolean {\n return this.cents < 0;\n }\n\n /**\n * Get absolute value\n */\n abs(): Money {\n return new Money(Math.abs(this.cents), this.currency);\n }\n\n /**\n * Negate the value\n */\n negate(): Money {\n return new Money(-this.cents, this.currency);\n }\n\n /**\n * Calculate a percentage of this money\n * @example money.percentage(10) returns 10% of the amount\n */\n percentage(percent: number): Money {\n return new Money(Math.round((this.cents * percent) / 100), this.currency);\n }\n\n /**\n * Add a percentage to this money\n * @example money.addPercentage(19) adds 19% (like tax)\n */\n addPercentage(percent: number): Money {\n return this.add(this.percentage(percent));\n }\n\n /**\n * Subtract a percentage from this money\n * @example money.subtractPercentage(10) subtracts 10% (like discount)\n */\n subtractPercentage(percent: number): Money {\n return this.subtract(this.percentage(percent));\n }\n\n /**\n * Get the minimum of this and another money value\n */\n min(other: IMoney): Money {\n this.assertSameCurrency(other);\n return this.cents <= other.cents ? this : new Money(other.cents, this.currency);\n }\n\n /**\n * Get the maximum of this and another money value\n */\n max(other: IMoney): Money {\n this.assertSameCurrency(other);\n return this.cents >= other.cents ? this : new Money(other.cents, this.currency);\n }\n\n /**\n * Clamp this money between a minimum and maximum\n */\n clamp(minValue: IMoney, maxValue: IMoney): Money {\n return this.max(minValue).min(maxValue);\n }\n\n /**\n * Get cents (smallest unit)\n */\n toCents(): number {\n return this.cents;\n }\n\n /**\n * Check if within a range (inclusive)\n */\n isBetween(min: IMoney, max: IMoney): boolean {\n this.assertSameCurrency(min);\n this.assertSameCurrency(max);\n return this.cents >= min.cents && this.cents <= max.cents;\n }\n\n /**\n * Sum multiple money values\n */\n static sum(values: IMoney[]): Money {\n if (values.length === 0) {\n throw new Error('Cannot sum empty array');\n }\n const currency = values[0].currency;\n const total = values.reduce((sum, m) => {\n if (m.currency.code !== currency.code) {\n throw new Error('Cannot sum different currencies');\n }\n return sum + m.cents;\n }, 0);\n return new Money(total, currency);\n }\n\n /**\n * Get the minimum from an array of money values\n */\n static minimum(values: IMoney[]): Money {\n if (values.length === 0) {\n throw new Error('Cannot get minimum of empty array');\n }\n return values.reduce((min, m) => (m.cents < min.cents ? m : min)) as Money;\n }\n\n /**\n * Get the maximum from an array of money values\n */\n static maximum(values: IMoney[]): Money {\n if (values.length === 0) {\n throw new Error('Cannot get maximum of empty array');\n }\n return values.reduce((max, m) => (m.cents > max.cents ? m : max)) as Money;\n }\n\n /**\n * Calculate the average of money values\n */\n static average(values: IMoney[]): Money {\n if (values.length === 0) {\n throw new Error('Cannot calculate average of empty array');\n }\n const total = Money.sum(values);\n return total.divide(values.length);\n }\n\n /**\n * Convert to JSON-serializable object\n */\n toJSON(): { cents: number; currency: string } {\n return {\n cents: this.cents,\n currency: this.currency.code,\n };\n }\n\n /**\n * String representation\n */\n toString(): string {\n return this.format();\n }\n\n private formatInteger(intStr: string): string {\n const parts: string[] = [];\n let remaining = intStr;\n\n while (remaining.length > 3) {\n parts.unshift(remaining.slice(-3));\n remaining = remaining.slice(0, -3);\n }\n\n if (remaining) {\n parts.unshift(remaining);\n }\n\n return parts.join(this.currency.thousandsSeparator);\n }\n\n private assertSameCurrency(other: IMoney): void {\n if (this.currency.code !== other.currency.code) {\n throw new Error(\n `Cannot perform operation with different currencies: ${this.currency.code} and ${other.currency.code}`\n );\n }\n }\n}\n"]}
|
package/dist/core/index.d.cts
CHANGED
|
@@ -163,6 +163,57 @@ declare class Money implements IMoney {
|
|
|
163
163
|
* Negate the value
|
|
164
164
|
*/
|
|
165
165
|
negate(): Money;
|
|
166
|
+
/**
|
|
167
|
+
* Calculate a percentage of this money
|
|
168
|
+
* @example money.percentage(10) returns 10% of the amount
|
|
169
|
+
*/
|
|
170
|
+
percentage(percent: number): Money;
|
|
171
|
+
/**
|
|
172
|
+
* Add a percentage to this money
|
|
173
|
+
* @example money.addPercentage(19) adds 19% (like tax)
|
|
174
|
+
*/
|
|
175
|
+
addPercentage(percent: number): Money;
|
|
176
|
+
/**
|
|
177
|
+
* Subtract a percentage from this money
|
|
178
|
+
* @example money.subtractPercentage(10) subtracts 10% (like discount)
|
|
179
|
+
*/
|
|
180
|
+
subtractPercentage(percent: number): Money;
|
|
181
|
+
/**
|
|
182
|
+
* Get the minimum of this and another money value
|
|
183
|
+
*/
|
|
184
|
+
min(other: IMoney): Money;
|
|
185
|
+
/**
|
|
186
|
+
* Get the maximum of this and another money value
|
|
187
|
+
*/
|
|
188
|
+
max(other: IMoney): Money;
|
|
189
|
+
/**
|
|
190
|
+
* Clamp this money between a minimum and maximum
|
|
191
|
+
*/
|
|
192
|
+
clamp(minValue: IMoney, maxValue: IMoney): Money;
|
|
193
|
+
/**
|
|
194
|
+
* Get cents (smallest unit)
|
|
195
|
+
*/
|
|
196
|
+
toCents(): number;
|
|
197
|
+
/**
|
|
198
|
+
* Check if within a range (inclusive)
|
|
199
|
+
*/
|
|
200
|
+
isBetween(min: IMoney, max: IMoney): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Sum multiple money values
|
|
203
|
+
*/
|
|
204
|
+
static sum(values: IMoney[]): Money;
|
|
205
|
+
/**
|
|
206
|
+
* Get the minimum from an array of money values
|
|
207
|
+
*/
|
|
208
|
+
static minimum(values: IMoney[]): Money;
|
|
209
|
+
/**
|
|
210
|
+
* Get the maximum from an array of money values
|
|
211
|
+
*/
|
|
212
|
+
static maximum(values: IMoney[]): Money;
|
|
213
|
+
/**
|
|
214
|
+
* Calculate the average of money values
|
|
215
|
+
*/
|
|
216
|
+
static average(values: IMoney[]): Money;
|
|
166
217
|
/**
|
|
167
218
|
* Convert to JSON-serializable object
|
|
168
219
|
*/
|
package/dist/core/index.d.ts
CHANGED
|
@@ -163,6 +163,57 @@ declare class Money implements IMoney {
|
|
|
163
163
|
* Negate the value
|
|
164
164
|
*/
|
|
165
165
|
negate(): Money;
|
|
166
|
+
/**
|
|
167
|
+
* Calculate a percentage of this money
|
|
168
|
+
* @example money.percentage(10) returns 10% of the amount
|
|
169
|
+
*/
|
|
170
|
+
percentage(percent: number): Money;
|
|
171
|
+
/**
|
|
172
|
+
* Add a percentage to this money
|
|
173
|
+
* @example money.addPercentage(19) adds 19% (like tax)
|
|
174
|
+
*/
|
|
175
|
+
addPercentage(percent: number): Money;
|
|
176
|
+
/**
|
|
177
|
+
* Subtract a percentage from this money
|
|
178
|
+
* @example money.subtractPercentage(10) subtracts 10% (like discount)
|
|
179
|
+
*/
|
|
180
|
+
subtractPercentage(percent: number): Money;
|
|
181
|
+
/**
|
|
182
|
+
* Get the minimum of this and another money value
|
|
183
|
+
*/
|
|
184
|
+
min(other: IMoney): Money;
|
|
185
|
+
/**
|
|
186
|
+
* Get the maximum of this and another money value
|
|
187
|
+
*/
|
|
188
|
+
max(other: IMoney): Money;
|
|
189
|
+
/**
|
|
190
|
+
* Clamp this money between a minimum and maximum
|
|
191
|
+
*/
|
|
192
|
+
clamp(minValue: IMoney, maxValue: IMoney): Money;
|
|
193
|
+
/**
|
|
194
|
+
* Get cents (smallest unit)
|
|
195
|
+
*/
|
|
196
|
+
toCents(): number;
|
|
197
|
+
/**
|
|
198
|
+
* Check if within a range (inclusive)
|
|
199
|
+
*/
|
|
200
|
+
isBetween(min: IMoney, max: IMoney): boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Sum multiple money values
|
|
203
|
+
*/
|
|
204
|
+
static sum(values: IMoney[]): Money;
|
|
205
|
+
/**
|
|
206
|
+
* Get the minimum from an array of money values
|
|
207
|
+
*/
|
|
208
|
+
static minimum(values: IMoney[]): Money;
|
|
209
|
+
/**
|
|
210
|
+
* Get the maximum from an array of money values
|
|
211
|
+
*/
|
|
212
|
+
static maximum(values: IMoney[]): Money;
|
|
213
|
+
/**
|
|
214
|
+
* Calculate the average of money values
|
|
215
|
+
*/
|
|
216
|
+
static average(values: IMoney[]): Money;
|
|
166
217
|
/**
|
|
167
218
|
* Convert to JSON-serializable object
|
|
168
219
|
*/
|
package/dist/core/index.js
CHANGED
|
@@ -201,6 +201,105 @@ var Money = class _Money {
|
|
|
201
201
|
negate() {
|
|
202
202
|
return new _Money(-this.cents, this.currency);
|
|
203
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Calculate a percentage of this money
|
|
206
|
+
* @example money.percentage(10) returns 10% of the amount
|
|
207
|
+
*/
|
|
208
|
+
percentage(percent) {
|
|
209
|
+
return new _Money(Math.round(this.cents * percent / 100), this.currency);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Add a percentage to this money
|
|
213
|
+
* @example money.addPercentage(19) adds 19% (like tax)
|
|
214
|
+
*/
|
|
215
|
+
addPercentage(percent) {
|
|
216
|
+
return this.add(this.percentage(percent));
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Subtract a percentage from this money
|
|
220
|
+
* @example money.subtractPercentage(10) subtracts 10% (like discount)
|
|
221
|
+
*/
|
|
222
|
+
subtractPercentage(percent) {
|
|
223
|
+
return this.subtract(this.percentage(percent));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Get the minimum of this and another money value
|
|
227
|
+
*/
|
|
228
|
+
min(other) {
|
|
229
|
+
this.assertSameCurrency(other);
|
|
230
|
+
return this.cents <= other.cents ? this : new _Money(other.cents, this.currency);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Get the maximum of this and another money value
|
|
234
|
+
*/
|
|
235
|
+
max(other) {
|
|
236
|
+
this.assertSameCurrency(other);
|
|
237
|
+
return this.cents >= other.cents ? this : new _Money(other.cents, this.currency);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Clamp this money between a minimum and maximum
|
|
241
|
+
*/
|
|
242
|
+
clamp(minValue, maxValue) {
|
|
243
|
+
return this.max(minValue).min(maxValue);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get cents (smallest unit)
|
|
247
|
+
*/
|
|
248
|
+
toCents() {
|
|
249
|
+
return this.cents;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Check if within a range (inclusive)
|
|
253
|
+
*/
|
|
254
|
+
isBetween(min, max) {
|
|
255
|
+
this.assertSameCurrency(min);
|
|
256
|
+
this.assertSameCurrency(max);
|
|
257
|
+
return this.cents >= min.cents && this.cents <= max.cents;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Sum multiple money values
|
|
261
|
+
*/
|
|
262
|
+
static sum(values) {
|
|
263
|
+
if (values.length === 0) {
|
|
264
|
+
throw new Error("Cannot sum empty array");
|
|
265
|
+
}
|
|
266
|
+
const currency = values[0].currency;
|
|
267
|
+
const total = values.reduce((sum, m) => {
|
|
268
|
+
if (m.currency.code !== currency.code) {
|
|
269
|
+
throw new Error("Cannot sum different currencies");
|
|
270
|
+
}
|
|
271
|
+
return sum + m.cents;
|
|
272
|
+
}, 0);
|
|
273
|
+
return new _Money(total, currency);
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Get the minimum from an array of money values
|
|
277
|
+
*/
|
|
278
|
+
static minimum(values) {
|
|
279
|
+
if (values.length === 0) {
|
|
280
|
+
throw new Error("Cannot get minimum of empty array");
|
|
281
|
+
}
|
|
282
|
+
return values.reduce((min, m) => m.cents < min.cents ? m : min);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Get the maximum from an array of money values
|
|
286
|
+
*/
|
|
287
|
+
static maximum(values) {
|
|
288
|
+
if (values.length === 0) {
|
|
289
|
+
throw new Error("Cannot get maximum of empty array");
|
|
290
|
+
}
|
|
291
|
+
return values.reduce((max, m) => m.cents > max.cents ? m : max);
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Calculate the average of money values
|
|
295
|
+
*/
|
|
296
|
+
static average(values) {
|
|
297
|
+
if (values.length === 0) {
|
|
298
|
+
throw new Error("Cannot calculate average of empty array");
|
|
299
|
+
}
|
|
300
|
+
const total = _Money.sum(values);
|
|
301
|
+
return total.divide(values.length);
|
|
302
|
+
}
|
|
204
303
|
/**
|
|
205
304
|
* Convert to JSON-serializable object
|
|
206
305
|
*/
|