@coderoftime/fints 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/LICENSE +28 -0
  3. package/README.md +156 -2
  4. package/dist/core/account.d.ts +140 -0
  5. package/dist/core/account.d.ts.map +1 -0
  6. package/dist/core/account.js +174 -0
  7. package/dist/core/account.js.map +1 -0
  8. package/dist/core/amount.d.ts +62 -0
  9. package/dist/core/amount.d.ts.map +1 -0
  10. package/dist/core/amount.js +189 -0
  11. package/dist/core/amount.js.map +1 -0
  12. package/dist/core/balance.d.ts +54 -0
  13. package/dist/core/balance.d.ts.map +1 -0
  14. package/dist/core/balance.js +53 -0
  15. package/dist/core/balance.js.map +1 -0
  16. package/dist/core/bank-info.d.ts +49 -0
  17. package/dist/core/bank-info.d.ts.map +1 -0
  18. package/dist/core/bank-info.js +52 -0
  19. package/dist/core/bank-info.js.map +1 -0
  20. package/dist/core/client.d.ts +126 -0
  21. package/dist/core/client.d.ts.map +1 -0
  22. package/dist/core/client.js +2 -0
  23. package/dist/core/client.js.map +1 -0
  24. package/dist/core/config.d.ts +68 -0
  25. package/dist/core/config.d.ts.map +1 -0
  26. package/dist/core/config.js +113 -0
  27. package/dist/core/config.js.map +1 -0
  28. package/dist/core/errors.d.ts +91 -0
  29. package/dist/core/errors.d.ts.map +1 -0
  30. package/dist/core/errors.js +182 -0
  31. package/dist/core/errors.js.map +1 -0
  32. package/dist/core/feedback.d.ts +69 -0
  33. package/dist/core/feedback.d.ts.map +1 -0
  34. package/dist/core/feedback.js +124 -0
  35. package/dist/core/feedback.js.map +1 -0
  36. package/dist/core/transaction.d.ts +98 -0
  37. package/dist/core/transaction.d.ts.map +1 -0
  38. package/dist/core/transaction.js +91 -0
  39. package/dist/core/transaction.js.map +1 -0
  40. package/dist/core/transport.d.ts +37 -0
  41. package/dist/core/transport.d.ts.map +1 -0
  42. package/dist/core/transport.js +92 -0
  43. package/dist/core/transport.js.map +1 -0
  44. package/dist/formats/camt.d.ts +12 -0
  45. package/dist/formats/camt.d.ts.map +1 -0
  46. package/dist/formats/camt.js +159 -0
  47. package/dist/formats/camt.js.map +1 -0
  48. package/dist/formats/mt940.d.ts +42 -0
  49. package/dist/formats/mt940.d.ts.map +1 -0
  50. package/dist/formats/mt940.js +297 -0
  51. package/dist/formats/mt940.js.map +1 -0
  52. package/dist/formats/readers.d.ts +11 -0
  53. package/dist/formats/readers.d.ts.map +1 -0
  54. package/dist/formats/readers.js +47 -0
  55. package/dist/formats/readers.js.map +1 -0
  56. package/dist/formats/sepa-purpose.d.ts +22 -0
  57. package/dist/formats/sepa-purpose.d.ts.map +1 -0
  58. package/dist/formats/sepa-purpose.js +68 -0
  59. package/dist/formats/sepa-purpose.js.map +1 -0
  60. package/dist/index.d.ts +28 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +27 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/session/app-approval.d.ts +60 -0
  65. package/dist/session/app-approval.d.ts.map +1 -0
  66. package/dist/session/app-approval.js +215 -0
  67. package/dist/session/app-approval.js.map +1 -0
  68. package/dist/session/bank-connection.d.ts +28 -0
  69. package/dist/session/bank-connection.d.ts.map +1 -0
  70. package/dist/session/bank-connection.js +48 -0
  71. package/dist/session/bank-connection.js.map +1 -0
  72. package/dist/session/dialog-runner.d.ts +34 -0
  73. package/dist/session/dialog-runner.d.ts.map +1 -0
  74. package/dist/session/dialog-runner.js +112 -0
  75. package/dist/session/dialog-runner.js.map +1 -0
  76. package/dist/session/login-guard.d.ts +73 -0
  77. package/dist/session/login-guard.d.ts.map +1 -0
  78. package/dist/session/login-guard.js +109 -0
  79. package/dist/session/login-guard.js.map +1 -0
  80. package/dist/session/parameter-data.d.ts +83 -0
  81. package/dist/session/parameter-data.d.ts.map +1 -0
  82. package/dist/session/parameter-data.js +68 -0
  83. package/dist/session/parameter-data.js.map +1 -0
  84. package/dist/session/pin-tan-client.d.ts +54 -0
  85. package/dist/session/pin-tan-client.d.ts.map +1 -0
  86. package/dist/session/pin-tan-client.js +153 -0
  87. package/dist/session/pin-tan-client.js.map +1 -0
  88. package/dist/session/pin-tan-dialog.d.ts +71 -0
  89. package/dist/session/pin-tan-dialog.d.ts.map +1 -0
  90. package/dist/session/pin-tan-dialog.js +124 -0
  91. package/dist/session/pin-tan-dialog.js.map +1 -0
  92. package/dist/session/protocol.d.ts +209 -0
  93. package/dist/session/protocol.d.ts.map +1 -0
  94. package/dist/session/protocol.js +75 -0
  95. package/dist/session/protocol.js.map +1 -0
  96. package/dist/session/response-handler.d.ts +48 -0
  97. package/dist/session/response-handler.d.ts.map +1 -0
  98. package/dist/session/response-handler.js +121 -0
  99. package/dist/session/response-handler.js.map +1 -0
  100. package/dist/session/sepa-accounts.d.ts +34 -0
  101. package/dist/session/sepa-accounts.d.ts.map +1 -0
  102. package/dist/session/sepa-accounts.js +96 -0
  103. package/dist/session/sepa-accounts.js.map +1 -0
  104. package/dist/session/session-setup.d.ts +72 -0
  105. package/dist/session/session-setup.d.ts.map +1 -0
  106. package/dist/session/session-setup.js +160 -0
  107. package/dist/session/session-setup.js.map +1 -0
  108. package/dist/session/session-state.d.ts +47 -0
  109. package/dist/session/session-state.d.ts.map +1 -0
  110. package/dist/session/session-state.js +147 -0
  111. package/dist/session/session-state.js.map +1 -0
  112. package/dist/session/tan-selection.d.ts +88 -0
  113. package/dist/session/tan-selection.d.ts.map +1 -0
  114. package/dist/session/tan-selection.js +178 -0
  115. package/dist/session/tan-selection.js.map +1 -0
  116. package/dist/session/user-prompts.d.ts +61 -0
  117. package/dist/session/user-prompts.d.ts.map +1 -0
  118. package/dist/session/user-prompts.js +164 -0
  119. package/dist/session/user-prompts.js.map +1 -0
  120. package/dist/tan/challenge.d.ts +114 -0
  121. package/dist/tan/challenge.d.ts.map +1 -0
  122. package/dist/tan/challenge.js +185 -0
  123. package/dist/tan/challenge.js.map +1 -0
  124. package/dist/tan/flicker.d.ts +36 -0
  125. package/dist/tan/flicker.d.ts.map +1 -0
  126. package/dist/tan/flicker.js +262 -0
  127. package/dist/tan/flicker.js.map +1 -0
  128. package/dist/tan/tan-image.d.ts +21 -0
  129. package/dist/tan/tan-image.d.ts.map +1 -0
  130. package/dist/tan/tan-image.js +41 -0
  131. package/dist/tan/tan-image.js.map +1 -0
  132. package/dist/tan/tan-models.d.ts +145 -0
  133. package/dist/tan/tan-models.d.ts.map +1 -0
  134. package/dist/tan/tan-models.js +148 -0
  135. package/dist/tan/tan-models.js.map +1 -0
  136. package/dist/util/async.d.ts +24 -0
  137. package/dist/util/async.d.ts.map +1 -0
  138. package/dist/util/async.js +49 -0
  139. package/dist/util/async.js.map +1 -0
  140. package/dist/util/bytes.d.ts +23 -0
  141. package/dist/util/bytes.d.ts.map +1 -0
  142. package/dist/util/bytes.js +136 -0
  143. package/dist/util/bytes.js.map +1 -0
  144. package/dist/util/call-context.d.ts +12 -0
  145. package/dist/util/call-context.d.ts.map +1 -0
  146. package/dist/util/call-context.js +29 -0
  147. package/dist/util/call-context.js.map +1 -0
  148. package/dist/util/dates.d.ts +62 -0
  149. package/dist/util/dates.d.ts.map +1 -0
  150. package/dist/util/dates.js +169 -0
  151. package/dist/util/dates.js.map +1 -0
  152. package/dist/util/errors.d.ts +9 -0
  153. package/dist/util/errors.d.ts.map +1 -0
  154. package/dist/util/errors.js +15 -0
  155. package/dist/util/errors.js.map +1 -0
  156. package/dist/util/immutable-map.d.ts +16 -0
  157. package/dist/util/immutable-map.d.ts.map +1 -0
  158. package/dist/util/immutable-map.js +38 -0
  159. package/dist/util/immutable-map.js.map +1 -0
  160. package/dist/util/json.d.ts +13 -0
  161. package/dist/util/json.d.ts.map +1 -0
  162. package/dist/util/json.js +44 -0
  163. package/dist/util/json.js.map +1 -0
  164. package/dist/util/text.d.ts +20 -0
  165. package/dist/util/text.d.ts.map +1 -0
  166. package/dist/util/text.js +78 -0
  167. package/dist/util/text.js.map +1 -0
  168. package/dist/util/xml.d.ts +35 -0
  169. package/dist/util/xml.d.ts.map +1 -0
  170. package/dist/util/xml.js +183 -0
  171. package/dist/util/xml.js.map +1 -0
  172. package/dist/v3/account-extension.d.ts +25 -0
  173. package/dist/v3/account-extension.d.ts.map +1 -0
  174. package/dist/v3/account-extension.js +176 -0
  175. package/dist/v3/account-extension.js.map +1 -0
  176. package/dist/v3/client.d.ts +24 -0
  177. package/dist/v3/client.d.ts.map +1 -0
  178. package/dist/v3/client.js +16 -0
  179. package/dist/v3/client.js.map +1 -0
  180. package/dist/v3/dialog.d.ts +78 -0
  181. package/dist/v3/dialog.d.ts.map +1 -0
  182. package/dist/v3/dialog.js +138 -0
  183. package/dist/v3/dialog.js.map +1 -0
  184. package/dist/v3/message.d.ts +72 -0
  185. package/dist/v3/message.d.ts.map +1 -0
  186. package/dist/v3/message.js +259 -0
  187. package/dist/v3/message.js.map +1 -0
  188. package/dist/v3/parameters.d.ts +79 -0
  189. package/dist/v3/parameters.d.ts.map +1 -0
  190. package/dist/v3/parameters.js +396 -0
  191. package/dist/v3/parameters.js.map +1 -0
  192. package/dist/v3/protocol.d.ts +68 -0
  193. package/dist/v3/protocol.d.ts.map +1 -0
  194. package/dist/v3/protocol.js +263 -0
  195. package/dist/v3/protocol.js.map +1 -0
  196. package/dist/v3/segments.d.ts +89 -0
  197. package/dist/v3/segments.d.ts.map +1 -0
  198. package/dist/v3/segments.js +283 -0
  199. package/dist/v3/segments.js.map +1 -0
  200. package/dist/v3/syntax.d.ts +76 -0
  201. package/dist/v3/syntax.d.ts.map +1 -0
  202. package/dist/v3/syntax.js +301 -0
  203. package/dist/v3/syntax.js.map +1 -0
  204. package/package.json +63 -4
  205. package/src/core/account.ts +250 -0
  206. package/src/core/amount.ts +209 -0
  207. package/src/core/balance.ts +84 -0
  208. package/src/core/bank-info.ts +66 -0
  209. package/src/core/client.ts +149 -0
  210. package/src/core/config.ts +141 -0
  211. package/src/core/errors.ts +203 -0
  212. package/src/core/feedback.ts +137 -0
  213. package/src/core/transaction.ts +156 -0
  214. package/src/core/transport.ts +114 -0
  215. package/src/formats/camt.ts +169 -0
  216. package/src/formats/mt940.ts +333 -0
  217. package/src/formats/readers.ts +56 -0
  218. package/src/formats/sepa-purpose.ts +75 -0
  219. package/src/index.ts +68 -0
  220. package/src/session/app-approval.ts +265 -0
  221. package/src/session/bank-connection.ts +51 -0
  222. package/src/session/dialog-runner.ts +119 -0
  223. package/src/session/login-guard.ts +135 -0
  224. package/src/session/parameter-data.ts +137 -0
  225. package/src/session/pin-tan-client.ts +186 -0
  226. package/src/session/pin-tan-dialog.ts +160 -0
  227. package/src/session/protocol.ts +311 -0
  228. package/src/session/response-handler.ts +130 -0
  229. package/src/session/sepa-accounts.ts +97 -0
  230. package/src/session/session-setup.ts +165 -0
  231. package/src/session/session-state.ts +170 -0
  232. package/src/session/tan-selection.ts +192 -0
  233. package/src/session/user-prompts.ts +174 -0
  234. package/src/tan/challenge.ts +252 -0
  235. package/src/tan/flicker.ts +270 -0
  236. package/src/tan/tan-image.ts +36 -0
  237. package/src/tan/tan-models.ts +247 -0
  238. package/src/util/async.ts +53 -0
  239. package/src/util/bytes.ts +135 -0
  240. package/src/util/call-context.ts +40 -0
  241. package/src/util/dates.ts +193 -0
  242. package/src/util/errors.ts +16 -0
  243. package/src/util/immutable-map.ts +47 -0
  244. package/src/util/json.ts +51 -0
  245. package/src/util/text.ts +82 -0
  246. package/src/util/xml.ts +172 -0
  247. package/src/v3/account-extension.ts +190 -0
  248. package/src/v3/client.ts +29 -0
  249. package/src/v3/dialog.ts +199 -0
  250. package/src/v3/message.ts +321 -0
  251. package/src/v3/parameters.ts +451 -0
  252. package/src/v3/protocol.ts +331 -0
  253. package/src/v3/segments.ts +331 -0
  254. package/src/v3/syntax.ts +307 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ## 0.1.0
2
+
3
+ First release: a TypeScript port of the FinTS 3.0 client of the Dart package `fints`.
4
+
5
+ - `FinTs3Client`: accounts, balances and transactions (camt.052 and MT940/MT942), with paging and SEPA account details.
6
+ - One-step and two-step TAN, PSD2 strong customer authentication and app approval (decoupled), timed by the bank's
7
+ parameters; TAN method and medium selection, chipTAN flicker codes and photoTAN images.
8
+ - Bank parameter data, customer system ID and the chosen TAN method in an exportable state string, in the layout of the
9
+ Dart package.
10
+ - A client never sends its PIN again once the bank has refused a login.
11
+ - Runs wherever `fetch`, Web Crypto and `TextDecoder` are available; no runtime dependencies.
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ Copyright 2026, the fints project authors.
2
+
3
+ Redistribution and use in source and binary forms, with or without
4
+ modification, are permitted provided that the following conditions are
5
+ met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+
10
+ 2. Redistributions in binary form must reproduce the above copyright
11
+ notice, this list of conditions and the following disclaimer in the
12
+ documentation and/or other materials provided with the distribution.
13
+
14
+ 3. Neither the name of the copyright holder nor the names of its
15
+ contributors may be used to endorse or promote products derived from
16
+ this software without specific prior written permission.
17
+
18
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -1,3 +1,157 @@
1
- # Temporary Holding Version
1
+ # @coderoftime/fints
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A FinTS 3.0 client for German banks, written in TypeScript, without runtime dependencies. It reads accounts, balances and transactions with PIN/TAN, including PSD2 strong customer authentication and app approval (pushTAN, SecureGo plus and the like).
4
+
5
+ It is a port of the Dart package [`fints`](https://pub.dev/packages/fints) and behaves the same: same session logic, same safeguards, same tests. FinTS 4.1, which hardly any bank offers, is not part of the port.
6
+
7
+ ## Features
8
+
9
+ - **PIN/TAN** with the one-step method (`999`) and two-step methods of process variant 2 (`HKTAN` process 4, then 2 or `S`).
10
+ - **Strong customer authentication** at login and per transaction where the bank asks for it, including exemptions (`3076`).
11
+ - **App approval** (decoupled), polled by the bank's own parameters; the client stops when the app is closed or the user cancels.
12
+ - **TAN media** lookup and selection, **chipTAN flicker codes** (HHD 1.3 and 1.4, as frames to animate) and **photoTAN/QR images** (as `data:` URLs).
13
+ - **Balances** (`HKSAL` 6–8) and **transactions** as camt.052 (`HKCAZ`) or MT940/MT942 (`HKKAZ` 6–7), with automatic paging.
14
+ - **IBAN and BIC** from the SEPA account list (`HKSPA`), and the account extension of the user parameter data.
15
+ - **Safe by default**: the PIN is never sent again after the bank refused a login, so a retrying app cannot lock the access; only what the bank's parameter data permit is sent; PIN and TAN are masked in every log.
16
+ - **Saved sessions**: bank and user parameters, customer system ID and the chosen TAN method in one state string, which spares the setup steps at the next start.
17
+ - Parsers for MT940 (German structured `:86:` fields, SEPA keywords) and camt.052/053.
18
+
19
+ ## Requirements
20
+
21
+ 1. **A FinTS product registration.** German banks only accept registered products. Register for free with the Deutsche Kreditwirtschaft at <https://www.fints.org/de/hersteller/produktregistrierung> and pass the ID as `productId`. Keep it out of your repository.
22
+ 2. **Your bank's FinTS URL and bank code (BLZ)**, for example from the DK institute list.
23
+ 3. **A runtime with `fetch`, Web Crypto and `TextDecoder`**: Node.js 22.12 or later, Deno, Bun, or a browser behind a server of your own (see [Using it with Vue](#using-it-with-vue)). The type declarations need TypeScript 5.6 or later.
24
+
25
+ ## Usage
26
+
27
+ ```ts
28
+ import { FinTs3Client, TanAnswer } from '@coderoftime/fints';
29
+
30
+ const client = new FinTs3Client({
31
+ config: {
32
+ url: 'https://fints.example-bank.de/fints',
33
+ bankCode: '12345678',
34
+ userId: 'my-login',
35
+ productId: process.env.FINTS_PRODUCT_ID!,
36
+ productVersion: '1.0',
37
+ },
38
+ pin,
39
+ options: {
40
+ state: savedState, // from a previous session, or undefined
41
+ tanMethodSelector: (methods) => pickMethod(methods),
42
+ tanMediumSelector: (media) => pickMedium(media),
43
+ onBankMessage: (message) => showNotice(message.subject, message.text),
44
+ onWarning: (warning) => showNotice(warning.code, warning.text), // banks want code and text shown
45
+ tanCallback: async (challenge) => {
46
+ if (challenge.isDecoupled) {
47
+ // Close the prompt when challenge.resolved settles; offer "approved" only if challenge.manualConfirmation.
48
+ const confirmed = await showApprovalPrompt(challenge);
49
+ return confirmed ? TanAnswer.approved : TanAnswer.cancelled;
50
+ }
51
+ // Show challenge.plainMessage, challenge.image?.toDataUrl() or challenge.flickerCode?.frames().
52
+ const tan = await askForTan(challenge);
53
+ return tan === undefined ? TanAnswer.cancelled : TanAnswer.tan(tan);
54
+ },
55
+ },
56
+ });
57
+
58
+ try {
59
+ const accounts = await client.getAccounts();
60
+ const balance = await client.getBalance(accounts[0]!);
61
+ const transactions = await client.getTransactions(accounts[0]!, { from: new Date(2024, 0, 1) });
62
+ } finally {
63
+ saveEncrypted(client.state); // no PIN or TAN, but account data
64
+ await client.close();
65
+ }
66
+ ```
67
+
68
+ All calls share one bank dialog, so the user approves once per session. Calls run one after another; a call from inside a callback would wait for itself, so on Node.js, Deno and Bun it fails with `FinTsStateError` (in browsers it is not detected).
69
+
70
+ Calendar dates, such as `Transaction.bookingDate`, are `Date`s at local midnight, of which only the day counts. Moments, such as `Balance.bookedAsOfTime`, are exact; the bank states them in German time.
71
+
72
+ ## Using it with Vue
73
+
74
+ **A web page cannot talk to a bank's FinTS server directly.** Browsers only let a page call another origin if the server allows it (CORS), and bank servers do not. So some server of yours always sits between the browser and the bank, and **that server sees the PIN**: FinTS PIN/TAN messages carry it in plain text inside TLS. It must be a server you and your users trust.
75
+
76
+ There are three ways to set it up:
77
+
78
+ 1. **The client runs on your server** (Nuxt server routes, Express, …). The Vue app calls your own API; the server keeps one `FinTs3Client` per session and passes the TAN challenges to the browser, for example with a pending request the browser answers. This keeps the bank traffic and the product ID on the server.
79
+ 2. **The client runs in the browser, through a relay.** The library runs in the Vue app, so TAN prompts are plain local UI, and a small endpoint of yours forwards each message to the bank. Pass a transport that posts to it:
80
+
81
+ ```ts
82
+ import type { FinTsTransport } from '@coderoftime/fints';
83
+
84
+ const relay: FinTsTransport = {
85
+ async post(url, body, contentType) {
86
+ const response = await fetch('/api/fints-relay', {
87
+ method: 'POST',
88
+ headers: { 'Content-Type': contentType, 'X-FinTS-URL': url.href },
89
+ body,
90
+ });
91
+ if (!response.ok) throw new Error(`Relay: HTTP ${response.status}`);
92
+ return new Uint8Array(await response.arrayBuffer());
93
+ },
94
+ close() {},
95
+ };
96
+ ```
97
+
98
+ The relay must forward only to the bank URLs of the DK institute list, never to any URL a caller names, or it is an open proxy. The product ID ends up in the page; anyone can read it there.
99
+
100
+ 3. **The app is not a web page**: Electron, Tauri or Capacitor can send requests without CORS. Pass a `FinTsTransport` that uses the platform's HTTP client, or `HttpTransport` where `fetch` is not bound by CORS.
101
+
102
+ A TAN prompt in Vue fits the callback as a promise the dialog resolves:
103
+
104
+ ```ts
105
+ import { ref } from 'vue';
106
+ import { TanAnswer, type TanChallenge } from '@coderoftime/fints';
107
+
108
+ export const pending = ref<{ challenge: TanChallenge; answer: (a: TanAnswer) => void }>();
109
+
110
+ export function tanCallback(challenge: TanChallenge): Promise<TanAnswer> {
111
+ return new Promise((answer) => {
112
+ pending.value = { challenge, answer };
113
+ void challenge.resolved?.then(() => (pending.value = undefined)); // the bank confirmed the app approval
114
+ });
115
+ }
116
+ // The dialog component shows pending.value.challenge and calls pending.value.answer(TanAnswer.tan(input)),
117
+ // TanAnswer.approved or TanAnswer.cancelled, then clears pending.value.
118
+ ```
119
+
120
+ ## Errors
121
+
122
+ | Error | Meaning |
123
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
124
+ | `FinTsPinError` | Wrong or blocked PIN. The client sends no PIN any more; create a new client to try again. |
125
+ | `FinTsTanError` | Wrong, expired or used TAN. The session stays usable. |
126
+ | `FinTsTanCancelledError` | The user cancelled the TAN or the app approval. |
127
+ | `FinTsDecoupledTimeoutError` | The app approval did not come within the bank's limit. |
128
+ | `FinTsLoginDataChangedError` | The bank issued new login data (`3072`); `loginName` tells it. |
129
+ | `FinTsDialogAbortedError` | The bank ended the dialog; the next call logs in anew. |
130
+ | `FinTsBankError` | Any other rejection; `feedback` holds the return codes. After a refused login, later calls fail with the same error without contacting the bank. |
131
+ | `FinTsUnsupportedError` | The bank or the user's parameter data do not offer the transaction; nothing was sent. |
132
+ | `FinTsProtocolError` | The bank's answer could not be read. |
133
+ | `FinTsTransportError` | No connection, an HTTP error or a timeout. |
134
+ | `FinTsStateError` | The client was closed, or called from inside one of its callbacks. |
135
+ | `TypeError` | An invalid argument, such as an empty PIN or a period that ends before it starts; the message never contains the PIN. |
136
+
137
+ The client never sends a message larger than the bank's maximum (`BankInfo.maxMessageSize`, in kilobytes), and never repeats a call by itself: the bank's answer cannot tell whether an order went through.
138
+
139
+ ## Differences from the Dart package
140
+
141
+ - Errors are named `…Error` instead of `…Exception`; invalid arguments are `TypeError`s, malformed data `FormatError`s.
142
+ - `getTransactions(account, { from, to, format })` takes an options object; enums are string unions such as `'mt940'`.
143
+ - The polling parameters of `TanMethod` are in seconds: `decoupledFirstPollDelaySeconds`, `decoupledPollIntervalSeconds`.
144
+ - `HttpTransport` takes a `fetch` and `timeoutMs`; `TanImage.toDataUrl()` is new.
145
+ - The saved state has the same layout, so a state saved by one package can be restored by the other.
146
+
147
+ ## Development
148
+
149
+ ```sh
150
+ npm install
151
+ npm run check # type check, lint, format check and tests
152
+ npm run build
153
+ ```
154
+
155
+ ## License
156
+
157
+ BSD 3-Clause, see [LICENSE](LICENSE). Commercial use is allowed; a product that ships the library must reproduce its copyright notice and license in its documentation or notices.
@@ -0,0 +1,140 @@
1
+ /** Account kind derived from the FinTS account type code. */
2
+ export type AccountCategory =
3
+ /** Current account (type codes 1 to 9). */
4
+ 'checking'
5
+ /** Savings account (10 to 19). */
6
+ | 'savings'
7
+ /** Fixed-term deposit (20 to 29). */
8
+ | 'fixedDeposit'
9
+ /** Securities account (30 to 39). */
10
+ | 'securities'
11
+ /** Loan account (40 to 49). */
12
+ | 'loan'
13
+ /** Credit card account (50 to 59). */
14
+ | 'creditCard'
15
+ /** Fund deposit (60 to 69). */
16
+ | 'fund'
17
+ /** Home savings contract (70 to 79). */
18
+ | 'homeSavings'
19
+ /** Insurance (80 to 89). */
20
+ | 'insurance'
21
+ /** Any other type code. */
22
+ | 'other'
23
+ /** The bank states no type code. */
24
+ | 'unknown';
25
+ /** The category of the FinTS account type `code`, which is `undefined` if the bank states none. */
26
+ export declare function accountCategoryOf(code: number | undefined): AccountCategory;
27
+ /** What `Account` takes; the bank states the details it knows. */
28
+ export interface AccountInit {
29
+ accountNumber?: string | undefined;
30
+ subAccount?: string | undefined;
31
+ bankCode: string;
32
+ countryCode?: string | undefined;
33
+ iban?: string | undefined;
34
+ bic?: string | undefined;
35
+ customerId?: string | undefined;
36
+ currency?: string | undefined;
37
+ ownerName?: string | undefined;
38
+ productName?: string | undefined;
39
+ typeCode?: number | undefined;
40
+ allowedTransactions?: readonly string[] | undefined;
41
+ allowedTransactionsComplete?: boolean | undefined;
42
+ dataStatus?: AccountDataStatus | undefined;
43
+ }
44
+ /** A bank account as reported by the user parameter data or the SEPA account query. */
45
+ export declare class Account {
46
+ /** National account number. */
47
+ readonly accountNumber: string | undefined;
48
+ /** Sub-account characteristic (Unterkontomerkmal). */
49
+ readonly subAccount: string | undefined;
50
+ /** German bank code (BLZ) of the bank holding the account. */
51
+ readonly bankCode: string;
52
+ /** Numeric country code of the bank, `280` for Germany. */
53
+ readonly countryCode: string;
54
+ /** International bank account number. */
55
+ readonly iban: string | undefined;
56
+ /** Bank identifier code of the bank holding the account. */
57
+ readonly bic: string | undefined;
58
+ /** Customer ID the account is assigned to; may differ from the login name. */
59
+ readonly customerId: string | undefined;
60
+ /** Currency of the account, for example `EUR`. */
61
+ readonly currency: string | undefined;
62
+ /** Name of the account holder. */
63
+ readonly ownerName: string | undefined;
64
+ /** The bank's name for the account, for example `Girokonto`. */
65
+ readonly productName: string | undefined;
66
+ /** Raw FinTS account type (1–99); see `category`. */
67
+ readonly typeCode: number | undefined;
68
+ /**
69
+ * The transactions the bank lists as permitted for this account, by their segment IDs, such as `HKSAL`.
70
+ *
71
+ * The client applies them itself: `getBalance` and `getTransactions` throw `FinTsUnsupportedError`, without asking
72
+ * the bank, where the parameter data rule the query out.
73
+ */
74
+ readonly allowedTransactions: readonly string[];
75
+ /**
76
+ * Whether `allowedTransactions` is complete: the bank permits nothing it does not list (FinTS "UPD-Verwendung" 0).
77
+ * Otherwise the bank's lists say nothing about a transaction they leave out, and only the bank can tell.
78
+ */
79
+ readonly allowedTransactionsComplete: boolean;
80
+ /** What the bank tells about the data it holds for the account, where it does. */
81
+ readonly dataStatus: AccountDataStatus | undefined;
82
+ constructor(init: AccountInit);
83
+ /** The kind of account, derived from `typeCode`. */
84
+ get category(): AccountCategory;
85
+ /**
86
+ * Whether `other` is the same account: by IBAN when both have one, otherwise by account number, sub-account and
87
+ * bank.
88
+ *
89
+ * Account numbers are compared without leading zeros, since banks pad them differently. An empty bank or country
90
+ * code, as of a balance record without bank identification, matches any.
91
+ */
92
+ matches(other: Account): boolean;
93
+ /** This account with the given details replaced; a detail that is `undefined` stays as it is. */
94
+ copyWith(changes: Omit<Partial<AccountInit>, 'bankCode' | 'countryCode'>): Account;
95
+ /**
96
+ * The product name and the last digits of the IBAN or account number only, so that logs and crash reports do not
97
+ * collect account data.
98
+ */
99
+ toString(): string;
100
+ }
101
+ /**
102
+ * What the bank tells about the data it holds for an account, so that an app asks only for what has changed (FinTS 3.0
103
+ * user parameter data, "Erweiterung, kontobezogen", Formals E.3.1 and F).
104
+ *
105
+ * The rules are binding for an app that stores transactions; banks may refuse an app that ignores them:
106
+ * - Do not fetch the transactions again while the bank names the ID they were stored with (`mt940TransactionsId` or
107
+ * `camtTransactionsId`, for the format the app asked for, so pass `format` to `getTransactions` explicitly), or, in
108
+ * the older format, the timestamp `lastTransactions`.
109
+ * - Do not fetch them before `nextTransactionsAt`.
110
+ * - Do not ask for the balance separately where `transactionBalancesCurrent` says that the transactions carry the
111
+ * current one.
112
+ */
113
+ export declare class AccountDataStatus {
114
+ /**
115
+ * IDs of the data the bank holds for the user, keyed by the segment ID of the query that fetches them, for example
116
+ * `HKKAZ` (transactions as MT940) or `HKCAZ` (as camt): the data have changed when the ID changes. An empty ID means
117
+ * that the user has no such data; a query the map leaves out, that the bank tells nothing about it.
118
+ */
119
+ readonly inventory: ReadonlyMap<string, string>;
120
+ /**
121
+ * The timestamp of the last transactions the bank provided, in the older format of the extension (`umsltzt`);
122
+ * compared like an ID in `inventory`.
123
+ */
124
+ readonly lastTransactions: string | undefined;
125
+ /** The moment before which the bank provides no new transactions. */
126
+ readonly nextTransactionsAt: Date | undefined;
127
+ /** Whether the balances the transactions carry are the current ones. */
128
+ readonly transactionBalancesCurrent: boolean;
129
+ constructor(init: {
130
+ inventory?: ReadonlyMap<string, string>;
131
+ lastTransactions?: string | undefined;
132
+ nextTransactionsAt?: Date | undefined;
133
+ transactionBalancesCurrent?: boolean;
134
+ });
135
+ /** The ID of the transactions the bank holds as MT940, its entry for `HKKAZ` in `inventory`. */
136
+ get mt940TransactionsId(): string | undefined;
137
+ /** The ID of the transactions the bank holds as camt, its entry for `HKCAZ` in `inventory`. */
138
+ get camtTransactionsId(): string | undefined;
139
+ }
140
+ //# sourceMappingURL=account.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.d.ts","sourceRoot":"","sources":["../../src/core/account.ts"],"names":[],"mappings":"AAIA,6DAA6D;AAC7D,MAAM,MAAM,eAAe;AACzB,2CAA2C;AACzC,UAAU;AACZ,kCAAkC;GAChC,SAAS;AACX,qCAAqC;GACnC,cAAc;AAChB,qCAAqC;GACnC,YAAY;AACd,+BAA+B;GAC7B,MAAM;AACR,sCAAsC;GACpC,YAAY;AACd,+BAA+B;GAC7B,MAAM;AACR,wCAAwC;GACtC,aAAa;AACf,4BAA4B;GAC1B,WAAW;AACb,2BAA2B;GACzB,OAAO;AACT,oCAAoC;GAClC,SAAS,CAAC;AAcd,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,eAAe,CAG3E;AAED,kEAAkE;AAClE,MAAM,WAAW,WAAW;IAC1B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,mBAAmB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACpD,2BAA2B,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAClD,UAAU,CAAC,EAAE,iBAAiB,GAAG,SAAS,CAAC;CAC5C;AAED,uFAAuF;AACvF,qBAAa,OAAO;IAClB,+BAA+B;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;IAE3C,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAExC,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,2DAA2D;IAC3D,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAE7B,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IAElC,4DAA4D;IAC5D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IAEjC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAExC,kDAAkD;IAClD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAEtC,kCAAkC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAEvC,gEAAgE;IAChE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAEzC,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAEtC;;;;;OAKG;IACH,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IAEhD;;;OAGG;IACH,QAAQ,CAAC,2BAA2B,EAAE,OAAO,CAAC;IAE9C,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,iBAAiB,GAAG,SAAS,CAAC;gBAEvC,IAAI,EAAE,WAAW;IAiB7B,oDAAoD;IACpD,IAAI,QAAQ,IAAI,eAAe,CAE9B;IAED;;;;;;OAMG;IACH,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAYhC,iGAAiG;IACjG,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,UAAU,GAAG,aAAa,CAAC,GAAG,OAAO;IAmBlF;;;OAGG;IACH,QAAQ,IAAI,MAAM;CAQnB;AAMD;;;;;;;;;;;GAWG;AACH,qBAAa,iBAAiB;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhD;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAAC;IAE9C,qEAAqE;IACrE,QAAQ,CAAC,kBAAkB,EAAE,IAAI,GAAG,SAAS,CAAC;IAE9C,wEAAwE;IACxE,QAAQ,CAAC,0BAA0B,EAAE,OAAO,CAAC;gBAEjC,IAAI,EAAE;QAChB,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACxC,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QACtC,kBAAkB,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;QACtC,0BAA0B,CAAC,EAAE,OAAO,CAAC;KACtC;IAOD,gGAAgG;IAChG,IAAI,mBAAmB,IAAI,MAAM,GAAG,SAAS,CAE5C;IAED,+FAA+F;IAC/F,IAAI,kBAAkB,IAAI,MAAM,GAAG,SAAS,CAE3C;CACF"}
@@ -0,0 +1,174 @@
1
+ import { ImmutableMap } from '../util/immutable-map.js';
2
+ import { maskedId } from '../util/text.js';
3
+ import { defaultCountryCode } from './config.js';
4
+ const categoriesByTens = [
5
+ 'checking',
6
+ 'savings',
7
+ 'fixedDeposit',
8
+ 'securities',
9
+ 'loan',
10
+ 'creditCard',
11
+ 'fund',
12
+ 'homeSavings',
13
+ 'insurance',
14
+ ];
15
+ /** The category of the FinTS account type `code`, which is `undefined` if the bank states none. */
16
+ export function accountCategoryOf(code) {
17
+ if (code === undefined || code < 1)
18
+ return 'unknown';
19
+ return categoriesByTens[Math.floor(code / 10)] ?? 'other';
20
+ }
21
+ /** A bank account as reported by the user parameter data or the SEPA account query. */
22
+ export class Account {
23
+ /** National account number. */
24
+ accountNumber;
25
+ /** Sub-account characteristic (Unterkontomerkmal). */
26
+ subAccount;
27
+ /** German bank code (BLZ) of the bank holding the account. */
28
+ bankCode;
29
+ /** Numeric country code of the bank, `280` for Germany. */
30
+ countryCode;
31
+ /** International bank account number. */
32
+ iban;
33
+ /** Bank identifier code of the bank holding the account. */
34
+ bic;
35
+ /** Customer ID the account is assigned to; may differ from the login name. */
36
+ customerId;
37
+ /** Currency of the account, for example `EUR`. */
38
+ currency;
39
+ /** Name of the account holder. */
40
+ ownerName;
41
+ /** The bank's name for the account, for example `Girokonto`. */
42
+ productName;
43
+ /** Raw FinTS account type (1–99); see `category`. */
44
+ typeCode;
45
+ /**
46
+ * The transactions the bank lists as permitted for this account, by their segment IDs, such as `HKSAL`.
47
+ *
48
+ * The client applies them itself: `getBalance` and `getTransactions` throw `FinTsUnsupportedError`, without asking
49
+ * the bank, where the parameter data rule the query out.
50
+ */
51
+ allowedTransactions;
52
+ /**
53
+ * Whether `allowedTransactions` is complete: the bank permits nothing it does not list (FinTS "UPD-Verwendung" 0).
54
+ * Otherwise the bank's lists say nothing about a transaction they leave out, and only the bank can tell.
55
+ */
56
+ allowedTransactionsComplete;
57
+ /** What the bank tells about the data it holds for the account, where it does. */
58
+ dataStatus;
59
+ constructor(init) {
60
+ this.accountNumber = init.accountNumber;
61
+ this.subAccount = init.subAccount;
62
+ this.bankCode = init.bankCode;
63
+ this.countryCode = init.countryCode ?? defaultCountryCode;
64
+ this.iban = init.iban;
65
+ this.bic = init.bic;
66
+ this.customerId = init.customerId;
67
+ this.currency = init.currency;
68
+ this.ownerName = init.ownerName;
69
+ this.productName = init.productName;
70
+ this.typeCode = init.typeCode;
71
+ this.allowedTransactions = Object.freeze([...(init.allowedTransactions ?? [])]);
72
+ this.allowedTransactionsComplete = init.allowedTransactionsComplete ?? false;
73
+ this.dataStatus = init.dataStatus;
74
+ }
75
+ /** The kind of account, derived from `typeCode`. */
76
+ get category() {
77
+ return accountCategoryOf(this.typeCode);
78
+ }
79
+ /**
80
+ * Whether `other` is the same account: by IBAN when both have one, otherwise by account number, sub-account and
81
+ * bank.
82
+ *
83
+ * Account numbers are compared without leading zeros, since banks pad them differently. An empty bank or country
84
+ * code, as of a balance record without bank identification, matches any.
85
+ */
86
+ matches(other) {
87
+ if (this.iban !== undefined && other.iban !== undefined)
88
+ return this.iban === other.iban;
89
+ const number = this.accountNumber?.replace(/^0+/, '');
90
+ return (number !== undefined &&
91
+ number === other.accountNumber?.replace(/^0+/, '') &&
92
+ (this.subAccount ?? '') === (other.subAccount ?? '') &&
93
+ sameOrUnstated(this.bankCode, other.bankCode) &&
94
+ sameOrUnstated(this.countryCode, other.countryCode));
95
+ }
96
+ /** This account with the given details replaced; a detail that is `undefined` stays as it is. */
97
+ copyWith(changes) {
98
+ return new Account({
99
+ accountNumber: changes.accountNumber ?? this.accountNumber,
100
+ subAccount: changes.subAccount ?? this.subAccount,
101
+ bankCode: this.bankCode,
102
+ countryCode: this.countryCode,
103
+ iban: changes.iban ?? this.iban,
104
+ bic: changes.bic ?? this.bic,
105
+ customerId: changes.customerId ?? this.customerId,
106
+ currency: changes.currency ?? this.currency,
107
+ ownerName: changes.ownerName ?? this.ownerName,
108
+ productName: changes.productName ?? this.productName,
109
+ typeCode: changes.typeCode ?? this.typeCode,
110
+ allowedTransactions: changes.allowedTransactions ?? this.allowedTransactions,
111
+ allowedTransactionsComplete: changes.allowedTransactionsComplete ?? this.allowedTransactionsComplete,
112
+ dataStatus: changes.dataStatus ?? this.dataStatus,
113
+ });
114
+ }
115
+ /**
116
+ * The product name and the last digits of the IBAN or account number only, so that logs and crash reports do not
117
+ * collect account data.
118
+ */
119
+ toString() {
120
+ const id = this.iban ?? this.accountNumber;
121
+ const parts = [
122
+ ...(id === undefined ? [] : [maskedId(id)]),
123
+ ...(this.productName === undefined ? [] : [this.productName]),
124
+ ];
125
+ return `Account(${parts.join(', ')})`;
126
+ }
127
+ }
128
+ function sameOrUnstated(a, b) {
129
+ return a === '' || b === '' || a === b;
130
+ }
131
+ /**
132
+ * What the bank tells about the data it holds for an account, so that an app asks only for what has changed (FinTS 3.0
133
+ * user parameter data, "Erweiterung, kontobezogen", Formals E.3.1 and F).
134
+ *
135
+ * The rules are binding for an app that stores transactions; banks may refuse an app that ignores them:
136
+ * - Do not fetch the transactions again while the bank names the ID they were stored with (`mt940TransactionsId` or
137
+ * `camtTransactionsId`, for the format the app asked for, so pass `format` to `getTransactions` explicitly), or, in
138
+ * the older format, the timestamp `lastTransactions`.
139
+ * - Do not fetch them before `nextTransactionsAt`.
140
+ * - Do not ask for the balance separately where `transactionBalancesCurrent` says that the transactions carry the
141
+ * current one.
142
+ */
143
+ export class AccountDataStatus {
144
+ /**
145
+ * IDs of the data the bank holds for the user, keyed by the segment ID of the query that fetches them, for example
146
+ * `HKKAZ` (transactions as MT940) or `HKCAZ` (as camt): the data have changed when the ID changes. An empty ID means
147
+ * that the user has no such data; a query the map leaves out, that the bank tells nothing about it.
148
+ */
149
+ inventory;
150
+ /**
151
+ * The timestamp of the last transactions the bank provided, in the older format of the extension (`umsltzt`);
152
+ * compared like an ID in `inventory`.
153
+ */
154
+ lastTransactions;
155
+ /** The moment before which the bank provides no new transactions. */
156
+ nextTransactionsAt;
157
+ /** Whether the balances the transactions carry are the current ones. */
158
+ transactionBalancesCurrent;
159
+ constructor(init) {
160
+ this.inventory = new ImmutableMap(init.inventory ?? []);
161
+ this.lastTransactions = init.lastTransactions;
162
+ this.nextTransactionsAt = init.nextTransactionsAt;
163
+ this.transactionBalancesCurrent = init.transactionBalancesCurrent ?? false;
164
+ }
165
+ /** The ID of the transactions the bank holds as MT940, its entry for `HKKAZ` in `inventory`. */
166
+ get mt940TransactionsId() {
167
+ return this.inventory.get('HKKAZ');
168
+ }
169
+ /** The ID of the transactions the bank holds as camt, its entry for `HKCAZ` in `inventory`. */
170
+ get camtTransactionsId() {
171
+ return this.inventory.get('HKCAZ');
172
+ }
173
+ }
174
+ //# sourceMappingURL=account.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/core/account.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /** A money amount stored as an integer number of `scale`-digit minor units. */
2
+ export declare class Amount {
3
+ /** The amount in units of 10^-`scale` of the currency, negative for a debit: 1234 with scale 2 is 12.34. */
4
+ readonly minorUnits: number;
5
+ /** Number of decimal places `minorUnits` has, from 0 to 5. */
6
+ readonly scale: number;
7
+ /** ISO 4217 currency code, for example `EUR`. */
8
+ readonly currency: string;
9
+ /**
10
+ * An amount of `minorUnits` with `scale` decimal places; throws `RangeError` for a `scale` outside 0 to 5, and for
11
+ * `minorUnits` that are no integer or beyond 2^53 - 1 in either direction, about 90 trillion at two decimal places.
12
+ */
13
+ constructor(minorUnits: number, scale: number, currency: string);
14
+ /**
15
+ * Parses a number as banks send it: digits with at most one decimal separator, `,` (FinTS 3.0, MT940) or `.` (camt),
16
+ * and no thousands separators.
17
+ *
18
+ * The amount has the decimal places of the `currency`'s minor unit (ISO 4217): two for most currencies, none for
19
+ * `JPY`, three for `KWD`. It has more where the value has more significant digits. So `100,` in `EUR` is `100.00`, in
20
+ * `JPY` `100`.
21
+ *
22
+ * Throws `FormatError` for anything else, so that `1.234,56` in bank data is an error, not a guess, and for more than
23
+ * five decimal places (trailing zeros aside) or more than 15 digits.
24
+ */
25
+ static parseDecimal(value: string, currency: string): Amount;
26
+ /** The amount as a floating-point number, which cannot hold every decimal fraction exactly; do not calculate with it. */
27
+ get value(): number;
28
+ /** Whether the amount is below zero. */
29
+ get isNegative(): boolean;
30
+ /** Whether the amount is zero. */
31
+ get isZero(): boolean;
32
+ /** This amount with the opposite sign. */
33
+ negate(): Amount;
34
+ /** This amount without its sign. */
35
+ abs(): Amount;
36
+ /**
37
+ * The sum with `other`, at the larger of the two scales; throws `TypeError` for another currency and `RangeError` for
38
+ * a sum beyond what an amount holds.
39
+ */
40
+ add(other: Amount): Amount;
41
+ /** The difference to `other`; throws `TypeError` for another currency. */
42
+ subtract(other: Amount): Amount;
43
+ /** Compares by value, whatever the scales; throws `TypeError` for another currency. */
44
+ compareTo(other: Amount): number;
45
+ /** Whether `other` is the same amount in the same currency, whatever the `scale`: 1.50 equals 1.500. */
46
+ equals(other: unknown): boolean;
47
+ /** Formats the amount without currency, for example `-12.50`. */
48
+ toDecimalString(decimalSeparator?: string): string;
49
+ toString(): string;
50
+ /** The minor units of both amounts at the larger scale, as `bigint`s, since the factor could leave the safe range. */
51
+ private atCommonScale;
52
+ private rescale;
53
+ private compareValue;
54
+ private checkCurrency;
55
+ }
56
+ /**
57
+ * `amount` as a credit (unchanged) or a debit (negated), by the bank's credit/debit `mark`.
58
+ *
59
+ * Throws `FormatError` for any other `mark`, `undefined` included: a missing mark must not be read as a credit.
60
+ */
61
+ export declare function withMark(amount: Amount, mark: string | undefined, credit: string, debit: string): Amount;
62
+ //# sourceMappingURL=amount.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"amount.d.ts","sourceRoot":"","sources":["../../src/core/amount.ts"],"names":[],"mappings":"AAmDA,+EAA+E;AAC/E,qBAAa,MAAM;IACjB,4GAA4G;IAC5G,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAEvB,iDAAiD;IACjD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B;;;OAGG;gBACS,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAW/D;;;;;;;;;;OAUG;IACH,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAmB5D,yHAAyH;IACzH,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,wCAAwC;IACxC,IAAI,UAAU,IAAI,OAAO,CAExB;IAED,kCAAkC;IAClC,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,0CAA0C;IAC1C,MAAM,IAAI,MAAM;IAIhB,oCAAoC;IACpC,GAAG,IAAI,MAAM;IAIb;;;OAGG;IACH,GAAG,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAU1B,0EAA0E;IAC1E,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAI/B,uFAAuF;IACvF,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAKhC,wGAAwG;IACxG,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;IAI/B,iEAAiE;IACjE,eAAe,CAAC,gBAAgB,SAAM,GAAG,MAAM;IAU/C,QAAQ,IAAI,MAAM;IAIlB,sHAAsH;IACtH,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,OAAO;IAIf,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;CAItB;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAIxG"}