@coderoftime/fints 0.0.0-stage → 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 +152 -0
- package/LICENSE +28 -0
- package/README.md +246 -2
- 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 +168 -0
- package/dist/core/account.d.ts.map +1 -0
- package/dist/core/account.js +245 -0
- package/dist/core/account.js.map +1 -0
- package/dist/core/amount.d.ts +63 -0
- package/dist/core/amount.d.ts.map +1 -0
- package/dist/core/amount.js +192 -0
- package/dist/core/amount.js.map +1 -0
- package/dist/core/balance.d.ts +55 -0
- package/dist/core/balance.d.ts.map +1 -0
- package/dist/core/balance.js +54 -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 +161 -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 +80 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +153 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/errors.d.ts +153 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +247 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/feedback.d.ts +72 -0
- package/dist/core/feedback.d.ts.map +1 -0
- package/dist/core/feedback.js +127 -0
- package/dist/core/feedback.js.map +1 -0
- package/dist/core/transaction.d.ts +103 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/core/transaction.js +96 -0
- package/dist/core/transaction.js.map +1 -0
- package/dist/core/transport.d.ts +64 -0
- package/dist/core/transport.d.ts.map +1 -0
- package/dist/core/transport.js +149 -0
- package/dist/core/transport.js.map +1 -0
- package/dist/formats/camt.d.ts +32 -0
- package/dist/formats/camt.d.ts.map +1 -0
- package/dist/formats/camt.js +242 -0
- package/dist/formats/camt.js.map +1 -0
- package/dist/formats/mt940.d.ts +54 -0
- package/dist/formats/mt940.d.ts.map +1 -0
- package/dist/formats/mt940.js +376 -0
- package/dist/formats/mt940.js.map +1 -0
- package/dist/formats/readers.d.ts +15 -0
- package/dist/formats/readers.d.ts.map +1 -0
- package/dist/formats/readers.js +42 -0
- package/dist/formats/readers.js.map +1 -0
- 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 +28 -0
- package/dist/formats/sepa-purpose.d.ts.map +1 -0
- package/dist/formats/sepa-purpose.js +89 -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 +66 -0
- package/dist/session/app-approval.d.ts.map +1 -0
- package/dist/session/app-approval.js +226 -0
- package/dist/session/app-approval.js.map +1 -0
- package/dist/session/dialog-runner.d.ts +31 -0
- package/dist/session/dialog-runner.d.ts.map +1 -0
- package/dist/session/dialog-runner.js +111 -0
- package/dist/session/dialog-runner.js.map +1 -0
- package/dist/session/login-guard.d.ts +78 -0
- package/dist/session/login-guard.d.ts.map +1 -0
- package/dist/session/login-guard.js +181 -0
- package/dist/session/login-guard.js.map +1 -0
- package/dist/session/sepa-accounts.d.ts +40 -0
- package/dist/session/sepa-accounts.d.ts.map +1 -0
- package/dist/session/sepa-accounts.js +112 -0
- package/dist/session/sepa-accounts.js.map +1 -0
- package/dist/session/session-setup.d.ts +80 -0
- package/dist/session/session-setup.d.ts.map +1 -0
- package/dist/session/session-setup.js +182 -0
- package/dist/session/session-setup.js.map +1 -0
- package/dist/session/session-state.d.ts +50 -0
- package/dist/session/session-state.d.ts.map +1 -0
- package/dist/session/session-state.js +182 -0
- package/dist/session/session-state.js.map +1 -0
- 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 +96 -0
- package/dist/session/tan-selection.d.ts.map +1 -0
- package/dist/session/tan-selection.js +189 -0
- package/dist/session/tan-selection.js.map +1 -0
- package/dist/session/user-prompts.d.ts +65 -0
- package/dist/session/user-prompts.d.ts.map +1 -0
- package/dist/session/user-prompts.js +184 -0
- package/dist/session/user-prompts.js.map +1 -0
- package/dist/tan/challenge.d.ts +135 -0
- package/dist/tan/challenge.d.ts.map +1 -0
- package/dist/tan/challenge.js +184 -0
- package/dist/tan/challenge.js.map +1 -0
- package/dist/tan/flicker.d.ts +42 -0
- package/dist/tan/flicker.d.ts.map +1 -0
- package/dist/tan/flicker.js +296 -0
- package/dist/tan/flicker.js.map +1 -0
- package/dist/tan/tan-image.d.ts +22 -0
- package/dist/tan/tan-image.d.ts.map +1 -0
- package/dist/tan/tan-image.js +43 -0
- package/dist/tan/tan-image.js.map +1 -0
- package/dist/tan/tan-models.d.ts +149 -0
- package/dist/tan/tan-models.d.ts.map +1 -0
- package/dist/tan/tan-models.js +153 -0
- package/dist/tan/tan-models.js.map +1 -0
- package/dist/util/async.d.ts +22 -0
- package/dist/util/async.d.ts.map +1 -0
- package/dist/util/async.js +45 -0
- package/dist/util/async.js.map +1 -0
- package/dist/util/bytes.d.ts +22 -0
- package/dist/util/bytes.d.ts.map +1 -0
- package/dist/util/bytes.js +128 -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 +30 -0
- package/dist/util/call-context.js.map +1 -0
- package/dist/util/dates.d.ts +55 -0
- package/dist/util/dates.d.ts.map +1 -0
- package/dist/util/dates.js +189 -0
- package/dist/util/dates.js.map +1 -0
- package/dist/util/errors.d.ts +17 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +24 -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 +17 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +62 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/text.d.ts +37 -0
- package/dist/util/text.d.ts.map +1 -0
- package/dist/util/text.js +107 -0
- package/dist/util/text.js.map +1 -0
- package/dist/util/xml.d.ts +36 -0
- package/dist/util/xml.d.ts.map +1 -0
- package/dist/util/xml.js +194 -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 +173 -0
- package/dist/v3/account-extension.js.map +1 -0
- 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 +112 -0
- package/dist/v3/dialog.d.ts.map +1 -0
- package/dist/v3/dialog.js +241 -0
- package/dist/v3/dialog.js.map +1 -0
- package/dist/v3/message.d.ts +85 -0
- package/dist/v3/message.d.ts.map +1 -0
- package/dist/v3/message.js +303 -0
- package/dist/v3/message.js.map +1 -0
- 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 +105 -0
- package/dist/v3/parameters.d.ts.map +1 -0
- package/dist/v3/parameters.js +450 -0
- package/dist/v3/parameters.js.map +1 -0
- package/dist/v3/protocol.d.ts +61 -0
- package/dist/v3/protocol.d.ts.map +1 -0
- package/dist/v3/protocol.js +113 -0
- package/dist/v3/protocol.js.map +1 -0
- 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 +106 -0
- package/dist/v3/segments.d.ts.map +1 -0
- package/dist/v3/segments.js +325 -0
- package/dist/v3/segments.js.map +1 -0
- package/dist/v3/syntax.d.ts +127 -0
- package/dist/v3/syntax.d.ts.map +1 -0
- package/dist/v3/syntax.js +367 -0
- package/dist/v3/syntax.js.map +1 -0
- package/package.json +67 -4
- package/src/client.ts +252 -0
- package/src/core/account.ts +323 -0
- package/src/core/amount.ts +211 -0
- package/src/core/balance.ts +85 -0
- package/src/core/bank-info.ts +66 -0
- package/src/core/client.ts +186 -0
- package/src/core/config.ts +182 -0
- package/src/core/errors.ts +277 -0
- package/src/core/feedback.ts +140 -0
- package/src/core/transaction.ts +161 -0
- package/src/core/transport.ts +189 -0
- package/src/formats/camt.ts +266 -0
- package/src/formats/mt940.ts +419 -0
- package/src/formats/readers.ts +49 -0
- package/src/formats/record-budget.ts +18 -0
- package/src/formats/sepa-purpose.ts +94 -0
- package/src/index.ts +70 -0
- package/src/session/app-approval.ts +278 -0
- package/src/session/dialog-runner.ts +122 -0
- package/src/session/login-guard.ts +221 -0
- package/src/session/sepa-accounts.ts +115 -0
- package/src/session/session-setup.ts +184 -0
- package/src/session/session-state.ts +204 -0
- package/src/session/signed-dialog.ts +192 -0
- package/src/session/tan-selection.ts +203 -0
- package/src/session/user-prompts.ts +193 -0
- package/src/tan/challenge.ts +261 -0
- package/src/tan/flicker.ts +308 -0
- package/src/tan/tan-image.ts +38 -0
- package/src/tan/tan-models.ts +252 -0
- package/src/util/async.ts +48 -0
- package/src/util/bytes.ts +129 -0
- package/src/util/call-context.ts +41 -0
- package/src/util/dates.ts +210 -0
- package/src/util/errors.ts +27 -0
- package/src/util/immutable-map.ts +47 -0
- package/src/util/json.ts +67 -0
- package/src/util/text.ts +112 -0
- package/src/util/xml.ts +183 -0
- package/src/v3/account-extension.ts +185 -0
- package/src/v3/bank-connection.ts +67 -0
- package/src/v3/dialog.ts +283 -0
- package/src/v3/message.ts +373 -0
- package/src/v3/parameter-store.ts +160 -0
- package/src/v3/parameters.ts +524 -0
- package/src/v3/protocol.ts +148 -0
- package/src/v3/queries.ts +190 -0
- package/src/v3/segments.ts +374 -0
- package/src/v3/syntax.ts +369 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { clientClosedError } from '../core/errors.js';
|
|
2
|
+
import { TanAnswer } from '../tan/challenge.js';
|
|
3
|
+
import { Deferred } from '../util/async.js';
|
|
4
|
+
import { invalidArgument } from '../util/errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Everything a session tells the app, asks of it or waits for: the bank's messages, warnings and log, the TAN callback,
|
|
7
|
+
* the selectors and the pauses between status requests.
|
|
8
|
+
*
|
|
9
|
+
* Once the client is closed nothing is told or asked, and every wait ends with `FinTsStateError`. Errors of the
|
|
10
|
+
* callbacks that only inform the app wait for `reportingCallbackErrors`, as `FinTsClientOptions` promises.
|
|
11
|
+
*/
|
|
12
|
+
export class UserPrompts {
|
|
13
|
+
options;
|
|
14
|
+
closed = false;
|
|
15
|
+
/** The first error an informing callback threw during the running call. */
|
|
16
|
+
callbackError;
|
|
17
|
+
/** Aborts the waits under way; each wait removes its abort when it is over, or when nothing waits for it any more. */
|
|
18
|
+
aborts = new Set();
|
|
19
|
+
constructor(options) {
|
|
20
|
+
this.options = options;
|
|
21
|
+
}
|
|
22
|
+
/** Ends every wait; the client calls it once. */
|
|
23
|
+
close() {
|
|
24
|
+
this.closed = true;
|
|
25
|
+
for (const abort of [...this.aborts])
|
|
26
|
+
abort();
|
|
27
|
+
this.aborts.clear();
|
|
28
|
+
}
|
|
29
|
+
/** Throws `FinTsStateError` once `close` was called. */
|
|
30
|
+
checkOpen() {
|
|
31
|
+
if (this.closed)
|
|
32
|
+
throw clientClosedError();
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Runs `call` and then fails it with the first error an informing callback threw meanwhile; an error of `call` itself
|
|
36
|
+
* takes precedence.
|
|
37
|
+
*/
|
|
38
|
+
async reportingCallbackErrors(call) {
|
|
39
|
+
this.callbackError = undefined;
|
|
40
|
+
const result = await call();
|
|
41
|
+
// Set by a callback while `call` ran, which the compiler cannot see.
|
|
42
|
+
const failure = this.callbackError;
|
|
43
|
+
if (failure !== undefined)
|
|
44
|
+
throw failure.error;
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
/** The app's logger, whose errors are kept for `reportingCallbackErrors`; `undefined` without one. */
|
|
48
|
+
get logger() {
|
|
49
|
+
const logger = this.options.logger;
|
|
50
|
+
if (logger === undefined)
|
|
51
|
+
return undefined;
|
|
52
|
+
return (direction, message) => {
|
|
53
|
+
this.inform(() => {
|
|
54
|
+
logger(direction, message);
|
|
55
|
+
});
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Passes the bank's `message` to the app, unless the client is closed. */
|
|
59
|
+
showBankMessage(message) {
|
|
60
|
+
const handler = this.options.onBankMessage;
|
|
61
|
+
if (handler !== undefined && !this.closed) {
|
|
62
|
+
this.inform(() => {
|
|
63
|
+
handler(message);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/** Passes the bank's `warning` to the app, unless the client is closed. */
|
|
68
|
+
showWarning(warning) {
|
|
69
|
+
const handler = this.options.onWarning;
|
|
70
|
+
if (handler !== undefined && !this.closed) {
|
|
71
|
+
this.inform(() => {
|
|
72
|
+
handler(warning);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
inform(callback) {
|
|
77
|
+
try {
|
|
78
|
+
callback();
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
this.callbackError ??= { error };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Shows `challenge` through the TAN callback and returns its answer; rejects with `TypeError` for anything but a
|
|
86
|
+
* `TanAnswer`. Once `TanChallenge.resolved` settles, nothing waits for the answer any more.
|
|
87
|
+
*/
|
|
88
|
+
askTan(challenge) {
|
|
89
|
+
return this.unlessClosed(async () => tanAnswerOf(await this.options.tanCallback(challenge)), challenge.resolved);
|
|
90
|
+
}
|
|
91
|
+
/** Waits for `delayMs` milliseconds, or until `interrupt` settles. */
|
|
92
|
+
pause(delayMs, interrupt) {
|
|
93
|
+
if (this.closed)
|
|
94
|
+
return Promise.reject(clientClosedError());
|
|
95
|
+
let timer;
|
|
96
|
+
const elapsed = new Promise((resolve) => {
|
|
97
|
+
timer = setTimeout(resolve, Math.max(0, delayMs));
|
|
98
|
+
});
|
|
99
|
+
// Neither an interrupted wait nor a closed client may leave a timer behind that keeps the process alive.
|
|
100
|
+
return this.unlessClosed(() => Promise.race([elapsed, ...(interrupt === undefined ? [] : [interrupt.then(() => undefined)])])).finally(() => {
|
|
101
|
+
clearTimeout(timer);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The TAN method to use among `candidates`: the app's choice, or the first without a selector or an answer.
|
|
106
|
+
*
|
|
107
|
+
* Throws `TypeError` if the selector returns a method that is not among `candidates`.
|
|
108
|
+
*/
|
|
109
|
+
async chooseMethod(candidates) {
|
|
110
|
+
const selector = this.options.tanMethodSelector;
|
|
111
|
+
if (candidates.length === 1 || selector === undefined)
|
|
112
|
+
return candidates[0];
|
|
113
|
+
const chosen = await this.unlessClosed(() => selector(candidates));
|
|
114
|
+
if (chosen === undefined)
|
|
115
|
+
return candidates[0];
|
|
116
|
+
const match = candidates.find((m) => m.securityFunction === chosen.securityFunction);
|
|
117
|
+
if (match === undefined)
|
|
118
|
+
throw invalidArgument('Returned a TAN method the bank did not offer', 'tanMethodSelector');
|
|
119
|
+
return match;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* The TAN medium to use among the active `media`: the app's choice, or the first without a selector or an answer.
|
|
123
|
+
*
|
|
124
|
+
* Throws `TypeError` if the selector returns a medium that is not among `media`.
|
|
125
|
+
*/
|
|
126
|
+
async chooseMedium(media) {
|
|
127
|
+
const selector = this.options.tanMediumSelector;
|
|
128
|
+
if (media.length === 1 || selector === undefined)
|
|
129
|
+
return media[0];
|
|
130
|
+
const chosen = await this.unlessClosed(() => selector(media));
|
|
131
|
+
if (chosen === undefined)
|
|
132
|
+
return media[0];
|
|
133
|
+
const match = media.find((m) => m.name === chosen.name);
|
|
134
|
+
if (match === undefined)
|
|
135
|
+
throw invalidArgument('Returned a TAN medium the bank did not list', 'tanMediumSelector');
|
|
136
|
+
return match;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Starts `wait` and ends it with `FinTsStateError` if `close` is called before it is over. Once `unwanted` settles,
|
|
140
|
+
* nothing waits for it any more: it is forgotten then, rather than kept until a callback that never answers does.
|
|
141
|
+
*
|
|
142
|
+
* Once the client is closed, the returned promise rejects rather than this function throwing: a caller that starts
|
|
143
|
+
* several waits at once would otherwise leave the failure of those started first unheard.
|
|
144
|
+
*/
|
|
145
|
+
unlessClosed(wait, unwanted) {
|
|
146
|
+
if (this.closed)
|
|
147
|
+
return Promise.reject(clientClosedError());
|
|
148
|
+
const result = new Deferred();
|
|
149
|
+
const abort = () => {
|
|
150
|
+
result.reject(clientClosedError());
|
|
151
|
+
};
|
|
152
|
+
const forget = () => {
|
|
153
|
+
this.aborts.delete(abort);
|
|
154
|
+
};
|
|
155
|
+
this.aborts.add(abort);
|
|
156
|
+
// Runs `wait` at once, as far as its first `await`, and turns an error it throws into a rejection.
|
|
157
|
+
const started = (async () => wait())();
|
|
158
|
+
started
|
|
159
|
+
.then((value) => {
|
|
160
|
+
result.resolve(value);
|
|
161
|
+
}, (error) => {
|
|
162
|
+
result.reject(error);
|
|
163
|
+
})
|
|
164
|
+
.finally(forget)
|
|
165
|
+
.catch(() => undefined);
|
|
166
|
+
void unwanted?.then(forget, forget);
|
|
167
|
+
return result.promise;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* `answer`, a result of the TAN callback, as a `TanAnswer`; throws `TypeError` for anything else, such as the
|
|
172
|
+
* `undefined` of a callback that returns nothing. The answer is read once, so that what was checked is what counts.
|
|
173
|
+
*/
|
|
174
|
+
function tanAnswerOf(answer) {
|
|
175
|
+
const { kind, tan } = (typeof answer === 'object' && answer !== null ? answer : {});
|
|
176
|
+
if (kind === 'tan' && typeof tan === 'string')
|
|
177
|
+
return TanAnswer.tan(tan);
|
|
178
|
+
if (kind === 'approved')
|
|
179
|
+
return TanAnswer.approved;
|
|
180
|
+
if (kind === 'cancelled')
|
|
181
|
+
return TanAnswer.cancelled;
|
|
182
|
+
throw invalidArgument('Returned no TanAnswer', 'tanCallback');
|
|
183
|
+
}
|
|
184
|
+
//# sourceMappingURL=user-prompts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-prompts.js","sourceRoot":"","sources":["../../src/session/user-prompts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { FlickerCode } from './flicker.js';
|
|
2
|
+
import { TanImage } from './tan-image.js';
|
|
3
|
+
import type { TanMedium, TanMethod } from './tan-models.js';
|
|
4
|
+
/** `message`, or `undefined` where it is empty or the filler banks send when there is nothing to show. */
|
|
5
|
+
export declare function challengeText(message: string | undefined): string | undefined;
|
|
6
|
+
/** What `TanChallenge` takes. */
|
|
7
|
+
export interface TanChallengeInit {
|
|
8
|
+
method: TanMethod;
|
|
9
|
+
reference: string;
|
|
10
|
+
message?: string | undefined;
|
|
11
|
+
hhdUc?: Uint8Array | undefined;
|
|
12
|
+
flickerCode?: FlickerCode | undefined;
|
|
13
|
+
image?: TanImage | undefined;
|
|
14
|
+
isDecoupled?: boolean;
|
|
15
|
+
validUntil?: Date | undefined;
|
|
16
|
+
tanMediumName?: string | undefined;
|
|
17
|
+
attempt?: number;
|
|
18
|
+
resolved?: Promise<void> | undefined;
|
|
19
|
+
}
|
|
20
|
+
/** A request from the bank to authorize an action with a TAN or an app approval. */
|
|
21
|
+
export declare class TanChallenge {
|
|
22
|
+
/** The TAN method the challenge is for. */
|
|
23
|
+
readonly method: TanMethod;
|
|
24
|
+
/** Bank reference of the pending action, one character per byte the bank sent (ISO 8859-1). */
|
|
25
|
+
readonly reference: string;
|
|
26
|
+
/**
|
|
27
|
+
* Challenge text as the bank sent it, including the formatting tags of a structured challenge
|
|
28
|
+
* (`TanMethod.structuredChallenge`); `plainMessage` is the text to show. Show either as text only, never as HTML
|
|
29
|
+
* (`innerHTML`, `v-html`): it is the bank's text, not markup to trust.
|
|
30
|
+
*/
|
|
31
|
+
readonly message: string | undefined;
|
|
32
|
+
/** Raw challenge data for TAN generators (HHD_UC). */
|
|
33
|
+
readonly hhdUc: Uint8Array | undefined;
|
|
34
|
+
/** chipTAN optical flicker code, if the challenge contains one. */
|
|
35
|
+
readonly flickerCode: FlickerCode | undefined;
|
|
36
|
+
/** photoTAN / QR-TAN image, if the challenge contains one. */
|
|
37
|
+
readonly image: TanImage | undefined;
|
|
38
|
+
/** Approval happens in a separate app; see `TanCallback` for what to answer. */
|
|
39
|
+
readonly isDecoupled: boolean;
|
|
40
|
+
/** The exact moment until which the bank accepts the TAN or the approval, where it states date and time of day. */
|
|
41
|
+
readonly validUntil: Date | undefined;
|
|
42
|
+
/** Name of the TAN medium the bank sent the challenge to. */
|
|
43
|
+
readonly tanMediumName: string | undefined;
|
|
44
|
+
/** Counts repeated prompts for the same decoupled approval. */
|
|
45
|
+
readonly attempt: number;
|
|
46
|
+
/**
|
|
47
|
+
* Settles when the client needs no more answer to this challenge: a later prompt for the approval replaces it, or the
|
|
48
|
+
* bank confirmed the approval, or the approval failed, timed out or was cancelled. Set where the client asks the bank
|
|
49
|
+
* by itself while the callback shows the challenge (`TanCallback`), `undefined` otherwise. It never rejects.
|
|
50
|
+
*/
|
|
51
|
+
readonly resolved: Promise<void> | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* A challenge as the bank sent it, with a copy of `validUntil`, which no other challenge shares. Use
|
|
54
|
+
* `TanChallenge.create` to have a flicker code or image detected in the data.
|
|
55
|
+
*/
|
|
56
|
+
constructor(init: TanChallengeInit);
|
|
57
|
+
/** Builds a challenge and detects a photoTAN image or chipTAN flicker code in `hhdUc` or `message`. */
|
|
58
|
+
static create(init: Omit<TanChallengeInit, 'flickerCode' | 'image' | 'resolved'>): TanChallenge;
|
|
59
|
+
/**
|
|
60
|
+
* Whether the user can report the approval in the banking app; `false` where only the bank's confirmation completes
|
|
61
|
+
* it, which `TanCallback` tells how to handle.
|
|
62
|
+
*/
|
|
63
|
+
get manualConfirmation(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* `message` as the user reads it.
|
|
66
|
+
*
|
|
67
|
+
* A method with structured challenges (`TanMethod.structuredChallenge`) may use the formatting tags the specification
|
|
68
|
+
* defines. Line breaks, paragraphs and list items become new lines. Bold, italics, underlining and list tags are
|
|
69
|
+
* removed, and the entity references of such markup are resolved. Any other challenge stays as the bank sent it,
|
|
70
|
+
* which is how the specification wants it shown.
|
|
71
|
+
*
|
|
72
|
+
* Show it as text, such as with `textContent`, or `{{ }}` in Vue, and `white-space: pre-line` for its line breaks;
|
|
73
|
+
* never as HTML (`innerHTML`, `v-html`). It keeps the tags the specification does not define, and `<` becomes
|
|
74
|
+
* `<`, so that as HTML the bank's text could run scripts in the app.
|
|
75
|
+
*/
|
|
76
|
+
get plainMessage(): string | undefined;
|
|
77
|
+
}
|
|
78
|
+
/** `challenge` again, counted as the next prompt for the same approval, with the bank's new instructions `message`. */
|
|
79
|
+
export declare function nextAttempt(challenge: TanChallenge, message?: string): TanChallenge;
|
|
80
|
+
/** `challenge` with `TanChallenge.resolved` set to `resolved`. */
|
|
81
|
+
export declare function withResolved(challenge: TanChallenge, resolved: Promise<void>): TanChallenge;
|
|
82
|
+
/** The user's answer to a `TanChallenge`: `TanAnswer.tan(…)`, `TanAnswer.approved` or `TanAnswer.cancelled`. */
|
|
83
|
+
export type TanAnswer =
|
|
84
|
+
/**
|
|
85
|
+
* The TAN the user entered; an empty one cancels a TAN challenge. Blanks around it do not count, and digits of
|
|
86
|
+
* another width, such as full-width ones, count as the digits they are (Unicode NFKC).
|
|
87
|
+
*/
|
|
88
|
+
{
|
|
89
|
+
readonly kind: 'tan';
|
|
90
|
+
readonly tan: string;
|
|
91
|
+
}
|
|
92
|
+
/** The user reports the approval in the banking app (`TanChallenge.isDecoupled`); the client then asks the bank. */
|
|
93
|
+
| {
|
|
94
|
+
readonly kind: 'approved';
|
|
95
|
+
}
|
|
96
|
+
/** The user cancels: the client ends the bank dialog, and the call fails with `FinTsTanCancelledError`. */
|
|
97
|
+
| {
|
|
98
|
+
readonly kind: 'cancelled';
|
|
99
|
+
};
|
|
100
|
+
/** The answers a `TanCallback` returns. */
|
|
101
|
+
export declare const TanAnswer: Readonly<{
|
|
102
|
+
/** The TAN the user entered; an empty one cancels a TAN challenge (`TanAnswer`). */
|
|
103
|
+
tan: (tan: string) => TanAnswer;
|
|
104
|
+
/** The user reports the approval in the banking app (`TanChallenge.isDecoupled`); the client then asks the bank. */
|
|
105
|
+
approved: {
|
|
106
|
+
readonly kind: "approved";
|
|
107
|
+
};
|
|
108
|
+
/** The user cancels: the client ends the bank dialog, and the call fails with `FinTsTanCancelledError`. */
|
|
109
|
+
cancelled: {
|
|
110
|
+
readonly kind: "cancelled";
|
|
111
|
+
};
|
|
112
|
+
}>;
|
|
113
|
+
/**
|
|
114
|
+
* Shows the `TanChallenge` to the user and returns the `TanAnswer`: `TanAnswer.tan(…)` for a TAN, `TanAnswer.approved`
|
|
115
|
+
* once the user reports an approval in the banking app (`TanChallenge.isDecoupled`), `TanAnswer.cancelled` to cancel
|
|
116
|
+
* either.
|
|
117
|
+
*
|
|
118
|
+
* The call fails with a `TypeError`, without reaching the bank, for anything but a `TanAnswer`, for `approved` to a TAN
|
|
119
|
+
* challenge, for a TAN to an app approval the user can report, and for a TAN that the method rules out: longer than
|
|
120
|
+
* `TanMethod.maxTanLength`, with anything but the digits 0 to 9 where `TanMethod.numericTan`, or that FinTS 3.0 cannot
|
|
121
|
+
* send (ISO 8859-1 only). Where the user cannot report the approval (`TanChallenge.manualConfirmation` is `false`),
|
|
122
|
+
* offer no confirm button: any `TanAnswer` but `cancelled` is ignored.
|
|
123
|
+
*
|
|
124
|
+
* If the bank has not confirmed a reported approval yet, the callback is called again with the next
|
|
125
|
+
* `TanChallenge.attempt` and the bank's latest instructions. Where the client polls the bank by itself, it sets
|
|
126
|
+
* `TanChallenge.resolved`: close the prompt when it settles, the callback's result is no longer waited for then. New
|
|
127
|
+
* instructions of the bank then replace the open prompt with the next attempt, which the callback gets after the
|
|
128
|
+
* handlers of the earlier prompt's `resolved` have run.
|
|
129
|
+
*/
|
|
130
|
+
export type TanCallback = (challenge: TanChallenge) => TanAnswer | Promise<TanAnswer>;
|
|
131
|
+
/** Picks one of several allowed TAN methods; the first is used when this returns `undefined`. */
|
|
132
|
+
export type TanMethodSelector = (methods: readonly TanMethod[]) => TanMethod | undefined | Promise<TanMethod | undefined>;
|
|
133
|
+
/** Picks one of several active TAN media; the first is used when this returns `undefined`. */
|
|
134
|
+
export type TanMediumSelector = (media: readonly TanMedium[]) => TanMedium | undefined | Promise<TanMedium | undefined>;
|
|
135
|
+
//# sourceMappingURL=challenge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"challenge.d.ts","sourceRoot":"","sources":["../../src/tan/challenge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { latin1Decode } from '../util/bytes.js';
|
|
2
|
+
import { copyDate } from '../util/dates.js';
|
|
3
|
+
import { characterReference } from '../util/text.js';
|
|
4
|
+
import { challengeInstruction, FlickerCode, hasEmbeddedFlickerCode } from './flicker.js';
|
|
5
|
+
import { TanImage } from './tan-image.js';
|
|
6
|
+
/** Challenge text banks send when there is nothing to show. */
|
|
7
|
+
const noChallenge = 'nochallenge';
|
|
8
|
+
/** `message`, or `undefined` where it is empty or the filler banks send when there is nothing to show. */
|
|
9
|
+
export function challengeText(message) {
|
|
10
|
+
return message === undefined || message === '' || message === noChallenge ? undefined : message;
|
|
11
|
+
}
|
|
12
|
+
const lineBreak = /<br\b[^<>]*>/gi;
|
|
13
|
+
const blockTag = /<\/?(?:p|li)\b[^<>]*>/gi;
|
|
14
|
+
/**
|
|
15
|
+
* The formatting tags a structured challenge may carry besides line breaks, paragraphs and list items (PIN/TAN, Data
|
|
16
|
+
* Dictionary "Challenge"). Angle brackets around anything else, such as `<50 EUR>`, are part of the text the user
|
|
17
|
+
* approves, so they stay. Excluding `<` and `>` from the tag keeps the search linear.
|
|
18
|
+
*/
|
|
19
|
+
const formattingTag = /<\/?(?:b|i|u|ul|ol)\b[^<>]*>/gi;
|
|
20
|
+
const entityReference = /&(#x?[0-9a-fA-F]+|[a-zA-Z]+);/g;
|
|
21
|
+
const blankLines = /\n{3,}/g;
|
|
22
|
+
const namedEntities = {
|
|
23
|
+
amp: '&',
|
|
24
|
+
lt: '<',
|
|
25
|
+
gt: '>',
|
|
26
|
+
quot: '"',
|
|
27
|
+
apos: "'",
|
|
28
|
+
nbsp: ' ',
|
|
29
|
+
auml: 'ä',
|
|
30
|
+
ouml: 'ö',
|
|
31
|
+
uuml: 'ü',
|
|
32
|
+
Auml: 'Ä',
|
|
33
|
+
Ouml: 'Ö',
|
|
34
|
+
Uuml: 'Ü',
|
|
35
|
+
szlig: 'ß',
|
|
36
|
+
euro: '€',
|
|
37
|
+
};
|
|
38
|
+
/** The character `name` refers to; `undefined` for an unknown name and for a number that is not a character. */
|
|
39
|
+
function decodeEntity(name) {
|
|
40
|
+
return characterReference(name) ?? (Object.hasOwn(namedEntities, name) ? namedEntities[name] : undefined);
|
|
41
|
+
}
|
|
42
|
+
/** A request from the bank to authorize an action with a TAN or an app approval. */
|
|
43
|
+
export class TanChallenge {
|
|
44
|
+
/** The TAN method the challenge is for. */
|
|
45
|
+
method;
|
|
46
|
+
/** Bank reference of the pending action, one character per byte the bank sent (ISO 8859-1). */
|
|
47
|
+
reference;
|
|
48
|
+
/**
|
|
49
|
+
* Challenge text as the bank sent it, including the formatting tags of a structured challenge
|
|
50
|
+
* (`TanMethod.structuredChallenge`); `plainMessage` is the text to show. Show either as text only, never as HTML
|
|
51
|
+
* (`innerHTML`, `v-html`): it is the bank's text, not markup to trust.
|
|
52
|
+
*/
|
|
53
|
+
message;
|
|
54
|
+
/** Raw challenge data for TAN generators (HHD_UC). */
|
|
55
|
+
hhdUc;
|
|
56
|
+
/** chipTAN optical flicker code, if the challenge contains one. */
|
|
57
|
+
flickerCode;
|
|
58
|
+
/** photoTAN / QR-TAN image, if the challenge contains one. */
|
|
59
|
+
image;
|
|
60
|
+
/** Approval happens in a separate app; see `TanCallback` for what to answer. */
|
|
61
|
+
isDecoupled;
|
|
62
|
+
/** The exact moment until which the bank accepts the TAN or the approval, where it states date and time of day. */
|
|
63
|
+
validUntil;
|
|
64
|
+
/** Name of the TAN medium the bank sent the challenge to. */
|
|
65
|
+
tanMediumName;
|
|
66
|
+
/** Counts repeated prompts for the same decoupled approval. */
|
|
67
|
+
attempt;
|
|
68
|
+
/**
|
|
69
|
+
* Settles when the client needs no more answer to this challenge: a later prompt for the approval replaces it, or the
|
|
70
|
+
* bank confirmed the approval, or the approval failed, timed out or was cancelled. Set where the client asks the bank
|
|
71
|
+
* by itself while the callback shows the challenge (`TanCallback`), `undefined` otherwise. It never rejects.
|
|
72
|
+
*/
|
|
73
|
+
resolved;
|
|
74
|
+
/**
|
|
75
|
+
* A challenge as the bank sent it, with a copy of `validUntil`, which no other challenge shares. Use
|
|
76
|
+
* `TanChallenge.create` to have a flicker code or image detected in the data.
|
|
77
|
+
*/
|
|
78
|
+
constructor(init) {
|
|
79
|
+
this.method = init.method;
|
|
80
|
+
this.reference = init.reference;
|
|
81
|
+
this.message = init.message;
|
|
82
|
+
this.hhdUc = init.hhdUc;
|
|
83
|
+
this.flickerCode = init.flickerCode;
|
|
84
|
+
this.image = init.image;
|
|
85
|
+
this.isDecoupled = init.isDecoupled ?? false;
|
|
86
|
+
this.validUntil = copyDate(init.validUntil);
|
|
87
|
+
this.tanMediumName = init.tanMediumName;
|
|
88
|
+
this.attempt = init.attempt ?? 1;
|
|
89
|
+
this.resolved = init.resolved;
|
|
90
|
+
}
|
|
91
|
+
/** Builds a challenge and detects a photoTAN image or chipTAN flicker code in `hhdUc` or `message`. */
|
|
92
|
+
static create(init) {
|
|
93
|
+
let image;
|
|
94
|
+
let flicker;
|
|
95
|
+
const data = init.hhdUc !== undefined && init.hhdUc.length > 0 ? init.hhdUc : undefined;
|
|
96
|
+
if (data !== undefined) {
|
|
97
|
+
image = TanImage.tryParse(data);
|
|
98
|
+
if (image === undefined)
|
|
99
|
+
flicker = FlickerCode.tryParse(latin1Decode(data));
|
|
100
|
+
}
|
|
101
|
+
else if (init.message !== undefined && hasEmbeddedFlickerCode(init.message)) {
|
|
102
|
+
flicker = FlickerCode.tryParse(init.message);
|
|
103
|
+
}
|
|
104
|
+
return new TanChallenge({
|
|
105
|
+
...init,
|
|
106
|
+
message: challengeText(init.message),
|
|
107
|
+
hhdUc: data,
|
|
108
|
+
flickerCode: flicker,
|
|
109
|
+
image,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Whether the user can report the approval in the banking app; `false` where only the bank's confirmation completes
|
|
114
|
+
* it, which `TanCallback` tells how to handle.
|
|
115
|
+
*/
|
|
116
|
+
get manualConfirmation() {
|
|
117
|
+
return !this.isDecoupled || this.method.decoupledManualConfirmation;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* `message` as the user reads it.
|
|
121
|
+
*
|
|
122
|
+
* A method with structured challenges (`TanMethod.structuredChallenge`) may use the formatting tags the specification
|
|
123
|
+
* defines. Line breaks, paragraphs and list items become new lines. Bold, italics, underlining and list tags are
|
|
124
|
+
* removed, and the entity references of such markup are resolved. Any other challenge stays as the bank sent it,
|
|
125
|
+
* which is how the specification wants it shown.
|
|
126
|
+
*
|
|
127
|
+
* Show it as text, such as with `textContent`, or `{{ }}` in Vue, and `white-space: pre-line` for its line breaks;
|
|
128
|
+
* never as HTML (`innerHTML`, `v-html`). It keeps the tags the specification does not define, and `<` becomes
|
|
129
|
+
* `<`, so that as HTML the bank's text could run scripts in the app.
|
|
130
|
+
*/
|
|
131
|
+
get plainMessage() {
|
|
132
|
+
const text = this.message;
|
|
133
|
+
if (text === undefined)
|
|
134
|
+
return undefined;
|
|
135
|
+
const instruction = challengeInstruction(text);
|
|
136
|
+
if (!this.method.structuredChallenge)
|
|
137
|
+
return instruction;
|
|
138
|
+
return instruction
|
|
139
|
+
.replace(lineBreak, '\n')
|
|
140
|
+
.replace(blockTag, '\n')
|
|
141
|
+
.replace(formattingTag, '')
|
|
142
|
+
.replace(entityReference, (match, name) => decodeEntity(name) ?? match)
|
|
143
|
+
.replace(blankLines, '\n\n')
|
|
144
|
+
.trim();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// Copies of a challenge for the prompts of one approval. Not methods of `TanChallenge`, so that they are not part of its
|
|
148
|
+
// API; they stand beside the class, so that a field added to it is added here as well.
|
|
149
|
+
/** `challenge` again, counted as the next prompt for the same approval, with the bank's new instructions `message`. */
|
|
150
|
+
export function nextAttempt(challenge, message) {
|
|
151
|
+
return copyChallenge(challenge, { attempt: challenge.attempt + 1, message: message ?? challenge.message });
|
|
152
|
+
}
|
|
153
|
+
/** `challenge` with `TanChallenge.resolved` set to `resolved`. */
|
|
154
|
+
export function withResolved(challenge, resolved) {
|
|
155
|
+
return copyChallenge(challenge, { resolved });
|
|
156
|
+
}
|
|
157
|
+
function copyChallenge(challenge, changes) {
|
|
158
|
+
return new TanChallenge({
|
|
159
|
+
method: challenge.method,
|
|
160
|
+
reference: challenge.reference,
|
|
161
|
+
message: challenge.message,
|
|
162
|
+
hhdUc: challenge.hhdUc,
|
|
163
|
+
flickerCode: challenge.flickerCode,
|
|
164
|
+
image: challenge.image,
|
|
165
|
+
isDecoupled: challenge.isDecoupled,
|
|
166
|
+
validUntil: challenge.validUntil,
|
|
167
|
+
tanMediumName: challenge.tanMediumName,
|
|
168
|
+
attempt: challenge.attempt,
|
|
169
|
+
resolved: challenge.resolved,
|
|
170
|
+
...changes,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const approvedAnswer = Object.freeze({ kind: 'approved' });
|
|
174
|
+
const cancelledAnswer = Object.freeze({ kind: 'cancelled' });
|
|
175
|
+
/** The answers a `TanCallback` returns. */
|
|
176
|
+
export const TanAnswer = Object.freeze({
|
|
177
|
+
/** The TAN the user entered; an empty one cancels a TAN challenge (`TanAnswer`). */
|
|
178
|
+
tan: (tan) => Object.freeze({ kind: 'tan', tan }),
|
|
179
|
+
/** The user reports the approval in the banking app (`TanChallenge.isDecoupled`); the client then asks the bank. */
|
|
180
|
+
approved: approvedAnswer,
|
|
181
|
+
/** The user cancels: the client ends the bank dialog, and the call fails with `FinTsTanCancelledError`. */
|
|
182
|
+
cancelled: cancelledAnswer,
|
|
183
|
+
});
|
|
184
|
+
//# 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,42 @@
|
|
|
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
|
+
/**
|
|
17
|
+
* Parses the challenge data sent by the bank; throws `FormatError` if it is not a flicker code a TAN generator can
|
|
18
|
+
* take: one with characters after the length it states, an empty data element, data other than printable ASCII, or
|
|
19
|
+
* more data than its length fields can state, such as more than 15 bytes in an element in HHD 1.3 or 255 bytes in all.
|
|
20
|
+
*/
|
|
21
|
+
static parse(code: string): FlickerCode;
|
|
22
|
+
/** Parses `code`, or returns `undefined` if it is not a flicker code. */
|
|
23
|
+
static tryParse(code: string): FlickerCode | undefined;
|
|
24
|
+
private static parseLayout;
|
|
25
|
+
/** Whether the code renders as the bank sent it: its data printable ASCII, every length within its field. */
|
|
26
|
+
private get fitsTanGenerator();
|
|
27
|
+
/** The data of the start code, which opens the challenge. */
|
|
28
|
+
get startCodeData(): string;
|
|
29
|
+
/** The first data element of the challenge; `undefined` if the code leaves it out. */
|
|
30
|
+
get dataElement1(): string | undefined;
|
|
31
|
+
/** The second data element of the challenge; `undefined` if the code leaves it out. */
|
|
32
|
+
get dataElement2(): string | undefined;
|
|
33
|
+
/** The third data element of the challenge; `undefined` if the code leaves it out. */
|
|
34
|
+
get dataElement3(): string | undefined;
|
|
35
|
+
/** The hex string transmitted to the TAN generator, including checksums. */
|
|
36
|
+
render(): string;
|
|
37
|
+
private payload;
|
|
38
|
+
private luhnChecksum;
|
|
39
|
+
/** Frames to animate: each is `[clock, bit0, bit1, bit2, bit3]` for the five bars. */
|
|
40
|
+
frames(): boolean[][];
|
|
41
|
+
}
|
|
42
|
+
//# 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;AA2BD,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;;;;OAIG;IACH,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;IA0B1B,6GAA6G;IAC7G,OAAO,KAAK,gBAAgB,GAG3B;IAED,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"}
|