@fin.cx/einvoice 5.0.3 → 5.1.3
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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/einvoice.d.ts +4 -8
- package/dist_ts/einvoice.js +46 -7
- package/dist_ts/formats/converters/xml-to-einvoice.converter.d.ts +21 -0
- package/dist_ts/formats/converters/xml-to-einvoice.converter.js +127 -0
- package/dist_ts/formats/semantic/bt-bg.model.d.ts +445 -0
- package/dist_ts/formats/semantic/bt-bg.model.js +12 -0
- package/dist_ts/formats/semantic/semantic.adapter.d.ts +87 -0
- package/dist_ts/formats/semantic/semantic.adapter.js +538 -0
- package/dist_ts/formats/semantic/semantic.validator.d.ts +48 -0
- package/dist_ts/formats/semantic/semantic.validator.js +596 -0
- package/dist_ts/formats/utils/currency.calculator.decimal.d.ts +98 -0
- package/dist_ts/formats/utils/currency.calculator.decimal.js +212 -0
- package/dist_ts/formats/utils/currency.utils.d.ts +99 -0
- package/dist_ts/formats/utils/currency.utils.js +250 -0
- package/dist_ts/formats/utils/decimal.d.ts +151 -0
- package/dist_ts/formats/utils/decimal.js +435 -0
- package/dist_ts/formats/validation/codelist.validator.d.ts +49 -0
- package/dist_ts/formats/validation/codelist.validator.js +188 -0
- package/dist_ts/formats/validation/conformance.harness.d.ts +99 -0
- package/dist_ts/formats/validation/conformance.harness.js +471 -0
- package/dist_ts/formats/validation/en16931.business-rules.validator.d.ts +58 -0
- package/dist_ts/formats/validation/en16931.business-rules.validator.js +459 -0
- package/dist_ts/formats/validation/facturx.validator.d.ts +75 -0
- package/dist_ts/formats/validation/facturx.validator.js +522 -0
- package/dist_ts/formats/validation/integrated.validator.d.ts +82 -0
- package/dist_ts/formats/validation/integrated.validator.js +311 -0
- package/dist_ts/formats/validation/peppol.validator.d.ts +77 -0
- package/dist_ts/formats/validation/peppol.validator.js +529 -0
- package/dist_ts/formats/validation/schematron.downloader.d.ts +71 -0
- package/dist_ts/formats/validation/schematron.downloader.js +254 -0
- package/dist_ts/formats/validation/schematron.integration.d.ts +60 -0
- package/dist_ts/formats/validation/schematron.integration.js +230 -0
- package/dist_ts/formats/validation/schematron.validator.d.ts +79 -0
- package/dist_ts/formats/validation/schematron.validator.js +288 -0
- package/dist_ts/formats/validation/schematron.worker.d.ts +50 -0
- package/dist_ts/formats/validation/schematron.worker.js +185 -0
- package/dist_ts/formats/validation/validation.types.d.ts +163 -0
- package/dist_ts/formats/validation/validation.types.js +181 -0
- package/dist_ts/formats/validation/vat-categories.validator.d.ts +87 -0
- package/dist_ts/formats/validation/vat-categories.validator.js +497 -0
- package/dist_ts/formats/validation/xrechnung.validator.d.ts +92 -0
- package/dist_ts/formats/validation/xrechnung.validator.js +417 -0
- package/dist_ts/interfaces/common.d.ts +1 -0
- package/dist_ts/interfaces/en16931-metadata.d.ts +83 -0
- package/dist_ts/interfaces/en16931-metadata.js +6 -0
- package/dist_ts/plugins.d.ts +3 -1
- package/dist_ts/plugins.js +7 -2
- package/dist_ts_install/download-schematron.d.ts +6 -0
- package/dist_ts_install/download-schematron.js +62 -0
- package/dist_ts_install/download-test-samples.d.ts +19 -0
- package/dist_ts_install/download-test-samples.js +169 -0
- package/dist_ts_install/download-xrechnung-rules.d.ts +7 -0
- package/dist_ts_install/download-xrechnung-rules.js +146 -0
- package/dist_ts_install/index.d.ts +8 -0
- package/dist_ts_install/index.js +246 -0
- package/npmextra.json +1 -1
- package/package.json +12 -6
- package/readme.md +255 -810
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/einvoice.ts +63 -14
- package/ts/formats/converters/xml-to-einvoice.converter.ts +142 -0
- package/ts/formats/semantic/bt-bg.model.ts +524 -0
- package/ts/formats/semantic/semantic.adapter.ts +600 -0
- package/ts/formats/semantic/semantic.validator.ts +654 -0
- package/ts/formats/utils/currency.calculator.decimal.ts +323 -0
- package/ts/formats/utils/currency.utils.ts +299 -0
- package/ts/formats/utils/decimal.ts +509 -0
- package/ts/formats/validation/codelist.validator.ts +317 -0
- package/ts/formats/validation/conformance.harness.ts +591 -0
- package/ts/formats/validation/en16931.business-rules.validator.ts +694 -0
- package/ts/formats/validation/facturx.validator.ts +579 -0
- package/ts/formats/validation/integrated.validator.ts +405 -0
- package/ts/formats/validation/peppol.validator.ts +589 -0
- package/ts/formats/validation/schematron.downloader.ts +304 -0
- package/ts/formats/validation/schematron.integration.ts +285 -0
- package/ts/formats/validation/schematron.validator.ts +348 -0
- package/ts/formats/validation/schematron.worker.ts +221 -0
- package/ts/formats/validation/validation.types.ts +274 -0
- package/ts/formats/validation/vat-categories.validator.ts +845 -0
- package/ts/formats/validation/xrechnung.validator.ts +494 -0
- package/ts/interfaces/common.ts +1 -0
- package/ts/interfaces/en16931-metadata.ts +104 -0
- package/ts/plugins.ts +8 -0
- package/ts/tspublish.json +3 -0
- package/dist_ts/classes.decoder.d.ts +0 -40
- package/dist_ts/classes.decoder.js +0 -320
- package/dist_ts/classes.encoder.d.ts +0 -51
- package/dist_ts/classes.encoder.js +0 -293
- package/dist_ts/classes.xinvoice.d.ts +0 -136
- package/dist_ts/classes.xinvoice.js +0 -352
- package/dist_ts/formats/base.decoder.d.ts +0 -19
- package/dist_ts/formats/base.decoder.js +0 -130
- package/dist_ts/formats/base.validator.d.ts +0 -44
- package/dist_ts/formats/base.validator.js +0 -39
- package/dist_ts/formats/decoder.factory.d.ts +0 -15
- package/dist_ts/formats/decoder.factory.js +0 -46
- package/dist_ts/formats/factorx.decoder.d.ts +0 -18
- package/dist_ts/formats/factorx.decoder.js +0 -178
- package/dist_ts/formats/factorx.encoder.d.ts +0 -51
- package/dist_ts/formats/factorx.encoder.js +0 -293
- package/dist_ts/formats/facturx.decoder.d.ts +0 -18
- package/dist_ts/formats/facturx.decoder.js +0 -210
- package/dist_ts/formats/facturx.encoder.d.ts +0 -51
- package/dist_ts/formats/facturx.encoder.js +0 -293
- package/dist_ts/formats/facturx.validator.d.ts +0 -67
- package/dist_ts/formats/facturx.validator.js +0 -266
- package/dist_ts/formats/pdf/robust-pdf.extractor.d.ts +0 -40
- package/dist_ts/formats/pdf/robust-pdf.extractor.js +0 -324
- package/dist_ts/formats/ubl.validator.d.ts +0 -82
- package/dist_ts/formats/ubl.validator.js +0 -306
- package/dist_ts/formats/validator.factory.d.ts +0 -18
- package/dist_ts/formats/validator.factory.js +0 -78
- package/dist_ts/formats/xinvoice.decoder.d.ts +0 -28
- package/dist_ts/formats/xinvoice.decoder.js +0 -332
- package/dist_ts/formats/xinvoice.encoder.d.ts +0 -19
- package/dist_ts/formats/xinvoice.encoder.js +0 -282
|
@@ -1,266 +0,0 @@
|
|
|
1
|
-
import { BaseValidator } from './base.validator.js';
|
|
2
|
-
import { ValidationLevel } from '../interfaces.js';
|
|
3
|
-
import * as xpath from 'xpath';
|
|
4
|
-
import { DOMParser } from 'xmldom';
|
|
5
|
-
/**
|
|
6
|
-
* Validator for Factur-X/ZUGFeRD invoice format
|
|
7
|
-
* Implements validation rules according to EN16931 and Factur-X specification
|
|
8
|
-
*/
|
|
9
|
-
export class FacturXValidator extends BaseValidator {
|
|
10
|
-
// XML namespaces for Factur-X/ZUGFeRD
|
|
11
|
-
static { this.NS_RSMT = 'urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'; }
|
|
12
|
-
static { this.NS_RAM = 'urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'; }
|
|
13
|
-
static { this.NS_UDT = 'urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100'; }
|
|
14
|
-
constructor(xml) {
|
|
15
|
-
super(xml);
|
|
16
|
-
// XML document for processing
|
|
17
|
-
this.xmlDoc = null;
|
|
18
|
-
// Factur-X profile (BASIC, EN16931, EXTENDED, etc.)
|
|
19
|
-
this.profile = '';
|
|
20
|
-
try {
|
|
21
|
-
// Parse XML document
|
|
22
|
-
this.xmlDoc = new DOMParser().parseFromString(xml, 'application/xml');
|
|
23
|
-
// Determine Factur-X profile
|
|
24
|
-
this.detectProfile();
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
this.addError('FX-PARSE', `Failed to parse XML: ${error}`, '/');
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Validates the Factur-X invoice against the specified level
|
|
32
|
-
* @param level Validation level
|
|
33
|
-
* @returns Validation result
|
|
34
|
-
*/
|
|
35
|
-
validate(level = ValidationLevel.SYNTAX) {
|
|
36
|
-
// Reset errors
|
|
37
|
-
this.errors = [];
|
|
38
|
-
// Check if document was parsed successfully
|
|
39
|
-
if (!this.xmlDoc) {
|
|
40
|
-
return {
|
|
41
|
-
valid: false,
|
|
42
|
-
errors: this.errors,
|
|
43
|
-
level: level
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
// Perform validation based on level
|
|
47
|
-
let valid = true;
|
|
48
|
-
if (level === ValidationLevel.SYNTAX) {
|
|
49
|
-
valid = this.validateSchema();
|
|
50
|
-
}
|
|
51
|
-
else if (level === ValidationLevel.SEMANTIC) {
|
|
52
|
-
valid = this.validateSchema() && this.validateStructure();
|
|
53
|
-
}
|
|
54
|
-
else if (level === ValidationLevel.BUSINESS) {
|
|
55
|
-
valid = this.validateSchema() &&
|
|
56
|
-
this.validateStructure() &&
|
|
57
|
-
this.validateBusinessRules();
|
|
58
|
-
}
|
|
59
|
-
return {
|
|
60
|
-
valid,
|
|
61
|
-
errors: this.errors,
|
|
62
|
-
level
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Validates XML against schema
|
|
67
|
-
* @returns True if schema validation passed
|
|
68
|
-
*/
|
|
69
|
-
validateSchema() {
|
|
70
|
-
// Basic schema validation (simplified for now)
|
|
71
|
-
if (!this.xmlDoc)
|
|
72
|
-
return false;
|
|
73
|
-
// Check for root element
|
|
74
|
-
const root = this.xmlDoc.documentElement;
|
|
75
|
-
if (!root || root.nodeName !== 'rsm:CrossIndustryInvoice') {
|
|
76
|
-
this.addError('FX-SCHEMA-1', 'Root element must be rsm:CrossIndustryInvoice', '/');
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
// Check for required namespaces
|
|
80
|
-
if (!root.lookupNamespaceURI('rsm') || !root.lookupNamespaceURI('ram')) {
|
|
81
|
-
this.addError('FX-SCHEMA-2', 'Required namespaces rsm and ram must be declared', '/');
|
|
82
|
-
return false;
|
|
83
|
-
}
|
|
84
|
-
return true;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Validates structure of the XML document
|
|
88
|
-
* @returns True if structure validation passed
|
|
89
|
-
*/
|
|
90
|
-
validateStructure() {
|
|
91
|
-
if (!this.xmlDoc)
|
|
92
|
-
return false;
|
|
93
|
-
let valid = true;
|
|
94
|
-
// Check for required main sections
|
|
95
|
-
const sections = [
|
|
96
|
-
'rsm:ExchangedDocumentContext',
|
|
97
|
-
'rsm:ExchangedDocument',
|
|
98
|
-
'rsm:SupplyChainTradeTransaction'
|
|
99
|
-
];
|
|
100
|
-
for (const section of sections) {
|
|
101
|
-
if (!this.exists(section)) {
|
|
102
|
-
this.addError('FX-STRUCT-1', `Required section ${section} is missing`, '/rsm:CrossIndustryInvoice');
|
|
103
|
-
valid = false;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
// Check for SupplyChainTradeTransaction sections
|
|
107
|
-
if (this.exists('rsm:SupplyChainTradeTransaction')) {
|
|
108
|
-
const tradeSubsections = [
|
|
109
|
-
'ram:ApplicableHeaderTradeAgreement',
|
|
110
|
-
'ram:ApplicableHeaderTradeDelivery',
|
|
111
|
-
'ram:ApplicableHeaderTradeSettlement'
|
|
112
|
-
];
|
|
113
|
-
for (const subsection of tradeSubsections) {
|
|
114
|
-
if (!this.exists(`rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeTransaction/${subsection}`)) {
|
|
115
|
-
this.addError('FX-STRUCT-2', `Required subsection ${subsection} is missing`, '/rsm:CrossIndustryInvoice/rsm:SupplyChainTradeTransaction/ram:ApplicableHeaderTradeTransaction');
|
|
116
|
-
valid = false;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return valid;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Validates business rules
|
|
124
|
-
* @returns True if business rule validation passed
|
|
125
|
-
*/
|
|
126
|
-
validateBusinessRules() {
|
|
127
|
-
if (!this.xmlDoc)
|
|
128
|
-
return false;
|
|
129
|
-
let valid = true;
|
|
130
|
-
// BR-16: Amount due for payment (BT-115) = Invoice total amount with VAT (BT-112) - Paid amount (BT-113)
|
|
131
|
-
valid = this.validateAmounts() && valid;
|
|
132
|
-
// BR-CO-3: Value added tax point date (BT-7) and Value added tax point date code (BT-8) are mutually exclusive
|
|
133
|
-
valid = this.validateMutuallyExclusiveFields() && valid;
|
|
134
|
-
// BR-S-1: An Invoice that contains a line (BG-25) where the Invoiced item VAT category code (BT-151) is "Standard rated"
|
|
135
|
-
// shall contain the Seller VAT Identifier (BT-31), the Seller tax registration identifier (BT-32)
|
|
136
|
-
// and/or the Seller tax representative VAT identifier (BT-63).
|
|
137
|
-
valid = this.validateSellerVatIdentifier() && valid;
|
|
138
|
-
return valid;
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Detects Factur-X profile from the XML
|
|
142
|
-
*/
|
|
143
|
-
detectProfile() {
|
|
144
|
-
if (!this.xmlDoc)
|
|
145
|
-
return;
|
|
146
|
-
// Look for profile identifier
|
|
147
|
-
const profileNode = xpath.select1('string(//rsm:ExchangedDocumentContext/ram:GuidelineSpecifiedDocumentContextParameter/ram:ID)', this.xmlDoc);
|
|
148
|
-
if (profileNode) {
|
|
149
|
-
const profileText = profileNode.toString();
|
|
150
|
-
if (profileText.includes('BASIC')) {
|
|
151
|
-
this.profile = 'BASIC';
|
|
152
|
-
}
|
|
153
|
-
else if (profileText.includes('EN16931')) {
|
|
154
|
-
this.profile = 'EN16931';
|
|
155
|
-
}
|
|
156
|
-
else if (profileText.includes('EXTENDED')) {
|
|
157
|
-
this.profile = 'EXTENDED';
|
|
158
|
-
}
|
|
159
|
-
else if (profileText.includes('MINIMUM')) {
|
|
160
|
-
this.profile = 'MINIMUM';
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
/**
|
|
165
|
-
* Validates amount calculations in the invoice
|
|
166
|
-
* @returns True if amount validation passed
|
|
167
|
-
*/
|
|
168
|
-
validateAmounts() {
|
|
169
|
-
if (!this.xmlDoc)
|
|
170
|
-
return false;
|
|
171
|
-
try {
|
|
172
|
-
// Extract amounts
|
|
173
|
-
const totalAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:GrandTotalAmount');
|
|
174
|
-
const paidAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:TotalPrepaidAmount') || 0;
|
|
175
|
-
const dueAmount = this.getNumberValue('//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation/ram:DuePayableAmount');
|
|
176
|
-
// Calculate expected due amount
|
|
177
|
-
const expectedDueAmount = totalAmount - paidAmount;
|
|
178
|
-
// Compare with a small tolerance for rounding errors
|
|
179
|
-
if (Math.abs(dueAmount - expectedDueAmount) > 0.01) {
|
|
180
|
-
this.addError('BR-16', `Amount due for payment (${dueAmount}) must equal Invoice total amount with VAT (${totalAmount}) - Paid amount (${paidAmount})`, '//ram:ApplicableHeaderTradeSettlement/ram:SpecifiedTradeSettlementHeaderMonetarySummation');
|
|
181
|
-
return false;
|
|
182
|
-
}
|
|
183
|
-
return true;
|
|
184
|
-
}
|
|
185
|
-
catch (error) {
|
|
186
|
-
this.addError('FX-AMOUNT', `Error validating amounts: ${error}`, '/');
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Validates mutually exclusive fields
|
|
192
|
-
* @returns True if validation passed
|
|
193
|
-
*/
|
|
194
|
-
validateMutuallyExclusiveFields() {
|
|
195
|
-
if (!this.xmlDoc)
|
|
196
|
-
return false;
|
|
197
|
-
try {
|
|
198
|
-
// Check for VAT point date and code (BR-CO-3)
|
|
199
|
-
const vatPointDate = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:TaxPointDate');
|
|
200
|
-
const vatPointDateCode = this.exists('//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax/ram:DueDateTypeCode');
|
|
201
|
-
if (vatPointDate && vatPointDateCode) {
|
|
202
|
-
this.addError('BR-CO-3', 'Value added tax point date and Value added tax point date code are mutually exclusive', '//ram:ApplicableHeaderTradeSettlement/ram:ApplicableTradeTax');
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
return true;
|
|
206
|
-
}
|
|
207
|
-
catch (error) {
|
|
208
|
-
this.addError('FX-MUTUAL', `Error validating mutually exclusive fields: ${error}`, '/');
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* Validates seller VAT identifier requirements
|
|
214
|
-
* @returns True if validation passed
|
|
215
|
-
*/
|
|
216
|
-
validateSellerVatIdentifier() {
|
|
217
|
-
if (!this.xmlDoc)
|
|
218
|
-
return false;
|
|
219
|
-
try {
|
|
220
|
-
// Check if there are any standard rated line items
|
|
221
|
-
const standardRatedItems = this.exists('//ram:SpecifiedLineTradeSettlement/ram:ApplicableTradeTax/ram:CategoryCode[text()="S"]');
|
|
222
|
-
if (standardRatedItems) {
|
|
223
|
-
// Check for seller VAT identifier
|
|
224
|
-
const sellerVatId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
225
|
-
const sellerTaxId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="FC"]');
|
|
226
|
-
const sellerTaxRepId = this.exists('//ram:ApplicableHeaderTradeAgreement/ram:SellerTaxRepresentativeTradeParty/ram:SpecifiedTaxRegistration/ram:ID[@schemeID="VA"]');
|
|
227
|
-
if (!sellerVatId && !sellerTaxId && !sellerTaxRepId) {
|
|
228
|
-
this.addError('BR-S-1', 'An Invoice with standard rated items must contain the Seller VAT Identifier, Tax registration identifier or Tax representative VAT identifier', '//ram:ApplicableHeaderTradeAgreement/ram:SellerTradeParty');
|
|
229
|
-
return false;
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
return true;
|
|
233
|
-
}
|
|
234
|
-
catch (error) {
|
|
235
|
-
this.addError('FX-VAT', `Error validating seller VAT identifier: ${error}`, '/');
|
|
236
|
-
return false;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
/**
|
|
240
|
-
* Helper method to check if a node exists
|
|
241
|
-
* @param xpathExpression XPath to check
|
|
242
|
-
* @returns True if node exists
|
|
243
|
-
*/
|
|
244
|
-
exists(xpathExpression) {
|
|
245
|
-
if (!this.xmlDoc)
|
|
246
|
-
return false;
|
|
247
|
-
const nodes = xpath.select(xpathExpression, this.xmlDoc);
|
|
248
|
-
// Handle different return types from xpath.select()
|
|
249
|
-
if (Array.isArray(nodes)) {
|
|
250
|
-
return nodes.length > 0;
|
|
251
|
-
}
|
|
252
|
-
return nodes ? true : false;
|
|
253
|
-
}
|
|
254
|
-
/**
|
|
255
|
-
* Helper method to get a number value from XPath
|
|
256
|
-
* @param xpathExpression XPath to get number from
|
|
257
|
-
* @returns Number value or NaN if not found
|
|
258
|
-
*/
|
|
259
|
-
getNumberValue(xpathExpression) {
|
|
260
|
-
if (!this.xmlDoc)
|
|
261
|
-
return NaN;
|
|
262
|
-
const node = xpath.select1(`string(${xpathExpression})`, this.xmlDoc);
|
|
263
|
-
return node ? parseFloat(node.toString()) : NaN;
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdHVyeC52YWxpZGF0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90cy9mb3JtYXRzL2ZhY3R1cngudmFsaWRhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUNwRCxPQUFPLEVBQUUsZUFBZSxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFbkQsT0FBTyxLQUFLLEtBQUssTUFBTSxPQUFPLENBQUM7QUFDL0IsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUVuQzs7O0dBR0c7QUFDSCxNQUFNLE9BQU8sZ0JBQWlCLFNBQVEsYUFBYTtJQUNqRCxzQ0FBc0M7YUFDdkIsWUFBTyxHQUFHLDhEQUE4RCxBQUFqRSxDQUFrRTthQUN6RSxXQUFNLEdBQUcsb0ZBQW9GLEFBQXZGLENBQXdGO2FBQzlGLFdBQU0sR0FBRyw2REFBNkQsQUFBaEUsQ0FBaUU7SUFRdEYsWUFBWSxHQUFXO1FBQ3JCLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQztRQVBiLDhCQUE4QjtRQUN0QixXQUFNLEdBQW9CLElBQUksQ0FBQztRQUV2QyxvREFBb0Q7UUFDNUMsWUFBTyxHQUFXLEVBQUUsQ0FBQztRQUszQixJQUFJLENBQUM7WUFDSCxxQkFBcUI7WUFDckIsSUFBSSxDQUFDLE1BQU0sR0FBRyxJQUFJLFNBQVMsRUFBRSxDQUFDLGVBQWUsQ0FBQyxHQUFHLEVBQUUsaUJBQWlCLENBQUMsQ0FBQztZQUV0RSw2QkFBNkI7WUFDN0IsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDO1FBQ3ZCLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEVBQUUsd0JBQXdCLEtBQUssRUFBRSxFQUFFLEdBQUcsQ0FBQyxDQUFDO1FBQ2xFLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNJLFFBQVEsQ0FBQyxRQUF5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
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Class for extracting XML from PDF files with robust error handling
|
|
3
|
-
*/
|
|
4
|
-
export declare class RobustPDFExtractor {
|
|
5
|
-
/**
|
|
6
|
-
* Extracts XML from a PDF buffer
|
|
7
|
-
* @param pdfBuffer PDF buffer
|
|
8
|
-
* @returns XML content or null if not found
|
|
9
|
-
*/
|
|
10
|
-
extractXml(pdfBuffer: Uint8Array | Buffer): Promise<string | null>;
|
|
11
|
-
/**
|
|
12
|
-
* Standard extraction method using PDF-lib
|
|
13
|
-
* @param pdfBuffer PDF buffer
|
|
14
|
-
* @returns XML content or null if not found
|
|
15
|
-
*/
|
|
16
|
-
private standardExtraction;
|
|
17
|
-
/**
|
|
18
|
-
* Alternative extraction method using a more robust approach
|
|
19
|
-
* @param pdfBuffer PDF buffer
|
|
20
|
-
* @returns XML content or null if not found
|
|
21
|
-
*/
|
|
22
|
-
private alternativeExtraction;
|
|
23
|
-
/**
|
|
24
|
-
* Extracts XML from a string
|
|
25
|
-
* @param pdfString PDF string
|
|
26
|
-
* @returns XML content or null if not found
|
|
27
|
-
*/
|
|
28
|
-
private extractXmlFromString;
|
|
29
|
-
/**
|
|
30
|
-
* Checks if an XML string is valid
|
|
31
|
-
* @param xmlString XML string to check
|
|
32
|
-
* @returns True if the XML is valid
|
|
33
|
-
*/
|
|
34
|
-
private isValidXml;
|
|
35
|
-
/**
|
|
36
|
-
* Extracts a sample XML file for testing
|
|
37
|
-
* @returns Sample XML content
|
|
38
|
-
*/
|
|
39
|
-
private extractSampleXml;
|
|
40
|
-
}
|
|
@@ -1,324 +0,0 @@
|
|
|
1
|
-
import { PDFDocument, PDFDict, PDFName, PDFRawStream, PDFArray, PDFString } from 'pdf-lib';
|
|
2
|
-
import * as pako from 'pako';
|
|
3
|
-
import * as fs from 'fs/promises';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
/**
|
|
6
|
-
* Class for extracting XML from PDF files with robust error handling
|
|
7
|
-
*/
|
|
8
|
-
export class RobustPDFExtractor {
|
|
9
|
-
/**
|
|
10
|
-
* Extracts XML from a PDF buffer
|
|
11
|
-
* @param pdfBuffer PDF buffer
|
|
12
|
-
* @returns XML content or null if not found
|
|
13
|
-
*/
|
|
14
|
-
async extractXml(pdfBuffer) {
|
|
15
|
-
try {
|
|
16
|
-
// First try the standard extraction
|
|
17
|
-
const standardXml = await this.standardExtraction(pdfBuffer);
|
|
18
|
-
if (standardXml) {
|
|
19
|
-
// Validate the XML
|
|
20
|
-
if (this.isValidXml(standardXml)) {
|
|
21
|
-
return standardXml;
|
|
22
|
-
}
|
|
23
|
-
else {
|
|
24
|
-
console.log('Extracted XML is not valid, trying alternative methods...');
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
// If standard extraction fails, try alternative methods
|
|
28
|
-
const alternativeXml = await this.alternativeExtraction(pdfBuffer);
|
|
29
|
-
if (alternativeXml && this.isValidXml(alternativeXml)) {
|
|
30
|
-
return alternativeXml;
|
|
31
|
-
}
|
|
32
|
-
// If all else fails, return a sample XML
|
|
33
|
-
console.log('All extraction methods failed, returning sample XML...');
|
|
34
|
-
return this.extractSampleXml();
|
|
35
|
-
}
|
|
36
|
-
catch (error) {
|
|
37
|
-
console.error('Error extracting XML from PDF:', error);
|
|
38
|
-
return this.extractSampleXml();
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Standard extraction method using PDF-lib
|
|
43
|
-
* @param pdfBuffer PDF buffer
|
|
44
|
-
* @returns XML content or null if not found
|
|
45
|
-
*/
|
|
46
|
-
async standardExtraction(pdfBuffer) {
|
|
47
|
-
try {
|
|
48
|
-
const pdfDoc = await PDFDocument.load(pdfBuffer);
|
|
49
|
-
// Get the document's metadata dictionary
|
|
50
|
-
const namesDictObj = pdfDoc.catalog.lookup(PDFName.of('Names'));
|
|
51
|
-
if (!(namesDictObj instanceof PDFDict)) {
|
|
52
|
-
console.warn('No Names dictionary found in PDF! This PDF does not contain embedded files.');
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
const embeddedFilesDictObj = namesDictObj.lookup(PDFName.of('EmbeddedFiles'));
|
|
56
|
-
if (!(embeddedFilesDictObj instanceof PDFDict)) {
|
|
57
|
-
console.warn('No EmbeddedFiles dictionary found! This PDF does not contain embedded files.');
|
|
58
|
-
return null;
|
|
59
|
-
}
|
|
60
|
-
const filesSpecObj = embeddedFilesDictObj.lookup(PDFName.of('Names'));
|
|
61
|
-
if (!(filesSpecObj instanceof PDFArray)) {
|
|
62
|
-
console.warn('No files specified in EmbeddedFiles dictionary!');
|
|
63
|
-
return null;
|
|
64
|
-
}
|
|
65
|
-
// Try to find an XML file in the embedded files
|
|
66
|
-
let xmlFile;
|
|
67
|
-
let xmlFileName;
|
|
68
|
-
for (let i = 0; i < filesSpecObj.size(); i += 2) {
|
|
69
|
-
const fileNameObj = filesSpecObj.lookup(i);
|
|
70
|
-
const fileSpecObj = filesSpecObj.lookup(i + 1);
|
|
71
|
-
if (!(fileNameObj instanceof PDFString)) {
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (!(fileSpecObj instanceof PDFDict)) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
// Get the filename as string
|
|
78
|
-
const fileName = fileNameObj.toString();
|
|
79
|
-
// Check if it's an XML file (checking both extension and known standard filenames)
|
|
80
|
-
if (fileName.toLowerCase().includes('.xml') ||
|
|
81
|
-
fileName.toLowerCase().includes('factur-x') ||
|
|
82
|
-
fileName.toLowerCase().includes('zugferd') ||
|
|
83
|
-
fileName.toLowerCase().includes('xrechnung')) {
|
|
84
|
-
const efDictObj = fileSpecObj.lookup(PDFName.of('EF'));
|
|
85
|
-
if (!(efDictObj instanceof PDFDict)) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const maybeStream = efDictObj.lookup(PDFName.of('F'));
|
|
89
|
-
if (maybeStream instanceof PDFRawStream) {
|
|
90
|
-
// Found an XML file - save it
|
|
91
|
-
xmlFile = maybeStream;
|
|
92
|
-
xmlFileName = fileName;
|
|
93
|
-
break;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
// If no XML file was found, return null
|
|
98
|
-
if (!xmlFile) {
|
|
99
|
-
console.warn('No embedded XML file found in the PDF!');
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
// Decompress and decode the XML content
|
|
103
|
-
try {
|
|
104
|
-
const xmlCompressedBytes = xmlFile.getContents().buffer;
|
|
105
|
-
const xmlBytes = pako.inflate(xmlCompressedBytes);
|
|
106
|
-
const xmlContent = new TextDecoder('utf-8').decode(xmlBytes);
|
|
107
|
-
console.log(`Successfully extracted XML from PDF file. File name: ${xmlFileName}`);
|
|
108
|
-
return xmlContent;
|
|
109
|
-
}
|
|
110
|
-
catch (decompressError) {
|
|
111
|
-
// Try without decompression
|
|
112
|
-
console.log('Decompression failed, trying without decompression...');
|
|
113
|
-
try {
|
|
114
|
-
const xmlBytes = xmlFile.getContents();
|
|
115
|
-
const xmlContent = new TextDecoder('utf-8').decode(xmlBytes);
|
|
116
|
-
console.log(`Successfully extracted uncompressed XML from PDF file. File name: ${xmlFileName}`);
|
|
117
|
-
return xmlContent;
|
|
118
|
-
}
|
|
119
|
-
catch (decodeError) {
|
|
120
|
-
console.error('Error decoding XML content:', decodeError);
|
|
121
|
-
return null;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
console.error('Error in standard extraction:', error);
|
|
127
|
-
return null;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Alternative extraction method using a more robust approach
|
|
132
|
-
* @param pdfBuffer PDF buffer
|
|
133
|
-
* @returns XML content or null if not found
|
|
134
|
-
*/
|
|
135
|
-
async alternativeExtraction(pdfBuffer) {
|
|
136
|
-
try {
|
|
137
|
-
// Convert buffer to string and look for XML patterns
|
|
138
|
-
const pdfString = Buffer.from(pdfBuffer).toString('utf8', 0, Math.min(pdfBuffer.length, 10000));
|
|
139
|
-
// Look for common XML patterns in the PDF
|
|
140
|
-
const xmlPatterns = [
|
|
141
|
-
/<\?xml[^>]*\?>/i,
|
|
142
|
-
/<CrossIndustryInvoice[^>]*>/i,
|
|
143
|
-
/<Invoice[^>]*>/i,
|
|
144
|
-
/<CreditNote[^>]*>/i,
|
|
145
|
-
/<rsm:CrossIndustryInvoice[^>]*>/i
|
|
146
|
-
];
|
|
147
|
-
for (const pattern of xmlPatterns) {
|
|
148
|
-
const match = pdfString.match(pattern);
|
|
149
|
-
if (match) {
|
|
150
|
-
console.log(`Found XML pattern in PDF: ${match[0]}`);
|
|
151
|
-
// Try to extract the XML content
|
|
152
|
-
const xmlContent = this.extractXmlFromString(pdfString);
|
|
153
|
-
if (xmlContent) {
|
|
154
|
-
console.log('Successfully extracted XML from PDF string');
|
|
155
|
-
return xmlContent;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
// If we couldn't find any XML patterns, try to extract a sample XML
|
|
160
|
-
return this.extractSampleXml();
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
console.error('Error in alternative extraction:', error);
|
|
164
|
-
return null;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Extracts XML from a string
|
|
169
|
-
* @param pdfString PDF string
|
|
170
|
-
* @returns XML content or null if not found
|
|
171
|
-
*/
|
|
172
|
-
extractXmlFromString(pdfString) {
|
|
173
|
-
try {
|
|
174
|
-
// Look for XML start and end tags
|
|
175
|
-
const xmlStartIndex = pdfString.indexOf('<?xml');
|
|
176
|
-
if (xmlStartIndex === -1) {
|
|
177
|
-
return null;
|
|
178
|
-
}
|
|
179
|
-
// Try to find the end of the XML document
|
|
180
|
-
const possibleEndTags = [
|
|
181
|
-
'</CrossIndustryInvoice>',
|
|
182
|
-
'</Invoice>',
|
|
183
|
-
'</CreditNote>',
|
|
184
|
-
'</rsm:CrossIndustryInvoice>'
|
|
185
|
-
];
|
|
186
|
-
let xmlEndIndex = -1;
|
|
187
|
-
for (const endTag of possibleEndTags) {
|
|
188
|
-
const endIndex = pdfString.indexOf(endTag);
|
|
189
|
-
if (endIndex !== -1) {
|
|
190
|
-
xmlEndIndex = endIndex + endTag.length;
|
|
191
|
-
break;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
if (xmlEndIndex === -1) {
|
|
195
|
-
return null;
|
|
196
|
-
}
|
|
197
|
-
// Extract the XML content
|
|
198
|
-
return pdfString.substring(xmlStartIndex, xmlEndIndex);
|
|
199
|
-
}
|
|
200
|
-
catch (error) {
|
|
201
|
-
console.error('Error extracting XML from string:', error);
|
|
202
|
-
return null;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
/**
|
|
206
|
-
* Checks if an XML string is valid
|
|
207
|
-
* @param xmlString XML string to check
|
|
208
|
-
* @returns True if the XML is valid
|
|
209
|
-
*/
|
|
210
|
-
isValidXml(xmlString) {
|
|
211
|
-
try {
|
|
212
|
-
// Check if the XML string contains basic XML structure
|
|
213
|
-
if (!xmlString.includes('<?xml') || !xmlString.includes('?>')) {
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
// Check if the XML string contains known invoice formats
|
|
217
|
-
const knownFormats = [
|
|
218
|
-
'<rsm:CrossIndustryInvoice',
|
|
219
|
-
'<CrossIndustryInvoice',
|
|
220
|
-
'<Invoice',
|
|
221
|
-
'<CreditNote',
|
|
222
|
-
'<ubl:Invoice',
|
|
223
|
-
'<ubl:CreditNote'
|
|
224
|
-
];
|
|
225
|
-
const hasKnownFormat = knownFormats.some(format => xmlString.includes(format));
|
|
226
|
-
if (!hasKnownFormat) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
// Check if the XML string contains binary data or invalid characters
|
|
230
|
-
const invalidChars = ['\u0000', '\u0001', '\u0002', '\u0003', '\u0004', '\u0005'];
|
|
231
|
-
const hasBinaryData = invalidChars.some(char => xmlString.includes(char));
|
|
232
|
-
if (hasBinaryData) {
|
|
233
|
-
return false;
|
|
234
|
-
}
|
|
235
|
-
return true;
|
|
236
|
-
}
|
|
237
|
-
catch (error) {
|
|
238
|
-
console.error('Error validating XML:', error);
|
|
239
|
-
return false;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Extracts a sample XML file for testing
|
|
244
|
-
* @returns Sample XML content
|
|
245
|
-
*/
|
|
246
|
-
extractSampleXml() {
|
|
247
|
-
try {
|
|
248
|
-
// Return a sample Factur-X XML for testing
|
|
249
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
250
|
-
<rsm:CrossIndustryInvoice xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"
|
|
251
|
-
xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
|
|
252
|
-
xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100">
|
|
253
|
-
<rsm:ExchangedDocumentContext>
|
|
254
|
-
<ram:GuidelineSpecifiedDocumentContextParameter>
|
|
255
|
-
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:factur-x.eu:1p0:en16931</ram:ID>
|
|
256
|
-
</ram:GuidelineSpecifiedDocumentContextParameter>
|
|
257
|
-
</rsm:ExchangedDocumentContext>
|
|
258
|
-
<rsm:ExchangedDocument>
|
|
259
|
-
<ram:ID>SAMPLE-001</ram:ID>
|
|
260
|
-
<ram:TypeCode>380</ram:TypeCode>
|
|
261
|
-
<ram:IssueDateTime>
|
|
262
|
-
<udt:DateTimeString format="102">20230101</udt:DateTimeString>
|
|
263
|
-
</ram:IssueDateTime>
|
|
264
|
-
</rsm:ExchangedDocument>
|
|
265
|
-
<rsm:SupplyChainTradeTransaction>
|
|
266
|
-
<ram:ApplicableHeaderTradeAgreement>
|
|
267
|
-
<ram:SellerTradeParty>
|
|
268
|
-
<ram:Name>Sample Seller</ram:Name>
|
|
269
|
-
<ram:PostalTradeAddress>
|
|
270
|
-
<ram:PostcodeCode>12345</ram:PostcodeCode>
|
|
271
|
-
<ram:LineOne>123 Seller Street</ram:LineOne>
|
|
272
|
-
<ram:CityName>Seller City</ram:CityName>
|
|
273
|
-
<ram:CountryID>DE</ram:CountryID>
|
|
274
|
-
</ram:PostalTradeAddress>
|
|
275
|
-
<ram:SpecifiedTaxRegistration>
|
|
276
|
-
<ram:ID schemeID="VA">DE123456789</ram:ID>
|
|
277
|
-
</ram:SpecifiedTaxRegistration>
|
|
278
|
-
</ram:SellerTradeParty>
|
|
279
|
-
<ram:BuyerTradeParty>
|
|
280
|
-
<ram:Name>Sample Buyer</ram:Name>
|
|
281
|
-
<ram:PostalTradeAddress>
|
|
282
|
-
<ram:PostcodeCode>54321</ram:PostcodeCode>
|
|
283
|
-
<ram:LineOne>456 Buyer Street</ram:LineOne>
|
|
284
|
-
<ram:CityName>Buyer City</ram:CityName>
|
|
285
|
-
<ram:CountryID>DE</ram:CountryID>
|
|
286
|
-
</ram:PostalTradeAddress>
|
|
287
|
-
</ram:BuyerTradeParty>
|
|
288
|
-
</ram:ApplicableHeaderTradeAgreement>
|
|
289
|
-
<ram:ApplicableHeaderTradeDelivery/>
|
|
290
|
-
<ram:ApplicableHeaderTradeSettlement>
|
|
291
|
-
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode>
|
|
292
|
-
<ram:SpecifiedTradeSettlementPaymentMeans>
|
|
293
|
-
<ram:TypeCode>30</ram:TypeCode>
|
|
294
|
-
</ram:SpecifiedTradeSettlementPaymentMeans>
|
|
295
|
-
<ram:ApplicableTradeTax>
|
|
296
|
-
<ram:CalculatedAmount>19.00</ram:CalculatedAmount>
|
|
297
|
-
<ram:TypeCode>VAT</ram:TypeCode>
|
|
298
|
-
<ram:BasisAmount>100.00</ram:BasisAmount>
|
|
299
|
-
<ram:CategoryCode>S</ram:CategoryCode>
|
|
300
|
-
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent>
|
|
301
|
-
</ram:ApplicableTradeTax>
|
|
302
|
-
<ram:SpecifiedTradePaymentTerms>
|
|
303
|
-
<ram:DueDateDateTime>
|
|
304
|
-
<udt:DateTimeString format="102">20230131</udt:DateTimeString>
|
|
305
|
-
</ram:DueDateDateTime>
|
|
306
|
-
</ram:SpecifiedTradePaymentTerms>
|
|
307
|
-
<ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
|
308
|
-
<ram:LineTotalAmount>100.00</ram:LineTotalAmount>
|
|
309
|
-
<ram:TaxBasisTotalAmount>100.00</ram:TaxBasisTotalAmount>
|
|
310
|
-
<ram:TaxTotalAmount currencyID="EUR">19.00</ram:TaxTotalAmount>
|
|
311
|
-
<ram:GrandTotalAmount>119.00</ram:GrandTotalAmount>
|
|
312
|
-
<ram:DuePayableAmount>119.00</ram:DuePayableAmount>
|
|
313
|
-
</ram:SpecifiedTradeSettlementHeaderMonetarySummation>
|
|
314
|
-
</ram:ApplicableHeaderTradeSettlement>
|
|
315
|
-
</rsm:SupplyChainTradeTransaction>
|
|
316
|
-
</rsm:CrossIndustryInvoice>`;
|
|
317
|
-
}
|
|
318
|
-
catch (error) {
|
|
319
|
-
console.error('Error extracting sample XML:', error);
|
|
320
|
-
return '';
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9idXN0LXBkZi5leHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9mb3JtYXRzL3BkZi9yb2J1c3QtcGRmLmV4dHJhY3Rvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLFFBQVEsRUFBRSxTQUFTLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDM0YsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFDN0IsT0FBTyxLQUFLLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDbEMsT0FBTyxLQUFLLElBQUksTUFBTSxNQUFNLENBQUM7QUFFN0I7O0dBRUc7QUFDSCxNQUFNLE9BQU8sa0JBQWtCO0lBQzdCOzs7O09BSUc7SUFDSSxLQUFLLENBQUMsVUFBVSxDQUFDLFNBQThCO1FBQ3BELElBQUksQ0FBQztZQUNILG9DQUFvQztZQUNwQyxNQUFNLFdBQVcsR0FBRyxNQUFNLElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUM3RCxJQUFJLFdBQVcsRUFBRSxDQUFDO2dCQUNoQixtQkFBbUI7Z0JBQ25CLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxXQUFXLENBQUMsRUFBRSxDQUFDO29CQUNqQyxPQUFPLFdBQVcsQ0FBQztnQkFDckIsQ0FBQztxQkFBTSxDQUFDO29CQUNOLE9BQU8sQ0FBQyxHQUFHLENBQUMsMkRBQTJELENBQUMsQ0FBQztnQkFDM0UsQ0FBQztZQUNILENBQUM7WUFFRCx3REFBd0Q7WUFDeEQsTUFBTSxjQUFjLEdBQUcsTUFBTSxJQUFJLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDbkUsSUFBSSxjQUFjLElBQUksSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO2dCQUN0RCxPQUFPLGNBQWMsQ0FBQztZQUN4QixDQUFDO1lBRUQseUNBQXlDO1lBQ3pDLE9BQU8sQ0FBQyxHQUFHLENBQUMsd0RBQXdELENBQUMsQ0FBQztZQUN0RSxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ2pDLENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxDQUFDLEtBQUssQ0FBQyxnQ0FBZ0MsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUN2RCxPQUFPLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ2pDLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxrQkFBa0IsQ0FBQyxTQUE4QjtRQUM3RCxJQUFJLENBQUM7WUFDSCxNQUFNLE1BQU0sR0FBRyxNQUFNLFdBQVcsQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUM7WUFFakQseUNBQXlDO1lBQ3pDLE1BQU0sWUFBWSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUNoRSxJQUFJLENBQUMsQ0FBQyxZQUFZLFlBQVksT0FBTyxDQUFDLEVBQUUsQ0FBQztnQkFDdkMsT0FBTyxDQUFDLElBQUksQ0FBQyw2RUFBNkUsQ0FBQyxDQUFDO2dCQUM1RixPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7WUFFRCxNQUFNLG9CQUFvQixHQUFHLFlBQVksQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxlQUFlLENBQUMsQ0FBQyxDQUFDO1lBQzlFLElBQUksQ0FBQyxDQUFDLG9CQUFvQixZQUFZLE9BQU8sQ0FBQyxFQUFFLENBQUM7Z0JBQy9DLE9BQU8sQ0FBQyxJQUFJLENBQUMsOEVBQThFLENBQUMsQ0FBQztnQkFDN0YsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO1lBRUQsTUFBTSxZQUFZLEdBQUcsb0JBQW9CLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztZQUN0RSxJQUFJLENBQUMsQ0FBQyxZQUFZLFlBQVksUUFBUSxDQUFDLEVBQUUsQ0FBQztnQkFDeEMsT0FBTyxDQUFDLElBQUksQ0FBQyxpREFBaUQsQ0FBQyxDQUFDO2dCQUNoRSxPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7WUFFRCxnREFBZ0Q7WUFDaEQsSUFBSSxPQUFpQyxDQUFDO1lBQ3RDLElBQUksV0FBK0IsQ0FBQztZQUVwQyxLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcsWUFBWSxDQUFDLElBQUksRUFBRSxFQUFFLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDaEQsTUFBTSxXQUFXLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDM0MsTUFBTSxXQUFXLEdBQUcsWUFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7Z0JBRS9DLElBQUksQ0FBQyxDQUFDLFdBQVcsWUFBWSxTQUFTLENBQUMsRUFBRSxDQUFDO29CQUN4QyxTQUFTO2dCQUNYLENBQUM7Z0JBQ0QsSUFBSSxDQUFDLENBQUMsV0FBVyxZQUFZLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ3RDLFNBQVM7Z0JBQ1gsQ0FBQztnQkFFRCw2QkFBNkI7Z0JBQzdCLE1BQU0sUUFBUSxHQUFHLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztnQkFFeEMsbUZBQW1GO2dCQUNuRixJQUFJLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDO29CQUN2QyxRQUFRLENBQUMsV0FBVyxFQUFFLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQztvQkFDM0MsUUFBUSxDQUFDLFdBQVcsRUFBRSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUM7b0JBQzFDLFFBQVEsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxRQUFRLENBQUMsV0FBVyxDQUFDLEVBQUUsQ0FBQztvQkFFakQsTUFBTSxTQUFTLEdBQUcsV0FBVyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7b0JBQ3ZELElBQUksQ0FBQyxDQUFDLFNBQVMsWUFBWSxPQUFPLENBQUMsRUFBRSxDQUFDO3dCQUNwQyxTQUFTO29CQUNYLENBQUM7b0JBRUQsTUFBTSxXQUFXLEdBQUcsU0FBUyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7b0JBQ3RELElBQUksV0FBVyxZQUFZLFlBQVksRUFBRSxDQUFDO3dCQUN4Qyw4QkFBOEI7d0JBQzlCLE9BQU8sR0FBRyxXQUFXLENBQUM7d0JBQ3RCLFdBQVcsR0FBRyxRQUFRLENBQUM7d0JBQ3ZCLE1BQU07b0JBQ1IsQ0FBQztnQkFDSCxDQUFDO1lBQ0gsQ0FBQztZQUVELHdDQUF3QztZQUN4QyxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2IsT0FBTyxDQUFDLElBQUksQ0FBQyx3Q0FBd0MsQ0FBQyxDQUFDO2dCQUN2RCxPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7WUFFRCx3Q0FBd0M7WUFDeEMsSUFBSSxDQUFDO2dCQUNILE1BQU0sa0JBQWtCLEdBQUcsT0FBTyxDQUFDLFdBQVcsRUFBRSxDQUFDLE1BQU0sQ0FBQztnQkFDeEQsTUFBTSxRQUFRLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDO2dCQUNsRCxNQUFNLFVBQVUsR0FBRyxJQUFJLFdBQVcsQ0FBQyxPQUFPLENBQUMsQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDLENBQUM7Z0JBRTdELE9BQU8sQ0FBQyxHQUFHLENBQUMsd0RBQXdELFdBQVcsRUFBRSxDQUFDLENBQUM7Z0JBQ25GLE9BQU8sVUFBVSxDQUFDO1lBQ3BCLENBQUM7WUFBQyxPQUFPLGVBQWUsRUFBRSxDQUFDO2dCQUN6Qiw0QkFBNEI7Z0JBQzVCLE9BQU8sQ0FBQyxHQUFHLENBQUMsdURBQXVELENBQUMsQ0FBQztnQkFDckUsSUFBSSxDQUFDO29CQUNILE1BQU0sUUFBUSxHQUFHLE9BQU8sQ0FBQyxXQUFXLEVBQUUsQ0FBQztvQkFDdkMsTUFBTSxVQUFVLEdBQUcsSUFBSSxXQUFXLENBQUMsT0FBTyxDQUFDLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxDQUFDO29CQUM3RCxPQUFPLENBQUMsR0FBRyxDQUFDLHFFQUFxRSxXQUFXLEVBQUUsQ0FBQyxDQUFDO29CQUNoRyxPQUFPLFVBQVUsQ0FBQztnQkFDcEIsQ0FBQztnQkFBQyxPQUFPLFdBQVcsRUFBRSxDQUFDO29CQUNyQixPQUFPLENBQUMsS0FBSyxDQUFDLDZCQUE2QixFQUFFLFdBQVcsQ0FBQyxDQUFDO29CQUMxRCxPQUFPLElBQUksQ0FBQztnQkFDZCxDQUFDO1lBQ0gsQ0FBQztRQUNILENBQUM7UUFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO1lBQ2YsT0FBTyxDQUFDLEtBQUssQ0FBQywrQkFBK0IsRUFBRSxLQUFLLENBQUMsQ0FBQztZQUN0RCxPQUFPLElBQUksQ0FBQztRQUNkLENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNLLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxTQUE4QjtRQUNoRSxJQUFJLENBQUM7WUFDSCxxREFBcUQ7WUFDckQsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxFQUFFLENBQUMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQztZQUVoRywwQ0FBMEM7WUFDMUMsTUFBTSxXQUFXLEdBQUc7Z0JBQ2xCLGlCQUFpQjtnQkFDakIsOEJBQThCO2dCQUM5QixpQkFBaUI7Z0JBQ2pCLG9CQUFvQjtnQkFDcEIsa0NBQWtDO2FBQ25DLENBQUM7WUFFRixLQUFLLE1BQU0sT0FBTyxJQUFJLFdBQVcsRUFBRSxDQUFDO2dCQUNsQyxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO2dCQUN2QyxJQUFJLEtBQUssRUFBRSxDQUFDO29CQUNWLE9BQU8sQ0FBQyxHQUFHLENBQUMsNkJBQTZCLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUM7b0JBRXJELGlDQUFpQztvQkFDakMsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO29CQUN4RCxJQUFJLFVBQVUsRUFBRSxDQUFDO3dCQUNmLE9BQU8sQ0FBQyxHQUFHLENBQUMsNENBQTRDLENBQUMsQ0FBQzt3QkFDMUQsT0FBTyxVQUFVLENBQUM7b0JBQ3BCLENBQUM7Z0JBQ0gsQ0FBQztZQUNILENBQUM7WUFFRCxvRUFBb0U7WUFDcEUsT0FBTyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQztRQUNqQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUNmLE9BQU8sQ0FBQyxLQUFLLENBQUMsa0NBQWtDLEVBQUUsS0FBSyxDQUFDLENBQUM7WUFDekQsT0FBTyxJQUFJLENBQUM7UUFDZCxDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7O09BSUc7SUFDSyxvQkFBb0IsQ0FBQyxTQUFpQjtRQUM1QyxJQUFJLENBQUM7WUFDSCxrQ0FBa0M7WUFDbEMsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNqRCxJQUFJLGFBQWEsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDO2dCQUN6QixPQUFPLElBQUksQ0FBQztZQUNkLENBQUM7WUFFRCwwQ0FBMEM7WUFDMUMsTUFBTSxlQUFlLEdBQUc7Z0JBQ3RCLHlCQUF5QjtnQkFDekIsWUFBWTtnQkFDWixlQUFlO2dCQUNmLDZCQUE2QjthQUM5QixDQUFDO1lBRUYsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDckIsS0FBSyxNQUFNLE1BQU0sSUFBSSxlQUFlLEVBQUUsQ0FBQztnQkFDckMsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsQ0FBQztnQkFDM0MsSUFBSSxRQUFRLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztvQkFDcEIsV0FBVyxHQUFHLFFBQVEsR0FBRyxNQUFNLENBQUMsTUFBTSxDQUFDO29CQUN2QyxNQUFNO2dCQUNSLENBQUM7WUFDSCxDQUFDO1lBRUQsSUFBSSxXQUFXLEtBQUssQ0FBQyxDQUFDLEVBQUUsQ0FBQztnQkFDdkIsT0FBTyxJQUFJLENBQUM7WUFDZCxDQUFDO1lBRUQsMEJBQTBCO1lBQzFCLE9BQU8sU0FBUyxDQUFDLFNBQVMsQ0FBQyxhQUFhLEVBQUUsV0FBVyxDQUFDLENBQUM7UUFDekQsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLG1DQUFtQyxFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzFELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0ssVUFBVSxDQUFDLFNBQWlCO1FBQ2xDLElBQUksQ0FBQztZQUNILHVEQUF1RDtZQUN2RCxJQUFJLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQztnQkFDOUQsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQseURBQXlEO1lBQ3pELE1BQU0sWUFBWSxHQUFHO2dCQUNuQiwyQkFBMkI7Z0JBQzNCLHVCQUF1QjtnQkFDdkIsVUFBVTtnQkFDVixhQUFhO2dCQUNiLGNBQWM7Z0JBQ2QsaUJBQWlCO2FBQ2xCLENBQUM7WUFFRixNQUFNLGNBQWMsR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO1lBQy9FLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztnQkFDcEIsT0FBTyxLQUFLLENBQUM7WUFDZixDQUFDO1lBRUQscUVBQXFFO1lBQ3JFLE1BQU0sWUFBWSxHQUFHLENBQUMsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsUUFBUSxFQUFFLFFBQVEsRUFBRSxRQUFRLENBQUMsQ0FBQztZQUNsRixNQUFNLGFBQWEsR0FBRyxZQUFZLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQzFFLElBQUksYUFBYSxFQUFFLENBQUM7Z0JBQ2xCLE9BQU8sS0FBSyxDQUFDO1lBQ2YsQ0FBQztZQUVELE9BQU8sSUFBSSxDQUFDO1FBQ2QsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLHVCQUF1QixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQzlDLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztJQUNILENBQUM7SUFFRDs7O09BR0c7SUFDSyxnQkFBZ0I7UUFDdEIsSUFBSSxDQUFDO1lBQ0gsMkNBQTJDO1lBQzNDLE9BQU87Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7NEJBbUVlLENBQUM7UUFDekIsQ0FBQztRQUFDLE9BQU8sS0FBSyxFQUFFLENBQUM7WUFDZixPQUFPLENBQUMsS0FBSyxDQUFDLDhCQUE4QixFQUFFLEtBQUssQ0FBQyxDQUFDO1lBQ3JELE9BQU8sRUFBRSxDQUFDO1FBQ1osQ0FBQztJQUNILENBQUM7Q0FDRiJ9
|