8 Commits

Author SHA1 Message Date
58506e287d refactor: Move downloaded resources from assets/ to assets_downloaded/
- Changed default download location to assets_downloaded/schematron
- Updated all references in SchematronDownloader, integration, and validator
- Updated postinstall scripts to use new location
- assets_downloaded/ is already in .gitignore to exclude downloaded files from git
- Moved existing downloaded files to new location
- All functionality tested and working correctly
2025-08-12 05:25:50 +00:00
b89da0ec3f update 2025-08-12 05:14:29 +00:00
8dd5509da6 fix(schematron): Correct download URLs for XRechnung and PEPPOL validation files
- Fixed XRechnung URLs to use correct filenames (XRechnung-UBL-validation.sch)
- Removed non-existent PEPPOL-T10 and PEPPOL-T14 files
- Added PEPPOL-EN16931-CII validation file
- All 7 validation files now download successfully:
  - 3 EN16931 files (UBL, CII, EDIFACT)
  - 2 PEPPOL files (UBL, CII)
  - 2 XRechnung files (UBL, CII)
2025-08-12 05:14:11 +00:00
2f597d79df feat(postinstall): Add robust postinstall resource download with version checking
- Implemented version checking to avoid unnecessary re-downloads
- Added retry logic with 3 attempts for network failures
- Included network connectivity detection
- Graceful error handling that never fails the install
- Support for CI environment variable (EINVOICE_SKIP_RESOURCES)
- Successfully tested all functionality including version checking and error handling
2025-08-12 05:02:42 +00:00
fcbe8151b7 5.1.1
Some checks failed
Default (tags) / security (push) Failing after 25s
Default (tags) / test (push) Failing after 17s
Default (tags) / release (push) Has been skipped
Default (tags) / metadata (push) Has been skipped
2025-08-11 19:35:16 +00:00
a106d66a10 fix(build/publishing): Remove migration guide and update publishing and schematron download configurations 2025-08-11 19:35:16 +00:00
cdb30d867d fix(docs): correct decimal calculation example in README
- Fixed method name from calculateLineTotal to calculateLineNet
- Corrected parameter order (quantity, unitPrice, discount)
- Fixed currency parameter location (constructor not method)
- Corrected calculation result from 3141.49 to 3141.56 (proper EUR rounding)
2025-08-11 19:24:19 +00:00
bc3028af55 refactor: move scripts to ts_install directory 2025-08-11 19:08:51 +00:00
24 changed files with 338 additions and 1580 deletions

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ dist_*/
# custom # custom
test/output test/output
.serena .serena
assets_downloaded/

View File

@@ -1,243 +0,0 @@
# Migration Guide: XInvoice to EInvoice (v4.x to v5.x)
This guide helps you migrate from `@fin.cx/xinvoice` v4.x to `@fin.cx/einvoice` v5.x.
## Overview
Version 5.0.0 introduces a complete rebranding from XInvoice to EInvoice. The name change better reflects the library's purpose as a comprehensive electronic invoice (e-invoice) processing solution that supports multiple international standards.
## Breaking Changes
### 1. Package Name Change
**Old:**
```json
"dependencies": {
"@fin.cx/xinvoice": "^4.3.0"
}
```
**New:**
```json
"dependencies": {
"@fin.cx/einvoice": "^5.0.0"
}
```
### 2. Import Changes
**Old:**
```typescript
import { XInvoice } from '@fin.cx/xinvoice';
import type { XInvoiceOptions } from '@fin.cx/xinvoice';
```
**New:**
```typescript
import { EInvoice } from '@fin.cx/einvoice';
import type { EInvoiceOptions } from '@fin.cx/einvoice';
```
### 3. Class Name Changes
**Old:**
```typescript
const invoice = new XInvoice();
const invoiceFromXml = await XInvoice.fromXml(xmlString);
const invoiceFromPdf = await XInvoice.fromPdf(pdfBuffer);
```
**New:**
```typescript
const invoice = new EInvoice();
const invoiceFromXml = await EInvoice.fromXml(xmlString);
const invoiceFromPdf = await EInvoice.fromPdf(pdfBuffer);
```
### 4. Type/Interface Changes
**Old:**
```typescript
const options: XInvoiceOptions = {
validateOnLoad: true,
validationLevel: ValidationLevel.BUSINESS
};
```
**New:**
```typescript
const options: EInvoiceOptions = {
validateOnLoad: true,
validationLevel: ValidationLevel.BUSINESS
};
```
## New Features in v5.x
### Enhanced Error Handling
Version 5.0.0 introduces specialized error classes for better error handling:
```typescript
import {
EInvoiceError,
EInvoiceParsingError,
EInvoiceValidationError,
EInvoicePDFError,
EInvoiceFormatError
} from '@fin.cx/einvoice';
try {
const invoice = await EInvoice.fromXml(xmlString);
} catch (error) {
if (error instanceof EInvoiceParsingError) {
console.error('Parsing failed:', error.getLocationMessage());
console.error('Suggestions:', error.getDetailedMessage());
} else if (error instanceof EInvoiceValidationError) {
console.error('Validation report:', error.getValidationReport());
} else if (error instanceof EInvoicePDFError) {
console.error('PDF operation failed:', error.message);
console.error('Recovery suggestions:', error.getRecoverySuggestions());
}
}
```
### Error Recovery
The new version includes error recovery capabilities:
```typescript
import { ErrorRecovery } from '@fin.cx/einvoice';
// Attempt to recover from XML parsing errors
const recovery = await ErrorRecovery.attemptXMLRecovery(xmlString, parsingError);
if (recovery.success && recovery.cleanedXml) {
const invoice = await EInvoice.fromXml(recovery.cleanedXml);
}
```
## Step-by-Step Migration
### 1. Update your package.json
```bash
# Remove old package
pnpm remove @fin.cx/xinvoice
# Install new package
pnpm add @fin.cx/einvoice
```
### 2. Update imports using find and replace
Find all occurrences of:
- `@fin.cx/xinvoice``@fin.cx/einvoice`
- `XInvoice``EInvoice`
- `XInvoiceOptions``EInvoiceOptions`
### 3. Update your code
Example migration:
**Before:**
```typescript
import { XInvoice, ValidationLevel } from '@fin.cx/xinvoice';
async function processInvoice(xmlData: string) {
try {
const xinvoice = await XInvoice.fromXml(xmlData);
const validation = await xinvoice.validate(ValidationLevel.BUSINESS);
if (!validation.valid) {
throw new Error('Validation failed');
}
return xinvoice;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
```
**After:**
```typescript
import { EInvoice, ValidationLevel, EInvoiceValidationError } from '@fin.cx/einvoice';
async function processInvoice(xmlData: string) {
try {
const einvoice = await EInvoice.fromXml(xmlData);
const validation = await einvoice.validate(ValidationLevel.BUSINESS);
if (!validation.valid) {
throw new EInvoiceValidationError(
'Invoice validation failed',
validation.errors
);
}
return einvoice;
} catch (error) {
if (error instanceof EInvoiceValidationError) {
console.error('Validation Report:', error.getValidationReport());
}
throw error;
}
}
```
### 4. Update your tests
Update test imports and class names:
**Before:**
```typescript
import { XInvoice } from '@fin.cx/xinvoice';
import { expect } from '@push.rocks/tapbundle';
test('should create invoice', async () => {
const invoice = new XInvoice();
expect(invoice).toBeInstanceOf(XInvoice);
});
```
**After:**
```typescript
import { EInvoice } from '@fin.cx/einvoice';
import { expect } from '@push.rocks/tapbundle';
test('should create invoice', async () => {
const invoice = new EInvoice();
expect(invoice).toBeInstanceOf(EInvoice);
});
```
## Compatibility
### Unchanged APIs
The following APIs remain unchanged:
- All method signatures on the main class
- All validation levels and invoice formats
- All export formats
- The structure of validation results
- PDF handling capabilities
### Deprecated Features
None. This is a pure rebranding release with enhanced error handling.
## Need Help?
If you encounter any issues during migration:
1. Check the [changelog](./changelog.md) for detailed changes
2. Review the updated [documentation](./readme.md)
3. Report issues at [GitHub Issues](https://github.com/fin-cx/einvoice/issues)
## Why the Name Change?
- **EInvoice** (electronic invoice) is more universally recognized
- Better represents support for multiple international standards
- Aligns with industry terminology (e-invoicing, e-invoice)
- More intuitive for new users discovering the library

View File

@@ -1,7 +0,0 @@
{
"source": "EN16931-CII",
"version": "1.3.14",
"url": "https://github.com/ConnectingEurope/eInvoicing-EN16931/raw/master/cii/schematron/EN16931-CII-validation.sch",
"format": "CII",
"downloadDate": "2025-08-11T11:05:40.209Z"
}

View File

@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed under European Union Public Licence (EUPL) version 1.2.
-->
<!--
CII syntax binding to the TC434
-->
<schema xmlns="http://purl.oclc.org/dsdl/schematron"
xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
xmlns:ccts="urn:un:unece:uncefact:documentation:standard:CoreComponentsTechnicalSpecification:2"
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"
xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
queryBinding="xslt2">
<title>EN16931 model bound to CII</title>
<ns prefix="rsm" uri="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"/>
<ns prefix="ccts" uri="urn:un:unece:uncefact:documentation:standard:CoreComponentsTechnicalSpecification:2"/>
<ns prefix="udt" uri="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"/>
<ns prefix="qdt" uri="urn:un:unece:uncefact:data:standard:QualifiedDataType:100"/>
<ns prefix="ram" uri="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"/>
<ns prefix="xs" uri="http://www.w3.org/2001/XMLSchema"/>
<phase id="EN16931-model-phase">
<active pattern="EN16931-CII-Model"/>
</phase>
<phase id="codelist_phase">
<active pattern="EN16931-Codes"/>
</phase>
<phase id="syntax_phase">
<active pattern="EN16931-CII-Syntax"/>
</phase>
<!-- Abstract CEN BII patterns -->
<!-- ========================= -->
<include href="abstract/EN16931-CII-model.sch"/>
<include href="abstract/EN16931-CII-syntax.sch"/>
<!-- Data Binding parameters -->
<!-- ======================= -->
<include href="CII/EN16931-CII-model.sch"/>
<include href="CII/EN16931-CII-syntax.sch"/>
<!-- Code Lists Binding rules -->
<!-- ======================== -->
<include href="codelist/EN16931-CII-codes.sch"/>
</schema>

View File

@@ -1,7 +0,0 @@
{
"source": "EN16931-EDIFACT",
"version": "1.3.14",
"url": "https://github.com/ConnectingEurope/eInvoicing-EN16931/raw/master/edifact/schematron/EN16931-EDIFACT-validation.sch",
"format": "CII",
"downloadDate": "2025-08-11T11:05:40.547Z"
}

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed under European Union Public Licence (EUPL) version 1.2.
-->
<!--
EDIFACT syntax binding to the EN16931
Author: Andreas Pelekies
Timestamp: 2016-10-31 00:00:00 +0200
-->
<schema xmlns="http://purl.oclc.org/dsdl/schematron" queryBinding="xslt2">
<title>EN16931 model bound to EDIFACT</title>
<phase id="EN16931-model-phase">
<active pattern="EN16931-EDIFACT-Model"/>
</phase>
<phase id="codelist_phase">
<active pattern="EN16931-Codes"/>
</phase>
<phase id="syntax_phase">
<active pattern="EN16931-EDIFACT-Syntax"/>
</phase>
<!-- Abstract CEN BII patterns -->
<!-- ========================= -->
<include href="abstract/EN16931-EDIFACT-model.sch"/>
<include href="abstract/EN16931-EDIFACT-syntax.sch"/>
<!-- Data Binding parameters -->
<!-- ======================= -->
<include href="EDIFACT/EN16931-EDIFACT-model.sch"/>
<include href="EDIFACT/EN16931-EDIFACT-syntax.sch"/>
<!-- Code Lists Binding rules -->
<!-- ======================== -->
<include href="codelist/EN16931-EDIFACT-codes.sch"/>
</schema>

View File

@@ -1,7 +0,0 @@
{
"source": "EN16931-UBL",
"version": "1.3.14",
"url": "https://github.com/ConnectingEurope/eInvoicing-EN16931/raw/master/ubl/schematron/EN16931-UBL-validation.sch",
"format": "UBL",
"downloadDate": "2025-08-11T11:05:39.868Z"
}

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed under European Union Public Licence (EUPL) version 1.2.
-->
<schema xmlns="http://purl.oclc.org/dsdl/schematron" xmlns:cac="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2" xmlns:cbc="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2" xmlns:cn="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2" xmlns:UBL="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2" queryBinding="xslt2">
<title>EN16931 model bound to UBL</title>
<ns prefix="ext" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonExtensionComponents-2"/>
<ns prefix="cbc" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2"/>
<ns prefix="cac" uri="urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2"/>
<ns prefix="qdt" uri="urn:oasis:names:specification:ubl:schema:xsd:QualifiedDataTypes-2"/>
<ns prefix="udt" uri="urn:oasis:names:specification:ubl:schema:xsd:UnqualifiedDataTypes-2"/>
<ns prefix="cn" uri="urn:oasis:names:specification:ubl:schema:xsd:CreditNote-2"/>
<ns prefix="ubl" uri="urn:oasis:names:specification:ubl:schema:xsd:Invoice-2"/>
<ns prefix="xs" uri="http://www.w3.org/2001/XMLSchema"/>
<phase id="EN16931model_phase">
<active pattern="UBL-model"/>
</phase>
<phase id="codelist_phase">
<active pattern="Codesmodel"/>
</phase>
<!-- Abstract CEN BII patterns -->
<!-- ========================= -->
<include href="abstract/EN16931-model.sch"/>
<include href="abstract/EN16931-syntax.sch"/>
<!-- Data Binding parameters -->
<!-- ======================= -->
<include href="UBL/EN16931-UBL-model.sch"/>
<include href="UBL/EN16931-UBL-syntax.sch"/>
<!-- Code Lists Binding rules -->
<!-- ======================== -->
<include href="codelist/EN16931-UBL-codes.sch"/>
</schema>

View File

@@ -1,7 +0,0 @@
{
"source": "PEPPOL-EN16931-UBL",
"version": "3.0.17",
"url": "https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/PEPPOL-EN16931-UBL.sch",
"format": "UBL",
"downloadDate": "2025-08-11T11:05:40.954Z"
}

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
404: Not Found

View File

@@ -1,5 +1,14 @@
# Changelog # Changelog
## 2025-08-11 - 5.1.1 - fix(build/publishing)
Remove migration guide and update publishing and schematron download configurations
- Deleted MIGRATION.md as migration instructions are no longer needed in v5.x
- Updated .claude/settings.local.json to include new permission settings
- Changed import in ts_install/download-schematron.ts to use 'dist_ts' instead of 'ts'
- Added tspublish.json files in both ts and ts_install with an explicit order configuration
- Refined publishing configuration (ts/tspublish.json) to align with new build process
## 2025-01-11 - 5.1.0 - feat(compliance) ## 2025-01-11 - 5.1.0 - feat(compliance)
Achieve 100% EN16931 compliance with comprehensive validation support Achieve 100% EN16931 compliance with comprehensive validation support

View File

@@ -1,6 +1,6 @@
{ {
"name": "@fin.cx/einvoice", "name": "@fin.cx/einvoice",
"version": "5.1.0", "version": "5.1.1",
"private": false, "private": false,
"description": "A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.", "description": "A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.",
"main": "dist_ts/index.js", "main": "dist_ts/index.js",
@@ -10,10 +10,11 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"test": "(tstest test/ --verbose --logfile --timeout 60)", "test": "(tstest test/ --verbose --logfile --timeout 60)",
"build": "(tsbuild --web --allowimplicitany)", "build": "(tsbuild tsfolders --allowimplicitany)",
"buildDocs": "(tsdoc)", "buildDocs": "(tsdoc)",
"download-schematron": "tsx scripts/download-schematron.ts", "postinstall": "node dist_ts_install/index.js 2>/dev/null || true",
"download-test-samples": "tsx scripts/download-test-samples.ts", "download-schematron": "tsx ts_install/download-schematron.ts",
"download-test-samples": "tsx ts_install/download-test-samples.ts",
"test:conformance": "tstest test/test.conformance-harness.ts" "test:conformance": "tstest test/test.conformance-harness.ts"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -178,13 +178,13 @@ No more floating-point errors! Built-in arbitrary precision arithmetic:
```typescript ```typescript
// Perfect financial calculations every time // Perfect financial calculations every time
const calculator = new DecimalCurrencyCalculator(); const calculator = new DecimalCurrencyCalculator('EUR');
const result = calculator.calculateLineTotal( const result = calculator.calculateLineNet(
'999.99', // Unit price
'3.14159', // Quantity '3.14159', // Quantity
'EUR' // Currency-aware rounding '999.99', // Unit price
'0' // Discount (optional)
); );
// Result: 3141.49 (correctly rounded for EUR) // Result: 3141.56 (correctly rounded for EUR)
``` ```
### 🔍 Multi-Level Validation ### 🔍 Multi-Level Validation

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@fin.cx/einvoice', name: '@fin.cx/einvoice',
version: '5.0.0', version: '5.1.1',
description: 'A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.' description: 'A TypeScript module for creating, manipulating, and embedding XML data within PDF files specifically tailored for electronic invoice (einvoice) packages.'
} }

View File

@@ -44,14 +44,14 @@ export const SCHEMATRON_SOURCES: Record<string, SchematronSource[]> = {
{ {
name: 'XRechnung-UBL', name: 'XRechnung-UBL',
version: '3.0.2', version: '3.0.2',
url: 'https://github.com/itplr-kosit/xrechnung-schematron/raw/master/src/schematron/ubl-invoice/XRechnung-UBL-3.0.sch', url: 'https://github.com/itplr-kosit/xrechnung-schematron/raw/master/src/validation/schematron/ubl/XRechnung-UBL-validation.sch',
description: 'XRechnung CIUS validation for UBL', description: 'XRechnung CIUS validation for UBL',
format: 'UBL' format: 'UBL'
}, },
{ {
name: 'XRechnung-CII', name: 'XRechnung-CII',
version: '3.0.2', version: '3.0.2',
url: 'https://github.com/itplr-kosit/xrechnung-schematron/raw/master/src/schematron/cii/XRechnung-CII-3.0.sch', url: 'https://github.com/itplr-kosit/xrechnung-schematron/raw/master/src/validation/schematron/cii/XRechnung-CII-validation.sch',
description: 'XRechnung CIUS validation for CII', description: 'XRechnung CIUS validation for CII',
format: 'CII' format: 'CII'
} }
@@ -61,22 +61,15 @@ export const SCHEMATRON_SOURCES: Record<string, SchematronSource[]> = {
name: 'PEPPOL-EN16931-UBL', name: 'PEPPOL-EN16931-UBL',
version: '3.0.17', version: '3.0.17',
url: 'https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/PEPPOL-EN16931-UBL.sch', url: 'https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/PEPPOL-EN16931-UBL.sch',
description: 'PEPPOL BIS Billing 3.0 validation rules', description: 'PEPPOL BIS Billing 3.0 validation rules for UBL',
format: 'UBL' format: 'UBL'
}, },
{ {
name: 'PEPPOL-T10', name: 'PEPPOL-EN16931-CII',
version: '3.0.17', version: '3.0.17',
url: 'https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/UBL-T10.sch', url: 'https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/PEPPOL-EN16931-CII.sch',
description: 'PEPPOL Transaction 10 (Invoice) validation', description: 'PEPPOL BIS Billing 3.0 validation rules for CII',
format: 'UBL' format: 'CII'
},
{
name: 'PEPPOL-T14',
version: '3.0.17',
url: 'https://github.com/OpenPEPPOL/peppol-bis-invoice-3/raw/master/rules/sch/UBL-T14.sch',
description: 'PEPPOL Transaction 14 (Credit Note) validation',
format: 'UBL'
} }
] ]
}; };
@@ -88,7 +81,7 @@ export class SchematronDownloader {
private cacheDir: string; private cacheDir: string;
private smartfile: any; private smartfile: any;
constructor(cacheDir: string = 'assets/schematron') { constructor(cacheDir: string = 'assets_downloaded/schematron') {
this.cacheDir = cacheDir; this.cacheDir = cacheDir;
} }
@@ -285,7 +278,7 @@ export const ISO_SCHEMATRON_SKELETONS = {
/** /**
* Download ISO Schematron skeleton files * Download ISO Schematron skeleton files
*/ */
export async function downloadISOSkeletons(targetDir: string = 'assets/schematron/iso'): Promise<void> { export async function downloadISOSkeletons(targetDir: string = 'assets_downloaded/schematron/iso'): Promise<void> {
await fs.mkdir(targetDir, { recursive: true }); await fs.mkdir(targetDir, { recursive: true });
console.log('Downloading ISO Schematron skeleton files...'); console.log('Downloading ISO Schematron skeleton files...');

View File

@@ -75,7 +75,7 @@ export class IntegratedValidator {
standard: 'EN16931' | 'PEPPOL' | 'XRECHNUNG', standard: 'EN16931' | 'PEPPOL' | 'XRECHNUNG',
format: 'UBL' | 'CII' format: 'UBL' | 'CII'
): Promise<string | null> { ): Promise<string | null> {
const basePath = 'assets/schematron'; const basePath = 'assets_downloaded/schematron';
// Map standard and format to file pattern // Map standard and format to file pattern
const patterns: Record<string, Record<string, string>> = { const patterns: Record<string, Record<string, string>> = {

View File

@@ -272,19 +272,19 @@ export async function createStandardValidator(
switch (standard) { switch (standard) {
case 'EN16931': case 'EN16931':
// Would load from ConnectingEurope/eInvoicing-EN16931 // Would load from ConnectingEurope/eInvoicing-EN16931
await validator.loadSchematron('assets/schematron/en16931/EN16931-UBL-validation.sch'); await validator.loadSchematron('assets_downloaded/schematron/en16931/EN16931-UBL-validation.sch');
break; break;
case 'XRECHNUNG': case 'XRECHNUNG':
// Would load from itplr-kosit/xrechnung-schematron // Would load from itplr-kosit/xrechnung-schematron
await validator.loadSchematron('assets/schematron/xrechnung/XRechnung-UBL-validation.sch'); await validator.loadSchematron('assets_downloaded/schematron/xrechnung/XRechnung-UBL-validation.sch');
break; break;
case 'PEPPOL': case 'PEPPOL':
// Would load from OpenPEPPOL/peppol-bis-invoice-3 // Would load from OpenPEPPOL/peppol-bis-invoice-3
await validator.loadSchematron('assets/schematron/peppol/PEPPOL-EN16931-UBL.sch'); await validator.loadSchematron('assets_downloaded/schematron/peppol/PEPPOL-EN16931-UBL.sch');
break; break;
case 'FACTURX': case 'FACTURX':
// Would load from Factur-X specific Schematron // Would load from Factur-X specific Schematron
await validator.loadSchematron('assets/schematron/facturx/Factur-X-EN16931-validation.sch'); await validator.loadSchematron('assets_downloaded/schematron/facturx/Factur-X-EN16931-validation.sch');
break; break;
} }

3
ts/tspublish.json Normal file
View File

@@ -0,0 +1,3 @@
{
"order": 1
}

View File

@@ -4,12 +4,12 @@
* Script to download official Schematron files for e-invoice validation * Script to download official Schematron files for e-invoice validation
*/ */
import { SchematronDownloader } from '../ts/formats/validation/schematron.downloader.js'; import { SchematronDownloader } from '../dist_ts/formats/validation/schematron.downloader.js';
async function main() { async function main() {
console.log('📥 Starting Schematron download...\n'); console.log('📥 Starting Schematron download...\n');
const downloader = new SchematronDownloader('assets/schematron'); const downloader = new SchematronDownloader('assets_downloaded/schematron');
await downloader.initialize(); await downloader.initialize();
// Download EN16931 Schematron files // Download EN16931 Schematron files
@@ -57,8 +57,12 @@ async function main() {
console.log('\n✅ Schematron download complete!'); console.log('\n✅ Schematron download complete!');
} }
// Run the script // Run if executed directly
main().catch(error => { if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(error => {
console.error('❌ Script failed:', error); console.error('❌ Script failed:', error);
process.exit(1); process.exit(1);
}); });
}
export default main;

View File

@@ -202,4 +202,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
main().catch(console.error); main().catch(console.error);
} }
export { downloadTestSamples, TEST_SAMPLE_SOURCES }; export default main;
export { TEST_SAMPLE_SOURCES };

View File

@@ -171,8 +171,12 @@ async function downloadXRechnungRules(): Promise<void> {
console.log('3. Add XRechnung-specific TypeScript validators'); console.log('3. Add XRechnung-specific TypeScript validators');
} }
// Run the script // Run if executed directly
downloadXRechnungRules().catch(error => { if (import.meta.url === `file://${process.argv[1]}`) {
downloadXRechnungRules().catch(error => {
console.error('Failed to download XRechnung rules:', error); console.error('Failed to download XRechnung rules:', error);
process.exit(1); process.exit(1);
}); });
}
export default downloadXRechnungRules;

275
ts_install/index.ts Normal file
View File

@@ -0,0 +1,275 @@
#!/usr/bin/env node
/**
* Post-install script to download required validation resources
* This script is automatically run after npm/pnpm install
* 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';
// Version for cache invalidation
const RESOURCES_VERSION = '1.0.0';
/**
* Check if we're in a proper npm install context
*/
function isValidInstallContext(): boolean {
// Skip if we're in a git install or similar
if (process.env.npm_lifecycle_event !== 'postinstall') {
return false;
}
// Skip in CI if explicitly disabled
if (process.env.CI && process.env.EINVOICE_SKIP_RESOURCES) {
console.log('⏭️ Skipping resource download (EINVOICE_SKIP_RESOURCES set)');
return false;
}
return true;
}
/**
* Create a checksum for a file
*/
function getFileChecksum(filePath: string): string | null {
try {
if (!fs.existsSync(filePath)) return null;
const content = fs.readFileSync(filePath);
return crypto.createHash('sha256').update(content).digest('hex');
} catch {
return null;
}
}
/**
* Check if resources are already downloaded and valid
*/
function checkExistingResources(): boolean {
const versionFile = path.join('assets_downloaded', 'schematron', '.version');
try {
if (!fs.existsSync(versionFile)) return false;
const version = fs.readFileSync(versionFile, 'utf-8').trim();
if (version !== RESOURCES_VERSION) {
console.log('📦 Resource version mismatch, re-downloading...');
return false;
}
// Check if key files exist
const keyFiles = [
'assets_downloaded/schematron/EN16931-UBL-v1.3.14.sch',
'assets_downloaded/schematron/EN16931-CII-v1.3.14.sch',
'assets_downloaded/schematron/PEPPOL-EN16931-UBL-v3.0.17.sch'
];
for (const file of keyFiles) {
if (!fs.existsSync(file)) {
console.log(`📦 Missing ${file}, re-downloading resources...`);
return false;
}
}
return true;
} catch {
return false;
}
}
/**
* Save version file after successful download
*/
function saveVersionFile(): void {
const versionFile = path.join('assets_downloaded', 'schematron', '.version');
try {
fs.mkdirSync(path.dirname(versionFile), { recursive: true });
fs.writeFileSync(versionFile, RESOURCES_VERSION);
} catch (error) {
console.warn('⚠️ Could not save version file:', error.message);
}
}
async function downloadSchematron() {
console.log('📥 Downloading Schematron validation files...\n');
const downloader = new SchematronDownloader('assets_downloaded/schematron');
await downloader.initialize();
let successCount = 0;
let failCount = 0;
// Download EN16931 Schematron files
console.log('🔵 Downloading EN16931 Schematron files...');
try {
const en16931Files = await downloader.downloadStandard('EN16931');
console.log(`✅ Downloaded ${en16931Files.length} EN16931 files`);
successCount += en16931Files.length;
} catch (error) {
console.error(`⚠️ Failed to download EN16931: ${error.message}`);
failCount++;
}
// Download PEPPOL Schematron files
console.log('\n🔵 Downloading PEPPOL Schematron files...');
try {
const peppolFiles = await downloader.downloadStandard('PEPPOL');
console.log(`✅ Downloaded ${peppolFiles.length} PEPPOL files`);
successCount += peppolFiles.length;
} catch (error) {
console.error(`⚠️ Failed to download PEPPOL: ${error.message}`);
failCount++;
}
// Download XRechnung Schematron files
console.log('\n🔵 Downloading XRechnung Schematron files...');
try {
const xrechnungFiles = await downloader.downloadStandard('XRECHNUNG');
console.log(`✅ Downloaded ${xrechnungFiles.length} XRechnung files`);
successCount += xrechnungFiles.length;
} catch (error) {
console.error(`⚠️ Failed to download XRechnung: ${error.message}`);
failCount++;
}
// Report results
if (successCount > 0) {
saveVersionFile();
console.log(`\n✅ Successfully downloaded ${successCount} validation files`);
}
if (failCount > 0) {
console.log(`⚠️ Failed to download ${failCount} resource sets`);
console.log(' Some validation features may be limited');
}
return { successCount, failCount };
}
async function main() {
// Check if we should run
if (!isValidInstallContext()) {
return;
}
console.log('='.repeat(60));
console.log('🚀 @fin.cx/einvoice - Validation Resources Setup');
console.log('='.repeat(60));
console.log();
try {
// Check if resources already exist and are current
if (checkExistingResources()) {
console.log('✅ Validation resources already installed and up-to-date');
console.log();
return;
}
// Check if we're in the right directory
const packageJsonPath = path.join(process.cwd(), 'package.json');
if (!fs.existsSync(packageJsonPath)) {
console.error('❌ Error: package.json not found');
console.error(' Installation context issue - skipping resource download');
return;
}
// Check if dist_ts exists (module should be built)
const distPath = path.join(process.cwd(), 'dist_ts');
if (!fs.existsSync(distPath)) {
console.log('⚠️ Module not yet built - skipping resource download');
console.log(' Resources will be downloaded on first use');
return;
}
// Check network connectivity (simple DNS check)
try {
await import('dns').then(dns =>
new Promise((resolve, reject) => {
dns.lookup('github.com', (err) => {
if (err) reject(err);
else resolve(true);
});
})
);
} catch {
console.log('⚠️ No network connectivity detected');
console.log(' Validation resources will be downloaded on first use');
console.log(' when network is available');
return;
}
// Download resources with retry logic
let attempts = 0;
const maxAttempts = 3;
let lastError;
while (attempts < maxAttempts) {
attempts++;
if (attempts > 1) {
console.log(`\n🔄 Retry attempt ${attempts}/${maxAttempts}...`);
}
try {
const { successCount, failCount } = await downloadSchematron();
if (successCount > 0) {
console.log();
console.log('='.repeat(60));
console.log('✅ Validation resources installed successfully!');
console.log('='.repeat(60));
console.log();
return;
}
if (failCount > 0 && attempts < maxAttempts) {
console.log(`\n⚠ Some downloads failed, retrying...`);
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s before retry
continue;
}
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
}
}
}
// If we get here, downloads failed after retries
console.error();
console.error('⚠️ Could not download all validation resources');
console.error(' The library will work but validation features may be limited');
console.error(' Resources will be attempted again on first use');
console.error();
if (lastError) {
console.error(' Last error:', lastError.message);
}
} catch (error) {
// Catch-all for unexpected errors
console.error();
console.error('⚠️ Unexpected error during resource setup:', error.message);
console.error(' This won\'t affect library installation');
console.error(' Resources will be downloaded on first use');
console.error();
}
}
// 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);
// Never fail the install
process.exit(0);
});
}
export default main;

View File

@@ -0,0 +1,3 @@
{
"order": 1
}