@moveris/shared 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -550,6 +550,7 @@ const status = getStatusMessage('capturing', DEFAULT_LOCALE);
550
550
  | `camera_angle_low` | "Raise camera to eye level" | Camera below face (Y < 0.3) |
551
551
  | `camera_angle_high` | "Lower camera to eye level" | Camera above face (Y > 0.7) |
552
552
  | `camera_tilted` | "Hold camera level" | Device tilt > 15° (via transformation matrix, falls back to eye-corner landmarks) |
553
+ | `flat_lighting` | "Find better lighting" | Face-region dynamic range < 180 L-units — soft warning, capture continues |
553
554
 
554
555
  ---
555
556
 
@@ -677,6 +678,13 @@ if (facialTransformationMatrix) {
677
678
  const angleResult = detectCameraAngle(landmarks); // requires ≥153 landmarks
678
679
  // { ratio: number, cameraAbove: boolean, cameraBelow: boolean }
679
680
  // ratio ~1.0 at eye level; >1.35 = camera above user; <0.75 = camera below user
681
+
682
+ // Analyse face-region dynamic range (flat lighting detection, MOV-1280)
683
+ // Pass pre-sampled face-region pixels — use sampleFaceRegionPixels() in @moveris/react
684
+ import { analyzeDynamicRange } from '@moveris/shared';
685
+ const drResult = analyzeDynamicRange(rgbaPixels);
686
+ // { range: number (max L* − min L*), isLow: boolean (< 180) }
687
+ // Soft warning — do not use as a hard capture gate
680
688
  ```
681
689
 
682
690
  #### Crop Constants (aligned with cognito-check)
package/dist/index.d.mts CHANGED
@@ -341,6 +341,14 @@ declare const MAX_FACE_RATIO = 0.7;
341
341
  declare const FACE_CROP_OUTPUT_SIZE = 224;
342
342
  declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
343
343
  declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
344
+ declare const DYNAMIC_RANGE_WARNING_THRESHOLD = 180;
345
+ interface DynamicRangeAnalysis {
346
+ range: number;
347
+ isLow: boolean;
348
+ }
349
+ declare function srgbToLinear(c: number): number;
350
+ declare function linearRgbToLabL(rLin: number, gLin: number, bLin: number): number;
351
+ declare function analyzeDynamicRange(rgbaPixels: Uint8ClampedArray | number[]): DynamicRangeAnalysis;
344
352
  declare function analyzeBlur(grayscalePixels: number[], width: number, height: number, threshold?: number): BlurAnalysis;
345
353
  declare function rgbaToGrayscale(rgbaPixels: Uint8ClampedArray | number[]): number[];
346
354
  declare function calculateBrightness(rgbaPixels: Uint8ClampedArray | number[]): number;
@@ -750,6 +758,7 @@ declare const FEEDBACK_MESSAGES: {
750
758
  readonly poor_lighting: "Improve lighting";
751
759
  readonly too_dark: "Low lighting - move to a brighter area";
752
760
  readonly backlit: "Backlit - try facing the light source";
761
+ readonly flat_lighting: "Find better lighting";
753
762
  readonly camera_angle_low: "Raise camera to eye level";
754
763
  readonly camera_angle_high: "Lower camera to eye level";
755
764
  readonly camera_tilted: "Hold camera level";
@@ -789,6 +798,7 @@ interface CaptureQualityState {
789
798
  cameraAngleHigh?: boolean;
790
799
  cameraTilted?: boolean;
791
800
  faceAreaLow?: boolean;
801
+ dynamicRangeLow?: boolean;
792
802
  }
793
803
  declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
794
804
  declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
@@ -839,4 +849,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
839
849
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
840
850
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
841
851
 
842
- 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 CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, 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, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, 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, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, 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, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
852
+ 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 CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, 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 DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, 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, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, 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, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
package/dist/index.d.ts CHANGED
@@ -341,6 +341,14 @@ declare const MAX_FACE_RATIO = 0.7;
341
341
  declare const FACE_CROP_OUTPUT_SIZE = 224;
342
342
  declare const MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
343
343
  declare const TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
344
+ declare const DYNAMIC_RANGE_WARNING_THRESHOLD = 180;
345
+ interface DynamicRangeAnalysis {
346
+ range: number;
347
+ isLow: boolean;
348
+ }
349
+ declare function srgbToLinear(c: number): number;
350
+ declare function linearRgbToLabL(rLin: number, gLin: number, bLin: number): number;
351
+ declare function analyzeDynamicRange(rgbaPixels: Uint8ClampedArray | number[]): DynamicRangeAnalysis;
344
352
  declare function analyzeBlur(grayscalePixels: number[], width: number, height: number, threshold?: number): BlurAnalysis;
345
353
  declare function rgbaToGrayscale(rgbaPixels: Uint8ClampedArray | number[]): number[];
346
354
  declare function calculateBrightness(rgbaPixels: Uint8ClampedArray | number[]): number;
@@ -750,6 +758,7 @@ declare const FEEDBACK_MESSAGES: {
750
758
  readonly poor_lighting: "Improve lighting";
751
759
  readonly too_dark: "Low lighting - move to a brighter area";
752
760
  readonly backlit: "Backlit - try facing the light source";
761
+ readonly flat_lighting: "Find better lighting";
753
762
  readonly camera_angle_low: "Raise camera to eye level";
754
763
  readonly camera_angle_high: "Lower camera to eye level";
755
764
  readonly camera_tilted: "Hold camera level";
@@ -789,6 +798,7 @@ interface CaptureQualityState {
789
798
  cameraAngleHigh?: boolean;
790
799
  cameraTilted?: boolean;
791
800
  faceAreaLow?: boolean;
801
+ dynamicRangeLow?: boolean;
792
802
  }
793
803
  declare function getCaptureQualityFeedback(state: CaptureQualityState): string;
794
804
  declare function canCaptureFrame(state: Omit<CaptureQualityState, 'framesCaptured' | 'targetFrames' | 'isCapturing'>): boolean;
@@ -839,4 +849,4 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
839
849
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
840
850
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
841
851
 
842
- 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 CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, 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, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, 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, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, 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, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, retryWithBackoff, rgbaToGrayscale, sleep, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
852
+ 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 CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, 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 DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, FACE_CENTER_VERTICAL_OFFSET, 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, IDEAL_CROP_MULTIPLIER, type JobStatus, type JobStatusResponse, LANDMARK_INDEX, LANDMARK_MAX_BOUND, LANDMARK_MIN_BOUND, LOW_LIGHT_THRESHOLD, type LandmarkValidationResult, type LightingAnalysis, LivenessApiError, type LivenessCallbacks, LivenessClient, type LivenessClientConfig, type LivenessConfig, type LivenessResult, type LivenessState, MAX_CROP_MULTIPLIER, MAX_FACE_PERCENTAGE_IN_CROP, MAX_FACE_RATIO, MAX_FACE_ROLL_DEGREES, MAX_IDEAL_FACE_RATIO, MIN_CAPTURE_ALIGNMENT, MIN_CROP_MULTIPLIER, 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, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
package/dist/index.js CHANGED
@@ -43,6 +43,7 @@ __export(index_exports, {
43
43
  DEFAULT_OVAL_REGION: () => DEFAULT_OVAL_REGION,
44
44
  DEFAULT_STABILIZER_CONFIG: () => DEFAULT_STABILIZER_CONFIG,
45
45
  DEFAULT_STATUS_MESSAGES: () => DEFAULT_STATUS_MESSAGES,
46
+ DYNAMIC_RANGE_WARNING_THRESHOLD: () => DYNAMIC_RANGE_WARNING_THRESHOLD,
46
47
  ERROR_MESSAGES: () => ERROR_MESSAGES,
47
48
  ERROR_MESSAGES_ES: () => ERROR_MESSAGES_ES,
48
49
  ES_LOCALE: () => ES_LOCALE,
@@ -88,6 +89,7 @@ __export(index_exports, {
88
89
  TARGET_FACE_PERCENTAGE_IN_CROP: () => TARGET_FACE_PERCENTAGE_IN_CROP,
89
90
  VALID_FRAME_COUNTS: () => VALID_FRAME_COUNTS,
90
91
  analyzeBlur: () => analyzeBlur,
92
+ analyzeDynamicRange: () => analyzeDynamicRange,
91
93
  analyzeEyeRegionBrightness: () => analyzeEyeRegionBrightness,
92
94
  analyzeEyeRegionContrast: () => analyzeEyeRegionContrast,
93
95
  analyzeLighting: () => analyzeLighting,
@@ -119,9 +121,11 @@ __export(index_exports, {
119
121
  isFaceFullyVisible: () => isFaceFullyVisible,
120
122
  isFaceInOval: () => isFaceInOval,
121
123
  isRetryableError: () => isRetryableError,
124
+ linearRgbToLabL: () => linearRgbToLabL,
122
125
  retryWithBackoff: () => retryWithBackoff,
123
126
  rgbaToGrayscale: () => rgbaToGrayscale,
124
127
  sleep: () => sleep,
128
+ srgbToLinear: () => srgbToLinear,
125
129
  toFrameData: () => toFrameData,
126
130
  toHybridFrameData: () => toHybridFrameData,
127
131
  toLivenessResult: () => toLivenessResult,
@@ -1294,6 +1298,10 @@ function getApiErrorMessage(code, message, customMessages) {
1294
1298
  if (code) {
1295
1299
  const custom = customMessages?.[code];
1296
1300
  if (custom) return custom;
1301
+ if (code === API_ERROR_CODES.MISSING_FIELD && message && !looksLikeRawData(message)) {
1302
+ const field = message.replace(" is required", "").trim();
1303
+ if (field) return `Missing required field: ${field} \u2014 please try again`;
1304
+ }
1297
1305
  const known = ERROR_MESSAGES[code];
1298
1306
  if (known) return known;
1299
1307
  }
@@ -1377,6 +1385,7 @@ var FEEDBACK_MESSAGES = {
1377
1385
  poor_lighting: "Improve lighting",
1378
1386
  too_dark: "Low lighting - move to a brighter area",
1379
1387
  backlit: "Backlit - try facing the light source",
1388
+ flat_lighting: "Find better lighting",
1380
1389
  // Camera angle (platform-agnostic)
1381
1390
  camera_angle_low: "Raise camera to eye level",
1382
1391
  camera_angle_high: "Lower camera to eye level",
@@ -1440,6 +1449,7 @@ var ES_LOCALE = {
1440
1449
  poor_lighting: "Mejora la iluminaci\xF3n",
1441
1450
  too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
1442
1451
  backlit: "Contraluz - intenta mirar hacia la fuente de luz",
1452
+ flat_lighting: "Busca mejor iluminaci\xF3n",
1443
1453
  // Camera angle (platform-agnostic)
1444
1454
  camera_angle_low: "Levanta la c\xE1mara a la altura de los ojos",
1445
1455
  camera_angle_high: "Baja la c\xE1mara a la altura de los ojos",
@@ -1482,7 +1492,8 @@ function getCaptureQualityFeedback(state) {
1482
1492
  cameraAngleLow,
1483
1493
  cameraAngleHigh,
1484
1494
  cameraTilted,
1485
- faceAreaLow
1495
+ faceAreaLow,
1496
+ dynamicRangeLow
1486
1497
  } = state;
1487
1498
  if (!hasFace) {
1488
1499
  return FEEDBACK_MESSAGES.no_face;
@@ -1508,6 +1519,9 @@ function getCaptureQualityFeedback(state) {
1508
1519
  if (isBlurry) {
1509
1520
  return FEEDBACK_MESSAGES.blurry;
1510
1521
  }
1522
+ if (dynamicRangeLow) {
1523
+ return FEEDBACK_MESSAGES.flat_lighting;
1524
+ }
1511
1525
  if (tooFarFromIdeal) {
1512
1526
  return FEEDBACK_MESSAGES.move_closer_ideal;
1513
1527
  }
@@ -1632,6 +1646,33 @@ var MAX_FACE_RATIO = 0.7;
1632
1646
  var FACE_CROP_OUTPUT_SIZE = 224;
1633
1647
  var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
1634
1648
  var TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
1649
+ var DYNAMIC_RANGE_WARNING_THRESHOLD = 180;
1650
+ function srgbToLinear(c) {
1651
+ const n = c / 255;
1652
+ return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
1653
+ }
1654
+ function linearRgbToLabL(rLin, gLin, bLin) {
1655
+ const Y = 0.2126 * rLin + 0.7152 * gLin + 0.0722 * bLin;
1656
+ const f = Y > 8856e-6 ? Math.cbrt(Y) : 7.787 * Y + 16 / 116;
1657
+ return 116 * f - 16;
1658
+ }
1659
+ function analyzeDynamicRange(rgbaPixels) {
1660
+ let minL = Infinity;
1661
+ let maxL = -Infinity;
1662
+ for (let i = 0; i < rgbaPixels.length; i += 4) {
1663
+ const r = rgbaPixels[i] ?? 0;
1664
+ const g = rgbaPixels[i + 1] ?? 0;
1665
+ const b = rgbaPixels[i + 2] ?? 0;
1666
+ const L = linearRgbToLabL(srgbToLinear(r), srgbToLinear(g), srgbToLinear(b));
1667
+ if (L < minL) minL = L;
1668
+ if (L > maxL) maxL = L;
1669
+ }
1670
+ if (!isFinite(minL) || !isFinite(maxL)) {
1671
+ return { range: 0, isLow: false };
1672
+ }
1673
+ const range = maxL - minL;
1674
+ return { range, isLow: range < DYNAMIC_RANGE_WARNING_THRESHOLD };
1675
+ }
1635
1676
  function analyzeBlur(grayscalePixels, width, height, threshold = DEFAULT_BLUR_THRESHOLD) {
1636
1677
  const laplacian = [];
1637
1678
  for (let y = 1; y < height - 1; y++) {
@@ -2180,6 +2221,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2180
2221
  DEFAULT_OVAL_REGION,
2181
2222
  DEFAULT_STABILIZER_CONFIG,
2182
2223
  DEFAULT_STATUS_MESSAGES,
2224
+ DYNAMIC_RANGE_WARNING_THRESHOLD,
2183
2225
  ERROR_MESSAGES,
2184
2226
  ERROR_MESSAGES_ES,
2185
2227
  ES_LOCALE,
@@ -2225,6 +2267,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2225
2267
  TARGET_FACE_PERCENTAGE_IN_CROP,
2226
2268
  VALID_FRAME_COUNTS,
2227
2269
  analyzeBlur,
2270
+ analyzeDynamicRange,
2228
2271
  analyzeEyeRegionBrightness,
2229
2272
  analyzeEyeRegionContrast,
2230
2273
  analyzeLighting,
@@ -2256,9 +2299,11 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2256
2299
  isFaceFullyVisible,
2257
2300
  isFaceInOval,
2258
2301
  isRetryableError,
2302
+ linearRgbToLabL,
2259
2303
  retryWithBackoff,
2260
2304
  rgbaToGrayscale,
2261
2305
  sleep,
2306
+ srgbToLinear,
2262
2307
  toFrameData,
2263
2308
  toHybridFrameData,
2264
2309
  toLivenessResult,
package/dist/index.mjs CHANGED
@@ -1155,6 +1155,10 @@ function getApiErrorMessage(code, message, customMessages) {
1155
1155
  if (code) {
1156
1156
  const custom = customMessages?.[code];
1157
1157
  if (custom) return custom;
1158
+ if (code === API_ERROR_CODES.MISSING_FIELD && message && !looksLikeRawData(message)) {
1159
+ const field = message.replace(" is required", "").trim();
1160
+ if (field) return `Missing required field: ${field} \u2014 please try again`;
1161
+ }
1158
1162
  const known = ERROR_MESSAGES[code];
1159
1163
  if (known) return known;
1160
1164
  }
@@ -1238,6 +1242,7 @@ var FEEDBACK_MESSAGES = {
1238
1242
  poor_lighting: "Improve lighting",
1239
1243
  too_dark: "Low lighting - move to a brighter area",
1240
1244
  backlit: "Backlit - try facing the light source",
1245
+ flat_lighting: "Find better lighting",
1241
1246
  // Camera angle (platform-agnostic)
1242
1247
  camera_angle_low: "Raise camera to eye level",
1243
1248
  camera_angle_high: "Lower camera to eye level",
@@ -1301,6 +1306,7 @@ var ES_LOCALE = {
1301
1306
  poor_lighting: "Mejora la iluminaci\xF3n",
1302
1307
  too_dark: "Poca luz - mu\xE9vete a un \xE1rea m\xE1s iluminada",
1303
1308
  backlit: "Contraluz - intenta mirar hacia la fuente de luz",
1309
+ flat_lighting: "Busca mejor iluminaci\xF3n",
1304
1310
  // Camera angle (platform-agnostic)
1305
1311
  camera_angle_low: "Levanta la c\xE1mara a la altura de los ojos",
1306
1312
  camera_angle_high: "Baja la c\xE1mara a la altura de los ojos",
@@ -1343,7 +1349,8 @@ function getCaptureQualityFeedback(state) {
1343
1349
  cameraAngleLow,
1344
1350
  cameraAngleHigh,
1345
1351
  cameraTilted,
1346
- faceAreaLow
1352
+ faceAreaLow,
1353
+ dynamicRangeLow
1347
1354
  } = state;
1348
1355
  if (!hasFace) {
1349
1356
  return FEEDBACK_MESSAGES.no_face;
@@ -1369,6 +1376,9 @@ function getCaptureQualityFeedback(state) {
1369
1376
  if (isBlurry) {
1370
1377
  return FEEDBACK_MESSAGES.blurry;
1371
1378
  }
1379
+ if (dynamicRangeLow) {
1380
+ return FEEDBACK_MESSAGES.flat_lighting;
1381
+ }
1372
1382
  if (tooFarFromIdeal) {
1373
1383
  return FEEDBACK_MESSAGES.move_closer_ideal;
1374
1384
  }
@@ -1493,6 +1503,33 @@ var MAX_FACE_RATIO = 0.7;
1493
1503
  var FACE_CROP_OUTPUT_SIZE = 224;
1494
1504
  var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
1495
1505
  var TARGET_FACE_PERCENTAGE_IN_CROP = 0.33;
1506
+ var DYNAMIC_RANGE_WARNING_THRESHOLD = 180;
1507
+ function srgbToLinear(c) {
1508
+ const n = c / 255;
1509
+ return n <= 0.04045 ? n / 12.92 : Math.pow((n + 0.055) / 1.055, 2.4);
1510
+ }
1511
+ function linearRgbToLabL(rLin, gLin, bLin) {
1512
+ const Y = 0.2126 * rLin + 0.7152 * gLin + 0.0722 * bLin;
1513
+ const f = Y > 8856e-6 ? Math.cbrt(Y) : 7.787 * Y + 16 / 116;
1514
+ return 116 * f - 16;
1515
+ }
1516
+ function analyzeDynamicRange(rgbaPixels) {
1517
+ let minL = Infinity;
1518
+ let maxL = -Infinity;
1519
+ for (let i = 0; i < rgbaPixels.length; i += 4) {
1520
+ const r = rgbaPixels[i] ?? 0;
1521
+ const g = rgbaPixels[i + 1] ?? 0;
1522
+ const b = rgbaPixels[i + 2] ?? 0;
1523
+ const L = linearRgbToLabL(srgbToLinear(r), srgbToLinear(g), srgbToLinear(b));
1524
+ if (L < minL) minL = L;
1525
+ if (L > maxL) maxL = L;
1526
+ }
1527
+ if (!isFinite(minL) || !isFinite(maxL)) {
1528
+ return { range: 0, isLow: false };
1529
+ }
1530
+ const range = maxL - minL;
1531
+ return { range, isLow: range < DYNAMIC_RANGE_WARNING_THRESHOLD };
1532
+ }
1496
1533
  function analyzeBlur(grayscalePixels, width, height, threshold = DEFAULT_BLUR_THRESHOLD) {
1497
1534
  const laplacian = [];
1498
1535
  for (let y = 1; y < height - 1; y++) {
@@ -2040,6 +2077,7 @@ export {
2040
2077
  DEFAULT_OVAL_REGION,
2041
2078
  DEFAULT_STABILIZER_CONFIG,
2042
2079
  DEFAULT_STATUS_MESSAGES,
2080
+ DYNAMIC_RANGE_WARNING_THRESHOLD,
2043
2081
  ERROR_MESSAGES,
2044
2082
  ERROR_MESSAGES_ES,
2045
2083
  ES_LOCALE,
@@ -2085,6 +2123,7 @@ export {
2085
2123
  TARGET_FACE_PERCENTAGE_IN_CROP,
2086
2124
  VALID_FRAME_COUNTS,
2087
2125
  analyzeBlur,
2126
+ analyzeDynamicRange,
2088
2127
  analyzeEyeRegionBrightness,
2089
2128
  analyzeEyeRegionContrast,
2090
2129
  analyzeLighting,
@@ -2116,9 +2155,11 @@ export {
2116
2155
  isFaceFullyVisible,
2117
2156
  isFaceInOval,
2118
2157
  isRetryableError,
2158
+ linearRgbToLabL,
2119
2159
  retryWithBackoff,
2120
2160
  rgbaToGrayscale,
2121
2161
  sleep,
2162
+ srgbToLinear,
2122
2163
  toFrameData,
2123
2164
  toHybridFrameData,
2124
2165
  toLivenessResult,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moveris/shared",
3
- "version": "3.2.0",
3
+ "version": "3.3.1",
4
4
  "description": "Core business logic for Moveris Live SDK",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",