@jasy/cli 1.0.0-alpha.2 → 1.0.0-alpha.4

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 Florian Heuberger
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 CHANGED
@@ -64,8 +64,8 @@ structural checks carry the everyday case.
64
64
  pure JS). No upload, DSGVO-safe.
65
65
  - **Excel by hand** - the `.xlsx` is a ZIP we build ourselves, deflated with our own writer and CRC32.
66
66
  - **Reads multi-file PDFs** - pulls the right e-invoice XML out even when a tool embedded its own JSON too.
67
- - Generates with [`@jasy/zugferd`](https://www.npmjs.com/package/@jasy/zugferd) on the hand-rolled
68
- [`@jasy/pdf`](https://www.npmjs.com/package/@jasy/pdf) engine.
67
+ - Generates with [`@jasy/zugferd`](https://npmx.dev/@jasy/zugferd) on the hand-rolled
68
+ [`@jasy/pdf`](https://npmx.dev/@jasy/pdf) engine.
69
69
 
70
70
  ---
71
71
 
@@ -12,7 +12,6 @@ const FORMATS = {
12
12
  excel: "xlsx",
13
13
  };
14
14
  export function exportCommand(args) {
15
- var _a, _b, _c;
16
15
  let file;
17
16
  let out;
18
17
  let fmtArg;
@@ -30,13 +29,13 @@ export function exportCommand(args) {
30
29
  process.exitCode = 1;
31
30
  return;
32
31
  }
33
- const format = (_b = (_a = FORMATS[fmtArg !== null && fmtArg !== void 0 ? fmtArg : ""]) !== null && _a !== void 0 ? _a : FORMATS[out ? extname(out).slice(1) : ""]) !== null && _b !== void 0 ? _b : "json";
32
+ const format = FORMATS[fmtArg ?? ""] ?? FORMATS[out ? extname(out).slice(1) : ""] ?? "json";
34
33
  if (format === "xlsx" && !out) {
35
34
  console.error("✗ xlsx is binary - give an output path with -o <file.xlsx>");
36
35
  process.exitCode = 1;
37
36
  return;
38
37
  }
39
- const base = (_c = process.env.INIT_CWD) !== null && _c !== void 0 ? _c : process.cwd();
38
+ const base = process.env.INIT_CWD ?? process.cwd();
40
39
  let bytes;
41
40
  try {
42
41
  bytes = readFileSync(resolve(base, file));
@@ -10,7 +10,6 @@ const bold = (s) => paint("1", s);
10
10
  const dim = (s) => paint("2", s);
11
11
  const money = (n) => n.toFixed(2);
12
12
  export function readCommand(args) {
13
- var _a, _b;
14
13
  let file;
15
14
  let out;
16
15
  let dumpXml = false;
@@ -29,7 +28,7 @@ export function readCommand(args) {
29
28
  return;
30
29
  }
31
30
  // resolve against where the user actually stood - pnpm scripts cd into the package dir
32
- const base = (_a = process.env.INIT_CWD) !== null && _a !== void 0 ? _a : process.cwd();
31
+ const base = process.env.INIT_CWD ?? process.cwd();
33
32
  let r;
34
33
  try {
35
34
  r = readInvoiceFile(resolve(base, file));
@@ -66,7 +65,7 @@ export function readCommand(args) {
66
65
  }
67
66
  else {
68
67
  console.log(` source ${r.isPdf ? "PDF - embedded XML extracted" : "raw XML"}`);
69
- console.log(` guideline ${(_b = r.meta.guideline) !== null && _b !== void 0 ? _b : "-"}`);
68
+ console.log(` guideline ${r.meta.guideline ?? "-"}`);
70
69
  console.log(` XML ${r.xml.length} bytes`);
71
70
  console.log(`\n ${dim("→ --xml print the XML · -o <file> save it")}`);
72
71
  }
@@ -15,7 +15,6 @@ const red = (s) => paint("31", s);
15
15
  const dim = (s) => paint("2", s);
16
16
  const bold = (s) => paint("1", s);
17
17
  export function validateCommand(args) {
18
- var _a;
19
18
  const file = args.find((a) => !a.startsWith("-"));
20
19
  const verbose = args.includes("-v") || args.includes("--verbose");
21
20
  const json = args.includes("--json");
@@ -30,7 +29,7 @@ export function validateCommand(args) {
30
29
  fail("usage: jasy validate <file.pdf|file.xml> [--json] [-v]");
31
30
  return;
32
31
  }
33
- const base = (_a = process.env.INIT_CWD) !== null && _a !== void 0 ? _a : process.cwd();
32
+ const base = process.env.INIT_CWD ?? process.cwd();
34
33
  let bytes;
35
34
  try {
36
35
  bytes = readFileSync(resolve(base, file));
@@ -47,7 +46,7 @@ export function validateCommand(args) {
47
46
  try {
48
47
  rules = validateInvoiceXml(read.xml, profileFor(read.meta));
49
48
  }
50
- catch (_b) {
49
+ catch {
51
50
  rules = null;
52
51
  }
53
52
  }
@@ -58,25 +57,28 @@ export function validateCommand(args) {
58
57
  try {
59
58
  vera = runVeraPdf(resolve(base, file));
60
59
  }
61
- catch (_c) {
60
+ catch {
62
61
  vera = null;
63
62
  }
64
63
  }
65
- const ok = (!rules || rules.valid) && (!pdfa || pdfa.ok) && (!vera || vera.ok);
64
+ // VALID requires that we actually recognised EN 16931 invoice data: a file we could not parse as an
65
+ // invoice (random bytes, or a plain PDF with no embedded XML) is "not an invoice" - never "valid".
66
+ const recognized = read.meta.syntax !== "unknown";
67
+ const ok = recognized && (!rules || rules.valid) && (!pdfa || pdfa.ok) && (!vera || vera.ok);
66
68
  if (!ok)
67
69
  process.exitCode = 1;
68
70
  if (json) {
69
- console.log(JSON.stringify(toJson(file, read, rules, pdfa, vera, ok)));
71
+ console.log(JSON.stringify(toJson(file, read, rules, pdfa, vera, recognized, ok)));
70
72
  return;
71
73
  }
72
- printReport(file, read, rules, pdfa, vera, ok, verbose);
74
+ printReport(file, read, rules, pdfa, vera, recognized, ok, verbose);
73
75
  }
74
76
  /** The machine-readable report (`--json`): the exact same data the printed report shows. */
75
- function toJson(file, read, rules, pdfa, vera, ok) {
76
- var _a;
77
+ function toJson(file, read, rules, pdfa, vera, recognized, ok) {
77
78
  return {
78
79
  file: basename(file),
79
80
  summary: describeInvoice(read.meta),
81
+ recognized,
80
82
  valid: ok,
81
83
  businessRules: rules && {
82
84
  kind: rules.profile.startsWith("xrechnung") ? "XRechnung" : "EN 16931",
@@ -98,7 +100,7 @@ function toJson(file, read, rules, pdfa, vera, ok) {
98
100
  available: true,
99
101
  valid: vera.ok,
100
102
  profile: vera.profile,
101
- failedRules: (_a = vera.failedRules) !== null && _a !== void 0 ? _a : vera.failures.length,
103
+ failedRules: vera.failedRules ?? vera.failures.length,
102
104
  failures: vera.failures,
103
105
  }
104
106
  : { available: false }
@@ -106,8 +108,7 @@ function toJson(file, read, rules, pdfa, vera, ok) {
106
108
  };
107
109
  }
108
110
  /** The human-readable report (default): a coloured summary line + per-check detail. */
109
- function printReport(file, read, rules, pdfa, vera, ok, verbose) {
110
- var _a;
111
+ function printReport(file, read, rules, pdfa, vera, recognized, ok, verbose) {
111
112
  const label = (s) => s.padEnd(20);
112
113
  console.log(`\n ${bold(basename(file))} ${dim("·")} ${describeInvoice(read.meta)}\n`);
113
114
  // business rules
@@ -139,7 +140,7 @@ function printReport(file, read, rules, pdfa, vera, ok, verbose) {
139
140
  }
140
141
  // full ISO 19005 (PDF/A) via veraPDF
141
142
  if (vera) {
142
- const n = (_a = vera.failedRules) !== null && _a !== void 0 ? _a : vera.failures.length;
143
+ const n = vera.failedRules ?? vera.failures.length;
143
144
  console.log(` ${label("PDF/A (veraPDF)")}${vera.ok ? green("✓ compliant") : red(`✗ ${n} failed`)}`);
144
145
  if (!vera.ok)
145
146
  for (const f of vera.failures)
@@ -148,5 +149,8 @@ function printReport(file, read, rules, pdfa, vera, ok, verbose) {
148
149
  else if (read.isPdf) {
149
150
  console.log(` ${label("PDF/A (veraPDF)")}${dim("n/a - `jasy verapdf --install` for the full ISO check")}`);
150
151
  }
151
- console.log(`\n → ${ok ? green(bold("VALID")) : red(bold("INVALID"))}\n`);
152
+ if (!recognized)
153
+ console.log(`\n → ${red(bold("NOT A ZUGFeRD / XRECHNUNG INVOICE"))} ${dim("(no EN 16931 data found)")}\n`);
154
+ else
155
+ console.log(`\n → ${ok ? green(bold("VALID")) : red(bold("INVALID"))}\n`);
152
156
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { resolve } from "node:path";
11
2
  import { detectTools, installVeraPdf, runVeraPdf, findVerapdf } from "../core/verapdf.js";
12
3
  // `jasy verapdf` - a guided doctor for the optional full-ISO PDF/A validator:
@@ -19,18 +10,15 @@ const green = (s) => paint("32", s);
19
10
  const red = (s) => paint("31", s);
20
11
  const dim = (s) => paint("2", s);
21
12
  const bold = (s) => paint("1", s);
22
- export function verapdfCommand(args) {
23
- return __awaiter(this, void 0, void 0, function* () {
24
- if (args.includes("--install"))
25
- return install();
26
- const file = args.find((a) => !a.startsWith("-"));
27
- if (file)
28
- return validateFile(file);
29
- doctor();
30
- });
13
+ export async function verapdfCommand(args) {
14
+ if (args.includes("--install"))
15
+ return install();
16
+ const file = args.find((a) => !a.startsWith("-"));
17
+ if (file)
18
+ return validateFile(file);
19
+ doctor();
31
20
  }
32
21
  function doctor() {
33
- var _a, _b;
34
22
  const t = detectTools();
35
23
  console.log(`
36
24
  ${bold("veraPDF")} - the official open-source PDF/A validator (PDF Association).
@@ -39,8 +27,8 @@ function doctor() {
39
27
  your invoice never leaves the machine. Free, no account.
40
28
  `);
41
29
  const row = (label, ok, detail) => console.log(` ${label.padEnd(11)}${ok ? green("✓ " + detail) : red("✗ " + detail)}`);
42
- row("Java", !!t.java, (_a = t.java) !== null && _a !== void 0 ? _a : "not found");
43
- row("veraPDF", !!t.verapdf, t.verapdf ? `${t.verapdf} ${dim((_b = t.verapdfPath) !== null && _b !== void 0 ? _b : "")}` : "not found");
30
+ row("Java", !!t.java, t.java ?? "not found");
31
+ row("veraPDF", !!t.verapdf, t.verapdf ? `${t.verapdf} ${dim(t.verapdfPath ?? "")}` : "not found");
44
32
  console.log("");
45
33
  if (!t.java) {
46
34
  console.log(` ${dim("Java is required - veraPDF is a Java app. Install a JRE 11+:")}`);
@@ -55,33 +43,30 @@ function doctor() {
55
43
  console.log(` ${green("Ready.")} ${dim("`jasy validate <file>` now adds the full ISO PDF/A check automatically.")}`);
56
44
  }
57
45
  }
58
- function install() {
59
- return __awaiter(this, void 0, void 0, function* () {
60
- try {
61
- const bin = yield installVeraPdf((s) => console.log(` ${dim(s)}`));
62
- console.log(` ${green(" veraPDF installed")} ${dim(bin)}`);
63
- console.log(` ${dim("`jasy validate <file>` now runs the full ISO PDF/A check.")}`);
64
- }
65
- catch (e) {
66
- console.error(` ${red("✗ " + e.message)}`);
67
- process.exitCode = 1;
68
- }
69
- });
46
+ async function install() {
47
+ try {
48
+ const bin = await installVeraPdf((s) => console.log(` ${dim(s)}`));
49
+ console.log(` ${green("✓ veraPDF installed")} ${dim(bin)}`);
50
+ console.log(` ${dim("`jasy validate <file>` now runs the full ISO PDF/A check.")}`);
51
+ }
52
+ catch (e) {
53
+ console.error(` ${red("✗ " + e.message)}`);
54
+ process.exitCode = 1;
55
+ }
70
56
  }
71
57
  function validateFile(file) {
72
- var _a, _b, _c;
73
58
  if (!findVerapdf()) {
74
59
  console.error(` ${red("✗ veraPDF is not installed")} - run ${bold("jasy verapdf --install")}`);
75
60
  process.exitCode = 1;
76
61
  return;
77
62
  }
78
- const base = (_a = process.env.INIT_CWD) !== null && _a !== void 0 ? _a : process.cwd();
63
+ const base = process.env.INIT_CWD ?? process.cwd();
79
64
  try {
80
65
  const r = runVeraPdf(resolve(base, file));
81
66
  const head = r.ok
82
67
  ? green("✓ compliant")
83
- : red(`✗ ${(_b = r.failedRules) !== null && _b !== void 0 ? _b : r.failures.length} rule(s) failed`);
84
- console.log(`\n ${(_c = r.profile) !== null && _c !== void 0 ? _c : "PDF/A"} ${head}`);
68
+ : red(`✗ ${r.failedRules ?? r.failures.length} rule(s) failed`);
69
+ console.log(`\n ${r.profile ?? "PDF/A"} ${head}`);
85
70
  for (const f of r.failures) {
86
71
  const n = f.failedChecks;
87
72
  console.log(` ${red("✗")} ISO clause ${bold(f.clause)} ${dim(`(${n} check${n === 1 ? "" : "s"})`)}`);
@@ -2,7 +2,6 @@
2
2
  // profile (plain EN16931 vs the German XRechnung CIUS). Pure tag/regex inspection - no full parse
3
3
  // needed (that comes later). Lets the CLI say "ZUGFeRD EN16931 (CII)" before doing anything heavy.
4
4
  export function detectInvoice(xml) {
5
- var _a, _b, _c, _d;
6
5
  const syntax = /<(?:rsm:)?CrossIndustryInvoice/.test(xml)
7
6
  ? "CII"
8
7
  : /<Invoice\b[^>]*urn:oasis:names:specification:ubl/.test(xml) ||
@@ -10,7 +9,9 @@ export function detectInvoice(xml) {
10
9
  ? "UBL"
11
10
  : "unknown";
12
11
  // BT-24: CII carries it in GuidelineSpecified…/ram:ID, UBL in cbc:CustomizationID.
13
- const guideline = (_d = (_b = (_a = xml.match(/GuidelineSpecifiedDocumentContextParameter>\s*<(?:ram:)?ID>([^<]+)</)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : (_c = xml.match(/<(?:cbc:)?CustomizationID>([^<]+)</)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : null;
12
+ const guideline = xml.match(/GuidelineSpecifiedDocumentContextParameter>\s*<(?:ram:)?ID>([^<]+)</)?.[1] ??
13
+ xml.match(/<(?:cbc:)?CustomizationID>([^<]+)</)?.[1] ??
14
+ null;
14
15
  const profile = !guideline
15
16
  ? "unknown"
16
17
  : /xrechnung/i.test(guideline)
@@ -13,7 +13,7 @@ function summary(inv, t) {
13
13
  }
14
14
  /** Full invoice model + a computed totals block, pretty-printed. */
15
15
  export function exportJson(inv, t) {
16
- return JSON.stringify(Object.assign(Object.assign({}, inv), { totals: summary(inv, t) }), null, 2);
16
+ return JSON.stringify({ ...inv, totals: summary(inv, t) }, null, 2);
17
17
  }
18
18
  /** A plain-text receipt - no ANSI, safe to pipe or save. */
19
19
  export function exportText(inv, t) {
@@ -22,7 +22,6 @@ export function extractEmbeddedXml(pdf) {
22
22
  // attaches its own gobl.json / source) - so we collect every Filespec and pick the invoice XML by name
23
23
  // (factur-x.xml / zugferd-invoice.xml / xrechnung.xml / order-x.xml), then any .xml, then the first.
24
24
  function findEmbeddedFileObject(s) {
25
- var _a, _b;
26
25
  const specs = [];
27
26
  const efRe = /\/EF\s*<<[^>]*?\/(?:UF|F)\s+(\d+)\s+0\s+R/g;
28
27
  for (let m = efRe.exec(s); m; m = efRe.exec(s)) {
@@ -30,7 +29,9 @@ function findEmbeddedFileObject(s) {
30
29
  }
31
30
  if (specs.length) {
32
31
  const invoiceXml = /(factur-x|zugferd-invoice|xrechnung|order-x|cii)\.xml$/i;
33
- const pick = (_b = (_a = specs.find((sp) => invoiceXml.test(sp.name))) !== null && _a !== void 0 ? _a : specs.find((sp) => /\.xml$/i.test(sp.name))) !== null && _b !== void 0 ? _b : specs[0];
32
+ const pick = specs.find((sp) => invoiceXml.test(sp.name)) ??
33
+ specs.find((sp) => /\.xml$/i.test(sp.name)) ??
34
+ specs[0];
34
35
  return pick.obj;
35
36
  }
36
37
  const t = s.search(/\/Type\s*\/EmbeddedFile/);
@@ -5,10 +5,9 @@ import { detectInvoice } from "./detect.js";
5
5
  const unesc = (s) => s.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
6
6
  /** Inner content of the first `<tag …>…</tag>` (CII tags don't self-nest, so non-greedy is exact). */
7
7
  function inner(xml, tag) {
8
- var _a;
9
8
  if (xml === undefined)
10
9
  return undefined;
11
- return (_a = new RegExp(`<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)</${tag}>`).exec(xml)) === null || _a === void 0 ? void 0 : _a[1];
10
+ return new RegExp(`<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)</${tag}>`).exec(xml)?.[1];
12
11
  }
13
12
  /** Inner content of every `<tag …>…</tag>`. */
14
13
  function innerAll(xml, tag) {
@@ -27,10 +26,9 @@ function val(xml, tag) {
27
26
  }
28
27
  /** An attribute on the first `<tag … name="X" …>`. */
29
28
  function attr(xml, tag, name) {
30
- var _a;
31
29
  if (xml === undefined)
32
30
  return undefined;
33
- return (_a = new RegExp(`<${tag}\\s[^>]*\\b${name}="([^"]*)"`).exec(xml)) === null || _a === void 0 ? void 0 : _a[1];
31
+ return new RegExp(`<${tag}\\s[^>]*\\b${name}="([^"]*)"`).exec(xml)?.[1];
34
32
  }
35
33
  const num = (s) => (s === undefined ? 0 : parseFloat(s));
36
34
  /** `format="102"` date `20260620` → `2026-06-20`. */
@@ -39,7 +37,6 @@ function date(scope) {
39
37
  return d && d.length === 8 ? `${d.slice(0, 4)}-${d.slice(4, 6)}-${d.slice(6, 8)}` : undefined;
40
38
  }
41
39
  function parseAddress(s) {
42
- var _a;
43
40
  return {
44
41
  postCode: val(s, "ram:PostcodeCode"),
45
42
  line1: val(s, "ram:LineOne"),
@@ -47,7 +44,7 @@ function parseAddress(s) {
47
44
  line3: val(s, "ram:LineThree"),
48
45
  city: val(s, "ram:CityName"),
49
46
  subdivision: val(s, "ram:CountrySubDivisionName"),
50
- country: (_a = val(s, "ram:CountryID")) !== null && _a !== void 0 ? _a : "",
47
+ country: val(s, "ram:CountryID") ?? "",
51
48
  };
52
49
  }
53
50
  function parseContact(s) {
@@ -66,55 +63,52 @@ function taxReg(s, scheme) {
66
63
  return m ? unesc(m[1]) : undefined;
67
64
  }
68
65
  function parseSeller(s) {
69
- var _a, _b;
70
66
  const org = inner(s, "ram:SpecifiedLegalOrganization");
71
67
  return {
72
- name: (_a = val(s, "ram:Name")) !== null && _a !== void 0 ? _a : "",
68
+ name: val(s, "ram:Name") ?? "",
73
69
  tradingName: val(org, "ram:TradingBusinessName"),
74
70
  legalRegistrationId: val(org, "ram:ID"),
75
71
  vatId: taxReg(s, "VA"),
76
72
  taxNumber: taxReg(s, "FC"),
77
73
  electronicAddress: val(inner(s, "ram:URIUniversalCommunication"), "ram:URIID"),
78
- address: parseAddress((_b = inner(s, "ram:PostalTradeAddress")) !== null && _b !== void 0 ? _b : ""),
74
+ address: parseAddress(inner(s, "ram:PostalTradeAddress") ?? ""),
79
75
  contact: parseContact(inner(s, "ram:DefinedTradeContact")),
80
76
  };
81
77
  }
82
78
  function parseBuyer(s) {
83
- var _a, _b;
84
79
  const org = inner(s, "ram:SpecifiedLegalOrganization");
85
80
  return {
86
- name: (_a = val(s, "ram:Name")) !== null && _a !== void 0 ? _a : "",
81
+ name: val(s, "ram:Name") ?? "",
87
82
  tradingName: val(org, "ram:TradingBusinessName"),
88
83
  legalRegistrationId: val(org, "ram:ID"),
89
84
  vatId: taxReg(s, "VA"),
90
85
  electronicAddress: val(inner(s, "ram:URIUniversalCommunication"), "ram:URIID"),
91
- address: parseAddress((_b = inner(s, "ram:PostalTradeAddress")) !== null && _b !== void 0 ? _b : ""),
86
+ address: parseAddress(inner(s, "ram:PostalTradeAddress") ?? ""),
92
87
  contact: parseContact(inner(s, "ram:DefinedTradeContact")),
93
88
  };
94
89
  }
95
90
  function parseLine(s, index) {
96
- var _a, _b, _c, _d, _e, _f, _g, _h;
97
- const doc = (_a = inner(s, "ram:AssociatedDocumentLineDocument")) !== null && _a !== void 0 ? _a : "";
98
- const product = (_b = inner(s, "ram:SpecifiedTradeProduct")) !== null && _b !== void 0 ? _b : "";
99
- const price = (_c = inner(s, "ram:NetPriceProductTradePrice")) !== null && _c !== void 0 ? _c : "";
100
- const del = (_d = inner(s, "ram:SpecifiedLineTradeDelivery")) !== null && _d !== void 0 ? _d : "";
101
- const tax = (_e = inner(inner(s, "ram:SpecifiedLineTradeSettlement"), "ram:ApplicableTradeTax")) !== null && _e !== void 0 ? _e : "";
91
+ const doc = inner(s, "ram:AssociatedDocumentLineDocument") ?? "";
92
+ const product = inner(s, "ram:SpecifiedTradeProduct") ?? "";
93
+ const price = inner(s, "ram:NetPriceProductTradePrice") ?? "";
94
+ const del = inner(s, "ram:SpecifiedLineTradeDelivery") ?? "";
95
+ const tax = inner(inner(s, "ram:SpecifiedLineTradeSettlement"), "ram:ApplicableTradeTax") ?? "";
102
96
  const id = val(doc, "ram:LineID");
103
97
  const note = inner(doc, "ram:IncludedNote");
104
98
  const basis = inner(price, "ram:BasisQuantity");
105
99
  return {
106
100
  id: id !== undefined && id !== String(index + 1) ? id : undefined, // omit the auto-number
107
- name: (_f = val(product, "ram:Name")) !== null && _f !== void 0 ? _f : "",
101
+ name: val(product, "ram:Name") ?? "",
108
102
  description: val(product, "ram:Description"),
109
103
  sellerItemId: val(product, "ram:SellerAssignedID"),
110
104
  buyerItemId: val(product, "ram:BuyerAssignedID"),
111
105
  standardItemId: val(product, "ram:GlobalID"),
112
106
  quantity: num(val(del, "ram:BilledQuantity")),
113
- unit: (_g = attr(del, "ram:BilledQuantity", "unitCode")) !== null && _g !== void 0 ? _g : "",
107
+ unit: attr(del, "ram:BilledQuantity", "unitCode") ?? "",
114
108
  netUnitPrice: num(val(price, "ram:ChargeAmount")),
115
109
  priceBaseQuantity: basis ? num(val(price, "ram:BasisQuantity")) : undefined,
116
110
  vat: {
117
- category: ((_h = val(tax, "ram:CategoryCode")) !== null && _h !== void 0 ? _h : "S"),
111
+ category: (val(tax, "ram:CategoryCode") ?? "S"),
118
112
  ratePercent: num(val(tax, "ram:RateApplicablePercent")),
119
113
  },
120
114
  note: note ? val(note, "ram:Content") : undefined,
@@ -122,7 +116,6 @@ function parseLine(s, index) {
122
116
  }
123
117
  /** A document-level allowance (discount) or charge (surcharge), BG-20 / BG-21. */
124
118
  function parseAllowanceCii(ac) {
125
- var _a;
126
119
  const cat = inner(ac, "ram:CategoryTradeTax");
127
120
  return {
128
121
  isCharge: val(inner(ac, "ram:ChargeIndicator"), "udt:Indicator") === "true",
@@ -130,7 +123,7 @@ function parseAllowanceCii(ac) {
130
123
  reason: val(ac, "ram:Reason"),
131
124
  reasonCode: val(ac, "ram:ReasonCode"),
132
125
  vat: {
133
- category: ((_a = val(cat, "ram:CategoryCode")) !== null && _a !== void 0 ? _a : "S"),
126
+ category: (val(cat, "ram:CategoryCode") ?? "S"),
134
127
  ratePercent: num(val(cat, "ram:RateApplicablePercent")),
135
128
  },
136
129
  };
@@ -149,12 +142,11 @@ function exemptionsCii(set) {
149
142
  }
150
143
  /** Parse a UN/CEFACT CII invoice (EN16931 / ZUGFeRD / XRechnung-CII) into the Invoice model. */
151
144
  export function parseCII(xml) {
152
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
153
- const header = (_a = inner(xml, "rsm:ExchangedDocument")) !== null && _a !== void 0 ? _a : "";
154
- const tx = (_b = inner(xml, "rsm:SupplyChainTradeTransaction")) !== null && _b !== void 0 ? _b : "";
155
- const agr = (_c = inner(tx, "ram:ApplicableHeaderTradeAgreement")) !== null && _c !== void 0 ? _c : "";
156
- const del = (_d = inner(tx, "ram:ApplicableHeaderTradeDelivery")) !== null && _d !== void 0 ? _d : "";
157
- const set = (_e = inner(tx, "ram:ApplicableHeaderTradeSettlement")) !== null && _e !== void 0 ? _e : "";
145
+ const header = inner(xml, "rsm:ExchangedDocument") ?? "";
146
+ const tx = inner(xml, "rsm:SupplyChainTradeTransaction") ?? "";
147
+ const agr = inner(tx, "ram:ApplicableHeaderTradeAgreement") ?? "";
148
+ const del = inner(tx, "ram:ApplicableHeaderTradeDelivery") ?? "";
149
+ const set = inner(tx, "ram:ApplicableHeaderTradeSettlement") ?? "";
158
150
  const type = num(val(header, "ram:TypeCode"));
159
151
  const notes = innerAll(header, "ram:IncludedNote")
160
152
  .map((n) => val(n, "ram:Content"))
@@ -188,17 +180,17 @@ export function parseCII(xml) {
188
180
  const paid = val(totals, "ram:TotalPrepaidAmount");
189
181
  const allowancesCharges = innerAll(set, "ram:SpecifiedTradeAllowanceCharge").map(parseAllowanceCii);
190
182
  return {
191
- number: (_f = val(header, "ram:ID")) !== null && _f !== void 0 ? _f : "",
192
- issueDate: (_g = date(inner(header, "ram:IssueDateTime"))) !== null && _g !== void 0 ? _g : "",
183
+ number: val(header, "ram:ID") ?? "",
184
+ issueDate: date(inner(header, "ram:IssueDateTime")) ?? "",
193
185
  type: type && type !== 380 ? type : undefined,
194
- currency: (_h = val(set, "ram:InvoiceCurrencyCode")) !== null && _h !== void 0 ? _h : "",
186
+ currency: val(set, "ram:InvoiceCurrencyCode") ?? "",
195
187
  dueDate: date(inner(terms, "ram:DueDateDateTime")),
196
188
  buyerReference: val(agr, "ram:BuyerReference"),
197
189
  purchaseOrderRef: val(inner(agr, "ram:BuyerOrderReferencedDocument"), "ram:IssuerAssignedID"),
198
190
  contractRef: val(inner(agr, "ram:ContractReferencedDocument"), "ram:IssuerAssignedID"),
199
191
  notes: notes.length ? notes : undefined,
200
- seller: parseSeller((_j = inner(agr, "ram:SellerTradeParty")) !== null && _j !== void 0 ? _j : ""),
201
- buyer: parseBuyer((_k = inner(agr, "ram:BuyerTradeParty")) !== null && _k !== void 0 ? _k : ""),
192
+ seller: parseSeller(inner(agr, "ram:SellerTradeParty") ?? ""),
193
+ buyer: parseBuyer(inner(agr, "ram:BuyerTradeParty") ?? ""),
202
194
  delivery,
203
195
  payeeName: val(inner(set, "ram:PayeeTradeParty"), "ram:Name"),
204
196
  lines: innerAll(tx, "ram:IncludedSupplyChainTradeLineItem").map(parseLine),
@@ -210,7 +202,6 @@ export function parseCII(xml) {
210
202
  }
211
203
  // ── UBL (OASIS Invoice-2) - the second EN16931 syntax (PEPPOL; XRechnung accepts it too) ────────────
212
204
  function parseAddressUbl(s) {
213
- var _a;
214
205
  return {
215
206
  line1: val(s, "cbc:StreetName"),
216
207
  line2: val(s, "cbc:AdditionalStreetName"),
@@ -218,7 +209,7 @@ function parseAddressUbl(s) {
218
209
  city: val(s, "cbc:CityName"),
219
210
  postCode: val(s, "cbc:PostalZone"),
220
211
  subdivision: val(s, "cbc:CountrySubentity"),
221
- country: (_a = val(inner(s, "cac:Country"), "cbc:IdentificationCode")) !== null && _a !== void 0 ? _a : "",
212
+ country: val(inner(s, "cac:Country"), "cbc:IdentificationCode") ?? "",
222
213
  };
223
214
  }
224
215
  function parseContactUbl(s) {
@@ -240,47 +231,44 @@ function ublTaxId(party, scheme) {
240
231
  return undefined;
241
232
  }
242
233
  function parsePartyUbl(scope) {
243
- var _a, _b, _c;
244
- const party = (_a = inner(scope, "cac:Party")) !== null && _a !== void 0 ? _a : "";
234
+ const party = inner(scope, "cac:Party") ?? "";
245
235
  const legal = inner(party, "cac:PartyLegalEntity");
246
236
  return {
247
237
  party,
248
- name: (_b = val(legal, "cbc:RegistrationName")) !== null && _b !== void 0 ? _b : "",
238
+ name: val(legal, "cbc:RegistrationName") ?? "",
249
239
  tradingName: val(inner(party, "cac:PartyName"), "cbc:Name"),
250
240
  legalRegistrationId: val(legal, "cbc:CompanyID"),
251
241
  vatId: ublTaxId(party, "VAT"),
252
242
  electronicAddress: val(party, "cbc:EndpointID"),
253
- address: parseAddressUbl((_c = inner(party, "cac:PostalAddress")) !== null && _c !== void 0 ? _c : ""),
243
+ address: parseAddressUbl(inner(party, "cac:PostalAddress") ?? ""),
254
244
  contact: parseContactUbl(inner(party, "cac:Contact")),
255
245
  };
256
246
  }
257
247
  function parseLineUbl(s, index) {
258
- var _a, _b, _c, _d, _e, _f;
259
- const item = (_a = inner(s, "cac:Item")) !== null && _a !== void 0 ? _a : "";
260
- const price = (_b = inner(s, "cac:Price")) !== null && _b !== void 0 ? _b : "";
261
- const tax = (_c = inner(item, "cac:ClassifiedTaxCategory")) !== null && _c !== void 0 ? _c : "";
248
+ const item = inner(s, "cac:Item") ?? "";
249
+ const price = inner(s, "cac:Price") ?? "";
250
+ const tax = inner(item, "cac:ClassifiedTaxCategory") ?? "";
262
251
  const id = val(s, "cbc:ID");
263
252
  const base = val(price, "cbc:BaseQuantity");
264
253
  return {
265
254
  id: id !== undefined && id !== String(index + 1) ? id : undefined, // omit the auto-number
266
- name: (_d = val(item, "cbc:Name")) !== null && _d !== void 0 ? _d : "",
255
+ name: val(item, "cbc:Name") ?? "",
267
256
  description: val(item, "cbc:Description"),
268
257
  sellerItemId: val(inner(item, "cac:SellersItemIdentification"), "cbc:ID"),
269
258
  buyerItemId: val(inner(item, "cac:BuyersItemIdentification"), "cbc:ID"),
270
259
  standardItemId: val(inner(item, "cac:StandardItemIdentification"), "cbc:ID"),
271
260
  quantity: num(val(s, "cbc:InvoicedQuantity")),
272
- unit: (_e = attr(s, "cbc:InvoicedQuantity", "unitCode")) !== null && _e !== void 0 ? _e : "",
261
+ unit: attr(s, "cbc:InvoicedQuantity", "unitCode") ?? "",
273
262
  netUnitPrice: num(val(price, "cbc:PriceAmount")),
274
263
  priceBaseQuantity: base !== undefined ? num(base) : undefined,
275
264
  vat: {
276
- category: ((_f = val(tax, "cbc:ID")) !== null && _f !== void 0 ? _f : "S"),
265
+ category: (val(tax, "cbc:ID") ?? "S"),
277
266
  ratePercent: num(val(tax, "cbc:Percent")),
278
267
  },
279
268
  note: val(s, "cbc:Note"),
280
269
  };
281
270
  }
282
271
  function parseAllowanceUbl(ac) {
283
- var _a;
284
272
  const cat = inner(ac, "cac:TaxCategory");
285
273
  return {
286
274
  isCharge: val(ac, "cbc:ChargeIndicator") === "true",
@@ -288,7 +276,7 @@ function parseAllowanceUbl(ac) {
288
276
  reason: val(ac, "cbc:AllowanceChargeReason"),
289
277
  reasonCode: val(ac, "cbc:AllowanceChargeReasonCode"),
290
278
  vat: {
291
- category: ((_a = val(cat, "cbc:ID")) !== null && _a !== void 0 ? _a : "S"),
279
+ category: (val(cat, "cbc:ID") ?? "S"),
292
280
  ratePercent: num(val(cat, "cbc:Percent")),
293
281
  },
294
282
  };
@@ -307,12 +295,11 @@ function exemptionsUbl(xml) {
307
295
  }
308
296
  /** Parse an OASIS UBL invoice (EN16931 / PEPPOL / XRechnung-UBL) into the Invoice model. */
309
297
  export function parseUBL(xml) {
310
- var _a, _b, _c, _d, _e;
311
298
  // UBL has no head wrapper; the header fields are direct children before the supplier party
312
299
  const cut = xml.indexOf("<cac:AccountingSupplierParty");
313
300
  const head = cut >= 0 ? xml.slice(0, cut) : xml;
314
- const seller = parsePartyUbl((_a = inner(xml, "cac:AccountingSupplierParty")) !== null && _a !== void 0 ? _a : "");
315
- const buyer = parsePartyUbl((_b = inner(xml, "cac:AccountingCustomerParty")) !== null && _b !== void 0 ? _b : "");
301
+ const seller = parsePartyUbl(inner(xml, "cac:AccountingSupplierParty") ?? "");
302
+ const buyer = parsePartyUbl(inner(xml, "cac:AccountingCustomerParty") ?? "");
316
303
  const type = num(val(head, "cbc:InvoiceTypeCode"));
317
304
  const notes = innerAll(head, "cbc:Note")
318
305
  .map(unesc)
@@ -348,10 +335,10 @@ export function parseUBL(xml) {
348
335
  const li = xml.indexOf("<cac:InvoiceLine");
349
336
  const allowancesCharges = innerAll(li >= 0 ? xml.slice(0, li) : xml, "cac:AllowanceCharge").map(parseAllowanceUbl);
350
337
  return {
351
- number: (_c = val(head, "cbc:ID")) !== null && _c !== void 0 ? _c : "",
352
- issueDate: (_d = val(head, "cbc:IssueDate")) !== null && _d !== void 0 ? _d : "",
338
+ number: val(head, "cbc:ID") ?? "",
339
+ issueDate: val(head, "cbc:IssueDate") ?? "",
353
340
  type: type && type !== 380 ? type : undefined,
354
- currency: (_e = val(head, "cbc:DocumentCurrencyCode")) !== null && _e !== void 0 ? _e : "",
341
+ currency: val(head, "cbc:DocumentCurrencyCode") ?? "",
355
342
  dueDate: val(head, "cbc:DueDate"),
356
343
  buyerReference: val(head, "cbc:BuyerReference"),
357
344
  purchaseOrderRef: val(inner(head, "cac:OrderReference"), "cbc:ID"),
package/dist/core/pdfa.js CHANGED
@@ -3,15 +3,14 @@
3
3
  // layer that actually trips e-invoice PDFs in practice: the rules our own writer satisfies, read
4
4
  // backwards. Great as a regression guard for us and a fast local signal for foreign PDFs.
5
5
  export function checkPdfA3(pdf) {
6
- var _a, _b, _c, _d, _e, _f, _g, _h, _j;
7
6
  const s = Buffer.from(pdf).toString("latin1");
8
- const xmp = (_b = (_a = s.match(/<x:xmpmeta[\s\S]*?<\/x:xmpmeta>/)) === null || _a === void 0 ? void 0 : _a[0]) !== null && _b !== void 0 ? _b : "";
7
+ const xmp = s.match(/<x:xmpmeta[\s\S]*?<\/x:xmpmeta>/)?.[0] ?? "";
9
8
  const checks = [];
10
9
  const add = (id, label, ok, detail) => {
11
10
  checks.push({ id, label, ok, detail });
12
11
  };
13
12
  // header + binary marker (≥4 bytes > 127 on the comment line) - required for PDF/A
14
- const version = (_c = s.match(/^%PDF-(\d\.\d)/)) === null || _c === void 0 ? void 0 : _c[1];
13
+ const version = s.match(/^%PDF-(\d\.\d)/)?.[1];
15
14
  add("header", "PDF header & version", !!version, version ? `%PDF-${version}` : "missing");
16
15
  const afterHeader = s.slice(s.indexOf("\n") + 1, s.indexOf("\n") + 9);
17
16
  const binaryMarker = afterHeader.startsWith("%") &&
@@ -25,12 +24,13 @@ export function checkPdfA3(pdf) {
25
24
  add("file-id", "document /ID present", /\/ID\s*\[\s*<[0-9A-Fa-f]/.test(s));
26
25
  // XMP metadata + PDF/A identification
27
26
  add("xmp", "XMP metadata present", xmp.length > 0);
28
- const part = (_d = xmp.match(/pdfaid:part[^0-9]{0,4}(\d)/)) === null || _d === void 0 ? void 0 : _d[1];
27
+ const part = xmp.match(/pdfaid:part[^0-9]{0,4}(\d)/)?.[1];
29
28
  add("pdfa-part", "declares PDF/A part 3", part === "3", part ? `part ${part}` : "missing");
30
- const conformance = (_e = xmp.match(/pdfaid:conformance[^ABU]{0,4}([ABU])/)) === null || _e === void 0 ? void 0 : _e[1];
29
+ const conformance = xmp.match(/pdfaid:conformance[^ABU]{0,4}([ABU])/)?.[1];
31
30
  add("pdfa-conformance", "PDF/A conformance level", !!conformance, conformance ? `level ${conformance}` : "missing");
32
31
  // Factur-X / ZUGFeRD XMP extension schema (the e-invoice fingerprint)
33
- const fxFile = (_g = (_f = xmp.match(/DocumentFileName[^>]*>\s*([^<\s]+\.xml)/i)) === null || _f === void 0 ? void 0 : _f[1]) !== null && _g !== void 0 ? _g : (/DocumentFileName/i.test(xmp) ? "?" : undefined);
32
+ const fxFile = xmp.match(/DocumentFileName[^>]*>\s*([^<\s]+\.xml)/i)?.[1] ??
33
+ (/DocumentFileName/i.test(xmp) ? "?" : undefined);
34
34
  add("facturx-xmp", "Factur-X/ZUGFeRD XMP extension", /ConformanceLevel/i.test(xmp) && /DocumentType/i.test(xmp), fxFile);
35
35
  // PDF/A OutputIntent with an embedded ICC profile (needed once colour/transparency is used)
36
36
  add("output-intent", "PDF/A OutputIntent (sRGB ICC)", /\/OutputIntent/.test(s) && /\/DestOutputProfile/.test(s) && /\/S\s*\/GTS_PDFA1/.test(s));
@@ -40,8 +40,8 @@ export function checkPdfA3(pdf) {
40
40
  /\/EF\s*<</.test(s));
41
41
  // every font must be embedded (no font descriptor without a font program)
42
42
  // count the descriptor objects (/Type /FontDescriptor), not the /FontDescriptor ref entries
43
- const descriptors = ((_h = s.match(/\/Type\s*\/FontDescriptor\b/g)) !== null && _h !== void 0 ? _h : []).length;
44
- const programs = ((_j = s.match(/\/FontFile[23]?\b/g)) !== null && _j !== void 0 ? _j : []).length;
43
+ const descriptors = (s.match(/\/Type\s*\/FontDescriptor\b/g) ?? []).length;
44
+ const programs = (s.match(/\/FontFile[23]?\b/g) ?? []).length;
45
45
  add("fonts-embedded", "all fonts embedded", descriptors === 0 || programs >= descriptors, `${programs}/${descriptors} programs`);
46
46
  return { ok: checks.every((c) => c.ok), checks };
47
47
  }
package/dist/core/read.js CHANGED
@@ -15,7 +15,7 @@ export function readInvoice(data) {
15
15
  invoice = parseInvoice(xml); // throws for not-yet-supported syntaxes
16
16
  totals = computeInvoice(invoice);
17
17
  }
18
- catch (_a) {
18
+ catch {
19
19
  /* leave the parsed view empty - the raw XML + detection still work */
20
20
  }
21
21
  return { isPdf, xml, meta, invoice, totals };
@@ -20,10 +20,9 @@ export function profileFor(meta) {
20
20
  return ubl ? "en16931-ubl" : "en16931-cii";
21
21
  }
22
22
  function runRuleSet(set, xml) {
23
- var _a;
24
23
  const sef = gunzipSync(readFileSync(join(RULES_DIR, `${set}.sef.json.gz`))).toString("utf-8");
25
24
  const out = SaxonJS.transform({ stylesheetText: sef, sourceText: xml, destination: "serialized" }, "sync");
26
- return parseSvrl((_a = out.principalResult) !== null && _a !== void 0 ? _a : "");
25
+ return parseSvrl(out.principalResult ?? "");
27
26
  }
28
27
  /** Validate invoice XML against a profile's Schematron rules → a structured pass/fail report. */
29
28
  export function validateInvoiceXml(xml, profile) {
@@ -39,17 +38,16 @@ export function validateInvoiceXml(xml, profile) {
39
38
  // The rules emit SVRL (Schematron Validation Report Language). Each <svrl:failed-assert> is a violation;
40
39
  // flag="warning" downgrades it. Regex is enough for this well-formed machine output.
41
40
  function parseSvrl(svrl) {
42
- var _a, _b, _c, _d, _e;
43
41
  const errors = [];
44
42
  const warnings = [];
45
43
  const re = /<svrl:failed-assert\b([^>]*)>([\s\S]*?)<\/svrl:failed-assert>/g;
46
44
  for (let m = re.exec(svrl); m; m = re.exec(svrl)) {
47
- const flag = (_b = (_a = attr(m[1], "flag")) !== null && _a !== void 0 ? _a : attr(m[1], "role")) !== null && _b !== void 0 ? _b : "fatal";
48
- const text = ((_d = (_c = m[2].match(/<svrl:text>([\s\S]*?)<\/svrl:text>/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : "")
45
+ const flag = attr(m[1], "flag") ?? attr(m[1], "role") ?? "fatal";
46
+ const text = (m[2].match(/<svrl:text>([\s\S]*?)<\/svrl:text>/)?.[1] ?? "")
49
47
  .replace(/\s+/g, " ")
50
48
  .trim();
51
49
  const violation = {
52
- id: (_e = text.match(/\[((?:BR|BG)-?[A-Z0-9-]+)\]/)) === null || _e === void 0 ? void 0 : _e[1],
50
+ id: text.match(/\[((?:BR|BG)-?[A-Z0-9-]+)\]/)?.[1],
53
51
  test: attr(m[1], "test"),
54
52
  location: attr(m[1], "location"),
55
53
  text,
@@ -60,6 +58,5 @@ function parseSvrl(svrl) {
60
58
  return { errors, warnings };
61
59
  }
62
60
  function attr(s, name) {
63
- var _a;
64
- return (_a = s.match(new RegExp(`\\b${name}="([^"]*)"`))) === null || _a === void 0 ? void 0 : _a[1];
61
+ return s.match(new RegExp(`\\b${name}="([^"]*)"`))?.[1];
65
62
  }
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { spawnSync } from "node:child_process";
11
2
  import { existsSync, mkdtempSync, writeFileSync, rmSync } from "node:fs";
12
3
  import { join } from "node:path";
@@ -31,22 +22,20 @@ export function findVerapdf() {
31
22
  }
32
23
  /** What's available right now - drives the `jasy verapdf` doctor. */
33
24
  export function detectTools() {
34
- var _a, _b, _c, _d;
35
25
  const jv = spawnSync("java", ["-version"], { encoding: "utf-8" }); // java prints the version to stderr
36
26
  const java = jv.error
37
27
  ? undefined
38
- : ((_b = (_a = (jv.stderr || jv.stdout).match(/version "([^"]+)"/)) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : "found");
28
+ : ((jv.stderr || jv.stdout).match(/version "([^"]+)"/)?.[1] ?? "found");
39
29
  const verapdfPath = findVerapdf();
40
30
  let verapdf;
41
31
  if (verapdfPath) {
42
32
  const vv = spawnSync(verapdfPath, ["--version"], { encoding: "utf-8" });
43
- verapdf = vv.error ? undefined : ((_d = (_c = vv.stdout.match(/veraPDF (\S+)/)) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : "found");
33
+ verapdf = vv.error ? undefined : (vv.stdout.match(/veraPDF (\S+)/)?.[1] ?? "found");
44
34
  }
45
35
  return { java, verapdf, verapdfPath };
46
36
  }
47
37
  /** Parse veraPDF's `--format xml` report (the real 1.30 shape - verified against live output). */
48
38
  export function parseVeraReport(xml) {
49
- var _a;
50
39
  const ok = /isCompliant="true"/.test(xml);
51
40
  const det = xml.match(/passedRules="(\d+)"\s+failedRules="(\d+)"/);
52
41
  const failures = [
@@ -54,7 +43,7 @@ export function parseVeraReport(xml) {
54
43
  ].map((m) => ({ clause: m[1], failedChecks: Number(m[2]) }));
55
44
  return {
56
45
  ok,
57
- profile: (_a = xml.match(/profileName="([^"]*)"/)) === null || _a === void 0 ? void 0 : _a[1],
46
+ profile: xml.match(/profileName="([^"]*)"/)?.[1],
58
47
  passedRules: det ? Number(det[1]) : undefined,
59
48
  failedRules: det ? Number(det[2]) : undefined,
60
49
  failures,
@@ -62,7 +51,6 @@ export function parseVeraReport(xml) {
62
51
  }
63
52
  /** Run veraPDF on a PDF (auto-detecting the PDF/A flavour) and return the parsed report. */
64
53
  export function runVeraPdf(file, bin = findVerapdf()) {
65
- var _a;
66
54
  if (!bin)
67
55
  throw new Error("veraPDF is not installed - run `jasy verapdf --install`");
68
56
  const r = spawnSync(bin, ["--format", "xml", file], {
@@ -71,7 +59,7 @@ export function runVeraPdf(file, bin = findVerapdf()) {
71
59
  });
72
60
  if (r.error)
73
61
  throw r.error; // e.g. Java missing - the verapdf launcher couldn't start
74
- const xml = (_a = r.stdout) !== null && _a !== void 0 ? _a : "";
62
+ const xml = r.stdout ?? "";
75
63
  if (!xml.includes("<validationReport")) {
76
64
  throw new Error("veraPDF returned no report" + (r.stderr ? `: ${r.stderr.split("\n")[0]}` : ""));
77
65
  }
@@ -122,40 +110,38 @@ function autoInstallXml(home) {
122
110
  </AutomatedInstallation>`;
123
111
  }
124
112
  /** Download + headless-install veraPDF into ~/.jasy/verapdf. `log` streams progress to the UI. */
125
- export function installVeraPdf(log) {
126
- return __awaiter(this, void 0, void 0, function* () {
127
- const tools = detectTools();
128
- if (!tools.java) {
129
- throw new Error("Java is required (veraPDF is a Java app). Install a JRE 11+ first, then re-run `jasy verapdf --install`.");
130
- }
131
- log(`Java ${tools.java} found.`);
132
- const tmp = mkdtempSync(join(tmpdir(), "jasy-vera-"));
133
- try {
134
- log("Downloading veraPDF (~33 MB) ...");
135
- const res = yield fetch(INSTALLER_URL);
136
- if (!res.ok)
137
- throw new Error(`download failed: HTTP ${res.status}`);
138
- const zip = Buffer.from(yield res.arrayBuffer());
139
- log("Extracting the installer ...");
140
- const jar = extractFromZip(zip, /izpack-installer-[^/]*\.jar$/);
141
- if (!jar)
142
- throw new Error("could not find the veraPDF installer inside the download");
143
- const jarPath = join(tmp, "installer.jar");
144
- const xmlPath = join(tmp, "auto-install.xml");
145
- writeFileSync(jarPath, jar);
146
- writeFileSync(xmlPath, autoInstallXml(VERAPDF_HOME));
147
- rmSync(VERAPDF_HOME, { recursive: true, force: true }); // clean (re)install
148
- log(`Installing into ${VERAPDF_HOME} ...`);
149
- const r = spawnSync("java", ["-Djava.awt.headless=true", "-jar", jarPath, xmlPath], {
150
- encoding: "utf-8",
151
- });
152
- if (!existsSync(MANAGED_BIN)) {
153
- throw new Error("install did not complete: " + (r.stderr || r.stdout || "").split("\n").slice(-3).join(" "));
154
- }
155
- return MANAGED_BIN;
156
- }
157
- finally {
158
- rmSync(tmp, { recursive: true, force: true });
113
+ export async function installVeraPdf(log) {
114
+ const tools = detectTools();
115
+ if (!tools.java) {
116
+ throw new Error("Java is required (veraPDF is a Java app). Install a JRE 11+ first, then re-run `jasy verapdf --install`.");
117
+ }
118
+ log(`Java ${tools.java} found.`);
119
+ const tmp = mkdtempSync(join(tmpdir(), "jasy-vera-"));
120
+ try {
121
+ log("Downloading veraPDF (~33 MB) ...");
122
+ const res = await fetch(INSTALLER_URL);
123
+ if (!res.ok)
124
+ throw new Error(`download failed: HTTP ${res.status}`);
125
+ const zip = Buffer.from(await res.arrayBuffer());
126
+ log("Extracting the installer ...");
127
+ const jar = extractFromZip(zip, /izpack-installer-[^/]*\.jar$/);
128
+ if (!jar)
129
+ throw new Error("could not find the veraPDF installer inside the download");
130
+ const jarPath = join(tmp, "installer.jar");
131
+ const xmlPath = join(tmp, "auto-install.xml");
132
+ writeFileSync(jarPath, jar);
133
+ writeFileSync(xmlPath, autoInstallXml(VERAPDF_HOME));
134
+ rmSync(VERAPDF_HOME, { recursive: true, force: true }); // clean (re)install
135
+ log(`Installing into ${VERAPDF_HOME} ...`);
136
+ const r = spawnSync("java", ["-Djava.awt.headless=true", "-jar", jarPath, xmlPath], {
137
+ encoding: "utf-8",
138
+ });
139
+ if (!existsSync(MANAGED_BIN)) {
140
+ throw new Error("install did not complete: " + (r.stderr || r.stdout || "").split("\n").slice(-3).join(" "));
159
141
  }
160
- });
142
+ return MANAGED_BIN;
143
+ }
144
+ finally {
145
+ rmSync(tmp, { recursive: true, force: true });
146
+ }
161
147
  }
package/dist/index.js CHANGED
@@ -1,13 +1,4 @@
1
1
  #!/usr/bin/env node
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  import { readCommand } from "./commands/read.js";
12
3
  import { validateCommand } from "./commands/validate.js";
13
4
  import { exportCommand } from "./commands/export.js";
@@ -19,41 +10,38 @@ const argv = process.argv.slice(2);
19
10
  const cmd = argv[0];
20
11
  // run a command and return true (then we exit), or open the interactive TUI and return false (it owns
21
12
  // the process). Wrapped in an async fn so `verapdf` can await without needing top-level await.
22
- function dispatch() {
23
- return __awaiter(this, void 0, void 0, function* () {
24
- if (cmd === "read") {
25
- readCommand(argv.slice(1));
26
- return true;
27
- }
28
- if (cmd === "validate") {
29
- validateCommand(argv.slice(1));
30
- return true;
31
- }
32
- if (cmd === "export") {
33
- exportCommand(argv.slice(1));
34
- return true;
35
- }
36
- if (cmd === "verapdf") {
37
- yield verapdfCommand(argv.slice(1));
38
- return true;
39
- }
40
- // shorthand: `jasy some-invoice.pdf` == `jasy read some-invoice.pdf`
41
- if (cmd && /\.(pdf|xml)$/i.test(cmd)) {
42
- readCommand(argv);
43
- return true;
44
- }
45
- if (cmd === "-h" || cmd === "--help") {
46
- printHelp();
47
- return true;
48
- }
49
- launchTui();
50
- return false;
51
- });
13
+ async function dispatch() {
14
+ if (cmd === "read") {
15
+ readCommand(argv.slice(1));
16
+ return true;
17
+ }
18
+ if (cmd === "validate") {
19
+ validateCommand(argv.slice(1));
20
+ return true;
21
+ }
22
+ if (cmd === "export") {
23
+ exportCommand(argv.slice(1));
24
+ return true;
25
+ }
26
+ if (cmd === "verapdf") {
27
+ await verapdfCommand(argv.slice(1));
28
+ return true;
29
+ }
30
+ // shorthand: `jasy some-invoice.pdf` == `jasy read some-invoice.pdf`
31
+ if (cmd && /\.(pdf|xml)$/i.test(cmd)) {
32
+ readCommand(argv);
33
+ return true;
34
+ }
35
+ if (cmd === "-h" || cmd === "--help") {
36
+ printHelp();
37
+ return true;
38
+ }
39
+ launchTui();
40
+ return false;
52
41
  }
53
42
  void dispatch().then((done) => {
54
- var _a;
55
43
  if (done)
56
- process.exit((_a = process.exitCode) !== null && _a !== void 0 ? _a : 0);
44
+ process.exit(process.exitCode ?? 0);
57
45
  });
58
46
  function printHelp() {
59
47
  console.log(`jasy - ZUGFeRD / XRechnung terminal
package/dist/tui/app.js CHANGED
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { readFileSync, writeFileSync } from "node:fs";
11
2
  import { basename, resolve, dirname, join } from "node:path";
12
3
  import { fileURLToPath } from "node:url";
@@ -36,7 +27,7 @@ const CRANE = (() => {
36
27
  .split("\n")
37
28
  .map((l) => l.replace(/\s+$/, "")); // keep leading spaces (alignment), drop trailing
38
29
  }
39
- catch (_a) {
30
+ catch {
40
31
  return []; // no logo file - the start screen just shows the box
41
32
  }
42
33
  })();
@@ -124,7 +115,7 @@ export function launchTui() {
124
115
  }
125
116
  // XML business rules (Schematron) - EN 16931, + XRechnung BR-DE when applicable
126
117
  rows.push({
127
- text: (rules === null || rules === void 0 ? void 0 : rules.profile.startsWith("xrechnung")) ? "XRechnung rules" : "EN 16931 rules",
118
+ text: rules?.profile.startsWith("xrechnung") ? "XRechnung rules" : "EN 16931 rules",
128
119
  fg: INK,
129
120
  status: rules ? (rules.valid ? "OK" : `${rules.errors.length} errors`) : "n/a",
130
121
  statusFg: rules ? (rules.valid ? OK : ERR) : FAINT,
@@ -181,7 +172,7 @@ export function launchTui() {
181
172
  const statusEnd = x + w - 3;
182
173
  const header = buildHeader(w); // pinned
183
174
  const body = buildBody(w); // scrollable
184
- const canExport = !!((loaded === null || loaded === void 0 ? void 0 : loaded.read.invoice) && loaded.read.totals);
175
+ const canExport = !!(loaded?.read.invoice && loaded.read.totals);
185
176
  // the crane sits above the box - only on the true start screen (nothing loaded) and only if it fits
186
177
  const showCrane = !loaded && !error && CRANE.length > 0 && cols >= CRANE_W && screenH >= CRANE.length + 9;
187
178
  const boxTop = TOP + (showCrane ? CRANE.length + 1 : 0);
@@ -196,10 +187,9 @@ export function launchTui() {
196
187
  const visible = body.slice(scroll, scroll + viewH);
197
188
  const boxH = 5 + headBlock + viewH + (notice ? 1 : 0);
198
189
  const drawRow = (r, y) => {
199
- var _a;
200
190
  draw.text(x + 2, y, r.text, { fg: r.fg });
201
191
  if (r.status)
202
- draw.text(statusEnd - r.status.length, y, r.status, { fg: (_a = r.statusFg) !== null && _a !== void 0 ? _a : r.fg });
192
+ draw.text(statusEnd - r.status.length, y, r.status, { fg: r.statusFg ?? r.fg });
203
193
  };
204
194
  draw.clear();
205
195
  if (showCrane)
@@ -255,8 +245,8 @@ export function launchTui() {
255
245
  process.exit(0);
256
246
  }
257
247
  function exportTo(format) {
258
- const inv = loaded === null || loaded === void 0 ? void 0 : loaded.read.invoice;
259
- if (!inv || !(loaded === null || loaded === void 0 ? void 0 : loaded.read.totals))
248
+ const inv = loaded?.read.invoice;
249
+ if (!inv || !loaded?.read.totals)
260
250
  return;
261
251
  const name = `${inv.number.replace(/[^\w.-]+/g, "_")}.${format === "txt" ? "txt" : format}`;
262
252
  try {
@@ -268,35 +258,33 @@ export function launchTui() {
268
258
  }
269
259
  render();
270
260
  }
271
- function openFlow() {
272
- return __awaiter(this, void 0, void 0, function* () {
273
- const chosen = yield openFileDialog({ screen, draw, input, startDir: process.cwd(), quit });
274
- if (chosen) {
275
- notice = null;
276
- scroll = 0;
277
- try {
278
- const bytes = readFileSync(chosen);
279
- const read = readInvoice(bytes);
280
- const pdfa = read.isPdf ? checkPdfA3(bytes) : null;
281
- let rules = null;
282
- if (read.meta.syntax !== "unknown") {
283
- try {
284
- rules = validateInvoiceXml(read.xml, profileFor(read.meta));
285
- }
286
- catch (_a) {
287
- rules = null;
288
- }
261
+ async function openFlow() {
262
+ const chosen = await openFileDialog({ screen, draw, input, startDir: process.cwd(), quit });
263
+ if (chosen) {
264
+ notice = null;
265
+ scroll = 0;
266
+ try {
267
+ const bytes = readFileSync(chosen);
268
+ const read = readInvoice(bytes);
269
+ const pdfa = read.isPdf ? checkPdfA3(bytes) : null;
270
+ let rules = null;
271
+ if (read.meta.syntax !== "unknown") {
272
+ try {
273
+ rules = validateInvoiceXml(read.xml, profileFor(read.meta));
274
+ }
275
+ catch {
276
+ rules = null;
289
277
  }
290
- loaded = { path: chosen, read, pdfa, rules };
291
- error = null;
292
- }
293
- catch (e) {
294
- error = e.message;
295
- loaded = null;
296
278
  }
279
+ loaded = { path: chosen, read, pdfa, rules };
280
+ error = null;
297
281
  }
298
- render();
299
- });
282
+ catch (e) {
283
+ error = e.message;
284
+ loaded = null;
285
+ }
286
+ }
287
+ render();
300
288
  }
301
289
  screen.enter();
302
290
  screen.hideCursor();
@@ -312,7 +300,7 @@ export function launchTui() {
312
300
  void openFlow();
313
301
  return true;
314
302
  }
315
- if ((key.name === "j" || key.name === "t" || key.name === "x") && (loaded === null || loaded === void 0 ? void 0 : loaded.read.invoice)) {
303
+ if ((key.name === "j" || key.name === "t" || key.name === "x") && loaded?.read.invoice) {
316
304
  exportTo(key.name === "j" ? "json" : key.name === "t" ? "txt" : "xlsx");
317
305
  return true;
318
306
  }
@@ -34,7 +34,7 @@ export function openFileDialog({ screen, draw, input, startDir, quit, }) {
34
34
  });
35
35
  items = [...up, ...entries];
36
36
  }
37
- catch (_a) {
37
+ catch {
38
38
  items = up;
39
39
  }
40
40
  state = { selectedIndex: 0, scrollOffset: 0 };
@@ -115,7 +115,7 @@ export function openFileDialog({ screen, draw, input, startDir, quit, }) {
115
115
  if (statSync(path).isFile())
116
116
  close(path);
117
117
  }
118
- catch (_a) {
118
+ catch {
119
119
  /* nothing to open */
120
120
  }
121
121
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jasy/cli",
3
- "version": "1.0.0-alpha.2",
3
+ "version": "1.0.0-alpha.4",
4
4
  "description": "Interactive terminal to validate, read and export ZUGFeRD / XRechnung e-invoices.",
5
5
  "keywords": [
6
6
  "cli",
@@ -24,7 +24,7 @@
24
24
  "dependencies": {
25
25
  "@jano-editor/ui": "1.0.0-alpha.8",
26
26
  "saxon-js": "^2.6.0",
27
- "@jasy/zugferd": "1.0.0-alpha.1"
27
+ "@jasy/zugferd": "1.0.0-alpha.2"
28
28
  },
29
29
  "devDependencies": {
30
30
  "@types/node": "^25.9.3",