Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
58506e287d | |||
b89da0ec3f | |||
8dd5509da6 | |||
2f597d79df | |||
fcbe8151b7 | |||
a106d66a10 | |||
cdb30d867d | |||
bc3028af55 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -20,3 +20,4 @@ dist_*/
|
|||||||
# custom
|
# custom
|
||||||
test/output
|
test/output
|
||||||
.serena
|
.serena
|
||||||
|
assets_downloaded/
|
||||||
|
243
MIGRATION.md
243
MIGRATION.md
@@ -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
|
|
@@ -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"
|
|
||||||
}
|
|
@@ -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>
|
|
@@ -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"
|
|
||||||
}
|
|
@@ -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>
|
|
@@ -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"
|
|
||||||
}
|
|
@@ -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>
|
|
@@ -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
@@ -1 +0,0 @@
|
|||||||
404: Not Found
|
|
@@ -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
|
||||||
|
|
||||||
|
@@ -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": {
|
||||||
|
10
readme.md
10
readme.md
@@ -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
|
||||||
|
@@ -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.'
|
||||||
}
|
}
|
||||||
|
@@ -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...');
|
||||||
|
@@ -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>> = {
|
||||||
|
@@ -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
3
ts/tspublish.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"order": 1
|
||||||
|
}
|
@@ -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
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
main().catch(error => {
|
main().catch(error => {
|
||||||
console.error('❌ Script failed:', error);
|
console.error('❌ Script failed:', error);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default main;
|
@@ -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 };
|
@@ -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
|
||||||
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
downloadXRechnungRules().catch(error => {
|
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
275
ts_install/index.ts
Normal 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;
|
3
ts_install/tspublish.json
Normal file
3
ts_install/tspublish.json
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"order": 1
|
||||||
|
}
|
Reference in New Issue
Block a user