@hexar/biometric-identity-sdk-core 1.0.18 → 1.0.20

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.
@@ -85,6 +85,10 @@ export declare class BiometricIdentitySDK {
85
85
  * This reduces large images by skipping compression if already small enough
86
86
  */
87
87
  private compressImage;
88
+ /**
89
+ * Compress image in React Native environment using react-native-image-resizer
90
+ */
91
+ private compressImageReactNative;
88
92
  /**
89
93
  * Compress image in browser environment using Canvas API
90
94
  */
@@ -25,7 +25,7 @@ class BiometricIdentitySDK {
25
25
  encryptionKey: config.encryptionKey || '',
26
26
  minMatchScore: config.minMatchScore || 85,
27
27
  minLivenessScore: config.minLivenessScore || 80,
28
- validationTimeout: config.validationTimeout || 120000,
28
+ validationTimeout: config.validationTimeout || 180000, // 180 seconds (3 minutes) to handle large payloads
29
29
  modelPaths: {},
30
30
  };
31
31
  this.state = {
@@ -299,7 +299,7 @@ class BiometricIdentitySDK {
299
299
  minimumRequired: 10
300
300
  });
301
301
  }
302
- const MAX_FRAMES = 10;
302
+ const MAX_FRAMES = 5; // Reduced to 5 to reduce payload size and processing time
303
303
  if (videoFrames.length > MAX_FRAMES) {
304
304
  const step = Math.floor(videoFrames.length / MAX_FRAMES);
305
305
  videoFrames = videoFrames.filter((_, index) => index % step === 0).slice(0, MAX_FRAMES);
@@ -330,9 +330,10 @@ class BiometricIdentitySDK {
330
330
  const compressedFrame = this.fixBase64Padding(await this.compressImage(frame));
331
331
  validFrames.push(compressedFrame);
332
332
  }
333
- if (validFrames.length < 10) {
334
- logger_1.logger.error(`Insufficient valid frames: ${validFrames.length} < 10`);
335
- throw new Error(`Insufficient valid video frames: ${validFrames.length} frames available, minimum 10 required`);
333
+ const MIN_FRAMES = 5; // Minimum frames required for validation
334
+ if (validFrames.length < MIN_FRAMES) {
335
+ logger_1.logger.error(`Insufficient valid frames: ${validFrames.length} < ${MIN_FRAMES}`);
336
+ throw new Error(`Insufficient valid video frames: ${validFrames.length} frames available, minimum ${MIN_FRAMES} required`);
336
337
  }
337
338
  logger_1.logger.info('Sending validation request to backend', {
338
339
  framesCount: validFrames.length,
@@ -363,14 +364,24 @@ class BiometricIdentitySDK {
363
364
  errorData: error?.errorData,
364
365
  errorStack: error?.stack?.substring(0, 500)
365
366
  });
366
- const errorMessage = error?.message ||
367
- (error?.status ? `Backend request failed with status ${error.status}` : 'Backend validation failed');
368
- throw this.createError(types_1.BiometricErrorCode.NETWORK_ERROR, errorMessage, {
369
- status: error?.status,
370
- statusText: error?.statusText,
367
+ // Handle timeout specifically with better error message
368
+ const isTimeout = error?.message?.includes('timeout') || error?.message?.includes('Request timeout') || error?.message?.includes('AbortError');
369
+ const errorMessage = isTimeout
370
+ ? 'La validación está tomando demasiado tiempo. Por favor, verifica tu conexión e intenta nuevamente.'
371
+ : (error?.message || (error?.status ? `Backend request failed with status ${error.status}` : 'Backend validation failed'));
372
+ const errorToThrow = this.createError(types_1.BiometricErrorCode.NETWORK_ERROR, errorMessage, {
373
+ status: isTimeout ? 504 : error?.status,
374
+ statusText: isTimeout ? 'Gateway Timeout' : error?.statusText,
371
375
  errorData: error?.errorData,
372
376
  originalError: error?.message
373
377
  });
378
+ // Update state before throwing so UI can show error
379
+ this.updateState({
380
+ isLoading: false,
381
+ currentStep: types_1.SDKStep.ERROR,
382
+ error: errorToThrow
383
+ });
384
+ throw errorToThrow;
374
385
  }
375
386
  }
376
387
  /**
@@ -425,11 +436,22 @@ class BiometricIdentitySDK {
425
436
  if (!base64Image || base64Image.length === 0) {
426
437
  return base64Image;
427
438
  }
428
- const MAX_SIZE = 500 * 1024;
439
+ const MAX_SIZE = 400 * 1024; // Balanced: reduce payload but maintain quality
429
440
  if (base64Image.length < MAX_SIZE) {
430
441
  return base64Image;
431
442
  }
432
443
  try {
444
+ // Try React Native compression first
445
+ const ImageResizer = globalThis.ImageResizer || globalThis.require?.('react-native-image-resizer');
446
+ if (ImageResizer && ImageResizer.default) {
447
+ try {
448
+ return await this.compressImageReactNative(base64Image, ImageResizer.default);
449
+ }
450
+ catch (rnError) {
451
+ logger_1.logger.warn('React Native compression failed, trying browser method', rnError);
452
+ }
453
+ }
454
+ // Try browser compression
433
455
  const globalWindow = globalThis.window;
434
456
  if (globalWindow && globalWindow.Image && globalWindow.document) {
435
457
  return await this.compressImageBrowser(base64Image, globalWindow);
@@ -441,6 +463,25 @@ class BiometricIdentitySDK {
441
463
  logger_1.logger.warn(`Image too large (${Math.round(base64Image.length / 1024)}KB) but compression not available`);
442
464
  return base64Image;
443
465
  }
466
+ /**
467
+ * Compress image in React Native environment using react-native-image-resizer
468
+ */
469
+ async compressImageReactNative(base64Image, ImageResizer) {
470
+ try {
471
+ // Convert base64 to temporary file path (this is a simplified approach)
472
+ // In practice, you'd need to write to a temp file first
473
+ // For now, we'll use a more aggressive browser-style compression
474
+ // that works in React Native by reducing quality
475
+ // Since we can't easily use ImageResizer without file paths in core package,
476
+ // we'll fall back to browser compression which should work in React Native context
477
+ // The actual compression should be done in the React Native package
478
+ throw new Error('React Native compression requires file path');
479
+ }
480
+ catch (error) {
481
+ // Fall through to browser compression
482
+ throw error;
483
+ }
484
+ }
444
485
  /**
445
486
  * Compress image in browser environment using Canvas API
446
487
  */
@@ -449,8 +490,8 @@ class BiometricIdentitySDK {
449
490
  const img = new window.Image();
450
491
  img.onload = () => {
451
492
  const canvas = window.document.createElement('canvas');
452
- const maxWidth = 1920;
453
- const maxHeight = 1920;
493
+ const maxWidth = 1600; // Balanced: reduce size but maintain OCR/face detection quality
494
+ const maxHeight = 1600; // Balanced: reduce size but maintain OCR/face detection quality
454
495
  let width = img.width;
455
496
  let height = img.height;
456
497
  if (width > maxWidth || height > maxHeight) {
@@ -466,7 +507,7 @@ class BiometricIdentitySDK {
466
507
  return;
467
508
  }
468
509
  ctx.drawImage(img, 0, 0, width, height);
469
- const compressedBase64 = canvas.toDataURL('image/jpeg', 0.85);
510
+ const compressedBase64 = canvas.toDataURL('image/jpeg', 0.75); // Balanced: reduce size but maintain quality for OCR/face matching
470
511
  const base64 = compressedBase64.split(',')[1];
471
512
  resolve(base64);
472
513
  };
@@ -16,7 +16,7 @@ class BackendClient {
16
16
  this.config = {
17
17
  apiEndpoint: config.apiEndpoint.replace(/\/$/, ''),
18
18
  apiKey: config.apiKey,
19
- timeout: config.timeout || 120000,
19
+ timeout: config.timeout || 180000, // 180 seconds (3 minutes) to handle large payloads
20
20
  };
21
21
  this.sdkSessionId = this.generateSDKSessionId();
22
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-core",
3
- "version": "1.0.18",
3
+ "version": "1.0.20",
4
4
  "description": "Core AI engine for biometric identity verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -40,7 +40,7 @@ export class BiometricIdentitySDK {
40
40
  encryptionKey: config.encryptionKey || '',
41
41
  minMatchScore: config.minMatchScore || 85,
42
42
  minLivenessScore: config.minLivenessScore || 80,
43
- validationTimeout: config.validationTimeout || 120000,
43
+ validationTimeout: config.validationTimeout || 180000, // 180 seconds (3 minutes) to handle large payloads
44
44
  modelPaths: {},
45
45
  };
46
46
 
@@ -381,7 +381,7 @@ export class BiometricIdentitySDK {
381
381
  });
382
382
  }
383
383
 
384
- const MAX_FRAMES = 10;
384
+ const MAX_FRAMES = 5; // Reduced to 5 to reduce payload size and processing time
385
385
  if (videoFrames.length > MAX_FRAMES) {
386
386
  const step = Math.floor(videoFrames.length / MAX_FRAMES);
387
387
  videoFrames = videoFrames.filter((_, index) => index % step === 0).slice(0, MAX_FRAMES);
@@ -418,9 +418,10 @@ export class BiometricIdentitySDK {
418
418
  validFrames.push(compressedFrame);
419
419
  }
420
420
 
421
- if (validFrames.length < 10) {
422
- logger.error(`Insufficient valid frames: ${validFrames.length} < 10`);
423
- throw new Error(`Insufficient valid video frames: ${validFrames.length} frames available, minimum 10 required`);
421
+ const MIN_FRAMES = 5; // Minimum frames required for validation
422
+ if (validFrames.length < MIN_FRAMES) {
423
+ logger.error(`Insufficient valid frames: ${validFrames.length} < ${MIN_FRAMES}`);
424
+ throw new Error(`Insufficient valid video frames: ${validFrames.length} frames available, minimum ${MIN_FRAMES} required`);
424
425
  }
425
426
 
426
427
  logger.info('Sending validation request to backend', {
@@ -455,19 +456,32 @@ export class BiometricIdentitySDK {
455
456
  errorStack: error?.stack?.substring(0, 500)
456
457
  });
457
458
 
458
- const errorMessage = error?.message ||
459
- (error?.status ? `Backend request failed with status ${error.status}` : 'Backend validation failed');
459
+ // Handle timeout specifically with better error message
460
+ const isTimeout = error?.message?.includes('timeout') || error?.message?.includes('Request timeout') || error?.message?.includes('AbortError');
460
461
 
461
- throw this.createError(
462
+ const errorMessage = isTimeout
463
+ ? 'La validación está tomando demasiado tiempo. Por favor, verifica tu conexión e intenta nuevamente.'
464
+ : (error?.message || (error?.status ? `Backend request failed with status ${error.status}` : 'Backend validation failed'));
465
+
466
+ const errorToThrow = this.createError(
462
467
  BiometricErrorCode.NETWORK_ERROR,
463
468
  errorMessage,
464
469
  {
465
- status: error?.status,
466
- statusText: error?.statusText,
470
+ status: isTimeout ? 504 : error?.status,
471
+ statusText: isTimeout ? 'Gateway Timeout' : error?.statusText,
467
472
  errorData: error?.errorData,
468
473
  originalError: error?.message
469
474
  }
470
475
  );
476
+
477
+ // Update state before throwing so UI can show error
478
+ this.updateState({
479
+ isLoading: false,
480
+ currentStep: SDKStep.ERROR,
481
+ error: errorToThrow
482
+ });
483
+
484
+ throw errorToThrow;
471
485
  }
472
486
  }
473
487
 
@@ -531,12 +545,23 @@ export class BiometricIdentitySDK {
531
545
  return base64Image;
532
546
  }
533
547
 
534
- const MAX_SIZE = 500 * 1024;
548
+ const MAX_SIZE = 400 * 1024; // Balanced: reduce payload but maintain quality
535
549
  if (base64Image.length < MAX_SIZE) {
536
550
  return base64Image;
537
551
  }
538
552
 
539
553
  try {
554
+ // Try React Native compression first
555
+ const ImageResizer = (globalThis as any).ImageResizer || (globalThis as any).require?.('react-native-image-resizer');
556
+ if (ImageResizer && ImageResizer.default) {
557
+ try {
558
+ return await this.compressImageReactNative(base64Image, ImageResizer.default);
559
+ } catch (rnError) {
560
+ logger.warn('React Native compression failed, trying browser method', rnError);
561
+ }
562
+ }
563
+
564
+ // Try browser compression
540
565
  const globalWindow = (globalThis as any).window;
541
566
  if (globalWindow && globalWindow.Image && globalWindow.document) {
542
567
  return await this.compressImageBrowser(base64Image, globalWindow);
@@ -549,6 +574,26 @@ export class BiometricIdentitySDK {
549
574
  return base64Image;
550
575
  }
551
576
 
577
+ /**
578
+ * Compress image in React Native environment using react-native-image-resizer
579
+ */
580
+ private async compressImageReactNative(base64Image: string, ImageResizer: any): Promise<string> {
581
+ try {
582
+ // Convert base64 to temporary file path (this is a simplified approach)
583
+ // In practice, you'd need to write to a temp file first
584
+ // For now, we'll use a more aggressive browser-style compression
585
+ // that works in React Native by reducing quality
586
+
587
+ // Since we can't easily use ImageResizer without file paths in core package,
588
+ // we'll fall back to browser compression which should work in React Native context
589
+ // The actual compression should be done in the React Native package
590
+ throw new Error('React Native compression requires file path');
591
+ } catch (error) {
592
+ // Fall through to browser compression
593
+ throw error;
594
+ }
595
+ }
596
+
552
597
  /**
553
598
  * Compress image in browser environment using Canvas API
554
599
  */
@@ -557,8 +602,8 @@ export class BiometricIdentitySDK {
557
602
  const img = new window.Image();
558
603
  img.onload = () => {
559
604
  const canvas = window.document.createElement('canvas');
560
- const maxWidth = 1920;
561
- const maxHeight = 1920;
605
+ const maxWidth = 1600; // Balanced: reduce size but maintain OCR/face detection quality
606
+ const maxHeight = 1600; // Balanced: reduce size but maintain OCR/face detection quality
562
607
  let width = img.width;
563
608
  let height = img.height;
564
609
 
@@ -579,7 +624,7 @@ export class BiometricIdentitySDK {
579
624
 
580
625
  ctx.drawImage(img, 0, 0, width, height);
581
626
 
582
- const compressedBase64 = canvas.toDataURL('image/jpeg', 0.85);
627
+ const compressedBase64 = canvas.toDataURL('image/jpeg', 0.75); // Balanced: reduce size but maintain quality for OCR/face matching
583
628
  const base64 = compressedBase64.split(',')[1];
584
629
  resolve(base64);
585
630
  };
@@ -141,7 +141,7 @@ export class BackendClient {
141
141
  this.config = {
142
142
  apiEndpoint: config.apiEndpoint.replace(/\/$/, ''),
143
143
  apiKey: config.apiKey,
144
- timeout: config.timeout || 120000,
144
+ timeout: config.timeout || 180000, // 180 seconds (3 minutes) to handle large payloads
145
145
  };
146
146
  this.sdkSessionId = this.generateSDKSessionId();
147
147
  }