@switchlabs/verify-ai-react-native 2.5.3 → 2.5.4

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.
@@ -8,6 +8,7 @@ import { SDK_VERSION } from '../version';
8
8
  import { BikeOverlay } from './BikeOverlay';
9
9
  import { ScooterOverlay } from './ScooterOverlay';
10
10
  import { ANDROID_ACCELEROMETER_AXIS_DOMINANCE_THRESHOLD, classifyAndroidAccelerometerOrientation, getOverlayRotationDeg, } from './scannerOrientation';
11
+ import { CAMERA_CAPTURE_RETRY_DELAY_MS, getCameraCaptureRetryPlan, } from './cameraCaptureRetry';
11
12
  /** Quality used when expo-image-manipulator is not available (lower = smaller). */
12
13
  const FALLBACK_QUALITY = 0.65;
13
14
  /** Quality used when expo-image-manipulator IS available (resize handles size). */
@@ -19,10 +20,6 @@ const CAMERA_NOT_READY_ERROR_CODE = 'ERR_CAMERA_NOT_READY';
19
20
  const CAMERA_INIT_ERROR_CODE = 'ERR_CAMERA_INIT_FAILED';
20
21
  const DEFAULT_TERMINAL_RESULT_DISPLAY_MS = 3000;
21
22
  const TRANSIENT_ERROR_DISPLAY_MS = 3000;
22
- const CAMERA_CAPTURE_RETRY_DELAY_MS = 350;
23
- const CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS = 800;
24
- const IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 3000;
25
- const ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 10000;
26
23
  // iOS AVCaptureSession cold start can legitimately exceed 8s on first launch (permission
27
24
  // prompt, first-ever session spin-up, thermal/low-power throttling). An 8s watchdog tore
28
25
  // down sessions that were seconds from ready and restarted from scratch, which made slow
@@ -958,6 +955,7 @@ export function VerifyAIScanner({ onCapture, policy, onResult, onError, onClose,
958
955
  let captureRetryTorchSuppressed = false;
959
956
  let captureRetrySettleDelayMs = CAMERA_CAPTURE_RETRY_DELAY_MS;
960
957
  let captureRetryRemountBackoffMs = 0;
958
+ let captureRetryReadyTimeoutMs = 0;
961
959
  let lastNativeCaptureErrorMessage = null;
962
960
  try {
963
961
  const capturePhysicalOrientation = physicalOrientation;
@@ -1025,13 +1023,11 @@ export function VerifyAIScanner({ onCapture, policy, onResult, onError, onClose,
1025
1023
  lastNativeCaptureErrorMessage = normalized.message;
1026
1024
  if (attempt === 1 && isNativeCameraCaptureError(normalized) && !terminated) {
1027
1025
  captureRetryAttempted = true;
1028
- captureRetryTorchSuppressed = !!enableTorch;
1029
- captureRetrySettleDelayMs = captureRetryTorchSuppressed
1030
- ? CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS
1031
- : CAMERA_CAPTURE_RETRY_DELAY_MS;
1032
- captureRetryRemountBackoffMs = captureRetryTorchSuppressed
1033
- ? CAMERA_CAPTURE_RETRY_DELAY_MS
1034
- : 0;
1026
+ const retryPlan = getCameraCaptureRetryPlan(Platform.OS, !!enableTorch);
1027
+ captureRetryTorchSuppressed = retryPlan.suppressTorch;
1028
+ captureRetrySettleDelayMs = retryPlan.settleDelayMs;
1029
+ captureRetryRemountBackoffMs = retryPlan.remountBackoffMs;
1030
+ captureRetryReadyTimeoutMs = retryPlan.readyTimeoutMs;
1035
1031
  const retryAt = new Date().toISOString();
1036
1032
  lastCaptureRetryAtRef.current = retryAt;
1037
1033
  cameraRemountCountRef.current++;
@@ -1042,9 +1038,6 @@ export function VerifyAIScanner({ onCapture, policy, onResult, onError, onClose,
1042
1038
  ? { backoffMs: captureRetryRemountBackoffMs }
1043
1039
  : undefined);
1044
1040
  await sleep(captureRetrySettleDelayMs);
1045
- const captureRetryReadyTimeoutMs = Platform.OS === 'android'
1046
- ? ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS
1047
- : IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS;
1048
1041
  captureRetryReady = await waitForCameraReady(captureRetryReadyTimeoutMs);
1049
1042
  telemetry?.track('camera_capture_retry', {
1050
1043
  component: 'scanner',
@@ -1142,6 +1135,7 @@ export function VerifyAIScanner({ onCapture, policy, onResult, onError, onClose,
1142
1135
  capture_retry_torch_suppressed: captureRetryTorchSuppressed,
1143
1136
  capture_retry_settle_delay_ms: captureRetrySettleDelayMs,
1144
1137
  capture_retry_remount_backoff_ms: captureRetryRemountBackoffMs,
1138
+ capture_retry_ready_timeout_ms: captureRetryReadyTimeoutMs,
1145
1139
  recovered_native_capture_failure: captureRetryAttempted ? 1 : 0,
1146
1140
  last_native_capture_error: lastNativeCaptureErrorMessage,
1147
1141
  }),
@@ -1218,6 +1212,7 @@ export function VerifyAIScanner({ onCapture, policy, onResult, onError, onClose,
1218
1212
  capture_retry_torch_suppressed: captureRetryTorchSuppressed,
1219
1213
  capture_retry_settle_delay_ms: captureRetrySettleDelayMs,
1220
1214
  capture_retry_remount_backoff_ms: captureRetryRemountBackoffMs,
1215
+ capture_retry_ready_timeout_ms: captureRetryReadyTimeoutMs,
1221
1216
  is_native_camera_capture_error: isNativeCameraCaptureError(error),
1222
1217
  last_native_capture_error: lastNativeCaptureErrorMessage,
1223
1218
  }),
@@ -0,0 +1,12 @@
1
+ export declare const CAMERA_CAPTURE_RETRY_DELAY_MS = 350;
2
+ export declare const CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS = 800;
3
+ export declare const IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 3000;
4
+ export declare const ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 10000;
5
+ export type CameraCaptureRetryPlatform = 'ios' | 'android' | string;
6
+ export interface CameraCaptureRetryPlan {
7
+ readyTimeoutMs: number;
8
+ remountBackoffMs: number;
9
+ settleDelayMs: number;
10
+ suppressTorch: boolean;
11
+ }
12
+ export declare function getCameraCaptureRetryPlan(platform: CameraCaptureRetryPlatform, torchRequested: boolean): CameraCaptureRetryPlan;
@@ -0,0 +1,23 @@
1
+ export const CAMERA_CAPTURE_RETRY_DELAY_MS = 350;
2
+ export const CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS = 800;
3
+ export const IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 3000;
4
+ export const ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 10000;
5
+ export function getCameraCaptureRetryPlan(platform, torchRequested) {
6
+ const suppressTorch = torchRequested;
7
+ return {
8
+ readyTimeoutMs: platform === 'android'
9
+ ? ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS
10
+ : IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS,
11
+ // Flutter fully restarts its CameraController after native capture errors.
12
+ // On iOS/Expo Camera, a key-only remount can report ready too quickly while
13
+ // the native capture session is still unable to produce a photo, so force a
14
+ // brief unmount before the retry.
15
+ remountBackoffMs: platform === 'ios' || suppressTorch
16
+ ? CAMERA_CAPTURE_RETRY_DELAY_MS
17
+ : 0,
18
+ settleDelayMs: suppressTorch
19
+ ? CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS
20
+ : CAMERA_CAPTURE_RETRY_DELAY_MS,
21
+ suppressTorch,
22
+ };
23
+ }
@@ -33,6 +33,8 @@ export interface VerificationResult {
33
33
  policy: string;
34
34
  violation_reasons: string[];
35
35
  feedback: string;
36
+ warning_violation_reasons?: string[];
37
+ warning_feedback?: string | null;
36
38
  metadata: Record<string, unknown>;
37
39
  image_url: string | null;
38
40
  /** Base64-encoded image data URI. Only present when `include_image_data` is requested. */
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "2.5.2";
1
+ export declare const SDK_VERSION = "2.5.4";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '2.5.2';
1
+ export const SDK_VERSION = '2.5.4';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@switchlabs/verify-ai-react-native",
3
- "version": "2.5.3",
3
+ "version": "2.5.4",
4
4
  "description": "React Native SDK for Verify AI - photo verification with AI vision processing",
5
5
  "repository": {
6
6
  "type": "git",
@@ -25,6 +25,7 @@
25
25
  ],
26
26
  "scripts": {
27
27
  "typecheck": "tsc --noEmit",
28
+ "test": "vitest run",
28
29
  "build": "tsc",
29
30
  "prepublishOnly": "npm run build"
30
31
  },
@@ -32,6 +32,10 @@ import {
32
32
  getOverlayRotationDeg,
33
33
  type ScannerPhysicalOrientation,
34
34
  } from './scannerOrientation';
35
+ import {
36
+ CAMERA_CAPTURE_RETRY_DELAY_MS,
37
+ getCameraCaptureRetryPlan,
38
+ } from './cameraCaptureRetry';
35
39
 
36
40
  /** Quality used when expo-image-manipulator is not available (lower = smaller). */
37
41
  const FALLBACK_QUALITY = 0.65;
@@ -44,10 +48,6 @@ const CAMERA_NOT_READY_ERROR_CODE = 'ERR_CAMERA_NOT_READY';
44
48
  const CAMERA_INIT_ERROR_CODE = 'ERR_CAMERA_INIT_FAILED';
45
49
  const DEFAULT_TERMINAL_RESULT_DISPLAY_MS = 3000;
46
50
  const TRANSIENT_ERROR_DISPLAY_MS = 3000;
47
- const CAMERA_CAPTURE_RETRY_DELAY_MS = 350;
48
- const CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS = 800;
49
- const IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 3000;
50
- const ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 10000;
51
51
  // iOS AVCaptureSession cold start can legitimately exceed 8s on first launch (permission
52
52
  // prompt, first-ever session spin-up, thermal/low-power throttling). An 8s watchdog tore
53
53
  // down sessions that were seconds from ready and restarted from scratch, which made slow
@@ -1172,6 +1172,7 @@ export function VerifyAIScanner({
1172
1172
  let captureRetryTorchSuppressed = false;
1173
1173
  let captureRetrySettleDelayMs = CAMERA_CAPTURE_RETRY_DELAY_MS;
1174
1174
  let captureRetryRemountBackoffMs = 0;
1175
+ let captureRetryReadyTimeoutMs = 0;
1175
1176
  let lastNativeCaptureErrorMessage: string | null = null;
1176
1177
 
1177
1178
  try {
@@ -1251,13 +1252,11 @@ export function VerifyAIScanner({
1251
1252
 
1252
1253
  if (attempt === 1 && isNativeCameraCaptureError(normalized) && !terminated) {
1253
1254
  captureRetryAttempted = true;
1254
- captureRetryTorchSuppressed = !!enableTorch;
1255
- captureRetrySettleDelayMs = captureRetryTorchSuppressed
1256
- ? CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS
1257
- : CAMERA_CAPTURE_RETRY_DELAY_MS;
1258
- captureRetryRemountBackoffMs = captureRetryTorchSuppressed
1259
- ? CAMERA_CAPTURE_RETRY_DELAY_MS
1260
- : 0;
1255
+ const retryPlan = getCameraCaptureRetryPlan(Platform.OS, !!enableTorch);
1256
+ captureRetryTorchSuppressed = retryPlan.suppressTorch;
1257
+ captureRetrySettleDelayMs = retryPlan.settleDelayMs;
1258
+ captureRetryRemountBackoffMs = retryPlan.remountBackoffMs;
1259
+ captureRetryReadyTimeoutMs = retryPlan.readyTimeoutMs;
1261
1260
  const retryAt = new Date().toISOString();
1262
1261
  lastCaptureRetryAtRef.current = retryAt;
1263
1262
  cameraRemountCountRef.current++;
@@ -1271,9 +1270,6 @@ export function VerifyAIScanner({
1271
1270
  : undefined,
1272
1271
  );
1273
1272
  await sleep(captureRetrySettleDelayMs);
1274
- const captureRetryReadyTimeoutMs = Platform.OS === 'android'
1275
- ? ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS
1276
- : IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS;
1277
1273
  captureRetryReady = await waitForCameraReady(captureRetryReadyTimeoutMs);
1278
1274
  telemetry?.track('camera_capture_retry', {
1279
1275
  component: 'scanner',
@@ -1397,6 +1393,7 @@ export function VerifyAIScanner({
1397
1393
  capture_retry_torch_suppressed: captureRetryTorchSuppressed,
1398
1394
  capture_retry_settle_delay_ms: captureRetrySettleDelayMs,
1399
1395
  capture_retry_remount_backoff_ms: captureRetryRemountBackoffMs,
1396
+ capture_retry_ready_timeout_ms: captureRetryReadyTimeoutMs,
1400
1397
  recovered_native_capture_failure: captureRetryAttempted ? 1 : 0,
1401
1398
  last_native_capture_error: lastNativeCaptureErrorMessage,
1402
1399
  }),
@@ -1479,6 +1476,7 @@ export function VerifyAIScanner({
1479
1476
  capture_retry_torch_suppressed: captureRetryTorchSuppressed,
1480
1477
  capture_retry_settle_delay_ms: captureRetrySettleDelayMs,
1481
1478
  capture_retry_remount_backoff_ms: captureRetryRemountBackoffMs,
1479
+ capture_retry_ready_timeout_ms: captureRetryReadyTimeoutMs,
1482
1480
  is_native_camera_capture_error: isNativeCameraCaptureError(error),
1483
1481
  last_native_capture_error: lastNativeCaptureErrorMessage,
1484
1482
  }),
@@ -0,0 +1,37 @@
1
+ export const CAMERA_CAPTURE_RETRY_DELAY_MS = 350;
2
+ export const CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS = 800;
3
+ export const IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 3000;
4
+ export const ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS = 10000;
5
+
6
+ export type CameraCaptureRetryPlatform = 'ios' | 'android' | string;
7
+
8
+ export interface CameraCaptureRetryPlan {
9
+ readyTimeoutMs: number;
10
+ remountBackoffMs: number;
11
+ settleDelayMs: number;
12
+ suppressTorch: boolean;
13
+ }
14
+
15
+ export function getCameraCaptureRetryPlan(
16
+ platform: CameraCaptureRetryPlatform,
17
+ torchRequested: boolean,
18
+ ): CameraCaptureRetryPlan {
19
+ const suppressTorch = torchRequested;
20
+
21
+ return {
22
+ readyTimeoutMs: platform === 'android'
23
+ ? ANDROID_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS
24
+ : IOS_CAMERA_CAPTURE_RETRY_READY_TIMEOUT_MS,
25
+ // Flutter fully restarts its CameraController after native capture errors.
26
+ // On iOS/Expo Camera, a key-only remount can report ready too quickly while
27
+ // the native capture session is still unable to produce a photo, so force a
28
+ // brief unmount before the retry.
29
+ remountBackoffMs: platform === 'ios' || suppressTorch
30
+ ? CAMERA_CAPTURE_RETRY_DELAY_MS
31
+ : 0,
32
+ settleDelayMs: suppressTorch
33
+ ? CAMERA_CAPTURE_RETRY_TORCH_SETTLE_MS
34
+ : CAMERA_CAPTURE_RETRY_DELAY_MS,
35
+ suppressTorch,
36
+ };
37
+ }
@@ -37,6 +37,8 @@ export interface VerificationResult {
37
37
  policy: string;
38
38
  violation_reasons: string[];
39
39
  feedback: string;
40
+ warning_violation_reasons?: string[];
41
+ warning_feedback?: string | null;
40
42
  metadata: Record<string, unknown>;
41
43
  image_url: string | null;
42
44
  /** Base64-encoded image data URI. Only present when `include_image_data` is requested. */
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '2.5.2';
1
+ export const SDK_VERSION = '2.5.4';