@hexar/biometric-identity-sdk-react-native 1.1.27 → 1.2.0

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiDD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA45BtD,CAAC;AA6OF,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"VideoRecorder.d.ts","sourceRoot":"","sources":["../../src/components/VideoRecorder.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAmD,MAAM,OAAO,CAAC;AAaxE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,iBAAiB,EAAmC,MAAM,oCAAoC,CAAC;AAE1I,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,sEAAsE;IACtE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,sCAAsC;IACtC,YAAY,CAAC,EAAE,mBAAmB,EAAE,CAAC;IACrC,0CAA0C;IAC1C,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,kDAAkD;IAClD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wCAAwC;IACxC,UAAU,EAAE,CAAC,SAAS,EAAE,oBAAoB,KAAK,IAAI,CAAC;IACtD,iCAAiC;IACjC,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,gDAAgD;IAChD,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,OAAO,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC;IACrB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAiDD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAm6BtD,CAAC;AA6OF,eAAe,aAAa,CAAC"}
@@ -349,16 +349,16 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
349
349
  }, [frames, completedChallenges, challenges, sessionId, onComplete, resetAndRetry, handleRecordingError, strings, minDurationMs, phase]);
350
350
  const startFrameCapture = (0, react_1.useCallback)(() => {
351
351
  if (cameraRef.current && device) {
352
- biometric_identity_sdk_core_1.logger.info('Starting frame capture');
352
+ biometric_identity_sdk_core_1.logger.info('Starting serial frame capture');
353
353
  framesRef.current = [];
354
354
  let consecutiveErrors = 0;
355
- const maxConsecutiveErrors = 5;
356
- frameCaptureInterval.current = setInterval(async () => {
357
- if (!isRecordingRef.current) {
358
- if (frameCaptureInterval.current) {
359
- clearInterval(frameCaptureInterval.current);
360
- frameCaptureInterval.current = null;
361
- }
355
+ const maxConsecutiveErrors = 10;
356
+ const MAX_FRAMES = 80;
357
+ // Serial capture: each frame is captured only after the previous one
358
+ // finishes, preventing overlapping takePhoto() calls that cause the
359
+ // camera to throw "busy" errors and kill the capture loop.
360
+ const captureNextFrame = async () => {
361
+ if (!isRecordingRef.current || framesRef.current.length >= MAX_FRAMES) {
362
362
  return;
363
363
  }
364
364
  try {
@@ -395,7 +395,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
395
395
  }
396
396
  if (base64Data) {
397
397
  setFrames(prev => {
398
- const newFrames = prev.length < 100 ? [...prev, base64Data] : prev;
398
+ const newFrames = prev.length < MAX_FRAMES ? [...prev, base64Data] : prev;
399
399
  framesRef.current = newFrames;
400
400
  if (newFrames.length % 10 === 0) {
401
401
  biometric_identity_sdk_core_1.logger.info('Captured frames:', newFrames.length);
@@ -410,14 +410,19 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
410
410
  }
411
411
  catch (error) {
412
412
  consecutiveErrors++;
413
+ biometric_identity_sdk_core_1.logger.warn('Frame capture error', consecutiveErrors, '/', maxConsecutiveErrors, error?.message);
413
414
  if (consecutiveErrors >= maxConsecutiveErrors) {
414
- if (frameCaptureInterval.current) {
415
- clearInterval(frameCaptureInterval.current);
416
- frameCaptureInterval.current = null;
417
- }
415
+ biometric_identity_sdk_core_1.logger.error('Too many consecutive capture errors, stopping');
416
+ return;
418
417
  }
419
418
  }
420
- }, 100);
419
+ // Schedule next capture only after this one completes (serial chain).
420
+ // Small delay lets the camera hardware reset between captures.
421
+ if (isRecordingRef.current && framesRef.current.length < MAX_FRAMES) {
422
+ frameCaptureInterval.current = setTimeout(captureNextFrame, 150);
423
+ }
424
+ };
425
+ captureNextFrame();
421
426
  }
422
427
  else {
423
428
  biometric_identity_sdk_core_1.logger.warn('Cannot start frame capture: camera or device not available');
@@ -429,7 +434,7 @@ const VideoRecorder = ({ theme, language, duration, instructions, challenges: pr
429
434
  recordingTimeoutRef.current = null;
430
435
  }
431
436
  if (frameCaptureInterval.current) {
432
- clearInterval(frameCaptureInterval.current);
437
+ clearTimeout(frameCaptureInterval.current);
433
438
  frameCaptureInterval.current = null;
434
439
  }
435
440
  if (!isRecordingRef.current) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hexar/biometric-identity-sdk-react-native",
3
- "version": "1.1.27",
3
+ "version": "1.2.0",
4
4
  "description": "React Native wrapper for Biometric Identity SDK",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -11,7 +11,7 @@
11
11
  "clean": "rm -rf dist"
12
12
  },
13
13
  "peerDependencies": {
14
- "@hexar/biometric-identity-sdk-core": ">=1.1.6",
14
+ "@hexar/biometric-identity-sdk-core": ">=1.1.7",
15
15
  "react": ">=18.0.0",
16
16
  "react-native": ">=0.70.0",
17
17
  "react-native-permissions": ">=4.0.0",
@@ -417,29 +417,29 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
417
417
 
418
418
  const startFrameCapture = useCallback(() => {
419
419
  if (cameraRef.current && device) {
420
- logger.info('Starting frame capture');
420
+ logger.info('Starting serial frame capture');
421
421
  framesRef.current = [];
422
422
  let consecutiveErrors = 0;
423
- const maxConsecutiveErrors = 5;
424
-
425
- frameCaptureInterval.current = setInterval(async () => {
426
- if (!isRecordingRef.current) {
427
- if (frameCaptureInterval.current) {
428
- clearInterval(frameCaptureInterval.current);
429
- frameCaptureInterval.current = null;
430
- }
423
+ const maxConsecutiveErrors = 10;
424
+ const MAX_FRAMES = 80;
425
+
426
+ // Serial capture: each frame is captured only after the previous one
427
+ // finishes, preventing overlapping takePhoto() calls that cause the
428
+ // camera to throw "busy" errors and kill the capture loop.
429
+ const captureNextFrame = async () => {
430
+ if (!isRecordingRef.current || framesRef.current.length >= MAX_FRAMES) {
431
431
  return;
432
432
  }
433
-
433
+
434
434
  try {
435
435
  const photo = await cameraRef.current?.takePhoto({
436
436
  flash: 'off',
437
437
  });
438
-
438
+
439
439
  if (photo) {
440
440
  consecutiveErrors = 0;
441
441
  let base64Data: string | null = null;
442
-
442
+
443
443
  try {
444
444
  const RNFS = require('react-native-fs');
445
445
  base64Data = await RNFS.readFile(photo.path, 'base64');
@@ -463,10 +463,10 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
463
463
  base64Data = null;
464
464
  }
465
465
  }
466
-
466
+
467
467
  if (base64Data) {
468
468
  setFrames(prev => {
469
- const newFrames = prev.length < 100 ? [...prev, base64Data!] : prev;
469
+ const newFrames = prev.length < MAX_FRAMES ? [...prev, base64Data!] : prev;
470
470
  framesRef.current = newFrames;
471
471
  if (newFrames.length % 10 === 0) {
472
472
  logger.info('Captured frames:', newFrames.length);
@@ -479,14 +479,21 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
479
479
  }
480
480
  } catch (error: any) {
481
481
  consecutiveErrors++;
482
+ logger.warn('Frame capture error', consecutiveErrors, '/', maxConsecutiveErrors, error?.message);
482
483
  if (consecutiveErrors >= maxConsecutiveErrors) {
483
- if (frameCaptureInterval.current) {
484
- clearInterval(frameCaptureInterval.current);
485
- frameCaptureInterval.current = null;
486
- }
484
+ logger.error('Too many consecutive capture errors, stopping');
485
+ return;
487
486
  }
488
487
  }
489
- }, 100);
488
+
489
+ // Schedule next capture only after this one completes (serial chain).
490
+ // Small delay lets the camera hardware reset between captures.
491
+ if (isRecordingRef.current && framesRef.current.length < MAX_FRAMES) {
492
+ frameCaptureInterval.current = setTimeout(captureNextFrame, 150);
493
+ }
494
+ };
495
+
496
+ captureNextFrame();
490
497
  } else {
491
498
  logger.warn('Cannot start frame capture: camera or device not available');
492
499
  }
@@ -499,7 +506,7 @@ export const VideoRecorder: React.FC<VideoRecorderProps> = ({
499
506
  }
500
507
 
501
508
  if (frameCaptureInterval.current) {
502
- clearInterval(frameCaptureInterval.current);
509
+ clearTimeout(frameCaptureInterval.current);
503
510
  frameCaptureInterval.current = null;
504
511
  }
505
512