@sanctum-key/react-native-sdk 1.0.28 → 1.0.30

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.
@@ -59,8 +59,10 @@ export interface EnhancedCameraViewProps {
59
59
  barcode: unknown;
60
60
  }) => void;
61
61
  silentCaptureResult?: ISilentCaptureResult;
62
- onError?: (event: {
62
+ onError?: (error: {
63
63
  message: string;
64
+ name?: string;
65
+ code?: string;
64
66
  }) => void;
65
67
  onClose?: () => void;
66
68
  enableFlash?: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"type.d.ts","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewStyle } from \"react-native\";\nimport { ISilentCaptureResult } from \"../../types/KYC.types\";\n\nexport interface IdCardOverlayProps {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n cornerOpacity: number;\n instructions: string;\n cornerColor?: string;\n stepperProps?: StepperProps;\n instructionsTile?: string;\n instructionsSubtitle?: string;\n orientation?: 'center' | 'left' | 'right';\n isAnalyzing?: boolean;\n isSuccess?: boolean;\n language: string\n}\n\n\ninterface StepperProps {\n back: () => void;\n selectedDocumentType: string;\n step: number;\n totalSteps: number;\n side: string;\n}\n\nexport interface ExtractMrzTextResponse {\n parsed_data: {\n mrz_type: string;\n document_code: string;\n issuer_code: string;\n surname: string;\n given_name: string;\n document_number: string;\n document_number_checkdigit: string;\n nationality_code: string;\n birth_date: string;\n sex: string;\n expiry_date: string;\n optional_data: string;\n mrz_text: string;\n status: string;\n },\n success: boolean;\n}\n\n\n\nexport interface EnhancedCameraViewProps {\n instructions?: string;\n showCamera: boolean;\n cameraType?: 'front' | 'back';\n style?: ViewStyle;\n onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;\n onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;\n silentCaptureResult?: ISilentCaptureResult;\n onError?: (event: { message: string }) => void;\n onClose?: () => void;\n enableFlash?: boolean;\n enableHdr?: boolean;\n quality?: 'low' | 'medium' | 'high';\n showCaptureButton?: boolean;\n showSwitchCamera?: boolean;\n overlayComponent?: React.ReactNode;\n isProcessing?: boolean; \n bbox?: {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n };\n canFlip?: boolean;\n // Video recording props\n enableVideo?: boolean;\n isRecording?: boolean;\n onVideoRecordingStart?: () => void;\n onVideoRecordingStop?: (result: { success: boolean; path?: string; error?: string }) => void;\n videoDuration?: number;\n /** Delay in ms before first auto-capture (lets camera focus and user position document). Recommended 2500–3000 for ID/document capture. */\n captureStabilizationDelayMs?: number;\n}\n\nexport interface CheckTemplateTypeResponse {\n template_path: string;\n lpips_score: number;\n}"]}
1
+ {"version":3,"file":"type.js","sourceRoot":"","sources":["../../../../src/components/OverLay/type.ts"],"names":[],"mappings":"","sourcesContent":["import { ViewStyle } from \"react-native\";\nimport { ISilentCaptureResult } from \"../../types/KYC.types\";\n\nexport interface IdCardOverlayProps {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n cornerOpacity: number;\n instructions: string;\n cornerColor?: string;\n stepperProps?: StepperProps;\n instructionsTile?: string;\n instructionsSubtitle?: string;\n orientation?: 'center' | 'left' | 'right';\n isAnalyzing?: boolean;\n isSuccess?: boolean;\n language: string\n}\n\n\ninterface StepperProps {\n back: () => void;\n selectedDocumentType: string;\n step: number;\n totalSteps: number;\n side: string;\n}\n\nexport interface ExtractMrzTextResponse {\n parsed_data: {\n mrz_type: string;\n document_code: string;\n issuer_code: string;\n surname: string;\n given_name: string;\n document_number: string;\n document_number_checkdigit: string;\n nationality_code: string;\n birth_date: string;\n sex: string;\n expiry_date: string;\n optional_data: string;\n mrz_text: string;\n status: string;\n },\n success: boolean;\n}\n\n\n\nexport interface EnhancedCameraViewProps {\n instructions?: string;\n showCamera: boolean;\n cameraType?: 'front' | 'back';\n style?: ViewStyle;\n onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;\n onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;\n silentCaptureResult?: ISilentCaptureResult;\n onError?: (error: { \n message: string; \n name?: string;\n code?: string;\n }) => void;\n onClose?: () => void;\n enableFlash?: boolean;\n enableHdr?: boolean;\n quality?: 'low' | 'medium' | 'high';\n showCaptureButton?: boolean;\n showSwitchCamera?: boolean;\n overlayComponent?: React.ReactNode;\n isProcessing?: boolean; \n bbox?: {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n };\n canFlip?: boolean;\n // Video recording props\n enableVideo?: boolean;\n isRecording?: boolean;\n onVideoRecordingStart?: () => void;\n onVideoRecordingStop?: (result: { success: boolean; path?: string; error?: string }) => void;\n videoDuration?: number;\n /** Delay in ms before first auto-capture (lets camera focus and user position document). Recommended 2500–3000 for ID/document capture. */\n captureStabilizationDelayMs?: number;\n}\n\nexport interface CheckTemplateTypeResponse {\n template_path: string;\n lpips_score: number;\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanctum-key/react-native-sdk",
3
- "version": "1.0.28",
3
+ "version": "1.0.30",
4
4
  "description": "Sanctum Key React Native SDK",
5
5
  "main": "build/src/index.js",
6
6
  "types": "build/src/index.d.ts",
@@ -27,20 +27,20 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
27
27
  }) => {
28
28
  const { t } = useI18n();
29
29
  const camera = useRef<Camera>(null);
30
-
31
30
  const webVideoRef = useRef<HTMLVideoElement | null>(null);
31
+
32
32
  const isCapturingRef = useRef(false);
33
33
  const isProcessingRef = useRef(isProcessing);
34
+
34
35
  const [cameraType] = useState<'front' | 'back'>(initialCameraType);
35
36
  const [hasPermission, setHasPermission] = useState<boolean | null>(null);
36
37
  const [isInitialized, setIsInitialized] = useState(false);
37
38
  const [refreshCamera] = useState(false);
38
39
 
39
- // 🚨 NEW STATE: Tells React when it is safe to mount the Web Scanner
40
40
  const [isVideoFound, setIsVideoFound] = useState(false);
41
-
42
41
  const scannedBarcodeRef = useRef<string | null>(null);
43
42
  const containerRef = useRef<any>(null);
43
+
44
44
  const device = useCameraDevice(cameraType);
45
45
 
46
46
  useEffect(() => {
@@ -54,14 +54,22 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
54
54
  const granted = await VisionCameraModule.requestAllPermissions();
55
55
  if (!granted) {
56
56
  setHasPermission(false);
57
- onError?.({ message: t('camera.permissionRequired') });
57
+ onError?.({
58
+ message: t('camera.permissionRequired'),
59
+ name: 'NotAllowedError', // Emulate web standard for easy catching
60
+ code: 'permission-denied'
61
+ });
58
62
  return;
59
63
  }
60
64
  }
61
65
  setHasPermission(true);
62
- } catch {
66
+ } catch (e: any) {
63
67
  setHasPermission(false);
64
- onError?.({ message: t('camera.errorOccurred') });
68
+ onError?.({
69
+ message: e.message || t('camera.errorOccurred'),
70
+ name: e.name || 'UnknownError',
71
+ code: e.code || 'unknown'
72
+ });
65
73
  }
66
74
  };
67
75
 
@@ -70,8 +78,15 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
70
78
  }, [showCamera, refreshCamera]);
71
79
 
72
80
  const onInitialized = useCallback(() => setIsInitialized(true), []);
81
+
82
+ // 🚨 UPDATED: Catch Vision Camera deep errors (e.g. session-in-use) and pass them up
73
83
  const onCameraError = useCallback((error: any) => {
74
- onError?.({ message: error.message || t('camera.errorOccurred') });
84
+ console.warn("🎥 [Vision Camera Error Caught]:", error);
85
+ onError?.({
86
+ message: error.message || t('camera.errorOccurred'),
87
+ code: error.code, // Contains specific RNVC codes (e.g., 'session/camera-in-use')
88
+ name: error.name // Contains Web standard names (e.g., 'NotReadableError')
89
+ });
75
90
  }, [onError, t]);
76
91
 
77
92
  const codeScanner = useCodeScanner({
@@ -92,12 +107,22 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
92
107
  const captureSilentPhoto = useCallback(async () => {
93
108
  if (!camera.current || !isInitialized || isProcessingRef.current || isCapturingRef.current) return;
94
109
  if (silentCaptureResult?.isAnalyzing) return;
110
+
95
111
  try {
96
112
  isCapturingRef.current = true;
97
- const photo = await camera.current.takePhoto({ enableShutterSound: false, flash: 'off' });
113
+ const photo = await camera.current.takePhoto({
114
+ enableShutterSound: false,
115
+ flash: 'off'
116
+ });
117
+
98
118
  const result = await VisionCameraModule.processPhotoResult(photo);
99
- onSilentCapture?.({ ...result, path: result.path || photo.path, barcode: scannedBarcodeRef.current });
119
+ onSilentCapture?.({
120
+ ...result,
121
+ path: result.path || photo.path,
122
+ barcode: scannedBarcodeRef.current
123
+ });
100
124
  } catch {
125
+ // Intentionally swallow silent capture errors so we don't spam the UI
101
126
  } finally {
102
127
  isCapturingRef.current = false;
103
128
  }
@@ -107,11 +132,13 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
107
132
  if (!showCamera || !isInitialized || isProcessing) return;
108
133
  let active = true;
109
134
  let id: ReturnType<typeof setInterval>;
135
+
110
136
  const timer = setTimeout(() => {
111
137
  if (!active) return;
112
138
  const intervalDelay = isScanningBarcode ? 3000 : 1500;
113
139
  id = setInterval(captureSilentPhoto, intervalDelay);
114
140
  }, 1500);
141
+
115
142
  return () => {
116
143
  active = false;
117
144
  clearTimeout(timer);
@@ -119,16 +146,12 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
119
146
  };
120
147
  }, [showCamera, isInitialized, isProcessing, captureSilentPhoto, isScanningBarcode]);
121
148
 
122
- // Web: Aggressive Video Element Hunter
123
149
  useEffect(() => {
124
- // 🚨 Kick off the search immediately if we are on the web and the camera should be showing
125
150
  if (Platform.OS !== 'web' || !showCamera) return;
126
151
 
127
152
  let timeoutId: ReturnType<typeof setTimeout>;
128
-
153
+
129
154
  const findVideo = () => {
130
- console.log('🔍 [WEB ENGINE] Searching for <video> element in DOM...');
131
-
132
155
  let video: HTMLVideoElement | null = null;
133
156
  if (containerRef.current?.querySelector) {
134
157
  video = containerRef.current.querySelector('video');
@@ -136,28 +159,20 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
136
159
  if (!video) {
137
160
  video = document.querySelector('video');
138
161
  }
139
-
162
+
140
163
  if (video) {
141
- console.log('✅ [WEB ENGINE] <video> element locked! Booting ZXing Scanner.');
142
164
  webVideoRef.current = video;
143
- setIsVideoFound(true); // This tells React to mount the scanner!
165
+ setIsVideoFound(true);
144
166
  } else {
145
- console.log('⏳ [WEB ENGINE] <video> not found yet, retrying in 500ms...');
146
167
  timeoutId = setTimeout(findVideo, 500);
147
168
  }
148
169
  };
149
170
 
150
- // Start hunting immediately, do NOT wait for isInitialized
151
171
  findVideo();
152
-
153
172
  return () => clearTimeout(timeoutId);
154
173
  }, [showCamera]);
155
174
 
156
- // 🚨 FIX: Memoized Web Callback to prevent infinite re-rendering loop
157
175
  const handleWebBarcodeScanned = useCallback((value: string) => {
158
- console.log(`🎉 [WEB SCANNER] SUCCESS! Decoded value:`, value);
159
- alert(`Decoded: ${value.substring(0, 50)}...`);
160
-
161
176
  if (!isProcessingRef.current && value !== scannedBarcodeRef.current) {
162
177
  scannedBarcodeRef.current = value;
163
178
  onBarcodeScanned?.(value);
@@ -165,7 +180,27 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
165
180
  }, [onBarcodeScanned]);
166
181
 
167
182
  if (hasPermission === null) return <View style={[styles.container, style]} />;
168
- if (hasPermission === false) return <View style={[styles.container, style]}><Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text></View>;
183
+
184
+ if (hasPermission === false) return (
185
+ <View style={[styles.container, style]}>
186
+ <Text style={styles.permissionMessage}>{t('camera.permissionRequired')}</Text>
187
+ </View>
188
+ );
189
+
190
+ if (!device && showCamera) {
191
+ // We delay the missing device error slightly to account for the camera mounting delay
192
+ setTimeout(() => {
193
+ if (!device) {
194
+ onError?.({
195
+ message: "No camera detected",
196
+ name: "NotFoundError",
197
+ code: "device/not-found"
198
+ });
199
+ }
200
+ }, 1000);
201
+ return <View style={[styles.container, style]} />;
202
+ }
203
+
169
204
  if (!device || !showCamera) return <View style={[styles.container, style]} />;
170
205
 
171
206
  return (
@@ -183,7 +218,6 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
183
218
  onError={onCameraError}
184
219
  codeScanner={Platform.OS !== 'web' && isScanningBarcode ? codeScanner : undefined}
185
220
  />
186
-
187
221
  {Platform.OS === 'web' && isScanningBarcode && isVideoFound && (
188
222
  <WebBarcodeScanner
189
223
  videoRef={webVideoRef as React.RefObject<HTMLVideoElement>}
@@ -191,7 +225,6 @@ export const EnhancedCameraView: React.FC<ExtendedCameraProps> = ({
191
225
  active={!isProcessingRef.current}
192
226
  />
193
227
  )}
194
-
195
228
  {overlayComponent}
196
229
  </View>
197
230
  );
@@ -221,7 +221,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
221
221
  setTimeout(() => {
222
222
  setCameraKey(prev => prev + 1);
223
223
  setIsRebootingCamera(false);
224
- }, 500);
224
+ }, 1000);
225
225
  };
226
226
 
227
227
  const toggleCameraLens = () => {
@@ -686,8 +686,43 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
686
686
  }
687
687
  }, [scanPhase, pendingCAImage, silentCaptureResult.barcode]);
688
688
 
689
- const handleError = (event: { message: string }) => {
690
- showAlert('Erreur', event.message);
689
+ const handleError = (event: { message: string, name?: string, code?: string }) => {
690
+ logger.error("Camera Error:", event);
691
+
692
+ // Normalize the error to lowercase for easy checking
693
+ const errString = `${event.message} ${event.name} ${event.code}`.toLowerCase();
694
+
695
+ let friendlyMessage = state.currentLanguage === 'en'
696
+ ? 'An unknown camera error occurred.'
697
+ : 'Une erreur de caméra inconnue est survenue.';
698
+
699
+ // 1. Detect Hardware Lock (Camera in use by Zoom, Teams, or another app)
700
+ if (
701
+ errString.includes('notreadableerror') ||
702
+ errString.includes('trackstarterror') ||
703
+ errString.includes('in use') ||
704
+ errString.includes('concurrent') ||
705
+ errString.includes('camera unavailable')
706
+ ) {
707
+ friendlyMessage = state.currentLanguage === 'en'
708
+ ? 'Your camera is currently being used by another application (like Zoom or Teams). Please close the other app and try again.'
709
+ : 'Votre caméra est actuellement utilisée par une autre application. Veuillez fermer l\'autre application et réessayer.';
710
+ }
711
+ // 2. Detect Permission Denied
712
+ else if (errString.includes('notallowederror') || errString.includes('permission')) {
713
+ friendlyMessage = state.currentLanguage === 'en'
714
+ ? 'Camera permission denied. Please allow camera access in your settings to proceed.'
715
+ : 'Autorisation de la caméra refusée. Veuillez autoriser l\'accès dans vos paramètres.';
716
+ }
717
+ // 3. Detect No Camera Found
718
+ else if (errString.includes('notfounderror') || errString.includes('no camera')) {
719
+ friendlyMessage = state.currentLanguage === 'en'
720
+ ? 'No camera detected on this device.'
721
+ : 'Aucune caméra détectée sur cet appareil.';
722
+ }
723
+
724
+ showAlert(state.currentLanguage === 'en' ? 'Camera Error' : 'Erreur de Caméra', friendlyMessage);
725
+
691
726
  setShowCamera(false);
692
727
  setIsProcessingCapture(false);
693
728
  };
@@ -57,7 +57,11 @@ export interface EnhancedCameraViewProps {
57
57
  onCapture?: (result: { success: boolean; path?: string; error?: string }) => void;
58
58
  onSilentCapture?: (result: { success: boolean; path?: string; error?: string; barcode:unknown }) => void;
59
59
  silentCaptureResult?: ISilentCaptureResult;
60
- onError?: (event: { message: string }) => void;
60
+ onError?: (error: {
61
+ message: string;
62
+ name?: string;
63
+ code?: string;
64
+ }) => void;
61
65
  onClose?: () => void;
62
66
  enableFlash?: boolean;
63
67
  enableHdr?: boolean;