@aws-amplify/ui-react-liveness 3.6.6 → 3.6.8

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.
@@ -1,13 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { useInterpret } from '@xstate/react';
3
3
  import { livenessMachine } from './service/machine/machine.mjs';
4
+ import './service/utils/constants.mjs';
4
5
  import './service/types/liveness.mjs';
5
6
  import '@tensorflow/tfjs-core';
6
7
  import '../../packages/react-liveness/node_modules/@tensorflow-models/face-detection/dist/face-detection.esm.mjs';
7
8
  import '@tensorflow/tfjs-backend-wasm';
8
9
  import '@tensorflow/tfjs-backend-cpu';
9
10
  import '@aws-amplify/core/internals/utils';
10
- import './service/utils/constants.mjs';
11
11
  import './service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
12
12
  import '@aws-amplify/ui';
13
13
  import './service/utils/createStreamingClient/createStreamingClient.mjs';
@@ -3,6 +3,7 @@ import { classNames } from '@aws-amplify/ui';
3
3
  import { View, Flex, Loader, Text, Button } from '@aws-amplify/ui-react';
4
4
  import { useColorMode } from '@aws-amplify/ui-react/internal';
5
5
  import '../service/machine/machine.mjs';
6
+ import { FACE_MOVEMENT_CHALLENGE } from '../service/utils/constants.mjs';
6
7
  import { FaceMatchState } from '../service/types/liveness.mjs';
7
8
  import '@tensorflow/tfjs-core';
8
9
  import '../../../packages/react-liveness/node_modules/@tensorflow-models/face-detection/dist/face-detection.esm.mjs';
@@ -10,7 +11,6 @@ import '@tensorflow/tfjs-backend-wasm';
10
11
  import '@tensorflow/tfjs-backend-cpu';
11
12
  import '@aws-amplify/core/internals/utils';
12
13
  import { drawStaticOval, clearOvalCanvas } from '../service/utils/liveness.mjs';
13
- import { FACE_MOVEMENT_CHALLENGE } from '../service/utils/constants.mjs';
14
14
  import '../service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
15
15
  import '../service/utils/createStreamingClient/createStreamingClient.mjs';
16
16
  import '../service/utils/StreamRecorder/StreamRecorder.mjs';
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Flex, Text, Button, View } from '@aws-amplify/ui-react';
3
3
  import '../service/machine/machine.mjs';
4
+ import '../service/utils/constants.mjs';
4
5
  import '../service/types/liveness.mjs';
5
6
  import { LivenessErrorState } from '../service/types/error.mjs';
6
7
  import '@tensorflow/tfjs-core';
@@ -8,7 +9,6 @@ import '../../../packages/react-liveness/node_modules/@tensorflow-models/face-de
8
9
  import '@tensorflow/tfjs-backend-wasm';
9
10
  import '@tensorflow/tfjs-backend-cpu';
10
11
  import '@aws-amplify/core/internals/utils';
11
- import '../service/utils/constants.mjs';
12
12
  import '../service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
13
13
  import '@aws-amplify/ui';
14
14
  import '../service/utils/createStreamingClient/createStreamingClient.mjs';
@@ -1,12 +1,12 @@
1
1
  import { v4 } from 'uuid';
2
2
  import { createMachine, assign, actions, spawn } from 'xstate';
3
3
  import { fillOverlayCanvasFractional, generateBboxFromLandmarks, getOvalBoundingBox, getIntersectionOverUnion, estimateIllumination, getOvalDetailsFromSessionInformation, drawLivenessOvalInCanvas, getStaticLivenessOvalDetails, isFaceDistanceBelowThreshold, getFaceMatchState, isCameraDeviceVirtual, getColorsSequencesFromSessionInformation } from '../utils/liveness.mjs';
4
+ import { FACE_MOVEMENT_AND_LIGHT_CHALLENGE, FACE_MOVEMENT_CHALLENGE, WS_CLOSURE_CODE } from '../utils/constants.mjs';
4
5
  import { FaceMatchState } from '../types/liveness.mjs';
5
6
  import { LivenessErrorState } from '../types/error.mjs';
6
7
  import { BlazeFaceFaceDetection } from '../utils/blazefaceFaceDetection.mjs';
7
8
  import { getFaceMatchStateInLivenessOval } from '../utils/getFaceMatchStateInLivenessOval.mjs';
8
9
  import { ColorSequenceDisplay } from '../utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
9
- import { FACE_MOVEMENT_AND_LIGHT_CHALLENGE, FACE_MOVEMENT_CHALLENGE, WS_CLOSURE_CODE } from '../utils/constants.mjs';
10
10
  import { createRequestStreamGenerator } from '../utils/createRequestStreamGenerator/createRequestStreamGenerator.mjs';
11
11
  import { createSessionEndEvent, getTrackDimensions, createColorDisplayEvent, createSessionStartEvent } from '../utils/createRequestStreamGenerator/utils.mjs';
12
12
  import { createStreamingClient } from '../utils/createStreamingClient/createStreamingClient.mjs';
@@ -203,6 +203,10 @@ const livenessMachine = createMachine({
203
203
  'spawnResponseStreamActor',
204
204
  ],
205
205
  },
206
+ onError: {
207
+ target: '#livenessMachine.error',
208
+ actions: 'updateErrorStateForRuntime',
209
+ },
206
210
  },
207
211
  },
208
212
  waitForSessionInfo: {
@@ -231,6 +235,10 @@ const livenessMachine = createMachine({
231
235
  target: 'checkFaceDetectedBeforeStart',
232
236
  actions: 'updateFaceMatchBeforeStartDetails',
233
237
  },
238
+ onError: {
239
+ target: 'error',
240
+ actions: 'updateErrorStateForRuntime',
241
+ },
234
242
  },
235
243
  },
236
244
  checkFaceDetectedBeforeStart: {
@@ -249,6 +257,10 @@ const livenessMachine = createMachine({
249
257
  target: 'checkFaceDistanceBeforeRecording',
250
258
  actions: 'updateFaceDistanceBeforeRecording',
251
259
  },
260
+ onError: {
261
+ target: 'error',
262
+ actions: 'updateErrorStateForRuntime',
263
+ },
252
264
  },
253
265
  },
254
266
  checkFaceDistanceBeforeRecording: {
@@ -926,13 +938,7 @@ const livenessMachine = createMachine({
926
938
  const { videoEl } = context.videoAssociatedParams;
927
939
  const { faceDetector } = context.ovalAssociatedParams;
928
940
  // initialize models
929
- try {
930
- await faceDetector.modelLoadingPromise;
931
- }
932
- catch (err) {
933
- // eslint-disable-next-line no-console
934
- console.log({ err });
935
- }
941
+ await faceDetector.modelLoadingPromise;
936
942
  // detect face
937
943
  const faceMatchState = await getFaceMatchState(faceDetector, videoEl);
938
944
  return { faceMatchState };
@@ -964,13 +970,7 @@ const livenessMachine = createMachine({
964
970
  const { videoEl, canvasEl, isMobile } = context.videoAssociatedParams;
965
971
  const { faceDetector } = context.ovalAssociatedParams;
966
972
  // initialize models
967
- try {
968
- await faceDetector.modelLoadingPromise;
969
- }
970
- catch (err) {
971
- // eslint-disable-next-line no-console
972
- console.log({ err });
973
- }
973
+ await faceDetector.modelLoadingPromise;
974
974
  // detect face
975
975
  const detectedFaces = await faceDetector.detectFaces(videoEl);
976
976
  let initialFace;
@@ -1,14 +1,40 @@
1
+ import { CONNECTION_TIMEOUT } from '../utils/constants.mjs';
2
+
1
3
  /**
2
4
  * The abstract class representing FaceDetection
3
5
  * to be implemented for different libraries.
4
6
  */
5
7
  class FaceDetection {
8
+ constructor() {
9
+ this._modelLoadTriggered = false;
10
+ }
6
11
  /**
7
12
  * Triggers the `loadModels` method and stores
8
13
  * the corresponding promise to be awaited later.
14
+ * Applies a timeout to prevent indefinite hangs if the
15
+ * network request for model assets stalls.
9
16
  */
10
17
  triggerModelLoading() {
11
- this.modelLoadingPromise = this.loadModels();
18
+ if (this._modelLoadTriggered) {
19
+ return;
20
+ }
21
+ this._modelLoadTriggered = true;
22
+ let timeoutId;
23
+ this.modelLoadingPromise = Promise.race([
24
+ // `finally` ensures the timeout is cleared whether loadModels
25
+ // resolves or rejects, preventing a leaked timer on fast failures.
26
+ this.loadModels().finally(() => {
27
+ clearTimeout(timeoutId);
28
+ }),
29
+ new Promise((_, reject) => {
30
+ timeoutId = setTimeout(() => reject(new Error('Face detection model loading timed out. Check your network connection and try again.')),
31
+ // Uses the same 10s timeout as the WebSocket connection phase.
32
+ // The WASM binary (~425KB) + model (~100KB) should load well
33
+ // within 10s on any reasonable connection. If it hasn't loaded
34
+ // by then, the request is likely stalled (not just slow).
35
+ CONNECTION_TIMEOUT);
36
+ }),
37
+ ]);
12
38
  }
13
39
  }
14
40
 
@@ -142,48 +142,97 @@ class CustomWebSocketFetchHandler {
142
142
  let streamError = undefined;
143
143
  // To notify onclose event that error has occurred.
144
144
  let socketErrorOccurred = false;
145
- // initialize as no-op.
146
- let reject = () => { };
147
- let resolve = () => { };
145
+ // Buffer for messages that arrive before the consumer calls .next().
146
+ // This prevents dropped messages when the server responds faster than
147
+ // the client sets up its async iterator (race condition).
148
+ const messageQueue = [];
149
+ let waitingResolve = null;
150
+ let waitingReject = null;
151
+ let done = false;
152
+ let endError = undefined;
148
153
  socket.onmessage = (event) => {
149
- resolve({
150
- done: false,
151
- value: new Uint8Array(event.data),
152
- });
154
+ const chunk = new Uint8Array(event.data);
155
+ if (waitingResolve) {
156
+ // Consumer is waiting for data — deliver immediately
157
+ const resolve = waitingResolve;
158
+ waitingResolve = null;
159
+ waitingReject = null;
160
+ resolve({ done: false, value: chunk });
161
+ }
162
+ else {
163
+ // Consumer hasn't called .next() yet — buffer the message
164
+ messageQueue.push(chunk);
165
+ }
153
166
  };
154
167
  socket.onerror = (error) => {
155
168
  socketErrorOccurred = true;
156
169
  socket.close();
157
- reject(error);
170
+ done = true;
171
+ endError = error;
172
+ if (waitingReject) {
173
+ const reject = waitingReject;
174
+ waitingResolve = null;
175
+ waitingReject = null;
176
+ reject(error);
177
+ }
158
178
  };
159
179
  socket.onclose = (event) => {
160
180
  this.removeNotUsableSockets(socket.url);
161
181
  if (socketErrorOccurred)
162
182
  return;
183
+ done = true;
163
184
  if (streamError) {
164
- reject(streamError);
185
+ endError = streamError;
165
186
  }
166
187
  else if (event.code !== WS_CLOSURE_CODE.SUCCESS_CODE &&
167
188
  event.code !== 1001) {
168
189
  // Server closed the connection with an abnormal code (e.g. 4001
169
190
  // StreamIdleTimeout, 4003 SessionExpired, 1006 abnormal closure).
170
- reject(new Error(`Server ended the connection unexpectedly (code ${event.code}` +
191
+ endError = new Error(`Server ended the connection unexpectedly (code ${event.code}` +
171
192
  (event.reason ? `: ${event.reason}` : '') +
172
- ')'));
193
+ ')');
173
194
  }
174
- else {
175
- resolve({
176
- done: true,
177
- value: undefined,
178
- });
195
+ if (waitingResolve) {
196
+ if (endError) {
197
+ const reject = waitingReject;
198
+ waitingResolve = null;
199
+ waitingReject = null;
200
+ reject(endError);
201
+ }
202
+ else {
203
+ const resolve = waitingResolve;
204
+ waitingResolve = null;
205
+ waitingReject = null;
206
+ resolve({ done: true, value: undefined });
207
+ }
179
208
  }
180
209
  };
181
210
  const outputStream = {
182
211
  [Symbol.asyncIterator]: () => ({
183
212
  next: () => {
213
+ // If there are buffered messages, deliver the next one immediately
214
+ if (messageQueue.length > 0) {
215
+ return Promise.resolve({
216
+ done: false,
217
+ value: messageQueue.shift(),
218
+ });
219
+ }
220
+ // If the stream has ended, resolve/reject accordingly
221
+ if (done) {
222
+ if (endError) {
223
+ return Promise.reject(endError instanceof Error
224
+ ? endError
225
+ : new Error('Stream ended with an error'));
226
+ }
227
+ return Promise.resolve({
228
+ done: true,
229
+ value: undefined,
230
+ });
231
+ }
232
+ // Otherwise wait for the next message or close event
184
233
  return new Promise((_resolve, _reject) => {
185
- resolve = _resolve;
186
- reject = _reject;
234
+ waitingResolve = _resolve;
235
+ waitingReject = _reject;
187
236
  });
188
237
  },
189
238
  }),
@@ -206,8 +255,7 @@ class CustomWebSocketFetchHandler {
206
255
  catch (err) {
207
256
  // We don't throw the error here because the send()'s returned
208
257
  // would already be settled by the time sending chunk throws error.
209
- // Instead, the notify the output stream to throw if there's
210
- // exceptions
258
+ // Instead, notify the output stream to throw if there's exceptions
211
259
  if (err instanceof Error) {
212
260
  streamError = err;
213
261
  }
@@ -1,6 +1,6 @@
1
+ import { FACE_MATCH_RANGE_MAX, FACE_MATCH_WEIGHT_MAX, FACE_MATCH_WEIGHT_MIN, FACE_MATCH_RANGE_MIN } from './constants.mjs';
1
2
  import { FaceMatchState } from '../types/liveness.mjs';
2
3
  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
4
 
5
5
  /**
6
6
  * Returns the state of the provided face with respect to the provided liveness oval.
@@ -1,6 +1,6 @@
1
+ import { OVAL_HEIGHT_WIDTH_RATIO, PUPIL_DISTANCE_WEIGHT, FACE_HEIGHT_WEIGHT } from './constants.mjs';
1
2
  import { IlluminationState, FaceMatchState } from '../types/liveness.mjs';
2
3
  import { LivenessErrorState } from '../types/error.mjs';
3
- import { OVAL_HEIGHT_WIDTH_RATIO, PUPIL_DISTANCE_WEIGHT, FACE_HEIGHT_WEIGHT } from './constants.mjs';
4
4
 
5
5
  /**
6
6
  * Returns the random number between min and max
@@ -3,13 +3,13 @@ import { ComponentClassName } from '@aws-amplify/ui';
3
3
  import { Flex, View } from '@aws-amplify/ui-react';
4
4
  import { CancelButton } from './CancelButton.mjs';
5
5
  import '../service/machine/machine.mjs';
6
+ import '../service/utils/constants.mjs';
6
7
  import '../service/types/liveness.mjs';
7
8
  import '@tensorflow/tfjs-core';
8
9
  import '../../../packages/react-liveness/node_modules/@tensorflow-models/face-detection/dist/face-detection.esm.mjs';
9
10
  import '@tensorflow/tfjs-backend-wasm';
10
11
  import '@tensorflow/tfjs-backend-cpu';
11
12
  import '@aws-amplify/core/internals/utils';
12
- import '../service/utils/constants.mjs';
13
13
  import '../service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
14
14
  import '../service/utils/createStreamingClient/createStreamingClient.mjs';
15
15
  import '../service/utils/StreamRecorder/StreamRecorder.mjs';
@@ -2,6 +2,7 @@ import React__default from 'react';
2
2
  import { Flex, Button, Text } from '@aws-amplify/ui-react';
3
3
  import { AlertIcon } from '@aws-amplify/ui-react/internal';
4
4
  import '../service/machine/machine.mjs';
5
+ import '../service/utils/constants.mjs';
5
6
  import '../service/types/liveness.mjs';
6
7
  import { LivenessErrorState } from '../service/types/error.mjs';
7
8
  import '@tensorflow/tfjs-core';
@@ -9,7 +10,6 @@ import '../../../packages/react-liveness/node_modules/@tensorflow-models/face-de
9
10
  import '@tensorflow/tfjs-backend-wasm';
10
11
  import '@tensorflow/tfjs-backend-cpu';
11
12
  import '@aws-amplify/core/internals/utils';
12
- import '../service/utils/constants.mjs';
13
13
  import '../service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
14
14
  import '@aws-amplify/ui';
15
15
  import '../service/utils/createStreamingClient/createStreamingClient.mjs';
@@ -1,13 +1,13 @@
1
1
  import * as React from 'react';
2
2
  import { VisuallyHidden, View } from '@aws-amplify/ui-react';
3
3
  import '../service/machine/machine.mjs';
4
+ import '../service/utils/constants.mjs';
4
5
  import { FaceMatchState, IlluminationState } from '../service/types/liveness.mjs';
5
6
  import '@tensorflow/tfjs-core';
6
7
  import '../../../packages/react-liveness/node_modules/@tensorflow-models/face-detection/dist/face-detection.esm.mjs';
7
8
  import '@tensorflow/tfjs-backend-wasm';
8
9
  import '@tensorflow/tfjs-backend-cpu';
9
10
  import '@aws-amplify/core/internals/utils';
10
- import '../service/utils/constants.mjs';
11
11
  import '../service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
12
12
  import '@aws-amplify/ui';
13
13
  import '../service/utils/createStreamingClient/createStreamingClient.mjs';
@@ -1,13 +1,13 @@
1
1
  export { default as FaceLivenessDetector } from './components/FaceLivenessDetector/FaceLivenessDetector.mjs';
2
2
  export { default as FaceLivenessDetectorCore } from './components/FaceLivenessDetector/FaceLivenessDetectorCore.mjs';
3
3
  import './components/FaceLivenessDetector/service/machine/machine.mjs';
4
+ import './components/FaceLivenessDetector/service/utils/constants.mjs';
4
5
  import './components/FaceLivenessDetector/service/types/liveness.mjs';
5
6
  import '@tensorflow/tfjs-core';
6
7
  import './packages/react-liveness/node_modules/@tensorflow-models/face-detection/dist/face-detection.esm.mjs';
7
8
  import '@tensorflow/tfjs-backend-wasm';
8
9
  import '@tensorflow/tfjs-backend-cpu';
9
10
  import '@aws-amplify/core/internals/utils';
10
- import './components/FaceLivenessDetector/service/utils/constants.mjs';
11
11
  import './components/FaceLivenessDetector/service/utils/ColorSequenceDisplay/ColorSequenceDisplay.mjs';
12
12
  import '@aws-amplify/ui';
13
13
  import './components/FaceLivenessDetector/service/utils/createStreamingClient/createStreamingClient.mjs';
@@ -1,3 +1,3 @@
1
- const VERSION = '3.6.6';
1
+ const VERSION = '3.6.8';
2
2
 
3
3
  export { VERSION };
package/dist/index.js CHANGED
@@ -43,17 +43,89 @@ function _interopNamespace(e) {
43
43
 
44
44
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
45
45
 
46
+ // Face distance is calculated as pupilDistance / ovalWidth.
47
+ // The further away you are from the camera the distance between your pupils will decrease, thus lowering the threshold values.
48
+ // Constants from science team to determine ocular distance (space between eyes)
49
+ const PUPIL_DISTANCE_WEIGHT = 2.0;
50
+ const FACE_HEIGHT_WEIGHT = 1.8;
51
+ // Constants from science team to find face match percentage
52
+ const FACE_MATCH_RANGE_MIN = 0;
53
+ const FACE_MATCH_RANGE_MAX = 1;
54
+ const FACE_MATCH_WEIGHT_MIN = 0.25;
55
+ const FACE_MATCH_WEIGHT_MAX = 0.75;
56
+ const OVAL_HEIGHT_WIDTH_RATIO = 1.618;
57
+ const WS_CLOSURE_CODE = {
58
+ SUCCESS_CODE: 1000,
59
+ DEFAULT_ERROR_CODE: 4000,
60
+ FACE_FIT_TIMEOUT: 4001,
61
+ USER_CANCEL: 4003,
62
+ RUNTIME_ERROR: 4005,
63
+ USER_ERROR_DURING_CONNECTION: 4007,
64
+ };
65
+ // number in milliseconds to record into each video chunk.
66
+ // see https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start#timeslice
67
+ const TIME_SLICE = 1000;
68
+ // in MS, the rate at which colors are rendered/checked
69
+ const TICK_RATE = 10;
70
+ /**
71
+ * The number of seconds before the presigned URL expires.
72
+ * Used to override aws sdk default value of 60
73
+ */
74
+ const REQUEST_EXPIRY = 299;
75
+ /**
76
+ * The maximum time in milliseconds that the connection phase of a request
77
+ * may take before the connection attempt is abandoned.
78
+ */
79
+ const CONNECTION_TIMEOUT = 10000;
80
+ const FACE_MOVEMENT_AND_LIGHT_CHALLENGE = {
81
+ type: 'FaceMovementAndLightChallenge',
82
+ version: '2.0.0',
83
+ };
84
+ const FACE_MOVEMENT_CHALLENGE = {
85
+ type: 'FaceMovementChallenge',
86
+ version: '1.0.0',
87
+ };
88
+ const SUPPORTED_CHALLENGES = [
89
+ FACE_MOVEMENT_AND_LIGHT_CHALLENGE,
90
+ FACE_MOVEMENT_CHALLENGE,
91
+ ];
92
+ const queryParameterString = SUPPORTED_CHALLENGES.map((challenge) => `${challenge.type}_${challenge.version}`).join(',');
93
+
46
94
  /**
47
95
  * The abstract class representing FaceDetection
48
96
  * to be implemented for different libraries.
49
97
  */
50
98
  let FaceDetection$1 = class FaceDetection {
99
+ constructor() {
100
+ this._modelLoadTriggered = false;
101
+ }
51
102
  /**
52
103
  * Triggers the `loadModels` method and stores
53
104
  * the corresponding promise to be awaited later.
105
+ * Applies a timeout to prevent indefinite hangs if the
106
+ * network request for model assets stalls.
54
107
  */
55
108
  triggerModelLoading() {
56
- this.modelLoadingPromise = this.loadModels();
109
+ if (this._modelLoadTriggered) {
110
+ return;
111
+ }
112
+ this._modelLoadTriggered = true;
113
+ let timeoutId;
114
+ this.modelLoadingPromise = Promise.race([
115
+ // `finally` ensures the timeout is cleared whether loadModels
116
+ // resolves or rejects, preventing a leaked timer on fast failures.
117
+ this.loadModels().finally(() => {
118
+ clearTimeout(timeoutId);
119
+ }),
120
+ new Promise((_, reject) => {
121
+ timeoutId = setTimeout(() => reject(new Error('Face detection model loading timed out. Check your network connection and try again.')),
122
+ // Uses the same 10s timeout as the WebSocket connection phase.
123
+ // The WASM binary (~425KB) + model (~100KB) should load well
124
+ // within 10s on any reasonable connection. If it hasn't loaded
125
+ // by then, the request is likely stalled (not just slow).
126
+ CONNECTION_TIMEOUT);
127
+ }),
128
+ ]);
57
129
  }
58
130
  };
59
131
 
@@ -95,54 +167,6 @@ const LivenessErrorState = {
95
167
  DEFAULT_CAMERA_NOT_FOUND_ERROR: 'DEFAULT_CAMERA_NOT_FOUND_ERROR',
96
168
  };
97
169
 
98
- // Face distance is calculated as pupilDistance / ovalWidth.
99
- // The further away you are from the camera the distance between your pupils will decrease, thus lowering the threshold values.
100
- // Constants from science team to determine ocular distance (space between eyes)
101
- const PUPIL_DISTANCE_WEIGHT = 2.0;
102
- const FACE_HEIGHT_WEIGHT = 1.8;
103
- // Constants from science team to find face match percentage
104
- const FACE_MATCH_RANGE_MIN = 0;
105
- const FACE_MATCH_RANGE_MAX = 1;
106
- const FACE_MATCH_WEIGHT_MIN = 0.25;
107
- const FACE_MATCH_WEIGHT_MAX = 0.75;
108
- const OVAL_HEIGHT_WIDTH_RATIO = 1.618;
109
- const WS_CLOSURE_CODE = {
110
- SUCCESS_CODE: 1000,
111
- DEFAULT_ERROR_CODE: 4000,
112
- FACE_FIT_TIMEOUT: 4001,
113
- USER_CANCEL: 4003,
114
- RUNTIME_ERROR: 4005,
115
- USER_ERROR_DURING_CONNECTION: 4007,
116
- };
117
- // number in milliseconds to record into each video chunk.
118
- // see https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder/start#timeslice
119
- const TIME_SLICE = 1000;
120
- // in MS, the rate at which colors are rendered/checked
121
- const TICK_RATE = 10;
122
- /**
123
- * The number of seconds before the presigned URL expires.
124
- * Used to override aws sdk default value of 60
125
- */
126
- const REQUEST_EXPIRY = 299;
127
- /**
128
- * The maximum time in milliseconds that the connection phase of a request
129
- * may take before the connection attempt is abandoned.
130
- */
131
- const CONNECTION_TIMEOUT = 10000;
132
- const FACE_MOVEMENT_AND_LIGHT_CHALLENGE = {
133
- type: 'FaceMovementAndLightChallenge',
134
- version: '2.0.0',
135
- };
136
- const FACE_MOVEMENT_CHALLENGE = {
137
- type: 'FaceMovementChallenge',
138
- version: '1.0.0',
139
- };
140
- const SUPPORTED_CHALLENGES = [
141
- FACE_MOVEMENT_AND_LIGHT_CHALLENGE,
142
- FACE_MOVEMENT_CHALLENGE,
143
- ];
144
- const queryParameterString = SUPPORTED_CHALLENGES.map((challenge) => `${challenge.type}_${challenge.version}`).join(',');
145
-
146
170
  /**
147
171
  * Returns the random number between min and max
148
172
  * seeded with the provided random seed.
@@ -1227,7 +1251,7 @@ function createRequestStreamGenerator(stream) {
1227
1251
  };
1228
1252
  }
1229
1253
 
1230
- const VERSION = '3.6.6';
1254
+ const VERSION = '3.6.8';
1231
1255
 
1232
1256
  const BASE_USER_AGENT = `ui-react-liveness/${VERSION}`;
1233
1257
  const getLivenessUserAgent = () => {
@@ -1372,48 +1396,97 @@ class CustomWebSocketFetchHandler {
1372
1396
  let streamError = undefined;
1373
1397
  // To notify onclose event that error has occurred.
1374
1398
  let socketErrorOccurred = false;
1375
- // initialize as no-op.
1376
- let reject = () => { };
1377
- let resolve = () => { };
1399
+ // Buffer for messages that arrive before the consumer calls .next().
1400
+ // This prevents dropped messages when the server responds faster than
1401
+ // the client sets up its async iterator (race condition).
1402
+ const messageQueue = [];
1403
+ let waitingResolve = null;
1404
+ let waitingReject = null;
1405
+ let done = false;
1406
+ let endError = undefined;
1378
1407
  socket.onmessage = (event) => {
1379
- resolve({
1380
- done: false,
1381
- value: new Uint8Array(event.data),
1382
- });
1408
+ const chunk = new Uint8Array(event.data);
1409
+ if (waitingResolve) {
1410
+ // Consumer is waiting for data — deliver immediately
1411
+ const resolve = waitingResolve;
1412
+ waitingResolve = null;
1413
+ waitingReject = null;
1414
+ resolve({ done: false, value: chunk });
1415
+ }
1416
+ else {
1417
+ // Consumer hasn't called .next() yet — buffer the message
1418
+ messageQueue.push(chunk);
1419
+ }
1383
1420
  };
1384
1421
  socket.onerror = (error) => {
1385
1422
  socketErrorOccurred = true;
1386
1423
  socket.close();
1387
- reject(error);
1424
+ done = true;
1425
+ endError = error;
1426
+ if (waitingReject) {
1427
+ const reject = waitingReject;
1428
+ waitingResolve = null;
1429
+ waitingReject = null;
1430
+ reject(error);
1431
+ }
1388
1432
  };
1389
1433
  socket.onclose = (event) => {
1390
1434
  this.removeNotUsableSockets(socket.url);
1391
1435
  if (socketErrorOccurred)
1392
1436
  return;
1437
+ done = true;
1393
1438
  if (streamError) {
1394
- reject(streamError);
1439
+ endError = streamError;
1395
1440
  }
1396
1441
  else if (event.code !== WS_CLOSURE_CODE.SUCCESS_CODE &&
1397
1442
  event.code !== 1001) {
1398
1443
  // Server closed the connection with an abnormal code (e.g. 4001
1399
1444
  // StreamIdleTimeout, 4003 SessionExpired, 1006 abnormal closure).
1400
- reject(new Error(`Server ended the connection unexpectedly (code ${event.code}` +
1445
+ endError = new Error(`Server ended the connection unexpectedly (code ${event.code}` +
1401
1446
  (event.reason ? `: ${event.reason}` : '') +
1402
- ')'));
1447
+ ')');
1403
1448
  }
1404
- else {
1405
- resolve({
1406
- done: true,
1407
- value: undefined,
1408
- });
1449
+ if (waitingResolve) {
1450
+ if (endError) {
1451
+ const reject = waitingReject;
1452
+ waitingResolve = null;
1453
+ waitingReject = null;
1454
+ reject(endError);
1455
+ }
1456
+ else {
1457
+ const resolve = waitingResolve;
1458
+ waitingResolve = null;
1459
+ waitingReject = null;
1460
+ resolve({ done: true, value: undefined });
1461
+ }
1409
1462
  }
1410
1463
  };
1411
1464
  const outputStream = {
1412
1465
  [Symbol.asyncIterator]: () => ({
1413
1466
  next: () => {
1467
+ // If there are buffered messages, deliver the next one immediately
1468
+ if (messageQueue.length > 0) {
1469
+ return Promise.resolve({
1470
+ done: false,
1471
+ value: messageQueue.shift(),
1472
+ });
1473
+ }
1474
+ // If the stream has ended, resolve/reject accordingly
1475
+ if (done) {
1476
+ if (endError) {
1477
+ return Promise.reject(endError instanceof Error
1478
+ ? endError
1479
+ : new Error('Stream ended with an error'));
1480
+ }
1481
+ return Promise.resolve({
1482
+ done: true,
1483
+ value: undefined,
1484
+ });
1485
+ }
1486
+ // Otherwise wait for the next message or close event
1414
1487
  return new Promise((_resolve, _reject) => {
1415
- resolve = _resolve;
1416
- reject = _reject;
1488
+ waitingResolve = _resolve;
1489
+ waitingReject = _reject;
1417
1490
  });
1418
1491
  },
1419
1492
  }),
@@ -1436,8 +1509,7 @@ class CustomWebSocketFetchHandler {
1436
1509
  catch (err) {
1437
1510
  // We don't throw the error here because the send()'s returned
1438
1511
  // would already be settled by the time sending chunk throws error.
1439
- // Instead, the notify the output stream to throw if there's
1440
- // exceptions
1512
+ // Instead, notify the output stream to throw if there's exceptions
1441
1513
  if (err instanceof Error) {
1442
1514
  streamError = err;
1443
1515
  }
@@ -1973,6 +2045,10 @@ const livenessMachine = xstate.createMachine({
1973
2045
  'spawnResponseStreamActor',
1974
2046
  ],
1975
2047
  },
2048
+ onError: {
2049
+ target: '#livenessMachine.error',
2050
+ actions: 'updateErrorStateForRuntime',
2051
+ },
1976
2052
  },
1977
2053
  },
1978
2054
  waitForSessionInfo: {
@@ -2001,6 +2077,10 @@ const livenessMachine = xstate.createMachine({
2001
2077
  target: 'checkFaceDetectedBeforeStart',
2002
2078
  actions: 'updateFaceMatchBeforeStartDetails',
2003
2079
  },
2080
+ onError: {
2081
+ target: 'error',
2082
+ actions: 'updateErrorStateForRuntime',
2083
+ },
2004
2084
  },
2005
2085
  },
2006
2086
  checkFaceDetectedBeforeStart: {
@@ -2019,6 +2099,10 @@ const livenessMachine = xstate.createMachine({
2019
2099
  target: 'checkFaceDistanceBeforeRecording',
2020
2100
  actions: 'updateFaceDistanceBeforeRecording',
2021
2101
  },
2102
+ onError: {
2103
+ target: 'error',
2104
+ actions: 'updateErrorStateForRuntime',
2105
+ },
2022
2106
  },
2023
2107
  },
2024
2108
  checkFaceDistanceBeforeRecording: {
@@ -2696,13 +2780,7 @@ const livenessMachine = xstate.createMachine({
2696
2780
  const { videoEl } = context.videoAssociatedParams;
2697
2781
  const { faceDetector } = context.ovalAssociatedParams;
2698
2782
  // initialize models
2699
- try {
2700
- await faceDetector.modelLoadingPromise;
2701
- }
2702
- catch (err) {
2703
- // eslint-disable-next-line no-console
2704
- console.log({ err });
2705
- }
2783
+ await faceDetector.modelLoadingPromise;
2706
2784
  // detect face
2707
2785
  const faceMatchState = await getFaceMatchState(faceDetector, videoEl);
2708
2786
  return { faceMatchState };
@@ -2734,13 +2812,7 @@ const livenessMachine = xstate.createMachine({
2734
2812
  const { videoEl, canvasEl, isMobile } = context.videoAssociatedParams;
2735
2813
  const { faceDetector } = context.ovalAssociatedParams;
2736
2814
  // initialize models
2737
- try {
2738
- await faceDetector.modelLoadingPromise;
2739
- }
2740
- catch (err) {
2741
- // eslint-disable-next-line no-console
2742
- console.log({ err });
2743
- }
2815
+ await faceDetector.modelLoadingPromise;
2744
2816
  // detect face
2745
2817
  const detectedFaces = await faceDetector.detectFaces(videoEl);
2746
2818
  let initialFace;
package/dist/styles.css CHANGED
@@ -1512,11 +1512,11 @@
1512
1512
  --amplify-colors-black: hsl(0, 0%, 0%);
1513
1513
  --amplify-colors-white: hsl(0, 0%, 100%);
1514
1514
  --amplify-colors-transparent: transparent;
1515
- --amplify-fonts-default-variable: "InterVariable", "Inter var", "Inter", -apple-system, BlinkMacSystemFont,
1516
- "Helvetica Neue", "Segoe UI", Oxygen, Ubuntu, Cantarell, "Open Sans",
1515
+ --amplify-fonts-default-variable: 'InterVariable', 'Inter var', 'Inter', -apple-system, BlinkMacSystemFont,
1516
+ 'Helvetica Neue', 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans',
1517
1517
  sans-serif;
1518
- --amplify-fonts-default-static: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue",
1519
- "Segoe UI", Oxygen, Ubuntu, Cantarell, "Open Sans", sans-serif;
1518
+ --amplify-fonts-default-static: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue',
1519
+ 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Open Sans', sans-serif;
1520
1520
  --amplify-font-sizes-xxxs: 0.375rem;
1521
1521
  --amplify-font-sizes-xxs: 0.5rem;
1522
1522
  --amplify-font-sizes-xs: 0.75rem;
@@ -2067,9 +2067,6 @@ strong.amplify-text {
2067
2067
  border-width: var(--amplify-components-button-menu-border-width);
2068
2068
  background-color: var(--amplify-components-button-menu-background-color);
2069
2069
  justify-content: var(--amplify-components-button-menu-justify-content);
2070
- --amplify-internal-button-disabled-color: var(
2071
- --amplify-components-button-menu-disabled-color
2072
- );
2073
2070
  }
2074
2071
  .amplify-button--menu:hover {
2075
2072
  color: var(--amplify-components-button-menu-hover-color);
@@ -2084,6 +2081,11 @@ strong.amplify-text {
2084
2081
  color: var(--amplify-components-button-menu-active-color);
2085
2082
  background-color: var(--amplify-components-button-menu-active-background-color);
2086
2083
  }
2084
+ .amplify-button--menu {
2085
+ --amplify-internal-button-disabled-color: var(
2086
+ --amplify-components-button-menu-disabled-color
2087
+ );
2088
+ }
2087
2089
  .amplify-button--primary {
2088
2090
  --amplify-internal-button-border-width: var(
2089
2091
  --amplify-components-button-primary-border-width
@@ -2097,24 +2099,6 @@ strong.amplify-text {
2097
2099
  --amplify-internal-button-color: var(
2098
2100
  --amplify-components-button-primary-color
2099
2101
  );
2100
- --amplify-internal-button-disabled-border-color: var(
2101
- --amplify-components-button-primary-disabled-border-color
2102
- );
2103
- --amplify-internal-button-disabled-background-color: var(
2104
- --amplify-components-button-primary-disabled-background-color
2105
- );
2106
- --amplify-internal-button-disabled-color: var(
2107
- --amplify-components-button-primary-disabled-color
2108
- );
2109
- --amplify-internal-button-loading-background-color: var(
2110
- --amplify-components-button-primary-loading-background-color
2111
- );
2112
- --amplify-internal-button-loading-border-color: var(
2113
- --amplify-components-button-primary-loading-border-color
2114
- );
2115
- --amplify-internal-button-loading-color: var(
2116
- --amplify-components-button-primary-loading-color
2117
- );
2118
2102
  }
2119
2103
  .amplify-button--primary:hover {
2120
2104
  --amplify-internal-button-background-color: var(
@@ -2387,6 +2371,26 @@ strong.amplify-text {
2387
2371
  --amplify-components-button-primary-overlay-active-color
2388
2372
  );
2389
2373
  }
2374
+ .amplify-button--primary {
2375
+ --amplify-internal-button-disabled-border-color: var(
2376
+ --amplify-components-button-primary-disabled-border-color
2377
+ );
2378
+ --amplify-internal-button-disabled-background-color: var(
2379
+ --amplify-components-button-primary-disabled-background-color
2380
+ );
2381
+ --amplify-internal-button-disabled-color: var(
2382
+ --amplify-components-button-primary-disabled-color
2383
+ );
2384
+ --amplify-internal-button-loading-background-color: var(
2385
+ --amplify-components-button-primary-loading-background-color
2386
+ );
2387
+ --amplify-internal-button-loading-border-color: var(
2388
+ --amplify-components-button-primary-loading-border-color
2389
+ );
2390
+ --amplify-internal-button-loading-color: var(
2391
+ --amplify-components-button-primary-loading-color
2392
+ );
2393
+ }
2390
2394
  .amplify-button--link {
2391
2395
  --amplify-internal-button-border-width: var(
2392
2396
  --amplify-components-button-link-border-width
@@ -2400,24 +2404,6 @@ strong.amplify-text {
2400
2404
  --amplify-internal-button-color: var(
2401
2405
  --amplify-components-button-link-color
2402
2406
  );
2403
- --amplify-internal-button-disabled-border-color: var(
2404
- --amplify-components-button-link-disabled-border-color
2405
- );
2406
- --amplify-internal-button-disabled-background-color: var(
2407
- --amplify-components-button-link-disabled-background-color
2408
- );
2409
- --amplify-internal-button-disabled-color: var(
2410
- --amplify-components-button-link-disabled-color
2411
- );
2412
- --amplify-internal-button-loading-background-color: var(
2413
- --amplify-components-button-link-loading-background-color
2414
- );
2415
- --amplify-internal-button-loading-border-color: var(
2416
- --amplify-components-button-link-loading-border-color
2417
- );
2418
- --amplify-internal-button-loading-color: var(
2419
- --amplify-components-button-link-loading-color
2420
- );
2421
2407
  }
2422
2408
  .amplify-button--link:hover {
2423
2409
  --amplify-internal-button-background-color: var(
@@ -2690,30 +2676,32 @@ strong.amplify-text {
2690
2676
  --amplify-components-button-link-overlay-active-color
2691
2677
  );
2692
2678
  }
2693
- .amplify-button--destructive {
2694
- border-width: var(--amplify-components-button-destructive-border-width);
2695
- background-color: var(--amplify-components-button-destructive-background-color);
2696
- border-color: var(--amplify-components-button-destructive-border-color);
2697
- color: var(--amplify-components-button-destructive-color);
2679
+ .amplify-button--link {
2698
2680
  --amplify-internal-button-disabled-border-color: var(
2699
- --amplify-components-button-destructive-disabled-border-color
2681
+ --amplify-components-button-link-disabled-border-color
2700
2682
  );
2701
2683
  --amplify-internal-button-disabled-background-color: var(
2702
- --amplify-components-button-destructive-disabled-background-color
2684
+ --amplify-components-button-link-disabled-background-color
2703
2685
  );
2704
2686
  --amplify-internal-button-disabled-color: var(
2705
- --amplify-components-button-destructive-disabled-color
2687
+ --amplify-components-button-link-disabled-color
2706
2688
  );
2707
2689
  --amplify-internal-button-loading-background-color: var(
2708
- --amplify-components-button-destructive-loading-background-color
2690
+ --amplify-components-button-link-loading-background-color
2709
2691
  );
2710
2692
  --amplify-internal-button-loading-border-color: var(
2711
- --amplify-components-button-destructive-loading-border-color
2693
+ --amplify-components-button-link-loading-border-color
2712
2694
  );
2713
2695
  --amplify-internal-button-loading-color: var(
2714
- --amplify-components-button-destructive-loading-color
2696
+ --amplify-components-button-link-loading-color
2715
2697
  );
2716
2698
  }
2699
+ .amplify-button--destructive {
2700
+ border-width: var(--amplify-components-button-destructive-border-width);
2701
+ background-color: var(--amplify-components-button-destructive-background-color);
2702
+ border-color: var(--amplify-components-button-destructive-border-color);
2703
+ color: var(--amplify-components-button-destructive-color);
2704
+ }
2717
2705
  .amplify-button--destructive:hover {
2718
2706
  background-color: var(--amplify-components-button-destructive-hover-background-color);
2719
2707
  border-color: var(--amplify-components-button-destructive-hover-border-color);
@@ -2730,6 +2718,26 @@ strong.amplify-text {
2730
2718
  border-color: var(--amplify-components-button-destructive-active-border-color);
2731
2719
  color: var(--amplify-components-button-destructive-active-color);
2732
2720
  }
2721
+ .amplify-button--destructive {
2722
+ --amplify-internal-button-disabled-border-color: var(
2723
+ --amplify-components-button-destructive-disabled-border-color
2724
+ );
2725
+ --amplify-internal-button-disabled-background-color: var(
2726
+ --amplify-components-button-destructive-disabled-background-color
2727
+ );
2728
+ --amplify-internal-button-disabled-color: var(
2729
+ --amplify-components-button-destructive-disabled-color
2730
+ );
2731
+ --amplify-internal-button-loading-background-color: var(
2732
+ --amplify-components-button-destructive-loading-background-color
2733
+ );
2734
+ --amplify-internal-button-loading-border-color: var(
2735
+ --amplify-components-button-destructive-loading-border-color
2736
+ );
2737
+ --amplify-internal-button-loading-color: var(
2738
+ --amplify-components-button-destructive-loading-color
2739
+ );
2740
+ }
2733
2741
  .amplify-button--warning {
2734
2742
  background-color: var(--amplify-components-button-warning-background-color);
2735
2743
  border-color: var(--amplify-components-button-warning-border-color);
@@ -2989,22 +2997,6 @@ strong.amplify-text {
2989
2997
  outline-style: var(--amplify-components-fieldcontrol-outline-style);
2990
2998
  outline-width: var(--amplify-components-fieldcontrol-outline-width);
2991
2999
  outline-offset: var(--amplify-components-fieldcontrol-outline-offset);
2992
- -webkit-user-select: text;
2993
- -moz-user-select: text;
2994
- user-select: text;
2995
- display: inline-block;
2996
- --amplify-components-fieldcontrol-color: var(
2997
- --amplify-components-input-color
2998
- );
2999
- --amplify-components-fieldcontrol-border-color: var(
3000
- --amplify-components-input-border-color
3001
- );
3002
- --amplify-components-fieldcontrol-font-size: var(
3003
- --amplify-components-input-font-size
3004
- );
3005
- --amplify-components-fieldcontrol-focus-border-color: var(
3006
- --amplify-components-input-focus-border-color
3007
- );
3008
3000
  }
3009
3001
  .amplify-input:focus {
3010
3002
  border-color: var(--amplify-components-fieldcontrol-focus-border-color);
@@ -3054,6 +3046,24 @@ strong.amplify-text {
3054
3046
  border-color: var(--amplify-components-fieldcontrol-disabled-border-color);
3055
3047
  background-color: var(--amplify-components-fieldcontrol-disabled-background-color);
3056
3048
  }
3049
+ .amplify-input {
3050
+ -webkit-user-select: text;
3051
+ -moz-user-select: text;
3052
+ user-select: text;
3053
+ display: inline-block;
3054
+ --amplify-components-fieldcontrol-color: var(
3055
+ --amplify-components-input-color
3056
+ );
3057
+ --amplify-components-fieldcontrol-border-color: var(
3058
+ --amplify-components-input-border-color
3059
+ );
3060
+ --amplify-components-fieldcontrol-font-size: var(
3061
+ --amplify-components-input-font-size
3062
+ );
3063
+ --amplify-components-fieldcontrol-focus-border-color: var(
3064
+ --amplify-components-input-focus-border-color
3065
+ );
3066
+ }
3057
3067
 
3058
3068
  .amplify-textarea {
3059
3069
  box-sizing: border-box;
@@ -3074,11 +3084,6 @@ strong.amplify-text {
3074
3084
  outline-style: var(--amplify-components-fieldcontrol-outline-style);
3075
3085
  outline-width: var(--amplify-components-fieldcontrol-outline-width);
3076
3086
  outline-offset: var(--amplify-components-fieldcontrol-outline-offset);
3077
- -webkit-user-select: text;
3078
- -moz-user-select: text;
3079
- user-select: text;
3080
- white-space: pre-wrap;
3081
- max-height: 100%;
3082
3087
  }
3083
3088
  .amplify-textarea:focus {
3084
3089
  border-color: var(--amplify-components-fieldcontrol-focus-border-color);
@@ -3128,6 +3133,13 @@ strong.amplify-text {
3128
3133
  border-color: var(--amplify-components-fieldcontrol-disabled-border-color);
3129
3134
  background-color: var(--amplify-components-fieldcontrol-disabled-background-color);
3130
3135
  }
3136
+ .amplify-textarea {
3137
+ -webkit-user-select: text;
3138
+ -moz-user-select: text;
3139
+ user-select: text;
3140
+ white-space: pre-wrap;
3141
+ max-height: 100%;
3142
+ }
3131
3143
 
3132
3144
  .amplify-image {
3133
3145
  height: var(--amplify-components-image-height);
@@ -3185,8 +3197,6 @@ strong.amplify-text {
3185
3197
  animation-timing-function: linear;
3186
3198
  animation-iteration-count: infinite;
3187
3199
  animation-duration: var(--amplify-components-loader-animation-duration);
3188
- stroke: var(--amplify-components-loader-stroke-filled);
3189
- stroke-linecap: var(--amplify-components-loader-stroke-linecap);
3190
3200
  }
3191
3201
  @media (prefers-reduced-motion) {
3192
3202
  .amplify-loader circle:last-of-type {
@@ -3195,6 +3205,10 @@ strong.amplify-text {
3195
3205
  stroke-dashoffset: 50%;
3196
3206
  }
3197
3207
  }
3208
+ .amplify-loader circle:last-of-type {
3209
+ stroke: var(--amplify-components-loader-stroke-filled);
3210
+ stroke-linecap: var(--amplify-components-loader-stroke-linecap);
3211
+ }
3198
3212
  .amplify-loader line:last-of-type {
3199
3213
  stroke: var(--amplify-components-loader-linear-stroke-filled);
3200
3214
  stroke-dasharray: 50% 200%;
@@ -3897,6 +3911,9 @@ strong.amplify-text {
3897
3911
  border-color: var(--amplify-internal-checkbox_button-focused-before-border-color);
3898
3912
  box-shadow: var(--amplify-internal-checkbox_button-focused-before-box-shadow);
3899
3913
  }
3914
+ .amplify-checkbox__button--error::before {
3915
+ border-color: var(--amplify-components-checkbox-button-error-border-color);
3916
+ }
3900
3917
  .amplify-checkbox__button--error {
3901
3918
  --amplify-internal-checkbox_button-focused-before-border-color: var(
3902
3919
  --amplify-components-checkbox-button-error-focus-border-color
@@ -3905,9 +3922,6 @@ strong.amplify-text {
3905
3922
  --amplify-components-checkbox-button-error-focus-box-shadow
3906
3923
  );
3907
3924
  }
3908
- .amplify-checkbox__button--error::before {
3909
- border-color: var(--amplify-components-checkbox-button-error-border-color);
3910
- }
3911
3925
  .amplify-checkbox__button--disabled::before {
3912
3926
  border-color: var(--amplify-components-checkbox-button-disabled-border-color);
3913
3927
  }
@@ -4166,7 +4180,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
4166
4180
  flex-direction: var(--amplify-components-fieldset-flex-direction);
4167
4181
  gap: var(--amplify-components-fieldset-gap);
4168
4182
  /* Sizes */
4169
- /* Variations */
4170
4183
  }
4171
4184
  .amplify-fieldset--small {
4172
4185
  gap: var(--amplify-components-fieldset-small-gap);
@@ -4174,6 +4187,9 @@ html[dir=rtl] .amplify-field-group__inner-start {
4174
4187
  .amplify-fieldset--large {
4175
4188
  gap: var(--amplify-components-fieldset-large-gap);
4176
4189
  }
4190
+ .amplify-fieldset {
4191
+ /* Variations */
4192
+ }
4177
4193
  .amplify-fieldset--outlined {
4178
4194
  border: var(--amplify-components-fieldset-outlined-border-width) var(--amplify-components-fieldset-outlined-border-style) var(--amplify-components-fieldset-outlined-border-color);
4179
4195
  padding: var(--amplify-components-fieldset-outlined-padding);
@@ -5280,11 +5296,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
5280
5296
  outline-style: var(--amplify-components-fieldcontrol-outline-style);
5281
5297
  outline-width: var(--amplify-components-fieldcontrol-outline-width);
5282
5298
  outline-offset: var(--amplify-components-fieldcontrol-outline-offset);
5283
- background-color: var(--amplify-components-select-background-color);
5284
- color: var(--amplify-components-select-color);
5285
- min-width: var(--amplify-components-select-min-width);
5286
- padding-inline-end: var(--amplify-components-select-padding-inline-end);
5287
- white-space: var(--amplify-components-select-white-space);
5288
5299
  }
5289
5300
  .amplify-select:focus {
5290
5301
  border-color: var(--amplify-components-fieldcontrol-focus-border-color);
@@ -5334,6 +5345,13 @@ html[dir=rtl] .amplify-field-group__inner-start {
5334
5345
  border-color: var(--amplify-components-fieldcontrol-disabled-border-color);
5335
5346
  background-color: var(--amplify-components-fieldcontrol-disabled-background-color);
5336
5347
  }
5348
+ .amplify-select {
5349
+ background-color: var(--amplify-components-select-background-color);
5350
+ color: var(--amplify-components-select-color);
5351
+ min-width: var(--amplify-components-select-min-width);
5352
+ padding-inline-end: var(--amplify-components-select-padding-inline-end);
5353
+ white-space: var(--amplify-components-select-white-space);
5354
+ }
5337
5355
  .amplify-select option {
5338
5356
  background-color: var(--amplify-components-select-option-background-color);
5339
5357
  color: var(--amplify-components-select-option-color);
@@ -5595,14 +5613,14 @@ html[dir=rtl] .amplify-field-group__inner-start {
5595
5613
  border-inline-start: none;
5596
5614
  }
5597
5615
 
5598
- .amplify-stepperfield__input {
5599
- -moz-appearance: textfield;
5600
- text-align: var(--amplify-components-stepperfield-input-text-align);
5601
- }
5602
5616
  .amplify-stepperfield__input::-webkit-outer-spin-button, .amplify-stepperfield__input::-webkit-inner-spin-button {
5603
5617
  -webkit-appearance: none;
5604
5618
  margin: 0;
5605
5619
  }
5620
+ .amplify-stepperfield__input {
5621
+ -moz-appearance: textfield;
5622
+ text-align: var(--amplify-components-stepperfield-input-text-align);
5623
+ }
5606
5624
  .amplify-stepperfield__input:not(:focus, [aria-invalid=true]) {
5607
5625
  border-inline-start: none;
5608
5626
  border-inline-end: none;
@@ -5715,9 +5733,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
5715
5733
  --amplify-components-table-header-border-width
5716
5734
  )
5717
5735
  0px var(--amplify-components-table-header-border-width) 0px;
5718
- /**
5719
- * Data attribute stylings
5720
- */
5721
5736
  }
5722
5737
  .amplify-table--small {
5723
5738
  --amplify-internal-table-caption-font-size: var(
@@ -5831,6 +5846,11 @@ html[dir=rtl] .amplify-field-group__inner-start {
5831
5846
  .amplify-table__td:last-child {
5832
5847
  border-right-width: var(--amplify-components-table-data-border-width);
5833
5848
  }
5849
+ .amplify-table {
5850
+ /**
5851
+ * Data attribute stylings
5852
+ */
5853
+ }
5834
5854
  .amplify-table[data-highlightonhover=true] .amplify-table__row:not(.amplify-table__head *):hover {
5835
5855
  background-color: var(--amplify-components-table-row-hover-background-color);
5836
5856
  }
@@ -5972,6 +5992,13 @@ html[dir=rtl] .amplify-field-group__inner-start {
5972
5992
  --amplify-internal-togglebutton-focus-color: var(
5973
5993
  --amplify-components-togglebutton-focus-color
5974
5994
  );
5995
+ }
5996
+ .amplify-togglebutton:focus {
5997
+ background-color: var(--amplify-internal-togglebutton-focus-background-color);
5998
+ border-color: var(--amplify-internal-togglebutton-focus-border-color);
5999
+ color: var(--amplify-internal-togglebutton-focus-color);
6000
+ }
6001
+ .amplify-togglebutton {
5975
6002
  --amplify-internal-togglebutton-hover-background-color: var(
5976
6003
  --amplify-components-togglebutton-hover-background-color
5977
6004
  );
@@ -5981,9 +6008,21 @@ html[dir=rtl] .amplify-field-group__inner-start {
5981
6008
  --amplify-internal-togglebutton-hover-color: var(
5982
6009
  --amplify-internal-togglebutton-color
5983
6010
  );
6011
+ }
6012
+ .amplify-togglebutton:hover {
6013
+ background-color: var(--amplify-internal-togglebutton-hover-background-color);
6014
+ border-color: var(--amplify-internal-togglebutton-hover-border-color);
6015
+ color: var(--amplify-internal-togglebutton-hover-color);
6016
+ }
6017
+ .amplify-togglebutton {
5984
6018
  --amplify-internal-togglebutton-active-background-color: var(
5985
6019
  --amplify-components-togglebutton-active-background-color
5986
6020
  );
6021
+ }
6022
+ .amplify-togglebutton:active {
6023
+ background-color: var(--amplify-internal-togglebutton-active-background-color);
6024
+ }
6025
+ .amplify-togglebutton {
5987
6026
  --amplify-internal-togglebutton-disabled-background-color: var(
5988
6027
  --amplify-components-togglebutton-disabled-background-color
5989
6028
  );
@@ -5994,19 +6033,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
5994
6033
  --amplify-components-togglebutton-disabled-color
5995
6034
  );
5996
6035
  }
5997
- .amplify-togglebutton:focus {
5998
- background-color: var(--amplify-internal-togglebutton-focus-background-color);
5999
- border-color: var(--amplify-internal-togglebutton-focus-border-color);
6000
- color: var(--amplify-internal-togglebutton-focus-color);
6001
- }
6002
- .amplify-togglebutton:hover {
6003
- background-color: var(--amplify-internal-togglebutton-hover-background-color);
6004
- border-color: var(--amplify-internal-togglebutton-hover-border-color);
6005
- color: var(--amplify-internal-togglebutton-hover-color);
6006
- }
6007
- .amplify-togglebutton:active {
6008
- background-color: var(--amplify-internal-togglebutton-active-background-color);
6009
- }
6010
6036
  .amplify-togglebutton:disabled {
6011
6037
  background-color: var(--amplify-internal-togglebutton-disabled-background-color);
6012
6038
  border-color: var(--amplify-internal-togglebutton-disabled-border-color);
@@ -6076,6 +6102,11 @@ html[dir=rtl] .amplify-field-group__inner-start {
6076
6102
  --amplify-internal-togglebutton-focus-color: var(
6077
6103
  --amplify-internal-togglebutton-primary-focus-color
6078
6104
  );
6105
+ }
6106
+ .amplify-togglebutton--primary:focus {
6107
+ box-shadow: var(--amplify-internal-togglebutton-primary-focus-box-shadow);
6108
+ }
6109
+ .amplify-togglebutton--primary {
6079
6110
  --amplify-internal-togglebutton-primary-hover-background-color: var(
6080
6111
  --amplify-components-togglebutton-primary-hover-background-color
6081
6112
  );
@@ -6113,9 +6144,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
6113
6144
  --amplify-internal-togglebutton-primary-disabled-color
6114
6145
  );
6115
6146
  }
6116
- .amplify-togglebutton--primary:focus {
6117
- box-shadow: var(--amplify-internal-togglebutton-primary-focus-box-shadow);
6118
- }
6119
6147
  .amplify-togglebutton--pressed {
6120
6148
  --amplify-internal-togglebutton-primary-background-color: var(
6121
6149
  --amplify-components-togglebutton-primary-pressed-background-color
@@ -6144,6 +6172,13 @@ html[dir=rtl] .amplify-field-group__inner-start {
6144
6172
  --amplify-internal-togglebutton-primary-hover-color: var(
6145
6173
  --amplify-components-togglebutton-primary-pressed-hover-color
6146
6174
  );
6175
+ }
6176
+ .amplify-togglebutton--pressed:hover {
6177
+ --amplify-internal-togglebutton-primary-focus-box-shadow: var(
6178
+ --amplify-components-togglebutton-primary-pressed-hover-box-shadow
6179
+ );
6180
+ }
6181
+ .amplify-togglebutton--pressed {
6147
6182
  --amplify-internal-togglebutton-primary-disabled-background-color: var(
6148
6183
  --amplify-components-togglebutton-primary-pressed-background-color
6149
6184
  );
@@ -6154,11 +6189,6 @@ html[dir=rtl] .amplify-field-group__inner-start {
6154
6189
  --amplify-components-togglebutton-primary-pressed-color
6155
6190
  );
6156
6191
  }
6157
- .amplify-togglebutton--pressed:hover {
6158
- --amplify-internal-togglebutton-primary-focus-box-shadow: var(
6159
- --amplify-components-togglebutton-primary-pressed-hover-box-shadow
6160
- );
6161
- }
6162
6192
  .amplify-togglebutton--link {
6163
6193
  --amplify-internal-togglebutton-link-background-color: var(
6164
6194
  --amplify-components-togglebutton-link-background-color
@@ -22,9 +22,12 @@ export type Coordinate = [number, number];
22
22
  */
23
23
  export declare abstract class FaceDetection {
24
24
  modelLoadingPromise: Promise<void>;
25
+ private _modelLoadTriggered;
25
26
  /**
26
27
  * Triggers the `loadModels` method and stores
27
28
  * the corresponding promise to be awaited later.
29
+ * Applies a timeout to prevent indefinite hangs if the
30
+ * network request for model assets stalls.
28
31
  */
29
32
  triggerModelLoading(): void;
30
33
  /**
@@ -1 +1 @@
1
- export declare const VERSION = "3.6.6";
1
+ export declare const VERSION = "3.6.8";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aws-amplify/ui-react-liveness",
3
- "version": "3.6.6",
3
+ "version": "3.6.8",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/esm/index.mjs",
6
6
  "exports": {
@@ -9,10 +9,12 @@
9
9
  "import": "./dist/esm/index.mjs",
10
10
  "require": "./dist/index.js"
11
11
  },
12
- "./styles.css": "./dist/styles.css"
12
+ "./styles.css": "./dist/styles.css",
13
+ "./dist/styles.css": "./dist/styles.css"
13
14
  },
14
15
  "browser": {
15
- "./styles.css": "./dist/styles.css"
16
+ "./styles.css": "./dist/styles.css",
17
+ "./dist/styles.css": "./dist/styles.css"
16
18
  },
17
19
  "types": "dist/types/index.d.ts",
18
20
  "sideEffects": [
@@ -49,10 +51,10 @@
49
51
  "react-dom": "^16.14 || ^17 || ^18 || ^19"
50
52
  },
51
53
  "dependencies": {
52
- "@aws-amplify/ui": "6.15.4",
53
- "@aws-amplify/ui-react": "6.15.4",
54
- "@aws-sdk/client-rekognitionstreaming": "3.967.0",
55
- "@aws-sdk/util-format-url": "3.965.0",
54
+ "@aws-amplify/ui": "6.15.5",
55
+ "@aws-amplify/ui-react": "6.15.5",
56
+ "@aws-sdk/client-rekognitionstreaming": "^3.974.0",
57
+ "@aws-sdk/util-format-url": "^3.972.37",
56
58
  "@smithy/eventstream-serde-browser": "^4.0.4",
57
59
  "@smithy/fetch-http-handler": "^5.0.4",
58
60
  "@smithy/protocol-http": "^3.0.3",
@@ -84,7 +86,7 @@
84
86
  "name": "FaceLivenessDetector",
85
87
  "path": "dist/esm/index.mjs",
86
88
  "import": "{ FaceLivenessDetector }",
87
- "limit": "229 kB"
89
+ "limit": "230 kB"
88
90
  }
89
91
  ]
90
- }
92
+ }