feat(core): improve in-memory validation, FatturaPA detection coverage, and published type compatibility
This commit is contained in:
@@ -1,49 +1,59 @@
|
||||
#!/usr/bin/env node
|
||||
/// <reference types="node" />
|
||||
|
||||
/**
|
||||
* Script to download official Schematron files for e-invoice validation
|
||||
*/
|
||||
|
||||
import { SchematronDownloader } from '../dist_ts/formats/validation/schematron.downloader.js';
|
||||
const schematronDownloaderModulePath = '../dist_ts/formats/validation/schematron.downloader.js';
|
||||
|
||||
async function createDownloader() {
|
||||
const { SchematronDownloader } = await import(schematronDownloaderModulePath);
|
||||
const downloader = new SchematronDownloader('assets_downloaded/schematron');
|
||||
await downloader.initialize();
|
||||
return downloader;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('📥 Starting Schematron download...\n');
|
||||
|
||||
const downloader = new SchematronDownloader('assets_downloaded/schematron');
|
||||
await downloader.initialize();
|
||||
const downloader = await createDownloader();
|
||||
|
||||
// Download EN16931 Schematron files
|
||||
console.log('🔵 Downloading EN16931 Schematron files...');
|
||||
try {
|
||||
const en16931Paths = await downloader.downloadStandard('EN16931');
|
||||
console.log(`✅ Downloaded ${en16931Paths.length} EN16931 files`);
|
||||
en16931Paths.forEach(p => console.log(` - ${p}`));
|
||||
en16931Paths.forEach((p: string) => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download EN16931: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`❌ Failed to download EN16931: ${errorMessage}`);
|
||||
}
|
||||
|
||||
console.log('\n🔵 Downloading PEPPOL Schematron files...');
|
||||
try {
|
||||
const peppolPaths = await downloader.downloadStandard('PEPPOL');
|
||||
console.log(`✅ Downloaded ${peppolPaths.length} PEPPOL files`);
|
||||
peppolPaths.forEach(p => console.log(` - ${p}`));
|
||||
peppolPaths.forEach((p: string) => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download PEPPOL: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`❌ Failed to download PEPPOL: ${errorMessage}`);
|
||||
}
|
||||
|
||||
console.log('\n🔵 Downloading XRechnung Schematron files...');
|
||||
try {
|
||||
const xrechnungPaths = await downloader.downloadStandard('XRECHNUNG');
|
||||
console.log(`✅ Downloaded ${xrechnungPaths.length} XRechnung files`);
|
||||
xrechnungPaths.forEach(p => console.log(` - ${p}`));
|
||||
xrechnungPaths.forEach((p: string) => console.log(` - ${p}`));
|
||||
} catch (error) {
|
||||
console.error(`❌ Failed to download XRechnung: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`❌ Failed to download XRechnung: ${errorMessage}`);
|
||||
}
|
||||
|
||||
// List cached files
|
||||
console.log('\n📂 Cached Schematron files:');
|
||||
const cached = await downloader.getCachedFiles();
|
||||
cached.forEach(file => {
|
||||
cached.forEach((file: { path: string; metadata: any }) => {
|
||||
if (file.metadata) {
|
||||
console.log(` - ${file.path}`);
|
||||
console.log(` Version: ${file.metadata.version}`);
|
||||
@@ -65,4 +75,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
});
|
||||
}
|
||||
|
||||
export default main;
|
||||
export default main;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
/// <reference types="node" />
|
||||
|
||||
/**
|
||||
* Download official EN16931 and PEPPOL test samples for conformance testing
|
||||
@@ -142,7 +143,8 @@ async function downloadTestSamples(source: TestSampleSource): Promise<void> {
|
||||
try {
|
||||
await downloadFile(source.repository, source.branch, filePath, targetPath);
|
||||
} catch (error) {
|
||||
console.error(` ❌ Error downloading ${fileName}: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(` ❌ Error downloading ${fileName}: ${errorMessage}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -203,4 +205,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
}
|
||||
|
||||
export default main;
|
||||
export { TEST_SAMPLE_SOURCES };
|
||||
export { TEST_SAMPLE_SOURCES };
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env tsx
|
||||
/// <reference types="node" />
|
||||
/**
|
||||
* Downloads official XRechnung Schematron validation rules
|
||||
* from the KoSIT repositories
|
||||
@@ -179,4 +180,4 @@ if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
});
|
||||
}
|
||||
|
||||
export default downloadXRechnungRules;
|
||||
export default downloadXRechnungRules;
|
||||
|
||||
+34
-18
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
/// <reference types="node" />
|
||||
|
||||
/**
|
||||
* Post-install script to download required validation resources
|
||||
@@ -6,11 +7,19 @@
|
||||
* All users need validation capabilities, so this is mandatory
|
||||
*/
|
||||
|
||||
import { SchematronDownloader } from '../dist_ts/formats/validation/schematron.downloader.js';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
const schematronDownloaderModulePath = '../dist_ts/formats/validation/schematron.downloader.js';
|
||||
|
||||
async function createDownloader() {
|
||||
const { SchematronDownloader } = await import(schematronDownloaderModulePath);
|
||||
const downloader = new SchematronDownloader('assets_downloaded/schematron');
|
||||
await downloader.initialize();
|
||||
return downloader;
|
||||
}
|
||||
|
||||
// Version for cache invalidation
|
||||
const RESOURCES_VERSION = '1.0.0';
|
||||
|
||||
@@ -89,15 +98,15 @@ function saveVersionFile(): void {
|
||||
fs.mkdirSync(path.dirname(versionFile), { recursive: true });
|
||||
fs.writeFileSync(versionFile, RESOURCES_VERSION);
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not save version file:', error.message);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.warn('⚠️ Could not save version file:', errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadSchematron() {
|
||||
console.log('📥 Downloading Schematron validation files...\n');
|
||||
|
||||
const downloader = new SchematronDownloader('assets_downloaded/schematron');
|
||||
await downloader.initialize();
|
||||
const downloader = await createDownloader();
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
@@ -109,7 +118,8 @@ async function downloadSchematron() {
|
||||
console.log(`✅ Downloaded ${en16931Files.length} EN16931 files`);
|
||||
successCount += en16931Files.length;
|
||||
} catch (error) {
|
||||
console.error(`⚠️ Failed to download EN16931: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`⚠️ Failed to download EN16931: ${errorMessage}`);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
@@ -120,7 +130,8 @@ async function downloadSchematron() {
|
||||
console.log(`✅ Downloaded ${peppolFiles.length} PEPPOL files`);
|
||||
successCount += peppolFiles.length;
|
||||
} catch (error) {
|
||||
console.error(`⚠️ Failed to download PEPPOL: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`⚠️ Failed to download PEPPOL: ${errorMessage}`);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
@@ -131,7 +142,8 @@ async function downloadSchematron() {
|
||||
console.log(`✅ Downloaded ${xrechnungFiles.length} XRechnung files`);
|
||||
successCount += xrechnungFiles.length;
|
||||
} catch (error) {
|
||||
console.error(`⚠️ Failed to download XRechnung: ${error.message}`);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error(`⚠️ Failed to download XRechnung: ${errorMessage}`);
|
||||
failCount++;
|
||||
}
|
||||
|
||||
@@ -232,13 +244,14 @@ async function main() {
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (attempts < maxAttempts) {
|
||||
console.log(`\n⚠️ Download failed: ${error.message}`);
|
||||
console.log(' Retrying...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3s before retry
|
||||
}
|
||||
} catch (error) {
|
||||
lastError = error;
|
||||
if (attempts < maxAttempts) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.log(`\n⚠️ Download failed: ${errorMessage}`);
|
||||
console.log(' Retrying...');
|
||||
await new Promise(resolve => setTimeout(resolve, 3000)); // Wait 3s before retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,13 +263,15 @@ async function main() {
|
||||
console.error();
|
||||
|
||||
if (lastError) {
|
||||
console.error(' Last error:', lastError.message);
|
||||
const errorMessage = lastError instanceof Error ? lastError.message : String(lastError);
|
||||
console.error(' Last error:', errorMessage);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
// Catch-all for unexpected errors
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error();
|
||||
console.error('⚠️ Unexpected error during resource setup:', error.message);
|
||||
console.error('⚠️ Unexpected error during resource setup:', errorMessage);
|
||||
console.error(' This won\'t affect library installation');
|
||||
console.error(' Resources will be downloaded on first use');
|
||||
console.error();
|
||||
@@ -266,10 +281,11 @@ async function main() {
|
||||
// Only run if this is the main module
|
||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||
main().catch(error => {
|
||||
console.error('⚠️ Resource setup error:', error.message);
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
console.error('⚠️ Resource setup error:', errorMessage);
|
||||
// Never fail the install
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
export default main;
|
||||
export default main;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
# ts_install 🧰
|
||||
|
||||
Install-time bootstrap module for `@fin.cx/einvoice`.
|
||||
|
||||
This directory contains the scripts that download optional validation resources and external test data used by the main library.
|
||||
|
||||
## Issue Reporting and Security
|
||||
|
||||
For reporting bugs, issues, or security vulnerabilities, please visit [community.foss.global/](https://community.foss.global/). This is the central community hub for all issue reporting. Developers who sign and comply with our contribution agreement and go through identification can also get a [code.foss.global/](https://code.foss.global/) account to submit Pull Requests directly.
|
||||
|
||||
## What it does
|
||||
|
||||
- runs the package `postinstall` bootstrap
|
||||
- checks whether downloaded Schematron resources are already present and current
|
||||
- skips cleanly when offline or when install context is not valid
|
||||
- downloads validation resources into `assets_downloaded/schematron`
|
||||
- exposes manual scripts for maintainers and advanced users
|
||||
|
||||
## Runtime behavior
|
||||
|
||||
- it only runs automatically when `npm_lifecycle_event === 'postinstall'`
|
||||
- it never fails package installation on download/setup problems
|
||||
- it skips when `dist_ts/` is missing
|
||||
- it skips in CI when `EINVOICE_SKIP_RESOURCES` is set
|
||||
- it keeps a `.version` marker to avoid unnecessary re-downloads
|
||||
|
||||
## Manual commands
|
||||
|
||||
```bash
|
||||
pnpm download-schematron
|
||||
pnpm download-test-samples
|
||||
```
|
||||
|
||||
## Output locations
|
||||
|
||||
- `assets_downloaded/schematron/`: downloaded validation rules
|
||||
- `test-samples/`: downloaded external sample corpus for conformance tests
|
||||
|
||||
## Who needs this?
|
||||
|
||||
- library consumers who want richer validation resources available locally
|
||||
- maintainers working on Schematron validation or conformance testing
|
||||
- CI or packaging flows that want explicit control over resource setup
|
||||
|
||||
For normal basic XML/PDF parsing and conversion, the main package API remains the primary entrypoint.
|
||||
Reference in New Issue
Block a user