@moveris/shared 3.8.1 → 3.9.0
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 +55 -16
- package/dist/index.d.mts +24 -7
- package/dist/index.d.ts +24 -7
- package/dist/index.js +44 -49
- package/dist/index.mjs +43 -44
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -56,6 +56,8 @@ const client = new LivenessClient(config: LivenessClientConfig);
|
|
|
56
56
|
| `enableRetry` | `boolean` | `true` | Enable automatic retry with exponential backoff |
|
|
57
57
|
| `customFetch` | `typeof fetch` | `fetch` | Custom fetch implementation (for React Native) |
|
|
58
58
|
| `deviceIntelligenceOverrides` | `DeviceIntelligenceOverrides` | - | Static overrides merged into every `device_intelligence` payload (e.g. `{ vpn_detected: true }`) |
|
|
59
|
+
| `consumer` | `ConsumerContext` | - | Consumer app context — traces which app and environment submitted each transaction |
|
|
60
|
+
| `trackClientTime` | `boolean` | `false` | When true, fires a fire-and-forget PATCH after each verdict to record end-to-end client duration |
|
|
59
61
|
|
|
60
62
|
#### Methods
|
|
61
63
|
|
|
@@ -152,6 +154,17 @@ const result = await client.hybrid50(frames, { fps: 30 });
|
|
|
152
154
|
const result = await client.hybrid150(frames, { fps: 30 });
|
|
153
155
|
```
|
|
154
156
|
|
|
157
|
+
##### `postClientTime(sessionId, clientTime)`
|
|
158
|
+
|
|
159
|
+
Fire-and-forget PATCH to record end-to-end client duration for a completed verification session. No-op when `trackClientTime` is `false`. Swallows all errors silently.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
// Called automatically by useLiveness / CognitoCheckWidget after the verdict.
|
|
163
|
+
// Approach B consumers can call it manually after onComplete resolves:
|
|
164
|
+
const elapsed = Math.round(performance.now() - startTime);
|
|
165
|
+
void client.postClientTime(result.sessionId, elapsed);
|
|
166
|
+
```
|
|
167
|
+
|
|
155
168
|
##### `updateDeviceIntelligenceOverrides(overrides)`
|
|
156
169
|
|
|
157
170
|
Merge additional fields into the cached device intelligence payload. Call this after construction to inject data only available at runtime (e.g. camera specs, VPN detection results). Merges shallowly — later calls are merged on top of earlier ones.
|
|
@@ -254,6 +267,27 @@ Deprecated models (v1 — sunset 2026-09-01):
|
|
|
254
267
|
| `'mixed-150'` | 150 | — |
|
|
255
268
|
| `'mixed-250'` | 250 | — |
|
|
256
269
|
|
|
270
|
+
#### ConsumerContext
|
|
271
|
+
|
|
272
|
+
Consumer app context attached to every verification request for transaction tracing.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
interface ConsumerContext {
|
|
276
|
+
url: string; // The consumer app's URL
|
|
277
|
+
env: 'development' | 'staging' | 'production'; // Consumer app environment
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Pass it at client construction — it is included in every subsequent request automatically:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
const client = new LivenessClient({
|
|
285
|
+
apiKey: 'mv_your_api_key',
|
|
286
|
+
consumer: { url: 'https://app.example.com', env: 'production' },
|
|
287
|
+
trackClientTime: true, // optional — fires postClientTime after each verdict
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
257
291
|
#### FrameSource
|
|
258
292
|
|
|
259
293
|
Source of the captured frames.
|
|
@@ -308,6 +342,7 @@ interface LivenessResult {
|
|
|
308
342
|
processingMs: number; // Server processing time
|
|
309
343
|
framesProcessed: number; // Number of frames analyzed
|
|
310
344
|
deprecation?: DeprecationInfo; // Present when X-Moveris-Model-Resolved header is returned
|
|
345
|
+
clientTime?: number; // End-to-end client duration in ms (start() to verdict received)
|
|
311
346
|
}
|
|
312
347
|
```
|
|
313
348
|
|
|
@@ -669,14 +704,13 @@ const inOval = isFaceInOval(faceBbox, ovalRegion);
|
|
|
669
704
|
console.log(inOval.isInOval); // true/false
|
|
670
705
|
console.log(inOval.alignmentScore); // 0-1
|
|
671
706
|
|
|
672
|
-
// Calculate crop region for face
|
|
707
|
+
// Calculate crop region for face — frame-relative 20% margin on each side,
|
|
708
|
+
// matches the API server-side crop and the model training pipeline.
|
|
673
709
|
const cropRegion = calculateFaceCropRegion(faceBbox, frameWidth, frameHeight);
|
|
674
|
-
// { x, y,
|
|
675
|
-
|
|
676
|
-
//
|
|
677
|
-
|
|
678
|
-
const multiplier = calculateAdaptiveCropMultiplier(faceBox, frameWidth, frameHeight);
|
|
679
|
-
// Returns 2.5–4.0x (matched to cognito-check for ~33% face coverage in 224×224 crop)
|
|
710
|
+
// { x, y, width, height } — possibly non-square rectangle in source pixel
|
|
711
|
+
// coordinates. Callers must resize the cropped pixels to 224×224 with
|
|
712
|
+
// bilinear interpolation; non-square crops are squashed (intentional, matches
|
|
713
|
+
// cv2.resize behaviour applied server-side).
|
|
680
714
|
|
|
681
715
|
// Detect face roll (device tilt) — prefer matrix when available (more accurate under glasses/occlusion)
|
|
682
716
|
if (facialTransformationMatrix) {
|
|
@@ -700,17 +734,21 @@ const drResult = analyzeDynamicRange(rgbaPixels);
|
|
|
700
734
|
// Soft warning — do not use as a hard capture gate
|
|
701
735
|
```
|
|
702
736
|
|
|
703
|
-
#### Crop Constants (aligned with
|
|
737
|
+
#### Crop Constants (aligned with the model contract)
|
|
738
|
+
|
|
739
|
+
These constants control how face crops are generated for the `fast-check-crops` endpoint. They match the algorithm applied server-side in `m-ai-check-api` and during model training:
|
|
704
740
|
|
|
705
|
-
|
|
741
|
+
1. Detect the face. Get the bounding box in source pixel coordinates.
|
|
742
|
+
2. Expand the bbox by `FACE_CROP_FRAME_MARGIN × frameWidth/Height` on each side.
|
|
743
|
+
3. Clamp the resulting rectangle to `[0, frameWidth] × [0, frameHeight]`. The crop is **not necessarily square**.
|
|
744
|
+
4. Resize the cropped pixels to exactly `FACE_CROP_OUTPUT_SIZE × FACE_CROP_OUTPUT_SIZE` with bilinear interpolation. Non-square crops are squashed (matches `cv2.resize` server-side).
|
|
706
745
|
|
|
707
|
-
| Constant | Value
|
|
708
|
-
| -------------------------------- |
|
|
709
|
-
| `
|
|
710
|
-
| `
|
|
711
|
-
| `
|
|
712
|
-
| `
|
|
713
|
-
| `TARGET_FACE_PERCENTAGE_IN_CROP` | 0.5 | Face should occupy ~50% of 224x224 crop |
|
|
746
|
+
| Constant | Value | Description |
|
|
747
|
+
| -------------------------------- | ------ | --------------------------------------------------------------------- |
|
|
748
|
+
| `FACE_CROP_FRAME_MARGIN` | `0.2` | Margin around the face bbox, as a fraction of frame W/H, on each side |
|
|
749
|
+
| `FACE_CROP_OUTPUT_SIZE` | `224` | Required output size for the cropped face image (px) |
|
|
750
|
+
| `MAX_FACE_PERCENTAGE_IN_CROP` | `0.45` | UX guard — nudge users back when the face dominates the crop |
|
|
751
|
+
| `TARGET_FACE_PERCENTAGE_IN_CROP` | `0.33` | UX-only target framing; not a model contract |
|
|
714
752
|
|
|
715
753
|
---
|
|
716
754
|
|
|
@@ -853,6 +891,7 @@ import type {
|
|
|
853
891
|
Verdict,
|
|
854
892
|
CapturedFrame,
|
|
855
893
|
CropData,
|
|
894
|
+
ConsumerContext,
|
|
856
895
|
LivenessClientConfig,
|
|
857
896
|
DetectionResult,
|
|
858
897
|
DetectionSummary,
|
package/dist/index.d.mts
CHANGED
|
@@ -26,6 +26,10 @@ type DeviceIntelligenceOverrides = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
type Verdict = 'live' | 'fake' | 'inconclusive';
|
|
29
|
+
interface ConsumerContext {
|
|
30
|
+
url: string;
|
|
31
|
+
env: 'development' | 'staging' | 'production';
|
|
32
|
+
}
|
|
29
33
|
interface ModelEntry {
|
|
30
34
|
id: string;
|
|
31
35
|
label: string;
|
|
@@ -68,6 +72,9 @@ interface FastCheckRequest {
|
|
|
68
72
|
frame_count?: number;
|
|
69
73
|
warnings?: string[];
|
|
70
74
|
device_intelligence?: DeviceIntelligence;
|
|
75
|
+
metadata?: {
|
|
76
|
+
consumer?: ConsumerContext;
|
|
77
|
+
} | null;
|
|
71
78
|
}
|
|
72
79
|
interface FastCheckCropsRequest {
|
|
73
80
|
session_id: string;
|
|
@@ -76,6 +83,9 @@ interface FastCheckCropsRequest {
|
|
|
76
83
|
crops: CropData[];
|
|
77
84
|
frame_count?: number;
|
|
78
85
|
device_intelligence?: DeviceIntelligence;
|
|
86
|
+
metadata?: {
|
|
87
|
+
consumer?: ConsumerContext;
|
|
88
|
+
} | null;
|
|
79
89
|
}
|
|
80
90
|
interface VerifyRequest {
|
|
81
91
|
session_id: string;
|
|
@@ -108,6 +118,9 @@ interface FastCheckStreamRequest {
|
|
|
108
118
|
frame_count?: number;
|
|
109
119
|
warnings?: string[];
|
|
110
120
|
device_intelligence?: DeviceIntelligence;
|
|
121
|
+
metadata?: {
|
|
122
|
+
consumer?: ConsumerContext;
|
|
123
|
+
} | null;
|
|
111
124
|
}
|
|
112
125
|
interface FastCheckResponse {
|
|
113
126
|
verdict: Verdict | null;
|
|
@@ -208,6 +221,7 @@ interface LivenessResult {
|
|
|
208
221
|
framesProcessed: number;
|
|
209
222
|
warnings?: string[];
|
|
210
223
|
deprecation?: DeprecationInfo;
|
|
224
|
+
clientTime?: number;
|
|
211
225
|
}
|
|
212
226
|
type LivenessState = 'idle' | 'capturing' | 'uploading' | 'processing' | 'complete' | 'error';
|
|
213
227
|
interface LivenessConfig {
|
|
@@ -362,10 +376,7 @@ declare const MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
362
376
|
declare const MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
363
377
|
declare const HIGH_ALIGNMENT = 0.85;
|
|
364
378
|
declare const GOOD_ALIGNMENT = 0.5;
|
|
365
|
-
declare const
|
|
366
|
-
declare const MIN_CROP_MULTIPLIER = 2.5;
|
|
367
|
-
declare const MAX_CROP_MULTIPLIER = 4;
|
|
368
|
-
declare const FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
379
|
+
declare const FACE_CROP_FRAME_MARGIN = 0.2;
|
|
369
380
|
declare const MIN_IDEAL_FACE_RATIO = 0.05;
|
|
370
381
|
declare const MAX_IDEAL_FACE_RATIO = 0.2;
|
|
371
382
|
declare const MIN_FACE_RATIO = 0.036;
|
|
@@ -392,12 +403,12 @@ declare const OVAL_REGION_MOBILE: OvalRegion;
|
|
|
392
403
|
declare const DEFAULT_OVAL_REGION: OvalRegion;
|
|
393
404
|
declare function isFaceInOval(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number, oval?: OvalRegion, tolerance?: number): FaceInOvalResult;
|
|
394
405
|
declare function calculateFaceAlignment(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceAlignmentResult;
|
|
395
|
-
declare function calculateAdaptiveCropMultiplier(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): number;
|
|
396
406
|
declare function isFaceCropFullyInFrame(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): boolean;
|
|
397
407
|
declare function calculateFaceCropRegion(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): {
|
|
398
408
|
x: number;
|
|
399
409
|
y: number;
|
|
400
|
-
|
|
410
|
+
width: number;
|
|
411
|
+
height: number;
|
|
401
412
|
};
|
|
402
413
|
declare function checkFrameQuality(options: {
|
|
403
414
|
faceBox?: FaceBoundingBox;
|
|
@@ -570,6 +581,8 @@ interface LivenessClientConfig {
|
|
|
570
581
|
enableRetry?: boolean;
|
|
571
582
|
customFetch?: typeof fetch;
|
|
572
583
|
deviceIntelligenceOverrides?: DeviceIntelligenceOverrides;
|
|
584
|
+
consumer?: ConsumerContext;
|
|
585
|
+
trackClientTime?: boolean;
|
|
573
586
|
}
|
|
574
587
|
declare class LivenessApiError extends Error {
|
|
575
588
|
readonly code: string;
|
|
@@ -590,6 +603,8 @@ declare class LivenessClient {
|
|
|
590
603
|
private readonly timeout;
|
|
591
604
|
private readonly enableRetry;
|
|
592
605
|
private readonly fetchFn;
|
|
606
|
+
private readonly consumer;
|
|
607
|
+
private readonly trackClientTime;
|
|
593
608
|
private diCollected;
|
|
594
609
|
private diCollecting;
|
|
595
610
|
private diOverrides;
|
|
@@ -610,6 +625,7 @@ declare class LivenessClient {
|
|
|
610
625
|
health(): Promise<HealthResponse>;
|
|
611
626
|
getModels(): Promise<ModelEntry[]>;
|
|
612
627
|
queueStats(): Promise<QueueStatsResponse>;
|
|
628
|
+
postClientTime(sessionId: string, clientTime: number): Promise<void>;
|
|
613
629
|
fastCheck(frames: CapturedFrame[], options?: {
|
|
614
630
|
sessionId?: string;
|
|
615
631
|
model?: FastCheckModel;
|
|
@@ -731,6 +747,7 @@ declare const API_PATHS: {
|
|
|
731
747
|
readonly hybrid150: "/api/v1/hybrid-150";
|
|
732
748
|
readonly jobResult: "/api/v1/result";
|
|
733
749
|
readonly queueStats: "/api/v1/queue/stats";
|
|
750
|
+
readonly sessions: "/api/v1/sessions";
|
|
734
751
|
};
|
|
735
752
|
declare const RETRY_CONFIG: {
|
|
736
753
|
readonly maxAttempts: 3;
|
|
@@ -945,4 +962,4 @@ declare function collectDeviceIntelligence(opts?: {
|
|
|
945
962
|
platformVersion?: string;
|
|
946
963
|
}): Promise<DeviceIntelligence | null>;
|
|
947
964
|
|
|
948
|
-
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds,
|
|
965
|
+
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type ConsumerContext, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CROP_FRAME_MARGIN, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceRollResult, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_FACE_AREA_RATIO, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_IDEAL_FACE_RATIO, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelVersion, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, OVAL_REGION_DESKTOP, OVAL_REGION_MOBILE, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ type DeviceIntelligenceOverrides = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
type Verdict = 'live' | 'fake' | 'inconclusive';
|
|
29
|
+
interface ConsumerContext {
|
|
30
|
+
url: string;
|
|
31
|
+
env: 'development' | 'staging' | 'production';
|
|
32
|
+
}
|
|
29
33
|
interface ModelEntry {
|
|
30
34
|
id: string;
|
|
31
35
|
label: string;
|
|
@@ -68,6 +72,9 @@ interface FastCheckRequest {
|
|
|
68
72
|
frame_count?: number;
|
|
69
73
|
warnings?: string[];
|
|
70
74
|
device_intelligence?: DeviceIntelligence;
|
|
75
|
+
metadata?: {
|
|
76
|
+
consumer?: ConsumerContext;
|
|
77
|
+
} | null;
|
|
71
78
|
}
|
|
72
79
|
interface FastCheckCropsRequest {
|
|
73
80
|
session_id: string;
|
|
@@ -76,6 +83,9 @@ interface FastCheckCropsRequest {
|
|
|
76
83
|
crops: CropData[];
|
|
77
84
|
frame_count?: number;
|
|
78
85
|
device_intelligence?: DeviceIntelligence;
|
|
86
|
+
metadata?: {
|
|
87
|
+
consumer?: ConsumerContext;
|
|
88
|
+
} | null;
|
|
79
89
|
}
|
|
80
90
|
interface VerifyRequest {
|
|
81
91
|
session_id: string;
|
|
@@ -108,6 +118,9 @@ interface FastCheckStreamRequest {
|
|
|
108
118
|
frame_count?: number;
|
|
109
119
|
warnings?: string[];
|
|
110
120
|
device_intelligence?: DeviceIntelligence;
|
|
121
|
+
metadata?: {
|
|
122
|
+
consumer?: ConsumerContext;
|
|
123
|
+
} | null;
|
|
111
124
|
}
|
|
112
125
|
interface FastCheckResponse {
|
|
113
126
|
verdict: Verdict | null;
|
|
@@ -208,6 +221,7 @@ interface LivenessResult {
|
|
|
208
221
|
framesProcessed: number;
|
|
209
222
|
warnings?: string[];
|
|
210
223
|
deprecation?: DeprecationInfo;
|
|
224
|
+
clientTime?: number;
|
|
211
225
|
}
|
|
212
226
|
type LivenessState = 'idle' | 'capturing' | 'uploading' | 'processing' | 'complete' | 'error';
|
|
213
227
|
interface LivenessConfig {
|
|
@@ -362,10 +376,7 @@ declare const MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
362
376
|
declare const MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
363
377
|
declare const HIGH_ALIGNMENT = 0.85;
|
|
364
378
|
declare const GOOD_ALIGNMENT = 0.5;
|
|
365
|
-
declare const
|
|
366
|
-
declare const MIN_CROP_MULTIPLIER = 2.5;
|
|
367
|
-
declare const MAX_CROP_MULTIPLIER = 4;
|
|
368
|
-
declare const FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
379
|
+
declare const FACE_CROP_FRAME_MARGIN = 0.2;
|
|
369
380
|
declare const MIN_IDEAL_FACE_RATIO = 0.05;
|
|
370
381
|
declare const MAX_IDEAL_FACE_RATIO = 0.2;
|
|
371
382
|
declare const MIN_FACE_RATIO = 0.036;
|
|
@@ -392,12 +403,12 @@ declare const OVAL_REGION_MOBILE: OvalRegion;
|
|
|
392
403
|
declare const DEFAULT_OVAL_REGION: OvalRegion;
|
|
393
404
|
declare function isFaceInOval(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number, oval?: OvalRegion, tolerance?: number): FaceInOvalResult;
|
|
394
405
|
declare function calculateFaceAlignment(boundingBox: FaceBoundingBox, frameWidth: number, frameHeight: number): FaceAlignmentResult;
|
|
395
|
-
declare function calculateAdaptiveCropMultiplier(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): number;
|
|
396
406
|
declare function isFaceCropFullyInFrame(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): boolean;
|
|
397
407
|
declare function calculateFaceCropRegion(faceBox: FaceBoundingBox, frameWidth: number, frameHeight: number): {
|
|
398
408
|
x: number;
|
|
399
409
|
y: number;
|
|
400
|
-
|
|
410
|
+
width: number;
|
|
411
|
+
height: number;
|
|
401
412
|
};
|
|
402
413
|
declare function checkFrameQuality(options: {
|
|
403
414
|
faceBox?: FaceBoundingBox;
|
|
@@ -570,6 +581,8 @@ interface LivenessClientConfig {
|
|
|
570
581
|
enableRetry?: boolean;
|
|
571
582
|
customFetch?: typeof fetch;
|
|
572
583
|
deviceIntelligenceOverrides?: DeviceIntelligenceOverrides;
|
|
584
|
+
consumer?: ConsumerContext;
|
|
585
|
+
trackClientTime?: boolean;
|
|
573
586
|
}
|
|
574
587
|
declare class LivenessApiError extends Error {
|
|
575
588
|
readonly code: string;
|
|
@@ -590,6 +603,8 @@ declare class LivenessClient {
|
|
|
590
603
|
private readonly timeout;
|
|
591
604
|
private readonly enableRetry;
|
|
592
605
|
private readonly fetchFn;
|
|
606
|
+
private readonly consumer;
|
|
607
|
+
private readonly trackClientTime;
|
|
593
608
|
private diCollected;
|
|
594
609
|
private diCollecting;
|
|
595
610
|
private diOverrides;
|
|
@@ -610,6 +625,7 @@ declare class LivenessClient {
|
|
|
610
625
|
health(): Promise<HealthResponse>;
|
|
611
626
|
getModels(): Promise<ModelEntry[]>;
|
|
612
627
|
queueStats(): Promise<QueueStatsResponse>;
|
|
628
|
+
postClientTime(sessionId: string, clientTime: number): Promise<void>;
|
|
613
629
|
fastCheck(frames: CapturedFrame[], options?: {
|
|
614
630
|
sessionId?: string;
|
|
615
631
|
model?: FastCheckModel;
|
|
@@ -731,6 +747,7 @@ declare const API_PATHS: {
|
|
|
731
747
|
readonly hybrid150: "/api/v1/hybrid-150";
|
|
732
748
|
readonly jobResult: "/api/v1/result";
|
|
733
749
|
readonly queueStats: "/api/v1/queue/stats";
|
|
750
|
+
readonly sessions: "/api/v1/sessions";
|
|
734
751
|
};
|
|
735
752
|
declare const RETRY_CONFIG: {
|
|
736
753
|
readonly maxAttempts: 3;
|
|
@@ -945,4 +962,4 @@ declare function collectDeviceIntelligence(opts?: {
|
|
|
945
962
|
platformVersion?: string;
|
|
946
963
|
}): Promise<DeviceIntelligence | null>;
|
|
947
964
|
|
|
948
|
-
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds,
|
|
965
|
+
export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type ConsumerContext, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CROP_FRAME_MARGIN, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FRAME_BUFFER_CONFIG, FRAME_CONFIG, type FaceAlignmentResult, type FaceBoundingBox, type FaceDetectionTiers, type FaceInOvalResult, type FaceLandmarkPoint, type FaceRollResult, type FaceVisibilityResult, type FastCheckCropsRequest, type FastCheckModel, type FastCheckRequest, type FastCheckResponse, type FastCheckStreamRequest, type FastCheckStreamResponse, type FeedbackLocale, type FeedbackMessageKey, type Frame, FrameBuffer, type FrameData, type FrameQualityResult, FrameQueue, type FrameSource, GOOD_ALIGNMENT, type GazeThresholds, HIGH_ALIGNMENT, HYBRID_MODEL_CONFIGS, type HandOcclusionConfig, type HeadPose, type HealthResponse, type Hybrid150CheckRequest, type Hybrid50CheckRequest, type HybridCheckRequest, type HybridCheckResponse, type HybridFrameData, type HybridModelConfig, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_FACE_AREA_RATIO, MIN_FACE_BOTTOM_MARGIN, MIN_FACE_RATIO, MIN_FACE_SIDE_MARGIN, MIN_FACE_TOP_MARGIN, MIN_IDEAL_FACE_RATIO, MIN_LANDMARK_COUNT, MODEL_CONFIGS, type ModelConfig, type ModelEntry, type ModelType, type ModelVersion, type ModelsResponse, OVAL_GUIDE_COLORS, OVAL_GUIDE_STYLES, OVAL_REGION_DESKTOP, OVAL_REGION_MOBILE, type OnErrorCallback, type OnFrameCapturedCallback, type OnProgressCallback, type OnResultCallback, type OnStateChangeCallback, type OvalGuideState, type OvalRegion, type QueueStatsResponse, RETRY_CONFIG, type RetryOptions, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.js
CHANGED
|
@@ -50,7 +50,7 @@ __export(index_exports, {
|
|
|
50
50
|
ES_LOCALE: () => ES_LOCALE,
|
|
51
51
|
EYE_LANDMARK_INDICES: () => EYE_LANDMARK_INDICES,
|
|
52
52
|
EYE_QUALITY_THRESHOLDS: () => EYE_QUALITY_THRESHOLDS,
|
|
53
|
-
|
|
53
|
+
FACE_CROP_FRAME_MARGIN: () => FACE_CROP_FRAME_MARGIN,
|
|
54
54
|
FACE_CROP_OUTPUT_SIZE: () => FACE_CROP_OUTPUT_SIZE,
|
|
55
55
|
FEEDBACK_MESSAGES: () => FEEDBACK_MESSAGES,
|
|
56
56
|
FRAME_BUFFER_CONFIG: () => FRAME_BUFFER_CONFIG,
|
|
@@ -60,20 +60,17 @@ __export(index_exports, {
|
|
|
60
60
|
GOOD_ALIGNMENT: () => GOOD_ALIGNMENT,
|
|
61
61
|
HIGH_ALIGNMENT: () => HIGH_ALIGNMENT,
|
|
62
62
|
HYBRID_MODEL_CONFIGS: () => HYBRID_MODEL_CONFIGS,
|
|
63
|
-
IDEAL_CROP_MULTIPLIER: () => IDEAL_CROP_MULTIPLIER,
|
|
64
63
|
LANDMARK_INDEX: () => LANDMARK_INDEX,
|
|
65
64
|
LANDMARK_MAX_BOUND: () => LANDMARK_MAX_BOUND,
|
|
66
65
|
LANDMARK_MIN_BOUND: () => LANDMARK_MIN_BOUND,
|
|
67
66
|
LOW_LIGHT_THRESHOLD: () => LOW_LIGHT_THRESHOLD,
|
|
68
67
|
LivenessApiError: () => LivenessApiError,
|
|
69
68
|
LivenessClient: () => LivenessClient,
|
|
70
|
-
MAX_CROP_MULTIPLIER: () => MAX_CROP_MULTIPLIER,
|
|
71
69
|
MAX_FACE_PERCENTAGE_IN_CROP: () => MAX_FACE_PERCENTAGE_IN_CROP,
|
|
72
70
|
MAX_FACE_RATIO: () => MAX_FACE_RATIO,
|
|
73
71
|
MAX_FACE_ROLL_DEGREES: () => MAX_FACE_ROLL_DEGREES,
|
|
74
72
|
MAX_IDEAL_FACE_RATIO: () => MAX_IDEAL_FACE_RATIO,
|
|
75
73
|
MIN_CAPTURE_ALIGNMENT: () => MIN_CAPTURE_ALIGNMENT,
|
|
76
|
-
MIN_CROP_MULTIPLIER: () => MIN_CROP_MULTIPLIER,
|
|
77
74
|
MIN_FACE_AREA_RATIO: () => MIN_FACE_AREA_RATIO,
|
|
78
75
|
MIN_FACE_BOTTOM_MARGIN: () => MIN_FACE_BOTTOM_MARGIN,
|
|
79
76
|
MIN_FACE_RATIO: () => MIN_FACE_RATIO,
|
|
@@ -95,7 +92,6 @@ __export(index_exports, {
|
|
|
95
92
|
analyzeEyeRegionBrightness: () => analyzeEyeRegionBrightness,
|
|
96
93
|
analyzeEyeRegionContrast: () => analyzeEyeRegionContrast,
|
|
97
94
|
analyzeLighting: () => analyzeLighting,
|
|
98
|
-
calculateAdaptiveCropMultiplier: () => calculateAdaptiveCropMultiplier,
|
|
99
95
|
calculateBrightness: () => calculateBrightness,
|
|
100
96
|
calculateFaceAlignment: () => calculateFaceAlignment,
|
|
101
97
|
calculateFaceCropRegion: () => calculateFaceCropRegion,
|
|
@@ -162,7 +158,8 @@ var API_PATHS = {
|
|
|
162
158
|
hybrid50: "/api/v1/hybrid-50",
|
|
163
159
|
hybrid150: "/api/v1/hybrid-150",
|
|
164
160
|
jobResult: "/api/v1/result",
|
|
165
|
-
queueStats: "/api/v1/queue/stats"
|
|
161
|
+
queueStats: "/api/v1/queue/stats",
|
|
162
|
+
sessions: "/api/v1/sessions"
|
|
166
163
|
};
|
|
167
164
|
var RETRY_CONFIG = {
|
|
168
165
|
maxAttempts: 3,
|
|
@@ -242,7 +239,7 @@ async function sleep(ms) {
|
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
// package.json
|
|
245
|
-
var version = "3.
|
|
242
|
+
var version = "3.9.0";
|
|
246
243
|
|
|
247
244
|
// src/utils/deviceIntelligence.ts
|
|
248
245
|
var IPINFO_URL = "https://ipinfo.io/json";
|
|
@@ -390,6 +387,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
390
387
|
this.enableRetry = config.enableRetry ?? true;
|
|
391
388
|
this.fetchFn = config.customFetch ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
|
|
392
389
|
this.diOverrides = { ...config.deviceIntelligenceOverrides };
|
|
390
|
+
this.consumer = config.consumer;
|
|
391
|
+
this.trackClientTime = config.trackClientTime ?? false;
|
|
393
392
|
}
|
|
394
393
|
/**
|
|
395
394
|
* Merge additional device intelligence overrides into the existing set.
|
|
@@ -638,6 +637,25 @@ var LivenessClient = class _LivenessClient {
|
|
|
638
637
|
async queueStats() {
|
|
639
638
|
return this.request(API_PATHS.queueStats);
|
|
640
639
|
}
|
|
640
|
+
/**
|
|
641
|
+
* Post end-to-end client duration for a completed verification session.
|
|
642
|
+
* Fire-and-forget — swallows all errors, no retry.
|
|
643
|
+
* No-op when `trackClientTime` is false (default).
|
|
644
|
+
*/
|
|
645
|
+
async postClientTime(sessionId, clientTime) {
|
|
646
|
+
if (!this.trackClientTime) return;
|
|
647
|
+
try {
|
|
648
|
+
await this.fetchFn(`${this.baseUrl}${API_PATHS.sessions}/${sessionId}/client-time`, {
|
|
649
|
+
method: "PATCH",
|
|
650
|
+
headers: {
|
|
651
|
+
"Content-Type": "application/json",
|
|
652
|
+
[AUTH_CONFIG.apiKeyHeader]: this.apiKey
|
|
653
|
+
},
|
|
654
|
+
body: JSON.stringify({ client_time: clientTime })
|
|
655
|
+
});
|
|
656
|
+
} catch {
|
|
657
|
+
}
|
|
658
|
+
}
|
|
641
659
|
// ===========================================================================
|
|
642
660
|
// Fast Check Endpoints
|
|
643
661
|
// ===========================================================================
|
|
@@ -658,7 +676,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
658
676
|
frames: toFrameData(frames),
|
|
659
677
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
660
678
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
661
|
-
...di ? { device_intelligence: di } : {}
|
|
679
|
+
...di ? { device_intelligence: di } : {},
|
|
680
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
662
681
|
};
|
|
663
682
|
const { data: response, headers } = await this.requestWithRetryRaw(
|
|
664
683
|
API_PATHS.fastCheck,
|
|
@@ -695,7 +714,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
695
714
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
696
715
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
697
716
|
...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {},
|
|
698
|
-
...di ? { device_intelligence: di } : {}
|
|
717
|
+
...di ? { device_intelligence: di } : {},
|
|
718
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
699
719
|
};
|
|
700
720
|
const { data: response, headers } = await this.requestWithRetryRaw(
|
|
701
721
|
API_PATHS.fastCheckCrops,
|
|
@@ -759,7 +779,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
759
779
|
frame: frameData,
|
|
760
780
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
761
781
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
762
|
-
...di ? { device_intelligence: di } : {}
|
|
782
|
+
...di ? { device_intelligence: di } : {},
|
|
783
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
763
784
|
};
|
|
764
785
|
return this.requestWithRetry(API_PATHS.fastCheckStream, {
|
|
765
786
|
method: "POST",
|
|
@@ -1786,10 +1807,7 @@ var MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
1786
1807
|
var MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
1787
1808
|
var HIGH_ALIGNMENT = 0.85;
|
|
1788
1809
|
var GOOD_ALIGNMENT = 0.5;
|
|
1789
|
-
var
|
|
1790
|
-
var MIN_CROP_MULTIPLIER = 2.5;
|
|
1791
|
-
var MAX_CROP_MULTIPLIER = 4;
|
|
1792
|
-
var FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
1810
|
+
var FACE_CROP_FRAME_MARGIN = 0.2;
|
|
1793
1811
|
var MIN_IDEAL_FACE_RATIO = 0.05;
|
|
1794
1812
|
var MAX_IDEAL_FACE_RATIO = 0.2;
|
|
1795
1813
|
var MIN_FACE_RATIO = 0.036;
|
|
@@ -1983,40 +2001,21 @@ function calculateFaceAlignment(boundingBox, frameWidth, frameHeight) {
|
|
|
1983
2001
|
tooFar: faceRatio < MIN_FACE_RATIO
|
|
1984
2002
|
};
|
|
1985
2003
|
}
|
|
1986
|
-
function calculateAdaptiveCropMultiplier(faceBox, frameWidth, frameHeight) {
|
|
1987
|
-
const faceSize = Math.max(faceBox.width, faceBox.height);
|
|
1988
|
-
const idealCropSize = faceSize * IDEAL_CROP_MULTIPLIER;
|
|
1989
|
-
const maxCropSize = Math.min(frameWidth, frameHeight);
|
|
1990
|
-
if (idealCropSize <= maxCropSize) {
|
|
1991
|
-
return IDEAL_CROP_MULTIPLIER;
|
|
1992
|
-
}
|
|
1993
|
-
const adaptiveMultiplier = maxCropSize / faceSize;
|
|
1994
|
-
return Math.max(MIN_CROP_MULTIPLIER, Math.min(MAX_CROP_MULTIPLIER, adaptiveMultiplier));
|
|
1995
|
-
}
|
|
1996
2004
|
function isFaceCropFullyInFrame(faceBox, frameWidth, frameHeight) {
|
|
1997
|
-
const
|
|
1998
|
-
const
|
|
1999
|
-
const
|
|
2000
|
-
const
|
|
2001
|
-
const faceTop = faceBox.originY - marginY;
|
|
2002
|
-
const faceRight = faceBox.originX + faceBox.width + marginX;
|
|
2003
|
-
const faceBottom = faceBox.originY + faceBox.height + marginY;
|
|
2005
|
+
const faceLeft = faceBox.originX;
|
|
2006
|
+
const faceTop = faceBox.originY;
|
|
2007
|
+
const faceRight = faceBox.originX + faceBox.width;
|
|
2008
|
+
const faceBottom = faceBox.originY + faceBox.height;
|
|
2004
2009
|
return faceLeft >= 0 && faceTop >= 0 && faceRight <= frameWidth && faceBottom <= frameHeight;
|
|
2005
2010
|
}
|
|
2006
2011
|
function calculateFaceCropRegion(faceBox, frameWidth, frameHeight) {
|
|
2007
|
-
const
|
|
2008
|
-
const
|
|
2009
|
-
const
|
|
2010
|
-
const
|
|
2011
|
-
const
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
let cropX = centerX - cropSize / 2;
|
|
2015
|
-
let cropY = centerY - cropSize / 2;
|
|
2016
|
-
cropX = Math.max(0, Math.min(frameWidth - cropSize, cropX));
|
|
2017
|
-
cropY = Math.max(0, Math.min(frameHeight - cropSize, cropY));
|
|
2018
|
-
const finalSize = Math.min(cropSize, frameWidth - cropX, frameHeight - cropY);
|
|
2019
|
-
return { x: cropX, y: cropY, size: finalSize };
|
|
2012
|
+
const marginX = FACE_CROP_FRAME_MARGIN * frameWidth;
|
|
2013
|
+
const marginY = FACE_CROP_FRAME_MARGIN * frameHeight;
|
|
2014
|
+
const x1 = Math.max(0, faceBox.originX - marginX);
|
|
2015
|
+
const y1 = Math.max(0, faceBox.originY - marginY);
|
|
2016
|
+
const x2 = Math.min(frameWidth, faceBox.originX + faceBox.width + marginX);
|
|
2017
|
+
const y2 = Math.min(frameHeight, faceBox.originY + faceBox.height + marginY);
|
|
2018
|
+
return { x: x1, y: y1, width: x2 - x1, height: y2 - y1 };
|
|
2020
2019
|
}
|
|
2021
2020
|
function checkFrameQuality(options) {
|
|
2022
2021
|
const {
|
|
@@ -2380,7 +2379,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2380
2379
|
ES_LOCALE,
|
|
2381
2380
|
EYE_LANDMARK_INDICES,
|
|
2382
2381
|
EYE_QUALITY_THRESHOLDS,
|
|
2383
|
-
|
|
2382
|
+
FACE_CROP_FRAME_MARGIN,
|
|
2384
2383
|
FACE_CROP_OUTPUT_SIZE,
|
|
2385
2384
|
FEEDBACK_MESSAGES,
|
|
2386
2385
|
FRAME_BUFFER_CONFIG,
|
|
@@ -2390,20 +2389,17 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2390
2389
|
GOOD_ALIGNMENT,
|
|
2391
2390
|
HIGH_ALIGNMENT,
|
|
2392
2391
|
HYBRID_MODEL_CONFIGS,
|
|
2393
|
-
IDEAL_CROP_MULTIPLIER,
|
|
2394
2392
|
LANDMARK_INDEX,
|
|
2395
2393
|
LANDMARK_MAX_BOUND,
|
|
2396
2394
|
LANDMARK_MIN_BOUND,
|
|
2397
2395
|
LOW_LIGHT_THRESHOLD,
|
|
2398
2396
|
LivenessApiError,
|
|
2399
2397
|
LivenessClient,
|
|
2400
|
-
MAX_CROP_MULTIPLIER,
|
|
2401
2398
|
MAX_FACE_PERCENTAGE_IN_CROP,
|
|
2402
2399
|
MAX_FACE_RATIO,
|
|
2403
2400
|
MAX_FACE_ROLL_DEGREES,
|
|
2404
2401
|
MAX_IDEAL_FACE_RATIO,
|
|
2405
2402
|
MIN_CAPTURE_ALIGNMENT,
|
|
2406
|
-
MIN_CROP_MULTIPLIER,
|
|
2407
2403
|
MIN_FACE_AREA_RATIO,
|
|
2408
2404
|
MIN_FACE_BOTTOM_MARGIN,
|
|
2409
2405
|
MIN_FACE_RATIO,
|
|
@@ -2425,7 +2421,6 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2425
2421
|
analyzeEyeRegionBrightness,
|
|
2426
2422
|
analyzeEyeRegionContrast,
|
|
2427
2423
|
analyzeLighting,
|
|
2428
|
-
calculateAdaptiveCropMultiplier,
|
|
2429
2424
|
calculateBrightness,
|
|
2430
2425
|
calculateFaceAlignment,
|
|
2431
2426
|
calculateFaceCropRegion,
|
package/dist/index.mjs
CHANGED
|
@@ -16,7 +16,8 @@ var API_PATHS = {
|
|
|
16
16
|
hybrid50: "/api/v1/hybrid-50",
|
|
17
17
|
hybrid150: "/api/v1/hybrid-150",
|
|
18
18
|
jobResult: "/api/v1/result",
|
|
19
|
-
queueStats: "/api/v1/queue/stats"
|
|
19
|
+
queueStats: "/api/v1/queue/stats",
|
|
20
|
+
sessions: "/api/v1/sessions"
|
|
20
21
|
};
|
|
21
22
|
var RETRY_CONFIG = {
|
|
22
23
|
maxAttempts: 3,
|
|
@@ -96,7 +97,7 @@ async function sleep(ms) {
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
// package.json
|
|
99
|
-
var version = "3.
|
|
100
|
+
var version = "3.9.0";
|
|
100
101
|
|
|
101
102
|
// src/utils/deviceIntelligence.ts
|
|
102
103
|
var IPINFO_URL = "https://ipinfo.io/json";
|
|
@@ -244,6 +245,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
244
245
|
this.enableRetry = config.enableRetry ?? true;
|
|
245
246
|
this.fetchFn = config.customFetch ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
|
|
246
247
|
this.diOverrides = { ...config.deviceIntelligenceOverrides };
|
|
248
|
+
this.consumer = config.consumer;
|
|
249
|
+
this.trackClientTime = config.trackClientTime ?? false;
|
|
247
250
|
}
|
|
248
251
|
/**
|
|
249
252
|
* Merge additional device intelligence overrides into the existing set.
|
|
@@ -492,6 +495,25 @@ var LivenessClient = class _LivenessClient {
|
|
|
492
495
|
async queueStats() {
|
|
493
496
|
return this.request(API_PATHS.queueStats);
|
|
494
497
|
}
|
|
498
|
+
/**
|
|
499
|
+
* Post end-to-end client duration for a completed verification session.
|
|
500
|
+
* Fire-and-forget — swallows all errors, no retry.
|
|
501
|
+
* No-op when `trackClientTime` is false (default).
|
|
502
|
+
*/
|
|
503
|
+
async postClientTime(sessionId, clientTime) {
|
|
504
|
+
if (!this.trackClientTime) return;
|
|
505
|
+
try {
|
|
506
|
+
await this.fetchFn(`${this.baseUrl}${API_PATHS.sessions}/${sessionId}/client-time`, {
|
|
507
|
+
method: "PATCH",
|
|
508
|
+
headers: {
|
|
509
|
+
"Content-Type": "application/json",
|
|
510
|
+
[AUTH_CONFIG.apiKeyHeader]: this.apiKey
|
|
511
|
+
},
|
|
512
|
+
body: JSON.stringify({ client_time: clientTime })
|
|
513
|
+
});
|
|
514
|
+
} catch {
|
|
515
|
+
}
|
|
516
|
+
}
|
|
495
517
|
// ===========================================================================
|
|
496
518
|
// Fast Check Endpoints
|
|
497
519
|
// ===========================================================================
|
|
@@ -512,7 +534,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
512
534
|
frames: toFrameData(frames),
|
|
513
535
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
514
536
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
515
|
-
...di ? { device_intelligence: di } : {}
|
|
537
|
+
...di ? { device_intelligence: di } : {},
|
|
538
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
516
539
|
};
|
|
517
540
|
const { data: response, headers } = await this.requestWithRetryRaw(
|
|
518
541
|
API_PATHS.fastCheck,
|
|
@@ -549,7 +572,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
549
572
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
550
573
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
551
574
|
...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {},
|
|
552
|
-
...di ? { device_intelligence: di } : {}
|
|
575
|
+
...di ? { device_intelligence: di } : {},
|
|
576
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
553
577
|
};
|
|
554
578
|
const { data: response, headers } = await this.requestWithRetryRaw(
|
|
555
579
|
API_PATHS.fastCheckCrops,
|
|
@@ -613,7 +637,8 @@ var LivenessClient = class _LivenessClient {
|
|
|
613
637
|
frame: frameData,
|
|
614
638
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
615
639
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
616
|
-
...di ? { device_intelligence: di } : {}
|
|
640
|
+
...di ? { device_intelligence: di } : {},
|
|
641
|
+
...this.consumer ? { metadata: { consumer: this.consumer } } : {}
|
|
617
642
|
};
|
|
618
643
|
return this.requestWithRetry(API_PATHS.fastCheckStream, {
|
|
619
644
|
method: "POST",
|
|
@@ -1640,10 +1665,7 @@ var MIN_FACE_SIDE_MARGIN = 0.05;
|
|
|
1640
1665
|
var MIN_CAPTURE_ALIGNMENT = 0.6;
|
|
1641
1666
|
var HIGH_ALIGNMENT = 0.85;
|
|
1642
1667
|
var GOOD_ALIGNMENT = 0.5;
|
|
1643
|
-
var
|
|
1644
|
-
var MIN_CROP_MULTIPLIER = 2.5;
|
|
1645
|
-
var MAX_CROP_MULTIPLIER = 4;
|
|
1646
|
-
var FACE_CENTER_VERTICAL_OFFSET = 0.05;
|
|
1668
|
+
var FACE_CROP_FRAME_MARGIN = 0.2;
|
|
1647
1669
|
var MIN_IDEAL_FACE_RATIO = 0.05;
|
|
1648
1670
|
var MAX_IDEAL_FACE_RATIO = 0.2;
|
|
1649
1671
|
var MIN_FACE_RATIO = 0.036;
|
|
@@ -1837,40 +1859,21 @@ function calculateFaceAlignment(boundingBox, frameWidth, frameHeight) {
|
|
|
1837
1859
|
tooFar: faceRatio < MIN_FACE_RATIO
|
|
1838
1860
|
};
|
|
1839
1861
|
}
|
|
1840
|
-
function calculateAdaptiveCropMultiplier(faceBox, frameWidth, frameHeight) {
|
|
1841
|
-
const faceSize = Math.max(faceBox.width, faceBox.height);
|
|
1842
|
-
const idealCropSize = faceSize * IDEAL_CROP_MULTIPLIER;
|
|
1843
|
-
const maxCropSize = Math.min(frameWidth, frameHeight);
|
|
1844
|
-
if (idealCropSize <= maxCropSize) {
|
|
1845
|
-
return IDEAL_CROP_MULTIPLIER;
|
|
1846
|
-
}
|
|
1847
|
-
const adaptiveMultiplier = maxCropSize / faceSize;
|
|
1848
|
-
return Math.max(MIN_CROP_MULTIPLIER, Math.min(MAX_CROP_MULTIPLIER, adaptiveMultiplier));
|
|
1849
|
-
}
|
|
1850
1862
|
function isFaceCropFullyInFrame(faceBox, frameWidth, frameHeight) {
|
|
1851
|
-
const
|
|
1852
|
-
const
|
|
1853
|
-
const
|
|
1854
|
-
const
|
|
1855
|
-
const faceTop = faceBox.originY - marginY;
|
|
1856
|
-
const faceRight = faceBox.originX + faceBox.width + marginX;
|
|
1857
|
-
const faceBottom = faceBox.originY + faceBox.height + marginY;
|
|
1863
|
+
const faceLeft = faceBox.originX;
|
|
1864
|
+
const faceTop = faceBox.originY;
|
|
1865
|
+
const faceRight = faceBox.originX + faceBox.width;
|
|
1866
|
+
const faceBottom = faceBox.originY + faceBox.height;
|
|
1858
1867
|
return faceLeft >= 0 && faceTop >= 0 && faceRight <= frameWidth && faceBottom <= frameHeight;
|
|
1859
1868
|
}
|
|
1860
1869
|
function calculateFaceCropRegion(faceBox, frameWidth, frameHeight) {
|
|
1861
|
-
const
|
|
1862
|
-
const
|
|
1863
|
-
const
|
|
1864
|
-
const
|
|
1865
|
-
const
|
|
1866
|
-
const
|
|
1867
|
-
|
|
1868
|
-
let cropX = centerX - cropSize / 2;
|
|
1869
|
-
let cropY = centerY - cropSize / 2;
|
|
1870
|
-
cropX = Math.max(0, Math.min(frameWidth - cropSize, cropX));
|
|
1871
|
-
cropY = Math.max(0, Math.min(frameHeight - cropSize, cropY));
|
|
1872
|
-
const finalSize = Math.min(cropSize, frameWidth - cropX, frameHeight - cropY);
|
|
1873
|
-
return { x: cropX, y: cropY, size: finalSize };
|
|
1870
|
+
const marginX = FACE_CROP_FRAME_MARGIN * frameWidth;
|
|
1871
|
+
const marginY = FACE_CROP_FRAME_MARGIN * frameHeight;
|
|
1872
|
+
const x1 = Math.max(0, faceBox.originX - marginX);
|
|
1873
|
+
const y1 = Math.max(0, faceBox.originY - marginY);
|
|
1874
|
+
const x2 = Math.min(frameWidth, faceBox.originX + faceBox.width + marginX);
|
|
1875
|
+
const y2 = Math.min(frameHeight, faceBox.originY + faceBox.height + marginY);
|
|
1876
|
+
return { x: x1, y: y1, width: x2 - x1, height: y2 - y1 };
|
|
1874
1877
|
}
|
|
1875
1878
|
function checkFrameQuality(options) {
|
|
1876
1879
|
const {
|
|
@@ -2233,7 +2236,7 @@ export {
|
|
|
2233
2236
|
ES_LOCALE,
|
|
2234
2237
|
EYE_LANDMARK_INDICES,
|
|
2235
2238
|
EYE_QUALITY_THRESHOLDS,
|
|
2236
|
-
|
|
2239
|
+
FACE_CROP_FRAME_MARGIN,
|
|
2237
2240
|
FACE_CROP_OUTPUT_SIZE,
|
|
2238
2241
|
FEEDBACK_MESSAGES,
|
|
2239
2242
|
FRAME_BUFFER_CONFIG,
|
|
@@ -2243,20 +2246,17 @@ export {
|
|
|
2243
2246
|
GOOD_ALIGNMENT,
|
|
2244
2247
|
HIGH_ALIGNMENT,
|
|
2245
2248
|
HYBRID_MODEL_CONFIGS,
|
|
2246
|
-
IDEAL_CROP_MULTIPLIER,
|
|
2247
2249
|
LANDMARK_INDEX,
|
|
2248
2250
|
LANDMARK_MAX_BOUND,
|
|
2249
2251
|
LANDMARK_MIN_BOUND,
|
|
2250
2252
|
LOW_LIGHT_THRESHOLD,
|
|
2251
2253
|
LivenessApiError,
|
|
2252
2254
|
LivenessClient,
|
|
2253
|
-
MAX_CROP_MULTIPLIER,
|
|
2254
2255
|
MAX_FACE_PERCENTAGE_IN_CROP,
|
|
2255
2256
|
MAX_FACE_RATIO,
|
|
2256
2257
|
MAX_FACE_ROLL_DEGREES,
|
|
2257
2258
|
MAX_IDEAL_FACE_RATIO,
|
|
2258
2259
|
MIN_CAPTURE_ALIGNMENT,
|
|
2259
|
-
MIN_CROP_MULTIPLIER,
|
|
2260
2260
|
MIN_FACE_AREA_RATIO,
|
|
2261
2261
|
MIN_FACE_BOTTOM_MARGIN,
|
|
2262
2262
|
MIN_FACE_RATIO,
|
|
@@ -2278,7 +2278,6 @@ export {
|
|
|
2278
2278
|
analyzeEyeRegionBrightness,
|
|
2279
2279
|
analyzeEyeRegionContrast,
|
|
2280
2280
|
analyzeLighting,
|
|
2281
|
-
calculateAdaptiveCropMultiplier,
|
|
2282
2281
|
calculateBrightness,
|
|
2283
2282
|
calculateFaceAlignment,
|
|
2284
2283
|
calculateFaceCropRegion,
|