@hexar/biometric-identity-sdk-core 1.0.15 → 1.0.17

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.
@@ -82,13 +82,21 @@ export declare class BiometricIdentitySDK {
82
82
  /**
83
83
  * Compress base64 image - simplified version that works in all environments
84
84
  * For React Native, images should be compressed at capture time
85
- * This is a no-op for now - compression should happen at capture time in React Native
85
+ * This reduces large images by skipping compression if already small enough
86
86
  */
87
87
  private compressImage;
88
88
  /**
89
89
  * Compress image in browser environment using Canvas API
90
90
  */
91
91
  private compressImageBrowser;
92
+ /**
93
+ * Fix base64 padding issues
94
+ */
95
+ private fixBase64Padding;
96
+ /**
97
+ * Validate if string is valid base64
98
+ */
99
+ private isValidBase64;
92
100
  /**
93
101
  * Get current SDK state
94
102
  */
@@ -299,7 +299,7 @@ class BiometricIdentitySDK {
299
299
  minimumRequired: 10
300
300
  });
301
301
  }
302
- const MAX_FRAMES = 20;
302
+ const MAX_FRAMES = 15;
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);
@@ -308,13 +308,34 @@ class BiometricIdentitySDK {
308
308
  sampledCount: videoFrames.length
309
309
  });
310
310
  }
311
- const compressedFrontImage = await this.compressImage(this.state.frontID.data);
311
+ const compressedFrontImage = this.fixBase64Padding(await this.compressImage(this.state.frontID.data));
312
312
  const compressedBackImage = this.state.backID?.data
313
- ? await this.compressImage(this.state.backID.data)
313
+ ? this.fixBase64Padding(await this.compressImage(this.state.backID.data))
314
314
  : undefined;
315
- const compressedFrames = await Promise.all(videoFrames.map(frame => this.compressImage(frame)));
315
+ const validFrames = [];
316
+ for (let i = 0; i < videoFrames.length; i++) {
317
+ const frame = videoFrames[i];
318
+ if (!frame || typeof frame !== 'string') {
319
+ logger_1.logger.warn(`Skipping invalid frame ${i}: not a string`);
320
+ continue;
321
+ }
322
+ if (frame.length < 100) {
323
+ logger_1.logger.warn(`Skipping invalid frame ${i}: too short (${frame.length} chars), likely a file path`);
324
+ continue;
325
+ }
326
+ if (!this.isValidBase64(frame)) {
327
+ logger_1.logger.warn(`Skipping invalid frame ${i}: not valid base64`);
328
+ continue;
329
+ }
330
+ const compressedFrame = this.fixBase64Padding(await this.compressImage(frame));
331
+ validFrames.push(compressedFrame);
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`);
336
+ }
316
337
  logger_1.logger.info('Sending validation request to backend', {
317
- framesCount: compressedFrames.length,
338
+ framesCount: validFrames.length,
318
339
  duration: this.state.videoData.duration,
319
340
  challengesCount: this.state.videoData.challengesCompleted?.length || 0,
320
341
  frontImageSize: compressedFrontImage.length,
@@ -326,7 +347,7 @@ class BiometricIdentitySDK {
326
347
  const response = await this.backendClient.fullValidation({
327
348
  frontIdImage: compressedFrontImage,
328
349
  backIdImage: compressedBackImage,
329
- videoFrames: compressedFrames,
350
+ videoFrames: validFrames,
330
351
  videoDurationMs: this.state.videoData.duration,
331
352
  challengesCompleted: this.state.videoData.challengesCompleted || [],
332
353
  });
@@ -398,13 +419,13 @@ class BiometricIdentitySDK {
398
419
  /**
399
420
  * Compress base64 image - simplified version that works in all environments
400
421
  * For React Native, images should be compressed at capture time
401
- * This is a no-op for now - compression should happen at capture time in React Native
422
+ * This reduces large images by skipping compression if already small enough
402
423
  */
403
424
  async compressImage(base64Image) {
404
425
  if (!base64Image || base64Image.length === 0) {
405
426
  return base64Image;
406
427
  }
407
- const MAX_SIZE = 1024 * 1024;
428
+ const MAX_SIZE = 500 * 1024;
408
429
  if (base64Image.length < MAX_SIZE) {
409
430
  return base64Image;
410
431
  }
@@ -417,6 +438,7 @@ class BiometricIdentitySDK {
417
438
  catch (error) {
418
439
  logger_1.logger.warn('Image compression not available, using original', error);
419
440
  }
441
+ logger_1.logger.warn(`Image too large (${Math.round(base64Image.length / 1024)}KB) but compression not available`);
420
442
  return base64Image;
421
443
  }
422
444
  /**
@@ -452,6 +474,35 @@ class BiometricIdentitySDK {
452
474
  img.src = `data:image/jpeg;base64,${base64Image}`;
453
475
  });
454
476
  }
477
+ /**
478
+ * Fix base64 padding issues
479
+ */
480
+ fixBase64Padding(base64) {
481
+ if (!base64)
482
+ return base64;
483
+ let cleaned = base64.replace(/\s/g, '');
484
+ if (cleaned.includes(',')) {
485
+ cleaned = cleaned.split(',')[1] || cleaned;
486
+ }
487
+ const padding = cleaned.length % 4;
488
+ if (padding > 0) {
489
+ cleaned += '='.repeat(4 - padding);
490
+ }
491
+ return cleaned;
492
+ }
493
+ /**
494
+ * Validate if string is valid base64
495
+ */
496
+ isValidBase64(str) {
497
+ if (!str || typeof str !== 'string')
498
+ return false;
499
+ const cleaned = str.replace(/\s/g, '');
500
+ if (cleaned.includes(',')) {
501
+ return true;
502
+ }
503
+ const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
504
+ return base64Regex.test(cleaned) && cleaned.length > 100;
505
+ }
455
506
  /**
456
507
  * Get current SDK state
457
508
  */
@@ -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 || 60000,
19
+ timeout: config.timeout || 120000,
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.15",
3
+ "version": "1.0.17",
4
4
  "description": "Core AI engine for biometric identity verification",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -381,7 +381,7 @@ export class BiometricIdentitySDK {
381
381
  });
382
382
  }
383
383
 
384
- const MAX_FRAMES = 20;
384
+ const MAX_FRAMES = 15;
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);
@@ -391,16 +391,40 @@ export class BiometricIdentitySDK {
391
391
  });
392
392
  }
393
393
 
394
- const compressedFrontImage = await this.compressImage(this.state.frontID.data);
394
+ const compressedFrontImage = this.fixBase64Padding(await this.compressImage(this.state.frontID.data));
395
395
  const compressedBackImage = this.state.backID?.data
396
- ? await this.compressImage(this.state.backID.data)
396
+ ? this.fixBase64Padding(await this.compressImage(this.state.backID.data))
397
397
  : undefined;
398
- const compressedFrames = await Promise.all(
399
- videoFrames.map(frame => this.compressImage(frame))
400
- );
398
+
399
+ const validFrames: string[] = [];
400
+ for (let i = 0; i < videoFrames.length; i++) {
401
+ const frame = videoFrames[i];
402
+ if (!frame || typeof frame !== 'string') {
403
+ logger.warn(`Skipping invalid frame ${i}: not a string`);
404
+ continue;
405
+ }
406
+
407
+ if (frame.length < 100) {
408
+ logger.warn(`Skipping invalid frame ${i}: too short (${frame.length} chars), likely a file path`);
409
+ continue;
410
+ }
411
+
412
+ if (!this.isValidBase64(frame)) {
413
+ logger.warn(`Skipping invalid frame ${i}: not valid base64`);
414
+ continue;
415
+ }
416
+
417
+ const compressedFrame = this.fixBase64Padding(await this.compressImage(frame));
418
+ validFrames.push(compressedFrame);
419
+ }
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`);
424
+ }
401
425
 
402
426
  logger.info('Sending validation request to backend', {
403
- framesCount: compressedFrames.length,
427
+ framesCount: validFrames.length,
404
428
  duration: this.state.videoData.duration,
405
429
  challengesCount: this.state.videoData.challengesCompleted?.length || 0,
406
430
  frontImageSize: compressedFrontImage.length,
@@ -413,7 +437,7 @@ export class BiometricIdentitySDK {
413
437
  const response = await this.backendClient.fullValidation({
414
438
  frontIdImage: compressedFrontImage,
415
439
  backIdImage: compressedBackImage,
416
- videoFrames: compressedFrames,
440
+ videoFrames: validFrames,
417
441
  videoDurationMs: this.state.videoData.duration,
418
442
  challengesCompleted: this.state.videoData.challengesCompleted || [],
419
443
  });
@@ -500,14 +524,14 @@ export class BiometricIdentitySDK {
500
524
  /**
501
525
  * Compress base64 image - simplified version that works in all environments
502
526
  * For React Native, images should be compressed at capture time
503
- * This is a no-op for now - compression should happen at capture time in React Native
527
+ * This reduces large images by skipping compression if already small enough
504
528
  */
505
529
  private async compressImage(base64Image: string): Promise<string> {
506
530
  if (!base64Image || base64Image.length === 0) {
507
531
  return base64Image;
508
532
  }
509
533
 
510
- const MAX_SIZE = 1024 * 1024;
534
+ const MAX_SIZE = 500 * 1024;
511
535
  if (base64Image.length < MAX_SIZE) {
512
536
  return base64Image;
513
537
  }
@@ -521,6 +545,7 @@ export class BiometricIdentitySDK {
521
545
  logger.warn('Image compression not available, using original', error);
522
546
  }
523
547
 
548
+ logger.warn(`Image too large (${Math.round(base64Image.length / 1024)}KB) but compression not available`);
524
549
  return base64Image;
525
550
  }
526
551
 
@@ -563,6 +588,41 @@ export class BiometricIdentitySDK {
563
588
  });
564
589
  }
565
590
 
591
+ /**
592
+ * Fix base64 padding issues
593
+ */
594
+ private fixBase64Padding(base64: string): string {
595
+ if (!base64) return base64;
596
+
597
+ let cleaned = base64.replace(/\s/g, '');
598
+
599
+ if (cleaned.includes(',')) {
600
+ cleaned = cleaned.split(',')[1] || cleaned;
601
+ }
602
+
603
+ const padding = cleaned.length % 4;
604
+ if (padding > 0) {
605
+ cleaned += '='.repeat(4 - padding);
606
+ }
607
+
608
+ return cleaned;
609
+ }
610
+
611
+ /**
612
+ * Validate if string is valid base64
613
+ */
614
+ private isValidBase64(str: string): boolean {
615
+ if (!str || typeof str !== 'string') return false;
616
+
617
+ const cleaned = str.replace(/\s/g, '');
618
+ if (cleaned.includes(',')) {
619
+ return true;
620
+ }
621
+
622
+ const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/;
623
+ return base64Regex.test(cleaned) && cleaned.length > 100;
624
+ }
625
+
566
626
  /**
567
627
  * Get current SDK state
568
628
  */
@@ -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 || 60000,
144
+ timeout: config.timeout || 120000,
145
145
  };
146
146
  this.sdkSessionId = this.generateSDKSessionId();
147
147
  }