@moveris/shared 1.0.0 → 1.0.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/README.md +453 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,25 +1,467 @@
|
|
|
1
1
|
# @moveris/shared
|
|
2
2
|
|
|
3
|
-
Core business logic for Moveris Live SDK. This package
|
|
4
|
-
|
|
5
|
-
- WebSocket client with reconnection logic
|
|
6
|
-
- Frame buffer management
|
|
7
|
-
- Authentication manager
|
|
8
|
-
- Type definitions
|
|
9
|
-
- Utility functions
|
|
3
|
+
Core business logic for Moveris Live SDK. This package provides the HTTP client, types, utilities, and frame management for liveness detection.
|
|
10
4
|
|
|
11
5
|
## Installation
|
|
12
6
|
|
|
13
7
|
```bash
|
|
14
8
|
pnpm add @moveris/shared
|
|
9
|
+
# or
|
|
10
|
+
npm install @moveris/shared
|
|
11
|
+
# or
|
|
12
|
+
yarn add @moveris/shared
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { LivenessClient, generateSessionId } from '@moveris/shared';
|
|
19
|
+
|
|
20
|
+
// Create the client
|
|
21
|
+
const client = new LivenessClient({
|
|
22
|
+
apiKey: 'mv_your_api_key',
|
|
23
|
+
baseUrl: 'https://api.moveris.io', // optional, uses default
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Perform a fast liveness check
|
|
27
|
+
const result = await client.fastCheck(frames, {
|
|
28
|
+
model: '10',
|
|
29
|
+
source: 'live',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
console.log(result.verdict); // 'live' or 'fake'
|
|
33
|
+
console.log(result.confidence); // 0-1
|
|
34
|
+
console.log(result.score); // 0-100
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## API Reference
|
|
38
|
+
|
|
39
|
+
### LivenessClient
|
|
40
|
+
|
|
41
|
+
The main client for interacting with the Moveris Liveness API.
|
|
42
|
+
|
|
43
|
+
#### Constructor
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
const client = new LivenessClient(config: LivenessClientConfig);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
| Option | Type | Default | Description |
|
|
50
|
+
| ------------- | -------------- | -------------------------- | ----------------------------------------------- |
|
|
51
|
+
| `apiKey` | `string` | **required** | Your Moveris API key |
|
|
52
|
+
| `baseUrl` | `string` | `'https://api.moveris.io'` | API base URL |
|
|
53
|
+
| `timeout` | `number` | `30000` | Request timeout in milliseconds |
|
|
54
|
+
| `enableRetry` | `boolean` | `true` | Enable automatic retry with exponential backoff |
|
|
55
|
+
| `customFetch` | `typeof fetch` | `fetch` | Custom fetch implementation (for React Native) |
|
|
56
|
+
|
|
57
|
+
#### Methods
|
|
58
|
+
|
|
59
|
+
##### `fastCheck(frames, options)`
|
|
60
|
+
|
|
61
|
+
Perform fast liveness check with server-side face detection. Ideal for quick verification with 10-250 frames.
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const result = await client.fastCheck(frames, {
|
|
65
|
+
sessionId: 'optional-uuid',
|
|
66
|
+
model: '10', // '10' | '50' | '250'
|
|
67
|
+
source: 'live', // 'live' | 'media'
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Parameters:**
|
|
72
|
+
|
|
73
|
+
| Option | Type | Default | Description |
|
|
74
|
+
| ----------- | ---------------- | -------------- | ----------------------------------------------------------------------- |
|
|
75
|
+
| `sessionId` | `string` | auto-generated | Unique session identifier (UUID) |
|
|
76
|
+
| `model` | `FastCheckModel` | `'10'` | Model to use: `'10'` (fast), `'50'` (balanced), `'250'` (high-accuracy) |
|
|
77
|
+
| `source` | `FrameSource` | `'live'` | Frame source: `'live'` (camera) or `'media'` (recorded video) |
|
|
78
|
+
|
|
79
|
+
**Returns:** `Promise<LivenessResult>`
|
|
80
|
+
|
|
81
|
+
##### `fastCheckCrops(crops, options)`
|
|
82
|
+
|
|
83
|
+
Perform fast liveness check with pre-cropped 224x224 face images. Use this when you handle face detection client-side.
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
const result = await client.fastCheckCrops(crops, {
|
|
87
|
+
model: '50',
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
##### `verify(frames, options)`
|
|
92
|
+
|
|
93
|
+
Verify liveness using spatial feature-based detection. Requires minimum 50 frames.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const result = await client.verify(frames, {
|
|
97
|
+
fps: 30,
|
|
98
|
+
frameWidth: 640,
|
|
99
|
+
frameHeight: 480,
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
##### `hybridCheck(frames, options)`
|
|
104
|
+
|
|
105
|
+
Perform hybrid liveness check combining CNN with physiological features. Requires minimum 50 frames.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const result = await client.hybridCheck(frames, { fps: 30 });
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
##### `hybrid50(frames, options)`
|
|
112
|
+
|
|
113
|
+
50-frame hybrid model with 93.8% balanced accuracy.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
const result = await client.hybrid50(frames, { fps: 30 });
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
##### `hybrid150(frames, options)`
|
|
120
|
+
|
|
121
|
+
150-frame hybrid model with 96.2% balanced accuracy. Best for high-security scenarios.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const result = await client.hybrid150(frames, { fps: 30 });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
##### `health()`
|
|
128
|
+
|
|
129
|
+
Check API health status.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const health = await client.health();
|
|
133
|
+
console.log(health.status); // 'ok'
|
|
134
|
+
console.log(health.models_loaded); // ['10', '50', '250']
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
##### `getJobResult(jobId)`
|
|
138
|
+
|
|
139
|
+
Get job status for async processing.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const job = await client.getJobResult('job-uuid');
|
|
143
|
+
console.log(job.status); // 'queued' | 'processing' | 'complete' | 'failed'
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
##### `waitForJobResult(jobId, timeout)`
|
|
147
|
+
|
|
148
|
+
Long-poll for job completion (max 120 seconds).
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
const job = await client.waitForJobResult('job-uuid', 30);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
### Types
|
|
157
|
+
|
|
158
|
+
#### FastCheckModel
|
|
159
|
+
|
|
160
|
+
Model selection for liveness detection speed/accuracy trade-off.
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
type FastCheckModel = '10' | '50' | '250';
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
| Value | Frames | Description |
|
|
167
|
+
| ------- | ------ | --------------------------------- |
|
|
168
|
+
| `'10'` | 10 | Fast verification, lowest latency |
|
|
169
|
+
| `'50'` | 50 | Balanced speed and accuracy |
|
|
170
|
+
| `'250'` | 250 | Highest accuracy, slower |
|
|
171
|
+
|
|
172
|
+
#### FrameSource
|
|
173
|
+
|
|
174
|
+
Source of the captured frames.
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
type FrameSource = 'media' | 'live';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Value | Description |
|
|
181
|
+
| --------- | ----------------------- |
|
|
182
|
+
| `'live'` | Real-time camera feed |
|
|
183
|
+
| `'media'` | Pre-recorded video file |
|
|
184
|
+
|
|
185
|
+
#### Verdict
|
|
186
|
+
|
|
187
|
+
Liveness determination result.
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
type Verdict = 'live' | 'fake';
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
| Value | Description |
|
|
194
|
+
| -------- | ---------------------------------------------------- |
|
|
195
|
+
| `'live'` | Real person detected |
|
|
196
|
+
| `'fake'` | Spoofing attempt detected (photo, video, mask, etc.) |
|
|
197
|
+
|
|
198
|
+
#### LivenessState
|
|
199
|
+
|
|
200
|
+
State machine for liveness detection flow.
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
type LivenessState =
|
|
204
|
+
| 'idle' // Ready to start
|
|
205
|
+
| 'capturing' // Capturing frames from camera
|
|
206
|
+
| 'uploading' // Uploading frames to API
|
|
207
|
+
| 'processing' // API is processing frames
|
|
208
|
+
| 'complete' // Verification complete
|
|
209
|
+
| 'error'; // Error occurred
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### LivenessResult
|
|
213
|
+
|
|
214
|
+
Result returned from liveness verification.
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
interface LivenessResult {
|
|
218
|
+
verdict: Verdict; // 'live' or 'fake'
|
|
219
|
+
confidence: number; // Confidence score (0-1)
|
|
220
|
+
score: number; // Liveness score (0-100)
|
|
221
|
+
sessionId: string; // Session identifier
|
|
222
|
+
processingMs: number; // Server processing time
|
|
223
|
+
framesProcessed: number; // Number of frames analyzed
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
#### CapturedFrame
|
|
228
|
+
|
|
229
|
+
Individual frame captured from camera.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
interface CapturedFrame {
|
|
233
|
+
index: number; // Frame sequence number (0-based)
|
|
234
|
+
timestampMs: number; // Timestamp from capture start
|
|
235
|
+
pixels: string; // Base64-encoded image data
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### Utilities
|
|
242
|
+
|
|
243
|
+
#### `generateSessionId()`
|
|
244
|
+
|
|
245
|
+
Generate a UUID v4 for session identification.
|
|
246
|
+
|
|
247
|
+
```typescript
|
|
248
|
+
import { generateSessionId } from '@moveris/shared';
|
|
249
|
+
|
|
250
|
+
const sessionId = generateSessionId();
|
|
251
|
+
// e.g., 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
#### `toFrameData(frames)`
|
|
255
|
+
|
|
256
|
+
Convert CapturedFrame array to API format.
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
import { toFrameData } from '@moveris/shared';
|
|
260
|
+
|
|
261
|
+
const apiFrames = toFrameData(capturedFrames);
|
|
15
262
|
```
|
|
16
263
|
|
|
17
|
-
|
|
264
|
+
#### `toHybridFrameData(frames)`
|
|
265
|
+
|
|
266
|
+
Convert frames to hybrid endpoint format.
|
|
18
267
|
|
|
19
268
|
```typescript
|
|
20
|
-
import {
|
|
269
|
+
import { toHybridFrameData } from '@moveris/shared';
|
|
270
|
+
|
|
271
|
+
const hybridFrames = toHybridFrameData(capturedFrames);
|
|
21
272
|
```
|
|
22
273
|
|
|
23
|
-
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
### Frame Buffer Management
|
|
277
|
+
|
|
278
|
+
#### FrameBuffer
|
|
279
|
+
|
|
280
|
+
Manages a sliding window buffer of frames with automatic cleanup.
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { FrameBuffer } from '@moveris/shared';
|
|
284
|
+
|
|
285
|
+
const buffer = new FrameBuffer({
|
|
286
|
+
maxSize: 10, // Maximum frames to keep
|
|
287
|
+
maxMemoryBytes: 4 * 1024 * 1024, // 4MB limit
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Add frames
|
|
291
|
+
buffer.add(frame);
|
|
292
|
+
|
|
293
|
+
// Get all frames
|
|
294
|
+
const frames = buffer.getAll();
|
|
295
|
+
|
|
296
|
+
// Acknowledge processed frames
|
|
297
|
+
buffer.acknowledge(5); // Remove frames up to index 5
|
|
298
|
+
|
|
299
|
+
// Clear buffer
|
|
300
|
+
buffer.clear();
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
#### FrameQueue
|
|
304
|
+
|
|
305
|
+
FIFO queue for frame processing.
|
|
306
|
+
|
|
307
|
+
```typescript
|
|
308
|
+
import { FrameQueue } from '@moveris/shared';
|
|
309
|
+
|
|
310
|
+
const queue = new FrameQueue({ maxSize: 50 });
|
|
311
|
+
|
|
312
|
+
queue.enqueue(frame);
|
|
313
|
+
const frame = queue.dequeue();
|
|
314
|
+
const peeked = queue.peek();
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
---
|
|
318
|
+
|
|
319
|
+
### Error Handling
|
|
320
|
+
|
|
321
|
+
#### LivenessApiError
|
|
322
|
+
|
|
323
|
+
Custom error class for API errors.
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
import { LivenessApiError } from '@moveris/shared';
|
|
327
|
+
|
|
328
|
+
try {
|
|
329
|
+
await client.fastCheck(frames);
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (error instanceof LivenessApiError) {
|
|
332
|
+
console.log(error.code); // 'insufficient_frames'
|
|
333
|
+
console.log(error.message); // 'Not enough frames provided'
|
|
334
|
+
console.log(error.statusCode); // 400
|
|
335
|
+
console.log(error.required); // 10
|
|
336
|
+
console.log(error.received); // 5
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Error Codes:**
|
|
342
|
+
|
|
343
|
+
| Code | Description |
|
|
344
|
+
| --------------------- | -------------------------- |
|
|
345
|
+
| `insufficient_frames` | Not enough frames provided |
|
|
346
|
+
| `invalid_model` | Invalid model specified |
|
|
347
|
+
| `invalid_key` | Invalid API key |
|
|
348
|
+
| `missing_field` | Required field missing |
|
|
349
|
+
| `timeout` | Request timeout |
|
|
350
|
+
| `network_error` | Network connectivity issue |
|
|
351
|
+
|
|
352
|
+
---
|
|
353
|
+
|
|
354
|
+
### Feedback Messages
|
|
355
|
+
|
|
356
|
+
Localized user feedback for liveness detection UI.
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { getFeedbackMessage, getStatusMessage, DEFAULT_LOCALE, ES_LOCALE } from '@moveris/shared';
|
|
360
|
+
|
|
361
|
+
// Get localized feedback
|
|
362
|
+
const message = getFeedbackMessage('face_not_centered', ES_LOCALE);
|
|
363
|
+
// "Centra tu rostro en el óvalo"
|
|
364
|
+
|
|
365
|
+
// Get status message
|
|
366
|
+
const status = getStatusMessage('capturing', DEFAULT_LOCALE);
|
|
367
|
+
// "Hold still..."
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
#### Feedback Keys
|
|
371
|
+
|
|
372
|
+
| Key | English | Description |
|
|
373
|
+
| ------------------- | ------------------------------ | ---------------------------- |
|
|
374
|
+
| `no_face` | "No face detected" | Face detection failed |
|
|
375
|
+
| `face_not_centered` | "Center your face in the oval" | Face outside guide |
|
|
376
|
+
| `too_close` | "Move back a little" | Face too close to camera |
|
|
377
|
+
| `too_far` | "Move closer" | Face too far from camera |
|
|
378
|
+
| `poor_lighting` | "Improve lighting" | Insufficient light |
|
|
379
|
+
| `hold_still` | "Hold still" | Movement detected |
|
|
380
|
+
| `capturing` | "Capturing..." | Frame capture in progress |
|
|
381
|
+
| `processing` | "Processing..." | API verification in progress |
|
|
382
|
+
| `success` | "Verification complete" | Successful completion |
|
|
383
|
+
| `failed` | "Verification failed" | Failed verification |
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
### Oval Guide States
|
|
388
|
+
|
|
389
|
+
Visual feedback states for the face positioning guide.
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import { getOvalGuideState, OVAL_GUIDE_COLORS } from '@moveris/shared';
|
|
393
|
+
|
|
394
|
+
const state = getOvalGuideState(alignmentScore);
|
|
395
|
+
// Returns: 'no_face' | 'poor' | 'good' | 'perfect'
|
|
396
|
+
|
|
397
|
+
const color = OVAL_GUIDE_COLORS[state];
|
|
398
|
+
// 'red' | 'orange' | 'yellow' | 'green'
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
| State | Color | Alignment Score |
|
|
402
|
+
| --------- | ------ | ---------------- |
|
|
403
|
+
| `no_face` | Red | No face detected |
|
|
404
|
+
| `poor` | Orange | < 0.5 |
|
|
405
|
+
| `good` | Yellow | 0.5 - 0.8 |
|
|
406
|
+
| `perfect` | Green | > 0.8 |
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
### Frame Analysis Utilities
|
|
411
|
+
|
|
412
|
+
Utilities for assessing frame quality before submission.
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
import { isFaceFullyVisible, isFaceInOval, calculateFaceCropRegion } from '@moveris/shared';
|
|
416
|
+
|
|
417
|
+
// Check if face is fully visible in frame
|
|
418
|
+
const visibility = isFaceFullyVisible(faceBbox, frameWidth, frameHeight);
|
|
419
|
+
console.log(visibility.isVisible); // true/false
|
|
420
|
+
console.log(visibility.reason); // 'left_edge' | 'top_edge' | etc.
|
|
421
|
+
|
|
422
|
+
// Check if face is within the oval guide
|
|
423
|
+
const inOval = isFaceInOval(faceBbox, ovalRegion);
|
|
424
|
+
console.log(inOval.isInOval); // true/false
|
|
425
|
+
console.log(inOval.alignmentScore); // 0-1
|
|
426
|
+
|
|
427
|
+
// Calculate crop region for face
|
|
428
|
+
const cropRegion = calculateFaceCropRegion(faceBbox, frameWidth, frameHeight);
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Configuration Constants
|
|
434
|
+
|
|
435
|
+
```typescript
|
|
436
|
+
import { DEFAULT_ENDPOINT, AUTH_CONFIG, RETRY_CONFIG, FRAME_CONFIG } from '@moveris/shared';
|
|
437
|
+
|
|
438
|
+
console.log(DEFAULT_ENDPOINT); // 'https://api.moveris.io'
|
|
439
|
+
console.log(AUTH_CONFIG.apiKeyHeader); // 'X-API-Key'
|
|
440
|
+
console.log(RETRY_CONFIG.maxAttempts); // 3
|
|
441
|
+
console.log(FRAME_CONFIG.maxBufferSize); // 10
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## TypeScript Support
|
|
447
|
+
|
|
448
|
+
This package is written in TypeScript and provides full type definitions.
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
import type {
|
|
452
|
+
LivenessResult,
|
|
453
|
+
LivenessState,
|
|
454
|
+
LivenessConfig,
|
|
455
|
+
FastCheckModel,
|
|
456
|
+
FrameSource,
|
|
457
|
+
Verdict,
|
|
458
|
+
CapturedFrame,
|
|
459
|
+
LivenessClientConfig,
|
|
460
|
+
} from '@moveris/shared';
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
---
|
|
464
|
+
|
|
465
|
+
## License
|
|
24
466
|
|
|
25
|
-
|
|
467
|
+
MIT
|