Documentation
VAMPIRE JS – documentation
VAMPIRE JS is VAMPIRE in JavaScript. It uses the same rule packages as VAMPIRE PHP and produces the same validation report for the same invoice. It runs in Node, in the browser and in Office add-ins, and in the browser it checks the invoice without uploading it.
What matters
To our knowledge, VAMPIRE PHP is the only tool that runs the official validation rules for e-invoices completely in pure PHP. VAMPIRE JS brings the same rule packages to pure JavaScript. Neither needs Java or an extra process, and the invoice never leaves your system.
Evidence: 220 of 220 invoices for which VAMPIRE and the official KoSIT validator reached the same result, measured on October 9, 2026.
42 ms
average check time per invoice with VAMPIRE JS in Node, taken over 253 invoices
measured on October 9, 2026
1,163–1,193 ms
check time of VAMPIRE JS in Chrome for a large XRechnung in UBL with 200 invoice lines
measured on October 9, 2026
3,459 of 3,459
files for which the schema check of VAMPIRE JS reached the same result as the XML library libxml2
measured on October 9, 2026
29,942 bytes
download size of VAMPIRE JS for the browser, that is the file vampire.js compressed with gzip, without rule packages
measured on October 9, 2026
How to get VAMPIRE JS
VAMPIRE JS is a paid product that you buy in the shop on invoicePress.de. You receive the package as a .tgz file, and npm installs it directly from that file; the individual steps are in the npm guide. The package needs only one outside component, the XML parser for Node, and it ships that component itself. So there is nothing else for you to track down.
version 18 and later, also on the command line
even in an HTML file without a web server
checks right inside the add-in
Your first check in five lines, in Node
The following file checks one invoice. It prints the result, that is accepted or rejected, and the number of findings. The file runs exactly as shown here. Call it in the folder of your package.json with node check.mjs invoice.xml.
// SPDX-License-Identifier: MIT
// Checks one e-invoice with VAMPIRE JS: node check.mjs invoice.xml ( run it in the folder of your package.json )
import { readFileSync } from 'node:fs';
import { Validator } from '@specialpress/einvoice-validator-js';
import { fromVampireFolder, DOMParser } from '@specialpress/einvoice-validator-js/node';
const rules = fromVampireFolder( 'node_modules/@specialpress/einvoice-validator-js/vampire' );
const validator = new Validator( { scenarios: rules.scenarios, loadRuleset: rules.loadRuleset, loadSchema: rules.loadSchema } );
const xml = readFileSync( process.argv[ 2 ], 'utf8' ).replace( /^/, '' );
const report = await validator.validate( new DOMParser().parseFromString( xml, 'text/xml' ), { name: process.argv[ 2 ] } );
console.log( process.argv[ 2 ] + ': ' + { true: 'accepted', false: 'rejected', null: 'no result' }[ report.accepted ], report.counts );
⚠️ Remove the byte order mark before you call the parser. Some XML files start with this invisible marker. Without .replace( /^/, '' ), the parser throws an exception on such a file and returns no report.
Findings, positive validation report, and KoSIT format
// SPDX-License-Identifier: MIT
// Checks one e-invoice with VAMPIRE JS, prints the findings and writes the reports: node report.mjs invoice.xml
// report.json the report, with the positive validation report ( every rule with its result )
// report-kosit.xml the report in the format of the KoSIT validator
import { readFileSync, writeFileSync } from 'node:fs';
import { Validator, kositXmlReport } from '@specialpress/einvoice-validator-js';
import { fromVampireFolder, DOMParser } from '@specialpress/einvoice-validator-js/node';
const rules = fromVampireFolder( 'node_modules/@specialpress/einvoice-validator-js/vampire' );
const validator = new Validator( { scenarios: rules.scenarios, loadRuleset: rules.loadRuleset, loadSchema: rules.loadSchema, ruleReport: true } );
// The bytes for the checksum of the positive report, the text without byte order mark for the parser.
const bytes = readFileSync( process.argv[ 2 ] );
const text = bytes.toString( 'utf8' ).replace( /^/, '' );
const doc = new DOMParser().parseFromString( text, 'text/xml' );
const report = await validator.validate( doc, { name: process.argv[ 2 ], source: bytes } );
for ( const f of report.findings ) console.log( f.severity.padEnd( 12 ), ( f.rule ?? '-' ).padEnd( 14 ), f.text.trim() );
console.log( report.ruleReport.counts );
writeFileSync( 'report.json', JSON.stringify( report, null, 2 ) );
writeFileSync( 'report-kosit.xml', kositXmlReport( report, { document: doc, source: text } ) );
process.exit( report.accepted === true ? 0 : 1 );
report.accepted is true, false or null. null means there is no result. This happens when a rule set the invoice needs is missing. A rule set holds the validation rules of one format in one version, for example the rules of XRechnung 3.0.2. report.ruleReport is the positive validation report: it lists every checked rule with its result on a line of its own. It appears only if you set ruleReport: true.
How to check in the browser
For the browser, VAMPIRE JS lives in the folder vampire/. It holds the validator vampire.js and the rule packages. Your page includes vampire/vampire.js, and the script then registers itself under the name einvoiceVampire. It loads the rule packages from the same folder on first use. The invoice is read and checked in the browser and is never uploaded. The page even works as an HTML file that you open on your computer with a double click, with no web server at all.
<!doctype html>
<!-- SPDX-License-Identifier: MIT -->
<!--
An e-invoice check in a plain web page with VAMPIRE JS. Put this file next to the folder vampire/ of your VAMPIRE JS
copy. The invoice is read and checked in the browser; nothing is uploaded. The page also works when opened by
double click ( file:// ), because vampire.js loads its rule packages as scripts, not with fetch.
-->
<html lang="en">
<meta charset="utf-8">
<title>E-invoice check</title>
<input type="file" id="file" accept=".xml">
<pre id="out"></pre>
<script src="vampire/vampire.js"></script>
<script>
document.getElementById( 'file' ).addEventListener( 'change', async ( event ) => {
const file = event.target.files[ 0 ];
const report = await einvoiceVampire.validate( await file.text(), { name: file.name, lang: 'en', ruleReport: true } );
document.getElementById( 'out' ).textContent = report.text + '\n\n' + JSON.stringify( report.ruleReport.counts );
} );
</script>
</html>
How to check several versions at once
einvoiceVampire.cascade( xml ) checks a ZUGFeRD or Factur-X invoice first against the rules of the newest version and, if it is rejected there, against the rules of the next older one. The check stops at the first version under whose rules the invoice is accepted. If no version accepts the invoice, the function returns the report of the newest version, because the invoice is then faulty under every version, and the newest one is the authoritative one. With { full: true }, the function checks every version and returns a table with one row per version, so you see the result of every single version. For this, the folder vampire/ must hold the rule packages of every version. The playground will run exactly this function.
How to check on the command line
npx einvoice-validate-js validate invoice.xml npx einvoice-validate-js validate invoice.xml --format=xml npx einvoice-validate-js validate invoice.xml --format=json --rule-report npx einvoice-validate-js selftest
Text output, JSON output and exit codes are the same as in VAMPIRE PHP, so if you use both editions you do not have to adjust anything. The exit code is 0 for an accepted invoice. It is 1 for a rejected invoice or for an invoice without a result, and 2 for an invalid call. The option --format=xml prints the report in the format of the KoSIT validator.
How we measured this is described in the section "How it was measured" at the end of this page. If VAMPIRE JS fits your project, buy it in the shop on invoicePress.de.
How it was measured
- 220 of 220 invoices for which VAMPIRE and the official KoSIT validator reached the same result (measured on October 9, 2026): We checked 253 test invoices with VAMPIRE and with the official KoSIT validator. Both could match 220 of them to a rule set, and for all 220 both reached the same result, that is accepted or rejected. For 215 of these 220 invoices, exactly the same rules fired as well; for the other five, the difference comes from the version of the EN 16931 rules for CII, because the KoSIT validator ships version 1.3.15 and VAMPIRE ships version 1.3.16. Neither validator could match the remaining 33 invoices to a rule set. Date of the measurement: October 9, 2026.
- 42 ms average check time per invoice with VAMPIRE JS in Node, taken over 253 invoices (measured on October 9, 2026): We measured the check time over the same 253 invoices on a development machine. VAMPIRE JS in Node needed 42 ms per invoice on average; this includes the schema check but not reading and parsing the file. VAMPIRE PHP needed 261 ms per invoice in the same comparison, there including reading and parsing.
- 1,163–1,193 ms check time of VAMPIRE JS in Chrome for a large XRechnung in UBL with 200 invoice lines (measured on October 9, 2026): We measured the check time in the browser with three invoices. Chrome needed 1,163 to 1,193 ms for an XRechnung in UBL with 200 lines, 743 to 999 ms for the same invoice in CII, and 146 ms on the first run and 52 ms on the second for a Factur-X invoice in the EXTENDED profile. For the UBL invoice, Edge needed 1,022 to 1,202 ms and Firefox 1,218 to 2,120 ms, with the first run being the slowest. All three browsers reached the same results.
- 3,459 of 3,459 files for which the schema check of VAMPIRE JS reached the same result as the XML library libxml2 (measured on October 9, 2026): VAMPIRE JS checks the structure of an invoice with a model translated from the XSD schema files. We compared this model with the XML library libxml2 on the test corpus and on faulty copies made from it, 3,459 files in total, of which libxml2 rejects 2,528. The result matched in all 3,459 cases. The schema check is still not a complete XSD validation, and every report says so.
- 29,942 bytes download size of VAMPIRE JS for the browser, that is the file vampire.js compressed with gzip, without rule packages (measured on October 9, 2026): We measured the size of the file vampire.js, which holds the browser part of VAMPIRE JS. It is 88,071 bytes in size and is transferred as 29,942 bytes when compressed with gzip. The rule packages are not included; the browser loads them from the same folder on first use.
VAMPIRE PHP free, VAMPIRE JS in the shop.
VAMPIRE PHP and all rule packages are free and available on GitHub and Packagist. You can buy VAMPIRE JS in the shop on invoicePress.de.
