@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.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dictionary/all.json +4656 -0
- package/dictionary/all.toon +639 -0
- package/dictionary/core.json +221 -0
- package/dictionary/core.toon +33 -0
- package/dictionary/graphql.json +212 -0
- package/dictionary/graphql.toon +32 -0
- package/dictionary/http.json +113 -0
- package/dictionary/http.toon +21 -0
- package/dictionary/identifiers.json +266 -0
- package/dictionary/identifiers.toon +38 -0
- package/dictionary/index.json +96 -0
- package/dictionary/index.toon +18 -0
- package/dictionary/integration.json +131 -0
- package/dictionary/integration.toon +23 -0
- package/dictionary/money.json +383 -0
- package/dictionary/money.toon +51 -0
- package/dictionary/ports.json +392 -0
- package/dictionary/ports.toon +52 -0
- package/dictionary/postgres.json +473 -0
- package/dictionary/postgres.toon +61 -0
- package/dictionary/publishing.json +131 -0
- package/dictionary/publishing.toon +23 -0
- package/dictionary/queue.json +365 -0
- package/dictionary/queue.toon +49 -0
- package/dictionary/reference/api.mdx +843 -0
- package/dictionary/reference/uk/api.mdx +844 -0
- package/dictionary/storage.json +194 -0
- package/dictionary/storage.toon +30 -0
- package/dictionary/testing.json +707 -0
- package/dictionary/testing.toon +87 -0
- package/dictionary/time.json +311 -0
- package/dictionary/time.toon +43 -0
- package/dictionary/vitest.json +356 -0
- package/dictionary/vitest.toon +48 -0
- package/dictionary/workflow.json +185 -0
- package/dictionary/workflow.toon +29 -0
- package/dist/bin/command.d.ts +45 -0
- package/dist/bin/command.js +113 -0
- package/dist/bin.d.ts +2 -0
- package/dist/bin.js +11 -0
- package/dist/core/bigint-json.d.ts +6 -0
- package/dist/core/bigint-json.js +6 -0
- package/dist/core/closed-set.d.ts +14 -0
- package/dist/core/closed-set.js +8 -0
- package/dist/core/domain-error.d.ts +8 -0
- package/dist/core/domain-error.js +11 -0
- package/dist/core/environment.d.ts +124 -0
- package/dist/core/environment.js +115 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.js +13 -0
- package/dist/core/ulid.d.ts +27 -0
- package/dist/core/ulid.js +27 -0
- package/dist/graphql/builder.d.ts +36 -0
- package/dist/graphql/builder.js +35 -0
- package/dist/graphql/context.d.ts +36 -0
- package/dist/graphql/context.js +69 -0
- package/dist/graphql/failure-classification.d.ts +11 -0
- package/dist/graphql/failure-classification.js +1 -0
- package/dist/graphql/failures.d.ts +62 -0
- package/dist/graphql/failures.js +30 -0
- package/dist/graphql/graphiql.d.ts +45 -0
- package/dist/graphql/graphiql.js +56 -0
- package/dist/graphql/index.d.ts +15 -0
- package/dist/graphql/index.js +14 -0
- package/dist/graphql/layer.d.ts +15 -0
- package/dist/graphql/layer.js +23 -0
- package/dist/graphql/request-handler.d.ts +26 -0
- package/dist/graphql/request-handler.js +33 -0
- package/dist/graphql/server.d.ts +19 -0
- package/dist/graphql/server.js +16 -0
- package/dist/graphql/settings.d.ts +55 -0
- package/dist/graphql/settings.js +82 -0
- package/dist/http/health.d.ts +70 -0
- package/dist/http/health.js +60 -0
- package/dist/http/index.d.ts +5 -0
- package/dist/http/index.js +5 -0
- package/dist/identifiers/client-code.d.ts +37 -0
- package/dist/identifiers/client-code.js +41 -0
- package/dist/identifiers/creditor-reference.d.ts +38 -0
- package/dist/identifiers/creditor-reference.js +32 -0
- package/dist/identifiers/damm.d.ts +22 -0
- package/dist/identifiers/damm.js +50 -0
- package/dist/identifiers/document-number.d.ts +68 -0
- package/dist/identifiers/document-number.js +73 -0
- package/dist/identifiers/document-numbers.d.ts +27 -0
- package/dist/identifiers/document-numbers.js +16 -0
- package/dist/identifiers/failures.d.ts +46 -0
- package/dist/identifiers/failures.js +48 -0
- package/dist/identifiers/index.d.ts +14 -0
- package/dist/identifiers/index.js +13 -0
- package/dist/identifiers/next-document-number.d.ts +26 -0
- package/dist/identifiers/next-document-number.js +31 -0
- package/dist/identifiers/ulid.d.ts +28 -0
- package/dist/identifiers/ulid.js +71 -0
- package/dist/integration/external-http-client.d.ts +22 -0
- package/dist/integration/external-http-client.js +74 -0
- package/dist/integration/index.d.ts +12 -0
- package/dist/integration/index.js +12 -0
- package/dist/integration/layer.d.ts +19 -0
- package/dist/integration/layer.js +30 -0
- package/dist/integration/retry.d.ts +65 -0
- package/dist/integration/retry.js +54 -0
- package/dist/integration/settings.d.ts +73 -0
- package/dist/integration/settings.js +92 -0
- package/dist/money/amount.d.ts +60 -0
- package/dist/money/amount.js +67 -0
- package/dist/money/arithmetic.d.ts +60 -0
- package/dist/money/arithmetic.js +73 -0
- package/dist/money/currency.d.ts +25 -0
- package/dist/money/currency.js +24 -0
- package/dist/money/decimal.d.ts +29 -0
- package/dist/money/decimal.js +39 -0
- package/dist/money/exchange-rate.d.ts +32 -0
- package/dist/money/exchange-rate.js +28 -0
- package/dist/money/failures.d.ts +86 -0
- package/dist/money/failures.js +97 -0
- package/dist/money/index.d.ts +15 -0
- package/dist/money/index.js +15 -0
- package/dist/money/percent.d.ts +35 -0
- package/dist/money/percent.js +50 -0
- package/dist/money/schema.d.ts +25 -0
- package/dist/money/schema.js +41 -0
- package/dist/ports/blob-store.d.ts +42 -0
- package/dist/ports/blob-store.js +13 -0
- package/dist/ports/document-number-counter.d.ts +36 -0
- package/dist/ports/document-number-counter.js +12 -0
- package/dist/ports/event-publisher.d.ts +29 -0
- package/dist/ports/event-publisher.js +13 -0
- package/dist/ports/external-http-client.d.ts +60 -0
- package/dist/ports/external-http-client.js +25 -0
- package/dist/ports/index.d.ts +17 -0
- package/dist/ports/index.js +17 -0
- package/dist/ports/integration-event-outbox.d.ts +44 -0
- package/dist/ports/integration-event-outbox.js +13 -0
- package/dist/ports/port-unavailable.d.ts +21 -0
- package/dist/ports/port-unavailable.js +30 -0
- package/dist/ports/processed-event-registry.d.ts +57 -0
- package/dist/ports/processed-event-registry.js +24 -0
- package/dist/ports/queue-message-producer.d.ts +26 -0
- package/dist/ports/queue-message-producer.js +12 -0
- package/dist/ports/queue-message-source.d.ts +39 -0
- package/dist/ports/queue-message-source.js +13 -0
- package/dist/ports/readiness-probe.d.ts +27 -0
- package/dist/ports/readiness-probe.js +13 -0
- package/dist/ports/transaction-boundary.d.ts +28 -0
- package/dist/ports/transaction-boundary.js +13 -0
- package/dist/postgres/client.d.ts +51 -0
- package/dist/postgres/client.js +81 -0
- package/dist/postgres/document-number-counter.d.ts +14 -0
- package/dist/postgres/document-number-counter.js +27 -0
- package/dist/postgres/failures.d.ts +28 -0
- package/dist/postgres/failures.js +18 -0
- package/dist/postgres/index.d.ts +18 -0
- package/dist/postgres/index.js +18 -0
- package/dist/postgres/integration-event-outbox.d.ts +15 -0
- package/dist/postgres/integration-event-outbox.js +60 -0
- package/dist/postgres/jsonb-value.d.ts +10 -0
- package/dist/postgres/jsonb-value.js +9 -0
- package/dist/postgres/layer.d.ts +57 -0
- package/dist/postgres/layer.js +72 -0
- package/dist/postgres/migrations/advisory-lock.d.ts +47 -0
- package/dist/postgres/migrations/advisory-lock.js +59 -0
- package/dist/postgres/migrations/runner.d.ts +56 -0
- package/dist/postgres/migrations/runner.js +69 -0
- package/dist/postgres/migrations/sql-file-loader.d.ts +59 -0
- package/dist/postgres/migrations/sql-file-loader.js +99 -0
- package/dist/postgres/processed-event-registry.d.ts +13 -0
- package/dist/postgres/processed-event-registry.js +44 -0
- package/dist/postgres/readiness-probe.d.ts +27 -0
- package/dist/postgres/readiness-probe.js +33 -0
- package/dist/postgres/settings.d.ts +134 -0
- package/dist/postgres/settings.js +210 -0
- package/dist/postgres/sql/0001_processed_event.sql +4 -0
- package/dist/postgres/sql/0002_integration_event_outbox.sql +13 -0
- package/dist/postgres/sql/0003_document_number_counter.sql +6 -0
- package/dist/postgres/sql-failure.d.ts +19 -0
- package/dist/postgres/sql-failure.js +22 -0
- package/dist/postgres/store-failure.d.ts +17 -0
- package/dist/postgres/store-failure.js +16 -0
- package/dist/postgres/transaction-boundary.d.ts +13 -0
- package/dist/postgres/transaction-boundary.js +21 -0
- package/dist/publishing/event-publisher.sns.d.ts +66 -0
- package/dist/publishing/event-publisher.sns.js +75 -0
- package/dist/publishing/event-publisher.stdout.d.ts +15 -0
- package/dist/publishing/event-publisher.stdout.js +24 -0
- package/dist/publishing/index.d.ts +10 -0
- package/dist/publishing/index.js +10 -0
- package/dist/publishing/layer.d.ts +25 -0
- package/dist/publishing/layer.js +39 -0
- package/dist/publishing/settings.d.ts +60 -0
- package/dist/publishing/settings.js +85 -0
- package/dist/queue/consumer.d.ts +91 -0
- package/dist/queue/consumer.js +92 -0
- package/dist/queue/envelope.d.ts +39 -0
- package/dist/queue/envelope.js +39 -0
- package/dist/queue/failures.d.ts +33 -0
- package/dist/queue/failures.js +15 -0
- package/dist/queue/index.d.ts +14 -0
- package/dist/queue/index.js +14 -0
- package/dist/queue/layer.d.ts +33 -0
- package/dist/queue/layer.js +58 -0
- package/dist/queue/message-codec.d.ts +43 -0
- package/dist/queue/message-codec.js +33 -0
- package/dist/queue/queue-message-producer.d.ts +61 -0
- package/dist/queue/queue-message-producer.js +52 -0
- package/dist/queue/queue-message-source.d.ts +93 -0
- package/dist/queue/queue-message-source.js +86 -0
- package/dist/queue/readiness.d.ts +78 -0
- package/dist/queue/readiness.js +86 -0
- package/dist/queue/settings.d.ts +106 -0
- package/dist/queue/settings.js +160 -0
- package/dist/storage/blob-store.d.ts +84 -0
- package/dist/storage/blob-store.js +109 -0
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.js +9 -0
- package/dist/storage/layer.d.ts +17 -0
- package/dist/storage/layer.js +30 -0
- package/dist/storage/settings.d.ts +48 -0
- package/dist/storage/settings.js +65 -0
- package/dist/testing/blob-store.d.ts +27 -0
- package/dist/testing/blob-store.js +45 -0
- package/dist/testing/clock.d.ts +29 -0
- package/dist/testing/clock.js +41 -0
- package/dist/testing/dna-perturbator/attention.d.ts +55 -0
- package/dist/testing/dna-perturbator/attention.js +49 -0
- package/dist/testing/dna-perturbator/chains.d.ts +31 -0
- package/dist/testing/dna-perturbator/chains.js +53 -0
- package/dist/testing/dna-perturbator/report.d.ts +12 -0
- package/dist/testing/dna-perturbator/report.js +22 -0
- package/dist/testing/dna-perturbator/verdict.d.ts +26 -0
- package/dist/testing/dna-perturbator/verdict.js +28 -0
- package/dist/testing/dna-perturbator.d.ts +48 -0
- package/dist/testing/dna-perturbator.js +145 -0
- package/dist/testing/document-number-counter.d.ts +29 -0
- package/dist/testing/document-number-counter.js +39 -0
- package/dist/testing/event-publisher.d.ts +28 -0
- package/dist/testing/event-publisher.js +33 -0
- package/dist/testing/external-http-client.d.ts +29 -0
- package/dist/testing/external-http-client.js +49 -0
- package/dist/testing/index.d.ts +24 -0
- package/dist/testing/index.js +24 -0
- package/dist/testing/integration-event-outbox.d.ts +37 -0
- package/dist/testing/integration-event-outbox.js +67 -0
- package/dist/testing/mutation-settings.d.ts +71 -0
- package/dist/testing/mutation-settings.js +57 -0
- package/dist/testing/postgres-rows.d.ts +12 -0
- package/dist/testing/postgres-rows.js +19 -0
- package/dist/testing/processed-event-registry.d.ts +25 -0
- package/dist/testing/processed-event-registry.js +39 -0
- package/dist/testing/queue-message-producer.d.ts +27 -0
- package/dist/testing/queue-message-producer.js +41 -0
- package/dist/testing/queue-message-source.d.ts +54 -0
- package/dist/testing/queue-message-source.js +71 -0
- package/dist/testing/quiet-reporter.d.ts +86 -0
- package/dist/testing/quiet-reporter.js +81 -0
- package/dist/testing/readiness-probe.d.ts +27 -0
- package/dist/testing/readiness-probe.js +33 -0
- package/dist/testing/scenario.d.ts +94 -0
- package/dist/testing/scenario.js +81 -0
- package/dist/testing/transaction-boundary.d.ts +30 -0
- package/dist/testing/transaction-boundary.js +36 -0
- package/dist/testing/typed-failure.d.ts +8 -0
- package/dist/testing/typed-failure.js +15 -0
- package/dist/time/calendar-day.d.ts +83 -0
- package/dist/time/calendar-day.js +112 -0
- package/dist/time/failures.d.ts +51 -0
- package/dist/time/failures.js +56 -0
- package/dist/time/index.d.ts +11 -0
- package/dist/time/index.js +11 -0
- package/dist/time/instant.d.ts +64 -0
- package/dist/time/instant.js +67 -0
- package/dist/time/period.d.ts +45 -0
- package/dist/time/period.js +55 -0
- package/dist/vitest/container-reuse.d.ts +14 -0
- package/dist/vitest/container-reuse.js +15 -0
- package/dist/vitest/dotenv.d.ts +16 -0
- package/dist/vitest/dotenv.js +48 -0
- package/dist/vitest/effect-single-instance.d.ts +36 -0
- package/dist/vitest/effect-single-instance.js +125 -0
- package/dist/vitest/fast-check-single-instance.d.ts +11 -0
- package/dist/vitest/fast-check-single-instance.js +26 -0
- package/dist/vitest/index.d.ts +31 -0
- package/dist/vitest/index.js +30 -0
- package/dist/vitest/localstack-container.d.ts +32 -0
- package/dist/vitest/localstack-container.js +55 -0
- package/dist/vitest/port-contract-tester.d.ts +8 -0
- package/dist/vitest/port-contract-tester.js +1 -0
- package/dist/vitest/port-contracts/blob-store.contract.d.ts +22 -0
- package/dist/vitest/port-contracts/blob-store.contract.js +108 -0
- package/dist/vitest/port-contracts/document-number-counter.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/document-number-counter.contract.js +90 -0
- package/dist/vitest/port-contracts/event-publisher.contract.d.ts +35 -0
- package/dist/vitest/port-contracts/event-publisher.contract.js +67 -0
- package/dist/vitest/port-contracts/external-http-client.contract.d.ts +35 -0
- package/dist/vitest/port-contracts/external-http-client.contract.js +52 -0
- package/dist/vitest/port-contracts/integration-event-outbox.contract.d.ts +23 -0
- package/dist/vitest/port-contracts/integration-event-outbox.contract.js +75 -0
- package/dist/vitest/port-contracts/processed-event-registry.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/processed-event-registry.contract.js +84 -0
- package/dist/vitest/port-contracts/queue-message-producer.contract.d.ts +26 -0
- package/dist/vitest/port-contracts/queue-message-producer.contract.js +48 -0
- package/dist/vitest/port-contracts/queue-message-source.contract.d.ts +29 -0
- package/dist/vitest/port-contracts/queue-message-source.contract.js +71 -0
- package/dist/vitest/port-contracts/readiness-probe.contract.d.ts +18 -0
- package/dist/vitest/port-contracts/readiness-probe.contract.js +41 -0
- package/dist/vitest/port-contracts/transaction-boundary.contract.d.ts +21 -0
- package/dist/vitest/port-contracts/transaction-boundary.contract.js +64 -0
- package/dist/vitest/postgres-container.d.ts +57 -0
- package/dist/vitest/postgres-container.js +118 -0
- package/dist/vitest/reader-port-contract.d.ts +41 -0
- package/dist/vitest/reader-port-contract.js +43 -0
- package/dist/vitest/repo-source-files.d.ts +37 -0
- package/dist/vitest/repo-source-files.js +61 -0
- package/dist/vitest/reset-tables.d.ts +18 -0
- package/dist/vitest/reset-tables.js +30 -0
- package/dist/vitest/settings-contract.d.ts +38 -0
- package/dist/vitest/settings-contract.js +77 -0
- package/dist/vitest/stub-server.d.ts +43 -0
- package/dist/vitest/stub-server.js +60 -0
- package/dist/vitest/term-registry.json +730 -0
- package/dist/vitest/testclock-fork-isolated-files.d.ts +13 -0
- package/dist/vitest/testclock-fork-isolated-files.js +18 -0
- package/dist/vitest/testclock-fork-isolation.d.ts +77 -0
- package/dist/vitest/testclock-fork-isolation.js +188 -0
- package/dist/workflow/claim-once.d.ts +16 -0
- package/dist/workflow/claim-once.js +9 -0
- package/dist/workflow/failures.d.ts +47 -0
- package/dist/workflow/failures.js +22 -0
- package/dist/workflow/index.d.ts +12 -0
- package/dist/workflow/index.js +12 -0
- package/dist/workflow/on-claim.d.ts +17 -0
- package/dist/workflow/on-claim.js +8 -0
- package/dist/workflow/publish-pending-events.d.ts +31 -0
- package/dist/workflow/publish-pending-events.js +31 -0
- package/dist/workflow/transactionally.d.ts +8 -0
- package/dist/workflow/transactionally.js +8 -0
- package/dist/workflow/versioned-payload.d.ts +35 -0
- package/dist/workflow/versioned-payload.js +43 -0
- 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';
|