From 48518cf8b6fdd084d584b52c05c046618fe4931a Mon Sep 17 00:00:00 2001 From: yolossn Date: Fri, 3 May 2024 11:11:15 +0530 Subject: [PATCH] wip --- app/electron/main.ts | 139 +- app/electron/preload.ts | 9 + frontend/src/components/App/runCommand.ts | 38 + frontend/src/plugin/registry.tsx | 4 +- plugins/headlamp-plugin/package-lock.json | 557 ++------ plugins/headlamp-plugin/package.json | 5 +- .../plugin-management-utils.js | 1266 ++++++++--------- plugins/headlamp-plugin/plugin-management.js | 416 ++++++ .../headlamp-plugin/plugin-management.test.js | 79 + 9 files changed, 1354 insertions(+), 1159 deletions(-) create mode 100644 plugins/headlamp-plugin/plugin-management.js create mode 100644 plugins/headlamp-plugin/plugin-management.test.js diff --git a/app/electron/main.ts b/app/electron/main.ts index fc983f1313d..d76d52673c5 100644 --- a/app/electron/main.ts +++ b/app/electron/main.ts @@ -1,5 +1,5 @@ import 'regenerator-runtime/runtime'; -import { ChildProcess, ChildProcessWithoutNullStreams, execSync, fork, spawn } from 'child_process'; +import { ChildProcessWithoutNullStreams, execSync, spawn } from 'child_process'; import { randomBytes } from 'crypto'; import dotenv from 'dotenv'; import { app, BrowserWindow, dialog, ipcMain, Menu, MenuItem, screen, shell } from 'electron'; @@ -11,6 +11,7 @@ import open from 'open'; import path from 'path'; import url from 'url'; import yargs from 'yargs'; +import PluginManager from '../../plugins/headlamp-plugin/plugin-management'; import i18n from './i18next.config'; import windowSize from './windowSize'; @@ -498,6 +499,87 @@ function startElecron() { console.log('Check for updates: ', shouldCheckForUpdates); + class PluginEventListeners { + private pluginManager: PluginManager; + private abortControllers: {}; + + constructor() { + this.pluginManager = new PluginManager(); + this.abortControllers = {}; + } + + setupEventHandlers() { + ipcMain.on('install-plugin', (event, URL, destinationFolder, headlampVersion) => { + const controller = new AbortController(); + const cancelSignal = controller.signal; + this.abortControllers[URL] = controller; + + this.pluginManager.install( + URL, + destinationFolder, + headlampVersion, + progressData => { + if (cancelSignal.aborted) { + console.log('aborting install plugin', URL); + return; + } + event.sender.send('install-plugin-progress', progressData); + if (progressData.type === 'success' || progressData.type === 'error') { + delete this.abortControllers[URL]; + } + }, + cancelSignal + ); + }); + + ipcMain.on('update-plugin', (event, pluginName, destinationFolder, headlampVersion) => { + const controller = new AbortController(); + const cancelSignal = controller.signal; + this.abortControllers[pluginName] = controller; + + console.log('in update plugin,', pluginName, destinationFolder, headlampVersion); + + this.pluginManager.update( + pluginName, + destinationFolder, + headlampVersion, + progressData => { + if (cancelSignal.aborted) { + console.log('aborting update plugin', pluginName); + return; + } + event.sender.send('update-plugin-progress', progressData); + if (progressData.type === 'success' || progressData.type === 'error') { + delete this.abortControllers[pluginName]; + } + }, + cancelSignal + ); + }); + + ipcMain.on('uninstall-plugin', (event, pluginName) => { + this.pluginManager.uninstall(pluginName, undefined, progressData => { + event.sender.send('uninstall-plugin-progress', progressData); + }); + }); + + ipcMain.on('list-plugins', (event, folder) => { + this.pluginManager.list(folder, progressData => { + event.sender.send('list-plugins', progressData); + }); + }); + + ipcMain.on('cancel-plugin-process', (event, identifier) => { + console.log('got cancel request', identifier, this.abortControllers); + if (this.abortControllers[identifier]) { + const controller = this.abortControllers[identifier]; + controller.abort(); + delete this.abortControllers[identifier]; + } + }); + } + } + async function createWindow() { let frontendPath = ''; if (isDev) { @@ -647,6 +729,8 @@ function startElecron() { } }); + new PluginEventListeners().setupEventHandlers(); + /** * Data sent from the renderer process when a 'run-command' event is emitted. */ @@ -707,59 +791,6 @@ function startElecron() { ipcMain.on('run-command', handleRunCommand); - /** - * Represents the arguments for a plugin command. - */ - interface PluginCommandArgs { - id: string; - command: string; - args: string[]; - } - - /** - * Handles a plugin commands from the renderer process. - * - * Forks the current electron process to run the plugin command and sends - * 'plugin-command-stdout', 'plugin-command-stderr', and 'plugin-command-exit' - * events back to the renderer process with the plugin command's output and exit code. - * - * - * @param {IpcMainEvent} event - The IPC event object representing the incoming command. - * @param {PluginCommandArgs} data - The data containing plugin command details. - * @returns {void} - */ - function handlePluginCommand(event: IpcMainEvent, data: PluginCommandArgs) { - const { id, command, args } = data; - - const includesJFlag = args.includes('-j'); - const argsWithJFlag = includesJFlag ? args : [...args, '-j']; - - const child: ChildProcess = fork('plugin-management.js', [command, ...argsWithJFlag], { - env: { - ELECTRON_RUN_AS_NODE: '1', - }, - silent: true, - }); - - if (child.stdout !== null) { - child.stdout.on('data', (data: string | Buffer) => { - event.sender.send('plugin-command-stdout', id, data.toString()); - }); - } - - if (child.stderr !== null) { - child.stderr.on('data', (data: string | Buffer) => { - event.sender.send('plugin-command-stderr', id, data.toString()); - }); - } - - child.on('exit', (code: number | null) => { - event.sender.send('plugin-command-exit', id, code); - }); - } - - ipcMain.on('plugin-command', handlePluginCommand); - if (!useExternalServer) { const runningHeadlamp = await getRunningHeadlampPIDs(); let shouldWaitForKill = true; diff --git a/app/electron/preload.ts b/app/electron/preload.ts index c1f93290d30..0f5b2fd1868 100644 --- a/app/electron/preload.ts +++ b/app/electron/preload.ts @@ -12,6 +12,11 @@ contextBridge.exposeInMainWorld('desktopApi', { 'pluginsLoaded', 'run-command', 'plugin-command', + 'install-plugin', + 'update-plugin', + 'uninstall-plugin', + 'list-plugins', + 'cancel-plugin-process', ]; if (validChannels.includes(channel)) { ipcRenderer.send(channel, data); @@ -29,6 +34,10 @@ contextBridge.exposeInMainWorld('desktopApi', { 'plugin-command-stdout', 'plugin-command-stderr', 'plugin-command-exit', + 'install-plugin-progress', + 'update-plugin-progress', + 'uninstall-plugin-progress', + 'list-plugins', ]; if (validChannels.includes(channel)) { // Deliberately strip event as it includes `sender` diff --git a/frontend/src/components/App/runCommand.ts b/frontend/src/components/App/runCommand.ts index dac359b19c4..0de30cbd00b 100644 --- a/frontend/src/components/App/runCommand.ts +++ b/frontend/src/components/App/runCommand.ts @@ -160,3 +160,41 @@ export function runPluginCommand( }, }; } + +export class PluginManagerWrapper { + static install(URL: string, progressCallback: (progress: {}) => void) { + window.desktopApi.send('install-plugin', URL); + + window.desktopApi.receive(`install-plugin-progress`, (progress: {}) => { + progressCallback(progress); + }); + } + + static update(name: string, progressCallback: (progress: {}) => void) { + window.desktopApi.send('update-plugin', name); + + window.desktopApi.receive(`update-plugin-progress`, (progress: {}) => { + progressCallback(progress); + }); + } + + static uninstall(name: string, progressCallback: (progress: {}) => void) { + window.desktopApi.send('uninstall-plugin', name, undefined); + + window.desktopApi.receive(`uninstall-plugin-progress`, (progress: {}) => { + progressCallback(progress); + }); + } + + static list(progressCallback: (progress: {}) => void) { + window.desktopApi.send('list-plugins'); + + window.desktopApi.receive(`list-plugins`, (progress: {}) => { + progressCallback(progress); + }); + } + + static cancel(identifier: string) { + window.desktopApi.send('cancel-plugin-process', identifier); + } +} diff --git a/frontend/src/plugin/registry.tsx b/frontend/src/plugin/registry.tsx index d39b57058f5..7a192a4fef0 100644 --- a/frontend/src/plugin/registry.tsx +++ b/frontend/src/plugin/registry.tsx @@ -2,7 +2,7 @@ import { has } from 'lodash'; import React from 'react'; import { AppLogoProps, AppLogoType } from '../components/App/AppLogo'; import { runCommand } from '../components/App/runCommand'; -import { runPluginCommand } from '../components/App/runCommand'; +import { PluginManagerWrapper } from '../components/App/runCommand'; import { setBrandingAppLogoComponent } from '../components/App/themeSlice'; import { ClusterChooserProps, ClusterChooserType } from '../components/cluster/ClusterChooser'; import { @@ -686,5 +686,5 @@ export { DefaultDetailsViewSection, getHeadlampAPIHeaders, runCommand, - runPluginCommand, + PluginManagerWrapper, }; diff --git a/plugins/headlamp-plugin/package-lock.json b/plugins/headlamp-plugin/package-lock.json index bda82166ce0..f8dbf27290d 100644 --- a/plugins/headlamp-plugin/package-lock.json +++ b/plugins/headlamp-plugin/package-lock.json @@ -155,6 +155,9 @@ }, "bin": { "headlamp-plugin": "bin/headlamp-plugin.js" + }, + "devDependencies": { + "jest": "^29.7.0" } }, "node_modules/@adobe/css-tools": { @@ -3412,7 +3415,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -3459,7 +3461,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -3485,7 +3486,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3500,7 +3500,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3516,7 +3515,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3527,20 +3525,17 @@ "node_modules/@jest/core/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/@jest/core/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@jest/core/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -3549,7 +3544,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -3574,7 +3568,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -3583,7 +3576,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -3603,7 +3595,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -3620,7 +3611,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -3635,7 +3625,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3650,7 +3639,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -3664,7 +3652,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -3675,14 +3662,12 @@ "node_modules/@jest/core/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@jest/core/node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true, "engines": { "node": ">=10" } @@ -3691,7 +3676,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -3700,7 +3684,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -3712,7 +3695,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -3725,7 +3707,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "peer": true, "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -3740,7 +3721,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "peer": true, "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -3764,7 +3744,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -3781,7 +3760,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -3796,7 +3774,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "peer": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -3839,7 +3816,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -3865,7 +3841,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -3880,7 +3855,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -3896,7 +3870,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -3907,20 +3880,17 @@ "node_modules/@jest/reporters/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/@jest/reporters/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/@jest/reporters/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -3929,7 +3899,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "peer": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -3945,7 +3914,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -3970,7 +3938,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -3979,7 +3946,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -3994,7 +3960,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4009,7 +3974,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -4018,7 +3982,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -4030,7 +3993,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -4054,7 +4016,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -4082,7 +4043,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -4097,7 +4057,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -4106,7 +4065,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -4131,7 +4089,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -4140,7 +4097,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -4155,7 +4111,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -4164,7 +4119,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -6055,7 +6009,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "peer": true, "dependencies": { "type-detect": "4.0.8" } @@ -6064,7 +6017,6 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.0" } @@ -13015,7 +12967,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -13036,7 +12987,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -13051,7 +13001,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -13067,7 +13016,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -13078,14 +13026,12 @@ "node_modules/create-jest/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/create-jest/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -13094,7 +13040,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14327,7 +14272,6 @@ "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "peer": true, "engines": { "node": ">=12" }, @@ -18786,7 +18730,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -18821,7 +18764,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "peer": true, "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -18835,7 +18777,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -18850,7 +18791,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -18881,7 +18821,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -18896,7 +18835,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -18912,7 +18850,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -18923,14 +18860,12 @@ "node_modules/jest-circus/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-circus/node_modules/dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "peer": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -18944,7 +18879,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -18953,7 +18887,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -18968,7 +18901,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -18982,7 +18914,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -18993,14 +18924,12 @@ "node_modules/jest-circus/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-circus/node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -19009,7 +18938,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -19021,7 +18949,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -19054,7 +18981,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -19069,7 +18995,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -19085,7 +19010,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -19099,7 +19023,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -19110,14 +19033,12 @@ "node_modules/jest-cli/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-cli/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -19126,7 +19047,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -19143,7 +19063,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -19157,7 +19076,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -19168,14 +19086,12 @@ "node_modules/jest-cli/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-cli/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -19187,7 +19103,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -19205,7 +19120,6 @@ "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "peer": true, "engines": { "node": ">=12" } @@ -19214,7 +19128,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -19259,7 +19172,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -19285,7 +19197,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -19300,7 +19211,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "peer": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -19321,7 +19231,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "peer": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -19336,7 +19245,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "peer": true, "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -19352,7 +19260,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -19368,7 +19275,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -19379,20 +19285,17 @@ "node_modules/jest-config/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-config/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/jest-config/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -19401,7 +19304,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -19426,7 +19328,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -19435,7 +19336,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -19455,7 +19355,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -19472,7 +19371,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -19487,7 +19385,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -19502,7 +19399,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -19516,7 +19412,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -19527,14 +19422,12 @@ "node_modules/jest-config/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-config/node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true, "engines": { "node": ">=10" } @@ -19543,7 +19436,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -19552,7 +19444,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -19564,7 +19455,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -19684,7 +19574,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "peer": true, "dependencies": { "detect-newline": "^3.0.0" }, @@ -19696,7 +19585,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -19712,7 +19600,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -19727,7 +19614,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -19743,7 +19629,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -19754,14 +19639,12 @@ "node_modules/jest-each/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-each/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -19770,7 +19653,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -19784,7 +19666,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -19795,14 +19676,12 @@ "node_modules/jest-each/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-each/node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -20019,7 +19898,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -20619,7 +20497,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "peer": true, "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" @@ -20632,7 +20509,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -20644,7 +20520,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -20657,8 +20532,7 @@ "node_modules/jest-leak-detector/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-matcher-utils": { "version": "29.7.0", @@ -20891,7 +20765,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -20949,7 +20822,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "peer": true, "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" @@ -20962,7 +20834,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -21082,7 +20953,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "peer": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -21114,7 +20984,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -21140,7 +21009,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -21155,7 +21023,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -21171,7 +21038,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -21182,20 +21048,17 @@ "node_modules/jest-runner/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-runner/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/jest-runner/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -21204,7 +21067,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -21229,7 +21091,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -21238,7 +21099,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -21258,7 +21118,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -21275,7 +21134,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -21290,7 +21148,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -21305,7 +21162,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -21320,7 +21176,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -21334,7 +21189,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -21345,14 +21199,12 @@ "node_modules/jest-runner/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-runner/node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true, "engines": { "node": ">=10" } @@ -21361,7 +21213,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -21370,7 +21221,6 @@ "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -21380,7 +21230,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -21392,7 +21241,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -21405,7 +21253,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "peer": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -21438,7 +21285,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -21464,7 +21310,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -21479,7 +21324,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -21495,7 +21339,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -21506,20 +21349,17 @@ "node_modules/jest-runtime/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-runtime/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/jest-runtime/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -21528,7 +21368,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -21553,7 +21392,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -21562,7 +21400,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -21582,7 +21419,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -21599,7 +21435,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -21614,7 +21449,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -21629,7 +21463,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -21643,7 +21476,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true, "engines": { "node": ">=10" }, @@ -21654,14 +21486,12 @@ "node_modules/jest-runtime/node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/jest-runtime/node_modules/resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true, "engines": { "node": ">=10" } @@ -21670,7 +21500,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true, "engines": { "node": ">=8" } @@ -21679,7 +21508,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "peer": true, "engines": { "node": ">=8" } @@ -21688,7 +21516,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -21700,7 +21527,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -22160,7 +21986,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "peer": true, "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", @@ -22179,7 +22004,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -22194,7 +22018,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -22210,7 +22033,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -22221,14 +22043,12 @@ "node_modules/jest-watcher/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/jest-watcher/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true, "engines": { "node": ">=8" } @@ -22237,7 +22057,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -27366,8 +27185,7 @@ "type": "opencollective", "url": "https://opencollective.com/fast-check" } - ], - "peer": true + ] }, "node_modules/q": { "version": "1.5.1", @@ -33741,7 +33559,6 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -33754,8 +33571,7 @@ "node_modules/v8-to-istanbul/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "node_modules/validate-npm-package-license": { "version": "3.0.4", @@ -37352,7 +37168,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "peer": true, "requires": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -37388,7 +37203,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -37411,7 +37225,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -37420,7 +37233,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -37430,7 +37242,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -37438,26 +37249,22 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -37476,14 +37283,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -37500,7 +37305,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -37514,7 +37318,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -37526,7 +37329,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -37537,7 +37339,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -37547,34 +37348,29 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -37583,7 +37379,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -37595,7 +37390,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "peer": true, "requires": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -37607,7 +37401,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "peer": true, "requires": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -37625,7 +37418,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -37639,7 +37431,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "peer": true, "requires": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -37651,7 +37442,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "peer": true, "requires": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -37683,7 +37473,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -37706,7 +37495,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -37715,7 +37503,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -37725,7 +37512,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -37733,26 +37519,22 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "istanbul-lib-instrument": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "peer": true, "requires": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -37765,7 +37547,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -37784,14 +37565,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -37803,7 +37582,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -37813,14 +37591,12 @@ "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -37829,7 +37605,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -37849,7 +37624,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "peer": true, "requires": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -37871,7 +37645,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "peer": true, "requires": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -37882,14 +37655,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -37908,14 +37679,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -37926,14 +37695,12 @@ "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -39104,7 +38871,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "peer": true, "requires": { "type-detect": "4.0.8" } @@ -39113,7 +38879,6 @@ "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "peer": true, "requires": { "@sinonjs/commons": "^3.0.0" } @@ -44302,7 +44067,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -44317,7 +44081,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -44326,7 +44089,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -44336,7 +44098,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -44344,20 +44105,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -45286,8 +45044,7 @@ "emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "peer": true + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==" }, "emoji-regex": { "version": "9.2.2", @@ -48636,7 +48393,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "peer": true, "requires": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -48657,7 +48413,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "peer": true, "requires": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -48668,7 +48423,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "requires": { "yocto-queue": "^0.1.0" } @@ -48679,7 +48433,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "peer": true, "requires": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -48707,7 +48460,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -48716,7 +48468,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -48726,7 +48477,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -48734,27 +48484,23 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "dedent": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "peer": true, "requires": {} }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "requires": { "yocto-queue": "^0.1.0" } @@ -48763,7 +48509,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -48773,28 +48518,24 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -48805,7 +48546,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "peer": true, "requires": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -48824,7 +48564,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -48833,7 +48572,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -48843,7 +48581,6 @@ "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "peer": true, "requires": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -48854,7 +48591,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -48862,20 +48598,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -48889,7 +48622,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -48899,22 +48631,19 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -48923,7 +48652,6 @@ "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "peer": true, "requires": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -48937,8 +48665,7 @@ "yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "peer": true + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" } } }, @@ -48946,7 +48673,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -48976,7 +48702,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -48999,7 +48724,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -49008,7 +48732,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "peer": true, "requires": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -49023,7 +48746,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "peer": true, "requires": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -49035,7 +48757,6 @@ "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "peer": true, "requires": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -49045,7 +48766,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -49055,7 +48775,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -49063,26 +48782,22 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -49101,14 +48816,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -49125,7 +48838,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -49139,7 +48851,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -49151,7 +48862,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -49162,7 +48872,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -49172,34 +48881,29 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -49208,7 +48912,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -49298,7 +49001,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "peer": true, "requires": { "detect-newline": "^3.0.0" } @@ -49307,7 +49009,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -49320,7 +49021,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -49329,7 +49029,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -49339,7 +49038,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -49347,20 +49045,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -49370,22 +49065,19 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -49558,7 +49250,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "peer": true, "requires": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -50029,7 +49720,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "peer": true, "requires": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" @@ -50038,14 +49728,12 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" }, "pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -50055,8 +49743,7 @@ "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" } } }, @@ -50230,7 +49917,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -50352,7 +50038,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "peer": true, "requires": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" @@ -50361,8 +50046,7 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" } } }, @@ -50370,7 +50054,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "peer": true, "requires": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", @@ -50399,7 +50082,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -50422,7 +50104,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -50431,7 +50112,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -50441,7 +50121,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -50449,26 +50128,22 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -50487,14 +50162,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -50511,7 +50184,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -50525,7 +50197,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -50537,7 +50208,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -50548,7 +50218,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "peer": true, "requires": { "yocto-queue": "^0.1.0" } @@ -50557,7 +50226,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -50567,34 +50235,29 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "peer": true, "requires": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -50604,7 +50267,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -50613,7 +50275,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -50625,7 +50286,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "peer": true, "requires": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", @@ -50655,7 +50315,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "peer": true, "requires": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -50678,7 +50337,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -50687,7 +50345,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -50697,7 +50354,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -50705,26 +50361,22 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", @@ -50743,14 +50395,12 @@ "jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "peer": true + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==" }, "jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "peer": true, "requires": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", @@ -50767,7 +50417,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "peer": true, "requires": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -50781,7 +50430,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "peer": true, "requires": { "@types/node": "*", "jest-util": "^29.7.0", @@ -50793,7 +50441,6 @@ "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -50804,7 +50451,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "peer": true, "requires": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -50814,40 +50460,34 @@ "ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "peer": true + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==" } } }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "peer": true + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "resolve.exports": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "peer": true + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==" }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "peer": true + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, "strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "peer": true + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -50856,7 +50496,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "peer": true, "requires": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -51206,7 +50845,6 @@ "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "peer": true, "requires": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", @@ -51222,7 +50860,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "peer": true, "requires": { "color-convert": "^2.0.1" } @@ -51231,7 +50868,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "peer": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -51241,7 +50877,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "peer": true, "requires": { "color-name": "~1.1.4" } @@ -51249,20 +50884,17 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "peer": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "peer": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "peer": true, "requires": { "has-flag": "^4.0.0" } @@ -54791,8 +54423,7 @@ "pure-rand": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "peer": true + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==" }, "q": { "version": "1.5.1", @@ -59512,7 +59143,6 @@ "version": "9.2.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", - "peer": true, "requires": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -59522,8 +59152,7 @@ "convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "peer": true + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" } } }, diff --git a/plugins/headlamp-plugin/package.json b/plugins/headlamp-plugin/package.json index 912c8f8e86a..58ba8cedac9 100644 --- a/plugins/headlamp-plugin/package.json +++ b/plugins/headlamp-plugin/package.json @@ -174,5 +174,8 @@ "plugins" ], "author": "Kinvolk GmbH", - "license": "Apache 2.0" + "license": "Apache 2.0", + "devDependencies": { + "jest": "^29.7.0" + } } diff --git a/plugins/headlamp-plugin/plugin-management-utils.js b/plugins/headlamp-plugin/plugin-management-utils.js index 16089d10d20..33d27fa5797 100644 --- a/plugins/headlamp-plugin/plugin-management-utils.js +++ b/plugins/headlamp-plugin/plugin-management-utils.js @@ -1,638 +1,628 @@ -const { table, getBorderCharacters } = require('table'); -const tar = require('tar'); -const zlib = require('zlib'); -const envPaths = require('env-paths'); -const fs = require('fs-extra'); -const path = require('path'); -const fetch = require('node-fetch').default; -const crypto = require('crypto'); -const stream = require('stream'); -const semver = require('semver'); - -/** - * Returns the default directory where Headlamp plugins are installed. - * If the data path exists, it is used as the base directory. - * Otherwise, the config path is used as the base directory. - * The 'plugins' subdirectory of the base directory is returned. - * - * @returns {string} The path to the default plugins directory. - */ -function defaultPluginsDir() { - const paths = envPaths('Headlamp', { suffix: '' }); - const configDir = fs.existsSync(paths.data) ? paths.data : paths.config; - return path.join(configDir, 'plugins'); -} - -/** - * Checks if a given folder is a valid Headlamp plugin folder. - * A valid plugin folder must exist, contain 'main.js' and 'package.json' files, - * and the 'package.json' file must have 'isManagedByHeadlampPlugin' set to true. - * - * @param {string} folder - The path to the folder to check. - * @returns {boolean} True if the folder is a valid Headlamp plugin folder, false otherwise. - */ -function checkValidPluginFolder(folder) { - if (!fs.existsSync(folder)) { - return false; - } - // Check if the folder contains main.js and package.json - const mainJsPath = path.join(folder, 'main.js'); - const packageJsonPath = path.join(folder, 'package.json'); - if (!fs.existsSync(mainJsPath) || !fs.existsSync(packageJsonPath)) { - return false; - } - - // Read package.json and check isManagedByHeadlampPlugin is set to true - const packageJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - if (packageJSON.isManagedByHeadlampPlugin) { - return true; - } - return false; -} - -/** - * Lists all valid plugins in a given folder. - * The function returns an array of objects, each representing a plugin. - * Each object contains the plugin name, version, folder name, Artifact Hub URL, repository name, and Artifact Hub version. - * - * @param {string} folder - The path to the folder to list plugins from. - * @returns {Array} An array of objects, each representing a valid plugin in the given folder. - */ -function listPlugins(folder) { - const pluginsData = []; - - // Read all entries in the specified folder - const entries = fs.readdirSync(folder, { withFileTypes: true }); - - // Filter out directories (plugins) - const pluginFolders = entries.filter(entry => entry.isDirectory()); - - // Iterate through each plugin folder - for (const pluginFolder of pluginFolders) { - const pluginDir = path.join(folder, pluginFolder.name); - - if (checkValidPluginFolder(pluginDir)) { - // Read package.json to get the plugin name and version - const packageJsonPath = path.join(pluginDir, 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - const pluginName = packageJson.name || pluginFolder.name; - const pluginTitle = packageJson.artifacthub.title; - const pluginVersion = packageJson.version || null; - const artifacthubURL = packageJson.artifacthub ? packageJson.artifacthub.url : null; - const repoName = packageJson.artifacthub ? packageJson.artifacthub.repoName : null; - const artifacthubVersion = packageJson.artifacthub ? packageJson.artifacthub.version : null; - // Store plugin data (folder name and plugin name) - pluginsData.push({ - pluginName, - pluginTitle, - pluginVersion, - folderName: pluginFolder.name, - artifacthubURL: artifacthubURL, - repoName: repoName, - artifacthubVersion: artifacthubVersion, - }); - } - } - - return pluginsData; -} - -/** - * Handles the list command for plugins. - * It lists all valid plugins in a given folder and outputs them either in JSON format or as a formatted table. - * The function prints an array of objects, each representing a plugin. - * Each object contains the plugin name, version, folder name, and Artifact Hub URL. - * - * @param {string} folder - The path to the folder to list plugins from. - * @param {boolean} jsonOutput - If true, the output will be in JSON format. If false, the output will be a formatted table. - */ -function handleListCommand(folder, jsonOutput = false) { - const pluginsData = listPlugins(folder); - - if (jsonOutput) { - console.log(JSON.stringify(pluginsData, null, 2)); - } else { - const formattedTable = table( - [ - ['Name', 'Title','Version', 'Folder Name', 'ArtifaceHub URL'], - ...pluginsData.map(plugin => [ - plugin.pluginName, - plugin.pluginTitle, - plugin.pluginVersion || 'N/A', - plugin.folderName, - plugin.artifacthubURL || 'N/A', - ]), - ], - { - border: getBorderCharacters('void'), - } - ); - console.log(formattedTable); - } -} - -/** - * Downloads a tarball from a given URL, verifies its checksum, and extracts it to a target path. - * The function throws an error if the download fails, if the content type of the response is not 'application/octet-stream', - * or if the checksum of the downloaded tarball does not match the expected checksum. - * - * @param {string} url - The URL to download the tarball from. - * @param {string} expectedChecksum - The expected SHA256 checksum of the tarball. - * @param {string} targetPath - The path to extract the tarball to. - * @throws {Error} If the download fails, if the content type of the response is not 'application/octet-stream', - * or if the checksum of the downloaded tarball does not match the expected checksum. - * @returns {Promise} A promise that resolves when the tarball has been downloaded and extracted, or rejects if an error occurs. - */ -async function downloadTarball(url, expectedChecksum, targetPath) { - // create target folder if it doesn't exist - if (!fs.existsSync(targetPath)) { - fs.mkdirSync(targetPath, { recursive: true }); - } - - const response = await fetch(url, { redirect: 'follow' }); - if (!response.ok) { - throw new Error(`Failed to download tarball. Status code: ${response.status}`); - } - - const contentType = response.headers.get('content-type'); - if (!contentType || !contentType.includes('application/octet-stream')) { - throw new Error(`Unexpected content type: ${contentType}`); - } - - const chunks = []; - let bufferLength = 0; - - for await (const chunk of response.body) { - chunks.push(chunk); - bufferLength += chunk.length; - } - - const buffer = Buffer.concat(chunks, bufferLength); - - // Check the checksum - const actualChecksum = crypto.createHash('sha256').update(buffer).digest('hex'); - if (actualChecksum !== expectedChecksum) { - throw new Error('Checksum mismatch'); - } - - const bufferStream = new stream.PassThrough(); - bufferStream.end(buffer); - - const respStream = bufferStream - .pipe(zlib.createGunzip()) - .pipe(tar.x({ C: targetPath, strip: 1, sync: true })); - - await new Promise((resolve, reject) => { - respStream.on('finish', resolve); - respStream.on('error', reject); - }); -} - -/** - * Downloads and installs a plugin. - * The function downloads a tarball from a given URL, verifies its checksum, and extracts it to a target path. - * It also checks if the Headlamp version is compatible with the plugin version. - * If the download fails, if the content type of the response is not 'application/octet-stream', - * or if the checksum of the downloaded tarball does not match the expected checksum, an error is thrown. - * After the tarball is extracted, the function updates the 'package.json' file of the plugin with metadata. - * - * @param {Object} pluginData - The data of the plugin to download and install. - * @param {string} headlampVersion - The version of Headlamp. - * @param {string} folder - The path to the folder to install the plugin to. - * @param {boolean} jsonOutput - If true, the output will be in JSON format. If false, the output will be a formatted table. - * @throws {Error} If the download fails, if the content type of the response is not 'application/octet-stream', - * or if the checksum of the downloaded tarball does not match the expected checksum. - * @returns {Promise} A promise that resolves when the plugin has been downloaded and installed, or rejects if an error occurs. - */ -async function downloadAndInstall(pluginData, headlampVersion, folder, jsonOutput) { - const pluginName = pluginData.name; - const archiveURL = pluginData.data['headlamp/plugin/archive-url']; - let checksum = pluginData.data['headlamp/plugin/archive-checksum']; - if (!archiveURL || !checksum) { - throw new Error('Invalid plugin metadata. Please check the plugin details.'); - } - if (checksum.startsWith('sha256:') || checksum.startsWith('SHA256:')) { - checksum = checksum.replace('sha256:', ''); - checksum = checksum.replace('SHA256:', ''); - } - - // If headlampVersion is provided, check compatibility - if (headlampVersion) { - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'info', - message: 'Checking headlamp version compatibility', - }) - ); - } - if (semver.satisfies(headlampVersion, pluginData.data['headlamp/plugin/version-compat'])) { - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'info', - message: 'Headlamp version is compatible with the plugin', - }) - ); - } else { - console.log('Headlamp version is compatible with the plugin'); - } - } else { - throw new Error('Headlamp version is not compatible with the plugin'); - } - } - - try { - // Create the target folder if it doesn't exist - if (!fs.existsSync(folder)) { - fs.mkdirSync(folder, { recursive: true }); - } - - // Download the tarball and extract it to the specified folder - const pluginDir = path.join(folder, pluginName); - - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'info', - message: 'Downloading plugin tarball', - }) - ); - } - - await downloadTarball(archiveURL, checksum, pluginDir); - } catch (err) { - throw new Error(`Failed to download and extract the plugin tarball,${err}`); - } - - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'info', - message: 'Updating package.json with metadata', - }) - ); - } - // Add artifacthub metadata to the plugins package.json - const packageJsonPath = path.join(folder, pluginName, 'package.json'); - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - packageJson.artifacthub = { - name: pluginName, - title: pluginData.display_name, - url: `https://artifacthub.io/packages/headlamp/${pluginData.repository.name}/${pluginName}`, - version: pluginData.version, - repoName: pluginData.repository.name, - }; - packageJson.isManagedByHeadlampPlugin = true; - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); -} - -/** - * Fetches the metadata of a plugin from Artifact Hub. - * - * @param {string} url - The URL of the plugin on Artifact Hub. - * @throws {Error} If the response is not OK. - * @returns {Promise} A promise that resolves to the metadata of the plugin as a JSON object. - */ -async function fetchPluginMetadata(url) { - const apiURL = url.replace( - 'https://artifacthub.io/packages/headlamp/', - 'https://artifacthub.io/api/v1/packages/headlamp/' - ); - const response = await fetch(apiURL); - if (!response.ok) { - throw new Error(`Failed to fetch plugin metadata. Status code: ${response.status}`); - } - return await response.json(); -} - -/** - * Handles the install command for plugins. - * The function checks if the given URL is a valid ArtifactHub URL, fetches the plugin metadata from the URL, - * and downloads and installs the plugin. - * If the URL is not valid, or if an error occurs during the fetching, downloading, or installing of the plugin, an error is thrown. - * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. - * - * @param {Object} argv - The arguments passed to the command. - * @param {string} argv.URL - The URL of the plugin on ArtifactHub. - * @param {string} argv.folder - The path to the folder to install the plugin to. - * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. - * @param {string} argv.headlampVersion - The version of Headlamp. - * @throws {Error} If the URL is not valid, or if an error occurs during the fetching, downloading, or installing of the plugin. - */ -async function handleInstallCommand(argv) { - const URL = argv.URL; - const folder = argv.folder; - const jsonOutput = argv.json; - - try { - // check if the URL is a valid artifacthub URL - if (!URL.startsWith('https://artifacthub.io/packages/headlamp/')) { - throw new Error('Invalid URL. Please provide a valid URL from ArtifactHub.'); - } - - // fetch the plugin metadata from the URL - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'info', - message: 'Fetching plugin metadata', - }) - ); - } - const pluginData = await fetchPluginMetadata(URL); - - await downloadAndInstall(pluginData, argv.headlampVersion, folder, jsonOutput); - - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'success', - message: 'Plugin installed successfully', - }) - ); - } else { - console.log('Plugin installed successfully'); - } - process.exitCode = 0; - } catch (err) { - if (jsonOutput) { - console.log( - JSON.stringify({ - status: 'error', - message: err.message, - }) - ); - } else { - console.error('Error:', err.message); - } - process.exitCode = 1; - } -} - -/** - * Handles the uninstall command for plugins. - * The function finds the plugin by name in the given folder, checks if the plugin is valid, and deletes the plugin folder. - * If the plugin is not found, or if the plugin is not valid, an error is thrown. - * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. - * - * @param {Object} argv - The arguments passed to the command. - * @param {string} argv.name - The name of the plugin to uninstall. - * @param {string} argv.folder - The path to the folder to uninstall the plugin from. - * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. - * @throws {Error} If the plugin is not found, or if the plugin is not valid. - * @returns {number} Returns 1 if an error occurred, otherwise nothing. - */ -function handleUninstallCommand(argv) { - // find the plugin folder name by plugin name - const name = argv.name; - const folder = argv.folder; - try { - const pluginsData = listPlugins(folder); - const pluginInfo = pluginsData.find(plugin => plugin.pluginName === name); - if (!pluginInfo) { - throw new Error(`The plugin '${name}' is not installed in the folder '${folder}'.`); - } - - const pluginDir = path.join(folder, pluginInfo.folderName); - if (checkValidPluginFolder(pluginDir)) { - // Delete the plugin folder - fs.rmSync(pluginDir, { - recursive: true, - force: true, - }); - if (argv.json) { - console.log( - JSON.stringify({ - status: 'success', - message: `The plugin '${name}' has been uninstalled successfully`, - }) - ); - } else { - console.log(`The plugin '${name}' has been uninstalled successfully`); - } - } else { - throw new Error(`The ${folder}/${name} is not a plugin".`); - } - } catch (err) { - if (argv.json) { - console.log( - JSON.stringify({ - status: 'error', - message: err.message, - }) - ); - } else { - console.error('Error:', err.message); - } - return 1; - } -} - -/** - * Handles the update command for plugins. - * The function finds the plugin by name in the given folder, checks if the plugin is valid, and updates the plugin if a newer version is available. - * If the plugin is not found, or if the plugin is not valid, or if the plugin is already up to date, an error is thrown. - * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. - * - * @param {Object} argv - The arguments passed to the command. - * @param {string} argv.name - The name of the plugin to update. - * @param {string} argv.folder - The path to the folder to update the plugin in. - * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. - * @param {string} argv.headlampVersion - The version of Headlamp. - * @throws {Error} If the plugin is not found, or if the plugin is not valid, or if the plugin is already up to date. - * @returns {Promise} A promise that resolves to 0 if the plugin was updated successfully, or 1 if an error occurred. - */ -async function handleUpdateCommand(argv) { - const name = argv.name; - const folder = argv.folder; - try { - const pluginsList = listPlugins(folder); - const pluginInfo = pluginsList.find(plugin => plugin.pluginName === name); - if (!pluginInfo) { - throw new Error(`The plugin "${name}" is not installed in the folder "${folder}".`); - } - - // read the package.json of the plugin - const packageJson = JSON.parse( - fs.readFileSync(path.join(folder, pluginInfo.folderName, 'package.json'), 'utf8') - ); - - const pluginData = await fetchPluginMetadata(packageJson.artifacthub.url); - - const latestPluginVersion = pluginData.version; - const currentPluginVersion = packageJson.artifacthub.version; - - // semver comparison - if (semver.lte(latestPluginVersion, currentPluginVersion)) { - throw new Error(`The plugin "${name}" is already up to date.`); - } - - await downloadAndInstall(pluginData, argv.headlampVersion, folder, argv.json); - if (argv.json) { - console.log( - JSON.stringify({ - status: 'success', - message: `The plugin "${name}" has been updated successfully`, - }) - ); - } else { - console.log(`The plugin "${name}" has been updated successfully`); - } - return 0; - } catch (err) { - if (argv.json) { - console.log( - JSON.stringify({ - status: 'error', - message: err.message, - }) - ); - } else { - console.error('Error:', err.message); - } - return 1; - } -} - -const commands = [ - { - type: 'list', - description: 'List all installed plugins', - handler: argv => { - process.exitCode = handleListCommand(argv.folder, argv.json); - }, - options: { - folder: { - describe: 'Folder to list the plugins', - type: 'string', - default: defaultPluginsDir(), - }, - json: { - alias: 'j', - describe: 'Output as JSON', - type: 'boolean', - default: false, - }, - }, - }, - { - type: 'install [URL]', - description: 'Install a plugin from artifacthub URL', - handler: argv => { - handleInstallCommand(argv).catch(() => { - process.exitCode = 1; - }); - }, - positional: { - URL: { - describe: 'Artifacthub URL of the plugin to install', - type: 'string', - }, - }, - options: { - folder: { - describe: 'Folder to install the plugin', - type: 'string', - default: defaultPluginsDir(), - }, - json: { - alias: 'j', - describe: 'Output as JSON', - type: 'boolean', - default: false, - }, - headlampVersion: { - describe: 'Headlamp version to check compatibility', - type: 'string', - }, - }, - }, - { - type: 'uninstall [name]', - description: 'Uninstall a plugin by name', - handler: argv => { - process.exitCode = handleUninstallCommand(argv); - }, - positional: { - name: { - describe: 'Name of the plugin to uninstall', - type: 'string', - }, - }, - options: { - folder: { - describe: 'Folder to uninstall the plugin', - type: 'string', - default: defaultPluginsDir(), - }, - json: { - alias: 'j', - describe: 'Output as JSON', - type: 'boolean', - default: false, - }, - }, - }, - { - type: 'update [name]', - description: 'Update a plugin by name', - handler: argv => { - process.exitCode = handleUpdateCommand(argv); - }, - positional: { - name: { - describe: 'Name of the plugin to update', - type: 'string', - }, - }, - options: { - folder: { - describe: 'Folder to update the plugin', - type: 'string', - default: defaultPluginsDir(), - }, - json: { - alias: 'j', - describe: 'Output as JSON', - type: 'boolean', - default: false, - }, - headlampVersion: { - describe: 'Headlamp version to check compatibility', - type: 'string', - }, - }, - }, -]; - -/** - * Adds commands to a yargs instance. - * The function iterates over an array of commands, and for each command, it adds the command to the yargs instance, - * adds any positional arguments and options to the command, and sets the command's handler function. - * - * @param {Object} yargsInstance - The yargs instance to add commands to. - */ -function addCommands(yargsInstance) { - commands.forEach(command => { - yargsInstance.command( - command.type, - command.description, - yargs => { - // Add positional arguments - if (command.positional) { - Object.keys(command.positional).forEach(positionalArg => { - yargs.positional(positionalArg, command.positional[positionalArg]); - }); - } - // Add options - Object.keys(command.options).forEach(option => { - yargs.option(option, command.options[option]); - }); - }, - command.handler - ); - }); -} - -module.exports = { addCommands }; +// const { table, getBorderCharacters } = require('table'); +// const tar = require('tar'); +// const zlib = require('zlib'); +// const envPaths = require('env-paths'); +// const fs = require('fs-extra'); +// const path = require('path'); +// const fetch = require('node-fetch').default; +// const crypto = require('crypto'); +// const stream = require('stream'); +// const semver = require('semver'); + +// /** +// * Returns the default directory where Headlamp plugins are installed. +// * If the data path exists, it is used as the base directory. +// * Otherwise, the config path is used as the base directory. +// * The 'plugins' subdirectory of the base directory is returned. +// * +// * @returns {string} The path to the default plugins directory. +// */ +// function defaultPluginsDir() { +// const paths = envPaths('Headlamp', { suffix: '' }); +// const configDir = fs.existsSync(paths.data) ? paths.data : paths.config; +// return path.join(configDir, 'plugins'); +// } + +// /** +// * Checks if a given folder is a valid Headlamp plugin folder. +// * A valid plugin folder must exist, contain 'main.js' and 'package.json' files, +// * and the 'package.json' file must have 'isManagedByHeadlampPlugin' set to true. +// * +// * @param {string} folder - The path to the folder to check. +// * @returns {boolean} True if the folder is a valid Headlamp plugin folder, false otherwise. +// */ +// function checkValidPluginFolder(folder) { +// if (!fs.existsSync(folder)) { +// return false; +// } +// // Check if the folder contains main.js and package.json +// const mainJsPath = path.join(folder, 'main.js'); +// const packageJsonPath = path.join(folder, 'package.json'); +// if (!fs.existsSync(mainJsPath) || !fs.existsSync(packageJsonPath)) { +// return false; +// } + +// // Read package.json and check isManagedByHeadlampPlugin is set to true +// const packageJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +// if (packageJSON.isManagedByHeadlampPlugin) { +// return true; +// } +// return false; +// } + +// /** +// * Lists all valid plugins in a given folder. +// * The function returns an array of objects, each representing a plugin. +// * Each object contains the plugin name, version, folder name, Artifact Hub URL, repository name, and Artifact Hub version. +// * +// * @param {string} folder - The path to the folder to list plugins from. +// * @returns {Array} An array of objects, each representing a valid plugin in the given folder. +// */ +// function listPlugins(folder) { +// const pluginsData = []; + +// // Read all entries in the specified folder +// const entries = fs.readdirSync(folder, { withFileTypes: true }); + +// // Filter out directories (plugins) +// const pluginFolders = entries.filter(entry => entry.isDirectory()); + +// // Iterate through each plugin folder +// for (const pluginFolder of pluginFolders) { +// const pluginDir = path.join(folder, pluginFolder.name); + +// if (checkValidPluginFolder(pluginDir)) { +// // Read package.json to get the plugin name and version +// const packageJsonPath = path.join(pluginDir, 'package.json'); +// const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +// const pluginName = packageJson.name || pluginFolder.name; +// const pluginTitle = packageJson.artifacthub.title; +// const pluginVersion = packageJson.version || null; +// const artifacthubURL = packageJson.artifacthub ? packageJson.artifacthub.url : null; +// const repoName = packageJson.artifacthub ? packageJson.artifacthub.repoName : null; +// const artifacthubVersion = packageJson.artifacthub ? packageJson.artifacthub.version : null; +// // Store plugin data (folder name and plugin name) +// pluginsData.push({ +// pluginName, +// pluginTitle, +// pluginVersion, +// folderName: pluginFolder.name, +// artifacthubURL: artifacthubURL, +// repoName: repoName, +// artifacthubVersion: artifacthubVersion, +// }); +// } +// } + +// return pluginsData; +// } + +// /** +// * Handles the list command for plugins. +// * It lists all valid plugins in a given folder and outputs them either in JSON format or as a formatted table. +// * The function prints an array of objects, each representing a plugin. +// * Each object contains the plugin name, version, folder name, and Artifact Hub URL. +// * +// * @param {string} folder - The path to the folder to list plugins from. +// * @param {boolean} jsonOutput - If true, the output will be in JSON format. If false, the output will be a formatted table. +// */ +// function handleListCommand(folder, jsonOutput = false) { +// const pluginsData = listPlugins(folder); + +// if (jsonOutput) { +// console.log(JSON.stringify(pluginsData, null, 2)); +// } else { +// const formattedTable = table( +// [ +// ['Name', 'Title','Version', 'Folder Name', 'ArtifaceHub URL'], +// ...pluginsData.map(plugin => [ +// plugin.pluginName, +// plugin.pluginTitle, +// plugin.pluginVersion || 'N/A', +// plugin.folderName, +// plugin.artifacthubURL || 'N/A', +// ]), +// ], +// { +// border: getBorderCharacters('void'), +// } +// ); +// console.log(formattedTable); +// } +// } + +// /** +// * Downloads a tarball from a given URL, verifies its checksum, and extracts it to a target path. +// * The function throws an error if the download fails, if the content type of the response is not 'application/octet-stream', +// * or if the checksum of the downloaded tarball does not match the expected checksum. +// * +// * @param {string} url - The URL to download the tarball from. +// * @param {string} expectedChecksum - The expected SHA256 checksum of the tarball. +// * @param {string} targetPath - The path to extract the tarball to. +// * @throws {Error} If the download fails, if the content type of the response is not 'application/octet-stream', +// * or if the checksum of the downloaded tarball does not match the expected checksum. +// * @returns {Promise} A promise that resolves when the tarball has been downloaded and extracted, or rejects if an error occurs. +// */ +// async function downloadTarball(url, expectedChecksum, targetPath) { +// // create target folder if it doesn't exist +// if (!fs.existsSync(targetPath)) { +// fs.mkdirSync(targetPath, { recursive: true }); +// } + +// const response = await fetch(url, { redirect: 'follow' }); +// if (!response.ok) { +// throw new Error(`Failed to download tarball. Status code: ${response.status}`); +// } + +// const contentType = response.headers.get('content-type'); +// if (!contentType || !contentType.includes('application/octet-stream')) { +// throw new Error(`Unexpected content type: ${contentType}`); +// } + +// const chunks = []; +// let bufferLength = 0; + +// for await (const chunk of response.body) { +// chunks.push(chunk); +// bufferLength += chunk.length; +// } + +// const buffer = Buffer.concat(chunks, bufferLength); + +// // Check the checksum +// const actualChecksum = crypto.createHash('sha256').update(buffer).digest('hex'); +// if (actualChecksum !== expectedChecksum) { +// throw new Error('Checksum mismatch'); +// } + +// const bufferStream = new stream.PassThrough(); +// bufferStream.end(buffer); + +// const respStream = bufferStream +// .pipe(zlib.createGunzip()) +// .pipe(tar.x({ C: targetPath, strip: 1, sync: true })); + +// await new Promise((resolve, reject) => { +// respStream.on('finish', resolve); +// respStream.on('error', reject); +// }); +// } + +// /** +// * Downloads and installs a plugin. +// * The function downloads a tarball from a given URL, verifies its checksum, and extracts it to a target path. +// * It also checks if the Headlamp version is compatible with the plugin version. +// * If the download fails, if the content type of the response is not 'application/octet-stream', +// * or if the checksum of the downloaded tarball does not match the expected checksum, an error is thrown. +// * After the tarball is extracted, the function updates the 'package.json' file of the plugin with metadata. +// * +// * @param {Object} pluginData - The data of the plugin to download and install. +// * @param {string} headlampVersion - The version of Headlamp. +// * @param {string} folder - The path to the folder to install the plugin to. +// * @param {function} stateCallback - A callback function to update the state of the plugin installation. +// * @throws {Error} If the download fails, if the content type of the response is not 'application/octet-stream', +// * or if the checksum of the downloaded tarball does not match the expected checksum. +// * @returns {Promise} A promise that resolves when the plugin has been downloaded and installed, or rejects if an error occurs. +// */ +// async function downloadAndInstall(pluginData, headlampVersion, folder, stateCallback) { +// const pluginName = pluginData.name; +// const archiveURL = pluginData.data['headlamp/plugin/archive-url']; +// let checksum = pluginData.data['headlamp/plugin/archive-checksum']; +// if (!archiveURL || !checksum) { +// throw new Error('Invalid plugin metadata. Please check the plugin details.'); +// } +// if (checksum.startsWith('sha256:') || checksum.startsWith('SHA256:')) { +// checksum = checksum.replace('sha256:', ''); +// checksum = checksum.replace('SHA256:', ''); +// } + +// // If headlampVersion is provided, check compatibility +// if (headlampVersion) { +// if (stateCallback) { +// stateCallback({ +// status: 'info', +// message: 'Checking headlamp version compatibility', +// }) +// } +// if (semver.satisfies(headlampVersion, pluginData.data['headlamp/plugin/version-compat'])) { +// if (stateCallback) { +// stateCallback({ +// status: 'info', +// message: 'Headlamp version is compatible with the plugin', +// }) +// } else { +// console.log('Headlamp version is compatible with the plugin'); +// } +// } else { +// throw new Error('Headlamp version is not compatible with the plugin'); +// } +// } + +// try { +// // Create the target folder if it doesn't exist +// if (!fs.existsSync(folder)) { +// fs.mkdirSync(folder, { recursive: true }); +// } + +// // Download the tarball and extract it to the specified folder +// const pluginDir = path.join(folder, pluginName); + +// if (jsonOutput) { +// console.log( +// JSON.stringify({ +// status: 'info', +// message: 'Downloading plugin tarball', +// }) +// ); +// } + +// await downloadTarball(archiveURL, checksum, pluginDir); +// } catch (err) { +// throw new Error(`Failed to download and extract the plugin tarball,${err}`); +// } + +// if (jsonOutput) { +// console.log( +// JSON.stringify({ +// status: 'info', +// message: 'Updating package.json with metadata', +// }) +// ); +// } +// // Add artifacthub metadata to the plugins package.json +// const packageJsonPath = path.join(folder, pluginName, 'package.json'); +// const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); +// packageJson.artifacthub = { +// name: pluginName, +// title: pluginData.display_name, +// url: `https://artifacthub.io/packages/headlamp/${pluginData.repository.name}/${pluginName}`, +// version: pluginData.version, +// repoName: pluginData.repository.name, +// }; +// packageJson.isManagedByHeadlampPlugin = true; +// fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); +// } + +// /** +// * Fetches the metadata of a plugin from Artifact Hub. +// * +// * @param {string} url - The URL of the plugin on Artifact Hub. +// * @throws {Error} If the response is not OK. +// * @returns {Promise} A promise that resolves to the metadata of the plugin as a JSON object. +// */ +// async function fetchPluginMetadata(url) { +// const apiURL = url.replace( +// 'https://artifacthub.io/packages/headlamp/', +// 'https://artifacthub.io/api/v1/packages/headlamp/' +// ); +// const response = await fetch(apiURL); +// if (!response.ok) { +// throw new Error(`Failed to fetch plugin metadata. Status code: ${response.status}`); +// } +// return await response.json(); +// } + +// /** +// * Handles the install command for plugins. +// * The function checks if the given URL is a valid ArtifactHub URL, fetches the plugin metadata from the URL, +// * and downloads and installs the plugin. +// * If the URL is not valid, or if an error occurs during the fetching, downloading, or installing of the plugin, an error is thrown. +// * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. +// * +// * @param {Object} argv - The arguments passed to the command. +// * @param {string} argv.URL - The URL of the plugin on ArtifactHub. +// * @param {string} argv.folder - The path to the folder to install the plugin to. +// * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. +// * @param {string} argv.headlampVersion - The version of Headlamp. +// * @throws {Error} If the URL is not valid, or if an error occurs during the fetching, downloading, or installing of the plugin. +// */ +// async function handleInstallCommand(argv) { +// const URL = argv.URL; +// const folder = argv.folder; +// // const jsonOutput = argv.json; +// const stateCallback = argv.stateCallback; + +// try { +// // check if the URL is a valid artifacthub URL +// if (!URL.startsWith('https://artifacthub.io/packages/headlamp/')) { +// throw new Error('Invalid URL. Please provide a valid URL from ArtifactHub.'); +// } + +// // fetch the plugin metadata from the URL +// if (stateCallback) { +// stateCallback({ +// status: 'info', +// message: 'Fetching plugin metadata', +// }) +// } +// const pluginData = await fetchPluginMetadata(URL); + +// await downloadAndInstall(pluginData, argv.headlampVersion, folder, jsonOutput); + +// if (stateCallback){ +// stateCallback({ +// status: 'success', +// message: 'Plugin installed successfully', +// }) +// } +// console.log('Plugin installed successfully'); + +// process.exitCode = 0; +// } catch (err) { +// if (stateCallback) { +// stateCallback({ +// status: 'error', +// message: err.message, +// }) +// } +// console.error('Error:', err.message); +// process.exitCode = 1; +// } +// } + +// /** +// * Handles the uninstall command for plugins. +// * The function finds the plugin by name in the given folder, checks if the plugin is valid, and deletes the plugin folder. +// * If the plugin is not found, or if the plugin is not valid, an error is thrown. +// * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. +// * +// * @param {Object} argv - The arguments passed to the command. +// * @param {string} argv.name - The name of the plugin to uninstall. +// * @param {string} argv.folder - The path to the folder to uninstall the plugin from. +// * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. +// * @throws {Error} If the plugin is not found, or if the plugin is not valid. +// * @returns {number} Returns 1 if an error occurred, otherwise nothing. +// */ +// function handleUninstallCommand(argv) { +// // find the plugin folder name by plugin name +// const name = argv.name; +// const folder = argv.folder; +// try { +// const pluginsData = listPlugins(folder); +// const pluginInfo = pluginsData.find(plugin => plugin.pluginName === name); +// if (!pluginInfo) { +// throw new Error(`The plugin '${name}' is not installed in the folder '${folder}'.`); +// } + +// const pluginDir = path.join(folder, pluginInfo.folderName); +// if (checkValidPluginFolder(pluginDir)) { +// // Delete the plugin folder +// fs.rmSync(pluginDir, { +// recursive: true, +// force: true, +// }); +// if (argv.json) { +// console.log( +// JSON.stringify({ +// status: 'success', +// message: `The plugin '${name}' has been uninstalled successfully`, +// }) +// ); +// } else { +// console.log(`The plugin '${name}' has been uninstalled successfully`); +// } +// } else { +// throw new Error(`The ${folder}/${name} is not a plugin".`); +// } +// } catch (err) { +// if (argv.json) { +// console.log( +// JSON.stringify({ +// status: 'error', +// message: err.message, +// }) +// ); +// } else { +// console.error('Error:', err.message); +// } +// return 1; +// } +// } + +// /** +// * Handles the update command for plugins. +// * The function finds the plugin by name in the given folder, checks if the plugin is valid, and updates the plugin if a newer version is available. +// * If the plugin is not found, or if the plugin is not valid, or if the plugin is already up to date, an error is thrown. +// * The function outputs the status and messages of the operation in either JSON format or as plain text, depending on the jsonOutput argument. +// * +// * @param {Object} argv - The arguments passed to the command. +// * @param {string} argv.name - The name of the plugin to update. +// * @param {string} argv.folder - The path to the folder to update the plugin in. +// * @param {boolean} argv.json - If true, the output will be in JSON format. If false, the output will be plain text. +// * @param {string} argv.headlampVersion - The version of Headlamp. +// * @throws {Error} If the plugin is not found, or if the plugin is not valid, or if the plugin is already up to date. +// * @returns {Promise} A promise that resolves to 0 if the plugin was updated successfully, or 1 if an error occurred. +// */ +// async function handleUpdateCommand(argv) { +// const name = argv.name; +// const folder = argv.folder; +// try { +// const pluginsList = listPlugins(folder); +// const pluginInfo = pluginsList.find(plugin => plugin.pluginName === name); +// if (!pluginInfo) { +// throw new Error(`The plugin "${name}" is not installed in the folder "${folder}".`); +// } + +// // read the package.json of the plugin +// const packageJson = JSON.parse( +// fs.readFileSync(path.join(folder, pluginInfo.folderName, 'package.json'), 'utf8') +// ); + +// const pluginData = await fetchPluginMetadata(packageJson.artifacthub.url); + +// const latestPluginVersion = pluginData.version; +// const currentPluginVersion = packageJson.artifacthub.version; + +// // semver comparison +// if (semver.lte(latestPluginVersion, currentPluginVersion)) { +// throw new Error(`The plugin "${name}" is already up to date.`); +// } + +// await downloadAndInstall(pluginData, argv.headlampVersion, folder, argv.json); +// if (argv.json) { +// console.log( +// JSON.stringify({ +// status: 'success', +// message: `The plugin "${name}" has been updated successfully`, +// }) +// ); +// } else { +// console.log(`The plugin "${name}" has been updated successfully`); +// } +// return 0; +// } catch (err) { +// if (argv.json) { +// console.log( +// JSON.stringify({ +// status: 'error', +// message: err.message, +// }) +// ); +// } else { +// console.error('Error:', err.message); +// } +// return 1; +// } +// } + +// const commands = [ +// { +// type: 'list', +// description: 'List all installed plugins', +// handler: argv => { +// process.exitCode = handleListCommand(argv.folder, argv.json); +// }, +// options: { +// folder: { +// describe: 'Folder to list the plugins', +// type: 'string', +// default: defaultPluginsDir(), +// }, +// json: { +// alias: 'j', +// describe: 'Output as JSON', +// type: 'boolean', +// default: false, +// }, +// }, +// }, +// { +// type: 'install [URL]', +// description: 'Install a plugin from artifacthub URL', +// handler: argv => { +// handleInstallCommand(argv).catch(() => { +// process.exitCode = 1; +// }); +// }, +// positional: { +// URL: { +// describe: 'Artifacthub URL of the plugin to install', +// type: 'string', +// }, +// }, +// options: { +// folder: { +// describe: 'Folder to install the plugin', +// type: 'string', +// default: defaultPluginsDir(), +// }, +// json: { +// alias: 'j', +// describe: 'Output as JSON', +// type: 'boolean', +// default: false, +// }, +// headlampVersion: { +// describe: 'Headlamp version to check compatibility', +// type: 'string', +// }, +// }, +// }, +// { +// type: 'uninstall [name]', +// description: 'Uninstall a plugin by name', +// handler: argv => { +// process.exitCode = handleUninstallCommand(argv); +// }, +// positional: { +// name: { +// describe: 'Name of the plugin to uninstall', +// type: 'string', +// }, +// }, +// options: { +// folder: { +// describe: 'Folder to uninstall the plugin', +// type: 'string', +// default: defaultPluginsDir(), +// }, +// json: { +// alias: 'j', +// describe: 'Output as JSON', +// type: 'boolean', +// default: false, +// }, +// }, +// }, +// { +// type: 'update [name]', +// description: 'Update a plugin by name', +// handler: argv => { +// process.exitCode = handleUpdateCommand(argv); +// }, +// positional: { +// name: { +// describe: 'Name of the plugin to update', +// type: 'string', +// }, +// }, +// options: { +// folder: { +// describe: 'Folder to update the plugin', +// type: 'string', +// default: defaultPluginsDir(), +// }, +// json: { +// alias: 'j', +// describe: 'Output as JSON', +// type: 'boolean', +// default: false, +// }, +// headlampVersion: { +// describe: 'Headlamp version to check compatibility', +// type: 'string', +// }, +// }, +// }, +// ]; + +// /** +// * Adds commands to a yargs instance. +// * The function iterates over an array of commands, and for each command, it adds the command to the yargs instance, +// * adds any positional arguments and options to the command, and sets the command's handler function. +// * +// * @param {Object} yargsInstance - The yargs instance to add commands to. +// */ +// function addCommands(yargsInstance) { +// commands.forEach(command => { +// yargsInstance.command( +// command.type, +// command.description, +// yargs => { +// // Add positional arguments +// if (command.positional) { +// Object.keys(command.positional).forEach(positionalArg => { +// yargs.positional(positionalArg, command.positional[positionalArg]); +// }); +// } +// // Add options +// Object.keys(command.options).forEach(option => { +// yargs.option(option, command.options[option]); +// }); +// }, +// command.handler +// ); +// }); +// } + +// module.exports = { addCommands }; diff --git a/plugins/headlamp-plugin/plugin-management.js b/plugins/headlamp-plugin/plugin-management.js new file mode 100644 index 00000000000..055f11c8844 --- /dev/null +++ b/plugins/headlamp-plugin/plugin-management.js @@ -0,0 +1,416 @@ +const fetch = require('node-fetch').default; +const fs = require('fs'); +const os = require('os'); +const zlib = require('zlib'); +const tar = require('tar'); +const path = require('path'); +const crypto = require('crypto'); +const stream = require('stream'); +const semver = require('semver'); +const envPaths = require('env-paths'); + +function sleep(ms) { + // return new Promise(function(resolve) { + // setTimeout(resolve, ms); + // }); +} + + +class PluginManager { + + /** + * Installs a plugin from the specified URL. + * @param {string} URL - The URL of the plugin to install. + * @param {string} [destinationFolder=defaultPluginsDir()] - The folder where the plugin will be installed. + * @param {string} [headlampVersion=""] - The version of Headlamp for compatibility checking. + * @param {function} [progressCallback=null] - Optional callback for progress updates. + * @param {AbortSignal} [signal=null] - Optional AbortSignal for cancellation. + * @returns {Promise} A promise that resolves when the installation is complete. + */ + async install(URL, destinationFolder=defaultPluginsDir(), headlampVersion="", progressCallback = null, signal = null) { + try { + const [name, tempFolder] = await downloadExtractPlugin( + URL, + headlampVersion, + progressCallback, + signal + ); + + sleep(2000); + + // create the destination folder if it doesn't exist + if (!fs.existsSync(destinationFolder)) { + fs.mkdirSync(destinationFolder, { recursive: true }); + } + // move the plugin to the destination folder + fs.renameSync(tempFolder, path.join(destinationFolder, path.basename(name))); + progressCallback({ type: 'success', message: 'Plugin Installed' }); + } catch (e) { + progressCallback({ type: 'error', message: e.message }); + } + } + + /** + * Updates an installed plugin to the latest version. + * @param {string} pluginName - The name of the plugin to update. + * @param {string} [destinationFolder=defaultPluginsDir()] - The folder where the plugin is installed. + * @param {string} [headlampVersion=""] - The version of Headlamp for compatibility checking. + * @param {function} [progressCallback=null] - Optional callback for progress updates. + * @param {AbortSignal} [signal=null] - Optional AbortSignal for cancellation. + * @returns {Promise} A promise that resolves when the update is complete. + */ + async update( + pluginName, + destinationFolder=defaultPluginsDir(), + headlampVersion="", + progressCallback = null, + signal = null + ) { + try { + const installedPlugins = this.list(destinationFolder); + const plugin = installedPlugins.find(p => p.pluginName === pluginName); + if (!plugin) { + throw new Error('Plugin not found'); + } + + const pluginDir = path.join(destinationFolder, plugin.folderName); + // read the package.json of the plugin + const packageJsonPath = path.join(pluginDir, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + + const pluginData = await fetchPluginInfo(plugin.artifacthubURL, progressCallback, signal); + + const latestVersion = pluginData.version; + const currentVersion = packageJson.artifacthub.version; + + if (semver.lte(latestVersion, currentVersion)) { + throw new Error('No updates available'); + } + + const [name, tempFolder] = await downloadExtractPlugin( + plugin.artifacthubURL, + headlampVersion, + progressCallback, + signal + ); + + sleep(2000); + + // create the destination folder if it doesn't exist + if (!fs.existsSync(destinationFolder)) { + fs.mkdirSync(destinationFolder, { recursive: true }); + } + + // remove the existing plugin folder + fs.rmdirSync(pluginDir, { recursive: true }); + + // create the plugin folder + fs.mkdirSync(pluginDir, { recursive: true }); + + // move the plugin to the destination folder + fs.renameSync(tempFolder, pluginDir); + progressCallback({ type: 'success', message: 'Plugin Updated' }); + } catch (e) { + progressCallback({ type: 'error', message: e.message }); + } + } + + /** + * Uninstalls a plugin from the specified folder. + * @param {string} name - The name of the plugin to uninstall. + * @param {string} [folder=defaultPluginsDir()] - The folder where the plugin is installed. + * @param {function} [progressCallback=null] - Optional callback for progress updates. + * @returns {void} + */ + uninstall(name, folder=defaultPluginsDir(), progressCallback = null) { + try { + const installedPlugins = this.list(folder); + const plugin = installedPlugins.find(p => p.pluginName === name); + if (!plugin) { + throw new Error('Plugin not found'); + } + + const pluginDir = path.join(folder, plugin.folderName); + if (!checkValidPluginFolder(pluginDir)) { + throw new Error('Invalid plugin folder'); + } + + if (fs.existsSync(pluginDir)) { + fs.rmdirSync(pluginDir, { recursive: true }); + } else { + throw new Error('Plugin not found'); + } + progressCallback({ type: 'success', message: 'Plugin Uninstalled' }); + } catch (e) { + progressCallback({ type: 'error', message: e.message }); + } + } + + /** + * Lists all valid plugins in the specified folder. + * @param {string} [folder=defaultPluginsDir()] - The folder to list plugins from. + * @param {function} [progressCallback=null] - Optional callback for progress updates. + * @returns {Array} An array of objects representing valid plugins. + */ + list(folder=defaultPluginsDir(), progressCallback = null) { + try { + const pluginsData = []; + + // Read all entries in the specified folder + const entries = fs.readdirSync(folder, { withFileTypes: true }); + + // Filter out directories (plugins) + const pluginFolders = entries.filter(entry => entry.isDirectory()); + + // Iterate through each plugin folder + for (const pluginFolder of pluginFolders) { + const pluginDir = path.join(folder, pluginFolder.name); + + if (checkValidPluginFolder(pluginDir)) { + // Read package.json to get the plugin name and version + const packageJsonPath = path.join(pluginDir, 'package.json'); + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + const pluginName = packageJson.name || pluginFolder.name; + const pluginTitle = packageJson.artifacthub.title; + const pluginVersion = packageJson.version || null; + const artifacthubURL = packageJson.artifacthub ? packageJson.artifacthub.url : null; + const repoName = packageJson.artifacthub ? packageJson.artifacthub.repoName : null; + const artifacthubVersion = packageJson.artifacthub + ? packageJson.artifacthub.version + : null; + // Store plugin data (folder name and plugin name) + pluginsData.push({ + pluginName, + pluginTitle, + pluginVersion, + folderName: pluginFolder.name, + artifacthubURL: artifacthubURL, + repoName: repoName, + artifacthubVersion: artifacthubVersion, + }); + } + } + + if (progressCallback) { + progressCallback({ type: 'success', message: 'Plugins Listed', data: pluginsData }); + } else { + return pluginsData; + } + } catch (e) { + if (progressCallback) { + progressCallback({ type: 'error', message: e.message }); + } else { + throw e; + } + } + } +} + +/** + * Downloads and extracts a plugin from the specified URL. + * @param {string} URL - The URL of the plugin to download and extract. + * @param {string} headlampVersion - The version of Headlamp for compatibility checking. + * @param {function} progressCallback - A callback function for reporting progress. + * @param {AbortSignal} signal - An optional AbortSignal for cancellation. + * @returns {Promise<[string, string]>} A promise that resolves to an array containing the plugin name and temporary folder path. + */ +async function downloadExtractPlugin(URL, headlampVersion, progressCallback, signal) { + try { + // fetch plugin metadata + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + const pluginInfo = await fetchPluginInfo(URL, progressCallback, signal); + await sleep(4000) + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + progressCallback({ type: 'info', message: 'Plugin Metadata Fetched' }); + + const pluginName = pluginInfo.name; + const archiveURL = pluginInfo.data['headlamp/plugin/archive-url']; + let checksum = pluginInfo.data['headlamp/plugin/archive-checksum']; + if (!archiveURL || !checksum) { + throw new Error('Invalid plugin metadata. Please check the plugin details.'); + } + if (checksum.startsWith('sha256:') || checksum.startsWith('SHA256:')) { + checksum = checksum.replace('sha256:', ''); + checksum = checksum.replace('SHA256:', ''); + } + + // check if the plugin is compatible with the current Headlamp version + if (headlampVersion) { + progressCallback({ type: 'info', message: 'Checking compatibility with Headlamp version' }); + if (semver.satisfies(headlampVersion, pluginInfo.data['headlamp/plugin/version-compat'])) { + progressCallback({ type: 'info', message: 'Headlamp version is compatible' }); + } else { + throw new Error('Headlamp version is not compatible with the plugin'); + } + } + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + + // create a temp folder + const tempFolder = fs.mkdirSync( + path.join(os.tmpdir() + `/${pluginName}-${Date.now().toString()}/${pluginName}`), + { recursive: true } + ); + + progressCallback({ type: 'info', message: 'Downloading Plugin' }); + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + + await sleep(4000) + const archResponse = await fetch(archiveURL, { redirect: 'follow', follow: 10 }, { signal }); + if (!archResponse.ok) { + throw new Error(`Failed to download tarball. Status code: ${archResponse.status}`); + } + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + + progressCallback({ type: 'info', message: 'Plugin Downloaded' }); + + const archChunks = []; + let archBufferLengeth = 0; + + for await (const chunk of archResponse.body) { + archChunks.push(chunk); + archBufferLengeth += chunk.length; + } + + const archBuffer = Buffer.concat(archChunks, archBufferLengeth); + + const archiveChecksum = crypto.createHash('sha256').update(archBuffer).digest('hex'); + + if (archiveChecksum !== checksum) { + throw new Error('Checksum mismatch.'); + } + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + + progressCallback({ type: 'info', message: 'Extracting Plugin' }); + + const archStream = new stream.PassThrough(); + archStream.end(archBuffer); + + const extractStream = archStream.pipe(zlib.createGunzip()).pipe( + tar.extract({ + cwd: tempFolder, + strip: 1, + sync: true, + }) + ); + + await new Promise((resolve, reject) => { + extractStream.on('finish', () => { + resolve(); + }); + extractStream.on('error', err => { + reject(err); + }); + }); + + if (signal && signal.aborted){ + throw new Error('Download cancelled'); + } + + progressCallback({ type: 'info', message: 'Plugin Extracted' }); + + // add artifacthub metadata to the plugin + const packageJSON = JSON.parse(fs.readFileSync(`${tempFolder}/package.json`, 'utf8')); + packageJSON.artifacthub = { + name: pluginName, + title: pluginInfo.display_name, + url: `https://artifacthub.io/packages/headlamp/${pluginInfo.repository.name}/${pluginName}`, + version: pluginInfo.version, + repoName: pluginInfo.repository.name, + }; + packageJSON.isManagedByHeadlampPlugin = true; + fs.writeFileSync(`${tempFolder}/package.json`, JSON.stringify(packageJSON, null, 2)); + return [pluginName, tempFolder]; + } catch (e) { + progressCallback({ type: 'error', message: e.message }); + } +} + +/** + * Fetches plugin metadata from the specified URL. + * @param {string} URL - The URL to fetch plugin metadata from. + * @param {function} progressCallback - A callback function for reporting progress. + * @param {AbortSignal} signal - An optional AbortSignal for cancellation. + * @returns {Promise} A promise that resolves to the fetched plugin metadata. + */ +async function fetchPluginInfo(URL, progressCallback, signal) { + try { + if (!URL.startsWith('https://artifacthub.io/packages/headlamp/')) { + throw new Error('Invalid URL. Please provide a valid URL from ArtifactHub.'); + } + + const apiURL = URL.replace( + 'https://artifacthub.io/packages/headlamp/', + 'https://artifacthub.io/api/v1/packages/headlamp/' + ); + + progressCallback({ type: 'info', message: 'Fetching Plugin Metadata' }); + + const response = await fetch(apiURL, { redirect: 'follow', follow: 10 }, { signal }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return await response.json(); + } catch (e) { + progressCallback({ type: 'error', message: e.message }); + } +} + +/** + * Checks if a given folder is a valid Headlamp plugin folder. + * A valid plugin folder must exist, contain 'main.js' and 'package.json' files, + * and the 'package.json' file must have 'isManagedByHeadlampPlugin' set to true. + * + * @param {string} folder - The path to the folder to check. + * @returns {boolean} True if the folder is a valid Headlamp plugin folder, false otherwise. + */ +function checkValidPluginFolder(folder) { + if (!fs.existsSync(folder)) { + return false; + } + // Check if the folder contains main.js and package.json + const mainJsPath = path.join(folder, 'main.js'); + const packageJsonPath = path.join(folder, 'package.json'); + if (!fs.existsSync(mainJsPath) || !fs.existsSync(packageJsonPath)) { + return false; + } + + // Read package.json and check isManagedByHeadlampPlugin is set to true + const packageJSON = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + if (packageJSON.isManagedByHeadlampPlugin) { + return true; + } + return false; +} + +/** + * Returns the default directory where Headlamp plugins are installed. + * If the data path exists, it is used as the base directory. + * Otherwise, the config path is used as the base directory. + * The 'plugins' subdirectory of the base directory is returned. + * + * @returns {string} The path to the default plugins directory. + */ +function defaultPluginsDir() { + const paths = envPaths('Headlamp', { suffix: '' }); + const configDir = fs.existsSync(paths.data) ? paths.data : paths.config; + return path.join(configDir, 'plugins'); +} + +module.exports = PluginManager; diff --git a/plugins/headlamp-plugin/plugin-management.test.js b/plugins/headlamp-plugin/plugin-management.test.js new file mode 100644 index 00000000000..4e247492b46 --- /dev/null +++ b/plugins/headlamp-plugin/plugin-management.test.js @@ -0,0 +1,79 @@ +const PluginManager = require('./plugin-management.js'); +const tmp = require('tmp'); +const fs = require('fs'); +const semver = require('semver'); + +// Mocking progressCallback function for testing +const mockProgressCallback = jest.fn((args) => { + // console.log("Progress Callback:", args); // Uncomment for debugging +});; + +describe('PluginManager Test Cases', () => { + let pluginManager; + let tempDir; + let installPassed = false; + + + beforeAll(() => { + // Create a temporary directory before all tests + tempDir = tmp.dirSync({ unsafeCleanup: true }).name; + }); + + afterAll(() => { + // Remove the temporary directory after all tests + fs.rmdirSync(tempDir, { recursive: true }); + }); + + + beforeEach(() => { + // Initialize a new PluginManager instance before each test + pluginManager = new PluginManager(); + jest.clearAllMocks(); + }); + + test('Install Plugin', async () => { + await pluginManager.install("https://artifacthub.io/packages/headlamp/test-123/appcatalog_headlamp_plugin", tempDir, "", mockProgressCallback); + expect(mockProgressCallback).toHaveBeenCalledWith({ type: 'success', message: 'Plugin Installed' }); + installPassed = true; + }); + + if (!installPassed) { + // Skip the following tests if the plugin installation failed + test('Skip Plugin Tests', () => { + expect(installPassed).toBe(true); + }); + return; + } + + test('List Plugins', () => { + const plugins = pluginManager.list(tempDir, mockProgressCallback); + // Assuming "app-catalog" plugin is in the list of plugins + expect(mockProgressCallback).toHaveBeenCalledWith({ type: 'success', message: 'Plugins Listed', data: expect.any(Array) }); + + }); + + test('No Update available for Plugin', async () => { + // No updates available for "app-catalog" plugin + await pluginManager.update("app-catalog", tempDir, "", mockProgressCallback); + expect(mockProgressCallback).toHaveBeenCalledWith({ type: 'error', message: 'No updates available' }); + }); + + test('Update Plugin', async () => { + // update the "app-catalog" plugin package.json with lower state + const packageJSONPath = `${tempDir}/appcatalog_headlamp_plugin/package.json`; + const packageJSON = JSON.parse(fs.readFileSync(packageJSONPath)); + packageJSON.artifacthub.version = `${semver.major(packageJSON.artifacthub.version)}.${semver.minor(packageJSON.artifacthub.version)}.${semver.patch(packageJSON.artifacthub.version)-1}`; // Reduce the version using semver + // Write the updated package.json back to the file + fs.writeFileSync(packageJSONPath, JSON.stringify(packageJSON, null, 2)); + + await pluginManager.update("app-catalog", tempDir, "", mockProgressCallback); + expect(mockProgressCallback).toHaveBeenCalledWith({ type: 'success', message: 'Plugin Updated' }); + }); + + test('Uninstall Plugin', () => { + // Assuming "app-catalog" plugin is already installed for testing uninstall + pluginManager.uninstall("app-catalog", tempDir, mockProgressCallback); + expect(mockProgressCallback).toHaveBeenCalledWith({ type: 'success', message: 'Plugin Uninstalled' }); + }); + +});