@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.
- package/lib/client/index.js +7 -2
- package/lib/components/VerifyAIScanner.js +23 -19
- package/lib/hooks/useVerifyAI.d.ts +1 -1
- package/lib/hooks/useVerifyAI.js +30 -18
- package/lib/index.d.ts +1 -2
- package/lib/index.js +8 -6
- package/lib/storage/offlineQueue.js +26 -19
- package/lib/types/index.js +2 -1
- package/package.json +24 -2
- package/src/components/VerifyAIScanner.tsx +1 -0
- package/src/hooks/useVerifyAI.ts +12 -2
- package/src/index.ts +3 -4
package/lib/client/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
|
74
|
+
return (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [styles.container, style] });
|
|
71
75
|
}
|
|
72
76
|
if (!permission.granted) {
|
|
73
|
-
return (
|
|
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 (
|
|
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' && (
|
|
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:
|
|
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:
|
|
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. */
|
package/lib/hooks/useVerifyAI.js
CHANGED
|
@@ -1,7 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
28
|
-
const client = useMemo(() => new VerifyAIClient(config), [config.apiKey, config.baseUrl, config.timeout]);
|
|
29
|
-
const offlineQueue = useMemo(() => (config.offlineMode ? new
|
|
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
|
-
|
|
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
|
-
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
40
|
+
await async_storage_1.default.multiSet([
|
|
35
41
|
[MANIFEST_KEY, JSON.stringify(ids)],
|
|
36
42
|
...pairs,
|
|
37
43
|
]);
|
|
38
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
121
|
+
await async_storage_1.default.multiRemove([MANIFEST_KEY, ...keys]);
|
|
116
122
|
}
|
|
117
123
|
else {
|
|
118
|
-
await
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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;
|
package/lib/types/index.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
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.
|
|
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"
|
package/src/hooks/useVerifyAI.ts
CHANGED
|
@@ -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
|
|
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 {
|