@arela/uploader 1.1.1 → 1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arela/uploader",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
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.0';
40
+ return packageJson.version || '1.1.3';
41
41
  } catch (error) {
42
42
  console.warn('⚠️ Could not read package.json version, using fallback');
43
- return '1.1.0';
43
+ return '1.1.3';
44
44
  }
45
45
  }
46
46
 
@@ -145,8 +145,15 @@ export const codigoAceptacionExtractor = {
145
145
  },
146
146
  };
147
147
 
148
- // 7) Num. E-Document: collects all 13-char alphanumeric codes following
149
- // `NUM. E-DOCUMENT` / `NUMERO DE E-DOCUMENT` labels.
148
+ // 7) Num. E-Document: collects all 13-char codes following
149
+ // `NUM. E-DOCUMENT` / `NUMERO DE E-DOCUMENT` labels. Real eDocument
150
+ // numbers START WITH A DIGIT (e.g. "0438261FSSN86"); MVE acuse folios
151
+ // ("MNVA26002Q3V5") are also listed under the same label and kept —
152
+ // downstream consumers filter them. The old bare [A-Z0-9]{13} also
153
+ // captured 13-letter words (TRANSPORTISTA), truncated tokens
154
+ // (IDENTIFICACIO[N], CURP prefixes) and RFCs.
155
+ const EDOC_RE = /(?<![A-Z0-9])(?:\d[A-Z0-9]{12}|MNVA[A-Z0-9]{9})(?![A-Z0-9])/g;
156
+
150
157
  export const numEDocumentoExtractor = {
151
158
  field: 'numEDocumento',
152
159
  extract: (source) => {
@@ -160,14 +167,14 @@ export const numEDocumentoExtractor = {
160
167
  if (!hasTitle) continue;
161
168
 
162
169
  // Codes on the title line itself
163
- const codesInLine = line.match(/[A-Z0-9]{13}/g) || [];
170
+ const codesInLine = line.match(EDOC_RE) || [];
164
171
  extractedCodes.push(...codesInLine);
165
172
 
166
173
  // Codes on the next few lines (e.g. CLAVE/COMPL. table rows)
167
174
  for (let j = 1; j <= 10 && i + j < lines.length; j++) {
168
175
  const nextLine = lines[i + j];
169
176
  if (/NUMERO|OBSERVACIONES/i.test(nextLine)) break;
170
- const codesInNextLine = nextLine.match(/[A-Z0-9]{13}/g) || [];
177
+ const codesInNextLine = nextLine.match(EDOC_RE) || [];
171
178
  extractedCodes.push(...codesInNextLine);
172
179
  }
173
180
  }
@@ -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',
@@ -255,6 +264,7 @@ export class FileDetectionService {
255
264
  */
256
265
  async extractTextFromPDF(filePath) {
257
266
  let parser;
267
+ currentPdfFile = filePath;
258
268
  try {
259
269
  const dataBuffer = fs.readFileSync(filePath);
260
270
  const uint8Array = new Uint8Array(dataBuffer);
@@ -316,4 +326,4 @@ export class FileDetectionService {
316
326
  }
317
327
 
318
328
  export default FileDetectionService;
319
- export { composeArelaPath };
329
+ 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(),