@aws-amplify/ui-react-liveness 3.3.8 → 3.4.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 +15 -15
- package/dist/esm/components/FaceLivenessDetector/FaceLivenessDetectorCore.mjs +4 -2
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/CameraSelector.mjs +13 -0
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/LivenessCameraModule.mjs +50 -28
- package/dist/esm/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.mjs +5 -4
- package/dist/esm/components/FaceLivenessDetector/service/machine/machine.mjs +247 -314
- package/dist/esm/components/FaceLivenessDetector/service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs +140 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/StreamRecorder/StreamRecorder.mjs +171 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/TelemetryReporter/TelemetryReporter.mjs +27 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/constants.mjs +30 -7
- package/dist/esm/components/FaceLivenessDetector/service/utils/createRequestStreamGenerator/createRequestStreamGenerator.mjs +32 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/createRequestStreamGenerator/utils.mjs +148 -0
- package/dist/esm/components/FaceLivenessDetector/service/utils/createStreamingClient/Signer.mjs +2 -3
- package/dist/esm/components/FaceLivenessDetector/service/utils/createStreamingClient/createStreamingClient.mjs +36 -6
- package/dist/esm/components/FaceLivenessDetector/service/utils/createStreamingClient/resolveCredentials.mjs +7 -6
- package/dist/esm/components/FaceLivenessDetector/service/utils/getFaceMatchStateInLivenessOval.mjs +9 -5
- package/dist/esm/components/FaceLivenessDetector/service/utils/liveness.mjs +19 -34
- package/dist/esm/components/FaceLivenessDetector/service/utils/{eventUtils.mjs → responseStreamEvent.mjs} +2 -2
- package/dist/esm/components/FaceLivenessDetector/service/utils/sessionInformation.mjs +45 -0
- package/dist/esm/components/FaceLivenessDetector/shared/DefaultStartScreenComponents.mjs +3 -2
- package/dist/esm/components/FaceLivenessDetector/shared/FaceLivenessErrorModal.mjs +4 -2
- package/dist/esm/components/FaceLivenessDetector/shared/Hint.mjs +4 -7
- package/dist/esm/components/FaceLivenessDetector/types/classNames.mjs +3 -0
- package/dist/esm/components/FaceLivenessDetector/utils/device.mjs +12 -12
- package/dist/esm/index.mjs +12 -0
- package/dist/esm/version.mjs +1 -1
- package/dist/index.js +956 -775
- package/dist/styles.css +17 -2
- package/dist/types/components/FaceLivenessDetector/FaceLivenessDetector.d.ts +3 -3
- package/dist/types/components/FaceLivenessDetector/FaceLivenessDetectorCore.d.ts +3 -3
- package/dist/types/components/FaceLivenessDetector/LivenessCheck/CameraSelector.d.ts +8 -0
- package/dist/types/components/FaceLivenessDetector/LivenessCheck/LivenessCameraModule.d.ts +3 -2
- package/dist/types/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.d.ts +2 -2
- package/dist/types/components/FaceLivenessDetector/displayText.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/hooks/useLivenessSelector.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/index.d.ts +6 -3
- package/dist/types/components/FaceLivenessDetector/providers/FaceLivenessDetectorProvider.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/machine/machine.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/types/credentials.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/types/liveness.d.ts +2 -2
- package/dist/types/components/FaceLivenessDetector/service/types/machine.d.ts +40 -27
- package/dist/types/components/FaceLivenessDetector/service/utils/ColorSequenceDisplay/ColorSequenceDisplay.d.ts +55 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/ColorSequenceDisplay/index.d.ts +2 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/StreamRecorder/StreamRecorder.d.ts +15 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/StreamRecorder/index.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/TelemetryReporter/TelemetryReporter.d.ts +8 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/TelemetryReporter/index.d.ts +2 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/blazefaceFaceDetection.d.ts +2 -1
- package/dist/types/components/FaceLivenessDetector/service/utils/constants.d.ts +27 -3
- package/dist/types/components/FaceLivenessDetector/service/utils/createRequestStreamGenerator/createRequestStreamGenerator.d.ts +15 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/createRequestStreamGenerator/index.d.ts +2 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/createRequestStreamGenerator/utils.d.ts +30 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/CustomWebSocketFetchHandler.d.ts +3 -2
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/Signer.d.ts +1 -2
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/createStreamingClient.d.ts +28 -6
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/index.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/resolveCredentials.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/utils/createStreamingClient/types.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/service/utils/getFaceMatchStateInLivenessOval.d.ts +4 -4
- package/dist/types/components/FaceLivenessDetector/service/utils/index.d.ts +7 -4
- package/dist/types/components/FaceLivenessDetector/service/utils/liveness.d.ts +16 -26
- package/dist/types/components/FaceLivenessDetector/service/utils/{eventUtils.d.ts → responseStreamEvent.d.ts} +2 -2
- package/dist/types/components/FaceLivenessDetector/service/utils/sessionInformation.d.ts +7 -0
- package/dist/types/components/FaceLivenessDetector/service/utils/types.d.ts +21 -0
- package/dist/types/components/FaceLivenessDetector/shared/DefaultStartScreenComponents.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/shared/FaceLivenessErrorModal.d.ts +2 -2
- package/dist/types/components/FaceLivenessDetector/shared/Hint.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/shared/Overlay.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/shared/Toast.d.ts +1 -1
- package/dist/types/components/FaceLivenessDetector/types/classNames.d.ts +3 -0
- package/dist/types/components/FaceLivenessDetector/utils/device.d.ts +1 -0
- package/dist/types/components/FaceLivenessDetector/utils/getDisplayText.d.ts +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +10 -10
- package/dist/esm/components/FaceLivenessDetector/service/utils/freshnessColorDisplay.mjs +0 -131
- package/dist/esm/components/FaceLivenessDetector/service/utils/streamProvider.mjs +0 -126
- package/dist/esm/components/FaceLivenessDetector/service/utils/videoRecorder.mjs +0 -108
- package/dist/types/components/FaceLivenessDetector/service/types/service.d.ts +0 -5
- package/dist/types/components/FaceLivenessDetector/service/utils/freshnessColorDisplay.d.ts +0 -21
- package/dist/types/components/FaceLivenessDetector/service/utils/streamProvider.d.ts +0 -42
- package/dist/types/components/FaceLivenessDetector/service/utils/videoRecorder.d.ts +0 -27
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { LivenessOvalDetails,
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { SessionInformation } from '@aws-sdk/client-rekognitionstreaming';
|
|
1
|
+
import type { LivenessOvalDetails, Face, BoundingBox, ErrorState, ParsedSessionInformation, FaceDetection } from '../types';
|
|
2
|
+
import { IlluminationState, FaceMatchState } from '../types';
|
|
3
|
+
import type { ColorSequence } from './ColorSequenceDisplay';
|
|
5
4
|
interface OvalBoundingBox {
|
|
6
5
|
ovalBoundingBox: BoundingBox;
|
|
7
6
|
minOvalX: number;
|
|
@@ -21,20 +20,21 @@ export declare function getIntersectionOverUnion(box1: BoundingBox, box2: Boundi
|
|
|
21
20
|
* Returns the details of a randomly generated liveness oval
|
|
22
21
|
* from SDK
|
|
23
22
|
*/
|
|
24
|
-
export declare function getOvalDetailsFromSessionInformation({
|
|
25
|
-
|
|
23
|
+
export declare function getOvalDetailsFromSessionInformation({ parsedSessionInformation, videoWidth, }: {
|
|
24
|
+
parsedSessionInformation: ParsedSessionInformation;
|
|
26
25
|
videoWidth: number;
|
|
27
26
|
}): LivenessOvalDetails;
|
|
28
27
|
/**
|
|
29
28
|
* Returns the details of a statically generated liveness oval based on the video dimensions
|
|
30
29
|
*/
|
|
31
|
-
export declare function getStaticLivenessOvalDetails({ width, height, widthSeed, centerXSeed, centerYSeed, ratioMultiplier, }: {
|
|
30
|
+
export declare function getStaticLivenessOvalDetails({ width, height, widthSeed, centerXSeed, centerYSeed, ratioMultiplier, ovalHeightWidthRatio, }: {
|
|
32
31
|
width: number;
|
|
33
32
|
height: number;
|
|
34
33
|
widthSeed?: number;
|
|
35
34
|
centerXSeed?: number;
|
|
36
35
|
centerYSeed?: number;
|
|
37
36
|
ratioMultiplier?: number;
|
|
37
|
+
ovalHeightWidthRatio?: number;
|
|
38
38
|
}): LivenessOvalDetails;
|
|
39
39
|
/**
|
|
40
40
|
* Draws the provided liveness oval on the canvas.
|
|
@@ -50,7 +50,12 @@ export declare function drawStaticOval(canvasEl: HTMLCanvasElement, videoEl: HTM
|
|
|
50
50
|
export declare function clearOvalCanvas({ canvas, }: {
|
|
51
51
|
canvas: HTMLCanvasElement;
|
|
52
52
|
}): void;
|
|
53
|
-
export declare function generateBboxFromLandmarks(
|
|
53
|
+
export declare function generateBboxFromLandmarks({ ovalHeightWidthRatio, face, oval, frameHeight, }: {
|
|
54
|
+
ovalHeightWidthRatio?: number;
|
|
55
|
+
face: Face;
|
|
56
|
+
oval: LivenessOvalDetails;
|
|
57
|
+
frameHeight: number;
|
|
58
|
+
}): BoundingBox;
|
|
54
59
|
/**
|
|
55
60
|
* Returns the illumination state in the provided video frame.
|
|
56
61
|
*/
|
|
@@ -82,31 +87,16 @@ interface FillOverlayCanvasFractionalInput {
|
|
|
82
87
|
scaleFactor: number;
|
|
83
88
|
}
|
|
84
89
|
export declare function fillOverlayCanvasFractional({ overlayCanvas, prevColor, nextColor, videoEl, ovalDetails, heightFraction, scaleFactor, }: FillOverlayCanvasFractionalInput): void;
|
|
85
|
-
export declare
|
|
86
|
-
export declare function getColorsSequencesFromSessionInformation(sessionInformation: SessionInformation): ClientFreshnessColorSequence[];
|
|
87
|
-
export declare function getRGBArrayFromColorString(colorStr: string): number[];
|
|
90
|
+
export declare function getColorsSequencesFromSessionInformation(parsedSessionInformation: ParsedSessionInformation): ColorSequence[];
|
|
88
91
|
export declare function getFaceMatchState(faceDetector: FaceDetection, videoEl: HTMLVideoElement): Promise<FaceMatchState>;
|
|
89
|
-
export declare function isFaceDistanceBelowThreshold({ faceDetector, videoEl, ovalDetails, reduceThreshold,
|
|
92
|
+
export declare function isFaceDistanceBelowThreshold({ parsedSessionInformation, faceDetector, videoEl, ovalDetails, reduceThreshold, }: {
|
|
93
|
+
parsedSessionInformation: ParsedSessionInformation;
|
|
90
94
|
faceDetector: FaceDetection;
|
|
91
95
|
videoEl: HTMLVideoElement;
|
|
92
96
|
ovalDetails: LivenessOvalDetails;
|
|
93
97
|
reduceThreshold?: boolean;
|
|
94
|
-
isMobile?: boolean;
|
|
95
98
|
}): Promise<{
|
|
96
99
|
isDistanceBelowThreshold: boolean;
|
|
97
100
|
error?: ErrorState;
|
|
98
101
|
}>;
|
|
99
|
-
export declare function getBoundingBox({ deviceHeight, deviceWidth, height, width, top, left, }: {
|
|
100
|
-
deviceHeight: number;
|
|
101
|
-
deviceWidth: number;
|
|
102
|
-
height: number;
|
|
103
|
-
width: number;
|
|
104
|
-
top: number;
|
|
105
|
-
left: number;
|
|
106
|
-
}): {
|
|
107
|
-
Height: number;
|
|
108
|
-
Width: number;
|
|
109
|
-
Top: number;
|
|
110
|
-
Left: number;
|
|
111
|
-
};
|
|
112
102
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { LivenessResponseStream } from '@aws-sdk/client-rekognitionstreaming';
|
|
2
|
-
export declare const
|
|
1
|
+
import type { LivenessResponseStream } from '@aws-sdk/client-rekognitionstreaming';
|
|
2
|
+
export declare const isServerSessionInformationEvent: (value: unknown) => value is LivenessResponseStream.ServerSessionInformationEventMember;
|
|
3
3
|
export declare const isConnectionTimeoutError: (error: unknown) => error is Error;
|
|
4
4
|
export declare const isDisconnectionEvent: (value: unknown) => value is LivenessResponseStream.DisconnectionEventMember;
|
|
5
5
|
export declare const isValidationExceptionEvent: (value: unknown) => value is LivenessResponseStream.ValidationExceptionMember;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { FaceMovementServerChallenge, FaceMovementAndLightServerChallenge, SessionInformation as ServerSessionInformation } from '@aws-sdk/client-rekognitionstreaming';
|
|
2
|
+
import type { FaceMovementAndLightChallenge, FaceMovementChallenge, ParsedSessionInformation } from '../types';
|
|
3
|
+
export declare const isFaceMovementAndLightChallenge: (value: unknown) => value is FaceMovementAndLightChallenge;
|
|
4
|
+
export declare const isFaceMovementChallenge: (value: unknown) => value is FaceMovementChallenge;
|
|
5
|
+
export declare const isFaceMovementAndLightServerChallenge: (value: unknown) => value is FaceMovementAndLightServerChallenge;
|
|
6
|
+
export declare const isFaceMovementServerChallenge: (value: unknown) => value is FaceMovementServerChallenge;
|
|
7
|
+
export declare const createSessionInfoFromServerSessionInformation: (serverSessionInformation: ServerSessionInformation) => ParsedSessionInformation;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ClientSessionInformationEvent, LivenessRequestStream } from '@aws-sdk/client-rekognitionstreaming';
|
|
2
|
+
export interface RequestStream extends AsyncGenerator<LivenessRequestStream> {
|
|
3
|
+
}
|
|
4
|
+
export interface VideoStream extends ReadableStream<StreamResult> {
|
|
5
|
+
}
|
|
6
|
+
export type StreamResultType = 'closeCode' | 'sessionInfo' | 'streamStop' | 'streamVideo';
|
|
7
|
+
export type StreamResult<T extends StreamResultType = StreamResultType> = T extends 'closeCode' ? {
|
|
8
|
+
type: T;
|
|
9
|
+
data: {
|
|
10
|
+
closeCode: number;
|
|
11
|
+
};
|
|
12
|
+
} : T extends 'streamVideo' ? {
|
|
13
|
+
type: T;
|
|
14
|
+
data: Blob;
|
|
15
|
+
} : T extends 'sessionInfo' ? {
|
|
16
|
+
type: T;
|
|
17
|
+
data: ClientSessionInformationEvent;
|
|
18
|
+
} : T extends 'streamStop' ? {
|
|
19
|
+
type: T;
|
|
20
|
+
data?: never;
|
|
21
|
+
} : never;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { CheckScreenComponents } from './FaceLivenessErrorModal';
|
|
2
|
+
import type { CheckScreenComponents } from './FaceLivenessErrorModal';
|
|
3
3
|
export type FaceLivenessDetectorComponents = StartScreenComponents & CheckScreenComponents;
|
|
4
4
|
export interface StartScreenComponents {
|
|
5
5
|
PhotosensitiveWarning?: React.ComponentType;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { ErrorState } from '../service';
|
|
3
|
-
import { ErrorDisplayText } from '../displayText';
|
|
2
|
+
import type { ErrorState } from '../service';
|
|
3
|
+
import type { ErrorDisplayText } from '../displayText';
|
|
4
4
|
export interface CheckScreenComponents {
|
|
5
5
|
ErrorView?: React.ComponentType<FaceLivenessErrorModalProps>;
|
|
6
6
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { IlluminationState, FaceMatchState } from '../service';
|
|
3
|
-
import { HintDisplayText } from '../displayText';
|
|
3
|
+
import type { HintDisplayText } from '../displayText';
|
|
4
4
|
export declare const selectErrorState: import("../hooks").LivenessSelectorFn<"CONNECTION_TIMEOUT" | "TIMEOUT" | "RUNTIME_ERROR" | "FRESHNESS_TIMEOUT" | "SERVER_ERROR" | "CAMERA_FRAMERATE_ERROR" | "CAMERA_ACCESS_ERROR" | "FACE_DISTANCE_ERROR" | "MOBILE_LANDSCAPE_ERROR" | "MULTIPLE_FACES_ERROR" | undefined>;
|
|
5
5
|
export declare const selectFaceMatchState: import("../hooks").LivenessSelectorFn<FaceMatchState | undefined>;
|
|
6
6
|
export declare const selectIlluminationState: import("../hooks").LivenessSelectorFn<IlluminationState | undefined>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { FlexProps } from '@aws-amplify/ui-react';
|
|
2
|
+
import type { FlexProps } from '@aws-amplify/ui-react';
|
|
3
3
|
interface OverlayProps extends FlexProps {
|
|
4
4
|
horizontal?: 'start' | 'center' | 'end';
|
|
5
5
|
vertical?: 'start' | 'center' | 'end' | 'space-between';
|
|
@@ -2,6 +2,8 @@ export declare enum LivenessClassNames {
|
|
|
2
2
|
CameraModule = "amplify-liveness-camera-module",
|
|
3
3
|
CancelContainer = "amplify-liveness-cancel-container",
|
|
4
4
|
CancelButton = "amplify-liveness-cancel-button",
|
|
5
|
+
CenteredLoader = "amplify-liveness-centered-loader",
|
|
6
|
+
ConnectingLoader = "amplify-liveness-connecting-loader",
|
|
5
7
|
CountdownContainer = "amplify-liveness-countdown-container",
|
|
6
8
|
DescriptionBullet = "amplify-liveness-description-bullet",
|
|
7
9
|
DescriptionBulletIndex = "amplify-liveness-description-bullet__index",
|
|
@@ -41,6 +43,7 @@ export declare enum LivenessClassNames {
|
|
|
41
43
|
Toast = "amplify-liveness-toast",
|
|
42
44
|
ToastContainer = "amplify-liveness-toast__container",
|
|
43
45
|
ToastMessage = "amplify-liveness-toast__message",
|
|
46
|
+
UserFacingVideo = "amplify-liveness-video--user-facing",
|
|
44
47
|
Video = "amplify-liveness-video",
|
|
45
48
|
VideoAnchor = "amplify-liveness-video-anchor"
|
|
46
49
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LivenessDisplayText, HintDisplayText, CameraDisplayText, InstructionDisplayText, StreamDisplayText, ErrorDisplayText } from '../displayText';
|
|
1
|
+
import type { LivenessDisplayText, HintDisplayText, CameraDisplayText, InstructionDisplayText, StreamDisplayText, ErrorDisplayText } from '../displayText';
|
|
2
2
|
interface LivenessDisplayTextInterface {
|
|
3
3
|
hintDisplayText: Required<HintDisplayText>;
|
|
4
4
|
cameraDisplayText: Required<CameraDisplayText>;
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export type { FaceLivenessDetectorProps, FaceLivenessDetectorCoreProps, AwsCredentialProvider, AwsCredentials, AwsTemporaryCredentials, ErrorState, } from './components';
|
|
2
|
+
export { FaceLivenessDetector, FaceLivenessDetectorCore } from './components';
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "3.
|
|
1
|
+
export declare const VERSION = "3.4.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aws-amplify/ui-react-liveness",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"module": "dist/esm/index.mjs",
|
|
6
6
|
"exports": {
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
"react-dom": "^16.14 || ^17 || ^18 || ^19"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@aws-amplify/ui": "6.10.
|
|
52
|
-
"@aws-amplify/ui-react": "6.11.
|
|
53
|
-
"@aws-sdk/client-rekognitionstreaming": "3.
|
|
51
|
+
"@aws-amplify/ui": "6.10.3",
|
|
52
|
+
"@aws-amplify/ui-react": "6.11.2",
|
|
53
|
+
"@aws-sdk/client-rekognitionstreaming": "3.828.0",
|
|
54
54
|
"@aws-sdk/util-format-url": "3.609.0",
|
|
55
|
-
"@smithy/eventstream-serde-browser": "^
|
|
56
|
-
"@smithy/fetch-http-handler": "^
|
|
55
|
+
"@smithy/eventstream-serde-browser": "^4.0.4",
|
|
56
|
+
"@smithy/fetch-http-handler": "^5.0.4",
|
|
57
57
|
"@smithy/protocol-http": "^3.0.3",
|
|
58
|
-
"@smithy/signature-v4": "
|
|
59
|
-
"@smithy/types": "^
|
|
58
|
+
"@smithy/signature-v4": "5.1.2",
|
|
59
|
+
"@smithy/types": "^4.3.1",
|
|
60
60
|
"@mediapipe/face_detection": "~0.4.0",
|
|
61
61
|
"@tensorflow-models/face-detection": "1.0.2",
|
|
62
62
|
"@tensorflow/tfjs-backend-cpu": "4.11.0",
|
|
@@ -64,8 +64,8 @@
|
|
|
64
64
|
"@tensorflow/tfjs-converter": "4.11.0",
|
|
65
65
|
"@tensorflow/tfjs-core": "4.11.0",
|
|
66
66
|
"@xstate/react": "^3.2.2",
|
|
67
|
-
"nanoid": "3.3.8",
|
|
68
67
|
"tslib": "^2.5.2",
|
|
68
|
+
"uuid": "^11.1.0",
|
|
69
69
|
"xstate": "^4.33.6"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
@@ -85,4 +85,4 @@
|
|
|
85
85
|
"limit": "225 kB"
|
|
86
86
|
}
|
|
87
87
|
]
|
|
88
|
-
}
|
|
88
|
+
}
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
import { fillOverlayCanvasFractional, getRGBArrayFromColorString } from './liveness.mjs';
|
|
2
|
-
|
|
3
|
-
const TICK_RATE = 10; // ms -- the rate at which we will render/check colors
|
|
4
|
-
var COLOR_STAGE;
|
|
5
|
-
(function (COLOR_STAGE) {
|
|
6
|
-
COLOR_STAGE["SCROLLING"] = "SCROLLING";
|
|
7
|
-
COLOR_STAGE["FLAT"] = "FLAT";
|
|
8
|
-
})(COLOR_STAGE || (COLOR_STAGE = {}));
|
|
9
|
-
class FreshnessColorDisplay {
|
|
10
|
-
constructor(context, freshnessColorsSequence) {
|
|
11
|
-
this.context = context;
|
|
12
|
-
this.freshnessColorsSequence = freshnessColorsSequence;
|
|
13
|
-
this.isFirstTick = true;
|
|
14
|
-
}
|
|
15
|
-
async displayColorTick() {
|
|
16
|
-
return new Promise((resolve, reject) => {
|
|
17
|
-
setTimeout(() => {
|
|
18
|
-
this.displayNextColorTick(resolve, reject);
|
|
19
|
-
}, Math.min(TICK_RATE));
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
init() {
|
|
23
|
-
this.stageIndex = 0;
|
|
24
|
-
this.currColorIndex = 0;
|
|
25
|
-
this.currColorSequence = this.freshnessColorsSequence[0];
|
|
26
|
-
this.prevColorSequence = this.freshnessColorsSequence[0];
|
|
27
|
-
this.stage = COLOR_STAGE.FLAT;
|
|
28
|
-
this.timeLastFlatOrScrollChange = Date.now();
|
|
29
|
-
this.timeLastFaceMatchChecked = Date.now();
|
|
30
|
-
}
|
|
31
|
-
displayNextColorTick(resolve, _) {
|
|
32
|
-
const { freshnessColorEl } = this.context.freshnessColorAssociatedParams;
|
|
33
|
-
const { ovalDetails, scaleFactor } = this.context.ovalAssociatedParams;
|
|
34
|
-
const { videoEl } = this.context.videoAssociatedParams;
|
|
35
|
-
const tickStartTime = Date.now();
|
|
36
|
-
// Send a colorStart time only for the first tick of the first color
|
|
37
|
-
if (this.isFirstTick) {
|
|
38
|
-
this.init();
|
|
39
|
-
this.isFirstTick = false;
|
|
40
|
-
this.sendColorStartTime({
|
|
41
|
-
tickStartTime: tickStartTime,
|
|
42
|
-
currColor: this.currColorSequence.color,
|
|
43
|
-
prevColor: this.currColorSequence.color,
|
|
44
|
-
currColorIndex: this.stageIndex,
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
let timeSinceLastColorChange = tickStartTime - this.timeLastFlatOrScrollChange;
|
|
48
|
-
freshnessColorEl.style.display = 'block';
|
|
49
|
-
// Every 10 ms tick we will check if the threshold for flat or scrolling, if so we will try to go to the next stage
|
|
50
|
-
if ((this.stage === COLOR_STAGE.FLAT &&
|
|
51
|
-
timeSinceLastColorChange >=
|
|
52
|
-
this.currColorSequence.flatDisplayDuration) ||
|
|
53
|
-
(this.stage === COLOR_STAGE.SCROLLING &&
|
|
54
|
-
timeSinceLastColorChange >= this.currColorSequence.downscrollDuration)) {
|
|
55
|
-
this.incrementStageIndex(tickStartTime);
|
|
56
|
-
timeSinceLastColorChange = 0;
|
|
57
|
-
}
|
|
58
|
-
// Every 10 ms tick we will update the colors displayed
|
|
59
|
-
if (this.currColorIndex < this.freshnessColorsSequence.length) {
|
|
60
|
-
const heightFraction = timeSinceLastColorChange /
|
|
61
|
-
(this.stage === COLOR_STAGE.SCROLLING
|
|
62
|
-
? this.currColorSequence.downscrollDuration
|
|
63
|
-
: this.currColorSequence.flatDisplayDuration);
|
|
64
|
-
fillOverlayCanvasFractional({
|
|
65
|
-
overlayCanvas: freshnessColorEl,
|
|
66
|
-
prevColor: this.prevColorSequence.color,
|
|
67
|
-
nextColor: this.currColorSequence.color,
|
|
68
|
-
videoEl: videoEl,
|
|
69
|
-
ovalDetails: ovalDetails,
|
|
70
|
-
heightFraction,
|
|
71
|
-
scaleFactor: scaleFactor,
|
|
72
|
-
});
|
|
73
|
-
resolve(false);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
freshnessColorEl.style.display = 'none';
|
|
77
|
-
resolve(true);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
// FLAT - prev = 0, curr = 0
|
|
81
|
-
// SCROLL - prev = 0, curr = 1
|
|
82
|
-
// FLAT - prev = 1, curr = 1
|
|
83
|
-
// SCROLL - prev = 1, curr = 2
|
|
84
|
-
// SCROLL - prev = 2, curr = 3
|
|
85
|
-
incrementStageIndex(tickStartTime) {
|
|
86
|
-
this.stageIndex += 1;
|
|
87
|
-
this.prevColorSequence = this.freshnessColorsSequence[this.currColorIndex];
|
|
88
|
-
if (this.stage === COLOR_STAGE.FLAT) {
|
|
89
|
-
this.currColorIndex += 1;
|
|
90
|
-
this.stage = COLOR_STAGE.SCROLLING;
|
|
91
|
-
}
|
|
92
|
-
else if (this.stage === COLOR_STAGE.SCROLLING) {
|
|
93
|
-
const nextFlatColor = this.freshnessColorsSequence[this.currColorIndex];
|
|
94
|
-
if (nextFlatColor.flatDisplayDuration > 0) {
|
|
95
|
-
this.stage = COLOR_STAGE.FLAT;
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
this.stage = COLOR_STAGE.SCROLLING;
|
|
99
|
-
this.currColorIndex += 1;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
this.currColorSequence = this.freshnessColorsSequence[this.currColorIndex];
|
|
103
|
-
this.timeLastFlatOrScrollChange = Date.now();
|
|
104
|
-
if (this.currColorSequence) {
|
|
105
|
-
this.sendColorStartTime({
|
|
106
|
-
tickStartTime: tickStartTime,
|
|
107
|
-
currColor: this.currColorSequence.color,
|
|
108
|
-
prevColor: this.prevColorSequence.color,
|
|
109
|
-
currColorIndex: this.stageIndex,
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
sendColorStartTime({ tickStartTime, currColor, prevColor, currColorIndex, }) {
|
|
114
|
-
const { livenessStreamProvider, challengeId } = this.context;
|
|
115
|
-
livenessStreamProvider.sendClientInfo({
|
|
116
|
-
Challenge: {
|
|
117
|
-
FaceMovementAndLightChallenge: {
|
|
118
|
-
ChallengeId: challengeId,
|
|
119
|
-
ColorDisplayed: {
|
|
120
|
-
CurrentColor: { RGB: getRGBArrayFromColorString(currColor) },
|
|
121
|
-
PreviousColor: { RGB: getRGBArrayFromColorString(prevColor) },
|
|
122
|
-
SequenceNumber: currColorIndex,
|
|
123
|
-
CurrentColorStartTimestamp: tickStartTime,
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
},
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export { FreshnessColorDisplay };
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import { StartFaceLivenessSessionCommand } from '@aws-sdk/client-rekognitionstreaming';
|
|
2
|
-
import { VideoRecorder } from './videoRecorder.mjs';
|
|
3
|
-
import { createStreamingClient } from './createStreamingClient/createStreamingClient.mjs';
|
|
4
|
-
|
|
5
|
-
const TIME_SLICE = 1000;
|
|
6
|
-
function isBlob(obj) {
|
|
7
|
-
return obj.arrayBuffer !== undefined;
|
|
8
|
-
}
|
|
9
|
-
function isClientSessionInformationEvent(obj) {
|
|
10
|
-
return obj.Challenge !== undefined;
|
|
11
|
-
}
|
|
12
|
-
function isEndStreamWithCodeEvent(obj) {
|
|
13
|
-
return obj.code !== undefined;
|
|
14
|
-
}
|
|
15
|
-
class LivenessStreamProvider {
|
|
16
|
-
constructor({ sessionId, region, stream, videoEl, credentialProvider, endpointOverride, systemClockOffset, }) {
|
|
17
|
-
this.sessionId = sessionId;
|
|
18
|
-
this.region = region;
|
|
19
|
-
this.stream = stream;
|
|
20
|
-
this.videoEl = videoEl;
|
|
21
|
-
this.videoRecorder = new VideoRecorder(stream);
|
|
22
|
-
this.credentialProvider = credentialProvider;
|
|
23
|
-
this.endpointOverride = endpointOverride;
|
|
24
|
-
this.systemClockOffset = systemClockOffset;
|
|
25
|
-
this.initPromise = this.init();
|
|
26
|
-
}
|
|
27
|
-
async getResponseStream() {
|
|
28
|
-
await this.initPromise;
|
|
29
|
-
return this.responseStream;
|
|
30
|
-
}
|
|
31
|
-
startRecordingLivenessVideo() {
|
|
32
|
-
this.videoRecorder.start(TIME_SLICE);
|
|
33
|
-
}
|
|
34
|
-
sendClientInfo(clientInfo) {
|
|
35
|
-
this.videoRecorder.dispatch(new MessageEvent('clientSesssionInfo', { data: { clientInfo } }));
|
|
36
|
-
}
|
|
37
|
-
async stopVideo() {
|
|
38
|
-
await this.videoRecorder.stop();
|
|
39
|
-
}
|
|
40
|
-
dispatchStopVideoEvent() {
|
|
41
|
-
this.videoRecorder.dispatch(new Event('stopVideo'));
|
|
42
|
-
}
|
|
43
|
-
async endStreamWithCode(code) {
|
|
44
|
-
if (this.videoRecorder.getState() === 'recording') {
|
|
45
|
-
await this.stopVideo();
|
|
46
|
-
}
|
|
47
|
-
this.videoRecorder.dispatch(new MessageEvent('endStreamWithCode', { data: { code } }));
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
async init() {
|
|
51
|
-
this._client = await createStreamingClient({
|
|
52
|
-
credentialsProvider: this.credentialProvider,
|
|
53
|
-
endpointOverride: this.endpointOverride,
|
|
54
|
-
region: this.region,
|
|
55
|
-
systemClockOffset: this.systemClockOffset,
|
|
56
|
-
});
|
|
57
|
-
this.responseStream = await this.startLivenessVideoConnection();
|
|
58
|
-
}
|
|
59
|
-
// Creates a generator from a stream of video chunks and livenessActionDocuments and yields VideoEvent and ClientEvents
|
|
60
|
-
getAsyncGeneratorFromReadableStream(stream) {
|
|
61
|
-
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
62
|
-
const current = this;
|
|
63
|
-
this._reader = stream.getReader();
|
|
64
|
-
return async function* () {
|
|
65
|
-
while (true) {
|
|
66
|
-
const { done, value } = (await current._reader.read());
|
|
67
|
-
if (done) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
// Video chunks blobs should be sent as video events
|
|
71
|
-
if (value === 'stopVideo') {
|
|
72
|
-
// sending an empty video chunk signals that we have ended sending video
|
|
73
|
-
yield {
|
|
74
|
-
VideoEvent: {
|
|
75
|
-
VideoChunk: new Uint8Array([]),
|
|
76
|
-
TimestampMillis: Date.now(),
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
else if (isBlob(value)) {
|
|
81
|
-
const buffer = await value.arrayBuffer();
|
|
82
|
-
const chunk = new Uint8Array(buffer);
|
|
83
|
-
if (chunk.length > 0) {
|
|
84
|
-
yield {
|
|
85
|
-
VideoEvent: {
|
|
86
|
-
VideoChunk: chunk,
|
|
87
|
-
TimestampMillis: Date.now(),
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
else if (isClientSessionInformationEvent(value)) {
|
|
93
|
-
yield {
|
|
94
|
-
ClientSessionInformationEvent: {
|
|
95
|
-
Challenge: value.Challenge,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
else if (isEndStreamWithCodeEvent(value)) {
|
|
100
|
-
yield {
|
|
101
|
-
VideoEvent: {
|
|
102
|
-
VideoChunk: new Uint8Array([]),
|
|
103
|
-
// this is a custom type that does not match LivenessRequestStream.
|
|
104
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
105
|
-
TimestampMillis: { closeCode: value.code },
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
async startLivenessVideoConnection() {
|
|
113
|
-
const livenessRequestGenerator = this.getAsyncGeneratorFromReadableStream(this.videoRecorder.videoStream)();
|
|
114
|
-
const mediaSettings = this.stream.getTracks()[0].getSettings();
|
|
115
|
-
const response = await this._client.send(new StartFaceLivenessSessionCommand({
|
|
116
|
-
ChallengeVersions: 'FaceMovementAndLightChallenge_1.0.0',
|
|
117
|
-
SessionId: this.sessionId,
|
|
118
|
-
LivenessRequestStream: livenessRequestGenerator,
|
|
119
|
-
VideoWidth: (mediaSettings.width ?? this.videoEl.width).toString(),
|
|
120
|
-
VideoHeight: (mediaSettings.height ?? this.videoEl.height).toString(),
|
|
121
|
-
}));
|
|
122
|
-
return response.LivenessResponseStream;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
export { LivenessStreamProvider };
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { isAndroidChromeWithBrokenH264 } from '../../utils/device.mjs';
|
|
2
|
-
|
|
3
|
-
// Equivalent to 2 Kbps - needed for maintaining video quality at 60 FPS
|
|
4
|
-
const BITS_PER_SECOND = 2000000;
|
|
5
|
-
// Only to be used with Chrome for the Android Chrome H264 Bug - https://issues.chromium.org/issues/343199623
|
|
6
|
-
const ALTERNATE_CHROME_MIME_TYPE = 'video/x-matroska;codecs=vp8';
|
|
7
|
-
/**
|
|
8
|
-
* Helper wrapper class over the native MediaRecorder.
|
|
9
|
-
*/
|
|
10
|
-
class VideoRecorder {
|
|
11
|
-
constructor(stream) {
|
|
12
|
-
if (typeof MediaRecorder === 'undefined') {
|
|
13
|
-
throw Error('MediaRecorder is not supported by this browser');
|
|
14
|
-
}
|
|
15
|
-
this._stream = stream;
|
|
16
|
-
this._chunks = [];
|
|
17
|
-
this._recorder = new MediaRecorder(stream, {
|
|
18
|
-
bitsPerSecond: BITS_PER_SECOND,
|
|
19
|
-
mimeType: isAndroidChromeWithBrokenH264()
|
|
20
|
-
? ALTERNATE_CHROME_MIME_TYPE
|
|
21
|
-
: undefined,
|
|
22
|
-
});
|
|
23
|
-
this._setupCallbacks();
|
|
24
|
-
}
|
|
25
|
-
getState() {
|
|
26
|
-
return this._recorder.state;
|
|
27
|
-
}
|
|
28
|
-
start(timeSlice) {
|
|
29
|
-
this.clearRecordedData();
|
|
30
|
-
this.recordingStartApiTimestamp = Date.now();
|
|
31
|
-
this._recorder.start(timeSlice);
|
|
32
|
-
}
|
|
33
|
-
async stop() {
|
|
34
|
-
if (this.getState() === 'recording') {
|
|
35
|
-
this._recorder.stop();
|
|
36
|
-
}
|
|
37
|
-
return this._recorderStopped;
|
|
38
|
-
}
|
|
39
|
-
pause() {
|
|
40
|
-
this._recorder.pause();
|
|
41
|
-
}
|
|
42
|
-
clearRecordedData() {
|
|
43
|
-
this._chunks = [];
|
|
44
|
-
}
|
|
45
|
-
dispatch(event) {
|
|
46
|
-
this._recorder.dispatchEvent(event);
|
|
47
|
-
}
|
|
48
|
-
getVideoChunkSize() {
|
|
49
|
-
return this._chunks.length;
|
|
50
|
-
}
|
|
51
|
-
_setupCallbacks() {
|
|
52
|
-
// Creates a Readablestream of video chunks. Waits to receive a clientSessionInfo event before pushing
|
|
53
|
-
// a livenessActionDocument to the ReadableStream and finally closing the ReadableStream
|
|
54
|
-
this.videoStream = new ReadableStream({
|
|
55
|
-
start: (controller) => {
|
|
56
|
-
if (!this._recorder) {
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
this._recorder.ondataavailable = (e) => {
|
|
60
|
-
if (e.data && e.data.size > 0) {
|
|
61
|
-
if (this._chunks.length === 0) {
|
|
62
|
-
this.firstChunkTimestamp = Date.now();
|
|
63
|
-
}
|
|
64
|
-
this._chunks.push(e.data);
|
|
65
|
-
controller.enqueue(e.data);
|
|
66
|
-
}
|
|
67
|
-
};
|
|
68
|
-
this._recorder.addEventListener('clientSesssionInfo', (e) => {
|
|
69
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
70
|
-
controller.enqueue(e.data.clientInfo);
|
|
71
|
-
});
|
|
72
|
-
this._recorder.addEventListener('stopVideo', () => {
|
|
73
|
-
controller.enqueue('stopVideo');
|
|
74
|
-
});
|
|
75
|
-
this._recorder.addEventListener('endStream', () => {
|
|
76
|
-
controller.close();
|
|
77
|
-
});
|
|
78
|
-
this._recorder.addEventListener('endStreamWithCode', (e) => {
|
|
79
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
|
|
80
|
-
controller.enqueue({
|
|
81
|
-
type: 'endStreamWithCode',
|
|
82
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
83
|
-
code: e.data.code,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
},
|
|
87
|
-
});
|
|
88
|
-
this.recorderStarted = new Promise((resolve) => {
|
|
89
|
-
this._recorder.onstart = () => {
|
|
90
|
-
this.recorderStartTimestamp = Date.now();
|
|
91
|
-
resolve();
|
|
92
|
-
};
|
|
93
|
-
});
|
|
94
|
-
this._recorderStopped = new Promise((resolve) => {
|
|
95
|
-
this._recorder.onstop = () => {
|
|
96
|
-
this.recorderEndTimestamp = Date.now();
|
|
97
|
-
resolve();
|
|
98
|
-
};
|
|
99
|
-
});
|
|
100
|
-
this._recorder.onerror = () => {
|
|
101
|
-
if (this.getState() !== 'stopped') {
|
|
102
|
-
this.stop();
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export { VideoRecorder };
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { LivenessContext } from '../types/machine';
|
|
2
|
-
import { ClientFreshnessColorSequence } from '../types/service';
|
|
3
|
-
export declare class FreshnessColorDisplay {
|
|
4
|
-
private freshnessColorsSequence;
|
|
5
|
-
private context;
|
|
6
|
-
private stageIndex;
|
|
7
|
-
private stage;
|
|
8
|
-
private currColorIndex;
|
|
9
|
-
private currColorSequence;
|
|
10
|
-
private prevColorSequence;
|
|
11
|
-
private timeLastFlatOrScrollChange;
|
|
12
|
-
private timeFaceMatched;
|
|
13
|
-
private timeLastFaceMatchChecked;
|
|
14
|
-
private isFirstTick;
|
|
15
|
-
constructor(context: LivenessContext, freshnessColorsSequence: ClientFreshnessColorSequence[]);
|
|
16
|
-
displayColorTick(): Promise<boolean>;
|
|
17
|
-
private init;
|
|
18
|
-
private displayNextColorTick;
|
|
19
|
-
private incrementStageIndex;
|
|
20
|
-
private sendColorStartTime;
|
|
21
|
-
}
|