@moveris/shared 2.9.0 → 3.1.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.js CHANGED
@@ -72,6 +72,7 @@ __export(index_exports, {
72
72
  MAX_IDEAL_FACE_RATIO: () => MAX_IDEAL_FACE_RATIO,
73
73
  MIN_CAPTURE_ALIGNMENT: () => MIN_CAPTURE_ALIGNMENT,
74
74
  MIN_CROP_MULTIPLIER: () => MIN_CROP_MULTIPLIER,
75
+ MIN_FACE_AREA_RATIO: () => MIN_FACE_AREA_RATIO,
75
76
  MIN_FACE_BOTTOM_MARGIN: () => MIN_FACE_BOTTOM_MARGIN,
76
77
  MIN_FACE_RATIO: () => MIN_FACE_RATIO,
77
78
  MIN_FACE_SIDE_MARGIN: () => MIN_FACE_SIDE_MARGIN,
@@ -85,6 +86,7 @@ __export(index_exports, {
85
86
  OVAL_REGION_MOBILE: () => OVAL_REGION_MOBILE,
86
87
  RETRY_CONFIG: () => RETRY_CONFIG,
87
88
  TARGET_FACE_PERCENTAGE_IN_CROP: () => TARGET_FACE_PERCENTAGE_IN_CROP,
89
+ VALID_FRAME_COUNTS: () => VALID_FRAME_COUNTS,
88
90
  analyzeBlur: () => analyzeBlur,
89
91
  analyzeEyeRegionBrightness: () => analyzeEyeRegionBrightness,
90
92
  analyzeEyeRegionContrast: () => analyzeEyeRegionContrast,
@@ -171,7 +173,8 @@ var FRAME_BUFFER_CONFIG = {
171
173
  var AUTH_CONFIG = {
172
174
  timeout: 3e4,
173
175
  // 30 seconds for API requests
174
- apiKeyHeader: "X-API-Key"
176
+ apiKeyHeader: "X-API-Key",
177
+ modelVersionHeader: "X-Model-Version"
175
178
  };
176
179
  var FRAME_CONFIG = {
177
180
  targetFPS: 30,
@@ -300,14 +303,22 @@ var LivenessClient = class _LivenessClient {
300
303
  constructor(config) {
301
304
  this.baseUrl = (config.baseUrl ?? DEFAULT_ENDPOINT).replace(/\/$/, "");
302
305
  this.apiKey = config.apiKey;
306
+ this.modelVersion = config.modelVersion;
303
307
  this.timeout = config.timeout ?? AUTH_CONFIG.timeout;
304
308
  this.enableRetry = config.enableRetry ?? true;
305
309
  this.fetchFn = config.customFetch ?? (typeof window !== "undefined" ? fetch.bind(window) : fetch);
306
310
  }
307
311
  /**
308
- * Make an authenticated API request
312
+ * Make an authenticated API request, returning the parsed body.
309
313
  */
310
314
  async request(path, options = {}) {
315
+ const { data } = await this.requestRaw(path, options);
316
+ return data;
317
+ }
318
+ /**
319
+ * Make an authenticated API request, returning both parsed body and headers.
320
+ */
321
+ async requestRaw(path, options = {}) {
311
322
  const url = `${this.baseUrl}${path}`;
312
323
  const controller = new AbortController();
313
324
  const timeoutId = setTimeout(() => controller.abort(), this.timeout);
@@ -325,7 +336,8 @@ var LivenessClient = class _LivenessClient {
325
336
  if (!response.ok) {
326
337
  throw await this.parseErrorResponse(response);
327
338
  }
328
- return await response.json();
339
+ const data = await response.json();
340
+ return { data, headers: response.headers };
329
341
  } catch (error) {
330
342
  clearTimeout(timeoutId);
331
343
  if (error instanceof LivenessApiError) {
@@ -410,6 +422,30 @@ var LivenessClient = class _LivenessClient {
410
422
  const match = /'error'\s*:\s*'([^']+)'/.exec(text) ?? /"error"\s*:\s*"([^"]+)"/.exec(text);
411
423
  return match?.[1] ?? null;
412
424
  }
425
+ /**
426
+ * Parse deprecation-related headers from an API response.
427
+ * Returns undefined when no deprecation info is present.
428
+ */
429
+ static parseDeprecationHeaders(headers) {
430
+ const resolved = headers.get("x-moveris-model-resolved");
431
+ if (!resolved) return void 0;
432
+ const deprecated = headers.get("deprecation") === "true";
433
+ return {
434
+ deprecated,
435
+ resolvedModel: resolved,
436
+ deprecatedModel: headers.get("x-moveris-deprecated-model") ?? void 0,
437
+ sunsetDate: headers.get("sunset") ?? void 0,
438
+ suggestedModel: headers.get("x-moveris-suggested-model") ?? void 0
439
+ };
440
+ }
441
+ /**
442
+ * Build extra request headers for model versioning.
443
+ */
444
+ buildModelVersionHeaders(modelVersion) {
445
+ const version = modelVersion ?? this.modelVersion;
446
+ if (!version) return {};
447
+ return { [AUTH_CONFIG.modelVersionHeader]: version };
448
+ }
413
449
  /**
414
450
  * Make a request with optional retry
415
451
  */
@@ -424,6 +460,20 @@ var LivenessClient = class _LivenessClient {
424
460
  }
425
461
  return this.request(path, options);
426
462
  }
463
+ /**
464
+ * Make a request with optional retry, returning both data and headers.
465
+ */
466
+ async requestWithRetryRaw(path, options = {}) {
467
+ if (this.enableRetry) {
468
+ return retryWithBackoff(() => this.requestRaw(path, options), {
469
+ maxAttempts: RETRY_CONFIG.maxAttempts,
470
+ initialDelay: RETRY_CONFIG.initialDelay,
471
+ maxDelay: RETRY_CONFIG.maxDelay,
472
+ backoffMultiplier: RETRY_CONFIG.backoffMultiplier
473
+ });
474
+ }
475
+ return this.requestRaw(path, options);
476
+ }
427
477
  // ===========================================================================
428
478
  // Health & Status
429
479
  // ===========================================================================
@@ -458,18 +508,31 @@ var LivenessClient = class _LivenessClient {
458
508
  * @returns Liveness result
459
509
  */
460
510
  async fastCheck(frames, options = {}) {
511
+ const effectiveVersion = options.modelVersion ?? this.modelVersion;
461
512
  const request = {
462
513
  session_id: options.sessionId ?? generateSessionId(),
463
514
  model: options.model ?? "10",
464
515
  source: options.source ?? "live",
465
516
  frames: toFrameData(frames),
517
+ ...options.frameCount != null ? { frame_count: options.frameCount } : {},
466
518
  ...options.warnings?.length ? { warnings: options.warnings } : {}
467
519
  };
468
- const response = await this.requestWithRetry(API_PATHS.fastCheck, {
469
- method: "POST",
470
- body: JSON.stringify(request)
471
- });
472
- return toLivenessResult(response);
520
+ const { data: response, headers } = await this.requestWithRetryRaw(
521
+ API_PATHS.fastCheck,
522
+ {
523
+ method: "POST",
524
+ body: JSON.stringify(request),
525
+ headers: this.buildModelVersionHeaders(effectiveVersion)
526
+ }
527
+ );
528
+ const result = toLivenessResult(response);
529
+ result.deprecation = _LivenessClient.parseDeprecationHeaders(headers);
530
+ if (result.deprecation?.deprecated) {
531
+ console.warn(
532
+ `[Moveris] Model "${result.deprecation.resolvedModel}" is deprecated.` + (result.deprecation.suggestedModel ? ` Migrate to "${result.deprecation.suggestedModel}".` : "") + (result.deprecation.sunsetDate ? ` Sunset date: ${result.deprecation.sunsetDate}.` : "")
533
+ );
534
+ }
535
+ return result;
473
536
  }
474
537
  /**
475
538
  * Perform fast liveness check with pre-cropped face images
@@ -479,19 +542,32 @@ var LivenessClient = class _LivenessClient {
479
542
  * @returns Liveness result
480
543
  */
481
544
  async fastCheckCrops(crops, options = {}) {
545
+ const effectiveVersion = options.modelVersion ?? this.modelVersion;
482
546
  const request = {
483
547
  session_id: options.sessionId ?? generateSessionId(),
484
548
  model: options.model ?? "10",
485
549
  source: options.source ?? "live",
486
550
  crops,
551
+ ...options.frameCount != null ? { frame_count: options.frameCount } : {},
487
552
  ...options.warnings?.length ? { warnings: options.warnings } : {},
488
553
  ...options.bgSegmentation !== void 0 ? { bg_segmentation: options.bgSegmentation } : {}
489
554
  };
490
- const response = await this.requestWithRetry(API_PATHS.fastCheckCrops, {
491
- method: "POST",
492
- body: JSON.stringify(request)
493
- });
494
- return toLivenessResult(response);
555
+ const { data: response, headers } = await this.requestWithRetryRaw(
556
+ API_PATHS.fastCheckCrops,
557
+ {
558
+ method: "POST",
559
+ body: JSON.stringify(request),
560
+ headers: this.buildModelVersionHeaders(effectiveVersion)
561
+ }
562
+ );
563
+ const result = toLivenessResult(response);
564
+ result.deprecation = _LivenessClient.parseDeprecationHeaders(headers);
565
+ if (result.deprecation?.deprecated) {
566
+ console.warn(
567
+ `[Moveris] Model "${result.deprecation.resolvedModel}" is deprecated.` + (result.deprecation.suggestedModel ? ` Migrate to "${result.deprecation.suggestedModel}".` : "") + (result.deprecation.sunsetDate ? ` Sunset date: ${result.deprecation.sunsetDate}.` : "")
568
+ );
569
+ }
570
+ return result;
495
571
  }
496
572
  /**
497
573
  * Send a single captured frame to the streaming endpoint.
@@ -519,28 +595,29 @@ var LivenessClient = class _LivenessClient {
519
595
  return this.sendStreamFrameInternal(frameData, {
520
596
  sessionId: options.sessionId,
521
597
  model: options.model ?? "10",
598
+ modelVersion: options.modelVersion,
599
+ frameCount: options.frameCount,
522
600
  source: options.source ?? "live",
523
601
  warnings: options.warnings
524
602
  });
525
603
  }
526
604
  /**
527
605
  * Send a single FrameData to the streaming endpoint with retry (internal)
528
- *
529
- * @param frame - Single frame to send
530
- * @param options - Session and model options
531
- * @returns Stream response with status
532
606
  */
533
607
  async sendStreamFrameInternal(frameData, options) {
608
+ const effectiveVersion = options.modelVersion ?? this.modelVersion;
534
609
  const request = {
535
610
  session_id: options.sessionId,
536
611
  model: options.model,
537
612
  source: options.source,
538
613
  frame: frameData,
614
+ ...options.frameCount != null ? { frame_count: options.frameCount } : {},
539
615
  ...options.warnings?.length ? { warnings: options.warnings } : {}
540
616
  };
541
617
  return this.requestWithRetry(API_PATHS.fastCheckStream, {
542
618
  method: "POST",
543
- body: JSON.stringify(request)
619
+ body: JSON.stringify(request),
620
+ headers: this.buildModelVersionHeaders(effectiveVersion)
544
621
  });
545
622
  }
546
623
  /**
@@ -565,6 +642,8 @@ var LivenessClient = class _LivenessClient {
565
642
  const response = await this.sendStreamFrameInternal(frameData, {
566
643
  sessionId,
567
644
  model,
645
+ modelVersion: options.modelVersion,
646
+ frameCount: options.frameCount,
568
647
  source,
569
648
  warnings: options.warnings
570
649
  });
@@ -615,6 +694,8 @@ var LivenessClient = class _LivenessClient {
615
694
  const response = await this.sendStreamFrameInternal(frameData, {
616
695
  sessionId,
617
696
  model,
697
+ modelVersion: options.modelVersion,
698
+ frameCount: options.frameCount,
618
699
  source,
619
700
  warnings: options.warnings
620
701
  });
@@ -891,6 +972,7 @@ var FrameQueue = class {
891
972
  };
892
973
 
893
974
  // src/types/models.ts
975
+ var VALID_FRAME_COUNTS = [10, 30, 60, 90, 120];
894
976
  var MODEL_CONFIGS = {
895
977
  // Standard fast-check models
896
978
  "10": {
@@ -898,21 +980,24 @@ var MODEL_CONFIGS = {
898
980
  minFrames: 10,
899
981
  recommendedFrames: 10,
900
982
  description: "Fast model - 10 frames, quick verification",
901
- deprecated: false
983
+ deprecated: false,
984
+ aliases: ["fast"]
902
985
  },
903
986
  "50": {
904
987
  type: "50",
905
988
  minFrames: 50,
906
989
  recommendedFrames: 50,
907
990
  description: "Balanced model - 50 frames, good accuracy",
908
- deprecated: false
991
+ deprecated: false,
992
+ aliases: ["spatial"]
909
993
  },
910
994
  "250": {
911
995
  type: "250",
912
996
  minFrames: 250,
913
997
  recommendedFrames: 250,
914
998
  description: "High-accuracy model - 250 frames, best accuracy",
915
- deprecated: false
999
+ deprecated: false,
1000
+ aliases: ["spatial"]
916
1001
  },
917
1002
  // Hybrid V2 models with physiological features
918
1003
  "hybrid-v2-10": {
@@ -920,63 +1005,72 @@ var MODEL_CONFIGS = {
920
1005
  minFrames: 10,
921
1006
  recommendedFrames: 10,
922
1007
  description: "Hybrid V2 10-frame model with physio features",
923
- deprecated: false
1008
+ deprecated: false,
1009
+ aliases: ["hybrid"]
924
1010
  },
925
1011
  "hybrid-v2-30": {
926
1012
  type: "hybrid-v2-30",
927
1013
  minFrames: 30,
928
1014
  recommendedFrames: 30,
929
1015
  description: "Hybrid V2 30-frame model with physio features",
930
- deprecated: false
1016
+ deprecated: false,
1017
+ aliases: ["hybrid"]
931
1018
  },
932
1019
  "hybrid-v2-50": {
933
1020
  type: "hybrid-v2-50",
934
1021
  minFrames: 50,
935
1022
  recommendedFrames: 50,
936
1023
  description: "Hybrid V2 50-frame model with physio features",
937
- deprecated: false
1024
+ deprecated: false,
1025
+ aliases: ["hybrid"]
938
1026
  },
939
1027
  "hybrid-v2-60": {
940
1028
  type: "hybrid-v2-60",
941
1029
  minFrames: 60,
942
1030
  recommendedFrames: 60,
943
1031
  description: "Hybrid V2 60-frame model with physio features",
944
- deprecated: false
1032
+ deprecated: false,
1033
+ aliases: ["hybrid"]
945
1034
  },
946
1035
  "hybrid-v2-90": {
947
1036
  type: "hybrid-v2-90",
948
1037
  minFrames: 90,
949
1038
  recommendedFrames: 90,
950
1039
  description: "Hybrid V2 90-frame model with physio features",
951
- deprecated: false
1040
+ deprecated: false,
1041
+ aliases: ["hybrid"]
952
1042
  },
953
1043
  "hybrid-v2-100": {
954
1044
  type: "hybrid-v2-100",
955
1045
  minFrames: 100,
956
1046
  recommendedFrames: 100,
957
1047
  description: "Hybrid V2 100-frame model with physio features",
958
- deprecated: false
1048
+ deprecated: false,
1049
+ aliases: ["hybrid"]
959
1050
  },
960
1051
  "hybrid-v2-125": {
961
1052
  type: "hybrid-v2-125",
962
1053
  minFrames: 125,
963
1054
  recommendedFrames: 125,
964
1055
  description: "Hybrid V2 125-frame model with physio features",
965
- deprecated: false
1056
+ deprecated: false,
1057
+ aliases: ["hybrid"]
966
1058
  },
967
1059
  "hybrid-v2-150": {
968
1060
  type: "hybrid-v2-150",
969
1061
  minFrames: 150,
970
1062
  recommendedFrames: 150,
971
1063
  description: "Hybrid V2 150-frame model with physio features",
972
- deprecated: false
1064
+ deprecated: false,
1065
+ aliases: ["hybrid"]
973
1066
  },
974
1067
  "hybrid-v2-250": {
975
1068
  type: "hybrid-v2-250",
976
1069
  minFrames: 250,
977
1070
  recommendedFrames: 250,
978
1071
  description: "Hybrid V2 250-frame model with physio features",
979
- deprecated: false
1072
+ deprecated: false,
1073
+ aliases: ["hybrid"]
980
1074
  },
981
1075
  // Mixed V1 models — deprecated, use mixed-*-v2 equivalents instead
982
1076
  "mixed-10": {
@@ -984,49 +1078,68 @@ var MODEL_CONFIGS = {
984
1078
  minFrames: 10,
985
1079
  recommendedFrames: 10,
986
1080
  description: "Mixed 10-frame model",
987
- deprecated: true
1081
+ deprecated: true,
1082
+ aliases: ["v1"],
1083
+ sunsetDate: "2026-09-01",
1084
+ replacement: "mixed-10-v2"
988
1085
  },
989
1086
  "mixed-30": {
990
1087
  type: "mixed-30",
991
1088
  minFrames: 30,
992
1089
  recommendedFrames: 30,
993
1090
  description: "Mixed 30-frame model",
994
- deprecated: true
1091
+ deprecated: true,
1092
+ aliases: ["v1"],
1093
+ sunsetDate: "2026-09-01",
1094
+ replacement: "mixed-30-v2"
995
1095
  },
996
1096
  "mixed-60": {
997
1097
  type: "mixed-60",
998
1098
  minFrames: 60,
999
1099
  recommendedFrames: 60,
1000
1100
  description: "Mixed 60-frame model",
1001
- deprecated: true
1101
+ deprecated: true,
1102
+ aliases: ["v1"],
1103
+ sunsetDate: "2026-09-01",
1104
+ replacement: "mixed-60-v2"
1002
1105
  },
1003
1106
  "mixed-90": {
1004
1107
  type: "mixed-90",
1005
1108
  minFrames: 90,
1006
1109
  recommendedFrames: 90,
1007
1110
  description: "Mixed 90-frame model",
1008
- deprecated: true
1111
+ deprecated: true,
1112
+ aliases: ["v1"],
1113
+ sunsetDate: "2026-09-01",
1114
+ replacement: "mixed-90-v2"
1009
1115
  },
1010
1116
  "mixed-120": {
1011
1117
  type: "mixed-120",
1012
1118
  minFrames: 120,
1013
1119
  recommendedFrames: 120,
1014
1120
  description: "Mixed 120-frame model",
1015
- deprecated: true
1121
+ deprecated: true,
1122
+ aliases: ["v1"],
1123
+ sunsetDate: "2026-09-01",
1124
+ replacement: "mixed-120-v2"
1016
1125
  },
1017
1126
  "mixed-150": {
1018
1127
  type: "mixed-150",
1019
1128
  minFrames: 150,
1020
1129
  recommendedFrames: 150,
1021
1130
  description: "Mixed 150-frame model",
1022
- deprecated: true
1131
+ deprecated: true,
1132
+ aliases: ["v1"],
1133
+ sunsetDate: "2026-09-01"
1023
1134
  },
1024
1135
  "mixed-250": {
1025
1136
  type: "mixed-250",
1026
1137
  minFrames: 250,
1027
1138
  recommendedFrames: 250,
1028
1139
  description: "Mixed 250-frame model",
1029
- deprecated: true
1140
+ deprecated: true,
1141
+ aliases: ["v1"],
1142
+ sunsetDate: "2026-09-01"
1030
1143
  },
1031
1144
  // Mixed V2 models (exp_042 checkpoints — EER: 4.0% / AUC: 0.991 at 30f)
1032
1145
  "mixed-10-v2": {
@@ -1034,35 +1147,40 @@ var MODEL_CONFIGS = {
1034
1147
  minFrames: 10,
1035
1148
  recommendedFrames: 10,
1036
1149
  description: "Mixed V2 10-frame model",
1037
- deprecated: false
1150
+ deprecated: false,
1151
+ aliases: ["latest", "v2"]
1038
1152
  },
1039
1153
  "mixed-30-v2": {
1040
1154
  type: "mixed-30-v2",
1041
1155
  minFrames: 30,
1042
1156
  recommendedFrames: 30,
1043
1157
  description: "Mixed V2 30-frame model (recommended \u2014 95.7% balanced accuracy)",
1044
- deprecated: false
1158
+ deprecated: false,
1159
+ aliases: ["latest", "v2"]
1045
1160
  },
1046
1161
  "mixed-60-v2": {
1047
1162
  type: "mixed-60-v2",
1048
1163
  minFrames: 60,
1049
1164
  recommendedFrames: 60,
1050
1165
  description: "Mixed V2 60-frame model",
1051
- deprecated: false
1166
+ deprecated: false,
1167
+ aliases: ["latest", "v2"]
1052
1168
  },
1053
1169
  "mixed-90-v2": {
1054
1170
  type: "mixed-90-v2",
1055
1171
  minFrames: 90,
1056
1172
  recommendedFrames: 90,
1057
1173
  description: "Mixed V2 90-frame model",
1058
- deprecated: false
1174
+ deprecated: false,
1175
+ aliases: ["latest", "v2"]
1059
1176
  },
1060
1177
  "mixed-120-v2": {
1061
1178
  type: "mixed-120-v2",
1062
1179
  minFrames: 120,
1063
1180
  recommendedFrames: 120,
1064
1181
  description: "Mixed V2 120-frame model",
1065
- deprecated: false
1182
+ deprecated: false,
1183
+ aliases: ["latest", "v2"]
1066
1184
  }
1067
1185
  };
1068
1186
  var HYBRID_MODEL_CONFIGS = {
@@ -1362,7 +1480,8 @@ function getCaptureQualityFeedback(state) {
1362
1480
  tooCloseToIdeal,
1363
1481
  cameraAngleLow,
1364
1482
  cameraAngleHigh,
1365
- cameraTilted
1483
+ cameraTilted,
1484
+ faceAreaLow
1366
1485
  } = state;
1367
1486
  if (!hasFace) {
1368
1487
  return FEEDBACK_MESSAGES.no_face;
@@ -1379,7 +1498,7 @@ function getCaptureQualityFeedback(state) {
1379
1498
  if (tooClose) {
1380
1499
  return FEEDBACK_MESSAGES.too_close;
1381
1500
  }
1382
- if (tooFar) {
1501
+ if (tooFar || faceAreaLow) {
1383
1502
  return FEEDBACK_MESSAGES.too_far;
1384
1503
  }
1385
1504
  if (isPartialFace) {
@@ -1420,9 +1539,10 @@ function canCaptureFrame(state) {
1420
1539
  tooCloseToIdeal,
1421
1540
  cameraAngleLow,
1422
1541
  cameraAngleHigh,
1423
- cameraTilted
1542
+ cameraTilted,
1543
+ faceAreaLow
1424
1544
  } = state;
1425
- return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !cameraAngleLow && !cameraAngleHigh && !cameraTilted;
1545
+ return hasFace && alignment >= ALIGNMENT_THRESHOLD_CAPTURE && !tooClose && !tooFar && !isBlurry && !isPartialFace && !tooFarFromIdeal && !tooCloseToIdeal && !cameraAngleLow && !cameraAngleHigh && !cameraTilted && !faceAreaLow;
1426
1546
  }
1427
1547
 
1428
1548
  // src/utils/validators.ts
@@ -1506,6 +1626,7 @@ var FACE_CENTER_VERTICAL_OFFSET = 0.05;
1506
1626
  var MIN_IDEAL_FACE_RATIO = 0.05;
1507
1627
  var MAX_IDEAL_FACE_RATIO = 0.2;
1508
1628
  var MIN_FACE_RATIO = 0.036;
1629
+ var MIN_FACE_AREA_RATIO = 0.04;
1509
1630
  var MAX_FACE_RATIO = 0.7;
1510
1631
  var FACE_CROP_OUTPUT_SIZE = 224;
1511
1632
  var MAX_FACE_PERCENTAGE_IN_CROP = 0.45;
@@ -2072,6 +2193,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2072
2193
  MAX_IDEAL_FACE_RATIO,
2073
2194
  MIN_CAPTURE_ALIGNMENT,
2074
2195
  MIN_CROP_MULTIPLIER,
2196
+ MIN_FACE_AREA_RATIO,
2075
2197
  MIN_FACE_BOTTOM_MARGIN,
2076
2198
  MIN_FACE_RATIO,
2077
2199
  MIN_FACE_SIDE_MARGIN,
@@ -2085,6 +2207,7 @@ function checkEyeRegionQuality(pixels, thresholds = EYE_QUALITY_THRESHOLDS) {
2085
2207
  OVAL_REGION_MOBILE,
2086
2208
  RETRY_CONFIG,
2087
2209
  TARGET_FACE_PERCENTAGE_IN_CROP,
2210
+ VALID_FRAME_COUNTS,
2088
2211
  analyzeBlur,
2089
2212
  analyzeEyeRegionBrightness,
2090
2213
  analyzeEyeRegionContrast,