@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 ||
|
|
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 =
|
|
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
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
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 =
|
|
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 =
|
|
453
|
-
const maxHeight =
|
|
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.
|
|
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 ||
|
|
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
|
@@ -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 ||
|
|
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 =
|
|
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
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
|
|
459
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
561
|
-
const maxHeight =
|
|
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.
|
|
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
|
};
|
package/src/api/BackendClient.ts
CHANGED
|
@@ -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 ||
|
|
144
|
+
timeout: config.timeout || 180000, // 180 seconds (3 minutes) to handle large payloads
|
|
145
145
|
};
|
|
146
146
|
this.sdkSessionId = this.generateSDKSessionId();
|
|
147
147
|
}
|