npm package: @moveris/shared
Description: Core business logic for Moveris Live SDK
Author: Jeyson Palacio
Latest version: 3.6.2

# @moveris/shared

Core business logic for Moveris Live SDK. This package provides the HTTP client, types, utilities, and frame management for liveness detection.

## Installation

```bash
pnpm add @moveris/shared
# or
npm install @moveris/shared
# or
yarn add @moveris/shared
```

## Quick Start

```typescript
import { LivenessClient, generateSessionId } from '@moveris/shared';

// Create the client
const client = new LivenessClient({
  apiKey: 'mv_your_api_key',
  baseUrl: 'https://api.moveris.com', // optional, uses default
});

// Perform a fast liveness check
const result = await client.fastCheck(frames, {
  sessionId: 'my-session-id', // optional — auto-generated if omitted
  model: 'mixed-10-v2',
  source: 'live',
});

console.log(result.verdict); // 'live', 'fake', or 'inconclusive'
console.log(result.confidence); // 0-1
console.log(result.score); // 0-100
```

## API Reference

### LivenessClient

The main client for interacting with the Moveris Liveness API.

#### Constructor

```typescript
const client = new LivenessClient(config: LivenessClientConfig);
```

| Option         | Type           | Default                     | Description                                                        |
| -------------- | -------------- | --------------------------- | ------------------------------------------------------------------ |
| `apiKey`       | `string`       | **required**                | Your Moveris API key                                               |
| `baseUrl`      | `string`       | `'https://api.moveris.com'` | API base URL                                                       |
| `modelVersion` | `ModelVersion` | -                           | Model version alias for `X-Model-Version` header (e.g. `'latest'`) |
| `timeout`      | `number`       | `30000`                     | Request timeout in milliseconds                                    |
| `enableRetry`  | `boolean`      | `true`                      | Enable automatic retry with exponential backoff                    |
| `customFetch`  | `typeof fetch` | `fetch`                     | Custom fetch implementation (for React Native)                     |

#### Methods

##### `fastCheck(frames, options)`

Perform fast liveness check with server-side face detection. Ideal for quick verification with 10-250 frames.

```typescript
const result = await client.fastCheck(frames, {
  sessionId: 'optional-uuid',
  model: 'mixed-10-v2',
  source: 'live', // 'live' | 'media'
});
```

**Parameters:**

| Option         | Type             | Default         | Description                                                       |
| -------------- | ---------------- | --------------- | ----------------------------------------------------------------- |
| `sessionId`    | `string`         | auto-generated  | Unique session identifier (UUID)                                  |
| `model`        | `FastCheckModel` | `'mixed-10-v2'` | Model to use (see Models section)                                 |
| `modelVersion` | `ModelVersion`   | -               | Version alias sent via `X-Model-Version` header (e.g. `'latest'`) |
| `frameCount`   | `number`         | -               | Frame count for alias-based model resolution                      |
| `source`       | `FrameSource`    | `'live'`        | Frame source: `'live'` (camera) or `'media'` (recorded video)     |

**Returns:** `Promise<LivenessResult>`

##### `fastCheckCrops(crops, options)`

Perform fast liveness check with pre-cropped 224x224 face images. Use this when you handle face detection client-side. Expects `CropData[]` (no timestamps — only `index` and `pixels`).

```typescript
import type { CropData } from '@moveris/shared';

const crops: CropData[] = capturedFrames.map((f) => ({
  index: f.index,
  pixels: f.pixels, // base64 224x224 PNG
}));

const result = await client.fastCheckCrops(crops, {
  model: '10',
  source: 'live',
  bgSegmentation: true, // optional — sent as bg_segmentation in payload
});
```

##### `streamFrame(frame, options)`

Send a single frame to the streaming endpoint. Returns the API response which may include a partial or final result. Used internally by `useLiveness` for sequential streaming.

```typescript
const response = await client.streamFrame(frame, {
  sessionId: 'uuid',
  model: '10',
  source: 'live',
  frameIndex: 0,
  totalFrames: 10,
});
```

##### `verify(frames, options)`

Verify liveness using spatial feature-based detection. Requires minimum 50 frames.

```typescript
const result = await client.verify(frames, {
  fps: 30,
  frameWidth: 640,
  frameHeight: 480,
});
```

##### `hybridCheck(frames, options)`

Perform hybrid liveness check combining CNN with physiological features. Requires minimum 50 frames.

```typescript
const result = await client.hybridCheck(frames, { fps: 30 });
```

##### `hybrid50(frames, options)`

50-frame hybrid model with 93.8% balanced accuracy.

```typescript
const result = await client.hybrid50(frames, { fps: 30 });
```

##### `hybrid150(frames, options)`

150-frame hybrid model with 96.2% balanced accuracy. Best for high-security scenarios.

```typescript
const result = await client.hybrid150(frames, { fps: 30 });
```

##### `health()`

Check API health status.

```typescript
const health = await client.health();
console.log(health.status); // 'ok'
console.log(health.models_loaded); // ['10', '50', '250']
```

##### `getJobResult(jobId)`

Get job status for async processing.

```typescript
const job = await client.getJobResult('job-uuid');
console.log(job.status); // 'queued' | 'processing' | 'complete' | 'failed'
```

##### `waitForJobResult(jobId, timeout)`

Long-poll for job completion (max 120 seconds).

```typescript
const job = await client.waitForJobResult('job-uuid', 30);
```

---

### Types

#### FastCheckModel

Model selection for liveness detection speed/accuracy trade-off.

```typescript
type FastCheckModel =
  | 'mixed-10-v2'
  | 'mixed-30-v2'
  | 'mixed-60-v2'
  | 'mixed-90-v2'
  | 'mixed-120-v2' // Active mixed-v2 models (recommended)
  | '10'
  | '50'
  | '250' // Legacy standard models
  | (string & object); // Forward-compatible with future model IDs
```

Active models (recommended):

| Value            | Frames | Description                    |
| ---------------- | ------ | ------------------------------ |
| `'mixed-10-v2'`  | 10     | Fast verification, low latency |
| `'mixed-30-v2'`  | 30     | Balanced speed and accuracy    |
| `'mixed-60-v2'`  | 60     | Higher accuracy                |
| `'mixed-90-v2'`  | 90     | High accuracy                  |
| `'mixed-120-v2'` | 120    | Highest accuracy, slower       |

Legacy models (still supported):

| Value   | Frames | Description                                      |
| ------- | ------ | ------------------------------------------------ |
| `'10'`  | 10     | Standard — fast (alias: `'fast'`)                |
| `'50'`  | 50     | Standard — balanced (alias: `'spatial'`)         |
| `'250'` | 250    | Standard — highest accuracy (alias: `'spatial'`) |

Hybrid-v2 models (alias: `'hybrid'`):

| Value             | Frames | Description                  |
| ----------------- | ------ | ---------------------------- |
| `'hybrid-v2-10'`  | 10     | Hybrid v2 — fast             |
| `'hybrid-v2-30'`  | 30     | Hybrid v2 — balanced         |
| `'hybrid-v2-60'`  | 60     | Hybrid v2 — higher accuracy  |
| `'hybrid-v2-90'`  | 90     | Hybrid v2 — high accuracy    |
| `'hybrid-v2-100'` | 100    | Hybrid v2 — high accuracy    |
| `'hybrid-v2-120'` | 120    | Hybrid v2 — highest accuracy |
| `'hybrid-v2-125'` | 125    | Hybrid v2 — highest accuracy |
| `'hybrid-v2-250'` | 250    | Hybrid v2 — maximum accuracy |

Deprecated models (v1 — sunset 2026-09-01):

| Value         | Frames | Replacement      |
| ------------- | ------ | ---------------- |
| `'mixed-10'`  | 10     | `'mixed-10-v2'`  |
| `'mixed-30'`  | 30     | `'mixed-30-v2'`  |
| `'mixed-60'`  | 60     | `'mixed-60-v2'`  |
| `'mixed-90'`  | 90     | `'mixed-90-v2'`  |
| `'mixed-120'` | 120    | `'mixed-120-v2'` |
| `'mixed-150'` | 150    | —                |
| `'mixed-250'` | 250    | —                |

#### FrameSource

Source of the captured frames.

```typescript
type FrameSource = 'media' | 'live';
```

| Value     | Description             |
| --------- | ----------------------- |
| `'live'`  | Real-time camera feed   |
| `'media'` | Pre-recorded video file |

#### Verdict

Liveness determination result.

```typescript
type Verdict = 'live' | 'fake' | 'inconclusive';
```

| Value            | Description                                                                      |
| ---------------- | -------------------------------------------------------------------------------- |
| `'live'`         | Real person detected                                                             |
| `'fake'`         | Spoofing attempt detected (photo, video, mask, etc.)                             |
| `'inconclusive'` | API processed successfully but could not determine a verdict — retry recommended |

#### LivenessState

State machine for liveness detection flow.

```typescript
type LivenessState =
  | 'idle' // Ready to start
  | 'capturing' // Capturing frames from camera
  | 'uploading' // Uploading frames to API
  | 'processing' // API is processing frames
  | 'complete' // Verification complete
  | 'error'; // Error occurred
```

#### LivenessResult

Result returned from liveness verification.

```typescript
interface LivenessResult {
  verdict: Verdict; // 'live', 'fake', or 'inconclusive'
  confidence: number; // Confidence score (0-1)
  score: number; // Liveness score (0-100)
  sessionId: string; // Session identifier
  processingMs: number; // Server processing time
  framesProcessed: number; // Number of frames analyzed
  deprecation?: DeprecationInfo; // Present when X-Moveris-Model-Resolved header is returned
}
```

#### CapturedFrame

Individual frame captured from camera.

```typescript
interface CapturedFrame {
  index: number; // Frame sequence number (0-based)
  timestampMs: number; // Timestamp from capture start
  pixels: string; // Base64-encoded image data
}
```

#### CropData

Pre-cropped face image for the `fast-check-crops` endpoint. Omits `timestampMs` since timing is not needed for cropped submissions.

```typescript
interface CropData {
  index: number; // Frame sequence number (0-based)
  pixels: string; // Base64-encoded 224x224 PNG image
}
```

#### ModelVersion

Version alias sent via the `X-Model-Version` request header for server-side model resolution.

```typescript
type ModelVersion = 'latest' | 'v2' | 'v1' | 'fast' | 'spatial' | 'hybrid' | (string & object);
```

| Value       | Resolves to                         |
| ----------- | ----------------------------------- |
| `'latest'`  | Current production mixed-v2 model   |
| `'v2'`      | Pinned mixed-v2 version             |
| `'v1'`      | Legacy mixed-v1 models (deprecated) |
| `'fast'`    | Model `'10'`                        |
| `'spatial'` | Models `'50'` / `'250'`             |
| `'hybrid'`  | Hybrid-v2 models                    |

> When `X-Model-Version` is set, the API also requires `frame_count` in the request body. Pass it via the `frameCount` option.

#### DeprecationInfo

Parsed from API response headers when the server resolves a model.

```typescript
interface DeprecationInfo {
  deprecated: boolean; // true when model is deprecated
  resolvedModel: string; // Concrete model id (X-Moveris-Model-Resolved)
  deprecatedModel?: string; // Deprecated model id (X-Moveris-Deprecated-Model)
  sunsetDate?: string; // ISO date of removal (Sunset header)
  suggestedModel?: string; // Replacement model (X-Moveris-Suggested-Model)
}
```

---

### Model Versioning & Deprecation

The client supports model alias resolution via the `X-Model-Version` header and automatically parses deprecation headers from API responses.

```typescript
// Use aliases for automatic server-side resolution
const client = new LivenessClient({
  apiKey: 'mv_your_api_key',
  modelVersion: 'latest', // sent as X-Model-Version header on every request
});

const result = await client.fastCheck(frames, {
  model: 'mixed-30-v2',
  frameCount: 30, // required when modelVersion is set
});

// Deprecation info is automatically parsed from response headers
if (result.deprecation?.deprecated) {
  console.warn(`Model "${result.deprecation.resolvedModel}" is deprecated.`);
  console.warn(`Migrate to "${result.deprecation.suggestedModel}".`);
  console.warn(`Sunset date: ${result.deprecation.sunsetDate}`);
}

// Per-call override (takes precedence over client-level modelVersion)
const result2 = await client.fastCheck(frames, {
  modelVersion: 'v2',
  frameCount: 10,
});
```

#### Valid Frame Counts

When using model version aliases, `frameCount` must be one of:

```typescript
import { VALID_FRAME_COUNTS } from '@moveris/shared';
// [10, 30, 60, 90, 120]
```

---

### Utilities

#### `generateSessionId()`

Generate a UUID v4 for session identification.

```typescript
import { generateSessionId } from '@moveris/shared';

const sessionId = generateSessionId();
// e.g., 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'
```

> **Session ID injection**: Every `LivenessClient` method (`fastCheck`, `fastCheckCrops`, `streamFrame`, `verify`, `hybrid50`, `hybrid150`) accepts an optional `sessionId` in its options object. If provided, that ID is used for the request; if omitted, a new UUID is generated automatically. This is useful for debugging, testing, or correlating client requests with server logs. In `@moveris/react`, the `sessionId` can be injected via the `useLiveness` hook config or the `LivenessView` / `LivenessModal` props.

#### `toFrameData(frames)`

Convert CapturedFrame array to API format.

```typescript
import { toFrameData } from '@moveris/shared';

const apiFrames = toFrameData(capturedFrames);
```

#### `toHybridFrameData(frames)`

Convert frames to hybrid endpoint format.

```typescript
import { toHybridFrameData } from '@moveris/shared';

const hybridFrames = toHybridFrameData(capturedFrames);
```

---

### Frame Buffer Management

#### FrameBuffer

Manages a sliding window buffer of frames with automatic cleanup.

```typescript
import { FrameBuffer } from '@moveris/shared';

const buffer = new FrameBuffer({
  maxSize: 10, // Maximum frames to keep
  maxMemoryBytes: 4 * 1024 * 1024, // 4MB limit
});

// Add frames
buffer.add(frame);

// Get all frames
const frames = buffer.getAll();

// Acknowledge processed frames
buffer.acknowledge(5); // Remove frames up to index 5

// Clear buffer
buffer.clear();
```

#### FrameQueue

FIFO queue for frame processing.

```typescript
import { FrameQueue } from '@moveris/shared';

const queue = new FrameQueue({ maxSize: 50 });

queue.enqueue(frame);
const frame = queue.dequeue();
const peeked = queue.peek();
```

---

### Error Handling

#### LivenessApiError

Custom error class for API errors.

```typescript
import { LivenessApiError } from '@moveris/shared';

try {
  await client.fastCheck(frames);
} catch (error) {
  if (error instanceof LivenessApiError) {
    console.log(error.code); // 'insufficient_frames'
    console.log(error.message); // 'Not enough frames provided'
    console.log(error.statusCode); // 400
    console.log(error.required); // 10
    console.log(error.received); // 5
  }
}
```

**Error Codes:**

| Code                  | Description                |
| --------------------- | -------------------------- |
| `insufficient_frames` | Not enough frames provided |
| `invalid_model`       | Invalid model specified    |
| `invalid_key`         | Invalid API key            |
| `missing_field`       | Required field missing     |
| `timeout`             | Request timeout            |
| `network_error`       | Network connectivity issue |

---

### Feedback Messages

Localized user feedback for liveness detection UI.

```typescript
import { getFeedbackMessage, getStatusMessage, DEFAULT_LOCALE, ES_LOCALE } from '@moveris/shared';

// Get localized feedback
const message = getFeedbackMessage('face_not_centered', ES_LOCALE);
// "Centra tu rostro en el óvalo"

// Get status message
const status = getStatusMessage('capturing', DEFAULT_LOCALE);
// "Hold still..."
```

#### Feedback Keys

| Key                 | English                        | Description                                                                       |
| ------------------- | ------------------------------ | --------------------------------------------------------------------------------- |
| `no_face`           | "No face detected"             | Face detection failed                                                             |
| `face_not_centered` | "Center your face in the oval" | Face outside guide                                                                |
| `too_close`         | "Move back a little"           | Face too close to camera                                                          |
| `too_far`           | "Move closer"                  | Face too far from camera                                                          |
| `poor_lighting`     | "Improve lighting"             | Insufficient light                                                                |
| `hold_still`        | "Hold still"                   | Movement detected                                                                 |
| `capturing`         | "Capturing..."                 | Frame capture in progress                                                         |
| `processing`        | "Processing..."                | API verification in progress                                                      |
| `success`           | "Verification complete"        | Successful completion                                                             |
| `failed`            | "Verification failed"          | Failed verification                                                               |
| `eyes_not_visible`  | "Eyes not clearly visible"     | Eye region featureless                                                            |
| `eyes_shadowed`     | "Eyes are in shadow…"          | Eye region too dark                                                               |
| `eyes_overexposed`  | "Eye region overexposed…"      | Eye region too bright                                                             |
| `glasses_glare`     | "Glare detected…"              | Specular highlights on eyes                                                       |
| `eye_quality_poor`  | "Eye region quality is poor"   | Generic eye quality failure                                                       |
| `camera_angle_low`  | "Raise camera to eye level"    | Camera below face (Y < 0.3)                                                       |
| `camera_angle_high` | "Lower camera to eye level"    | Camera above face (Y > 0.7)                                                       |
| `camera_tilted`     | "Hold camera level"            | Device tilt > 15° (via transformation matrix, falls back to eye-corner landmarks) |
| `flat_lighting`     | "Find better lighting"         | Face-region dynamic range < 60 L-units — soft warning, capture continues          |

---

### Oval Guide States

Visual feedback states for the face positioning guide.

```typescript
import { getOvalGuideState, OVAL_GUIDE_COLORS } from '@moveris/shared';

const state = getOvalGuideState(alignmentScore);
// Returns: 'no_face' | 'poor' | 'good' | 'perfect'

const color = OVAL_GUIDE_COLORS[state];
// 'red' | 'orange' | 'yellow' | 'green'
```

| State     | Color  | Alignment Score  |
| --------- | ------ | ---------------- |
| `no_face` | Red    | No face detected |
| `poor`    | Orange | < 0.5            |
| `good`    | Yellow | 0.5 - 0.8        |
| `perfect` | Green  | > 0.8            |

---

### Eye Region Quality Analysis

Pre-request eye-region quality gate. Platform-agnostic functions that analyze RGBA pixel data from eye regions to detect shadows, glare, occlusion, and poor visibility.

```typescript
import {
  checkEyeRegionQuality,
  analyzeEyeRegionBrightness,
  analyzeEyeRegionContrast,
  detectSpecularHighlights,
  EYE_QUALITY_THRESHOLDS,
} from '@moveris/shared';

// Combined quality check (recommended)
const quality = checkEyeRegionQuality(eyeRegionPixels);
console.log(quality.passed); // boolean
console.log(quality.brightness); // 0-255
console.log(quality.contrast); // standard deviation of luminance
console.log(quality.hasGlare); // boolean
console.log(quality.glareRatio); // 0-1
console.log(quality.message); // user-facing feedback or null

// Individual checks
const brightness = analyzeEyeRegionBrightness(pixels);
const contrast = analyzeEyeRegionContrast(pixels);
const glareRatio = detectSpecularHighlights(pixels);
```

| Check       | Threshold             | Condition                               |
| ----------- | --------------------- | --------------------------------------- |
| Shadowed    | `minBrightness: 40`   | Average luminance too low               |
| Overexposed | `maxBrightness: 230`  | Average luminance too high              |
| Glare       | `maxGlareRatio: 0.15` | >15% of pixels are specular highlights  |
| Occluded    | `minContrast: 12`     | Standard deviation of luminance too low |

Glare detection uses a **relative threshold** (`mean brightness × 2.5`) so sensitivity adapts to ambient lighting, avoiding false positives in bright environments.

Custom thresholds can be passed as a second argument to `checkEyeRegionQuality()`.

### Eye Region Landmarks

Extract bounding boxes for left and right eye regions from MediaPipe 468-point face mesh landmarks.

```typescript
import { getEyeRegionBounds, EYE_LANDMARK_INDICES, validateFaceLandmarks } from '@moveris/shared';

const bounds = getEyeRegionBounds(landmarks);
if (bounds) {
  console.log(bounds.leftEye); // { x, y, width, height } (normalized 0-1)
  console.log(bounds.rightEye); // { x, y, width, height } (normalized 0-1)
}
```

---

### Frame Analysis Utilities

Utilities for assessing frame quality before submission.

```typescript
import {
  isFaceFullyVisible,
  isFaceInOval,
  calculateFaceCropRegion,
  detectFaceRoll,
  detectFaceRollFromMatrix,
  detectCameraAngle,
} from '@moveris/shared';

// Check if face is fully visible in frame
const visibility = isFaceFullyVisible(faceBbox, frameWidth, frameHeight);
console.log(visibility.isVisible); // true/false
console.log(visibility.reason); // 'left_edge' | 'top_edge' | etc.

// Check if face is within the oval guide
const inOval = isFaceInOval(faceBbox, ovalRegion);
console.log(inOval.isInOval); // true/false
console.log(inOval.alignmentScore); // 0-1

// Calculate crop region for face (224x224, aligned with cognito-check constants)
const cropRegion = calculateFaceCropRegion(faceBbox, frameWidth, frameHeight);
// { x, y, size } — square crop region in pixel coordinates

// Calculate adaptive crop multiplier based on face size
import { calculateAdaptiveCropMultiplier } from '@moveris/shared';
const multiplier = calculateAdaptiveCropMultiplier(faceBox, frameWidth, frameHeight);
// Returns 2.5–4.0x (matched to cognito-check for ~33% face coverage in 224×224 crop)

// Detect face roll (device tilt) — prefer matrix when available (more accurate under glasses/occlusion)
if (facialTransformationMatrix) {
  const rollResult = detectFaceRollFromMatrix(facialTransformationMatrix); // 16-element column-major 4×4
  // { roll: number (degrees), tooTilted: boolean }
} else {
  const rollResult = detectFaceRoll(landmarks); // fallback: eye-corner landmarks, requires ≥364 points
  // { roll: number (degrees), tooTilted: boolean }
}

// Detect camera vertical angle from forehead/nose/chin landmark ratio
const angleResult = detectCameraAngle(landmarks); // requires ≥153 landmarks
// { ratio: number, cameraAbove: boolean, cameraBelow: boolean }
// ratio ~1.0 at eye level; >1.35 = camera above user; <0.75 = camera below user

// Analyse face-region dynamic range (flat lighting detection, MOV-1280)
// Pass pre-sampled face-region pixels — use sampleFaceRegionPixels() in @moveris/react
import { analyzeDynamicRange } from '@moveris/shared';
const drResult = analyzeDynamicRange(rgbaPixels);
// { range: number (max L* − min L*), isLow: boolean (< 60) }
// Soft warning — do not use as a hard capture gate
```

#### Crop Constants (aligned with cognito-check)

These constants control how face crops are generated for the `fast-check-crops` endpoint:

| Constant                         | Value | Description                                      |
| -------------------------------- | ----- | ------------------------------------------------ |
| `IDEAL_CROP_MULTIPLIER`          | 2.0   | Default crop region = 2x face size               |
| `MIN_CROP_MULTIPLIER`            | 1.8   | Minimum crop (face very close)                   |
| `MAX_CROP_MULTIPLIER`            | 2.5   | Maximum crop (face very far)                     |
| `FACE_CENTER_VERTICAL_OFFSET`    | 0.05  | Slight upward shift to include forehead for rPPG |
| `TARGET_FACE_PERCENTAGE_IN_CROP` | 0.5   | Face should occupy ~50% of 224x224 crop          |

---

### Model Configuration

```typescript
import { MODEL_CONFIGS, isDeprecatedModel, getActiveModels } from '@moveris/shared';

// Access model metadata
const config = MODEL_CONFIGS['mixed-10-v2'];
console.log(config.minFrames); // 10
console.log(config.deprecated); // false
console.log(config.aliases); // ['v2', 'latest']

// Check if a model is deprecated
if (isDeprecatedModel('mixed-10')) {
  const mc = MODEL_CONFIGS['mixed-10'];
  console.log(`Deprecated! Sunset: ${mc.sunsetDate}, replace with ${mc.replacement}`);
}

// Get only active (non-deprecated) models
const active = getActiveModels();
// ['mixed-10-v2', 'mixed-30-v2', ..., '10', '50', '250', 'hybrid-v2-10', ...]
```

---

## Configuration Constants

```typescript
import { DEFAULT_ENDPOINT, AUTH_CONFIG, RETRY_CONFIG, FRAME_CONFIG } from '@moveris/shared';

console.log(DEFAULT_ENDPOINT); // 'https://api.moveris.com'
console.log(AUTH_CONFIG.apiKeyHeader); // 'X-API-Key'
console.log(RETRY_CONFIG.maxAttempts); // 3
console.log(FRAME_CONFIG.maxBufferSize); // 10
```

---

## TypeScript Support

This package is written in TypeScript and provides full type definitions.

```typescript
import type {
  LivenessResult,
  LivenessState,
  LivenessConfig,
  FastCheckModel,
  ModelVersion,
  DeprecationInfo,
  ModelConfig,
  FrameSource,
  Verdict,
  CapturedFrame,
  CropData,
  LivenessClientConfig,
  DetectionResult,
  DetectionSummary,
  FaceBoundingBox,
  HeadPose,
  FaceLandmarkPoint,
  LandmarkValidationResult,
  EyeRegionQuality,
  EyeQualityThresholds,
  EyeRegionBounds,
  EyeRegionsBounds,
} from '@moveris/shared';
```

---

## License

MIT