@fin.cx/einvoice 5.2.0 → 5.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.smartconfig.json +29 -11
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/einvoice.d.ts +2 -2
- package/dist_ts/einvoice.js +11 -9
- package/dist_ts/formats/cii/facturx/facturx.encoder.js +25 -16
- package/dist_ts/formats/pdf/extractors/associated.extractor.js +3 -2
- package/dist_ts/formats/pdf/extractors/base.extractor.d.ts +1 -1
- package/dist_ts/formats/pdf/extractors/base.extractor.js +2 -2
- package/dist_ts/formats/pdf/extractors/standard.extractor.js +3 -2
- package/dist_ts/formats/pdf/pdf.embedder.js +3 -2
- package/dist_ts/formats/pdf/pdf.extractor.d.ts +2 -1
- package/dist_ts/formats/pdf/pdf.extractor.js +20 -10
- package/dist_ts/formats/validation/schematron.downloader.d.ts +0 -1
- package/dist_ts/formats/validation/schematron.downloader.js +1 -3
- package/dist_ts/formats/validation/schematron.integration.js +5 -2
- package/dist_ts/formats/validation/schematron.validator.d.ts +9 -11
- package/dist_ts/formats/validation/schematron.validator.js +61 -67
- package/dist_ts/formats/validation/schematron.worker.d.ts +7 -6
- package/dist_ts/formats/validation/schematron.worker.js +44 -39
- package/dist_ts/plugins.d.ts +1 -3
- package/dist_ts/plugins.js +1 -9
- package/dist_ts/vendor/pdflib.d.ts +2 -0
- package/dist_ts/vendor/pdflib.js +9 -0
- package/dist_ts/vendor/saxonjs.d.ts +4 -3
- package/dist_ts/vendor/saxonjs.js +9 -3
- package/package.json +22 -18
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +12 -11
- package/ts/formats/cii/facturx/facturx.encoder.ts +25 -15
- package/ts/formats/pdf/extractors/associated.extractor.ts +2 -1
- package/ts/formats/pdf/extractors/base.extractor.ts +3 -2
- package/ts/formats/pdf/extractors/standard.extractor.ts +2 -1
- package/ts/formats/pdf/pdf.embedder.ts +6 -3
- package/ts/formats/pdf/pdf.extractor.ts +24 -16
- package/ts/formats/validation/schematron.downloader.ts +0 -2
- package/ts/formats/validation/schematron.integration.ts +4 -1
- package/ts/formats/validation/schematron.validator.ts +72 -73
- package/ts/formats/validation/schematron.worker.ts +59 -41
- package/ts/plugins.ts +0 -26
- package/ts/readme.md +21 -0
- package/ts/vendor/pdflib.ts +12 -0
- package/ts/vendor/saxonjs.ts +13 -5
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
BaseXMLExtractor,
|
|
3
|
-
StandardXMLExtractor,
|
|
4
|
-
AssociatedFilesExtractor,
|
|
5
|
-
TextXMLExtractor
|
|
6
|
-
} from './extractors/index.js';
|
|
1
|
+
import type { BaseXMLExtractor } from './extractors/base.extractor.js';
|
|
7
2
|
import { FormatDetector } from '../utils/format.detector.js';
|
|
8
3
|
import { InvoiceFormat } from '../../interfaces/common.js';
|
|
9
4
|
|
|
@@ -36,18 +31,30 @@ export interface PDFExtractResult {
|
|
|
36
31
|
* Uses multiple specialized extractors in sequence to maximize success rate
|
|
37
32
|
*/
|
|
38
33
|
export class PDFExtractor {
|
|
39
|
-
private extractors
|
|
34
|
+
private extractors?: BaseXMLExtractor[];
|
|
40
35
|
|
|
41
36
|
/**
|
|
42
37
|
* Constructor initializes the chain of extractors
|
|
43
38
|
*/
|
|
44
|
-
constructor() {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
39
|
+
constructor() {}
|
|
40
|
+
|
|
41
|
+
private async getExtractors(): Promise<BaseXMLExtractor[]> {
|
|
42
|
+
if (!this.extractors) {
|
|
43
|
+
const [standardModule, associatedModule, textModule] = await Promise.all([
|
|
44
|
+
import('./extractors/standard.extractor.js'),
|
|
45
|
+
import('./extractors/associated.extractor.js'),
|
|
46
|
+
import('./extractors/text.extractor.js')
|
|
47
|
+
]);
|
|
48
|
+
|
|
49
|
+
// Add extractors in order of preference/likelihood of success
|
|
50
|
+
this.extractors = [
|
|
51
|
+
new standardModule.StandardXMLExtractor(), // Standard PDF/A-3 embedded files
|
|
52
|
+
new associatedModule.AssociatedFilesExtractor(), // Associated files (ZUGFeRD v1, some Factur-X)
|
|
53
|
+
new textModule.TextXMLExtractor() // Text-based extraction (fallback)
|
|
54
|
+
];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this.extractors;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
/**
|
|
@@ -69,7 +76,8 @@ export class PDFExtractor {
|
|
|
69
76
|
const pdfBufferArray = Buffer.isBuffer(pdfBuffer) ? new Uint8Array(pdfBuffer) : pdfBuffer;
|
|
70
77
|
|
|
71
78
|
// Try each extractor in sequence
|
|
72
|
-
|
|
79
|
+
const extractors = await this.getExtractors();
|
|
80
|
+
for (const extractor of extractors) {
|
|
73
81
|
const extractorName = extractor.constructor.name;
|
|
74
82
|
console.log(`Trying extraction with ${extractorName}...`);
|
|
75
83
|
|
|
@@ -138,4 +146,4 @@ export class PDFExtractor {
|
|
|
138
146
|
}
|
|
139
147
|
};
|
|
140
148
|
}
|
|
141
|
-
}
|
|
149
|
+
}
|
|
@@ -79,7 +79,6 @@ export const SCHEMATRON_SOURCES: Record<string, SchematronSource[]> = {
|
|
|
79
79
|
*/
|
|
80
80
|
export class SchematronDownloader {
|
|
81
81
|
private cacheDir: string;
|
|
82
|
-
private smartfile: any;
|
|
83
82
|
|
|
84
83
|
constructor(cacheDir: string = 'assets_downloaded/schematron') {
|
|
85
84
|
this.cacheDir = cacheDir;
|
|
@@ -90,7 +89,6 @@ export class SchematronDownloader {
|
|
|
90
89
|
*/
|
|
91
90
|
public async initialize(): Promise<void> {
|
|
92
91
|
// Ensure cache directory exists
|
|
93
|
-
this.smartfile = await import('@push.rocks/smartfile');
|
|
94
92
|
await fs.mkdir(this.cacheDir, { recursive: true });
|
|
95
93
|
}
|
|
96
94
|
|
|
@@ -61,10 +61,13 @@ export class IntegratedValidator {
|
|
|
61
61
|
try {
|
|
62
62
|
await fs.access(schematronPath);
|
|
63
63
|
} catch {
|
|
64
|
-
throw new Error(`Schematron file not found: ${schematronPath}. Run '
|
|
64
|
+
throw new Error(`Schematron file not found: ${schematronPath}. Run 'pnpm run download-schematron' first.`);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
await this.schematronValidator.loadSchematron(schematronPath, true);
|
|
68
|
+
if (!this.schematronValidator.canValidate()) {
|
|
69
|
+
throw new Error(`Schematron file is not a precompiled Saxon-JS SEF resource: ${schematronPath}`);
|
|
70
|
+
}
|
|
68
71
|
this.schematronLoaded = true;
|
|
69
72
|
}
|
|
70
73
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as plugins from '../../plugins.js';
|
|
2
2
|
import type { ValidationResult } from './validation.types.js';
|
|
3
|
+
import { loadSaxonJS, type ISaxonJSTransformOptions, type ISaxonJSTransformResult } from '../../vendor/saxonjs.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Schematron validation options
|
|
@@ -11,14 +12,17 @@ export interface SchematronOptions {
|
|
|
11
12
|
maxErrors?: number; // Maximum errors before stopping
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
type TSchematronSourceKind = 'none' | 'schematron' | 'sef';
|
|
16
|
+
|
|
14
17
|
/**
|
|
15
18
|
* Schematron validation engine using Saxon-JS
|
|
16
19
|
* Provides official standards validation through Schematron rules
|
|
17
20
|
*/
|
|
18
21
|
export class SchematronValidator {
|
|
19
|
-
private compiledStylesheet: any;
|
|
20
22
|
private schematronRules: string;
|
|
21
|
-
private
|
|
23
|
+
private sourceKind: TSchematronSourceKind = 'none';
|
|
24
|
+
private stylesheetFileName?: string;
|
|
25
|
+
private stylesheetInternal?: unknown;
|
|
22
26
|
|
|
23
27
|
constructor(schematronRules?: string) {
|
|
24
28
|
this.schematronRules = schematronRules || '';
|
|
@@ -28,42 +32,25 @@ export class SchematronValidator {
|
|
|
28
32
|
* Load Schematron rules from file or string
|
|
29
33
|
*/
|
|
30
34
|
public async loadSchematron(source: string, isFilePath: boolean = true): Promise<void> {
|
|
35
|
+
let sourceContent: string;
|
|
36
|
+
|
|
31
37
|
if (isFilePath) {
|
|
32
|
-
|
|
38
|
+
sourceContent = await plugins.fs.readFile(source, 'utf-8');
|
|
33
39
|
} else {
|
|
34
|
-
|
|
35
|
-
this.schematronRules = source;
|
|
40
|
+
sourceContent = source;
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
// For now, we'll use a simplified approach with direct XSLT generation
|
|
50
|
-
// In production, we would use the official ISO Schematron skeleton XSLTs
|
|
51
|
-
|
|
52
|
-
try {
|
|
53
|
-
// Convert Schematron to XSLT
|
|
54
|
-
// This is a simplified version - in production we'd use the full ISO skeleton
|
|
55
|
-
const xslt = this.generateXSLTFromSchematron(this.schematronRules);
|
|
56
|
-
|
|
57
|
-
// Compile the XSLT with Saxon-JS
|
|
58
|
-
this.compiledStylesheet = await plugins.SaxonJS.compile({
|
|
59
|
-
stylesheetText: xslt,
|
|
60
|
-
warnings: 'silent'
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
this.isCompiled = true;
|
|
64
|
-
} catch (error) {
|
|
65
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
66
|
-
throw new Error(`Failed to compile Schematron: ${errorMessage}`);
|
|
42
|
+
|
|
43
|
+
this.schematronRules = sourceContent;
|
|
44
|
+
this.sourceKind = this.detectSourceKind(source, sourceContent, isFilePath);
|
|
45
|
+
this.stylesheetFileName = undefined;
|
|
46
|
+
this.stylesheetInternal = undefined;
|
|
47
|
+
|
|
48
|
+
if (this.sourceKind === 'sef') {
|
|
49
|
+
if (isFilePath) {
|
|
50
|
+
this.stylesheetFileName = source;
|
|
51
|
+
} else {
|
|
52
|
+
this.stylesheetInternal = JSON.parse(sourceContent);
|
|
53
|
+
}
|
|
67
54
|
}
|
|
68
55
|
}
|
|
69
56
|
|
|
@@ -78,19 +65,28 @@ export class SchematronValidator {
|
|
|
78
65
|
throw new Error('No Schematron rules loaded');
|
|
79
66
|
}
|
|
80
67
|
|
|
81
|
-
// Ensure Schematron is compiled
|
|
82
|
-
await this.compileSchematron();
|
|
83
|
-
|
|
84
68
|
const results: ValidationResult[] = [];
|
|
69
|
+
|
|
70
|
+
if (!this.canValidate()) {
|
|
71
|
+
return [this.createUnavailableResult()];
|
|
72
|
+
}
|
|
85
73
|
|
|
86
74
|
try {
|
|
87
|
-
|
|
88
|
-
const transformResult = await plugins.SaxonJS.transform({
|
|
89
|
-
stylesheetInternal: this.compiledStylesheet,
|
|
75
|
+
const transformOptions: ISaxonJSTransformOptions = {
|
|
90
76
|
sourceText: xmlContent,
|
|
91
77
|
destination: 'serialized',
|
|
92
78
|
stylesheetParams: options.parameters || {}
|
|
93
|
-
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
if (this.stylesheetFileName) {
|
|
82
|
+
transformOptions.stylesheetFileName = this.stylesheetFileName;
|
|
83
|
+
} else {
|
|
84
|
+
transformOptions.stylesheetInternal = this.stylesheetInternal;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Transform the XML with a precompiled Saxon-JS SEF stylesheet.
|
|
88
|
+
const SaxonJS = await loadSaxonJS();
|
|
89
|
+
const transformResult = await SaxonJS.transform(transformOptions, 'async') as ISaxonJSTransformResult;
|
|
94
90
|
|
|
95
91
|
// Parse the SVRL (Schematron Validation Report Language) output
|
|
96
92
|
results.push(...this.parseSVRL(transformResult.principalResult));
|
|
@@ -189,43 +185,46 @@ export class SchematronValidator {
|
|
|
189
185
|
};
|
|
190
186
|
}
|
|
191
187
|
|
|
192
|
-
/**
|
|
193
|
-
* Generate simplified XSLT from Schematron
|
|
194
|
-
* This is a placeholder - in production, use ISO Schematron skeleton
|
|
195
|
-
*/
|
|
196
|
-
private generateXSLTFromSchematron(schematron: string): string {
|
|
197
|
-
// This is a simplified transformation
|
|
198
|
-
// In production, we would use the official ISO Schematron skeleton XSLTs
|
|
199
|
-
// (iso_schematron_skeleton.xsl, iso_svrl_for_xslt2.xsl, etc.)
|
|
200
|
-
|
|
201
|
-
// For now, return a basic XSLT that creates SVRL output
|
|
202
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
203
|
-
<xsl:stylesheet version="3.0"
|
|
204
|
-
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
205
|
-
xmlns:svrl="http://purl.oclc.org/dsdl/svrl">
|
|
206
|
-
|
|
207
|
-
<xsl:output method="xml" indent="yes"/>
|
|
208
|
-
|
|
209
|
-
<xsl:template match="/">
|
|
210
|
-
<svrl:schematron-output>
|
|
211
|
-
<!-- This is a placeholder transformation -->
|
|
212
|
-
<!-- Real implementation would process Schematron patterns and rules -->
|
|
213
|
-
<svrl:active-pattern>
|
|
214
|
-
<xsl:attribute name="document">
|
|
215
|
-
<xsl:value-of select="base-uri(/)"/>
|
|
216
|
-
</xsl:attribute>
|
|
217
|
-
</svrl:active-pattern>
|
|
218
|
-
</svrl:schematron-output>
|
|
219
|
-
</xsl:template>
|
|
220
|
-
</xsl:stylesheet>`;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
188
|
/**
|
|
224
189
|
* Check if validator has rules loaded
|
|
225
190
|
*/
|
|
226
191
|
public hasRules(): boolean {
|
|
227
192
|
return !!this.schematronRules;
|
|
228
193
|
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Check if loaded rules can be executed by Saxon-JS.
|
|
197
|
+
*/
|
|
198
|
+
public canValidate(): boolean {
|
|
199
|
+
return this.sourceKind === 'sef' && Boolean(this.stylesheetFileName || this.stylesheetInternal);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
private detectSourceKind(source: string, sourceContent: string, isFilePath: boolean): TSchematronSourceKind {
|
|
203
|
+
const trimmed = sourceContent.trim();
|
|
204
|
+
if (!trimmed) {
|
|
205
|
+
return 'none';
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isFilePath && /\.sef\.json$/i.test(source)) {
|
|
209
|
+
return 'sef';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (trimmed.startsWith('{')) {
|
|
213
|
+
return 'sef';
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return 'schematron';
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private createUnavailableResult(): ValidationResult {
|
|
220
|
+
return {
|
|
221
|
+
ruleId: 'SCHEMATRON-UNCOMPILED',
|
|
222
|
+
source: 'SCHEMATRON',
|
|
223
|
+
severity: 'error',
|
|
224
|
+
message: 'Schematron validation requires a precompiled Saxon-JS SEF JSON stylesheet. Raw .sch files are loaded for inspection only and are not executed.',
|
|
225
|
+
remediation: 'Compile Schematron rules to .sef.json with Saxon tooling and load the compiled SEF resource.'
|
|
226
|
+
};
|
|
227
|
+
}
|
|
229
228
|
|
|
230
229
|
/**
|
|
231
230
|
* Get list of available phases from Schematron
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { Worker } from 'worker_threads';
|
|
2
|
-
import * as path from 'path';
|
|
3
2
|
import type { ValidationResult } from './validation.types.js';
|
|
4
3
|
import type { SchematronOptions } from './schematron.validator.js';
|
|
5
4
|
|
|
5
|
+
export interface ISchematronWorkerPoolInitializeOptions {
|
|
6
|
+
isFilePath?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
6
9
|
/**
|
|
7
10
|
* Worker pool for Schematron validation
|
|
8
11
|
* Provides non-blocking validation in worker threads
|
|
@@ -17,7 +20,8 @@ export class SchematronWorkerPool {
|
|
|
17
20
|
reject: (error: Error) => void;
|
|
18
21
|
}> = [];
|
|
19
22
|
private maxWorkers: number;
|
|
20
|
-
private
|
|
23
|
+
private stylesheetFileName?: string;
|
|
24
|
+
private stylesheetInternal?: unknown;
|
|
21
25
|
|
|
22
26
|
constructor(maxWorkers: number = 4) {
|
|
23
27
|
this.maxWorkers = maxWorkers;
|
|
@@ -26,48 +30,81 @@ export class SchematronWorkerPool {
|
|
|
26
30
|
/**
|
|
27
31
|
* Initialize worker pool
|
|
28
32
|
*/
|
|
29
|
-
public async initialize(
|
|
30
|
-
|
|
33
|
+
public async initialize(
|
|
34
|
+
stylesheetSource: string,
|
|
35
|
+
options: ISchematronWorkerPoolInitializeOptions = {}
|
|
36
|
+
): Promise<void> {
|
|
37
|
+
this.loadStylesheet(stylesheetSource, options);
|
|
31
38
|
|
|
32
39
|
// Create workers
|
|
33
40
|
for (let i = 0; i < this.maxWorkers; i++) {
|
|
34
41
|
await this.createWorker();
|
|
35
42
|
}
|
|
36
43
|
}
|
|
44
|
+
|
|
45
|
+
private loadStylesheet(
|
|
46
|
+
stylesheetSource: string,
|
|
47
|
+
options: ISchematronWorkerPoolInitializeOptions
|
|
48
|
+
): void {
|
|
49
|
+
const source = stylesheetSource.trim();
|
|
50
|
+
const isFilePath = options.isFilePath ?? /\.sef\.json$/i.test(stylesheetSource);
|
|
51
|
+
|
|
52
|
+
this.stylesheetFileName = undefined;
|
|
53
|
+
this.stylesheetInternal = undefined;
|
|
54
|
+
|
|
55
|
+
if (isFilePath) {
|
|
56
|
+
if (!/\.sef\.json$/i.test(stylesheetSource)) {
|
|
57
|
+
throw new Error('Schematron worker validation requires a precompiled Saxon-JS SEF JSON stylesheet file.');
|
|
58
|
+
}
|
|
59
|
+
this.stylesheetFileName = stylesheetSource;
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!source.startsWith('{')) {
|
|
64
|
+
throw new Error('Schematron worker validation requires a precompiled Saxon-JS SEF JSON stylesheet. Raw .sch files are not executed.');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
this.stylesheetInternal = JSON.parse(source);
|
|
68
|
+
}
|
|
37
69
|
|
|
38
70
|
/**
|
|
39
71
|
* Create a new worker
|
|
40
72
|
*/
|
|
41
73
|
private async createWorker(): Promise<void> {
|
|
42
|
-
const workerPath = path.join(import.meta.url, 'schematron.worker.impl.js');
|
|
43
|
-
|
|
44
74
|
const worker = new Worker(`
|
|
45
75
|
const { parentPort } = require('worker_threads');
|
|
46
76
|
const SaxonJS = require('saxon-js');
|
|
47
77
|
|
|
48
|
-
let
|
|
78
|
+
let stylesheetFileName = null;
|
|
79
|
+
let stylesheetInternal = null;
|
|
49
80
|
|
|
50
81
|
parentPort.on('message', async (msg) => {
|
|
51
82
|
try {
|
|
52
83
|
if (msg.type === 'init') {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
84
|
+
stylesheetFileName = msg.stylesheetFileName || null;
|
|
85
|
+
stylesheetInternal = msg.stylesheetInternal || null;
|
|
86
|
+
if (!stylesheetFileName && !stylesheetInternal) {
|
|
87
|
+
throw new Error('Worker not initialized with a SEF stylesheet');
|
|
88
|
+
}
|
|
58
89
|
parentPort.postMessage({ type: 'ready' });
|
|
59
90
|
} else if (msg.type === 'validate') {
|
|
60
|
-
if (!
|
|
91
|
+
if (!stylesheetFileName && !stylesheetInternal) {
|
|
61
92
|
throw new Error('Worker not initialized');
|
|
62
93
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const result = await SaxonJS.transform({
|
|
66
|
-
stylesheetInternal: compiledStylesheet,
|
|
94
|
+
|
|
95
|
+
const transformOptions = {
|
|
67
96
|
sourceText: msg.xmlContent,
|
|
68
97
|
destination: 'serialized',
|
|
69
98
|
stylesheetParams: msg.options.parameters || {}
|
|
70
|
-
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
if (stylesheetFileName) {
|
|
102
|
+
transformOptions.stylesheetFileName = stylesheetFileName;
|
|
103
|
+
} else {
|
|
104
|
+
transformOptions.stylesheetInternal = stylesheetInternal;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const result = await SaxonJS.transform(transformOptions, 'async');
|
|
71
108
|
|
|
72
109
|
parentPort.postMessage({
|
|
73
110
|
type: 'result',
|
|
@@ -83,7 +120,7 @@ export class SchematronWorkerPool {
|
|
|
83
120
|
});
|
|
84
121
|
`, { eval: true });
|
|
85
122
|
|
|
86
|
-
// Initialize worker with
|
|
123
|
+
// Initialize worker with a precompiled SEF stylesheet.
|
|
87
124
|
await new Promise<void>((resolve, reject) => {
|
|
88
125
|
worker.once('message', (msg) => {
|
|
89
126
|
if (msg.type === 'ready') {
|
|
@@ -96,7 +133,8 @@ export class SchematronWorkerPool {
|
|
|
96
133
|
// Send initialization message
|
|
97
134
|
worker.postMessage({
|
|
98
135
|
type: 'init',
|
|
99
|
-
|
|
136
|
+
stylesheetFileName: this.stylesheetFileName,
|
|
137
|
+
stylesheetInternal: this.stylesheetInternal
|
|
100
138
|
});
|
|
101
139
|
});
|
|
102
140
|
|
|
@@ -174,26 +212,6 @@ export class SchematronWorkerPool {
|
|
|
174
212
|
return results;
|
|
175
213
|
}
|
|
176
214
|
|
|
177
|
-
/**
|
|
178
|
-
* Generate XSLT from Schematron (simplified)
|
|
179
|
-
*/
|
|
180
|
-
private generateXSLTFromSchematron(schematron: string): string {
|
|
181
|
-
// Simplified - would use ISO Schematron skeleton in production
|
|
182
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
183
|
-
<xsl:stylesheet version="3.0"
|
|
184
|
-
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
185
|
-
xmlns:svrl="http://purl.oclc.org/dsdl/svrl">
|
|
186
|
-
|
|
187
|
-
<xsl:output method="xml" indent="yes"/>
|
|
188
|
-
|
|
189
|
-
<xsl:template match="/">
|
|
190
|
-
<svrl:schematron-output>
|
|
191
|
-
<svrl:active-pattern document="{base-uri(/)}"/>
|
|
192
|
-
</svrl:schematron-output>
|
|
193
|
-
</xsl:template>
|
|
194
|
-
</xsl:stylesheet>`;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
215
|
/**
|
|
198
216
|
* Terminate all workers
|
|
199
217
|
*/
|
|
@@ -218,4 +236,4 @@ export class SchematronWorkerPool {
|
|
|
218
236
|
queuedTasks: this.taskQueue.length
|
|
219
237
|
};
|
|
220
238
|
}
|
|
221
|
-
}
|
|
239
|
+
}
|
package/ts/plugins.ts
CHANGED
|
@@ -9,24 +9,10 @@ import * as fs from 'fs/promises';
|
|
|
9
9
|
import * as path from 'path';
|
|
10
10
|
import * as crypto from 'crypto';
|
|
11
11
|
|
|
12
|
-
// PDF-related imports
|
|
13
|
-
import {
|
|
14
|
-
PDFDocument,
|
|
15
|
-
PDFDict,
|
|
16
|
-
PDFName,
|
|
17
|
-
PDFRawStream,
|
|
18
|
-
PDFArray,
|
|
19
|
-
PDFString,
|
|
20
|
-
AFRelationship
|
|
21
|
-
} from 'pdf-lib';
|
|
22
|
-
|
|
23
12
|
// XML-related imports
|
|
24
13
|
import { DOMParser, XMLSerializer, xmldom } from './vendor/xmldom.js';
|
|
25
14
|
import * as xpath from 'xpath';
|
|
26
15
|
|
|
27
|
-
// XSLT/Schematron imports
|
|
28
|
-
import { SaxonJS } from './vendor/saxonjs.js';
|
|
29
|
-
|
|
30
16
|
// Compression-related imports
|
|
31
17
|
import { pako } from './vendor/pako.js';
|
|
32
18
|
|
|
@@ -40,24 +26,12 @@ export {
|
|
|
40
26
|
path,
|
|
41
27
|
crypto,
|
|
42
28
|
|
|
43
|
-
// PDF-lib exports
|
|
44
|
-
PDFDocument,
|
|
45
|
-
PDFDict,
|
|
46
|
-
PDFName,
|
|
47
|
-
PDFRawStream,
|
|
48
|
-
PDFArray,
|
|
49
|
-
PDFString,
|
|
50
|
-
AFRelationship,
|
|
51
|
-
|
|
52
29
|
// XML-related exports
|
|
53
30
|
DOMParser,
|
|
54
31
|
XMLSerializer,
|
|
55
32
|
xmldom,
|
|
56
33
|
xpath,
|
|
57
34
|
|
|
58
|
-
// XSLT/Schematron exports
|
|
59
|
-
SaxonJS,
|
|
60
|
-
|
|
61
35
|
// Compression-related exports
|
|
62
36
|
pako,
|
|
63
37
|
|
package/ts/readme.md
CHANGED
|
@@ -53,3 +53,24 @@ const xml = await invoice.exportXml('facturx');
|
|
|
53
53
|
|
|
54
54
|
- `../readme.md`: full package README for end users
|
|
55
55
|
- `../ts_install/readme.md`: install-time resource bootstrap module
|
|
56
|
+
|
|
57
|
+
## License and Legal Information
|
|
58
|
+
|
|
59
|
+
This repository contains open-source code licensed under the MIT License. A copy of the license can be found in the [license](../license) file.
|
|
60
|
+
|
|
61
|
+
**Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file.
|
|
62
|
+
|
|
63
|
+
### Trademarks
|
|
64
|
+
|
|
65
|
+
This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH or third parties, and are not included within the scope of the MIT license granted herein.
|
|
66
|
+
|
|
67
|
+
Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines or the guidelines of the respective third-party owners, and any usage must be approved in writing. Third-party trademarks used herein are the property of their respective owners and used only in a descriptive manner, e.g. for an implementation of an API or similar.
|
|
68
|
+
|
|
69
|
+
### Company Information
|
|
70
|
+
|
|
71
|
+
Task Venture Capital GmbH<br>
|
|
72
|
+
Registered at District Court Bremen HRB 35230 HB, Germany
|
|
73
|
+
|
|
74
|
+
For any legal inquiries or further information, please contact us via email at hello@task.vc.
|
|
75
|
+
|
|
76
|
+
By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type TPdfLibModule = typeof import('pdf-lib');
|
|
2
|
+
|
|
3
|
+
let cachedPdfLib: Promise<TPdfLibModule> | undefined;
|
|
4
|
+
|
|
5
|
+
// Use pdf-lib's bundled UMD build; the package entry requires JSON font assets,
|
|
6
|
+
// which some TS runners corrupt with require hooks.
|
|
7
|
+
const nativeImport = Function('specifier', 'return import(specifier)') as (specifier: string) => Promise<TPdfLibModule>;
|
|
8
|
+
|
|
9
|
+
export const loadPdfLib = async (): Promise<TPdfLibModule> => {
|
|
10
|
+
cachedPdfLib ||= nativeImport('pdf-lib/dist/pdf-lib.js');
|
|
11
|
+
return cachedPdfLib;
|
|
12
|
+
};
|
package/ts/vendor/saxonjs.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import * as saxonJSRuntime from 'saxon-js';
|
|
2
|
-
|
|
3
1
|
export interface ISaxonJSCompileOptions {
|
|
4
2
|
stylesheetText: string;
|
|
5
3
|
warnings?: string;
|
|
@@ -8,6 +6,7 @@ export interface ISaxonJSCompileOptions {
|
|
|
8
6
|
|
|
9
7
|
export interface ISaxonJSTransformOptions {
|
|
10
8
|
stylesheetInternal?: unknown;
|
|
9
|
+
stylesheetFileName?: string;
|
|
11
10
|
sourceText: string;
|
|
12
11
|
destination?: string;
|
|
13
12
|
stylesheetParams?: Record<string, unknown>;
|
|
@@ -20,9 +19,18 @@ export interface ISaxonJSTransformResult {
|
|
|
20
19
|
}
|
|
21
20
|
|
|
22
21
|
export interface ISaxonJSModule {
|
|
23
|
-
compile(options: ISaxonJSCompileOptions)
|
|
24
|
-
transform(options: ISaxonJSTransformOptions): Promise<ISaxonJSTransformResult
|
|
22
|
+
compile?: (options: ISaxonJSCompileOptions) => Promise<unknown>;
|
|
23
|
+
transform(options: ISaxonJSTransformOptions, executionMode?: 'async' | 'sync'): Promise<ISaxonJSTransformResult> | ISaxonJSTransformResult;
|
|
25
24
|
[key: string]: unknown;
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
let cachedSaxonJS: Promise<ISaxonJSModule> | undefined;
|
|
28
|
+
|
|
29
|
+
export const loadSaxonJS = async (): Promise<ISaxonJSModule> => {
|
|
30
|
+
cachedSaxonJS ||= import('saxon-js').then((saxonJSRuntime) => {
|
|
31
|
+
const runtime = (saxonJSRuntime as any).default ?? saxonJSRuntime;
|
|
32
|
+
return runtime as ISaxonJSModule;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return cachedSaxonJS;
|
|
36
|
+
};
|