@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/README.md +147 -17
- package/dist/index.d.mts +38 -1
- package/dist/index.d.ts +38 -1
- package/dist/index.js +169 -46
- package/dist/index.mjs +167 -46
- package/package.json +1 -1
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
|
-
|
|
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.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
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.
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
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,
|