@moveris/shared 3.6.2 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -47,14 +47,15 @@ The main client for interacting with the Moveris Liveness API.
47
47
  const client = new LivenessClient(config: LivenessClientConfig);
48
48
  ```
49
49
 
50
- | Option | Type | Default | Description |
51
- | -------------- | -------------- | --------------------------- | ------------------------------------------------------------------ |
52
- | `apiKey` | `string` | **required** | Your Moveris API key |
53
- | `baseUrl` | `string` | `'https://api.moveris.com'` | API base URL |
54
- | `modelVersion` | `ModelVersion` | - | Model version alias for `X-Model-Version` header (e.g. `'latest'`) |
55
- | `timeout` | `number` | `30000` | Request timeout in milliseconds |
56
- | `enableRetry` | `boolean` | `true` | Enable automatic retry with exponential backoff |
57
- | `customFetch` | `typeof fetch` | `fetch` | Custom fetch implementation (for React Native) |
50
+ | Option | Type | Default | Description |
51
+ | ----------------------------- | ----------------------------- | --------------------------- | ------------------------------------------------------------------------------------------------ |
52
+ | `apiKey` | `string` | **required** | Your Moveris API key |
53
+ | `baseUrl` | `string` | `'https://api.moveris.com'` | API base URL |
54
+ | `modelVersion` | `ModelVersion` | - | Model version alias for `X-Model-Version` header (e.g. `'latest'`) |
55
+ | `timeout` | `number` | `30000` | Request timeout in milliseconds |
56
+ | `enableRetry` | `boolean` | `true` | Enable automatic retry with exponential backoff |
57
+ | `customFetch` | `typeof fetch` | `fetch` | Custom fetch implementation (for React Native) |
58
+ | `deviceIntelligenceOverrides` | `DeviceIntelligenceOverrides` | - | Static overrides merged into every `device_intelligence` payload (e.g. `{ vpn_detected: true }`) |
58
59
 
59
60
  #### Methods
60
61
 
@@ -151,6 +152,17 @@ const result = await client.hybrid50(frames, { fps: 30 });
151
152
  const result = await client.hybrid150(frames, { fps: 30 });
152
153
  ```
153
154
 
155
+ ##### `updateDeviceIntelligenceOverrides(overrides)`
156
+
157
+ Merge additional fields into the cached device intelligence payload. Call this after construction to inject data only available at runtime (e.g. camera specs, VPN detection results). Merges shallowly — later calls are merged on top of earlier ones.
158
+
159
+ ```typescript
160
+ client.updateDeviceIntelligenceOverrides({
161
+ camera: { device_name: 'FaceTime HD Camera', resolution: '1280x720' },
162
+ });
163
+ client.updateDeviceIntelligenceOverrides({ vpn_detected: true });
164
+ ```
165
+
154
166
  ##### `health()`
155
167
 
156
168
  Check API health status.
@@ -726,6 +738,91 @@ const active = getActiveModels();
726
738
 
727
739
  ---
728
740
 
741
+ ## Device Intelligence
742
+
743
+ The SDK automatically collects device metadata and attaches it to every API request as `device_intelligence`. Collection happens inside `LivenessClient` — no extra work required for standard consumers.
744
+
745
+ ### `collectDeviceIntelligence(opts?)`
746
+
747
+ Collect device metadata manually. Useful for Approach B consumers (custom UI + raw `fetch()`) who bypass `LivenessClient`.
748
+
749
+ ```typescript
750
+ import { collectDeviceIntelligence } from '@moveris/shared';
751
+
752
+ const di = await collectDeviceIntelligence({
753
+ platformVersion: 'shared@3.6.2', // optional — defaults to SHARED_SDK_PLATFORM
754
+ overrides: { vpn_detected: true }, // optional — merged on top of collected data
755
+ });
756
+
757
+ if (di) {
758
+ console.log(di.ip); // '1.2.3.4'
759
+ console.log(di.country); // 'US'
760
+ console.log(di.isp); // 'Comcast Cable'
761
+ console.log(di.platform_version); // 'shared@3.6.2'
762
+ }
763
+ // Returns null if ipinfo.io is unreachable — handle gracefully
764
+ ```
765
+
766
+ > If you import from `@moveris/react`, use that package's `collectDeviceIntelligence` instead — it pre-bakes `REACT_SDK_PLATFORM` as the default `platformVersion` so the full `react@A.B.C+shared@X.Y.Z` string is used automatically.
767
+
768
+ ### `SHARED_SDK_PLATFORM`
769
+
770
+ Pre-formatted platform string for the `platform_version` field. Reads the version from `package.json` at build time — never goes stale.
771
+
772
+ ```typescript
773
+ import { SHARED_SDK_PLATFORM } from '@moveris/shared';
774
+
775
+ console.log(SHARED_SDK_PLATFORM); // 'shared@3.6.2'
776
+ ```
777
+
778
+ ### Device Intelligence Types
779
+
780
+ ```typescript
781
+ import type {
782
+ DeviceIntelligence,
783
+ DeviceIntelligenceOverrides,
784
+ DeviceIntelligenceGeo,
785
+ DeviceIntelligenceCamera,
786
+ } from '@moveris/shared';
787
+ ```
788
+
789
+ ```typescript
790
+ interface DeviceIntelligence {
791
+ ip: string;
792
+ country: string; // ISO 3166-1 alpha-2, e.g. 'US'
793
+ region: string; // e.g. 'California'
794
+ city: string; // e.g. 'San Francisco'
795
+ isp?: string; // Internet service provider
796
+ user_agent?: string; // navigator.userAgent
797
+ platform_version?: string; // SDK version string
798
+ vpn_detected?: boolean;
799
+ camera?: DeviceIntelligenceCamera;
800
+ }
801
+
802
+ interface DeviceIntelligenceCamera {
803
+ device_name?: string; // e.g. 'FaceTime HD Camera'
804
+ resolution?: string; // e.g. '1280x720'
805
+ }
806
+
807
+ interface DeviceIntelligenceOverrides {
808
+ vpn_detected?: boolean;
809
+ camera?: DeviceIntelligenceCamera;
810
+ [key: string]: unknown;
811
+ }
812
+ ```
813
+
814
+ ### CSP Note
815
+
816
+ If your app enforces a Content Security Policy, add `https://ipinfo.io` to `connect-src`:
817
+
818
+ ```
819
+ connect-src 'self' https://api.moveris.com https://ipinfo.io;
820
+ ```
821
+
822
+ Without this, the ipinfo.io fetch will be blocked silently and `device_intelligence` will be omitted from all requests.
823
+
824
+ ---
825
+
729
826
  ## Configuration Constants
730
827
 
731
828
  ```typescript
@@ -767,6 +864,11 @@ import type {
767
864
  EyeQualityThresholds,
768
865
  EyeRegionBounds,
769
866
  EyeRegionsBounds,
867
+ // Device Intelligence Types
868
+ DeviceIntelligence,
869
+ DeviceIntelligenceOverrides,
870
+ DeviceIntelligenceGeo,
871
+ DeviceIntelligenceCamera,
770
872
  } from '@moveris/shared';
771
873
  ```
772
874
 
package/dist/index.d.mts CHANGED
@@ -1,3 +1,30 @@
1
+ interface DeviceIntelligenceGeo {
2
+ country: string;
3
+ region: string;
4
+ city: string;
5
+ isp?: string;
6
+ }
7
+ interface DeviceIntelligenceCamera {
8
+ device_name: string;
9
+ resolution: string;
10
+ }
11
+ interface DeviceIntelligence {
12
+ ip_address: string;
13
+ geo: DeviceIntelligenceGeo;
14
+ vpn_detected: boolean;
15
+ camera: DeviceIntelligenceCamera;
16
+ user_agent: string;
17
+ platform_version: string;
18
+ }
19
+ type DeviceIntelligenceOverrides = {
20
+ ip_address?: string;
21
+ geo?: Partial<DeviceIntelligenceGeo>;
22
+ vpn_detected?: boolean;
23
+ camera?: Partial<DeviceIntelligenceCamera>;
24
+ user_agent?: string;
25
+ platform_version?: string;
26
+ };
27
+
1
28
  type Verdict = 'live' | 'fake' | 'inconclusive';
2
29
  interface ModelEntry {
3
30
  id: string;
@@ -40,6 +67,7 @@ interface FastCheckRequest {
40
67
  frames: FrameData[];
41
68
  frame_count?: number;
42
69
  warnings?: string[];
70
+ device_intelligence?: DeviceIntelligence;
43
71
  }
44
72
  interface FastCheckCropsRequest {
45
73
  session_id: string;
@@ -47,6 +75,7 @@ interface FastCheckCropsRequest {
47
75
  source?: FrameSource;
48
76
  crops: CropData[];
49
77
  frame_count?: number;
78
+ device_intelligence?: DeviceIntelligence;
50
79
  }
51
80
  interface VerifyRequest {
52
81
  session_id: string;
@@ -78,6 +107,7 @@ interface FastCheckStreamRequest {
78
107
  frame: FrameData;
79
108
  frame_count?: number;
80
109
  warnings?: string[];
110
+ device_intelligence?: DeviceIntelligence;
81
111
  }
82
112
  interface FastCheckResponse {
83
113
  verdict: Verdict | null;
@@ -539,6 +569,7 @@ interface LivenessClientConfig {
539
569
  timeout?: number;
540
570
  enableRetry?: boolean;
541
571
  customFetch?: typeof fetch;
572
+ deviceIntelligenceOverrides?: DeviceIntelligenceOverrides;
542
573
  }
543
574
  declare class LivenessApiError extends Error {
544
575
  readonly code: string;
@@ -559,7 +590,13 @@ declare class LivenessClient {
559
590
  private readonly timeout;
560
591
  private readonly enableRetry;
561
592
  private readonly fetchFn;
593
+ private diCollected;
594
+ private diCollecting;
595
+ private diOverrides;
562
596
  constructor(config: LivenessClientConfig);
597
+ updateDeviceIntelligenceOverrides(partial: DeviceIntelligenceOverrides): void;
598
+ private getDeviceIntelligence;
599
+ private applyDiOverrides;
563
600
  private request;
564
601
  private requestRaw;
565
602
  private parseErrorResponse;
@@ -902,4 +939,10 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
902
939
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
903
940
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
904
941
 
905
- export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type 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 };
942
+ declare const SHARED_SDK_PLATFORM: string;
943
+ declare function collectDeviceIntelligence(opts?: {
944
+ overrides?: DeviceIntelligenceOverrides;
945
+ platformVersion?: string;
946
+ }): Promise<DeviceIntelligence | null>;
947
+
948
+ export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, 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, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,30 @@
1
+ interface DeviceIntelligenceGeo {
2
+ country: string;
3
+ region: string;
4
+ city: string;
5
+ isp?: string;
6
+ }
7
+ interface DeviceIntelligenceCamera {
8
+ device_name: string;
9
+ resolution: string;
10
+ }
11
+ interface DeviceIntelligence {
12
+ ip_address: string;
13
+ geo: DeviceIntelligenceGeo;
14
+ vpn_detected: boolean;
15
+ camera: DeviceIntelligenceCamera;
16
+ user_agent: string;
17
+ platform_version: string;
18
+ }
19
+ type DeviceIntelligenceOverrides = {
20
+ ip_address?: string;
21
+ geo?: Partial<DeviceIntelligenceGeo>;
22
+ vpn_detected?: boolean;
23
+ camera?: Partial<DeviceIntelligenceCamera>;
24
+ user_agent?: string;
25
+ platform_version?: string;
26
+ };
27
+
1
28
  type Verdict = 'live' | 'fake' | 'inconclusive';
2
29
  interface ModelEntry {
3
30
  id: string;
@@ -40,6 +67,7 @@ interface FastCheckRequest {
40
67
  frames: FrameData[];
41
68
  frame_count?: number;
42
69
  warnings?: string[];
70
+ device_intelligence?: DeviceIntelligence;
43
71
  }
44
72
  interface FastCheckCropsRequest {
45
73
  session_id: string;
@@ -47,6 +75,7 @@ interface FastCheckCropsRequest {
47
75
  source?: FrameSource;
48
76
  crops: CropData[];
49
77
  frame_count?: number;
78
+ device_intelligence?: DeviceIntelligence;
50
79
  }
51
80
  interface VerifyRequest {
52
81
  session_id: string;
@@ -78,6 +107,7 @@ interface FastCheckStreamRequest {
78
107
  frame: FrameData;
79
108
  frame_count?: number;
80
109
  warnings?: string[];
110
+ device_intelligence?: DeviceIntelligence;
81
111
  }
82
112
  interface FastCheckResponse {
83
113
  verdict: Verdict | null;
@@ -539,6 +569,7 @@ interface LivenessClientConfig {
539
569
  timeout?: number;
540
570
  enableRetry?: boolean;
541
571
  customFetch?: typeof fetch;
572
+ deviceIntelligenceOverrides?: DeviceIntelligenceOverrides;
542
573
  }
543
574
  declare class LivenessApiError extends Error {
544
575
  readonly code: string;
@@ -559,7 +590,13 @@ declare class LivenessClient {
559
590
  private readonly timeout;
560
591
  private readonly enableRetry;
561
592
  private readonly fetchFn;
593
+ private diCollected;
594
+ private diCollecting;
595
+ private diOverrides;
562
596
  constructor(config: LivenessClientConfig);
597
+ updateDeviceIntelligenceOverrides(partial: DeviceIntelligenceOverrides): void;
598
+ private getDeviceIntelligence;
599
+ private applyDiOverrides;
563
600
  private request;
564
601
  private requestRaw;
565
602
  private parseErrorResponse;
@@ -902,4 +939,10 @@ declare function analyzeEyeRegionContrast(pixels: Uint8Array | Uint8ClampedArray
902
939
  declare function detectSpecularHighlights(pixels: Uint8Array | Uint8ClampedArray, relativeFactor?: number, meanBrightness?: number): number;
903
940
  declare function checkEyeRegionQuality(pixels: Uint8Array | Uint8ClampedArray, thresholds?: EyeQualityThresholds): EyeRegionQuality;
904
941
 
905
- export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type 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 };
942
+ declare const SHARED_SDK_PLATFORM: string;
943
+ declare function collectDeviceIntelligence(opts?: {
944
+ overrides?: DeviceIntelligenceOverrides;
945
+ platformVersion?: string;
946
+ }): Promise<DeviceIntelligence | null>;
947
+
948
+ export { ALIGNMENT_THRESHOLD_CAPTURE, ALIGNMENT_THRESHOLD_GOOD, ALIGNMENT_THRESHOLD_PERFECT, ALIGNMENT_THRESHOLD_POOR, API_ENDPOINTS, API_ERROR_CODES, API_PATHS, AUTH_CONFIG, type ApiErrorCode, BACKLIT_RATIO_THRESHOLD, BLUR_THRESHOLD_MOBILE, BaseFrameCollector, type BlurAnalysis, CAMERA_ANGLE_HIGH_RATIO, CAMERA_ANGLE_LOW_RATIO, type CameraAngleResult, type CameraCapabilities, type CameraRequirements, type CameraValidationResult, type CaptureQualityState, type CapturedFrame, type CropData, DEFAULT_BLUR_THRESHOLD, DEFAULT_CAMERA_REQUIREMENTS, DEFAULT_ENDPOINT, DEFAULT_FACE_DETECTION_TIERS, DEFAULT_GAZE_THRESHOLDS, DEFAULT_HAND_OCCLUSION_CONFIG, DEFAULT_LIVENESS_CONFIG, DEFAULT_LOCALE, DEFAULT_OVAL_REGION, DEFAULT_STABILIZER_CONFIG, DEFAULT_STATUS_MESSAGES, DYNAMIC_RANGE_WARNING_THRESHOLD, type DeprecationInfo, type DetectionResult, type DetectionSummary, type DetectorConfig, type DeviceIntelligence, type DeviceIntelligenceCamera, type DeviceIntelligenceGeo, type DeviceIntelligenceOverrides, type DynamicRangeAnalysis, ERROR_MESSAGES, ERROR_MESSAGES_ES, ES_LOCALE, EYE_LANDMARK_INDICES, EYE_QUALITY_THRESHOLDS, type ErrorResponse, type EyeQualityThresholds, type EyeRegionBounds, type EyeRegionQuality, type EyeRegionsBounds, 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, SHARED_SDK_PLATFORM, type StabilizationProgress, type StabilizationResult, type StabilizerConfig, type StatusMessageKey, type StreamingStatus, TARGET_FACE_PERCENTAGE_IN_CROP, VALID_FRAME_COUNTS, type Verdict, type VerifyRequest, type VerifyResponse, type VideoFrameMetadata, analyzeBlur, analyzeDynamicRange, analyzeEyeRegionBrightness, analyzeEyeRegionContrast, analyzeLighting, calculateAdaptiveCropMultiplier, calculateBrightness, calculateFaceAlignment, calculateFaceCropRegion, canCaptureFrame, checkEyeRegionQuality, checkFrameQuality, collectDeviceIntelligence, decodeBase64, detectCameraAngle, detectFaceRoll, detectFaceRollFromMatrix, detectSpecularHighlights, encodeBase64, generateSessionId, getActiveModels, getApiErrorMessage, getCaptureQualityFeedback, getEyeRegionBounds, getFeedbackMessage, getMinFramesForModel, getOvalGuideState, getStatusMessage, hasEnoughFrames, isDeprecatedModel, isFaceCropFullyInFrame, isFaceFullyVisible, isFaceInOval, isRetryableError, linearRgbToLabL, retryWithBackoff, rgbaToGrayscale, sleep, srgbToLinear, toFrameData, toHybridFrameData, toLivenessResult, toLivenessResultFromStream, validateApiKey, validateFaceLandmarks, validateFrameCount, validateFrameData, validateFrameIndex, validateTimestamp, validateUUID, validateUrl };
package/dist/index.js CHANGED
@@ -87,6 +87,7 @@ __export(index_exports, {
87
87
  OVAL_REGION_DESKTOP: () => OVAL_REGION_DESKTOP,
88
88
  OVAL_REGION_MOBILE: () => OVAL_REGION_MOBILE,
89
89
  RETRY_CONFIG: () => RETRY_CONFIG,
90
+ SHARED_SDK_PLATFORM: () => SHARED_SDK_PLATFORM,
90
91
  TARGET_FACE_PERCENTAGE_IN_CROP: () => TARGET_FACE_PERCENTAGE_IN_CROP,
91
92
  VALID_FRAME_COUNTS: () => VALID_FRAME_COUNTS,
92
93
  analyzeBlur: () => analyzeBlur,
@@ -101,6 +102,7 @@ __export(index_exports, {
101
102
  canCaptureFrame: () => canCaptureFrame,
102
103
  checkEyeRegionQuality: () => checkEyeRegionQuality,
103
104
  checkFrameQuality: () => checkFrameQuality,
105
+ collectDeviceIntelligence: () => collectDeviceIntelligence,
104
106
  decodeBase64: () => decodeBase64,
105
107
  detectCameraAngle: () => detectCameraAngle,
106
108
  detectFaceRoll: () => detectFaceRoll,
@@ -239,6 +241,55 @@ async function sleep(ms) {
239
241
  await new Promise((resolve) => setTimeout(resolve, ms));
240
242
  }
241
243
 
244
+ // package.json
245
+ var version = "3.8.0";
246
+
247
+ // src/utils/deviceIntelligence.ts
248
+ var IPINFO_URL = "https://ipinfo.io/json";
249
+ var SHARED_SDK_VERSION = version;
250
+ var SHARED_SDK_PLATFORM = `shared@${SHARED_SDK_VERSION}`;
251
+ function parseIsp(org) {
252
+ if (!org) return void 0;
253
+ return org.replace(/^AS\d+\s+/, "").trim() || void 0;
254
+ }
255
+ function mergeOverrides(collected, overrides) {
256
+ if (!overrides) return collected;
257
+ return {
258
+ ...collected,
259
+ ...overrides,
260
+ geo: { ...collected.geo, ...overrides.geo },
261
+ camera: { ...collected.camera, ...overrides.camera }
262
+ };
263
+ }
264
+ async function collectDeviceIntelligence(opts) {
265
+ try {
266
+ const response = await fetch(IPINFO_URL);
267
+ if (!response.ok) return null;
268
+ const data = await response.json();
269
+ if (!data.ip) return null;
270
+ const isp = parseIsp(data.org);
271
+ const collected = {
272
+ ip_address: data.ip,
273
+ geo: {
274
+ country: data.country ?? "Unknown",
275
+ region: data.region ?? "Unknown",
276
+ city: data.city ?? "Unknown",
277
+ ...isp ? { isp } : {}
278
+ },
279
+ vpn_detected: false,
280
+ camera: {
281
+ device_name: "Unknown",
282
+ resolution: "Unknown"
283
+ },
284
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "Unknown",
285
+ platform_version: opts?.platformVersion ?? SHARED_SDK_PLATFORM
286
+ };
287
+ return mergeOverrides(collected, opts?.overrides);
288
+ } catch {
289
+ return null;
290
+ }
291
+ }
292
+
242
293
  // src/client/LivenessClient.ts
243
294
  var LivenessApiError = class extends Error {
244
295
  constructor(message, code, statusCode, required, received) {
@@ -328,12 +379,58 @@ function generateSessionId() {
328
379
  }
329
380
  var LivenessClient = class _LivenessClient {
330
381
  constructor(config) {
382
+ // Device intelligence: lazily collected on first API call, then cached.
383
+ // undefined = not yet attempted, null = collection failed.
384
+ this.diCollected = void 0;
385
+ this.diCollecting = null;
331
386
  this.baseUrl = (config.baseUrl ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
332
387
  this.apiKey = config.apiKey;
333
388
  this.modelVersion = config.modelVersion;
334
389
  this.timeout = config.timeout ?? AUTH_CONFIG.timeout;
335
390
  this.enableRetry = config.enableRetry ?? true;
336
391
  this.fetchFn = config.customFetch ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
392
+ this.diOverrides = { ...config.deviceIntelligenceOverrides };
393
+ }
394
+ /**
395
+ * Merge additional device intelligence overrides into the existing set.
396
+ * Called by the React layer to inject camera info once the stream is active.
397
+ * Overrides deep-merge — only specified keys are replaced.
398
+ */
399
+ updateDeviceIntelligenceOverrides(partial) {
400
+ this.diOverrides = {
401
+ ...this.diOverrides,
402
+ ...partial,
403
+ ...partial.geo ? { geo: { ...this.diOverrides.geo, ...partial.geo } } : {},
404
+ ...partial.camera ? { camera: { ...this.diOverrides.camera, ...partial.camera } } : {}
405
+ };
406
+ }
407
+ /**
408
+ * Return the merged device intelligence (auto-collected + overrides).
409
+ * Collection is lazy — runs once on first call, then returns cached result.
410
+ * Returns null if collection failed; callers should omit the field in that case.
411
+ */
412
+ async getDeviceIntelligence() {
413
+ if (this.diCollected !== void 0) {
414
+ return this.diCollected ? this.applyDiOverrides(this.diCollected) : null;
415
+ }
416
+ if (!this.diCollecting) {
417
+ this.diCollecting = collectDeviceIntelligence({
418
+ platformVersion: SHARED_SDK_PLATFORM
419
+ }).then((result) => {
420
+ this.diCollected = result ?? null;
421
+ });
422
+ }
423
+ await this.diCollecting;
424
+ return this.diCollected ? this.applyDiOverrides(this.diCollected) : null;
425
+ }
426
+ applyDiOverrides(collected) {
427
+ const ov = this.diOverrides;
428
+ return {
429
+ ...collected,
430
+ ...ov,
431
+ geo: { ...collected.geo, ...ov.geo },
432
+ camera: { ...collected.camera, ...ov.camera }
433
+ };
337
434
  }
338
435
  /**
339
436
  * Make an authenticated API request, returning the parsed body.
@@ -486,9 +583,9 @@ var LivenessClient = class _LivenessClient {
486
583
  * Build extra request headers for model versioning.
487
584
  */
488
585
  buildModelVersionHeaders(modelVersion) {
489
- const version = modelVersion ?? this.modelVersion;
490
- if (!version) return {};
491
- return { [AUTH_CONFIG.modelVersionHeader]: version };
586
+ const version2 = modelVersion ?? this.modelVersion;
587
+ if (!version2) return {};
588
+ return { [AUTH_CONFIG.modelVersionHeader]: version2 };
492
589
  }
493
590
  /**
494
591
  * Make a request with optional retry
@@ -553,13 +650,15 @@ var LivenessClient = class _LivenessClient {
553
650
  */
554
651
  async fastCheck(frames, options = {}) {
555
652
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
653
+ const di = await this.getDeviceIntelligence();
556
654
  const request = {
557
655
  session_id: options.sessionId ?? generateSessionId(),
558
656
  model: options.model ?? "10",
559
657
  source: options.source ?? "live",
560
658
  frames: toFrameData(frames),
561
659
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
562
- ...options.warnings?.length ? { warnings: options.warnings } : {}
660
+ ...options.warnings?.length ? { warnings: options.warnings } : {},
661
+ ...di ? { device_intelligence: di } : {}
563
662
  };
564
663
  const { data: response, headers } = await this.requestWithRetryRaw(
565
664
  API_PATHS.fastCheck,
@@ -587,6 +686,7 @@ var LivenessClient = class _LivenessClient {
587
686
  */
588
687
  async fastCheckCrops(crops, options = {}) {
589
688
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
689
+ const di = await this.getDeviceIntelligence();
590
690
  const request = {
591
691
  session_id: options.sessionId ?? generateSessionId(),
592
692
  model: options.model ?? "10",
@@ -594,7 +694,8 @@ var LivenessClient = class _LivenessClient {
594
694
  crops,
595
695
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
596
696
  ...options.warnings?.length ? { warnings: options.warnings } : {},
597
- ...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {}
697
+ ...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {},
698
+ ...di ? { device_intelligence: di } : {}
598
699
  };
599
700
  const { data: response, headers } = await this.requestWithRetryRaw(
600
701
  API_PATHS.fastCheckCrops,
@@ -650,13 +751,15 @@ var LivenessClient = class _LivenessClient {
650
751
  */
651
752
  async sendStreamFrameInternal(frameData, options) {
652
753
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
754
+ const di = await this.getDeviceIntelligence();
653
755
  const request = {
654
756
  session_id: options.sessionId,
655
757
  model: options.model,
656
758
  source: options.source,
657
759
  frame: frameData,
658
760
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
659
- ...options.warnings?.length ? { warnings: options.warnings } : {}
761
+ ...options.warnings?.length ? { warnings: options.warnings } : {},
762
+ ...di ? { device_intelligence: di } : {}
660
763
  };
661
764
  return this.requestWithRetry(API_PATHS.fastCheckStream, {
662
765
  method: "POST",
@@ -2314,6 +2417,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2314
2417
  OVAL_REGION_DESKTOP,
2315
2418
  OVAL_REGION_MOBILE,
2316
2419
  RETRY_CONFIG,
2420
+ SHARED_SDK_PLATFORM,
2317
2421
  TARGET_FACE_PERCENTAGE_IN_CROP,
2318
2422
  VALID_FRAME_COUNTS,
2319
2423
  analyzeBlur,
@@ -2328,6 +2432,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2328
2432
  canCaptureFrame,
2329
2433
  checkEyeRegionQuality,
2330
2434
  checkFrameQuality,
2435
+ collectDeviceIntelligence,
2331
2436
  decodeBase64,
2332
2437
  detectCameraAngle,
2333
2438
  detectFaceRoll,
package/dist/index.mjs CHANGED
@@ -95,6 +95,55 @@ async function sleep(ms) {
95
95
  await new Promise((resolve) => setTimeout(resolve, ms));
96
96
  }
97
97
 
98
+ // package.json
99
+ var version = "3.8.0";
100
+
101
+ // src/utils/deviceIntelligence.ts
102
+ var IPINFO_URL = "https://ipinfo.io/json";
103
+ var SHARED_SDK_VERSION = version;
104
+ var SHARED_SDK_PLATFORM = `shared@${SHARED_SDK_VERSION}`;
105
+ function parseIsp(org) {
106
+ if (!org) return void 0;
107
+ return org.replace(/^AS\d+\s+/, "").trim() || void 0;
108
+ }
109
+ function mergeOverrides(collected, overrides) {
110
+ if (!overrides) return collected;
111
+ return {
112
+ ...collected,
113
+ ...overrides,
114
+ geo: { ...collected.geo, ...overrides.geo },
115
+ camera: { ...collected.camera, ...overrides.camera }
116
+ };
117
+ }
118
+ async function collectDeviceIntelligence(opts) {
119
+ try {
120
+ const response = await fetch(IPINFO_URL);
121
+ if (!response.ok) return null;
122
+ const data = await response.json();
123
+ if (!data.ip) return null;
124
+ const isp = parseIsp(data.org);
125
+ const collected = {
126
+ ip_address: data.ip,
127
+ geo: {
128
+ country: data.country ?? "Unknown",
129
+ region: data.region ?? "Unknown",
130
+ city: data.city ?? "Unknown",
131
+ ...isp ? { isp } : {}
132
+ },
133
+ vpn_detected: false,
134
+ camera: {
135
+ device_name: "Unknown",
136
+ resolution: "Unknown"
137
+ },
138
+ user_agent: typeof navigator !== "undefined" ? navigator.userAgent : "Unknown",
139
+ platform_version: opts?.platformVersion ?? SHARED_SDK_PLATFORM
140
+ };
141
+ return mergeOverrides(collected, opts?.overrides);
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+
98
147
  // src/client/LivenessClient.ts
99
148
  var LivenessApiError = class extends Error {
100
149
  constructor(message, code, statusCode, required, received) {
@@ -184,12 +233,58 @@ function generateSessionId() {
184
233
  }
185
234
  var LivenessClient = class _LivenessClient {
186
235
  constructor(config) {
236
+ // Device intelligence: lazily collected on first API call, then cached.
237
+ // undefined = not yet attempted, null = collection failed.
238
+ this.diCollected = void 0;
239
+ this.diCollecting = null;
187
240
  this.baseUrl = (config.baseUrl ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
188
241
  this.apiKey = config.apiKey;
189
242
  this.modelVersion = config.modelVersion;
190
243
  this.timeout = config.timeout ?? AUTH_CONFIG.timeout;
191
244
  this.enableRetry = config.enableRetry ?? true;
192
245
  this.fetchFn = config.customFetch ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
246
+ this.diOverrides = { ...config.deviceIntelligenceOverrides };
247
+ }
248
+ /**
249
+ * Merge additional device intelligence overrides into the existing set.
250
+ * Called by the React layer to inject camera info once the stream is active.
251
+ * Overrides deep-merge — only specified keys are replaced.
252
+ */
253
+ updateDeviceIntelligenceOverrides(partial) {
254
+ this.diOverrides = {
255
+ ...this.diOverrides,
256
+ ...partial,
257
+ ...partial.geo ? { geo: { ...this.diOverrides.geo, ...partial.geo } } : {},
258
+ ...partial.camera ? { camera: { ...this.diOverrides.camera, ...partial.camera } } : {}
259
+ };
260
+ }
261
+ /**
262
+ * Return the merged device intelligence (auto-collected + overrides).
263
+ * Collection is lazy — runs once on first call, then returns cached result.
264
+ * Returns null if collection failed; callers should omit the field in that case.
265
+ */
266
+ async getDeviceIntelligence() {
267
+ if (this.diCollected !== void 0) {
268
+ return this.diCollected ? this.applyDiOverrides(this.diCollected) : null;
269
+ }
270
+ if (!this.diCollecting) {
271
+ this.diCollecting = collectDeviceIntelligence({
272
+ platformVersion: SHARED_SDK_PLATFORM
273
+ }).then((result) => {
274
+ this.diCollected = result ?? null;
275
+ });
276
+ }
277
+ await this.diCollecting;
278
+ return this.diCollected ? this.applyDiOverrides(this.diCollected) : null;
279
+ }
280
+ applyDiOverrides(collected) {
281
+ const ov = this.diOverrides;
282
+ return {
283
+ ...collected,
284
+ ...ov,
285
+ geo: { ...collected.geo, ...ov.geo },
286
+ camera: { ...collected.camera, ...ov.camera }
287
+ };
193
288
  }
194
289
  /**
195
290
  * Make an authenticated API request, returning the parsed body.
@@ -342,9 +437,9 @@ var LivenessClient = class _LivenessClient {
342
437
  * Build extra request headers for model versioning.
343
438
  */
344
439
  buildModelVersionHeaders(modelVersion) {
345
- const version = modelVersion ?? this.modelVersion;
346
- if (!version) return {};
347
- return { [AUTH_CONFIG.modelVersionHeader]: version };
440
+ const version2 = modelVersion ?? this.modelVersion;
441
+ if (!version2) return {};
442
+ return { [AUTH_CONFIG.modelVersionHeader]: version2 };
348
443
  }
349
444
  /**
350
445
  * Make a request with optional retry
@@ -409,13 +504,15 @@ var LivenessClient = class _LivenessClient {
409
504
  */
410
505
  async fastCheck(frames, options = {}) {
411
506
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
507
+ const di = await this.getDeviceIntelligence();
412
508
  const request = {
413
509
  session_id: options.sessionId ?? generateSessionId(),
414
510
  model: options.model ?? "10",
415
511
  source: options.source ?? "live",
416
512
  frames: toFrameData(frames),
417
513
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
418
- ...options.warnings?.length ? { warnings: options.warnings } : {}
514
+ ...options.warnings?.length ? { warnings: options.warnings } : {},
515
+ ...di ? { device_intelligence: di } : {}
419
516
  };
420
517
  const { data: response, headers } = await this.requestWithRetryRaw(
421
518
  API_PATHS.fastCheck,
@@ -443,6 +540,7 @@ var LivenessClient = class _LivenessClient {
443
540
  */
444
541
  async fastCheckCrops(crops, options = {}) {
445
542
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
543
+ const di = await this.getDeviceIntelligence();
446
544
  const request = {
447
545
  session_id: options.sessionId ?? generateSessionId(),
448
546
  model: options.model ?? "10",
@@ -450,7 +548,8 @@ var LivenessClient = class _LivenessClient {
450
548
  crops,
451
549
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
452
550
  ...options.warnings?.length ? { warnings: options.warnings } : {},
453
- ...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {}
551
+ ...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {},
552
+ ...di ? { device_intelligence: di } : {}
454
553
  };
455
554
  const { data: response, headers } = await this.requestWithRetryRaw(
456
555
  API_PATHS.fastCheckCrops,
@@ -506,13 +605,15 @@ var LivenessClient = class _LivenessClient {
506
605
  */
507
606
  async sendStreamFrameInternal(frameData, options) {
508
607
  const effectiveVersion = options.modelVersion ?? this.modelVersion;
608
+ const di = await this.getDeviceIntelligence();
509
609
  const request = {
510
610
  session_id: options.sessionId,
511
611
  model: options.model,
512
612
  source: options.source,
513
613
  frame: frameData,
514
614
  ...options.frameCount != null ? { frame_count: options.frameCount } : {},
515
- ...options.warnings?.length ? { warnings: options.warnings } : {}
615
+ ...options.warnings?.length ? { warnings: options.warnings } : {},
616
+ ...di ? { device_intelligence: di } : {}
516
617
  };
517
618
  return this.requestWithRetry(API_PATHS.fastCheckStream, {
518
619
  method: "POST",
@@ -2169,6 +2270,7 @@ export {
2169
2270
  OVAL_REGION_DESKTOP,
2170
2271
  OVAL_REGION_MOBILE,
2171
2272
  RETRY_CONFIG,
2273
+ SHARED_SDK_PLATFORM,
2172
2274
  TARGET_FACE_PERCENTAGE_IN_CROP,
2173
2275
  VALID_FRAME_COUNTS,
2174
2276
  analyzeBlur,
@@ -2183,6 +2285,7 @@ export {
2183
2285
  canCaptureFrame,
2184
2286
  checkEyeRegionQuality,
2185
2287
  checkFrameQuality,
2288
+ collectDeviceIntelligence,
2186
2289
  decodeBase64,
2187
2290
  detectCameraAngle,
2188
2291
  detectFaceRoll,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moveris/shared",
3
- "version": "3.6.2",
3
+ "version": "3.8.0",
4
4
  "description": "Core business logic for Moveris Live SDK",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",