@coderoftime/fints 0.1.0 → 0.3.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 +519 -1
- package/README.md +138 -40
- package/dist/client.d.ts +49 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +242 -0
- package/dist/client.js.map +1 -0
- package/dist/core/account.d.ts +46 -7
- package/dist/core/account.d.ts.map +1 -1
- package/dist/core/account.js +143 -24
- package/dist/core/account.js.map +1 -1
- package/dist/core/amount.d.ts +15 -4
- package/dist/core/amount.d.ts.map +1 -1
- package/dist/core/amount.js +46 -9
- package/dist/core/amount.js.map +1 -1
- package/dist/core/balance.d.ts +4 -0
- package/dist/core/balance.d.ts.map +1 -1
- package/dist/core/balance.js +11 -5
- package/dist/core/balance.js.map +1 -1
- package/dist/core/bank-info.d.ts +21 -4
- package/dist/core/bank-info.d.ts.map +1 -1
- package/dist/core/bank-info.js +26 -5
- package/dist/core/bank-info.js.map +1 -1
- package/dist/core/client.d.ts +81 -19
- package/dist/core/client.d.ts.map +1 -1
- package/dist/core/config.d.ts +40 -20
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +85 -30
- package/dist/core/config.js.map +1 -1
- package/dist/core/errors.d.ts +100 -26
- package/dist/core/errors.d.ts.map +1 -1
- package/dist/core/errors.js +136 -53
- package/dist/core/errors.js.map +1 -1
- package/dist/core/feedback.d.ts +11 -1
- package/dist/core/feedback.d.ts.map +1 -1
- package/dist/core/feedback.js +14 -2
- package/dist/core/feedback.js.map +1 -1
- package/dist/core/transaction.d.ts +11 -1
- package/dist/core/transaction.d.ts.map +1 -1
- package/dist/core/transaction.js +21 -4
- package/dist/core/transaction.js.map +1 -1
- package/dist/core/transport.d.ts +48 -6
- package/dist/core/transport.d.ts.map +1 -1
- package/dist/core/transport.js +126 -29
- package/dist/core/transport.js.map +1 -1
- package/dist/formats/camt.d.ts +28 -5
- package/dist/formats/camt.d.ts.map +1 -1
- package/dist/formats/camt.js +132 -50
- package/dist/formats/camt.js.map +1 -1
- package/dist/formats/mt940.d.ts +29 -5
- package/dist/formats/mt940.d.ts.map +1 -1
- package/dist/formats/mt940.js +209 -113
- 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 +28 -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 +9 -0
- package/dist/formats/sepa-purpose.d.ts.map +1 -1
- package/dist/formats/sepa-purpose.js +32 -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 +11 -11
- package/dist/session/dialog-runner.d.ts.map +1 -1
- package/dist/session/dialog-runner.js +34 -19
- 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 +170 -83
- 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 +41 -24
- package/dist/session/session-setup.d.ts.map +1 -1
- package/dist/session/session-setup.js +106 -63
- package/dist/session/session-setup.js.map +1 -1
- package/dist/session/session-state.d.ts +10 -6
- package/dist/session/session-state.d.ts.map +1 -1
- package/dist/session/session-state.js +72 -18
- package/dist/session/session-state.js.map +1 -1
- package/dist/session/signed-dialog.d.ts +83 -0
- package/dist/session/signed-dialog.d.ts.map +1 -0
- package/dist/session/signed-dialog.js +185 -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 +14 -5
- package/dist/session/user-prompts.d.ts.map +1 -1
- package/dist/session/user-prompts.js +53 -18
- package/dist/session/user-prompts.js.map +1 -1
- package/dist/tan/challenge.d.ts +47 -24
- package/dist/tan/challenge.d.ts.map +1 -1
- package/dist/tan/challenge.js +56 -51
- package/dist/tan/challenge.js.map +1 -1
- package/dist/tan/flicker.d.ts +11 -1
- package/dist/tan/flicker.d.ts.map +1 -1
- package/dist/tan/flicker.js +70 -24
- package/dist/tan/flicker.js.map +1 -1
- package/dist/tan/tan-image.d.ts +6 -2
- package/dist/tan/tan-image.d.ts.map +1 -1
- package/dist/tan/tan-image.js +14 -6
- package/dist/tan/tan-image.js.map +1 -1
- package/dist/tan/tan-models.d.ts +14 -2
- package/dist/tan/tan-models.d.ts.map +1 -1
- package/dist/tan/tan-models.js +24 -7
- package/dist/tan/tan-models.js.map +1 -1
- package/dist/util/arguments.d.ts +13 -0
- package/dist/util/arguments.d.ts.map +1 -0
- package/dist/util/arguments.js +40 -0
- package/dist/util/arguments.js.map +1 -0
- package/dist/util/async.d.ts +4 -3
- package/dist/util/async.d.ts.map +1 -1
- package/dist/util/async.js +6 -7
- package/dist/util/async.js.map +1 -1
- package/dist/util/bytes.d.ts +3 -4
- package/dist/util/bytes.d.ts.map +1 -1
- package/dist/util/bytes.js +13 -21
- 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 +6 -20
- package/dist/util/dates.d.ts.map +1 -1
- package/dist/util/dates.js +58 -28
- package/dist/util/dates.js.map +1 -1
- package/dist/util/errors.d.ts +13 -3
- package/dist/util/errors.d.ts.map +1 -1
- package/dist/util/errors.js +19 -6
- package/dist/util/errors.js.map +1 -1
- package/dist/util/immutable-map.d.ts +5 -2
- package/dist/util/immutable-map.d.ts.map +1 -1
- package/dist/util/immutable-map.js +15 -12
- package/dist/util/immutable-map.js.map +1 -1
- package/dist/util/inspect.d.ts +11 -0
- package/dist/util/inspect.d.ts.map +1 -0
- package/dist/util/inspect.js +14 -0
- package/dist/util/inspect.js.map +1 -0
- 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 +38 -5
- package/dist/util/text.d.ts.map +1 -1
- package/dist/util/text.js +90 -16
- package/dist/util/text.js.map +1 -1
- package/dist/util/xml.d.ts +9 -6
- package/dist/util/xml.d.ts.map +1 -1
- package/dist/util/xml.js +129 -56
- 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 +54 -0
- package/dist/v3/bank-connection.d.ts.map +1 -0
- package/dist/v3/bank-connection.js +118 -0
- package/dist/v3/bank-connection.js.map +1 -0
- package/dist/v3/dialog.d.ts +84 -46
- package/dist/v3/dialog.d.ts.map +1 -1
- package/dist/v3/dialog.js +174 -60
- package/dist/v3/dialog.js.map +1 -1
- package/dist/v3/message.d.ts +32 -11
- package/dist/v3/message.d.ts.map +1 -1
- package/dist/v3/message.js +144 -83
- package/dist/v3/message.js.map +1 -1
- package/dist/v3/parameter-store.d.ts +111 -0
- package/dist/v3/parameter-store.d.ts.map +1 -0
- package/dist/v3/parameter-store.js +197 -0
- package/dist/v3/parameter-store.js.map +1 -0
- package/dist/v3/parameters.d.ts +44 -17
- package/dist/v3/parameters.d.ts.map +1 -1
- package/dist/v3/parameters.js +136 -60
- package/dist/v3/parameters.js.map +1 -1
- package/dist/v3/protocol.d.ts +50 -50
- package/dist/v3/protocol.d.ts.map +1 -1
- package/dist/v3/protocol.js +91 -210
- 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 +128 -0
- package/dist/v3/queries.js.map +1 -0
- package/dist/v3/segments.d.ts +43 -19
- package/dist/v3/segments.d.ts.map +1 -1
- package/dist/v3/segments.js +84 -33
- package/dist/v3/segments.js.map +1 -1
- package/dist/v3/syntax.d.ts +68 -13
- package/dist/v3/syntax.d.ts.map +1 -1
- package/dist/v3/syntax.js +183 -102
- package/dist/v3/syntax.js.map +1 -1
- package/package.json +10 -6
- package/src/client.ts +293 -0
- package/src/core/account.ts +153 -24
- package/src/core/amount.ts +45 -10
- package/src/core/balance.ts +12 -5
- package/src/core/bank-info.ts +29 -5
- package/src/core/client.ts +83 -19
- package/src/core/config.ts +95 -35
- package/src/core/errors.ts +148 -54
- package/src/core/feedback.ts +16 -2
- package/src/core/transaction.ts +22 -4
- package/src/core/transport.ts +153 -32
- package/src/formats/camt.ts +153 -45
- package/src/formats/mt940.ts +244 -122
- package/src/formats/readers.ts +28 -31
- package/src/formats/record-budget.ts +18 -0
- package/src/formats/sepa-purpose.ts +29 -2
- package/src/index.ts +4 -2
- package/src/session/app-approval.ts +51 -38
- package/src/session/dialog-runner.ts +61 -31
- package/src/session/login-guard.ts +189 -87
- package/src/session/sepa-accounts.ts +63 -45
- package/src/session/session-setup.ts +105 -68
- package/src/session/session-state.ts +74 -23
- package/src/session/signed-dialog.ts +222 -0
- package/src/session/tan-selection.ts +50 -39
- package/src/session/user-prompts.ts +62 -18
- package/src/tan/challenge.ts +87 -64
- package/src/tan/flicker.ts +72 -23
- package/src/tan/tan-image.ts +15 -6
- package/src/tan/tan-models.ts +26 -7
- package/src/util/arguments.ts +40 -0
- package/src/util/async.ts +10 -9
- package/src/util/bytes.ts +14 -20
- package/src/util/call-context.ts +3 -2
- package/src/util/dates.ts +64 -33
- package/src/util/errors.ts +22 -6
- package/src/util/immutable-map.ts +15 -12
- package/src/util/inspect.ts +14 -0
- package/src/util/json.ts +16 -0
- package/src/util/text.ts +87 -15
- package/src/util/xml.ts +130 -52
- package/src/v3/account-extension.ts +7 -12
- package/src/v3/bank-connection.ts +126 -0
- package/src/v3/dialog.ts +186 -92
- package/src/v3/message.ts +140 -79
- package/src/v3/parameter-store.ts +226 -0
- package/src/v3/parameters.ts +166 -66
- package/src/v3/protocol.ts +103 -253
- package/src/v3/queries.ts +198 -0
- package/src/v3/segments.ts +86 -31
- package/src/v3/syntax.ts +172 -93
- 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,25 +3,37 @@
|
|
|
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 {
|
|
9
|
+
import { trimBlanks } from '../util/text.js';
|
|
10
|
+
import { type Segment, SegmentSpec, type SegmentValue, Verbatim } from './syntax.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The filler the specification names for a reference that does not exist.
|
|
14
|
+
*
|
|
15
|
+
* The bank puts it into a dummy answer when no approval is needed. The client sends it as the TAN medium name in the
|
|
16
|
+
* dialog that looks up the TAN media, before it knows a medium; the bank ignores it there (PIN/TAN B.4.3.1.3).
|
|
17
|
+
*/
|
|
18
|
+
export const noReference = 'noref';
|
|
11
19
|
|
|
12
20
|
/** Bank identification (KIK): country code and bank code. */
|
|
13
|
-
|
|
21
|
+
function bankIdentification(countryCode: string, bankCode: string): SegmentValue {
|
|
14
22
|
return [countryCode, bankCode];
|
|
15
23
|
}
|
|
16
24
|
|
|
17
25
|
/** National account (KTV): account number, sub-account and bank identification. */
|
|
18
|
-
|
|
26
|
+
function nationalAccount(a: Account): SegmentValue[] {
|
|
19
27
|
return [a.accountNumber, a.subAccount, a.countryCode, a.bankCode];
|
|
20
28
|
}
|
|
21
29
|
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
/**
|
|
31
|
+
* International account (KTI): IBAN and BIC, then the national account where the bank allows it (HISPAS). An IBAN goes
|
|
32
|
+
* only with its BIC (Messages B.3.2): without one the national account names the account, where it has a number.
|
|
33
|
+
*/
|
|
34
|
+
function internationalAccount(a: Account, nationalAllowed: boolean): SegmentValue[] {
|
|
35
|
+
const iban = a.bic !== undefined || a.accountNumber === undefined ? a.iban : undefined;
|
|
36
|
+
return [iban, a.bic, ...(nationalAllowed || iban === undefined ? nationalAccount(a) : [])];
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
function dateOrEmpty(date: Date | undefined): string | undefined {
|
|
@@ -33,7 +45,10 @@ const orderReferenceField = 5;
|
|
|
33
45
|
const moreTansField = 6;
|
|
34
46
|
const tanMediumField = 11;
|
|
35
47
|
|
|
36
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Builders for the request segments the client sends. Values the bank issued, such as the customer system ID, the TAN
|
|
50
|
+
* order reference and the position to continue at, go back as `Verbatim`.
|
|
51
|
+
*/
|
|
37
52
|
export const Segments = Object.freeze({
|
|
38
53
|
/** HKIDN: who is logging in, from which customer system. */
|
|
39
54
|
identification(parts: {
|
|
@@ -46,7 +61,7 @@ export const Segments = Object.freeze({
|
|
|
46
61
|
return new SegmentSpec('HKIDN', 2, [
|
|
47
62
|
bankIdentification(parts.countryCode, parts.bankCode),
|
|
48
63
|
parts.customerId,
|
|
49
|
-
parts.systemId,
|
|
64
|
+
new Verbatim(parts.systemId),
|
|
50
65
|
parts.systemIdRequired ? 1 : 0,
|
|
51
66
|
]);
|
|
52
67
|
},
|
|
@@ -74,71 +89,90 @@ export const Segments = Object.freeze({
|
|
|
74
89
|
},
|
|
75
90
|
|
|
76
91
|
dialogEnd(dialogId: string): SegmentSpec {
|
|
77
|
-
return new SegmentSpec('HKEND', 1, [dialogId]);
|
|
92
|
+
return new SegmentSpec('HKEND', 1, [new Verbatim(dialogId)]);
|
|
78
93
|
},
|
|
79
94
|
|
|
80
|
-
/**
|
|
81
|
-
|
|
95
|
+
/**
|
|
96
|
+
* HKTAN process 4: asks for strong customer authentication of the `segmentType` sent in the same message, with the TAN
|
|
97
|
+
* medium `mediumName`: a `Verbatim` where it has to go back as the bank sent it (`ParameterStore.tanMediumToSend`).
|
|
98
|
+
*/
|
|
99
|
+
tanProcess4(version: number, segmentType: string, mediumName?: string | Verbatim): SegmentSpec {
|
|
82
100
|
return SegmentSpec.numbered('HKTAN', version, { 1: '4', 2: segmentType, [tanMediumField]: mediumName });
|
|
83
101
|
},
|
|
84
102
|
|
|
85
103
|
/** HKTAN process 2: submits the TAN (carried in the signature) for the order with `reference`. */
|
|
86
104
|
tanProcess2(version: number, reference: string): SegmentSpec {
|
|
87
|
-
return SegmentSpec.numbered('HKTAN', version, {
|
|
105
|
+
return SegmentSpec.numbered('HKTAN', version, {
|
|
106
|
+
1: '2',
|
|
107
|
+
[orderReferenceField]: new Verbatim(reference),
|
|
108
|
+
[moreTansField]: false,
|
|
109
|
+
});
|
|
88
110
|
},
|
|
89
111
|
|
|
90
112
|
/** HKTAN process S: asks whether the order with `reference` was approved in the banking app. */
|
|
91
113
|
tanProcessStatus(version: number, reference: string): SegmentSpec {
|
|
92
|
-
return SegmentSpec.numbered('HKTAN', version, {
|
|
114
|
+
return SegmentSpec.numbered('HKTAN', version, {
|
|
115
|
+
1: 'S',
|
|
116
|
+
[orderReferenceField]: new Verbatim(reference),
|
|
117
|
+
[moreTansField]: false,
|
|
118
|
+
});
|
|
93
119
|
},
|
|
94
120
|
|
|
95
|
-
/**
|
|
96
|
-
|
|
121
|
+
/**
|
|
122
|
+
* HKSAL: the balance of one account (not of all accounts), named by its national account as well where
|
|
123
|
+
* `nationalAllowed` (HISPAS).
|
|
124
|
+
*/
|
|
125
|
+
balance(version: number, account: Account, nationalAllowed: boolean, continuation?: string): SegmentSpec {
|
|
97
126
|
return new SegmentSpec('HKSAL', version, [
|
|
98
|
-
version >= 7 ? internationalAccount(account) : nationalAccount(account),
|
|
127
|
+
version >= 7 ? internationalAccount(account, nationalAllowed) : nationalAccount(account),
|
|
99
128
|
false,
|
|
100
129
|
null,
|
|
101
|
-
continuation,
|
|
130
|
+
Verbatim.of(continuation),
|
|
102
131
|
]);
|
|
103
132
|
},
|
|
104
133
|
|
|
105
|
-
/** HKKAZ: MT940 transactions of one account
|
|
134
|
+
/** HKKAZ: MT940 transactions of one account, named by its national account as well where `nationalAllowed`. */
|
|
106
135
|
transactionsMt940(
|
|
107
136
|
version: number,
|
|
108
137
|
account: Account,
|
|
138
|
+
nationalAllowed: boolean,
|
|
109
139
|
period: { from?: Date | undefined; to?: Date | undefined; continuation?: string | undefined },
|
|
110
140
|
): SegmentSpec {
|
|
111
141
|
return new SegmentSpec('HKKAZ', version, [
|
|
112
|
-
version >= 7 ? internationalAccount(account) : nationalAccount(account),
|
|
142
|
+
version >= 7 ? internationalAccount(account, nationalAllowed) : nationalAccount(account),
|
|
113
143
|
false,
|
|
114
144
|
dateOrEmpty(period.from),
|
|
115
145
|
dateOrEmpty(period.to),
|
|
116
146
|
null,
|
|
117
|
-
period.continuation,
|
|
147
|
+
Verbatim.of(period.continuation),
|
|
118
148
|
]);
|
|
119
149
|
},
|
|
120
150
|
|
|
121
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* HKCAZ: camt transactions of one account in one of `formats`, named by its national account as well where
|
|
153
|
+
* `nationalAllowed`.
|
|
154
|
+
*/
|
|
122
155
|
transactionsCamt(
|
|
123
156
|
version: number,
|
|
124
157
|
account: Account,
|
|
158
|
+
nationalAllowed: boolean,
|
|
125
159
|
formats: readonly string[],
|
|
126
160
|
period: { from?: Date | undefined; to?: Date | undefined; continuation?: string | undefined },
|
|
127
161
|
): SegmentSpec {
|
|
128
162
|
return new SegmentSpec('HKCAZ', version, [
|
|
129
|
-
internationalAccount(account),
|
|
163
|
+
internationalAccount(account, nationalAllowed),
|
|
130
164
|
formats,
|
|
131
165
|
false,
|
|
132
166
|
dateOrEmpty(period.from),
|
|
133
167
|
dateOrEmpty(period.to),
|
|
134
168
|
null,
|
|
135
|
-
period.continuation,
|
|
169
|
+
Verbatim.of(period.continuation),
|
|
136
170
|
]);
|
|
137
171
|
},
|
|
138
172
|
|
|
139
173
|
/** HKSPA: IBAN and BIC of all accounts. */
|
|
140
174
|
sepaAccounts(version: number, continuation?: string): SegmentSpec {
|
|
141
|
-
return new SegmentSpec('HKSPA', version, [null, ...(version >= 2 ? [null, continuation] : [])]);
|
|
175
|
+
return new SegmentSpec('HKSPA', version, [null, ...(version >= 2 ? [null, Verbatim.of(continuation)] : [])]);
|
|
142
176
|
},
|
|
143
177
|
|
|
144
178
|
/** HKTAB: all TAN media of all classes. */
|
|
@@ -155,6 +189,7 @@ export class TanResponse {
|
|
|
155
189
|
constructor(
|
|
156
190
|
/** The TAN process the segment answers: `4` asks for an approval, `S` answers a status request. */
|
|
157
191
|
readonly process: string | undefined,
|
|
192
|
+
/** The order reference, as `Segment.verbatim` reads it, to send back byte for byte. */
|
|
158
193
|
readonly reference: string | undefined,
|
|
159
194
|
readonly challenge: string | undefined,
|
|
160
195
|
readonly hhdUc: Uint8Array | undefined,
|
|
@@ -165,7 +200,7 @@ export class TanResponse {
|
|
|
165
200
|
static parse(s: Segment): TanResponse {
|
|
166
201
|
return new TanResponse(
|
|
167
202
|
s.text(Hitan.process),
|
|
168
|
-
s.
|
|
203
|
+
s.verbatim(Hitan.reference),
|
|
169
204
|
s.text(Hitan.challenge),
|
|
170
205
|
s.binary(Hitan.hhdUc),
|
|
171
206
|
parseFintsDateTime(s.text(Hitan.validUntil, 0), s.text(Hitan.validUntil, 1)),
|
|
@@ -242,8 +277,17 @@ export function parseBalance(s: Segment, account: Account): Balance {
|
|
|
242
277
|
* The account a balance (HISAL) belongs to: international (IBAN, BIC, then national) from version 7, national before.
|
|
243
278
|
*/
|
|
244
279
|
export function balanceAccount(s: Segment): Account {
|
|
245
|
-
|
|
246
|
-
|
|
280
|
+
return accountAt(s, Hisal.account, s.version >= 7);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/** The account camt transactions (HICAZ) belong to, which the first element names (international). */
|
|
284
|
+
export function camtAccount(s: Segment): Account {
|
|
285
|
+
return accountAt(s, 1, true);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** The account at `index` of `s`: an `international` one (KTI), else a national one (KTV). */
|
|
289
|
+
function accountAt(s: Segment, index: number, international: boolean): Account {
|
|
290
|
+
const fields = s.texts(index);
|
|
247
291
|
const national = international ? 2 : 0;
|
|
248
292
|
return new Account({
|
|
249
293
|
iban: international ? fields[0] : undefined,
|
|
@@ -295,8 +339,14 @@ export function parseSepaAccounts(s: Segment, defaultCountry: string, defaultBan
|
|
|
295
339
|
*/
|
|
296
340
|
const accountWidth = 4;
|
|
297
341
|
|
|
298
|
-
/**
|
|
299
|
-
|
|
342
|
+
/**
|
|
343
|
+
* Parses the TAN media list (HITAB versions 4 and 5, PIN/TAN "TAN-Medium-Liste").
|
|
344
|
+
*
|
|
345
|
+
* Each name whose text would not go back as the bank sent it goes into `namesAsSent` as `Segment.verbatim` reads it,
|
|
346
|
+
* keyed by its text: a name in UTF-8, which its text does not tell where it fits ISO 8859-1, and one with blanks at
|
|
347
|
+
* either end, which text goes out without (Formals B.4.1).
|
|
348
|
+
*/
|
|
349
|
+
export function parseTanMedia(s: Segment, namesAsSent?: Map<string, string>): TanMedium[] {
|
|
300
350
|
// Version 5 adds the security function in front of the card number.
|
|
301
351
|
const version5 = s.version >= 5;
|
|
302
352
|
const cardNumber = version5 ? 3 : 2;
|
|
@@ -310,6 +360,11 @@ export function parseTanMedia(s: Segment): TanMedium[] {
|
|
|
310
360
|
const fields = s.texts(i);
|
|
311
361
|
const mediumClass = fields[0];
|
|
312
362
|
if (mediumClass === undefined) continue;
|
|
363
|
+
const name = fields[after + 3];
|
|
364
|
+
const nameAsSent = s.verbatim(i, after + 3);
|
|
365
|
+
if (name !== undefined && nameAsSent !== undefined && nameAsSent !== trimBlanks(name)) {
|
|
366
|
+
namesAsSent?.set(name, nameAsSent);
|
|
367
|
+
}
|
|
313
368
|
media.push(
|
|
314
369
|
new TanMedium({
|
|
315
370
|
mediumClass,
|
|
@@ -319,7 +374,7 @@ export function parseTanMedia(s: Segment): TanMedium[] {
|
|
|
319
374
|
cardSequenceNumber: fields[cardNumber + 1],
|
|
320
375
|
validFrom: parseFintsDate(fields[after]),
|
|
321
376
|
validUntil: parseFintsDate(fields[after + 1]),
|
|
322
|
-
name
|
|
377
|
+
name,
|
|
323
378
|
mobileNumberMasked: fields[after + 4],
|
|
324
379
|
mobileNumber: fields[after + 5],
|
|
325
380
|
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, trimBlanks, 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,10 +59,19 @@ 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
|
-
|
|
62
|
+
private typeValue: string | undefined;
|
|
63
|
+
|
|
64
|
+
constructor(
|
|
65
|
+
readonly elements: readonly (readonly DataElement[])[],
|
|
66
|
+
/** The bytes the segment was read from: `start` to `end` in `source`, terminator included. */
|
|
67
|
+
private readonly source: Uint8Array,
|
|
68
|
+
private readonly start: number,
|
|
69
|
+
private readonly end: number,
|
|
70
|
+
) {}
|
|
50
71
|
|
|
51
72
|
get type(): string {
|
|
52
|
-
|
|
73
|
+
this.typeValue ??= trimBlanks(this.text(0) ?? '');
|
|
74
|
+
return this.typeValue;
|
|
53
75
|
}
|
|
54
76
|
|
|
55
77
|
get version(): number {
|
|
@@ -74,14 +96,31 @@ export class Segment {
|
|
|
74
96
|
return e.text;
|
|
75
97
|
}
|
|
76
98
|
|
|
99
|
+
/** The binary data at `index`; `undefined` for an element that holds text. */
|
|
77
100
|
binary(index: number, sub = 0): Uint8Array | undefined {
|
|
101
|
+
const e = this.element(index, sub);
|
|
102
|
+
return e?.isBinary === true ? e.bytes : undefined;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The binary data or the text without escape characters at `index`; `undefined` for an empty element. */
|
|
106
|
+
bytes(index: number, sub = 0): Uint8Array | undefined {
|
|
107
|
+
const e = this.element(index, sub);
|
|
108
|
+
return e === undefined || e.isEmpty ? undefined : e.bytes;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* The element at `index` as the bank sent it, for a value the client sends back, such as the dialog ID: untrimmed,
|
|
113
|
+
* and one character per byte (ISO 8859-1, the FinTS 3.0 character set), so that `Verbatim` gives the bank back the
|
|
114
|
+
* very bytes it sent, in whatever encoding. `undefined` for an empty element.
|
|
115
|
+
*/
|
|
116
|
+
verbatim(index: number, sub = 0): string | undefined {
|
|
78
117
|
const e = this.element(index, sub);
|
|
79
118
|
if (e === undefined || e.isEmpty) return undefined;
|
|
80
|
-
return e.bytes;
|
|
119
|
+
return latin1Decode(e.bytes);
|
|
81
120
|
}
|
|
82
121
|
|
|
83
122
|
integer(index: number, sub = 0): number | undefined {
|
|
84
|
-
return tryParseInt(this.text(index, sub)
|
|
123
|
+
return tryParseInt(this.text(index, sub));
|
|
85
124
|
}
|
|
86
125
|
|
|
87
126
|
/** The texts of the group at `index`; `undefined` for empty elements. */
|
|
@@ -89,22 +128,9 @@ export class Segment {
|
|
|
89
128
|
return this.group(index).map((e) => (e.isEmpty ? undefined : e.text));
|
|
90
129
|
}
|
|
91
130
|
|
|
131
|
+
/** The bytes the segment was read from, as the bank sent them, terminator included. */
|
|
92
132
|
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());
|
|
133
|
+
return this.source.subarray(this.start, this.end);
|
|
108
134
|
}
|
|
109
135
|
}
|
|
110
136
|
|
|
@@ -124,111 +150,162 @@ function writeBinary(out: BytesBuilder, bytes: Uint8Array): void {
|
|
|
124
150
|
|
|
125
151
|
/**
|
|
126
152
|
* 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.
|
|
153
|
+
* come from a bank that sends UTF-8, such as a TAN medium named `Max’s iPhone`. `FinTsConfig` checks the login data for
|
|
154
|
+
* ISO 8859-1 up front, and the client the PIN and the TAN.
|
|
155
|
+
*
|
|
156
|
+
* @internal Exported for the tests.
|
|
129
157
|
*/
|
|
130
158
|
export function encodeText(value: string): Uint8Array {
|
|
131
159
|
return isLatin1(value) ? latin1Encode(value) : utf8Encode(value);
|
|
132
160
|
}
|
|
133
161
|
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
162
|
+
/**
|
|
163
|
+
* The data elements and segments one parse may produce together, segment headers and group elements included: what
|
|
164
|
+
* the user parameter data of 6,000 accounts take, at about 155 data elements each, which is about what 9,999 KB, the
|
|
165
|
+
* largest message size HIBPA can state, hold of real data. Without a limit, a message of nothing but separators would
|
|
166
|
+
* take a hundred times its size in memory.
|
|
167
|
+
*
|
|
168
|
+
* @internal Exported for the tests.
|
|
169
|
+
*/
|
|
170
|
+
export const maxParts = 1_000_000;
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* What `IncomingMessage3.size` counts for each data element it keeps: more than any of them takes once parsed, so that
|
|
174
|
+
* a message of many short elements counts as the memory it holds rather than its bytes.
|
|
175
|
+
*/
|
|
176
|
+
export const parsedElementBytes = 512;
|
|
177
|
+
|
|
178
|
+
/** Every empty data element of a received segment; it has no bytes to change. */
|
|
179
|
+
const emptyElement = new DataElement(new Uint8Array(0), 0, 0, false);
|
|
180
|
+
|
|
181
|
+
function isSeparator(byte: number | undefined): boolean {
|
|
182
|
+
return byte === dataElementSeparator || byte === groupElementSeparator || byte === segmentTerminator;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/** Line breaks and spaces between segments are padding, not content. */
|
|
186
|
+
function isPadding(byte: number): boolean {
|
|
187
|
+
return byte === 0x0a || byte === 0x0d || byte === 0x20;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function isDigit(byte: number | undefined): boolean {
|
|
191
|
+
return byte !== undefined && byte >= 0x30 && byte <= 0x39;
|
|
138
192
|
}
|
|
139
193
|
|
|
140
194
|
/**
|
|
141
195
|
* Splits a FinTS 3.0 message into segments, resolving escapes and binary data (`@length@bytes`).
|
|
142
196
|
*
|
|
143
|
-
* Throws `FormatError` for a
|
|
197
|
+
* The elements share the memory of `data`. Throws `FormatError` for anything a complete message cannot hold: a
|
|
198
|
+
* malformed binary length, binary data beyond the end of `data` or not followed by a separator, a last segment without
|
|
199
|
+
* terminator, and more than `maxParts` data elements and segments, an empty one included.
|
|
144
200
|
*/
|
|
145
201
|
export function parseSegments(data: Uint8Array): Segment[] {
|
|
146
202
|
const segments: Segment[] = [];
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
let binary: Uint8Array | undefined;
|
|
151
|
-
let segmentStarted = false;
|
|
152
|
-
|
|
153
|
-
const endElement = (): void => {
|
|
154
|
-
group.push(binary !== undefined ? new DataElement(binary, true) : new DataElement(Uint8Array.from(current), false));
|
|
155
|
-
current = [];
|
|
156
|
-
binary = undefined;
|
|
203
|
+
let parts = 0;
|
|
204
|
+
const count = (): void => {
|
|
205
|
+
if (++parts > maxParts) throw new FormatError(`More than ${maxParts} data elements and segments`);
|
|
157
206
|
};
|
|
207
|
+
// Created with their first element: an array that starts empty reserves room for more than most of them hold.
|
|
208
|
+
let elements: DataElement[][] | undefined;
|
|
209
|
+
let group: DataElement[] | undefined;
|
|
210
|
+
let segmentStart = 0;
|
|
211
|
+
let i = 0;
|
|
158
212
|
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
213
|
+
const readBinary = (): DataElement => {
|
|
214
|
+
let end = i + 1;
|
|
215
|
+
let length = 0;
|
|
216
|
+
while (isDigit(data[end])) {
|
|
217
|
+
length = length * 10 + data[end++]! - 0x30;
|
|
218
|
+
if (length > data.length) throw new FormatError('Binary data beyond the end of the message');
|
|
219
|
+
}
|
|
220
|
+
if (end === i + 1 || data[end] !== binaryMarker) throw new FormatError('Malformed binary length');
|
|
221
|
+
const start = end + 1;
|
|
222
|
+
if (length > data.length - start) throw new FormatError('Binary data beyond the end of the message');
|
|
223
|
+
i = start + length;
|
|
224
|
+
if (i < data.length && !isSeparator(data[i])) throw new FormatError('Binary data not followed by a separator');
|
|
225
|
+
return new DataElement(data, start, i, true);
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
const readText = (): DataElement => {
|
|
229
|
+
const start = i;
|
|
230
|
+
let escaped = false;
|
|
231
|
+
while (i < data.length && !isSeparator(data[i])) {
|
|
232
|
+
if (data[i] === escapeCharacter) {
|
|
233
|
+
escaped = true;
|
|
234
|
+
i++;
|
|
235
|
+
}
|
|
236
|
+
i++;
|
|
237
|
+
}
|
|
238
|
+
if (i > data.length) throw new FormatError('Escape character at the end of the message');
|
|
239
|
+
return i === start ? emptyElement : new DataElement(data, start, i, false, escaped);
|
|
168
240
|
};
|
|
169
241
|
|
|
170
|
-
let i = 0;
|
|
171
242
|
while (i < data.length) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (i + 1 < data.length) current.push(data[i + 1]!);
|
|
175
|
-
segmentStarted = true;
|
|
176
|
-
i += 2;
|
|
243
|
+
if (elements === undefined && group === undefined && isPadding(data[i]!)) {
|
|
244
|
+
i++;
|
|
177
245
|
continue;
|
|
178
246
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
}
|
|
247
|
+
count();
|
|
248
|
+
if (elements === undefined && group === undefined) segmentStart = i;
|
|
249
|
+
const element = data[i] === binaryMarker ? readBinary() : readText();
|
|
250
|
+
if (group === undefined) group = [element];
|
|
251
|
+
else group.push(element);
|
|
252
|
+
const separator = data[i++];
|
|
253
|
+
if (separator === groupElementSeparator) continue;
|
|
254
|
+
if (elements === undefined) elements = [group];
|
|
255
|
+
else elements.push(group);
|
|
256
|
+
group = undefined;
|
|
257
|
+
if (separator === segmentTerminator) {
|
|
258
|
+
count();
|
|
259
|
+
const segment = new Segment(elements, data, segmentStart, i);
|
|
260
|
+
// Skip empty segments, such as a stray double terminator.
|
|
261
|
+
if (segment.type !== '') segments.push(segment);
|
|
262
|
+
elements = undefined;
|
|
211
263
|
}
|
|
212
|
-
i++;
|
|
213
264
|
}
|
|
214
|
-
if (
|
|
265
|
+
if (elements !== undefined || group !== undefined) throw new FormatError('Segment without terminator');
|
|
215
266
|
return segments;
|
|
216
267
|
}
|
|
217
268
|
|
|
269
|
+
/** `text` without the escape characters in front of syntax characters. */
|
|
270
|
+
function unescape(text: Uint8Array): Uint8Array {
|
|
271
|
+
const out = new Uint8Array(text.length);
|
|
272
|
+
let o = 0;
|
|
273
|
+
for (let j = 0; j < text.length; j++) {
|
|
274
|
+
if (text[j] === escapeCharacter) j++;
|
|
275
|
+
out[o++] = text[j]!;
|
|
276
|
+
}
|
|
277
|
+
return out.subarray(0, o);
|
|
278
|
+
}
|
|
279
|
+
|
|
218
280
|
/** Binary data in a segment to send, written as `@length@bytes`. */
|
|
219
281
|
export class Binary {
|
|
220
282
|
constructor(readonly bytes: Uint8Array) {}
|
|
221
283
|
}
|
|
222
284
|
|
|
285
|
+
/**
|
|
286
|
+
* A value the bank issued, such as the dialog ID, to send back as `Segment.verbatim` reads it: escaped, but not
|
|
287
|
+
* trimmed, so that the bank gets back the very bytes it sent. Text from elsewhere, such as a TAN medium name the app
|
|
288
|
+
* passes, goes out as `encodeText` encodes it.
|
|
289
|
+
*/
|
|
290
|
+
export class Verbatim {
|
|
291
|
+
constructor(readonly text: string) {}
|
|
292
|
+
|
|
293
|
+
/** `text` as a `Verbatim`; `undefined` stays empty. */
|
|
294
|
+
static of(text: string | undefined): Verbatim | undefined {
|
|
295
|
+
return text === undefined ? undefined : new Verbatim(text);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
223
299
|
/** A value of a segment to send; see `SegmentSpec`. */
|
|
224
|
-
export type SegmentValue = string | number | boolean | Binary | null | undefined | readonly SegmentValue[];
|
|
300
|
+
export type SegmentValue = string | number | boolean | Binary | Verbatim | null | undefined | readonly SegmentValue[];
|
|
225
301
|
|
|
226
302
|
/**
|
|
227
303
|
* A segment to send.
|
|
228
304
|
*
|
|
229
305
|
* `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
|
|
306
|
+
* element group, a boolean for `J`/`N`, `Binary` for binary data, `Verbatim` for a value the bank issued, and anything
|
|
307
|
+
* else as its text, without leading and trailing blanks. Trailing empty elements are left out, as the specification
|
|
308
|
+
* asks.
|
|
232
309
|
*/
|
|
233
310
|
export class SegmentSpec {
|
|
234
311
|
constructor(
|
|
@@ -296,12 +373,14 @@ function encodeValue(value: SingleValue): Uint8Array {
|
|
|
296
373
|
// Empty.
|
|
297
374
|
} else if (value instanceof Binary) {
|
|
298
375
|
writeBinary(out, value.bytes);
|
|
376
|
+
} else if (value instanceof Verbatim) {
|
|
377
|
+
writeEscaped(out, encodeText(value.text));
|
|
299
378
|
} else if (typeof value === 'boolean') {
|
|
300
379
|
out.addByte(value ? 0x4a : 0x4e); // 'J' or 'N'
|
|
301
380
|
} else {
|
|
302
381
|
// All but binary data go without leading and trailing blanks (Formals B.4.1). Encode first, then escape the bytes,
|
|
303
382
|
// so that nothing unescaped slips into the syntax.
|
|
304
|
-
writeEscaped(out, encodeText(String(value)
|
|
383
|
+
writeEscaped(out, encodeText(trimBlanks(String(value))));
|
|
305
384
|
}
|
|
306
385
|
return out.takeBytes();
|
|
307
386
|
}
|
|
@@ -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"}
|