@ekwo-ai/xaf 0.9.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 +21 -0
- package/README.md +80 -0
- package/dist/decimal.d.ts +18 -0
- package/dist/decimal.d.ts.map +1 -0
- package/dist/decimal.js +39 -0
- package/dist/decimal.js.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +12 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +50 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +272 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +98 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +16 -0
- package/dist/types.js.map +1 -0
- package/dist/xml.d.ts +47 -0
- package/dist/xml.d.ts.map +1 -0
- package/dist/xml.js +303 -0
- package/dist/xml.js.map +1 -0
- package/package.json +56 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Karuna Co OÜ, trading as Ekwo
|
|
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,80 @@
|
|
|
1
|
+
# `@ekwo-ai/xaf`
|
|
2
|
+
|
|
3
|
+
Reads an **XML Audit File Financial** — the *XML Auditfile Financieel*, XAF,
|
|
4
|
+
the file an accounting package hands over when an auditor or a tax authority
|
|
5
|
+
asks for the books of a year — into plain objects, in TypeScript, with no
|
|
6
|
+
dependencies. Versions **3.2** and **4.0** are read, each recognised by the
|
|
7
|
+
namespace of its schema.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { readXaf } from '@ekwo-ai/xaf';
|
|
11
|
+
|
|
12
|
+
const books = readXaf(bytes); // a string, or the bytes of the file
|
|
13
|
+
|
|
14
|
+
books.version; // '4.0'
|
|
15
|
+
books.currency; // 'EUR', from header/curCode
|
|
16
|
+
books.entries; // [{ journal: 'VK', number: '2025001', date: '2025-01-15', lines: […] }, …]
|
|
17
|
+
books.opening; // the obLine of the opening balance
|
|
18
|
+
books.openingDate; // opBalDate (3.2), or the first day of the year (4.0)
|
|
19
|
+
books.violations; // a transaction that does not balance, totals the lines disagree with
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## What is read
|
|
23
|
+
|
|
24
|
+
| Part of the books | Where it sits in the file |
|
|
25
|
+
|---|---|
|
|
26
|
+
| currency | `header/curCode` |
|
|
27
|
+
| accounts | `company/generalLedger/ledgerAccount`: `accID`, `accDesc`, `accTp` (`B`, `P`, `M`, kept as written) |
|
|
28
|
+
| parties | `company/customersSuppliers/customerSupplier`: `custSupID`, `custSupName`, `eMail`, `taxRegIdent`, `commerceNr`, the country of its first address |
|
|
29
|
+
| opening balance | `company/openingBalance/obLine`: `accID`, `amnt`, `amntTp`; its date `opBalDate` in 3.2, `header/startDate` in 4.0, which has none |
|
|
30
|
+
| entries | `company/transactions/journal` (`jrnID`, `desc`) `/transaction` (`nr`, `desc`, `trDt`) |
|
|
31
|
+
| lines | `trLine`: `accID`, `desc`, `amnt` and `amntTp` (`D` or `C`), `custSupID`, `docRef` (the first one is the entry's reference), `currency/curCode` and `curAmnt`, and in 3.2 `matchKeyID` |
|
|
32
|
+
|
|
33
|
+
- An amount is an `xsd:decimal`; its side is `amntTp`. A negative amount is
|
|
34
|
+
the other side.
|
|
35
|
+
- The file states its own totals — `linesCount`, `totalDebit`, `totalCredit` —
|
|
36
|
+
for the opening balance and for the transactions, and each is held against
|
|
37
|
+
what the lines add up to. A file that disagrees with itself, or a transaction
|
|
38
|
+
that does not balance, comes back as written, with a violation.
|
|
39
|
+
- The encoding is the one the XML declaration names — UTF-8 or ISO-8859-1 —
|
|
40
|
+
and UTF-8 when it names none, as XML provides. A caller who names another
|
|
41
|
+
is refused rather than obeyed.
|
|
42
|
+
- The XML is read by a strict reader of its own: no DOCTYPE, no entity beyond
|
|
43
|
+
the five XML predefines, bounds on size, depth and element count.
|
|
44
|
+
|
|
45
|
+
What is not read: the sub-ledgers of version 3.2 (`subledgers`,
|
|
46
|
+
`obSubledgers`), which restate by party what the lines already say; the VAT
|
|
47
|
+
codes and the tax amounts of a line; the periods; the history of changes.
|
|
48
|
+
|
|
49
|
+
## What it returns
|
|
50
|
+
|
|
51
|
+
The shape declared in [`src/types.ts`](./src/types.ts), which is, field for
|
|
52
|
+
field, the shape every reader of an accounting export in this repository
|
|
53
|
+
declares for itself, plus the `version` of the file and the `openingDate` of
|
|
54
|
+
its opening balance. Amounts are decimal strings, never negative. What is not
|
|
55
|
+
an audit file is thrown as a `BooksFileError` with a `code`.
|
|
56
|
+
|
|
57
|
+
It knows no chart of accounts: in [Ekwo OS](https://github.com/Ekwo-ai/ekwo-os)
|
|
58
|
+
the correspondence is made by `ekwo import xaf <file>`.
|
|
59
|
+
|
|
60
|
+
## Sources
|
|
61
|
+
|
|
62
|
+
The format was read on 24 September 2026 from the schemas the Dutch tax
|
|
63
|
+
administration publishes on its developer portal — `XmlAuditfileFinancieel3.2.xsd`
|
|
64
|
+
and `XmlAuditfileFinancieel4.0.xsd`, kept under [`test/xsd/`](./test/xsd/README.md)
|
|
65
|
+
with where each came from — and from the pages of that portal:
|
|
66
|
+
[Auditfile Financieel (XAF) v 3.2.1](https://odb.belastingdienst.nl/auditfiles/auditfile-financieel-xaf-v-3-2-1/),
|
|
67
|
+
[XMLAuditfile Financieel (XAF) v 4.0.3](https://odb.belastingdienst.nl/auditfiles/xmlauditfile-financieel-xaf-v-4-0-3/),
|
|
68
|
+
[Achtergrond informatie XAF4.0](https://odb.belastingdienst.nl/auditfiles/achtergrond-informatie-xaf4-0/).
|
|
69
|
+
|
|
70
|
+
It was written against the audit file **Exact Online** exports, as its help
|
|
71
|
+
centre describes it on the same day:
|
|
72
|
+
[Auditfiles in xml-formaat (XAF) exporteren](https://support.exactonline.com/community/s/article/All-All-HNO-Task-general-importexport-gen-impexp-auditt?language=nl_NL)
|
|
73
|
+
— *Import/Export > Export > Audit file*; entries not yet processed are not in
|
|
74
|
+
the file. `ekwo import exact-online <file>` is the same as `ekwo import xaf`.
|
|
75
|
+
|
|
76
|
+
The fixtures under `test/fixtures/`, one per version, are written by hand, hold
|
|
77
|
+
no real data, and are validated against the schema of their version by
|
|
78
|
+
`test/schema.test.ts`.
|
|
79
|
+
|
|
80
|
+
MIT.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amounts as the file wrote them, added without a float.
|
|
3
|
+
*
|
|
4
|
+
* An audit file writes its amounts as `xsd:decimal` with two fraction digits
|
|
5
|
+
* at most, the side in an element of its own. An amount is held here as a
|
|
6
|
+
* `bigint` of millionths — the unit every reader of books in this repository
|
|
7
|
+
* adds in — and what leaves is a string with two decimals, and more when the
|
|
8
|
+
* file wrote more that are not zero.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* An `xsd:decimal` as millionths, or null when it is not one: an optional
|
|
12
|
+
* sign, digits, an optional point and digits. No exponent, no thousands
|
|
13
|
+
* separator, no comma — the lexical space of the type, and nothing else.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseXsdDecimal(text: string): bigint | null;
|
|
16
|
+
/** Millionths as a decimal string: signed, two decimals, more when they are written. */
|
|
17
|
+
export declare function formatDecimal(value: bigint): string;
|
|
18
|
+
//# sourceMappingURL=decimal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decimal.d.ts","sourceRoot":"","sources":["../src/decimal.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS3D;AAED,wFAAwF;AACxF,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMnD"}
|
package/dist/decimal.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amounts as the file wrote them, added without a float.
|
|
3
|
+
*
|
|
4
|
+
* An audit file writes its amounts as `xsd:decimal` with two fraction digits
|
|
5
|
+
* at most, the side in an element of its own. An amount is held here as a
|
|
6
|
+
* `bigint` of millionths — the unit every reader of books in this repository
|
|
7
|
+
* adds in — and what leaves is a string with two decimals, and more when the
|
|
8
|
+
* file wrote more that are not zero.
|
|
9
|
+
*/
|
|
10
|
+
const SCALE = 6;
|
|
11
|
+
const UNIT = 10n ** BigInt(SCALE);
|
|
12
|
+
const XSD_DECIMAL = /^([+-])?([0-9]*)(?:\.([0-9]*))?$/;
|
|
13
|
+
/**
|
|
14
|
+
* An `xsd:decimal` as millionths, or null when it is not one: an optional
|
|
15
|
+
* sign, digits, an optional point and digits. No exponent, no thousands
|
|
16
|
+
* separator, no comma — the lexical space of the type, and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
export function parseXsdDecimal(text) {
|
|
19
|
+
const match = XSD_DECIMAL.exec(text.trim());
|
|
20
|
+
if (match === null)
|
|
21
|
+
return null;
|
|
22
|
+
const whole = match[2] ?? '';
|
|
23
|
+
const fraction = match[3] ?? '';
|
|
24
|
+
if (whole === '' && fraction === '')
|
|
25
|
+
return null;
|
|
26
|
+
if (fraction.length > SCALE)
|
|
27
|
+
return null;
|
|
28
|
+
const units = BigInt(whole === '' ? '0' : whole) * UNIT + BigInt(fraction.padEnd(SCALE, '0') || '0');
|
|
29
|
+
return match[1] === '-' ? -units : units;
|
|
30
|
+
}
|
|
31
|
+
/** Millionths as a decimal string: signed, two decimals, more when they are written. */
|
|
32
|
+
export function formatDecimal(value) {
|
|
33
|
+
const negative = value < 0n;
|
|
34
|
+
const digits = (negative ? -value : value).toString().padStart(SCALE + 1, '0');
|
|
35
|
+
const whole = digits.slice(0, -SCALE);
|
|
36
|
+
const fraction = digits.slice(-SCALE).replace(/0+$/, '').padEnd(2, '0');
|
|
37
|
+
return `${negative ? '-' : ''}${whole}.${fraction}`;
|
|
38
|
+
}
|
|
39
|
+
//# 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,IAAI,GAAG,GAAG,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;AAClC,MAAM,WAAW,GAAG,kCAAkC,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,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;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,GAAG,CAAC,CAAC;IACrG,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;AAC3C,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,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,KAAK,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACxE,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;AACtD,CAAC"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What makes a file unreadable, as opposed to books that read and do not add
|
|
3
|
+
* up. The second comes back in `violations`; the first is thrown, because a
|
|
4
|
+
* file that is not an audit file has no line to hang a violation on.
|
|
5
|
+
*/
|
|
6
|
+
export type BooksFileErrorCode =
|
|
7
|
+
/** Larger than `maxBytes`. Refused before a single character is read. */
|
|
8
|
+
'too_large'
|
|
9
|
+
/** Bytes that are not the encoding the declaration names, or a caller who names another one. */
|
|
10
|
+
| 'unsupported_encoding'
|
|
11
|
+
/** A `<!DOCTYPE`. No audit file 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 an audit file ever nests them. */
|
|
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 an audit file: another root, another namespace. */
|
|
22
|
+
| 'not_an_auditfile'
|
|
23
|
+
/** An audit file of a version this reader was not written against. */
|
|
24
|
+
| 'unsupported_version'
|
|
25
|
+
/** An element the schema requires, missing. */
|
|
26
|
+
| 'missing_element'
|
|
27
|
+
/** A value the schema does not allow where it is: a date, an amount, a side. */
|
|
28
|
+
| 'invalid_value';
|
|
29
|
+
export declare class BooksFileError extends Error {
|
|
30
|
+
readonly code: BooksFileErrorCode;
|
|
31
|
+
/** Kept for the shape the other readers share; an XML file has no rows. */
|
|
32
|
+
readonly row: number | null;
|
|
33
|
+
constructor(code: BooksFileErrorCode, message: string);
|
|
34
|
+
}
|
|
35
|
+
//# 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,kBAAkB;AAC5B,yEAAyE;AACvE,WAAW;AACb,gGAAgG;GAC9F,sBAAsB;AACxB,4EAA4E;GAC1E,mBAAmB;AACrB,6EAA6E;GAC3E,kBAAkB;AACpB,iEAAiE;GAC/D,UAAU;AACZ,wCAAwC;GACtC,mBAAmB;AACrB,4DAA4D;GAC1D,eAAe;AACjB,2EAA2E;GACzE,kBAAkB;AACpB,sEAAsE;GACpE,qBAAqB;AACvB,+CAA+C;GAC7C,iBAAiB;AACnB,gFAAgF;GAC9E,eAAe,CAAC;AAEpB,qBAAa,cAAe,SAAQ,KAAK;IACvC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,2EAA2E;IAC3E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;gBAEhB,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM;CAMtD"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export class BooksFileError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
/** Kept for the shape the other readers share; an XML file has no rows. */
|
|
4
|
+
row;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'BooksFileError';
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.row = null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AA6BA,MAAM,OAAO,cAAe,SAAQ,KAAK;IAC9B,IAAI,CAAqB;IAClC,2EAA2E;IAClE,GAAG,CAAgB;IAE5B,YAAY,IAAwB,EAAE,OAAe;QACnD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;QAC7B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;IAClB,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XML Audit File Financial — the *XML Auditfile Financieel*, XAF — read
|
|
3
|
+
* back: the file an accounting package hands over when an auditor or a tax
|
|
4
|
+
* authority asks for the books of a year, in the XML the published schema
|
|
5
|
+
* defines. Versions 3.2 and 4.0 are read, each recognised by its namespace;
|
|
6
|
+
* any other is refused by name.
|
|
7
|
+
*
|
|
8
|
+
* What is taken from it, and where it sits in the file:
|
|
9
|
+
*
|
|
10
|
+
* - the **currency** of the books, `header/curCode`;
|
|
11
|
+
* - the **accounts**, `company/generalLedger/ledgerAccount` — `accID`,
|
|
12
|
+
* `accDesc`, and `accTp` kept as written;
|
|
13
|
+
* - the **parties**, `company/customersSuppliers/customerSupplier`, by their
|
|
14
|
+
* `custSupID`;
|
|
15
|
+
* - the **opening balance**, `company/openingBalance/obLine`;
|
|
16
|
+
* - the **entries**, `company/transactions/journal/transaction`, each with its
|
|
17
|
+
* lines, `trLine`: an account, an amount and its side (`amnt`, `amntTp`),
|
|
18
|
+
* a party, an amount in another currency.
|
|
19
|
+
*
|
|
20
|
+
* The file states its own totals — the number of lines, the debits and the
|
|
21
|
+
* credits of the opening and of the transactions — and each is held against
|
|
22
|
+
* what its lines add up to: a file that disagrees with itself comes back with
|
|
23
|
+
* a violation, as written.
|
|
24
|
+
*
|
|
25
|
+
* It depends on nothing, reads no database and knows no chart of accounts.
|
|
26
|
+
*/
|
|
27
|
+
import type { ImportedBooks, ReadOptions } from './types.js';
|
|
28
|
+
export * from './types.js';
|
|
29
|
+
export { BooksFileError, type BooksFileErrorCode } from './errors.js';
|
|
30
|
+
/** The versions read, by the namespace their schema declares. */
|
|
31
|
+
export declare const XAF_NAMESPACES: {
|
|
32
|
+
readonly '3.2': "http://www.auditfiles.nl/XAF/3.2";
|
|
33
|
+
readonly '4.0': "http://www.odb.belastingdienst.nl/Belastingdienst/BCPP/1.1/structures/XmlauditfileXAF_4.0";
|
|
34
|
+
};
|
|
35
|
+
export type XafVersion = keyof typeof XAF_NAMESPACES;
|
|
36
|
+
/** The books of an audit file, and the version it was written in. */
|
|
37
|
+
export interface XafBooks extends ImportedBooks {
|
|
38
|
+
version: XafVersion;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Reads an audit file, as a string or as the bytes of the file. Entries are
|
|
42
|
+
* the `transaction` elements of each `journal`; the opening balance is the
|
|
43
|
+
* `obLine` elements, which carry no date of their own — the version 3.2 gives
|
|
44
|
+
* one, `opBalDate`, which `openingDate` returns, and the version 4.0 opens on
|
|
45
|
+
* `header/startDate`.
|
|
46
|
+
*/
|
|
47
|
+
export declare function readXaf(input: string | Uint8Array, options?: ReadOptions): XafBooks & {
|
|
48
|
+
openingDate: string | null;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAIH,OAAO,KAAK,EAEV,aAAa,EAIb,WAAW,EAEZ,MAAM,YAAY,CAAC;AAGpB,cAAc,YAAY,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAItE,iEAAiE;AACjE,eAAO,MAAM,cAAc;;;CAGjB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,cAAc,CAAC;AAErD,qEAAqE;AACrE,MAAM,WAAW,QAAS,SAAQ,aAAa;IAC7C,OAAO,EAAE,UAAU,CAAC;CACrB;AA4ED;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,OAAO,GAAE,WAAgB,GAAG,QAAQ,GAAG;IAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA6KxH"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The XML Audit File Financial — the *XML Auditfile Financieel*, XAF — read
|
|
3
|
+
* back: the file an accounting package hands over when an auditor or a tax
|
|
4
|
+
* authority asks for the books of a year, in the XML the published schema
|
|
5
|
+
* defines. Versions 3.2 and 4.0 are read, each recognised by its namespace;
|
|
6
|
+
* any other is refused by name.
|
|
7
|
+
*
|
|
8
|
+
* What is taken from it, and where it sits in the file:
|
|
9
|
+
*
|
|
10
|
+
* - the **currency** of the books, `header/curCode`;
|
|
11
|
+
* - the **accounts**, `company/generalLedger/ledgerAccount` — `accID`,
|
|
12
|
+
* `accDesc`, and `accTp` kept as written;
|
|
13
|
+
* - the **parties**, `company/customersSuppliers/customerSupplier`, by their
|
|
14
|
+
* `custSupID`;
|
|
15
|
+
* - the **opening balance**, `company/openingBalance/obLine`;
|
|
16
|
+
* - the **entries**, `company/transactions/journal/transaction`, each with its
|
|
17
|
+
* lines, `trLine`: an account, an amount and its side (`amnt`, `amntTp`),
|
|
18
|
+
* a party, an amount in another currency.
|
|
19
|
+
*
|
|
20
|
+
* The file states its own totals — the number of lines, the debits and the
|
|
21
|
+
* credits of the opening and of the transactions — and each is held against
|
|
22
|
+
* what its lines add up to: a file that disagrees with itself comes back with
|
|
23
|
+
* a violation, as written.
|
|
24
|
+
*
|
|
25
|
+
* It depends on nothing, reads no database and knows no chart of accounts.
|
|
26
|
+
*/
|
|
27
|
+
import { formatDecimal, parseXsdDecimal } from './decimal.js';
|
|
28
|
+
import { BooksFileError } from './errors.js';
|
|
29
|
+
import { parseXml } from './xml.js';
|
|
30
|
+
export * from './types.js';
|
|
31
|
+
export { BooksFileError } from './errors.js';
|
|
32
|
+
const DEFAULTS = { maxBytes: 256 * 1024 * 1024, maxElements: 20_000_000, maxDepth: 32 };
|
|
33
|
+
/** The versions read, by the namespace their schema declares. */
|
|
34
|
+
export const XAF_NAMESPACES = {
|
|
35
|
+
'3.2': 'http://www.auditfiles.nl/XAF/3.2',
|
|
36
|
+
'4.0': 'http://www.odb.belastingdienst.nl/Belastingdienst/BCPP/1.1/structures/XmlauditfileXAF_4.0',
|
|
37
|
+
};
|
|
38
|
+
/** The element children of `parent` called `name`, in its namespace. */
|
|
39
|
+
function all(parent, name) {
|
|
40
|
+
return parent === undefined ? [] : parent.children.filter((child) => child.name === name && child.namespace === parent.namespace);
|
|
41
|
+
}
|
|
42
|
+
function one(parent, name) {
|
|
43
|
+
return all(parent, name)[0];
|
|
44
|
+
}
|
|
45
|
+
/** The trimmed text of the child `name`, or '' where there is none. */
|
|
46
|
+
function text(parent, name) {
|
|
47
|
+
return one(parent, name)?.text.trim() ?? '';
|
|
48
|
+
}
|
|
49
|
+
function required(parent, name, where) {
|
|
50
|
+
const value = text(parent, name);
|
|
51
|
+
if (value === '')
|
|
52
|
+
throw new BooksFileError('missing_element', `${where} has no ${name}, which the schema requires`);
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
/** An `xsd:date`, its time zone set aside, checked against the calendar. */
|
|
56
|
+
function isoDate(value, where) {
|
|
57
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})(?:Z|[+-]\d{2}:\d{2})?$/.exec(value);
|
|
58
|
+
if (match === null)
|
|
59
|
+
throw new BooksFileError('invalid_value', `${where}: "${value}" is not an xsd:date, YYYY-MM-DD`);
|
|
60
|
+
const iso = `${match[1]}-${match[2]}-${match[3]}`;
|
|
61
|
+
const parsed = new Date(`${iso}T00:00:00Z`);
|
|
62
|
+
if (Number.isNaN(parsed.getTime()) || parsed.toISOString().slice(0, 10) !== iso) {
|
|
63
|
+
throw new BooksFileError('invalid_value', `${where}: ${value} is not a day of the calendar`);
|
|
64
|
+
}
|
|
65
|
+
return iso;
|
|
66
|
+
}
|
|
67
|
+
function amountOf(value, where) {
|
|
68
|
+
const parsed = parseXsdDecimal(value);
|
|
69
|
+
if (parsed === null)
|
|
70
|
+
throw new BooksFileError('invalid_value', `${where}: "${value}" is not an amount`);
|
|
71
|
+
return parsed;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The debit and the credit of an amount and its side. A negative amount — the
|
|
75
|
+
* schema's decimal allows a sign — is the other side, as it is everywhere
|
|
76
|
+
* else in this repository.
|
|
77
|
+
*/
|
|
78
|
+
function sides(element, where) {
|
|
79
|
+
const amount = amountOf(required(element, 'amnt', where), where);
|
|
80
|
+
const side = required(element, 'amntTp', where);
|
|
81
|
+
if (side !== 'D' && side !== 'C')
|
|
82
|
+
throw new BooksFileError('invalid_value', `${where}: amntTp is "${side}", and the schema allows D or C`);
|
|
83
|
+
const signed = side === 'D' ? amount : -amount;
|
|
84
|
+
return signed >= 0n ? { debit: signed, credit: 0n } : { debit: 0n, credit: -signed };
|
|
85
|
+
}
|
|
86
|
+
/** The totals a part of the file states, held against what its lines add up to. */
|
|
87
|
+
function checkTotals(part, what, counted, violations) {
|
|
88
|
+
const lines = text(part, 'linesCount');
|
|
89
|
+
const debit = text(part, 'totalDebit');
|
|
90
|
+
const credit = text(part, 'totalCredit');
|
|
91
|
+
if (lines !== '' && lines !== String(counted.lines)) {
|
|
92
|
+
violations.push({ rule: 'lines_count', message: `the ${what} say they hold ${lines} lines, and ${counted.lines} are in the file`, row: null });
|
|
93
|
+
}
|
|
94
|
+
if (debit !== '' && amountOf(debit, `the ${what}' totalDebit`) !== counted.debit) {
|
|
95
|
+
violations.push({ rule: 'total_debit', message: `the ${what} state a total debit of ${debit}, and their lines add up to ${formatDecimal(counted.debit)}`, row: null });
|
|
96
|
+
}
|
|
97
|
+
if (credit !== '' && amountOf(credit, `the ${what}' totalCredit`) !== counted.credit) {
|
|
98
|
+
violations.push({ rule: 'total_credit', message: `the ${what} state a total credit of ${credit}, and their lines add up to ${formatDecimal(counted.credit)}`, row: null });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const EMPTY_LINE = { contact: null, label: null, currency: null, amountCurrency: null, dueDate: null, matching: null };
|
|
102
|
+
/**
|
|
103
|
+
* Reads an audit file, as a string or as the bytes of the file. Entries are
|
|
104
|
+
* the `transaction` elements of each `journal`; the opening balance is the
|
|
105
|
+
* `obLine` elements, which carry no date of their own — the version 3.2 gives
|
|
106
|
+
* one, `opBalDate`, which `openingDate` returns, and the version 4.0 opens on
|
|
107
|
+
* `header/startDate`.
|
|
108
|
+
*/
|
|
109
|
+
export function readXaf(input, options = {}) {
|
|
110
|
+
const root = parseXml(input, { maxBytes: options.maxBytes ?? DEFAULTS.maxBytes, maxElements: options.maxElements ?? DEFAULTS.maxElements, maxDepth: DEFAULTS.maxDepth }, options.encoding);
|
|
111
|
+
const version = Object.entries(XAF_NAMESPACES).find(([, namespace]) => namespace === root.namespace)?.[0];
|
|
112
|
+
if (root.name !== 'auditfile') {
|
|
113
|
+
throw new BooksFileError('not_an_auditfile', `the document element is <${root.name}>, and an audit file's is <auditfile>`);
|
|
114
|
+
}
|
|
115
|
+
if (version === undefined) {
|
|
116
|
+
throw new BooksFileError(/XAF/i.test(root.namespace) ? 'unsupported_version' : 'not_an_auditfile', `the namespace is "${root.namespace}"; this reader reads the audit file of version 3.2 (${XAF_NAMESPACES['3.2']}) and 4.0 (${XAF_NAMESPACES['4.0']})`);
|
|
117
|
+
}
|
|
118
|
+
const header = one(root, 'header');
|
|
119
|
+
if (header === undefined)
|
|
120
|
+
throw new BooksFileError('missing_element', 'the audit file has no header');
|
|
121
|
+
const currency = required(header, 'curCode', 'the header').toUpperCase();
|
|
122
|
+
if (!/^[A-Z]{3}$/.test(currency))
|
|
123
|
+
throw new BooksFileError('invalid_value', `the header's curCode is "${currency}", and an ISO 4217 code is three letters`);
|
|
124
|
+
const startDate = isoDate(required(header, 'startDate', 'the header'), 'the header startDate');
|
|
125
|
+
const company = one(root, 'company');
|
|
126
|
+
if (company === undefined)
|
|
127
|
+
throw new BooksFileError('missing_element', 'the audit file has no company');
|
|
128
|
+
const violations = [];
|
|
129
|
+
const accounts = new Map();
|
|
130
|
+
const contacts = new Map();
|
|
131
|
+
for (const account of all(one(company, 'generalLedger'), 'ledgerAccount')) {
|
|
132
|
+
const code = required(account, 'accID', 'a ledgerAccount');
|
|
133
|
+
const name = text(account, 'accDesc');
|
|
134
|
+
const type = text(account, 'accTp');
|
|
135
|
+
accounts.set(code, { code, name: name === '' ? null : name, type: type === '' ? null : type });
|
|
136
|
+
}
|
|
137
|
+
for (const party of all(one(company, 'customersSuppliers'), 'customerSupplier')) {
|
|
138
|
+
const code = required(party, 'custSupID', 'a customerSupplier');
|
|
139
|
+
const name = text(party, 'custSupName');
|
|
140
|
+
const address = all(party, 'streetAddress').concat(all(party, 'postalAddress')).map((a) => text(a, 'country').toUpperCase()).find((c) => c !== '');
|
|
141
|
+
const country = address ?? text(party, 'taxRegistrationCountry').toUpperCase();
|
|
142
|
+
const vat = text(party, 'taxRegIdent');
|
|
143
|
+
const registration = text(party, 'commerceNr');
|
|
144
|
+
const email = text(party, 'eMail');
|
|
145
|
+
contacts.set(code, {
|
|
146
|
+
code,
|
|
147
|
+
name: name === '' ? code : name,
|
|
148
|
+
vatNumber: vat === '' ? null : vat,
|
|
149
|
+
registrationNumber: registration === '' ? null : registration,
|
|
150
|
+
email: email === '' ? null : email,
|
|
151
|
+
country: /^[A-Z]{2}$/.test(country) ? country : null,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const knowAccount = (code) => {
|
|
155
|
+
if (!accounts.has(code))
|
|
156
|
+
accounts.set(code, { code, name: null, type: null });
|
|
157
|
+
};
|
|
158
|
+
// The opening balance.
|
|
159
|
+
const opening = [];
|
|
160
|
+
let openingDate = null;
|
|
161
|
+
const openingBalance = one(company, 'openingBalance');
|
|
162
|
+
if (openingBalance !== undefined) {
|
|
163
|
+
const written = text(openingBalance, 'opBalDate');
|
|
164
|
+
openingDate = written === '' ? startDate : isoDate(written, 'the opening balance opBalDate');
|
|
165
|
+
const counted = { lines: 0, debit: 0n, credit: 0n };
|
|
166
|
+
for (const obLine of all(openingBalance, 'obLine')) {
|
|
167
|
+
const where = `opening balance line ${text(obLine, 'nr') || counted.lines + 1}`;
|
|
168
|
+
const account = required(obLine, 'accID', where);
|
|
169
|
+
const { debit, credit } = sides(obLine, where);
|
|
170
|
+
knowAccount(account);
|
|
171
|
+
opening.push({ ...EMPTY_LINE, account, debit: formatDecimal(debit), credit: formatDecimal(credit) });
|
|
172
|
+
counted.lines += 1;
|
|
173
|
+
counted.debit += debit;
|
|
174
|
+
counted.credit += credit;
|
|
175
|
+
}
|
|
176
|
+
checkTotals(openingBalance, 'opening balance', counted, violations);
|
|
177
|
+
if (counted.debit !== counted.credit) {
|
|
178
|
+
violations.push({
|
|
179
|
+
rule: 'opening_balance',
|
|
180
|
+
message: `the opening balance has debit ${formatDecimal(counted.debit)} and credit ${formatDecimal(counted.credit)}`,
|
|
181
|
+
row: null,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// The entries.
|
|
186
|
+
const entries = [];
|
|
187
|
+
const transactions = one(company, 'transactions');
|
|
188
|
+
if (transactions !== undefined) {
|
|
189
|
+
const counted = { lines: 0, debit: 0n, credit: 0n };
|
|
190
|
+
for (const journal of all(transactions, 'journal')) {
|
|
191
|
+
const code = required(journal, 'jrnID', 'a journal');
|
|
192
|
+
const journalName = text(journal, 'desc');
|
|
193
|
+
for (const transaction of all(journal, 'transaction')) {
|
|
194
|
+
const number = required(transaction, 'nr', `a transaction of journal ${code}`);
|
|
195
|
+
const where = `transaction ${number} of journal ${code}`;
|
|
196
|
+
const date = isoDate(required(transaction, 'trDt', where), `${where}, trDt`);
|
|
197
|
+
const lines = [];
|
|
198
|
+
let debits = 0n;
|
|
199
|
+
let credits = 0n;
|
|
200
|
+
let reference = null;
|
|
201
|
+
for (const trLine of all(transaction, 'trLine')) {
|
|
202
|
+
const at = `line ${text(trLine, 'nr') || lines.length + 1} of ${where}`;
|
|
203
|
+
const account = required(trLine, 'accID', at);
|
|
204
|
+
const { debit, credit } = sides(trLine, at);
|
|
205
|
+
knowAccount(account);
|
|
206
|
+
const party = text(trLine, 'custSupID');
|
|
207
|
+
if (party !== '' && !contacts.has(party)) {
|
|
208
|
+
contacts.set(party, { code: party, name: party, vatNumber: null, registrationNumber: null, email: null, country: null });
|
|
209
|
+
}
|
|
210
|
+
const foreign = one(trLine, 'currency');
|
|
211
|
+
const foreignCode = text(foreign, 'curCode').toUpperCase();
|
|
212
|
+
const foreignAmount = text(foreign, 'curAmnt');
|
|
213
|
+
const inCurrency = foreign === undefined || foreignAmount === '' ? null : amountOf(foreignAmount, `${at}, curAmnt`);
|
|
214
|
+
const docRef = text(trLine, 'docRef');
|
|
215
|
+
if (reference === null && docRef !== '')
|
|
216
|
+
reference = docRef;
|
|
217
|
+
const label = text(trLine, 'desc');
|
|
218
|
+
const matching = text(trLine, 'matchKeyID');
|
|
219
|
+
lines.push({
|
|
220
|
+
account,
|
|
221
|
+
contact: party === '' ? null : party,
|
|
222
|
+
label: label === '' ? null : label,
|
|
223
|
+
debit: formatDecimal(debit),
|
|
224
|
+
credit: formatDecimal(credit),
|
|
225
|
+
currency: foreignCode === '' || inCurrency === null ? null : foreignCode,
|
|
226
|
+
amountCurrency: foreignCode === '' || inCurrency === null ? null : formatDecimal(inCurrency < 0n ? -inCurrency : inCurrency),
|
|
227
|
+
dueDate: null,
|
|
228
|
+
matching: matching === '' ? null : matching,
|
|
229
|
+
});
|
|
230
|
+
debits += debit;
|
|
231
|
+
credits += credit;
|
|
232
|
+
}
|
|
233
|
+
if (lines.length === 0) {
|
|
234
|
+
violations.push({ rule: 'empty_entry', message: `${where} has no line`, row: null });
|
|
235
|
+
}
|
|
236
|
+
else if (debits !== credits) {
|
|
237
|
+
violations.push({ rule: 'balance', message: `${where} has debit ${formatDecimal(debits)} and credit ${formatDecimal(credits)}`, row: null });
|
|
238
|
+
}
|
|
239
|
+
counted.lines += lines.length;
|
|
240
|
+
counted.debit += debits;
|
|
241
|
+
counted.credit += credits;
|
|
242
|
+
const description = text(transaction, 'desc');
|
|
243
|
+
entries.push({
|
|
244
|
+
journal: code,
|
|
245
|
+
journalName: journalName === '' ? null : journalName,
|
|
246
|
+
number,
|
|
247
|
+
date,
|
|
248
|
+
reference,
|
|
249
|
+
description: description !== '' ? description : (lines[0]?.label ?? null),
|
|
250
|
+
lines,
|
|
251
|
+
row: null,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
checkTotals(transactions, 'transactions', counted, violations);
|
|
256
|
+
}
|
|
257
|
+
if (entries.length === 0 && opening.length === 0) {
|
|
258
|
+
throw new BooksFileError('missing_element', 'the audit file holds no transaction and no opening balance');
|
|
259
|
+
}
|
|
260
|
+
return {
|
|
261
|
+
format: 'xaf',
|
|
262
|
+
version,
|
|
263
|
+
currency,
|
|
264
|
+
accounts: [...accounts.values()],
|
|
265
|
+
contacts: [...contacts.values()],
|
|
266
|
+
entries,
|
|
267
|
+
opening,
|
|
268
|
+
openingDate,
|
|
269
|
+
violations,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a set of books is once it has been read out of the software that kept
|
|
3
|
+
* them: accounts, parties, entries and an opening balance. The shape is
|
|
4
|
+
* declared here and imported from nowhere — it names no table and no ledger.
|
|
5
|
+
*
|
|
6
|
+
* It is, field for field, the shape every reader of an accounting export in
|
|
7
|
+
* this repository declares for itself (`@ekwo-ai/fec`, and the readers beside
|
|
8
|
+
* this one), so whatever takes one takes the others. It is not a shared
|
|
9
|
+
* abstraction: none imports another, and a reader that has nothing to say for
|
|
10
|
+
* a part of it returns that part empty.
|
|
11
|
+
*
|
|
12
|
+
* Every amount is a **decimal string**, never negative: a line carries a debit
|
|
13
|
+
* or a credit, and the side is the sign. No figure passes through a `number`.
|
|
14
|
+
*/
|
|
15
|
+
/** ISO 8601 calendar date, `YYYY-MM-DD`. */
|
|
16
|
+
export type IsoDate = string;
|
|
17
|
+
/** A fixed-point number, `1234.50`, never negative. */
|
|
18
|
+
export type Decimal = string;
|
|
19
|
+
/** One account of the chart the books were kept on, in the words of that software. */
|
|
20
|
+
export interface ImportedAccount {
|
|
21
|
+
code: string;
|
|
22
|
+
name: string | null;
|
|
23
|
+
/** What the source calls its kind, verbatim. Null when it says nothing. */
|
|
24
|
+
type: string | null;
|
|
25
|
+
}
|
|
26
|
+
/** A customer, a supplier or anybody else a line names. */
|
|
27
|
+
export interface ImportedContact {
|
|
28
|
+
/** How the source identifies the party on a line: its code, or its name where it has no code. */
|
|
29
|
+
code: string;
|
|
30
|
+
name: string;
|
|
31
|
+
vatNumber: string | null;
|
|
32
|
+
registrationNumber: string | null;
|
|
33
|
+
email: string | null;
|
|
34
|
+
/** ISO 3166-1 alpha-2, when the source writes one. Never inferred. */
|
|
35
|
+
country: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface ImportedLine {
|
|
38
|
+
/** The account code in the source's chart. */
|
|
39
|
+
account: string;
|
|
40
|
+
/** The `code` of an {@link ImportedContact}, or null. */
|
|
41
|
+
contact: string | null;
|
|
42
|
+
label: string | null;
|
|
43
|
+
debit: Decimal;
|
|
44
|
+
credit: Decimal;
|
|
45
|
+
/** A foreign currency the line was in, when the source says so. */
|
|
46
|
+
currency: string | null;
|
|
47
|
+
/** The amount in that currency, never negative. */
|
|
48
|
+
amountCurrency: Decimal | null;
|
|
49
|
+
dueDate: IsoDate | null;
|
|
50
|
+
/** The reconciliation mark the source put on the line, as written. */
|
|
51
|
+
matching: string | null;
|
|
52
|
+
}
|
|
53
|
+
export interface ImportedEntry {
|
|
54
|
+
/** The journal code in the source, or null when it has none. */
|
|
55
|
+
journal: string | null;
|
|
56
|
+
journalName: string | null;
|
|
57
|
+
/** The entry's number in the source. */
|
|
58
|
+
number: string | null;
|
|
59
|
+
date: IsoDate;
|
|
60
|
+
reference: string | null;
|
|
61
|
+
description: string | null;
|
|
62
|
+
lines: ImportedLine[];
|
|
63
|
+
/** The row of the file the entry starts on, from 1, for a message. */
|
|
64
|
+
row: number | null;
|
|
65
|
+
}
|
|
66
|
+
export interface Violation {
|
|
67
|
+
rule: string;
|
|
68
|
+
message: string;
|
|
69
|
+
/** The row of the file, from 1, or null for a rule about the whole file. */
|
|
70
|
+
row: number | null;
|
|
71
|
+
}
|
|
72
|
+
export interface ImportedBooks {
|
|
73
|
+
/** Which reader: `xaf` here. */
|
|
74
|
+
format: string;
|
|
75
|
+
/** The currency the file says its amounts are in. Null when it says none: never guessed. */
|
|
76
|
+
currency: string | null;
|
|
77
|
+
accounts: ImportedAccount[];
|
|
78
|
+
contacts: ImportedContact[];
|
|
79
|
+
entries: ImportedEntry[];
|
|
80
|
+
/** A trial balance: one line per account, to become the opening entry of a year. */
|
|
81
|
+
opening: ImportedLine[];
|
|
82
|
+
violations: Violation[];
|
|
83
|
+
}
|
|
84
|
+
export type Encoding = 'utf-8' | 'iso-8859-1';
|
|
85
|
+
export interface ReadOptions {
|
|
86
|
+
/**
|
|
87
|
+
* How bytes are decoded. An XML file says it in its declaration, and that is
|
|
88
|
+
* what is read: UTF-8 when it says nothing, as XML itself provides. Given
|
|
89
|
+
* here, it has to agree with the declaration, or the file is refused — the
|
|
90
|
+
* caller and the file disagreeing is not settled by picking one.
|
|
91
|
+
*/
|
|
92
|
+
encoding?: Encoding;
|
|
93
|
+
/** Refused beyond this many bytes, before anything is read. Default 256 MiB. */
|
|
94
|
+
maxBytes?: number;
|
|
95
|
+
/** Refused beyond this many elements, counted while reading. Default 20 million. */
|
|
96
|
+
maxElements?: number;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,4CAA4C;AAC5C,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,uDAAuD;AACvD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,sFAAsF;AACtF,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,2EAA2E;IAC3E,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,2DAA2D;AAC3D,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,sEAAsE;IACtE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,mEAAmE;IACnE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,mDAAmD;IACnD,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IACxB,sEAAsE;IACtE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,aAAa;IAC5B,gEAAgE;IAChE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,wCAAwC;IACxC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,EAAE,OAAO,CAAC;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,sEAAsE;IACtE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,oFAAoF;IACpF,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,UAAU,EAAE,SAAS,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,QAAQ,GAAG,OAAO,GAAG,YAAY,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,gFAAgF;IAChF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a set of books is once it has been read out of the software that kept
|
|
3
|
+
* them: accounts, parties, entries and an opening balance. The shape is
|
|
4
|
+
* declared here and imported from nowhere — it names no table and no ledger.
|
|
5
|
+
*
|
|
6
|
+
* It is, field for field, the shape every reader of an accounting export in
|
|
7
|
+
* this repository declares for itself (`@ekwo-ai/fec`, and the readers beside
|
|
8
|
+
* this one), so whatever takes one takes the others. It is not a shared
|
|
9
|
+
* abstraction: none imports another, and a reader that has nothing to say for
|
|
10
|
+
* a part of it returns that part empty.
|
|
11
|
+
*
|
|
12
|
+
* Every amount is a **decimal string**, never negative: a line carries a debit
|
|
13
|
+
* or a credit, and the side is the sign. No figure passes through a `number`.
|
|
14
|
+
*/
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG"}
|
package/dist/xml.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Encoding } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* A strict, small XML reader, written here because an audit file comes from
|
|
4
|
+
* outside and this package depends on nothing. It is the reader the bank
|
|
5
|
+
* statement brick of this repository wrote for the same reasons, copied rather
|
|
6
|
+
* than shared, with one difference: an audit file may be written in Latin-1,
|
|
7
|
+
* and says so in its declaration.
|
|
8
|
+
*
|
|
9
|
+
* It reads the XML accounting software writes and refuses the XML an attacker
|
|
10
|
+
* writes:
|
|
11
|
+
*
|
|
12
|
+
* - **no DOCTYPE at all**, so no internal subset, no entity declaration, no
|
|
13
|
+
* external entity, no parameter entity — the billion-laughs family and the
|
|
14
|
+
* XXE family both start with `<!DOCTYPE` and both stop there;
|
|
15
|
+
* - the five predefined entities and numeric character references, and any
|
|
16
|
+
* other `&name;` is an error rather than an empty string;
|
|
17
|
+
* - a size limit applied before anything is read, a depth limit and an element
|
|
18
|
+
* limit applied while reading, and no recursion: nesting costs heap the
|
|
19
|
+
* limits bound, never stack;
|
|
20
|
+
* - the encoding the declaration names — UTF-8 when it names none, as XML
|
|
21
|
+
* provides — decoded fatally where a byte can be wrong, and refused when the
|
|
22
|
+
* caller names another;
|
|
23
|
+
* - namespaces resolved, because a file may write `<auditfile xmlns="…">` or
|
|
24
|
+
* `<x:auditfile xmlns:x="…">` and both are the same file.
|
|
25
|
+
*
|
|
26
|
+
* It is not a general XML parser and says so: element and attribute names are
|
|
27
|
+
* ASCII (every name of the audit file is), and nothing is validated against a
|
|
28
|
+
* schema.
|
|
29
|
+
*/
|
|
30
|
+
export interface XmlElement {
|
|
31
|
+
/** Local name, without prefix. */
|
|
32
|
+
name: string;
|
|
33
|
+
/** Namespace URI, `''` when the element is in none. */
|
|
34
|
+
namespace: string;
|
|
35
|
+
/** Attributes by the name they were written under; `xmlns` ones left out. */
|
|
36
|
+
attributes: Record<string, string>;
|
|
37
|
+
children: XmlElement[];
|
|
38
|
+
/** The element's own character data, concatenated; children's is theirs. */
|
|
39
|
+
text: string;
|
|
40
|
+
}
|
|
41
|
+
export interface XmlLimits {
|
|
42
|
+
maxBytes: number;
|
|
43
|
+
maxDepth: number;
|
|
44
|
+
maxElements: number;
|
|
45
|
+
}
|
|
46
|
+
export declare function parseXml(input: string | Uint8Array, limits: XmlLimits, encoding?: Encoding): XmlElement;
|
|
47
|
+
//# sourceMappingURL=xml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml.d.ts","sourceRoot":"","sources":["../src/xml.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,WAAW,UAAU;IACzB,kCAAkC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,SAAS,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,4EAA4E;IAC5E,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AA2JD,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,UAAU,CAsKvG"}
|
package/dist/xml.js
ADDED
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { BooksFileError } from './errors.js';
|
|
2
|
+
const NAME = /[A-Za-z_][A-Za-z0-9_.-]*(?::[A-Za-z_][A-Za-z0-9_.-]*)?/y;
|
|
3
|
+
const ATTRIBUTE = /\s+([A-Za-z_][A-Za-z0-9_.-]*(?::[A-Za-z_][A-Za-z0-9_.-]*)?)\s*=\s*(?:"([^<"]*)"|'([^<']*)')/y;
|
|
4
|
+
const DECLARATION = /^<\?xml\s+version\s*=\s*(?:"1\.[0-9]+"|'1\.[0-9]+')(?:\s+encoding\s*=\s*(?:"([A-Za-z][A-Za-z0-9._-]*)"|'([A-Za-z][A-Za-z0-9._-]*)'))?(?:\s+standalone\s*=\s*(?:"(?:yes|no)"|'(?:yes|no)'))?\s*\?>/;
|
|
5
|
+
const REFERENCE = /&(?:#x([0-9a-fA-F]+)|#([0-9]+)|([A-Za-z_][A-Za-z0-9_.-]*));/g;
|
|
6
|
+
const PREDEFINED = {
|
|
7
|
+
lt: '<',
|
|
8
|
+
gt: '>',
|
|
9
|
+
amp: '&',
|
|
10
|
+
quot: '"',
|
|
11
|
+
apos: "'",
|
|
12
|
+
};
|
|
13
|
+
const XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
|
|
14
|
+
function malformed(what, at) {
|
|
15
|
+
return new BooksFileError('malformed_xml', `${what} (at character ${at})`);
|
|
16
|
+
}
|
|
17
|
+
/** XML 1.0 `Char`, by code point. */
|
|
18
|
+
function isXmlChar(code) {
|
|
19
|
+
return (code === 0x9 ||
|
|
20
|
+
code === 0xa ||
|
|
21
|
+
code === 0xd ||
|
|
22
|
+
(code >= 0x20 && code <= 0xd7ff) ||
|
|
23
|
+
(code >= 0xe000 && code <= 0xfffd) ||
|
|
24
|
+
(code >= 0x10000 && code <= 0x10ffff));
|
|
25
|
+
}
|
|
26
|
+
/** The first code unit XML does not allow in a document, or -1. */
|
|
27
|
+
function firstForbiddenCharacter(source) {
|
|
28
|
+
for (let index = 0; index < source.length; index += 1) {
|
|
29
|
+
const code = source.charCodeAt(index);
|
|
30
|
+
if (code >= 0x20 && code < 0xfffe)
|
|
31
|
+
continue;
|
|
32
|
+
if (code === 0x9 || code === 0xa || code === 0xd)
|
|
33
|
+
continue;
|
|
34
|
+
return index;
|
|
35
|
+
}
|
|
36
|
+
return -1;
|
|
37
|
+
}
|
|
38
|
+
function decodeReferences(raw, at) {
|
|
39
|
+
if (!raw.includes('&'))
|
|
40
|
+
return raw;
|
|
41
|
+
let out = '';
|
|
42
|
+
let last = 0;
|
|
43
|
+
for (const match of raw.matchAll(REFERENCE)) {
|
|
44
|
+
const index = match.index;
|
|
45
|
+
const between = raw.slice(last, index);
|
|
46
|
+
if (between.includes('&'))
|
|
47
|
+
throw malformed('a bare "&"', at + last + between.indexOf('&'));
|
|
48
|
+
out += between;
|
|
49
|
+
if (match[3] !== undefined) {
|
|
50
|
+
const value = PREDEFINED[match[3]];
|
|
51
|
+
if (value === undefined) {
|
|
52
|
+
throw new BooksFileError('undefined_entity', `entity &${match[3]}; is not one of the five XML predefines, and this reader declares no other (at character ${at + index})`);
|
|
53
|
+
}
|
|
54
|
+
out += value;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const code = match[1] !== undefined ? parseInt(match[1], 16) : parseInt(match[2], 10);
|
|
58
|
+
if (!isXmlChar(code)) {
|
|
59
|
+
throw malformed(`character reference ${match[0]} is not an XML character`, at + index);
|
|
60
|
+
}
|
|
61
|
+
out += String.fromCodePoint(code);
|
|
62
|
+
}
|
|
63
|
+
last = index + match[0].length;
|
|
64
|
+
}
|
|
65
|
+
const rest = raw.slice(last);
|
|
66
|
+
if (rest.includes('&'))
|
|
67
|
+
throw malformed('a bare "&"', at + last + rest.indexOf('&'));
|
|
68
|
+
return out + rest;
|
|
69
|
+
}
|
|
70
|
+
/** The encodings a declaration may name, by the label it uses. */
|
|
71
|
+
const LABELS = {
|
|
72
|
+
'utf-8': 'utf-8',
|
|
73
|
+
utf8: 'utf-8',
|
|
74
|
+
'iso-8859-1': 'iso-8859-1',
|
|
75
|
+
'iso8859-1': 'iso-8859-1',
|
|
76
|
+
latin1: 'iso-8859-1',
|
|
77
|
+
'latin-1': 'iso-8859-1',
|
|
78
|
+
};
|
|
79
|
+
function decode(input, limits, said) {
|
|
80
|
+
if (typeof input === 'string') {
|
|
81
|
+
// A string has no bytes to count; its length is the closest honest bound,
|
|
82
|
+
// and no encoding writes a code unit in less than one byte.
|
|
83
|
+
if (input.length > limits.maxBytes) {
|
|
84
|
+
throw new BooksFileError('too_large', `the file is ${input.length} characters, over the limit of ${limits.maxBytes}`);
|
|
85
|
+
}
|
|
86
|
+
return input.charCodeAt(0) === 0xfeff ? input.slice(1) : input;
|
|
87
|
+
}
|
|
88
|
+
if (input.byteLength > limits.maxBytes) {
|
|
89
|
+
throw new BooksFileError('too_large', `the file is ${input.byteLength} bytes, over the limit of ${limits.maxBytes}`);
|
|
90
|
+
}
|
|
91
|
+
const first = input[0];
|
|
92
|
+
const second = input[1];
|
|
93
|
+
if ((first === 0xfe && second === 0xff) || (first === 0xff && second === 0xfe)) {
|
|
94
|
+
throw new BooksFileError('unsupported_encoding', 'the file is UTF-16; this reader takes UTF-8 or Latin-1');
|
|
95
|
+
}
|
|
96
|
+
// The declaration is ASCII in both encodings read here, so it can be read
|
|
97
|
+
// before the rest is decoded.
|
|
98
|
+
const bom = first === 0xef && second === 0xbb && input[2] === 0xbf;
|
|
99
|
+
const head = String.fromCharCode(...input.subarray(bom ? 3 : 0, Math.min(input.length, 256)));
|
|
100
|
+
const declaration = DECLARATION.exec(head);
|
|
101
|
+
const label = (declaration?.[1] ?? declaration?.[2])?.toLowerCase();
|
|
102
|
+
let encoding = 'utf-8';
|
|
103
|
+
if (label !== undefined) {
|
|
104
|
+
const known = LABELS[label];
|
|
105
|
+
if (known === undefined) {
|
|
106
|
+
throw new BooksFileError('unsupported_encoding', `the file declares encoding ${label}; this reader takes UTF-8 or ISO-8859-1 — decode it first and pass the string`);
|
|
107
|
+
}
|
|
108
|
+
encoding = known;
|
|
109
|
+
}
|
|
110
|
+
if (said !== undefined && said !== encoding) {
|
|
111
|
+
throw new BooksFileError('unsupported_encoding', `the file is ${label === undefined ? 'UTF-8, as XML is when its declaration names no encoding' : `declared ${label}`}, and ${said} was asked for; the file is read as it says, never as somebody else says`);
|
|
112
|
+
}
|
|
113
|
+
if (encoding === 'iso-8859-1') {
|
|
114
|
+
if (bom)
|
|
115
|
+
throw new BooksFileError('unsupported_encoding', 'the file starts with the byte order mark of UTF-8 and declares ISO-8859-1');
|
|
116
|
+
const parts = [];
|
|
117
|
+
for (let i = 0; i < input.length; i += 8192)
|
|
118
|
+
parts.push(String.fromCharCode(...input.subarray(i, i + 8192)));
|
|
119
|
+
return parts.join('');
|
|
120
|
+
}
|
|
121
|
+
let text;
|
|
122
|
+
try {
|
|
123
|
+
text = new TextDecoder('utf-8', { fatal: true }).decode(input);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
throw new BooksFileError('unsupported_encoding', "the bytes are not UTF-8, and the declaration names no other encoding; a file saved in Latin-1 says so in its declaration");
|
|
127
|
+
}
|
|
128
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
129
|
+
}
|
|
130
|
+
function isSpace(character) {
|
|
131
|
+
return character === ' ' || character === '\n' || character === '\t';
|
|
132
|
+
}
|
|
133
|
+
export function parseXml(input, limits, encoding) {
|
|
134
|
+
const source = decode(input, limits, encoding).replace(/\r\n?/g, '\n');
|
|
135
|
+
const forbidden = firstForbiddenCharacter(source);
|
|
136
|
+
if (forbidden !== -1)
|
|
137
|
+
throw malformed('a control character XML does not allow', forbidden);
|
|
138
|
+
let at = 0;
|
|
139
|
+
const declaration = DECLARATION.exec(source);
|
|
140
|
+
if (declaration)
|
|
141
|
+
at = declaration[0].length;
|
|
142
|
+
const rootScope = new Map([['xml', XML_NAMESPACE]]);
|
|
143
|
+
const stack = [];
|
|
144
|
+
let root;
|
|
145
|
+
let elements = 0;
|
|
146
|
+
const addText = (raw, where, literal) => {
|
|
147
|
+
const top = stack[stack.length - 1];
|
|
148
|
+
if (top === undefined) {
|
|
149
|
+
if (raw.trim() !== '')
|
|
150
|
+
throw malformed('text outside the document element', where);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
top.text.push(literal ? raw : decodeReferences(raw, where));
|
|
154
|
+
};
|
|
155
|
+
while (at < source.length) {
|
|
156
|
+
const lt = source.indexOf('<', at);
|
|
157
|
+
if (lt === -1) {
|
|
158
|
+
addText(source.slice(at), at, false);
|
|
159
|
+
at = source.length;
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
if (lt > at)
|
|
163
|
+
addText(source.slice(at, lt), at, false);
|
|
164
|
+
at = lt;
|
|
165
|
+
if (source.startsWith('<!--', at)) {
|
|
166
|
+
const end = source.indexOf('-->', at + 4);
|
|
167
|
+
if (end === -1)
|
|
168
|
+
throw malformed('a comment that never ends', at);
|
|
169
|
+
at = end + 3;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (source.startsWith('<![CDATA[', at)) {
|
|
173
|
+
if (stack.length === 0)
|
|
174
|
+
throw malformed('a CDATA section outside the document element', at);
|
|
175
|
+
const end = source.indexOf(']]>', at + 9);
|
|
176
|
+
if (end === -1)
|
|
177
|
+
throw malformed('a CDATA section that never ends', at);
|
|
178
|
+
addText(source.slice(at + 9, end), at, true);
|
|
179
|
+
at = end + 3;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (source.startsWith('<!DOCTYPE', at)) {
|
|
183
|
+
throw new BooksFileError('doctype_forbidden', 'the file carries a DOCTYPE; an audit file needs none, and entity declarations are refused unread');
|
|
184
|
+
}
|
|
185
|
+
if (source.startsWith('<!', at))
|
|
186
|
+
throw malformed('a markup declaration', at);
|
|
187
|
+
if (source.startsWith('<?', at)) {
|
|
188
|
+
const end = source.indexOf('?>', at + 2);
|
|
189
|
+
if (end === -1)
|
|
190
|
+
throw malformed('a processing instruction that never ends', at);
|
|
191
|
+
if (/^<\?xml[\s?]/i.test(source.slice(at, at + 6))) {
|
|
192
|
+
throw malformed('an XML declaration anywhere but the first character', at);
|
|
193
|
+
}
|
|
194
|
+
at = end + 2;
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (source.startsWith('</', at)) {
|
|
198
|
+
NAME.lastIndex = at + 2;
|
|
199
|
+
const name = NAME.exec(source);
|
|
200
|
+
if (!name)
|
|
201
|
+
throw malformed('a closing tag without a name', at);
|
|
202
|
+
let end = NAME.lastIndex;
|
|
203
|
+
while (isSpace(source[end]))
|
|
204
|
+
end += 1;
|
|
205
|
+
if (source[end] !== '>')
|
|
206
|
+
throw malformed('a closing tag that never ends', at);
|
|
207
|
+
const open = stack.pop();
|
|
208
|
+
if (open === undefined || open.qualifiedName !== name[0]) {
|
|
209
|
+
throw malformed(`</${name[0]}> closes ${open ? `<${open.qualifiedName}>` : 'nothing'}`, at);
|
|
210
|
+
}
|
|
211
|
+
open.element.text = open.text.join('');
|
|
212
|
+
at = end + 1;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
// An opening tag.
|
|
216
|
+
NAME.lastIndex = at + 1;
|
|
217
|
+
const name = NAME.exec(source);
|
|
218
|
+
if (!name)
|
|
219
|
+
throw malformed('a "<" that opens nothing', at);
|
|
220
|
+
if (root !== undefined && stack.length === 0) {
|
|
221
|
+
throw malformed('a second document element', at);
|
|
222
|
+
}
|
|
223
|
+
elements += 1;
|
|
224
|
+
if (elements > limits.maxElements) {
|
|
225
|
+
throw new BooksFileError('too_many_elements', `the file holds more than ${limits.maxElements} elements`);
|
|
226
|
+
}
|
|
227
|
+
if (stack.length >= limits.maxDepth) {
|
|
228
|
+
throw new BooksFileError('too_deep', `elements are nested deeper than ${limits.maxDepth}`);
|
|
229
|
+
}
|
|
230
|
+
let cursor = NAME.lastIndex;
|
|
231
|
+
const written = [];
|
|
232
|
+
for (;;) {
|
|
233
|
+
ATTRIBUTE.lastIndex = cursor;
|
|
234
|
+
const attribute = ATTRIBUTE.exec(source);
|
|
235
|
+
if (!attribute)
|
|
236
|
+
break;
|
|
237
|
+
const raw = (attribute[2] ?? attribute[3]);
|
|
238
|
+
written.push([
|
|
239
|
+
attribute[1],
|
|
240
|
+
decodeReferences(raw, cursor).replace(/[\t\n]/g, ' '),
|
|
241
|
+
]);
|
|
242
|
+
cursor = ATTRIBUTE.lastIndex;
|
|
243
|
+
}
|
|
244
|
+
while (isSpace(source[cursor]))
|
|
245
|
+
cursor += 1;
|
|
246
|
+
const selfClosing = source[cursor] === '/';
|
|
247
|
+
if (selfClosing)
|
|
248
|
+
cursor += 1;
|
|
249
|
+
if (source[cursor] !== '>')
|
|
250
|
+
throw malformed(`<${name[0]}> is not a well-formed tag`, at);
|
|
251
|
+
const parentScope = stack[stack.length - 1]?.scope ?? rootScope;
|
|
252
|
+
let scope = parentScope;
|
|
253
|
+
const attributes = {};
|
|
254
|
+
const seen = new Set();
|
|
255
|
+
for (const [key, value] of written) {
|
|
256
|
+
if (seen.has(key))
|
|
257
|
+
throw malformed(`attribute ${key} is written twice on <${name[0]}>`, at);
|
|
258
|
+
seen.add(key);
|
|
259
|
+
if (key === 'xmlns' || key.startsWith('xmlns:')) {
|
|
260
|
+
if (scope === parentScope)
|
|
261
|
+
scope = new Map(parentScope);
|
|
262
|
+
const prefix = key === 'xmlns' ? '' : key.slice(6);
|
|
263
|
+
if (prefix === 'xml' || prefix === 'xmlns') {
|
|
264
|
+
throw malformed(`prefix ${prefix} cannot be declared`, at);
|
|
265
|
+
}
|
|
266
|
+
if (prefix !== '' && value === '')
|
|
267
|
+
throw malformed(`prefix ${prefix} is bound to nothing`, at);
|
|
268
|
+
scope.set(prefix, value);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
attributes[key] = value;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
const colon = name[0].indexOf(':');
|
|
275
|
+
const prefix = colon === -1 ? '' : name[0].slice(0, colon);
|
|
276
|
+
const namespace = scope.get(prefix);
|
|
277
|
+
if (namespace === undefined && prefix !== '') {
|
|
278
|
+
throw malformed(`prefix ${prefix} is bound to no namespace`, at);
|
|
279
|
+
}
|
|
280
|
+
const element = {
|
|
281
|
+
name: colon === -1 ? name[0] : name[0].slice(colon + 1),
|
|
282
|
+
namespace: namespace ?? '',
|
|
283
|
+
attributes,
|
|
284
|
+
children: [],
|
|
285
|
+
text: '',
|
|
286
|
+
};
|
|
287
|
+
const parent = stack[stack.length - 1];
|
|
288
|
+
if (parent)
|
|
289
|
+
parent.element.children.push(element);
|
|
290
|
+
else
|
|
291
|
+
root = element;
|
|
292
|
+
if (!selfClosing)
|
|
293
|
+
stack.push({ element, qualifiedName: name[0], scope, text: [] });
|
|
294
|
+
at = cursor + 1;
|
|
295
|
+
}
|
|
296
|
+
const unclosed = stack[stack.length - 1];
|
|
297
|
+
if (unclosed)
|
|
298
|
+
throw malformed(`<${unclosed.qualifiedName}> is never closed`, source.length);
|
|
299
|
+
if (root === undefined)
|
|
300
|
+
throw malformed('no document element', 0);
|
|
301
|
+
return root;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=xml.js.map
|
package/dist/xml.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml.js","sourceRoot":"","sources":["../src/xml.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ekwo-ai/xaf",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Reads an XML Audit File Financial (XAF, versions 3.2 and 4.0) — the accounts, the parties, the opening balance and every transaction of a year — into plain objects in TypeScript: amounts as decimal strings, the file's own totals checked, and what does not add up. A strict XML reader, no DOCTYPE, no entities. Zero dependencies.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"xaf",
|
|
7
|
+
"auditfile",
|
|
8
|
+
"audit-file",
|
|
9
|
+
"xml",
|
|
10
|
+
"accounting",
|
|
11
|
+
"bookkeeping",
|
|
12
|
+
"migration",
|
|
13
|
+
"import",
|
|
14
|
+
"parser"
|
|
15
|
+
],
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Ekwo AI",
|
|
18
|
+
"homepage": "https://github.com/Ekwo-ai/ekwo-os/tree/main/packages/formats/xaf#readme",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/Ekwo-ai/ekwo-os.git",
|
|
22
|
+
"directory": "packages/formats/xaf"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/Ekwo-ai/ekwo-os/issues"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md",
|
|
35
|
+
"LICENSE"
|
|
36
|
+
],
|
|
37
|
+
"main": "./dist/index.js",
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"exports": {
|
|
40
|
+
".": {
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"import": "./dist/index.js"
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "tsc -p tsconfig.build.json",
|
|
47
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
48
|
+
"test": "vitest run"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/node": "^24.0.0",
|
|
52
|
+
"typescript": "^5.9.0",
|
|
53
|
+
"vitest": "^3.2.0",
|
|
54
|
+
"xmllint-wasm": "^5.3.0"
|
|
55
|
+
}
|
|
56
|
+
}
|