@moveris/shared 3.13.0 → 3.16.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/dist/index.d.mts +179 -1
- package/dist/index.d.ts +179 -1
- package/dist/index.js +287 -3
- package/dist/index.mjs +282 -3
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -97,7 +97,46 @@ interface LiveCheckRequest {
|
|
|
97
97
|
metadata?: {
|
|
98
98
|
consumer?: ConsumerContext;
|
|
99
99
|
} | null;
|
|
100
|
+
trace_id?: string;
|
|
101
|
+
forensic_client?: unknown;
|
|
100
102
|
}
|
|
103
|
+
interface V2UploadFrameData extends FrameData {
|
|
104
|
+
landmarks?: {
|
|
105
|
+
x: number;
|
|
106
|
+
y: number;
|
|
107
|
+
z: number;
|
|
108
|
+
}[] | null;
|
|
109
|
+
}
|
|
110
|
+
interface V2UploadRequest {
|
|
111
|
+
session_id: string;
|
|
112
|
+
model?: FastCheckModel;
|
|
113
|
+
source?: FrameSource;
|
|
114
|
+
fps?: number;
|
|
115
|
+
frames: V2UploadFrameData[];
|
|
116
|
+
frame_count?: number;
|
|
117
|
+
warnings?: string[];
|
|
118
|
+
}
|
|
119
|
+
interface V2UploadResponse {
|
|
120
|
+
session_id: string;
|
|
121
|
+
frames_received: number;
|
|
122
|
+
frames_required: number;
|
|
123
|
+
is_complete: boolean;
|
|
124
|
+
ttl_seconds: number;
|
|
125
|
+
stored_durable: boolean;
|
|
126
|
+
}
|
|
127
|
+
interface V2FastCheckRequest {
|
|
128
|
+
session_id: string;
|
|
129
|
+
model?: FastCheckModel;
|
|
130
|
+
source?: FrameSource;
|
|
131
|
+
fps?: number;
|
|
132
|
+
frame_count?: number;
|
|
133
|
+
warnings?: string[];
|
|
134
|
+
device_intelligence?: DeviceIntelligence;
|
|
135
|
+
metadata?: {
|
|
136
|
+
consumer?: ConsumerContext;
|
|
137
|
+
} | null;
|
|
138
|
+
}
|
|
139
|
+
type V2LiveCheckRequest = V2FastCheckRequest;
|
|
101
140
|
interface FastCheckCropsRequest {
|
|
102
141
|
session_id: string;
|
|
103
142
|
model?: FastCheckModel;
|
|
@@ -602,6 +641,59 @@ interface CameraValidationResult {
|
|
|
602
641
|
}
|
|
603
642
|
declare const DEFAULT_CAMERA_REQUIREMENTS: CameraRequirements;
|
|
604
643
|
|
|
644
|
+
declare const FORENSIC_SCHEMA_VERSION = "1.0";
|
|
645
|
+
type ForensicSource = 'webcam' | 'media';
|
|
646
|
+
interface ClientStage {
|
|
647
|
+
stage_id: 'capture' | 'landmarks' | 'encode' | 'transport';
|
|
648
|
+
layer: 'sdk' | 'wire';
|
|
649
|
+
ok: boolean;
|
|
650
|
+
metrics: Record<string, unknown>;
|
|
651
|
+
}
|
|
652
|
+
interface ClientFragment {
|
|
653
|
+
trace_id: string;
|
|
654
|
+
schema_version: string;
|
|
655
|
+
source: ForensicSource;
|
|
656
|
+
origin: {
|
|
657
|
+
sdk_version?: string;
|
|
658
|
+
demo_commit?: string;
|
|
659
|
+
video_path?: string;
|
|
660
|
+
};
|
|
661
|
+
target: {
|
|
662
|
+
environment?: string;
|
|
663
|
+
model?: string;
|
|
664
|
+
endpoint?: string;
|
|
665
|
+
};
|
|
666
|
+
stages: ClientStage[];
|
|
667
|
+
}
|
|
668
|
+
interface CaptureSummary {
|
|
669
|
+
framesCaptured: number;
|
|
670
|
+
alignmentMean?: number;
|
|
671
|
+
blurVarMean?: number;
|
|
672
|
+
brightnessMean?: number;
|
|
673
|
+
landmarksPresentPct?: number;
|
|
674
|
+
validationPassPct?: number;
|
|
675
|
+
rollMean?: number;
|
|
676
|
+
yawMean?: number;
|
|
677
|
+
pitchMean?: number;
|
|
678
|
+
encoding?: string;
|
|
679
|
+
width?: number;
|
|
680
|
+
height?: number;
|
|
681
|
+
bytesPerFrame?: number;
|
|
682
|
+
framesSent?: number;
|
|
683
|
+
requestBytes?: number;
|
|
684
|
+
sessionId?: string;
|
|
685
|
+
}
|
|
686
|
+
interface FragmentContext {
|
|
687
|
+
sdkVersion?: string;
|
|
688
|
+
demoCommit?: string;
|
|
689
|
+
videoPath?: string;
|
|
690
|
+
environment?: string;
|
|
691
|
+
model?: string;
|
|
692
|
+
endpoint?: string;
|
|
693
|
+
}
|
|
694
|
+
declare function generateTraceId(): string;
|
|
695
|
+
declare function buildClientFragment(traceId: string, source: ForensicSource, summary: CaptureSummary, ctx?: FragmentContext): ClientFragment;
|
|
696
|
+
|
|
605
697
|
interface LivenessClientConfig {
|
|
606
698
|
baseUrl?: string;
|
|
607
699
|
apiKey: string;
|
|
@@ -613,6 +705,7 @@ interface LivenessClientConfig {
|
|
|
613
705
|
consumer?: ConsumerContext;
|
|
614
706
|
trackClientTime?: boolean;
|
|
615
707
|
extraMetadata?: ExtraMetadata;
|
|
708
|
+
traceId?: string;
|
|
616
709
|
}
|
|
617
710
|
declare class LivenessApiError extends Error {
|
|
618
711
|
readonly code: string;
|
|
@@ -622,6 +715,9 @@ declare class LivenessApiError extends Error {
|
|
|
622
715
|
constructor(message: string, code: string, statusCode: number, required?: number, received?: number);
|
|
623
716
|
}
|
|
624
717
|
declare function toFrameData(frames: CapturedFrame[]): FrameData[];
|
|
718
|
+
declare function toV2UploadFrameData(frames: CapturedFrame[], options?: {
|
|
719
|
+
withLandmarks?: boolean;
|
|
720
|
+
}): V2UploadFrameData[];
|
|
625
721
|
declare function toHybridFrameData(frames: CapturedFrame[]): HybridFrameData[];
|
|
626
722
|
declare function toLivenessResult(response: FastCheckResponse | VerifyResponse | HybridCheckResponse): LivenessResult;
|
|
627
723
|
declare function toLivenessResultFromStream(response: FastCheckStreamResponse): LivenessResult;
|
|
@@ -636,6 +732,7 @@ declare class LivenessClient {
|
|
|
636
732
|
private readonly consumer;
|
|
637
733
|
private readonly trackClientTime;
|
|
638
734
|
private readonly extraMetadata;
|
|
735
|
+
private readonly traceId;
|
|
639
736
|
private diCollected;
|
|
640
737
|
private diCollecting;
|
|
641
738
|
private diOverrides;
|
|
@@ -673,6 +770,8 @@ declare class LivenessClient {
|
|
|
673
770
|
frameCount?: number;
|
|
674
771
|
source?: FrameSource;
|
|
675
772
|
warnings?: string[];
|
|
773
|
+
traceId?: string;
|
|
774
|
+
forensic?: ClientFragment;
|
|
676
775
|
}): Promise<LivenessResult>;
|
|
677
776
|
fastCheckCrops(crops: CropData[], options?: {
|
|
678
777
|
sessionId?: string;
|
|
@@ -735,6 +834,35 @@ declare class LivenessClient {
|
|
|
735
834
|
}): Promise<LivenessResult>;
|
|
736
835
|
getJobResult(jobId: string): Promise<JobStatusResponse>;
|
|
737
836
|
waitForJobResult(jobId: string, timeout?: number): Promise<JobStatusResponse>;
|
|
837
|
+
v2Upload(frames: CapturedFrame[], options: {
|
|
838
|
+
sessionId: string;
|
|
839
|
+
model?: FastCheckModel;
|
|
840
|
+
modelVersion?: ModelVersion;
|
|
841
|
+
source?: FrameSource;
|
|
842
|
+
fps?: number;
|
|
843
|
+
frameCount?: number;
|
|
844
|
+
warnings?: string[];
|
|
845
|
+
withLandmarks?: boolean;
|
|
846
|
+
}): Promise<V2UploadResponse>;
|
|
847
|
+
v2FastCheck(options: {
|
|
848
|
+
sessionId: string;
|
|
849
|
+
model?: FastCheckModel;
|
|
850
|
+
modelVersion?: ModelVersion;
|
|
851
|
+
source?: FrameSource;
|
|
852
|
+
fps?: number;
|
|
853
|
+
frameCount?: number;
|
|
854
|
+
warnings?: string[];
|
|
855
|
+
}): Promise<LivenessResult>;
|
|
856
|
+
v2LiveCheck(options: {
|
|
857
|
+
sessionId: string;
|
|
858
|
+
model?: FastCheckModel;
|
|
859
|
+
modelVersion?: ModelVersion;
|
|
860
|
+
source?: FrameSource;
|
|
861
|
+
fps?: number;
|
|
862
|
+
frameCount?: number;
|
|
863
|
+
warnings?: string[];
|
|
864
|
+
}): Promise<LivenessResult>;
|
|
865
|
+
private runV2Check;
|
|
738
866
|
}
|
|
739
867
|
|
|
740
868
|
declare class FrameBuffer {
|
|
@@ -789,6 +917,9 @@ declare const API_PATHS: {
|
|
|
789
917
|
readonly jobResult: "/api/v1/result";
|
|
790
918
|
readonly queueStats: "/api/v1/queue/stats";
|
|
791
919
|
readonly sessions: "/api/v1/sessions";
|
|
920
|
+
readonly v2Upload: "/api/v2/upload";
|
|
921
|
+
readonly v2FastCheck: "/api/v2/fast-check";
|
|
922
|
+
readonly v2LiveCheck: "/api/v2/live-check";
|
|
792
923
|
};
|
|
793
924
|
declare const RETRY_CONFIG: {
|
|
794
925
|
readonly maxAttempts: 3;
|
|
@@ -1003,4 +1134,51 @@ declare function collectDeviceIntelligence(opts?: {
|
|
|
1003
1134
|
platformVersion?: string;
|
|
1004
1135
|
}): Promise<DeviceIntelligence | null>;
|
|
1005
1136
|
|
|
1006
|
-
|
|
1137
|
+
interface RecorderFrame {
|
|
1138
|
+
pixels?: string;
|
|
1139
|
+
landmarks?: unknown[] | null;
|
|
1140
|
+
}
|
|
1141
|
+
interface RecorderQuality {
|
|
1142
|
+
alignment?: number;
|
|
1143
|
+
blurVariance?: number;
|
|
1144
|
+
brightness?: number;
|
|
1145
|
+
landmarksValid?: boolean;
|
|
1146
|
+
width?: number;
|
|
1147
|
+
height?: number;
|
|
1148
|
+
encoding?: string;
|
|
1149
|
+
}
|
|
1150
|
+
declare class ForensicRecorder {
|
|
1151
|
+
readonly traceId: string;
|
|
1152
|
+
private readonly source;
|
|
1153
|
+
private readonly ctx;
|
|
1154
|
+
private framesCaptured;
|
|
1155
|
+
private landmarksPresent;
|
|
1156
|
+
private landmarksValid;
|
|
1157
|
+
private qualityTicks;
|
|
1158
|
+
private bytesTotal;
|
|
1159
|
+
private encoding;
|
|
1160
|
+
private width;
|
|
1161
|
+
private height;
|
|
1162
|
+
private readonly alignment;
|
|
1163
|
+
private readonly blur;
|
|
1164
|
+
private readonly brightness;
|
|
1165
|
+
private framesSent;
|
|
1166
|
+
private requestBytes;
|
|
1167
|
+
private sessionId;
|
|
1168
|
+
constructor(opts?: {
|
|
1169
|
+
source?: ForensicSource;
|
|
1170
|
+
traceId?: string;
|
|
1171
|
+
ctx?: FragmentContext;
|
|
1172
|
+
});
|
|
1173
|
+
onFrame(frame: RecorderFrame): void;
|
|
1174
|
+
onQuality(q: RecorderQuality): void;
|
|
1175
|
+
setTransport(t: {
|
|
1176
|
+
framesSent?: number;
|
|
1177
|
+
requestBytes?: number;
|
|
1178
|
+
sessionId?: string;
|
|
1179
|
+
}): void;
|
|
1180
|
+
summary(): CaptureSummary;
|
|
1181
|
+
build(): ClientFragment;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
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 CaptureSummary, type CapturedFrame, type ClientFragment, type ClientStage, 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 ExtraMetadata, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CROP_FRAME_MARGIN, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FORENSIC_SCHEMA_VERSION, 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, ForensicRecorder, type ForensicSource, type FragmentContext, 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, type LiveCheckFrameData, type LiveCheckRequest, 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 RecorderFrame, type RecorderQuality, type RetryOptions, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type V2FastCheckRequest, type V2LiveCheckRequest, type V2UploadFrameData, type V2UploadRequest, type V2UploadResponse, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, buildClientFragment, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, generateTraceId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, toV2UploadFrameData, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.d.ts
CHANGED
|
@@ -97,7 +97,46 @@ interface LiveCheckRequest {
|
|
|
97
97
|
metadata?: {
|
|
98
98
|
consumer?: ConsumerContext;
|
|
99
99
|
} | null;
|
|
100
|
+
trace_id?: string;
|
|
101
|
+
forensic_client?: unknown;
|
|
100
102
|
}
|
|
103
|
+
interface V2UploadFrameData extends FrameData {
|
|
104
|
+
landmarks?: {
|
|
105
|
+
x: number;
|
|
106
|
+
y: number;
|
|
107
|
+
z: number;
|
|
108
|
+
}[] | null;
|
|
109
|
+
}
|
|
110
|
+
interface V2UploadRequest {
|
|
111
|
+
session_id: string;
|
|
112
|
+
model?: FastCheckModel;
|
|
113
|
+
source?: FrameSource;
|
|
114
|
+
fps?: number;
|
|
115
|
+
frames: V2UploadFrameData[];
|
|
116
|
+
frame_count?: number;
|
|
117
|
+
warnings?: string[];
|
|
118
|
+
}
|
|
119
|
+
interface V2UploadResponse {
|
|
120
|
+
session_id: string;
|
|
121
|
+
frames_received: number;
|
|
122
|
+
frames_required: number;
|
|
123
|
+
is_complete: boolean;
|
|
124
|
+
ttl_seconds: number;
|
|
125
|
+
stored_durable: boolean;
|
|
126
|
+
}
|
|
127
|
+
interface V2FastCheckRequest {
|
|
128
|
+
session_id: string;
|
|
129
|
+
model?: FastCheckModel;
|
|
130
|
+
source?: FrameSource;
|
|
131
|
+
fps?: number;
|
|
132
|
+
frame_count?: number;
|
|
133
|
+
warnings?: string[];
|
|
134
|
+
device_intelligence?: DeviceIntelligence;
|
|
135
|
+
metadata?: {
|
|
136
|
+
consumer?: ConsumerContext;
|
|
137
|
+
} | null;
|
|
138
|
+
}
|
|
139
|
+
type V2LiveCheckRequest = V2FastCheckRequest;
|
|
101
140
|
interface FastCheckCropsRequest {
|
|
102
141
|
session_id: string;
|
|
103
142
|
model?: FastCheckModel;
|
|
@@ -602,6 +641,59 @@ interface CameraValidationResult {
|
|
|
602
641
|
}
|
|
603
642
|
declare const DEFAULT_CAMERA_REQUIREMENTS: CameraRequirements;
|
|
604
643
|
|
|
644
|
+
declare const FORENSIC_SCHEMA_VERSION = "1.0";
|
|
645
|
+
type ForensicSource = 'webcam' | 'media';
|
|
646
|
+
interface ClientStage {
|
|
647
|
+
stage_id: 'capture' | 'landmarks' | 'encode' | 'transport';
|
|
648
|
+
layer: 'sdk' | 'wire';
|
|
649
|
+
ok: boolean;
|
|
650
|
+
metrics: Record<string, unknown>;
|
|
651
|
+
}
|
|
652
|
+
interface ClientFragment {
|
|
653
|
+
trace_id: string;
|
|
654
|
+
schema_version: string;
|
|
655
|
+
source: ForensicSource;
|
|
656
|
+
origin: {
|
|
657
|
+
sdk_version?: string;
|
|
658
|
+
demo_commit?: string;
|
|
659
|
+
video_path?: string;
|
|
660
|
+
};
|
|
661
|
+
target: {
|
|
662
|
+
environment?: string;
|
|
663
|
+
model?: string;
|
|
664
|
+
endpoint?: string;
|
|
665
|
+
};
|
|
666
|
+
stages: ClientStage[];
|
|
667
|
+
}
|
|
668
|
+
interface CaptureSummary {
|
|
669
|
+
framesCaptured: number;
|
|
670
|
+
alignmentMean?: number;
|
|
671
|
+
blurVarMean?: number;
|
|
672
|
+
brightnessMean?: number;
|
|
673
|
+
landmarksPresentPct?: number;
|
|
674
|
+
validationPassPct?: number;
|
|
675
|
+
rollMean?: number;
|
|
676
|
+
yawMean?: number;
|
|
677
|
+
pitchMean?: number;
|
|
678
|
+
encoding?: string;
|
|
679
|
+
width?: number;
|
|
680
|
+
height?: number;
|
|
681
|
+
bytesPerFrame?: number;
|
|
682
|
+
framesSent?: number;
|
|
683
|
+
requestBytes?: number;
|
|
684
|
+
sessionId?: string;
|
|
685
|
+
}
|
|
686
|
+
interface FragmentContext {
|
|
687
|
+
sdkVersion?: string;
|
|
688
|
+
demoCommit?: string;
|
|
689
|
+
videoPath?: string;
|
|
690
|
+
environment?: string;
|
|
691
|
+
model?: string;
|
|
692
|
+
endpoint?: string;
|
|
693
|
+
}
|
|
694
|
+
declare function generateTraceId(): string;
|
|
695
|
+
declare function buildClientFragment(traceId: string, source: ForensicSource, summary: CaptureSummary, ctx?: FragmentContext): ClientFragment;
|
|
696
|
+
|
|
605
697
|
interface LivenessClientConfig {
|
|
606
698
|
baseUrl?: string;
|
|
607
699
|
apiKey: string;
|
|
@@ -613,6 +705,7 @@ interface LivenessClientConfig {
|
|
|
613
705
|
consumer?: ConsumerContext;
|
|
614
706
|
trackClientTime?: boolean;
|
|
615
707
|
extraMetadata?: ExtraMetadata;
|
|
708
|
+
traceId?: string;
|
|
616
709
|
}
|
|
617
710
|
declare class LivenessApiError extends Error {
|
|
618
711
|
readonly code: string;
|
|
@@ -622,6 +715,9 @@ declare class LivenessApiError extends Error {
|
|
|
622
715
|
constructor(message: string, code: string, statusCode: number, required?: number, received?: number);
|
|
623
716
|
}
|
|
624
717
|
declare function toFrameData(frames: CapturedFrame[]): FrameData[];
|
|
718
|
+
declare function toV2UploadFrameData(frames: CapturedFrame[], options?: {
|
|
719
|
+
withLandmarks?: boolean;
|
|
720
|
+
}): V2UploadFrameData[];
|
|
625
721
|
declare function toHybridFrameData(frames: CapturedFrame[]): HybridFrameData[];
|
|
626
722
|
declare function toLivenessResult(response: FastCheckResponse | VerifyResponse | HybridCheckResponse): LivenessResult;
|
|
627
723
|
declare function toLivenessResultFromStream(response: FastCheckStreamResponse): LivenessResult;
|
|
@@ -636,6 +732,7 @@ declare class LivenessClient {
|
|
|
636
732
|
private readonly consumer;
|
|
637
733
|
private readonly trackClientTime;
|
|
638
734
|
private readonly extraMetadata;
|
|
735
|
+
private readonly traceId;
|
|
639
736
|
private diCollected;
|
|
640
737
|
private diCollecting;
|
|
641
738
|
private diOverrides;
|
|
@@ -673,6 +770,8 @@ declare class LivenessClient {
|
|
|
673
770
|
frameCount?: number;
|
|
674
771
|
source?: FrameSource;
|
|
675
772
|
warnings?: string[];
|
|
773
|
+
traceId?: string;
|
|
774
|
+
forensic?: ClientFragment;
|
|
676
775
|
}): Promise<LivenessResult>;
|
|
677
776
|
fastCheckCrops(crops: CropData[], options?: {
|
|
678
777
|
sessionId?: string;
|
|
@@ -735,6 +834,35 @@ declare class LivenessClient {
|
|
|
735
834
|
}): Promise<LivenessResult>;
|
|
736
835
|
getJobResult(jobId: string): Promise<JobStatusResponse>;
|
|
737
836
|
waitForJobResult(jobId: string, timeout?: number): Promise<JobStatusResponse>;
|
|
837
|
+
v2Upload(frames: CapturedFrame[], options: {
|
|
838
|
+
sessionId: string;
|
|
839
|
+
model?: FastCheckModel;
|
|
840
|
+
modelVersion?: ModelVersion;
|
|
841
|
+
source?: FrameSource;
|
|
842
|
+
fps?: number;
|
|
843
|
+
frameCount?: number;
|
|
844
|
+
warnings?: string[];
|
|
845
|
+
withLandmarks?: boolean;
|
|
846
|
+
}): Promise<V2UploadResponse>;
|
|
847
|
+
v2FastCheck(options: {
|
|
848
|
+
sessionId: string;
|
|
849
|
+
model?: FastCheckModel;
|
|
850
|
+
modelVersion?: ModelVersion;
|
|
851
|
+
source?: FrameSource;
|
|
852
|
+
fps?: number;
|
|
853
|
+
frameCount?: number;
|
|
854
|
+
warnings?: string[];
|
|
855
|
+
}): Promise<LivenessResult>;
|
|
856
|
+
v2LiveCheck(options: {
|
|
857
|
+
sessionId: string;
|
|
858
|
+
model?: FastCheckModel;
|
|
859
|
+
modelVersion?: ModelVersion;
|
|
860
|
+
source?: FrameSource;
|
|
861
|
+
fps?: number;
|
|
862
|
+
frameCount?: number;
|
|
863
|
+
warnings?: string[];
|
|
864
|
+
}): Promise<LivenessResult>;
|
|
865
|
+
private runV2Check;
|
|
738
866
|
}
|
|
739
867
|
|
|
740
868
|
declare class FrameBuffer {
|
|
@@ -789,6 +917,9 @@ declare const API_PATHS: {
|
|
|
789
917
|
readonly jobResult: "/api/v1/result";
|
|
790
918
|
readonly queueStats: "/api/v1/queue/stats";
|
|
791
919
|
readonly sessions: "/api/v1/sessions";
|
|
920
|
+
readonly v2Upload: "/api/v2/upload";
|
|
921
|
+
readonly v2FastCheck: "/api/v2/fast-check";
|
|
922
|
+
readonly v2LiveCheck: "/api/v2/live-check";
|
|
792
923
|
};
|
|
793
924
|
declare const RETRY_CONFIG: {
|
|
794
925
|
readonly maxAttempts: 3;
|
|
@@ -1003,4 +1134,51 @@ declare function collectDeviceIntelligence(opts?: {
|
|
|
1003
1134
|
platformVersion?: string;
|
|
1004
1135
|
}): Promise<DeviceIntelligence | null>;
|
|
1005
1136
|
|
|
1006
|
-
|
|
1137
|
+
interface RecorderFrame {
|
|
1138
|
+
pixels?: string;
|
|
1139
|
+
landmarks?: unknown[] | null;
|
|
1140
|
+
}
|
|
1141
|
+
interface RecorderQuality {
|
|
1142
|
+
alignment?: number;
|
|
1143
|
+
blurVariance?: number;
|
|
1144
|
+
brightness?: number;
|
|
1145
|
+
landmarksValid?: boolean;
|
|
1146
|
+
width?: number;
|
|
1147
|
+
height?: number;
|
|
1148
|
+
encoding?: string;
|
|
1149
|
+
}
|
|
1150
|
+
declare class ForensicRecorder {
|
|
1151
|
+
readonly traceId: string;
|
|
1152
|
+
private readonly source;
|
|
1153
|
+
private readonly ctx;
|
|
1154
|
+
private framesCaptured;
|
|
1155
|
+
private landmarksPresent;
|
|
1156
|
+
private landmarksValid;
|
|
1157
|
+
private qualityTicks;
|
|
1158
|
+
private bytesTotal;
|
|
1159
|
+
private encoding;
|
|
1160
|
+
private width;
|
|
1161
|
+
private height;
|
|
1162
|
+
private readonly alignment;
|
|
1163
|
+
private readonly blur;
|
|
1164
|
+
private readonly brightness;
|
|
1165
|
+
private framesSent;
|
|
1166
|
+
private requestBytes;
|
|
1167
|
+
private sessionId;
|
|
1168
|
+
constructor(opts?: {
|
|
1169
|
+
source?: ForensicSource;
|
|
1170
|
+
traceId?: string;
|
|
1171
|
+
ctx?: FragmentContext;
|
|
1172
|
+
});
|
|
1173
|
+
onFrame(frame: RecorderFrame): void;
|
|
1174
|
+
onQuality(q: RecorderQuality): void;
|
|
1175
|
+
setTransport(t: {
|
|
1176
|
+
framesSent?: number;
|
|
1177
|
+
requestBytes?: number;
|
|
1178
|
+
sessionId?: string;
|
|
1179
|
+
}): void;
|
|
1180
|
+
summary(): CaptureSummary;
|
|
1181
|
+
build(): ClientFragment;
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
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 CaptureSummary, type CapturedFrame, type ClientFragment, type ClientStage, 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 ExtraMetadata, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CROP_FRAME_MARGIN, FACE_CROP_OUTPUT_SIZE, FEEDBACK_MESSAGES, FORENSIC_SCHEMA_VERSION, 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, ForensicRecorder, type ForensicSource, type FragmentContext, 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, type LiveCheckFrameData, type LiveCheckRequest, 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 RecorderFrame, type RecorderQuality, type RetryOptions, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, type V2FastCheckRequest, type V2LiveCheckRequest, type V2UploadFrameData, type V2UploadRequest, type V2UploadResponse, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, buildClientFragment, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, generateTraceId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, toV2UploadFrameData, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
|
package/dist/index.js
CHANGED
|
@@ -53,8 +53,10 @@ __export(index_exports, {
|
|
|
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
|
+
FORENSIC_SCHEMA_VERSION: () => FORENSIC_SCHEMA_VERSION,
|
|
56
57
|
FRAME_BUFFER_CONFIG: () => FRAME_BUFFER_CONFIG,
|
|
57
58
|
FRAME_CONFIG: () => FRAME_CONFIG,
|
|
59
|
+
ForensicRecorder: () => ForensicRecorder,
|
|
58
60
|
FrameBuffer: () => FrameBuffer,
|
|
59
61
|
FrameQueue: () => FrameQueue,
|
|
60
62
|
GOOD_ALIGNMENT: () => GOOD_ALIGNMENT,
|
|
@@ -92,6 +94,7 @@ __export(index_exports, {
|
|
|
92
94
|
analyzeEyeRegionBrightness: () => analyzeEyeRegionBrightness,
|
|
93
95
|
analyzeEyeRegionContrast: () => analyzeEyeRegionContrast,
|
|
94
96
|
analyzeLighting: () => analyzeLighting,
|
|
97
|
+
buildClientFragment: () => buildClientFragment,
|
|
95
98
|
calculateBrightness: () => calculateBrightness,
|
|
96
99
|
calculateFaceAlignment: () => calculateFaceAlignment,
|
|
97
100
|
calculateFaceCropRegion: () => calculateFaceCropRegion,
|
|
@@ -106,6 +109,7 @@ __export(index_exports, {
|
|
|
106
109
|
detectSpecularHighlights: () => detectSpecularHighlights,
|
|
107
110
|
encodeBase64: () => encodeBase64,
|
|
108
111
|
generateSessionId: () => generateSessionId,
|
|
112
|
+
generateTraceId: () => generateTraceId,
|
|
109
113
|
getActiveModels: () => getActiveModels,
|
|
110
114
|
getApiErrorMessage: () => getApiErrorMessage,
|
|
111
115
|
getCaptureQualityFeedback: () => getCaptureQualityFeedback,
|
|
@@ -129,6 +133,7 @@ __export(index_exports, {
|
|
|
129
133
|
toHybridFrameData: () => toHybridFrameData,
|
|
130
134
|
toLivenessResult: () => toLivenessResult,
|
|
131
135
|
toLivenessResultFromStream: () => toLivenessResultFromStream,
|
|
136
|
+
toV2UploadFrameData: () => toV2UploadFrameData,
|
|
132
137
|
validateApiKey: () => validateApiKey,
|
|
133
138
|
validateFaceLandmarks: () => validateFaceLandmarks,
|
|
134
139
|
validateFrameCount: () => validateFrameCount,
|
|
@@ -161,7 +166,12 @@ var API_PATHS = {
|
|
|
161
166
|
hybrid150: "/api/v1/hybrid-150",
|
|
162
167
|
jobResult: "/api/v1/result",
|
|
163
168
|
queueStats: "/api/v1/queue/stats",
|
|
164
|
-
sessions: "/api/v1/sessions"
|
|
169
|
+
sessions: "/api/v1/sessions",
|
|
170
|
+
// v2 upload-first pipeline (MOV-1936). Separate from v1; frames are uploaded
|
|
171
|
+
// first and resolved server-side by session_id at check time.
|
|
172
|
+
v2Upload: "/api/v2/upload",
|
|
173
|
+
v2FastCheck: "/api/v2/fast-check",
|
|
174
|
+
v2LiveCheck: "/api/v2/live-check"
|
|
165
175
|
};
|
|
166
176
|
var RETRY_CONFIG = {
|
|
167
177
|
maxAttempts: 3,
|
|
@@ -241,7 +251,7 @@ async function sleep(ms) {
|
|
|
241
251
|
}
|
|
242
252
|
|
|
243
253
|
// package.json
|
|
244
|
-
var version = "3.
|
|
254
|
+
var version = "3.16.0";
|
|
245
255
|
|
|
246
256
|
// src/utils/deviceIntelligence.ts
|
|
247
257
|
var IPINFO_URL = "https://ipinfo.io/json";
|
|
@@ -289,6 +299,85 @@ async function collectDeviceIntelligence(opts) {
|
|
|
289
299
|
}
|
|
290
300
|
}
|
|
291
301
|
|
|
302
|
+
// src/forensic/clientFragment.ts
|
|
303
|
+
var FORENSIC_SCHEMA_VERSION = "1.0";
|
|
304
|
+
function defined(obj) {
|
|
305
|
+
const out = {};
|
|
306
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
307
|
+
if (v !== void 0 && v !== null) out[k] = v;
|
|
308
|
+
}
|
|
309
|
+
return out;
|
|
310
|
+
}
|
|
311
|
+
function generateTraceId() {
|
|
312
|
+
const c = globalThis.crypto;
|
|
313
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
314
|
+
return "trace-" + Math.abs(hashString(String(Date.now()) + ":" + performanceNow())).toString(36);
|
|
315
|
+
}
|
|
316
|
+
function performanceNow() {
|
|
317
|
+
const p = globalThis.performance;
|
|
318
|
+
return p?.now ? p.now() : 0;
|
|
319
|
+
}
|
|
320
|
+
function hashString(s) {
|
|
321
|
+
let h = 0;
|
|
322
|
+
for (let i = 0; i < s.length; i++) h = Math.imul(31, h) + s.charCodeAt(i) | 0;
|
|
323
|
+
return h;
|
|
324
|
+
}
|
|
325
|
+
function buildClientFragment(traceId, source, summary, ctx = {}) {
|
|
326
|
+
const stages = [];
|
|
327
|
+
const capture = defined({
|
|
328
|
+
frames_captured: summary.framesCaptured,
|
|
329
|
+
alignment_mean: summary.alignmentMean,
|
|
330
|
+
blur_var_mean: summary.blurVarMean,
|
|
331
|
+
brightness_mean: summary.brightnessMean
|
|
332
|
+
});
|
|
333
|
+
if (Object.keys(capture).length) {
|
|
334
|
+
stages.push({ stage_id: "capture", layer: "sdk", ok: true, metrics: capture });
|
|
335
|
+
}
|
|
336
|
+
const landmarks = defined({
|
|
337
|
+
landmarks_present_pct: summary.landmarksPresentPct,
|
|
338
|
+
validation_pass_pct: summary.validationPassPct,
|
|
339
|
+
roll_mean: summary.rollMean,
|
|
340
|
+
yaw_mean: summary.yawMean,
|
|
341
|
+
pitch_mean: summary.pitchMean
|
|
342
|
+
});
|
|
343
|
+
if (Object.keys(landmarks).length) {
|
|
344
|
+
stages.push({ stage_id: "landmarks", layer: "sdk", ok: true, metrics: landmarks });
|
|
345
|
+
}
|
|
346
|
+
const encode = defined({
|
|
347
|
+
encoding: summary.encoding,
|
|
348
|
+
w: summary.width,
|
|
349
|
+
h: summary.height,
|
|
350
|
+
bytes_per_frame: summary.bytesPerFrame
|
|
351
|
+
});
|
|
352
|
+
if (Object.keys(encode).length) {
|
|
353
|
+
stages.push({ stage_id: "encode", layer: "sdk", ok: true, metrics: encode });
|
|
354
|
+
}
|
|
355
|
+
const transport = defined({
|
|
356
|
+
frames_sent: summary.framesSent,
|
|
357
|
+
request_bytes: summary.requestBytes,
|
|
358
|
+
session_id: summary.sessionId
|
|
359
|
+
});
|
|
360
|
+
if (Object.keys(transport).length) {
|
|
361
|
+
stages.push({ stage_id: "transport", layer: "wire", ok: true, metrics: transport });
|
|
362
|
+
}
|
|
363
|
+
return {
|
|
364
|
+
trace_id: traceId,
|
|
365
|
+
schema_version: FORENSIC_SCHEMA_VERSION,
|
|
366
|
+
source,
|
|
367
|
+
origin: defined({
|
|
368
|
+
sdk_version: ctx.sdkVersion,
|
|
369
|
+
demo_commit: ctx.demoCommit,
|
|
370
|
+
video_path: ctx.videoPath
|
|
371
|
+
}),
|
|
372
|
+
target: defined({
|
|
373
|
+
environment: ctx.environment,
|
|
374
|
+
model: ctx.model,
|
|
375
|
+
endpoint: ctx.endpoint
|
|
376
|
+
}),
|
|
377
|
+
stages
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
|
|
292
381
|
// src/client/LivenessClient.ts
|
|
293
382
|
var LivenessApiError = class extends Error {
|
|
294
383
|
constructor(message, code, statusCode, required, received) {
|
|
@@ -315,6 +404,16 @@ function toFrameData(frames) {
|
|
|
315
404
|
pixels: frame.pixels
|
|
316
405
|
}));
|
|
317
406
|
}
|
|
407
|
+
function toV2UploadFrameData(frames, options = {}) {
|
|
408
|
+
return frames.map((frame) => ({
|
|
409
|
+
index: frame.index,
|
|
410
|
+
timestamp_ms: frame.timestampMs,
|
|
411
|
+
pixels: frame.pixels,
|
|
412
|
+
...options.withLandmarks ? {
|
|
413
|
+
landmarks: frame.landmarks ? frame.landmarks.map((lm) => ({ x: lm.x, y: lm.y, z: lm.z })) : null
|
|
414
|
+
} : {}
|
|
415
|
+
}));
|
|
416
|
+
}
|
|
318
417
|
function toHybridFrameData(frames) {
|
|
319
418
|
return frames.map((frame) => ({
|
|
320
419
|
timestamp_ms: frame.timestampMs,
|
|
@@ -403,6 +502,7 @@ var LivenessClient = class _LivenessClient {
|
|
|
403
502
|
this.consumer = config.consumer;
|
|
404
503
|
this.trackClientTime = config.trackClientTime ?? false;
|
|
405
504
|
this.extraMetadata = config.extraMetadata;
|
|
505
|
+
this.traceId = config.traceId;
|
|
406
506
|
}
|
|
407
507
|
/**
|
|
408
508
|
* Merge additional device intelligence overrides into the existing set.
|
|
@@ -732,11 +832,14 @@ var LivenessClient = class _LivenessClient {
|
|
|
732
832
|
async liveCheck(frames, options = {}) {
|
|
733
833
|
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
734
834
|
const di = await this.getDeviceIntelligence();
|
|
835
|
+
const traceId = options.forensic?.trace_id ?? options.traceId ?? this.traceId ?? generateTraceId();
|
|
735
836
|
const request = {
|
|
736
837
|
session_id: options.sessionId ?? generateSessionId(),
|
|
737
838
|
model: options.model ?? "10",
|
|
738
839
|
source: options.source ?? "live",
|
|
739
840
|
frames: toLiveCheckFrameData(frames),
|
|
841
|
+
trace_id: traceId,
|
|
842
|
+
...options.forensic ? { forensic_client: options.forensic } : {},
|
|
740
843
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
741
844
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
742
845
|
...di ? { device_intelligence: di } : {},
|
|
@@ -747,7 +850,7 @@ var LivenessClient = class _LivenessClient {
|
|
|
747
850
|
{
|
|
748
851
|
method: "POST",
|
|
749
852
|
body: JSON.stringify(request),
|
|
750
|
-
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
853
|
+
headers: { ...this.buildModelVersionHeaders(effectiveVersion), "X-Trace-Id": traceId }
|
|
751
854
|
}
|
|
752
855
|
);
|
|
753
856
|
const result = toLivenessResult(response);
|
|
@@ -1057,6 +1160,103 @@ var LivenessClient = class _LivenessClient {
|
|
|
1057
1160
|
`${API_PATHS.jobResult}/${jobId}/wait?timeout=${timeout}`
|
|
1058
1161
|
);
|
|
1059
1162
|
}
|
|
1163
|
+
// ===========================================================================
|
|
1164
|
+
// v2 Pipeline (MOV-1936): upload-first
|
|
1165
|
+
//
|
|
1166
|
+
// Two-phase flow, fully separate from the v1 endpoints above:
|
|
1167
|
+
// 1. v2Upload() — buffer frames server-side (call once or repeatedly).
|
|
1168
|
+
// 2. v2FastCheck() / v2LiveCheck() — run inference; NO frames in the body,
|
|
1169
|
+
// the server resolves them by session_id from the prior upload(s).
|
|
1170
|
+
// The check responses reuse the v1 FastCheckResponse shape, so the existing
|
|
1171
|
+
// `toLivenessResult` + deprecation-header handling applies unchanged.
|
|
1172
|
+
// ===========================================================================
|
|
1173
|
+
/**
|
|
1174
|
+
* Buffer a batch of frames for a session (POST /api/v2/upload).
|
|
1175
|
+
*
|
|
1176
|
+
* Inference is NOT run here — this only stores frames keyed by `session_id`.
|
|
1177
|
+
* May be called multiple times for the same session (e.g. chunked during
|
|
1178
|
+
* capture); the server accumulates and reports cumulative progress.
|
|
1179
|
+
*
|
|
1180
|
+
* Set `withLandmarks` for the live-check path so MediaPipe landmarks travel
|
|
1181
|
+
* with each frame; leave it off for the fast-check path.
|
|
1182
|
+
*
|
|
1183
|
+
* @param frames - Captured frames to buffer
|
|
1184
|
+
* @param options - Session, model, source and upload flags
|
|
1185
|
+
* @returns Upload progress (frames_received / frames_required / is_complete)
|
|
1186
|
+
*/
|
|
1187
|
+
async v2Upload(frames, options) {
|
|
1188
|
+
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
1189
|
+
const request = {
|
|
1190
|
+
session_id: options.sessionId,
|
|
1191
|
+
model: options.model ?? "10",
|
|
1192
|
+
source: options.source ?? "live",
|
|
1193
|
+
frames: toV2UploadFrameData(frames, { withLandmarks: options.withLandmarks }),
|
|
1194
|
+
...options.fps != null ? { fps: options.fps } : {},
|
|
1195
|
+
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
1196
|
+
...options.warnings?.length ? { warnings: options.warnings } : {}
|
|
1197
|
+
};
|
|
1198
|
+
return this.requestWithRetry(API_PATHS.v2Upload, {
|
|
1199
|
+
method: "POST",
|
|
1200
|
+
body: JSON.stringify(request),
|
|
1201
|
+
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
/**
|
|
1205
|
+
* Run a fast-check against frames previously uploaded via {@link v2Upload}
|
|
1206
|
+
* (POST /api/v2/fast-check). The request carries NO frames — the server
|
|
1207
|
+
* resolves them by `session_id`.
|
|
1208
|
+
*
|
|
1209
|
+
* @param options - Session and request context
|
|
1210
|
+
* @returns Liveness result
|
|
1211
|
+
*/
|
|
1212
|
+
async v2FastCheck(options) {
|
|
1213
|
+
return this.runV2Check(API_PATHS.v2FastCheck, options);
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Run a live-check against frames previously uploaded via {@link v2Upload}
|
|
1217
|
+
* with `withLandmarks: true` (POST /api/v2/live-check). The request carries
|
|
1218
|
+
* NO frames. The server requires a V3 model and surfaces the V3 `diagnostics`
|
|
1219
|
+
* block on the result.
|
|
1220
|
+
*
|
|
1221
|
+
* @param options - Session and request context
|
|
1222
|
+
* @returns Liveness result (includes `diagnostics`)
|
|
1223
|
+
*/
|
|
1224
|
+
async v2LiveCheck(options) {
|
|
1225
|
+
return this.runV2Check(API_PATHS.v2LiveCheck, options);
|
|
1226
|
+
}
|
|
1227
|
+
/**
|
|
1228
|
+
* Shared body for the v2 fast-check / live-check requests (internal).
|
|
1229
|
+
* Both endpoints take the identical no-frames payload and response shape;
|
|
1230
|
+
* only the path differs.
|
|
1231
|
+
*/
|
|
1232
|
+
async runV2Check(path, options) {
|
|
1233
|
+
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
1234
|
+
const di = await this.getDeviceIntelligence();
|
|
1235
|
+
const metadata = this.buildMetadata();
|
|
1236
|
+
const request = {
|
|
1237
|
+
session_id: options.sessionId,
|
|
1238
|
+
model: options.model ?? "10",
|
|
1239
|
+
source: options.source ?? "live",
|
|
1240
|
+
...options.fps != null ? { fps: options.fps } : {},
|
|
1241
|
+
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
1242
|
+
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
1243
|
+
...di ? { device_intelligence: di } : {},
|
|
1244
|
+
...metadata ? { metadata } : {}
|
|
1245
|
+
};
|
|
1246
|
+
const { data: response, headers } = await this.requestWithRetryRaw(path, {
|
|
1247
|
+
method: "POST",
|
|
1248
|
+
body: JSON.stringify(request),
|
|
1249
|
+
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
1250
|
+
});
|
|
1251
|
+
const result = toLivenessResult(response);
|
|
1252
|
+
result.deprecation = _LivenessClient.parseDeprecationHeaders(headers);
|
|
1253
|
+
if (result.deprecation?.deprecated) {
|
|
1254
|
+
console.warn(
|
|
1255
|
+
`[Moveris] Model "${result.deprecation.resolvedModel}" is deprecated.` + (result.deprecation.suggestedModel ? ` Migrate to "${result.deprecation.suggestedModel}".` : "") + (result.deprecation.sunsetDate ? ` Sunset date: ${result.deprecation.sunsetDate}.` : "")
|
|
1256
|
+
);
|
|
1257
|
+
}
|
|
1258
|
+
return result;
|
|
1259
|
+
}
|
|
1060
1260
|
};
|
|
1061
1261
|
|
|
1062
1262
|
// src/buffer/FrameBuffer.ts
|
|
@@ -2461,6 +2661,85 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2461
2661
|
message: null
|
|
2462
2662
|
};
|
|
2463
2663
|
}
|
|
2664
|
+
|
|
2665
|
+
// src/forensic/recorder.ts
|
|
2666
|
+
var RunningMean = class {
|
|
2667
|
+
constructor() {
|
|
2668
|
+
this.sum = 0;
|
|
2669
|
+
this.n = 0;
|
|
2670
|
+
}
|
|
2671
|
+
add(v) {
|
|
2672
|
+
if (typeof v === "number" && !Number.isNaN(v)) {
|
|
2673
|
+
this.sum += v;
|
|
2674
|
+
this.n += 1;
|
|
2675
|
+
}
|
|
2676
|
+
}
|
|
2677
|
+
get value() {
|
|
2678
|
+
return this.n ? this.sum / this.n : void 0;
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
var ForensicRecorder = class {
|
|
2682
|
+
constructor(opts = {}) {
|
|
2683
|
+
this.framesCaptured = 0;
|
|
2684
|
+
this.landmarksPresent = 0;
|
|
2685
|
+
this.landmarksValid = 0;
|
|
2686
|
+
this.qualityTicks = 0;
|
|
2687
|
+
this.bytesTotal = 0;
|
|
2688
|
+
this.alignment = new RunningMean();
|
|
2689
|
+
this.blur = new RunningMean();
|
|
2690
|
+
this.brightness = new RunningMean();
|
|
2691
|
+
this.source = opts.source ?? "webcam";
|
|
2692
|
+
this.traceId = opts.traceId ?? generateTraceId();
|
|
2693
|
+
this.ctx = opts.ctx ?? {};
|
|
2694
|
+
}
|
|
2695
|
+
/** Record one captured frame (counts, landmark presence, encoded size). */
|
|
2696
|
+
onFrame(frame) {
|
|
2697
|
+
this.framesCaptured += 1;
|
|
2698
|
+
if (frame.landmarks && frame.landmarks.length) this.landmarksPresent += 1;
|
|
2699
|
+
if (typeof frame.pixels === "string") this.bytesTotal += frame.pixels.length;
|
|
2700
|
+
}
|
|
2701
|
+
/** Record one quality update (alignment/blur/brightness/landmark validity). */
|
|
2702
|
+
onQuality(q) {
|
|
2703
|
+
this.qualityTicks += 1;
|
|
2704
|
+
this.alignment.add(q.alignment);
|
|
2705
|
+
this.blur.add(q.blurVariance);
|
|
2706
|
+
this.brightness.add(q.brightness);
|
|
2707
|
+
if (q.landmarksValid) this.landmarksValid += 1;
|
|
2708
|
+
if (q.encoding) this.encoding = q.encoding;
|
|
2709
|
+
if (q.width) this.width = q.width;
|
|
2710
|
+
if (q.height) this.height = q.height;
|
|
2711
|
+
}
|
|
2712
|
+
/** Record the transport leg once the request is built/sent. */
|
|
2713
|
+
setTransport(t) {
|
|
2714
|
+
this.framesSent = t.framesSent;
|
|
2715
|
+
this.requestBytes = t.requestBytes;
|
|
2716
|
+
this.sessionId = t.sessionId;
|
|
2717
|
+
}
|
|
2718
|
+
/** Summarise the session into the CaptureSummary the fragment builder expects. */
|
|
2719
|
+
summary() {
|
|
2720
|
+
return {
|
|
2721
|
+
framesCaptured: this.framesCaptured,
|
|
2722
|
+
alignmentMean: this.alignment.value,
|
|
2723
|
+
blurVarMean: this.blur.value,
|
|
2724
|
+
brightnessMean: this.brightness.value,
|
|
2725
|
+
landmarksPresentPct: this.framesCaptured ? this.landmarksPresent / this.framesCaptured : void 0,
|
|
2726
|
+
// validity is a fraction of QUALITY ticks, not frames — quality updates fire
|
|
2727
|
+
// more often than frames, so dividing by frames could exceed 1.0.
|
|
2728
|
+
validationPassPct: this.qualityTicks ? this.landmarksValid / this.qualityTicks : void 0,
|
|
2729
|
+
encoding: this.encoding,
|
|
2730
|
+
width: this.width,
|
|
2731
|
+
height: this.height,
|
|
2732
|
+
bytesPerFrame: this.framesCaptured && this.bytesTotal ? Math.round(this.bytesTotal / this.framesCaptured) : void 0,
|
|
2733
|
+
framesSent: this.framesSent ?? (this.framesCaptured || void 0),
|
|
2734
|
+
requestBytes: this.requestBytes,
|
|
2735
|
+
sessionId: this.sessionId
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
/** Build the client fragment for this session. */
|
|
2739
|
+
build() {
|
|
2740
|
+
return buildClientFragment(this.traceId, this.source, this.summary(), this.ctx);
|
|
2741
|
+
}
|
|
2742
|
+
};
|
|
2464
2743
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2465
2744
|
0 && (module.exports = {
|
|
2466
2745
|
ALIGNMENT_THRESHOLD_CAPTURE,
|
|
@@ -2496,8 +2775,10 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2496
2775
|
FACE_CROP_FRAME_MARGIN,
|
|
2497
2776
|
FACE_CROP_OUTPUT_SIZE,
|
|
2498
2777
|
FEEDBACK_MESSAGES,
|
|
2778
|
+
FORENSIC_SCHEMA_VERSION,
|
|
2499
2779
|
FRAME_BUFFER_CONFIG,
|
|
2500
2780
|
FRAME_CONFIG,
|
|
2781
|
+
ForensicRecorder,
|
|
2501
2782
|
FrameBuffer,
|
|
2502
2783
|
FrameQueue,
|
|
2503
2784
|
GOOD_ALIGNMENT,
|
|
@@ -2535,6 +2816,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2535
2816
|
analyzeEyeRegionBrightness,
|
|
2536
2817
|
analyzeEyeRegionContrast,
|
|
2537
2818
|
analyzeLighting,
|
|
2819
|
+
buildClientFragment,
|
|
2538
2820
|
calculateBrightness,
|
|
2539
2821
|
calculateFaceAlignment,
|
|
2540
2822
|
calculateFaceCropRegion,
|
|
@@ -2549,6 +2831,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2549
2831
|
detectSpecularHighlights,
|
|
2550
2832
|
encodeBase64,
|
|
2551
2833
|
generateSessionId,
|
|
2834
|
+
generateTraceId,
|
|
2552
2835
|
getActiveModels,
|
|
2553
2836
|
getApiErrorMessage,
|
|
2554
2837
|
getCaptureQualityFeedback,
|
|
@@ -2572,6 +2855,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2572
2855
|
toHybridFrameData,
|
|
2573
2856
|
toLivenessResult,
|
|
2574
2857
|
toLivenessResultFromStream,
|
|
2858
|
+
toV2UploadFrameData,
|
|
2575
2859
|
validateApiKey,
|
|
2576
2860
|
validateFaceLandmarks,
|
|
2577
2861
|
validateFrameCount,
|
package/dist/index.mjs
CHANGED
|
@@ -19,7 +19,12 @@ var API_PATHS = {
|
|
|
19
19
|
hybrid150: "/api/v1/hybrid-150",
|
|
20
20
|
jobResult: "/api/v1/result",
|
|
21
21
|
queueStats: "/api/v1/queue/stats",
|
|
22
|
-
sessions: "/api/v1/sessions"
|
|
22
|
+
sessions: "/api/v1/sessions",
|
|
23
|
+
// v2 upload-first pipeline (MOV-1936). Separate from v1; frames are uploaded
|
|
24
|
+
// first and resolved server-side by session_id at check time.
|
|
25
|
+
v2Upload: "/api/v2/upload",
|
|
26
|
+
v2FastCheck: "/api/v2/fast-check",
|
|
27
|
+
v2LiveCheck: "/api/v2/live-check"
|
|
23
28
|
};
|
|
24
29
|
var RETRY_CONFIG = {
|
|
25
30
|
maxAttempts: 3,
|
|
@@ -99,7 +104,7 @@ async function sleep(ms) {
|
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
// package.json
|
|
102
|
-
var version = "3.
|
|
107
|
+
var version = "3.16.0";
|
|
103
108
|
|
|
104
109
|
// src/utils/deviceIntelligence.ts
|
|
105
110
|
var IPINFO_URL = "https://ipinfo.io/json";
|
|
@@ -147,6 +152,85 @@ async function collectDeviceIntelligence(opts) {
|
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
|
|
155
|
+
// src/forensic/clientFragment.ts
|
|
156
|
+
var FORENSIC_SCHEMA_VERSION = "1.0";
|
|
157
|
+
function defined(obj) {
|
|
158
|
+
const out = {};
|
|
159
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
160
|
+
if (v !== void 0 && v !== null) out[k] = v;
|
|
161
|
+
}
|
|
162
|
+
return out;
|
|
163
|
+
}
|
|
164
|
+
function generateTraceId() {
|
|
165
|
+
const c = globalThis.crypto;
|
|
166
|
+
if (c?.randomUUID) return c.randomUUID();
|
|
167
|
+
return "trace-" + Math.abs(hashString(String(Date.now()) + ":" + performanceNow())).toString(36);
|
|
168
|
+
}
|
|
169
|
+
function performanceNow() {
|
|
170
|
+
const p = globalThis.performance;
|
|
171
|
+
return p?.now ? p.now() : 0;
|
|
172
|
+
}
|
|
173
|
+
function hashString(s) {
|
|
174
|
+
let h = 0;
|
|
175
|
+
for (let i = 0; i < s.length; i++) h = Math.imul(31, h) + s.charCodeAt(i) | 0;
|
|
176
|
+
return h;
|
|
177
|
+
}
|
|
178
|
+
function buildClientFragment(traceId, source, summary, ctx = {}) {
|
|
179
|
+
const stages = [];
|
|
180
|
+
const capture = defined({
|
|
181
|
+
frames_captured: summary.framesCaptured,
|
|
182
|
+
alignment_mean: summary.alignmentMean,
|
|
183
|
+
blur_var_mean: summary.blurVarMean,
|
|
184
|
+
brightness_mean: summary.brightnessMean
|
|
185
|
+
});
|
|
186
|
+
if (Object.keys(capture).length) {
|
|
187
|
+
stages.push({ stage_id: "capture", layer: "sdk", ok: true, metrics: capture });
|
|
188
|
+
}
|
|
189
|
+
const landmarks = defined({
|
|
190
|
+
landmarks_present_pct: summary.landmarksPresentPct,
|
|
191
|
+
validation_pass_pct: summary.validationPassPct,
|
|
192
|
+
roll_mean: summary.rollMean,
|
|
193
|
+
yaw_mean: summary.yawMean,
|
|
194
|
+
pitch_mean: summary.pitchMean
|
|
195
|
+
});
|
|
196
|
+
if (Object.keys(landmarks).length) {
|
|
197
|
+
stages.push({ stage_id: "landmarks", layer: "sdk", ok: true, metrics: landmarks });
|
|
198
|
+
}
|
|
199
|
+
const encode = defined({
|
|
200
|
+
encoding: summary.encoding,
|
|
201
|
+
w: summary.width,
|
|
202
|
+
h: summary.height,
|
|
203
|
+
bytes_per_frame: summary.bytesPerFrame
|
|
204
|
+
});
|
|
205
|
+
if (Object.keys(encode).length) {
|
|
206
|
+
stages.push({ stage_id: "encode", layer: "sdk", ok: true, metrics: encode });
|
|
207
|
+
}
|
|
208
|
+
const transport = defined({
|
|
209
|
+
frames_sent: summary.framesSent,
|
|
210
|
+
request_bytes: summary.requestBytes,
|
|
211
|
+
session_id: summary.sessionId
|
|
212
|
+
});
|
|
213
|
+
if (Object.keys(transport).length) {
|
|
214
|
+
stages.push({ stage_id: "transport", layer: "wire", ok: true, metrics: transport });
|
|
215
|
+
}
|
|
216
|
+
return {
|
|
217
|
+
trace_id: traceId,
|
|
218
|
+
schema_version: FORENSIC_SCHEMA_VERSION,
|
|
219
|
+
source,
|
|
220
|
+
origin: defined({
|
|
221
|
+
sdk_version: ctx.sdkVersion,
|
|
222
|
+
demo_commit: ctx.demoCommit,
|
|
223
|
+
video_path: ctx.videoPath
|
|
224
|
+
}),
|
|
225
|
+
target: defined({
|
|
226
|
+
environment: ctx.environment,
|
|
227
|
+
model: ctx.model,
|
|
228
|
+
endpoint: ctx.endpoint
|
|
229
|
+
}),
|
|
230
|
+
stages
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
|
|
150
234
|
// src/client/LivenessClient.ts
|
|
151
235
|
var LivenessApiError = class extends Error {
|
|
152
236
|
constructor(message, code, statusCode, required, received) {
|
|
@@ -173,6 +257,16 @@ function toFrameData(frames) {
|
|
|
173
257
|
pixels: frame.pixels
|
|
174
258
|
}));
|
|
175
259
|
}
|
|
260
|
+
function toV2UploadFrameData(frames, options = {}) {
|
|
261
|
+
return frames.map((frame) => ({
|
|
262
|
+
index: frame.index,
|
|
263
|
+
timestamp_ms: frame.timestampMs,
|
|
264
|
+
pixels: frame.pixels,
|
|
265
|
+
...options.withLandmarks ? {
|
|
266
|
+
landmarks: frame.landmarks ? frame.landmarks.map((lm) => ({ x: lm.x, y: lm.y, z: lm.z })) : null
|
|
267
|
+
} : {}
|
|
268
|
+
}));
|
|
269
|
+
}
|
|
176
270
|
function toHybridFrameData(frames) {
|
|
177
271
|
return frames.map((frame) => ({
|
|
178
272
|
timestamp_ms: frame.timestampMs,
|
|
@@ -261,6 +355,7 @@ var LivenessClient = class _LivenessClient {
|
|
|
261
355
|
this.consumer = config.consumer;
|
|
262
356
|
this.trackClientTime = config.trackClientTime ?? false;
|
|
263
357
|
this.extraMetadata = config.extraMetadata;
|
|
358
|
+
this.traceId = config.traceId;
|
|
264
359
|
}
|
|
265
360
|
/**
|
|
266
361
|
* Merge additional device intelligence overrides into the existing set.
|
|
@@ -590,11 +685,14 @@ var LivenessClient = class _LivenessClient {
|
|
|
590
685
|
async liveCheck(frames, options = {}) {
|
|
591
686
|
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
592
687
|
const di = await this.getDeviceIntelligence();
|
|
688
|
+
const traceId = options.forensic?.trace_id ?? options.traceId ?? this.traceId ?? generateTraceId();
|
|
593
689
|
const request = {
|
|
594
690
|
session_id: options.sessionId ?? generateSessionId(),
|
|
595
691
|
model: options.model ?? "10",
|
|
596
692
|
source: options.source ?? "live",
|
|
597
693
|
frames: toLiveCheckFrameData(frames),
|
|
694
|
+
trace_id: traceId,
|
|
695
|
+
...options.forensic ? { forensic_client: options.forensic } : {},
|
|
598
696
|
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
599
697
|
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
600
698
|
...di ? { device_intelligence: di } : {},
|
|
@@ -605,7 +703,7 @@ var LivenessClient = class _LivenessClient {
|
|
|
605
703
|
{
|
|
606
704
|
method: "POST",
|
|
607
705
|
body: JSON.stringify(request),
|
|
608
|
-
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
706
|
+
headers: { ...this.buildModelVersionHeaders(effectiveVersion), "X-Trace-Id": traceId }
|
|
609
707
|
}
|
|
610
708
|
);
|
|
611
709
|
const result = toLivenessResult(response);
|
|
@@ -915,6 +1013,103 @@ var LivenessClient = class _LivenessClient {
|
|
|
915
1013
|
`${API_PATHS.jobResult}/${jobId}/wait?timeout=${timeout}`
|
|
916
1014
|
);
|
|
917
1015
|
}
|
|
1016
|
+
// ===========================================================================
|
|
1017
|
+
// v2 Pipeline (MOV-1936): upload-first
|
|
1018
|
+
//
|
|
1019
|
+
// Two-phase flow, fully separate from the v1 endpoints above:
|
|
1020
|
+
// 1. v2Upload() — buffer frames server-side (call once or repeatedly).
|
|
1021
|
+
// 2. v2FastCheck() / v2LiveCheck() — run inference; NO frames in the body,
|
|
1022
|
+
// the server resolves them by session_id from the prior upload(s).
|
|
1023
|
+
// The check responses reuse the v1 FastCheckResponse shape, so the existing
|
|
1024
|
+
// `toLivenessResult` + deprecation-header handling applies unchanged.
|
|
1025
|
+
// ===========================================================================
|
|
1026
|
+
/**
|
|
1027
|
+
* Buffer a batch of frames for a session (POST /api/v2/upload).
|
|
1028
|
+
*
|
|
1029
|
+
* Inference is NOT run here — this only stores frames keyed by `session_id`.
|
|
1030
|
+
* May be called multiple times for the same session (e.g. chunked during
|
|
1031
|
+
* capture); the server accumulates and reports cumulative progress.
|
|
1032
|
+
*
|
|
1033
|
+
* Set `withLandmarks` for the live-check path so MediaPipe landmarks travel
|
|
1034
|
+
* with each frame; leave it off for the fast-check path.
|
|
1035
|
+
*
|
|
1036
|
+
* @param frames - Captured frames to buffer
|
|
1037
|
+
* @param options - Session, model, source and upload flags
|
|
1038
|
+
* @returns Upload progress (frames_received / frames_required / is_complete)
|
|
1039
|
+
*/
|
|
1040
|
+
async v2Upload(frames, options) {
|
|
1041
|
+
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
1042
|
+
const request = {
|
|
1043
|
+
session_id: options.sessionId,
|
|
1044
|
+
model: options.model ?? "10",
|
|
1045
|
+
source: options.source ?? "live",
|
|
1046
|
+
frames: toV2UploadFrameData(frames, { withLandmarks: options.withLandmarks }),
|
|
1047
|
+
...options.fps != null ? { fps: options.fps } : {},
|
|
1048
|
+
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
1049
|
+
...options.warnings?.length ? { warnings: options.warnings } : {}
|
|
1050
|
+
};
|
|
1051
|
+
return this.requestWithRetry(API_PATHS.v2Upload, {
|
|
1052
|
+
method: "POST",
|
|
1053
|
+
body: JSON.stringify(request),
|
|
1054
|
+
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
1055
|
+
});
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Run a fast-check against frames previously uploaded via {@link v2Upload}
|
|
1059
|
+
* (POST /api/v2/fast-check). The request carries NO frames — the server
|
|
1060
|
+
* resolves them by `session_id`.
|
|
1061
|
+
*
|
|
1062
|
+
* @param options - Session and request context
|
|
1063
|
+
* @returns Liveness result
|
|
1064
|
+
*/
|
|
1065
|
+
async v2FastCheck(options) {
|
|
1066
|
+
return this.runV2Check(API_PATHS.v2FastCheck, options);
|
|
1067
|
+
}
|
|
1068
|
+
/**
|
|
1069
|
+
* Run a live-check against frames previously uploaded via {@link v2Upload}
|
|
1070
|
+
* with `withLandmarks: true` (POST /api/v2/live-check). The request carries
|
|
1071
|
+
* NO frames. The server requires a V3 model and surfaces the V3 `diagnostics`
|
|
1072
|
+
* block on the result.
|
|
1073
|
+
*
|
|
1074
|
+
* @param options - Session and request context
|
|
1075
|
+
* @returns Liveness result (includes `diagnostics`)
|
|
1076
|
+
*/
|
|
1077
|
+
async v2LiveCheck(options) {
|
|
1078
|
+
return this.runV2Check(API_PATHS.v2LiveCheck, options);
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* Shared body for the v2 fast-check / live-check requests (internal).
|
|
1082
|
+
* Both endpoints take the identical no-frames payload and response shape;
|
|
1083
|
+
* only the path differs.
|
|
1084
|
+
*/
|
|
1085
|
+
async runV2Check(path, options) {
|
|
1086
|
+
const effectiveVersion = options.modelVersion ?? this.modelVersion;
|
|
1087
|
+
const di = await this.getDeviceIntelligence();
|
|
1088
|
+
const metadata = this.buildMetadata();
|
|
1089
|
+
const request = {
|
|
1090
|
+
session_id: options.sessionId,
|
|
1091
|
+
model: options.model ?? "10",
|
|
1092
|
+
source: options.source ?? "live",
|
|
1093
|
+
...options.fps != null ? { fps: options.fps } : {},
|
|
1094
|
+
...options.frameCount != null ? { frame_count: options.frameCount } : {},
|
|
1095
|
+
...options.warnings?.length ? { warnings: options.warnings } : {},
|
|
1096
|
+
...di ? { device_intelligence: di } : {},
|
|
1097
|
+
...metadata ? { metadata } : {}
|
|
1098
|
+
};
|
|
1099
|
+
const { data: response, headers } = await this.requestWithRetryRaw(path, {
|
|
1100
|
+
method: "POST",
|
|
1101
|
+
body: JSON.stringify(request),
|
|
1102
|
+
headers: this.buildModelVersionHeaders(effectiveVersion)
|
|
1103
|
+
});
|
|
1104
|
+
const result = toLivenessResult(response);
|
|
1105
|
+
result.deprecation = _LivenessClient.parseDeprecationHeaders(headers);
|
|
1106
|
+
if (result.deprecation?.deprecated) {
|
|
1107
|
+
console.warn(
|
|
1108
|
+
`[Moveris] Model "${result.deprecation.resolvedModel}" is deprecated.` + (result.deprecation.suggestedModel ? ` Migrate to "${result.deprecation.suggestedModel}".` : "") + (result.deprecation.sunsetDate ? ` Sunset date: ${result.deprecation.sunsetDate}.` : "")
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
return result;
|
|
1112
|
+
}
|
|
918
1113
|
};
|
|
919
1114
|
|
|
920
1115
|
// src/buffer/FrameBuffer.ts
|
|
@@ -2319,6 +2514,85 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
|
|
|
2319
2514
|
message: null
|
|
2320
2515
|
};
|
|
2321
2516
|
}
|
|
2517
|
+
|
|
2518
|
+
// src/forensic/recorder.ts
|
|
2519
|
+
var RunningMean = class {
|
|
2520
|
+
constructor() {
|
|
2521
|
+
this.sum = 0;
|
|
2522
|
+
this.n = 0;
|
|
2523
|
+
}
|
|
2524
|
+
add(v) {
|
|
2525
|
+
if (typeof v === "number" && !Number.isNaN(v)) {
|
|
2526
|
+
this.sum += v;
|
|
2527
|
+
this.n += 1;
|
|
2528
|
+
}
|
|
2529
|
+
}
|
|
2530
|
+
get value() {
|
|
2531
|
+
return this.n ? this.sum / this.n : void 0;
|
|
2532
|
+
}
|
|
2533
|
+
};
|
|
2534
|
+
var ForensicRecorder = class {
|
|
2535
|
+
constructor(opts = {}) {
|
|
2536
|
+
this.framesCaptured = 0;
|
|
2537
|
+
this.landmarksPresent = 0;
|
|
2538
|
+
this.landmarksValid = 0;
|
|
2539
|
+
this.qualityTicks = 0;
|
|
2540
|
+
this.bytesTotal = 0;
|
|
2541
|
+
this.alignment = new RunningMean();
|
|
2542
|
+
this.blur = new RunningMean();
|
|
2543
|
+
this.brightness = new RunningMean();
|
|
2544
|
+
this.source = opts.source ?? "webcam";
|
|
2545
|
+
this.traceId = opts.traceId ?? generateTraceId();
|
|
2546
|
+
this.ctx = opts.ctx ?? {};
|
|
2547
|
+
}
|
|
2548
|
+
/** Record one captured frame (counts, landmark presence, encoded size). */
|
|
2549
|
+
onFrame(frame) {
|
|
2550
|
+
this.framesCaptured += 1;
|
|
2551
|
+
if (frame.landmarks && frame.landmarks.length) this.landmarksPresent += 1;
|
|
2552
|
+
if (typeof frame.pixels === "string") this.bytesTotal += frame.pixels.length;
|
|
2553
|
+
}
|
|
2554
|
+
/** Record one quality update (alignment/blur/brightness/landmark validity). */
|
|
2555
|
+
onQuality(q) {
|
|
2556
|
+
this.qualityTicks += 1;
|
|
2557
|
+
this.alignment.add(q.alignment);
|
|
2558
|
+
this.blur.add(q.blurVariance);
|
|
2559
|
+
this.brightness.add(q.brightness);
|
|
2560
|
+
if (q.landmarksValid) this.landmarksValid += 1;
|
|
2561
|
+
if (q.encoding) this.encoding = q.encoding;
|
|
2562
|
+
if (q.width) this.width = q.width;
|
|
2563
|
+
if (q.height) this.height = q.height;
|
|
2564
|
+
}
|
|
2565
|
+
/** Record the transport leg once the request is built/sent. */
|
|
2566
|
+
setTransport(t) {
|
|
2567
|
+
this.framesSent = t.framesSent;
|
|
2568
|
+
this.requestBytes = t.requestBytes;
|
|
2569
|
+
this.sessionId = t.sessionId;
|
|
2570
|
+
}
|
|
2571
|
+
/** Summarise the session into the CaptureSummary the fragment builder expects. */
|
|
2572
|
+
summary() {
|
|
2573
|
+
return {
|
|
2574
|
+
framesCaptured: this.framesCaptured,
|
|
2575
|
+
alignmentMean: this.alignment.value,
|
|
2576
|
+
blurVarMean: this.blur.value,
|
|
2577
|
+
brightnessMean: this.brightness.value,
|
|
2578
|
+
landmarksPresentPct: this.framesCaptured ? this.landmarksPresent / this.framesCaptured : void 0,
|
|
2579
|
+
// validity is a fraction of QUALITY ticks, not frames — quality updates fire
|
|
2580
|
+
// more often than frames, so dividing by frames could exceed 1.0.
|
|
2581
|
+
validationPassPct: this.qualityTicks ? this.landmarksValid / this.qualityTicks : void 0,
|
|
2582
|
+
encoding: this.encoding,
|
|
2583
|
+
width: this.width,
|
|
2584
|
+
height: this.height,
|
|
2585
|
+
bytesPerFrame: this.framesCaptured && this.bytesTotal ? Math.round(this.bytesTotal / this.framesCaptured) : void 0,
|
|
2586
|
+
framesSent: this.framesSent ?? (this.framesCaptured || void 0),
|
|
2587
|
+
requestBytes: this.requestBytes,
|
|
2588
|
+
sessionId: this.sessionId
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
/** Build the client fragment for this session. */
|
|
2592
|
+
build() {
|
|
2593
|
+
return buildClientFragment(this.traceId, this.source, this.summary(), this.ctx);
|
|
2594
|
+
}
|
|
2595
|
+
};
|
|
2322
2596
|
export {
|
|
2323
2597
|
ALIGNMENT_THRESHOLD_CAPTURE,
|
|
2324
2598
|
ALIGNMENT_THRESHOLD_GOOD,
|
|
@@ -2353,8 +2627,10 @@ export {
|
|
|
2353
2627
|
FACE_CROP_FRAME_MARGIN,
|
|
2354
2628
|
FACE_CROP_OUTPUT_SIZE,
|
|
2355
2629
|
FEEDBACK_MESSAGES,
|
|
2630
|
+
FORENSIC_SCHEMA_VERSION,
|
|
2356
2631
|
FRAME_BUFFER_CONFIG,
|
|
2357
2632
|
FRAME_CONFIG,
|
|
2633
|
+
ForensicRecorder,
|
|
2358
2634
|
FrameBuffer,
|
|
2359
2635
|
FrameQueue,
|
|
2360
2636
|
GOOD_ALIGNMENT,
|
|
@@ -2392,6 +2668,7 @@ export {
|
|
|
2392
2668
|
analyzeEyeRegionBrightness,
|
|
2393
2669
|
analyzeEyeRegionContrast,
|
|
2394
2670
|
analyzeLighting,
|
|
2671
|
+
buildClientFragment,
|
|
2395
2672
|
calculateBrightness,
|
|
2396
2673
|
calculateFaceAlignment,
|
|
2397
2674
|
calculateFaceCropRegion,
|
|
@@ -2406,6 +2683,7 @@ export {
|
|
|
2406
2683
|
detectSpecularHighlights,
|
|
2407
2684
|
encodeBase64,
|
|
2408
2685
|
generateSessionId,
|
|
2686
|
+
generateTraceId,
|
|
2409
2687
|
getActiveModels,
|
|
2410
2688
|
getApiErrorMessage,
|
|
2411
2689
|
getCaptureQualityFeedback,
|
|
@@ -2429,6 +2707,7 @@ export {
|
|
|
2429
2707
|
toHybridFrameData,
|
|
2430
2708
|
toLivenessResult,
|
|
2431
2709
|
toLivenessResultFromStream,
|
|
2710
|
+
toV2UploadFrameData,
|
|
2432
2711
|
validateApiKey,
|
|
2433
2712
|
validateFaceLandmarks,
|
|
2434
2713
|
validateFrameCount,
|