diff --git a/app/electron/main.ts b/app/electron/main.ts index fc983f1313d..ec98dd1e06d 100644 --- a/app/electron/main.ts +++ b/app/electron/main.ts @@ -1,48 +1,67 @@ -import 'regenerator-runtime/runtime'; -import { ChildProcess, ChildProcessWithoutNullStreams, execSync, fork, spawn } from 'child_process'; -import { randomBytes } from 'crypto'; -import dotenv from 'dotenv'; -import { app, BrowserWindow, dialog, ipcMain, Menu, MenuItem, screen, shell } from 'electron'; -import { IpcMainEvent, MenuItemConstructorOptions } from 'electron/main'; -import log from 'electron-log'; -import find_process from 'find-process'; -import fs from 'fs'; -import open from 'open'; -import path from 'path'; -import url from 'url'; -import yargs from 'yargs'; -import i18n from './i18next.config'; -import windowSize from './windowSize'; - -dotenv.config({ path: path.join(process.resourcesPath, '.env') }); +import "regenerator-runtime/runtime"; +import { + ChildProcess, + ChildProcessWithoutNullStreams, + execSync, + fork, + spawn, +} from "child_process"; +import { randomBytes } from "crypto"; +import dotenv from "dotenv"; +import { + app, + BrowserWindow, + dialog, + ipcMain, + Menu, + MenuItem, + screen, + shell, +} from "electron"; +import { IpcMainEvent, MenuItemConstructorOptions } from "electron/main"; +import log from "electron-log"; +import find_process from "find-process"; +import fs from "fs"; +import open from "open"; +import path from "path"; +import url from "url"; +import yargs from "yargs"; +import i18n from "./i18next.config"; +import windowSize from "./windowSize"; +import PluginManager from "../../plugins/headlamp-plugin/plugin-management"; + +dotenv.config({ path: path.join(process.resourcesPath, ".env") }); const args = yargs - .command('$0 [kubeconfig]', '', yargs => { + .command("$0 [kubeconfig]", "", (yargs) => { yargs - .option('headless', { - describe: 'Open Headlamp in the default web browser instead of its app window', + .option("headless", { + describe: + "Open Headlamp in the default web browser instead of its app window", }) - .option('disable-gpu', { - describe: 'Disable use of GPU. For people who may have buggy graphics drivers', - type: 'boolean', + .option("disable-gpu", { + describe: + "Disable use of GPU. For people who may have buggy graphics drivers", + type: "boolean", }) - .positional('kubeconfig', { + .positional("kubeconfig", { describe: - 'Path to the kube config file (uses the default kube config location if not specified)', - type: 'string', + "Path to the kube config file (uses the default kube config location if not specified)", + type: "string", }); }) .help().argv; const isHeadlessMode = args.headless; -let disableGPU = args['disable-gpu']; +let disableGPU = args["disable-gpu"]; const defaultPort = 4466; -const backendToken = randomBytes(32).toString('hex'); +const backendToken = randomBytes(32).toString("hex"); const isDev = process.env.ELECTRON_DEV || false; const useExternalServer = process.env.EXTERNAL_SERVER || false; -const shouldCheckForUpdates = process.env.HEADLAMP_CHECK_FOR_UPDATES !== 'false'; -const manifestDir = isDev ? path.resolve('./') : process.resourcesPath; -const manifestFile = path.join(manifestDir, 'app-build-manifest.json'); +const shouldCheckForUpdates = + process.env.HEADLAMP_CHECK_FOR_UPDATES !== "false"; +const manifestDir = isDev ? path.resolve("./") : process.resourcesPath; +const manifestFile = path.join(manifestDir, "app-build-manifest.json"); const buildManifest = fs.existsSync(manifestFile) ? require(manifestFile) : {}; // make it global so that it doesn't get garbage collected @@ -50,27 +69,27 @@ let mainWindow: BrowserWindow | null; function startServer(flags: string[] = []): ChildProcessWithoutNullStreams { const serverFilePath = isDev - ? path.resolve('../backend/headlamp-server') - : path.join(process.resourcesPath, './headlamp-server'); + ? path.resolve("../backend/headlamp-server") + : path.join(process.resourcesPath, "./headlamp-server"); let serverArgs = []; if (!!args.kubeconfig) { - serverArgs = serverArgs.concat(['--kubeconfig', args.kubeconfig]); + serverArgs = serverArgs.concat(["--kubeconfig", args.kubeconfig]); } - const proxyUrls = !!buildManifest && buildManifest['proxy-urls']; + const proxyUrls = !!buildManifest && buildManifest["proxy-urls"]; if (!!proxyUrls && proxyUrls.length > 0) { - serverArgs = serverArgs.concat(['--proxy-urls', proxyUrls.join(',')]); + serverArgs = serverArgs.concat(["--proxy-urls", proxyUrls.join(",")]); } - const bundledPlugins = path.join(process.resourcesPath, '.plugins'); + const bundledPlugins = path.join(process.resourcesPath, ".plugins"); function isEmpty(path) { return fs.readdirSync(path).length === 0; } // Enable the Helm and dynamic cluster endpoints - process.env.HEADLAMP_CONFIG_ENABLE_HELM = 'true'; - process.env.HEADLAMP_CONFIG_ENABLE_DYNAMIC_CLUSTERS = 'true'; + process.env.HEADLAMP_CONFIG_ENABLE_HELM = "true"; + process.env.HEADLAMP_CONFIG_ENABLE_DYNAMIC_CLUSTERS = "true"; // Pass a token to the backend that can be used for auth on some routes process.env.HEADLAMP_BACKEND_TOKEN = backendToken; @@ -81,7 +100,7 @@ function startServer(flags: string[] = []): ChildProcessWithoutNullStreams { } serverArgs = serverArgs.concat(flags); - console.log('arguments passed to backend server', serverArgs); + console.log("arguments passed to backend server", serverArgs); // We run detached but not in shell, otherwise it's hard to make sure the // server process gets killed. When changing these options, please make sure @@ -98,11 +117,11 @@ function startServer(flags: string[] = []): ChildProcessWithoutNullStreams { */ function isWSL(): boolean { try { - const data = fs.readFileSync('/proc/version', { - encoding: 'utf8', - flag: 'r', + const data = fs.readFileSync("/proc/version", { + encoding: "utf8", + flag: "r", }); - return data.indexOf('icrosoft') !== -1; + return data.indexOf("icrosoft") !== -1; } catch { return false; } @@ -113,13 +132,13 @@ let intentionalQuit: boolean; let serverProcessQuit: boolean; function quitServerProcess() { - if ((!serverProcess || serverProcessQuit) && process.platform !== 'win32') { - log.error('server process already not running'); + if ((!serverProcess || serverProcessQuit) && process.platform !== "win32") { + log.error("server process already not running"); return; } intentionalQuit = true; - log.info('stopping server process...'); + log.info("stopping server process..."); if (!serverProcess) { return; @@ -134,29 +153,29 @@ function quitServerProcess() { } function getDefaultAppMenu(): AppMenu[] { - const isMac = process.platform === 'darwin'; + const isMac = process.platform === "darwin"; - const sep = { type: 'separator' }; + const sep = { type: "separator" }; const aboutMenu = { - label: i18n.t('About'), - role: 'about', - id: 'original-about', + label: i18n.t("About"), + role: "about", + id: "original-about", afterPlugins: true, }; const quitMenu = { - label: i18n.t('Quit'), - role: 'quit', - id: 'original-quit', + label: i18n.t("Quit"), + role: "quit", + id: "original-quit", }; const selectAllMenu = { - label: i18n.t('Select All'), - role: 'selectAll', - id: 'original-select-all', + label: i18n.t("Select All"), + role: "selectAll", + id: "original-select-all", }; const deleteMenu = { - label: i18n.t('Delete'), - role: 'delete', - id: 'original-delete', + label: i18n.t("Delete"), + role: "delete", + id: "original-delete", }; const appMenu = [ @@ -169,25 +188,25 @@ function getDefaultAppMenu(): AppMenu[] { aboutMenu, sep, { - label: i18n.t('Services'), - role: 'services', - id: 'original-services', + label: i18n.t("Services"), + role: "services", + id: "original-services", }, sep, { - label: i18n.t('Hide'), - role: 'hide', - id: 'original-hide', + label: i18n.t("Hide"), + role: "hide", + id: "original-hide", }, { - label: i18n.t('Hide Others'), - role: 'hideothers', - id: 'original-hide-others', + label: i18n.t("Hide Others"), + role: "hideothers", + id: "original-hide-others", }, { - label: i18n.t('Show All'), - role: 'unhide', - id: 'original-show-all', + label: i18n.t("Show All"), + role: "unhide", + id: "original-show-all", }, sep, quitMenu, @@ -197,61 +216,61 @@ function getDefaultAppMenu(): AppMenu[] { : []), // { role: 'fileMenu' } { - label: i18n.t('File'), - id: 'original-file', + label: i18n.t("File"), + id: "original-file", submenu: [ isMac ? { - label: i18n.t('Close'), - role: 'close', - id: 'original-close', + label: i18n.t("Close"), + role: "close", + id: "original-close", } : quitMenu, ], }, // { role: 'editMenu' } { - label: i18n.t('Edit'), - id: 'original-edit', + label: i18n.t("Edit"), + id: "original-edit", submenu: [ { - label: i18n.t('Cut'), - role: 'cut', - id: 'original-cut', + label: i18n.t("Cut"), + role: "cut", + id: "original-cut", }, { - label: i18n.t('Copy'), - role: 'copy', - id: 'original-copy', + label: i18n.t("Copy"), + role: "copy", + id: "original-copy", }, { - label: i18n.t('Paste'), - role: 'paste', - id: 'original-paste', + label: i18n.t("Paste"), + role: "paste", + id: "original-paste", }, ...(isMac ? [ { - label: i18n.t('Paste and Match Style'), - role: 'pasteAndMatchStyle', - id: 'original-paste-and-match-style', + label: i18n.t("Paste and Match Style"), + role: "pasteAndMatchStyle", + id: "original-paste-and-match-style", }, deleteMenu, selectAllMenu, sep, { - label: i18n.t('Speech'), - id: 'original-speech', + label: i18n.t("Speech"), + id: "original-speech", submenu: [ { - label: i18n.t('Start Speaking'), - role: 'startspeaking', - id: 'original-start-speaking', + label: i18n.t("Start Speaking"), + role: "startspeaking", + id: "original-start-speaking", }, { - label: i18n.t('Stop Speaking'), - role: 'stopspeaking', - id: 'original-stop-speaking', + label: i18n.t("Stop Speaking"), + role: "stopspeaking", + id: "original-stop-speaking", }, ], }, @@ -261,96 +280,96 @@ function getDefaultAppMenu(): AppMenu[] { }, // { role: 'viewMenu' } { - label: i18n.t('View'), - id: 'original-view', + label: i18n.t("View"), + id: "original-view", submenu: [ { - label: i18n.t('Reload'), - role: 'forcereload', - id: 'original-force-reload', + label: i18n.t("Reload"), + role: "forcereload", + id: "original-force-reload", }, { - label: i18n.t('Toggle Developer Tools'), - role: 'toggledevtools', - id: 'original-toggle-dev-tools', + label: i18n.t("Toggle Developer Tools"), + role: "toggledevtools", + id: "original-toggle-dev-tools", }, sep, { - label: i18n.t('Reset Zoom'), - role: 'resetzoom', - id: 'original-reset-zoom', + label: i18n.t("Reset Zoom"), + role: "resetzoom", + id: "original-reset-zoom", }, { - label: i18n.t('Zoom In'), - role: 'zoomin', - id: 'original-zoom-in', + label: i18n.t("Zoom In"), + role: "zoomin", + id: "original-zoom-in", }, { - label: i18n.t('Zoom Out'), - role: 'zoomout', - id: 'original-zoom-out', + label: i18n.t("Zoom Out"), + role: "zoomout", + id: "original-zoom-out", }, sep, { - label: i18n.t('Toggle Fullscreen'), - role: 'togglefullscreen', - id: 'original-toggle-fullscreen', + label: i18n.t("Toggle Fullscreen"), + role: "togglefullscreen", + id: "original-toggle-fullscreen", }, ], }, { - label: i18n.t('Window'), - id: 'original-window', + label: i18n.t("Window"), + id: "original-window", submenu: [ { - label: i18n.t('Minimize'), - role: 'minimize', - id: 'original-minimize', + label: i18n.t("Minimize"), + role: "minimize", + id: "original-minimize", }, ...(isMac ? [ sep, { - label: i18n.t('Bring All to Front'), - role: 'front', - id: 'original-front', + label: i18n.t("Bring All to Front"), + role: "front", + id: "original-front", }, sep, { - label: i18n.t('Window'), - role: 'window', - id: 'original-window', + label: i18n.t("Window"), + role: "window", + id: "original-window", }, ] : [ { - label: i18n.t('Close'), - role: 'close', - id: 'original-close', + label: i18n.t("Close"), + role: "close", + id: "original-close", }, ]), ], }, { - label: i18n.t('Help'), - role: 'help', - id: 'original-help', + label: i18n.t("Help"), + role: "help", + id: "original-help", afterPlugins: true, submenu: [ { - label: i18n.t('Documentation'), - id: 'original-documentation', - url: 'https://headlamp.dev/docs/latest/', + label: i18n.t("Documentation"), + id: "original-documentation", + url: "https://headlamp.dev/docs/latest/", }, { - label: i18n.t('Open an Issue'), - id: 'original-open-issue', - url: 'https://github.com/headlamp-k8s/headlamp/issues', + label: i18n.t("Open an Issue"), + id: "original-open-issue", + url: "https://github.com/headlamp-k8s/headlamp/issues", }, { - label: i18n.t('About'), - id: 'original-about', - url: 'https://github.com/headlamp-k8s/headlamp', + label: i18n.t("About"), + id: "original-about", + url: "https://github.com/headlamp-k8s/headlamp", }, ], }, @@ -415,14 +434,15 @@ function updateMenuLabels(menus: AppMenu[]) { // Replace all labels with default labels if the default and current // menu ids are the same. - menusList.forEach(menu => { + menusList.forEach((menu) => { if (!!menu.label && defaultMenusObj[menu.id]) { menu.label = defaultMenusObj[menu.id].label; } }); } -export interface AppMenu extends Omit, 'click'> { +export interface AppMenu + extends Omit, "click"> { /** A URL to open (if not starting with http, then it'll be opened in the external browser) */ url?: string; /** The submenus of this menu */ @@ -434,9 +454,12 @@ export interface AppMenu extends Omit, 'clic afterPlugins?: boolean; } -function menusToTemplate(mainWindow: BrowserWindow | null, menusFromPlugins: AppMenu[]) { +function menusToTemplate( + mainWindow: BrowserWindow | null, + menusFromPlugins: AppMenu[] +) { const menusToDisplay: MenuItemConstructorOptions[] = []; - menusFromPlugins.forEach(appMenu => { + menusFromPlugins.forEach((appMenu) => { const { url, afterPlugins = false, ...otherProps } = appMenu; const menu: MenuItemConstructorOptions = otherProps; @@ -447,7 +470,7 @@ function menusToTemplate(mainWindow: BrowserWindow | null, menusFromPlugins: App if (!!url) { menu.click = async () => { // Open external links in the external browser. - if (!!mainWindow && !url.startsWith('http')) { + if (!!mainWindow && !url.startsWith("http")) { mainWindow.webContents.loadURL(url); } else { await shell.openExternal(url); @@ -467,26 +490,26 @@ function menusToTemplate(mainWindow: BrowserWindow | null, menusFromPlugins: App } async function getRunningHeadlampPIDs() { - const processes = await find_process('name', 'headlamp-server.*'); + const processes = await find_process("name", "headlamp-server.*"); if (processes.length === 0) { return null; } - return processes.map(pInfo => pInfo.pid); + return processes.map((pInfo) => pInfo.pid); } function killProcess(pid: number) { - if (process.platform === 'win32') { + if (process.platform === "win32") { // Otherwise on Windows the process will stick around. - execSync('taskkill /pid ' + pid + ' /T /F'); + execSync("taskkill /pid " + pid + " /T /F"); } else { - process.kill(pid, 'SIGHUP'); + process.kill(pid, "SIGHUP"); } } function startElecron() { - log.transports.file.level = 'info'; - log.info('App starting...'); + log.transports.file.level = "info"; + log.info("App starting..."); let appVersion: string; if (isDev && process.env.HEADLAMP_APP_VERSION) { @@ -496,21 +519,117 @@ function startElecron() { appVersion = app.getVersion(); } - console.log('Check for updates: ', shouldCheckForUpdates); + console.log("Check for updates: ", shouldCheckForUpdates); + + class PluginEventListeners { + private pluginManager: PluginManager; + + constructor() { + this.pluginManager = new PluginManager(); + + this.setupEventHandlers(); + } + + setupEventHandlers() { + console.log("setting up event handlers") + ipcMain.on("install-plugin-progress", (event, progressData) => { + event.sender.send("plugin-manager-progress", progressData); + }); + + ipcMain.on("update-plugin-progress", (event, progressData) => { + event.sender.send("plugin-manager-progress", progressData); + }); + + ipcMain.on("uninstall-plugin-progress", (event, progressData) => { + event.sender.send("plugin-manager-progress", progressData); + }); + + ipcMain.on("list-plugins-progress", (event, progressData) => { + event.sender.send("plugin-manager-progress", progressData); + }); + + ipcMain.on( + "install-plugin", + (event, URL, destinationFolder, headlampVersion) => { + const controller = new AbortController(); + const cancelSignal = controller.signal; + + ipcMain.on("cancel-install", () => { + controller.abort(); + }); + + this.pluginManager.install( + URL, + destinationFolder, + headlampVersion, + (progressData) => { + event.sender.send("install-plugin-progress", progressData); + }, + cancelSignal + ); + } + ); + + ipcMain.on( + "update-plugin", + (event, pluginName, destinationFolder, headlampVersion) => { + const controller = new AbortController(); + const cancelSignal = controller.signal; + + ipcMain.on("cancel-update", () => { + controller.abort(); + }); + + this.pluginManager.update( + pluginName, + destinationFolder, + headlampVersion, + (progressData) => { + event.sender.send("update-plugin-progress", progressData); + }, + cancelSignal + ); + } + ); + + ipcMain.on("uninstall-plugin", (event, pluginName, folder) => { + const controller = new AbortController(); + const cancelSignal = controller.signal; + + ipcMain.on("cancel-uninstall", () => { + controller.abort(); + }); + + this.pluginManager.uninstall( + pluginName, + folder, + (progressData) => { + event.sender.send("uninstall-plugin-progress", progressData); + } + ); + }); + + ipcMain.on("list-plugins", (event, folder) => { + this.pluginManager.list(folder, (progressData) => { + event.sender.send("list-plugins-progress", progressData); + }); + }); + } + } async function createWindow() { - let frontendPath = ''; + let frontendPath = ""; if (isDev) { - frontendPath = path.resolve('..', 'frontend', 'build', 'index.html'); + frontendPath = path.resolve("..", "frontend", "build", "index.html"); } else { - frontendPath = path.join(process.resourcesPath, 'frontend', 'index.html'); + frontendPath = path.join(process.resourcesPath, "frontend", "index.html"); } const startUrl = process.env.ELECTRON_START_URL || url.format({ pathname: frontendPath, - protocol: 'file:', + protocol: "file:", slashes: true, query: { backendToken: backendToken, @@ -519,7 +638,10 @@ function startElecron() { // WSL has a problem with full size window placement, so make it smaller. const withMargin = isWSL(); - const { width, height } = windowSize(screen.getPrimaryDisplay().workAreaSize, withMargin); + const { width, height } = windowSize( + screen.getPrimaryDisplay().workAreaSize, + withMargin + ); mainWindow = new BrowserWindow({ width, @@ -536,25 +658,25 @@ function startElecron() { mainWindow.webContents.setWindowOpenHandler(({ url }) => { // allow all urls starting with app startUrl to open in electron if (url.startsWith(startUrl)) { - return { action: 'allow' }; + return { action: "allow" }; } // otherwise open url in a browser and prevent default shell.openExternal(url); - return { action: 'deny' }; + return { action: "deny" }; }); - mainWindow.webContents.on('dom-ready', () => { - mainWindow?.webContents.send('currentMenu', getDefaultAppMenu()); + mainWindow.webContents.on("dom-ready", () => { + mainWindow?.webContents.send("currentMenu", getDefaultAppMenu()); }); - mainWindow.on('closed', () => { + mainWindow.on("closed", () => { mainWindow = null; }); // Force Single Instance Application const gotTheLock = app.requestSingleInstanceLock(); if (gotTheLock) { - app.on('second-instance', () => { + app.on("second-instance", () => { // Someone tried to run a second instance, we should focus our window. if (mainWindow) { if (mainWindow.isMinimized()) mainWindow.restore(); @@ -570,22 +692,22 @@ function startElecron() { if a library is trying to open a url other than app url in electron take it to the default browser */ - mainWindow.webContents.on('will-navigate', (event, url) => { + mainWindow.webContents.on("will-navigate", (event, url) => { if (url.startsWith(startUrl)) { return; } shell.openExternal(url); }); - app.on('open-url', (event, url) => { + app.on("open-url", (event, url) => { mainWindow?.focus(); let urlObj; try { urlObj = new URL(url); } catch (e) { dialog.showErrorBox( - i18n.t('Invalid URL'), - i18n.t('Application opened with an invalid URL: {{ url }}', { url }) + i18n.t("Invalid URL"), + i18n.t("Application opened with an invalid URL: {{ url }}", { url }) ); return; } @@ -593,45 +715,47 @@ function startElecron() { const urlParam = urlObj.hostname; let baseUrl = startUrl; // this check helps us to avoid adding multiple / to the startUrl when appending the incoming url to it - if (baseUrl.endsWith('/')) { + if (baseUrl.endsWith("/")) { baseUrl = baseUrl.slice(0, startUrl.length - 1); } // load the index.html from build and route to the hostname received in the protocol handler url - mainWindow?.loadURL(baseUrl + '#' + urlParam + urlObj.search); + mainWindow?.loadURL(baseUrl + "#" + urlParam + urlObj.search); }); - i18n.on('languageChanged', () => { + i18n.on("languageChanged", () => { updateMenuLabels(currentMenu); setMenu(mainWindow, currentMenu); }); - ipcMain.on('appConfig', () => { - mainWindow?.webContents.send('appConfig', { + ipcMain.on("appConfig", () => { + mainWindow?.webContents.send("appConfig", { checkForUpdates: shouldCheckForUpdates, appVersion, }); }); - ipcMain.on('pluginsLoaded', () => { + ipcMain.on("pluginsLoaded", () => { loadFullMenu = true; - console.info('Plugins are loaded. Loading full menu.'); + console.info("Plugins are loaded. Loading full menu."); setMenu(mainWindow, currentMenu); }); - ipcMain.on('setMenu', (event: IpcMainEvent, menus: any) => { + ipcMain.on("setMenu", (event: IpcMainEvent, menus: any) => { if (!mainWindow) { return; } // We don't even process this call if we're running in headless mode. if (isHeadlessMode) { - console.log('Ignoring menu change from plugins because of headless mode.'); + console.log( + "Ignoring menu change from plugins because of headless mode." + ); return; } // Ignore the menu change if we received null. if (!menus) { - console.log('Ignoring menu change from plugins because null was sent.'); + console.log("Ignoring menu change from plugins because null was sent."); return; } @@ -641,12 +765,14 @@ function startElecron() { setMenu(mainWindow, menus); }); - ipcMain.on('locale', (event: IpcMainEvent, newLocale: string) => { + ipcMain.on("locale", (event: IpcMainEvent, newLocale: string) => { if (!!newLocale && i18n.language !== newLocale) { i18n.changeLanguage(newLocale); } }); + // new PluginEventListeners().setupEventHandlers(); + /** * Data sent from the renderer process when a 'run-command' event is emitted. */ @@ -674,14 +800,17 @@ function startElecron() { * @param event - The event object. * @param eventData - The data sent from the renderer process. */ - function handleRunCommand(event: IpcMainEvent, eventData: CommandData): void { + function handleRunCommand( + event: IpcMainEvent, + eventData: CommandData + ): void { // Only allow "minikube", and "az" commands - const validCommands = ['minikube', 'az']; + const validCommands = ["minikube", "az"]; if (!validCommands.includes(eventData.command)) { console.error( - `Invalid command: ${eventData.command}, only valid commands are: ${JSON.stringify( - validCommands - )}` + `Invalid command: ${ + eventData.command + }, only valid commands are: ${JSON.stringify(validCommands)}` ); return; } @@ -692,73 +821,75 @@ function startElecron() { eventData.options ); - child.stdout.on('data', (data: string | Buffer) => { - event.sender.send('command-stdout', eventData.id, data.toString()); + child.stdout.on("data", (data: string | Buffer) => { + event.sender.send("command-stdout", eventData.id, data.toString()); }); - child.stderr.on('data', (data: string | Buffer) => { - event.sender.send('command-stderr', eventData.id, data.toString()); + child.stderr.on("data", (data: string | Buffer) => { + event.sender.send("command-stderr", eventData.id, data.toString()); }); - child.on('exit', (code: number | null) => { - event.sender.send('command-exit', eventData.id, code); + child.on("exit", (code: number | null) => { + event.sender.send("command-exit", eventData.id, code); }); } - ipcMain.on('run-command', handleRunCommand); + 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); + // 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); + + ipcMain.on(""); if (!useExternalServer) { const runningHeadlamp = await getRunningHeadlampPIDs(); @@ -767,16 +898,16 @@ function startElecron() { if (!!runningHeadlamp) { const resp = dialog.showMessageBoxSync(mainWindow, { // Avoiding mentioning Headlamp here because it may run under a different name depending on branding (plugins). - title: i18n.t('Another process is running'), + title: i18n.t("Another process is running"), message: i18n.t( - 'Looks like another process is already running. Continue by terminating that process automatically, or quit?' + "Looks like another process is already running. Continue by terminating that process automatically, or quit?" ), - type: 'question', - buttons: [i18n.t('Continue'), i18n.t('Quit')], + type: "question", + buttons: [i18n.t("Continue"), i18n.t("Quit")], }); if (resp === 0) { - runningHeadlamp.forEach(pid => { + runningHeadlamp.forEach((pid) => { try { killProcess(pid); } catch (e) { @@ -805,12 +936,14 @@ function startElecron() { // Wait (10 * powers of 2) ms with a max of 250 ms const waitTime = Math.min(10 * tries ** 2, 250); // ms - await new Promise(f => setTimeout(f, waitTime)); + await new Promise((f) => setTimeout(f, waitTime)); timeWaited += waitTime; stillRunning = !!(await getRunningHeadlampPIDs()); - console.debug(stillRunning ? 'Still running...' : 'No longer running!'); + console.debug( + stillRunning ? "Still running..." : "No longer running!" + ); } } @@ -818,8 +951,8 @@ function startElecron() { const processes = await getRunningHeadlampPIDs(); if (!!processes) { dialog.showMessageBoxSync({ - type: 'warning', - title: i18n.t('Failed to quit the other running process'), + type: "warning", + title: i18n.t("Failed to quit the other running process"), message: i18n.t( `Could not quit the other running process, PIDs: {{ process_list }}. Please stop that process and relaunch the app.`, { process_list: processes } @@ -839,14 +972,16 @@ function startElecron() { } if (disableGPU) { - log.info('Disabling GPU hardware acceleration. Reason: related flag is set.'); + log.info( + "Disabling GPU hardware acceleration. Reason: related flag is set." + ); } else if ( disableGPU === undefined && - process.platform === 'linux' && - ['arm', 'arm64'].includes(process.arch) + process.platform === "linux" && + ["arm", "arm64"].includes(process.arch) ) { log.info( - 'Disabling GPU hardware acceleration. Reason: known graphical issues in Linux on ARM (use --disable-gpu=false to force it if needed).' + "Disabling GPU hardware acceleration. Reason: known graphical issues in Linux on ARM (use --disable-gpu=false to force it if needed)." ); disableGPU = true; } @@ -855,46 +990,48 @@ function startElecron() { app.disableHardwareAcceleration(); } - app.on('ready', createWindow); - app.on('activate', function () { + app.on("ready", createWindow); + app.on("activate", function () { if (mainWindow === null) { createWindow(); } }); - app.once('window-all-closed', app.quit); + app.once("window-all-closed", app.quit); - app.once('before-quit', () => { - i18n.off('languageChanged'); + app.once("before-quit", () => { + i18n.off("languageChanged"); if (mainWindow) { - mainWindow.removeAllListeners('close'); + mainWindow.removeAllListeners("close"); } }); } -app.on('quit', quitServerProcess); +app.on("quit", quitServerProcess); /** * add some error handlers to the serverProcess. * @param {ChildProcess} serverProcess to attach the error handlers to. */ -function attachServerEventHandlers(serverProcess: ChildProcessWithoutNullStreams) { - serverProcess.on('error', err => { +function attachServerEventHandlers( + serverProcess: ChildProcessWithoutNullStreams +) { + serverProcess.on("error", (err) => { log.error(`server process failed to start: ${err}`); }); - serverProcess.stdout.on('data', data => { + serverProcess.stdout.on("data", (data) => { log.info(`server process stdout: ${data}`); }); - serverProcess.stderr.on('data', data => { + serverProcess.stderr.on("data", (data) => { const sterrMessage = `server process stderr: ${data}`; - if (data && data.indexOf && data.indexOf('Requesting') !== -1) { + if (data && data.indexOf && data.indexOf("Requesting") !== -1) { // The server prints out urls it's getting, which aren't errors. log.info(sterrMessage); } else { log.error(sterrMessage); } }); - serverProcess.on('close', (code, signal) => { + serverProcess.on("close", (code, signal) => { const closeMessage = `server process process exited with code:${code} signal:${signal}`; if (!intentionalQuit) { // @todo: message mainWindow, or loadURL to an error url? @@ -907,7 +1044,10 @@ function attachServerEventHandlers(serverProcess: ChildProcessWithoutNullStreams } if (isHeadlessMode) { - serverProcess = startServer(['-html-static-dir', path.join(process.resourcesPath, './frontend')]); + serverProcess = startServer([ + "-html-static-dir", + path.join(process.resourcesPath, "./frontend"), + ]); attachServerEventHandlers(serverProcess); (async () => { await open(`http://localhost:${defaultPort}`); 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..f527d39dab6 --- /dev/null +++ b/plugins/headlamp-plugin/plugin-management.js @@ -0,0 +1,374 @@ +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'); + +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 + ); + // 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 - 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, + 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 + ); + // 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 + const pluginInfo = await fetchPluginInfo(URL, progressCallback, signal); + 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'); + } + } + + // 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' }); + + const archResponse = await fetch(archiveURL, { redirect: 'follow', follow: 10 }, { signal }); + if (!archResponse.ok) { + throw new Error(`Failed to download tarball. Status code: ${archResponse.status}`); + } + + 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.'); + } + + 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); + }); + }); + + 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' }); + }); + +});