diff --git a/README.md b/README.md index 3348a0b..ed786b8 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,6 @@ Minecluster or MCL is a web interface used to manage multiple instance of Minecraft Servers in Kubernetes. This app is built to be an all in one for self-hosting Minecraft server. It uses rendered helm charts based on itzg/minecraft-server More info coming soon. + +## ⚠ Warning ⚠ +Development is very active and there is no garuntee for compatability or migration across versions 1/15/24 diff --git a/lib/controllers/file-controller.js b/lib/controllers/file-controller.js index 36aa0b0..1a92c17 100644 --- a/lib/controllers/file-controller.js +++ b/lib/controllers/file-controller.js @@ -10,7 +10,7 @@ import { sendError } from "../util/ExpressClientError.js"; export async function listFiles(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); + if (!serverSpec.id) return res.status(400).send("Server id missing!"); listServerFiles(serverSpec) .then((f) => { const fileData = f.map((fi, i) => ({ @@ -29,7 +29,7 @@ export async function listFiles(req, res) { export async function createFolder(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); + if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); createServerFolder(serverSpec) .then(() => res.sendStatus(200)) @@ -39,7 +39,7 @@ export async function createFolder(req, res) { export async function deleteItem(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); - if (!serverSpec.name) return res.status(400).send("Server name required!"); + if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); if (serverSpec.isDir === undefined || serverSpec.isDir === null) return res.status(400).send("IsDIr required!"); @@ -50,7 +50,7 @@ export async function deleteItem(req, res) { export async function uploadItem(req, res) { const serverSpec = req.body; - if (!serverSpec.name) return res.status(400).send("Server name required!"); + if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); uploadServerItem(serverSpec, req.file) .then(() => res.sendStatus(200)) @@ -59,7 +59,7 @@ export async function uploadItem(req, res) { export async function getItem(req, res) { const serverSpec = req.body; - if (!serverSpec.name) return res.status(400).send("Server name required!"); + if (!serverSpec.id) return res.status(400).send("Server id missing!"); if (!serverSpec.path) return res.status(400).send("Path required!"); getServerItem(serverSpec, res) .then(({ ds, ftpTransfer }) => { diff --git a/lib/controllers/lifecycle-controller.js b/lib/controllers/lifecycle-controller.js index 5b0105e..96073ed 100644 --- a/lib/controllers/lifecycle-controller.js +++ b/lib/controllers/lifecycle-controller.js @@ -6,43 +6,61 @@ import { getServerEntry, } from "../database/queries/server-queries.js"; import { sendError } from "../util/ExpressClientError.js"; -import { - startServerContainer, - stopServerContainer, -} from "../k8s/server-control.js"; import { toggleServer } from "../k8s/k8s-server-control.js"; +const dnsRegex = new RegExp( + `^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`, +); + function payloadFilter(req, res) { const serverSpec = req.body; if (!serverSpec) return res.sendStatus(400); - const { name, host, version, serverType, difficulty, gamemode, memory } = + const { name, host, version, serverType, memory } = serverSpec; + const { backupHost, backupBucket, backupId, backupKey, backupInterval } = serverSpec; if (!name) return res.status(400).send("Server name is required!"); if (!host) return res.status(400).send("Server host is required!"); + if (!dnsRegex.test(host)) return res.status(400).send("Hostname invalid!"); if (!version) return res.status(400).send("Server version is required!"); - if (!difficulty) - return res.status(400).send("Server difficulty is required!"); if (!serverType) return res.status(400).send("Server type is required!"); - if (!gamemode) return res.status(400).send("Server Gamemode is required!"); if (!memory) return res.status(400).send("Memory is required!"); - req.body.name = req.body.name.toLowerCase(); + // TODO: Impliment non creation time backups + if ( + !!backupHost || + !!backupBucket || + !!backupId || + !!backupKey || + !!backupInterval + ) { + // If any keys are required, all are required + if ( + !( + !!backupHost && + !!backupBucket && + !!backupId && + !!backupKey && + !!backupInterval + ) + ) + return res.status(400).send("All backup keys are required!"); + if (!dnsRegex.test(backupHost)) + return res.status(400).send("Backup Host invalid!"); + } return "filtered"; } -function checkServerName(serverSpec) { +function checkServerId(serverSpec) { if (!serverSpec) throw new ExpressClientError({ c: 400 }); - if (!serverSpec.name) - throw new ExpressClientError({ c: 400, m: "Server name required!" }); + if (!serverSpec.id) + throw new ExpressClientError({ c: 400, m: "Server id missing!" }); } export async function createServer(req, res) { if (payloadFilter(req, res) !== "filtered") return; const serverSpec = req.body; try { - const serverSpecs = await getServerEntry(serverSpec.name); - if (serverSpecs.length !== 0) throw Error("Server already exists in DB!"); - await createServerResources(serverSpec); - await createServerEntry(serverSpec); + const serverEntry = await createServerEntry(serverSpec); + await createServerResources(serverEntry); res.sendStatus(200); } catch (e) { sendError(res)(e); @@ -53,11 +71,11 @@ export async function deleteServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerName(serverSpec); + checkServerId(serverSpec); } catch (e) { return sendError(res)(e); } - const deleteEntry = deleteServerEntry(serverSpec.name); + const deleteEntry = deleteServerEntry(serverSpec.id); const deleteResources = deleteServerResources(serverSpec); Promise.all([deleteEntry, deleteResources]) .then(() => res.sendStatus(200)) @@ -68,12 +86,12 @@ export async function startServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerName(serverSpec); + checkServerId(serverSpec); } catch (e) { return sendError(res)(e); } - const { name } = serverSpec; - toggleServer(name, true) + const { id } = serverSpec; + toggleServer(id, true) .then(() => res.sendStatus(200)) .catch(sendError(res)); } @@ -82,12 +100,12 @@ export async function stopServer(req, res) { // Ensure spec is safe const serverSpec = req.body; try { - checkServerName(serverSpec); + checkServerId(serverSpec); } catch (e) { return sendError(res)(e); } - const { name } = serverSpec; - toggleServer(name, false) + const { id } = serverSpec; + toggleServer(id, false) .then(() => res.sendStatus(200)) .catch(sendError(res)); } diff --git a/lib/controllers/status-controller.js b/lib/controllers/status-controller.js index b2fa8b7..cd5ff57 100644 --- a/lib/controllers/status-controller.js +++ b/lib/controllers/status-controller.js @@ -1,12 +1,12 @@ import { getDeployments } from "../k8s/k8s-server-control.js"; -import { getInstances } from "../k8s/server-control.js"; +import { getInstances } from "../k8s/server-status.js"; import { sendError } from "../util/ExpressClientError.js"; export function serverList(req, res) { getDeployments() .then((sd) => res.json(sd.map((s) => s.metadata.name.substring(4)))) .catch((e) => { - ERR("SERVER CONTROL", e); + ERR("STATUS CONTROLLER", e); res.status(500).send("Couldn't get server list"); }); } diff --git a/lib/controllers/sub-controllers/console-controller.js b/lib/controllers/sub-controllers/console-controller.js index 40cf777..264146a 100644 --- a/lib/controllers/sub-controllers/console-controller.js +++ b/lib/controllers/sub-controllers/console-controller.js @@ -3,6 +3,7 @@ import k8s from "@kubernetes/client-node"; import { Rcon as RconClient } from "rcon-client"; import stream from "stream"; import { ERR, WARN } from "../../util/logging.js"; +import { getServerEntry } from "../../database/queries/server-queries.js"; // Kubernetes Configuration const kc = new k8s.KubeConfig(); @@ -12,8 +13,9 @@ const namespace = process.env.MCL_SERVER_NAMESPACE; // Retrieves logs from the minecraft server container export async function webConsoleLogs(socket) { - const { serverName } = socket.mcs; - const podName = `mcl-${serverName}`; + const { serverId } = socket.mcs; + const server = await getServerEntry(serverId); + const podName = `mcl-${server.mclName}`; const containerName = `${podName}-server`; const podResponse = await k8sCore.listNamespacedPod(namespace); const pods = podResponse.body.items.map((vp1) => vp1.metadata.name); @@ -41,14 +43,15 @@ export async function webConsoleLogs(socket) { export async function webConsoleRcon(socket) { if (socket.rconClient) return VERB("RCON", "Socket already connected to RCON"); - const rconSecret = `mcl-${socket.mcs.serverName}-rcon-secret`; + const { serverId } = socket.mcs; + const server = await getServerEntry(serverId); + const rconSecret = `mcl-${server.mclName}-rcon-secret`; const rconRes = await k8sCore.readNamespacedSecret(rconSecret, namespace); const rconPassword = Buffer.from( rconRes.body.data["rcon-password"], "base64", ).toString("utf8"); - const { serverName } = socket.mcs; - const rconHost = `mcl-${serverName}-rcon.${namespace}.svc.cluster.local`; + const rconHost = `mcl-${server.mclName}-rcon.${namespace}.svc.cluster.local`; const rcon = new RconClient({ host: rconHost, port: 25575, @@ -58,7 +61,7 @@ export async function webConsoleRcon(socket) { try { await rcon.connect(); } catch (error) { - socket.emit("push", "Could not connect RCON Input to server!"); + socket.emit("rcon-error", "Could not connect RCON Input to server!"); WARN("RCON", `Could not connect to '${rconHost}'`); } socket.rconClient = rcon; diff --git a/lib/database/migrations/1_create_servers_table.sql b/lib/database/migrations/1_create_servers_table.sql index 5306630..2f22691 100644 --- a/lib/database/migrations/1_create_servers_table.sql +++ b/lib/database/migrations/1_create_servers_table.sql @@ -1,12 +1,18 @@ CREATE SEQUENCE servers_id_seq; CREATE TABLE servers ( id bigint NOT NULL DEFAULT nextval('servers_id_seq') PRIMARY KEY, - name varchar(255) DEFAULT NULL, host varchar(255) DEFAULT NULL, + name varchar(255) DEFAULT NULL, version varchar(63) DEFAULT 'latest', server_type varchar(63) DEFAULT 'VANILLA', + cpu varchar(63) DEFAULT '500', memory varchar(63) DEFAULT '512', - CONSTRAINT unique_name UNIQUE(name), + backup_enabled BOOLEAN DEFAULT FALSE, + backup_host varchar(255) DEFAULT NULL, + backup_bucket_path varchar(255) DEFAULT NULL, + backup_id varchar(255) DEFAULT NULL, + backup_key varchar(255) DEFAULT NULL, + backup_interval varchar(255) DEFAULT NULL, CONSTRAINT unique_host UNIQUE(host) ); ALTER SEQUENCE servers_id_seq OWNED BY servers.id; \ No newline at end of file diff --git a/lib/database/queries/server-queries.js b/lib/database/queries/server-queries.js index d8baa8f..15bf2ac 100644 --- a/lib/database/queries/server-queries.js +++ b/lib/database/queries/server-queries.js @@ -7,35 +7,122 @@ const asExpressClientError = (e) => { throw new ExpressClientError({ m: e.message, c: 409 }); }; +const getMclName = (host, id) => `${host.replaceAll(".", "-")}-${id}`; + export async function createServerEntry(serverSpec) { - const { name, host, version, serverType: server_type, memory } = serverSpec; - const q = insertQuery(table, { name, host, version, server_type, memory }); + const { + name, + host, + version, + serverType: server_type, + memory, + backupHost: backup_host, + backupBucket: backup_bucket_path, + backupId: backup_id, + backupKey: backup_key, + backupInterval: backup_interval, + } = serverSpec; + var q = insertQuery(table, { + name, + host, + version, + server_type, + memory, + backup_enabled: !!backup_interval, // We already verified the payload, so any backup key will work + backup_host, + backup_bucket_path, + backup_id, + backup_key, + backup_interval, + }); + q += "\n RETURNING *"; + try { + const entries = await pg.query(q); + const { + id, + name, + host, + version, + server_type: serverType, + memory, + backup_enabled: backupEnabled, + backup_host: backupHost, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, + } = entries[0]; + const mclName = getMclName(host, id); + return { + name, + mclName, + id, + host, + version, + serverType, + memory, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; + } catch (e) { + asExpressClientError(e); + } +} + +export async function deleteServerEntry(serverId) { + if (!serverId) asExpressClientError({ message: "Server ID Required!" }); + const q = deleteQuery(table, { id: serverId }); return pg.query(q).catch(asExpressClientError); } -export async function deleteServerEntry(serverName) { - if (!serverName) asExpressClientError({ message: "Server Name Required!" }); - const q = deleteQuery(table, { name: serverName }); - return pg.query(q).catch(asExpressClientError); -} - -export async function getServerEntry(serverName) { - if (!serverName) asExpressClientError({ message: "Server Name Required!" }); - const q = selectWhereQuery(table, { name: serverName }); +export async function getServerEntry(serverId) { + if (!serverId) asExpressClientError({ message: "Server ID Required!" }); + const q = selectWhereQuery(table, { id: serverId }); try { const serverSpecs = await pg.query(q); if (serverSpecs.length === 0) return []; if (!serverSpecs.length === 1) throw Error("Multiple servers found with the same name!"); const { + id, name, host, version, server_type: serverType, memory, + backup_enabled: backupEnabled, + backup_host: backupHost, + backup_bucket_path: backupPath, + backup_id: backupId, + backup_key: backupKey, + backup_interval: backupInterval, } = serverSpecs[0]; - return { name, host, version, serverType, memory }; + const mclName = getMclName(host, id); + return { + name, + mclName, + id, + host, + version, + serverType, + memory, + backupEnabled, + backupHost, + backupPath, + backupId, + backupKey, + backupInterval, + }; } catch (e) { asExpressClientError(e); } } + +export async function getServerEntries() { + const q = `SELECT * FROM ${table}`; + return pg.query(q); +} diff --git a/lib/k8s/configs/backup-secret.yml b/lib/k8s/configs/backup-secret.yml new file mode 100644 index 0000000..eae59e3 --- /dev/null +++ b/lib/k8s/configs/backup-secret.yml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Secret +metadata: + annotations: + minecluster.dunemask.net/id: changeme-server-id + labels: + app: changeme-app-label + name: changeme-backup-secret +type: Opaque +data: + rclone.conf: "" diff --git a/lib/k8s/configs/containers/ftp-server.yml b/lib/k8s/configs/containers/ftp-server.yml index 759bc20..aade99b 100644 --- a/lib/k8s/configs/containers/ftp-server.yml +++ b/lib/k8s/configs/containers/ftp-server.yml @@ -6,23 +6,21 @@ env: image: garethflowers/ftp-server imagePullPolicy: IfNotPresent livenessProbe: -exec: - command: ["echo"] -failureThreshold: 20 -initialDelaySeconds: 30 -periodSeconds: 5 -successThreshold: 1 -timeoutSeconds: 1 + exec: { command: ["echo"] } + failureThreshold: 20 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 name: changeme-name-ftp ports: [] # Programatically add all the ports for easier readability, Ports include: 20,21,40000-400009 readinessProbe: - exec: - command: ["echo"] - failureThreshold: 20 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 + exec: { command: ["echo"] } + failureThreshold: 20 + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + timeoutSeconds: 1 resources: requests: cpu: 50m diff --git a/lib/k8s/configs/containers/minecraft-backup.yml b/lib/k8s/configs/containers/minecraft-backup.yml index 074025a..3caeae8 100644 --- a/lib/k8s/configs/containers/minecraft-backup.yml +++ b/lib/k8s/configs/containers/minecraft-backup.yml @@ -33,20 +33,20 @@ env: - name: DEST_DIR value: /backups - name: LINK_LATEST - value: "false" + value: "true" - name: TAR_COMPRESS_METHOD value: gzip - name: ZSTD_PARAMETERS value: -3 --long=25 --single-thread - name: RCLONE_REMOTE - value: mc-dunemask-net + value: mcl-backup-changeme - name: RCLONE_DEST_DIR - value: /minecraft-backups/deltasmp-backups + value: /mcl/backups/changeme - name: RCLONE_COMPRESS_METHOD value: gzip image: itzg/mc-backup:latest imagePullPolicy: IfNotPresent -name: mcs-deltasmp-minecraft-mc-backup +name: mcl-backup-changeme resources: requests: cpu: 500m diff --git a/lib/k8s/configs/containers/minecraft-server.yml b/lib/k8s/configs/containers/minecraft-server.yml index 4034e36..56e9b1b 100644 --- a/lib/k8s/configs/containers/minecraft-server.yml +++ b/lib/k8s/configs/containers/minecraft-server.yml @@ -72,12 +72,10 @@ env: image: itzg/minecraft-server:latest imagePullPolicy: IfNotPresent livenessProbe: - exec: - command: - - mc-health - failureThreshold: 20 + exec: { command: [mc-health] } + failureThreshold: 200 initialDelaySeconds: 30 - periodSeconds: 5 + periodSeconds: 3 successThreshold: 1 timeoutSeconds: 1 name: changeme-name-server @@ -88,15 +86,13 @@ ports: - containerPort: 25575 name: rcon protocol: TCP -readinessProbe: - exec: - command: - - mc-health - failureThreshold: 20 - initialDelaySeconds: 30 - periodSeconds: 5 - successThreshold: 1 - timeoutSeconds: 1 +# readinessProbe: # Disabling this allows for users to manipulate files even if the container is starting +# exec: {command: [mc-health]} +# failureThreshold: 200 +# initialDelaySeconds: 30 +# periodSeconds: 3 +# successThreshold: 1 +# timeoutSeconds: 1 resources: requests: cpu: 500m diff --git a/lib/k8s/configs/rcon-secret.yml b/lib/k8s/configs/rcon-secret.yml index ff52e1a..a90c319 100644 --- a/lib/k8s/configs/rcon-secret.yml +++ b/lib/k8s/configs/rcon-secret.yml @@ -4,7 +4,7 @@ data: kind: Secret metadata: annotations: - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app-label name: changeme-rcon-secret diff --git a/lib/k8s/configs/rcon-svc.yml b/lib/k8s/configs/rcon-svc.yml index 9a68813..49a7089 100644 --- a/lib/k8s/configs/rcon-svc.yml +++ b/lib/k8s/configs/rcon-svc.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app name: changeme-rcon diff --git a/lib/k8s/configs/server-deployment.yml b/lib/k8s/configs/server-deployment.yml index 0d5d335..275013a 100644 --- a/lib/k8s/configs/server-deployment.yml +++ b/lib/k8s/configs/server-deployment.yml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id name: changeme-name namespace: changeme-namespace spec: @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app spec: @@ -35,10 +35,10 @@ spec: claimName: changeme-pvc-name - emptyDir: {} name: backupdir - - name: rclone-config - secret: - defaultMode: 420 - items: - - key: rclone.conf - path: rclone.conf - secretName: rclone-config + # - name: rclone-config + # secret: + # defaultMode: 420 + # items: + # - key: rclone.conf + # path: rclone.conf + # secretName: rclone-config diff --git a/lib/k8s/configs/server-pvc.yml b/lib/k8s/configs/server-pvc.yml index f502e8a..bf21ea4 100644 --- a/lib/k8s/configs/server-pvc.yml +++ b/lib/k8s/configs/server-pvc.yml @@ -2,7 +2,7 @@ apiVersion: v1 kind: PersistentVolumeClaim metadata: annotations: - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id labels: service: changeme-service-name name: changeme-pvc-name diff --git a/lib/k8s/configs/server-svc.yml b/lib/k8s/configs/server-svc.yml index c7e7fa2..f21db9a 100644 --- a/lib/k8s/configs/server-svc.yml +++ b/lib/k8s/configs/server-svc.yml @@ -4,7 +4,7 @@ metadata: annotations: ingress.qumine.io/hostname: changeme-url ingress.qumine.io/portname: minecraft - minecluster.dunemask.net/server-name: changeme-server-name + minecluster.dunemask.net/id: changeme-server-id labels: app: changeme-app name: changeme-name diff --git a/lib/k8s/k8s-server-control.js b/lib/k8s/k8s-server-control.js index 4e5233d..ec40b90 100644 --- a/lib/k8s/k8s-server-control.js +++ b/lib/k8s/k8s-server-control.js @@ -20,10 +20,10 @@ const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); const mineclusterManaged = (o) => o.metadata && o.metadata.annotations && - o.metadata.annotations["minecluster.dunemask.net/server-name"] !== undefined; + o.metadata.annotations["minecluster.dunemask.net/id"] !== undefined; -export const serverMatch = (serverName) => (o) => - o.metadata.annotations["minecluster.dunemask.net/server-name"] === serverName; +export const serverMatch = (serverId) => (o) => + o.metadata.annotations["minecluster.dunemask.net/id"] === serverId; export async function getDeployments() { const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); @@ -50,8 +50,8 @@ export async function getVolumes() { return serverVolumes; } -export function getServerAssets(serverName) { - const serverFilter = serverMatch(serverName); +export function getServerAssets(serverId) { + const serverFilter = serverMatch(serverId); return Promise.all([ getDeployments(), getServices(), @@ -66,17 +66,18 @@ export function getServerAssets(serverName) { if (deployments.length > 1) throw Error("Deployment filter broken!"); if (volumes.length > 1) throw Error("Volume filter broken!"); - if (secrets.length > 1) throw Error("Secrets broken!"); + if (secrets.length > 2) throw Error("Secrets broken!"); const serverAssets = { deployment: deployments[0], - service: services.find( - (s) => s.metadata.name === `mcl-${serverName}-server`, - ), + service: services.find((s) => s.metadata.name.endsWith("-server")), volume: volumes[0], - rconService: services.find( - (s) => s.metadata.name === `mcl-${serverName}-rcon`, + rconService: services.find((s) => s.metadata.name.endsWith("-rcon")), + rconSecret: secrets.find((s) => + s.metadata.name.endsWith("-rcon-secret"), + ), + backupSecret: secrets.find((s) => + s.metadata.name.endsWith("-backup-secret"), ), - rconSecret: secrets[0], }; for (var k in serverAssets) if (serverAssets[k]) return serverAssets; // If no assets exist, return nothing @@ -84,59 +85,51 @@ export function getServerAssets(serverName) { .catch((e) => ERR("SERVER ASSETS", e)); } -export async function getDeployment(serverName) { +export async function getDeployment(serverId) { const servers = await getDeployments(); const serverDeployment = servers.find( - (s) => - s.metadata.annotations["minecluster.dunemask.net/server-name"] === - serverName, + (s) => s.metadata.annotations["minecluster.dunemask.net/id"] === serverId, ); if (!serverDeployment) - throw Error(`MCL Deployment '${serverName}' could not be found!`); + throw Error(`MCL Deployment with ID '${serverId}' could not be found!`); return serverDeployment; } -export async function getContainers(serverName) { - const deployment = await getDeployment(serverName); +export async function getContainers(serverId) { + const deployment = await getDeployment(serverId); return deployment.spec.template.spec.containers; } -async function containerControl(serverName, deployment, scaleUp) { +async function containerControl(serverSpec, deployment, scaleUp) { const { containers } = deployment.spec.template.spec; const depFtp = containers.find((c) => c.name.endsWith("-ftp")); const depServer = containers.find((c) => c.name.endsWith("-server")); const depBackup = containers.find((c) => c.name.endsWith("-backup")); - const serverSpec = await getServerEntry(serverName); const ftpContainer = depFtp ?? getFtpContainer(serverSpec); const serverContainer = depServer ?? getCoreServerContainer(serverSpec); const backupContainer = depBackup ?? getBackupContainer(serverSpec); - if (scaleUp) return [ftpContainer, serverContainer]; + if (scaleUp && serverSpec.backupEnabled) + return [ftpContainer, serverContainer, backupContainer]; + else if (scaleUp) return [ftpContainer, serverContainer]; return [ftpContainer]; } -export async function toggleServer(serverName, scaleUp = false) { - const deployment = await getDeployment(serverName); - deployment.spec.template.spec.containers = await containerControl( - serverName, - deployment, - scaleUp, - ); - return k8sDeps.replaceNamespacedDeployment( - deployment.metadata.name, - namespace, - deployment, - ); +export function terminationControl(containers) { + return containers.length > 1 ? 30 /*seconds*/ : 1 /*seconds */; } -export async function scaleDeployment(serverName, scaleUp = false) { - const deployment = await getDeployment(serverName); - if (deployment.spec.replicas === 1 && scaleUp) - return VERB( - "KSC", - `MCL Deployment '${serverName}' is already scaled! Ignoring scale adjustment.`, - ); - deployment.spec.replicas = scaleUp ? 1 : 0; +export async function toggleServer(serverId, scaleUp = false) { + const [deployment, serverSpec] = await Promise.all([ + getDeployment(serverId), + getServerEntry(serverId), + ]); + const containers = await containerControl(serverSpec, deployment, scaleUp); + const ts = terminationControl(containers); + + // Speed up container termination if not running a server + deployment.spec.template.spec.terminationGracePeriodSeconds = ts; + deployment.spec.template.spec.containers = containers; return k8sDeps.replaceNamespacedDeployment( deployment.metadata.name, diff --git a/lib/k8s/server-containers.js b/lib/k8s/server-containers.js index 4694828..0192d2a 100644 --- a/lib/k8s/server-containers.js +++ b/lib/k8s/server-containers.js @@ -4,9 +4,9 @@ import yaml from "js-yaml"; const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); export function getFtpContainer(serverSpec) { - const { name } = serverSpec; + const { mclName } = serverSpec; const ftpContainer = loadYaml("lib/k8s/configs/containers/ftp-server.yml"); - ftpContainer.name = `mcl-${name}-ftp`; + ftpContainer.name = `mcl-${mclName}-ftp`; const ftpPortList = [ { p: 20, n: "ftp-data" }, { p: 21, n: "ftp-commands" }, @@ -22,10 +22,10 @@ export function getFtpContainer(serverSpec) { } export function getCoreServerContainer(serverSpec) { - const { name, version, serverType, memory } = serverSpec; + const { mclName, version, serverType, memory } = serverSpec; const container = loadYaml("lib/k8s/configs/containers/minecraft-server.yml"); // Container Updates - container.name = `mcl-${name}-server`; + container.name = `mcl-${mclName}-server`; container.resources.requests.memory = `${memory}Mi`; const findEnv = (k) => container.env.find(({ name: n }) => n === k); @@ -36,7 +36,7 @@ export function getCoreServerContainer(serverSpec) { updateEnv("VERSION", version); updateEnv("MEMORY", `${memory}M`); // RCON - const rs = `mcl-${name}-rcon-secret`; + const rs = `mcl-${mclName}-rcon-secret`; findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs; return container; } @@ -50,18 +50,29 @@ export function getServerContainer(serverSpec) { const updateEnv = (k, v) => (findEnv(k).value = v); // Enviornment variables - updateEnv("DIFFICULTY", difficulty); + /*updateEnv("DIFFICULTY", difficulty); updateEnv("MODE", gamemode); updateEnv("MOTD", motd); updateEnv("MAX_PLAYERS", maxPlayers); updateEnv("SEED", seed); updateEnv("OPS", ops); - updateEnv("WHITELIST", whitelist); + updateEnv("WHITELIST", whitelist); */ return container; } export function getBackupContainer(serverSpec) { + const { mclName, backupEnabled, backupPath } = serverSpec; const container = loadYaml("lib/k8s/configs/containers/minecraft-backup.yml"); + if (!backupEnabled) return; + const findEnv = (k) => container.env.find(({ name: n }) => n === k); + const updateEnv = (k, v) => (findEnv(k).value = v); + updateEnv("RCLONE_REMOTE", `${mclName}-backup`); + updateEnv("RCLONE_DEST_DIR", backupPath); + container.name = `mcl-${mclName}-backup`; + // RCON + const rs = `mcl-${mclName}-rcon-secret`; + findEnv("RCON_PASSWORD").valueFrom.secretKeyRef.name = rs; + return container; } diff --git a/lib/k8s/server-control.js b/lib/k8s/server-control.js deleted file mode 100644 index bcb5a35..0000000 --- a/lib/k8s/server-control.js +++ /dev/null @@ -1,94 +0,0 @@ -import k8s from "@kubernetes/client-node"; -import { - getDeployment, - getDeployments, - getServerAssets, - scaleDeployment, -} from "./k8s-server-control.js"; -import { ERR } from "../util/logging.js"; -import ExpressClientError from "../util/ExpressClientError.js"; -const kc = new k8s.KubeConfig(); -kc.loadFromDefault(); - -const k8sMetrics = new k8s.Metrics(kc); -const namespace = process.env.MCL_SERVER_NAMESPACE; - -export async function startServerContainer(serverSpec) { - const { name } = serverSpec; - try { - await scaleDeployment(name, true); - } catch (e) { - ERR("SERVER CONTROL", e); - throw new ExpressClientError({ - c: 500, - m: `Error updating server '${name}'!\n`, - }); - } -} - -export async function stopServerContainer(serverSpec) { - const { name } = serverSpec; - try { - await scaleDeployment(name, false); - } catch (e) { - ERR("SERVER CONTROL", e); - throw new ExpressClientError({ - c: 500, - m: `Error updating server '${name}'!`, - }); - } -} - -export async function getInstances() { - const serverDeployments = await getDeployments(); - const podMetricsResponse = await k8sMetrics.getPodMetrics(namespace); - var name, metrics, services, serverAvailable, ftpAvailable; - const serverInstances = serverDeployments.map((s) => { - name = s.metadata.annotations["minecluster.dunemask.net/server-name"]; - metrics = null; - const { containers } = s.spec.template.spec; - services = containers.map(({ name }) => name.split("-").pop()); - const serverStatusList = s.status.conditions.map( - ({ type: statusType, status: sts }) => ({ statusType, sts }), - ); - const deploymentAvailable = - serverStatusList.find( - (ss) => ss.statusType === "Available" && ss.sts === "True", - ) !== undefined; - serverAvailable = services.includes(`server`) && deploymentAvailable; - ftpAvailable = services.includes("ftp") && deploymentAvailable; - - const pod = podMetricsResponse.items.find(({ metadata: md }) => { - return md.labels && md.labels.app && md.labels.app === `mcl-${name}-app`; - }); - if (serverAvailable && pod) { - const podCpus = pod.containers.map( - ({ usage }) => parseInt(usage.cpu) / 1_000_000, - ); - const podMems = pod.containers.map( - ({ usage }) => parseInt(usage.memory) / 1024, - ); - metrics = { - cpu: Math.ceil(podCpus.reduce((a, b) => a + b)), - memory: Math.ceil(podMems.reduce((a, b) => a + b)), - }; - } - return { name, metrics, services, serverAvailable, ftpAvailable }; - }); - return serverInstances; -} - -export async function getNamespaceMetrics() { - const serverInstances = await getInstances(); - var clusterMetrics = { cpu: 0, memory: 0 }; - if (servers.length > 1) { - const clusterCpu = serverInstances - .map(({ metrics }) => (metrics ? metrics.cpu : 0)) - .reduce((a, b) => a + b); - const clusterMem = serverInstances - .map(({ metrics }) => (metrics ? metrics.memory : 0)) - .reduce((a, b) => a + b); - clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; - } - return clusterMetrics; -} diff --git a/lib/k8s/server-create.js b/lib/k8s/server-create.js index 8ef2fe3..c1c77ba 100644 --- a/lib/k8s/server-create.js +++ b/lib/k8s/server-create.js @@ -4,7 +4,7 @@ import k8s from "@kubernetes/client-node"; import yaml from "js-yaml"; import fs from "node:fs"; import path from "node:path"; -import ExpressClientError from "../util/ExpressClientError.js"; + import { getFtpContainer, getServerContainer, @@ -19,33 +19,54 @@ const namespace = process.env.MCL_SERVER_NAMESPACE; const loadYaml = (f) => yaml.load(fs.readFileSync(path.resolve(f), "utf8")); +function createBackupSecret(serverSpec) { + if (!serverSpec.backupEnabled) return; // If backup not defined, don't create RCLONE secret + const { mclName, id, backupId, backupKey, backupHost } = serverSpec; + const backupYaml = loadYaml("lib/k8s/configs/backup-secret.yml"); + backupYaml.metadata.labels.app = `mcl-${mclName}-app`; + backupYaml.metadata.name = `mcl-${mclName}-backup-secret`; + backupYaml.metadata.namespace = namespace; + backupYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + const rcloneConfig = [ + `[${mclName}-backup]`, + "type = s3", + "provider = Minio", + "env_auth = false", + `access_key_id = ${backupId}`, + `secret_access_key = ${backupKey}`, + `endpoint = ${backupHost}`, + `acl = private`, + ].join("\n"); + backupYaml.data["rclone.conf"] = Buffer.from(rcloneConfig).toString("base64"); + return backupYaml; +} + function createRconSecret(serverSpec) { - const { name } = serverSpec; + const { mclName, id } = serverSpec; const rconYaml = loadYaml("lib/k8s/configs/rcon-secret.yml"); // TODO: Dyamic rconPassword const rconPassword = bcrypt.hashSync(uuidv4(), 10); rconYaml.data["rcon-password"] = Buffer.from(rconPassword).toString("base64"); - rconYaml.metadata.labels.app = `mcl-${name}-app`; - rconYaml.metadata.name = `mcl-${name}-rcon-secret`; + rconYaml.metadata.labels.app = `mcl-${mclName}-app`; + rconYaml.metadata.name = `mcl-${mclName}-rcon-secret`; rconYaml.metadata.namespace = namespace; - rconYaml.metadata.annotations["minecluster.dunemask.net/server-name"] = name; + rconYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; return rconYaml; } function createServerVolume(serverSpec) { - const { name } = serverSpec; + const { mclName, id } = serverSpec; const volumeYaml = loadYaml("lib/k8s/configs/server-pvc.yml"); - volumeYaml.metadata.labels.service = `mcl-${name}-server`; - volumeYaml.metadata.name = `mcl-${name}-volume`; + volumeYaml.metadata.labels.service = `mcl-${mclName}-server`; + volumeYaml.metadata.name = `mcl-${mclName}-volume`; volumeYaml.metadata.namespace = namespace; - volumeYaml.metadata.annotations["minecluster.dunemask.net/server-name"] = - name; - volumeYaml.spec.resources.requests.storage = "1Gi"; // TODO: Changeme + volumeYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + volumeYaml.spec.resources.requests.storage = "5Gi"; // TODO: Changeme return volumeYaml; } function createServerDeploy(serverSpec) { - const { name, host } = serverSpec; + const { mclName, id, backupEnabled } = serverSpec; const deployYaml = loadYaml("lib/k8s/configs/server-deployment.yml"); const { metadata } = deployYaml; const serverContainer = getServerContainer(serverSpec); @@ -53,39 +74,54 @@ function createServerDeploy(serverSpec) { const ftpContainer = getFtpContainer(serverSpec); // Configure Metadata; - metadata.name = `mcl-${name}`; + metadata.name = `mcl-${mclName}`; metadata.namespace = namespace; - metadata.annotations["minecluster.dunemask.net/server-name"] = name; + metadata.annotations["minecluster.dunemask.net/id"] = id; deployYaml.metadata = metadata; + deployYaml.spec.template.spec.terminationGracePeriodSeconds = 1; + // Configure Lables & Selectors - deployYaml.spec.selector.matchLabels.app = `mcl-${name}-app`; - deployYaml.spec.template.metadata.labels.app = `mcl-${name}-app`; + deployYaml.spec.selector.matchLabels.app = `mcl-${mclName}-app`; + deployYaml.spec.template.metadata.labels.app = `mcl-${mclName}-app`; + deployYaml.spec.template.metadata.annotations["minecluster.dunemask.net/id"] = + id; // Volumes deployYaml.spec.template.spec.volumes.find( ({ name }) => name === "datadir", - ).persistentVolumeClaim.claimName = `mcl-${name}-volume`; + ).persistentVolumeClaim.claimName = `mcl-${mclName}-volume`; + + // Backups + if (backupEnabled) { + deployYaml.spec.template.spec.volumes.push({ + name: "rclone-config", + secret: { + defaultMode: 420, + items: [{ key: "rclone.conf", path: "rclone.conf" }], + secretName: `mcl-${mclName}-backup-secret`, + }, + }); + } // Apply Containers TODO: User control for autostart - deployYaml.spec.template.spec.containers.push(serverContainer); + // deployYaml.spec.template.spec.containers.push(serverContainer); deployYaml.spec.template.spec.containers.push(ftpContainer); deployYaml.spec.replicas = 1; return deployYaml; } function createServerService(serverSpec) { - const { name, host } = serverSpec; + const { mclName, host, id } = serverSpec; const serviceYaml = loadYaml("lib/k8s/configs/server-svc.yml"); serviceYaml.metadata.annotations["ingress.qumine.io/hostname"] = host; serviceYaml.metadata.annotations["mc-router.itzg.me/externalServerName"] = host; - serviceYaml.metadata.labels.app = `mcl-${name}-app`; - serviceYaml.metadata.name = `mcl-${name}-server`; + serviceYaml.metadata.labels.app = `mcl-${mclName}-app`; + serviceYaml.metadata.name = `mcl-${mclName}-server`; serviceYaml.metadata.namespace = namespace; - serviceYaml.metadata.annotations["minecluster.dunemask.net/server-name"] = - name; - serviceYaml.spec.selector.app = `mcl-${name}-app`; + serviceYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + serviceYaml.spec.selector.app = `mcl-${mclName}-app`; // Port List: const serverPortList = [{ p: 25565, n: "minecraft" }]; @@ -107,33 +143,26 @@ function createServerService(serverSpec) { return serviceYaml; } -function createRconService(serverSpec) { - const { name } = serverSpec; +function createRconService(createSpec) { + const { id, mclName } = createSpec; const rconSvcYaml = loadYaml("lib/k8s/configs/rcon-svc.yml"); - rconSvcYaml.metadata.labels.app = `mcl-${name}-app`; - rconSvcYaml.metadata.name = `mcl-${name}-rcon`; + rconSvcYaml.metadata.labels.app = `mcl-${mclName}-app`; + rconSvcYaml.metadata.name = `mcl-${mclName}-rcon`; rconSvcYaml.metadata.namespace = namespace; - rconSvcYaml.metadata.annotations["minecluster.dunemask.net/server-name"] = - name; - rconSvcYaml.spec.selector.app = `mcl-${name}-app`; + rconSvcYaml.metadata.annotations["minecluster.dunemask.net/id"] = id; + rconSvcYaml.spec.selector.app = `mcl-${mclName}-app`; return rconSvcYaml; } -export default async function createServerResources(serverSpec) { - const deploymentRes = await k8sDeps.listNamespacedDeployment(namespace); - const deployments = deploymentRes.body.items.map((i) => i.metadata.name); - if (deployments.includes(`mcl-${serverSpec.name}`)) - throw new ExpressClientError({ m: "Server already exists!", c: 409 }); - const pvcRes = await k8sCore.listNamespacedPersistentVolumeClaim(namespace); - const pvcs = pvcRes.body.items.map((i) => i.metadata.name); - if (pvcs.includes(`mcl-${serverSpec.name}-volume`)) - throw new ExpressClientError({ m: "Server PVC already exists!", c: 409 }); - const rconSecret = createRconSecret(serverSpec); - const serverVolume = createServerVolume(serverSpec); - const serverDeploy = createServerDeploy(serverSpec); - const serverService = createServerService(serverSpec); - const rconService = createRconService(serverSpec); +export default async function createServerResources(createSpec) { + const backupSecret = createBackupSecret(createSpec); + const rconSecret = createRconSecret(createSpec); + const serverVolume = createServerVolume(createSpec); + const serverDeploy = createServerDeploy(createSpec); + const serverService = createServerService(createSpec); + const rconService = createRconService(createSpec); k8sCore.createNamespacedPersistentVolumeClaim(namespace, serverVolume); + if (!!backupSecret) k8sCore.createNamespacedSecret(namespace, backupSecret); k8sCore.createNamespacedSecret(namespace, rconSecret); k8sCore.createNamespacedService(namespace, serverService); k8sCore.createNamespacedService(namespace, rconService); diff --git a/lib/k8s/server-delete.js b/lib/k8s/server-delete.js index e226fbf..c47b383 100644 --- a/lib/k8s/server-delete.js +++ b/lib/k8s/server-delete.js @@ -22,9 +22,9 @@ function deleteOnExist(o, fn) { } export default async function deleteServerResources(serverSpec) { - const { name } = serverSpec; + const { id } = serverSpec; // Ensure deployment exists - const server = await getServerAssets(name); + const server = await getServerAssets(id); if (!server) throw new ExpressClientError({ c: 404, @@ -47,6 +47,10 @@ export default async function deleteServerResources(serverSpec) { const deleteRconSecret = deleteOnExist(server.rconSecret, (name) => k8sCore.deleteNamespacedSecret(name, namespace), ); + + const deleteBackupSecret = deleteOnExist(server.backupSecret, (name) => + k8sCore.deleteNamespacedSecret(name, namespace), + ); const deleteVolume = deleteOnExist(server.volume, (name) => k8sCore.deleteNamespacedPersistentVolumeClaim(name, namespace), ); @@ -55,6 +59,7 @@ export default async function deleteServerResources(serverSpec) { deleteService, deleteRconService, deleteRconSecret, + deleteBackupSecret, deleteVolume, ]).catch(deleteError); } diff --git a/lib/k8s/server-files.js b/lib/k8s/server-files.js index ec1f7cc..4f04f47 100644 --- a/lib/k8s/server-files.js +++ b/lib/k8s/server-files.js @@ -34,8 +34,8 @@ export async function getFtpClient(serverService) { } export async function useServerFtp(serverSpec, fn) { - const { name } = serverSpec; - const server = await getServerAssets(name); + const { id } = serverSpec; + const server = await getServerAssets(id); if (!server) throw new ExpressClientError({ c: 404, diff --git a/lib/k8s/server-status.js b/lib/k8s/server-status.js new file mode 100644 index 0000000..6317d8e --- /dev/null +++ b/lib/k8s/server-status.js @@ -0,0 +1,83 @@ +import k8s from "@kubernetes/client-node"; +import { getDeployments } from "./k8s-server-control.js"; +import { getServerEntries } from "../database/queries/server-queries.js"; +const kc = new k8s.KubeConfig(); +kc.loadFromDefault(); + +const k8sMetrics = new k8s.Metrics(kc); +const namespace = process.env.MCL_SERVER_NAMESPACE; + +function getServerMetrics(podMetricsRes, serverId, serverAvailable) { + const pod = podMetricsRes.items.find(({ metadata: md }) => { + return ( + md.annotations && + md.annotations["minecluster.dunemask.net/id"] === serverId + ); + }); + if (!serverAvailable || !pod) return null; + const podCpus = pod.containers.map( + ({ usage }) => parseInt(usage.cpu) / 1_000_000, + ); + const podMems = pod.containers.map( + ({ usage }) => parseInt(usage.memory) / 1024, + ); + return { + cpu: Math.ceil(podCpus.reduce((a, b) => a + b)), + memory: Math.ceil(podMems.reduce((a, b) => a + b)), + }; +} + +function getServerStatus(server) { + const { containers } = server.spec.template.spec; + const services = containers.map(({ name }) => name.split("-").pop()); + const serverStatusList = server.status.conditions.map( + ({ type: statusType, status: sts }) => ({ statusType, sts }), + ); + const deploymentAvailable = + serverStatusList.find( + (ss) => ss.statusType === "Available" && ss.sts === "True", + ) !== undefined; + const serverAvailable = services.includes(`server`) && deploymentAvailable; + const ftpAvailable = services.includes("ftp"); // TODO this needs some handling for container creation + return { serverAvailable, ftpAvailable, services }; +} + +export async function getInstances() { + const [serverDeployments, podMetricsRes, entries] = await Promise.all([ + getDeployments(), + k8sMetrics.getPodMetrics(namespace), + getServerEntries(), + ]); + + var serverId, metrics; + const serverInstances = serverDeployments.map((s) => { + serverId = s.metadata.annotations["minecluster.dunemask.net/id"]; + const entry = entries.find((e) => e.id === serverId); + const { ftpAvailable, serverAvailable, services } = getServerStatus(s); + metrics = getServerMetrics(podMetricsRes, serverId, serverAvailable); + return { + name: !!entry ? entry.name : "Unknown", + id: serverId, + metrics, + services, + serverAvailable, + ftpAvailable, + }; + }); + return serverInstances; +} + +export async function getNamespaceMetrics() { + const serverInstances = await getInstances(); + var clusterMetrics = { cpu: 0, memory: 0 }; + if (servers.length > 1) { + const clusterCpu = serverInstances + .map(({ metrics }) => (metrics ? metrics.cpu : 0)) + .reduce((a, b) => a + b); + const clusterMem = serverInstances + .map(({ metrics }) => (metrics ? metrics.memory : 0)) + .reduce((a, b) => a + b); + clusterMetrics = { cpu: clusterCpu, memory: clusterMem }; + } + return clusterMetrics; +} diff --git a/lib/server/sockets.js b/lib/server/sockets.js index 370e3d4..d9804ef 100644 --- a/lib/server/sockets.js +++ b/lib/server/sockets.js @@ -18,7 +18,7 @@ async function rconSend(socket, m) { const socketConnect = async (io, socket) => { VERB("WS", "Websocket connecting"); - socket.mcs = { serverName: socket.handshake.query.serverName }; + socket.mcs = { serverId: socket.handshake.query.serverId }; try { await webConsoleLogs(socket); await webConsoleRcon(socket); diff --git a/package-lock.json b/package-lock.json index d111705..63c29b0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,6 @@ "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", - "chonky": "^2.3.2", - "chonky-icon-fontawesome": "^2.3.2", "express": "^4.18.2", "figlet": "^1.7.0", "js-yaml": "^4.1.0", @@ -35,12 +33,15 @@ "@mui/material": "^5.14.20", "@tanstack/react-query": "^5.12.2", "@vitejs/plugin-react": "^4.2.1", + "chonky": "^2.3.2", + "chonky-icon-fontawesome": "^2.3.2", "concurrently": "^8.2.2", "nodemon": "^3.0.2", "prettier": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.1", + "react-toastify": "^9.1.3", "socket.io-client": "^4.7.2", "vite": "^5.0.7" } @@ -49,6 +50,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", "@jridgewell/trace-mapping": "^0.3.9" @@ -881,6 +883,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, "dependencies": { "@babel/highlight": "^7.23.4", "chalk": "^2.4.2" @@ -893,6 +896,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -904,6 +908,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -917,6 +922,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -924,12 +930,14 @@ "node_modules/@babel/code-frame/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -938,6 +946,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -946,6 +955,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.5.tgz", "integrity": "sha512-Cwc2XjUrG4ilcfOw4wBAK+enbdgwAcAJCfGUItPBKR7Mjw4aEfAFYrLxeRp4jWgtNIKn3n2AlBOfwwafl+42/g==", + "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.23.5", @@ -974,12 +984,14 @@ "node_modules/@babel/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==" + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, "node_modules/@babel/generator": { "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.5.tgz", "integrity": "sha512-BPssCHrBD+0YrxviOa3QzpqwhNIXKEtOa2jQrm4FlmkC2apYgRnQcmPWiGZDlGxiNtltnUFolMe8497Esry+jA==", + "dev": true, "dependencies": { "@babel/types": "^7.23.5", "@jridgewell/gen-mapping": "^0.3.2", @@ -994,6 +1006,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz", "integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==", + "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -1005,6 +1018,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", + "dev": true, "dependencies": { "@babel/compat-data": "^7.22.9", "@babel/helper-validator-option": "^7.22.15", @@ -1020,6 +1034,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1028,6 +1043,7 @@ "version": "7.23.0", "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/types": "^7.23.0" @@ -1040,6 +1056,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -1051,6 +1068,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, "dependencies": { "@babel/types": "^7.22.15" }, @@ -1062,6 +1080,7 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, "dependencies": { "@babel/helper-environment-visitor": "^7.22.20", "@babel/helper-module-imports": "^7.22.15", @@ -1080,6 +1099,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1088,6 +1108,7 @@ "version": "7.22.5", "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -1099,6 +1120,7 @@ "version": "7.22.6", "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, "dependencies": { "@babel/types": "^7.22.5" }, @@ -1110,6 +1132,7 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1118,6 +1141,7 @@ "version": "7.22.20", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1126,6 +1150,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -1134,6 +1159,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.5.tgz", "integrity": "sha512-oO7us8FzTEsG3U6ag9MfdF1iA/7Z6dz+MtFhifZk8C8o453rGJFFWUP1t+ULM9TUIAzC9uxXEiXjOiVMyd7QPg==", + "dev": true, "dependencies": { "@babel/template": "^7.22.15", "@babel/traverse": "^7.23.5", @@ -1147,6 +1173,7 @@ "version": "7.23.4", "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", + "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", "chalk": "^2.4.2", @@ -1160,6 +1187,7 @@ "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "dependencies": { "color-convert": "^1.9.0" }, @@ -1171,6 +1199,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", @@ -1184,6 +1213,7 @@ "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, "dependencies": { "color-name": "1.1.3" } @@ -1191,12 +1221,14 @@ "node_modules/@babel/highlight/node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true }, "node_modules/@babel/highlight/node_modules/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, "engines": { "node": ">=0.8.0" } @@ -1205,6 +1237,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.5.tgz", "integrity": "sha512-hOOqoiNXrmGdFbhgCzu6GiURxUgM27Xwd/aPuu8RfHEZPBzL1Z54okAHAQjXfcQNwvrlkAmAp4SlRTZ45vlthQ==", + "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1216,6 +1249,7 @@ "version": "7.23.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz", "integrity": "sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==", + "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.22.5" }, @@ -1260,6 +1294,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz", "integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==", + "dev": true, "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1271,6 +1306,7 @@ "version": "7.22.15", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.22.13", "@babel/parser": "^7.22.15", @@ -1284,6 +1320,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.5.tgz", "integrity": "sha512-czx7Xy5a6sapWWRx61m1Ke1Ra4vczu1mCTtJam5zRTBOonfdJ+S/B6HYmGYu3fJtr8GGET3si6IhgWVBhJ/m8w==", + "dev": true, "dependencies": { "@babel/code-frame": "^7.23.5", "@babel/generator": "^7.23.5", @@ -1304,6 +1341,7 @@ "version": "7.23.5", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.5.tgz", "integrity": "sha512-ON5kSOJwVO6xXVRTvOI0eOnWe7VdUcIpsovGo9U/Br4Ie4UVFQTboO2cYnDhAGU6Fp+UxSiT+pMft0SMHfuq6w==", + "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.23.4", "@babel/helper-validator-identifier": "^7.22.20", @@ -1355,6 +1393,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "dev": true, "dependencies": { "@emotion/memoize": "^0.8.1" } @@ -1362,7 +1401,8 @@ "node_modules/@emotion/memoize": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==", + "dev": true }, "node_modules/@emotion/react": { "version": "11.11.1", @@ -1433,7 +1473,8 @@ "node_modules/@emotion/stylis": { "version": "0.8.5", "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", + "dev": true }, "node_modules/@emotion/unitless": { "version": "0.8.1", @@ -1856,6 +1897,7 @@ "version": "1.11.4", "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dev": true, "dependencies": { "@formatjs/intl-localematcher": "0.2.25", "tslib": "^2.1.0" @@ -1865,6 +1907,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -1873,6 +1916,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/icu-skeleton-parser": "1.3.6", @@ -1883,6 +1927,7 @@ "version": "1.3.6", "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "tslib": "^2.1.0" @@ -1892,6 +1937,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/fast-memoize": "1.2.1", @@ -1914,6 +1960,7 @@ "version": "5.4.3", "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/intl-localematcher": "0.2.25", @@ -1924,6 +1971,7 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/intl-localematcher": "0.2.25", @@ -1934,6 +1982,7 @@ "version": "0.2.25", "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dev": true, "dependencies": { "tslib": "^2.1.0" } @@ -1942,6 +1991,7 @@ "version": "0.2.36", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz", "integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==", + "dev": true, "hasInstallScript": true, "engines": { "node": ">=6" @@ -1951,6 +2001,7 @@ "version": "1.2.36", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz", "integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==", + "dev": true, "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-common-types": "^0.2.36" @@ -1963,6 +2014,7 @@ "version": "5.13.1", "resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.13.1.tgz", "integrity": "sha512-dKwF+NpIV2LVCNBA7hibH53k+ChF4Wu59P2z35gu3zwRBZpmpLVhS9k1/RiSqUqkyXUQvA2rSv48GY6wp5axZQ==", + "dev": true, "dependencies": { "@fortawesome/fontawesome-common-types": "^0.2.29" }, @@ -1974,6 +2026,7 @@ "version": "5.15.4", "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz", "integrity": "sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==", + "dev": true, "hasInstallScript": true, "dependencies": { "@fortawesome/fontawesome-common-types": "^0.2.36" @@ -1986,6 +2039,7 @@ "version": "0.1.19", "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", + "dev": true, "dependencies": { "prop-types": "^15.8.1" }, @@ -1998,6 +2052,7 @@ "version": "0.3.3", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, "dependencies": { "@jridgewell/set-array": "^1.0.1", "@jridgewell/sourcemap-codec": "^1.4.10", @@ -2011,6 +2066,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -2019,6 +2075,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, "engines": { "node": ">=6.0.0" } @@ -2026,12 +2083,14 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2128,6 +2187,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", + "dev": true, "peerDependencies": { "@types/react": "*" }, @@ -2404,22 +2464,26 @@ "node_modules/@react-dnd/asap": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-4.0.1.tgz", - "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==" + "integrity": "sha512-kLy0PJDDwvwwTXxqTFNAAllPHD73AycE9ypWeln/IguoGBEbvFcPDbCV03G52bEcC5E+YgupBE0VzHGdC8SIXg==", + "dev": true }, "node_modules/@react-dnd/invariant": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-2.0.0.tgz", - "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==" + "integrity": "sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw==", + "dev": true }, "node_modules/@react-dnd/shallowequal": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz", - "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==" + "integrity": "sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==", + "dev": true }, "node_modules/@reduxjs/toolkit": { "version": "1.9.7", "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-1.9.7.tgz", "integrity": "sha512-t7v8ZPxhhKgOKtU+uyJT13lu4vL7az5aFi4IdoDs/eS548edn2M8Ik9h8fxgvMjGoAUVFSt6ZC1P5cWmQ014QQ==", + "dev": true, "dependencies": { "immer": "^9.0.21", "redux": "^4.2.1", @@ -3337,6 +3401,7 @@ "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.3.1.tgz", "integrity": "sha512-zeOWb0JGBoVmlQoznvqXbE0tEC/HONsnoUNH19Hc96NFsTAwTXbTqb8FMYkru1F/iqp7a18Ws3nWJvtA1sHD1A==", "deprecated": "This is a stub types definition. classnames provides its own type definitions, so you do not need this installed.", + "dev": true, "dependencies": { "classnames": "*" } @@ -3357,12 +3422,14 @@ "node_modules/@types/fuzzy-search": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/fuzzy-search/-/fuzzy-search-2.1.5.tgz", - "integrity": "sha512-Yw8OsjhVKbKw83LMDOZ9RXc+N+um48DmZYMrz7QChpHkQuygsc5O40oCL7SfvWgpaaviCx2TbNXYUBwhMtBH5w==" + "integrity": "sha512-Yw8OsjhVKbKw83LMDOZ9RXc+N+um48DmZYMrz7QChpHkQuygsc5O40oCL7SfvWgpaaviCx2TbNXYUBwhMtBH5w==", + "dev": true }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", "integrity": "sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==", + "dev": true, "dependencies": { "@types/react": "*", "hoist-non-react-statics": "^3.3.0" @@ -3376,7 +3443,8 @@ "node_modules/@types/memoizee": { "version": "0.4.11", "resolved": "https://registry.npmjs.org/@types/memoizee/-/memoizee-0.4.11.tgz", - "integrity": "sha512-2gyorIBZu8GoDr9pYjROkxWWcFtHCquF7TVbN2I+/OvgZhnIGQS0vX5KJz4lXNKb8XOSfxFOSG5OLru1ESqLUg==" + "integrity": "sha512-2gyorIBZu8GoDr9pYjROkxWWcFtHCquF7TVbN2I+/OvgZhnIGQS0vX5KJz4lXNKb8XOSfxFOSG5OLru1ESqLUg==", + "dev": true }, "node_modules/@types/node": { "version": "20.8.3", @@ -3392,12 +3460,14 @@ "node_modules/@types/prop-types": { "version": "15.7.11", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==", + "dev": true }, "node_modules/@types/react": { "version": "17.0.73", "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.73.tgz", "integrity": "sha512-6AcjgPIVsXTIsFDgsGW0iQhvg0xb2vt2qAWgXyncnVNRaW9ZXTTwAh7RQoh7PzK1AhjPoGDvUBkdAREih9n5oQ==", + "dev": true, "dependencies": { "@types/prop-types": "*", "@types/scheduler": "*", @@ -3408,6 +3478,7 @@ "version": "7.1.33", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz", "integrity": "sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==", + "dev": true, "dependencies": { "@types/hoist-non-react-statics": "^3.3.0", "@types/react": "*", @@ -3419,6 +3490,7 @@ "version": "4.4.10", "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -3427,6 +3499,7 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.4.tgz", "integrity": "sha512-nhYwlFiYa8M3S+O2T9QO/e1FQUYMr/wJENUdf/O0dhRi1RS/93rjrYQFYdbUqtdFySuhrtnEDX29P6eKOttY+A==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -3435,6 +3508,7 @@ "version": "1.8.8", "resolved": "https://registry.npmjs.org/@types/react-window/-/react-window-1.8.8.tgz", "integrity": "sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==", + "dev": true, "dependencies": { "@types/react": "*" } @@ -3442,7 +3516,8 @@ "node_modules/@types/redux-watch": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@types/redux-watch/-/redux-watch-1.1.2.tgz", - "integrity": "sha512-pooK4MLNYmTyfOU7jy/ZmS2mpbSHkEVpWIcK4/BRbaegOnCB4PYA2JY88vBXE2LVkhPM+du1Z+lVzRiewMz6wg==" + "integrity": "sha512-pooK4MLNYmTyfOU7jy/ZmS2mpbSHkEVpWIcK4/BRbaegOnCB4PYA2JY88vBXE2LVkhPM+du1Z+lVzRiewMz6wg==", + "dev": true }, "node_modules/@types/request": { "version": "2.48.8", @@ -3458,12 +3533,14 @@ "node_modules/@types/scheduler": { "version": "0.16.2", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", - "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" + "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==", + "dev": true }, "node_modules/@types/shortid": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz", - "integrity": "sha512-9BCYD9btg2CY4kPcpMQ+vCR8U6V8f/KvixYD5ZbxoWlkhddNF5IeZMVL3p+QFUkg+Hb+kPAG9Jgk4bnnF1v/Fw==" + "integrity": "sha512-9BCYD9btg2CY4kPcpMQ+vCR8U6V8f/KvixYD5ZbxoWlkhddNF5IeZMVL3p+QFUkg+Hb+kPAG9Jgk4bnnF1v/Fw==", + "dev": true }, "node_modules/@types/tough-cookie": { "version": "4.0.2", @@ -3708,6 +3785,7 @@ "version": "2.1.4", "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", + "dev": true, "dependencies": { "@babel/helper-annotate-as-pure": "^7.22.5", "@babel/helper-module-imports": "^7.22.5", @@ -3856,6 +3934,7 @@ "version": "4.22.2", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.2.tgz", "integrity": "sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==", + "dev": true, "funding": [ { "type": "opencollective", @@ -3958,6 +4037,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -3966,6 +4046,7 @@ "version": "1.0.30001566", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001566.tgz", "integrity": "sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==", + "dev": true, "funding": [ { "type": "opencollective", @@ -4028,6 +4109,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/chonky/-/chonky-2.3.2.tgz", "integrity": "sha512-ed2u+SEjEPSn8bv/zC0sXfMG/XS6Ydm4J2leLCvRb7a/2BZxKqE1DFETxqfLeJ2OA1IRujvwnuvmJO8ZgyOGyA==", + "dev": true, "dependencies": { "@material-ui/core": "4.11.3", "@reduxjs/toolkit": "^1.5.0", @@ -4070,6 +4152,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/chonky-icon-fontawesome/-/chonky-icon-fontawesome-2.3.2.tgz", "integrity": "sha512-19Duy25JxteIlQJfZKLH7ZSujU1zYBMLKovWDOLlw1EdPlXb9exsfYUUkwfNS+31KpwttfaqnGuN8Gyed/R6UQ==", + "dev": true, "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.32", "@fortawesome/free-brands-svg-icons": "5.13.1", @@ -4087,13 +4170,15 @@ "node_modules/chonky/node_modules/@emotion/hash": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", + "dev": true }, "node_modules/chonky/node_modules/@material-ui/core": { "version": "4.11.3", "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz", "integrity": "sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw==", "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dev": true, "dependencies": { "@babel/runtime": "^7.4.4", "@material-ui/styles": "^4.11.3", @@ -4131,6 +4216,7 @@ "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", "deprecated": "Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.", + "dev": true, "dependencies": { "@babel/runtime": "^7.4.4", "@emotion/hash": "^0.8.0", @@ -4171,6 +4257,7 @@ "version": "4.12.2", "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", + "dev": true, "dependencies": { "@babel/runtime": "^7.4.4", "@material-ui/utils": "^4.11.3", @@ -4199,6 +4286,7 @@ "version": "4.11.3", "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.4.4", "prop-types": "^15.7.2", @@ -4216,6 +4304,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, "engines": { "node": ">=6" } @@ -4223,12 +4312,14 @@ "node_modules/chonky/node_modules/csstype": { "version": "2.6.21", "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" + "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==", + "dev": true }, "node_modules/chonky/node_modules/react-dom": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", + "dev": true, "peer": true, "dependencies": { "loose-envify": "^1.1.0", @@ -4242,12 +4333,14 @@ "node_modules/chonky/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true }, "node_modules/chonky/node_modules/scheduler": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", + "dev": true, "peer": true, "dependencies": { "loose-envify": "^1.1.0", @@ -4265,7 +4358,8 @@ "node_modules/classnames": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", - "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==", + "dev": true }, "node_modules/cliui": { "version": "8.0.1", @@ -4532,6 +4626,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "dev": true, "engines": { "node": ">=4" } @@ -4540,6 +4635,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/css-jss/-/css-jss-10.10.0.tgz", "integrity": "sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "^10.10.0", @@ -4550,6 +4646,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dev": true, "dependencies": { "camelize": "^1.0.0", "css-color-keywords": "^1.0.0", @@ -4560,6 +4657,7 @@ "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.8.3", "is-in-browser": "^1.0.2" @@ -4568,7 +4666,8 @@ "node_modules/csstype": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==", + "dev": true }, "node_modules/dashdash": { "version": "1.14.1", @@ -4617,6 +4716,7 @@ "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4663,6 +4763,7 @@ "version": "11.1.3", "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-11.1.3.tgz", "integrity": "sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA==", + "dev": true, "dependencies": { "@react-dnd/asap": "^4.0.0", "@react-dnd/invariant": "^2.0.0", @@ -4673,6 +4774,7 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" @@ -4695,7 +4797,8 @@ "node_modules/electron-to-chromium": { "version": "1.4.608", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.608.tgz", - "integrity": "sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==" + "integrity": "sha512-J2f/3iIIm3Mo0npneITZ2UPe4B1bg8fTNrFjD8715F/k1BvbviRuqYGkET1PgprrczXYTHFvotbBOmUp6KE0uA==", + "dev": true }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4809,6 +4912,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, "engines": { "node": ">=6" } @@ -4849,7 +4953,8 @@ "node_modules/exact-trie": { "version": "1.0.13", "resolved": "https://registry.npmjs.org/exact-trie/-/exact-trie-1.0.13.tgz", - "integrity": "sha512-2N0sx9jMlzZxRmSOpFKmcuaPcLXYLGRp69DohigW5E7R/uo9i6S1zJ/PuAckf70099am1ts7YBRMLO8Nr8AJLg==" + "integrity": "sha512-2N0sx9jMlzZxRmSOpFKmcuaPcLXYLGRp69DohigW5E7R/uo9i6S1zJ/PuAckf70099am1ts7YBRMLO8Nr8AJLg==", + "dev": true }, "node_modules/express": { "version": "4.18.2", @@ -4931,7 +5036,8 @@ "node_modules/fast-sort": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/fast-sort/-/fast-sort-2.2.0.tgz", - "integrity": "sha512-W7zqnn2zsYoQA87FKmYtgOsbJohOrh7XrtZrCVHN5XZKqTBTv5UG+rSS3+iWbg/nepRQUOu+wnas8BwtK8kiCg==" + "integrity": "sha512-W7zqnn2zsYoQA87FKmYtgOsbJohOrh7XrtZrCVHN5XZKqTBTv5UG+rSS3+iWbg/nepRQUOu+wnas8BwtK8kiCg==", + "dev": true }, "node_modules/fast-xml-parser": { "version": "4.2.5", @@ -4978,6 +5084,7 @@ "version": "6.4.0", "resolved": "https://registry.npmjs.org/filesize/-/filesize-6.4.0.tgz", "integrity": "sha512-mjFIpOHC4jbfcTfoh4rkWpI31mF7viw9ikj/JyLoKzqlwG/YsefKfvYlYhdYdg/9mtK2z1AzgN/0LvVQ3zdlSQ==", + "dev": true, "engines": { "node": ">= 0.4.0" } @@ -5129,7 +5236,8 @@ "node_modules/fuzzy-search": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/fuzzy-search/-/fuzzy-search-3.2.1.tgz", - "integrity": "sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==" + "integrity": "sha512-vAcPiyomt1ioKAsAL2uxSABHJ4Ju/e4UeDM+g1OlR0vV4YhLGMNsdLNvZTpEDY4JCSt0E4hASCNM5t2ETtsbyg==", + "dev": true }, "node_modules/gauge": { "version": "3.0.2", @@ -5154,6 +5262,7 @@ "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, "engines": { "node": ">=6.9.0" } @@ -5223,6 +5332,7 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, "engines": { "node": ">=4" } @@ -5274,6 +5384,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, "engines": { "node": ">=4" } @@ -5312,6 +5423,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "dev": true, "dependencies": { "react-is": "^16.7.0" } @@ -5319,12 +5431,14 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "node_modules/hotkeys-js": { "version": "3.13.2", "resolved": "https://registry.npmjs.org/hotkeys-js/-/hotkeys-js-3.13.2.tgz", "integrity": "sha512-SXd+x90nhOdo5s+DZNPtrvXs0ZahQLXT0tWQ68bzjxCNUZ7eV4ZlNqjHLi3Kt2URpR8EBJOB2dBLdNfwIeql1A==", + "dev": true, "funding": { "url": "https://jaywcjlove.github.io/#/sponsor" } @@ -5378,7 +5492,8 @@ "node_modules/hyphenate-style-name": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==", + "dev": true }, "node_modules/iconv-lite": { "version": "0.4.24", @@ -5406,6 +5521,7 @@ "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==", + "dev": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/immer" @@ -5445,6 +5561,7 @@ "version": "9.13.0", "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/fast-memoize": "1.2.1", @@ -5562,7 +5679,8 @@ "node_modules/is-in-browser": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==" + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", + "dev": true }, "node_modules/is-number": { "version": "7.0.0", @@ -5630,7 +5748,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "node_modules/js-yaml": { "version": "4.1.0", @@ -5652,6 +5771,7 @@ "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, "bin": { "jsesc": "bin/jsesc" }, @@ -5684,6 +5804,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, "bin": { "json5": "lib/cli.js" }, @@ -5717,6 +5838,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "csstype": "^3.0.2", @@ -5732,6 +5854,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "hyphenate-style-name": "^1.0.3", @@ -5742,6 +5865,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-compose/-/jss-plugin-compose-10.10.0.tgz", "integrity": "sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5752,6 +5876,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0" @@ -5761,6 +5886,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-expand/-/jss-plugin-expand-10.10.0.tgz", "integrity": "sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0" @@ -5770,6 +5896,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-extend/-/jss-plugin-extend-10.10.0.tgz", "integrity": "sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5780,6 +5907,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0" @@ -5789,6 +5917,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5799,6 +5928,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0" @@ -5808,6 +5938,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5818,6 +5949,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-observable/-/jss-plugin-rule-value-observable-10.10.0.tgz", "integrity": "sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5828,6 +5960,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-template/-/jss-plugin-template-10.10.0.tgz", "integrity": "sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5838,6 +5971,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "css-vendor": "^2.0.8", @@ -5848,6 +5982,7 @@ "version": "10.10.0", "resolved": "https://registry.npmjs.org/jss-preset-default/-/jss-preset-default-10.10.0.tgz", "integrity": "sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "jss": "10.10.0", @@ -5874,12 +6009,14 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5891,6 +6028,7 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, "dependencies": { "yallist": "^3.0.2" } @@ -5920,7 +6058,8 @@ "node_modules/memoize-one": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", - "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "dev": true }, "node_modules/merge-descriptors": { "version": "1.0.1", @@ -6142,7 +6281,8 @@ "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==" + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true }, "node_modules/nodemon": { "version": "3.0.2", @@ -6277,6 +6417,7 @@ "version": "0.11.8", "resolved": "https://registry.npmjs.org/object-path/-/object-path-0.11.8.tgz", "integrity": "sha512-YJjNZrlXJFM42wTBn6zgOJVar9KFJvzx6sTWDte8sWZF//cnjl0BxHNpfZx+ZffXX63A9q0b1zsFiBX4g4X5KA==", + "dev": true, "engines": { "node": ">= 10.12.0" } @@ -6526,12 +6667,14 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, "engines": { "node": ">=8.6" }, @@ -6542,7 +6685,8 @@ "node_modules/popper.js": { "version": "1.16.1-lts", "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", - "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==", + "dev": true }, "node_modules/postcss": { "version": "8.4.32", @@ -6575,7 +6719,8 @@ "node_modules/postcss-value-parser": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true }, "node_modules/postgres-array": { "version": "2.0.0", @@ -6651,6 +6796,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -6660,7 +6806,8 @@ "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true }, "node_modules/proxy-addr": { "version": "2.0.7", @@ -6769,6 +6916,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6779,12 +6927,14 @@ "node_modules/react-display-name": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/react-display-name/-/react-display-name-0.2.5.tgz", - "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==" + "integrity": "sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==", + "dev": true }, "node_modules/react-dnd": { "version": "11.1.3", "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-11.1.3.tgz", "integrity": "sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ==", + "dev": true, "dependencies": { "@react-dnd/shallowequal": "^2.0.0", "@types/hoist-non-react-statics": "^3.3.1", @@ -6800,6 +6950,7 @@ "version": "11.1.3", "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz", "integrity": "sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw==", + "dev": true, "dependencies": { "dnd-core": "^11.1.3" } @@ -6808,6 +6959,7 @@ "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.0" @@ -6820,6 +6972,7 @@ "version": "5.25.1", "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dev": true, "dependencies": { "@formatjs/ecma402-abstract": "1.11.4", "@formatjs/icu-messageformat-parser": "2.1.0", @@ -6845,12 +6998,14 @@ "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==" + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true }, "node_modules/react-jss": { "version": "10.10.0", "resolved": "https://registry.npmjs.org/react-jss/-/react-jss-10.10.0.tgz", "integrity": "sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.3.1", "@emotion/is-prop-valid": "^0.7.3", @@ -6872,6 +7027,7 @@ "version": "0.7.3", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz", "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==", + "dev": true, "dependencies": { "@emotion/memoize": "0.7.1" } @@ -6879,12 +7035,14 @@ "node_modules/react-jss/node_modules/@emotion/memoize": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", - "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==", + "dev": true }, "node_modules/react-redux": { "version": "7.2.9", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dev": true, "dependencies": { "@babel/runtime": "^7.15.4", "@types/react-redux": "^7.1.20", @@ -6908,7 +7066,8 @@ "node_modules/react-redux/node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true }, "node_modules/react-refresh": { "version": "0.14.0", @@ -6951,10 +7110,33 @@ "react-dom": ">=16.8" } }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dev": true, + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-toastify/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "dev": true, "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -6970,6 +7152,7 @@ "version": "1.0.20", "resolved": "https://registry.npmjs.org/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.20.tgz", "integrity": "sha512-OdIyHwj4S4wyhbKHOKM1wLSj/UDXm839Z3Cvfg2a9j+He6yDa6i5p0qQvEiCnyQlGO/HyfSnigQwuxvYalaAXA==", + "dev": true, "peerDependencies": { "react": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc", "react-dom": "^15.3.0 || ^16.0.0-alpha || ^17.0.0 || ^18.0.0-rc" @@ -6979,6 +7162,7 @@ "version": "1.8.10", "resolved": "https://registry.npmjs.org/react-window/-/react-window-1.8.10.tgz", "integrity": "sha512-Y0Cx+dnU6NLa5/EvoHukUD0BklJ8qITCtVEPY1C/nL8wwoZ0b5aEw8Ff1dOVHw7fCzMt55XfJDd8S8W8LCaUCg==", + "dev": true, "dependencies": { "@babel/runtime": "^7.0.0", "memoize-one": ">=3.1.1 <6" @@ -7020,6 +7204,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dev": true, "dependencies": { "@babel/runtime": "^7.9.2" } @@ -7028,6 +7213,7 @@ "version": "2.4.2", "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.4.2.tgz", "integrity": "sha512-+P3TjtnP0k/FEjcBL5FZpoovtvrTNT/UXd4/sluaSyrURlSlhLSzEdfsTBW7WsKB6yPvgd7q/iZPICFjW4o57Q==", + "dev": true, "peerDependencies": { "redux": "^4" } @@ -7036,6 +7222,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/redux-watch/-/redux-watch-1.2.0.tgz", "integrity": "sha512-Ws4Q+e5zFGMyy1H709c1Ws8apSd6MqoJRIzBDHbI4nikome/IZWVTYXdQNz+VJxPjyX/h2E+lYEo41fXgjCF8g==", + "dev": true, "dependencies": { "object-path": "^0.11.5" } @@ -7043,7 +7230,8 @@ "node_modules/regenerator-runtime": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" + "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", + "dev": true }, "node_modules/request": { "version": "2.88.2", @@ -7118,7 +7306,8 @@ "node_modules/reselect": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.8.tgz", - "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==" + "integrity": "sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==", + "dev": true }, "node_modules/resolve": { "version": "1.22.4", @@ -7258,6 +7447,7 @@ "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, "dependencies": { "loose-envify": "^1.1.0" } @@ -7338,12 +7528,14 @@ "node_modules/shallow-equal": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.2.1.tgz", - "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==" + "integrity": "sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==", + "dev": true }, "node_modules/shallowequal": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", + "dev": true }, "node_modules/shell-quote": { "version": "1.8.1", @@ -7359,6 +7551,7 @@ "resolved": "https://registry.npmjs.org/shortid/-/shortid-2.2.16.tgz", "integrity": "sha512-Ugt+GIZqvGXCIItnsL+lvFJOiN7RYqlGy7QE41O3YC1xbNSeDGIRO7xg2JJXIAj1cAGnOeC1r7/T9pgrtQbv4g==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, "dependencies": { "nanoid": "^2.1.0" } @@ -7366,7 +7559,8 @@ "node_modules/shortid/node_modules/nanoid": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-2.1.11.tgz", - "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==" + "integrity": "sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==", + "dev": true }, "node_modules/side-channel": { "version": "1.0.4", @@ -7630,6 +7824,7 @@ "version": "5.3.11", "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", + "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.0.0", "@babel/traverse": "^7.4.5", @@ -7658,7 +7853,8 @@ "node_modules/styled-components/node_modules/@emotion/unitless": { "version": "0.7.5", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", + "dev": true }, "node_modules/stylis": { "version": "4.2.0", @@ -7670,6 +7866,7 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, "dependencies": { "has-flag": "^3.0.0" }, @@ -7693,6 +7890,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -7722,6 +7920,7 @@ "version": "3.3.0", "resolved": "https://registry.npmjs.org/theming/-/theming-3.3.0.tgz", "integrity": "sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==", + "dev": true, "dependencies": { "hoist-non-react-statics": "^3.3.0", "prop-types": "^15.5.8", @@ -7738,12 +7937,14 @@ "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "dev": true }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, "engines": { "node": ">=4" } @@ -7809,7 +8010,8 @@ "node_modules/tsdef": { "version": "0.0.14", "resolved": "https://registry.npmjs.org/tsdef/-/tsdef-0.0.14.tgz", - "integrity": "sha512-UjMD4XKRWWFlFBfwKVQmGFT5YzW/ZaF8x6KpCDf92u9wgKeha/go3FU0e5WqDjXsCOdfiavCkfwfVHNDxRDGMA==" + "integrity": "sha512-UjMD4XKRWWFlFBfwKVQmGFT5YzW/ZaF8x6KpCDf92u9wgKeha/go3FU0e5WqDjXsCOdfiavCkfwfVHNDxRDGMA==", + "dev": true }, "node_modules/tslib": { "version": "2.5.0", @@ -7872,6 +8074,7 @@ "version": "1.0.13", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", + "dev": true, "funding": [ { "type": "opencollective", @@ -8163,7 +8366,8 @@ "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true }, "node_modules/yaml": { "version": "1.10.2", diff --git a/package.json b/package.json index 62e7f28..41f4b21 100644 --- a/package.json +++ b/package.json @@ -28,12 +28,15 @@ "@mui/material": "^5.14.20", "@tanstack/react-query": "^5.12.2", "@vitejs/plugin-react": "^4.2.1", + "chonky": "^2.3.2", + "chonky-icon-fontawesome": "^2.3.2", "concurrently": "^8.2.2", "nodemon": "^3.0.2", "prettier": "^3.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.20.1", + "react-toastify": "^9.1.3", "socket.io-client": "^4.7.2", "vite": "^5.0.7" }, @@ -43,8 +46,6 @@ "basic-ftp": "^5.0.4", "bcrypt": "^5.1.1", "chalk": "^5.3.0", - "chonky": "^2.3.2", - "chonky-icon-fontawesome": "^2.3.2", "express": "^4.18.2", "figlet": "^1.7.0", "js-yaml": "^4.1.0", diff --git a/src/components/files/FilePreview.jsx b/src/components/files/FilePreview.jsx new file mode 100644 index 0000000..9920a0f --- /dev/null +++ b/src/components/files/FilePreview.jsx @@ -0,0 +1,74 @@ +import { useState, useEffect } from "react"; +import useMediaQuery from "@mui/material/useMediaQuery"; +import { useTheme } from "@mui/material/styles"; +import Button from "@mui/material/Button"; +import DialogTitle from "@mui/material/DialogTitle"; +import DialogContent from "@mui/material/DialogContent"; +import DialogActions from "@mui/material/DialogActions"; +import Dialog from "@mui/material/Dialog"; +import Toolbar from "@mui/material/Toolbar"; + +const textFileTypes = ["properties", "txt", "yaml", "yml", "json", "env"]; +const imageFileTypes = ["png", "jpeg", "jpg"]; + +export const supportedFileTypes = [...textFileTypes, ...imageFileTypes]; + +export function useFilePreview(isOpen = false) { + const [open, setOpen] = useState(isOpen); + const dialogToggle = () => setOpen(!open); + return [open, dialogToggle]; +} + +function TextPreview(props) { + const { fileText } = props; + return