@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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arela/uploader",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "description": "CLI to upload files/directories to Arela",
5
5
  "bin": {
6
6
  "arela": "./src/index.js"
@@ -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.2';
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.2';
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 =
@@ -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)