diff --git a/.changeset/soft-singers-study.md b/.changeset/soft-singers-study.md new file mode 100644 index 00000000000..b005feedec2 --- /dev/null +++ b/.changeset/soft-singers-study.md @@ -0,0 +1,5 @@ +--- +"@aws-amplify/ui-react-liveness": patch +--- + +chore(liveness): Custom websocket handler diff --git a/packages/react-liveness/package.json b/packages/react-liveness/package.json index 5bb2669a924..e2861247afc 100644 --- a/packages/react-liveness/package.json +++ b/packages/react-liveness/package.json @@ -51,6 +51,7 @@ "@aws-amplify/ui": "5.8.0", "@aws-amplify/ui-react": "5.3.0", "@aws-sdk/client-rekognitionstreaming": "3.360.0", + "@smithy/eventstream-serde-browser": "^2.0.4", "@tensorflow-models/blazeface": "0.0.7", "@tensorflow/tfjs-backend-cpu": "3.11.0", "@tensorflow/tfjs-backend-wasm": "3.11.0", @@ -78,7 +79,9 @@ "eslint": "^8.44.0", "jest": "^27.0.4", "jest-canvas-mock": "^2.4.0", + "jest-websocket-mock": "^2.4.1", "jest-when": "^3.5.1", + "mock-socket": "^9.2.1", "react": "^17.0.2", "react-dom": "^17.0.2", "rimraf": "^3.0.2", diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.tsx b/packages/react-liveness/src/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.tsx index 59d30a3fa25..9315131f189 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.tsx +++ b/packages/react-liveness/src/components/FaceLivenessDetector/LivenessCheck/LivenessCheck.tsx @@ -20,13 +20,11 @@ import { } from '../displayText'; import { LandscapeErrorModal } from '../shared/LandscapeErrorModal'; import { CheckScreenComponents } from '../shared/FaceLivenessErrorModal'; +import { selectErrorState } from '../shared'; const CHECK_CLASS_NAME = 'liveness-detector-check'; -export const selectErrorState = createLivenessSelector( - (state) => state.context.errorState -); -export const selectIsRecordingStopped = createLivenessSelector( +const selectIsRecordingStopped = createLivenessSelector( (state) => state.context.isRecordingStopped ); diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/machine/index.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/machine/index.ts index b9a2e6583f2..4dc0c78903f 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/machine/index.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/machine/index.ts @@ -46,6 +46,7 @@ import { LivenessResponseStream, } from '@aws-sdk/client-rekognitionstreaming'; import { STATIC_VIDEO_CONSTRAINTS } from '../../StartLiveness/helpers'; +import { WS_CLOSURE_CODE } from '../utils/constants'; export const MIN_FACE_MATCH_TIME = 500; @@ -758,7 +759,22 @@ export const livenessMachine = createMachine( if (freshnessColorEl) { freshnessColorEl.style.display = 'none'; } - await context.livenessStreamProvider?.endStream(); + + let closureCode = WS_CLOSURE_CODE.DEFAULT_ERROR_CODE; + if (context.errorState === LivenessErrorState.TIMEOUT) { + closureCode = WS_CLOSURE_CODE.FACE_FIT_TIMEOUT; + } else if (context.errorState === LivenessErrorState.RUNTIME_ERROR) { + closureCode = WS_CLOSURE_CODE.RUNTIME_ERROR; + } else if ( + context.errorState === LivenessErrorState.FACE_DISTANCE_ERROR || + context.errorState === LivenessErrorState.MULTIPLE_FACES_ERROR + ) { + closureCode = WS_CLOSURE_CODE.USER_ERROR_DURING_CONNECTION; + } else if (context.errorState === undefined) { + closureCode = WS_CLOSURE_CODE.USER_CANCEL; + } + + await context.livenessStreamProvider?.endStreamWithCode(closureCode); }, freezeStream: async (context) => { const { videoMediaStream, videoEl } = context.videoAssociatedParams!; diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/CustomWebSocketFetchHandler.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/CustomWebSocketFetchHandler.ts new file mode 100644 index 00000000000..2c38b4ad167 --- /dev/null +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/CustomWebSocketFetchHandler.ts @@ -0,0 +1,269 @@ +/** + * Note: This file was copied from https://github.com/aws/aws-sdk-js-v3/blob/main/packages/middleware-websocket/src/websocket-fetch-handler.ts#L176 + * Because of this the file is not fully typed at this time but we should eventually work on fully typing this file. + */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { formatUrl } from '@aws-sdk/util-format-url'; +import { + iterableToReadableStream, + readableStreamtoIterable, +} from '@smithy/eventstream-serde-browser'; +import { FetchHttpHandler } from '@smithy/fetch-http-handler'; +import { HttpRequest, HttpResponse } from '@smithy/protocol-http'; +import { + Provider, + RequestHandler, + RequestHandlerMetadata, +} from '@smithy/types'; +import { WS_CLOSURE_CODE } from './constants'; + +const DEFAULT_WS_CONNECTION_TIMEOUT_MS = 2000; + +const isWebSocketRequest = (request: HttpRequest) => + request.protocol === 'ws:' || request.protocol === 'wss:'; + +const isReadableStream = (payload: any): payload is ReadableStream => + typeof ReadableStream === 'function' && payload instanceof ReadableStream; + +/** + * Transfer payload data to an AsyncIterable. + * When the ReadableStream API is available in the runtime(e.g. browser), and + * the request body is ReadableStream, so we need to transfer it to AsyncIterable + * to make the stream consumable by WebSocket. + */ +const getIterator = (stream: any): AsyncIterable => { + // Noop if stream is already an async iterable + if (stream[Symbol.asyncIterator]) { + return stream; + } + + if (isReadableStream(stream)) { + //If stream is a ReadableStream, transfer the ReadableStream to async iterable. + return readableStreamtoIterable(stream); + } + + //For other types, just wrap them with an async iterable. + return { + [Symbol.asyncIterator]: async function* () { + yield stream; + }, + }; +}; + +/** + * Convert async iterable to a ReadableStream when ReadableStream API + * is available(browsers). Otherwise, leave as it is(ReactNative). + */ +const toReadableStream = (asyncIterable: AsyncIterable) => + typeof ReadableStream === 'function' + ? iterableToReadableStream(asyncIterable) + : asyncIterable; + +export interface WebSocketFetchHandlerOptions { + /** + * The maximum time in milliseconds that the connection phase of a request + * may take before the connection attempt is abandoned. + */ + connectionTimeout?: number; +} + +/** + * Base handler for websocket requests and HTTP request. By default, the request input and output + * body will be in a ReadableStream, because of interface consistency among middleware. + * If ReadableStream is not available, like in React-Native, the response body + * will be an async iterable. + */ +export class CustomWebSocketFetchHandler { + public readonly metadata: RequestHandlerMetadata = { + handlerProtocol: 'websocket/h1.1', + }; + private readonly configPromise: Promise; + private readonly httpHandler: RequestHandler; + private readonly sockets: Record = {}; + private readonly utf8decoder = new TextDecoder(); // default 'utf-8' or 'utf8' + + constructor( + options?: + | WebSocketFetchHandlerOptions + | Provider, + httpHandler: RequestHandler = new FetchHttpHandler() + ) { + this.httpHandler = httpHandler; + if (typeof options === 'function') { + this.configPromise = options().then((opts) => opts ?? {}); + } else { + this.configPromise = Promise.resolve(options ?? {}); + } + } + + /** + * Destroys the WebSocketHandler. + * Closes all sockets from the socket pool. + */ + destroy(): void { + for (const [key, sockets] of Object.entries(this.sockets)) { + for (const socket of sockets) { + socket.close(1000, `Socket closed through destroy() call`); + } + delete this.sockets[key]; + } + } + + async handle(request: HttpRequest): Promise<{ response: HttpResponse }> { + if (!isWebSocketRequest(request)) { + return this.httpHandler.handle(request); + } + const url = formatUrl(request); + const socket: WebSocket = new WebSocket(url); + + // Add socket to sockets pool + if (!this.sockets[url]) { + this.sockets[url] = []; + } + this.sockets[url].push(socket); + + socket.binaryType = 'arraybuffer'; + const { connectionTimeout = DEFAULT_WS_CONNECTION_TIMEOUT_MS } = await this + .configPromise; + await this.waitForReady(socket, connectionTimeout); + const { body } = request; + const bodyStream = getIterator(body); + const asyncIterable = this.connect(socket, bodyStream); + const outputPayload = toReadableStream(asyncIterable); + return { + response: new HttpResponse({ + statusCode: 200, // indicates connection success + body: outputPayload, + }), + }; + } + + /** + * Removes all closing/closed sockets from the socket pool for URL. + */ + private removeNotUsableSockets(url: string): void { + this.sockets[url] = (this.sockets[url] ?? []).filter( + (socket) => + ![WebSocket.CLOSING, WebSocket.CLOSED].includes( + socket.readyState as 2 | 3 + ) + ); + } + + private waitForReady( + socket: WebSocket, + connectionTimeout: number + ): Promise { + return new Promise((resolve, reject) => { + const timeout = setTimeout(() => { + this.removeNotUsableSockets(socket.url); + reject({ + $metadata: { + httpStatusCode: 500, + }, + }); + }, connectionTimeout); + + socket.onopen = () => { + clearTimeout(timeout); + resolve(); + }; + }); + } + + private connect( + socket: WebSocket, + data: AsyncIterable + ): AsyncIterable { + // To notify output stream any error thrown after response + // is returned while data keeps streaming. + let streamError: Error | undefined = undefined; + + // To notify onclose event that error has occurred. + let socketErrorOccurred = false; + + // initialize as no-op. + let reject: (err?: unknown) => void = () => {}; + let resolve: ({ + done, + value, + }: { + done: boolean; + value: Uint8Array; + }) => void = () => {}; + + socket.onmessage = (event) => { + resolve({ + done: false, + value: new Uint8Array(event.data), + }); + }; + + socket.onerror = (error) => { + socketErrorOccurred = true; + socket.close(); + reject(error); + }; + + socket.onclose = () => { + this.removeNotUsableSockets(socket.url); + if (socketErrorOccurred) return; + + if (streamError) { + reject(streamError); + } else { + resolve({ + done: true, + value: undefined as any, // unchecked because done=true. + }); + } + }; + + const outputStream: AsyncIterable = { + [Symbol.asyncIterator]: () => ({ + next: () => { + return new Promise((_resolve, _reject) => { + resolve = _resolve; + reject = _reject; + }); + }, + }), + }; + + const send = async (): Promise => { + try { + for await (const inputChunk of data) { + const decodedString = this.utf8decoder.decode(inputChunk); + if (decodedString.includes('closeCode')) { + const match = decodedString.match(/"closeCode":([0-9]*)/); + if (match) { + const closeCode = match[1]; + socket.close(parseInt(closeCode)); + } + continue; + } + + socket.send(inputChunk); + } + } catch (err) { + // We don't throw the error here because the send()'s returned + // would already be settled by the time sending chunk throws error. + // Instead, the notify the output stream to throw if there's + // exceptions + streamError = err as Error | undefined; + } finally { + // WS status code: https://tools.ietf.org/html/rfc6455#section-7.4 + socket.close(WS_CLOSURE_CODE.SUCCESS_CODE); + } + }; + + send(); + + return outputStream; + } +} diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__mocks__/testUtils.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__mocks__/testUtils.ts index c923fcea666..79fcdf8e093 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__mocks__/testUtils.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__mocks__/testUtils.ts @@ -78,7 +78,7 @@ export const mockOvalDetails: LivenessOvalDetails = { }; export const mockLivenessStreamProvider: any = { sendClientInfo: jest.fn(), - endStream: jest.fn(), + endStreamWithCode: jest.fn(), stopVideo: jest.fn(), dispatchStopVideoEvent: jest.fn(), getResponseStream: jest.fn().mockResolvedValue([mockedStream]), // TODO: a following PR after PR634 will be made to have the stream emit the proper mock data. diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/CustomWebscoketFetchHandler.test.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/CustomWebscoketFetchHandler.test.ts new file mode 100644 index 00000000000..d6caf8ab027 --- /dev/null +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/CustomWebscoketFetchHandler.test.ts @@ -0,0 +1,184 @@ +// Copied from https://github.com/aws/aws-sdk-js-v3/blob/main/packages/middleware-websocket/src/websocket-fetch-handler.spec.ts + +import { FetchHttpHandler } from '@smithy/fetch-http-handler'; +import { HttpRequest } from '@smithy/protocol-http'; +import WS from 'jest-websocket-mock'; +import { WebSocket } from 'mock-socket'; +import { PassThrough } from 'stream'; +import { TextDecoder } from 'util'; + +import { CustomWebSocketFetchHandler } from '../CustomWebSocketFetchHandler'; + +jest.mock('@smithy/fetch-http-handler'); + +Object.defineProperty(window, 'TextDecoder', { + writable: true, + value: TextDecoder, +}); + +describe(CustomWebSocketFetchHandler.name, () => { + const mockHostname = 'localhost:6789'; + const mockUrl = `ws://${mockHostname}/`; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('should handle WebSocket connections', () => { + beforeEach(() => { + (global as any).WebSocket = WebSocket; + }); + + afterEach(() => { + WS.clean(); + }); + + it('should contain protocol metadata', () => { + const handler = new CustomWebSocketFetchHandler(); + expect(handler.metadata.handlerProtocol).toContain('websocket'); + }); + + it('populates socket in socket pool based on handle() requests', async () => { + const handler = new CustomWebSocketFetchHandler(); + new WS(mockUrl); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl]).not.toBeDefined(); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: 'ws:', + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl]).toBeDefined(); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(1); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: 'ws:', + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(2); + }); + + it('closes socket in socket pool on handler.destroy()', async () => { + const handler = new CustomWebSocketFetchHandler(); + new WS(mockUrl); + + await handler.handle( + new HttpRequest({ + body: new PassThrough(), + hostname: mockHostname, + protocol: 'ws:', + }) + ); + + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + const socket = handler.sockets[mockUrl][0]; + + expect(socket.readyState).toBe(WebSocket.OPEN); + handler.destroy(); + + // Verify that socket.close() is called + expect(socket.readyState).toBe(WebSocket.CLOSING); + }); + + it('should throw in output stream if input stream throws', async () => { + expect.assertions(3); + const handler = new CustomWebSocketFetchHandler(); + //Using Node stream is fine because they are also async iterables. + const payload = new PassThrough(); + const server = new WS(mockUrl); + const { + response: { body: responsePayload }, + } = await handler.handle( + new HttpRequest({ + body: payload, + hostname: mockHostname, + protocol: 'ws:', + }) + ); + await server.connected; + payload.emit('error', new Error('FakeError')); + try { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for await (const chunk of responsePayload) { + /** pass */ + console.log(chunk); + continue; + } + } catch (err) { + expect(err).toBeDefined(); + expect((err as any).message).toEqual('FakeError'); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockUrl].length).toBe(0); + } + }); + + it('should return retryable error if cannot setup ws connection', async () => { + expect.assertions(5); + const originalFn = setTimeout; + (global as any).setTimeout = jest.fn().mockImplementation(setTimeout); + const connectionTimeout = 1000; + const handler = new CustomWebSocketFetchHandler(async () => ({ + connectionTimeout, + })); + //Using Node stream is fine because they are also async iterables. + const payload = new PassThrough(); + const mockInvalidHostname = 'localhost:9876'; + const mockInvalidUrl = `ws://${mockInvalidHostname}/`; + + try { + await handler.handle( + new HttpRequest({ + body: payload, + hostname: mockInvalidHostname, //invalid websocket endpoint + protocol: 'ws:', + }) + ); + } catch (err) { + expect(err).toBeDefined(); + expect((err as any).$metadata).toBeDefined(); + expect((err as any).$metadata.httpStatusCode >= 500).toBe(true); + expect( + ((global as any).setTimeout as jest.Mock).mock.calls.filter( + (args) => { + //find the 'setTimeout' call from the websocket handler + return args[0].toString().indexOf('$metadata') >= 0; + } + )[0][1] + ).toBe(connectionTimeout); + // @ts-expect-error Property 'sockets' is private and only accessible within class 'WebSocketHandler'. + expect(handler.sockets[mockInvalidUrl].length).toBe(0); + } + (global as any).setTimeout = originalFn; + }); + }); + + describe('should handle http requests', () => { + it('should create fetch http handler at construction', () => { + new CustomWebSocketFetchHandler(); + expect(FetchHttpHandler).toBeCalled(); + }); + + it('should make http request with fetch handler', async () => { + const httpHandler = new FetchHttpHandler(); + const handler = new CustomWebSocketFetchHandler(undefined, httpHandler); + const request = new HttpRequest({}); + try { + await handler.handle(request); + } catch (e) {} + //@ts-ignore + expect(httpHandler.__proto__.handle).toHaveBeenCalledWith(request); + }); + }); +}); diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/streamProvider.test.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/streamProvider.test.ts index 08c16e9696a..74a68c40247 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/streamProvider.test.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/__tests__/streamProvider.test.ts @@ -2,6 +2,7 @@ import 'web-streams-polyfill'; import 'blob-polyfill'; +import { TextDecoder } from 'util'; import { Amplify } from '@aws-amplify/core'; import { RekognitionStreamingClient } from '@aws-sdk/client-rekognitionstreaming'; import { LivenessStreamProvider } from '../streamProvider'; @@ -13,6 +14,11 @@ jest.mock('../videoRecorder'); jest.mock('@aws-sdk/client-rekognitionstreaming'); jest.mock('@aws-amplify/core'); +Object.defineProperty(window, 'TextDecoder', { + writable: true, + value: TextDecoder, +}); + const mockGet = jest.fn().mockImplementation(() => { return { accessKeyId: 'accessKeyId', @@ -244,7 +250,7 @@ describe('LivenessStreamProvider', () => { }); }); - describe('endStream', () => { + describe('endStreamWithCode', () => { test('should stop video and end the stream and return a promise if cancelled successfully', async () => { const provider = new LivenessStreamProvider({ sessionId: 'sessionId', @@ -252,7 +258,7 @@ describe('LivenessStreamProvider', () => { stream: mockVideoMediaStream, videoEl: mockVideoEl, }); - const response = await provider.endStream(); + const response = await provider.endStreamWithCode(); expect(mockVideoRecorder.stop).toHaveBeenCalled(); expect(response).toBeUndefined(); @@ -266,7 +272,7 @@ describe('LivenessStreamProvider', () => { videoEl: mockVideoEl, }); (provider as any)._reader = undefined; - const response = await provider.endStream(); + const response = await provider.endStreamWithCode(); expect(mockVideoRecorder.stop).toHaveBeenCalled(); expect(response).toBeUndefined(); diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/constants.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/constants.ts index 9ce4f0d5047..3dc1216aabb 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/constants.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/constants.ts @@ -6,3 +6,12 @@ export const FACE_DISTANCE_THRESHOLD = 0.32; export const REDUCED_THRESHOLD = 0.4; export const REDUCED_THRESHOLD_MOBILE = 0.37; + +export const WS_CLOSURE_CODE = { + SUCCESS_CODE: 1000, + DEFAULT_ERROR_CODE: 4000, + FACE_FIT_TIMEOUT: 4001, + USER_CANCEL: 4003, + RUNTIME_ERROR: 4005, + USER_ERROR_DURING_CONNECTION: 4007, +}; diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/streamProvider.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/streamProvider.ts index 80e6435e249..1324a04b9c8 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/streamProvider.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/streamProvider.ts @@ -10,10 +10,10 @@ import { RekognitionStreamingClientConfig, StartFaceLivenessSessionCommand, } from '@aws-sdk/client-rekognitionstreaming'; -import { WebSocketFetchHandler } from '@aws-sdk/middleware-websocket'; import { VideoRecorder } from './videoRecorder'; import { getLivenessUserAgent } from '../../utils/platform'; import { AwsCredentialProvider } from '../types'; +import { CustomWebSocketFetchHandler } from './CustomWebSocketFetchHandler'; export interface StartLivenessStreamInput { sessionId: string; @@ -50,6 +50,15 @@ function isClientSessionInformationEvent( return (obj as ClientSessionInformationEvent).Challenge !== undefined; } +interface EndStreamWithCodeEvent { + type: string; + code: number; +} + +function isEndStreamWithCodeEvent(obj: unknown): obj is EndStreamWithCodeEvent { + return (obj as EndStreamWithCodeEvent).code !== undefined; +} + export class LivenessStreamProvider extends AmazonAIInterpretPredictionsProvider { public sessionId: string; public region: string; @@ -107,16 +116,17 @@ export class LivenessStreamProvider extends AmazonAIInterpretPredictionsProvider this.videoRecorder.dispatch(new Event('stopVideo')); } - public async endStream(): Promise { + public async endStreamWithCode(code?: number): Promise { if (this.videoRecorder.getState() === 'recording') { await this.stopVideo(); - this.dispatchStopVideoEvent(); } - if (!this._reader) { - return; - } - await this._reader.cancel(); - return this._reader.closed; + this.videoRecorder.dispatch( + new MessageEvent('endStreamWithCode', { + data: { code: code }, + }) + ); + + return; } private async init() { @@ -132,7 +142,9 @@ export class LivenessStreamProvider extends AmazonAIInterpretPredictionsProvider credentials, region: this.region, customUserAgent: `${getAmplifyUserAgent()} ${getLivenessUserAgent()}`, - requestHandler: new WebSocketFetchHandler({ connectionTimeout: 10_000 }), + requestHandler: new CustomWebSocketFetchHandler({ + connectionTimeout: 10_000, + }), }; if (ENDPOINT) { @@ -187,6 +199,13 @@ export class LivenessStreamProvider extends AmazonAIInterpretPredictionsProvider Challenge: value.Challenge, }, }; + } else if (isEndStreamWithCodeEvent(value)) { + yield { + VideoEvent: { + VideoChunk: [], + TimestampMillis: { closeCode: value.code }, + }, + }; } } }; diff --git a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/videoRecorder.ts b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/videoRecorder.ts index cbfab41951a..7664b274369 100644 --- a/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/videoRecorder.ts +++ b/packages/react-liveness/src/components/FaceLivenessDetector/service/utils/videoRecorder.ts @@ -9,7 +9,9 @@ export interface VideoRecorderOptions { * Helper wrapper class over the native MediaRecorder. */ export class VideoRecorder { - public videoStream!: ReadableStream; + public videoStream!: ReadableStream< + Blob | string | { type: string; code: number } + >; public recordingStartApiTimestamp: number | undefined; public recorderStartTimestamp: number | undefined; public recorderEndTimestamp: number | undefined; @@ -99,6 +101,15 @@ export class VideoRecorder { this._recorder.addEventListener('endStream', () => { controller.close(); }); + + this._recorder.addEventListener('endStreamWithCode', (e: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access + controller.enqueue({ + type: 'endStreamWithCode', + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + code: e.data.code as number, + }); + }); }, }); diff --git a/yarn.lock b/yarn.lock index d94d44a54c5..716cd5d72c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3656,7 +3656,7 @@ "@babel/code-frame@7.10.4": version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== dependencies: "@babel/highlight" "^7.10.4" @@ -3676,11 +3676,12 @@ "@babel/highlight" "^7.18.6" "@babel/code-frame@^7.5.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== + version "7.22.13" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz#e3c1c099402598483b7a8c46a721d1038803755e" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== dependencies: - "@babel/highlight" "^7.22.5" + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" "@babel/compat-data@^7.17.7", "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.21.4": version "7.21.4" @@ -4067,13 +4068,13 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== +"@babel/highlight@^7.22.13": + version "7.22.13" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz#9cda839e5d3be9ca9e8c26b6dd69e7548f0cbf16" + integrity sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ== dependencies: "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" + chalk "^2.4.2" js-tokens "^4.0.0" "@babel/parser@^7.1.0", "@babel/parser@^7.13.16", "@babel/parser@^7.14.0", "@babel/parser@^7.14.7", "@babel/parser@^7.16.4", "@babel/parser@^7.18.8", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4", "@babel/parser@^7.7.0": @@ -5793,7 +5794,7 @@ "@cypress/request@^2.88.11", "@cypress/request@^3.0.0": version "3.0.0" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-3.0.0.tgz#7f58dfda087615ed4e6aab1b25fffe7630d6dd85" + resolved "https://registry.npmjs.org/@cypress/request/-/request-3.0.0.tgz#7f58dfda087615ed4e6aab1b25fffe7630d6dd85" integrity sha512-GKFCqwZwMYmL3IBoNeR2MM1SnxRIGERsQOTWeQKoYBt2JLqcqiy7JXqO894FLrpjZYqGxW92MNwRH2BN56obdQ== dependencies: aws-sign2 "~0.7.0" @@ -6620,6 +6621,13 @@ dependencies: "@sinclair/typebox" "^0.25.16" +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz#430b5ce8a4e0044a7e3819663305a7b3091c8e03" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + "@jest/source-map@^26.6.2": version "26.6.2" resolved "https://registry.npmjs.org/@jest/source-map/-/source-map-26.6.2.tgz#29af5e1e2e324cafccc936f218309f54ab69d535" @@ -7991,6 +7999,11 @@ resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718" integrity sha512-XJfwUVUKDHF5ugKwIcxEgc9k8b7HbznCp6eUfWgu710hMPNIO4aw4/zB5RogDQz8nd6gyCDpU9O/m6qYEWY6yQ== +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + "@sinonjs/commons@^1.7.0": version "1.8.6" resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz#80c516a4dc264c2a69115e7578d62581ff455ed9" @@ -8105,6 +8118,51 @@ "@smithy/util-hex-encoding" "^1.0.2" tslib "^2.5.0" +"@smithy/eventstream-codec@^2.0.0", "@smithy/eventstream-codec@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-2.0.5.tgz#771f50657f1958db3e19b9f2726d62e2e0672546" + integrity sha512-iqR6OuOV3zbQK8uVs9o+9AxhVk8kW9NAxA71nugwUB+kTY9C35pUd0A5/m4PRT0Y0oIW7W4kgnSR3fdYXQjECw== + dependencies: + "@aws-crypto/crc32" "3.0.0" + "@smithy/types" "^2.2.2" + "@smithy/util-hex-encoding" "^2.0.0" + tslib "^2.5.0" + +"@smithy/eventstream-serde-browser@^2.0.4", "@smithy/eventstream-serde-browser@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-2.0.5.tgz#5f4d3d78a9fcb0a5a6f5b20f69141c8cc6b0ef6b" + integrity sha512-8NU51y94qFJbxL6SmvgWDfITHO/svvbAigkLYk2pckX17TGCSf4EXuGpGLliJp5Ljh5+vASC7mUH2jYX7MWBxA== + dependencies: + "@smithy/eventstream-serde-universal" "^2.0.5" + "@smithy/types" "^2.2.2" + tslib "^2.5.0" + +"@smithy/eventstream-serde-config-resolver@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-2.0.5.tgz#1e551a308dc2e91b8c732815077dbf99beb1300f" + integrity sha512-u3gvukRaTH4X6tsryuZ4T1WGIEP34fPaTTzphFDJe8GJz/k11oBW1MPnkcaucBMxLnObK9swCF85j5cp1Kj1oA== + dependencies: + "@smithy/types" "^2.2.2" + tslib "^2.5.0" + +"@smithy/eventstream-serde-node@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-2.0.5.tgz#ceea04afcef95caf0e4148c606721c1882a1d9b5" + integrity sha512-/C8jb+k/vKUBIe80D30vzjvRXlJf76kG2AJY7/NwiqWuD2usRuuDFCDaswXdVsSh9P1+FeaxZ48chsK10yDryQ== + dependencies: + "@smithy/eventstream-serde-universal" "^2.0.5" + "@smithy/types" "^2.2.2" + tslib "^2.5.0" + +"@smithy/eventstream-serde-universal@^2.0.5": + version "2.0.5" + resolved "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-2.0.5.tgz#5a656557575ee4ad69515434e45f19f7816f09f8" + integrity sha512-+vHvbQtlSVYTQ/20tNpVaKi0EpTR7E8GoEUHJypRZIRgiT03b3h2MAWk+SNaqMrCJrYG9vKLkJFzDylRlUvDWg== + dependencies: + "@smithy/eventstream-codec" "^2.0.5" + "@smithy/types" "^2.2.2" + tslib "^2.5.0" + "@smithy/fetch-http-handler@^1.0.1", "@smithy/fetch-http-handler@^1.0.2": version "1.0.2" resolved "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-1.0.2.tgz#4186ee6451de22e867f43c05236dcff43eca6e91" @@ -12713,13 +12771,13 @@ browserify-zlib@0.2.0, browserify-zlib@^0.2.0: pako "~1.0.5" browserslist@4.14.2, browserslist@4.16.6, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.15, browserslist@^4.20.0, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5, browserslist@^4.9.1: - version "4.21.8" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.8.tgz#db2498e1f4b80ed199c076248a094935860b6017" - integrity sha512-j+7xYe+v+q2Id9qbBeCI8WX5NmZSRe8es1+0xntD/+gaWXznP8tFEkv5IgSaHf5dS1YwVMbX/4W6m937mj+wQw== + version "4.21.10" + resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== dependencies: - caniuse-lite "^1.0.30001502" - electron-to-chromium "^1.4.428" - node-releases "^2.0.12" + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" update-browserslist-db "^1.0.11" bs-logger@0.x, bs-logger@^0.2.6: @@ -13091,10 +13149,15 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001228, can resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001478.tgz#0ef8a1cf8b16be47a0f9fc4ecfc952232724b32a" integrity sha512-gMhDyXGItTHipJj2ApIvR+iVB5hd0KP3svMWWXDvZOmjzJJassGLMfxRkQCSYgGd2gtdL/ReeiyvMSFD1Ss6Mw== -caniuse-lite@^1.0.30001314, caniuse-lite@^1.0.30001502: - version "1.0.30001502" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001502.tgz#f7e4a76eb1d2d585340f773767be1fefc118dca8" - integrity sha512-AZ+9tFXw1sS0o0jcpJQIXvFTOB/xGiQ4OQ2t98QX3NDn2EZTSRBC801gxrsGgViuq2ak/NLkNgSNEPtCr5lfKg== +caniuse-lite@^1.0.30001314: + version "1.0.30001524" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz#1e14bce4f43c41a7deaeb5ebfe86664fe8dadb80" + integrity sha512-Jj917pJtYg9HSJBF95HVX3Cdr89JUyLT4IZ8SvM5aDRni95swKgYi3TgYLH5hnGfPE/U1dg6IfZ50UsIlLkwSA== + +caniuse-lite@^1.0.30001517: + version "1.0.30001521" + resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001521.tgz#e9930cf499f7c1e80334b6c1fbca52e00d889e56" + integrity sha512-fnx1grfpEOvDGH+V17eccmNjucGUnCbP6KL+l5KqBIerp26WK/+RQ7CIDE37KGJjaPyqWXXlFUyKiWmvdNNKmQ== canvas@^2.11.2, canvas@^2.9.1: version "2.11.2" @@ -14217,7 +14280,7 @@ cross-fetch@3.1.5: cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== dependencies: path-key "^3.1.0" @@ -14523,14 +14586,14 @@ cssnano-preset-default@^6.0.0: cssnano-preset-simple@^4.0.0: version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-preset-simple/-/cssnano-preset-simple-4.0.0.tgz#f1aeed23d6d3cb99f946243189934ecfd3c887ee" + resolved "https://registry.npmjs.org/cssnano-preset-simple/-/cssnano-preset-simple-4.0.0.tgz#f1aeed23d6d3cb99f946243189934ecfd3c887ee" integrity sha512-hSqVEM4JOnp5dwAm8LNjkN8wlqZ0zbvbtUbh3bQqfZFIqCNZXoU2xpM/YC01k4TcJJHJemThkbNDaurZTgj5pw== dependencies: caniuse-lite "^1.0.30001314" cssnano-simple@3.0.0, cssnano-simple@3.0.2: version "3.0.2" - resolved "https://registry.yarnpkg.com/cssnano-simple/-/cssnano-simple-3.0.2.tgz#97c301cad6d8f121ac3361aa01cab68bbf8f9504" + resolved "https://registry.npmjs.org/cssnano-simple/-/cssnano-simple-3.0.2.tgz#97c301cad6d8f121ac3361aa01cab68bbf8f9504" integrity sha512-16jgh/dmhAhA529g2+s6vlxbIDvG9r/sT8y50y63ZBIXVxPqG1KpL3pjNOu9a9HWGJOGeG6j7TqexpHOvRGMkw== dependencies: cssnano-preset-simple "^4.0.0" @@ -15108,6 +15171,11 @@ diff-sequences@^29.4.3: resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.4.3.tgz#9314bc1fabe09267ffeca9cbafc457d8499a13f2" integrity sha512-ofrBgwpPhCD85kMKtE9RYFFq6OC1A89oW2vvgWZNCwxrUpRUILopY7lsYyMDSjc8g6U6aiO0Qubg6r4Wgt5ZnA== +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz#4deaf894d11407c51efc8418012f9e70b84ea921" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + diff@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -15459,15 +15527,15 @@ ee-first@1.1.1: ejs@^2.7.4, ejs@^3.1.7: version "3.1.9" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" + resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361" integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ== dependencies: jake "^10.8.5" -electron-to-chromium@^1.4.428: - version "1.4.429" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.429.tgz#055a2543ba8b1a847bebf521791715ec30f8d97b" - integrity sha512-COua8RvN548KwPFzKMrTjFbmDsQRgdi0zSAhmo70TwC1tfLOSqq8p09n+GkdF5buvzE/NEYn1dP3itbfhun9gg== +electron-to-chromium@^1.4.477: + version "1.4.494" + resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.494.tgz#588f7a3d19d32a31f3a7e05d81b61d95d25b1555" + integrity sha512-KF7wtsFFDu4ws1ZsSOt4pdmO1yWVNWCFtijVYZPUeW4SV7/hy/AESjLn/+qIWgq7mHscNOKAwN5AIM1+YAy+Ww== element-resize-detector@^1.2.1: version "1.2.4" @@ -15590,7 +15658,7 @@ enhanced-resolve@^5.10.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@^5.14.1: +enhanced-resolve@^5.15.0: version "5.15.0" resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== @@ -16233,7 +16301,7 @@ escape-html@^1.0.3, escape-html@~1.0.3: escape-string-regexp@2.0.0, escape-string-regexp@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== escape-string-regexp@^1.0.5: @@ -17081,7 +17149,18 @@ fast-diff@^1.1.2: resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== -fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: +fast-glob@^3.1.1: + version "3.3.1" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9: version "3.2.12" resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== @@ -17206,7 +17285,7 @@ file-uri-to-path@1.0.0: resolved "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -filelist@^1.0.1: +filelist@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== @@ -17215,7 +17294,7 @@ filelist@^1.0.1: filesize@6.1.0: version "6.1.0" - resolved "https://registry.yarnpkg.com/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" + resolved "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00" integrity sha512-LpCHtPQ3sFx67z+uh2HnSyWSLLu5Jxo21795uRDuar/EOuYWXib5EmPaGIBuSnRqH2IODiKA2k5re/K9OnN/Yg== fill-range@^4.0.0: @@ -17320,7 +17399,7 @@ find-root@^1.1.0: find-up@4.1.0, find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== dependencies: locate-path "^5.0.0" @@ -17423,7 +17502,7 @@ forever-agent@~0.6.1: fork-ts-checker-webpack-plugin@4.1.6: version "4.1.6" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" + resolved "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-4.1.6.tgz#5055c703febcf37fa06405d400c122b905167fc5" integrity sha512-DUxuQaKoqfNne8iikd14SAkh5uw4+8vNifp6gmA73yYNS6ywLIWSLD/n/mBzHQRpW3J7rbATEakmiA8JvkTyZw== dependencies: "@babel/code-frame" "^7.5.5" @@ -17988,7 +18067,7 @@ globalthis@^1.0.0, globalthis@^1.0.3: globby@11.0.1: version "11.0.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" + resolved "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz#9a2bf107a068f3ffeabc49ad702c79ede8cfd357" integrity sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ== dependencies: array-union "^2.1.0" @@ -18384,7 +18463,7 @@ hermes-profile-transformer@^0.0.6: highlight.js@^10.4.1, highlight.js@~9.13.0, highlight.js@~9.18.2: version "10.7.3" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" + resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A== hmac-drbg@^1.0.1: @@ -18708,7 +18787,7 @@ ignore@^4.0.6: ignore@^5.0.5, ignore@^5.1.4, ignore@^5.2.0: version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== image-size@1.0.0: @@ -18730,7 +18809,7 @@ image-size@~0.5.0: immer@8.0.1, immer@9.0.6, immer@^9.0.6: version "9.0.21" - resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" + resolved "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176" integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA== immutable@^4.0.0: @@ -19611,14 +19690,14 @@ iterate-value@^1.0.2: iterate-iterator "^1.0.1" jake@^10.8.5: - version "10.8.5" - resolved "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz#f2183d2c59382cb274226034543b9c03b8164c46" - integrity sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw== + version "10.8.7" + resolved "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f" + integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w== dependencies: async "^3.2.3" chalk "^4.0.2" - filelist "^1.0.1" - minimatch "^3.0.4" + filelist "^1.0.4" + minimatch "^3.1.2" jasmine-core@^3.6.0: version "3.99.1" @@ -19879,6 +19958,16 @@ jest-diff@^27.5.1: jest-get-type "^27.5.1" pretty-format "^27.5.1" +jest-diff@^29.2.0: + version "29.6.4" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz#85aaa6c92a79ae8cd9a54ebae8d5b6d9a513314a" + integrity sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.6.3" + jest-diff@^29.5.0: version "29.5.0" resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.5.0.tgz#e0d83a58eb5451dcc1fa61b1c3ee4e8f5a290d63" @@ -20034,6 +20123,11 @@ jest-get-type@^29.4.3: resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.4.3.tgz#1ab7a5207c995161100b5187159ca82dd48b3dd5" integrity sha512-J5Xez4nRRMjk8emnTpWrlkyb9pfRQQanDrvWHhsR1+VUfbwxi30eVcZFlcdGInRibU4G5LwHXpI7IRHU0CY+gg== +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz#36f499fdcea197c1045a127319c0481723908fd1" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + jest-haste-map@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz#dd7e60fe7dc0e9f911a23d79c5ff7fb5c2cafeaa" @@ -20792,6 +20886,14 @@ jest-watcher@^29.5.0: jest-util "^29.5.0" string-length "^4.0.1" +jest-websocket-mock@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/jest-websocket-mock/-/jest-websocket-mock-2.4.1.tgz#e8ed6001cbeff7739b9ee4a74f5937ec1c339408" + integrity sha512-M/T3U5qYGoX69846gj1fFUCx8GeaOQRly2zu9mUzxDKU4iXceBwn4AHxxeCc7W6Wn98WxQNtfO+0gr7LYUGrzg== + dependencies: + jest-diff "^29.2.0" + mock-socket "^9.1.0" + jest-when@^3.5.1: version "3.5.2" resolved "https://registry.npmjs.org/jest-when/-/jest-when-3.5.2.tgz#651d8a73751ab55c29698d388dffd3460cd52bdc" @@ -21462,7 +21564,7 @@ loader-runner@^4.2.0: loader-utils@1.2.3, loader-utils@2.0.0, loader-utils@2.0.4, loader-utils@3.2.1, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^2.0.0: version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" + resolved "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== dependencies: big.js "^5.2.2" @@ -23118,7 +23220,7 @@ minimalistic-crypto-utils@^1.0.1: minimatch@3.0.4, minimatch@3.0.5, minimatch@5.1.0: version "3.0.5" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz#4da8f1290ee0f0f8e83d60ca69f8f134068604a3" integrity sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw== dependencies: brace-expansion "^1.1.7" @@ -23316,6 +23418,11 @@ mkdirp@^2.1.5: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19" integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A== +mock-socket@^9.1.0, mock-socket@^9.2.1: + version "9.2.1" + resolved "https://registry.npmjs.org/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282" + integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag== + module-alias@^2.2.2: version "2.2.3" resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.3.tgz#ec2e85c68973bda6ab71ce7c93b763ec96053221" @@ -23708,7 +23815,7 @@ node-fetch@^2.2.0, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1, node node-forge@1.3.0, node-forge@^1: version "1.3.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2" integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA== node-gyp-build@^4.2.2: @@ -23799,10 +23906,10 @@ node-notifier@^8.0.0: uuid "^8.3.0" which "^2.0.2" -node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +node-releases@^2.0.13: + version "2.0.13" + resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" + integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== node-stream-zip@^1.9.1: version "1.15.0" @@ -24240,7 +24347,7 @@ open@^6.2.0: open@^7.0.0, open@^7.0.2: version "7.4.2" - resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== dependencies: is-docker "^2.0.0" @@ -24860,7 +24967,7 @@ pkg-dir@^3.0.0: pkg-up@3.1.0, pkg-up@^3.1.0: version "3.1.0" - resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" + resolved "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz#100ec235cc150e4fd42519412596a28512a0def5" integrity sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA== dependencies: find-up "^3.0.0" @@ -25854,6 +25961,15 @@ pretty-format@^29.0.0, pretty-format@^29.5.0: ansi-styles "^5.0.0" react-is "^18.0.0" +pretty-format@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz#d432bb4f1ca6f9463410c3fb25a0ba88e594ace7" + integrity sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" @@ -25871,7 +25987,7 @@ prism-react-renderer@^1.2.1: prismjs@^1.25.0, prismjs@^1.8.4, prismjs@~1.17.0, prismjs@~1.23.0: version "1.29.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== proc-log@^2.0.0, proc-log@^2.0.1: @@ -25947,7 +26063,7 @@ promise@^8.2.0: prompts@2.4.0: version "2.4.0" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" + resolved "https://registry.npmjs.org/prompts/-/prompts-2.4.0.tgz#4aa5de0723a231d1ee9121c40fdf663df73f61d7" integrity sha512-awZAKrk3vN6CroQukBL+R9051a4R3zCZBlJm/HBfrSZ8iTpYix3VX1vU4mveiLpiwmOJT4wokTF9m6HUk4KqWQ== dependencies: kleur "^3.0.3" @@ -26359,7 +26475,7 @@ react-copy-to-clipboard@^5.0.4, react-copy-to-clipboard@^5.1.0: react-dev-utils@^11.0.4, react-dev-utils@^9.0.0: version "11.0.4" - resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" + resolved "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" integrity sha512-dx0LvIGHcOPtKbeiSUM4jqpBl3TcY7CDjZdfOIcKeznE7BWr9dg0iPG90G5yfVQ+p/rGNMXdbfStvzQZEVEi4A== dependencies: "@babel/code-frame" "7.10.4" @@ -26431,7 +26547,7 @@ react-error-boundary@^3.1.0: react-error-overlay@^6.0.9: version "6.0.11" - resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" + resolved "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb" integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg== react-fast-compare@^3.2.0: @@ -27873,7 +27989,7 @@ schema-utils@^3.0.0, schema-utils@^3.1.0, schema-utils@^3.1.1: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^3.1.2: +schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== @@ -27936,7 +28052,7 @@ semver-dsl@^1.0.1: semver@7.3.8, semver@7.5.4, semver@7.x, semver@^7.0.0, semver@^7.1.1, semver@^7.2.1, semver@^7.3.2, semver@^7.3.5, semver@^7.3.6, semver@^7.3.7, semver@^7.5.3: version "7.5.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" @@ -28156,7 +28272,7 @@ shebang-regex@^3.0.0: shell-quote@1.7.2, shell-quote@1.7.3, shell-quote@^1.6.1, shell-quote@^1.7.2, shell-quote@^1.7.3: version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== shelljs@^0.8.3: @@ -29743,7 +29859,7 @@ trim-lines@^3.0.0: trim-newlines@3.0.1, trim-newlines@^2.0.0, trim-newlines@^3.0.0: version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== trough@^1.0.0: @@ -31146,9 +31262,9 @@ webpack-virtual-modules@^0.5.0: integrity sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw== webpack@5.76.1, webpack@^5.76.0, webpack@^5.88.0: - version "5.86.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.86.0.tgz#b0eb81794b62aee0b7e7eb8c5073495217d9fc6d" - integrity sha512-3BOvworZ8SO/D4GVP+GoRC3fVeg5MO4vzmq8TJJEkdmopxyazGDxN8ClqN12uzrZW9Tv8EED8v5VSb6Sqyi0pg== + version "5.88.2" + resolved "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" + integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" @@ -31159,7 +31275,7 @@ webpack@5.76.1, webpack@^5.76.0, webpack@^5.88.0: acorn-import-assertions "^1.9.0" browserslist "^4.14.5" chrome-trace-event "^1.0.2" - enhanced-resolve "^5.14.1" + enhanced-resolve "^5.15.0" es-module-lexer "^1.2.1" eslint-scope "5.1.1" events "^3.2.0" @@ -31169,7 +31285,7 @@ webpack@5.76.1, webpack@^5.76.0, webpack@^5.88.0: loader-runner "^4.2.0" mime-types "^2.1.27" neo-async "^2.6.2" - schema-utils "^3.1.2" + schema-utils "^3.2.0" tapable "^2.1.1" terser-webpack-plugin "^5.3.7" watchpack "^2.4.0" @@ -31607,7 +31723,7 @@ yallist@^4.0.0: yaml@1.10.2, yaml@2.2.2, yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2, yaml@^2.1.1, yaml@^2.2.1, yaml@^2.2.2: version "2.2.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz#ec551ef37326e6d42872dad1970300f8eb83a073" integrity sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA== yargs-parser@20.x, yargs-parser@^20.2.2: