@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.
- package/CHANGELOG.md +11 -0
- package/LICENSE +28 -0
- package/README.md +156 -2
- package/dist/core/account.d.ts +140 -0
- package/dist/core/account.d.ts.map +1 -0
- package/dist/core/account.js +174 -0
- package/dist/core/account.js.map +1 -0
- package/dist/core/amount.d.ts +62 -0
- package/dist/core/amount.d.ts.map +1 -0
- package/dist/core/amount.js +189 -0
- package/dist/core/amount.js.map +1 -0
- package/dist/core/balance.d.ts +54 -0
- package/dist/core/balance.d.ts.map +1 -0
- package/dist/core/balance.js +53 -0
- package/dist/core/balance.js.map +1 -0
- package/dist/core/bank-info.d.ts +49 -0
- package/dist/core/bank-info.d.ts.map +1 -0
- package/dist/core/bank-info.js +52 -0
- package/dist/core/bank-info.js.map +1 -0
- package/dist/core/client.d.ts +126 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +2 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/config.d.ts +68 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +113 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/errors.d.ts +91 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +182 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/feedback.d.ts +69 -0
- package/dist/core/feedback.d.ts.map +1 -0
- package/dist/core/feedback.js +124 -0
- package/dist/core/feedback.js.map +1 -0
- package/dist/core/transaction.d.ts +98 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/core/transaction.js +91 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/core/transport.d.ts +37 -0
- package/dist/core/transport.d.ts.map +1 -0
- package/dist/core/transport.js +92 -0
- package/dist/core/transport.js.map +1 -0
- package/dist/formats/camt.d.ts +12 -0
- package/dist/formats/camt.d.ts.map +1 -0
- package/dist/formats/camt.js +159 -0
- package/dist/formats/camt.js.map +1 -0
- package/dist/formats/mt940.d.ts +42 -0
- package/dist/formats/mt940.d.ts.map +1 -0
- package/dist/formats/mt940.js +297 -0
- package/dist/formats/mt940.js.map +1 -0
- package/dist/formats/readers.d.ts +11 -0
- package/dist/formats/readers.d.ts.map +1 -0
- package/dist/formats/readers.js +47 -0
- package/dist/formats/readers.js.map +1 -0
- package/dist/formats/sepa-purpose.d.ts +22 -0
- package/dist/formats/sepa-purpose.d.ts.map +1 -0
- package/dist/formats/sepa-purpose.js +68 -0
- package/dist/formats/sepa-purpose.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/session/app-approval.d.ts +60 -0
- package/dist/session/app-approval.d.ts.map +1 -0
- package/dist/session/app-approval.js +215 -0
- package/dist/session/app-approval.js.map +1 -0
- package/dist/session/bank-connection.d.ts +28 -0
- package/dist/session/bank-connection.d.ts.map +1 -0
- package/dist/session/bank-connection.js +48 -0
- package/dist/session/bank-connection.js.map +1 -0
- package/dist/session/dialog-runner.d.ts +34 -0
- package/dist/session/dialog-runner.d.ts.map +1 -0
- package/dist/session/dialog-runner.js +112 -0
- package/dist/session/dialog-runner.js.map +1 -0
- package/dist/session/login-guard.d.ts +73 -0
- package/dist/session/login-guard.d.ts.map +1 -0
- package/dist/session/login-guard.js +109 -0
- package/dist/session/login-guard.js.map +1 -0
- package/dist/session/parameter-data.d.ts +83 -0
- package/dist/session/parameter-data.d.ts.map +1 -0
- package/dist/session/parameter-data.js +68 -0
- package/dist/session/parameter-data.js.map +1 -0
- package/dist/session/pin-tan-client.d.ts +54 -0
- package/dist/session/pin-tan-client.d.ts.map +1 -0
- package/dist/session/pin-tan-client.js +153 -0
- package/dist/session/pin-tan-client.js.map +1 -0
- package/dist/session/pin-tan-dialog.d.ts +71 -0
- package/dist/session/pin-tan-dialog.d.ts.map +1 -0
- package/dist/session/pin-tan-dialog.js +124 -0
- package/dist/session/pin-tan-dialog.js.map +1 -0
- package/dist/session/protocol.d.ts +209 -0
- package/dist/session/protocol.d.ts.map +1 -0
- package/dist/session/protocol.js +75 -0
- package/dist/session/protocol.js.map +1 -0
- package/dist/session/response-handler.d.ts +48 -0
- package/dist/session/response-handler.d.ts.map +1 -0
- package/dist/session/response-handler.js +121 -0
- package/dist/session/response-handler.js.map +1 -0
- package/dist/session/sepa-accounts.d.ts +34 -0
- package/dist/session/sepa-accounts.d.ts.map +1 -0
- package/dist/session/sepa-accounts.js +96 -0
- package/dist/session/sepa-accounts.js.map +1 -0
- package/dist/session/session-setup.d.ts +72 -0
- package/dist/session/session-setup.d.ts.map +1 -0
- package/dist/session/session-setup.js +160 -0
- package/dist/session/session-setup.js.map +1 -0
- package/dist/session/session-state.d.ts +47 -0
- package/dist/session/session-state.d.ts.map +1 -0
- package/dist/session/session-state.js +147 -0
- package/dist/session/session-state.js.map +1 -0
- package/dist/session/tan-selection.d.ts +88 -0
- package/dist/session/tan-selection.d.ts.map +1 -0
- package/dist/session/tan-selection.js +178 -0
- package/dist/session/tan-selection.js.map +1 -0
- package/dist/session/user-prompts.d.ts +61 -0
- package/dist/session/user-prompts.d.ts.map +1 -0
- package/dist/session/user-prompts.js +164 -0
- package/dist/session/user-prompts.js.map +1 -0
- package/dist/tan/challenge.d.ts +114 -0
- package/dist/tan/challenge.d.ts.map +1 -0
- package/dist/tan/challenge.js +185 -0
- package/dist/tan/challenge.js.map +1 -0
- package/dist/tan/flicker.d.ts +36 -0
- package/dist/tan/flicker.d.ts.map +1 -0
- package/dist/tan/flicker.js +262 -0
- package/dist/tan/flicker.js.map +1 -0
- package/dist/tan/tan-image.d.ts +21 -0
- package/dist/tan/tan-image.d.ts.map +1 -0
- package/dist/tan/tan-image.js +41 -0
- package/dist/tan/tan-image.js.map +1 -0
- package/dist/tan/tan-models.d.ts +145 -0
- package/dist/tan/tan-models.d.ts.map +1 -0
- package/dist/tan/tan-models.js +148 -0
- package/dist/tan/tan-models.js.map +1 -0
- package/dist/util/async.d.ts +24 -0
- package/dist/util/async.d.ts.map +1 -0
- package/dist/util/async.js +49 -0
- package/dist/util/async.js.map +1 -0
- package/dist/util/bytes.d.ts +23 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +136 -0
- package/dist/util/bytes.js.map +1 -0
- package/dist/util/call-context.d.ts +12 -0
- package/dist/util/call-context.d.ts.map +1 -0
- package/dist/util/call-context.js +29 -0
- package/dist/util/call-context.js.map +1 -0
- package/dist/util/dates.d.ts +62 -0
- package/dist/util/dates.d.ts.map +1 -0
- package/dist/util/dates.js +169 -0
- package/dist/util/dates.js.map +1 -0
- package/dist/util/errors.d.ts +9 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +15 -0
- package/dist/util/errors.js.map +1 -0
- package/dist/util/immutable-map.d.ts +16 -0
- package/dist/util/immutable-map.d.ts.map +1 -0
- package/dist/util/immutable-map.js +38 -0
- package/dist/util/immutable-map.js.map +1 -0
- package/dist/util/json.d.ts +13 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +44 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/text.d.ts +20 -0
- package/dist/util/text.d.ts.map +1 -0
- package/dist/util/text.js +78 -0
- package/dist/util/text.js.map +1 -0
- package/dist/util/xml.d.ts +35 -0
- package/dist/util/xml.d.ts.map +1 -0
- package/dist/util/xml.js +183 -0
- package/dist/util/xml.js.map +1 -0
- package/dist/v3/account-extension.d.ts +25 -0
- package/dist/v3/account-extension.d.ts.map +1 -0
- package/dist/v3/account-extension.js +176 -0
- package/dist/v3/account-extension.js.map +1 -0
- package/dist/v3/client.d.ts +24 -0
- package/dist/v3/client.d.ts.map +1 -0
- package/dist/v3/client.js +16 -0
- package/dist/v3/client.js.map +1 -0
- package/dist/v3/dialog.d.ts +78 -0
- package/dist/v3/dialog.d.ts.map +1 -0
- package/dist/v3/dialog.js +138 -0
- package/dist/v3/dialog.js.map +1 -0
- package/dist/v3/message.d.ts +72 -0
- package/dist/v3/message.d.ts.map +1 -0
- package/dist/v3/message.js +259 -0
- package/dist/v3/message.js.map +1 -0
- package/dist/v3/parameters.d.ts +79 -0
- package/dist/v3/parameters.d.ts.map +1 -0
- package/dist/v3/parameters.js +396 -0
- package/dist/v3/parameters.js.map +1 -0
- package/dist/v3/protocol.d.ts +68 -0
- package/dist/v3/protocol.d.ts.map +1 -0
- package/dist/v3/protocol.js +263 -0
- package/dist/v3/protocol.js.map +1 -0
- package/dist/v3/segments.d.ts +89 -0
- package/dist/v3/segments.d.ts.map +1 -0
- package/dist/v3/segments.js +283 -0
- package/dist/v3/segments.js.map +1 -0
- package/dist/v3/syntax.d.ts +76 -0
- package/dist/v3/syntax.d.ts.map +1 -0
- package/dist/v3/syntax.js +301 -0
- package/dist/v3/syntax.js.map +1 -0
- package/package.json +63 -4
- package/src/core/account.ts +250 -0
- package/src/core/amount.ts +209 -0
- package/src/core/balance.ts +84 -0
- package/src/core/bank-info.ts +66 -0
- package/src/core/client.ts +149 -0
- package/src/core/config.ts +141 -0
- package/src/core/errors.ts +203 -0
- package/src/core/feedback.ts +137 -0
- package/src/core/transaction.ts +156 -0
- package/src/core/transport.ts +114 -0
- package/src/formats/camt.ts +169 -0
- package/src/formats/mt940.ts +333 -0
- package/src/formats/readers.ts +56 -0
- package/src/formats/sepa-purpose.ts +75 -0
- package/src/index.ts +68 -0
- package/src/session/app-approval.ts +265 -0
- package/src/session/bank-connection.ts +51 -0
- package/src/session/dialog-runner.ts +119 -0
- package/src/session/login-guard.ts +135 -0
- package/src/session/parameter-data.ts +137 -0
- package/src/session/pin-tan-client.ts +186 -0
- package/src/session/pin-tan-dialog.ts +160 -0
- package/src/session/protocol.ts +311 -0
- package/src/session/response-handler.ts +130 -0
- package/src/session/sepa-accounts.ts +97 -0
- package/src/session/session-setup.ts +165 -0
- package/src/session/session-state.ts +170 -0
- package/src/session/tan-selection.ts +192 -0
- package/src/session/user-prompts.ts +174 -0
- package/src/tan/challenge.ts +252 -0
- package/src/tan/flicker.ts +270 -0
- package/src/tan/tan-image.ts +36 -0
- package/src/tan/tan-models.ts +247 -0
- package/src/util/async.ts +53 -0
- package/src/util/bytes.ts +135 -0
- package/src/util/call-context.ts +40 -0
- package/src/util/dates.ts +193 -0
- package/src/util/errors.ts +16 -0
- package/src/util/immutable-map.ts +47 -0
- package/src/util/json.ts +51 -0
- package/src/util/text.ts +82 -0
- package/src/util/xml.ts +172 -0
- package/src/v3/account-extension.ts +190 -0
- package/src/v3/client.ts +29 -0
- package/src/v3/dialog.ts +199 -0
- package/src/v3/message.ts +321 -0
- package/src/v3/parameters.ts +451 -0
- package/src/v3/protocol.ts +331 -0
- package/src/v3/segments.ts +331 -0
- package/src/v3/syntax.ts +307 -0
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { latin1Decode } from '../util/bytes.js';
|
|
2
|
+
import { challengeInstruction, FlickerCode, hasEmbeddedFlickerCode } from './flicker.js';
|
|
3
|
+
import { TanImage } from './tan-image.js';
|
|
4
|
+
/** Challenge text banks send when there is nothing to show. */
|
|
5
|
+
const noChallenge = 'nochallenge';
|
|
6
|
+
/** `message`, or `undefined` where it is empty or the filler banks send when there is nothing to show. */
|
|
7
|
+
export function challengeText(message) {
|
|
8
|
+
return message === undefined || message === '' || message === noChallenge ? undefined : message;
|
|
9
|
+
}
|
|
10
|
+
const printableAscii = /^[\x20-\x7E]+$/;
|
|
11
|
+
const lineBreak = /<br\b[^<>]*>/gi;
|
|
12
|
+
const blockTag = /<\/?(?:p|li)\b[^<>]*>/gi;
|
|
13
|
+
/**
|
|
14
|
+
* The formatting tags a structured challenge may carry besides line breaks, paragraphs and list items (PIN/TAN, Data
|
|
15
|
+
* Dictionary "Challenge"). Angle brackets around anything else, such as `<50 EUR>`, are part of the text the user
|
|
16
|
+
* approves, so they stay. Excluding `<` and `>` from the tag keeps the search linear.
|
|
17
|
+
*/
|
|
18
|
+
const formattingTag = /<\/?(?:b|i|u|ul|ol)\b[^<>]*>/gi;
|
|
19
|
+
const entityReference = /&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g;
|
|
20
|
+
const blankLines = /\n{3,}/g;
|
|
21
|
+
const namedEntities = {
|
|
22
|
+
amp: '&',
|
|
23
|
+
lt: '<',
|
|
24
|
+
gt: '>',
|
|
25
|
+
quot: '"',
|
|
26
|
+
apos: "'",
|
|
27
|
+
nbsp: ' ',
|
|
28
|
+
auml: 'ä',
|
|
29
|
+
ouml: 'ö',
|
|
30
|
+
uuml: 'ü',
|
|
31
|
+
Auml: 'Ä',
|
|
32
|
+
Ouml: 'Ö',
|
|
33
|
+
Uuml: 'Ü',
|
|
34
|
+
szlig: 'ß',
|
|
35
|
+
euro: '€',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The character `name` refers to; `undefined` for an unknown name and for a number that is not a character, as XML
|
|
39
|
+
* defines them: `NUL`, half a surrogate pair, or beyond Unicode.
|
|
40
|
+
*/
|
|
41
|
+
function decodeEntity(name) {
|
|
42
|
+
if (name.startsWith('#')) {
|
|
43
|
+
const isHex = name.startsWith('#x') || name.startsWith('#X');
|
|
44
|
+
const digits = name.substring(isHex ? 2 : 1);
|
|
45
|
+
const code = (isHex ? /^[0-9a-fA-F]+$/ : /^[0-9]+$/).test(digits) ? parseInt(digits, isHex ? 16 : 10) : NaN;
|
|
46
|
+
const isCharacter = code > 0 && (code < 0xd800 || code > 0xdfff) && code <= 0x10ffff;
|
|
47
|
+
return isCharacter ? String.fromCodePoint(code) : undefined;
|
|
48
|
+
}
|
|
49
|
+
return Object.hasOwn(namedEntities, name) ? namedEntities[name] : undefined;
|
|
50
|
+
}
|
|
51
|
+
/** A request from the bank to authorize an action with a TAN or an app approval. */
|
|
52
|
+
export class TanChallenge {
|
|
53
|
+
/** The TAN method the challenge is for. */
|
|
54
|
+
method;
|
|
55
|
+
/** Bank reference of the pending action. */
|
|
56
|
+
reference;
|
|
57
|
+
/** Challenge text as sent by the bank; may contain simple HTML. See `plainMessage`. */
|
|
58
|
+
message;
|
|
59
|
+
/** Raw challenge data for TAN generators (HHD_UC). */
|
|
60
|
+
hhdUc;
|
|
61
|
+
/** chipTAN optical flicker code, if the challenge contains one. */
|
|
62
|
+
flickerCode;
|
|
63
|
+
/** photoTAN / QR-TAN image, if the challenge contains one. */
|
|
64
|
+
image;
|
|
65
|
+
/** Approval happens in a separate app; see `TanCallback` for what to answer. */
|
|
66
|
+
isDecoupled;
|
|
67
|
+
/** The exact moment until which the bank accepts the TAN or the approval, where it states date and time of day. */
|
|
68
|
+
validUntil;
|
|
69
|
+
/** Name of the TAN medium the bank sent the challenge to. */
|
|
70
|
+
tanMediumName;
|
|
71
|
+
/** Counts repeated prompts for the same decoupled approval. */
|
|
72
|
+
attempt;
|
|
73
|
+
/**
|
|
74
|
+
* Settles when the client needs no more answer to this challenge: the bank confirmed the approval, or the approval
|
|
75
|
+
* failed, timed out or was cancelled. Set where the client asks the bank by itself while the callback shows the
|
|
76
|
+
* challenge (`TanCallback`), `undefined` otherwise. It never rejects.
|
|
77
|
+
*/
|
|
78
|
+
resolved;
|
|
79
|
+
/** A challenge as the bank sent it. Use `TanChallenge.create` to have a flicker code or image detected in the data. */
|
|
80
|
+
constructor(init) {
|
|
81
|
+
this.method = init.method;
|
|
82
|
+
this.reference = init.reference;
|
|
83
|
+
this.message = init.message;
|
|
84
|
+
this.hhdUc = init.hhdUc;
|
|
85
|
+
this.flickerCode = init.flickerCode;
|
|
86
|
+
this.image = init.image;
|
|
87
|
+
this.isDecoupled = init.isDecoupled ?? false;
|
|
88
|
+
this.validUntil = init.validUntil;
|
|
89
|
+
this.tanMediumName = init.tanMediumName;
|
|
90
|
+
this.attempt = init.attempt ?? 1;
|
|
91
|
+
this.resolved = init.resolved;
|
|
92
|
+
}
|
|
93
|
+
/** Builds a challenge and detects a photoTAN image or chipTAN flicker code in `hhdUc` or `message`. */
|
|
94
|
+
static create(init) {
|
|
95
|
+
let image;
|
|
96
|
+
let flicker;
|
|
97
|
+
const data = init.hhdUc !== undefined && init.hhdUc.length > 0 ? init.hhdUc : undefined;
|
|
98
|
+
if (data !== undefined) {
|
|
99
|
+
image = TanImage.tryParse(data);
|
|
100
|
+
if (image === undefined) {
|
|
101
|
+
const text = latin1Decode(data);
|
|
102
|
+
if (printableAscii.test(text))
|
|
103
|
+
flicker = FlickerCode.tryParse(text);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
else if (init.message !== undefined && hasEmbeddedFlickerCode(init.message)) {
|
|
107
|
+
flicker = FlickerCode.tryParse(init.message);
|
|
108
|
+
}
|
|
109
|
+
return new TanChallenge({
|
|
110
|
+
...init,
|
|
111
|
+
message: challengeText(init.message),
|
|
112
|
+
hhdUc: data,
|
|
113
|
+
flickerCode: flicker,
|
|
114
|
+
image,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Whether the user can report the approval in the banking app; `false` where only the bank's confirmation completes
|
|
119
|
+
* it, which `TanCallback` tells how to handle.
|
|
120
|
+
*/
|
|
121
|
+
get manualConfirmation() {
|
|
122
|
+
return !this.isDecoupled || this.method.decoupledManualConfirmation;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* `message` as the user reads it.
|
|
126
|
+
*
|
|
127
|
+
* A method with structured challenges (`TanMethod.structuredChallenge`) may use the formatting tags the specification
|
|
128
|
+
* defines. Line breaks, paragraphs and list items become new lines. Bold, italics, underlining and list tags are
|
|
129
|
+
* removed, and the entity references of such markup are resolved. Any other challenge stays as the bank sent it,
|
|
130
|
+
* which is how the specification wants it shown.
|
|
131
|
+
*/
|
|
132
|
+
get plainMessage() {
|
|
133
|
+
const text = this.message;
|
|
134
|
+
if (text === undefined)
|
|
135
|
+
return undefined;
|
|
136
|
+
const instruction = challengeInstruction(text);
|
|
137
|
+
if (!this.method.structuredChallenge)
|
|
138
|
+
return instruction;
|
|
139
|
+
return instruction
|
|
140
|
+
.replace(lineBreak, '\n')
|
|
141
|
+
.replace(blockTag, '\n')
|
|
142
|
+
.replace(formattingTag, '')
|
|
143
|
+
.replace(entityReference, (match, name) => decodeEntity(name) ?? match)
|
|
144
|
+
.replace(blankLines, '\n\n')
|
|
145
|
+
.trim();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Copies of a challenge for the prompts of one approval. Not methods of `TanChallenge`, so that they are not part of its
|
|
149
|
+
// API; they stand beside the class, so that a field added to it is added here as well.
|
|
150
|
+
/** `challenge` again, counted as the next prompt for the same approval, with the bank's new instructions `message`. */
|
|
151
|
+
export function nextAttempt(challenge, message) {
|
|
152
|
+
return copyChallenge(challenge, { attempt: challenge.attempt + 1, message: message ?? challenge.message });
|
|
153
|
+
}
|
|
154
|
+
/** `challenge` with `TanChallenge.resolved` set to `resolved`. */
|
|
155
|
+
export function withResolved(challenge, resolved) {
|
|
156
|
+
return copyChallenge(challenge, { resolved });
|
|
157
|
+
}
|
|
158
|
+
function copyChallenge(challenge, changes) {
|
|
159
|
+
return new TanChallenge({
|
|
160
|
+
method: challenge.method,
|
|
161
|
+
reference: challenge.reference,
|
|
162
|
+
message: challenge.message,
|
|
163
|
+
hhdUc: challenge.hhdUc,
|
|
164
|
+
flickerCode: challenge.flickerCode,
|
|
165
|
+
image: challenge.image,
|
|
166
|
+
isDecoupled: challenge.isDecoupled,
|
|
167
|
+
validUntil: challenge.validUntil,
|
|
168
|
+
tanMediumName: challenge.tanMediumName,
|
|
169
|
+
attempt: challenge.attempt,
|
|
170
|
+
resolved: challenge.resolved,
|
|
171
|
+
...changes,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
const approvedAnswer = Object.freeze({ kind: 'approved' });
|
|
175
|
+
const cancelledAnswer = Object.freeze({ kind: 'cancelled' });
|
|
176
|
+
/** The answers a `TanCallback` returns. */
|
|
177
|
+
export const TanAnswer = Object.freeze({
|
|
178
|
+
/** The TAN the user entered; an empty one cancels. */
|
|
179
|
+
tan: (tan) => Object.freeze({ kind: 'tan', tan }),
|
|
180
|
+
/** The user reports the approval in the banking app (`TanChallenge.isDecoupled`); the client then asks the bank. */
|
|
181
|
+
approved: approvedAnswer,
|
|
182
|
+
/** The user cancels: the client ends the bank dialog, and the call fails with `FinTsTanCancelledError`. */
|
|
183
|
+
cancelled: cancelledAnswer,
|
|
184
|
+
});
|
|
185
|
+
//# sourceMappingURL=challenge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge.js","sourceRoot":"","sources":["../../src/tan/challenge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** Version of the chipTAN HHD specification a flicker code follows: 1.3, or 1.4, which adds control bytes. */
|
|
2
|
+
export type HhdVersion = 'hhd13' | 'hhd14';
|
|
3
|
+
/** Whether `message` is a combined challenge with an embedded flicker code. */
|
|
4
|
+
export declare function hasEmbeddedFlickerCode(message: string): boolean;
|
|
5
|
+
/** The instruction text of a combined challenge; `message` itself if it is none. */
|
|
6
|
+
export declare function challengeInstruction(message: string): string;
|
|
7
|
+
/** A chipTAN optical challenge (flicker code) in HHD 1.3 or 1.4 format. */
|
|
8
|
+
export declare class FlickerCode {
|
|
9
|
+
/** The HHD version the code follows, told by the control bytes of its start code. */
|
|
10
|
+
readonly version: HhdVersion;
|
|
11
|
+
/** Length of the challenge (LC) as stated at the start of the code. */
|
|
12
|
+
readonly lc: number;
|
|
13
|
+
private readonly startCode;
|
|
14
|
+
private readonly dataElements;
|
|
15
|
+
private constructor();
|
|
16
|
+
/** Parses the challenge data sent by the bank; throws `FormatError` if it is not a flicker code. */
|
|
17
|
+
static parse(code: string): FlickerCode;
|
|
18
|
+
/** Parses `code`, or returns `undefined` if it is not a flicker code. */
|
|
19
|
+
static tryParse(code: string): FlickerCode | undefined;
|
|
20
|
+
private static parseLayout;
|
|
21
|
+
/** The data of the start code, which opens the challenge. */
|
|
22
|
+
get startCodeData(): string;
|
|
23
|
+
/** The first data element of the challenge; `undefined` if the code leaves it out. */
|
|
24
|
+
get dataElement1(): string | undefined;
|
|
25
|
+
/** The second data element of the challenge; `undefined` if the code leaves it out. */
|
|
26
|
+
get dataElement2(): string | undefined;
|
|
27
|
+
/** The third data element of the challenge; `undefined` if the code leaves it out. */
|
|
28
|
+
get dataElement3(): string | undefined;
|
|
29
|
+
/** The hex string transmitted to the TAN generator, including checksums. */
|
|
30
|
+
render(): string;
|
|
31
|
+
private payload;
|
|
32
|
+
private luhnChecksum;
|
|
33
|
+
/** Frames to animate: each is `[clock, bit0, bit1, bit2, bit3]` for the five bars. */
|
|
34
|
+
frames(): boolean[][];
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=flicker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flicker.d.ts","sourceRoot":"","sources":["../../src/tan/flicker.ts"],"names":[],"mappings":"AAGA,8GAA8G;AAC9G,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,OAAO,CAAC;AAQ3C,+EAA+E;AAC/E,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,oFAAoF;AACpF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAK5D;AAiBD,2EAA2E;AAC3E,qBAAa,WAAW;IAEpB,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,UAAU;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,EAAE,MAAM;IACnB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAN/B,OAAO;IASP,oGAAoG;IACpG,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAavC,yEAAyE;IACzE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAStD,OAAO,CAAC,MAAM,CAAC,WAAW;IAwB1B,6DAA6D;IAC7D,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED,sFAAsF;IACtF,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,uFAAuF;IACvF,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,sFAAsF;IACtF,IAAI,YAAY,IAAI,MAAM,GAAG,SAAS,CAErC;IAED,4EAA4E;IAC5E,MAAM,IAAI,MAAM;IAKhB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,YAAY;IAapB,sFAAsF;IACtF,MAAM,IAAI,OAAO,EAAE,EAAE;CAatB"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { FormatError } from '../util/errors.js';
|
|
2
|
+
import { withoutWhitespace } from '../util/text.js';
|
|
3
|
+
// A combined challenge embeds the flicker code in the challenge text:
|
|
4
|
+
// `CHLGUC` + four-digit length + code + `CHLGTEXT` + four digits + instruction text.
|
|
5
|
+
const codeMarker = 'CHLGUC';
|
|
6
|
+
const textMarker = 'CHLGTEXT';
|
|
7
|
+
const markerLengthDigits = 4;
|
|
8
|
+
/** Whether `message` is a combined challenge with an embedded flicker code. */
|
|
9
|
+
export function hasEmbeddedFlickerCode(message) {
|
|
10
|
+
return message.includes(codeMarker);
|
|
11
|
+
}
|
|
12
|
+
/** The instruction text of a combined challenge; `message` itself if it is none. */
|
|
13
|
+
export function challengeInstruction(message) {
|
|
14
|
+
const start = message.indexOf(textMarker);
|
|
15
|
+
if (start < 0)
|
|
16
|
+
return message;
|
|
17
|
+
const textStart = start + textMarker.length + markerLengthDigits;
|
|
18
|
+
return textStart < message.length ? message.substring(textStart) : '';
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Digit counts of the challenge length (LC) and of the data element lengths (LDE) to try when parsing.
|
|
22
|
+
*
|
|
23
|
+
* HHD 1.4 states the LC with three digits, HHD 1.3 with two.
|
|
24
|
+
*/
|
|
25
|
+
const layouts = [
|
|
26
|
+
{ lcDigits: 3, lengthDigits: 2 },
|
|
27
|
+
{ lcDigits: 3, lengthDigits: 3 },
|
|
28
|
+
{ lcDigits: 2, lengthDigits: 2 },
|
|
29
|
+
];
|
|
30
|
+
function hex(value, width) {
|
|
31
|
+
return value.toString(16).toUpperCase().padStart(width, '0');
|
|
32
|
+
}
|
|
33
|
+
/** A chipTAN optical challenge (flicker code) in HHD 1.3 or 1.4 format. */
|
|
34
|
+
export class FlickerCode {
|
|
35
|
+
version;
|
|
36
|
+
lc;
|
|
37
|
+
startCode;
|
|
38
|
+
dataElements;
|
|
39
|
+
constructor(
|
|
40
|
+
/** The HHD version the code follows, told by the control bytes of its start code. */
|
|
41
|
+
version,
|
|
42
|
+
/** Length of the challenge (LC) as stated at the start of the code. */
|
|
43
|
+
lc, startCode, dataElements) {
|
|
44
|
+
this.version = version;
|
|
45
|
+
this.lc = lc;
|
|
46
|
+
this.startCode = startCode;
|
|
47
|
+
this.dataElements = dataElements;
|
|
48
|
+
}
|
|
49
|
+
/** Parses the challenge data sent by the bank; throws `FormatError` if it is not a flicker code. */
|
|
50
|
+
static parse(code) {
|
|
51
|
+
const cleaned = extractCode(code);
|
|
52
|
+
for (const layout of layouts) {
|
|
53
|
+
try {
|
|
54
|
+
return FlickerCode.parseLayout(new Reader(cleaned), layout.lcDigits, layout.lengthDigits);
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
// Not this layout; try the next one.
|
|
58
|
+
if (!(error instanceof FormatError))
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new FormatError('Invalid flicker code', code);
|
|
63
|
+
}
|
|
64
|
+
/** Parses `code`, or returns `undefined` if it is not a flicker code. */
|
|
65
|
+
static tryParse(code) {
|
|
66
|
+
try {
|
|
67
|
+
return FlickerCode.parse(code);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
if (error instanceof FormatError)
|
|
71
|
+
return undefined;
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
static parseLayout(code, lcDigits, lengthDigits) {
|
|
76
|
+
const lc = code.decimal(lcDigits);
|
|
77
|
+
const challenge = new Reader(code.take(lc));
|
|
78
|
+
// The start code has a hex length field whose top bit announces control bytes (HHD 1.4); the top bit of each
|
|
79
|
+
// control byte announces another one.
|
|
80
|
+
const startLength = challenge.hex(2);
|
|
81
|
+
const controlBytes = [];
|
|
82
|
+
if ((startLength & 0x80) !== 0) {
|
|
83
|
+
do {
|
|
84
|
+
controlBytes.push(challenge.hex(2));
|
|
85
|
+
} while ((controlBytes.at(-1) & 0x80) !== 0 && controlBytes.length < 10);
|
|
86
|
+
}
|
|
87
|
+
const startCode = new StartCode(challenge.take(startLength & 0x3f), controlBytes);
|
|
88
|
+
const dataElements = [];
|
|
89
|
+
for (let i = 0; i < 3; i++) {
|
|
90
|
+
dataElements.push(new DataElement(challenge.isAtEnd ? undefined : challenge.take(challenge.decimal(lengthDigits) & 0x3f)));
|
|
91
|
+
}
|
|
92
|
+
// The fields fill exactly the LC characters; a layout that reads them otherwise is the wrong one.
|
|
93
|
+
if (!challenge.isAtEnd)
|
|
94
|
+
throw new FormatError('Flicker code fields do not match its length');
|
|
95
|
+
return new FlickerCode(controlBytes.length === 0 ? 'hhd13' : 'hhd14', lc, startCode, dataElements);
|
|
96
|
+
}
|
|
97
|
+
/** The data of the start code, which opens the challenge. */
|
|
98
|
+
get startCodeData() {
|
|
99
|
+
return this.startCode.data ?? '';
|
|
100
|
+
}
|
|
101
|
+
/** The first data element of the challenge; `undefined` if the code leaves it out. */
|
|
102
|
+
get dataElement1() {
|
|
103
|
+
return this.dataElements[0]?.data;
|
|
104
|
+
}
|
|
105
|
+
/** The second data element of the challenge; `undefined` if the code leaves it out. */
|
|
106
|
+
get dataElement2() {
|
|
107
|
+
return this.dataElements[1]?.data;
|
|
108
|
+
}
|
|
109
|
+
/** The third data element of the challenge; `undefined` if the code leaves it out. */
|
|
110
|
+
get dataElement3() {
|
|
111
|
+
return this.dataElements[2]?.data;
|
|
112
|
+
}
|
|
113
|
+
/** The hex string transmitted to the TAN generator, including checksums. */
|
|
114
|
+
render() {
|
|
115
|
+
const payload = this.payload();
|
|
116
|
+
return `${payload}${this.luhnChecksum()}${xorChecksum(payload)}`;
|
|
117
|
+
}
|
|
118
|
+
payload() {
|
|
119
|
+
let s = this.startCode.renderLength(this.version);
|
|
120
|
+
for (const c of this.startCode.controlBytes)
|
|
121
|
+
s += hex(c, 2);
|
|
122
|
+
s += this.startCode.renderData();
|
|
123
|
+
for (const element of this.dataElements) {
|
|
124
|
+
s += element.renderLength(this.version);
|
|
125
|
+
s += element.renderData();
|
|
126
|
+
}
|
|
127
|
+
return `${hex(Math.floor((s.length + 2) / 2), 2)}${s}`;
|
|
128
|
+
}
|
|
129
|
+
luhnChecksum() {
|
|
130
|
+
let s = '';
|
|
131
|
+
for (const c of this.startCode.controlBytes)
|
|
132
|
+
s += hex(c, 2);
|
|
133
|
+
s += this.startCode.renderData();
|
|
134
|
+
for (const element of this.dataElements)
|
|
135
|
+
if (element.data !== undefined)
|
|
136
|
+
s += element.renderData();
|
|
137
|
+
let sum = 0;
|
|
138
|
+
for (let i = 0; i + 1 < s.length; i += 2) {
|
|
139
|
+
sum += parseInt(s[i], 16) + digitSum(2 * parseInt(s[i + 1], 16));
|
|
140
|
+
}
|
|
141
|
+
const mod = sum % 10;
|
|
142
|
+
return mod === 0 ? '0' : hex(10 - mod, 1);
|
|
143
|
+
}
|
|
144
|
+
/** Frames to animate: each is `[clock, bit0, bit1, bit2, bit3]` for the five bars. */
|
|
145
|
+
frames() {
|
|
146
|
+
const code = `0FFF${this.render()}`;
|
|
147
|
+
const result = [];
|
|
148
|
+
for (let i = 0; i + 1 < code.length; i += 2) {
|
|
149
|
+
for (const nibble of [code[i + 1], code[i]]) {
|
|
150
|
+
const v = parseInt(nibble, 16);
|
|
151
|
+
const bits = [(v & 1) !== 0, (v & 2) !== 0, (v & 4) !== 0, (v & 8) !== 0];
|
|
152
|
+
result.push([true, ...bits]);
|
|
153
|
+
result.push([false, ...bits]);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/** The code of a combined challenge, or `code` itself without whitespace. */
|
|
160
|
+
function extractCode(code) {
|
|
161
|
+
const compact = withoutWhitespace(code);
|
|
162
|
+
const start = compact.indexOf(codeMarker);
|
|
163
|
+
const codeStart = start + codeMarker.length + markerLengthDigits;
|
|
164
|
+
const end = compact.indexOf(textMarker);
|
|
165
|
+
return start >= 0 && end >= codeStart ? compact.substring(codeStart, end) : compact;
|
|
166
|
+
}
|
|
167
|
+
function xorChecksum(payload) {
|
|
168
|
+
let x = 0;
|
|
169
|
+
for (const c of payload)
|
|
170
|
+
x ^= parseInt(c, 16);
|
|
171
|
+
return hex(x, 1);
|
|
172
|
+
}
|
|
173
|
+
function digitSum(n) {
|
|
174
|
+
let sum = 0;
|
|
175
|
+
while (n !== 0) {
|
|
176
|
+
sum += n % 10;
|
|
177
|
+
n = Math.floor(n / 10);
|
|
178
|
+
}
|
|
179
|
+
return sum;
|
|
180
|
+
}
|
|
181
|
+
/** Reads the fields of a flicker code from left to right; throws `FormatError` when a field is missing or malformed. */
|
|
182
|
+
class Reader {
|
|
183
|
+
code;
|
|
184
|
+
position = 0;
|
|
185
|
+
constructor(code) {
|
|
186
|
+
this.code = code;
|
|
187
|
+
}
|
|
188
|
+
get remaining() {
|
|
189
|
+
return this.code.length - this.position;
|
|
190
|
+
}
|
|
191
|
+
get isAtEnd() {
|
|
192
|
+
return this.remaining === 0;
|
|
193
|
+
}
|
|
194
|
+
take(length) {
|
|
195
|
+
if (length > this.remaining)
|
|
196
|
+
throw new FormatError('Flicker code too short');
|
|
197
|
+
const field = this.code.substring(this.position, this.position + length);
|
|
198
|
+
this.position += length;
|
|
199
|
+
return field;
|
|
200
|
+
}
|
|
201
|
+
decimal(digits) {
|
|
202
|
+
return parseInt(Reader.match(/^[0-9]+$/, this.take(digits)), 10);
|
|
203
|
+
}
|
|
204
|
+
hex(digits) {
|
|
205
|
+
return parseInt(Reader.match(/^[0-9A-Fa-f]+$/, this.take(digits)), 16);
|
|
206
|
+
}
|
|
207
|
+
static match(pattern, field) {
|
|
208
|
+
if (!pattern.test(field))
|
|
209
|
+
throw new FormatError('Invalid length field', field);
|
|
210
|
+
return field;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* A data element of the flicker code: a length field (LDE) followed by the data; `undefined` data if the code leaves
|
|
215
|
+
* the element out.
|
|
216
|
+
*/
|
|
217
|
+
class DataElement {
|
|
218
|
+
data;
|
|
219
|
+
constructor(data) {
|
|
220
|
+
this.data = data;
|
|
221
|
+
}
|
|
222
|
+
get isBcd() {
|
|
223
|
+
return this.data === undefined || /^[0-9]+$/.test(this.data);
|
|
224
|
+
}
|
|
225
|
+
renderData() {
|
|
226
|
+
const d = this.data;
|
|
227
|
+
if (d === undefined)
|
|
228
|
+
return '';
|
|
229
|
+
if (!this.isBcd) {
|
|
230
|
+
let ascii = '';
|
|
231
|
+
for (let i = 0; i < d.length; i++)
|
|
232
|
+
ascii += hex(d.charCodeAt(i), 2);
|
|
233
|
+
return ascii;
|
|
234
|
+
}
|
|
235
|
+
return d.length % 2 === 1 ? `${d}F` : d;
|
|
236
|
+
}
|
|
237
|
+
renderLength(version) {
|
|
238
|
+
if (this.data === undefined)
|
|
239
|
+
return '';
|
|
240
|
+
const length = Math.floor(this.renderData().length / 2);
|
|
241
|
+
if (this.isBcd)
|
|
242
|
+
return hex(length, 2);
|
|
243
|
+
if (version === 'hhd14')
|
|
244
|
+
return hex(length + 0x40, 2);
|
|
245
|
+
return `1${hex(length, 1)}`;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/** The start code: a data element whose length field may announce control bytes (HHD 1.4). */
|
|
249
|
+
class StartCode extends DataElement {
|
|
250
|
+
controlBytes;
|
|
251
|
+
constructor(data, controlBytes) {
|
|
252
|
+
super(data);
|
|
253
|
+
this.controlBytes = controlBytes;
|
|
254
|
+
}
|
|
255
|
+
renderLength(version) {
|
|
256
|
+
const s = super.renderLength(version);
|
|
257
|
+
if (version === 'hhd13' || this.controlBytes.length === 0)
|
|
258
|
+
return s;
|
|
259
|
+
return hex(parseInt(s, 16) + 0x80, 2);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
//# sourceMappingURL=flicker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flicker.js","sourceRoot":"","sources":["../../src/tan/flicker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** An image challenge (photoTAN, QR-TAN) with its MIME type. */
|
|
2
|
+
export declare class TanImage {
|
|
3
|
+
/** The media type of `bytes`, for example `image/png`. */
|
|
4
|
+
readonly mimeType: string;
|
|
5
|
+
/** The encoded image. */
|
|
6
|
+
readonly bytes: Uint8Array;
|
|
7
|
+
constructor(
|
|
8
|
+
/** The media type of `bytes`, for example `image/png`. */
|
|
9
|
+
mimeType: string,
|
|
10
|
+
/** The encoded image. */
|
|
11
|
+
bytes: Uint8Array);
|
|
12
|
+
/**
|
|
13
|
+
* Reads the bank's image container: a two-byte length and the MIME type, then a two-byte length and the image.
|
|
14
|
+
*
|
|
15
|
+
* `undefined` unless the MIME type is an image type and the container holds the whole image.
|
|
16
|
+
*/
|
|
17
|
+
static tryParse(data: Uint8Array): TanImage | undefined;
|
|
18
|
+
/** The image as a `data:` URL, ready for the `src` of an `<img>`. */
|
|
19
|
+
toDataUrl(): string;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=tan-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-image.d.ts","sourceRoot":"","sources":["../../src/tan/tan-image.ts"],"names":[],"mappings":"AAIA,gEAAgE;AAChE,qBAAa,QAAQ;IAEjB,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM;IACzB,yBAAyB;IACzB,QAAQ,CAAC,KAAK,EAAE,UAAU;;IAH1B,0DAA0D;IACjD,QAAQ,EAAE,MAAM;IACzB,yBAAyB;IAChB,KAAK,EAAE,UAAU;IAG5B;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,GAAG,QAAQ,GAAG,SAAS;IAavD,qEAAqE;IACrE,SAAS,IAAI,MAAM;CAGpB"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { base64Encode, latin1Decode } from '../util/bytes.js';
|
|
2
|
+
const imageMimeType = /^image\/[a-zA-Z0-9.+-]+$/;
|
|
3
|
+
/** An image challenge (photoTAN, QR-TAN) with its MIME type. */
|
|
4
|
+
export class TanImage {
|
|
5
|
+
mimeType;
|
|
6
|
+
bytes;
|
|
7
|
+
constructor(
|
|
8
|
+
/** The media type of `bytes`, for example `image/png`. */
|
|
9
|
+
mimeType,
|
|
10
|
+
/** The encoded image. */
|
|
11
|
+
bytes) {
|
|
12
|
+
this.mimeType = mimeType;
|
|
13
|
+
this.bytes = bytes;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Reads the bank's image container: a two-byte length and the MIME type, then a two-byte length and the image.
|
|
17
|
+
*
|
|
18
|
+
* `undefined` unless the MIME type is an image type and the container holds the whole image.
|
|
19
|
+
*/
|
|
20
|
+
static tryParse(data) {
|
|
21
|
+
if (data.length < 4)
|
|
22
|
+
return undefined;
|
|
23
|
+
const mimeLength = (data[0] << 8) | data[1];
|
|
24
|
+
if (mimeLength === 0 || data.length < 2 + mimeLength + 2)
|
|
25
|
+
return undefined;
|
|
26
|
+
const mime = latin1Decode(data.subarray(2, 2 + mimeLength));
|
|
27
|
+
if (!imageMimeType.test(mime))
|
|
28
|
+
return undefined;
|
|
29
|
+
const offset = 2 + mimeLength;
|
|
30
|
+
const imageLength = (data[offset] << 8) | data[offset + 1];
|
|
31
|
+
const start = offset + 2;
|
|
32
|
+
if (start + imageLength > data.length)
|
|
33
|
+
return undefined;
|
|
34
|
+
return new TanImage(mime, data.subarray(start, start + imageLength));
|
|
35
|
+
}
|
|
36
|
+
/** The image as a `data:` URL, ready for the `src` of an `<img>`. */
|
|
37
|
+
toDataUrl() {
|
|
38
|
+
return `data:${this.mimeType};base64,${base64Encode(this.bytes)}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=tan-image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tan-image.js","sourceRoot":"","sources":["../../src/tan/tan-image.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAE9D,MAAM,aAAa,GAAG,0BAA0B,CAAC;AAEjD,gEAAgE;AAChE,MAAM,OAAO,QAAQ;IAGR;IAEA;IAJX;IACE,0DAA0D;IACjD,QAAgB;IACzB,yBAAyB;IAChB,KAAiB;QAFjB,aAAQ,GAAR,QAAQ,CAAQ;QAEhB,UAAK,GAAL,KAAK,CAAY;IACzB,CAAC;IAEJ;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAgB;QAC9B,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QACtC,MAAM,UAAU,GAAG,CAAC,IAAI,CAAC,CAAC,CAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;QAC9C,IAAI,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,UAAU,GAAG,CAAC;YAAE,OAAO,SAAS,CAAC;QAC3E,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,SAAS,CAAC;QAChD,MAAM,MAAM,GAAG,CAAC,GAAG,UAAU,CAAC;QAC9B,MAAM,WAAW,GAAG,CAAC,IAAI,CAAC,MAAM,CAAE,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAE,CAAC;QAC7D,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,CAAC;QACzB,IAAI,KAAK,GAAG,WAAW,GAAG,IAAI,CAAC,MAAM;YAAE,OAAO,SAAS,CAAC;QACxD,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,WAAW,CAAC,CAAC,CAAC;IACvE,CAAC;IAED,qEAAqE;IACrE,SAAS;QACP,OAAO,QAAQ,IAAI,CAAC,QAAQ,WAAW,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;IACpE,CAAC;CACF"}
|