@satorio/machinery 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.
Files changed (341) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dictionary/all.json +4656 -0
  4. package/dictionary/all.toon +639 -0
  5. package/dictionary/core.json +221 -0
  6. package/dictionary/core.toon +33 -0
  7. package/dictionary/graphql.json +212 -0
  8. package/dictionary/graphql.toon +32 -0
  9. package/dictionary/http.json +113 -0
  10. package/dictionary/http.toon +21 -0
  11. package/dictionary/identifiers.json +266 -0
  12. package/dictionary/identifiers.toon +38 -0
  13. package/dictionary/index.json +96 -0
  14. package/dictionary/index.toon +18 -0
  15. package/dictionary/integration.json +131 -0
  16. package/dictionary/integration.toon +23 -0
  17. package/dictionary/money.json +383 -0
  18. package/dictionary/money.toon +51 -0
  19. package/dictionary/ports.json +392 -0
  20. package/dictionary/ports.toon +52 -0
  21. package/dictionary/postgres.json +473 -0
  22. package/dictionary/postgres.toon +61 -0
  23. package/dictionary/publishing.json +131 -0
  24. package/dictionary/publishing.toon +23 -0
  25. package/dictionary/queue.json +365 -0
  26. package/dictionary/queue.toon +49 -0
  27. package/dictionary/reference/api.mdx +843 -0
  28. package/dictionary/reference/uk/api.mdx +844 -0
  29. package/dictionary/storage.json +194 -0
  30. package/dictionary/storage.toon +30 -0
  31. package/dictionary/testing.json +707 -0
  32. package/dictionary/testing.toon +87 -0
  33. package/dictionary/time.json +311 -0
  34. package/dictionary/time.toon +43 -0
  35. package/dictionary/vitest.json +356 -0
  36. package/dictionary/vitest.toon +48 -0
  37. package/dictionary/workflow.json +185 -0
  38. package/dictionary/workflow.toon +29 -0
  39. package/dist/bin/command.d.ts +45 -0
  40. package/dist/bin/command.js +113 -0
  41. package/dist/bin.d.ts +2 -0
  42. package/dist/bin.js +11 -0
  43. package/dist/core/bigint-json.d.ts +6 -0
  44. package/dist/core/bigint-json.js +6 -0
  45. package/dist/core/closed-set.d.ts +14 -0
  46. package/dist/core/closed-set.js +8 -0
  47. package/dist/core/domain-error.d.ts +8 -0
  48. package/dist/core/domain-error.js +11 -0
  49. package/dist/core/environment.d.ts +124 -0
  50. package/dist/core/environment.js +115 -0
  51. package/dist/core/index.d.ts +13 -0
  52. package/dist/core/index.js +13 -0
  53. package/dist/core/ulid.d.ts +27 -0
  54. package/dist/core/ulid.js +27 -0
  55. package/dist/graphql/builder.d.ts +36 -0
  56. package/dist/graphql/builder.js +35 -0
  57. package/dist/graphql/context.d.ts +36 -0
  58. package/dist/graphql/context.js +69 -0
  59. package/dist/graphql/failure-classification.d.ts +11 -0
  60. package/dist/graphql/failure-classification.js +1 -0
  61. package/dist/graphql/failures.d.ts +62 -0
  62. package/dist/graphql/failures.js +30 -0
  63. package/dist/graphql/graphiql.d.ts +45 -0
  64. package/dist/graphql/graphiql.js +56 -0
  65. package/dist/graphql/index.d.ts +15 -0
  66. package/dist/graphql/index.js +14 -0
  67. package/dist/graphql/layer.d.ts +15 -0
  68. package/dist/graphql/layer.js +23 -0
  69. package/dist/graphql/request-handler.d.ts +26 -0
  70. package/dist/graphql/request-handler.js +33 -0
  71. package/dist/graphql/server.d.ts +19 -0
  72. package/dist/graphql/server.js +16 -0
  73. package/dist/graphql/settings.d.ts +55 -0
  74. package/dist/graphql/settings.js +82 -0
  75. package/dist/http/health.d.ts +70 -0
  76. package/dist/http/health.js +60 -0
  77. package/dist/http/index.d.ts +5 -0
  78. package/dist/http/index.js +5 -0
  79. package/dist/identifiers/client-code.d.ts +37 -0
  80. package/dist/identifiers/client-code.js +41 -0
  81. package/dist/identifiers/creditor-reference.d.ts +38 -0
  82. package/dist/identifiers/creditor-reference.js +32 -0
  83. package/dist/identifiers/damm.d.ts +22 -0
  84. package/dist/identifiers/damm.js +50 -0
  85. package/dist/identifiers/document-number.d.ts +68 -0
  86. package/dist/identifiers/document-number.js +73 -0
  87. package/dist/identifiers/document-numbers.d.ts +27 -0
  88. package/dist/identifiers/document-numbers.js +16 -0
  89. package/dist/identifiers/failures.d.ts +46 -0
  90. package/dist/identifiers/failures.js +48 -0
  91. package/dist/identifiers/index.d.ts +14 -0
  92. package/dist/identifiers/index.js +13 -0
  93. package/dist/identifiers/next-document-number.d.ts +26 -0
  94. package/dist/identifiers/next-document-number.js +31 -0
  95. package/dist/identifiers/ulid.d.ts +28 -0
  96. package/dist/identifiers/ulid.js +71 -0
  97. package/dist/integration/external-http-client.d.ts +22 -0
  98. package/dist/integration/external-http-client.js +74 -0
  99. package/dist/integration/index.d.ts +12 -0
  100. package/dist/integration/index.js +12 -0
  101. package/dist/integration/layer.d.ts +19 -0
  102. package/dist/integration/layer.js +30 -0
  103. package/dist/integration/retry.d.ts +65 -0
  104. package/dist/integration/retry.js +54 -0
  105. package/dist/integration/settings.d.ts +73 -0
  106. package/dist/integration/settings.js +92 -0
  107. package/dist/money/amount.d.ts +60 -0
  108. package/dist/money/amount.js +67 -0
  109. package/dist/money/arithmetic.d.ts +60 -0
  110. package/dist/money/arithmetic.js +73 -0
  111. package/dist/money/currency.d.ts +25 -0
  112. package/dist/money/currency.js +24 -0
  113. package/dist/money/decimal.d.ts +29 -0
  114. package/dist/money/decimal.js +39 -0
  115. package/dist/money/exchange-rate.d.ts +32 -0
  116. package/dist/money/exchange-rate.js +28 -0
  117. package/dist/money/failures.d.ts +86 -0
  118. package/dist/money/failures.js +97 -0
  119. package/dist/money/index.d.ts +15 -0
  120. package/dist/money/index.js +15 -0
  121. package/dist/money/percent.d.ts +35 -0
  122. package/dist/money/percent.js +50 -0
  123. package/dist/money/schema.d.ts +25 -0
  124. package/dist/money/schema.js +41 -0
  125. package/dist/ports/blob-store.d.ts +42 -0
  126. package/dist/ports/blob-store.js +13 -0
  127. package/dist/ports/document-number-counter.d.ts +36 -0
  128. package/dist/ports/document-number-counter.js +12 -0
  129. package/dist/ports/event-publisher.d.ts +29 -0
  130. package/dist/ports/event-publisher.js +13 -0
  131. package/dist/ports/external-http-client.d.ts +60 -0
  132. package/dist/ports/external-http-client.js +25 -0
  133. package/dist/ports/index.d.ts +17 -0
  134. package/dist/ports/index.js +17 -0
  135. package/dist/ports/integration-event-outbox.d.ts +44 -0
  136. package/dist/ports/integration-event-outbox.js +13 -0
  137. package/dist/ports/port-unavailable.d.ts +21 -0
  138. package/dist/ports/port-unavailable.js +30 -0
  139. package/dist/ports/processed-event-registry.d.ts +57 -0
  140. package/dist/ports/processed-event-registry.js +24 -0
  141. package/dist/ports/queue-message-producer.d.ts +26 -0
  142. package/dist/ports/queue-message-producer.js +12 -0
  143. package/dist/ports/queue-message-source.d.ts +39 -0
  144. package/dist/ports/queue-message-source.js +13 -0
  145. package/dist/ports/readiness-probe.d.ts +27 -0
  146. package/dist/ports/readiness-probe.js +13 -0
  147. package/dist/ports/transaction-boundary.d.ts +28 -0
  148. package/dist/ports/transaction-boundary.js +13 -0
  149. package/dist/postgres/client.d.ts +51 -0
  150. package/dist/postgres/client.js +81 -0
  151. package/dist/postgres/document-number-counter.d.ts +14 -0
  152. package/dist/postgres/document-number-counter.js +27 -0
  153. package/dist/postgres/failures.d.ts +28 -0
  154. package/dist/postgres/failures.js +18 -0
  155. package/dist/postgres/index.d.ts +18 -0
  156. package/dist/postgres/index.js +18 -0
  157. package/dist/postgres/integration-event-outbox.d.ts +15 -0
  158. package/dist/postgres/integration-event-outbox.js +60 -0
  159. package/dist/postgres/jsonb-value.d.ts +10 -0
  160. package/dist/postgres/jsonb-value.js +9 -0
  161. package/dist/postgres/layer.d.ts +57 -0
  162. package/dist/postgres/layer.js +72 -0
  163. package/dist/postgres/migrations/advisory-lock.d.ts +47 -0
  164. package/dist/postgres/migrations/advisory-lock.js +59 -0
  165. package/dist/postgres/migrations/runner.d.ts +56 -0
  166. package/dist/postgres/migrations/runner.js +69 -0
  167. package/dist/postgres/migrations/sql-file-loader.d.ts +59 -0
  168. package/dist/postgres/migrations/sql-file-loader.js +99 -0
  169. package/dist/postgres/processed-event-registry.d.ts +13 -0
  170. package/dist/postgres/processed-event-registry.js +44 -0
  171. package/dist/postgres/readiness-probe.d.ts +27 -0
  172. package/dist/postgres/readiness-probe.js +33 -0
  173. package/dist/postgres/settings.d.ts +134 -0
  174. package/dist/postgres/settings.js +210 -0
  175. package/dist/postgres/sql/0001_processed_event.sql +4 -0
  176. package/dist/postgres/sql/0002_integration_event_outbox.sql +13 -0
  177. package/dist/postgres/sql/0003_document_number_counter.sql +6 -0
  178. package/dist/postgres/sql-failure.d.ts +19 -0
  179. package/dist/postgres/sql-failure.js +22 -0
  180. package/dist/postgres/store-failure.d.ts +17 -0
  181. package/dist/postgres/store-failure.js +16 -0
  182. package/dist/postgres/transaction-boundary.d.ts +13 -0
  183. package/dist/postgres/transaction-boundary.js +21 -0
  184. package/dist/publishing/event-publisher.sns.d.ts +66 -0
  185. package/dist/publishing/event-publisher.sns.js +75 -0
  186. package/dist/publishing/event-publisher.stdout.d.ts +15 -0
  187. package/dist/publishing/event-publisher.stdout.js +24 -0
  188. package/dist/publishing/index.d.ts +10 -0
  189. package/dist/publishing/index.js +10 -0
  190. package/dist/publishing/layer.d.ts +25 -0
  191. package/dist/publishing/layer.js +39 -0
  192. package/dist/publishing/settings.d.ts +60 -0
  193. package/dist/publishing/settings.js +85 -0
  194. package/dist/queue/consumer.d.ts +91 -0
  195. package/dist/queue/consumer.js +92 -0
  196. package/dist/queue/envelope.d.ts +39 -0
  197. package/dist/queue/envelope.js +39 -0
  198. package/dist/queue/failures.d.ts +33 -0
  199. package/dist/queue/failures.js +15 -0
  200. package/dist/queue/index.d.ts +14 -0
  201. package/dist/queue/index.js +14 -0
  202. package/dist/queue/layer.d.ts +33 -0
  203. package/dist/queue/layer.js +58 -0
  204. package/dist/queue/message-codec.d.ts +43 -0
  205. package/dist/queue/message-codec.js +33 -0
  206. package/dist/queue/queue-message-producer.d.ts +61 -0
  207. package/dist/queue/queue-message-producer.js +52 -0
  208. package/dist/queue/queue-message-source.d.ts +93 -0
  209. package/dist/queue/queue-message-source.js +86 -0
  210. package/dist/queue/readiness.d.ts +78 -0
  211. package/dist/queue/readiness.js +86 -0
  212. package/dist/queue/settings.d.ts +106 -0
  213. package/dist/queue/settings.js +160 -0
  214. package/dist/storage/blob-store.d.ts +84 -0
  215. package/dist/storage/blob-store.js +109 -0
  216. package/dist/storage/index.d.ts +9 -0
  217. package/dist/storage/index.js +9 -0
  218. package/dist/storage/layer.d.ts +17 -0
  219. package/dist/storage/layer.js +30 -0
  220. package/dist/storage/settings.d.ts +48 -0
  221. package/dist/storage/settings.js +65 -0
  222. package/dist/testing/blob-store.d.ts +27 -0
  223. package/dist/testing/blob-store.js +45 -0
  224. package/dist/testing/clock.d.ts +29 -0
  225. package/dist/testing/clock.js +41 -0
  226. package/dist/testing/dna-perturbator/attention.d.ts +55 -0
  227. package/dist/testing/dna-perturbator/attention.js +49 -0
  228. package/dist/testing/dna-perturbator/chains.d.ts +31 -0
  229. package/dist/testing/dna-perturbator/chains.js +53 -0
  230. package/dist/testing/dna-perturbator/report.d.ts +12 -0
  231. package/dist/testing/dna-perturbator/report.js +22 -0
  232. package/dist/testing/dna-perturbator/verdict.d.ts +26 -0
  233. package/dist/testing/dna-perturbator/verdict.js +28 -0
  234. package/dist/testing/dna-perturbator.d.ts +48 -0
  235. package/dist/testing/dna-perturbator.js +145 -0
  236. package/dist/testing/document-number-counter.d.ts +29 -0
  237. package/dist/testing/document-number-counter.js +39 -0
  238. package/dist/testing/event-publisher.d.ts +28 -0
  239. package/dist/testing/event-publisher.js +33 -0
  240. package/dist/testing/external-http-client.d.ts +29 -0
  241. package/dist/testing/external-http-client.js +49 -0
  242. package/dist/testing/index.d.ts +24 -0
  243. package/dist/testing/index.js +24 -0
  244. package/dist/testing/integration-event-outbox.d.ts +37 -0
  245. package/dist/testing/integration-event-outbox.js +67 -0
  246. package/dist/testing/mutation-settings.d.ts +71 -0
  247. package/dist/testing/mutation-settings.js +57 -0
  248. package/dist/testing/postgres-rows.d.ts +12 -0
  249. package/dist/testing/postgres-rows.js +19 -0
  250. package/dist/testing/processed-event-registry.d.ts +25 -0
  251. package/dist/testing/processed-event-registry.js +39 -0
  252. package/dist/testing/queue-message-producer.d.ts +27 -0
  253. package/dist/testing/queue-message-producer.js +41 -0
  254. package/dist/testing/queue-message-source.d.ts +54 -0
  255. package/dist/testing/queue-message-source.js +71 -0
  256. package/dist/testing/quiet-reporter.d.ts +86 -0
  257. package/dist/testing/quiet-reporter.js +81 -0
  258. package/dist/testing/readiness-probe.d.ts +27 -0
  259. package/dist/testing/readiness-probe.js +33 -0
  260. package/dist/testing/scenario.d.ts +94 -0
  261. package/dist/testing/scenario.js +81 -0
  262. package/dist/testing/transaction-boundary.d.ts +30 -0
  263. package/dist/testing/transaction-boundary.js +36 -0
  264. package/dist/testing/typed-failure.d.ts +8 -0
  265. package/dist/testing/typed-failure.js +15 -0
  266. package/dist/time/calendar-day.d.ts +83 -0
  267. package/dist/time/calendar-day.js +112 -0
  268. package/dist/time/failures.d.ts +51 -0
  269. package/dist/time/failures.js +56 -0
  270. package/dist/time/index.d.ts +11 -0
  271. package/dist/time/index.js +11 -0
  272. package/dist/time/instant.d.ts +64 -0
  273. package/dist/time/instant.js +67 -0
  274. package/dist/time/period.d.ts +45 -0
  275. package/dist/time/period.js +55 -0
  276. package/dist/vitest/container-reuse.d.ts +14 -0
  277. package/dist/vitest/container-reuse.js +15 -0
  278. package/dist/vitest/dotenv.d.ts +16 -0
  279. package/dist/vitest/dotenv.js +48 -0
  280. package/dist/vitest/effect-single-instance.d.ts +36 -0
  281. package/dist/vitest/effect-single-instance.js +125 -0
  282. package/dist/vitest/fast-check-single-instance.d.ts +11 -0
  283. package/dist/vitest/fast-check-single-instance.js +26 -0
  284. package/dist/vitest/index.d.ts +31 -0
  285. package/dist/vitest/index.js +30 -0
  286. package/dist/vitest/localstack-container.d.ts +32 -0
  287. package/dist/vitest/localstack-container.js +55 -0
  288. package/dist/vitest/port-contract-tester.d.ts +8 -0
  289. package/dist/vitest/port-contract-tester.js +1 -0
  290. package/dist/vitest/port-contracts/blob-store.contract.d.ts +22 -0
  291. package/dist/vitest/port-contracts/blob-store.contract.js +108 -0
  292. package/dist/vitest/port-contracts/document-number-counter.contract.d.ts +18 -0
  293. package/dist/vitest/port-contracts/document-number-counter.contract.js +90 -0
  294. package/dist/vitest/port-contracts/event-publisher.contract.d.ts +35 -0
  295. package/dist/vitest/port-contracts/event-publisher.contract.js +67 -0
  296. package/dist/vitest/port-contracts/external-http-client.contract.d.ts +35 -0
  297. package/dist/vitest/port-contracts/external-http-client.contract.js +52 -0
  298. package/dist/vitest/port-contracts/integration-event-outbox.contract.d.ts +23 -0
  299. package/dist/vitest/port-contracts/integration-event-outbox.contract.js +75 -0
  300. package/dist/vitest/port-contracts/processed-event-registry.contract.d.ts +18 -0
  301. package/dist/vitest/port-contracts/processed-event-registry.contract.js +84 -0
  302. package/dist/vitest/port-contracts/queue-message-producer.contract.d.ts +26 -0
  303. package/dist/vitest/port-contracts/queue-message-producer.contract.js +48 -0
  304. package/dist/vitest/port-contracts/queue-message-source.contract.d.ts +29 -0
  305. package/dist/vitest/port-contracts/queue-message-source.contract.js +71 -0
  306. package/dist/vitest/port-contracts/readiness-probe.contract.d.ts +18 -0
  307. package/dist/vitest/port-contracts/readiness-probe.contract.js +41 -0
  308. package/dist/vitest/port-contracts/transaction-boundary.contract.d.ts +21 -0
  309. package/dist/vitest/port-contracts/transaction-boundary.contract.js +64 -0
  310. package/dist/vitest/postgres-container.d.ts +57 -0
  311. package/dist/vitest/postgres-container.js +118 -0
  312. package/dist/vitest/reader-port-contract.d.ts +41 -0
  313. package/dist/vitest/reader-port-contract.js +43 -0
  314. package/dist/vitest/repo-source-files.d.ts +37 -0
  315. package/dist/vitest/repo-source-files.js +61 -0
  316. package/dist/vitest/reset-tables.d.ts +18 -0
  317. package/dist/vitest/reset-tables.js +30 -0
  318. package/dist/vitest/settings-contract.d.ts +38 -0
  319. package/dist/vitest/settings-contract.js +77 -0
  320. package/dist/vitest/stub-server.d.ts +43 -0
  321. package/dist/vitest/stub-server.js +60 -0
  322. package/dist/vitest/term-registry.json +730 -0
  323. package/dist/vitest/testclock-fork-isolated-files.d.ts +13 -0
  324. package/dist/vitest/testclock-fork-isolated-files.js +18 -0
  325. package/dist/vitest/testclock-fork-isolation.d.ts +77 -0
  326. package/dist/vitest/testclock-fork-isolation.js +188 -0
  327. package/dist/workflow/claim-once.d.ts +16 -0
  328. package/dist/workflow/claim-once.js +9 -0
  329. package/dist/workflow/failures.d.ts +47 -0
  330. package/dist/workflow/failures.js +22 -0
  331. package/dist/workflow/index.d.ts +12 -0
  332. package/dist/workflow/index.js +12 -0
  333. package/dist/workflow/on-claim.d.ts +17 -0
  334. package/dist/workflow/on-claim.js +8 -0
  335. package/dist/workflow/publish-pending-events.d.ts +31 -0
  336. package/dist/workflow/publish-pending-events.js +31 -0
  337. package/dist/workflow/transactionally.d.ts +8 -0
  338. package/dist/workflow/transactionally.js +8 -0
  339. package/dist/workflow/versioned-payload.d.ts +35 -0
  340. package/dist/workflow/versioned-payload.js +43 -0
  341. package/package.json +309 -0
@@ -0,0 +1,60 @@
1
+ import { type Dinero } from 'dinero.js/bigint';
2
+ import { Either } from 'effect';
3
+ import type { Currency } from './currency.js';
4
+ import { NotAnAmountError } from './failures.js';
5
+ /**
6
+ * An exact amount of one currency. The value is the money library's own
7
+ * immutable amount, so every function that library ships works on it
8
+ * unchanged, and this mechanism adds only what the library leaves to the
9
+ * business: which currency codes exist, how a rounding is decided, and
10
+ * what an amount looks like on the wire.
11
+ */
12
+ export type Money = Dinero<bigint>;
13
+ /**
14
+ * An amount counted in the currency's own smallest unit: 4055 minor
15
+ * units of a currency counted in cents is 40.55.
16
+ */
17
+ export declare const money: (currency: Currency, minorUnits: bigint) => Money;
18
+ /**
19
+ * An amount held at a scale finer than the currency's own, so a calculation
20
+ * drops no digit.
21
+ *
22
+ * An amount at a scale finer than the currency's own, for a figure a
23
+ * calculation still holds in full: 400455 units at a scale of four is 40.0455.
24
+ * Nothing rounds until you say so.
25
+ */
26
+ export declare const moneyAtScale: (currency: Currency, units: bigint, scale: bigint) => Money;
27
+ /**
28
+ * Nothing at all, in one currency. This is the empty value a sum folds
29
+ * from, so a sum of no amounts still says which currency it is in.
30
+ */
31
+ export declare const zeroMoney: (currency: Currency) => Money;
32
+ /**
33
+ * The currency an amount is counted in.
34
+ */
35
+ export declare const currencyOfMoney: (amount: Money) => Currency;
36
+ /**
37
+ * The scale an amount is currently held at: how many decimals its own
38
+ * units are counted in, which grows as a calculation keeps precision.
39
+ */
40
+ export declare const scaleOfMoney: (amount: Money) => bigint;
41
+ /**
42
+ * Reads an amount somebody wrote: `40.0455`, `-25`, `0.10`. Every
43
+ * decimal written is kept, so the reader never drops a digit and never
44
+ * has to guess how fine the sender meant to be. A text of any other
45
+ * shape comes back as a refusal.
46
+ */
47
+ export declare const moneyFromDecimalForm: (currency: Currency, text: string) => Either.Either<Money, NotAnAmountError>;
48
+ /**
49
+ * Writes an amount for the wire and for a person: `40.0455`, `-25.00`.
50
+ * It never writes fewer decimals than the currency itself counts in, so
51
+ * a whole number of a currency counted in cents still reads `25.00`.
52
+ */
53
+ export declare const moneyInDecimalForm: (amount: Money) => string;
54
+ /**
55
+ * The amount counted in the currency's own smallest unit, rounded to it
56
+ * by the one policy this mechanism keeps. This is the number a payment
57
+ * request carries, and the point where a fraction of a cent stops
58
+ * existing.
59
+ */
60
+ export declare const minorUnitsOf: (amount: Money) => bigint;
@@ -0,0 +1,67 @@
1
+ import { dinero, halfAwayFromZero, toDecimal, toSnapshot, transformScale, } from 'dinero.js/bigint';
2
+ import { Either } from 'effect';
3
+ import { exactDecimalOf } from './decimal.js';
4
+ import { NotAnAmountError } from './failures.js';
5
+ /**
6
+ * An amount counted in the currency's own smallest unit: 4055 minor
7
+ * units of a currency counted in cents is 40.55.
8
+ */
9
+ export const money = (currency, minorUnits) => dinero({ amount: minorUnits, currency });
10
+ /**
11
+ * An amount held at a scale finer than the currency's own, so a calculation
12
+ * drops no digit.
13
+ *
14
+ * An amount at a scale finer than the currency's own, for a figure a
15
+ * calculation still holds in full: 400455 units at a scale of four is 40.0455.
16
+ * Nothing rounds until you say so.
17
+ */
18
+ export const moneyAtScale = (currency, units, scale) => dinero({ amount: units, currency, scale });
19
+ /**
20
+ * Nothing at all, in one currency. This is the empty value a sum folds
21
+ * from, so a sum of no amounts still says which currency it is in.
22
+ */
23
+ export const zeroMoney = (currency) => money(currency, 0n);
24
+ /**
25
+ * The currency an amount is counted in.
26
+ */
27
+ export const currencyOfMoney = (amount) => toSnapshot(amount).currency;
28
+ /**
29
+ * The scale an amount is currently held at: how many decimals its own
30
+ * units are counted in, which grows as a calculation keeps precision.
31
+ */
32
+ export const scaleOfMoney = (amount) => toSnapshot(amount).scale;
33
+ /**
34
+ * Reads an amount somebody wrote: `40.0455`, `-25`, `0.10`. Every
35
+ * decimal written is kept, so the reader never drops a digit and never
36
+ * has to guess how fine the sender meant to be. A text of any other
37
+ * shape comes back as a refusal.
38
+ */
39
+ export const moneyFromDecimalForm = (currency, text) => {
40
+ const read = exactDecimalOf(text);
41
+ return read === null
42
+ ? Either.left(new NotAnAmountError({ text }))
43
+ : Either.right(moneyAtScale(currency, read.units, read.scale));
44
+ };
45
+ const atLeastTheCurrencyScale = (amount) => {
46
+ const snapshot = toSnapshot(amount);
47
+ const currencyScale = snapshot.currency.exponent;
48
+ return snapshot.scale >= currencyScale
49
+ ? amount
50
+ : transformScale(amount, currencyScale, halfAwayFromZero);
51
+ };
52
+ /**
53
+ * Writes an amount for the wire and for a person: `40.0455`, `-25.00`.
54
+ * It never writes fewer decimals than the currency itself counts in, so
55
+ * a whole number of a currency counted in cents still reads `25.00`.
56
+ */
57
+ export const moneyInDecimalForm = (amount) => toDecimal(atLeastTheCurrencyScale(amount));
58
+ /**
59
+ * The amount counted in the currency's own smallest unit, rounded to it
60
+ * by the one policy this mechanism keeps. This is the number a payment
61
+ * request carries, and the point where a fraction of a cent stops
62
+ * existing.
63
+ */
64
+ export const minorUnitsOf = (amount) => {
65
+ const toTheCent = transformScale(amount, currencyOfMoney(amount).exponent, halfAwayFromZero);
66
+ return toSnapshot(toTheCent).amount;
67
+ };
@@ -0,0 +1,60 @@
1
+ import { Either } from 'effect';
2
+ import { type Money } from './amount.js';
3
+ import type { Currency } from './currency.js';
4
+ import { CurrenciesDoNotMeetError, ProportionsDoNotSplitError } from './failures.js';
5
+ import type { Percent } from './percent.js';
6
+ /**
7
+ * Rounds an amount to the currency's own smallest unit, half away from
8
+ * zero. This is the one rounding policy this package keeps: a half cent
9
+ * goes up when the amount is positive and down when it is negative, so a
10
+ * credit note mirrors the invoice it reverses exactly. Round once, at
11
+ * the end of a calculation — a figure rounded twice through a finer
12
+ * scale moves by a cent nobody can account for.
13
+ */
14
+ export declare const roundedToTheCent: (amount: Money) => Money;
15
+ /**
16
+ * Adds two amounts of one currency. Two currencies come back as a
17
+ * refusal rather than a throw, so the caller reads the failure in the
18
+ * same channel every other value in this package travels.
19
+ */
20
+ export declare const addMoney: (left: Money, right: Money) => Either.Either<Money, CurrenciesDoNotMeetError>;
21
+ /**
22
+ * Adds up any number of amounts, starting from nothing in the currency
23
+ * you name. Nothing plus an amount is that amount, and the order the
24
+ * amounts arrive in never changes the total: those two laws are what
25
+ * makes a journal safe to fold in any order, and a property test holds
26
+ * them.
27
+ */
28
+ export declare const sumOfMoney: (currency: Currency, amounts: readonly Money[]) => Either.Either<Money, CurrenciesDoNotMeetError>;
29
+ /**
30
+ * Turns an amount around: what was owed becomes what is owed back.
31
+ */
32
+ export declare const negatedMoney: (amount: Money) => Money;
33
+ /**
34
+ * Puts two amounts of one currency in order.
35
+ *
36
+ * Puts two amounts of one currency in order: below zero when the first stands
37
+ * lower, zero when they are the same amount, above zero when the first stands
38
+ * higher. Two currencies come back as a refusal.
39
+ */
40
+ export declare const compareMoney: (left: Money, right: Money) => Either.Either<number, CurrenciesDoNotMeetError>;
41
+ /**
42
+ * Whether two amounts are the same amount. Amounts in two currencies are
43
+ * simply not the same amount, which is an answer rather than a refusal.
44
+ */
45
+ export declare const sameMoney: (left: Money, right: Money) => boolean;
46
+ /**
47
+ * Takes a share of an amount and keeps every digit of the product. It
48
+ * rounds nothing on purpose: pass the result through `roundedToTheCent`
49
+ * once, when the calculation is over, so no figure is rounded twice.
50
+ */
51
+ export declare const percentOf: (amount: Money, percent: Percent) => Money;
52
+ /**
53
+ * Splits an amount into shares that add back up to exactly what went in.
54
+ *
55
+ * Splits an amount into shares by proportion, and the shares add back up to
56
+ * exactly what went in — the remainder is handed out one smallest unit at a
57
+ * time rather than left behind. Proportions that cannot split anything come
58
+ * back as a refusal.
59
+ */
60
+ export declare const sharesOf: (amount: Money, proportions: readonly bigint[]) => Either.Either<readonly Money[], ProportionsDoNotSplitError>;
@@ -0,0 +1,73 @@
1
+ import { add, allocate, compare, equal, halfAwayFromZero, haveSameCurrency, multiply, transformScale, } from 'dinero.js/bigint';
2
+ import { Either } from 'effect';
3
+ import { currencyOfMoney, zeroMoney } from './amount.js';
4
+ import { currencyCodeOf } from './currency.js';
5
+ import { CurrenciesDoNotMeetError, ProportionsDoNotSplitError, } from './failures.js';
6
+ const meetOrRefuse = (left, right) => haveSameCurrency([left, right])
7
+ ? Either.void
8
+ : Either.left(new CurrenciesDoNotMeetError({
9
+ left: currencyCodeOf(currencyOfMoney(left)),
10
+ right: currencyCodeOf(currencyOfMoney(right)),
11
+ }));
12
+ /**
13
+ * Rounds an amount to the currency's own smallest unit, half away from
14
+ * zero. This is the one rounding policy this package keeps: a half cent
15
+ * goes up when the amount is positive and down when it is negative, so a
16
+ * credit note mirrors the invoice it reverses exactly. Round once, at
17
+ * the end of a calculation — a figure rounded twice through a finer
18
+ * scale moves by a cent nobody can account for.
19
+ */
20
+ export const roundedToTheCent = (amount) => transformScale(amount, currencyOfMoney(amount).exponent, halfAwayFromZero);
21
+ /**
22
+ * Adds two amounts of one currency. Two currencies come back as a
23
+ * refusal rather than a throw, so the caller reads the failure in the
24
+ * same channel every other value in this package travels.
25
+ */
26
+ export const addMoney = (left, right) => Either.map(meetOrRefuse(left, right), () => add(left, right));
27
+ /**
28
+ * Adds up any number of amounts, starting from nothing in the currency
29
+ * you name. Nothing plus an amount is that amount, and the order the
30
+ * amounts arrive in never changes the total: those two laws are what
31
+ * makes a journal safe to fold in any order, and a property test holds
32
+ * them.
33
+ */
34
+ export const sumOfMoney = (currency, amounts) => amounts.reduce((total, amount) => Either.flatMap(total, (running) => addMoney(running, amount)), Either.right(zeroMoney(currency)));
35
+ /**
36
+ * Turns an amount around: what was owed becomes what is owed back.
37
+ */
38
+ export const negatedMoney = (amount) => multiply(amount, { amount: -1n, scale: 0n });
39
+ /**
40
+ * Puts two amounts of one currency in order.
41
+ *
42
+ * Puts two amounts of one currency in order: below zero when the first stands
43
+ * lower, zero when they are the same amount, above zero when the first stands
44
+ * higher. Two currencies come back as a refusal.
45
+ */
46
+ export const compareMoney = (left, right) => Either.map(meetOrRefuse(left, right), () => compare(left, right));
47
+ /**
48
+ * Whether two amounts are the same amount. Amounts in two currencies are
49
+ * simply not the same amount, which is an answer rather than a refusal.
50
+ */
51
+ export const sameMoney = (left, right) => haveSameCurrency([left, right]) && equal(left, right);
52
+ /**
53
+ * Takes a share of an amount and keeps every digit of the product. It
54
+ * rounds nothing on purpose: pass the result through `roundedToTheCent`
55
+ * once, when the calculation is over, so no figure is rounded twice.
56
+ */
57
+ export const percentOf = (amount, percent) => multiply(amount, percent);
58
+ /**
59
+ * Splits an amount into shares that add back up to exactly what went in.
60
+ *
61
+ * Splits an amount into shares by proportion, and the shares add back up to
62
+ * exactly what went in — the remainder is handed out one smallest unit at a
63
+ * time rather than left behind. Proportions that cannot split anything come
64
+ * back as a refusal.
65
+ */
66
+ export const sharesOf = (amount, proportions) => {
67
+ const splits = proportions.length > 0 &&
68
+ proportions.every((proportion) => proportion >= 0n) &&
69
+ proportions.some((proportion) => proportion > 0n);
70
+ return splits
71
+ ? Either.right(allocate(amount, proportions))
72
+ : Either.left(new ProportionsDoNotSplitError({ proportions }));
73
+ };
@@ -0,0 +1,25 @@
1
+ import type { DineroCurrency } from 'dinero.js/bigint';
2
+ import { Either } from 'effect';
3
+ import { NotACurrencyCodeError } from './failures.js';
4
+ /**
5
+ * One currency of the ISO 4217 table: its code, the base it counts in,
6
+ * and how many decimals one unit holds. Every currency here comes from
7
+ * the money library's own table, so nothing in this package keeps a
8
+ * second list of how many decimals a currency has.
9
+ */
10
+ export type Currency = DineroCurrency<bigint>;
11
+ /**
12
+ * Reads a currency code somebody wrote: `USD`, `EUR`, `UAH`. A code the
13
+ * ISO 4217 table does not hold comes back as a refusal rather than a
14
+ * currency nobody can count decimals in.
15
+ */
16
+ export declare const currencyOf: (code: string) => Either.Either<Currency, NotACurrencyCodeError>;
17
+ /**
18
+ * Writes a currency back as the three letters ISO 4217 gives it.
19
+ */
20
+ export declare const currencyCodeOf: (currency: Currency) => string;
21
+ /**
22
+ * How many decimals one unit of this currency holds: two for a currency
23
+ * counted in cents, zero for one counted in whole units.
24
+ */
25
+ export declare const decimalsOf: (currency: Currency) => number;
@@ -0,0 +1,24 @@
1
+ import * as isoCurrencies from 'dinero.js/bigint/currencies';
2
+ import { Either } from 'effect';
3
+ import { NotACurrencyCodeError } from './failures.js';
4
+ const BY_CODE = new Map(Object.values(isoCurrencies).map((currency) => [currency.code, currency]));
5
+ /**
6
+ * Reads a currency code somebody wrote: `USD`, `EUR`, `UAH`. A code the
7
+ * ISO 4217 table does not hold comes back as a refusal rather than a
8
+ * currency nobody can count decimals in.
9
+ */
10
+ export const currencyOf = (code) => {
11
+ const known = BY_CODE.get(code);
12
+ return known === undefined
13
+ ? Either.left(new NotACurrencyCodeError({ text: code }))
14
+ : Either.right(known);
15
+ };
16
+ /**
17
+ * Writes a currency back as the three letters ISO 4217 gives it.
18
+ */
19
+ export const currencyCodeOf = (currency) => currency.code;
20
+ /**
21
+ * How many decimals one unit of this currency holds: two for a currency
22
+ * counted in cents, zero for one counted in whole units.
23
+ */
24
+ export const decimalsOf = (currency) => Number(currency.exponent);
@@ -0,0 +1,29 @@
1
+ /**
2
+ * An exact decimal number, held as a whole number of steps and the count
3
+ * of decimals those steps are counted in. `40.0455` is `400455` units at
4
+ * a scale of four, so no digit is ever handed to a floating-point number.
5
+ *
6
+ * @internal
7
+ */
8
+ export interface ExactDecimal {
9
+ readonly units: bigint;
10
+ readonly scale: bigint;
11
+ }
12
+ /**
13
+ * Reads a written decimal number, keeping every digit the writer wrote:
14
+ * nothing here rounds, and nothing here refuses a number for being
15
+ * finer than expected. A text of any other shape comes back as `null` —
16
+ * the caller turns that into its own refusal, because only the caller
17
+ * knows what was being read.
18
+ *
19
+ * @internal
20
+ */
21
+ export declare const exactDecimalOf: (text: string) => ExactDecimal | null;
22
+ /**
23
+ * Writes an exact decimal back, trimming the trailing zeros a scale
24
+ * carried but a reader never asked for: thirty percent reads `0.3`, not
25
+ * `0.300000`.
26
+ *
27
+ * @internal
28
+ */
29
+ export declare const trimmedDecimalOf: (value: ExactDecimal) => string;
@@ -0,0 +1,39 @@
1
+ const WRITTEN_DECIMAL = /^(-?)(\d+)(?:\.(\d+))?$/;
2
+ /**
3
+ * Reads a written decimal number, keeping every digit the writer wrote:
4
+ * nothing here rounds, and nothing here refuses a number for being
5
+ * finer than expected. A text of any other shape comes back as `null` —
6
+ * the caller turns that into its own refusal, because only the caller
7
+ * knows what was being read.
8
+ *
9
+ * @internal
10
+ */
11
+ export const exactDecimalOf = (text) => {
12
+ const parts = WRITTEN_DECIMAL.exec(text);
13
+ if (parts === null)
14
+ return null;
15
+ const [, sign, whole = '', decimals = ''] = parts;
16
+ const magnitude = BigInt(`${whole}${decimals}`);
17
+ return {
18
+ units: sign === '-' ? -magnitude : magnitude,
19
+ scale: BigInt(decimals.length),
20
+ };
21
+ };
22
+ /**
23
+ * Writes an exact decimal back, trimming the trailing zeros a scale
24
+ * carried but a reader never asked for: thirty percent reads `0.3`, not
25
+ * `0.300000`.
26
+ *
27
+ * @internal
28
+ */
29
+ export const trimmedDecimalOf = (value) => {
30
+ const negative = value.units < 0n;
31
+ const scale = Number(value.scale);
32
+ const digits = (negative ? -value.units : value.units)
33
+ .toString(10)
34
+ .padStart(scale + 1, '0');
35
+ const whole = digits.slice(0, digits.length - scale);
36
+ const decimals = digits.slice(digits.length - scale).replace(/0+$/, '');
37
+ const body = decimals === '' ? whole : `${whole}.${decimals}`;
38
+ return negative ? `-${body}` : body;
39
+ };
@@ -0,0 +1,32 @@
1
+ import { Either } from 'effect';
2
+ import type { Money } from './amount.js';
3
+ import { type Currency } from './currency.js';
4
+ import { NotAnExchangeRateError } from './failures.js';
5
+ /**
6
+ * How many units of the currency you are going to, one unit of the
7
+ * currency you are coming from buys. The value is exact: `0.89` is 89
8
+ * units at a scale of two, and no digit of it reaches a floating-point
9
+ * number.
10
+ */
11
+ export interface ExchangeRate {
12
+ readonly amount: bigint;
13
+ readonly scale: bigint;
14
+ }
15
+ /**
16
+ * Reads a rate somebody wrote: `0.89`, `41.57306`. A text of any other
17
+ * shape, or a rate that is not above zero, comes back as a refusal — a
18
+ * rate of nothing would turn every amount into nothing, quietly.
19
+ */
20
+ export declare const exchangeRateFromDecimalForm: (text: string) => Either.Either<ExchangeRate, NotAnExchangeRateError>;
21
+ /**
22
+ * Writes a rate back, trimmed, the way the source that published it
23
+ * wrote it: `0.89`.
24
+ */
25
+ export declare const exchangeRateInDecimalForm: (rate: ExchangeRate) => string;
26
+ /**
27
+ * Turns an amount into another currency at the rate you name, keeping
28
+ * every digit of the product. It rounds nothing on purpose: pass the
29
+ * result through `roundedToTheCent` once, at the end, so a converted
30
+ * figure is never rounded twice.
31
+ */
32
+ export declare const atRate: (amount: Money, target: Currency, rate: ExchangeRate) => Money;
@@ -0,0 +1,28 @@
1
+ import { convert } from 'dinero.js/bigint';
2
+ import { Either } from 'effect';
3
+ import { currencyCodeOf } from './currency.js';
4
+ import { exactDecimalOf, trimmedDecimalOf } from './decimal.js';
5
+ import { NotAnExchangeRateError } from './failures.js';
6
+ /**
7
+ * Reads a rate somebody wrote: `0.89`, `41.57306`. A text of any other
8
+ * shape, or a rate that is not above zero, comes back as a refusal — a
9
+ * rate of nothing would turn every amount into nothing, quietly.
10
+ */
11
+ export const exchangeRateFromDecimalForm = (text) => {
12
+ const read = exactDecimalOf(text);
13
+ return read === null || read.units <= 0n
14
+ ? Either.left(new NotAnExchangeRateError({ text }))
15
+ : Either.right({ amount: read.units, scale: read.scale });
16
+ };
17
+ /**
18
+ * Writes a rate back, trimmed, the way the source that published it
19
+ * wrote it: `0.89`.
20
+ */
21
+ export const exchangeRateInDecimalForm = (rate) => trimmedDecimalOf({ units: rate.amount, scale: rate.scale });
22
+ /**
23
+ * Turns an amount into another currency at the rate you name, keeping
24
+ * every digit of the product. It rounds nothing on purpose: pass the
25
+ * result through `roundedToTheCent` once, at the end, so a converted
26
+ * figure is never rounded twice.
27
+ */
28
+ export const atRate = (amount, target, rate) => convert(amount, target, { [currencyCodeOf(target)]: rate });
@@ -0,0 +1,86 @@
1
+ import type { Refusal } from '@satorio/machinery/core';
2
+ declare const NotACurrencyCodeError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
3
+ readonly _tag: "NotACurrencyCodeError";
4
+ } & Readonly<A>;
5
+ /**
6
+ * The text handed in does not name a currency this mechanism knows. Every
7
+ * currency here comes from the money library's own ISO 4217 table, so a
8
+ * code outside it has no exponent and no base to count in.
9
+ */
10
+ export declare class NotACurrencyCodeError extends NotACurrencyCodeError_base<{
11
+ readonly text: string;
12
+ }> {
13
+ get refusal(): Refusal;
14
+ }
15
+ declare const NotAnAmountError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
16
+ readonly _tag: "NotAnAmountError";
17
+ } & Readonly<A>;
18
+ /**
19
+ * The text handed in does not write an amount. The reader keeps every
20
+ * decimal somebody wrote, so this refusal is about the shape of the
21
+ * text and never about a digit the reader was unwilling to hold.
22
+ */
23
+ export declare class NotAnAmountError extends NotAnAmountError_base<{
24
+ readonly text: string;
25
+ }> {
26
+ get refusal(): Refusal;
27
+ }
28
+ declare const NotAnExchangeRateError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
29
+ readonly _tag: "NotAnExchangeRateError";
30
+ } & Readonly<A>;
31
+ /**
32
+ * The text handed in does not write an exchange rate. A rate has to
33
+ * stand above zero: a rate of nothing turns every amount into nothing,
34
+ * and it does it without a word.
35
+ */
36
+ export declare class NotAnExchangeRateError extends NotAnExchangeRateError_base<{
37
+ readonly text: string;
38
+ }> {
39
+ get refusal(): Refusal;
40
+ }
41
+ declare const NotAPercentError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
42
+ readonly _tag: "NotAPercentError";
43
+ } & Readonly<A>;
44
+ /**
45
+ * The text handed in does not write a percent. `form` names which of the
46
+ * two written forms was being read, so the refusal quotes the rule the
47
+ * sender was actually held to.
48
+ */
49
+ export declare class NotAPercentError extends NotAPercentError_base<{
50
+ readonly text: string;
51
+ readonly form: 'percentage' | 'ratio';
52
+ }> {
53
+ get refusal(): Refusal;
54
+ }
55
+ declare const CurrenciesDoNotMeetError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
56
+ readonly _tag: "CurrenciesDoNotMeetError";
57
+ } & Readonly<A>;
58
+ /**
59
+ * Two amounts in two currencies were asked to meet. Nothing here adds
60
+ * them, compares them, or folds them together: a rate turns one into the
61
+ * other first, and the rate is a fact somebody has to supply.
62
+ */
63
+ export declare class CurrenciesDoNotMeetError extends CurrenciesDoNotMeetError_base<{
64
+ readonly left: string;
65
+ readonly right: string;
66
+ }> {
67
+ get refusal(): Refusal;
68
+ }
69
+ declare const ProportionsDoNotSplitError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
70
+ readonly _tag: "ProportionsDoNotSplitError";
71
+ } & Readonly<A>;
72
+ /**
73
+ * The proportions handed in cannot split an amount. A split needs at
74
+ * least one proportion, none of them below zero, and at least one above
75
+ * it, or there is no share for the remainder to land in.
76
+ */
77
+ export declare class ProportionsDoNotSplitError extends ProportionsDoNotSplitError_base<{
78
+ readonly proportions: readonly bigint[];
79
+ }> {
80
+ get refusal(): Refusal;
81
+ }
82
+ /**
83
+ * Every way this mechanism refuses a value.
84
+ */
85
+ export type MoneyFailure = NotACurrencyCodeError | NotAnAmountError | NotAnExchangeRateError | NotAPercentError | CurrenciesDoNotMeetError | ProportionsDoNotSplitError;
86
+ export {};
@@ -0,0 +1,97 @@
1
+ import { Data } from 'effect';
2
+ /**
3
+ * The text handed in does not name a currency this mechanism knows. Every
4
+ * currency here comes from the money library's own ISO 4217 table, so a
5
+ * code outside it has no exponent and no base to count in.
6
+ */
7
+ export class NotACurrencyCodeError extends Data.TaggedError('NotACurrencyCodeError') {
8
+ get refusal() {
9
+ return {
10
+ what: `${JSON.stringify(this.text)} is not a currency code this mechanism knows.`,
11
+ why: 'Every currency here comes from the ISO 4217 table, which says how ' +
12
+ 'many decimals the currency counts in; a code outside it has none.',
13
+ whatToDo: 'Name the currency as three upper-case letters, such as USD or EUR, ' +
14
+ 'then hand it back.',
15
+ };
16
+ }
17
+ }
18
+ /**
19
+ * The text handed in does not write an amount. The reader keeps every
20
+ * decimal somebody wrote, so this refusal is about the shape of the
21
+ * text and never about a digit the reader was unwilling to hold.
22
+ */
23
+ export class NotAnAmountError extends Data.TaggedError('NotAnAmountError') {
24
+ get refusal() {
25
+ return {
26
+ what: `${JSON.stringify(this.text)} is not written as an amount.`,
27
+ why: 'An amount is a decimal number, and this reader keeps every ' +
28
+ "decimal of it, because a dropped digit is somebody's money.",
29
+ whatToDo: 'Write the amount as 40.0455 or as -25, then hand it back.',
30
+ };
31
+ }
32
+ }
33
+ /**
34
+ * The text handed in does not write an exchange rate. A rate has to
35
+ * stand above zero: a rate of nothing turns every amount into nothing,
36
+ * and it does it without a word.
37
+ */
38
+ export class NotAnExchangeRateError extends Data.TaggedError('NotAnExchangeRateError') {
39
+ get refusal() {
40
+ return {
41
+ what: `${JSON.stringify(this.text)} is not written as an exchange rate.`,
42
+ why: 'A rate is a decimal number above zero, and a rate of nothing ' +
43
+ 'would turn every amount it touches into nothing.',
44
+ whatToDo: 'Write the rate as 0.89 or as 41.57306, then hand it back.',
45
+ };
46
+ }
47
+ }
48
+ /**
49
+ * The text handed in does not write a percent. `form` names which of the
50
+ * two written forms was being read, so the refusal quotes the rule the
51
+ * sender was actually held to.
52
+ */
53
+ export class NotAPercentError extends Data.TaggedError('NotAPercentError') {
54
+ get refusal() {
55
+ return {
56
+ what: `${JSON.stringify(this.text)} is not written as a percent in ${this.form} form.`,
57
+ why: 'A percent is one exact decimal number, and this mechanism reads ' +
58
+ 'the form it was asked for rather than guessing which one somebody ' +
59
+ 'meant.',
60
+ whatToDo: this.form === 'percentage'
61
+ ? 'Write thirty percent as 30, then hand it back.'
62
+ : 'Write thirty percent as 0.3, then hand it back.',
63
+ };
64
+ }
65
+ }
66
+ /**
67
+ * Two amounts in two currencies were asked to meet. Nothing here adds
68
+ * them, compares them, or folds them together: a rate turns one into the
69
+ * other first, and the rate is a fact somebody has to supply.
70
+ */
71
+ export class CurrenciesDoNotMeetError extends Data.TaggedError('CurrenciesDoNotMeetError') {
72
+ get refusal() {
73
+ return {
74
+ what: `An amount in ${this.left} met an amount in ${this.right}.`,
75
+ why: 'Two currencies fold apart, so adding or ordering them would ' +
76
+ 'invent a number nobody can bank.',
77
+ whatToDo: 'Turn one into the other through a rate you can name, then hand ' +
78
+ 'the two amounts back in one currency.',
79
+ };
80
+ }
81
+ }
82
+ /**
83
+ * The proportions handed in cannot split an amount. A split needs at
84
+ * least one proportion, none of them below zero, and at least one above
85
+ * it, or there is no share for the remainder to land in.
86
+ */
87
+ export class ProportionsDoNotSplitError extends Data.TaggedError('ProportionsDoNotSplitError') {
88
+ get refusal() {
89
+ return {
90
+ what: `These proportions cannot split an amount: [${this.proportions.join(', ')}].`,
91
+ why: 'A split needs at least one proportion, none below zero, and at ' +
92
+ 'least one above it; otherwise no share can hold the remainder.',
93
+ whatToDo: 'Hand in one proportion per share, each zero or more, with at ' +
94
+ 'least one above zero.',
95
+ };
96
+ }
97
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Public entry to the money mechanism, over dinero.js, the optional peer your
3
+ * service installs: exact amounts of one currency, shares, rates, and
4
+ * rounding. It gives you an exact amount of one currency over the money
5
+ * library this package stands on, a share of an amount in both forms a
6
+ * business writes it, an exchange rate, the one rounding policy every figure
7
+ * here goes through, and the decimal text an amount travels the wire as.
8
+ */
9
+ export { currencyOfMoney, type Money, minorUnitsOf, money, moneyAtScale, moneyFromDecimalForm, moneyInDecimalForm, scaleOfMoney, zeroMoney, } from './amount.js';
10
+ export { addMoney, compareMoney, negatedMoney, percentOf, roundedToTheCent, sameMoney, sharesOf, sumOfMoney, } from './arithmetic.js';
11
+ export { type Currency, currencyCodeOf, currencyOf, decimalsOf, } from './currency.js';
12
+ export { atRate, type ExchangeRate, exchangeRateFromDecimalForm, exchangeRateInDecimalForm, } from './exchange-rate.js';
13
+ export { CurrenciesDoNotMeetError, type MoneyFailure, NotACurrencyCodeError, NotAnAmountError, NotAnExchangeRateError, NotAPercentError, ProportionsDoNotSplitError, } from './failures.js';
14
+ export { type Percent, percentFromPercentageForm, percentFromRatioForm, percentInPercentageForm, percentInRatioForm, samePercent, } from './percent.js';
15
+ export { MoneyFromDecimalString, PercentFromPercentageString, PercentFromRatioString, } from './schema.js';
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Public entry to the money mechanism, over dinero.js, the optional peer your
3
+ * service installs: exact amounts of one currency, shares, rates, and
4
+ * rounding. It gives you an exact amount of one currency over the money
5
+ * library this package stands on, a share of an amount in both forms a
6
+ * business writes it, an exchange rate, the one rounding policy every figure
7
+ * here goes through, and the decimal text an amount travels the wire as.
8
+ */
9
+ export { currencyOfMoney, minorUnitsOf, money, moneyAtScale, moneyFromDecimalForm, moneyInDecimalForm, scaleOfMoney, zeroMoney, } from './amount.js';
10
+ export { addMoney, compareMoney, negatedMoney, percentOf, roundedToTheCent, sameMoney, sharesOf, sumOfMoney, } from './arithmetic.js';
11
+ export { currencyCodeOf, currencyOf, decimalsOf, } from './currency.js';
12
+ export { atRate, exchangeRateFromDecimalForm, exchangeRateInDecimalForm, } from './exchange-rate.js';
13
+ export { CurrenciesDoNotMeetError, NotACurrencyCodeError, NotAnAmountError, NotAnExchangeRateError, NotAPercentError, ProportionsDoNotSplitError, } from './failures.js';
14
+ export { percentFromPercentageForm, percentFromRatioForm, percentInPercentageForm, percentInRatioForm, samePercent, } from './percent.js';
15
+ export { MoneyFromDecimalString, PercentFromPercentageString, PercentFromRatioString, } from './schema.js';