@arela/uploader 1.1.2 → 1.1.4
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/package.json
CHANGED
package/src/config/config.js
CHANGED
|
@@ -37,10 +37,10 @@ class Config {
|
|
|
37
37
|
const __dirname = path.dirname(__filename);
|
|
38
38
|
const packageJsonPath = path.resolve(__dirname, '../../package.json');
|
|
39
39
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
40
|
-
return packageJson.version || '1.1.
|
|
40
|
+
return packageJson.version || '1.1.4';
|
|
41
41
|
} catch (error) {
|
|
42
42
|
console.warn('⚠️ Could not read package.json version, using fallback');
|
|
43
|
-
return '1.1.
|
|
43
|
+
return '1.1.4';
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -100,7 +100,7 @@ export function extractDocumentFields(source, fileExtension, filePath) {
|
|
|
100
100
|
|
|
101
101
|
// Resolve final type if the definition supports it (e.g., pedimento_simplificado vs proforma)
|
|
102
102
|
const resolvedType = docType.resolveType
|
|
103
|
-
? docType.resolveType(fields)
|
|
103
|
+
? docType.resolveType(fields, filePath)
|
|
104
104
|
: docType.type;
|
|
105
105
|
|
|
106
106
|
console.log(` → Resolved type: ${resolvedType}`);
|
|
@@ -74,8 +74,15 @@ export const pedimentoCompletoDefinition = {
|
|
|
74
74
|
* - R1 rectifications require fechaPagoRectificacion
|
|
75
75
|
* - Everything else requires paymentDate
|
|
76
76
|
* No payment evidence ⇒ proforma_completo.
|
|
77
|
+
*
|
|
78
|
+
* Exception: the "CoveFact" annex (…-CoveFact.pdf) doesn't always print the
|
|
79
|
+
* payment section, so the payment-based demotion misfires and the file gets
|
|
80
|
+
* stuck as proforma_completo (NON_PUSHABLE) forever — the CLI never
|
|
81
|
+
* re-identifies rows that already have a detected_type. The filename is the
|
|
82
|
+
* payment-independent signal for this variant.
|
|
77
83
|
*/
|
|
78
|
-
resolveType: (fields) => {
|
|
84
|
+
resolveType: (fields, filePath) => {
|
|
85
|
+
if (/covefact/i.test(filePath ?? '')) return 'pedimento_completo';
|
|
79
86
|
const clavePedimento =
|
|
80
87
|
fields?.find((f) => f.name === 'clavePedimento')?.value ?? null;
|
|
81
88
|
const paymentDate =
|
package/src/file-detection.js
CHANGED
|
@@ -5,6 +5,15 @@ import { PDFParse } from 'pdf-parse';
|
|
|
5
5
|
import { extractDocumentFields } from './document-type-shared.js';
|
|
6
6
|
import { classifyDocument } from './scoring/scoring-engine.js';
|
|
7
7
|
|
|
8
|
+
// pdf.js can reject internal worker promises on corrupt PDFs (e.g.
|
|
9
|
+
// XRefEntryException) that no try/catch around getText() can reach. Track the
|
|
10
|
+
// last PDF handed to the parser so the global unhandledRejection handler can
|
|
11
|
+
// name it; the event fires after finally blocks run, so it is never cleared.
|
|
12
|
+
let currentPdfFile = null;
|
|
13
|
+
function getCurrentPdfFile() {
|
|
14
|
+
return currentPdfFile;
|
|
15
|
+
}
|
|
16
|
+
|
|
8
17
|
// Document types that participate in arela_path composition.
|
|
9
18
|
const ARELA_PATH_TYPES = new Set([
|
|
10
19
|
'pedimento_simplificado',
|
|
@@ -47,6 +56,15 @@ function composeArelaPath(
|
|
|
47
56
|
return null;
|
|
48
57
|
}
|
|
49
58
|
|
|
59
|
+
// The CoveFact annex resolves to pedimento_completo without payment
|
|
60
|
+
// evidence (see pedimento-completo.js resolveType), so it must never anchor
|
|
61
|
+
// an operation on its own: with no self-composed path it only becomes
|
|
62
|
+
// pushable when a PAID sibling pedimento propagates the folder's
|
|
63
|
+
// arela_path. Unpaid operations therefore never reach Arela through it.
|
|
64
|
+
if (/covefact/i.test(filePath ?? '')) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
50
68
|
const rfc = fields?.find((f) => f.name === 'rfc')?.value;
|
|
51
69
|
let patente = fields?.find((f) => f.name === 'patente')?.value;
|
|
52
70
|
const aduana = fields?.find((f) => f.name === 'aduanaEntradaSalida')?.value;
|
|
@@ -255,6 +273,7 @@ export class FileDetectionService {
|
|
|
255
273
|
*/
|
|
256
274
|
async extractTextFromPDF(filePath) {
|
|
257
275
|
let parser;
|
|
276
|
+
currentPdfFile = filePath;
|
|
258
277
|
try {
|
|
259
278
|
const dataBuffer = fs.readFileSync(filePath);
|
|
260
279
|
const uint8Array = new Uint8Array(dataBuffer);
|
|
@@ -316,4 +335,4 @@ export class FileDetectionService {
|
|
|
316
335
|
}
|
|
317
336
|
|
|
318
337
|
export default FileDetectionService;
|
|
319
|
-
export { composeArelaPath };
|
|
338
|
+
export { composeArelaPath, getCurrentPdfFile };
|
package/src/index.js
CHANGED
|
@@ -13,8 +13,21 @@ import watchCommand from './commands/WatchCommand.js';
|
|
|
13
13
|
import workerCommand from './commands/WorkerCommand.js';
|
|
14
14
|
import appConfig from './config/config.js';
|
|
15
15
|
import ErrorHandler from './errors/ErrorHandler.js';
|
|
16
|
+
import { getCurrentPdfFile } from './file-detection.js';
|
|
16
17
|
import logger from './services/LoggingService.js';
|
|
17
18
|
|
|
19
|
+
// Exception names thrown by pdf.js (used via pdf-parse) when a PDF is
|
|
20
|
+
// malformed, encrypted, or truncated. Matched by error.name because the
|
|
21
|
+
// classes live inside pdfjs-dist and are not exported by pdf-parse.
|
|
22
|
+
const PDFJS_PARSE_ERRORS = new Set([
|
|
23
|
+
'XRefEntryException',
|
|
24
|
+
'XRefParseException',
|
|
25
|
+
'InvalidPDFException',
|
|
26
|
+
'MissingPDFException',
|
|
27
|
+
'PasswordException',
|
|
28
|
+
'FormatError',
|
|
29
|
+
]);
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Arela Uploader CLI
|
|
20
33
|
* Professional file uploader with document detection and organization
|
|
@@ -660,6 +673,22 @@ class ArelaUploaderCLI {
|
|
|
660
673
|
process.on('unhandledRejection', (reason, promise) => {
|
|
661
674
|
const error =
|
|
662
675
|
reason instanceof Error ? reason : new Error(String(reason));
|
|
676
|
+
|
|
677
|
+
// pdf.js rejects internal worker promises when a PDF is corrupt; those
|
|
678
|
+
// rejections cannot be caught around the parser call, and one bad file
|
|
679
|
+
// must not abort the rest of the batch.
|
|
680
|
+
if (PDFJS_PARSE_ERRORS.has(error.name)) {
|
|
681
|
+
const file = getCurrentPdfFile();
|
|
682
|
+
console.warn(
|
|
683
|
+
`⚠️ Corrupt or unreadable PDF${file ? ` (${file})` : ''}: ${error.message} — skipping`,
|
|
684
|
+
);
|
|
685
|
+
this.errorHandler.handleError(error, {
|
|
686
|
+
context: 'unhandledRejection',
|
|
687
|
+
file,
|
|
688
|
+
});
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
|
|
663
692
|
this.errorHandler.handleFatalError(error, {
|
|
664
693
|
context: 'unhandledRejection',
|
|
665
694
|
promise: promise.toString(),
|
|
@@ -225,7 +225,7 @@ export function classifyDocument(matchers, { source, extension, filePath }) {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
const resolvedType = def.resolveType
|
|
228
|
-
? def.resolveType(fields)
|
|
228
|
+
? def.resolveType(fields, filePath)
|
|
229
229
|
: def.documentType;
|
|
230
230
|
const pedimento = def.extractNumPedimento
|
|
231
231
|
? def.extractNumPedimento(source, fields, filePath)
|