@trustchex/react-native-sdk 1.475.0 → 1.475.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.
@@ -297,10 +297,43 @@ const reconstructMRZCandidates = rawText => {
297
297
  const after = flat.indexOf(l1Source) + l1Source.length;
298
298
  l2 = pad(flat.slice(after, after + 30), 30);
299
299
  }
300
- // Line 3 (name): the longest letter+filler OCR line that is NOT the doc or
301
- // date line exclude obvious front-of-card title words by requiring fillers.
302
- const nameRuns = ocrLines.filter(l => l !== l1Source && l !== l2Line && /^[A-Z<]+$/.test(l) && /</.test(l) && l.length >= 5);
303
- const l3 = pad(nameRuns.sort((a, b) => b.length - a.length)[0] ?? '', 30);
300
+ // Line 3 (name): the longest mostly-letters+filler OCR line that is NOT the
301
+ // doc or date line. OCR mangles the name line too a leading "O" reads as
302
+ // "0", and the trailing "<" fillers read as K/C so we DON'T require a
303
+ // pure [A-Z<] line (that wrongly drops "0ZCAN<<ERDAL…" entirely). Accept a
304
+ // line that is predominantly letters/fillers, then normalise it.
305
+ const isNameLike = l => {
306
+ if (l === l1Source || l === l2Line || l.length < 5) return false;
307
+ if (!/</.test(l)) return false; // must have at least one filler
308
+ // Must look like "SURNAME<<NAMES…": starts with a letter (or its digit
309
+ // look-alike) and is dominated by letters/fillers, not a numeric record.
310
+ if (!/^[A-Z0-9]/.test(l)) return false;
311
+ const letters = (l.match(/[A-Z]/g) || []).length;
312
+ const digits = (l.match(/[0-9]/g) || []).length;
313
+ // A date/doc record is digit-heavy; a name is letter-heavy.
314
+ return letters >= 3 && letters >= digits;
315
+ };
316
+ // Normalise a name line: digit look-alikes back to letters (O/I/S/B/Z/G),
317
+ // and the filler-letter run misreads (K/C/E/G adjacent to fillers) to "<".
318
+ const normaliseName = l => {
319
+ const digitToLetter = {
320
+ '0': 'O',
321
+ '1': 'I',
322
+ '5': 'S',
323
+ '8': 'B',
324
+ '2': 'Z',
325
+ '6': 'G'
326
+ };
327
+ return l
328
+ // digit look-alikes inside the name → letters
329
+ .replace(/[015862]/g, d => digitToLetter[d] ?? d)
330
+ // a run of filler-letters (K/C/E/G) flanked by fillers or at the tail
331
+ // is the trailing/internal filler region
332
+ .replace(/(?<=<)[KCEG]+(?=<|$)/g, m => '<'.repeat(m.length)).replace(/[KCEG]+$/g, m => '<'.repeat(m.length));
333
+ };
334
+ const nameRuns = ocrLines.filter(isNameLike);
335
+ const bestName = nameRuns.sort((a, b) => b.length - a.length)[0] ?? '';
336
+ const l3 = pad(normaliseName(bestName), 30);
304
337
  candidates.push([l1, l2, l3]);
305
338
  }
306
339
  }
@@ -798,6 +831,21 @@ const correctedMrzFromParse = result => {
798
831
  return buffers.map(b => b.join('')).join('\n');
799
832
  };
800
833
 
834
+ /**
835
+ * Heuristic: does a parsed result's name look like it still carries OCR noise
836
+ * (a digit where a name letter belongs, or a run of filler-confusable letters
837
+ * that should have been "<")? Names are letters only, so any digit is noise; a
838
+ * 3+ run of K/C/E/G is very likely misread fillers leaking into the given names.
839
+ */
840
+ const nameLooksNoisy = r => {
841
+ const name = `${r.fields?.lastName ?? ''} ${r.fields?.firstName ?? ''}`;
842
+ // A DIGIT in a name is unambiguous OCR noise (MRZ names are letters only).
843
+ // A 5+ run of filler-confusable letters is almost certainly misread padding —
844
+ // real given names like "ECE"/"GECE" are short, so keep the threshold high to
845
+ // avoid false positives on legitimate names.
846
+ return /[0-9]/.test(name) || /[KCEG]{5,}/.test(name);
847
+ };
848
+
801
849
  /**
802
850
  * Validates MRZ text using the mrz npm package
803
851
  * @param mrzText Raw or cleaned MRZ text
@@ -822,6 +870,27 @@ const validateMRZ = (mrzText, autocorrect = true) => {
822
870
  result = null;
823
871
  }
824
872
 
873
+ // The direct parse can validate (the name field has no check digit) while the
874
+ // name line still carries OCR noise — a leading "O" read as "0", or trailing
875
+ // "<" fillers read as K/C/E/G that leak into the given names. The
876
+ // reconstruction path normalises the name line; when the direct result's name
877
+ // looks noisy, prefer a reconstructed candidate that validates with a cleaner
878
+ // name. This only ever swaps in another fully check-digit-valid reading.
879
+ if (result?.valid && nameLooksNoisy(result)) {
880
+ for (const reLines of reconstructMRZCandidates(mrzText)) {
881
+ let cand;
882
+ try {
883
+ cand = parse(reLines);
884
+ } catch {
885
+ continue;
886
+ }
887
+ if (cand.valid && !nameLooksNoisy(cand)) {
888
+ result = cand;
889
+ break;
890
+ }
891
+ }
892
+ }
893
+
825
894
  // Primary recovery: exhaustive, CHECK-DIGIT-DRIVEN correction scoped to the
826
895
  // fields whose check digit actually fails. It tries every OCR-ambiguous
827
896
  // permutation within those fields — ordered by how likely the OCR error is
@@ -2,4 +2,4 @@
2
2
 
3
3
  // This file is auto-generated. Do not edit manually.
4
4
  // Version is synced from package.json during build.
5
- export const SDK_VERSION = '1.475.0';
5
+ export const SDK_VERSION = '1.475.1';
@@ -1 +1 @@
1
- {"version":3,"file":"mrz.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/mrz.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAepD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;sBAOwB,MAAM,KAAG,MAAM;2BAs2B7B,MAAM,gBACF,OAAO,KACnB,mBAAmB;0CAwIuB,MAAM,KAAG,mBAAmB;wCA3yB9B,MAAM,KAAG,MAAM,EAAE,EAAE;wCAqzBnB,MAAM,KAAG,MAAM;gCAmDvB,MAAM;;;;;+BAnrBP,MAAM,KAAG,OAAO;iCA4Bd,MAAM,KAAG,MAAM;wCAqqBR,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI;;AAgBxE,wBAUE"}
1
+ {"version":3,"file":"mrz.utils.d.ts","sourceRoot":"","sources":["../../../../../src/Shared/Libs/mrz.utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAepD;;GAEG;AACH,MAAM,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,SAAS,CAAC;IAClB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;;sBAOwB,MAAM,KAAG,MAAM;2BAk5B7B,MAAM,gBACF,OAAO,KACnB,mBAAmB;0CA6JuB,MAAM,KAAG,mBAAmB;wCA52B9B,MAAM,KAAG,MAAM,EAAE,EAAE;wCAs3BnB,MAAM,KAAG,MAAM;gCAmDvB,MAAM;;;;;+BAvtBP,MAAM,KAAG,OAAO;iCA4Bd,MAAM,KAAG,MAAM;wCAysBR,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI;;AAgBxE,wBAUE"}
@@ -1,2 +1,2 @@
1
- export declare const SDK_VERSION = "1.475.0";
1
+ export declare const SDK_VERSION = "1.475.1";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trustchex/react-native-sdk",
3
- "version": "1.475.0",
3
+ "version": "1.475.1",
4
4
  "description": "Trustchex mobile app react native SDK for android or ios devices",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -69,7 +69,8 @@
69
69
  "uuid": "11.1.1",
70
70
  "yaml": "2.8.3",
71
71
  "fast-uri": "3.1.2",
72
- "flatted": "3.4.2"
72
+ "flatted": "3.4.2",
73
+ "react-native-screens": "4.18.0"
73
74
  },
74
75
  "scripts": {
75
76
  "example": "yarn workspace @trustchex/react-native-sdk-example",
@@ -347,17 +347,46 @@ const reconstructMRZCandidates = (rawText: string): string[][] => {
347
347
  const after = flat.indexOf(l1Source) + l1Source.length;
348
348
  l2 = pad(flat.slice(after, after + 30), 30);
349
349
  }
350
- // Line 3 (name): the longest letter+filler OCR line that is NOT the doc or
351
- // date line exclude obvious front-of-card title words by requiring fillers.
352
- const nameRuns = ocrLines.filter(
353
- (l) =>
354
- l !== l1Source &&
355
- l !== l2Line &&
356
- /^[A-Z<]+$/.test(l) &&
357
- /</.test(l) &&
358
- l.length >= 5
359
- );
360
- const l3 = pad(nameRuns.sort((a, b) => b.length - a.length)[0] ?? '', 30);
350
+ // Line 3 (name): the longest mostly-letters+filler OCR line that is NOT the
351
+ // doc or date line. OCR mangles the name line too a leading "O" reads as
352
+ // "0", and the trailing "<" fillers read as K/C — so we DON'T require a
353
+ // pure [A-Z<] line (that wrongly drops "0ZCAN<<ERDAL…" entirely). Accept a
354
+ // line that is predominantly letters/fillers, then normalise it.
355
+ const isNameLike = (l: string): boolean => {
356
+ if (l === l1Source || l === l2Line || l.length < 5) return false;
357
+ if (!/</.test(l)) return false; // must have at least one filler
358
+ // Must look like "SURNAME<<NAMES…": starts with a letter (or its digit
359
+ // look-alike) and is dominated by letters/fillers, not a numeric record.
360
+ if (!/^[A-Z0-9]/.test(l)) return false;
361
+ const letters = (l.match(/[A-Z]/g) || []).length;
362
+ const digits = (l.match(/[0-9]/g) || []).length;
363
+ // A date/doc record is digit-heavy; a name is letter-heavy.
364
+ return letters >= 3 && letters >= digits;
365
+ };
366
+ // Normalise a name line: digit look-alikes back to letters (O/I/S/B/Z/G),
367
+ // and the filler-letter run misreads (K/C/E/G adjacent to fillers) to "<".
368
+ const normaliseName = (l: string): string => {
369
+ const digitToLetter: Record<string, string> = {
370
+ '0': 'O',
371
+ '1': 'I',
372
+ '5': 'S',
373
+ '8': 'B',
374
+ '2': 'Z',
375
+ '6': 'G',
376
+ };
377
+ return (
378
+ l
379
+ // digit look-alikes inside the name → letters
380
+ .replace(/[015862]/g, (d) => digitToLetter[d] ?? d)
381
+ // a run of filler-letters (K/C/E/G) flanked by fillers or at the tail
382
+ // is the trailing/internal filler region
383
+ .replace(/(?<=<)[KCEG]+(?=<|$)/g, (m) => '<'.repeat(m.length))
384
+ .replace(/[KCEG]+$/g, (m) => '<'.repeat(m.length))
385
+ );
386
+ };
387
+ const nameRuns = ocrLines.filter(isNameLike);
388
+ const bestName = nameRuns.sort((a, b) => b.length - a.length)[0] ?? '';
389
+ const l3 = pad(normaliseName(bestName), 30);
361
390
  candidates.push([l1, l2, l3]);
362
391
  }
363
392
  }
@@ -904,6 +933,21 @@ const correctedMrzFromParse = (
904
933
  return buffers.map((b) => b.join('')).join('\n');
905
934
  };
906
935
 
936
+ /**
937
+ * Heuristic: does a parsed result's name look like it still carries OCR noise
938
+ * (a digit where a name letter belongs, or a run of filler-confusable letters
939
+ * that should have been "<")? Names are letters only, so any digit is noise; a
940
+ * 3+ run of K/C/E/G is very likely misread fillers leaking into the given names.
941
+ */
942
+ const nameLooksNoisy = (r: ReturnType<typeof parse>): boolean => {
943
+ const name = `${r.fields?.lastName ?? ''} ${r.fields?.firstName ?? ''}`;
944
+ // A DIGIT in a name is unambiguous OCR noise (MRZ names are letters only).
945
+ // A 5+ run of filler-confusable letters is almost certainly misread padding —
946
+ // real given names like "ECE"/"GECE" are short, so keep the threshold high to
947
+ // avoid false positives on legitimate names.
948
+ return /[0-9]/.test(name) || /[KCEG]{5,}/.test(name);
949
+ };
950
+
907
951
  /**
908
952
  * Validates MRZ text using the mrz npm package
909
953
  * @param mrzText Raw or cleaned MRZ text
@@ -929,6 +973,27 @@ const validateMRZ = (
929
973
  result = null;
930
974
  }
931
975
 
976
+ // The direct parse can validate (the name field has no check digit) while the
977
+ // name line still carries OCR noise — a leading "O" read as "0", or trailing
978
+ // "<" fillers read as K/C/E/G that leak into the given names. The
979
+ // reconstruction path normalises the name line; when the direct result's name
980
+ // looks noisy, prefer a reconstructed candidate that validates with a cleaner
981
+ // name. This only ever swaps in another fully check-digit-valid reading.
982
+ if (result?.valid && nameLooksNoisy(result)) {
983
+ for (const reLines of reconstructMRZCandidates(mrzText)) {
984
+ let cand: ReturnType<typeof parse>;
985
+ try {
986
+ cand = parse(reLines);
987
+ } catch {
988
+ continue;
989
+ }
990
+ if (cand.valid && !nameLooksNoisy(cand)) {
991
+ result = cand;
992
+ break;
993
+ }
994
+ }
995
+ }
996
+
932
997
  // Primary recovery: exhaustive, CHECK-DIGIT-DRIVEN correction scoped to the
933
998
  // fields whose check digit actually fails. It tries every OCR-ambiguous
934
999
  // permutation within those fields — ordered by how likely the OCR error is
package/src/version.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated. Do not edit manually.
2
2
  // Version is synced from package.json during build.
3
- export const SDK_VERSION = '1.475.0';
3
+ export const SDK_VERSION = '1.475.1';