@ekwo-ai/camt053 0.4.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ekwo AI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,240 @@
1
+ # `@ekwo-ai/camt053`
2
+
3
+ Reads an **ISO 20022 bank-to-customer statement** — `camt.053`, the account
4
+ statement banks deliver as XML — into plain objects, in TypeScript, with no
5
+ dependencies.
6
+
7
+ Give it the file and it gives you the statements in it, their lines, and the
8
+ list of what does not add up.
9
+
10
+ ```ts
11
+ import { readCamt053 } from '@ekwo-ai/camt053';
12
+
13
+ const { version, statements, violations } = readCamt053(bytesOrString);
14
+
15
+ for (const statement of statements) {
16
+ statement.account.identifier; // { kind: 'iban', value } or { kind: 'other', value, scheme, issuer }
17
+ statement.openingBalance; // { type: 'OPBD', amount: '1000.00', currency: 'EUR', date: '2026-03-01' }
18
+ statement.closingBalance;
19
+ statement.balanced; // true, false, or null when it could not be checked
20
+ for (const line of statement.lines) {
21
+ line.amount; // '-450.50' — signed, a decimal string, never a float
22
+ line.bookingDate; // '2026-03-05'
23
+ line.counterparty; // { name, account, agentBic, ultimateName }
24
+ line.remittance; // { unstructured: ['Invoice 2026-0107'], structured: [{ reference, type, issuer }] }
25
+ line.bankReference; // the bank's own reference for the movement
26
+ }
27
+ }
28
+ ```
29
+
30
+ It knows no database, no ledger and no country. The shape it returns is
31
+ declared in [`src/types.ts`](./src/types.ts) and imported from nowhere.
32
+
33
+ Every other brick beside this one *writes* a file an administration takes. This
34
+ one *reads* a file a bank sent, and that changes what has to be proved: not
35
+ that the output is valid, but that the input was understood — and that a file
36
+ written to hurt the reader does not.
37
+
38
+ ## What it does
39
+
40
+ - Reads **versions 02 to 14** of the message, and **says which one it read**
41
+ (`version`, `namespace`). The version is the last part of the namespace of
42
+ the document element, and nothing is assumed. The same invented statement is
43
+ written in each of the thirteen versions, held against the schema ISO
44
+ publishes for that version, and has to read to the same objects out of all
45
+ thirteen. A later version is still read and reported as
46
+ `version_not_verified`; version 01 is refused, because under the same name it
47
+ is another message.
48
+ - Returns **every statement** of the file — a message may carry several
49
+ accounts — and says on each violation which statement it is about.
50
+ - Identifies an account by **what the file gives**: an IBAN *or* `Othr/Id` with
51
+ its scheme and issuer. ISO 20022 offers the choice because half the world has
52
+ no IBAN; a reader that returned only IBANs would read half the statements.
53
+ - Signs every amount from the account holder's side — money in is positive, an
54
+ overdrawn balance is negative — and returns it as a **decimal string with the
55
+ digits the file wrote**. Sums are made on integers of hundred-thousandths
56
+ (the schema allows five decimals); no figure passes through a `number`.
57
+ - Checks that **opening balance + booked entries = closing balance**, to the
58
+ last decimal. When it is not, the statement comes back as the bank wrote it,
59
+ `balanced` is `false`, and `balance_mismatch` says the difference. **Nothing
60
+ is corrected**: a reader that adjusts a balance hides the one fact the check
61
+ exists to show — a truncated export, a missing page, a line lost on the way.
62
+ - Counts only **booked** entries (`BOOK`). A pending or informational entry is
63
+ returned, marked `booked: false`, and left out of the arithmetic, which is
64
+ what the bank did when it computed the closing balance.
65
+ - Keeps the **structured communication and the free one apart**, and keeps both
66
+ when a payment carries both. A creditor reference is something a payer copied
67
+ from an invoice and a machine can match; free text is something a person
68
+ typed. Folding one into the other throws away the difference reconciliation
69
+ lives on.
70
+ - **Splits a batch into its transactions** — one line per `TxDtls` — when, and
71
+ only when, every transaction carries an amount in the entry's currency and
72
+ they add up to the entry exactly. Each line then keeps its own references,
73
+ counterparty and communication, plus `entry`, `detail`, `detailCount` and
74
+ `entryAmount` to put the batch back together. Otherwise the entry stays one
75
+ line and `batch_not_split` says why. Either way the lines of a statement add
76
+ up to what moved on the account, so the balance check does not depend on the
77
+ choice. (Reconciliation wants the transaction; the balance wants the entry.
78
+ The split is made only where both get what they want.)
79
+ - Names the **counterparty** as the debtor of money coming in and the creditor
80
+ of money going out — and the other way round when `RvslInd` says the entry
81
+ undoes an earlier one, where the parties keep the roles they had in the
82
+ original: a transfer that bounced comes back *from* the party it was sent to.
83
+ - Takes the **day the bank wrote**. `2026-03-31T23:30:00+02:00` is 31 March; it
84
+ is not moved to UTC, which would book it on another day for half the planet.
85
+ - Returns the bank's own reference (`AcctSvcrRef`, of the transaction, else of
86
+ the entry), the entry reference, the end-to-end identifier **as written** —
87
+ including the `NOTPROVIDED` placeholder, which is the bank's word and not an
88
+ absence this reader should decide — the mandate, the bank transaction code
89
+ (ISO domain/family/sub-family and the proprietary one), the return reason,
90
+ and what was ordered in another currency with the rate, unconverted.
91
+
92
+ ## A statement is hostile input
93
+
94
+ It arrives by e-mail, from a download, from a third party's API. The XML reader
95
+ is written in this package, some three hundred lines, and is strict on purpose:
96
+
97
+ - **Any `<!DOCTYPE` is refused**, unread. No statement needs one; the
98
+ billion-laughs family and the external-entity family (XXE) both need one.
99
+ There is no entity table to expand and nothing that opens a file or a socket.
100
+ - The five predefined entities and numeric character references are decoded;
101
+ **any other entity is an error**, not an empty string. A character reference
102
+ that is not an XML character is an error.
103
+ - **Size** is checked before a character is read (`maxBytes`, 32 MiB by
104
+ default), **depth** and **element count** while reading (`maxDepth` 64,
105
+ `maxElements` 2,000,000). The reader does not recurse: a hundred thousand
106
+ nested elements are refused by the depth limit, and would not have touched
107
+ the stack.
108
+ - **UTF-8 only, decoded fatally.** A byte that is not UTF-8 is an error and not
109
+ a replacement character in somebody's name. A file that declares another
110
+ encoding is refused: decode it and pass the string.
111
+ - Namespaces are resolved, so `<Document xmlns="…">` and
112
+ `<ns2:Document xmlns:ns2="…">` are the same statement, and an element of
113
+ another namespace is never read as the statement's.
114
+ - Not well-formed is not read: mismatched tags, a second root, a bare `&`, a
115
+ repeated attribute, a control character.
116
+
117
+ It is not a general XML parser: element and attribute names are ASCII, which
118
+ every ISO 20022 name is, and nothing is validated against a schema at runtime.
119
+
120
+ ## What is thrown, and what comes back as a violation
121
+
122
+ **Thrown** (`StatementFileError`, with a `code`), because there is no statement
123
+ to report on:
124
+
125
+ | code | when |
126
+ |---|---|
127
+ | `too_large`, `too_deep`, `too_many_elements` | a limit was passed |
128
+ | `unsupported_encoding` | not UTF-8 |
129
+ | `doctype_forbidden`, `undefined_entity` | see above |
130
+ | `malformed_xml` | not well-formed |
131
+ | `not_a_statement` | well-formed, and not a camt.053 — a camt.052, an HTML error page |
132
+ | `unsupported_version` | camt.053.001.01 |
133
+ | `incomplete_statement` | no `Stmt`, or a statement without an identifier or an account |
134
+
135
+ **Returned** in `violations`, with the statement and the line it is on, because
136
+ one bad line should not cost the other three hundred:
137
+
138
+ | code | when |
139
+ |---|---|
140
+ | `balance_mismatch` | opening + booked entries ≠ closing; the message says the difference |
141
+ | `opening_balance_missing`, `closing_balance_missing` | no `OPBD` (or `PRCD`), no `CLBD`; the balance is not checked |
142
+ | `balance_currency_mismatch` | a balance in another currency than the account |
143
+ | `foreign_currency_entry` | an entry in another currency than the account — reported, **not converted**; the balance is not checked |
144
+ | `invalid_amount`, `invalid_direction`, `invalid_date` | the line comes back with `null` there |
145
+ | `booking_date_missing` | a booked entry without `BookgDt` (the schema allows it) |
146
+ | `invalid_iban` | written as an IBAN and failing ISO 13616's own check digits — returned as written |
147
+ | `duplicate_bank_reference`, `duplicate_entry_reference` | two entries of one statement under one reference |
148
+ | `batch_not_split`, `batch_count_mismatch` | see above |
149
+ | `version_not_verified` | a version this package has no schema for |
150
+
151
+ `invalid_amount`, `invalid_direction` and `invalid_date` are each something the
152
+ schema refuses: the tests put each one in a file and watch validation fail.
153
+ `balance_mismatch` is the opposite, and the reason this package exists — the
154
+ tests show the schema **accepting** a statement that does not add up.
155
+
156
+ ## What it does not do
157
+
158
+ - **It converts nothing.** An entry in another currency is reported; what was
159
+ ordered in dollars is returned in dollars beside what moved in the account's
160
+ currency. Rates belong to whoever keeps the books.
161
+ - **It matches nothing.** It does not know an invoice from a salary. It hands
162
+ over what reconciliation needs, separated.
163
+ - **It does not decide what a duplicate is.** Two lines with the same date,
164
+ amount and counterparty are two lines; whether a file was already imported is
165
+ a question for whoever holds the previous ones.
166
+ - **It does not read every element.** Charges, interest, tax, securities and
167
+ card details, availability, and the postal address of a party are not
168
+ returned. They are skipped, not misread.
169
+ - **It does not read camt.052 or camt.054.** The intraday report and the
170
+ debit/credit notification share most of their types with the statement, and
171
+ the reading would be close to the same — but a report has no closing booked
172
+ balance to check and a notification has no balance at all, so they are other
173
+ documents with other guarantees, and they are refused by name until someone
174
+ needs them.
175
+ - **It reads no paginated statement as one.** `StmtPgntn` is returned (`page`);
176
+ a statement delivered over several files opens and closes each page on
177
+ interim balances (`ITBD`), which this package returns in `balances` and does
178
+ not take for an opening or a closing. Such a page reports
179
+ `opening_balance_missing` rather than guessing.
180
+
181
+ ## Sources
182
+
183
+ - ISO 20022, *Bank-to-Customer Cash Management*, message
184
+ `BankToCustomerStatement` (`camt.053.001.02` to `.14`): the schemas under
185
+ [`test/xsd/`](./test/xsd/README.md), from the ISO 20022 message catalogue and
186
+ archive, <https://www.iso20022.org/iso-20022-message-definitions>.
187
+ - ISO 13616 — the IBAN and its check digits (ISO 7064, MOD 97-10).
188
+ - ISO 11649 — the structured creditor reference (`RF…`), which this package
189
+ returns and does not validate.
190
+ - Extensible Markup Language (XML) 1.0, Fifth Edition, and Namespaces in XML
191
+ 1.0 — <https://www.w3.org/TR/xml/>, <https://www.w3.org/TR/xml-names/>.
192
+
193
+ ## What is verified, and what is not
194
+
195
+ **Verified**: every file this package is tested on is first held against the
196
+ schema ISO publishes for its version (`test/schema.test.ts`, thirteen schemas,
197
+ unmodified under `test/xsd/`, with no network and no system tool) — so the
198
+ reader is tested on camt.053, not on its author's idea of camt.053. One
199
+ statement is written in all thirteen versions and reads to the same objects.
200
+ The violations that are schema violations are shown to be refused by the
201
+ schema; the one that matters most, the balance, is shown **not** to be. The
202
+ hostile files are each refused by name, and the two that ask for time or
203
+ memory are refused within a bound the test measures.
204
+
205
+ Writing the fixtures against the schemas refuted the first draft once, the same
206
+ hour: `BIC` became `BICFI` in version 03, not in 04 as the draft had it. And
207
+ fetching the schemas found two versions the draft did not know: 14, published
208
+ in March 2026, and 01, which is served under the same name and is another
209
+ message.
210
+
211
+ **Not verified** — and this is the larger half:
212
+
213
+ - **No file from a bank.** Every statement here is invented, because a real one
214
+ is somebody's account. The schema says what a bank *may* send; it does not
215
+ say what any bank *does* send. Banks differ on exactly the things this
216
+ package cares about: whether a batch is detailed, where the reference is
217
+ (`AcctSvcrRef` on the entry, on the transaction, on both, on neither),
218
+ whether the counterparty of a card payment is a party or a line of free text,
219
+ what `PRCD` means on a Monday. *A reading that passes on invented files is an
220
+ indication, not a proof*, and the first real statement from each bank is a
221
+ test nobody has run.
222
+ - **No usage guideline.** EPC, CGI-MP, and the implementation guides of
223
+ national banking federations restrict the message — which elements are
224
+ filled, with which codes. None is tested.
225
+ - **The counterparty of a reversal.** That the parties keep their original
226
+ roles when `RvslInd` is true is this package's reading of the message
227
+ definition and of common practice; the schema cannot say, and banks may
228
+ differ.
229
+ - **Versions 02 and 08 are the ones in use.** The eleven others are verified
230
+ against their schemas and, as far as the authors know, against nothing else:
231
+ nobody here has seen a camt.053.001.11 in the wild.
232
+ - **The external code sets** (bank transaction codes, return reasons, balance
233
+ types) are returned as written and not checked against ISO's published
234
+ lists.
235
+ - **Performance** is bounded, not measured: a statement at the default size
236
+ limit is read in memory, as a whole.
237
+
238
+ ## Licence
239
+
240
+ MIT. See [`LICENSE`](./LICENSE).
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Amounts as the file wrote them, added without a float.
3
+ *
4
+ * ISO 20022 amounts are `xs:decimal` with at most five fraction digits and
5
+ * eighteen digits in all, and unsigned: the sign is a separate element. A
6
+ * statement's arithmetic is a sum of those, and a sum of binary floats is not
7
+ * the sum the bank computed. Everything here is a `bigint` of hundred-
8
+ * thousandths, and what leaves is a string.
9
+ */
10
+ /** An unsigned ISO 20022 amount in hundred-thousandths, or null if it is not one. */
11
+ export declare function parseUnsigned(text: string): bigint | null;
12
+ /**
13
+ * The amount as a decimal string: signed, no exponent, no leading zeros, and
14
+ * the fraction digits the file wrote — never fewer than it wrote, so that
15
+ * `100.00` does not come back as `100`, and never more.
16
+ */
17
+ export declare function signedText(text: string, negative: boolean): string;
18
+ /** Hundred-thousandths back to a decimal string, with at least two decimals. */
19
+ export declare function formatScaled(value: bigint): string;
20
+ //# sourceMappingURL=decimal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decimal.d.ts","sourceRoot":"","sources":["../src/decimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,qFAAqF;AACrF,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUzD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAMlE;AAED,gFAAgF;AAChF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Amounts as the file wrote them, added without a float.
3
+ *
4
+ * ISO 20022 amounts are `xs:decimal` with at most five fraction digits and
5
+ * eighteen digits in all, and unsigned: the sign is a separate element. A
6
+ * statement's arithmetic is a sum of those, and a sum of binary floats is not
7
+ * the sum the bank computed. Everything here is a `bigint` of hundred-
8
+ * thousandths, and what leaves is a string.
9
+ */
10
+ const SCALE = 5;
11
+ const UNSIGNED = /^\+?([0-9]*)(?:\.([0-9]*))?$/;
12
+ /** An unsigned ISO 20022 amount in hundred-thousandths, or null if it is not one. */
13
+ export function parseUnsigned(text) {
14
+ const match = UNSIGNED.exec(text.trim());
15
+ if (!match)
16
+ return null;
17
+ const whole = match[1] ?? '';
18
+ const fraction = match[2] ?? '';
19
+ if (whole === '' && fraction === '')
20
+ return null;
21
+ if (fraction.length > SCALE || whole.replace(/^0+/, '').length + fraction.length > 18) {
22
+ return null;
23
+ }
24
+ return BigInt((whole === '' ? '0' : whole) + fraction.padEnd(SCALE, '0'));
25
+ }
26
+ /**
27
+ * The amount as a decimal string: signed, no exponent, no leading zeros, and
28
+ * the fraction digits the file wrote — never fewer than it wrote, so that
29
+ * `100.00` does not come back as `100`, and never more.
30
+ */
31
+ export function signedText(text, negative) {
32
+ const match = UNSIGNED.exec(text.trim());
33
+ const whole = (match?.[1] ?? '').replace(/^0+(?=[0-9])/, '') || '0';
34
+ const fraction = match?.[2] ?? '';
35
+ const unsigned = fraction === '' ? whole : `${whole}.${fraction}`;
36
+ return negative && /[1-9]/.test(unsigned) ? `-${unsigned}` : unsigned;
37
+ }
38
+ /** Hundred-thousandths back to a decimal string, with at least two decimals. */
39
+ export function formatScaled(value) {
40
+ const negative = value < 0n;
41
+ const digits = (negative ? -value : value).toString().padStart(SCALE + 1, '0');
42
+ const whole = digits.slice(0, -SCALE);
43
+ const fraction = digits.slice(-SCALE).replace(/0{1,3}$/, '');
44
+ return `${negative ? '-' : ''}${whole}.${fraction}`;
45
+ }
46
+ //# sourceMappingURL=decimal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decimal.js","sourceRoot":"","sources":["../src/decimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,KAAK,GAAG,CAAC,CAAC;AAChB,MAAM,QAAQ,GAAG,8BAA8B,CAAC;AAEhD,qFAAqF;AACrF,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAChC,IAAI,KAAK,KAAK,EAAE,IAAI,QAAQ,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACjD,IAAI,QAAQ,CAAC,MAAM,GAAG,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACtF,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,QAAiB;IACxD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC;IACpE,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;IAClE,OAAO,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACxE,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,MAAM,QAAQ,GAAG,KAAK,GAAG,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,KAAK,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC;IAC/E,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC7D,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;AACtD,CAAC"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * What makes a file unreadable, as opposed to a statement that reads and does
3
+ * not add up. The second comes back in `violations`; the first is thrown,
4
+ * because there is no statement to hang a violation on.
5
+ */
6
+ export type StatementFileErrorCode =
7
+ /** Larger than `maxBytes`. Refused before a single character is read. */
8
+ 'too_large'
9
+ /** Bytes that are not UTF-8, or a declaration that names another encoding. */
10
+ | 'unsupported_encoding'
11
+ /** A `<!DOCTYPE`. No statement needs one, and every entity attack does. */
12
+ | 'doctype_forbidden'
13
+ /** An entity other than the five XML predefines and character references. */
14
+ | 'undefined_entity'
15
+ /** Elements nested deeper than `maxDepth`. */
16
+ | 'too_deep'
17
+ /** More elements than `maxElements`. */
18
+ | 'too_many_elements'
19
+ /** Not well-formed XML, in the subset this reader takes. */
20
+ | 'malformed_xml'
21
+ /** Well-formed, and not a camt.053: another root, another namespace. */
22
+ | 'not_a_statement'
23
+ /** camt.053.001.01, the 2007 message: same name, another shape. */
24
+ | 'unsupported_version'
25
+ /** A camt.053 without the parts that make it one: no statement, no account. */
26
+ | 'incomplete_statement';
27
+ export declare class StatementFileError extends Error {
28
+ readonly code: StatementFileErrorCode;
29
+ constructor(code: StatementFileErrorCode, message: string);
30
+ }
31
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,sBAAsB;AAChC,yEAAyE;AACvE,WAAW;AACb,8EAA8E;GAC5E,sBAAsB;AACxB,2EAA2E;GACzE,mBAAmB;AACrB,6EAA6E;GAC3E,kBAAkB;AACpB,8CAA8C;GAC5C,UAAU;AACZ,wCAAwC;GACtC,mBAAmB;AACrB,4DAA4D;GAC1D,eAAe;AACjB,wEAAwE;GACtE,iBAAiB;AACnB,mEAAmE;GACjE,qBAAqB;AACvB,+EAA+E;GAC7E,sBAAsB,CAAC;AAE3B,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,QAAQ,CAAC,IAAI,EAAE,sBAAsB,CAAC;gBAE1B,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM;CAK1D"}
package/dist/errors.js ADDED
@@ -0,0 +1,9 @@
1
+ export class StatementFileError extends Error {
2
+ code;
3
+ constructor(code, message) {
4
+ super(message);
5
+ this.name = 'StatementFileError';
6
+ this.code = code;
7
+ }
8
+ }
9
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA2BA,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAClC,IAAI,CAAyB;IAEtC,YAAY,IAA4B,EAAE,OAAe;QACvD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ import type { ReadOptions, StatementFile } from './types.js';
2
+ export { StatementFileError, type StatementFileErrorCode } from './errors.js';
3
+ export type * from './types.js';
4
+ /**
5
+ * The versions of camt.053 this package's tests hold against the schema ISO
6
+ * publishes for each: a fixture per version validates, and is read. Another
7
+ * version of the same message is still read — the elements this package takes
8
+ * have kept their names since 2009 — and reported as `version_not_verified`.
9
+ */
10
+ export declare const VERIFIED_VERSIONS: readonly string[];
11
+ /** ISO 13616: move four characters to the end, letters to numbers, mod 97 is 1. */
12
+ export declare function isValidIban(iban: string): boolean;
13
+ /**
14
+ * Read a camt.053 file — a string, or the bytes as they came — into its
15
+ * statements, their lines, and what does not add up.
16
+ *
17
+ * Throws a `StatementFileError` for what is not a readable camt.053 at all.
18
+ * Everything else comes back, with the trouble named in `violations` and
19
+ * nothing silently repaired: a closing balance that does not follow from the
20
+ * opening one is returned as the bank wrote it, and reported.
21
+ */
22
+ export declare function readCamt053(input: string | Uint8Array, options?: ReadOptions): StatementFile;
23
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,WAAW,EAIX,aAAa,EAId,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC9E,mBAAmB,YAAY,CAAC;AAEhC;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAc9C,CAAC;AAgEF,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAUjD;AAgYD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,WAAgB,GAAG,aAAa,CAoChG"}