@coderoftime/fints 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -1
- package/README.md +127 -37
- package/dist/client.d.ts +48 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +199 -0
- package/dist/client.js.map +1 -0
- package/dist/core/account.d.ts +35 -7
- package/dist/core/account.d.ts.map +1 -1
- package/dist/core/account.js +84 -13
- package/dist/core/account.js.map +1 -1
- package/dist/core/amount.d.ts +2 -1
- package/dist/core/amount.d.ts.map +1 -1
- package/dist/core/amount.js +4 -1
- package/dist/core/amount.js.map +1 -1
- package/dist/core/balance.d.ts +1 -0
- package/dist/core/balance.d.ts.map +1 -1
- package/dist/core/balance.js +6 -5
- package/dist/core/balance.js.map +1 -1
- package/dist/core/bank-info.d.ts +1 -1
- package/dist/core/bank-info.js +1 -1
- package/dist/core/client.d.ts +50 -15
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/config.d.ts +28 -16
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +66 -26
- package/dist/core/config.js.map +1 -1
- package/dist/core/errors.d.ts +85 -23
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +112 -47
- package/dist/core/errors.js.map +1 -1
- package/dist/core/feedback.d.ts +4 -1
- package/dist/core/feedback.d.ts.map +1 -1
- package/dist/core/feedback.js +4 -1
- package/dist/core/feedback.js.map +1 -1
- package/dist/core/transaction.d.ts +6 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +9 -4
- package/dist/core/transaction.js.map +1 -1
- package/dist/core/transport.d.ts +33 -6
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/core/transport.js +77 -20
- package/dist/core/transport.js.map +1 -1
- package/dist/formats/camt.d.ts +24 -4
- package/dist/formats/camt.d.ts.map +1 -1
- package/dist/formats/camt.js +124 -41
- package/dist/formats/camt.js.map +1 -1
- package/dist/formats/mt940.d.ts +15 -3
- package/dist/formats/mt940.d.ts.map +1 -1
- package/dist/formats/mt940.js +161 -82
- package/dist/formats/mt940.js.map +1 -1
- package/dist/formats/readers.d.ts +6 -2
- package/dist/formats/readers.d.ts.map +1 -1
- package/dist/formats/readers.js +24 -29
- package/dist/formats/readers.js.map +1 -1
- package/dist/formats/record-budget.d.ts +7 -0
- package/dist/formats/record-budget.d.ts.map +1 -0
- package/dist/formats/record-budget.js +17 -0
- package/dist/formats/record-budget.js.map +1 -0
- package/dist/formats/sepa-purpose.d.ts +6 -0
- package/dist/formats/sepa-purpose.d.ts.map +1 -1
- package/dist/formats/sepa-purpose.js +23 -2
- package/dist/formats/sepa-purpose.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/session/app-approval.d.ts +11 -5
- package/dist/session/app-approval.d.ts.map +1 -1
- package/dist/session/app-approval.js +23 -12
- package/dist/session/app-approval.js.map +1 -1
- package/dist/session/dialog-runner.d.ts +7 -10
- package/dist/session/dialog-runner.d.ts.map +1 -1
- package/dist/session/dialog-runner.js +15 -16
- package/dist/session/dialog-runner.js.map +1 -1
- package/dist/session/login-guard.d.ts +54 -49
- package/dist/session/login-guard.d.ts.map +1 -1
- package/dist/session/login-guard.js +156 -84
- package/dist/session/login-guard.js.map +1 -1
- package/dist/session/sepa-accounts.d.ts +20 -14
- package/dist/session/sepa-accounts.d.ts.map +1 -1
- package/dist/session/sepa-accounts.js +62 -46
- package/dist/session/sepa-accounts.js.map +1 -1
- package/dist/session/session-setup.d.ts +33 -25
- package/dist/session/session-setup.d.ts.map +1 -1
- package/dist/session/session-setup.js +87 -65
- package/dist/session/session-setup.js.map +1 -1
- package/dist/session/session-state.d.ts +9 -6
- package/dist/session/session-state.d.ts.map +1 -1
- package/dist/session/session-state.js +52 -17
- package/dist/session/session-state.js.map +1 -1
- package/dist/session/signed-dialog.d.ts +77 -0
- package/dist/session/signed-dialog.d.ts.map +1 -0
- package/dist/session/signed-dialog.js +170 -0
- package/dist/session/signed-dialog.js.map +1 -0
- package/dist/session/tan-selection.d.ts +26 -18
- package/dist/session/tan-selection.d.ts.map +1 -1
- package/dist/session/tan-selection.js +44 -33
- package/dist/session/tan-selection.js.map +1 -1
- package/dist/session/user-prompts.d.ts +8 -4
- package/dist/session/user-prompts.d.ts.map +1 -1
- package/dist/session/user-prompts.js +34 -14
- package/dist/session/user-prompts.js.map +1 -1
- package/dist/tan/challenge.d.ts +34 -13
- package/dist/tan/challenge.d.ts.map +1 -1
- package/dist/tan/challenge.js +25 -26
- package/dist/tan/challenge.js.map +1 -1
- package/dist/tan/flicker.d.ts +7 -1
- package/dist/tan/flicker.d.ts.map +1 -1
- package/dist/tan/flicker.js +40 -6
- package/dist/tan/flicker.js.map +1 -1
- package/dist/tan/tan-image.d.ts +3 -2
- package/dist/tan/tan-image.d.ts.map +1 -1
- package/dist/tan/tan-image.js +7 -5
- package/dist/tan/tan-image.js.map +1 -1
- package/dist/tan/tan-models.d.ts +5 -1
- package/dist/tan/tan-models.d.ts.map +1 -1
- package/dist/tan/tan-models.js +10 -5
- package/dist/tan/tan-models.js.map +1 -1
- package/dist/util/async.d.ts +0 -2
- package/dist/util/async.d.ts.map +1 -1
- package/dist/util/async.js +0 -4
- package/dist/util/async.js.map +1 -1
- package/dist/util/bytes.d.ts +2 -3
- package/dist/util/bytes.d.ts.map +1 -1
- package/dist/util/bytes.js +12 -20
- package/dist/util/bytes.js.map +1 -1
- package/dist/util/call-context.d.ts.map +1 -1
- package/dist/util/call-context.js +3 -2
- package/dist/util/call-context.js.map +1 -1
- package/dist/util/dates.d.ts +9 -16
- package/dist/util/dates.d.ts.map +1 -1
- package/dist/util/dates.js +39 -19
- package/dist/util/dates.js.map +1 -1
- package/dist/util/errors.d.ts +9 -1
- package/dist/util/errors.d.ts.map +1 -1
- package/dist/util/errors.js +12 -3
- package/dist/util/errors.js.map +1 -1
- package/dist/util/json.d.ts +4 -0
- package/dist/util/json.d.ts.map +1 -1
- package/dist/util/json.js +18 -0
- package/dist/util/json.js.map +1 -1
- package/dist/util/text.d.ts +21 -4
- package/dist/util/text.d.ts.map +1 -1
- package/dist/util/text.js +42 -13
- package/dist/util/text.js.map +1 -1
- package/dist/util/xml.d.ts +4 -3
- package/dist/util/xml.d.ts.map +1 -1
- package/dist/util/xml.js +37 -26
- package/dist/util/xml.js.map +1 -1
- package/dist/v3/account-extension.d.ts.map +1 -1
- package/dist/v3/account-extension.js +7 -10
- package/dist/v3/account-extension.js.map +1 -1
- package/dist/v3/bank-connection.d.ts +34 -0
- package/dist/v3/bank-connection.d.ts.map +1 -0
- package/dist/v3/bank-connection.js +58 -0
- package/dist/v3/bank-connection.js.map +1 -0
- package/dist/v3/dialog.d.ts +80 -46
- package/dist/v3/dialog.d.ts.map +1 -1
- package/dist/v3/dialog.js +162 -59
- package/dist/v3/dialog.js.map +1 -1
- package/dist/v3/message.d.ts +17 -4
- package/dist/v3/message.d.ts.map +1 -1
- package/dist/v3/message.js +62 -18
- package/dist/v3/message.js.map +1 -1
- package/dist/v3/parameter-store.d.ts +75 -0
- package/dist/v3/parameter-store.d.ts.map +1 -0
- package/dist/v3/parameter-store.js +143 -0
- package/dist/v3/parameter-store.js.map +1 -0
- package/dist/v3/parameters.d.ts +38 -12
- package/dist/v3/parameters.d.ts.map +1 -1
- package/dist/v3/parameters.js +91 -37
- package/dist/v3/parameters.js.map +1 -1
- package/dist/v3/protocol.d.ts +43 -50
- package/dist/v3/protocol.d.ts.map +1 -1
- package/dist/v3/protocol.js +58 -208
- package/dist/v3/protocol.js.map +1 -1
- package/dist/v3/queries.d.ts +51 -0
- package/dist/v3/queries.d.ts.map +1 -0
- package/dist/v3/queries.js +120 -0
- package/dist/v3/queries.js.map +1 -0
- package/dist/v3/segments.d.ts +29 -12
- package/dist/v3/segments.d.ts.map +1 -1
- package/dist/v3/segments.js +65 -23
- package/dist/v3/segments.js.map +1 -1
- package/dist/v3/syntax.d.ts +65 -14
- package/dist/v3/syntax.d.ts.map +1 -1
- package/dist/v3/syntax.js +167 -101
- package/dist/v3/syntax.js.map +1 -1
- package/package.json +10 -6
- package/src/client.ts +252 -0
- package/src/core/account.ts +86 -13
- package/src/core/amount.ts +3 -1
- package/src/core/balance.ts +6 -5
- package/src/core/bank-info.ts +1 -1
- package/src/core/client.ts +52 -15
- package/src/core/config.ts +69 -28
- package/src/core/errors.ts +122 -48
- package/src/core/feedback.ts +4 -1
- package/src/core/transaction.ts +9 -4
- package/src/core/transport.ts +98 -23
- package/src/formats/camt.ts +136 -39
- package/src/formats/mt940.ts +167 -81
- package/src/formats/readers.ts +24 -31
- package/src/formats/record-budget.ts +18 -0
- package/src/formats/sepa-purpose.ts +21 -2
- package/src/index.ts +4 -2
- package/src/session/app-approval.ts +51 -38
- package/src/session/dialog-runner.ts +31 -28
- package/src/session/login-guard.ts +174 -88
- package/src/session/sepa-accounts.ts +63 -45
- package/src/session/session-setup.ts +90 -71
- package/src/session/session-state.ts +56 -22
- package/src/session/signed-dialog.ts +192 -0
- package/src/session/tan-selection.ts +50 -39
- package/src/session/user-prompts.ts +35 -16
- package/src/tan/challenge.ts +40 -31
- package/src/tan/flicker.ts +44 -6
- package/src/tan/tan-image.ts +7 -5
- package/src/tan/tan-models.ts +10 -5
- package/src/util/async.ts +0 -5
- package/src/util/bytes.ts +13 -19
- package/src/util/call-context.ts +3 -2
- package/src/util/dates.ts +41 -24
- package/src/util/errors.ts +14 -3
- package/src/util/json.ts +16 -0
- package/src/util/text.ts +42 -12
- package/src/util/xml.ts +37 -26
- package/src/v3/account-extension.ts +7 -12
- package/src/v3/bank-connection.ts +67 -0
- package/src/v3/dialog.ts +175 -91
- package/src/v3/message.ts +69 -17
- package/src/v3/parameter-store.ts +160 -0
- package/src/v3/parameters.ts +110 -37
- package/src/v3/protocol.ts +70 -253
- package/src/v3/queries.ts +190 -0
- package/src/v3/segments.ts +65 -22
- package/src/v3/syntax.ts +153 -91
- package/dist/session/bank-connection.d.ts +0 -28
- package/dist/session/bank-connection.d.ts.map +0 -1
- package/dist/session/bank-connection.js +0 -48
- package/dist/session/bank-connection.js.map +0 -1
- package/dist/session/parameter-data.d.ts +0 -83
- package/dist/session/parameter-data.d.ts.map +0 -1
- package/dist/session/parameter-data.js +0 -68
- package/dist/session/parameter-data.js.map +0 -1
- package/dist/session/pin-tan-client.d.ts +0 -54
- package/dist/session/pin-tan-client.d.ts.map +0 -1
- package/dist/session/pin-tan-client.js +0 -153
- package/dist/session/pin-tan-client.js.map +0 -1
- package/dist/session/pin-tan-dialog.d.ts +0 -71
- package/dist/session/pin-tan-dialog.d.ts.map +0 -1
- package/dist/session/pin-tan-dialog.js +0 -124
- package/dist/session/pin-tan-dialog.js.map +0 -1
- package/dist/session/protocol.d.ts +0 -209
- package/dist/session/protocol.d.ts.map +0 -1
- package/dist/session/protocol.js +0 -75
- package/dist/session/protocol.js.map +0 -1
- package/dist/session/response-handler.d.ts +0 -48
- package/dist/session/response-handler.d.ts.map +0 -1
- package/dist/session/response-handler.js +0 -121
- package/dist/session/response-handler.js.map +0 -1
- package/dist/v3/client.d.ts +0 -24
- package/dist/v3/client.d.ts.map +0 -1
- package/dist/v3/client.js +0 -16
- package/dist/v3/client.js.map +0 -1
- package/src/session/bank-connection.ts +0 -51
- package/src/session/parameter-data.ts +0 -137
- package/src/session/pin-tan-client.ts +0 -186
- package/src/session/pin-tan-dialog.ts +0 -160
- package/src/session/protocol.ts +0 -311
- package/src/session/response-handler.ts +0 -130
- package/src/v3/client.ts +0 -29
package/src/v3/segments.ts
CHANGED
|
@@ -3,24 +3,31 @@
|
|
|
3
3
|
import { Account } from '../core/account.js';
|
|
4
4
|
import { Amount, withMark } from '../core/amount.js';
|
|
5
5
|
import { Balance } from '../core/balance.js';
|
|
6
|
-
import { noReference } from '../session/protocol.js';
|
|
7
6
|
import { TanMedium, tanMediumStatusOf } from '../tan/tan-models.js';
|
|
8
7
|
import { formatFintsDate, parseFintsDate, parseFintsDateTime } from '../util/dates.js';
|
|
9
8
|
import { FormatError } from '../util/errors.js';
|
|
10
|
-
import { type Segment, SegmentSpec, type SegmentValue } from './syntax.js';
|
|
9
|
+
import { type Segment, SegmentSpec, type SegmentValue, Verbatim } from './syntax.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The filler the specification names for a reference that does not exist.
|
|
13
|
+
*
|
|
14
|
+
* The bank puts it into a dummy answer when no approval is needed. The client sends it as the TAN medium name in the
|
|
15
|
+
* dialog that looks up the TAN media, before it knows a medium; the bank ignores it there (PIN/TAN B.4.3.1.3).
|
|
16
|
+
*/
|
|
17
|
+
export const noReference = 'noref';
|
|
11
18
|
|
|
12
19
|
/** Bank identification (KIK): country code and bank code. */
|
|
13
|
-
|
|
20
|
+
function bankIdentification(countryCode: string, bankCode: string): SegmentValue {
|
|
14
21
|
return [countryCode, bankCode];
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/** National account (KTV): account number, sub-account and bank identification. */
|
|
18
|
-
|
|
25
|
+
function nationalAccount(a: Account): SegmentValue[] {
|
|
19
26
|
return [a.accountNumber, a.subAccount, a.countryCode, a.bankCode];
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
/** International account (KTI): IBAN, BIC, then the national account. */
|
|
23
|
-
|
|
30
|
+
function internationalAccount(a: Account): SegmentValue[] {
|
|
24
31
|
return [a.iban, a.bic, ...nationalAccount(a)];
|
|
25
32
|
}
|
|
26
33
|
|
|
@@ -33,7 +40,10 @@ const orderReferenceField = 5;
|
|
|
33
40
|
const moreTansField = 6;
|
|
34
41
|
const tanMediumField = 11;
|
|
35
42
|
|
|
36
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Builders for the request segments the client sends. Values the bank issued, such as the customer system ID, the TAN
|
|
45
|
+
* order reference and the position to continue at, go back as `Verbatim`.
|
|
46
|
+
*/
|
|
37
47
|
export const Segments = Object.freeze({
|
|
38
48
|
/** HKIDN: who is logging in, from which customer system. */
|
|
39
49
|
identification(parts: {
|
|
@@ -46,7 +56,7 @@ export const Segments = Object.freeze({
|
|
|
46
56
|
return new SegmentSpec('HKIDN', 2, [
|
|
47
57
|
bankIdentification(parts.countryCode, parts.bankCode),
|
|
48
58
|
parts.customerId,
|
|
49
|
-
parts.systemId,
|
|
59
|
+
new Verbatim(parts.systemId),
|
|
50
60
|
parts.systemIdRequired ? 1 : 0,
|
|
51
61
|
]);
|
|
52
62
|
},
|
|
@@ -74,22 +84,37 @@ export const Segments = Object.freeze({
|
|
|
74
84
|
},
|
|
75
85
|
|
|
76
86
|
dialogEnd(dialogId: string): SegmentSpec {
|
|
77
|
-
return new SegmentSpec('HKEND', 1, [dialogId]);
|
|
87
|
+
return new SegmentSpec('HKEND', 1, [new Verbatim(dialogId)]);
|
|
78
88
|
},
|
|
79
89
|
|
|
80
|
-
/**
|
|
90
|
+
/**
|
|
91
|
+
* HKTAN process 4: asks for strong customer authentication of the `segmentType` sent in the same message, with the TAN
|
|
92
|
+
* medium `mediumName`, as the bank sent it.
|
|
93
|
+
*/
|
|
81
94
|
tanProcess4(version: number, segmentType: string, mediumName?: string): SegmentSpec {
|
|
82
|
-
return SegmentSpec.numbered('HKTAN', version, {
|
|
95
|
+
return SegmentSpec.numbered('HKTAN', version, {
|
|
96
|
+
1: '4',
|
|
97
|
+
2: segmentType,
|
|
98
|
+
[tanMediumField]: Verbatim.of(mediumName),
|
|
99
|
+
});
|
|
83
100
|
},
|
|
84
101
|
|
|
85
102
|
/** HKTAN process 2: submits the TAN (carried in the signature) for the order with `reference`. */
|
|
86
103
|
tanProcess2(version: number, reference: string): SegmentSpec {
|
|
87
|
-
return SegmentSpec.numbered('HKTAN', version, {
|
|
104
|
+
return SegmentSpec.numbered('HKTAN', version, {
|
|
105
|
+
1: '2',
|
|
106
|
+
[orderReferenceField]: new Verbatim(reference),
|
|
107
|
+
[moreTansField]: false,
|
|
108
|
+
});
|
|
88
109
|
},
|
|
89
110
|
|
|
90
111
|
/** HKTAN process S: asks whether the order with `reference` was approved in the banking app. */
|
|
91
112
|
tanProcessStatus(version: number, reference: string): SegmentSpec {
|
|
92
|
-
return SegmentSpec.numbered('HKTAN', version, {
|
|
113
|
+
return SegmentSpec.numbered('HKTAN', version, {
|
|
114
|
+
1: 'S',
|
|
115
|
+
[orderReferenceField]: new Verbatim(reference),
|
|
116
|
+
[moreTansField]: false,
|
|
117
|
+
});
|
|
93
118
|
},
|
|
94
119
|
|
|
95
120
|
/** HKSAL: the balance of one account (not of all accounts). */
|
|
@@ -98,7 +123,7 @@ export const Segments = Object.freeze({
|
|
|
98
123
|
version >= 7 ? internationalAccount(account) : nationalAccount(account),
|
|
99
124
|
false,
|
|
100
125
|
null,
|
|
101
|
-
continuation,
|
|
126
|
+
Verbatim.of(continuation),
|
|
102
127
|
]);
|
|
103
128
|
},
|
|
104
129
|
|
|
@@ -114,7 +139,7 @@ export const Segments = Object.freeze({
|
|
|
114
139
|
dateOrEmpty(period.from),
|
|
115
140
|
dateOrEmpty(period.to),
|
|
116
141
|
null,
|
|
117
|
-
period.continuation,
|
|
142
|
+
Verbatim.of(period.continuation),
|
|
118
143
|
]);
|
|
119
144
|
},
|
|
120
145
|
|
|
@@ -132,13 +157,13 @@ export const Segments = Object.freeze({
|
|
|
132
157
|
dateOrEmpty(period.from),
|
|
133
158
|
dateOrEmpty(period.to),
|
|
134
159
|
null,
|
|
135
|
-
period.continuation,
|
|
160
|
+
Verbatim.of(period.continuation),
|
|
136
161
|
]);
|
|
137
162
|
},
|
|
138
163
|
|
|
139
164
|
/** HKSPA: IBAN and BIC of all accounts. */
|
|
140
165
|
sepaAccounts(version: number, continuation?: string): SegmentSpec {
|
|
141
|
-
return new SegmentSpec('HKSPA', version, [null, ...(version >= 2 ? [null, continuation] : [])]);
|
|
166
|
+
return new SegmentSpec('HKSPA', version, [null, ...(version >= 2 ? [null, Verbatim.of(continuation)] : [])]);
|
|
142
167
|
},
|
|
143
168
|
|
|
144
169
|
/** HKTAB: all TAN media of all classes. */
|
|
@@ -155,6 +180,7 @@ export class TanResponse {
|
|
|
155
180
|
constructor(
|
|
156
181
|
/** The TAN process the segment answers: `4` asks for an approval, `S` answers a status request. */
|
|
157
182
|
readonly process: string | undefined,
|
|
183
|
+
/** The order reference, as `Segment.verbatim` reads it, to send back byte for byte. */
|
|
158
184
|
readonly reference: string | undefined,
|
|
159
185
|
readonly challenge: string | undefined,
|
|
160
186
|
readonly hhdUc: Uint8Array | undefined,
|
|
@@ -165,7 +191,7 @@ export class TanResponse {
|
|
|
165
191
|
static parse(s: Segment): TanResponse {
|
|
166
192
|
return new TanResponse(
|
|
167
193
|
s.text(Hitan.process),
|
|
168
|
-
s.
|
|
194
|
+
s.verbatim(Hitan.reference),
|
|
169
195
|
s.text(Hitan.challenge),
|
|
170
196
|
s.binary(Hitan.hhdUc),
|
|
171
197
|
parseFintsDateTime(s.text(Hitan.validUntil, 0), s.text(Hitan.validUntil, 1)),
|
|
@@ -242,8 +268,17 @@ export function parseBalance(s: Segment, account: Account): Balance {
|
|
|
242
268
|
* The account a balance (HISAL) belongs to: international (IBAN, BIC, then national) from version 7, national before.
|
|
243
269
|
*/
|
|
244
270
|
export function balanceAccount(s: Segment): Account {
|
|
245
|
-
|
|
246
|
-
|
|
271
|
+
return accountAt(s, Hisal.account, s.version >= 7);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** The account camt transactions (HICAZ) belong to, which the first element names (international). */
|
|
275
|
+
export function camtAccount(s: Segment): Account {
|
|
276
|
+
return accountAt(s, 1, true);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** The account at `index` of `s`: an `international` one (KTI), else a national one (KTV). */
|
|
280
|
+
function accountAt(s: Segment, index: number, international: boolean): Account {
|
|
281
|
+
const fields = s.texts(index);
|
|
247
282
|
const national = international ? 2 : 0;
|
|
248
283
|
return new Account({
|
|
249
284
|
iban: international ? fields[0] : undefined,
|
|
@@ -295,8 +330,13 @@ export function parseSepaAccounts(s: Segment, defaultCountry: string, defaultBan
|
|
|
295
330
|
*/
|
|
296
331
|
const accountWidth = 4;
|
|
297
332
|
|
|
298
|
-
/**
|
|
299
|
-
|
|
333
|
+
/**
|
|
334
|
+
* Parses the TAN media list (HITAB versions 4 and 5, PIN/TAN "TAN-Medium-Liste").
|
|
335
|
+
*
|
|
336
|
+
* Each name whose text is not what `Segment.verbatim` reads, such as a name in UTF-8, goes into `namesAsSent`, keyed
|
|
337
|
+
* by its text: it has to go back as the bank sent it, which its text does not tell where it fits ISO 8859-1.
|
|
338
|
+
*/
|
|
339
|
+
export function parseTanMedia(s: Segment, namesAsSent?: Map<string, string>): TanMedium[] {
|
|
300
340
|
// Version 5 adds the security function in front of the card number.
|
|
301
341
|
const version5 = s.version >= 5;
|
|
302
342
|
const cardNumber = version5 ? 3 : 2;
|
|
@@ -310,6 +350,9 @@ export function parseTanMedia(s: Segment): TanMedium[] {
|
|
|
310
350
|
const fields = s.texts(i);
|
|
311
351
|
const mediumClass = fields[0];
|
|
312
352
|
if (mediumClass === undefined) continue;
|
|
353
|
+
const name = fields[after + 3];
|
|
354
|
+
const nameAsSent = s.verbatim(i, after + 3);
|
|
355
|
+
if (name !== undefined && nameAsSent !== undefined && nameAsSent !== name) namesAsSent?.set(name, nameAsSent);
|
|
313
356
|
media.push(
|
|
314
357
|
new TanMedium({
|
|
315
358
|
mediumClass,
|
|
@@ -319,7 +362,7 @@ export function parseTanMedia(s: Segment): TanMedium[] {
|
|
|
319
362
|
cardSequenceNumber: fields[cardNumber + 1],
|
|
320
363
|
validFrom: parseFintsDate(fields[after]),
|
|
321
364
|
validUntil: parseFintsDate(fields[after + 1]),
|
|
322
|
-
name
|
|
365
|
+
name,
|
|
323
366
|
mobileNumberMasked: fields[after + 4],
|
|
324
367
|
mobileNumber: fields[after + 5],
|
|
325
368
|
lastUsed: parseFintsDate(fields[after + 13]),
|
package/src/v3/syntax.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { BytesBuilder, latin1Decode, latin1Encode, utf8Encode } from '../util/bytes.js';
|
|
4
4
|
import { FormatError } from '../util/errors.js';
|
|
5
|
-
import { decodeBankText, tryParseInt } from '../util/text.js';
|
|
5
|
+
import { decodeBankText, isLatin1, tryParseInt } from '../util/text.js';
|
|
6
6
|
|
|
7
7
|
// FinTS 3.0 syntax characters.
|
|
8
8
|
const dataElementSeparator = 0x2b; // '+'
|
|
@@ -21,17 +21,30 @@ function isSyntaxCharacter(byte: number): boolean {
|
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* One (group) data element of a received segment: text or binary data, from `start` to `end` in `source`. A view or
|
|
26
|
+
* copy of its own would take more memory than most elements hold.
|
|
27
|
+
*/
|
|
25
28
|
export class DataElement {
|
|
26
29
|
private decoded: string | undefined;
|
|
27
30
|
|
|
28
31
|
constructor(
|
|
29
|
-
readonly
|
|
32
|
+
private readonly source: Uint8Array,
|
|
33
|
+
private readonly start: number,
|
|
34
|
+
private readonly end: number,
|
|
30
35
|
readonly isBinary: boolean,
|
|
36
|
+
/** Whether the text holds escape characters, which `bytes` leaves out. */
|
|
37
|
+
private readonly escaped = false,
|
|
31
38
|
) {}
|
|
32
39
|
|
|
40
|
+
/** The binary data, or the text without escape characters. */
|
|
41
|
+
get bytes(): Uint8Array {
|
|
42
|
+
const bytes = this.source.subarray(this.start, this.end);
|
|
43
|
+
return this.escaped ? unescape(bytes) : bytes;
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
get isEmpty(): boolean {
|
|
34
|
-
return !this.isBinary && this.
|
|
47
|
+
return !this.isBinary && this.start === this.end;
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
get text(): string {
|
|
@@ -46,7 +59,13 @@ export class DataElement {
|
|
|
46
59
|
* Index 0 is the segment header (type, number, version, reference); the data elements follow from index 1.
|
|
47
60
|
*/
|
|
48
61
|
export class Segment {
|
|
49
|
-
constructor(
|
|
62
|
+
constructor(
|
|
63
|
+
readonly elements: readonly (readonly DataElement[])[],
|
|
64
|
+
/** The bytes the segment was read from: `start` to `end` in `source`, terminator included. */
|
|
65
|
+
private readonly source: Uint8Array,
|
|
66
|
+
private readonly start: number,
|
|
67
|
+
private readonly end: number,
|
|
68
|
+
) {}
|
|
50
69
|
|
|
51
70
|
get type(): string {
|
|
52
71
|
return this.text(0)?.trim() ?? '';
|
|
@@ -74,10 +93,21 @@ export class Segment {
|
|
|
74
93
|
return e.text;
|
|
75
94
|
}
|
|
76
95
|
|
|
96
|
+
/** The binary data at `index`; `undefined` for an element that holds text. */
|
|
77
97
|
binary(index: number, sub = 0): Uint8Array | undefined {
|
|
98
|
+
const e = this.element(index, sub);
|
|
99
|
+
return e?.isBinary === true ? e.bytes : undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* The element at `index` as the bank sent it, for a value the client sends back, such as the dialog ID: untrimmed,
|
|
104
|
+
* and one character per byte (ISO 8859-1, the FinTS 3.0 character set), so that `Verbatim` gives the bank back the
|
|
105
|
+
* very bytes it sent, in whatever encoding. `undefined` for an empty element.
|
|
106
|
+
*/
|
|
107
|
+
verbatim(index: number, sub = 0): string | undefined {
|
|
78
108
|
const e = this.element(index, sub);
|
|
79
109
|
if (e === undefined || e.isEmpty) return undefined;
|
|
80
|
-
return e.bytes;
|
|
110
|
+
return latin1Decode(e.bytes);
|
|
81
111
|
}
|
|
82
112
|
|
|
83
113
|
integer(index: number, sub = 0): number | undefined {
|
|
@@ -89,22 +119,9 @@ export class Segment {
|
|
|
89
119
|
return this.group(index).map((e) => (e.isEmpty ? undefined : e.text));
|
|
90
120
|
}
|
|
91
121
|
|
|
122
|
+
/** The bytes the segment was read from, as the bank sent them, terminator included. */
|
|
92
123
|
encode(): Uint8Array {
|
|
93
|
-
|
|
94
|
-
this.elements.forEach((g, i) => {
|
|
95
|
-
if (i > 0) out.addByte(dataElementSeparator);
|
|
96
|
-
g.forEach((e, j) => {
|
|
97
|
-
if (j > 0) out.addByte(groupElementSeparator);
|
|
98
|
-
if (e.isBinary) writeBinary(out, e.bytes);
|
|
99
|
-
else writeEscaped(out, e.bytes);
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
out.addByte(segmentTerminator);
|
|
103
|
-
return out.takeBytes();
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
toString(): string {
|
|
107
|
-
return decodeBankText(this.encode());
|
|
124
|
+
return this.source.subarray(this.start, this.end);
|
|
108
125
|
}
|
|
109
126
|
}
|
|
110
127
|
|
|
@@ -124,111 +141,154 @@ function writeBinary(out: BytesBuilder, bytes: Uint8Array): void {
|
|
|
124
141
|
|
|
125
142
|
/**
|
|
126
143
|
* Encodes `value` in ISO 8859-1, the FinTS 3.0 character set, or in UTF-8 where it does not fit: such text can only
|
|
127
|
-
* come from a bank that sends UTF-8, such as a TAN medium named `Max’s iPhone`.
|
|
128
|
-
* ISO 8859-1 up front.
|
|
144
|
+
* come from a bank that sends UTF-8, such as a TAN medium named `Max’s iPhone`. `FinTsConfig` checks the login data for
|
|
145
|
+
* ISO 8859-1 up front, and the client the PIN and the TAN.
|
|
146
|
+
*
|
|
147
|
+
* @internal Exported for the tests.
|
|
129
148
|
*/
|
|
130
149
|
export function encodeText(value: string): Uint8Array {
|
|
131
150
|
return isLatin1(value) ? latin1Encode(value) : utf8Encode(value);
|
|
132
151
|
}
|
|
133
152
|
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
/**
|
|
154
|
+
* The data elements one message may hold, segment headers and group elements included: several times what the
|
|
155
|
+
* parameter data of a user with hundreds of accounts take, and few enough that a hostile message cannot exhaust the
|
|
156
|
+
* memory, at about `dataElementMemory` bytes each.
|
|
157
|
+
*/
|
|
158
|
+
export const maxDataElements = 200_000;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The memory a data element of a received message takes, in bytes, with its share of the segment and of a return code:
|
|
162
|
+
* about this much in V8, however few bytes of the message it is made of.
|
|
163
|
+
*/
|
|
164
|
+
export const dataElementMemory = 256;
|
|
165
|
+
|
|
166
|
+
/** Every empty data element of a received segment; it has no bytes to change. */
|
|
167
|
+
const emptyElement = new DataElement(new Uint8Array(0), 0, 0, false);
|
|
168
|
+
|
|
169
|
+
function isSeparator(byte: number | undefined): boolean {
|
|
170
|
+
return byte === dataElementSeparator || byte === groupElementSeparator || byte === segmentTerminator;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** Line breaks and spaces between segments are padding, not content. */
|
|
174
|
+
function isPadding(byte: number): boolean {
|
|
175
|
+
return byte === 0x0a || byte === 0x0d || byte === 0x20;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function isDigit(byte: number | undefined): boolean {
|
|
179
|
+
return byte !== undefined && byte >= 0x30 && byte <= 0x39;
|
|
138
180
|
}
|
|
139
181
|
|
|
140
182
|
/**
|
|
141
183
|
* Splits a FinTS 3.0 message into segments, resolving escapes and binary data (`@length@bytes`).
|
|
142
184
|
*
|
|
143
|
-
* Throws `FormatError` for a
|
|
185
|
+
* The elements share the memory of `data`. Throws `FormatError` for anything a complete message cannot hold: a
|
|
186
|
+
* malformed binary length, binary data beyond the end of `data` or not followed by a separator, a last segment without
|
|
187
|
+
* terminator, and more elements than `budget` has left, which parses of the same message share.
|
|
144
188
|
*/
|
|
145
|
-
export function parseSegments(data: Uint8Array): Segment[] {
|
|
189
|
+
export function parseSegments(data: Uint8Array, budget = { elements: maxDataElements }): Segment[] {
|
|
146
190
|
const segments: Segment[] = [];
|
|
147
|
-
|
|
148
|
-
let
|
|
149
|
-
let
|
|
150
|
-
let
|
|
151
|
-
let
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
191
|
+
// Created with their first element: an array that starts empty reserves room for more than most of them hold.
|
|
192
|
+
let elements: DataElement[][] | undefined;
|
|
193
|
+
let group: DataElement[] | undefined;
|
|
194
|
+
let segmentStart = 0;
|
|
195
|
+
let i = 0;
|
|
196
|
+
|
|
197
|
+
const readBinary = (): DataElement => {
|
|
198
|
+
let end = i + 1;
|
|
199
|
+
let length = 0;
|
|
200
|
+
while (isDigit(data[end])) {
|
|
201
|
+
length = length * 10 + data[end++]! - 0x30;
|
|
202
|
+
if (length > data.length) throw new FormatError('Binary data beyond the end of the message');
|
|
203
|
+
}
|
|
204
|
+
if (end === i + 1 || data[end] !== binaryMarker) throw new FormatError('Malformed binary length');
|
|
205
|
+
const start = end + 1;
|
|
206
|
+
if (length > data.length - start) throw new FormatError('Binary data beyond the end of the message');
|
|
207
|
+
i = start + length;
|
|
208
|
+
if (i < data.length && !isSeparator(data[i])) throw new FormatError('Binary data not followed by a separator');
|
|
209
|
+
return new DataElement(data, start, i, true);
|
|
157
210
|
};
|
|
158
211
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
212
|
+
const readText = (): DataElement => {
|
|
213
|
+
const start = i;
|
|
214
|
+
let escaped = false;
|
|
215
|
+
while (i < data.length && !isSeparator(data[i])) {
|
|
216
|
+
if (data[i] === escapeCharacter) {
|
|
217
|
+
escaped = true;
|
|
218
|
+
i++;
|
|
219
|
+
}
|
|
220
|
+
i++;
|
|
221
|
+
}
|
|
222
|
+
if (i > data.length) throw new FormatError('Escape character at the end of the message');
|
|
223
|
+
return i === start ? emptyElement : new DataElement(data, start, i, false, escaped);
|
|
168
224
|
};
|
|
169
225
|
|
|
170
|
-
let i = 0;
|
|
171
226
|
while (i < data.length) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (i + 1 < data.length) current.push(data[i + 1]!);
|
|
175
|
-
segmentStarted = true;
|
|
176
|
-
i += 2;
|
|
227
|
+
if (elements === undefined && group === undefined && isPadding(data[i]!)) {
|
|
228
|
+
i++;
|
|
177
229
|
continue;
|
|
178
230
|
}
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
231
|
+
if (--budget.elements < 0) throw new FormatError('Too many data elements');
|
|
232
|
+
if (elements === undefined && group === undefined) segmentStart = i;
|
|
233
|
+
const element = data[i] === binaryMarker ? readBinary() : readText();
|
|
234
|
+
if (group === undefined) group = [element];
|
|
235
|
+
else group.push(element);
|
|
236
|
+
const separator = data[i++];
|
|
237
|
+
if (separator === groupElementSeparator) continue;
|
|
238
|
+
if (elements === undefined) elements = [group];
|
|
239
|
+
else elements.push(group);
|
|
240
|
+
group = undefined;
|
|
241
|
+
if (separator === segmentTerminator) {
|
|
242
|
+
const segment = new Segment(elements, data, segmentStart, i);
|
|
243
|
+
// Skip empty segments, such as a stray double terminator.
|
|
244
|
+
if (segment.type !== '') segments.push(segment);
|
|
245
|
+
elements = undefined;
|
|
190
246
|
}
|
|
191
|
-
if (c === groupElementSeparator) {
|
|
192
|
-
endElement();
|
|
193
|
-
} else if (c === dataElementSeparator) {
|
|
194
|
-
endElement();
|
|
195
|
-
elements.push(group);
|
|
196
|
-
group = [];
|
|
197
|
-
} else if (c === segmentTerminator) {
|
|
198
|
-
endSegment();
|
|
199
|
-
} else {
|
|
200
|
-
// Line breaks and spaces between segments are padding, not content.
|
|
201
|
-
const isPadding =
|
|
202
|
-
!segmentStarted &&
|
|
203
|
-
current.length === 0 &&
|
|
204
|
-
elements.length === 0 &&
|
|
205
|
-
group.length === 0 &&
|
|
206
|
-
(c === 0x0a || c === 0x0d || c === 0x20);
|
|
207
|
-
if (!isPadding) {
|
|
208
|
-
current.push(c);
|
|
209
|
-
segmentStarted = true;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
i++;
|
|
213
247
|
}
|
|
214
|
-
if (
|
|
248
|
+
if (elements !== undefined || group !== undefined) throw new FormatError('Segment without terminator');
|
|
215
249
|
return segments;
|
|
216
250
|
}
|
|
217
251
|
|
|
252
|
+
/** `text` without the escape characters in front of syntax characters. */
|
|
253
|
+
function unescape(text: Uint8Array): Uint8Array {
|
|
254
|
+
const out = new Uint8Array(text.length);
|
|
255
|
+
let o = 0;
|
|
256
|
+
for (let j = 0; j < text.length; j++) {
|
|
257
|
+
if (text[j] === escapeCharacter) j++;
|
|
258
|
+
out[o++] = text[j]!;
|
|
259
|
+
}
|
|
260
|
+
return out.subarray(0, o);
|
|
261
|
+
}
|
|
262
|
+
|
|
218
263
|
/** Binary data in a segment to send, written as `@length@bytes`. */
|
|
219
264
|
export class Binary {
|
|
220
265
|
constructor(readonly bytes: Uint8Array) {}
|
|
221
266
|
}
|
|
222
267
|
|
|
268
|
+
/**
|
|
269
|
+
* A value the bank issued, such as the dialog ID, to send back as `Segment.verbatim` reads it: escaped, but not
|
|
270
|
+
* trimmed, so that the bank gets back the very bytes it sent. Text from elsewhere, such as a TAN medium name the app
|
|
271
|
+
* passes, goes out as `encodeText` encodes it.
|
|
272
|
+
*/
|
|
273
|
+
export class Verbatim {
|
|
274
|
+
constructor(readonly text: string) {}
|
|
275
|
+
|
|
276
|
+
/** `text` as a `Verbatim`; `undefined` stays empty. */
|
|
277
|
+
static of(text: string | undefined): Verbatim | undefined {
|
|
278
|
+
return text === undefined ? undefined : new Verbatim(text);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
223
282
|
/** A value of a segment to send; see `SegmentSpec`. */
|
|
224
|
-
export type SegmentValue = string | number | boolean | Binary | null | undefined | readonly SegmentValue[];
|
|
283
|
+
export type SegmentValue = string | number | boolean | Binary | Verbatim | null | undefined | readonly SegmentValue[];
|
|
225
284
|
|
|
226
285
|
/**
|
|
227
286
|
* A segment to send.
|
|
228
287
|
*
|
|
229
288
|
* `elements` holds the data elements after the header: `null` or `undefined` for an empty element, an array for a data
|
|
230
|
-
* element group, a boolean for `J`/`N`, `Binary` for binary data
|
|
231
|
-
* trailing blanks. Trailing empty elements are left out, as the specification
|
|
289
|
+
* element group, a boolean for `J`/`N`, `Binary` for binary data, `Verbatim` for a value the bank issued, and anything
|
|
290
|
+
* else as its text, without leading and trailing blanks. Trailing empty elements are left out, as the specification
|
|
291
|
+
* asks.
|
|
232
292
|
*/
|
|
233
293
|
export class SegmentSpec {
|
|
234
294
|
constructor(
|
|
@@ -296,6 +356,8 @@ function encodeValue(value: SingleValue): Uint8Array {
|
|
|
296
356
|
// Empty.
|
|
297
357
|
} else if (value instanceof Binary) {
|
|
298
358
|
writeBinary(out, value.bytes);
|
|
359
|
+
} else if (value instanceof Verbatim) {
|
|
360
|
+
writeEscaped(out, encodeText(value.text));
|
|
299
361
|
} else if (typeof value === 'boolean') {
|
|
300
362
|
out.addByte(value ? 0x4a : 0x4e); // 'J' or 'N'
|
|
301
363
|
} else {
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { MessageDirection, MessageLogger } from '../core/client.js';
|
|
2
|
-
import { type FinTsTransport } from '../core/transport.js';
|
|
3
|
-
/**
|
|
4
|
-
* Throws `FinTsProtocolError` if a message of `size` bytes exceeds the bank's `limit` in kilobytes (1024 bytes): the
|
|
5
|
-
* specification does not allow to send such a message.
|
|
6
|
-
*/
|
|
7
|
-
export declare function checkMessageSize(size: number, limit: number | undefined): void;
|
|
8
|
-
/** The bank's FinTS endpoint: posts messages there and reports them to the logger. */
|
|
9
|
-
export declare class BankConnection {
|
|
10
|
-
/** Where the messages go. */
|
|
11
|
-
readonly url: URL;
|
|
12
|
-
private readonly logger;
|
|
13
|
-
private readonly transport;
|
|
14
|
-
/** A connection to `url` through `transport`, an `HttpTransport` by default. */
|
|
15
|
-
constructor(
|
|
16
|
-
/** Where the messages go. */
|
|
17
|
-
url: URL, transport: FinTsTransport | undefined, logger: MessageLogger | undefined);
|
|
18
|
-
/**
|
|
19
|
-
* Posts `body`; reports any failure of the transport as `FinTsError`, with the original as its `cause`: a custom
|
|
20
|
-
* transport that calls `fetch` fails with a `TypeError` where the network does.
|
|
21
|
-
*/
|
|
22
|
-
post(body: Uint8Array, contentType: string): Promise<Uint8Array>;
|
|
23
|
-
/** Reports a message to the logger; `message` is only built when a logger is set. */
|
|
24
|
-
log(direction: MessageDirection, message: () => string): void;
|
|
25
|
-
/** Closes the transport. */
|
|
26
|
-
close(): void;
|
|
27
|
-
}
|
|
28
|
-
//# sourceMappingURL=bank-connection.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bank-connection.d.ts","sourceRoot":"","sources":["../../src/session/bank-connection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEzE,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAI9E;AAED,sFAAsF;AACtF,qBAAa,cAAc;IAKvB,6BAA6B;IAC7B,QAAQ,CAAC,GAAG,EAAE,GAAG;IAEjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAiB;IAE3C,gFAAgF;;IAE9E,6BAA6B;IACpB,GAAG,EAAE,GAAG,EACjB,SAAS,EAAE,cAAc,GAAG,SAAS,EACpB,MAAM,EAAE,aAAa,GAAG,SAAS;IAKpD;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAStE,qFAAqF;IACrF,GAAG,CAAC,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,MAAM,GAAG,IAAI;IAI7D,4BAA4B;IAC5B,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { FinTsError, FinTsProtocolError, FinTsTransportError } from '../core/errors.js';
|
|
2
|
-
import { HttpTransport } from '../core/transport.js';
|
|
3
|
-
/**
|
|
4
|
-
* Throws `FinTsProtocolError` if a message of `size` bytes exceeds the bank's `limit` in kilobytes (1024 bytes): the
|
|
5
|
-
* specification does not allow to send such a message.
|
|
6
|
-
*/
|
|
7
|
-
export function checkMessageSize(size, limit) {
|
|
8
|
-
if (limit !== undefined && limit > 0 && size > limit * 1024) {
|
|
9
|
-
throw new FinTsProtocolError(`A message of ${size} bytes exceeds the bank's maximum message size of ${limit} KB`);
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
/** The bank's FinTS endpoint: posts messages there and reports them to the logger. */
|
|
13
|
-
export class BankConnection {
|
|
14
|
-
url;
|
|
15
|
-
logger;
|
|
16
|
-
transport;
|
|
17
|
-
/** A connection to `url` through `transport`, an `HttpTransport` by default. */
|
|
18
|
-
constructor(
|
|
19
|
-
/** Where the messages go. */
|
|
20
|
-
url, transport, logger) {
|
|
21
|
-
this.url = url;
|
|
22
|
-
this.logger = logger;
|
|
23
|
-
this.transport = transport ?? new HttpTransport();
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Posts `body`; reports any failure of the transport as `FinTsError`, with the original as its `cause`: a custom
|
|
27
|
-
* transport that calls `fetch` fails with a `TypeError` where the network does.
|
|
28
|
-
*/
|
|
29
|
-
async post(body, contentType) {
|
|
30
|
-
try {
|
|
31
|
-
return await this.transport.post(this.url, body, contentType);
|
|
32
|
-
}
|
|
33
|
-
catch (error) {
|
|
34
|
-
if (error instanceof FinTsError)
|
|
35
|
-
throw error;
|
|
36
|
-
throw new FinTsTransportError(error instanceof Error ? error.message : String(error), { cause: error });
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** Reports a message to the logger; `message` is only built when a logger is set. */
|
|
40
|
-
log(direction, message) {
|
|
41
|
-
this.logger?.(direction, message());
|
|
42
|
-
}
|
|
43
|
-
/** Closes the transport. */
|
|
44
|
-
close() {
|
|
45
|
-
this.transport.close();
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
//# sourceMappingURL=bank-connection.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"bank-connection.js","sourceRoot":"","sources":["../../src/session/bank-connection.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,aAAa,EAAuB,MAAM,sBAAsB,CAAC;AAE1E;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAyB;IACtE,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,GAAG,IAAI,EAAE,CAAC;QAC5D,MAAM,IAAI,kBAAkB,CAAC,gBAAgB,IAAI,qDAAqD,KAAK,KAAK,CAAC,CAAC;IACpH,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,MAAM,OAAO,cAAc;IAMd;IAEQ;IAPF,SAAS,CAAiB;IAE3C,gFAAgF;IAChF;IACE,6BAA6B;IACpB,GAAQ,EACjB,SAAqC,EACpB,MAAiC;QAFzC,QAAG,GAAH,GAAG,CAAK;QAEA,WAAM,GAAN,MAAM,CAA2B;QAElD,IAAI,CAAC,SAAS,GAAG,SAAS,IAAI,IAAI,aAAa,EAAE,CAAC;IACpD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,CAAC,IAAgB,EAAE,WAAmB;QAC9C,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,EAAE,WAAW,CAAC,CAAC;QAChE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,UAAU;gBAAE,MAAM,KAAK,CAAC;YAC7C,MAAM,IAAI,mBAAmB,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;IAED,qFAAqF;IACrF,GAAG,CAAC,SAA2B,EAAE,OAAqB;QACpD,IAAI,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,4BAA4B;IAC5B,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF"}
|