@switchlabs/verify-ai-react-native 0.1.0 → 0.1.1

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,6 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VerifyAIRequestError = exports.VerifyAIClient = void 0;
1
4
  const DEFAULT_BASE_URL = 'https://verify.switchlabs.dev/api/v1';
2
5
  const DEFAULT_TIMEOUT = 30000;
3
- export class VerifyAIClient {
6
+ class VerifyAIClient {
4
7
  constructor(config) {
5
8
  if (!config.apiKey) {
6
9
  throw new Error('VerifyAI: apiKey is required');
@@ -105,7 +108,8 @@ export class VerifyAIClient {
105
108
  return this.request(`/verifications/${id}`);
106
109
  }
107
110
  }
108
- export class VerifyAIRequestError extends Error {
111
+ exports.VerifyAIClient = VerifyAIClient;
112
+ class VerifyAIRequestError extends Error {
109
113
  constructor(message, status, body) {
110
114
  super(message);
111
115
  this.name = 'VerifyAIRequestError';
@@ -122,3 +126,4 @@ export class VerifyAIRequestError extends Error {
122
126
  return this.body.upgrade_url;
123
127
  }
124
128
  }
129
+ exports.VerifyAIRequestError = VerifyAIRequestError;
@@ -1,7 +1,10 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useRef, useState, useCallback } from 'react';
3
- import { View, Text, TouchableOpacity, StyleSheet, ActivityIndicator, } from 'react-native';
4
- import { CameraView, useCameraPermissions, } from 'expo-camera';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.VerifyAIScanner = VerifyAIScanner;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const react_native_1 = require("react-native");
7
+ const expo_camera_1 = require("expo-camera");
5
8
  /**
6
9
  * Camera scanner component for capturing verification photos.
7
10
  * Uses expo-camera for the camera view and provides a simple capture UI.
@@ -23,12 +26,12 @@ import { CameraView, useCameraPermissions, } from 'expo-camera';
23
26
  * />
24
27
  * ```
25
28
  */
26
- export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style, showCaptureButton = true, captureRef, }) {
27
- const cameraRef = useRef(null);
28
- const [status, setStatus] = useState('idle');
29
- const [result, setResult] = useState(null);
30
- const [permission, requestPermission] = useCameraPermissions();
31
- const handleCapture = useCallback(async () => {
29
+ function VerifyAIScanner({ onCapture, onResult, onError, overlay, style, showCaptureButton = true, captureRef, }) {
30
+ const cameraRef = (0, react_1.useRef)(null);
31
+ const [status, setStatus] = (0, react_1.useState)('idle');
32
+ const [result, setResult] = (0, react_1.useState)(null);
33
+ const [permission, requestPermission] = (0, expo_camera_1.useCameraPermissions)();
34
+ const handleCapture = (0, react_1.useCallback)(async () => {
32
35
  if (!cameraRef.current || status === 'capturing' || status === 'processing')
33
36
  return;
34
37
  setStatus('capturing');
@@ -48,6 +51,7 @@ export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style,
48
51
  setResult(verificationResult);
49
52
  setStatus('success');
50
53
  onResult?.(verificationResult);
54
+ setTimeout(() => setStatus('idle'), 3000);
51
55
  }
52
56
  else {
53
57
  // null result means queued for offline
@@ -67,25 +71,25 @@ export function VerifyAIScanner({ onCapture, onResult, onError, overlay, style,
67
71
  captureRef.current = handleCapture;
68
72
  }
69
73
  if (!permission) {
70
- return _jsx(View, { style: [styles.container, style] });
74
+ return (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style] });
71
75
  }
72
76
  if (!permission.granted) {
73
- return (_jsxs(View, { style: [styles.container, styles.permissionContainer, style], children: [_jsx(Text, { style: styles.permissionText, children: "Camera access is required for photo verification" }), _jsx(TouchableOpacity, { style: styles.permissionButton, onPress: requestPermission, children: _jsx(Text, { style: styles.permissionButtonText, children: "Grant Camera Access" }) })] }));
77
+ return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [styles.container, styles.permissionContainer, style], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.permissionText, children: "Camera access is required for photo verification" }), (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: styles.permissionButton, onPress: requestPermission, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.permissionButtonText, children: "Grant Camera Access" }) })] }));
74
78
  }
75
- return (_jsx(View, { style: [styles.container, style], children: _jsxs(CameraView, { ref: cameraRef, style: styles.camera, facing: "back", children: [_jsxs(View, { style: styles.overlay, children: [overlay?.title && (_jsx(View, { style: styles.topBar, children: _jsx(Text, { style: styles.titleText, children: overlay.title }) })), overlay?.showGuideFrame && (_jsx(View, { style: styles.guideContainer, children: _jsx(View, { style: [
79
+ return ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style], children: (0, jsx_runtime_1.jsxs)(expo_camera_1.CameraView, { ref: cameraRef, style: styles.camera, facing: "back", children: [(0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.overlay, children: [overlay?.title && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.topBar, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.titleText, children: overlay.title }) })), overlay?.showGuideFrame && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.guideContainer, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
76
80
  styles.guideFrame,
77
81
  overlay.guideFrameAspectRatio
78
82
  ? { aspectRatio: overlay.guideFrameAspectRatio }
79
83
  : undefined,
80
- ] }) })), overlay?.instructions && status === 'idle' && (_jsx(View, { style: styles.instructionsContainer, children: _jsx(Text, { style: styles.instructionsText, children: overlay.instructions }) })), status === 'processing' && (_jsxs(View, { style: styles.statusOverlay, children: [_jsx(ActivityIndicator, { size: "large", color: "#fff" }), _jsx(Text, { style: styles.statusText, children: "Analyzing photo..." })] })), status === 'success' && result && (_jsxs(View, { style: styles.statusOverlay, children: [_jsx(View, { style: [
84
+ ] }) })), overlay?.instructions && status === 'idle' && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.instructionsContainer, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.instructionsText, children: overlay.instructions }) })), status === 'processing' && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.statusOverlay, children: [(0, jsx_runtime_1.jsx)(react_native_1.ActivityIndicator, { size: "large", color: "#fff" }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.statusText, children: "Analyzing photo..." })] })), status === 'success' && result && ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: styles.statusOverlay, children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
81
85
  styles.resultBadge,
82
86
  result.is_compliant ? styles.resultPass : styles.resultFail,
83
- ], children: _jsx(Text, { style: styles.resultText, children: result.is_compliant ? 'PASS' : 'FAIL' }) }), _jsx(Text, { style: styles.feedbackText, children: result.feedback })] })), status === 'error' && (_jsx(View, { style: styles.statusOverlay, children: _jsx(Text, { style: styles.errorText, children: "Verification failed. Try again." }) }))] }), showCaptureButton && (_jsx(View, { style: styles.bottomBar, children: _jsx(TouchableOpacity, { style: [
87
+ ], children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.resultText, children: result.is_compliant ? 'PASS' : 'FAIL' }) }), (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.feedbackText, children: result.feedback })] })), status === 'error' && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.statusOverlay, children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { style: styles.errorText, children: "Verification failed. Try again." }) }))] }), showCaptureButton && ((0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.bottomBar, children: (0, jsx_runtime_1.jsx)(react_native_1.TouchableOpacity, { style: [
84
88
  styles.captureButton,
85
89
  (status === 'capturing' || status === 'processing') && styles.captureButtonDisabled,
86
- ], onPress: handleCapture, disabled: status === 'capturing' || status === 'processing', activeOpacity: 0.7, children: _jsx(View, { style: styles.captureButtonInner }) }) }))] }) }));
90
+ ], onPress: handleCapture, disabled: status === 'capturing' || status === 'processing', activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(react_native_1.View, { style: styles.captureButtonInner }) }) }))] }) }));
87
91
  }
88
- const styles = StyleSheet.create({
92
+ const styles = react_native_1.StyleSheet.create({
89
93
  container: {
90
94
  flex: 1,
91
95
  backgroundColor: '#000',
@@ -94,7 +98,7 @@ const styles = StyleSheet.create({
94
98
  flex: 1,
95
99
  },
96
100
  overlay: {
97
- ...StyleSheet.absoluteFillObject,
101
+ ...react_native_1.StyleSheet.absoluteFillObject,
98
102
  justifyContent: 'space-between',
99
103
  },
100
104
  topBar: {
@@ -132,7 +136,7 @@ const styles = StyleSheet.create({
132
136
  textAlign: 'center',
133
137
  },
134
138
  statusOverlay: {
135
- ...StyleSheet.absoluteFillObject,
139
+ ...react_native_1.StyleSheet.absoluteFillObject,
136
140
  backgroundColor: 'rgba(0, 0, 0, 0.6)',
137
141
  justifyContent: 'center',
138
142
  alignItems: 'center',
@@ -1,5 +1,5 @@
1
1
  import { VerifyAIClient } from '../client';
2
- import { OfflineQueue } from '../storage/offlineQueue';
2
+ import type { OfflineQueue } from '../storage/offlineQueue';
3
3
  import type { VerifyAIConfig, VerificationRequest, VerificationResult, VerificationListParams, VerificationListResponse } from '../types';
4
4
  export interface UseVerifyAIReturn {
5
5
  /** Submit a verification. Uses offline queue if offlineMode is enabled and request fails. */
@@ -1,7 +1,19 @@
1
- import { useMemo, useCallback, useState, useEffect } from 'react';
2
- import { AppState } from 'react-native';
3
- import { VerifyAIClient } from '../client';
4
- import { OfflineQueue } from '../storage/offlineQueue';
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useVerifyAI = useVerifyAI;
4
+ const react_1 = require("react");
5
+ const react_native_1 = require("react-native");
6
+ const client_1 = require("../client");
7
+ // Lazy-load OfflineQueue to avoid hard dependency on @react-native-async-storage/async-storage.
8
+ // Consumers who don't use offlineMode won't need it installed.
9
+ let OfflineQueueClass = null;
10
+ try {
11
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
12
+ OfflineQueueClass = require('../storage/offlineQueue').OfflineQueue;
13
+ }
14
+ catch {
15
+ // AsyncStorage not installed — offlineMode will be unavailable
16
+ }
5
17
  /**
6
18
  * React hook for Verify AI. Provides verification methods,
7
19
  * loading/error state, and optional offline queue management.
@@ -24,22 +36,22 @@ import { OfflineQueue } from '../storage/offlineQueue';
24
36
  * };
25
37
  * ```
26
38
  */
27
- export function useVerifyAI(config) {
28
- const client = useMemo(() => new VerifyAIClient(config), [config.apiKey, config.baseUrl, config.timeout]);
29
- const offlineQueue = useMemo(() => (config.offlineMode ? new OfflineQueue(client) : null), [client, config.offlineMode]);
30
- const [loading, setLoading] = useState(false);
31
- const [error, setError] = useState(null);
32
- const [lastResult, setLastResult] = useState(null);
33
- const [queueSize, setQueueSize] = useState(0);
39
+ function useVerifyAI(config) {
40
+ const client = (0, react_1.useMemo)(() => new client_1.VerifyAIClient(config), [config.apiKey, config.baseUrl, config.timeout]);
41
+ const offlineQueue = (0, react_1.useMemo)(() => (config.offlineMode && OfflineQueueClass ? new OfflineQueueClass(client) : null), [client, config.offlineMode]);
42
+ const [loading, setLoading] = (0, react_1.useState)(false);
43
+ const [error, setError] = (0, react_1.useState)(null);
44
+ const [lastResult, setLastResult] = (0, react_1.useState)(null);
45
+ const [queueSize, setQueueSize] = (0, react_1.useState)(0);
34
46
  // Refresh queue size
35
- const refreshQueueSize = useCallback(async () => {
47
+ const refreshQueueSize = (0, react_1.useCallback)(async () => {
36
48
  if (offlineQueue) {
37
49
  const size = await offlineQueue.getQueueSize();
38
50
  setQueueSize(size);
39
51
  }
40
52
  }, [offlineQueue]);
41
53
  // Process queue on app foreground
42
- useEffect(() => {
54
+ (0, react_1.useEffect)(() => {
43
55
  if (!offlineQueue)
44
56
  return;
45
57
  refreshQueueSize();
@@ -48,10 +60,10 @@ export function useVerifyAI(config) {
48
60
  offlineQueue.processQueue().then(() => refreshQueueSize());
49
61
  }
50
62
  };
51
- const subscription = AppState.addEventListener('change', handleAppState);
63
+ const subscription = react_native_1.AppState.addEventListener('change', handleAppState);
52
64
  return () => subscription.remove();
53
65
  }, [offlineQueue, refreshQueueSize]);
54
- const verify = useCallback(async (request) => {
66
+ const verify = (0, react_1.useCallback)(async (request) => {
55
67
  setLoading(true);
56
68
  setError(null);
57
69
  try {
@@ -74,9 +86,9 @@ export function useVerifyAI(config) {
74
86
  setLoading(false);
75
87
  }
76
88
  }, [client, offlineQueue, refreshQueueSize]);
77
- const listVerifications = useCallback((params) => client.listVerifications(params), [client]);
78
- const getVerification = useCallback((id) => client.getVerification(id), [client]);
79
- const processQueue = useCallback(async () => {
89
+ const listVerifications = (0, react_1.useCallback)((params) => client.listVerifications(params), [client]);
90
+ const getVerification = (0, react_1.useCallback)((id) => client.getVerification(id), [client]);
91
+ const processQueue = (0, react_1.useCallback)(async () => {
80
92
  if (!offlineQueue)
81
93
  return;
82
94
  await offlineQueue.processQueue((_, result) => setLastResult(result));
package/lib/index.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export { VerifyAIClient, VerifyAIRequestError } from './client';
2
2
  export { useVerifyAI } from './hooks/useVerifyAI';
3
3
  export type { UseVerifyAIReturn } from './hooks/useVerifyAI';
4
- export { VerifyAIScanner } from './components/VerifyAIScanner';
5
4
  export type { VerifyAIScannerProps } from './components/VerifyAIScanner';
6
- export { OfflineQueue } from './storage/offlineQueue';
5
+ export type { OfflineQueue } from './storage/offlineQueue';
7
6
  export type { VerifyAIConfig, VerificationRequest, VerificationResult, VerificationListResponse, VerificationListParams, QueueItem, VerifyAIError, ScannerStatus, ScannerOverlayConfig, } from './types';
package/lib/index.js CHANGED
@@ -1,8 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useVerifyAI = exports.VerifyAIRequestError = exports.VerifyAIClient = void 0;
1
4
  // Client
2
- export { VerifyAIClient, VerifyAIRequestError } from './client';
5
+ var client_1 = require("./client");
6
+ Object.defineProperty(exports, "VerifyAIClient", { enumerable: true, get: function () { return client_1.VerifyAIClient; } });
7
+ Object.defineProperty(exports, "VerifyAIRequestError", { enumerable: true, get: function () { return client_1.VerifyAIRequestError; } });
3
8
  // Hooks
4
- export { useVerifyAI } from './hooks/useVerifyAI';
5
- // Components
6
- export { VerifyAIScanner } from './components/VerifyAIScanner';
7
- // Offline Queue
8
- export { OfflineQueue } from './storage/offlineQueue';
9
+ var useVerifyAI_1 = require("./hooks/useVerifyAI");
10
+ Object.defineProperty(exports, "useVerifyAI", { enumerable: true, get: function () { return useVerifyAI_1.useVerifyAI; } });
@@ -1,8 +1,14 @@
1
- import AsyncStorage from '@react-native-async-storage/async-storage';
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OfflineQueue = void 0;
7
+ const async_storage_1 = __importDefault(require("@react-native-async-storage/async-storage"));
2
8
  const MANIFEST_KEY = '@verifyai/queue_manifest';
3
9
  const ITEM_PREFIX = '@verifyai/queue_item_';
4
10
  const LEGACY_KEY = '@verifyai/offline_queue';
5
- export class OfflineQueue {
11
+ class OfflineQueue {
6
12
  constructor(client) {
7
13
  this.processing = false;
8
14
  this.migrated = false;
@@ -16,13 +22,13 @@ export class OfflineQueue {
16
22
  if (this.migrated)
17
23
  return;
18
24
  this.migrated = true;
19
- const legacy = await AsyncStorage.getItem(LEGACY_KEY);
25
+ const legacy = await async_storage_1.default.getItem(LEGACY_KEY);
20
26
  if (!legacy)
21
27
  return;
22
28
  try {
23
29
  const items = JSON.parse(legacy);
24
30
  if (!Array.isArray(items) || items.length === 0) {
25
- await AsyncStorage.removeItem(LEGACY_KEY);
31
+ await async_storage_1.default.removeItem(LEGACY_KEY);
26
32
  return;
27
33
  }
28
34
  const ids = [];
@@ -31,24 +37,24 @@ export class OfflineQueue {
31
37
  ids.push(item.id);
32
38
  pairs.push([`${ITEM_PREFIX}${item.id}`, JSON.stringify(item)]);
33
39
  }
34
- await AsyncStorage.multiSet([
40
+ await async_storage_1.default.multiSet([
35
41
  [MANIFEST_KEY, JSON.stringify(ids)],
36
42
  ...pairs,
37
43
  ]);
38
- await AsyncStorage.removeItem(LEGACY_KEY);
44
+ await async_storage_1.default.removeItem(LEGACY_KEY);
39
45
  }
40
46
  catch {
41
47
  // If migration fails, remove corrupt legacy data
42
- await AsyncStorage.removeItem(LEGACY_KEY);
48
+ await async_storage_1.default.removeItem(LEGACY_KEY);
43
49
  }
44
50
  }
45
51
  async getManifest() {
46
52
  await this.migrateIfNeeded();
47
- const raw = await AsyncStorage.getItem(MANIFEST_KEY);
53
+ const raw = await async_storage_1.default.getItem(MANIFEST_KEY);
48
54
  return raw ? JSON.parse(raw) : [];
49
55
  }
50
56
  async setManifest(ids) {
51
- await AsyncStorage.setItem(MANIFEST_KEY, JSON.stringify(ids));
57
+ await async_storage_1.default.setItem(MANIFEST_KEY, JSON.stringify(ids));
52
58
  }
53
59
  /**
54
60
  * Add a verification request to the offline queue.
@@ -63,7 +69,7 @@ export class OfflineQueue {
63
69
  };
64
70
  const ids = await this.getManifest();
65
71
  ids.push(item.id);
66
- await AsyncStorage.setItem(`${ITEM_PREFIX}${item.id}`, JSON.stringify(item));
72
+ await async_storage_1.default.setItem(`${ITEM_PREFIX}${item.id}`, JSON.stringify(item));
67
73
  await this.setManifest(ids);
68
74
  return item.id;
69
75
  }
@@ -75,7 +81,7 @@ export class OfflineQueue {
75
81
  if (ids.length === 0)
76
82
  return [];
77
83
  const keys = ids.map((id) => `${ITEM_PREFIX}${id}`);
78
- const pairs = await AsyncStorage.multiGet(keys);
84
+ const pairs = await async_storage_1.default.multiGet(keys);
79
85
  const items = [];
80
86
  for (const [, value] of pairs) {
81
87
  if (value) {
@@ -103,7 +109,7 @@ export class OfflineQueue {
103
109
  const ids = await this.getManifest();
104
110
  const filtered = ids.filter((i) => i !== id);
105
111
  await this.setManifest(filtered);
106
- await AsyncStorage.removeItem(`${ITEM_PREFIX}${id}`);
112
+ await async_storage_1.default.removeItem(`${ITEM_PREFIX}${id}`);
107
113
  }
108
114
  /**
109
115
  * Clear all items from the queue.
@@ -112,10 +118,10 @@ export class OfflineQueue {
112
118
  const ids = await this.getManifest();
113
119
  if (ids.length > 0) {
114
120
  const keys = ids.map((id) => `${ITEM_PREFIX}${id}`);
115
- await AsyncStorage.multiRemove([MANIFEST_KEY, ...keys]);
121
+ await async_storage_1.default.multiRemove([MANIFEST_KEY, ...keys]);
116
122
  }
117
123
  else {
118
- await AsyncStorage.removeItem(MANIFEST_KEY);
124
+ await async_storage_1.default.removeItem(MANIFEST_KEY);
119
125
  }
120
126
  }
121
127
  /**
@@ -139,7 +145,7 @@ export class OfflineQueue {
139
145
  try {
140
146
  const ids = await this.getManifest();
141
147
  for (const id of ids) {
142
- const raw = await AsyncStorage.getItem(`${ITEM_PREFIX}${id}`);
148
+ const raw = await async_storage_1.default.getItem(`${ITEM_PREFIX}${id}`);
143
149
  if (!raw)
144
150
  continue;
145
151
  let item;
@@ -148,24 +154,24 @@ export class OfflineQueue {
148
154
  }
149
155
  catch {
150
156
  // Remove corrupt item
151
- await AsyncStorage.removeItem(`${ITEM_PREFIX}${id}`);
157
+ await async_storage_1.default.removeItem(`${ITEM_PREFIX}${id}`);
152
158
  continue;
153
159
  }
154
160
  try {
155
161
  const result = await this.client.verify(item.request);
156
162
  processed++;
157
- await AsyncStorage.removeItem(`${ITEM_PREFIX}${id}`);
163
+ await async_storage_1.default.removeItem(`${ITEM_PREFIX}${id}`);
158
164
  onResult?.(item.id, result);
159
165
  }
160
166
  catch {
161
167
  item.retryCount++;
162
168
  if (item.retryCount < maxRetries) {
163
- await AsyncStorage.setItem(`${ITEM_PREFIX}${id}`, JSON.stringify(item));
169
+ await async_storage_1.default.setItem(`${ITEM_PREFIX}${id}`, JSON.stringify(item));
164
170
  remainingIds.push(id);
165
171
  }
166
172
  else {
167
173
  failed++;
168
- await AsyncStorage.removeItem(`${ITEM_PREFIX}${id}`);
174
+ await async_storage_1.default.removeItem(`${ITEM_PREFIX}${id}`);
169
175
  }
170
176
  }
171
177
  }
@@ -177,3 +183,4 @@ export class OfflineQueue {
177
183
  }
178
184
  }
179
185
  }
186
+ exports.OfflineQueue = OfflineQueue;
@@ -1 +1,2 @@
1
- export {};
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,12 +1,34 @@
1
1
  {
2
2
  "name": "@switchlabs/verify-ai-react-native",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "React Native SDK for Verify AI - photo verification with AI vision processing",
5
5
  "main": "./lib/index.js",
6
- "module": "./lib/index.js",
7
6
  "types": "./lib/index.d.ts",
8
7
  "source": "./src/index.ts",
9
8
  "react-native": "./src/index.ts",
9
+ "exports": {
10
+ ".": {
11
+ "react-native": "./src/index.ts",
12
+ "types": "./lib/index.d.ts",
13
+ "default": "./lib/index.js"
14
+ },
15
+ "./scanner": {
16
+ "react-native": "./src/components/VerifyAIScanner.tsx",
17
+ "types": "./lib/components/VerifyAIScanner.d.ts",
18
+ "default": "./lib/components/VerifyAIScanner.js"
19
+ },
20
+ "./offline": {
21
+ "react-native": "./src/storage/offlineQueue.ts",
22
+ "types": "./lib/storage/offlineQueue.d.ts",
23
+ "default": "./lib/storage/offlineQueue.js"
24
+ }
25
+ },
26
+ "typesVersions": {
27
+ "*": {
28
+ "scanner": ["./lib/components/VerifyAIScanner.d.ts"],
29
+ "offline": ["./lib/storage/offlineQueue.d.ts"]
30
+ }
31
+ },
10
32
  "files": [
11
33
  "src",
12
34
  "lib"
@@ -94,6 +94,7 @@ export function VerifyAIScanner({
94
94
  setResult(verificationResult);
95
95
  setStatus('success');
96
96
  onResult?.(verificationResult);
97
+ setTimeout(() => setStatus('idle'), 3000);
97
98
  } else {
98
99
  // null result means queued for offline
99
100
  setStatus('idle');
@@ -1,7 +1,7 @@
1
1
  import { useRef, useMemo, useCallback, useState, useEffect } from 'react';
2
2
  import { AppState, type AppStateStatus } from 'react-native';
3
3
  import { VerifyAIClient } from '../client';
4
- import { OfflineQueue } from '../storage/offlineQueue';
4
+ import type { OfflineQueue } from '../storage/offlineQueue';
5
5
  import type {
6
6
  VerifyAIConfig,
7
7
  VerificationRequest,
@@ -10,6 +10,16 @@ import type {
10
10
  VerificationListResponse,
11
11
  } from '../types';
12
12
 
13
+ // Lazy-load OfflineQueue to avoid hard dependency on @react-native-async-storage/async-storage.
14
+ // Consumers who don't use offlineMode won't need it installed.
15
+ let OfflineQueueClass: (new (client: VerifyAIClient) => OfflineQueue) | null = null;
16
+ try {
17
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
18
+ OfflineQueueClass = require('../storage/offlineQueue').OfflineQueue;
19
+ } catch {
20
+ // AsyncStorage not installed — offlineMode will be unavailable
21
+ }
22
+
13
23
  export interface UseVerifyAIReturn {
14
24
  /** Submit a verification. Uses offline queue if offlineMode is enabled and request fails. */
15
25
  verify: (request: VerificationRequest) => Promise<VerificationResult | null>;
@@ -58,7 +68,7 @@ export interface UseVerifyAIReturn {
58
68
  export function useVerifyAI(config: VerifyAIConfig): UseVerifyAIReturn {
59
69
  const client = useMemo(() => new VerifyAIClient(config), [config.apiKey, config.baseUrl, config.timeout]);
60
70
  const offlineQueue = useMemo(
61
- () => (config.offlineMode ? new OfflineQueue(client) : null),
71
+ () => (config.offlineMode && OfflineQueueClass ? new OfflineQueueClass(client) : null),
62
72
  [client, config.offlineMode]
63
73
  );
64
74
 
package/src/index.ts CHANGED
@@ -5,12 +5,11 @@ export { VerifyAIClient, VerifyAIRequestError } from './client';
5
5
  export { useVerifyAI } from './hooks/useVerifyAI';
6
6
  export type { UseVerifyAIReturn } from './hooks/useVerifyAI';
7
7
 
8
- // Components
9
- export { VerifyAIScanner } from './components/VerifyAIScanner';
8
+ // Components — import { VerifyAIScanner } from '@switchlabs/verify-ai-react-native/scanner'
10
9
  export type { VerifyAIScannerProps } from './components/VerifyAIScanner';
11
10
 
12
- // Offline Queue
13
- export { OfflineQueue } from './storage/offlineQueue';
11
+ // Offline Queue — import { OfflineQueue } from '@switchlabs/verify-ai-react-native/offline'
12
+ export type { OfflineQueue } from './storage/offlineQueue';
14
13
 
15
14
  // Types
16
15
  export type {