@switchlabs/verify-ai-react-native 2.5.6 → 2.5.7

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/README.md CHANGED
@@ -1,6 +1,32 @@
1
1
  # @switchlabs/verify-ai-react-native
2
2
 
3
- React Native SDK for Verify AI photo verification.
3
+ Send a photo and a policy, get back a pass/fail verdict with a confidence
4
+ value, the criteria that failed, and human-readable reasoning. Built for
5
+ end-of-ride parking compliance, vehicle and equipment condition and damage
6
+ grading, proof of delivery, and rental return checks.
7
+
8
+ The SDK wraps live camera capture (no gallery-picker path in the scanner
9
+ flow), on-device pre-screening, an offline queue, and the REST API.
10
+
11
+ ```tsx
12
+ const result = await verify({ image: base64, policy: 'scooter_parking' });
13
+ if (result && !result.is_compliant) {
14
+ console.log(result.violation_reasons, result.feedback);
15
+ }
16
+ ```
17
+
18
+ **Try it before you install anything:** upload a photo at
19
+ [verify.switchlabs.dev/demo](https://verify.switchlabs.dev/demo). No account,
20
+ no card.
21
+
22
+ **Get a key:** [free sandbox](https://verify.switchlabs.dev/login?mode=signup)
23
+ with $5 of credit and no credit card. Keys start with `vai_` and are sent in an
24
+ `X-API-Key` header.
25
+
26
+ **Pricing:** $0.008 per standard verification for the first 100,000 in a
27
+ calendar month, $0.006 from 100,001 to 1,000,000, and $0.005 above 1,000,000.
28
+ No platform fee, no monthly minimum, no annual contract.
29
+ [Full pricing](https://verify.switchlabs.dev/pricing).
4
30
 
5
31
  ## Install
6
32
 
@@ -2,6 +2,13 @@ import { TelemetryReporter } from '../telemetry/TelemetryReporter';
2
2
  import { SDK_VERSION } from '../version';
3
3
  const DEFAULT_BASE_URL = 'https://verify.switchlabs.dev/api/v1';
4
4
  const DEFAULT_TIMEOUT = 60000;
5
+ const MAX_IN_PROGRESS_RETRIES = 10;
6
+ function retryAfterMs(value) {
7
+ if (value !== null && /^\d+$/.test(value.trim()))
8
+ return Number(value) * 1000;
9
+ const date = value ? Date.parse(value) : NaN;
10
+ return Number.isFinite(date) ? Math.max(0, date - Date.now()) : 1000;
11
+ }
5
12
  const VEHICLE_TYPE_LABELS = {
6
13
  scooter: 'Scooter',
7
14
  'e-bike': 'E-Bike',
@@ -150,17 +157,34 @@ export class VerifyAIClient {
150
157
  const method = (options.method || 'GET').toUpperCase();
151
158
  const url = `${this.baseUrl}${path}`;
152
159
  const context = { path, url, method };
160
+ const deadline = Date.now() + this.timeout;
153
161
  try {
154
- const response = await fetch(url, {
155
- ...options,
156
- signal: controller.signal,
157
- headers: {
158
- 'X-API-Key': this.apiKey,
159
- ...options.headers,
160
- },
161
- });
162
- const rawBody = await response.text();
163
- const body = this.parseResponseBody(rawBody);
162
+ let response;
163
+ let body;
164
+ let retries = 0;
165
+ while (true) {
166
+ response = await fetch(url, {
167
+ ...options,
168
+ signal: controller.signal,
169
+ headers: {
170
+ 'X-API-Key': this.apiKey,
171
+ ...options.headers,
172
+ },
173
+ });
174
+ const rawBody = await response.text();
175
+ body = this.parseResponseBody(rawBody);
176
+ const code = body && typeof body === 'object' ? body.code : undefined;
177
+ const key = new Headers(options.headers).get('Idempotency-Key');
178
+ const delay = retryAfterMs(response.headers.get('Retry-After'));
179
+ // Only replay an identical, explicitly keyed verification reservation.
180
+ // Keep one timeout budget and report errors only after retries exhaust.
181
+ if (path !== '/verify' || method !== 'POST' || !key || response.status !== 409 ||
182
+ code !== 'idempotency_in_progress' || retries >= MAX_IN_PROGRESS_RETRIES ||
183
+ delay >= deadline - Date.now() || controller.signal.aborted)
184
+ break;
185
+ retries += 1;
186
+ await new Promise(resolve => setTimeout(resolve, delay));
187
+ }
164
188
  if (!response.ok) {
165
189
  const errorBody = (body && typeof body === 'object' ? body : null);
166
190
  throw this.buildRequestError(stringifyErrorMessage(errorBody?.error, `Request failed with status ${response.status}`), response.status, context, {
package/lib/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const SDK_VERSION = "2.5.6";
1
+ export declare const SDK_VERSION = "2.5.7";
package/lib/version.js CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '2.5.6';
1
+ export const SDK_VERSION = '2.5.7';
package/package.json CHANGED
@@ -1,11 +1,10 @@
1
1
  {
2
2
  "name": "@switchlabs/verify-ai-react-native",
3
- "version": "2.5.6",
3
+ "version": "2.5.7",
4
4
  "description": "React Native SDK for Verify AI - photo verification with AI vision processing",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/levyelectric/verify.git",
8
- "directory": "packages/verify-ai-react-native"
5
+ "homepage": "https://verify.switchlabs.dev/docs/sdks/react-native",
6
+ "bugs": {
7
+ "url": "https://verify.switchlabs.dev/contact"
9
8
  },
10
9
  "main": "./lib/index.js",
11
10
  "types": "./lib/index.d.ts",
@@ -35,7 +34,12 @@
35
34
  "photo-verification",
36
35
  "ai-vision",
37
36
  "parking-compliance",
38
- "damage-detection"
37
+ "damage-detection",
38
+ "proof-of-delivery",
39
+ "image-verification",
40
+ "vehicle-inspection",
41
+ "expo",
42
+ "photo-verification-api"
39
43
  ],
40
44
  "author": "Switch Labs",
41
45
  "license": "MIT",
@@ -86,5 +90,10 @@
86
90
  "react-native-fast-tflite": "^1.6.1",
87
91
  "typescript": "^5.5.0",
88
92
  "vitest": "^4.1.0"
93
+ },
94
+ "repository": {
95
+ "type": "git",
96
+ "url": "git+https://github.com/levyelectric/verify.git",
97
+ "directory": "packages/verify-ai-react-native"
89
98
  }
90
99
  }
@@ -14,6 +14,13 @@ import { SDK_VERSION } from '../version';
14
14
 
15
15
  const DEFAULT_BASE_URL = 'https://verify.switchlabs.dev/api/v1';
16
16
  const DEFAULT_TIMEOUT = 60000;
17
+ const MAX_IN_PROGRESS_RETRIES = 10;
18
+
19
+ function retryAfterMs(value: string | null): number {
20
+ if (value !== null && /^\d+$/.test(value.trim())) return Number(value) * 1000;
21
+ const date = value ? Date.parse(value) : NaN;
22
+ return Number.isFinite(date) ? Math.max(0, date - Date.now()) : 1000;
23
+ }
17
24
 
18
25
  const VEHICLE_TYPE_LABELS: Record<string, string> = {
19
26
  scooter: 'Scooter',
@@ -214,19 +221,35 @@ export class VerifyAIClient {
214
221
  const method = (options.method || 'GET').toUpperCase();
215
222
  const url = `${this.baseUrl}${path}`;
216
223
  const context: RequestContext = { path, url, method };
224
+ const deadline = Date.now() + this.timeout;
217
225
 
218
226
  try {
219
- const response = await fetch(url, {
220
- ...options,
221
- signal: controller.signal,
222
- headers: {
223
- 'X-API-Key': this.apiKey,
224
- ...options.headers,
225
- },
226
- });
227
-
228
- const rawBody = await response.text();
229
- const body = this.parseResponseBody(rawBody);
227
+ let response: Response;
228
+ let body: unknown;
229
+ let retries = 0;
230
+ while (true) {
231
+ response = await fetch(url, {
232
+ ...options,
233
+ signal: controller.signal,
234
+ headers: {
235
+ 'X-API-Key': this.apiKey,
236
+ ...options.headers,
237
+ },
238
+ });
239
+
240
+ const rawBody = await response.text();
241
+ body = this.parseResponseBody(rawBody);
242
+ const code = body && typeof body === 'object' ? (body as VerifyAIError).code : undefined;
243
+ const key = new Headers(options.headers).get('Idempotency-Key');
244
+ const delay = retryAfterMs(response.headers.get('Retry-After'));
245
+ // Only replay an identical, explicitly keyed verification reservation.
246
+ // Keep one timeout budget and report errors only after retries exhaust.
247
+ if (path !== '/verify' || method !== 'POST' || !key || response.status !== 409 ||
248
+ code !== 'idempotency_in_progress' || retries >= MAX_IN_PROGRESS_RETRIES ||
249
+ delay >= deadline - Date.now() || controller.signal.aborted) break;
250
+ retries += 1;
251
+ await new Promise(resolve => setTimeout(resolve, delay));
252
+ }
230
253
 
231
254
  if (!response.ok) {
232
255
  const errorBody = (body && typeof body === 'object' ? body : null) as VerifyAIError | null;
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const SDK_VERSION = '2.5.6';
1
+ export const SDK_VERSION = '2.5.7';