@moveris/shared 3.15.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 CHANGED
@@ -97,6 +97,8 @@ interface LiveCheckRequest {
97
97
  metadata?: {
98
98
  consumer?: ConsumerContext;
99
99
  } | null;
100
+ trace_id?: string;
101
+ forensic_client?: unknown;
100
102
  }
101
103
  interface V2UploadFrameData extends FrameData {
102
104
  landmarks?: {
@@ -639,6 +641,59 @@ interface CameraValidationResult {
639
641
  }
640
642
  declare const DEFAULT_CAMERA_REQUIREMENTS: CameraRequirements;
641
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
+
642
697
  interface LivenessClientConfig {
643
698
  baseUrl?: string;
644
699
  apiKey: string;
@@ -650,6 +705,7 @@ interface LivenessClientConfig {
650
705
  consumer?: ConsumerContext;
651
706
  trackClientTime?: boolean;
652
707
  extraMetadata?: ExtraMetadata;
708
+ traceId?: string;
653
709
  }
654
710
  declare class LivenessApiError extends Error {
655
711
  readonly code: string;
@@ -676,6 +732,7 @@ declare class LivenessClient {
676
732
  private readonly consumer;
677
733
  private readonly trackClientTime;
678
734
  private readonly extraMetadata;
735
+ private readonly traceId;
679
736
  private diCollected;
680
737
  private diCollecting;
681
738
  private diOverrides;
@@ -713,6 +770,8 @@ declare class LivenessClient {
713
770
  frameCount?: number;
714
771
  source?: FrameSource;
715
772
  warnings?: string[];
773
+ traceId?: string;
774
+ forensic?: ClientFragment;
716
775
  }): Promise<LivenessResult>;
717
776
  fastCheckCrops(crops: CropData[], options?: {
718
777
  sessionId?: string;
@@ -1075,4 +1134,51 @@ declare function collectDeviceIntelligence(opts?: {
1075
1134
  platformVersion?: string;
1076
1135
  }): Promise<DeviceIntelligence | null>;
1077
1136
 
1078
- 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 ExtraMetadata, 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, 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 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, 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, toV2UploadFrameData, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
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,6 +97,8 @@ interface LiveCheckRequest {
97
97
  metadata?: {
98
98
  consumer?: ConsumerContext;
99
99
  } | null;
100
+ trace_id?: string;
101
+ forensic_client?: unknown;
100
102
  }
101
103
  interface V2UploadFrameData extends FrameData {
102
104
  landmarks?: {
@@ -639,6 +641,59 @@ interface CameraValidationResult {
639
641
  }
640
642
  declare const DEFAULT_CAMERA_REQUIREMENTS: CameraRequirements;
641
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
+
642
697
  interface LivenessClientConfig {
643
698
  baseUrl?: string;
644
699
  apiKey: string;
@@ -650,6 +705,7 @@ interface LivenessClientConfig {
650
705
  consumer?: ConsumerContext;
651
706
  trackClientTime?: boolean;
652
707
  extraMetadata?: ExtraMetadata;
708
+ traceId?: string;
653
709
  }
654
710
  declare class LivenessApiError extends Error {
655
711
  readonly code: string;
@@ -676,6 +732,7 @@ declare class LivenessClient {
676
732
  private readonly consumer;
677
733
  private readonly trackClientTime;
678
734
  private readonly extraMetadata;
735
+ private readonly traceId;
679
736
  private diCollected;
680
737
  private diCollecting;
681
738
  private diOverrides;
@@ -713,6 +770,8 @@ declare class LivenessClient {
713
770
  frameCount?: number;
714
771
  source?: FrameSource;
715
772
  warnings?: string[];
773
+ traceId?: string;
774
+ forensic?: ClientFragment;
716
775
  }): Promise<LivenessResult>;
717
776
  fastCheckCrops(crops: CropData[], options?: {
718
777
  sessionId?: string;
@@ -1075,4 +1134,51 @@ declare function collectDeviceIntelligence(opts?: {
1075
1134
  platformVersion?: string;
1076
1135
  }): Promise<DeviceIntelligence | null>;
1077
1136
 
1078
- 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 ExtraMetadata, 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, 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 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, 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, toV2UploadFrameData, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
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,
@@ -247,7 +251,7 @@ async function sleep(ms) {
247
251
  }
248
252
 
249
253
  // package.json
250
- var version = "3.15.0";
254
+ var version = "3.16.0";
251
255
 
252
256
  // src/utils/deviceIntelligence.ts
253
257
  var IPINFO_URL = "https://ipinfo.io/json";
@@ -295,6 +299,85 @@ async function collectDeviceIntelligence(opts) {
295
299
  }
296
300
  }
297
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
+
298
381
  // src/client/LivenessClient.ts
299
382
  var LivenessApiError = class extends Error {
300
383
  constructor(message, code, statusCode, required, received) {
@@ -419,6 +502,7 @@ var LivenessClient = class _LivenessClient {
419
502
  this.consumer = config.consumer;
420
503
  this.trackClientTime = config.trackClientTime ?? false;
421
504
  this.extraMetadata = config.extraMetadata;
505
+ this.traceId = config.traceId;
422
506
  }
423
507
  /**
424
508
  * Merge additional device intelligence overrides into the existing set.
@@ -748,11 +832,14 @@ var LivenessClient = class _LivenessClient {
748
832
  async liveCheck(frames, options = {}) {
749
833
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
750
834
  const di = await this.getDeviceIntelligence();
835
+ const traceId = options.forensic?.trace_id ?? options.traceId ?? this.traceId ?? generateTraceId();
751
836
  const request = {
752
837
  session_id: options.sessionId ?? generateSessionId(),
753
838
  model: options.model ?? "10",
754
839
  source: options.source ?? "live",
755
840
  frames: toLiveCheckFrameData(frames),
841
+ trace_id: traceId,
842
+ ...options.forensic ? { forensic_client: options.forensic } : {},
756
843
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
757
844
  ...options.warnings?.length ? { warnings: options.warnings } : {},
758
845
  ...di ? { device_intelligence: di } : {},
@@ -763,7 +850,7 @@ var LivenessClient = class _LivenessClient {
763
850
  {
764
851
  method: "POST",
765
852
  body: JSON.stringify(request),
766
- headers: this.buildModelVersionHeaders(effectiveVersion)
853
+ headers: { ...this.buildModelVersionHeaders(effectiveVersion), "X-Trace-Id": traceId }
767
854
  }
768
855
  );
769
856
  const result = toLivenessResult(response);
@@ -2574,6 +2661,85 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2574
2661
  message: null
2575
2662
  };
2576
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
+ };
2577
2743
  // Annotate the CommonJS export names for ESM import in node:
2578
2744
  0 && (module.exports = {
2579
2745
  ALIGNMENT_THRESHOLD_CAPTURE,
@@ -2609,8 +2775,10 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2609
2775
  FACE_CROP_FRAME_MARGIN,
2610
2776
  FACE_CROP_OUTPUT_SIZE,
2611
2777
  FEEDBACK_MESSAGES,
2778
+ FORENSIC_SCHEMA_VERSION,
2612
2779
  FRAME_BUFFER_CONFIG,
2613
2780
  FRAME_CONFIG,
2781
+ ForensicRecorder,
2614
2782
  FrameBuffer,
2615
2783
  FrameQueue,
2616
2784
  GOOD_ALIGNMENT,
@@ -2648,6 +2816,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2648
2816
  analyzeEyeRegionBrightness,
2649
2817
  analyzeEyeRegionContrast,
2650
2818
  analyzeLighting,
2819
+ buildClientFragment,
2651
2820
  calculateBrightness,
2652
2821
  calculateFaceAlignment,
2653
2822
  calculateFaceCropRegion,
@@ -2662,6 +2831,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2662
2831
  detectSpecularHighlights,
2663
2832
  encodeBase64,
2664
2833
  generateSessionId,
2834
+ generateTraceId,
2665
2835
  getActiveModels,
2666
2836
  getApiErrorMessage,
2667
2837
  getCaptureQualityFeedback,
package/dist/index.mjs CHANGED
@@ -104,7 +104,7 @@ async function sleep(ms) {
104
104
  }
105
105
 
106
106
  // package.json
107
- var version = "3.15.0";
107
+ var version = "3.16.0";
108
108
 
109
109
  // src/utils/deviceIntelligence.ts
110
110
  var IPINFO_URL = "https://ipinfo.io/json";
@@ -152,6 +152,85 @@ async function collectDeviceIntelligence(opts) {
152
152
  }
153
153
  }
154
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
+
155
234
  // src/client/LivenessClient.ts
156
235
  var LivenessApiError = class extends Error {
157
236
  constructor(message, code, statusCode, required, received) {
@@ -276,6 +355,7 @@ var LivenessClient = class _LivenessClient {
276
355
  this.consumer = config.consumer;
277
356
  this.trackClientTime = config.trackClientTime ?? false;
278
357
  this.extraMetadata = config.extraMetadata;
358
+ this.traceId = config.traceId;
279
359
  }
280
360
  /**
281
361
  * Merge additional device intelligence overrides into the existing set.
@@ -605,11 +685,14 @@ var LivenessClient = class _LivenessClient {
605
685
  async liveCheck(frames, options = {}) {
606
686
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
607
687
  const di = await this.getDeviceIntelligence();
688
+ const traceId = options.forensic?.trace_id ?? options.traceId ?? this.traceId ?? generateTraceId();
608
689
  const request = {
609
690
  session_id: options.sessionId ?? generateSessionId(),
610
691
  model: options.model ?? "10",
611
692
  source: options.source ?? "live",
612
693
  frames: toLiveCheckFrameData(frames),
694
+ trace_id: traceId,
695
+ ...options.forensic ? { forensic_client: options.forensic } : {},
613
696
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
614
697
  ...options.warnings?.length ? { warnings: options.warnings } : {},
615
698
  ...di ? { device_intelligence: di } : {},
@@ -620,7 +703,7 @@ var LivenessClient = class _LivenessClient {
620
703
  {
621
704
  method: "POST",
622
705
  body: JSON.stringify(request),
623
- headers: this.buildModelVersionHeaders(effectiveVersion)
706
+ headers: { ...this.buildModelVersionHeaders(effectiveVersion), "X-Trace-Id": traceId }
624
707
  }
625
708
  );
626
709
  const result = toLivenessResult(response);
@@ -2431,6 +2514,85 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2431
2514
  message: null
2432
2515
  };
2433
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
+ };
2434
2596
  export {
2435
2597
  ALIGNMENT_THRESHOLD_CAPTURE,
2436
2598
  ALIGNMENT_THRESHOLD_GOOD,
@@ -2465,8 +2627,10 @@ export {
2465
2627
  FACE_CROP_FRAME_MARGIN,
2466
2628
  FACE_CROP_OUTPUT_SIZE,
2467
2629
  FEEDBACK_MESSAGES,
2630
+ FORENSIC_SCHEMA_VERSION,
2468
2631
  FRAME_BUFFER_CONFIG,
2469
2632
  FRAME_CONFIG,
2633
+ ForensicRecorder,
2470
2634
  FrameBuffer,
2471
2635
  FrameQueue,
2472
2636
  GOOD_ALIGNMENT,
@@ -2504,6 +2668,7 @@ export {
2504
2668
  analyzeEyeRegionBrightness,
2505
2669
  analyzeEyeRegionContrast,
2506
2670
  analyzeLighting,
2671
+ buildClientFragment,
2507
2672
  calculateBrightness,
2508
2673
  calculateFaceAlignment,
2509
2674
  calculateFaceCropRegion,
@@ -2518,6 +2683,7 @@ export {
2518
2683
  detectSpecularHighlights,
2519
2684
  encodeBase64,
2520
2685
  generateSessionId,
2686
+ generateTraceId,
2521
2687
  getActiveModels,
2522
2688
  getApiErrorMessage,
2523
2689
  getCaptureQualityFeedback,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moveris/shared",
3
- "version": "3.15.0",
3
+ "version": "3.16.0",
4
4
  "description": "Core business logic for Moveris Live SDK",
5
5
  "author": "Moveris - Jeyson Palacio, Arthur ITurres, Eric D. Brown",
6
6
  "main": "./dist/index.js",