@switchlabs/verify-ai-react-native 2.4.20 → 2.4.22
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.d.ts +1 -1
- package/lib/client/index.js +52 -12
- package/lib/hooks/useVerifyAI.js +2 -1
- package/lib/ml/modelManager.js +2 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +1 -1
- package/src/client/index.ts +68 -20
- package/src/hooks/useVerifyAI.ts +2 -1
- package/src/ml/modelManager.ts +2 -1
- package/src/version.ts +1 -1
package/lib/client/index.d.ts
CHANGED
|
@@ -93,7 +93,7 @@ export declare class VerifyAIClient {
|
|
|
93
93
|
export declare class VerifyAIRequestError extends Error {
|
|
94
94
|
status: number;
|
|
95
95
|
body: VerifyAIError;
|
|
96
|
-
constructor(message:
|
|
96
|
+
constructor(message: unknown, status: number, body: VerifyAIError);
|
|
97
97
|
get isRateLimited(): boolean;
|
|
98
98
|
get isUnauthorized(): boolean;
|
|
99
99
|
get isServerError(): boolean;
|
package/lib/client/index.js
CHANGED
|
@@ -19,10 +19,43 @@ function enrichMetadata(metadata) {
|
|
|
19
19
|
}
|
|
20
20
|
return enriched;
|
|
21
21
|
}
|
|
22
|
+
function stringifyErrorMessage(value, fallback) {
|
|
23
|
+
if (typeof value === 'string' && value.trim()) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (value instanceof Error && value.message) {
|
|
27
|
+
return value.message;
|
|
28
|
+
}
|
|
29
|
+
if (value && typeof value === 'object') {
|
|
30
|
+
const record = value;
|
|
31
|
+
for (const key of ['message', 'error', 'detail', 'code']) {
|
|
32
|
+
const candidate = record[key];
|
|
33
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
34
|
+
return candidate;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const json = JSON.stringify(value);
|
|
39
|
+
if (json && json !== '{}') {
|
|
40
|
+
return json;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
// Fall through to String(value) below.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (value != null) {
|
|
48
|
+
const text = String(value);
|
|
49
|
+
if (text && text !== '[object Object]') {
|
|
50
|
+
return text;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return fallback;
|
|
54
|
+
}
|
|
22
55
|
export class VerifyAIClient {
|
|
23
56
|
constructor(config) {
|
|
24
57
|
if (!config.apiKey) {
|
|
25
|
-
throw new Error(
|
|
58
|
+
throw new Error(`VerifyAI[v${SDK_VERSION}]: apiKey is required`);
|
|
26
59
|
}
|
|
27
60
|
this.apiKey = config.apiKey;
|
|
28
61
|
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
@@ -43,8 +76,9 @@ export class VerifyAIClient {
|
|
|
43
76
|
}
|
|
44
77
|
}
|
|
45
78
|
buildRequestError(message, status, context, body = {}) {
|
|
79
|
+
const safeMessage = stringifyErrorMessage(message, 'VerifyAI request failed');
|
|
46
80
|
const error = {
|
|
47
|
-
error:
|
|
81
|
+
error: safeMessage,
|
|
48
82
|
status,
|
|
49
83
|
code: status === 408 ? 'timeout' : status === 0 ? 'network_error' : 'request_error',
|
|
50
84
|
path: context.path,
|
|
@@ -52,7 +86,7 @@ export class VerifyAIClient {
|
|
|
52
86
|
method: context.method,
|
|
53
87
|
...body,
|
|
54
88
|
};
|
|
55
|
-
return new VerifyAIRequestError(
|
|
89
|
+
return new VerifyAIRequestError(safeMessage, status, error);
|
|
56
90
|
}
|
|
57
91
|
normalizeRequestError(error, context) {
|
|
58
92
|
if (error instanceof VerifyAIRequestError) {
|
|
@@ -64,7 +98,7 @@ export class VerifyAIClient {
|
|
|
64
98
|
if (error instanceof TypeError) {
|
|
65
99
|
return this.buildRequestError('Network request failed', 0, context, { code: 'network_error' });
|
|
66
100
|
}
|
|
67
|
-
const message = error instanceof Error ? error.message : 'VerifyAI request failed';
|
|
101
|
+
const message = error instanceof Error ? error.message : stringifyErrorMessage(error, 'VerifyAI request failed');
|
|
68
102
|
return this.buildRequestError(message, 0, context);
|
|
69
103
|
}
|
|
70
104
|
async executeRequest(path, options = {}) {
|
|
@@ -86,7 +120,7 @@ export class VerifyAIClient {
|
|
|
86
120
|
const body = this.parseResponseBody(rawBody);
|
|
87
121
|
if (!response.ok) {
|
|
88
122
|
const errorBody = (body && typeof body === 'object' ? body : null);
|
|
89
|
-
throw this.buildRequestError(errorBody?.error
|
|
123
|
+
throw this.buildRequestError(stringifyErrorMessage(errorBody?.error, `Request failed with status ${response.status}`), response.status, context, {
|
|
90
124
|
current_usage: errorBody?.current_usage,
|
|
91
125
|
limit: errorBody?.limit,
|
|
92
126
|
upgrade_url: errorBody?.upgrade_url,
|
|
@@ -95,7 +129,7 @@ export class VerifyAIClient {
|
|
|
95
129
|
});
|
|
96
130
|
}
|
|
97
131
|
if (!body || typeof body !== 'object') {
|
|
98
|
-
throw this.buildRequestError(
|
|
132
|
+
throw this.buildRequestError(`VerifyAI[v${SDK_VERSION}]: Invalid response payload`, 0, context, {
|
|
99
133
|
code: 'invalid_response',
|
|
100
134
|
request_id: response.headers.get('X-Request-Id') || undefined,
|
|
101
135
|
});
|
|
@@ -220,9 +254,11 @@ export class VerifyAIClient {
|
|
|
220
254
|
});
|
|
221
255
|
}
|
|
222
256
|
catch (error) {
|
|
223
|
-
//
|
|
224
|
-
// Vercel
|
|
225
|
-
//
|
|
257
|
+
// 5xx during multipart can mean the server crashed before reservation
|
|
258
|
+
// (e.g. Vercel multipart-parse failure) OR after the verification was
|
|
259
|
+
// saved (e.g. signed-URL generation throw). Retry as base64 JSON and
|
|
260
|
+
// forward the original Idempotency-Key so the server can dedupe instead
|
|
261
|
+
// of inserting a second verify_ai_verifications row.
|
|
226
262
|
if (error instanceof VerifyAIRequestError && error.status >= 500 && error.isServerError) {
|
|
227
263
|
try {
|
|
228
264
|
const { Buffer } = await import('buffer');
|
|
@@ -235,7 +271,7 @@ export class VerifyAIClient {
|
|
|
235
271
|
metadata: request.metadata,
|
|
236
272
|
provider: request.provider,
|
|
237
273
|
include_image_data: request.include_image_data,
|
|
238
|
-
});
|
|
274
|
+
}, options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : undefined);
|
|
239
275
|
}
|
|
240
276
|
catch {
|
|
241
277
|
// If the retry itself fails, throw the original error
|
|
@@ -316,10 +352,14 @@ export class VerifyAIClient {
|
|
|
316
352
|
}
|
|
317
353
|
export class VerifyAIRequestError extends Error {
|
|
318
354
|
constructor(message, status, body) {
|
|
319
|
-
|
|
355
|
+
const safeMessage = stringifyErrorMessage(message, 'VerifyAI request failed');
|
|
356
|
+
super(safeMessage);
|
|
320
357
|
this.name = 'VerifyAIRequestError';
|
|
321
358
|
this.status = status;
|
|
322
|
-
this.body =
|
|
359
|
+
this.body = {
|
|
360
|
+
...body,
|
|
361
|
+
error: stringifyErrorMessage(body.error, safeMessage),
|
|
362
|
+
};
|
|
323
363
|
}
|
|
324
364
|
get isRateLimited() {
|
|
325
365
|
return this.status === 429;
|
package/lib/hooks/useVerifyAI.js
CHANGED
|
@@ -3,6 +3,7 @@ import { AppState, Platform } from 'react-native';
|
|
|
3
3
|
import { VerifyAIClient, VerifyAIRequestError } from '../client';
|
|
4
4
|
import { OfflineQueue } from '../storage/offlineQueue';
|
|
5
5
|
import { TelemetryContext } from '../telemetry/TelemetryContext';
|
|
6
|
+
import { SDK_VERSION } from '../version';
|
|
6
7
|
function isQueueableError(error) {
|
|
7
8
|
if (error instanceof VerifyAIRequestError) {
|
|
8
9
|
return error.isRetryable;
|
|
@@ -86,7 +87,7 @@ export function useVerifyAI(config) {
|
|
|
86
87
|
inferenceEngineRef.current = new InferenceEngine();
|
|
87
88
|
}
|
|
88
89
|
catch (err) {
|
|
89
|
-
console.warn(
|
|
90
|
+
console.warn(`[VerifyAI v${SDK_VERSION}] Failed to load ML modules:`, err);
|
|
90
91
|
}
|
|
91
92
|
})();
|
|
92
93
|
return () => {
|
package/lib/ml/modelManager.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import { Buffer } from 'buffer';
|
|
6
6
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
7
|
+
import { SDK_VERSION } from '../version';
|
|
7
8
|
async function loadFileSystem() {
|
|
8
9
|
try {
|
|
9
10
|
return await import('expo-file-system');
|
|
@@ -68,7 +69,7 @@ export class ModelManager {
|
|
|
68
69
|
async downloadArtifacts(manifest, policyId) {
|
|
69
70
|
const fileSystem = await loadFileSystem();
|
|
70
71
|
if (!fileSystem || !fileSystem.documentDirectory) {
|
|
71
|
-
console.warn(
|
|
72
|
+
console.warn(`[VerifyAI v${SDK_VERSION}] expo-file-system not available, skipping download`);
|
|
72
73
|
return;
|
|
73
74
|
}
|
|
74
75
|
const cacheDir = `${fileSystem.documentDirectory}verify-ai/bundles/${policyId}/`;
|
package/lib/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.4.
|
|
1
|
+
export declare const SDK_VERSION = "2.4.22";
|
package/lib/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '2.4.
|
|
1
|
+
export const SDK_VERSION = '2.4.22';
|
package/package.json
CHANGED
package/src/client/index.ts
CHANGED
|
@@ -34,6 +34,44 @@ function enrichMetadata(metadata?: Record<string, unknown>): Record<string, unkn
|
|
|
34
34
|
return enriched;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function stringifyErrorMessage(value: unknown, fallback: string): string {
|
|
38
|
+
if (typeof value === 'string' && value.trim()) {
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (value instanceof Error && value.message) {
|
|
43
|
+
return value.message;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (value && typeof value === 'object') {
|
|
47
|
+
const record = value as Record<string, unknown>;
|
|
48
|
+
for (const key of ['message', 'error', 'detail', 'code']) {
|
|
49
|
+
const candidate = record[key];
|
|
50
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
51
|
+
return candidate;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
const json = JSON.stringify(value);
|
|
57
|
+
if (json && json !== '{}') {
|
|
58
|
+
return json;
|
|
59
|
+
}
|
|
60
|
+
} catch {
|
|
61
|
+
// Fall through to String(value) below.
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (value != null) {
|
|
66
|
+
const text = String(value);
|
|
67
|
+
if (text && text !== '[object Object]') {
|
|
68
|
+
return text;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return fallback;
|
|
73
|
+
}
|
|
74
|
+
|
|
37
75
|
interface RequestContext {
|
|
38
76
|
path: string;
|
|
39
77
|
url: string;
|
|
@@ -48,7 +86,7 @@ export class VerifyAIClient {
|
|
|
48
86
|
|
|
49
87
|
constructor(config: VerifyAIConfig) {
|
|
50
88
|
if (!config.apiKey) {
|
|
51
|
-
throw new Error(
|
|
89
|
+
throw new Error(`VerifyAI[v${SDK_VERSION}]: apiKey is required`);
|
|
52
90
|
}
|
|
53
91
|
this.apiKey = config.apiKey;
|
|
54
92
|
this.baseUrl = (config.baseUrl || DEFAULT_BASE_URL).replace(/\/$/, '');
|
|
@@ -71,13 +109,14 @@ export class VerifyAIClient {
|
|
|
71
109
|
}
|
|
72
110
|
|
|
73
111
|
private buildRequestError(
|
|
74
|
-
message:
|
|
112
|
+
message: unknown,
|
|
75
113
|
status: number,
|
|
76
114
|
context: RequestContext,
|
|
77
115
|
body: Partial<VerifyAIError> = {}
|
|
78
116
|
): VerifyAIRequestError {
|
|
117
|
+
const safeMessage = stringifyErrorMessage(message, 'VerifyAI request failed');
|
|
79
118
|
const error: VerifyAIError = {
|
|
80
|
-
error:
|
|
119
|
+
error: safeMessage,
|
|
81
120
|
status,
|
|
82
121
|
code: status === 408 ? 'timeout' : status === 0 ? 'network_error' : 'request_error',
|
|
83
122
|
path: context.path,
|
|
@@ -86,7 +125,7 @@ export class VerifyAIClient {
|
|
|
86
125
|
...body,
|
|
87
126
|
};
|
|
88
127
|
|
|
89
|
-
return new VerifyAIRequestError(
|
|
128
|
+
return new VerifyAIRequestError(safeMessage, status, error);
|
|
90
129
|
}
|
|
91
130
|
|
|
92
131
|
private normalizeRequestError(error: unknown, context: RequestContext): VerifyAIRequestError {
|
|
@@ -102,7 +141,7 @@ export class VerifyAIClient {
|
|
|
102
141
|
return this.buildRequestError('Network request failed', 0, context, { code: 'network_error' });
|
|
103
142
|
}
|
|
104
143
|
|
|
105
|
-
const message = error instanceof Error ? error.message : 'VerifyAI request failed';
|
|
144
|
+
const message = error instanceof Error ? error.message : stringifyErrorMessage(error, 'VerifyAI request failed');
|
|
106
145
|
return this.buildRequestError(message, 0, context);
|
|
107
146
|
}
|
|
108
147
|
|
|
@@ -132,7 +171,7 @@ export class VerifyAIClient {
|
|
|
132
171
|
if (!response.ok) {
|
|
133
172
|
const errorBody = (body && typeof body === 'object' ? body : null) as VerifyAIError | null;
|
|
134
173
|
throw this.buildRequestError(
|
|
135
|
-
errorBody?.error
|
|
174
|
+
stringifyErrorMessage(errorBody?.error, `Request failed with status ${response.status}`),
|
|
136
175
|
response.status,
|
|
137
176
|
context,
|
|
138
177
|
{
|
|
@@ -147,7 +186,7 @@ export class VerifyAIClient {
|
|
|
147
186
|
|
|
148
187
|
if (!body || typeof body !== 'object') {
|
|
149
188
|
throw this.buildRequestError(
|
|
150
|
-
|
|
189
|
+
`VerifyAI[v${SDK_VERSION}]: Invalid response payload`,
|
|
151
190
|
0,
|
|
152
191
|
context,
|
|
153
192
|
{
|
|
@@ -284,22 +323,27 @@ export class VerifyAIClient {
|
|
|
284
323
|
body: formData,
|
|
285
324
|
});
|
|
286
325
|
} catch (error) {
|
|
287
|
-
//
|
|
288
|
-
// Vercel
|
|
289
|
-
//
|
|
326
|
+
// 5xx during multipart can mean the server crashed before reservation
|
|
327
|
+
// (e.g. Vercel multipart-parse failure) OR after the verification was
|
|
328
|
+
// saved (e.g. signed-URL generation throw). Retry as base64 JSON and
|
|
329
|
+
// forward the original Idempotency-Key so the server can dedupe instead
|
|
330
|
+
// of inserting a second verify_ai_verifications row.
|
|
290
331
|
if (error instanceof VerifyAIRequestError && error.status >= 500 && error.isServerError) {
|
|
291
332
|
try {
|
|
292
333
|
const { Buffer } = await import('buffer');
|
|
293
334
|
const fileResponse = await fetch(request.imageUri);
|
|
294
335
|
const arrayBuffer = await fileResponse.arrayBuffer();
|
|
295
336
|
const base64 = Buffer.from(arrayBuffer).toString('base64');
|
|
296
|
-
return this.verify(
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
337
|
+
return this.verify(
|
|
338
|
+
{
|
|
339
|
+
image: base64,
|
|
340
|
+
policy: request.policy,
|
|
341
|
+
metadata: request.metadata,
|
|
342
|
+
provider: request.provider,
|
|
343
|
+
include_image_data: request.include_image_data,
|
|
344
|
+
},
|
|
345
|
+
options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : undefined,
|
|
346
|
+
);
|
|
303
347
|
} catch {
|
|
304
348
|
// If the retry itself fails, throw the original error
|
|
305
349
|
throw error;
|
|
@@ -384,11 +428,15 @@ export class VerifyAIRequestError extends Error {
|
|
|
384
428
|
status: number;
|
|
385
429
|
body: VerifyAIError;
|
|
386
430
|
|
|
387
|
-
constructor(message:
|
|
388
|
-
|
|
431
|
+
constructor(message: unknown, status: number, body: VerifyAIError) {
|
|
432
|
+
const safeMessage = stringifyErrorMessage(message, 'VerifyAI request failed');
|
|
433
|
+
super(safeMessage);
|
|
389
434
|
this.name = 'VerifyAIRequestError';
|
|
390
435
|
this.status = status;
|
|
391
|
-
this.body =
|
|
436
|
+
this.body = {
|
|
437
|
+
...body,
|
|
438
|
+
error: stringifyErrorMessage(body.error, safeMessage),
|
|
439
|
+
};
|
|
392
440
|
}
|
|
393
441
|
|
|
394
442
|
get isRateLimited(): boolean {
|
package/src/hooks/useVerifyAI.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { VerifyAIClient, VerifyAIRequestError } from '../client';
|
|
|
4
4
|
import { OfflineQueue } from '../storage/offlineQueue';
|
|
5
5
|
import { TelemetryContext } from '../telemetry/TelemetryContext';
|
|
6
6
|
import type { TelemetryReporter } from '../telemetry/TelemetryReporter';
|
|
7
|
+
import { SDK_VERSION } from '../version';
|
|
7
8
|
import type {
|
|
8
9
|
VerifyAIConfig,
|
|
9
10
|
VerificationRequest,
|
|
@@ -161,7 +162,7 @@ export function useVerifyAI(config: UseVerifyAIConfig): UseVerifyAIReturn {
|
|
|
161
162
|
});
|
|
162
163
|
inferenceEngineRef.current = new InferenceEngine();
|
|
163
164
|
} catch (err) {
|
|
164
|
-
console.warn(
|
|
165
|
+
console.warn(`[VerifyAI v${SDK_VERSION}] Failed to load ML modules:`, err);
|
|
165
166
|
}
|
|
166
167
|
})();
|
|
167
168
|
|
package/src/ml/modelManager.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { Buffer } from 'buffer';
|
|
7
7
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
8
8
|
import type { BundleManifest, ModelArtifact } from './types';
|
|
9
|
+
import { SDK_VERSION } from '../version';
|
|
9
10
|
|
|
10
11
|
export type { BundleManifest, ModelArtifact };
|
|
11
12
|
|
|
@@ -113,7 +114,7 @@ export class ModelManager {
|
|
|
113
114
|
private async downloadArtifacts(manifest: BundleManifest, policyId: string): Promise<void> {
|
|
114
115
|
const fileSystem = await loadFileSystem();
|
|
115
116
|
if (!fileSystem || !fileSystem.documentDirectory) {
|
|
116
|
-
console.warn(
|
|
117
|
+
console.warn(`[VerifyAI v${SDK_VERSION}] expo-file-system not available, skipping download`);
|
|
117
118
|
return;
|
|
118
119
|
}
|
|
119
120
|
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const SDK_VERSION = '2.4.
|
|
1
|
+
export const SDK_VERSION = '2.4.22';
|