@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
@@ -0,0 +1,34 @@
1
+ import type { Account } from '../core/account.js';
2
+ import type { DialogRunner } from './dialog-runner.js';
3
+ import type { LoginGuard } from './login-guard.js';
4
+ import type { BankResponse, PinTanProtocol } from './protocol.js';
5
+ import type { SessionState } from './session-state.js';
6
+ /**
7
+ * IBAN and BIC of the user's accounts, from the bank's SEPA account list where the user parameter data lack them
8
+ * (Formals F.3.4).
9
+ *
10
+ * The list is read once per version of the user parameter data and kept in the saved state. Under version 0, which
11
+ * holds for one dialog only (Formals E.2), without user parameter data, or where the bank rejects the list, it is kept
12
+ * for this client only. Where the bank ends the dialog in answer to the list, the next call asks once more.
13
+ */
14
+ export declare class SepaAccounts<R extends BankResponse, O> {
15
+ private readonly protocol;
16
+ private readonly state;
17
+ private readonly dialogs;
18
+ private readonly guard;
19
+ /**
20
+ * The list this client keeps unsaved, with the version of the user parameter data it was read under; empty where the
21
+ * bank rejected it.
22
+ */
23
+ private unsaved;
24
+ /** Whether a call already went on without the list because the bank ended the dialog in answer to it. */
25
+ private abortSkipped;
26
+ constructor(protocol: PinTanProtocol<R, O>, state: SessionState, dialogs: DialogRunner<R, O>, guard: LoginGuard);
27
+ /** The accounts of the user parameter data completed with IBAN and BIC, then the accounts only the list names. */
28
+ completedAccounts(): Promise<Account[]>;
29
+ /** `account` with IBAN and BIC filled in from the list. */
30
+ complete(account: Account): Promise<Account>;
31
+ /** The list, read in the session's dialog, whose start delivers the user parameter data it is cached for. */
32
+ private list;
33
+ }
34
+ //# sourceMappingURL=sepa-accounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sepa-accounts.d.ts","sourceRoot":"","sources":["../../src/session/sepa-accounts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;;;GAOG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC;IAW/C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAbxB;;;OAGG;IACH,OAAO,CAAC,OAAO,CAA+E;IAE9F,yGAAyG;IACzG,OAAO,CAAC,YAAY,CAAS;gBAGV,QAAQ,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EAC3B,KAAK,EAAE,UAAU;IAGpC,kHAAkH;IAC5G,iBAAiB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;IAS7C,2DAA2D;IACrD,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAKlD,6GAA6G;YAC/F,IAAI;CAwCnB"}
@@ -0,0 +1,96 @@
1
+ import { FinTsDialogAbortedError } from '../core/errors.js';
2
+ /**
3
+ * IBAN and BIC of the user's accounts, from the bank's SEPA account list where the user parameter data lack them
4
+ * (Formals F.3.4).
5
+ *
6
+ * The list is read once per version of the user parameter data and kept in the saved state. Under version 0, which
7
+ * holds for one dialog only (Formals E.2), without user parameter data, or where the bank rejects the list, it is kept
8
+ * for this client only. Where the bank ends the dialog in answer to the list, the next call asks once more.
9
+ */
10
+ export class SepaAccounts {
11
+ protocol;
12
+ state;
13
+ dialogs;
14
+ guard;
15
+ /**
16
+ * The list this client keeps unsaved, with the version of the user parameter data it was read under; empty where the
17
+ * bank rejected it.
18
+ */
19
+ unsaved;
20
+ /** Whether a call already went on without the list because the bank ended the dialog in answer to it. */
21
+ abortSkipped = false;
22
+ constructor(protocol, state, dialogs, guard) {
23
+ this.protocol = protocol;
24
+ this.state = state;
25
+ this.dialogs = dialogs;
26
+ this.guard = guard;
27
+ }
28
+ /** The accounts of the user parameter data completed with IBAN and BIC, then the accounts only the list names. */
29
+ async completedAccounts() {
30
+ const sepa = await this.list();
31
+ const accounts = this.protocol.userParameters?.accounts ?? [];
32
+ return [
33
+ ...accounts.map((account) => withSepaDetails(account, sepa)),
34
+ ...sepa.filter((s) => !accounts.some((a) => s.matches(a))),
35
+ ];
36
+ }
37
+ /** `account` with IBAN and BIC filled in from the list. */
38
+ async complete(account) {
39
+ if (account.iban !== undefined && account.bic !== undefined)
40
+ return account;
41
+ return withSepaDetails(account, await this.list());
42
+ }
43
+ /** The list, read in the session's dialog, whose start delivers the user parameter data it is cached for. */
44
+ async list() {
45
+ await this.dialogs.open();
46
+ const upd = this.protocol.userParameters;
47
+ if (upd !== undefined &&
48
+ upd.accounts.length > 0 &&
49
+ upd.accounts.every((a) => a.iban !== undefined && a.bic !== undefined)) {
50
+ return [];
51
+ }
52
+ const updVersion = upd?.version;
53
+ const saved = this.state.sepaAccounts(updVersion);
54
+ if (saved !== undefined)
55
+ return saved;
56
+ if (this.unsaved !== undefined && this.unsaved.updVersion === updVersion)
57
+ return this.unsaved.accounts;
58
+ const query = this.protocol.sepaAccounts();
59
+ if (query === undefined)
60
+ return [];
61
+ // Where the bank rejects the list, the accounts stay as the user parameter data state them.
62
+ const outcome = { aborted: false };
63
+ const accounts = await this.guard.tryLookUp(async () => {
64
+ try {
65
+ return await this.dialogs.query(() => query);
66
+ }
67
+ catch (error) {
68
+ if (error instanceof FinTsDialogAbortedError)
69
+ outcome.aborted = true;
70
+ throw error;
71
+ }
72
+ });
73
+ if (outcome.aborted && !this.abortSkipped) {
74
+ // The bank may have ended the dialog for a reason of its own, such as a timeout, so this call goes on without the
75
+ // list and the next one asks again in its new dialog. Only once: banks also end the dialog when they reject the
76
+ // list, and each time costs the next call a login.
77
+ this.abortSkipped = true;
78
+ return [];
79
+ }
80
+ if (accounts !== undefined && updVersion !== undefined && updVersion > 0) {
81
+ this.state.cacheSepaAccounts(accounts, updVersion);
82
+ }
83
+ else {
84
+ this.unsaved = { updVersion, accounts: accounts ?? [] };
85
+ }
86
+ return accounts ?? [];
87
+ }
88
+ }
89
+ function withSepaDetails(account, sepa) {
90
+ const match = sepa.find((s) => account.matches(s));
91
+ if (match === undefined)
92
+ return account;
93
+ // A BIC of the list takes precedence over the one of the user parameter data (Formals E.3.1.1, "BIC").
94
+ return account.copyWith({ iban: account.iban ?? match.iban, bic: match.bic ?? account.bic });
95
+ }
96
+ //# sourceMappingURL=sepa-accounts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sepa-accounts.js","sourceRoot":"","sources":["../../src/session/sepa-accounts.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import type { BankInfo } from '../core/bank-info.js';
2
+ import type { TanMethod } from '../tan/tan-models.js';
3
+ import type { LoginGuard } from './login-guard.js';
4
+ import type { BankResponse, PinTanProtocol, ProtocolDialog } from './protocol.js';
5
+ import type { ResponseHandler } from './response-handler.js';
6
+ import type { TanSelection } from './tan-selection.js';
7
+ /**
8
+ * What has to be settled before a login: the bank parameter data, the synchronization, and through `TanSelection` the
9
+ * TAN method and medium. Each of the dialogs this takes serves one purpose only.
10
+ *
11
+ * Where the bank refuses a login for a reason the session can take care of, `remedied` does so and logs in again.
12
+ */
13
+ export declare class SessionSetup<R extends BankResponse, O> {
14
+ private readonly protocol;
15
+ private readonly responses;
16
+ private readonly tans;
17
+ private readonly guard;
18
+ /** Whether a synchronization dialog ran, so that a helpful one runs once only. */
19
+ private synchronizationTried;
20
+ constructor(protocol: PinTanProtocol<R, O>, responses: ResponseHandler<R>, tans: TanSelection<R, O>, guard: LoginGuard);
21
+ /**
22
+ * The bank's general information: from an anonymous dialog where the bank answers one, else from the synchronization.
23
+ * No TAN method or medium is chosen for it.
24
+ */
25
+ bankInfo(): Promise<BankInfo>;
26
+ /** The TAN methods the bank allows for this user, which the synchronization reports. */
27
+ methods(): Promise<TanMethod[]>;
28
+ /** Settles what is still open: the bank parameter data, the synchronization, the TAN method and medium. */
29
+ prepare(): Promise<void>;
30
+ /** A new dialog signed with the TAN method and medium in use, once `prepare` has settled them. */
31
+ newDialog(): Promise<ProtocolDialog<R, O>>;
32
+ /**
33
+ * Runs `attempt`, which may log in, and runs it once more for each reason of a refused login the session can take
34
+ * care of, so that a bank that keeps refusing fails with its own error:
35
+ *
36
+ * | Refusal (`LoginGuard.remediableReasons`) | Remedy |
37
+ * | --- | --- |
38
+ * | 3920 without the method, or 9075 after the one-step method | another allowed method (`TanSelection.replace`) |
39
+ * | 9391 | a new synchronization, which issues a customer system ID |
40
+ */
41
+ remedied<T>(attempt: () => Promise<T>): Promise<T>;
42
+ /** Takes care of `reason`, given for refusing a login signed with `rejected`; returns whether it could. */
43
+ private remedy;
44
+ /**
45
+ * Loads the bank parameter data if they are missing, then synchronizes if that is due.
46
+ *
47
+ * The bank parameter data come first, from an anonymous dialog: the dialogs signed with the PIN need them for the
48
+ * dialog language, the PIN length and the TAN methods (PIN/TAN B.4.3.1, Formals C.3.1.3). A bank that does not answer
49
+ * anonymously delivers them with the synchronization.
50
+ *
51
+ * After a refused login it throws the refusal right away, before the user is asked for a TAN method or medium.
52
+ */
53
+ private loadParameters;
54
+ /**
55
+ * Asks for the bank parameter data without logging in. A bank that does not answer anonymously is no error, but a
56
+ * failed connection is: the synchronization would only wait for the same server again.
57
+ */
58
+ private fetchBankParametersAnonymously;
59
+ /** Runs the synchronization dialog where it is due (`TanSelection.synchronizationDialog`). */
60
+ private synchronizeIfDue;
61
+ /**
62
+ * The TAN media of the method in use; `undefined` if the bank does not list them or rejects the list, after which the
63
+ * client reports a missing medium before the login.
64
+ *
65
+ * The lookup runs in a dialog of its own, whose start asks for the list, since the regular dialog start needs the
66
+ * medium the list tells (PIN/TAN B.4.3.1.3).
67
+ */
68
+ private lookUpTanMedia;
69
+ /** Runs `use` in `dialog`, a dialog of its own, and ends it afterwards, however `use` ends. */
70
+ private once;
71
+ }
72
+ //# sourceMappingURL=session-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-setup.d.ts","sourceRoot":"","sources":["../../src/session/session-setup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,kBAAkB,CAAC;AAElE,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAClF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD;;;;;GAKG;AACH,qBAAa,YAAY,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC;IAK/C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAPxB,kFAAkF;IAClF,OAAO,CAAC,oBAAoB,CAAS;gBAGlB,QAAQ,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAC9B,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC,EAC7B,IAAI,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EACxB,KAAK,EAAE,UAAU;IAGpC;;;OAGG;IACG,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC;IAMnC,wFAAwF;IAClF,OAAO,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAKrC,2GAA2G;IACrG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAM9B,kGAAkG;IAC5F,SAAS,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAKhD;;;;;;;;OAQG;IACG,QAAQ,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAkBxD,2GAA2G;YAC7F,MAAM;IAYpB;;;;;;;;OAQG;YACW,cAAc;IAM5B;;;OAGG;YACW,8BAA8B;IAS5C,8FAA8F;YAChF,gBAAgB;IAc9B;;;;;;OAMG;YACW,cAAc;IAW5B,+FAA+F;YACjF,IAAI;CAOnB"}
@@ -0,0 +1,160 @@
1
+ import { FinTsBankError, FinTsError, FinTsProtocolError, FinTsTransportError, parseBankData } from '../core/errors.js';
2
+ import { requireBankParameters } from './parameter-data.js';
3
+ /**
4
+ * What has to be settled before a login: the bank parameter data, the synchronization, and through `TanSelection` the
5
+ * TAN method and medium. Each of the dialogs this takes serves one purpose only.
6
+ *
7
+ * Where the bank refuses a login for a reason the session can take care of, `remedied` does so and logs in again.
8
+ */
9
+ export class SessionSetup {
10
+ protocol;
11
+ responses;
12
+ tans;
13
+ guard;
14
+ /** Whether a synchronization dialog ran, so that a helpful one runs once only. */
15
+ synchronizationTried = false;
16
+ constructor(protocol, responses, tans, guard) {
17
+ this.protocol = protocol;
18
+ this.responses = responses;
19
+ this.tans = tans;
20
+ this.guard = guard;
21
+ }
22
+ /**
23
+ * The bank's general information: from an anonymous dialog where the bank answers one, else from the synchronization.
24
+ * No TAN method or medium is chosen for it.
25
+ */
26
+ async bankInfo() {
27
+ if (this.protocol.bankParameters === undefined)
28
+ await this.fetchBankParametersAnonymously();
29
+ if (this.protocol.bankParameters === undefined)
30
+ await this.synchronizeIfDue();
31
+ return requireBankParameters(this.protocol.bankParameters).bankInfo;
32
+ }
33
+ /** The TAN methods the bank allows for this user, which the synchronization reports. */
34
+ async methods() {
35
+ await this.loadParameters();
36
+ return this.tans.available();
37
+ }
38
+ /** Settles what is still open: the bank parameter data, the synchronization, the TAN method and medium. */
39
+ async prepare() {
40
+ await this.loadParameters();
41
+ await this.tans.ensureMethodChosen();
42
+ if (this.tans.mediumToLookUp)
43
+ await this.tans.chooseMedium(await this.lookUpTanMedia());
44
+ }
45
+ /** A new dialog signed with the TAN method and medium in use, once `prepare` has settled them. */
46
+ async newDialog() {
47
+ await this.prepare();
48
+ return this.tans.newDialog();
49
+ }
50
+ /**
51
+ * Runs `attempt`, which may log in, and runs it once more for each reason of a refused login the session can take
52
+ * care of, so that a bank that keeps refusing fails with its own error:
53
+ *
54
+ * | Refusal (`LoginGuard.remediableReasons`) | Remedy |
55
+ * | --- | --- |
56
+ * | 3920 without the method, or 9075 after the one-step method | another allowed method (`TanSelection.replace`) |
57
+ * | 9391 | a new synchronization, which issues a customer system ID |
58
+ */
59
+ async remedied(attempt) {
60
+ // A refusal of an earlier call stops the attempt before it sends the PIN; there is nothing to remedy.
61
+ if (this.guard.hasRefusal)
62
+ return attempt();
63
+ const remedied = new Set();
64
+ for (;;) {
65
+ try {
66
+ return await attempt();
67
+ }
68
+ catch (error) {
69
+ if (!(error instanceof FinTsBankError))
70
+ throw error;
71
+ const reason = this.guard.remediableReasons(error).find((r) => !remedied.has(r));
72
+ const rejected = this.guard.refusedMethod;
73
+ if (reason === undefined || rejected === undefined || !(await this.remedy(reason, rejected)))
74
+ throw error;
75
+ remedied.add(reason);
76
+ this.guard.pardon(error);
77
+ }
78
+ }
79
+ }
80
+ /** Takes care of `reason`, given for refusing a login signed with `rejected`; returns whether it could. */
81
+ async remedy(reason, rejected) {
82
+ switch (reason) {
83
+ case 'tanMethodNotAllowed':
84
+ // The parameter data name the methods to choose from; the refusal may be the first to deliver them.
85
+ if (this.protocol.bankParameters === undefined)
86
+ await this.fetchBankParametersAnonymously();
87
+ return this.tans.replace(rejected);
88
+ case 'systemIdRejected':
89
+ // Without a customer system ID there is none to lose, and logging in once more would only send the PIN again.
90
+ return this.protocol.forgetSystemId();
91
+ }
92
+ }
93
+ /**
94
+ * Loads the bank parameter data if they are missing, then synchronizes if that is due.
95
+ *
96
+ * The bank parameter data come first, from an anonymous dialog: the dialogs signed with the PIN need them for the
97
+ * dialog language, the PIN length and the TAN methods (PIN/TAN B.4.3.1, Formals C.3.1.3). A bank that does not answer
98
+ * anonymously delivers them with the synchronization.
99
+ *
100
+ * After a refused login it throws the refusal right away, before the user is asked for a TAN method or medium.
101
+ */
102
+ async loadParameters() {
103
+ this.guard.check();
104
+ if (this.protocol.bankParameters === undefined)
105
+ await this.fetchBankParametersAnonymously();
106
+ await this.synchronizeIfDue();
107
+ }
108
+ /**
109
+ * Asks for the bank parameter data without logging in. A bank that does not answer anonymously is no error, but a
110
+ * failed connection is: the synchronization would only wait for the same server again.
111
+ */
112
+ async fetchBankParametersAnonymously() {
113
+ try {
114
+ this.responses.showBankMessages(await this.protocol.fetchBankParametersAnonymously());
115
+ }
116
+ catch (error) {
117
+ // Optional: the login delivers the parameters as well.
118
+ if (error instanceof FinTsTransportError || !(error instanceof FinTsError))
119
+ throw error;
120
+ }
121
+ }
122
+ /** Runs the synchronization dialog where it is due (`TanSelection.synchronizationDialog`). */
123
+ async synchronizeIfDue() {
124
+ const due = this.protocol.synchronization === 'required' ||
125
+ (this.protocol.synchronization === 'helpful' && !this.synchronizationTried);
126
+ if (!due)
127
+ return;
128
+ this.synchronizationTried = true;
129
+ await this.once(this.tans.synchronizationDialog(), (dialog) => this.responses.startDialog(dialog, () => dialog.start(true)));
130
+ if (this.protocol.synchronization === 'required') {
131
+ throw new FinTsProtocolError('The bank did not complete the synchronization');
132
+ }
133
+ }
134
+ /**
135
+ * The TAN media of the method in use; `undefined` if the bank does not list them or rejects the list, after which the
136
+ * client reports a missing medium before the login.
137
+ *
138
+ * The lookup runs in a dialog of its own, whose start asks for the list, since the regular dialog start needs the
139
+ * medium the list tells (PIN/TAN B.4.3.1.3).
140
+ */
141
+ async lookUpTanMedia() {
142
+ const query = this.protocol.tanMedia();
143
+ if (query === undefined)
144
+ return undefined;
145
+ return this.guard.tryLookUp(() => this.once(this.tans.lookupDialog(), async (dialog) => {
146
+ const pages = await this.responses.startDialogWith(dialog, dialog.startWith(query.order(undefined)));
147
+ return parseBankData(() => query.read(pages));
148
+ }));
149
+ }
150
+ /** Runs `use` in `dialog`, a dialog of its own, and ends it afterwards, however `use` ends. */
151
+ async once(dialog, use) {
152
+ try {
153
+ return await use(dialog);
154
+ }
155
+ finally {
156
+ await dialog.end();
157
+ }
158
+ }
159
+ }
160
+ //# sourceMappingURL=session-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-setup.js","sourceRoot":"","sources":["../../src/session/session-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,47 @@
1
+ import { Account } from '../core/account.js';
2
+ import { type JsonObject } from '../util/json.js';
3
+ import type { BankResponse, PinTanProtocol } from './protocol.js';
4
+ /** The session and the protocol's parameter data as one JSON string, which `restoreSession` reads. */
5
+ export declare function saveSession<R extends BankResponse, O>(protocol: PinTanProtocol<R, O>, session: SessionState): string;
6
+ /**
7
+ * The session saved in `json`, and the protocol's parameter data restored from it.
8
+ *
9
+ * A missing or damaged state, or one saved for another user, bank, server or protocol version, gives a new session,
10
+ * which only costs a new synchronization.
11
+ */
12
+ export declare function restoreSession<R extends BankResponse, O>(json: string | undefined, protocol: PinTanProtocol<R, O>): SessionState;
13
+ /**
14
+ * The session data saved between app starts: the allowed TAN methods, the TAN method and medium in use, and the SEPA
15
+ * account list.
16
+ */
17
+ export declare class SessionState {
18
+ private allowedMethodsValue;
19
+ private tanMethodValue;
20
+ private tanMediumValue;
21
+ private sepaAccountsValue;
22
+ private sepaUpdVersion;
23
+ /** TAN methods the bank allows for this user (return code 3920); `undefined` until the bank has said. */
24
+ get allowedMethods(): readonly string[] | undefined;
25
+ /** Security function of the TAN method in use. */
26
+ get tanMethod(): string | undefined;
27
+ /** Name of the TAN medium in use. */
28
+ get tanMedium(): string | undefined;
29
+ /** Keeps the TAN methods the bank reports as allowed for this user. */
30
+ rememberAllowedMethods(securityFunctions: readonly string[]): void;
31
+ /**
32
+ * Switches to the TAN method `securityFunction` and the TAN `medium` together, so that no medium of another method
33
+ * stays behind; `undefined` leaves the choice to the next login.
34
+ */
35
+ useTanMethod(securityFunction: string | undefined, medium?: string): void;
36
+ /** Switches to the TAN `medium` of the method in use. */
37
+ useTanMedium(medium: string | undefined): void;
38
+ /** The SEPA account list read under the user parameter data of `updVersion`, if it is cached. */
39
+ sepaAccounts(updVersion: number | undefined): readonly Account[] | undefined;
40
+ /** Caches the SEPA account list `accounts`, read under the user parameter data of `updVersion`. */
41
+ cacheSepaAccounts(accounts: readonly Account[], updVersion: number | undefined): void;
42
+ /** Reads what `toJson` wrote; throws `FormatError` if `json` is damaged. */
43
+ static fromJson(json: JsonObject): SessionState;
44
+ /** The session as JSON data. */
45
+ toJson(): JsonObject;
46
+ }
47
+ //# sourceMappingURL=session-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.d.ts","sourceRoot":"","sources":["../../src/session/session-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAG7C,OAAO,EAOL,KAAK,UAAU,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAQlE,sGAAsG;AACtG,wBAAgB,WAAW,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,YAAY,GAAG,MAAM,CAQpH;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,YAAY,EAAE,CAAC,EACtD,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,QAAQ,EAAE,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAC7B,YAAY,CAoBd;AAOD;;;GAGG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,mBAAmB,CAAgC;IAC3D,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,cAAc,CAAqB;IAE3C,yGAAyG;IACzG,IAAI,cAAc,IAAI,SAAS,MAAM,EAAE,GAAG,SAAS,CAElD;IAED,kDAAkD;IAClD,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED,qCAAqC;IACrC,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED,uEAAuE;IACvE,sBAAsB,CAAC,iBAAiB,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI;IAIlE;;;OAGG;IACH,YAAY,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzE,yDAAyD;IACzD,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAI9C,iGAAiG;IACjG,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,OAAO,EAAE,GAAG,SAAS;IAI5E,mGAAmG;IACnG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAKrF,4EAA4E;IAC5E,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,YAAY;IAW/C,gCAAgC;IAChC,MAAM,IAAI,UAAU;CASrB"}
@@ -0,0 +1,147 @@
1
+ import { Account } from '../core/account.js';
2
+ import { hostOf } from '../core/config.js';
3
+ import { FormatError } from '../util/errors.js';
4
+ import { isJsonObject, isString, readObject, readOptionalInt, readOptionalList, readOptionalString, } from '../util/json.js';
5
+ /** Layout of the saved session; a state of another layout is ignored. */
6
+ const stateFormat = 1;
7
+ /** The protocol version the state is for, named as the Dart package names it, so that both read each other's states. */
8
+ const protocolVersion = 'v300';
9
+ /** The session and the protocol's parameter data as one JSON string, which `restoreSession` reads. */
10
+ export function saveSession(protocol, session) {
11
+ return JSON.stringify({
12
+ format: stateFormat,
13
+ version: protocolVersion,
14
+ owner: ownerOf(protocol.config),
15
+ session: session.toJson(),
16
+ protocol: protocol.saveState(),
17
+ });
18
+ }
19
+ /**
20
+ * The session saved in `json`, and the protocol's parameter data restored from it.
21
+ *
22
+ * A missing or damaged state, or one saved for another user, bank, server or protocol version, gives a new session,
23
+ * which only costs a new synchronization.
24
+ */
25
+ export function restoreSession(json, protocol) {
26
+ if (json === undefined)
27
+ return new SessionState();
28
+ try {
29
+ const saved = JSON.parse(json);
30
+ if (!isJsonObject(saved) || saved.format !== stateFormat || saved.version !== protocolVersion) {
31
+ return new SessionState();
32
+ }
33
+ const owner = readOptionalList(saved, 'owner', isString);
34
+ const expected = ownerOf(protocol.config);
35
+ if (owner?.length !== expected.length || owner.some((value, i) => value !== expected[i])) {
36
+ return new SessionState();
37
+ }
38
+ const session = SessionState.fromJson(readObject(saved, 'session'));
39
+ protocol.restoreState(readObject(saved, 'protocol'));
40
+ return session;
41
+ }
42
+ catch (error) {
43
+ // Damaged JSON, base64 or parameter data, and values of the wrong type, only cost a new synchronization.
44
+ if (error instanceof FormatError || error instanceof SyntaxError)
45
+ return new SessionState();
46
+ throw error;
47
+ }
48
+ }
49
+ /** Whose data a state holds, so that the state of another user or bank is never used. */
50
+ function ownerOf(config) {
51
+ return [hostOf(config.url), config.bankCode, config.userId, config.customerId];
52
+ }
53
+ /**
54
+ * The session data saved between app starts: the allowed TAN methods, the TAN method and medium in use, and the SEPA
55
+ * account list.
56
+ */
57
+ export class SessionState {
58
+ allowedMethodsValue;
59
+ tanMethodValue;
60
+ tanMediumValue;
61
+ sepaAccountsValue;
62
+ sepaUpdVersion;
63
+ /** TAN methods the bank allows for this user (return code 3920); `undefined` until the bank has said. */
64
+ get allowedMethods() {
65
+ return this.allowedMethodsValue;
66
+ }
67
+ /** Security function of the TAN method in use. */
68
+ get tanMethod() {
69
+ return this.tanMethodValue;
70
+ }
71
+ /** Name of the TAN medium in use. */
72
+ get tanMedium() {
73
+ return this.tanMediumValue;
74
+ }
75
+ /** Keeps the TAN methods the bank reports as allowed for this user. */
76
+ rememberAllowedMethods(securityFunctions) {
77
+ this.allowedMethodsValue = Object.freeze([...securityFunctions]);
78
+ }
79
+ /**
80
+ * Switches to the TAN method `securityFunction` and the TAN `medium` together, so that no medium of another method
81
+ * stays behind; `undefined` leaves the choice to the next login.
82
+ */
83
+ useTanMethod(securityFunction, medium) {
84
+ this.tanMethodValue = securityFunction;
85
+ this.tanMediumValue = medium;
86
+ }
87
+ /** Switches to the TAN `medium` of the method in use. */
88
+ useTanMedium(medium) {
89
+ this.tanMediumValue = medium;
90
+ }
91
+ /** The SEPA account list read under the user parameter data of `updVersion`, if it is cached. */
92
+ sepaAccounts(updVersion) {
93
+ return updVersion !== undefined && updVersion === this.sepaUpdVersion ? this.sepaAccountsValue : undefined;
94
+ }
95
+ /** Caches the SEPA account list `accounts`, read under the user parameter data of `updVersion`. */
96
+ cacheSepaAccounts(accounts, updVersion) {
97
+ this.sepaAccountsValue = Object.freeze([...accounts]);
98
+ this.sepaUpdVersion = updVersion;
99
+ }
100
+ /** Reads what `toJson` wrote; throws `FormatError` if `json` is damaged. */
101
+ static fromJson(json) {
102
+ const allowedMethods = readOptionalList(json, 'allowedMethods', isString);
103
+ const sepaAccounts = readOptionalList(json, 'sepaAccounts', isJsonObject)?.map(accountFromJson);
104
+ const sepaUpdVersion = readOptionalInt(json, 'sepaUpdVersion');
105
+ const state = new SessionState();
106
+ state.useTanMethod(readOptionalString(json, 'tanMethod'), readOptionalString(json, 'tanMedium'));
107
+ if (allowedMethods !== undefined)
108
+ state.rememberAllowedMethods(allowedMethods);
109
+ if (sepaAccounts !== undefined)
110
+ state.cacheSepaAccounts(sepaAccounts, sepaUpdVersion);
111
+ return state;
112
+ }
113
+ /** The session as JSON data. */
114
+ toJson() {
115
+ return {
116
+ allowedMethods: this.allowedMethodsValue ?? null,
117
+ tanMethod: this.tanMethodValue ?? null,
118
+ tanMedium: this.tanMediumValue ?? null,
119
+ sepaUpdVersion: this.sepaUpdVersion ?? null,
120
+ sepaAccounts: this.sepaAccountsValue?.map(accountToJson) ?? null,
121
+ };
122
+ }
123
+ }
124
+ function accountToJson(a) {
125
+ return {
126
+ iban: a.iban ?? null,
127
+ bic: a.bic ?? null,
128
+ accountNumber: a.accountNumber ?? null,
129
+ subAccount: a.subAccount ?? null,
130
+ countryCode: a.countryCode,
131
+ bankCode: a.bankCode,
132
+ };
133
+ }
134
+ function accountFromJson(json) {
135
+ const bankCode = readOptionalString(json, 'bankCode');
136
+ if (bankCode === undefined)
137
+ throw new FormatError('Saved state: account without bank code');
138
+ return new Account({
139
+ iban: readOptionalString(json, 'iban'),
140
+ bic: readOptionalString(json, 'bic'),
141
+ accountNumber: readOptionalString(json, 'accountNumber'),
142
+ subAccount: readOptionalString(json, 'subAccount'),
143
+ countryCode: readOptionalString(json, 'countryCode'),
144
+ bankCode,
145
+ });
146
+ }
147
+ //# sourceMappingURL=session-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-state.js","sourceRoot":"","sources":["../../src/session/session-state.ts"],"names":[],"mappings":"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"}