@ducatjs/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.
Files changed (201) hide show
  1. package/README.md +479 -62
  2. package/data/cash-rounding.json +123 -2
  3. package/data/cldr-cash.json +101 -0
  4. package/data/historic.json +78 -0
  5. package/data/iso4217-list-three.xml +1185 -0
  6. package/dist/adyen-fx.d.ts.map +1 -1
  7. package/dist/adyen-fx.js +22 -4
  8. package/dist/adyen-fx.js.map +1 -1
  9. package/dist/boc.d.ts +31 -0
  10. package/dist/boc.d.ts.map +1 -0
  11. package/dist/boc.js +114 -0
  12. package/dist/boc.js.map +1 -0
  13. package/dist/boe.d.ts +33 -0
  14. package/dist/boe.d.ts.map +1 -0
  15. package/dist/boe.js +114 -0
  16. package/dist/boe.js.map +1 -0
  17. package/dist/cash.d.ts +30 -1
  18. package/dist/cash.d.ts.map +1 -1
  19. package/dist/cash.js +50 -2
  20. package/dist/cash.js.map +1 -1
  21. package/dist/convert.d.ts +10 -9
  22. package/dist/convert.d.ts.map +1 -1
  23. package/dist/convert.js +13 -3
  24. package/dist/convert.js.map +1 -1
  25. package/dist/drizzle/mysql.d.ts +105 -0
  26. package/dist/drizzle/mysql.d.ts.map +1 -0
  27. package/dist/drizzle/mysql.js +60 -0
  28. package/dist/drizzle/mysql.js.map +1 -0
  29. package/dist/drizzle/pg.d.ts +103 -0
  30. package/dist/drizzle/pg.d.ts.map +1 -0
  31. package/dist/drizzle/pg.js +60 -0
  32. package/dist/drizzle/pg.js.map +1 -0
  33. package/dist/drizzle/sqlite.d.ts +102 -0
  34. package/dist/drizzle/sqlite.d.ts.map +1 -0
  35. package/dist/drizzle/sqlite.js +59 -0
  36. package/dist/drizzle/sqlite.js.map +1 -0
  37. package/dist/ecb.d.ts +37 -2
  38. package/dist/ecb.d.ts.map +1 -1
  39. package/dist/ecb.js +148 -10
  40. package/dist/ecb.js.map +1 -1
  41. package/dist/fed.d.ts +35 -0
  42. package/dist/fed.d.ts.map +1 -0
  43. package/dist/fed.js +134 -0
  44. package/dist/fed.js.map +1 -0
  45. package/dist/fetch.d.ts +45 -0
  46. package/dist/fetch.d.ts.map +1 -1
  47. package/dist/fetch.js +167 -24
  48. package/dist/fetch.js.map +1 -1
  49. package/dist/fn.d.ts +8 -1
  50. package/dist/fn.d.ts.map +1 -1
  51. package/dist/fn.js +8 -1
  52. package/dist/fn.js.map +1 -1
  53. package/dist/generated/cash.d.ts +2 -0
  54. package/dist/generated/cash.d.ts.map +1 -1
  55. package/dist/generated/cash.js +101 -0
  56. package/dist/generated/cash.js.map +1 -1
  57. package/dist/generated/historic.d.ts +337 -0
  58. package/dist/generated/historic.d.ts.map +1 -0
  59. package/dist/generated/historic.js +66 -0
  60. package/dist/generated/historic.js.map +1 -0
  61. package/dist/historic.d.ts +40 -0
  62. package/dist/historic.d.ts.map +1 -0
  63. package/dist/historic.js +43 -0
  64. package/dist/historic.js.map +1 -0
  65. package/dist/history.d.ts +96 -0
  66. package/dist/history.d.ts.map +1 -0
  67. package/dist/history.js +213 -0
  68. package/dist/history.js.map +1 -0
  69. package/dist/hmrc.d.ts +28 -0
  70. package/dist/hmrc.d.ts.map +1 -0
  71. package/dist/hmrc.js +108 -0
  72. package/dist/hmrc.js.map +1 -0
  73. package/dist/index.d.ts +1 -1
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +1 -1
  76. package/dist/index.js.map +1 -1
  77. package/dist/kysely.d.ts +120 -0
  78. package/dist/kysely.d.ts.map +1 -0
  79. package/dist/kysely.js +309 -0
  80. package/dist/kysely.js.map +1 -0
  81. package/dist/money.d.ts +93 -9
  82. package/dist/money.d.ts.map +1 -1
  83. package/dist/money.js +135 -9
  84. package/dist/money.js.map +1 -1
  85. package/dist/parse.d.ts +45 -1
  86. package/dist/parse.d.ts.map +1 -1
  87. package/dist/parse.js +290 -3
  88. package/dist/parse.js.map +1 -1
  89. package/dist/pg.d.ts +100 -0
  90. package/dist/pg.d.ts.map +1 -0
  91. package/dist/pg.js +343 -0
  92. package/dist/pg.js.map +1 -0
  93. package/dist/plugins/all.d.ts +8 -1
  94. package/dist/plugins/all.d.ts.map +1 -1
  95. package/dist/plugins/all.js +8 -1
  96. package/dist/plugins/all.js.map +1 -1
  97. package/dist/plugins/convert.d.ts +3 -4
  98. package/dist/plugins/convert.d.ts.map +1 -1
  99. package/dist/plugins/convert.js.map +1 -1
  100. package/dist/plugins/tax.d.ts +4 -5
  101. package/dist/plugins/tax.d.ts.map +1 -1
  102. package/dist/plugins/tax.js.map +1 -1
  103. package/dist/policy.d.ts +34 -0
  104. package/dist/policy.d.ts.map +1 -0
  105. package/dist/policy.js +90 -0
  106. package/dist/policy.js.map +1 -0
  107. package/dist/prisma.d.ts +25 -14
  108. package/dist/prisma.d.ts.map +1 -1
  109. package/dist/prisma.js +29 -19
  110. package/dist/prisma.js.map +1 -1
  111. package/dist/provider.d.ts +21 -7
  112. package/dist/provider.d.ts.map +1 -1
  113. package/dist/provider.js +51 -11
  114. package/dist/provider.js.map +1 -1
  115. package/dist/rate-text.d.ts +38 -0
  116. package/dist/rate-text.d.ts.map +1 -0
  117. package/dist/rate-text.js +143 -0
  118. package/dist/rate-text.js.map +1 -0
  119. package/dist/rate.d.ts +61 -5
  120. package/dist/rate.d.ts.map +1 -1
  121. package/dist/rate.js +133 -33
  122. package/dist/rate.js.map +1 -1
  123. package/dist/rounding.js +1 -1
  124. package/dist/rounding.js.map +1 -1
  125. package/dist/schema.d.ts +43 -0
  126. package/dist/schema.d.ts.map +1 -0
  127. package/dist/schema.js +77 -0
  128. package/dist/schema.js.map +1 -0
  129. package/dist/sql.d.ts +54 -0
  130. package/dist/sql.d.ts.map +1 -0
  131. package/dist/sql.js +131 -0
  132. package/dist/sql.js.map +1 -0
  133. package/dist/tax.d.ts +13 -9
  134. package/dist/tax.d.ts.map +1 -1
  135. package/dist/tax.js +20 -4
  136. package/dist/tax.js.map +1 -1
  137. package/dist/testing/arbitraries.d.ts +43 -0
  138. package/dist/testing/arbitraries.d.ts.map +1 -0
  139. package/dist/testing/arbitraries.js +98 -0
  140. package/dist/testing/arbitraries.js.map +1 -0
  141. package/dist/testing/index.d.ts +67 -0
  142. package/dist/testing/index.d.ts.map +1 -0
  143. package/dist/testing/index.js +159 -0
  144. package/dist/testing/index.js.map +1 -0
  145. package/dist/testing/jest.d.ts +2 -0
  146. package/dist/testing/jest.d.ts.map +1 -0
  147. package/dist/testing/jest.js +15 -0
  148. package/dist/testing/jest.js.map +1 -0
  149. package/dist/testing/vitest.d.ts +7 -0
  150. package/dist/testing/vitest.d.ts.map +1 -0
  151. package/dist/testing/vitest.js +12 -0
  152. package/dist/testing/vitest.js.map +1 -0
  153. package/dist/valibot.d.ts +33 -0
  154. package/dist/valibot.d.ts.map +1 -0
  155. package/dist/valibot.js +61 -0
  156. package/dist/valibot.js.map +1 -0
  157. package/dist/zod.d.ts +36 -0
  158. package/dist/zod.d.ts.map +1 -0
  159. package/dist/zod.js +58 -0
  160. package/dist/zod.js.map +1 -0
  161. package/package.json +116 -5
  162. package/src/adyen-fx.ts +25 -4
  163. package/src/boc.ts +136 -0
  164. package/src/boe.ts +131 -0
  165. package/src/cash.ts +70 -2
  166. package/src/convert.ts +33 -15
  167. package/src/drizzle/mysql.ts +78 -0
  168. package/src/drizzle/pg.ts +78 -0
  169. package/src/drizzle/sqlite.ts +70 -0
  170. package/src/ecb.ts +179 -13
  171. package/src/fed.ts +146 -0
  172. package/src/fetch.ts +207 -28
  173. package/src/fn.ts +14 -0
  174. package/src/generated/cash.ts +103 -0
  175. package/src/generated/historic.ts +67 -0
  176. package/src/historic.ts +77 -0
  177. package/src/history.ts +333 -0
  178. package/src/hmrc.ts +125 -0
  179. package/src/index.ts +4 -0
  180. package/src/kysely.ts +588 -0
  181. package/src/money.ts +210 -12
  182. package/src/parse.ts +371 -4
  183. package/src/pg.ts +410 -0
  184. package/src/plugins/all.ts +10 -0
  185. package/src/plugins/convert.ts +4 -5
  186. package/src/plugins/tax.ts +9 -10
  187. package/src/policy.ts +124 -0
  188. package/src/prisma.ts +91 -10
  189. package/src/provider.ts +81 -16
  190. package/src/rate-text.ts +151 -0
  191. package/src/rate.ts +205 -36
  192. package/src/rounding.ts +1 -1
  193. package/src/schema.ts +119 -0
  194. package/src/sql.ts +178 -0
  195. package/src/tax.ts +40 -9
  196. package/src/testing/arbitraries.ts +134 -0
  197. package/src/testing/index.ts +247 -0
  198. package/src/testing/jest.ts +18 -0
  199. package/src/testing/vitest.ts +18 -0
  200. package/src/valibot.ts +87 -0
  201. package/src/zod.ts +78 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * `@ducatjs/money/testing`: makes Money pleasant to test with Vitest and Jest. It provides value
3
+ * equality for `toEqual`, readable snapshots and failure output (`12.34 EUR`), and money matchers.
4
+ * Call `installMoneyMatchers(expect)` in a setup file, or import `@ducatjs/money/testing/vitest`
5
+ * or `@ducatjs/money/testing/jest`, which do that for you. Nothing here changes production code.
6
+ */
7
+ import { currencyExponent } from '../currency.js';
8
+ import { parseDecimal } from '../decimal.js';
9
+ import { compare, equals, isMoney, money, sum, zero } from '../money.js';
10
+ /**
11
+ * Money compares by value in `toEqual`, `toStrictEqual` and nested structures: same currency and
12
+ * the same amount, whatever the scale (`1.0` and `1.00`) and whether the minor units are a number
13
+ * or a bigint. For anything other than two Money values it returns undefined, so the usual
14
+ * comparison applies.
15
+ */
16
+ export const moneyEquality = (left, right) => {
17
+ if (isMoney(left) && isMoney(right)) {
18
+ return equals(left, right);
19
+ }
20
+ // Anything else, including a Money from a second installed copy of the package (which isMoney
21
+ // does not recognise), is left to the runner's own field-by-field comparison.
22
+ return undefined;
23
+ };
24
+ /** A snapshot serializer that prints Money as `12.34 EUR` (unquoted, so it isn't a string). */
25
+ export const moneySerializer = {
26
+ test: (value) => isMoney(value),
27
+ serialize: (value) => String(value),
28
+ };
29
+ /**
30
+ * Prints a received value for a failure message. JSON.stringify would throw on a bigint or a
31
+ * cycle, and a matcher must fail cleanly, not throw while explaining the failure.
32
+ */
33
+ function show(value, seen = new Set()) {
34
+ if (isMoney(value)) {
35
+ return String(value);
36
+ }
37
+ if (typeof value === 'bigint') {
38
+ return `${value}n`;
39
+ }
40
+ if (typeof value === 'string') {
41
+ return JSON.stringify(value);
42
+ }
43
+ if (typeof value !== 'object' || value === null) {
44
+ return String(value);
45
+ }
46
+ if (seen.has(value)) {
47
+ return '[Circular]';
48
+ }
49
+ seen.add(value);
50
+ if (Array.isArray(value)) {
51
+ return `[${value.map((item) => show(item, seen)).join(', ')}]`;
52
+ }
53
+ const fields = Object.entries(value).map(([key, field]) => `${key}: ${show(field, seen)}`);
54
+ return `{ ${fields.join(', ')} }`;
55
+ }
56
+ function result(context, pass, received, expectation, expected) {
57
+ return {
58
+ pass,
59
+ message: () => `expected ${show(received)} ${context.isNot ? 'not ' : ''}${expectation}`,
60
+ actual: received,
61
+ expected,
62
+ };
63
+ }
64
+ /**
65
+ * Comparing amounts in different currencies, or something that isn't Money, is a mistake in the
66
+ * test, so it throws instead of failing; otherwise `.not` would quietly pass on it.
67
+ */
68
+ function requireComparable(matcher, received, other) {
69
+ if (!isMoney(received) || !isMoney(other)) {
70
+ throw new TypeError(`${matcher}: expected two Money values, got ${show(received)} and ${show(other)}`);
71
+ }
72
+ if (received.currency !== other.currency) {
73
+ throw new TypeError(`${matcher}: cannot compare ${received.currency} with ${other.currency}`);
74
+ }
75
+ }
76
+ /** Decimal places in an amount as `money()` takes it, so `toBeMoney` can keep every digit. */
77
+ function decimalsOf(amount) {
78
+ // parseDecimal understands exponent notation ('1.5e-7'), as money() does
79
+ return typeof amount === 'bigint' ? 0 : parseDecimal(String(amount)).scale;
80
+ }
81
+ function comparison(name, wording, accepts) {
82
+ return function (received, other) {
83
+ requireComparable(name, received, other);
84
+ const pass = accepts(compare(received, other));
85
+ return result(this, pass, received, `${wording} ${show(other)}`);
86
+ };
87
+ }
88
+ /** The matcher implementations, for `expect.extend`. */
89
+ export const moneyMatchers = {
90
+ toBeMoney(received, amount, currency) {
91
+ const scale = Math.max(decimalsOf(amount), currencyExponent(currency));
92
+ const expected = money(amount, currency, { scale });
93
+ const pass = isMoney(received) && equals(received, expected);
94
+ return result(this, pass, received, `to be ${expected}`, expected);
95
+ },
96
+ toHaveCurrency(received, currency) {
97
+ const pass = isMoney(received) && received.currency === currency;
98
+ return result(this, pass, received, `to have currency ${currency}`);
99
+ },
100
+ toBeGreaterThanMoney: comparison('toBeGreaterThanMoney', 'to be greater than', (order) => order > 0),
101
+ toBeGreaterThanOrEqualMoney: comparison('toBeGreaterThanOrEqualMoney', 'to be greater than or equal to', (order) => order >= 0),
102
+ toBeLessThanMoney: comparison('toBeLessThanMoney', 'to be less than', (order) => order < 0),
103
+ toBeLessThanOrEqualMoney: comparison('toBeLessThanOrEqualMoney', 'to be less than or equal to', (order) => order <= 0),
104
+ toSumTo(received, total) {
105
+ // Not a list of Money, or parts in another currency, is a mistake in the test: throw.
106
+ if (!Array.isArray(received) || !isMoney(total)) {
107
+ throw new TypeError(`toSumTo: expected a list of Money and a Money total, got ${show(received)}`);
108
+ }
109
+ for (const part of received) {
110
+ requireComparable('toSumTo', part, total);
111
+ }
112
+ const partsSum = sum(received, total.currency);
113
+ const pass = equals(partsSum, total);
114
+ return {
115
+ pass,
116
+ message: () => `expected ${show(received)} ${this.isNot ? 'not ' : ''}to sum to ${show(total)}` +
117
+ (this.isNot ? '' : `; they sum to ${partsSum}`),
118
+ actual: partsSum,
119
+ expected: total,
120
+ };
121
+ },
122
+ };
123
+ const INSPECT = Symbol.for('nodejs.util.inspect.custom');
124
+ /**
125
+ * Installs everything on a test runner's `expect`: the matchers, value equality and the snapshot
126
+ * serializer. It also gives Money a Node inspect hook, so `console.log(price)` prints
127
+ * `Money(12.34 EUR)`. Safe to call more than once. A runner that lacks `addEqualityTesters` or
128
+ * `addSnapshotSerializer` (Bun supports neither) gets the matchers and the inspect hook.
129
+ */
130
+ export function installMoneyMatchers(expect) {
131
+ expect.extend(moneyMatchers);
132
+ // Optional runner features: Bun has no equality testers, and its addSnapshotSerializer throws
133
+ // "Not implemented". The matchers work without either.
134
+ try {
135
+ expect.addEqualityTesters?.([moneyEquality]);
136
+ }
137
+ catch {
138
+ // not supported by this runner
139
+ }
140
+ try {
141
+ expect.addSnapshotSerializer?.(moneySerializer);
142
+ }
143
+ catch {
144
+ // not supported by this runner
145
+ }
146
+ // The inspect hook is the one change to Money itself; only test setups call this function.
147
+ const prototype = Object.getPrototypeOf(zero('EUR'));
148
+ if (!Object.hasOwn(prototype, INSPECT)) {
149
+ Object.defineProperty(prototype, INSPECT, {
150
+ value() {
151
+ return `Money(${String(this)})`;
152
+ },
153
+ writable: true,
154
+ configurable: true,
155
+ enumerable: false,
156
+ });
157
+ }
158
+ }
159
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from './index.js';
2
+ //# sourceMappingURL=jest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.d.ts","sourceRoot":"","sources":["../../src/testing/jest.ts"],"names":[],"mappings":"AASA,cAAc,YAAY,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * `@ducatjs/money/testing/jest`: one import in a Jest setup file (`setupFilesAfterEnv`) installs
3
+ * the money matchers, value equality and the snapshot serializer on Jest's global `expect`. It
4
+ * needs no Jest package of its own. For the types, add `MoneyMatchers` to Jest's `Matchers` in a
5
+ * declaration file (see the README): augmenting Jest's global namespace from here would clash
6
+ * with Vitest's types in projects that have both.
7
+ */
8
+ import { installMoneyMatchers } from './index.js';
9
+ export * from './index.js';
10
+ const globalExpect = globalThis.expect;
11
+ if (globalExpect === undefined) {
12
+ throw new Error('@ducatjs/money/testing/jest needs Jest’s global expect; import it from a setup file, or call installMoneyMatchers(expect)');
13
+ }
14
+ installMoneyMatchers(globalExpect);
15
+ //# sourceMappingURL=jest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jest.js","sourceRoot":"","sources":["../../src/testing/jest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,oBAAoB,EAA6B,MAAM,YAAY,CAAC;AAE7E,cAAc,YAAY,CAAC;AAE3B,MAAM,YAAY,GAAI,UAAgD,CAAC,MAAM,CAAC;AAC9E,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;IAC/B,MAAM,IAAI,KAAK,CACb,2HAA2H,CAC5H,CAAC;AACJ,CAAC;AACD,oBAAoB,CAAC,YAAY,CAAC,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { type MoneyMatchers } from './index.js';
2
+ export * from './index.js';
3
+ declare module 'vitest' {
4
+ interface Matchers<T = any> extends MoneyMatchers<T> {
5
+ }
6
+ }
7
+ //# sourceMappingURL=vitest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.d.ts","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AAQA,OAAO,EAAwB,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtE,cAAc,YAAY,CAAC;AAE3B,OAAO,QAAQ,QAAQ,CAAC;IAEtB,UAAU,QAAQ,CAAC,CAAC,GAAG,GAAG,CAAE,SAAQ,aAAa,CAAC,CAAC,CAAC;KAAG;CACxD"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `@ducatjs/money/testing/vitest`: one import in a Vitest setup file installs the money matchers,
3
+ * value equality and the snapshot serializer, and types the matchers on `expect`:
4
+ *
5
+ * // vitest.config.ts: test: { setupFiles: ['./test/setup.ts'] }
6
+ * import '@ducatjs/money/testing/vitest';
7
+ */
8
+ import { expect } from 'vitest';
9
+ import { installMoneyMatchers } from './index.js';
10
+ export * from './index.js';
11
+ installMoneyMatchers(expect);
12
+ //# sourceMappingURL=vitest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vitest.js","sourceRoot":"","sources":["../../src/testing/vitest.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,oBAAoB,EAAsB,MAAM,YAAY,CAAC;AAEtE,cAAc,YAAY,CAAC;AAO3B,oBAAoB,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * `@ducatjs/money/valibot`: Valibot schemas for the JSON form of Money, `{ "amount": "12.34",
3
+ * "currency": "EUR" }`, for API boundaries. Valibot is an optional peer dependency: only this entry
4
+ * imports it.
5
+ */
6
+ import * as valibot from 'valibot';
7
+ import { type MoneyJSON } from './money.js';
8
+ import { type MoneyFor, type MoneySchemaOptions } from './schema.js';
9
+ export type { MoneyFor, MoneySchemaOptions } from './schema.js';
10
+ declare const jsonShape: valibot.ObjectSchema<{
11
+ readonly amount: valibot.StringSchema<undefined>;
12
+ readonly currency: valibot.StringSchema<undefined>;
13
+ }, undefined>;
14
+ /** The schema type both functions return: the JSON shape, then a transformation. */
15
+ export type MoneySchema<TOutput> = valibot.SchemaWithPipe<readonly [typeof jsonShape, valibot.RawTransformAction<MoneyJSON, TOutput>]>;
16
+ /**
17
+ * Parses `{ amount, currency }` into Money, accepting exactly what the Zod entry accepts (see its
18
+ * `moneySchema`): an exact decimal string with no more decimals than the scale and at most
19
+ * `maxDigits` digits, padded to the scale, and a listed or known currency. Problems are Valibot
20
+ * issues on `amount` or `currency`; other keys are dropped. Bad options throw when the schema is
21
+ * built. Valibot has no codecs, so encode with `money.toJSON()` (or `JSON.stringify`), which this
22
+ * schema reads back.
23
+ *
24
+ * const Order = valibot.object({ total: moneySchema({ currency: 'EUR' }) })
25
+ * v.parse(Order, { total: { amount: '12.34', currency: 'EUR' } }).total // Money<'EUR'>
26
+ */
27
+ export declare function moneySchema<C extends string = string>(options?: MoneySchemaOptions<C>): MoneySchema<MoneyFor<C>>;
28
+ /**
29
+ * Like `moneySchema`, but the output stays the JSON object: it checks that the input would parse,
30
+ * for code that passes the JSON on rather than computing with it.
31
+ */
32
+ export declare function moneyJSONSchema<C extends string = string>(options?: MoneySchemaOptions<C>): MoneySchema<MoneyJSON<C>>;
33
+ //# sourceMappingURL=valibot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valibot.d.ts","sourceRoot":"","sources":["../src/valibot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAGL,KAAK,QAAQ,EAEb,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhE,QAAA,MAAM,SAAS;;;aAA2E,CAAC;AAE3F,oFAAoF;AACpF,MAAM,MAAM,WAAW,CAAC,OAAO,IAAI,OAAO,CAAC,cAAc,CACvD,SAAS,CAAC,OAAO,SAAS,EAAE,OAAO,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAC5E,CAAC;AAeF;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACnD,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAc1B;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACvD,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAc3B"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * `@ducatjs/money/valibot`: Valibot schemas for the JSON form of Money, `{ "amount": "12.34",
3
+ * "currency": "EUR" }`, for API boundaries. Valibot is an optional peer dependency: only this entry
4
+ * imports it.
5
+ */
6
+ import * as valibot from 'valibot';
7
+ import { isMoney } from './money.js';
8
+ import { checkSchemaOptions, readMoneyJSON, } from './schema.js';
9
+ const jsonShape = valibot.object({ amount: valibot.string(), currency: valibot.string() });
10
+ /** A Valibot issue path pointing at the field that is wrong. */
11
+ function pathTo(json, issue) {
12
+ return [
13
+ {
14
+ type: 'object',
15
+ origin: 'value',
16
+ input: json,
17
+ key: issue.field,
18
+ value: json[issue.field],
19
+ },
20
+ ];
21
+ }
22
+ /**
23
+ * Parses `{ amount, currency }` into Money, accepting exactly what the Zod entry accepts (see its
24
+ * `moneySchema`): an exact decimal string with no more decimals than the scale and at most
25
+ * `maxDigits` digits, padded to the scale, and a listed or known currency. Problems are Valibot
26
+ * issues on `amount` or `currency`; other keys are dropped. Bad options throw when the schema is
27
+ * built. Valibot has no codecs, so encode with `money.toJSON()` (or `JSON.stringify`), which this
28
+ * schema reads back.
29
+ *
30
+ * const Order = valibot.object({ total: moneySchema({ currency: 'EUR' }) })
31
+ * v.parse(Order, { total: { amount: '12.34', currency: 'EUR' } }).total // Money<'EUR'>
32
+ */
33
+ export function moneySchema(options) {
34
+ checkSchemaOptions(options);
35
+ return valibot.pipe(jsonShape, valibot.rawTransform(({ dataset, addIssue, NEVER }) => {
36
+ const json = dataset.value;
37
+ const result = readMoneyJSON(json, options);
38
+ if (isMoney(result)) {
39
+ return result;
40
+ }
41
+ addIssue({ message: result.message, path: pathTo(json, result) });
42
+ return NEVER;
43
+ }));
44
+ }
45
+ /**
46
+ * Like `moneySchema`, but the output stays the JSON object: it checks that the input would parse,
47
+ * for code that passes the JSON on rather than computing with it.
48
+ */
49
+ export function moneyJSONSchema(options) {
50
+ checkSchemaOptions(options);
51
+ return valibot.pipe(jsonShape, valibot.rawTransform(({ dataset, addIssue, NEVER }) => {
52
+ const json = dataset.value;
53
+ const result = readMoneyJSON(json, options);
54
+ if (isMoney(result)) {
55
+ return json;
56
+ }
57
+ addIssue({ message: result.message, path: pathTo(json, result) });
58
+ return NEVER;
59
+ }));
60
+ }
61
+ //# sourceMappingURL=valibot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valibot.js","sourceRoot":"","sources":["../src/valibot.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,OAAO,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,OAAO,EAA8B,MAAM,YAAY,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,aAAa,GAId,MAAM,aAAa,CAAC;AAIrB,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAO3F,gEAAgE;AAChE,SAAS,MAAM,CAAC,IAAe,EAAE,KAAiB;IAChD,OAAO;QACL;YACE,IAAI,EAAE,QAAQ;YACd,MAAM,EAAE,OAAO;YACf,KAAK,EAAE,IAA0C;YACjD,GAAG,EAAE,KAAK,CAAC,KAAK;YAChB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;SACzB;KACF,CAAC;AACJ,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,WAAW,CACzB,OAA+B;IAE/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,OAAO,CAAC,IAAI,CACjB,SAAS,EACT,OAAO,CAAC,YAAY,CAAyB,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QAC5E,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,OAAO,MAA8B,CAAC;QACxC,CAAC;QACD,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CACH,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B;IAE/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,OAAO,CAAC,IAAI,CACjB,SAAS,EACT,OAAO,CAAC,YAAY,CAA0B,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,EAAE;QAC7E,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,OAAO,IAAoB,CAAC;QAC9B,CAAC;QACD,QAAQ,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QAClE,OAAO,KAAK,CAAC;IACf,CAAC,CAAC,CACH,CAAC;AACJ,CAAC"}
package/dist/zod.d.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * `@ducatjs/money/zod`: Zod schemas for the JSON form of Money, `{ "amount": "12.34", "currency":
3
+ * "EUR" }`, for API boundaries. Zod is an optional peer dependency: only this entry imports it.
4
+ */
5
+ import { z } from 'zod';
6
+ import { type MoneyJSON } from './money.js';
7
+ import { type MoneyFor, type MoneySchemaOptions } from './schema.js';
8
+ export type { MoneyFor, MoneySchemaOptions } from './schema.js';
9
+ declare const jsonShape: z.ZodObject<{
10
+ amount: z.ZodString;
11
+ currency: z.ZodString;
12
+ }, z.core.$strip>;
13
+ /** The schema `moneySchema` returns: a Zod codec between the JSON form and Money. */
14
+ export type MoneyCodec<C extends string> = z.ZodCodec<typeof jsonShape, z.ZodCustom<MoneyFor<C>>>;
15
+ /**
16
+ * Parses `{ amount, currency }` into Money, and encodes Money back to that JSON form. It is a Zod
17
+ * codec, so `schema.parse(json)` (or `z.decode`) gives Money and `z.encode(schema, money)` gives
18
+ * `{ amount, currency }`, written at the amount's own scale.
19
+ *
20
+ * The amount must be an exact decimal string with no more decimals than the scale (the
21
+ * currency's exponent unless `scale` is given; `rounding` allows more), and at most `maxDigits`
22
+ * digits. The currency must be one of `currency`, or any known code. Shorter amounts are padded to
23
+ * the scale ("1.5" EUR gives 1.50 EUR). Problems are Zod issues on `amount` or `currency`; keys
24
+ * other than those two are dropped. Bad options throw when the schema is built.
25
+ *
26
+ * const Order = z.object({ total: moneySchema({ currency: 'EUR' }) })
27
+ * Order.parse({ total: { amount: '12.34', currency: 'EUR' } }).total // Money<'EUR'>
28
+ */
29
+ export declare function moneySchema<C extends string = string>(options?: MoneySchemaOptions<C>): MoneyCodec<C>;
30
+ /**
31
+ * Like `moneySchema`, but the output stays the JSON object (with only `amount` and `currency`): it
32
+ * checks that the input would parse, for code that passes the JSON on rather than computing with
33
+ * it.
34
+ */
35
+ export declare function moneyJSONSchema<C extends string = string>(options?: MoneySchemaOptions<C>): z.ZodType<MoneyJSON<C>, MoneyJSON>;
36
+ //# sourceMappingURL=zod.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../src/zod.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAuB,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAGL,KAAK,QAAQ,EACb,KAAK,kBAAkB,EACxB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEhE,QAAA,MAAM,SAAS;;;iBAAyD,CAAC;AAEzE,qFAAqF;AACrF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,SAAS,EAAE,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAElG;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACnD,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,UAAU,CAAC,CAAC,CAAC,CAsBf;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EACvD,OAAO,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC9B,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAUpC"}
package/dist/zod.js ADDED
@@ -0,0 +1,58 @@
1
+ /**
2
+ * `@ducatjs/money/zod`: Zod schemas for the JSON form of Money, `{ "amount": "12.34", "currency":
3
+ * "EUR" }`, for API boundaries. Zod is an optional peer dependency: only this entry imports it.
4
+ */
5
+ import { z } from 'zod';
6
+ import { isMoney } from './money.js';
7
+ import { checkSchemaOptions, readMoneyJSON, } from './schema.js';
8
+ const jsonShape = z.object({ amount: z.string(), currency: z.string() });
9
+ /**
10
+ * Parses `{ amount, currency }` into Money, and encodes Money back to that JSON form. It is a Zod
11
+ * codec, so `schema.parse(json)` (or `z.decode`) gives Money and `z.encode(schema, money)` gives
12
+ * `{ amount, currency }`, written at the amount's own scale.
13
+ *
14
+ * The amount must be an exact decimal string with no more decimals than the scale (the
15
+ * currency's exponent unless `scale` is given; `rounding` allows more), and at most `maxDigits`
16
+ * digits. The currency must be one of `currency`, or any known code. Shorter amounts are padded to
17
+ * the scale ("1.5" EUR gives 1.50 EUR). Problems are Zod issues on `amount` or `currency`; keys
18
+ * other than those two are dropped. Bad options throw when the schema is built.
19
+ *
20
+ * const Order = z.object({ total: moneySchema({ currency: 'EUR' }) })
21
+ * Order.parse({ total: { amount: '12.34', currency: 'EUR' } }).total // Money<'EUR'>
22
+ */
23
+ export function moneySchema(options) {
24
+ checkSchemaOptions(options);
25
+ return z.codec(jsonShape, z.custom((value) => isMoney(value)), {
26
+ decode: (json, payload) => {
27
+ const result = readMoneyJSON(json, options);
28
+ if (isMoney(result)) {
29
+ return result;
30
+ }
31
+ payload.issues.push({
32
+ code: 'custom',
33
+ message: result.message,
34
+ input: json,
35
+ path: [result.field],
36
+ });
37
+ return z.NEVER;
38
+ },
39
+ encode: (value) => value.toJSON(),
40
+ });
41
+ }
42
+ /**
43
+ * Like `moneySchema`, but the output stays the JSON object (with only `amount` and `currency`): it
44
+ * checks that the input would parse, for code that passes the JSON on rather than computing with
45
+ * it.
46
+ */
47
+ export function moneyJSONSchema(options) {
48
+ checkSchemaOptions(options);
49
+ return jsonShape.transform((json, context) => {
50
+ const result = readMoneyJSON(json, options);
51
+ if (isMoney(result)) {
52
+ return json;
53
+ }
54
+ context.addIssue({ code: 'custom', message: result.message, path: [result.field] });
55
+ return z.NEVER;
56
+ });
57
+ }
58
+ //# sourceMappingURL=zod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod.js","sourceRoot":"","sources":["../src/zod.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,OAAO,EAA8B,MAAM,YAAY,CAAC;AACjE,OAAO,EACL,kBAAkB,EAClB,aAAa,GAGd,MAAM,aAAa,CAAC;AAIrB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAKzE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,WAAW,CACzB,OAA+B;IAE/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,CAAC,CAAC,KAAK,CACZ,SAAS,EACT,CAAC,CAAC,MAAM,CAAc,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAChD;QACE,MAAM,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;YACxB,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;gBACpB,OAAO,MAA8B,CAAC;YACxC,CAAC;YACD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC;gBAClB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,KAAK,EAAE,IAAI;gBACX,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC;aACrB,CAAC,CAAC;YACH,OAAO,CAAC,CAAC,KAAK,CAAC;QACjB,CAAC;QACD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAE,KAAe,CAAC,MAAM,EAAE;KAC7C,CACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B;IAE/B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,SAAS,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;QAC3C,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACpB,OAAO,IAAoB,CAAC;QAC9B,CAAC;QACD,OAAO,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACpF,OAAO,CAAC,CAAC,KAAK,CAAC;IACjB,CAAC,CAAC,CAAC;AACL,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ducatjs/money",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Exact, fast, typed Money for TypeScript: integer minor units, bigint fallback, Stripe/Adyen views, allocation, cash rounding, Prisma helpers.",
5
5
  "homepage": "https://github.com/public/ducat#readme",
6
6
  "bugs": {
@@ -23,7 +23,11 @@
23
23
  "type": "module",
24
24
  "sideEffects": [
25
25
  "./dist/plugins/*.js",
26
- "./src/plugins/*.ts"
26
+ "./src/plugins/*.ts",
27
+ "./dist/testing/vitest.js",
28
+ "./dist/testing/jest.js",
29
+ "./src/testing/vitest.ts",
30
+ "./src/testing/jest.ts"
27
31
  ],
28
32
  "exports": {
29
33
  ".": {
@@ -50,6 +54,30 @@
50
54
  "types": "./dist/ecb.d.ts",
51
55
  "default": "./dist/ecb.js"
52
56
  },
57
+ "./convert/history": {
58
+ "types": "./dist/history.d.ts",
59
+ "default": "./dist/history.js"
60
+ },
61
+ "./convert/boc": {
62
+ "types": "./dist/boc.d.ts",
63
+ "default": "./dist/boc.js"
64
+ },
65
+ "./convert/fed": {
66
+ "types": "./dist/fed.d.ts",
67
+ "default": "./dist/fed.js"
68
+ },
69
+ "./historic": {
70
+ "types": "./dist/historic.d.ts",
71
+ "default": "./dist/historic.js"
72
+ },
73
+ "./convert/boe": {
74
+ "types": "./dist/boe.d.ts",
75
+ "default": "./dist/boe.js"
76
+ },
77
+ "./convert/hmrc": {
78
+ "types": "./dist/hmrc.d.ts",
79
+ "default": "./dist/hmrc.js"
80
+ },
53
81
  "./convert/fetch": {
54
82
  "types": "./dist/fetch.d.ts",
55
83
  "default": "./dist/fetch.js"
@@ -58,6 +86,10 @@
58
86
  "types": "./dist/plugins/exact.d.ts",
59
87
  "default": "./dist/plugins/exact.js"
60
88
  },
89
+ "./policy": {
90
+ "types": "./dist/policy.d.ts",
91
+ "default": "./dist/policy.js"
92
+ },
61
93
  "./cash": {
62
94
  "types": "./dist/plugins/cash.d.ts",
63
95
  "default": "./dist/plugins/cash.js"
@@ -110,6 +142,50 @@
110
142
  "types": "./dist/plugins/prisma.d.ts",
111
143
  "default": "./dist/plugins/prisma.js"
112
144
  },
145
+ "./drizzle/pg": {
146
+ "types": "./dist/drizzle/pg.d.ts",
147
+ "default": "./dist/drizzle/pg.js"
148
+ },
149
+ "./drizzle/mysql": {
150
+ "types": "./dist/drizzle/mysql.d.ts",
151
+ "default": "./dist/drizzle/mysql.js"
152
+ },
153
+ "./drizzle/sqlite": {
154
+ "types": "./dist/drizzle/sqlite.d.ts",
155
+ "default": "./dist/drizzle/sqlite.js"
156
+ },
157
+ "./zod": {
158
+ "types": "./dist/zod.d.ts",
159
+ "default": "./dist/zod.js"
160
+ },
161
+ "./valibot": {
162
+ "types": "./dist/valibot.d.ts",
163
+ "default": "./dist/valibot.js"
164
+ },
165
+ "./pg": {
166
+ "types": "./dist/pg.d.ts",
167
+ "default": "./dist/pg.js"
168
+ },
169
+ "./testing": {
170
+ "types": "./dist/testing/index.d.ts",
171
+ "default": "./dist/testing/index.js"
172
+ },
173
+ "./testing/vitest": {
174
+ "types": "./dist/testing/vitest.d.ts",
175
+ "default": "./dist/testing/vitest.js"
176
+ },
177
+ "./testing/jest": {
178
+ "types": "./dist/testing/jest.d.ts",
179
+ "default": "./dist/testing/jest.js"
180
+ },
181
+ "./testing/arbitraries": {
182
+ "types": "./dist/testing/arbitraries.d.ts",
183
+ "default": "./dist/testing/arbitraries.js"
184
+ },
185
+ "./kysely": {
186
+ "types": "./dist/kysely.d.ts",
187
+ "default": "./dist/kysely.js"
188
+ },
113
189
  "./all": {
114
190
  "types": "./dist/plugins/all.d.ts",
115
191
  "default": "./dist/plugins/all.js"
@@ -117,8 +193,8 @@
117
193
  "./package.json": "./package.json"
118
194
  },
119
195
  "scripts": {
120
- "build": "tsc -p tsconfig.json",
121
- "typecheck": "tsc -p tsconfig.test.json && tsc -p test/core-only/tsconfig.json && tsc -p test/browser/tsconfig.json",
196
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.drizzle.json && tsc -p tsconfig.kysely.json && tsc -p tsconfig.peers.json",
197
+ "typecheck": "tsc -p tsconfig.test.json && tsc -p tsconfig.test-drizzle.json && tsc -p test/core-only/tsconfig.json && tsc -p test/browser/tsconfig.json && tsc -p test/jest-types/tsconfig.json",
122
198
  "test": "vitest run",
123
199
  "test:deep": "DUCAT_DEEP=1 vitest run",
124
200
  "compare": "pnpm --dir compare battery && pnpm --dir compare tables",
@@ -149,22 +225,57 @@
149
225
  "@prisma/client": "^6.19.3",
150
226
  "@stryker-mutator/core": "10.1.1",
151
227
  "@stryker-mutator/vitest-runner": "10.1.1",
228
+ "@types/better-sqlite3": "9.6.0",
152
229
  "@types/big.js": "^7.0.0",
153
230
  "@types/node": "^26.6.6",
154
231
  "@types/pg": "8.23.1",
232
+ "better-sqlite3": "13.0.3",
155
233
  "big.js": "^7.0.1",
156
234
  "currency.js": "^2.0.4",
157
235
  "decimal.js": "^10.6.0",
158
236
  "dinero.js": "^2.0.2",
237
+ "drizzle-orm": "0.45.4",
159
238
  "esbuild": "^0.28.2",
160
239
  "fast-check": "^4.10.2",
240
+ "kysely": "0.29.8",
161
241
  "mysql2": "3.24.6",
162
242
  "oxfmt": "0.77.0",
163
243
  "oxlint": "1.92.0",
164
244
  "pg": "8.23.1",
245
+ "pg-cursor": "2.22.1",
165
246
  "ts-money": "^0.4.8",
166
247
  "typescript": "^5.9.3",
167
- "vitest": "^4.1.12"
248
+ "valibot": "1.5.0",
249
+ "vitest": "^4.1.12",
250
+ "zod": "4.6.7"
251
+ },
252
+ "peerDependencies": {
253
+ "drizzle-orm": ">=0.45.0 <1",
254
+ "fast-check": "^4.0.0",
255
+ "kysely": ">=0.28.0 <1",
256
+ "valibot": "^1.0.0",
257
+ "vitest": ">=3.2.0 <5",
258
+ "zod": "^4.0.0"
259
+ },
260
+ "peerDependenciesMeta": {
261
+ "drizzle-orm": {
262
+ "optional": true
263
+ },
264
+ "fast-check": {
265
+ "optional": true
266
+ },
267
+ "kysely": {
268
+ "optional": true
269
+ },
270
+ "valibot": {
271
+ "optional": true
272
+ },
273
+ "vitest": {
274
+ "optional": true
275
+ },
276
+ "zod": {
277
+ "optional": true
278
+ }
168
279
  },
169
280
  "engines": {
170
281
  "node": ">=24"