@ducatjs/money 0.1.0 → 0.2.1
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/NOTICE +73 -0
- package/README.md +479 -62
- package/data/cash-rounding.json +123 -2
- package/data/cldr-cash.json +101 -0
- package/data/historic.json +78 -0
- package/data/iso4217-list-three.xml +1185 -0
- package/dist/adyen-fx.d.ts.map +1 -1
- package/dist/adyen-fx.js +22 -4
- package/dist/adyen-fx.js.map +1 -1
- package/dist/boc.d.ts +31 -0
- package/dist/boc.d.ts.map +1 -0
- package/dist/boc.js +114 -0
- package/dist/boc.js.map +1 -0
- package/dist/boe.d.ts +33 -0
- package/dist/boe.d.ts.map +1 -0
- package/dist/boe.js +114 -0
- package/dist/boe.js.map +1 -0
- package/dist/cash.d.ts +30 -1
- package/dist/cash.d.ts.map +1 -1
- package/dist/cash.js +50 -2
- package/dist/cash.js.map +1 -1
- package/dist/convert.d.ts +10 -9
- package/dist/convert.d.ts.map +1 -1
- package/dist/convert.js +13 -3
- package/dist/convert.js.map +1 -1
- package/dist/drizzle/mysql.d.ts +105 -0
- package/dist/drizzle/mysql.d.ts.map +1 -0
- package/dist/drizzle/mysql.js +60 -0
- package/dist/drizzle/mysql.js.map +1 -0
- package/dist/drizzle/pg.d.ts +103 -0
- package/dist/drizzle/pg.d.ts.map +1 -0
- package/dist/drizzle/pg.js +60 -0
- package/dist/drizzle/pg.js.map +1 -0
- package/dist/drizzle/sqlite.d.ts +102 -0
- package/dist/drizzle/sqlite.d.ts.map +1 -0
- package/dist/drizzle/sqlite.js +59 -0
- package/dist/drizzle/sqlite.js.map +1 -0
- package/dist/ecb.d.ts +37 -2
- package/dist/ecb.d.ts.map +1 -1
- package/dist/ecb.js +148 -10
- package/dist/ecb.js.map +1 -1
- package/dist/fed.d.ts +35 -0
- package/dist/fed.d.ts.map +1 -0
- package/dist/fed.js +134 -0
- package/dist/fed.js.map +1 -0
- package/dist/fetch.d.ts +45 -0
- package/dist/fetch.d.ts.map +1 -1
- package/dist/fetch.js +167 -24
- package/dist/fetch.js.map +1 -1
- package/dist/fn.d.ts +8 -1
- package/dist/fn.d.ts.map +1 -1
- package/dist/fn.js +8 -1
- package/dist/fn.js.map +1 -1
- package/dist/generated/cash.d.ts +2 -0
- package/dist/generated/cash.d.ts.map +1 -1
- package/dist/generated/cash.js +101 -0
- package/dist/generated/cash.js.map +1 -1
- package/dist/generated/historic.d.ts +337 -0
- package/dist/generated/historic.d.ts.map +1 -0
- package/dist/generated/historic.js +66 -0
- package/dist/generated/historic.js.map +1 -0
- package/dist/historic.d.ts +40 -0
- package/dist/historic.d.ts.map +1 -0
- package/dist/historic.js +43 -0
- package/dist/historic.js.map +1 -0
- package/dist/history.d.ts +96 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +213 -0
- package/dist/history.js.map +1 -0
- package/dist/hmrc.d.ts +28 -0
- package/dist/hmrc.d.ts.map +1 -0
- package/dist/hmrc.js +108 -0
- package/dist/hmrc.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/kysely.d.ts +120 -0
- package/dist/kysely.d.ts.map +1 -0
- package/dist/kysely.js +309 -0
- package/dist/kysely.js.map +1 -0
- package/dist/money.d.ts +93 -9
- package/dist/money.d.ts.map +1 -1
- package/dist/money.js +135 -9
- package/dist/money.js.map +1 -1
- package/dist/parse.d.ts +45 -1
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +290 -3
- package/dist/parse.js.map +1 -1
- package/dist/pg.d.ts +100 -0
- package/dist/pg.d.ts.map +1 -0
- package/dist/pg.js +343 -0
- package/dist/pg.js.map +1 -0
- package/dist/plugins/adyen.d.ts +1 -1
- package/dist/plugins/adyen.d.ts.map +1 -1
- package/dist/plugins/adyen.js.map +1 -1
- package/dist/plugins/all.d.ts +8 -1
- package/dist/plugins/all.d.ts.map +1 -1
- package/dist/plugins/all.js +8 -1
- package/dist/plugins/all.js.map +1 -1
- package/dist/plugins/allocate.d.ts +1 -1
- package/dist/plugins/allocate.d.ts.map +1 -1
- package/dist/plugins/allocate.js.map +1 -1
- package/dist/plugins/braintree.d.ts +1 -1
- package/dist/plugins/braintree.d.ts.map +1 -1
- package/dist/plugins/braintree.js.map +1 -1
- package/dist/plugins/cash.d.ts +1 -1
- package/dist/plugins/cash.d.ts.map +1 -1
- package/dist/plugins/cash.js.map +1 -1
- package/dist/plugins/checkout.d.ts +1 -1
- package/dist/plugins/checkout.d.ts.map +1 -1
- package/dist/plugins/checkout.js.map +1 -1
- package/dist/plugins/convert.d.ts +5 -5
- package/dist/plugins/convert.d.ts.map +1 -1
- package/dist/plugins/convert.js.map +1 -1
- package/dist/plugins/exact.d.ts +1 -1
- package/dist/plugins/exact.d.ts.map +1 -1
- package/dist/plugins/exact.js.map +1 -1
- package/dist/plugins/fixed.d.ts +1 -1
- package/dist/plugins/fixed.d.ts.map +1 -1
- package/dist/plugins/fixed.js.map +1 -1
- package/dist/plugins/format.d.ts +1 -1
- package/dist/plugins/format.d.ts.map +1 -1
- package/dist/plugins/format.js.map +1 -1
- package/dist/plugins/klarna.d.ts +1 -1
- package/dist/plugins/klarna.d.ts.map +1 -1
- package/dist/plugins/klarna.js.map +1 -1
- package/dist/plugins/paypal.d.ts +1 -1
- package/dist/plugins/paypal.d.ts.map +1 -1
- package/dist/plugins/paypal.js +0 -4
- package/dist/plugins/paypal.js.map +1 -1
- package/dist/plugins/prisma.d.ts +1 -1
- package/dist/plugins/prisma.d.ts.map +1 -1
- package/dist/plugins/prisma.js +0 -4
- package/dist/plugins/prisma.js.map +1 -1
- package/dist/plugins/stripe.d.ts +1 -1
- package/dist/plugins/stripe.d.ts.map +1 -1
- package/dist/plugins/stripe.js +0 -4
- package/dist/plugins/stripe.js.map +1 -1
- package/dist/plugins/tax.d.ts +6 -6
- package/dist/plugins/tax.d.ts.map +1 -1
- package/dist/plugins/tax.js.map +1 -1
- package/dist/policy.d.ts +34 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +90 -0
- package/dist/policy.js.map +1 -0
- package/dist/prisma.d.ts +25 -14
- package/dist/prisma.d.ts.map +1 -1
- package/dist/prisma.js +29 -19
- package/dist/prisma.js.map +1 -1
- package/dist/provider.d.ts +21 -7
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +51 -11
- package/dist/provider.js.map +1 -1
- package/dist/rate-text.d.ts +38 -0
- package/dist/rate-text.d.ts.map +1 -0
- package/dist/rate-text.js +143 -0
- package/dist/rate-text.js.map +1 -0
- package/dist/rate.d.ts +61 -5
- package/dist/rate.d.ts.map +1 -1
- package/dist/rate.js +133 -33
- package/dist/rate.js.map +1 -1
- package/dist/rounding.js +1 -1
- package/dist/rounding.js.map +1 -1
- package/dist/schema.d.ts +43 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +77 -0
- package/dist/schema.js.map +1 -0
- package/dist/sql.d.ts +54 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +131 -0
- package/dist/sql.js.map +1 -0
- package/dist/tax.d.ts +13 -9
- package/dist/tax.d.ts.map +1 -1
- package/dist/tax.js +20 -4
- package/dist/tax.js.map +1 -1
- package/dist/testing/arbitraries.d.ts +43 -0
- package/dist/testing/arbitraries.d.ts.map +1 -0
- package/dist/testing/arbitraries.js +98 -0
- package/dist/testing/arbitraries.js.map +1 -0
- package/dist/testing/index.d.ts +67 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +159 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/jest.d.ts +2 -0
- package/dist/testing/jest.d.ts.map +1 -0
- package/dist/testing/jest.js +15 -0
- package/dist/testing/jest.js.map +1 -0
- package/dist/testing/vitest.d.ts +7 -0
- package/dist/testing/vitest.d.ts.map +1 -0
- package/dist/testing/vitest.js +12 -0
- package/dist/testing/vitest.js.map +1 -0
- package/dist/valibot.d.ts +33 -0
- package/dist/valibot.d.ts.map +1 -0
- package/dist/valibot.js +61 -0
- package/dist/valibot.js.map +1 -0
- package/dist/zod.d.ts +36 -0
- package/dist/zod.d.ts.map +1 -0
- package/dist/zod.js +58 -0
- package/dist/zod.js.map +1 -0
- package/package.json +116 -5
- package/src/adyen-fx.ts +25 -4
- package/src/boc.ts +136 -0
- package/src/boe.ts +131 -0
- package/src/cash.ts +70 -2
- package/src/convert.ts +33 -15
- package/src/drizzle/mysql.ts +78 -0
- package/src/drizzle/pg.ts +78 -0
- package/src/drizzle/sqlite.ts +70 -0
- package/src/ecb.ts +179 -13
- package/src/fed.ts +146 -0
- package/src/fetch.ts +207 -28
- package/src/fn.ts +14 -0
- package/src/generated/cash.ts +103 -0
- package/src/generated/historic.ts +67 -0
- package/src/historic.ts +77 -0
- package/src/history.ts +333 -0
- package/src/hmrc.ts +125 -0
- package/src/index.ts +4 -0
- package/src/kysely.ts +588 -0
- package/src/money.ts +210 -12
- package/src/parse.ts +371 -4
- package/src/pg.ts +410 -0
- package/src/plugins/adyen.ts +3 -2
- package/src/plugins/all.ts +10 -0
- package/src/plugins/allocate.ts +3 -2
- package/src/plugins/braintree.ts +3 -2
- package/src/plugins/cash.ts +3 -2
- package/src/plugins/checkout.ts +3 -2
- package/src/plugins/convert.ts +6 -6
- package/src/plugins/exact.ts +3 -2
- package/src/plugins/fixed.ts +3 -2
- package/src/plugins/format.ts +3 -2
- package/src/plugins/klarna.ts +3 -2
- package/src/plugins/paypal.ts +3 -2
- package/src/plugins/prisma.ts +3 -2
- package/src/plugins/stripe.ts +3 -2
- package/src/plugins/tax.ts +11 -11
- package/src/policy.ts +124 -0
- package/src/prisma.ts +91 -10
- package/src/provider.ts +81 -16
- package/src/rate-text.ts +151 -0
- package/src/rate.ts +205 -36
- package/src/rounding.ts +1 -1
- package/src/schema.ts +119 -0
- package/src/sql.ts +178 -0
- package/src/tax.ts +40 -9
- package/src/testing/arbitraries.ts +134 -0
- package/src/testing/index.ts +247 -0
- package/src/testing/jest.ts +18 -0
- package/src/testing/vitest.ts +18 -0
- package/src/valibot.ts +87 -0
- package/src/zod.ts +78 -0
package/src/pg.ts
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@ducatjs/money/pg`: node-postgres type parsers that keep NUMERIC and BIGINT exact, a Postgres
|
|
3
|
+
* composite type for Money with a `sum` aggregate, and a guard for NUMERIC(p, s) columns.
|
|
4
|
+
*
|
|
5
|
+
* import pg from 'pg';
|
|
6
|
+
* import { exactTypes } from '@ducatjs/money/pg';
|
|
7
|
+
*
|
|
8
|
+
* const pool = new pg.Pool({ types: exactTypes(pg.types) });
|
|
9
|
+
*
|
|
10
|
+
* It imports nothing from pg at run time: pass `pg.types` in.
|
|
11
|
+
*/
|
|
12
|
+
import { currencyExponent } from './currency.js';
|
|
13
|
+
import { money as parseMoney, toDecimal, type CurrencyInput, type Money } from './money.js';
|
|
14
|
+
import { checkDecimalSize, checkIntegerDigits, type DecimalColumnSize } from './sql.js';
|
|
15
|
+
|
|
16
|
+
export { DEFAULT_DECIMAL_SIZE, type DecimalColumnSize } from './sql.js';
|
|
17
|
+
|
|
18
|
+
/** A text-format value parser, as node-postgres calls it. */
|
|
19
|
+
export type TypeParser = (text: string) => unknown;
|
|
20
|
+
|
|
21
|
+
/** The part of `pg.types` (or a client's `types` option) these parsers build on. */
|
|
22
|
+
export interface TypeParserSource {
|
|
23
|
+
getTypeParser(oid: number, format?: 'text' | 'binary'): TypeParser;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** The OIDs of a composite money type and its array type, from `moneyTypeOids`. */
|
|
27
|
+
export interface MoneyTypeOids {
|
|
28
|
+
readonly oid: number;
|
|
29
|
+
readonly arrayOid: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Options for `exactTypes`. */
|
|
33
|
+
export interface ExactTypesOptions {
|
|
34
|
+
/** Also parse this composite money type (and its array) into Money. */
|
|
35
|
+
readonly money?: MoneyTypeOids;
|
|
36
|
+
/**
|
|
37
|
+
* How BIGINT (and BIGINT[]) values come back: `'bigint'` (the default) or `'string'`, pg's own
|
|
38
|
+
* exact format. A bigint is what `fromMinor` takes, but it applies to every BIGINT on the client,
|
|
39
|
+
* `count(*)` and bigserial ids included, and `JSON.stringify` throws on it.
|
|
40
|
+
*/
|
|
41
|
+
readonly int8?: 'bigint' | 'string';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const INT8_OID = 20;
|
|
45
|
+
const NUMERIC_OID = 1700;
|
|
46
|
+
const INT8_ARRAY_OID = 1016;
|
|
47
|
+
const NUMERIC_ARRAY_OID = 1231;
|
|
48
|
+
|
|
49
|
+
const INTEGER_TEXT = /^-?\d+$/;
|
|
50
|
+
const NUMERIC_TEXT = /^-?\d+(?:\.\d+)?$/;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* BIGINT text as a bigint. node-postgres returns it as a string by default, which is exact but not
|
|
54
|
+
* a number type the Money constructors accept.
|
|
55
|
+
*/
|
|
56
|
+
export function parseBigInt(text: string): bigint {
|
|
57
|
+
if (!INTEGER_TEXT.test(text)) {
|
|
58
|
+
throw new SyntaxError(`Expected BIGINT text, got "${text}"`);
|
|
59
|
+
}
|
|
60
|
+
return BigInt(text);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** BIGINT text, checked and kept as the string. */
|
|
64
|
+
function checkBigIntText(text: string): string {
|
|
65
|
+
parseBigInt(text);
|
|
66
|
+
return text;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* NUMERIC text, checked and returned as the exact decimal string. Postgres NUMERIC can hold `NaN`
|
|
71
|
+
* and `±Infinity`, which are not amounts, so they throw.
|
|
72
|
+
*/
|
|
73
|
+
export function parseNumeric(text: string): string {
|
|
74
|
+
if (!NUMERIC_TEXT.test(text)) {
|
|
75
|
+
throw new SyntaxError(`Expected a finite NUMERIC, got "${text}"`);
|
|
76
|
+
}
|
|
77
|
+
return text;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Splits Postgres array text (`{1,2,NULL}`, `{{1,2},{3,4}}`, `{"(1.00,EUR)"}`) into nested arrays of
|
|
82
|
+
* element text, with `null` for NULL. Quoted elements are unescaped.
|
|
83
|
+
*/
|
|
84
|
+
export function parseArray(text: string): unknown[] {
|
|
85
|
+
let position = 0;
|
|
86
|
+
|
|
87
|
+
// an array may start with explicit bounds, `[1:2]={...}`
|
|
88
|
+
if (text.startsWith('[')) {
|
|
89
|
+
position = text.indexOf('=') + 1;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseLevel(): unknown[] {
|
|
93
|
+
if (text[position] !== '{') {
|
|
94
|
+
throw new SyntaxError(`Malformed array text "${text}"`);
|
|
95
|
+
}
|
|
96
|
+
position++;
|
|
97
|
+
const items: unknown[] = [];
|
|
98
|
+
if (text[position] === '}') {
|
|
99
|
+
position++;
|
|
100
|
+
return items;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (;;) {
|
|
104
|
+
if (text[position] === '{') {
|
|
105
|
+
items.push(parseLevel());
|
|
106
|
+
} else if (text[position] === '"') {
|
|
107
|
+
position++;
|
|
108
|
+
let value = '';
|
|
109
|
+
while (text[position] !== '"') {
|
|
110
|
+
if (position >= text.length) {
|
|
111
|
+
throw new SyntaxError(`Unterminated quote in array text "${text}"`);
|
|
112
|
+
}
|
|
113
|
+
if (text[position] === '\\') {
|
|
114
|
+
position++;
|
|
115
|
+
}
|
|
116
|
+
value += text[position];
|
|
117
|
+
position++;
|
|
118
|
+
}
|
|
119
|
+
position++;
|
|
120
|
+
items.push(value);
|
|
121
|
+
} else {
|
|
122
|
+
const start = position;
|
|
123
|
+
while (position < text.length && text[position] !== ',' && text[position] !== '}') {
|
|
124
|
+
position++;
|
|
125
|
+
}
|
|
126
|
+
const value = text.slice(start, position);
|
|
127
|
+
items.push(value === 'NULL' ? null : value);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (text[position] === ',') {
|
|
131
|
+
position++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (text[position] === '}') {
|
|
135
|
+
position++;
|
|
136
|
+
return items;
|
|
137
|
+
}
|
|
138
|
+
throw new SyntaxError(`Malformed array text "${text}"`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result = parseLevel();
|
|
143
|
+
if (position !== text.length) {
|
|
144
|
+
throw new SyntaxError(`Malformed array text "${text}"`);
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function mapArray(items: unknown[], parseElement: (text: string) => unknown): unknown[] {
|
|
150
|
+
return items.map((item) => {
|
|
151
|
+
if (item === null) {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
if (Array.isArray(item)) {
|
|
155
|
+
return mapArray(item, parseElement);
|
|
156
|
+
}
|
|
157
|
+
return parseElement(item as string);
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Splits record text, `(12.34,EUR)` or `(12.34,"A\\B")`, into its fields, with `null` for an empty
|
|
163
|
+
* unquoted field. Quoted fields are unescaped: Postgres doubles `"` and `\` inside them on output,
|
|
164
|
+
* and accepts a backslash before any character on input.
|
|
165
|
+
*/
|
|
166
|
+
function parseRecord(text: string): (string | null)[] {
|
|
167
|
+
if (!text.startsWith('(') || !text.endsWith(')')) {
|
|
168
|
+
throw new SyntaxError(`Expected record text in parentheses, got "${text}"`);
|
|
169
|
+
}
|
|
170
|
+
const fields: (string | null)[] = [];
|
|
171
|
+
let position = 1;
|
|
172
|
+
const end = text.length - 1;
|
|
173
|
+
|
|
174
|
+
for (;;) {
|
|
175
|
+
let field = '';
|
|
176
|
+
let isQuotedOrNonEmpty = false;
|
|
177
|
+
while (position < end && text[position] !== ',') {
|
|
178
|
+
const character = text[position]!;
|
|
179
|
+
if (character === '"') {
|
|
180
|
+
isQuotedOrNonEmpty = true;
|
|
181
|
+
position++;
|
|
182
|
+
for (;;) {
|
|
183
|
+
if (position >= end) {
|
|
184
|
+
throw new SyntaxError(`Unbalanced quote in record text "${text}"`);
|
|
185
|
+
}
|
|
186
|
+
const quoted = text[position]!;
|
|
187
|
+
if (quoted === '\\') {
|
|
188
|
+
field += text[position + 1] ?? '';
|
|
189
|
+
position += 2;
|
|
190
|
+
} else if (quoted === '"' && text[position + 1] === '"') {
|
|
191
|
+
field += '"';
|
|
192
|
+
position += 2;
|
|
193
|
+
} else if (quoted === '"') {
|
|
194
|
+
position++;
|
|
195
|
+
break;
|
|
196
|
+
} else {
|
|
197
|
+
field += quoted;
|
|
198
|
+
position++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (character === '\\') {
|
|
204
|
+
field += text[position + 1] ?? '';
|
|
205
|
+
position += 2;
|
|
206
|
+
} else {
|
|
207
|
+
field += character;
|
|
208
|
+
position++;
|
|
209
|
+
}
|
|
210
|
+
isQuotedOrNonEmpty = true;
|
|
211
|
+
}
|
|
212
|
+
fields.push(isQuotedOrNonEmpty ? field : null);
|
|
213
|
+
if (position >= end) {
|
|
214
|
+
return fields;
|
|
215
|
+
}
|
|
216
|
+
position++;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Money from the text of the composite type, `(12.34,EUR)`. The amount keeps the scale it was
|
|
222
|
+
* stored with, and never less than the currency's exponent. NULL fields, `NaN`, `Infinity` and
|
|
223
|
+
* malformed text throw.
|
|
224
|
+
*/
|
|
225
|
+
export function parseMoneyComposite(text: string): Money {
|
|
226
|
+
const fields = parseRecord(text);
|
|
227
|
+
if (fields.length !== 2) {
|
|
228
|
+
throw new SyntaxError(`Expected (amount,currency) composite text, got "${text}"`);
|
|
229
|
+
}
|
|
230
|
+
const [amount, currency] = fields;
|
|
231
|
+
if (amount === null || amount === undefined || currency === null || currency === undefined) {
|
|
232
|
+
throw new TypeError(`A money composite needs both an amount and a currency, got "${text}"`);
|
|
233
|
+
}
|
|
234
|
+
parseNumeric(amount);
|
|
235
|
+
|
|
236
|
+
const fractionDigits = amount.includes('.') ? amount.length - amount.indexOf('.') - 1 : 0;
|
|
237
|
+
const scale = Math.max(fractionDigits, currencyExponent(currency));
|
|
238
|
+
return parseMoney(amount, currency, { scale });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* The composite type's text for a query parameter: `(12.34,"EUR")`, at the Money's own scale. The
|
|
243
|
+
* currency is always quoted and escaped, so custom codes containing `\`, `"`, `,` or `)` survive.
|
|
244
|
+
* Cast it in SQL (`$1::ducat_money`) where Postgres can't infer the type.
|
|
245
|
+
*/
|
|
246
|
+
export function toMoneyComposite(money: Money): string {
|
|
247
|
+
const currency = money.currency.replace(/["\\]/g, (character) => `\\${character}`);
|
|
248
|
+
return `(${toDecimal(money)},"${currency}")`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Type parsers for node-postgres that never turn an amount into a float: BIGINT as bigint, NUMERIC
|
|
253
|
+
* as an exact string (rejecting `NaN` and `Infinity`), their arrays the same way (pg's default
|
|
254
|
+
* parses NUMERIC arrays as floats), and optionally the composite money type as Money. Every other
|
|
255
|
+
* type uses `base`.
|
|
256
|
+
*
|
|
257
|
+
* new pg.Pool({ types: exactTypes(pg.types, { money: await moneyTypeOids(client) }) })
|
|
258
|
+
*/
|
|
259
|
+
export function exactTypes(base: TypeParserSource, options?: ExactTypesOptions): TypeParserSource {
|
|
260
|
+
const moneyOids = options?.money;
|
|
261
|
+
const parseInt8 = options?.int8 === 'string' ? checkBigIntText : parseBigInt;
|
|
262
|
+
|
|
263
|
+
return {
|
|
264
|
+
getTypeParser(oid: number, format?: 'text' | 'binary'): TypeParser {
|
|
265
|
+
// the binary format is only used when a query asks for it; leave it to pg
|
|
266
|
+
if (format === 'binary') {
|
|
267
|
+
return base.getTypeParser(oid, format);
|
|
268
|
+
}
|
|
269
|
+
if (oid === INT8_OID) {
|
|
270
|
+
return parseInt8;
|
|
271
|
+
}
|
|
272
|
+
if (oid === NUMERIC_OID) {
|
|
273
|
+
return parseNumeric;
|
|
274
|
+
}
|
|
275
|
+
if (oid === INT8_ARRAY_OID) {
|
|
276
|
+
return (text) => mapArray(parseArray(text), parseInt8);
|
|
277
|
+
}
|
|
278
|
+
if (oid === NUMERIC_ARRAY_OID) {
|
|
279
|
+
return (text) => mapArray(parseArray(text), parseNumeric);
|
|
280
|
+
}
|
|
281
|
+
if (moneyOids !== undefined && oid === moneyOids.oid) {
|
|
282
|
+
return parseMoneyComposite;
|
|
283
|
+
}
|
|
284
|
+
if (moneyOids !== undefined && oid === moneyOids.arrayOid) {
|
|
285
|
+
return (text) => mapArray(parseArray(text), parseMoneyComposite);
|
|
286
|
+
}
|
|
287
|
+
return base.getTypeParser(oid, format);
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** The default name of the composite money type. */
|
|
293
|
+
export const MONEY_TYPE_NAME = 'ducat_money';
|
|
294
|
+
|
|
295
|
+
const IDENTIFIER = /^[a-z_][a-z0-9_]*(?:\.[a-z_][a-z0-9_]*)?$/;
|
|
296
|
+
// Postgres truncates longer names, which would make `name` and `name_add` collide with others
|
|
297
|
+
const MAX_IDENTIFIER_LENGTH = 63;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The DDL for a composite money type, `(amount numeric, currency text)`, and a `sum` aggregate over
|
|
301
|
+
* it that throws on mixed currencies, and on a value with a NULL amount or currency. The amount is
|
|
302
|
+
* an unconstrained NUMERIC, so it keeps every digit and its scale. A schema-qualified name puts the
|
|
303
|
+
* type, its add function and the aggregate in that schema.
|
|
304
|
+
*
|
|
305
|
+
* Run the statements once, in order, in a migration: they are not idempotent, and a second run
|
|
306
|
+
* fails with `duplicate_object`.
|
|
307
|
+
*
|
|
308
|
+
* for (const statement of moneyTypeSql()) await client.query(statement);
|
|
309
|
+
* SELECT sum(price) FROM items; -- (37.50,EUR)
|
|
310
|
+
*/
|
|
311
|
+
export function moneyTypeSql(name: string = MONEY_TYPE_NAME): string[] {
|
|
312
|
+
if (!IDENTIFIER.test(name)) {
|
|
313
|
+
throw new RangeError(`"${name}" is not a plain lower-case SQL name (optionally schema.name)`);
|
|
314
|
+
}
|
|
315
|
+
const dot = name.indexOf('.');
|
|
316
|
+
const schemaPrefix = dot < 0 ? '' : name.slice(0, dot + 1);
|
|
317
|
+
const typeName = name.slice(dot + 1);
|
|
318
|
+
if (`${typeName}_add`.length > MAX_IDENTIFIER_LENGTH) {
|
|
319
|
+
throw new RangeError(`"${typeName}" is too long for a Postgres name with an "_add" suffix`);
|
|
320
|
+
}
|
|
321
|
+
const add = `${name}_add`;
|
|
322
|
+
|
|
323
|
+
return [
|
|
324
|
+
`CREATE TYPE ${name} AS (amount numeric, currency text)`,
|
|
325
|
+
// search_path FROM CURRENT: the cast below resolves the same way whatever the caller's path
|
|
326
|
+
`CREATE FUNCTION ${add}(total ${name}, item ${name}) RETURNS ${name}
|
|
327
|
+
LANGUAGE plpgsql IMMUTABLE PARALLEL SAFE SET search_path FROM CURRENT AS $$
|
|
328
|
+
BEGIN
|
|
329
|
+
IF item IS NULL THEN
|
|
330
|
+
RETURN total;
|
|
331
|
+
END IF;
|
|
332
|
+
IF item.amount IS NULL OR item.currency IS NULL THEN
|
|
333
|
+
RAISE EXCEPTION 'a money value needs both an amount and a currency'
|
|
334
|
+
USING ERRCODE = 'null_value_not_allowed';
|
|
335
|
+
END IF;
|
|
336
|
+
IF total IS NULL THEN
|
|
337
|
+
RETURN item;
|
|
338
|
+
END IF;
|
|
339
|
+
IF total.currency IS DISTINCT FROM item.currency THEN
|
|
340
|
+
RAISE EXCEPTION 'cannot add % and % amounts', total.currency, item.currency
|
|
341
|
+
USING ERRCODE = 'data_exception';
|
|
342
|
+
END IF;
|
|
343
|
+
RETURN ROW(total.amount + item.amount, total.currency)::${name};
|
|
344
|
+
END
|
|
345
|
+
$$`,
|
|
346
|
+
`CREATE AGGREGATE ${schemaPrefix}sum(${name}) ` +
|
|
347
|
+
`(SFUNC = ${add}, STYPE = ${name}, COMBINEFUNC = ${add}, PARALLEL = SAFE)`,
|
|
348
|
+
];
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/** The part of a pg Client or Pool that `moneyTypeOids` uses. */
|
|
352
|
+
export interface Queryable {
|
|
353
|
+
query(
|
|
354
|
+
sql: string,
|
|
355
|
+
values: unknown[],
|
|
356
|
+
): Promise<{ rows: { oid: number | string; typarray: number | string }[] }>;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Looks up the OIDs of the composite money type, for `exactTypes`. Run it once at start-up on a
|
|
361
|
+
* client without the money parser. OIDs differ between databases, and change if the type is
|
|
362
|
+
* dropped and recreated; values of a type whose OID `exactTypes` wasn't given come back as pg's
|
|
363
|
+
* raw text. With a type per schema, look up and pass each one's OIDs.
|
|
364
|
+
*/
|
|
365
|
+
export async function moneyTypeOids(
|
|
366
|
+
client: Queryable,
|
|
367
|
+
name: string = MONEY_TYPE_NAME,
|
|
368
|
+
): Promise<MoneyTypeOids> {
|
|
369
|
+
const result = await client.query(
|
|
370
|
+
'SELECT oid, typarray FROM pg_type WHERE oid = to_regtype($1)',
|
|
371
|
+
[name],
|
|
372
|
+
);
|
|
373
|
+
const row = result.rows[0];
|
|
374
|
+
if (row === undefined) {
|
|
375
|
+
throw new RangeError(`No type "${name}" in this database; run moneyTypeSql() first`);
|
|
376
|
+
}
|
|
377
|
+
return { oid: Number(row.oid), arrayOid: Number(row.typarray) };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The exact decimal string for a NUMERIC(precision, scale) column, or a throw if Postgres would
|
|
382
|
+
* change it: it rounds extra decimals silently on insert, and rejects too many integer digits.
|
|
383
|
+
* Unlike the column types, any scale up to the column's is accepted, and trailing zeros beyond it
|
|
384
|
+
* are dropped since they don't change the value.
|
|
385
|
+
*/
|
|
386
|
+
export function toNumeric<C extends CurrencyInput>(
|
|
387
|
+
money: Money<C>,
|
|
388
|
+
size: DecimalColumnSize,
|
|
389
|
+
): string {
|
|
390
|
+
checkDecimalSize(money.currency, size);
|
|
391
|
+
const decimal = toDecimal(money);
|
|
392
|
+
const [integerPart, fraction = ''] = decimal.replace('-', '').split('.');
|
|
393
|
+
|
|
394
|
+
const significantFraction = fraction.replace(/0+$/, '');
|
|
395
|
+
if (significantFraction.length > size.scale) {
|
|
396
|
+
throw new RangeError(
|
|
397
|
+
`${money} has ${significantFraction.length} decimals; NUMERIC(${size.precision},${size.scale}) ` +
|
|
398
|
+
`would round it to ${size.scale}`,
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
checkIntegerDigits(money, decimal, size);
|
|
403
|
+
|
|
404
|
+
if (fraction.length <= size.scale) {
|
|
405
|
+
return decimal;
|
|
406
|
+
}
|
|
407
|
+
const sign = decimal.startsWith('-') ? '-' : '';
|
|
408
|
+
const kept = fraction.slice(0, size.scale);
|
|
409
|
+
return kept === '' ? `${sign}${integerPart}` : `${sign}${integerPart}.${kept}`;
|
|
410
|
+
}
|
package/src/plugins/adyen.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/** `@ducatjs/money/adyen`: Adyen's minor-unit view. Importing this module adds `toAdyen`. */
|
|
2
2
|
import { toAdyen } from '../adyen.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { Money } from '../index.js';
|
|
4
|
+
import { extendMoney } from '../money.js';
|
|
4
5
|
import type { RoundingMode } from '../rounding.js';
|
|
5
6
|
|
|
6
7
|
export * from '../adyen.js';
|
|
7
8
|
|
|
8
|
-
declare module '../
|
|
9
|
+
declare module '../index.js' {
|
|
9
10
|
interface Money<C extends string> {
|
|
10
11
|
/**
|
|
11
12
|
* Adyen `amount` object: `money('15000', 'IDR').toAdyen()` is `{ value: 15000, currency: 'IDR'
|
package/src/plugins/all.ts
CHANGED
|
@@ -12,6 +12,7 @@ export {
|
|
|
12
12
|
toScale,
|
|
13
13
|
compare,
|
|
14
14
|
equals,
|
|
15
|
+
identical,
|
|
15
16
|
lessThan,
|
|
16
17
|
lessThanOrEqual,
|
|
17
18
|
greaterThan,
|
|
@@ -23,18 +24,27 @@ export {
|
|
|
23
24
|
min,
|
|
24
25
|
max,
|
|
25
26
|
clamp,
|
|
27
|
+
majorPart,
|
|
28
|
+
minorPart,
|
|
26
29
|
toDecimal,
|
|
27
30
|
toMinor,
|
|
28
31
|
toMinorNumber,
|
|
29
32
|
} from '../money.js';
|
|
33
|
+
export { roundingPolicy, isoPolicy, assertScale, type RoundingPolicyOptions } from '../policy.js';
|
|
30
34
|
export * from './allocate.js';
|
|
31
35
|
export * from './tax.js';
|
|
32
36
|
export * from './convert.js';
|
|
33
37
|
export * from './exact.js';
|
|
34
38
|
export * from '../ecb.js';
|
|
39
|
+
export * from '../boc.js';
|
|
40
|
+
export * from '../boe.js';
|
|
41
|
+
export * from '../hmrc.js';
|
|
42
|
+
export * from '../fed.js';
|
|
43
|
+
export * from '../history.js';
|
|
35
44
|
export * from '../stripe-fx.js';
|
|
36
45
|
export * from '../adyen-fx.js';
|
|
37
46
|
export * from './cash.js';
|
|
47
|
+
export * from '../historic.js';
|
|
38
48
|
export * from './format.js';
|
|
39
49
|
export * from '../parse.js';
|
|
40
50
|
export * from './stripe.js';
|
package/src/plugins/allocate.ts
CHANGED
|
@@ -14,11 +14,12 @@ import {
|
|
|
14
14
|
type Tranche,
|
|
15
15
|
type Weight,
|
|
16
16
|
} from '../allocate.js';
|
|
17
|
-
import {
|
|
17
|
+
import type { Money } from '../index.js';
|
|
18
|
+
import { extendMoney } from '../money.js';
|
|
18
19
|
|
|
19
20
|
export * from '../allocate.js';
|
|
20
21
|
|
|
21
|
-
declare module '../
|
|
22
|
+
declare module '../index.js' {
|
|
22
23
|
interface Money<C extends string> {
|
|
23
24
|
/** Splits by weights; the parts always sum to this. */
|
|
24
25
|
allocate(weights: readonly Weight[], options?: AllocateOptions): Money<C>[];
|
package/src/plugins/braintree.ts
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
* `@ducatjs/money/braintree`: Braintree's amount view. Importing this module adds `toBraintree`.
|
|
3
3
|
*/
|
|
4
4
|
import { toBraintree, type BraintreeAmountOptions } from '../braintree.js';
|
|
5
|
-
import {
|
|
5
|
+
import type { Money } from '../index.js';
|
|
6
|
+
import { extendMoney } from '../money.js';
|
|
6
7
|
|
|
7
8
|
export * from '../braintree.js';
|
|
8
9
|
|
|
9
|
-
declare module '../
|
|
10
|
+
declare module '../index.js' {
|
|
10
11
|
interface Money<C extends string> {
|
|
11
12
|
/** The exact `amount` string for Braintree: `money('10', 'USD').toBraintree()` is '10.00'. */
|
|
12
13
|
toBraintree(options?: BraintreeAmountOptions): string;
|
package/src/plugins/cash.ts
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
* rounding to an increment. Importing this module adds the methods below.
|
|
4
4
|
*/
|
|
5
5
|
import { cashRound, roundToIncrement, type CashRoundOptions } from '../cash.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { Money } from '../index.js';
|
|
7
|
+
import { extendMoney } from '../money.js';
|
|
7
8
|
import type { RoundingMode } from '../rounding.js';
|
|
8
9
|
|
|
9
10
|
export * from '../cash.js';
|
|
10
11
|
|
|
11
|
-
declare module '../
|
|
12
|
+
declare module '../index.js' {
|
|
12
13
|
interface Money<C extends string> {
|
|
13
14
|
/**
|
|
14
15
|
* Rounds a cash total to the local cash step (CHF 0.05, SEK 1, ...); unchanged when no rule
|
package/src/plugins/checkout.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* and `toCheckoutAmount`.
|
|
4
4
|
*/
|
|
5
5
|
import { toCheckout, toCheckoutAmount, type CheckoutAmountOptions } from '../checkout.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { Money } from '../index.js';
|
|
7
|
+
import { extendMoney } from '../money.js';
|
|
7
8
|
|
|
8
9
|
export * from '../checkout.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/**
|
|
13
14
|
* The integer `amount` to send to Checkout.com: `money('5000', 'CLP').toCheckoutAmount()` is
|
package/src/plugins/convert.ts
CHANGED
|
@@ -4,27 +4,27 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { convert } from '../convert.js';
|
|
6
6
|
import type { Factor } from '../decimal.js';
|
|
7
|
-
import
|
|
7
|
+
import type { Money } from '../index.js';
|
|
8
|
+
import { extendMoney, type CurrencyInput, type RoundingInput } from '../money.js';
|
|
8
9
|
import type { ProviderConvertOptions } from '../convert.js';
|
|
9
10
|
import type { RateProvider } from '../provider.js';
|
|
10
11
|
import type { ConvertOptions, Rate } from '../rate.js';
|
|
11
|
-
import type { RoundingMode } from '../rounding.js';
|
|
12
12
|
|
|
13
13
|
export * from '../convert.js';
|
|
14
14
|
|
|
15
|
-
declare module '../
|
|
15
|
+
declare module '../index.js' {
|
|
16
16
|
interface Money<C extends string> {
|
|
17
17
|
/** Converts at a Rate, rounding once: `eur.convert(eurUsd, 'halfEven')`. */
|
|
18
18
|
convert<To extends string>(
|
|
19
19
|
rate: Rate<NoInfer<C>, To>,
|
|
20
|
-
rounding?:
|
|
20
|
+
rounding?: RoundingInput,
|
|
21
21
|
options?: ConvertOptions,
|
|
22
22
|
): Money<To>;
|
|
23
23
|
/** Converts at an exact decimal-string rate: `eur.convert('USD', '1.0832', 'halfEven')`. */
|
|
24
24
|
convert<To extends CurrencyInput>(
|
|
25
25
|
to: To,
|
|
26
26
|
rate: Factor,
|
|
27
|
-
rounding?:
|
|
27
|
+
rounding?: RoundingInput,
|
|
28
28
|
options?: ConvertOptions,
|
|
29
29
|
): Money<To>;
|
|
30
30
|
/**
|
|
@@ -34,7 +34,7 @@ declare module '../money.js' {
|
|
|
34
34
|
convert<To extends CurrencyInput>(
|
|
35
35
|
to: To,
|
|
36
36
|
provider: RateProvider,
|
|
37
|
-
rounding?:
|
|
37
|
+
rounding?: RoundingInput,
|
|
38
38
|
options?: ProviderConvertOptions,
|
|
39
39
|
): Money<To>;
|
|
40
40
|
}
|
package/src/plugins/exact.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* `Money#exact`.
|
|
4
4
|
*/
|
|
5
5
|
import { exact, type ExactMoney } from '../exact.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { Money } from '../index.js';
|
|
7
|
+
import { extendMoney } from '../money.js';
|
|
7
8
|
|
|
8
9
|
export * from '../exact.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/**
|
|
13
14
|
* The exact value, for chained arithmetic that rounds once: `price.exact().divide(3).round()`.
|
package/src/plugins/fixed.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* Money. Importing this module adds `Money#toFixedPoint()`.
|
|
4
4
|
*/
|
|
5
5
|
import { moneyToFixed, type Fixed } from '../fixed.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { Money } from '../index.js';
|
|
7
|
+
import { extendMoney } from '../money.js';
|
|
7
8
|
|
|
8
9
|
export * from '../fixed.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/**
|
|
13
14
|
* The exact Fixed of this amount, at its scale: `money('12.34', 'EUR').toFixedPoint()` is
|
package/src/plugins/format.ts
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
* below.
|
|
4
4
|
*/
|
|
5
5
|
import { format, formatToParts } from '../format.js';
|
|
6
|
-
import {
|
|
6
|
+
import type { Money } from '../index.js';
|
|
7
|
+
import { extendMoney } from '../money.js';
|
|
7
8
|
|
|
8
9
|
export * from '../format.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/** Locale formatting via Intl: `price.format('de-DE')` gives "19,99 €" */
|
|
13
14
|
format(locale?: string, options?: Intl.NumberFormatOptions): string;
|
package/src/plugins/klarna.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/** `@ducatjs/money/klarna`: Klarna's amount view. Importing this module adds `toKlarna`. */
|
|
2
2
|
import { toKlarna, type KlarnaAmountOptions } from '../klarna.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { Money } from '../index.js';
|
|
4
|
+
import { extendMoney } from '../money.js';
|
|
4
5
|
|
|
5
6
|
export * from '../klarna.js';
|
|
6
7
|
|
|
7
|
-
declare module '../
|
|
8
|
+
declare module '../index.js' {
|
|
8
9
|
interface Money<C extends string> {
|
|
9
10
|
/** The integer for any Klarna amount field: `money('25', 'EUR').toKlarna()` is 2500. */
|
|
10
11
|
toKlarna(options?: KlarnaAmountOptions): number;
|
package/src/plugins/paypal.ts
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* `@ducatjs/money/paypal`: PayPal's amount view. Importing this module adds `toPayPal` and
|
|
3
3
|
* `toPayPalValue`.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import type { Money } from '../index.js';
|
|
6
|
+
import { extendMoney } from '../money.js';
|
|
6
7
|
import { toPayPal, toPayPalValue, type PayPalAmountOptions } from '../paypal.js';
|
|
7
8
|
|
|
8
9
|
export * from '../paypal.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/** The exact `value` string for PayPal: `money('1000', 'HUF').toPayPalValue()` is '1000'. */
|
|
13
14
|
toPayPalValue(options?: PayPalAmountOptions): string;
|
package/src/plugins/prisma.ts
CHANGED
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
* `@ducatjs/money/prisma`: Prisma and SQL column helpers. Importing this module adds
|
|
3
3
|
* `toDecimalValue`.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
5
|
+
import type { Money } from '../index.js';
|
|
6
|
+
import { extendMoney } from '../money.js';
|
|
6
7
|
import { toDecimalValue } from '../prisma.js';
|
|
7
8
|
|
|
8
9
|
export * from '../prisma.js';
|
|
9
10
|
|
|
10
|
-
declare module '../
|
|
11
|
+
declare module '../index.js' {
|
|
11
12
|
interface Money<C extends string> {
|
|
12
13
|
/** A Decimal for a NUMERIC column: `m.toDecimalValue(Prisma.Decimal)`. */
|
|
13
14
|
toDecimalValue<D>(Decimal: new (value: string) => D): D;
|