@aws-amplify/ui-react-liveness 3.0.15 → 3.0.17
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/esm/components/FaceLivenessDetector/FaceLivenessDetectorCore.mjs +1 -1
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/LivenessCameraModule.mjs +1 -1
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.mjs +1 -1
- package/dist/esm/components/FaceLivenessDetector/displayText.mjs +2 -0
- package/dist/esm/components/FaceLivenessDetector/service/machine/{index.mjs → machine.mjs} +43 -35
- package/dist/esm/components/FaceLivenessDetector/service/types/error.mjs +1 -0
- package/dist/esm/components/FaceLivenessDetector/service/types/liveness.mjs +0 -1
- package/dist/esm/components/FaceLivenessDetector/service/utils/constants.mjs +10 -2
- package/dist/esm/components/FaceLivenessDetector/service/utils/createStreamingClient/CustomWebSocketFetchHandler.mjs +3 -6
- package/dist/esm/components/FaceLivenessDetector/service/utils/eventUtils.mjs +7 -1
- package/dist/esm/components/FaceLivenessDetector/service/utils/getFaceMatchStateInLivenessOval.mjs +59 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/liveness.mjs +22 -74
- package/dist/esm/components/FaceLivenessDetector/shared/DefaultStartScreenComponents.mjs +1 -1
- package/dist/esm/components/FaceLivenessDetector/shared/FaceLivenessErrorModal.mjs +6 -2
- package/dist/esm/components/FaceLivenessDetector/shared/Hint.mjs +5 -8
- package/dist/esm/components/FaceLivenessDetector/utils/getDisplayText.mjs +3 -1
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +145 -122
- package/dist/styles.css +1 -1
- package/dist/types/components/FaceLivenessDetector/displayText.d.ts +2 -0
- package/dist/types/components/FaceLivenessDetector/service/machine/index.d.ts +1 -5
- package/dist/types/components/FaceLivenessDetector/service/machine/machine.d.ts +5 -0
- package/dist/types/components/FaceLivenessDetector/service/types/error.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/types/liveness.d.ts +0 -1
- package/dist/types/components/FaceLivenessDetector/service/types/machine.d.ts +2 -3
- package/dist/types/components/FaceLivenessDetector/service/utils/constants.d.ts +6 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/CustomWebSocketFetchHandler.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/eventUtils.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/getFaceMatchStateInLivenessOval.d.ts +17 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/index.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/liveness.d.ts +2 -8
- package/dist/types/components/FaceLivenessDetector/shared/FaceLivenessErrorModal.d.ts +2 -0
- package/dist/types/components/FaceLivenessDetector/shared/Hint.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { useInterpret } from '@xstate/react';
|
|
3
|
-
import { livenessMachine } from './service/machine/
|
|
3
|
+
import { livenessMachine } from './service/machine/machine.mjs';
|
|
4
4
|
import './service/types/liveness.mjs';
|
|
5
5
|
import '@tensorflow/tfjs-core';
|
|
6
6
|
import '@tensorflow-models/face-detection';
|
|
@@ -2,7 +2,7 @@ import React__default, { useRef, useState } from 'react';
|
|
|
2
2
|
import { classNames } from '@aws-amplify/ui';
|
|
3
3
|
import { Loader, View, Flex, Text, Label, SelectField, Button } from '@aws-amplify/ui-react';
|
|
4
4
|
import { useColorMode } from '@aws-amplify/ui-react/internal';
|
|
5
|
-
import '../service/machine/
|
|
5
|
+
import '../service/machine/machine.mjs';
|
|
6
6
|
import { FaceMatchState } from '../service/types/liveness.mjs';
|
|
7
7
|
import '@tensorflow/tfjs-core';
|
|
8
8
|
import '@tensorflow-models/face-detection';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { Flex, Text, Button, View } from '@aws-amplify/ui-react';
|
|
3
|
-
import '../service/machine/
|
|
3
|
+
import '../service/machine/machine.mjs';
|
|
4
4
|
import '../service/types/liveness.mjs';
|
|
5
5
|
import { LivenessErrorState } from '../service/types/error.mjs';
|
|
6
6
|
import '@tensorflow/tfjs-core';
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const defaultErrorDisplayText = {
|
|
2
2
|
errorLabelText: 'Error',
|
|
3
|
+
connectionTimeoutHeaderText: 'Connection time out',
|
|
4
|
+
connectionTimeoutMessageText: 'Connection has timed out.',
|
|
3
5
|
timeoutHeaderText: 'Time out',
|
|
4
6
|
timeoutMessageText: "Face didn't fit inside oval in time limit. Try again and completely fill the oval with face in it.",
|
|
5
7
|
faceDistanceHeaderText: 'Forward movement detected',
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
2
|
import { createMachine, assign, spawn, actions } from 'xstate';
|
|
3
|
-
import { drawStaticOval, getBoundingBox, getColorsSequencesFromSessionInformation, isCameraDeviceVirtual, getFaceMatchState, isFaceDistanceBelowThreshold, estimateIllumination, getOvalDetailsFromSessionInformation, generateBboxFromLandmarks, drawLivenessOvalInCanvas, getOvalBoundingBox, getIntersectionOverUnion,
|
|
3
|
+
import { drawStaticOval, getBoundingBox, getColorsSequencesFromSessionInformation, isCameraDeviceVirtual, getFaceMatchState, isFaceDistanceBelowThreshold, estimateIllumination, getOvalDetailsFromSessionInformation, generateBboxFromLandmarks, drawLivenessOvalInCanvas, getOvalBoundingBox, getIntersectionOverUnion, getStaticLivenessOvalDetails } from '../utils/liveness.mjs';
|
|
4
4
|
import { FaceMatchState } from '../types/liveness.mjs';
|
|
5
5
|
import { LivenessErrorState } from '../types/error.mjs';
|
|
6
6
|
import { BlazeFaceFaceDetection } from '../utils/blazefaceFaceDetection.mjs';
|
|
7
|
+
import { getFaceMatchStateInLivenessOval } from '../utils/getFaceMatchStateInLivenessOval.mjs';
|
|
7
8
|
import { LivenessStreamProvider } from '../utils/streamProvider.mjs';
|
|
8
9
|
import { FreshnessColorDisplay } from '../utils/freshnessColorDisplay.mjs';
|
|
9
|
-
import { isServerSesssionInformationEvent, isDisconnectionEvent, isValidationExceptionEvent, isInternalServerExceptionEvent, isThrottlingExceptionEvent, isServiceQuotaExceededExceptionEvent, isInvalidSignatureRegionException } from '../utils/eventUtils.mjs';
|
|
10
|
+
import { isServerSesssionInformationEvent, isDisconnectionEvent, isValidationExceptionEvent, isInternalServerExceptionEvent, isThrottlingExceptionEvent, isServiceQuotaExceededExceptionEvent, isInvalidSignatureRegionException, isConnectionTimeoutError } from '../utils/eventUtils.mjs';
|
|
10
11
|
import { STATIC_VIDEO_CONSTRAINTS } from '../../utils/helpers.mjs';
|
|
11
12
|
import { WS_CLOSURE_CODE } from '../utils/constants.mjs';
|
|
12
13
|
|
|
13
14
|
const CAMERA_ID_KEY = 'AmplifyLivenessCameraId';
|
|
14
15
|
const DEFAULT_FACE_FIT_TIMEOUT = 7000;
|
|
15
|
-
const MIN_FACE_MATCH_TIME = 1000;
|
|
16
16
|
let responseStream;
|
|
17
17
|
const responseStreamActor = async (callback) => {
|
|
18
18
|
try {
|
|
@@ -56,14 +56,20 @@ const responseStreamActor = async (callback) => {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
|
-
let returnedError = error;
|
|
60
59
|
if (isInvalidSignatureRegionException(error)) {
|
|
61
|
-
returnedError = new Error('Invalid region in FaceLivenessDetector or credentials are scoped to the wrong region.');
|
|
62
|
-
}
|
|
63
|
-
if (returnedError instanceof Error) {
|
|
64
60
|
callback({
|
|
65
61
|
type: 'SERVER_ERROR',
|
|
66
|
-
data: {
|
|
62
|
+
data: {
|
|
63
|
+
error: new Error('Invalid region in FaceLivenessDetector or credentials are scoped to the wrong region.'),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if (error instanceof Error) {
|
|
68
|
+
callback({
|
|
69
|
+
type: isConnectionTimeoutError(error)
|
|
70
|
+
? 'CONNECTION_TIMEOUT'
|
|
71
|
+
: 'SERVER_ERROR',
|
|
72
|
+
data: { error },
|
|
67
73
|
});
|
|
68
74
|
}
|
|
69
75
|
}
|
|
@@ -102,7 +108,6 @@ const livenessMachine = createMachine({
|
|
|
102
108
|
currentDetectedFace: undefined,
|
|
103
109
|
startFace: undefined,
|
|
104
110
|
endFace: undefined,
|
|
105
|
-
initialFaceMatchTime: undefined,
|
|
106
111
|
},
|
|
107
112
|
freshnessColorAssociatedParams: {
|
|
108
113
|
freshnessColorEl: undefined,
|
|
@@ -142,6 +147,10 @@ const livenessMachine = createMachine({
|
|
|
142
147
|
target: 'error',
|
|
143
148
|
actions: 'updateErrorStateForServer',
|
|
144
149
|
},
|
|
150
|
+
CONNECTION_TIMEOUT: {
|
|
151
|
+
target: 'error',
|
|
152
|
+
actions: 'updateErrorStateForConnectionTimeout',
|
|
153
|
+
},
|
|
145
154
|
RUNTIME_ERROR: {
|
|
146
155
|
target: 'error',
|
|
147
156
|
},
|
|
@@ -287,6 +296,8 @@ const livenessMachine = createMachine({
|
|
|
287
296
|
100: { target: 'checkRecordingStarted' },
|
|
288
297
|
},
|
|
289
298
|
},
|
|
299
|
+
// Evaluates face match and moves to checkMatch
|
|
300
|
+
// which continually checks for match until either timeout or face match
|
|
290
301
|
ovalMatching: {
|
|
291
302
|
entry: 'cancelOvalDrawingTimeout',
|
|
292
303
|
invoke: {
|
|
@@ -297,29 +308,32 @@ const livenessMachine = createMachine({
|
|
|
297
308
|
},
|
|
298
309
|
},
|
|
299
310
|
},
|
|
311
|
+
// If `hasFaceMatchedInOval` is true, then move to `delayBeforeFlash`, which pauses
|
|
312
|
+
// for one second to show "Hold still" text before moving to `flashFreshnessColors`.
|
|
313
|
+
// If not, move back to ovalMatching and re-evaluate match state
|
|
300
314
|
checkMatch: {
|
|
301
315
|
after: {
|
|
302
316
|
0: {
|
|
303
|
-
target: '
|
|
304
|
-
cond: '
|
|
317
|
+
target: 'delayBeforeFlash',
|
|
318
|
+
cond: 'hasFaceMatchedInOval',
|
|
305
319
|
actions: [
|
|
320
|
+
'setFaceMatchTimeAndStartFace',
|
|
306
321
|
'updateEndFaceMatch',
|
|
307
322
|
'setupFlashFreshnessColors',
|
|
308
323
|
'cancelOvalMatchTimeout',
|
|
309
324
|
'cancelOvalDrawingTimeout',
|
|
310
325
|
],
|
|
311
326
|
},
|
|
312
|
-
0.1: {
|
|
313
|
-
target: 'ovalMatching',
|
|
314
|
-
cond: 'hasFaceMatchedInOval',
|
|
315
|
-
actions: 'setFaceMatchTimeAndStartFace',
|
|
316
|
-
},
|
|
317
327
|
1: {
|
|
318
328
|
target: 'ovalMatching',
|
|
319
|
-
cond: 'hasNotFaceMatchedInOval',
|
|
320
329
|
},
|
|
321
330
|
},
|
|
322
331
|
},
|
|
332
|
+
delayBeforeFlash: {
|
|
333
|
+
after: {
|
|
334
|
+
1000: 'flashFreshnessColors',
|
|
335
|
+
},
|
|
336
|
+
},
|
|
323
337
|
flashFreshnessColors: {
|
|
324
338
|
invoke: {
|
|
325
339
|
src: 'flashColors',
|
|
@@ -584,14 +598,13 @@ const livenessMachine = createMachine({
|
|
|
584
598
|
startFace: context.faceMatchAssociatedParams.startFace === undefined
|
|
585
599
|
? context.faceMatchAssociatedParams.currentDetectedFace
|
|
586
600
|
: context.faceMatchAssociatedParams.startFace,
|
|
587
|
-
initialFaceMatchTime: context.faceMatchAssociatedParams.initialFaceMatchTime ===
|
|
588
|
-
undefined
|
|
589
|
-
? Date.now()
|
|
590
|
-
: context.faceMatchAssociatedParams.initialFaceMatchTime,
|
|
591
601
|
};
|
|
592
602
|
},
|
|
593
603
|
}),
|
|
594
604
|
resetErrorState: assign({ errorState: (_) => undefined }),
|
|
605
|
+
updateErrorStateForConnectionTimeout: assign({
|
|
606
|
+
errorState: (_) => LivenessErrorState.CONNECTION_TIMEOUT,
|
|
607
|
+
}),
|
|
595
608
|
updateErrorStateForTimeout: assign({
|
|
596
609
|
errorState: (_, event) => event.data?.errorState || LivenessErrorState.TIMEOUT,
|
|
597
610
|
}),
|
|
@@ -759,21 +772,10 @@ const livenessMachine = createMachine({
|
|
|
759
772
|
},
|
|
760
773
|
guards: {
|
|
761
774
|
shouldTimeoutOnFailedAttempts: (context) => context.failedAttempts >= context.maxFailedAttempts,
|
|
762
|
-
hasFaceMatchedInOvalWithMinTime: (context) => {
|
|
763
|
-
const { faceMatchState, initialFaceMatchTime } = context.faceMatchAssociatedParams;
|
|
764
|
-
const timeSinceInitialFaceMatch = Date.now() - initialFaceMatchTime;
|
|
765
|
-
const hasMatched = faceMatchState === FaceMatchState.MATCHED &&
|
|
766
|
-
timeSinceInitialFaceMatch >= MIN_FACE_MATCH_TIME;
|
|
767
|
-
return hasMatched;
|
|
768
|
-
},
|
|
769
775
|
hasFaceMatchedInOval: (context) => {
|
|
770
776
|
return (context.faceMatchAssociatedParams.faceMatchState ===
|
|
771
777
|
FaceMatchState.MATCHED);
|
|
772
778
|
},
|
|
773
|
-
hasNotFaceMatchedInOval: (context) => {
|
|
774
|
-
return (context.faceMatchAssociatedParams.faceMatchState !==
|
|
775
|
-
FaceMatchState.MATCHED);
|
|
776
|
-
},
|
|
777
779
|
hasSingleFace: (context) => {
|
|
778
780
|
return (context.faceMatchAssociatedParams.faceMatchState ===
|
|
779
781
|
FaceMatchState.FACE_IDENTIFIED);
|
|
@@ -990,7 +992,7 @@ const livenessMachine = createMachine({
|
|
|
990
992
|
videoWidth: videoEl.width,
|
|
991
993
|
});
|
|
992
994
|
// renormalize initial face
|
|
993
|
-
const renormalizedFace = generateBboxFromLandmarks(initialFace, ovalDetails);
|
|
995
|
+
const renormalizedFace = generateBboxFromLandmarks(initialFace, ovalDetails, videoEl.videoHeight);
|
|
994
996
|
initialFace.top = renormalizedFace.top;
|
|
995
997
|
initialFace.left = renormalizedFace.left;
|
|
996
998
|
initialFace.height = renormalizedFace.bottom - renormalizedFace.top;
|
|
@@ -1019,7 +1021,7 @@ const livenessMachine = createMachine({
|
|
|
1019
1021
|
let faceMatchPercentage = 0;
|
|
1020
1022
|
let detectedFace;
|
|
1021
1023
|
let illuminationState;
|
|
1022
|
-
const initialFaceBoundingBox = generateBboxFromLandmarks(initialFace, ovalDetails);
|
|
1024
|
+
const initialFaceBoundingBox = generateBboxFromLandmarks(initialFace, ovalDetails, videoEl.videoHeight);
|
|
1023
1025
|
const { ovalBoundingBox } = getOvalBoundingBox(ovalDetails);
|
|
1024
1026
|
const initialFaceIntersection = getIntersectionOverUnion(initialFaceBoundingBox, ovalBoundingBox);
|
|
1025
1027
|
switch (detectedFaces.length) {
|
|
@@ -1032,7 +1034,13 @@ const livenessMachine = createMachine({
|
|
|
1032
1034
|
case 1: {
|
|
1033
1035
|
//exactly one face detected, match face with oval;
|
|
1034
1036
|
detectedFace = detectedFaces[0];
|
|
1035
|
-
const { faceMatchState: faceMatchStateInLivenessOval, faceMatchPercentage: faceMatchPercentageInLivenessOval, } = getFaceMatchStateInLivenessOval(
|
|
1037
|
+
const { faceMatchState: faceMatchStateInLivenessOval, faceMatchPercentage: faceMatchPercentageInLivenessOval, } = getFaceMatchStateInLivenessOval({
|
|
1038
|
+
face: detectedFace,
|
|
1039
|
+
ovalDetails: ovalDetails,
|
|
1040
|
+
initialFaceIntersection,
|
|
1041
|
+
sessionInformation: serverSessionInformation,
|
|
1042
|
+
frameHeight: videoEl.videoHeight,
|
|
1043
|
+
});
|
|
1036
1044
|
faceMatchState = faceMatchStateInLivenessOval;
|
|
1037
1045
|
faceMatchPercentage = faceMatchPercentageInLivenessOval;
|
|
1038
1046
|
break;
|
|
@@ -14,7 +14,6 @@ var FaceMatchState;
|
|
|
14
14
|
(function (FaceMatchState) {
|
|
15
15
|
FaceMatchState["MATCHED"] = "MATCHED";
|
|
16
16
|
FaceMatchState["TOO_FAR"] = "TOO FAR";
|
|
17
|
-
FaceMatchState["TOO_CLOSE"] = "TOO CLOSE";
|
|
18
17
|
FaceMatchState["CANT_IDENTIFY"] = "CANNOT IDENTIFY";
|
|
19
18
|
FaceMatchState["FACE_IDENTIFIED"] = "ONE FACE IDENTIFIED";
|
|
20
19
|
FaceMatchState["TOO_MANY"] = "TOO MANY FACES";
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
// Face distance is calculated as pupilDistance / ovalWidth.
|
|
2
2
|
// The further away you are from the camera the distance between your pupils will decrease, thus lowering the threshold values.
|
|
3
|
-
// These
|
|
3
|
+
// These FACE_DISTANCE_THRESHOLD values are determined by the science team and should only be changed with their approval.
|
|
4
4
|
// We want to ensure at the start of a check that the user's pupilDistance/ovalWidth is below FACE_DISTANCE_THRESHOLD to ensure that they are starting
|
|
5
5
|
// a certain distance away from the camera.
|
|
6
6
|
const FACE_DISTANCE_THRESHOLD = 0.32;
|
|
7
7
|
const REDUCED_THRESHOLD = 0.4;
|
|
8
8
|
const REDUCED_THRESHOLD_MOBILE = 0.37;
|
|
9
|
+
// Constants from science team to determine ocular distance (space between eyes)
|
|
10
|
+
const PUPIL_DISTANCE_WEIGHT = 2.0;
|
|
11
|
+
const FACE_HEIGHT_WEIGHT = 1.8;
|
|
12
|
+
// Constants from science team to find face match percentage
|
|
13
|
+
const FACE_MATCH_RANGE_MIN = 0;
|
|
14
|
+
const FACE_MATCH_RANGE_MAX = 1;
|
|
15
|
+
const FACE_MATCH_WEIGHT_MIN = 0.25;
|
|
16
|
+
const FACE_MATCH_WEIGHT_MAX = 0.75;
|
|
9
17
|
const WS_CLOSURE_CODE = {
|
|
10
18
|
SUCCESS_CODE: 1000,
|
|
11
19
|
DEFAULT_ERROR_CODE: 4000,
|
|
@@ -15,4 +23,4 @@ const WS_CLOSURE_CODE = {
|
|
|
15
23
|
USER_ERROR_DURING_CONNECTION: 4007,
|
|
16
24
|
};
|
|
17
25
|
|
|
18
|
-
export { FACE_DISTANCE_THRESHOLD, REDUCED_THRESHOLD, REDUCED_THRESHOLD_MOBILE, WS_CLOSURE_CODE };
|
|
26
|
+
export { FACE_DISTANCE_THRESHOLD, FACE_HEIGHT_WEIGHT, FACE_MATCH_RANGE_MAX, FACE_MATCH_RANGE_MIN, FACE_MATCH_WEIGHT_MAX, FACE_MATCH_WEIGHT_MIN, PUPIL_DISTANCE_WEIGHT, REDUCED_THRESHOLD, REDUCED_THRESHOLD_MOBILE, WS_CLOSURE_CODE };
|
|
@@ -9,6 +9,7 @@ import { WS_CLOSURE_CODE } from '../constants.mjs';
|
|
|
9
9
|
* Because of this the file is not fully typed at this time but we should eventually work on fully typing this file.
|
|
10
10
|
*/
|
|
11
11
|
const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 2000;
|
|
12
|
+
const WEBSOCKET_CONNECTION_TIMEOUT_MESSAGE = 'Websocket connection timeout';
|
|
12
13
|
const isWebSocketRequest = (request) => request.protocol === 'ws:' || request.protocol === 'wss:';
|
|
13
14
|
const isReadableStream = (payload) => typeof ReadableStream === 'function' && payload instanceof ReadableStream;
|
|
14
15
|
/**
|
|
@@ -108,11 +109,7 @@ class CustomWebSocketFetchHandler {
|
|
|
108
109
|
return new Promise((resolve, reject) => {
|
|
109
110
|
const timeout = setTimeout(() => {
|
|
110
111
|
this.removeNotUsableSockets(socket.url);
|
|
111
|
-
reject(
|
|
112
|
-
$metadata: {
|
|
113
|
-
httpStatusCode: 500,
|
|
114
|
-
},
|
|
115
|
-
});
|
|
112
|
+
reject(new Error(WEBSOCKET_CONNECTION_TIMEOUT_MESSAGE));
|
|
116
113
|
}, connectionTimeout);
|
|
117
114
|
socket.onopen = () => {
|
|
118
115
|
clearTimeout(timeout);
|
|
@@ -196,4 +193,4 @@ class CustomWebSocketFetchHandler {
|
|
|
196
193
|
}
|
|
197
194
|
}
|
|
198
195
|
|
|
199
|
-
export { CustomWebSocketFetchHandler };
|
|
196
|
+
export { CustomWebSocketFetchHandler, WEBSOCKET_CONNECTION_TIMEOUT_MESSAGE };
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
import { WEBSOCKET_CONNECTION_TIMEOUT_MESSAGE } from './createStreamingClient/CustomWebSocketFetchHandler.mjs';
|
|
2
|
+
|
|
1
3
|
const isServerSesssionInformationEvent = (value) => {
|
|
2
4
|
return !!value
|
|
3
5
|
?.ServerSessionInformationEvent;
|
|
4
6
|
};
|
|
7
|
+
const isConnectionTimeoutError = (error) => {
|
|
8
|
+
const { message } = error;
|
|
9
|
+
return message.includes(WEBSOCKET_CONNECTION_TIMEOUT_MESSAGE);
|
|
10
|
+
};
|
|
5
11
|
const isDisconnectionEvent = (value) => {
|
|
6
12
|
return !!value
|
|
7
13
|
?.DisconnectionEvent;
|
|
@@ -27,4 +33,4 @@ const isInvalidSignatureRegionException = (error) => {
|
|
|
27
33
|
return (name === 'InvalidSignatureException' && message.includes('valid region'));
|
|
28
34
|
};
|
|
29
35
|
|
|
30
|
-
export { isDisconnectionEvent, isInternalServerExceptionEvent, isInvalidSignatureRegionException, isServerSesssionInformationEvent, isServiceQuotaExceededExceptionEvent, isThrottlingExceptionEvent, isValidationExceptionEvent };
|
|
36
|
+
export { isConnectionTimeoutError, isDisconnectionEvent, isInternalServerExceptionEvent, isInvalidSignatureRegionException, isServerSesssionInformationEvent, isServiceQuotaExceededExceptionEvent, isThrottlingExceptionEvent, isValidationExceptionEvent };
|
package/dist/esm/components/FaceLivenessDetector/service/utils/getFaceMatchStateInLivenessOval.mjs
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { FaceMatchState } from '../types/liveness.mjs';
|
|
2
|
+
import { generateBboxFromLandmarks, getOvalBoundingBox, getIntersectionOverUnion } from './liveness.mjs';
|
|
3
|
+
import { FACE_MATCH_RANGE_MAX, FACE_MATCH_WEIGHT_MAX, FACE_MATCH_WEIGHT_MIN, FACE_MATCH_RANGE_MIN } from './constants.mjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Returns the state of the provided face with respect to the provided liveness oval.
|
|
7
|
+
*/
|
|
8
|
+
function getFaceMatchStateInLivenessOval({ face, ovalDetails, initialFaceIntersection, sessionInformation, frameHeight, }) {
|
|
9
|
+
let faceMatchState;
|
|
10
|
+
const challengeConfig = sessionInformation?.Challenge?.FaceMovementAndLightChallenge
|
|
11
|
+
?.ChallengeConfig;
|
|
12
|
+
if (!challengeConfig ||
|
|
13
|
+
!challengeConfig.OvalIouThreshold ||
|
|
14
|
+
!challengeConfig.OvalIouHeightThreshold ||
|
|
15
|
+
!challengeConfig.OvalIouWidthThreshold ||
|
|
16
|
+
!challengeConfig.FaceIouHeightThreshold ||
|
|
17
|
+
!challengeConfig.FaceIouWidthThreshold) {
|
|
18
|
+
throw new Error('Challenge information not returned from session information.');
|
|
19
|
+
}
|
|
20
|
+
const { OvalIouThreshold, FaceIouHeightThreshold, FaceIouWidthThreshold } = challengeConfig;
|
|
21
|
+
const faceBoundingBox = generateBboxFromLandmarks(face, ovalDetails, frameHeight);
|
|
22
|
+
const minFaceX = faceBoundingBox.left;
|
|
23
|
+
const maxFaceX = faceBoundingBox.right;
|
|
24
|
+
const minFaceY = faceBoundingBox.top;
|
|
25
|
+
const maxFaceY = faceBoundingBox.bottom;
|
|
26
|
+
const { ovalBoundingBox, minOvalX, minOvalY, maxOvalX, maxOvalY } = getOvalBoundingBox(ovalDetails);
|
|
27
|
+
const intersection = getIntersectionOverUnion(faceBoundingBox, ovalBoundingBox);
|
|
28
|
+
const intersectionThreshold = OvalIouThreshold;
|
|
29
|
+
const faceDetectionWidthThreshold = ovalDetails.width * FaceIouWidthThreshold;
|
|
30
|
+
const faceDetectionHeightThreshold = ovalDetails.height * FaceIouHeightThreshold;
|
|
31
|
+
/** From Science
|
|
32
|
+
* p=max(min(1,0.75∗(si−s0)/(st−s0)+0.25)),0)
|
|
33
|
+
*/
|
|
34
|
+
const faceMatchPercentage = Math.max(Math.min(FACE_MATCH_RANGE_MAX, (FACE_MATCH_WEIGHT_MAX * (intersection - initialFaceIntersection)) /
|
|
35
|
+
(intersectionThreshold - initialFaceIntersection) +
|
|
36
|
+
FACE_MATCH_WEIGHT_MIN), FACE_MATCH_RANGE_MIN) * 100;
|
|
37
|
+
const isFaceOutsideOvalToTheLeft = minOvalX > minFaceX && maxOvalX > maxFaceX;
|
|
38
|
+
const isFaceOutsideOvalToTheRight = minFaceX > minOvalX && maxFaceX > maxOvalX;
|
|
39
|
+
const isFaceMatched = intersection > intersectionThreshold;
|
|
40
|
+
const isFaceMatchedClosely = minOvalY - minFaceY > faceDetectionHeightThreshold ||
|
|
41
|
+
maxFaceY - maxOvalY > faceDetectionHeightThreshold ||
|
|
42
|
+
(minOvalX - minFaceX > faceDetectionWidthThreshold &&
|
|
43
|
+
maxFaceX - maxOvalX > faceDetectionWidthThreshold);
|
|
44
|
+
if (isFaceMatched) {
|
|
45
|
+
faceMatchState = FaceMatchState.MATCHED;
|
|
46
|
+
}
|
|
47
|
+
else if (isFaceOutsideOvalToTheLeft || isFaceOutsideOvalToTheRight) {
|
|
48
|
+
faceMatchState = FaceMatchState.OFF_CENTER;
|
|
49
|
+
}
|
|
50
|
+
else if (isFaceMatchedClosely) {
|
|
51
|
+
faceMatchState = FaceMatchState.MATCHED;
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
faceMatchState = FaceMatchState.TOO_FAR;
|
|
55
|
+
}
|
|
56
|
+
return { faceMatchState, faceMatchPercentage };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export { getFaceMatchStateInLivenessOval };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { IlluminationState, FaceMatchState } from '../types/liveness.mjs';
|
|
2
2
|
import { LivenessErrorState } from '../types/error.mjs';
|
|
3
|
-
import { FACE_DISTANCE_THRESHOLD, REDUCED_THRESHOLD_MOBILE, REDUCED_THRESHOLD } from './constants.mjs';
|
|
3
|
+
import { PUPIL_DISTANCE_WEIGHT, FACE_HEIGHT_WEIGHT, FACE_DISTANCE_THRESHOLD, REDUCED_THRESHOLD_MOBILE, REDUCED_THRESHOLD } from './constants.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Returns the random number between min and max
|
|
@@ -180,87 +180,33 @@ function getPupilDistanceAndFaceHeight(face) {
|
|
|
180
180
|
const faceHeight = Math.sqrt((eyeCenter[0] - mouth[0]) ** 2 + (eyeCenter[1] - mouth[1]) ** 2);
|
|
181
181
|
return { pupilDistance, faceHeight };
|
|
182
182
|
}
|
|
183
|
-
function generateBboxFromLandmarks(face, oval) {
|
|
184
|
-
const { leftEye, rightEye, nose, leftEar, rightEar
|
|
183
|
+
function generateBboxFromLandmarks(face, oval, frameHeight) {
|
|
184
|
+
const { leftEye, rightEye, nose, leftEar, rightEar } = face;
|
|
185
185
|
const { height: ovalHeight, centerY } = oval;
|
|
186
186
|
const ovalTop = centerY - ovalHeight / 2;
|
|
187
187
|
const eyeCenter = [];
|
|
188
188
|
eyeCenter[0] = (leftEye[0] + rightEye[0]) / 2;
|
|
189
189
|
eyeCenter[1] = (leftEye[1] + rightEye[1]) / 2;
|
|
190
190
|
const { pupilDistance: pd, faceHeight: fh } = getPupilDistanceAndFaceHeight(face);
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
const oh = 1.618 * ow;
|
|
194
|
-
let cx;
|
|
191
|
+
const ocularWidth = (PUPIL_DISTANCE_WEIGHT * pd + FACE_HEIGHT_WEIGHT * fh) / 2;
|
|
192
|
+
let centerFaceX, centerFaceY;
|
|
195
193
|
if (eyeCenter[1] <= (ovalTop + ovalHeight) / 2) {
|
|
196
|
-
|
|
194
|
+
centerFaceX = (eyeCenter[0] + nose[0]) / 2;
|
|
195
|
+
centerFaceY = (eyeCenter[1] + nose[1]) / 2;
|
|
197
196
|
}
|
|
198
197
|
else {
|
|
199
|
-
|
|
198
|
+
// when face tilts down
|
|
199
|
+
centerFaceX = eyeCenter[0];
|
|
200
|
+
centerFaceY = eyeCenter[1];
|
|
200
201
|
}
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
const
|
|
202
|
+
const faceWidth = ocularWidth;
|
|
203
|
+
const faceHeight = 1.68 * faceWidth;
|
|
204
|
+
const top = Math.max(centerFaceY - faceHeight / 2, 0);
|
|
205
|
+
const bottom = Math.min(centerFaceY + faceHeight / 2, frameHeight);
|
|
206
|
+
const left = Math.min(centerFaceX - ocularWidth / 2, rightEar[0]);
|
|
207
|
+
const right = Math.max(centerFaceX + ocularWidth / 2, leftEar[0]);
|
|
205
208
|
return { bottom, left, right, top };
|
|
206
209
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Returns the state of the provided face with respect to the provided liveness oval.
|
|
209
|
-
*/
|
|
210
|
-
// eslint-disable-next-line max-params
|
|
211
|
-
function getFaceMatchStateInLivenessOval(face, ovalDetails, initialFaceIntersection, sessionInformation) {
|
|
212
|
-
let faceMatchState;
|
|
213
|
-
const challengeConfig = sessionInformation?.Challenge?.FaceMovementAndLightChallenge
|
|
214
|
-
?.ChallengeConfig;
|
|
215
|
-
if (!challengeConfig ||
|
|
216
|
-
!challengeConfig.OvalIouThreshold ||
|
|
217
|
-
!challengeConfig.OvalIouHeightThreshold ||
|
|
218
|
-
!challengeConfig.OvalIouWidthThreshold ||
|
|
219
|
-
!challengeConfig.FaceIouHeightThreshold ||
|
|
220
|
-
!challengeConfig.FaceIouWidthThreshold) {
|
|
221
|
-
throw new Error('Challenge information not returned from session information.');
|
|
222
|
-
}
|
|
223
|
-
const { OvalIouThreshold, OvalIouHeightThreshold, OvalIouWidthThreshold, FaceIouHeightThreshold, FaceIouWidthThreshold, } = challengeConfig;
|
|
224
|
-
const faceBoundingBox = generateBboxFromLandmarks(face, ovalDetails);
|
|
225
|
-
const minFaceX = faceBoundingBox.left;
|
|
226
|
-
const maxFaceX = faceBoundingBox.right;
|
|
227
|
-
const minFaceY = faceBoundingBox.top;
|
|
228
|
-
const maxFaceY = faceBoundingBox.bottom;
|
|
229
|
-
const { ovalBoundingBox, minOvalX, minOvalY, maxOvalX, maxOvalY } = getOvalBoundingBox(ovalDetails);
|
|
230
|
-
const intersection = getIntersectionOverUnion(faceBoundingBox, ovalBoundingBox);
|
|
231
|
-
const intersectionThreshold = OvalIouThreshold;
|
|
232
|
-
const ovalMatchWidthThreshold = ovalDetails.width * OvalIouWidthThreshold;
|
|
233
|
-
const ovalMatchHeightThreshold = ovalDetails.height * OvalIouHeightThreshold;
|
|
234
|
-
const faceDetectionWidthThreshold = ovalDetails.width * FaceIouWidthThreshold;
|
|
235
|
-
const faceDetectionHeightThreshold = ovalDetails.height * FaceIouHeightThreshold;
|
|
236
|
-
/** From Science
|
|
237
|
-
* p=max(min(1,0.75∗(si−s0)/(st−s0)+0.25)),0)
|
|
238
|
-
*/
|
|
239
|
-
const faceMatchPercentage = Math.max(Math.min(1, (0.75 * (intersection - initialFaceIntersection)) /
|
|
240
|
-
(intersectionThreshold - initialFaceIntersection) +
|
|
241
|
-
0.25), 0) * 100;
|
|
242
|
-
const faceIsOutsideOvalToTheLeft = minOvalX > minFaceX && maxOvalX > maxFaceX;
|
|
243
|
-
const faceIsOutsideOvalToTheRight = minFaceX > minOvalX && maxFaceX > maxOvalX;
|
|
244
|
-
if (intersection > intersectionThreshold &&
|
|
245
|
-
Math.abs(minOvalX - minFaceX) < ovalMatchWidthThreshold &&
|
|
246
|
-
Math.abs(maxOvalX - maxFaceX) < ovalMatchWidthThreshold &&
|
|
247
|
-
Math.abs(maxOvalY - maxFaceY) < ovalMatchHeightThreshold) {
|
|
248
|
-
faceMatchState = FaceMatchState.MATCHED;
|
|
249
|
-
}
|
|
250
|
-
else if (faceIsOutsideOvalToTheLeft || faceIsOutsideOvalToTheRight) {
|
|
251
|
-
faceMatchState = FaceMatchState.OFF_CENTER;
|
|
252
|
-
}
|
|
253
|
-
else if (minOvalY - minFaceY > faceDetectionHeightThreshold ||
|
|
254
|
-
maxFaceY - maxOvalY > faceDetectionHeightThreshold ||
|
|
255
|
-
(minOvalX - minFaceX > faceDetectionWidthThreshold &&
|
|
256
|
-
maxFaceX - maxOvalX > faceDetectionWidthThreshold)) {
|
|
257
|
-
faceMatchState = FaceMatchState.TOO_CLOSE;
|
|
258
|
-
}
|
|
259
|
-
else {
|
|
260
|
-
faceMatchState = FaceMatchState.TOO_FAR;
|
|
261
|
-
}
|
|
262
|
-
return { faceMatchState, faceMatchPercentage };
|
|
263
|
-
}
|
|
264
210
|
/**
|
|
265
211
|
* Returns the illumination state in the provided video frame.
|
|
266
212
|
*/
|
|
@@ -436,8 +382,10 @@ async function isFaceDistanceBelowThreshold({ faceDetector, videoEl, ovalDetails
|
|
|
436
382
|
detectedFace = detectedFaces[0];
|
|
437
383
|
const { width } = ovalDetails;
|
|
438
384
|
const { pupilDistance, faceHeight } = getPupilDistanceAndFaceHeight(detectedFace);
|
|
439
|
-
const
|
|
440
|
-
|
|
385
|
+
const calibratedPupilDistance = (PUPIL_DISTANCE_WEIGHT * pupilDistance +
|
|
386
|
+
FACE_HEIGHT_WEIGHT * faceHeight) /
|
|
387
|
+
2 /
|
|
388
|
+
PUPIL_DISTANCE_WEIGHT;
|
|
441
389
|
if (width) {
|
|
442
390
|
isDistanceBelowThreshold =
|
|
443
391
|
calibratedPupilDistance / width <
|
|
@@ -469,4 +417,4 @@ function getBoundingBox({ deviceHeight, deviceWidth, height, width, top, left, }
|
|
|
469
417
|
};
|
|
470
418
|
}
|
|
471
419
|
|
|
472
|
-
export { clearOvalCanvas, drawLivenessOvalInCanvas, drawStaticOval, estimateIllumination, fillOverlayCanvasFractional, generateBboxFromLandmarks, getBoundingBox, getColorsSequencesFromSessionInformation, getFaceMatchState,
|
|
420
|
+
export { clearOvalCanvas, drawLivenessOvalInCanvas, drawStaticOval, estimateIllumination, fillOverlayCanvasFractional, generateBboxFromLandmarks, getBoundingBox, getColorsSequencesFromSessionInformation, getFaceMatchState, getIntersectionOverUnion, getOvalBoundingBox, getOvalDetailsFromSessionInformation, getRGBArrayFromColorString, getStaticLivenessOvalDetails, isCameraDeviceVirtual, isClientFreshnessColorSequence, isFaceDistanceBelowThreshold };
|
|
@@ -2,7 +2,7 @@ import React__default from 'react';
|
|
|
2
2
|
import { ComponentClassName } from '@aws-amplify/ui';
|
|
3
3
|
import { View, Flex } from '@aws-amplify/ui-react';
|
|
4
4
|
import { CancelButton } from './CancelButton.mjs';
|
|
5
|
-
import '../service/machine/
|
|
5
|
+
import '../service/machine/machine.mjs';
|
|
6
6
|
import '../service/types/liveness.mjs';
|
|
7
7
|
import '@tensorflow/tfjs-core';
|
|
8
8
|
import '@tensorflow-models/face-detection';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React__default from 'react';
|
|
2
2
|
import { Flex, Text, Button } from '@aws-amplify/ui-react';
|
|
3
3
|
import { AlertIcon } from '@aws-amplify/ui-react/internal';
|
|
4
|
-
import '../service/machine/
|
|
4
|
+
import '../service/machine/machine.mjs';
|
|
5
5
|
import '../service/types/liveness.mjs';
|
|
6
6
|
import { LivenessErrorState } from '../service/types/error.mjs';
|
|
7
7
|
import '@tensorflow/tfjs-core';
|
|
@@ -19,10 +19,14 @@ import { LivenessClassNames } from '../types/classNames.mjs';
|
|
|
19
19
|
|
|
20
20
|
const renderToastErrorModal = (props) => {
|
|
21
21
|
const { error: errorState, displayText } = props;
|
|
22
|
-
const { errorLabelText, timeoutHeaderText, timeoutMessageText, faceDistanceHeaderText, faceDistanceMessageText, multipleFacesHeaderText, multipleFacesMessageText, clientHeaderText, clientMessageText, serverHeaderText, serverMessageText, } = displayText;
|
|
22
|
+
const { connectionTimeoutHeaderText, connectionTimeoutMessageText, errorLabelText, timeoutHeaderText, timeoutMessageText, faceDistanceHeaderText, faceDistanceMessageText, multipleFacesHeaderText, multipleFacesMessageText, clientHeaderText, clientMessageText, serverHeaderText, serverMessageText, } = displayText;
|
|
23
23
|
let heading;
|
|
24
24
|
let message;
|
|
25
25
|
switch (errorState) {
|
|
26
|
+
case LivenessErrorState.CONNECTION_TIMEOUT:
|
|
27
|
+
heading = connectionTimeoutHeaderText;
|
|
28
|
+
message = connectionTimeoutMessageText;
|
|
29
|
+
break;
|
|
26
30
|
case LivenessErrorState.TIMEOUT:
|
|
27
31
|
heading = timeoutHeaderText;
|
|
28
32
|
message = timeoutMessageText;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { VisuallyHidden, View } from '@aws-amplify/ui-react';
|
|
3
|
-
import '../service/machine/
|
|
3
|
+
import '../service/machine/machine.mjs';
|
|
4
4
|
import { FaceMatchState, IlluminationState } from '../service/types/liveness.mjs';
|
|
5
5
|
import '@tensorflow/tfjs-core';
|
|
6
6
|
import '@tensorflow-models/face-detection';
|
|
@@ -52,7 +52,6 @@ const Hint = ({ hintDisplayText }) => {
|
|
|
52
52
|
[FaceMatchState.CANT_IDENTIFY]: hintDisplayText.hintCanNotIdentifyText,
|
|
53
53
|
[FaceMatchState.FACE_IDENTIFIED]: hintDisplayText.hintTooFarText,
|
|
54
54
|
[FaceMatchState.TOO_MANY]: hintDisplayText.hintTooManyFacesText,
|
|
55
|
-
[FaceMatchState.TOO_CLOSE]: hintDisplayText.hintTooCloseText,
|
|
56
55
|
[FaceMatchState.TOO_FAR]: hintDisplayText.hintTooFarText,
|
|
57
56
|
[FaceMatchState.MATCHED]: hintDisplayText.hintHoldFaceForFreshnessText,
|
|
58
57
|
[FaceMatchState.OFF_CENTER]: hintDisplayText.hintFaceOffCenterText,
|
|
@@ -98,13 +97,11 @@ const Hint = ({ hintDisplayText }) => {
|
|
|
98
97
|
return React.createElement(DefaultToast, { text: hintDisplayText.hintHoldFaceForFreshnessText });
|
|
99
98
|
}
|
|
100
99
|
if (isRecording && !isFlashingFreshness) {
|
|
101
|
-
// During face matching, we want to only show the
|
|
102
|
-
// TOO_FAR texts.
|
|
103
|
-
// the TOO_CLOSE text, but for FACE_IDENTIFED, CANT_IDENTIFY, TOO_MANY
|
|
100
|
+
// During face matching, we want to only show the
|
|
101
|
+
// TOO_FAR texts. For FACE_IDENTIFIED, CANT_IDENTIFY, TOO_MANY
|
|
104
102
|
// we are defaulting to the TOO_FAR text (for now).
|
|
105
103
|
let resultHintString = FaceMatchStateStringMap[FaceMatchState.TOO_FAR];
|
|
106
|
-
if (faceMatchState === FaceMatchState.
|
|
107
|
-
faceMatchState === FaceMatchState.MATCHED) {
|
|
104
|
+
if (faceMatchState === FaceMatchState.MATCHED) {
|
|
108
105
|
resultHintString = FaceMatchStateStringMap[faceMatchState];
|
|
109
106
|
}
|
|
110
107
|
// If the face is outside the oval set the aria-label to a string about centering face in oval
|
|
@@ -118,7 +115,7 @@ const Hint = ({ hintDisplayText }) => {
|
|
|
118
115
|
faceMatchPercentage > 50) {
|
|
119
116
|
a11yHintString = hintDisplayText.hintMatchIndicatorText;
|
|
120
117
|
}
|
|
121
|
-
return (React.createElement(Toast, { size: "large", variation:
|
|
118
|
+
return (React.createElement(Toast, { size: "large", variation: 'primary' },
|
|
122
119
|
React.createElement(VisuallyHidden, { "aria-live": "assertive" }, a11yHintString),
|
|
123
120
|
React.createElement(View, { "aria-label": a11yHintString }, resultHintString)));
|
|
124
121
|
}
|
|
@@ -12,7 +12,7 @@ function getDisplayText(overrideDisplayText) {
|
|
|
12
12
|
...defaultLivenessDisplayText,
|
|
13
13
|
...overrideDisplayText,
|
|
14
14
|
};
|
|
15
|
-
const { a11yVideoLabelText, cameraMinSpecificationsHeadingText, cameraMinSpecificationsMessageText, cameraNotFoundHeadingText, cameraNotFoundMessageText, cancelLivenessCheckText, clientHeaderText, clientMessageText, errorLabelText, hintCanNotIdentifyText, hintCenterFaceText, hintCenterFaceInstructionText, hintFaceOffCenterText, hintConnectingText, hintFaceDetectedText, hintHoldFaceForFreshnessText, hintIlluminationNormalText, hintIlluminationTooBrightText, hintIlluminationTooDarkText, hintMoveFaceFrontOfCameraText, hintTooManyFacesText, hintTooCloseText, hintTooFarText, hintVerifyingText, hintCheckCompleteText, hintMatchIndicatorText, faceDistanceHeaderText, faceDistanceMessageText, goodFitCaptionText, goodFitAltText, landscapeHeaderText, landscapeMessageText, multipleFacesHeaderText, multipleFacesMessageText, photosensitivityWarningBodyText, photosensitivityWarningHeadingText, photosensitivityWarningInfoText, photosensitivityWarningLabelText, photosensitivyWarningBodyText, photosensitivyWarningHeadingText, photosensitivyWarningInfoText, photosensitivyWarningLabelText, portraitMessageText, retryCameraPermissionsText, recordingIndicatorText, serverHeaderText, serverMessageText, startScreenBeginCheckText, timeoutHeaderText, timeoutMessageText, tooFarCaptionText, tooFarAltText, tryAgainText, waitingCameraPermissionText, } = displayText;
|
|
15
|
+
const { a11yVideoLabelText, cameraMinSpecificationsHeadingText, cameraMinSpecificationsMessageText, cameraNotFoundHeadingText, cameraNotFoundMessageText, cancelLivenessCheckText, connectionTimeoutHeaderText, connectionTimeoutMessageText, clientHeaderText, clientMessageText, errorLabelText, hintCanNotIdentifyText, hintCenterFaceText, hintCenterFaceInstructionText, hintFaceOffCenterText, hintConnectingText, hintFaceDetectedText, hintHoldFaceForFreshnessText, hintIlluminationNormalText, hintIlluminationTooBrightText, hintIlluminationTooDarkText, hintMoveFaceFrontOfCameraText, hintTooManyFacesText, hintTooCloseText, hintTooFarText, hintVerifyingText, hintCheckCompleteText, hintMatchIndicatorText, faceDistanceHeaderText, faceDistanceMessageText, goodFitCaptionText, goodFitAltText, landscapeHeaderText, landscapeMessageText, multipleFacesHeaderText, multipleFacesMessageText, photosensitivityWarningBodyText, photosensitivityWarningHeadingText, photosensitivityWarningInfoText, photosensitivityWarningLabelText, photosensitivyWarningBodyText, photosensitivyWarningHeadingText, photosensitivyWarningInfoText, photosensitivyWarningLabelText, portraitMessageText, retryCameraPermissionsText, recordingIndicatorText, serverHeaderText, serverMessageText, startScreenBeginCheckText, timeoutHeaderText, timeoutMessageText, tooFarCaptionText, tooFarAltText, tryAgainText, waitingCameraPermissionText, } = displayText;
|
|
16
16
|
const hintDisplayText = {
|
|
17
17
|
hintMoveFaceFrontOfCameraText,
|
|
18
18
|
hintTooManyFacesText,
|
|
@@ -61,6 +61,8 @@ function getDisplayText(overrideDisplayText) {
|
|
|
61
61
|
recordingIndicatorText,
|
|
62
62
|
};
|
|
63
63
|
const errorDisplayText = {
|
|
64
|
+
connectionTimeoutHeaderText,
|
|
65
|
+
connectionTimeoutMessageText,
|
|
64
66
|
errorLabelText,
|
|
65
67
|
timeoutHeaderText,
|
|
66
68
|
timeoutMessageText,
|
package/dist/esm/version.mjs
CHANGED