diff --git a/CHANGELOG.md b/CHANGELOG.md index ce0c8bb3c3..ee254f1ff2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## [0.85.38](https://github.com/kurtosis-tech/kurtosis/compare/0.85.37...0.85.38) (2023-11-29) + + +### Bug Fixes + +* support logs db for k8s ([#1864](https://github.com/kurtosis-tech/kurtosis/issues/1864)) ([8afa9c7](https://github.com/kurtosis-tech/kurtosis/commit/8afa9c7a7fcd7d7370e2d9740fb4e8e7bc6fe463)) + +## [0.85.37](https://github.com/kurtosis-tech/kurtosis/compare/0.85.36...0.85.37) (2023-11-29) + + +### Features + +* emui catalog overview ([#1865](https://github.com/kurtosis-tech/kurtosis/issues/1865)) ([2f118d9](https://github.com/kurtosis-tech/kurtosis/commit/2f118d92da40f2f5933c5d8f58a5a08c29b96c9a)) + ## [0.85.36](https://github.com/kurtosis-tech/kurtosis/compare/0.85.35...0.85.36) (2023-11-27) diff --git a/LICENSE.md b/LICENSE.md index e0257cd856..bef11c27ce 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -3,7 +3,7 @@ Business Source License 1.1 Parameters Licensor: Kurtosis Technologies, Inc. -Licensed Work: Kurtosis 0.85.36 +Licensed Work: Kurtosis 0.85.38 The Licensed Work is (c) 2023 Kurtosis Technologies, Inc. Additional Use Grant: You may make use of the Licensed Work, provided that you may not use the Licensed Work for an Environment Orchestration Service. @@ -12,7 +12,7 @@ you may not use the Licensed Work for an Environment Orchestration Service. allows third parties (other than your employees and contractors) to create distributed system environments. -Change Date: 2027-11-27 +Change Date: 2027-11-29 Change License: AGPLv3 (GNU Affero General Public License Version 3) diff --git a/api/golang/kurtosis_version/kurtosis_version.go b/api/golang/kurtosis_version/kurtosis_version.go index 2a09b043f8..b93fb3e88e 100644 --- a/api/golang/kurtosis_version/kurtosis_version.go +++ b/api/golang/kurtosis_version/kurtosis_version.go @@ -9,6 +9,6 @@ const ( // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers will know if they're compatible with the currently-running // API container - KurtosisVersion = "0.85.36" + KurtosisVersion = "0.85.38" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! ) diff --git a/api/rust/Cargo.toml b/api/rust/Cargo.toml index 517b2b77a1..da14da4a3f 100644 --- a/api/rust/Cargo.toml +++ b/api/rust/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "kurtosis-sdk" -version = "0.85.36" +version = "0.85.38" license = "BUSL-1.1" description = "Rust SDK for Kurtosis" edition = "2021" diff --git a/api/typescript/package.json b/api/typescript/package.json index bf65fbfa3f..4a1f3748b6 100644 --- a/api/typescript/package.json +++ b/api/typescript/package.json @@ -1,7 +1,7 @@ { "name": "kurtosis-sdk", "//": "NOTE: DO NOT UPDATE THIS VERSION MANUALLY - IT WILL BE UPDATED DURING THE RELEASE PROCESS!", - "version": "0.85.36", + "version": "0.85.38", "main": "./build/index", "description": "This repo contains a Typescript client for communicating with the Kurtosis Engine server, which is responsible for creating, managing and destroying Kurtosis Enclaves.", "types": "./build/index", diff --git a/api/typescript/src/kurtosis_version/kurtosis_version.ts b/api/typescript/src/kurtosis_version/kurtosis_version.ts index f866d41909..877c62f02d 100644 --- a/api/typescript/src/kurtosis_version/kurtosis_version.ts +++ b/api/typescript/src/kurtosis_version/kurtosis_version.ts @@ -1,5 +1,5 @@ // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! // This is necessary so that Kurt Core consumers (e.g. modules) will know if they're compatible with the currently-running // API container -export const KURTOSIS_VERSION: string = "0.85.36" +export const KURTOSIS_VERSION: string = "0.85.38" // !!!!!!!!!!! DO NOT UPDATE! WILL BE MANUALLY UPDATED DURING THE RELEASE PROCESS !!!!!!!!!!!!!!!!!!!!!! diff --git a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go index 6178286635..da406dbd69 100644 --- a/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go +++ b/core/server/api_container/server/startosis_engine/kurtosis_instruction/add_service/add_service_shared.go @@ -71,7 +71,7 @@ func validateSingleService(validatorEnvironment *startosis_validator.ValidatorEn } if validatorEnvironment.DoesServiceNameExist(serviceName) == startosis_validator.ComponentCreatedOrUpdatedDuringPackageRun { - return startosis_errors.NewValidationError("There was an error validating '%s' as service '%s' was created inside this package. Adding the same service twice in the same package is not allowed", AddServiceBuiltinName, serviceName) + return startosis_errors.NewValidationError("There was an error validating '%s' as service with the name '%s' already exists inside the package. Adding two different services with the same name isn't allowed; we recommend prefixing/suffixing the two service names or using two different names entirely.", AddServiceBuiltinName, serviceName) } if serviceConfig.GetFilesArtifactsExpansion() != nil { for _, artifactName := range serviceConfig.GetFilesArtifactsExpansion().ServiceDirpathsToArtifactIdentifiers { diff --git a/enclave-manager/web/src/components/KeyboardCommands.tsx b/enclave-manager/web/src/components/KeyboardCommands.tsx index 7c759c06f3..7bd883a070 100644 --- a/enclave-manager/web/src/components/KeyboardCommands.tsx +++ b/enclave-manager/web/src/components/KeyboardCommands.tsx @@ -13,3 +13,17 @@ export const FindCommand = (props: TextProps) => { ); }; + +export const OmniboxCommand = (props: TextProps) => { + let text = "^K"; + + if (navigator.userAgent.indexOf("Mac") > -1) { + text = "⌘K"; + } + + return ( + + {text} + + ); +}; diff --git a/enclave-manager/web/src/components/KurtosisBreadcrumbs.tsx b/enclave-manager/web/src/components/KurtosisBreadcrumbs.tsx index e61651f247..5cfce79a8d 100644 --- a/enclave-manager/web/src/components/KurtosisBreadcrumbs.tsx +++ b/enclave-manager/web/src/components/KurtosisBreadcrumbs.tsx @@ -16,16 +16,28 @@ import { import { ReactElement, useMemo } from "react"; import { BsCaretDownFill } from "react-icons/bs"; import { Link, Params, UIMatch, useMatches } from "react-router-dom"; -import { EmuiAppState, useEmuiAppContext } from "../emui/EmuiAppContext"; +import { EnclavesState, useEnclavesContext } from "../emui/enclaves/EnclavesContext"; import { isDefined } from "../utils"; import { RemoveFunctions } from "../utils/types"; import { BREADCRUMBS_HEIGHT, MAIN_APP_MAX_WIDTH_WITHOUT_PADDING } from "./theme/constants"; -export type KurtosisBreadcrumbsHandle = { - crumb?: (state: RemoveFunctions, params: Params) => KurtosisBreadcrumb | KurtosisBreadcrumb[]; - extraControls?: (state: RemoveFunctions, params: Params) => ReactElement | null; +type KurtosisBaseBreadcrumbsHandle = { + type: string; }; +export type KurtosisEnclavesBreadcrumbsHandle = KurtosisBaseBreadcrumbsHandle & { + type: "enclavesHandle"; + crumb?: (state: RemoveFunctions, params: Params) => KurtosisBreadcrumb | KurtosisBreadcrumb[]; + extraControls?: (state: RemoveFunctions, params: Params) => ReactElement | null; +}; + +export type KurtosisCatalogBreadcrumbsHandle = { + type: "catalogHandle"; + crumb?: () => KurtosisBreadcrumb | KurtosisBreadcrumb[]; +}; + +export type KurtosisBreadcrumbsHandle = KurtosisEnclavesBreadcrumbsHandle | KurtosisCatalogBreadcrumbsHandle; + type KurtosisBreadcrumbMenuItem = { name: string; destination: string; @@ -39,11 +51,42 @@ export type KurtosisBreadcrumb = { }; export const KurtosisBreadcrumbs = () => { - const { enclaves, filesAndArtifactsByEnclave, starlarkRunsByEnclave, servicesByEnclave, starlarkRunningInEnclaves } = - useEmuiAppContext(); - const matches = useMatches() as UIMatch[]; + const handlers = new Set(matches.map((match) => match.handle?.type).filter(isDefined)); + if (handlers.size === 0) { + throw Error(`Currently routes with no breadcrumb handles are not supported`); + } + if (handlers.size > 1) { + throw Error(`Routes with multiple breadcrumb handles are not supported.`); + } + const handleType = [...handlers][0]; + const isEnclavesMatches = ( + matches: UIMatch[], + onlyType: KurtosisBreadcrumbsHandle["type"], + ): matches is UIMatch[] => onlyType === "enclavesHandle"; + const isCatalogMatches = ( + matches: UIMatch[], + onlyType: KurtosisBreadcrumbsHandle["type"], + ): matches is UIMatch[] => onlyType === "catalogHandle"; + if (isEnclavesMatches(matches, handleType)) { + return ; + } + if (isCatalogMatches(matches, handleType)) { + return ; + } + + throw new Error(`Unable to handle breadcrumbs of type ${handleType}`); +}; + +type KurtosisEnclavesBreadcrumbsProps = { + matches: UIMatch[]; +}; + +const KurtosisEnclavesBreadcrumbs = ({ matches }: KurtosisEnclavesBreadcrumbsProps) => { + const { enclaves, filesAndArtifactsByEnclave, starlarkRunsByEnclave, servicesByEnclave, starlarkRunningInEnclaves } = + useEnclavesContext(); + const matchCrumbs = useMemo( () => matches.flatMap((match) => { @@ -100,6 +143,35 @@ export const KurtosisBreadcrumbs = () => { ], ); + return ; +}; + +type KurtosisCatalogBreadcrumbsProps = { + matches: UIMatch[]; +}; + +const KurtosisCatalogBreadcrumbs = ({ matches }: KurtosisCatalogBreadcrumbsProps) => { + const matchCrumbs = useMemo( + () => + matches.flatMap((match) => { + if (isDefined(match.handle?.crumb)) { + const r = match.handle.crumb(); + return Array.isArray(r) ? r : [r]; + } + return []; + }), + [matches], + ); + + return ; +}; + +type KurtosisBreadcrumbsImplProps = { + matchCrumbs: KurtosisBreadcrumb[]; + extraControls?: ReactElement[]; +}; + +const KurtosisBreadcrumbsImpl = ({ matchCrumbs, extraControls }: KurtosisBreadcrumbsImplProps) => { return ( diff --git a/enclave-manager/web/src/components/KurtosisThemeProvider.tsx b/enclave-manager/web/src/components/KurtosisThemeProvider.tsx index 069503dd5f..2e876ebd2d 100644 --- a/enclave-manager/web/src/components/KurtosisThemeProvider.tsx +++ b/enclave-manager/web/src/components/KurtosisThemeProvider.tsx @@ -36,6 +36,7 @@ const theme = extendTheme({ }, colors: { kurtosisGreen: { + 50: "#00371E", 100: "#005e11", 200: "#008c19", 300: "#00bb22", @@ -126,8 +127,8 @@ const theme = extendTheme({ }; }, solid: defineStyle((props) => ({ - _hover: { bg: "gray.700" }, - _active: { bg: "gray.700" }, + _hover: { bg: "gray.600" }, + _active: { bg: "gray.600" }, color: `${props.colorScheme}.400`, bg: "gray.700", })), diff --git a/enclave-manager/web/src/components/PageTitle.tsx b/enclave-manager/web/src/components/PageTitle.tsx new file mode 100644 index 0000000000..4c5119fdec --- /dev/null +++ b/enclave-manager/web/src/components/PageTitle.tsx @@ -0,0 +1,12 @@ +import { Heading, HeadingProps } from "@chakra-ui/react"; +import { PropsWithChildren } from "react"; + +type PageTitleProps = PropsWithChildren; + +export const PageTitle = ({ children, ...headingProps }: PageTitleProps) => { + return ( + + {children} + + ); +}; diff --git a/enclave-manager/web/src/components/catalog/KurtosisPackageCard.tsx b/enclave-manager/web/src/components/catalog/KurtosisPackageCard.tsx new file mode 100644 index 0000000000..fb86cd91ef --- /dev/null +++ b/enclave-manager/web/src/components/catalog/KurtosisPackageCard.tsx @@ -0,0 +1,59 @@ +import { Flex, Icon, Image, Text } from "@chakra-ui/react"; +import { IoStarSharp } from "react-icons/io5"; +import { useKurtosisClient } from "../../client/enclaveManager/KurtosisClientContext"; +import { KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { isDefined } from "../../utils"; +import { RunKurtosisPackageButton } from "./widgets/RunKurtosisPackageButton"; +import { SaveKurtosisPackageButton } from "./widgets/SaveKurtosisPackageButton"; + +type KurtosisPackageCardProps = { kurtosisPackage: KurtosisPackage; onClick?: () => void }; + +export const KurtosisPackageCard = ({ kurtosisPackage }: KurtosisPackageCardProps) => { + const client = useKurtosisClient(); + + const name = isDefined(kurtosisPackage.repositoryMetadata) + ? `${kurtosisPackage.repositoryMetadata.name} ${kurtosisPackage.repositoryMetadata.rootPath.split("/").join(" ")}` + : kurtosisPackage.name; + + return ( + + + + + + {name} + + + + {kurtosisPackage.repositoryMetadata?.owner.replaceAll("-", " ") || "Unknown owner"} + + + {kurtosisPackage.stars > 0 && ( + <> + + {kurtosisPackage.stars.toString()} + + )} + + + + + + + + + + ); +}; diff --git a/enclave-manager/web/src/components/catalog/KurtosisPackageCardGrid.tsx b/enclave-manager/web/src/components/catalog/KurtosisPackageCardGrid.tsx new file mode 100644 index 0000000000..facdb9698d --- /dev/null +++ b/enclave-manager/web/src/components/catalog/KurtosisPackageCardGrid.tsx @@ -0,0 +1,24 @@ +import { Grid, GridItem } from "@chakra-ui/react"; +import { memo } from "react"; +import { KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { KurtosisPackageCard } from "./KurtosisPackageCard"; + +type KurtosisPackageCardGridProps = { + packages: KurtosisPackage[]; + onPackageClicked?: (kurtosisPackage: KurtosisPackage) => void; +}; + +export const KurtosisPackageCardGrid = memo(({ packages, onPackageClicked }: KurtosisPackageCardGridProps) => { + return ( + + {packages.map((kurtosisPackage) => ( + onPackageClicked(kurtosisPackage) : undefined} + > + + + ))} + + ); +}); diff --git a/enclave-manager/web/src/components/catalog/widgets/RunKurtosisPackageButton.tsx b/enclave-manager/web/src/components/catalog/widgets/RunKurtosisPackageButton.tsx new file mode 100644 index 0000000000..f626c28eb6 --- /dev/null +++ b/enclave-manager/web/src/components/catalog/widgets/RunKurtosisPackageButton.tsx @@ -0,0 +1,37 @@ +import { Button, ButtonProps } from "@chakra-ui/react"; +import { useState } from "react"; +import { FiDownload } from "react-icons/fi"; +import { KurtosisPackage } from "../../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { EnclavesContextProvider } from "../../../emui/enclaves/EnclavesContext"; +import { ConfigureEnclaveModal } from "../../enclaves/modals/ConfigureEnclaveModal"; + +type RunKurtosisPackageButtonProps = ButtonProps & { + kurtosisPackage: KurtosisPackage; +}; + +export const RunKurtosisPackageButton = ({ kurtosisPackage, ...buttonProps }: RunKurtosisPackageButtonProps) => { + const [configuringEnclave, setConfiguringEnclave] = useState(false); + + return ( + <> + + {configuringEnclave && ( + + setConfiguringEnclave(false)} + kurtosisPackage={kurtosisPackage} + /> + + )} + + ); +}; diff --git a/enclave-manager/web/src/components/catalog/widgets/SaveKurtosisPackageButton.tsx b/enclave-manager/web/src/components/catalog/widgets/SaveKurtosisPackageButton.tsx new file mode 100644 index 0000000000..722374cb3c --- /dev/null +++ b/enclave-manager/web/src/components/catalog/widgets/SaveKurtosisPackageButton.tsx @@ -0,0 +1,46 @@ +import { Button, ButtonProps } from "@chakra-ui/react"; +import { memo, MouseEventHandler, useCallback, useMemo } from "react"; +import { MdBookmarkAdd } from "react-icons/md"; +import { KurtosisPackage } from "../../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { useCatalogContext } from "../../../emui/catalog/CatalogContext"; + +type SaveKurtosisPackageButtonProps = ButtonProps & { + kurtosisPackage: KurtosisPackage; +}; + +export const SaveKurtosisPackageButton = ({ kurtosisPackage, ...buttonProps }: SaveKurtosisPackageButtonProps) => { + const { savedPackages, togglePackageSaved } = useCatalogContext(); + const isPackageSaved = useMemo( + () => savedPackages.some((p) => p.name === kurtosisPackage.name), + [savedPackages, kurtosisPackage], + ); + + const handleClick = useCallback(() => togglePackageSaved(kurtosisPackage), [togglePackageSaved, kurtosisPackage]); + + return ; +}; + +type SaveKurtosisPackageButtonMemoProps = Omit & { + isPackageSaved: boolean; + onClick: MouseEventHandler; +}; + +// this is memo'd to skip unecessary renders, which effectively doubles the performance of this component (as it is +// displayed a lot. +const SaveKurtosisPackageButtonMemo = memo( + ({ isPackageSaved, onClick, ...buttonProps }: SaveKurtosisPackageButtonMemoProps) => { + return ( + + ); + }, +); diff --git a/enclave-manager/web/src/components/enclaves/logs/LogViewer.tsx b/enclave-manager/web/src/components/enclaves/logs/LogViewer.tsx index 0a84cecc03..aced192bfb 100644 --- a/enclave-manager/web/src/components/enclaves/logs/LogViewer.tsx +++ b/enclave-manager/web/src/components/enclaves/logs/LogViewer.tsx @@ -185,6 +185,7 @@ export const LogViewer = ({ isDisabled={logLines.length === 0} isIconButton aria-label={"Copy logs"} + color={"gray.100"} /> diff --git a/enclave-manager/web/src/components/enclaves/modals/ConfigureEnclaveModal.tsx b/enclave-manager/web/src/components/enclaves/modals/ConfigureEnclaveModal.tsx index e7d6313860..e3db76ed33 100644 --- a/enclave-manager/web/src/components/enclaves/modals/ConfigureEnclaveModal.tsx +++ b/enclave-manager/web/src/components/enclaves/modals/ConfigureEnclaveModal.tsx @@ -19,7 +19,7 @@ import { SubmitHandler } from "react-hook-form"; import { useNavigate } from "react-router-dom"; import { useKurtosisClient } from "../../../client/enclaveManager/KurtosisClientContext"; import { ArgumentValueType, KurtosisPackage } from "../../../client/packageIndexer/api/kurtosis_package_indexer_pb"; -import { useEmuiAppContext } from "../../../emui/EmuiAppContext"; +import { useEnclavesContext } from "../../../emui/enclaves/EnclavesContext"; import { EnclaveFullInfo } from "../../../emui/enclaves/types"; import { assertDefined, isDefined, stringifyError } from "../../../utils"; import { KURTOSIS_PACKAGE_ID_URL_ARG, KURTOSIS_PACKAGE_PARAMS_URL_ARG } from "../../constants"; @@ -51,7 +51,7 @@ export const ConfigureEnclaveModal = ({ existingEnclave, }: ConfigureEnclaveModalProps) => { const kurtosisClient = useKurtosisClient(); - const { createEnclave, runStarlarkPackage } = useEmuiAppContext(); + const { createEnclave, runStarlarkPackage } = useEnclavesContext(); const navigator = useNavigate(); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(); diff --git a/enclave-manager/web/src/components/enclaves/tables/PortsTable.tsx b/enclave-manager/web/src/components/enclaves/tables/PortsTable.tsx index 1ef5ba533b..f2b22464ee 100644 --- a/enclave-manager/web/src/components/enclaves/tables/PortsTable.tsx +++ b/enclave-manager/web/src/components/enclaves/tables/PortsTable.tsx @@ -98,7 +98,7 @@ export const PortsTable = ({ privatePorts, publicPorts, publicIp }: PortsTablePr ); }; diff --git a/enclave-manager/web/src/components/enclaves/widgets/DeleteEnclavesButton.tsx b/enclave-manager/web/src/components/enclaves/widgets/DeleteEnclavesButton.tsx index 539a70ae9e..bb827e5d7d 100644 --- a/enclave-manager/web/src/components/enclaves/widgets/DeleteEnclavesButton.tsx +++ b/enclave-manager/web/src/components/enclaves/widgets/DeleteEnclavesButton.tsx @@ -2,7 +2,7 @@ import { Button, ButtonProps, Tooltip } from "@chakra-ui/react"; import { useState } from "react"; import { FiTrash2 } from "react-icons/fi"; import { useNavigate } from "react-router-dom"; -import { useEmuiAppContext } from "../../../emui/EmuiAppContext"; +import { useEnclavesContext } from "../../../emui/enclaves/EnclavesContext"; import { EnclaveFullInfo } from "../../../emui/enclaves/types"; import { KurtosisAlertModal } from "../../KurtosisAlertModal"; @@ -11,7 +11,7 @@ type DeleteEnclavesButtonProps = ButtonProps & { }; export const DeleteEnclavesButton = ({ enclaves, ...buttonProps }: DeleteEnclavesButtonProps) => { - const { destroyEnclaves } = useEmuiAppContext(); + const { destroyEnclaves } = useEnclavesContext(); const navigator = useNavigate(); const [showModal, setShowModal] = useState(false); diff --git a/enclave-manager/web/src/components/theme/constants.ts b/enclave-manager/web/src/components/theme/constants.ts index 968973524f..cb18dbfea8 100644 --- a/enclave-manager/web/src/components/theme/constants.ts +++ b/enclave-manager/web/src/components/theme/constants.ts @@ -7,7 +7,7 @@ export const MAIN_APP_BOTTOM_PADDING = "20px"; export const MAIN_APP_LEFT_PADDING = "112px"; export const MAIN_APP_RIGHT_PADDING = "40px"; -export const MAIN_APP_MAX_WIDTH: CSS.Property.MaxWidth | number = "1320px"; -export const MAIN_APP_MAX_WIDTH_WITHOUT_PADDING: CSS.Property.MaxWidth | number = `${1320 - 112 - 40}px`; +export const MAIN_APP_MAX_WIDTH: CSS.Property.MaxWidth | number = "1440px"; +export const MAIN_APP_MAX_WIDTH_WITHOUT_PADDING: CSS.Property.MaxWidth | number = `${1440 - 112 - 40}px`; export const FLEX_STANDARD_GAP: CSS.Property.Gap | number = "32px"; diff --git a/enclave-manager/web/src/emui/App.tsx b/enclave-manager/web/src/emui/App.tsx index dff686686e..7250d80235 100644 --- a/enclave-manager/web/src/emui/App.tsx +++ b/enclave-manager/web/src/emui/App.tsx @@ -1,16 +1,14 @@ import { useMemo } from "react"; import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom"; import { KurtosisClientProvider, useKurtosisClient } from "../client/enclaveManager/KurtosisClientContext"; -import { - KurtosisPackageIndexerProvider, - useKurtosisPackageIndexerClient, -} from "../client/packageIndexer/KurtosisPackageIndexerClientContext"; +import { KurtosisPackageIndexerProvider } from "../client/packageIndexer/KurtosisPackageIndexerClientContext"; import { AppLayout } from "../components/AppLayout"; import { CreateEnclave } from "../components/enclaves/CreateEnclave"; import { KurtosisThemeProvider } from "../components/KurtosisThemeProvider"; +import { CatalogContextProvider } from "./catalog/CatalogContext"; import { catalogRoutes } from "./catalog/CatalogRoutes"; -import { EmuiAppContextProvider } from "./EmuiAppContext"; import { enclaveRoutes } from "./enclaves/EnclaveRoutes"; +import { EnclavesContextProvider } from "./enclaves/EnclavesContext"; const logLogo = (t: string) => console.log(`%c ${t}`, "background: black; color: #00C223"); logLogo(` @@ -40,9 +38,7 @@ export const EmuiApp = () => { - - - + @@ -51,7 +47,6 @@ export const EmuiApp = () => { const KurtosisRouter = () => { const kurtosisClient = useKurtosisClient(); - const kurtosisIndexerClient = useKurtosisPackageIndexerClient(); const router = useMemo( () => @@ -61,12 +56,28 @@ const KurtosisRouter = () => { element: ( - ), children: [ - { path: "/", children: enclaveRoutes(kurtosisClient) }, - { path: "/catalog", children: catalogRoutes(kurtosisIndexerClient) }, + { + path: "/", + element: ( + + + + + ), + children: enclaveRoutes(), + }, + { + path: "/catalog", + element: ( + + + + ), + children: catalogRoutes(), + }, ], }, ], @@ -74,7 +85,7 @@ const KurtosisRouter = () => { basename: kurtosisClient.getBaseApplicationUrl().pathname, }, ), - [kurtosisClient, kurtosisIndexerClient], + [kurtosisClient], ); return ; diff --git a/enclave-manager/web/src/emui/Navbar.tsx b/enclave-manager/web/src/emui/Navbar.tsx index 5daf20f873..8f6907773c 100644 --- a/enclave-manager/web/src/emui/Navbar.tsx +++ b/enclave-manager/web/src/emui/Navbar.tsx @@ -1,4 +1,4 @@ -import { FiHome } from "react-icons/fi"; +import { FiHome, FiPackage } from "react-icons/fi"; import { PiLinkSimpleBold } from "react-icons/pi"; import { Link, useLocation } from "react-router-dom"; import { KURTOSIS_CLOUD_CONNECT_URL } from "../client/constants"; @@ -18,14 +18,14 @@ export const Navbar = () => { isActive={location.pathname === "/" || location.pathname.startsWith("/enclave")} /> + + } isActive={location.pathname.startsWith("/catalog")} /> + {kurtosisClient.isRunningInCloud() && ( } /> )} - {/**/} - {/* } isActive={location.pathname.startsWith("/catalog")} />*/} - {/**/} ); }; diff --git a/enclave-manager/web/src/emui/catalog/Catalog.tsx b/enclave-manager/web/src/emui/catalog/Catalog.tsx index b10246cbc7..c3ee02b193 100644 --- a/enclave-manager/web/src/emui/catalog/Catalog.tsx +++ b/enclave-manager/web/src/emui/catalog/Catalog.tsx @@ -1,33 +1,150 @@ -import { Box, Flex } from "@chakra-ui/react"; -import { Suspense } from "react"; -import { Await, useLoaderData } from "react-router-dom"; +import { SmallCloseIcon } from "@chakra-ui/icons"; +import { + Box, + Card, + CardBody, + CardHeader, + Flex, + Heading, + Icon, + IconButton, + Input, + InputGroup, + InputLeftElement, + InputRightElement, + Text, +} from "@chakra-ui/react"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { FiSearch } from "react-icons/fi"; +import { MdBookmarkAdded } from "react-icons/md"; +import { GetPackagesResponse, KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { AppPageLayout } from "../../components/AppLayout"; +import { KurtosisPackageCardGrid } from "../../components/catalog/KurtosisPackageCardGrid"; +import { OmniboxCommand } from "../../components/KeyboardCommands"; import { KurtosisAlert } from "../../components/KurtosisAlert"; -import { CatalogLoaderResolved } from "./loader"; +import { PageTitle } from "../../components/PageTitle"; +import { useCatalogContext } from "./CatalogContext"; export const Catalog = () => { - const { catalog } = useLoaderData() as CatalogLoaderResolved; + const { catalog, savedPackages } = useCatalogContext(); - return ( - - } /> - - ); + if (catalog.isErr) { + return ( + + + + ); + } + + return ; }; type CatalogImplProps = { - catalog: CatalogLoaderResolved["catalog"]; + catalog: GetPackagesResponse; + savedPackages: KurtosisPackage[]; }; -const CatalogImpl = ({ catalog }: CatalogImplProps) => { - if (catalog.isErr) { - return ; - } +const CatalogImpl = ({ catalog, savedPackages }: CatalogImplProps) => { + const searchRef = useRef(null); + const [searchTerm, setSearchTerm] = useState(""); + const isSearching = searchTerm.length > 0; + const filteredCatalog = useMemo( + () => catalog.packages.filter((kurtosisPackage) => kurtosisPackage.name.toLowerCase().indexOf(searchTerm) > 0), + [searchTerm, catalog], + ); + + useEffect(() => { + const listener = function (e: KeyboardEvent) { + const element = searchRef?.current; + if ((e.ctrlKey && e.keyCode === 75) || (e.metaKey && e.keyCode === 75)) { + if (element !== document.activeElement) { + e.preventDefault(); + element?.focus(); + } + } + + // Clear the search on escape + if (e.key === "Escape" || e.keyCode === 27) { + if (element === document.activeElement) { + e.preventDefault(); + setSearchTerm(""); + } + } + }; + window.addEventListener("keydown", listener); + return () => window.removeEventListener("keydown", listener); + }, [searchRef]); return ( - - {catalog.value.map((kurtosisPackage) => ( - {kurtosisPackage.name} - ))} - + + + Package Catalog + + + + + + + + setSearchTerm(e.target.value)} + placeholder={"Search"} + /> + + {isSearching ? ( + } + onClick={() => setSearchTerm("")} + /> + ) : ( + + )} + + + + {isSearching && ( + <> + + {filteredCatalog.length} Matches + + + + )} + {!isSearching && ( + <> + {" "} + {savedPackages.length > 0 && ( + + + + + Saved + + + + + + + )} + + All + + + + )} + + ); }; diff --git a/enclave-manager/web/src/emui/catalog/CatalogContext.tsx b/enclave-manager/web/src/emui/catalog/CatalogContext.tsx new file mode 100644 index 0000000000..8c5148198c --- /dev/null +++ b/enclave-manager/web/src/emui/catalog/CatalogContext.tsx @@ -0,0 +1,92 @@ +import { Flex, Heading, Spinner } from "@chakra-ui/react"; +import { createContext, PropsWithChildren, useCallback, useContext, useEffect, useState } from "react"; +import { Result } from "true-myth"; +import { GetPackagesResponse, KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { useKurtosisPackageIndexerClient } from "../../client/packageIndexer/KurtosisPackageIndexerClientContext"; +import { isDefined } from "../../utils"; +import { loadSavedPackageNames, storeSavedPackages } from "./storage"; + +export type CatalogsState = { + catalog: Result; + savedPackages: KurtosisPackage[]; + refreshCatalog: () => Promise>; + togglePackageSaved: (kurtosisPackage: KurtosisPackage) => void; +}; + +const CatalogContext = createContext(null as any); + +export const CatalogContextProvider = ({ children }: PropsWithChildren) => { + const packageIndexerClient = useKurtosisPackageIndexerClient(); + const [catalog, setCatalog] = useState>(); + const [savedPackages, setSavedPackages] = useState([]); + + const refreshCatalog = useCallback(async () => { + setCatalog(undefined); + const catalog = await packageIndexerClient.getPackages(); + setCatalog(catalog); + + if (catalog.isOk) { + const savedPackageNames = new Set(loadSavedPackageNames()); + setSavedPackages(catalog.value.packages.filter((kurtosisPackage) => savedPackageNames.has(kurtosisPackage.name))); + } + + return catalog; + }, [packageIndexerClient]); + + const togglePackageSaved = useCallback((kurtosisPackage: KurtosisPackage) => { + setSavedPackages((savedPackages) => { + const packageSavedAlready = savedPackages.some((p) => p.name === kurtosisPackage.name); + const newSavedPackages: KurtosisPackage[] = packageSavedAlready + ? savedPackages.filter((p) => p.name !== kurtosisPackage.name) + : [...savedPackages, kurtosisPackage]; + storeSavedPackages(newSavedPackages); + return newSavedPackages; + }); + }, []); + + useEffect(() => { + refreshCatalog(); + }, [refreshCatalog]); + + if (!isDefined(catalog)) { + return ( + + + + Fetching Catalog... + + + ); + } + + return ( + + {children} + + ); +}; + +export const useCatalogContext = () => { + return useContext(CatalogContext); +}; + +export const usePackageCatalog = () => { + const { catalog } = useCatalogContext(); + return catalog; +}; + +export const useKurtosisPackage = (packageId: string): Result => { + const catalog = usePackageCatalog(); + const kurtosisPackage = catalog.map((catalog) => + catalog.packages.find((kurtosisPackage) => kurtosisPackage.name === packageId), + ); + + if (kurtosisPackage.isErr) { + return kurtosisPackage.cast(); + } else { + if (!isDefined(kurtosisPackage.value)) { + return Result.err(`No package with id ${packageId} could be found.`); + } + return Result.ok(kurtosisPackage.value); + } +}; diff --git a/enclave-manager/web/src/emui/catalog/CatalogRoutes.tsx b/enclave-manager/web/src/emui/catalog/CatalogRoutes.tsx index 07920fd9e8..293e183cc7 100644 --- a/enclave-manager/web/src/emui/catalog/CatalogRoutes.tsx +++ b/enclave-manager/web/src/emui/catalog/CatalogRoutes.tsx @@ -1,14 +1,10 @@ -import { RouteObject } from "react-router-dom"; - -import { KurtosisPackageIndexerClient } from "../../client/packageIndexer/KurtosisPackageIndexerClient"; +import { KurtosisCatalogRouteObject } from "../types"; import { Catalog } from "./Catalog"; -import { catalogLoader } from "./loader"; -export const catalogRoutes = (kurtosisIndexerClient: KurtosisPackageIndexerClient): RouteObject[] => [ +export const catalogRoutes = (): KurtosisCatalogRouteObject[] => [ { path: "/catalog", - handle: { crumb: () => ({ name: "Catalog", destination: "/catalog" }) }, - loader: catalogLoader(kurtosisIndexerClient), + handle: { type: "catalogHandle" as "catalogHandle", crumb: () => ({ name: "Catalog", destination: "/catalog" }) }, id: "catalog", element: , }, diff --git a/enclave-manager/web/src/emui/catalog/loader.ts b/enclave-manager/web/src/emui/catalog/loader.ts deleted file mode 100644 index 5ea73f5560..0000000000 --- a/enclave-manager/web/src/emui/catalog/loader.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { defer } from "react-router-dom"; -import { Result } from "true-myth"; -import { KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; -import { KurtosisPackageIndexerClient } from "../../client/packageIndexer/KurtosisPackageIndexerClient"; - -const loadCatalog = async ( - kurtosisIndexerClient: KurtosisPackageIndexerClient, -): Promise> => { - const packagesResponse = await kurtosisIndexerClient.getPackages(); - if (packagesResponse.isErr) { - return Result.err(packagesResponse.error || "Unknown api error"); - } - - return Result.ok(packagesResponse.value.packages); -}; - -export type CatalogLoaderResolved = { - catalog: Awaited>; -}; - -export const catalogLoader = (kurtosisIndexerClient: KurtosisPackageIndexerClient) => async () => { - return defer({ catalog: loadCatalog(kurtosisIndexerClient) }); -}; diff --git a/enclave-manager/web/src/emui/catalog/storage.ts b/enclave-manager/web/src/emui/catalog/storage.ts new file mode 100644 index 0000000000..c764a42b11 --- /dev/null +++ b/enclave-manager/web/src/emui/catalog/storage.ts @@ -0,0 +1,26 @@ +import { KurtosisPackage } from "../../client/packageIndexer/api/kurtosis_package_indexer_pb"; +import { isDefined, stringifyError } from "../../utils"; + +const SAVED_PACKAGES_LOCAL_STORAGE_KEY = "kurtosis-saved-packages"; + +export const storeSavedPackages = (kurtosisPackages: KurtosisPackage[]) => { + localStorage.setItem( + SAVED_PACKAGES_LOCAL_STORAGE_KEY, + JSON.stringify(kurtosisPackages.map((kurtosisPackage) => kurtosisPackage.name)), + ); +}; + +export const loadSavedPackageNames = () => { + try { + const savedRawValue = localStorage.getItem(SAVED_PACKAGES_LOCAL_STORAGE_KEY); + + if (!isDefined(savedRawValue)) { + return []; + } + + return JSON.parse(savedRawValue); + } catch (error: any) { + console.error(`Unable to load saved package names. Got error: ${stringifyError(error)}`); + return []; + } +}; diff --git a/enclave-manager/web/src/emui/enclaves/EnclaveList.tsx b/enclave-manager/web/src/emui/enclaves/EnclaveList.tsx index 92d5315170..3418036644 100644 --- a/enclave-manager/web/src/emui/enclaves/EnclaveList.tsx +++ b/enclave-manager/web/src/emui/enclaves/EnclaveList.tsx @@ -1,11 +1,12 @@ -import { Button, ButtonGroup, Flex, Text } from "@chakra-ui/react"; +import { Button, ButtonGroup, Flex } from "@chakra-ui/react"; import { useEffect, useMemo, useState } from "react"; import { AppPageLayout } from "../../components/AppLayout"; import { CreateEnclaveButton } from "../../components/enclaves/CreateEnclaveButton"; import { EnclavesTable } from "../../components/enclaves/tables/EnclavesTable"; import { DeleteEnclavesButton } from "../../components/enclaves/widgets/DeleteEnclavesButton"; import { KurtosisAlert } from "../../components/KurtosisAlert"; -import { useFullEnclaves } from "../EmuiAppContext"; +import { PageTitle } from "../../components/PageTitle"; +import { useFullEnclaves } from "./EnclavesContext"; import { EnclaveFullInfo } from "./types"; export const EnclaveList = () => { @@ -31,9 +32,7 @@ export const EnclaveList = () => { return ( - - Enclaves - + Enclaves {selectedEnclaves.length > 0 && ( diff --git a/enclave-manager/web/src/emui/enclaves/EnclaveRoutes.tsx b/enclave-manager/web/src/emui/enclaves/EnclaveRoutes.tsx index 000553324c..a9cbcd11af 100644 --- a/enclave-manager/web/src/emui/enclaves/EnclaveRoutes.tsx +++ b/enclave-manager/web/src/emui/enclaves/EnclaveRoutes.tsx @@ -1,34 +1,28 @@ import { Icon } from "@chakra-ui/react"; import { FilesArtifactNameAndUuid, ServiceInfo } from "enclave-manager-sdk/build/api_container_service_pb"; import { FiPlus } from "react-icons/fi"; -import { Outlet, Params, RouteObject } from "react-router-dom"; -import { KurtosisClient } from "../../client/enclaveManager/KurtosisClient"; +import { Outlet, Params } from "react-router-dom"; import { GoToEnclaveOverviewButton } from "../../components/enclaves/GotToEncalaveOverviewButton"; -import { KurtosisBreadcrumbsHandle } from "../../components/KurtosisBreadcrumbs"; import { RemoveFunctions } from "../../utils/types"; -import { EmuiAppState } from "../EmuiAppContext"; +import { KurtosisEnclavesRouteObject } from "../types"; import { Artifact } from "./enclave/artifact/Artifact"; import { Enclave } from "./enclave/Enclave"; import { EnclaveRouteContextProvider } from "./enclave/EnclaveRouteContext"; import { EnclaveLogs } from "./enclave/logs/EnclaveLogs"; import { Service } from "./enclave/service/Service"; import { EnclaveList } from "./EnclaveList"; +import { EnclavesState } from "./EnclavesContext"; -type KurtosisRouteObject = RouteObject & { - handle?: KurtosisBreadcrumbsHandle; - children?: KurtosisRouteObject[]; -}; - -export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObject[] => [ +export const enclaveRoutes = (): KurtosisEnclavesRouteObject[] => [ { path: "/enclaves?", - handle: { crumb: () => ({ name: "Enclaves", destination: "/" }) }, + handle: { type: "enclavesHandle" as "enclavesHandle", crumb: () => ({ name: "Enclaves", destination: "/" }) }, id: "enclaves", element: , }, { path: "/enclave", - handle: { crumb: () => ({ name: "Enclaves", destination: "/" }) }, + handle: { type: "enclavesHandle" as "enclavesHandle", crumb: () => ({ name: "Enclaves", destination: "/" }) }, children: [ { path: "/enclave/:enclaveUUID", @@ -39,7 +33,8 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje ), handle: { - crumb: ({ enclaves: enclavesResult }: RemoveFunctions, params: Params) => { + type: "enclavesHandle" as "enclavesHandle", + crumb: ({ enclaves: enclavesResult }: RemoveFunctions, params: Params) => { const enclaves = enclavesResult.unwrapOr([]); const enclave = enclaves.find((enclave) => enclave.shortenedUuid === params.enclaveUUID); return { @@ -67,7 +62,8 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje { path: "service/:serviceUUID", handle: { - crumb: ({ servicesByEnclave }: RemoveFunctions, params: Params) => { + type: "enclavesHandle" as "enclavesHandle", + crumb: ({ servicesByEnclave }: RemoveFunctions, params: Params) => { const services = Object.values( servicesByEnclave[params.enclaveUUID || ""]?.unwrapOr({ serviceInfo: {} as Record, @@ -96,7 +92,8 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje id: "serviceActiveTab", element: , handle: { - crumb: (data: RemoveFunctions, params: Params) => { + type: "enclavesHandle" as "enclavesHandle", + crumb: (data: RemoveFunctions, params: Params) => { const activeTab = params.activeTab; let routeName = activeTab?.toLowerCase() === "logs" ? "Logs" : "Overview"; @@ -116,7 +113,8 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje path: "file/:fileUUID", element: , handle: { - crumb: ({ filesAndArtifactsByEnclave }: RemoveFunctions, params: Params) => { + type: "enclavesHandle" as "enclavesHandle", + crumb: ({ filesAndArtifactsByEnclave }: RemoveFunctions, params: Params) => { const artifacts = Object.values( filesAndArtifactsByEnclave[params.enclaveUUID || ""]?.unwrapOr({ fileNamesAndUuids: [] as FilesArtifactNameAndUuid[], @@ -141,7 +139,7 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje ]; }, hasTabs: false, - extraControls: (state: RemoveFunctions, params: Params) => ( + extraControls: (state: RemoveFunctions, params: Params) => ( ), }, @@ -151,8 +149,9 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje id: "enclaveLogs", element: , handle: { + type: "enclavesHandle" as "enclavesHandle", hasTabs: false, - extraControls: ({ starlarkRunningInEnclaves }: RemoveFunctions, params: Params) => + extraControls: ({ starlarkRunningInEnclaves }: RemoveFunctions, params: Params) => starlarkRunningInEnclaves.some((enclave) => enclave.shortenedUuid === params.enclaveUUID) ? null : ( ), @@ -167,7 +166,8 @@ export const enclaveRoutes = (kurtosisClient: KurtosisClient): KurtosisRouteObje id: "enclaveActiveTab", element: , handle: { - crumb: (data: RemoveFunctions, params: Params) => { + type: "enclavesHandle" as "enclavesHandle", + crumb: (data: RemoveFunctions, params: Params) => { const activeTab = params.activeTab; let routeName = diff --git a/enclave-manager/web/src/emui/EmuiAppContext.tsx b/enclave-manager/web/src/emui/enclaves/EnclavesContext.tsx similarity index 90% rename from enclave-manager/web/src/emui/EmuiAppContext.tsx rename to enclave-manager/web/src/emui/enclaves/EnclavesContext.tsx index b42eadc8c1..da01d34509 100644 --- a/enclave-manager/web/src/emui/EmuiAppContext.tsx +++ b/enclave-manager/web/src/emui/enclaves/EnclavesContext.tsx @@ -18,12 +18,12 @@ import { useState, } from "react"; import { Result } from "true-myth"; -import { useKurtosisClient } from "../client/enclaveManager/KurtosisClientContext"; -import { assertDefined, isDefined } from "../utils"; -import { RemoveFunctions } from "../utils/types"; -import { EnclaveFullInfo } from "./enclaves/types"; +import { useKurtosisClient } from "../../client/enclaveManager/KurtosisClientContext"; +import { assertDefined, isDefined } from "../../utils"; +import { RemoveFunctions } from "../../utils/types"; +import { EnclaveFullInfo } from "./types"; -export type EmuiAppState = { +export type EnclavesState = { enclaves: Result[], string>; servicesByEnclave: Record>; filesAndArtifactsByEnclave: Record>; @@ -52,13 +52,17 @@ export type EmuiAppState = { updateStarlarkFinishedInEnclave: (enclave: RemoveFunctions) => void; }; -const EmuiAppContext = createContext(null as any); +const EnclavesContext = createContext(null as any); -export const EmuiAppContextProvider = ({ children }: PropsWithChildren) => { - const [isInitialLoading, setIsInitialLoading] = useState(true); +type EnclavesContextProviderProps = PropsWithChildren<{ + skipInitialLoad?: boolean; +}>; - const [state, setState] = useState>({ - enclaves: Result.err("Enclaves not initialised, call refreshEnclaves"), +export const EnclavesContextProvider = ({ skipInitialLoad, children }: EnclavesContextProviderProps) => { + const [isInitialLoading, setIsInitialLoading] = useState(!skipInitialLoad); + + const [state, setState] = useState>({ + enclaves: skipInitialLoad ? Result.ok([]) : Result.err("Enclaves not initialised, call refreshEnclaves"), servicesByEnclave: {}, filesAndArtifactsByEnclave: {}, starlarkRunsByEnclave: {}, @@ -185,10 +189,12 @@ export const EmuiAppContextProvider = ({ children }: PropsWithChildren) => { useEffect(() => { (async () => { - await refreshEnclaves(); - setIsInitialLoading(false); + if (isInitialLoading) { + await refreshEnclaves(); + setIsInitialLoading(false); + } })(); - }, [refreshEnclaves]); + }, [refreshEnclaves, isInitialLoading]); if (isInitialLoading) { return ( @@ -202,7 +208,7 @@ export const EmuiAppContextProvider = ({ children }: PropsWithChildren) => { } return ( - { }} > {children} - + ); }; -export const useEmuiAppContext = () => { - return useContext(EmuiAppContext); +export const useEnclavesContext = () => { + return useContext(EnclavesContext); }; export const useFullEnclave = (enclaveUUID: string): Result => { @@ -233,7 +239,7 @@ export const useFullEnclave = (enclaveUUID: string): Result enclave.shortenedUuid === enclaveUUID) : null; @@ -288,7 +294,7 @@ export const useFullEnclaves = (): Result => { refreshServices, refreshStarlarkRun, refreshFilesAndArtifacts, - } = useEmuiAppContext(); + } = useEnclavesContext(); // This hook can trigger a lot of requests to refresh data. To avoid creating waterfalls // of effects this refreshId along with cache values are used to restrict changes to the diff --git a/enclave-manager/web/src/emui/enclaves/enclave/Enclave.tsx b/enclave-manager/web/src/emui/enclaves/enclave/Enclave.tsx index 803cd79bfb..fdf04487cf 100644 --- a/enclave-manager/web/src/emui/enclaves/enclave/Enclave.tsx +++ b/enclave-manager/web/src/emui/enclaves/enclave/Enclave.tsx @@ -1,4 +1,4 @@ -import { Flex, TabPanel, TabPanels, Tabs, Text } from "@chakra-ui/react"; +import { Flex, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import { useNavigate, useParams } from "react-router-dom"; import { FunctionComponent, useState } from "react"; @@ -8,7 +8,8 @@ import { DeleteEnclavesButton } from "../../../components/enclaves/widgets/Delet import { FeatureNotImplementedModal } from "../../../components/FeatureNotImplementedModal"; import { HoverLineTabList } from "../../../components/HoverLineTabList"; import { KurtosisAlert } from "../../../components/KurtosisAlert"; -import { useFullEnclave } from "../../EmuiAppContext"; +import { PageTitle } from "../../../components/PageTitle"; +import { useFullEnclave } from "../EnclavesContext"; import { EnclaveFullInfo } from "../types"; import { EnclaveOverview } from "./overview/EnclaveOverview"; @@ -55,9 +56,7 @@ const EnclaveImpl = ({ enclave }: EnclaveImplProps) => { - - {enclave.name} - + {enclave.name} path)} activeTab={activeTab} /> diff --git a/enclave-manager/web/src/emui/enclaves/enclave/EnclaveRouteContext.tsx b/enclave-manager/web/src/emui/enclaves/enclave/EnclaveRouteContext.tsx index c54b2e57c1..857c4e3139 100644 --- a/enclave-manager/web/src/emui/enclaves/enclave/EnclaveRouteContext.tsx +++ b/enclave-manager/web/src/emui/enclaves/enclave/EnclaveRouteContext.tsx @@ -2,7 +2,7 @@ import { createContext, PropsWithChildren, useContext } from "react"; import { useParams } from "react-router-dom"; import { AppPageLayout } from "../../../components/AppLayout"; import { KurtosisAlert } from "../../../components/KurtosisAlert"; -import { useFullEnclave } from "../../EmuiAppContext"; +import { useFullEnclave } from "../EnclavesContext"; import { EnclaveFullInfo } from "../types"; type EnclaveRouteContextState = { diff --git a/enclave-manager/web/src/emui/enclaves/enclave/artifact/Artifact.tsx b/enclave-manager/web/src/emui/enclaves/enclave/artifact/Artifact.tsx index 96861e824f..94e08649ed 100644 --- a/enclave-manager/web/src/emui/enclaves/enclave/artifact/Artifact.tsx +++ b/enclave-manager/web/src/emui/enclaves/enclave/artifact/Artifact.tsx @@ -13,7 +13,7 @@ import { FileTree, FileTreeNode } from "../../../../components/FileTree"; import { KurtosisAlert } from "../../../../components/KurtosisAlert"; import { TitledCard } from "../../../../components/TitledCard"; import { isDefined } from "../../../../utils"; -import { useFullEnclave } from "../../../EmuiAppContext"; +import { useFullEnclave } from "../../EnclavesContext"; import { EnclaveFullInfo } from "../../types"; export const Artifact = () => { diff --git a/enclave-manager/web/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx b/enclave-manager/web/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx index 4caf997248..7fceae31fc 100644 --- a/enclave-manager/web/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx +++ b/enclave-manager/web/src/emui/enclaves/enclave/logs/EnclaveLogs.tsx @@ -9,7 +9,7 @@ import { LogViewer } from "../../../../components/enclaves/logs/LogViewer"; import { LogLineMessage } from "../../../../components/enclaves/logs/types"; import { DeleteEnclavesButton } from "../../../../components/enclaves/widgets/DeleteEnclavesButton"; import { isAsyncIterable, stringifyError } from "../../../../utils"; -import { useEmuiAppContext } from "../../../EmuiAppContext"; +import { useEnclavesContext } from "../../EnclavesContext"; import { useEnclaveFromParams } from "../EnclaveRouteContext"; // These are the stages we want to catch and handle in the UI @@ -44,7 +44,7 @@ export function starlarkResponseLineToLogLineMessage(l: StarlarkRunResponseLine) export const EnclaveLogs = () => { const enclave = useEnclaveFromParams(); const { refreshServices, refreshFilesAndArtifacts, refreshStarlarkRun, updateStarlarkFinishedInEnclave } = - useEmuiAppContext(); + useEnclavesContext(); const navigator = useNavigate(); const location = useLocation() as Location<{ logs: AsyncIterable }>; const [progress, setProgress] = useState({ stage: "waiting" }); diff --git a/enclave-manager/web/src/emui/enclaves/enclave/service/Service.tsx b/enclave-manager/web/src/emui/enclaves/enclave/service/Service.tsx index b7e5e9f2ac..77b3886ac5 100644 --- a/enclave-manager/web/src/emui/enclaves/enclave/service/Service.tsx +++ b/enclave-manager/web/src/emui/enclaves/enclave/service/Service.tsx @@ -1,10 +1,11 @@ -import { Flex, Spinner, TabPanel, TabPanels, Tabs, Text } from "@chakra-ui/react"; +import { Flex, Spinner, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import { ServiceInfo } from "enclave-manager-sdk/build/api_container_service_pb"; import { FunctionComponent } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { AppPageLayout } from "../../../../components/AppLayout"; import { HoverLineTabList } from "../../../../components/HoverLineTabList"; import { KurtosisAlert } from "../../../../components/KurtosisAlert"; +import { PageTitle } from "../../../../components/PageTitle"; import { isDefined } from "../../../../utils"; import { EnclaveFullInfo } from "../../types"; import { useEnclaveFromParams } from "../EnclaveRouteContext"; @@ -70,9 +71,7 @@ const ServiceImpl = ({ enclave, service }: ServiceImplProps) => { - - {service.name} - + {service.name} path)} activeTab={activeTab} /> diff --git a/enclave-manager/web/src/emui/types.ts b/enclave-manager/web/src/emui/types.ts new file mode 100644 index 0000000000..77ea2e28e1 --- /dev/null +++ b/enclave-manager/web/src/emui/types.ts @@ -0,0 +1,12 @@ +import { RouteObject } from "react-router-dom"; +import { KurtosisCatalogBreadcrumbsHandle, KurtosisEnclavesBreadcrumbsHandle } from "../components/KurtosisBreadcrumbs"; + +export type KurtosisEnclavesRouteObject = RouteObject & { + handle?: KurtosisEnclavesBreadcrumbsHandle; + children?: KurtosisEnclavesRouteObject[]; +}; + +export type KurtosisCatalogRouteObject = RouteObject & { + handle?: KurtosisCatalogBreadcrumbsHandle; + children?: KurtosisEnclavesRouteObject[]; +}; diff --git a/enclave-manager/web/tsconfig.json b/enclave-manager/web/tsconfig.json index 9d379a3c4a..6e7d7314d0 100644 --- a/enclave-manager/web/tsconfig.json +++ b/enclave-manager/web/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2019", "lib": ["dom", "dom.iterable", "esnext"], "allowJs": true, "skipLibCheck": true, diff --git a/engine/server/engine/main.go b/engine/server/engine/main.go index b5be184041..a2661a2fee 100644 --- a/engine/server/engine/main.go +++ b/engine/server/engine/main.go @@ -19,6 +19,8 @@ import ( em_api "github.com/kurtosis-tech/kurtosis/enclave-manager/server" "github.com/kurtosis-tech/kurtosis/engine/launcher/args" "github.com/kurtosis-tech/kurtosis/engine/launcher/args/kurtosis_backend_config" + "github.com/kurtosis-tech/kurtosis/engine/server/engine/centralized_logs" + "github.com/kurtosis-tech/kurtosis/engine/server/engine/centralized_logs/client_implementations/kurtosis_backend" "github.com/kurtosis-tech/kurtosis/engine/server/engine/centralized_logs/client_implementations/persistent_volume" "github.com/kurtosis-tech/kurtosis/engine/server/engine/centralized_logs/client_implementations/persistent_volume/log_file_manager" "github.com/kurtosis-tech/kurtosis/engine/server/engine/centralized_logs/client_implementations/persistent_volume/logs_clock" @@ -140,19 +142,11 @@ func runMain() error { return stacktrace.Propagate(err, "An error occurred getting the Kurtosis backend for backend type '%v' and config '%+v'", serverArgs.KurtosisBackendType, backendConfig) } + logsDatabaseClient := getLogsDatabaseClient(serverArgs.KurtosisBackendType, kurtosisBackend) + + // TODO: Move log file management into LogsDatabaseClient osFs := volume_filesystem.NewOsVolumeFilesystem() realTime := logs_clock.NewRealClock() - - // TODO: remove once users are fully migrated to log retention/new log schema - // pulls logs per enclave/per service id - perFileStreamStrategy := stream_logs_strategy.NewPerFileStreamLogsStrategy() - perFileLogsDatabaseClient := persistent_volume.NewPersistentVolumeLogsDatabaseClient(kurtosisBackend, osFs, perFileStreamStrategy) - - // pulls logs /per week/per enclave/per service - perWeekStreamStrategy := stream_logs_strategy.NewPerWeekStreamLogsStrategy(realTime) - perWeekLogsDatabaseClient := persistent_volume.NewPersistentVolumeLogsDatabaseClient(kurtosisBackend, osFs, perWeekStreamStrategy) - - // TODO: Move logFileManager into LogsDatabaseClient logFileManager := log_file_manager.NewLogFileManager(kurtosisBackend, osFs, realTime) logFileManager.StartLogFileManagement(ctx) @@ -232,8 +226,7 @@ func runMain() error { enclaveManager, serverArgs.MetricsUserID, serverArgs.DidUserAcceptSendingMetrics, - perWeekLogsDatabaseClient, - perFileLogsDatabaseClient, + logsDatabaseClient, logFileManager, metricsClient) apiPath, handler := kurtosis_engine_rpc_api_bindingsconnect.NewEngineServiceHandler(engineConnectServer) @@ -329,6 +322,21 @@ func getKurtosisBackend(ctx context.Context, kurtosisBackendType args.KurtosisBa return kurtosisBackend, nil } +// if cluster is docker, return logs client for centralized logging, otherwise use logs db of kurtosis backend which uses k8s logs under the hood +func getLogsDatabaseClient(kurtosisBackendType args.KurtosisBackendType, kurtosisBackend backend_interface.KurtosisBackend) centralized_logs.LogsDatabaseClient { + var logsDatabaseClient centralized_logs.LogsDatabaseClient + switch kurtosisBackendType { + case args.KurtosisBackendType_Docker: + osFs := volume_filesystem.NewOsVolumeFilesystem() + realTime := logs_clock.NewRealClock() + perWeekStreamLogsStrategy := stream_logs_strategy.NewPerWeekStreamLogsStrategy(realTime) + logsDatabaseClient = persistent_volume.NewPersistentVolumeLogsDatabaseClient(kurtosisBackend, osFs, perWeekStreamLogsStrategy) + case args.KurtosisBackendType_Kubernetes: + logsDatabaseClient = kurtosis_backend.NewKurtosisBackendLogsDatabaseClient(kurtosisBackend) + } + return logsDatabaseClient +} + func formatFilenameFunctionForLogs(filename string, functionName string) string { var output strings.Builder output.WriteString("[") diff --git a/engine/server/engine/server/engine_connect_server_service.go b/engine/server/engine/server/engine_connect_server_service.go index 015bb17c28..5fd20a33b5 100644 --- a/engine/server/engine/server/engine_connect_server_service.go +++ b/engine/server/engine/server/engine_connect_server_service.go @@ -15,17 +15,12 @@ import ( "github.com/sirupsen/logrus" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" - "time" ) const ( subnetworkDisableBecauseItIsDeprecated = false ) -var ( - logRetentionFeatureReleaseTime = time.Date(2023, 9, 7, 13, 0, 0, 0, time.UTC) -) - type EngineConnectServerService struct { // The version tag of the engine server image, so it can report its own version imageVersionTag string @@ -38,14 +33,8 @@ type EngineConnectServerService struct { // User consent to send metrics didUserAcceptSendingMetrics bool - // The clients for consuming container logs from the logs' database server - - // per week pulls logs from enclaves created post log retention feature - perWeekLogsDatabaseClient centralized_logs.LogsDatabaseClient - - // per file pulls logs from enclaves created pre log retention feature - // TODO: remove once users are fully migrated to log retention/new log schema - perFileLogsDatabaseClient centralized_logs.LogsDatabaseClient + // The client for consuming container logs from the logs database + logsDatabaseClient centralized_logs.LogsDatabaseClient logFileManager *log_file_manager.LogFileManager @@ -57,8 +46,7 @@ func NewEngineConnectServerService( enclaveManager *enclave_manager.EnclaveManager, metricsUserId string, didUserAcceptSendingMetrics bool, - perWeekLogsDatabaseClient centralized_logs.LogsDatabaseClient, - perFileLogsDatabaseClient centralized_logs.LogsDatabaseClient, + logsDatabaseClient centralized_logs.LogsDatabaseClient, logFileManager *log_file_manager.LogFileManager, metricsClient metrics_client.MetricsClient, ) *EngineConnectServerService { @@ -67,8 +55,7 @@ func NewEngineConnectServerService( enclaveManager: enclaveManager, metricsUserID: metricsUserId, didUserAcceptSendingMetrics: didUserAcceptSendingMetrics, - perWeekLogsDatabaseClient: perWeekLogsDatabaseClient, - perFileLogsDatabaseClient: perFileLogsDatabaseClient, + logsDatabaseClient: logsDatabaseClient, logFileManager: logFileManager, metricsClient: metricsClient, } @@ -205,7 +192,7 @@ func (service *EngineConnectServerService) GetServiceLogs(ctx context.Context, c requestedServiceUuids[serviceUuid] = true } - if service.perWeekLogsDatabaseClient == nil || service.perFileLogsDatabaseClient == nil { + if service.logsDatabaseClient == nil { return stacktrace.NewError("It's not possible to return service logs because there is no logs database client; this is bug in Kurtosis") } @@ -225,14 +212,7 @@ func (service *EngineConnectServerService) GetServiceLogs(ctx context.Context, c return stacktrace.Propagate(err, "An error occurred creating the conjunctive log line filters from the GRPC's conjunctive log line filters '%+v'", args.GetConjunctiveFilters()) } - // get enclave creation time to determine strategy to pull logs - enclaveCreationTime, err := service.getEnclaveCreationTime(ctx, enclaveUuid) - if err != nil { - return stacktrace.Propagate(err, "An error occurred while trying to get the enclave creation time to determine how to pull logs.") - } - logsDatabaseClient := service.getLogsDatabaseClient(enclaveCreationTime) - - serviceLogsByServiceUuidChan, errChan, cancelCtxFunc, err = logsDatabaseClient.StreamUserServiceLogs( + serviceLogsByServiceUuidChan, errChan, cancelCtxFunc, err = service.logsDatabaseClient.StreamUserServiceLogs( contextWithCancel, enclaveUuid, requestedServiceUuids, @@ -300,8 +280,7 @@ func (service *EngineConnectServerService) reportAnyMissingUuidsAndGetNotFoundUu requestedServiceUuids map[user_service.ServiceUUID]bool, stream *connect.ServerStream[kurtosis_engine_rpc_api_bindings.GetServiceLogsResponse], ) (map[string]bool, error) { - // doesn't matter which logs client is used here - existingServiceUuids, err := service.perWeekLogsDatabaseClient.FilterExistingServiceUuids(ctx, enclaveUuid, requestedServiceUuids) + existingServiceUuids, err := service.logsDatabaseClient.FilterExistingServiceUuids(ctx, enclaveUuid, requestedServiceUuids) if err != nil { return nil, stacktrace.Propagate(err, "An error occurred retrieving the exhaustive list of service UUIDs from the log client for enclave '%v' and for the requested UUIDs '%+v'", enclaveUuid, requestedServiceUuids) } @@ -422,30 +401,3 @@ func newConjunctiveLogLineFiltersFromGRPCLogLineFilters( return conjunctiveLogLineFilters, nil } - -// If the enclave was created prior to log retention, return the per file logs client -func (service *EngineConnectServerService) getLogsDatabaseClient(enclaveCreationTime time.Time) centralized_logs.LogsDatabaseClient { - if enclaveCreationTime.After(logRetentionFeatureReleaseTime) { - return service.perWeekLogsDatabaseClient - } else { - return service.perFileLogsDatabaseClient - } -} - -func (service *EngineConnectServerService) getEnclaveCreationTime(ctx context.Context, enclaveUuid enclave.EnclaveUUID) (time.Time, error) { - enclaves, err := service.enclaveManager.GetEnclaves(ctx) - if err != nil { - return time.Time{}, err - } - - enclaveObj, found := enclaves[string(enclaveUuid)] - if !found { - return time.Time{}, stacktrace.NewError("Engine could not find enclave '%v'", enclaveUuid) - } - - timestamp := enclaveObj.GetCreationTime() - if timestamp == nil { - return time.Time{}, stacktrace.NewError("An error occurred getting the creation time for enclave '%v'. This is a bug in Kurtosis", enclaveUuid) - } - return timestamp.AsTime(), nil -} diff --git a/engine/server/webapp/asset-manifest.json b/engine/server/webapp/asset-manifest.json index 6071a1d071..eb99fe0bb6 100644 --- a/engine/server/webapp/asset-manifest.json +++ b/engine/server/webapp/asset-manifest.json @@ -1,10 +1,10 @@ { "files": { - "main.js": "./static/js/main.97e90b42.js", + "main.js": "./static/js/main.d987c6a5.js", "index.html": "./index.html", - "main.97e90b42.js.map": "./static/js/main.97e90b42.js.map" + "main.d987c6a5.js.map": "./static/js/main.d987c6a5.js.map" }, "entrypoints": [ - "static/js/main.97e90b42.js" + "static/js/main.d987c6a5.js" ] } \ No newline at end of file diff --git a/engine/server/webapp/index.html b/engine/server/webapp/index.html index 4d2794531a..16bd042f15 100644 --- a/engine/server/webapp/index.html +++ b/engine/server/webapp/index.html @@ -1 +1 @@ -Kurtosis Enclave Manager
\ No newline at end of file +Kurtosis Enclave Manager
\ No newline at end of file diff --git a/engine/server/webapp/static/js/main.97e90b42.js b/engine/server/webapp/static/js/main.97e90b42.js deleted file mode 100644 index c8d1166050..0000000000 --- a/engine/server/webapp/static/js/main.97e90b42.js +++ /dev/null @@ -1,3 +0,0 @@ -/*! For license information please see main.97e90b42.js.LICENSE.txt */ -!function(){var e={5304:function(e,t,n){"use strict";function r(e,t){for(var n=0;n=e.length?{done:!0}:{done:!1,value:e[r++]}},e:function(e){throw e},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var a,u=!0,s=!1;return{s:function(){n=n.call(e)},n:function(){var e=n.next();return u=e.done,e},e:function(e){s=!0,a=e},f:function(){try{u||null==n.return||n.return()}finally{if(s)throw a}}}}function i(e,t){(null==t||t>e.length)&&(t=e.length);for(var n=0,r=new Array(t);n0?40*e+55:0,u=t>0?40*t+55:0,l=n>0?40*n+55:0;r[i]=function(e){var t,n=[],r=o(e);try{for(r.s();!(t=r.n()).done;){var i=t.value;n.push(s(i))}}catch(a){r.e(a)}finally{r.f()}return"#"+n.join("")}([a,u,l])}(t,n,r,e)}))}))})),f(0,23).forEach((function(t){var n=t+232,r=s(10*t+8);e[n]="#"+r+r+r})),e}()};function s(e){for(var t=e.toString(16);t.length<2;)t="0"+t;return t}function l(e,t,n,r){var o;return"text"===t?o=function(e,t){if(t.escapeXML)return a.encodeXML(e);return e}(n,r):"display"===t?o=function(e,t,n){t=parseInt(t,10);var r,o={"-1":function(){return"
"},0:function(){return e.length&&c(e)},1:function(){return p(e,"b")},3:function(){return p(e,"i")},4:function(){return p(e,"u")},8:function(){return h(e,"display:none")},9:function(){return p(e,"strike")},22:function(){return h(e,"font-weight:normal;text-decoration:none;font-style:normal")},23:function(){return g(e,"i")},24:function(){return g(e,"u")},39:function(){return v(e,n.fg)},49:function(){return m(e,n.bg)},53:function(){return h(e,"text-decoration:overline")}};o[t]?r=o[t]():4"})).join("")}function f(e,t){for(var n=[],r=e;r<=t;r++)n.push(r);return n}function d(e){var t=null;return 0===(e=parseInt(e,10))?t="all":1===e?t="bold":2")}function h(e,t){return p(e,"span",t)}function v(e,t){return p(e,"span","color:"+t)}function m(e,t){return p(e,"span","background-color:"+t)}function g(e,t){var n;if(e.slice(-1)[0]===t&&(n=e.pop()),n)return""}var y=function(){function e(t){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),(t=t||{}).colors&&(t.colors=Object.assign({},u.colors,t.colors)),this.options=Object.assign({},u,t),this.stack=[],this.stickyStack=[]}var t,n,i;return t=e,(n=[{key:"toHtml",value:function(e){var t=this;e="string"===typeof e?[e]:e;var n=this.stack,r=this.options,i=[];return this.stickyStack.forEach((function(e){var t=l(n,e.token,e.data,r);t&&i.push(t)})),function(e,t,n){var r=!1;function i(){return""}function a(e){return t.newline?n("display",-1):n("text",e),""}var u=[{pattern:/^\x08+/,sub:i},{pattern:/^\x1b\[[012]?K/,sub:i},{pattern:/^\x1b\[\(B/,sub:i},{pattern:/^\x1b\[[34]8;2;\d+;\d+;\d+m/,sub:function(e){return n("rgb",e),""}},{pattern:/^\x1b\[38;5;(\d+)m/,sub:function(e,t){return n("xterm256Foreground",t),""}},{pattern:/^\x1b\[48;5;(\d+)m/,sub:function(e,t){return n("xterm256Background",t),""}},{pattern:/^\n/,sub:a},{pattern:/^\r+\n/,sub:a},{pattern:/^\r/,sub:a},{pattern:/^\x1b\[((?:\d{1,3};?)+|)m/,sub:function(e,t){r=!0,0===t.trim().length&&(t="0");var i,a=o(t=t.trimRight(";").split(";"));try{for(a.s();!(i=a.n()).done;){var u=i.value;n("display",u)}}catch(s){a.e(s)}finally{a.f()}return""}},{pattern:/^\x1b\[\d?J/,sub:i},{pattern:/^\x1b\[\d{0,3};\d{0,3}f/,sub:i},{pattern:/^\x1b\[?[\d;]{0,3}/,sub:i},{pattern:/^(([^\x1b\x08\r\n])+)/,sub:function(e){return n("text",e),""}}];function s(t,n){n>3&&r||(r=!1,e=e.replace(t.pattern,t.sub))}var l=[],c=e.length;e:for(;c>0;){for(var f=0,d=0,p=u.length;d0?this.children[this.children.length-1]:null},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"childNodes",{get:function(){return this.children},set:function(e){this.children=e},enumerable:!1,configurable:!0}),t}(a);t.NodeWithChildren=f;var d=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type=i.ElementType.CDATA,t}return r(t,e),Object.defineProperty(t.prototype,"nodeType",{get:function(){return 4},enumerable:!1,configurable:!0}),t}(f);t.CDATA=d;var p=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.type=i.ElementType.Root,t}return r(t,e),Object.defineProperty(t.prototype,"nodeType",{get:function(){return 9},enumerable:!1,configurable:!0}),t}(f);t.Document=p;var h=function(e){function t(t,n,r,o){void 0===r&&(r=[]),void 0===o&&(o="script"===t?i.ElementType.Script:"style"===t?i.ElementType.Style:i.ElementType.Tag);var a=e.call(this,r)||this;return a.name=t,a.attribs=n,a.type=o,a}return r(t,e),Object.defineProperty(t.prototype,"nodeType",{get:function(){return 1},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"tagName",{get:function(){return this.name},set:function(e){this.name=e},enumerable:!1,configurable:!0}),Object.defineProperty(t.prototype,"attributes",{get:function(){var e=this;return Object.keys(this.attribs).map((function(t){var n,r;return{name:t,value:e.attribs[t],namespace:null===(n=e["x-attribsNamespace"])||void 0===n?void 0:n[t],prefix:null===(r=e["x-attribsPrefix"])||void 0===r?void 0:r[t]}}))},enumerable:!1,configurable:!0}),t}(f);function v(e){return(0,i.isTag)(e)}function m(e){return e.type===i.ElementType.CDATA}function g(e){return e.type===i.ElementType.Text}function y(e){return e.type===i.ElementType.Comment}function b(e){return e.type===i.ElementType.Directive}function w(e){return e.type===i.ElementType.Root}function k(e,t){var n;if(void 0===t&&(t=!1),g(e))n=new s(e.data);else if(y(e))n=new l(e.data);else if(v(e)){var r=t?x(e.children):[],i=new h(e.name,o({},e.attribs),r);r.forEach((function(e){return e.parent=i})),null!=e.namespace&&(i.namespace=e.namespace),e["x-attribsNamespace"]&&(i["x-attribsNamespace"]=o({},e["x-attribsNamespace"])),e["x-attribsPrefix"]&&(i["x-attribsPrefix"]=o({},e["x-attribsPrefix"])),n=i}else if(m(e)){r=t?x(e.children):[];var a=new d(r);r.forEach((function(e){return e.parent=a})),n=a}else if(w(e)){r=t?x(e.children):[];var u=new p(r);r.forEach((function(e){return e.parent=u})),e["x-mode"]&&(u["x-mode"]=e["x-mode"]),n=u}else{if(!b(e))throw new Error("Not implemented yet: ".concat(e.type));var f=new c(e.name,e.data);null!=e["x-name"]&&(f["x-name"]=e["x-name"],f["x-publicId"]=e["x-publicId"],f["x-systemId"]=e["x-systemId"]),n=f}return n.startIndex=e.startIndex,n.endIndex=e.endIndex,null!=e.sourceCodeLocation&&(n.sourceCodeLocation=e.sourceCodeLocation),n}function x(e){for(var t=e.map((function(e){return k(e,!0)})),n=1;n65535&&(e-=65536,t+=String.fromCharCode(e>>>10&1023|55296),e=56320|1023&e),t+=String.fromCharCode(e)};t.default=function(e){return e>=55296&&e<=57343||e>1114111?"\ufffd":(e in o.default&&(e=o.default[e]),i(e))}},2056:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0}),t.escapeUTF8=t.escape=t.encodeNonAsciiHTML=t.encodeHTML=t.encodeXML=void 0;var o=c(r(n(2586)).default),i=f(o);t.encodeXML=m(o);var a,u,s=c(r(n(9323)).default),l=f(s);function c(e){return Object.keys(e).sort().reduce((function(t,n){return t[e[n]]="&"+n+";",t}),{})}function f(e){for(var t=[],n=[],r=0,o=Object.keys(e);r1?p(e):e.charCodeAt(0)).toString(16).toUpperCase()+";"}var v=new RegExp(i.source+"|"+d.source,"g");function m(e){return function(t){return t.replace(v,(function(t){return e[t]||h(t)}))}}t.escape=function(e){return e.replace(v,h)},t.escapeUTF8=function(e){return e.replace(i,h)}},4191:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.decodeXMLStrict=t.decodeHTML5Strict=t.decodeHTML4Strict=t.decodeHTML5=t.decodeHTML4=t.decodeHTMLStrict=t.decodeHTML=t.decodeXML=t.encodeHTML5=t.encodeHTML4=t.escapeUTF8=t.escape=t.encodeNonAsciiHTML=t.encodeHTML=t.encodeXML=t.encode=t.decodeStrict=t.decode=void 0;var r=n(1298),o=n(2056);t.decode=function(e,t){return(!t||t<=0?r.decodeXML:r.decodeHTML)(e)},t.decodeStrict=function(e,t){return(!t||t<=0?r.decodeXML:r.decodeHTMLStrict)(e)},t.encode=function(e,t){return(!t||t<=0?o.encodeXML:o.encodeHTML)(e)};var i=n(2056);Object.defineProperty(t,"encodeXML",{enumerable:!0,get:function(){return i.encodeXML}}),Object.defineProperty(t,"encodeHTML",{enumerable:!0,get:function(){return i.encodeHTML}}),Object.defineProperty(t,"encodeNonAsciiHTML",{enumerable:!0,get:function(){return i.encodeNonAsciiHTML}}),Object.defineProperty(t,"escape",{enumerable:!0,get:function(){return i.escape}}),Object.defineProperty(t,"escapeUTF8",{enumerable:!0,get:function(){return i.escapeUTF8}}),Object.defineProperty(t,"encodeHTML4",{enumerable:!0,get:function(){return i.encodeHTML}}),Object.defineProperty(t,"encodeHTML5",{enumerable:!0,get:function(){return i.encodeHTML}});var a=n(1298);Object.defineProperty(t,"decodeXML",{enumerable:!0,get:function(){return a.decodeXML}}),Object.defineProperty(t,"decodeHTML",{enumerable:!0,get:function(){return a.decodeHTML}}),Object.defineProperty(t,"decodeHTMLStrict",{enumerable:!0,get:function(){return a.decodeHTMLStrict}}),Object.defineProperty(t,"decodeHTML4",{enumerable:!0,get:function(){return a.decodeHTML}}),Object.defineProperty(t,"decodeHTML5",{enumerable:!0,get:function(){return a.decodeHTML}}),Object.defineProperty(t,"decodeHTML4Strict",{enumerable:!0,get:function(){return a.decodeHTMLStrict}}),Object.defineProperty(t,"decodeHTML5Strict",{enumerable:!0,get:function(){return a.decodeHTMLStrict}}),Object.defineProperty(t,"decodeXMLStrict",{enumerable:!0,get:function(){return a.decodeXML}})},1132:function(e){"use strict";var t=Object.prototype.hasOwnProperty,n=Object.prototype.toString,r=Object.defineProperty,o=Object.getOwnPropertyDescriptor,i=function(e){return"function"===typeof Array.isArray?Array.isArray(e):"[object Array]"===n.call(e)},a=function(e){if(!e||"[object Object]"!==n.call(e))return!1;var r,o=t.call(e,"constructor"),i=e.constructor&&e.constructor.prototype&&t.call(e.constructor.prototype,"isPrototypeOf");if(e.constructor&&!o&&!i)return!1;for(r in e);return"undefined"===typeof r||t.call(e,r)},u=function(e,t){r&&"__proto__"===t.name?r(e,t.name,{enumerable:!0,configurable:!0,value:t.newValue,writable:!0}):e[t.name]=t.newValue},s=function(e,n){if("__proto__"===n){if(!t.call(e,n))return;if(o)return o(e,n).value}return e[n]};e.exports=function e(){var t,n,r,o,l,c,f=arguments[0],d=1,p=arguments.length,h=!1;for("boolean"===typeof f&&(h=f,f=arguments[1]||{},d=2),(null==f||"object"!==typeof f&&"function"!==typeof f)&&(f={});d/i,u=//i,s=function(e,t){throw new Error("This browser does not support `document.implementation.createHTMLDocument`")},l=function(e,t){throw new Error("This browser does not support `DOMParser.prototype.parseFromString`")},c="object"===typeof window&&window.DOMParser;if("function"===typeof c){var f=new c;s=l=function(e,t){return t&&(e="<".concat(t,">").concat(e,"")),f.parseFromString(e,"text/html")}}if("object"===typeof document&&document.implementation){var d=document.implementation.createHTMLDocument();s=function(e,t){if(t){var n=d.documentElement.querySelector(t);return n&&(n.innerHTML=e),d}return d.documentElement.innerHTML=e,d}}var p,h="object"===typeof document&&document.createElement("template");h&&h.content&&(p=function(e){return h.innerHTML=e,h.content.childNodes}),t.default=function(e){var t,c,f=e.match(i),d=f&&f[1]?f[1].toLowerCase():"";switch(d){case n:var h=l(e);if(!a.test(e))null===(t=null===(m=h.querySelector(r))||void 0===m?void 0:m.parentNode)||void 0===t||t.removeChild(m);if(!u.test(e))null===(c=null===(m=h.querySelector(o))||void 0===m?void 0:m.parentNode)||void 0===c||c.removeChild(m);return h.querySelectorAll(n);case r:case o:var v=s(e).querySelectorAll(d);return u.test(e)&&a.test(e)?v[0].parentNode.childNodes:v;default:return p?p(e):(m=s(e,o).querySelector(o)).childNodes;var m}}},159:function(e,t,n){"use strict";var r=this&&this.__importDefault||function(e){return e&&e.__esModule?e:{default:e}};Object.defineProperty(t,"__esModule",{value:!0});var o=r(n(9409)),i=n(1716),a=/<(![a-zA-Z\s]+)>/;t.default=function(e){if("string"!==typeof e)throw new TypeError("First argument must be a string");if(!e)return[];var t=e.match(a),n=t?t[1]:void 0;return(0,i.formatDOM)((0,o.default)(e),null,n)}},1716:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.formatDOM=t.formatAttributes=void 0;var r=n(8079),o=n(9127);function i(e){for(var t={},n=0,r=e.length;n1&&(f=v(f,{key:f.key||x})),y.push(w(f,l,x));else if("text"!==l.type){switch(d=l.attribs,s(l)?a(d.style,d):d&&(d=o(d,l.name)),p=null,l.type){case"script":case"style":l.children[0]&&(d.dangerouslySetInnerHTML={__html:l.children[0].data});break;case"tag":"textarea"===l.name&&l.children[0]?d.defaultValue=l.children[0].data:l.children&&l.children.length&&(p=e(l.children,n));break;default:continue}S>1&&(d.key=x),y.push(w(m(l.name,d,p),l,x))}else{if((c=!l.data.trim().length)&&l.parent&&!u(l.parent))continue;if(k&&c)continue;y.push(w(l.data,l,x))}return 1===y.length?y[0]:y}},4141:function(e,t,n){var r=n(2791),o=n(5792).default,i=new Set(["annotation-xml","color-profile","font-face","font-face-src","font-face-uri","font-face-format","font-face-name","missing-glyph"]);var a={reactCompat:!0};var u=r.version.split(".")[0]>=16,s=new Set(["tr","tbody","thead","tfoot","colgroup","table","head","html","frameset"]);e.exports={PRESERVE_CUSTOM_ATTRIBUTES:u,ELEMENTS_WITH_NO_TEXT_CHILDREN:s,isCustomComponent:function(e,t){return-1===e.indexOf("-")?t&&"string"===typeof t.is:!i.has(e)},setStyleProp:function(e,t){if(null!==e&&void 0!==e)try{t.style=o(e,a)}catch(n){t.style={}}},canTextBeChildOfNode:function(e){return!s.has(e.name)},returnFirstArg:function(e){return e}}},1065:function(e){var t=/\/\*[^*]*\*+([^/*][^*]*\*+)*\//g,n=/\n/g,r=/^\s*/,o=/^(\*?[-#/*\\\w]+(\[[0-9a-z_-]+\])?)\s*/,i=/^:\s*/,a=/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^)]*?\)|[^};])+)/,u=/^[;\s]*/,s=/^\s+|\s+$/g,l="";function c(e){return e?e.replace(s,l):l}e.exports=function(e,s){if("string"!==typeof e)throw new TypeError("First argument must be a string");if(!e)return[];s=s||{};var f=1,d=1;function p(e){var t=e.match(n);t&&(f+=t.length);var r=e.lastIndexOf("\n");d=~r?e.length-r:d+e.length}function h(){var e={line:f,column:d};return function(t){return t.position=new v(e),b(),t}}function v(e){this.start=e,this.end={line:f,column:d},this.source=s.source}v.prototype.content=e;var m=[];function g(t){var n=new Error(s.source+":"+f+":"+d+": "+t);if(n.reason=t,n.filename=s.source,n.line=f,n.column=d,n.source=e,!s.silent)throw n;m.push(n)}function y(t){var n=t.exec(e);if(n){var r=n[0];return p(r),e=e.slice(r.length),n}}function b(){y(r)}function w(e){var t;for(e=e||[];t=k();)!1!==t&&e.push(t);return e}function k(){var t=h();if("/"==e.charAt(0)&&"*"==e.charAt(1)){for(var n=2;l!=e.charAt(n)&&("*"!=e.charAt(n)||"/"!=e.charAt(n+1));)++n;if(n+=2,l===e.charAt(n-1))return g("End of comment missing");var r=e.slice(2,n-2);return d+=2,p(r),e=e.slice(n),d+=2,t({type:"comment",comment:r})}}function x(){var e=h(),n=y(o);if(n){if(k(),!y(i))return g("property missing ':'");var r=y(a),s=e({type:"declaration",property:c(n[0].replace(t,l)),value:r?c(r[0].replace(t,l)):l});return y(u),s}}return b(),function(){var e,t=[];for(w(t);e=x();)!1!==e&&(t.push(e),w(t));return t}()}},6198:function(e,t,n){e=n.nmd(e);var r="__lodash_hash_undefined__",o=9007199254740991,i="[object Arguments]",a="[object AsyncFunction]",u="[object Function]",s="[object GeneratorFunction]",l="[object Null]",c="[object Object]",f="[object Proxy]",d="[object Undefined]",p=/^\[object .+?Constructor\]$/,h=/^(?:0|[1-9]\d*)$/,v={};v["[object Float32Array]"]=v["[object Float64Array]"]=v["[object Int8Array]"]=v["[object Int16Array]"]=v["[object Int32Array]"]=v["[object Uint8Array]"]=v["[object Uint8ClampedArray]"]=v["[object Uint16Array]"]=v["[object Uint32Array]"]=!0,v[i]=v["[object Array]"]=v["[object ArrayBuffer]"]=v["[object Boolean]"]=v["[object DataView]"]=v["[object Date]"]=v["[object Error]"]=v[u]=v["[object Map]"]=v["[object Number]"]=v[c]=v["[object RegExp]"]=v["[object Set]"]=v["[object String]"]=v["[object WeakMap]"]=!1;var m="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,g="object"==typeof self&&self&&self.Object===Object&&self,y=m||g||Function("return this")(),b=t&&!t.nodeType&&t,w=b&&e&&!e.nodeType&&e,k=w&&w.exports===b,x=k&&m.process,S=function(){try{var e=w&&w.require&&w.require("util").types;return e||x&&x.binding&&x.binding("util")}catch(t){}}(),T=S&&S.isTypedArray;var E,_,C=Array.prototype,I=Function.prototype,O=Object.prototype,P=y["__core-js_shared__"],N=I.toString,A=O.hasOwnProperty,D=function(){var e=/[^.]+$/.exec(P&&P.keys&&P.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}(),R=O.toString,j=N.call(Object),Z=RegExp("^"+N.call(A).replace(/[\\^$.*+?()[\]{}|]/g,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),F=k?y.Buffer:void 0,M=y.Symbol,L=y.Uint8Array,B=F?F.allocUnsafe:void 0,z=(E=Object.getPrototypeOf,_=Object,function(e){return E(_(e))}),V=Object.create,U=O.propertyIsEnumerable,q=C.splice,H=M?M.toStringTag:void 0,W=function(){try{var e=ye(Object,"defineProperty");return e({},"",{}),e}catch(t){}}(),J=F?F.isBuffer:void 0,G=Math.max,Y=Date.now,K=ye(y,"Map"),X=ye(Object,"create"),$=function(){function e(){}return function(t){if(!Pe(t))return{};if(V)return V(t);e.prototype=t;var n=new e;return e.prototype=void 0,n}}();function Q(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t-1},ee.prototype.set=function(e,t){var n=this.__data__,r=ae(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},te.prototype.clear=function(){this.size=0,this.__data__={hash:new Q,map:new(K||ee),string:new Q}},te.prototype.delete=function(e){var t=ge(this,e).delete(e);return this.size-=t?1:0,t},te.prototype.get=function(e){return ge(this,e).get(e)},te.prototype.has=function(e){return ge(this,e).has(e)},te.prototype.set=function(e,t){var n=ge(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},ne.prototype.clear=function(){this.__data__=new ee,this.size=0},ne.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},ne.prototype.get=function(e){return this.__data__.get(e)},ne.prototype.has=function(e){return this.__data__.has(e)},ne.prototype.set=function(e,t){var n=this.__data__;if(n instanceof ee){var r=n.__data__;if(!K||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new te(r)}return n.set(e,t),this.size=n.size,this};var se,le=function(e,t,n){for(var r=-1,o=Object(e),i=n(e),a=i.length;a--;){var u=i[se?a:++r];if(!1===t(o[u],u,o))break}return e};function ce(e){return null==e?void 0===e?d:l:H&&H in Object(e)?function(e){var t=A.call(e,H),n=e[H];try{e[H]=void 0;var r=!0}catch(i){}var o=R.call(e);r&&(t?e[H]=n:delete e[H]);return o}(e):function(e){return R.call(e)}(e)}function fe(e){return Ne(e)&&ce(e)==i}function de(e){return!(!Pe(e)||function(e){return!!D&&D in e}(e))&&(Ie(e)?Z:p).test(function(e){if(null!=e){try{return N.call(e)}catch(t){}try{return e+""}catch(t){}}return""}(e))}function pe(e){if(!Pe(e))return function(e){var t=[];if(null!=e)for(var n in Object(e))t.push(n);return t}(e);var t=we(e),n=[];for(var r in e)("constructor"!=r||!t&&A.call(e,r))&&n.push(r);return n}function he(e,t,n,r,o){e!==t&&le(t,(function(i,a){if(o||(o=new ne),Pe(i))!function(e,t,n,r,o,i,a){var u=ke(e,n),s=ke(t,n),l=a.get(s);if(l)return void oe(e,n,l);var f=i?i(u,s,n+"",e,t,a):void 0,d=void 0===f;if(d){var p=Ee(s),h=!p&&Ce(s),v=!p&&!h&&Ae(s);f=s,p||h||v?Ee(u)?f=u:Ne(m=u)&&_e(m)?f=function(e,t){var n=-1,r=e.length;t||(t=Array(r));for(;++n-1&&e%1==0&&e0){if(++t>=800)return arguments[0]}else t=0;return e.apply(void 0,arguments)}}(me);function Se(e,t){return e===t||e!==e&&t!==t}var Te=fe(function(){return arguments}())?fe:function(e){return Ne(e)&&A.call(e,"callee")&&!U.call(e,"callee")},Ee=Array.isArray;function _e(e){return null!=e&&Oe(e.length)&&!Ie(e)}var Ce=J||function(){return!1};function Ie(e){if(!Pe(e))return!1;var t=ce(e);return t==u||t==s||t==a||t==f}function Oe(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=o}function Pe(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function Ne(e){return null!=e&&"object"==typeof e}var Ae=T?function(e){return function(t){return e(t)}}(T):function(e){return Ne(e)&&Oe(e.length)&&!!v[ce(e)]};function De(e){return _e(e)?re(e,!0):pe(e)}var Re,je=(Re=function(e,t,n,r){he(e,t,n,r)},ve((function(e,t){var n=-1,r=t.length,o=r>1?t[r-1]:void 0,i=r>2?t[2]:void 0;for(o=Re.length>3&&"function"==typeof o?(r--,o):void 0,i&&function(e,t,n){if(!Pe(n))return!1;var r=typeof t;return!!("number"==r?_e(n)&&be(t,n.length):"string"==r&&t in n)&&Se(n[t],e)}(t[0],t[1],i)&&(o=r<3?void 0:o,r=1),e=Object(e);++n"']/g,K=RegExp(G.source),X=RegExp(Y.source),$=/<%-([\s\S]+?)%>/g,Q=/<%([\s\S]+?)%>/g,ee=/<%=([\s\S]+?)%>/g,te=/\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,ne=/^\w*$/,re=/[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g,oe=/[\\^$.*+?()[\]{}|]/g,ie=RegExp(oe.source),ae=/^\s+/,ue=/\s/,se=/\{(?:\n\/\* \[wrapped with .+\] \*\/)?\n?/,le=/\{\n\/\* \[wrapped with (.+)\] \*/,ce=/,? & /,fe=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g,de=/[()=,{}\[\]\/\s]/,pe=/\\(\\)?/g,he=/\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g,ve=/\w*$/,me=/^[-+]0x[0-9a-f]+$/i,ge=/^0b[01]+$/i,ye=/^\[object .+?Constructor\]$/,be=/^0o[0-7]+$/i,we=/^(?:0|[1-9]\d*)$/,ke=/[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g,xe=/($^)/,Se=/['\n\r\u2028\u2029\\]/g,Te="\\ud800-\\udfff",Ee="\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff",_e="\\u2700-\\u27bf",Ce="a-z\\xdf-\\xf6\\xf8-\\xff",Ie="A-Z\\xc0-\\xd6\\xd8-\\xde",Oe="\\ufe0e\\ufe0f",Pe="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",Ne="['\u2019]",Ae="["+Te+"]",De="["+Pe+"]",Re="["+Ee+"]",je="\\d+",Ze="["+_e+"]",Fe="["+Ce+"]",Me="[^"+Te+Pe+je+_e+Ce+Ie+"]",Le="\\ud83c[\\udffb-\\udfff]",Be="[^"+Te+"]",ze="(?:\\ud83c[\\udde6-\\uddff]){2}",Ve="[\\ud800-\\udbff][\\udc00-\\udfff]",Ue="["+Ie+"]",qe="\\u200d",He="(?:"+Fe+"|"+Me+")",We="(?:"+Ue+"|"+Me+")",Je="(?:['\u2019](?:d|ll|m|re|s|t|ve))?",Ge="(?:['\u2019](?:D|LL|M|RE|S|T|VE))?",Ye="(?:"+Re+"|"+Le+")"+"?",Ke="["+Oe+"]?",Xe=Ke+Ye+("(?:"+qe+"(?:"+[Be,ze,Ve].join("|")+")"+Ke+Ye+")*"),$e="(?:"+[Ze,ze,Ve].join("|")+")"+Xe,Qe="(?:"+[Be+Re+"?",Re,ze,Ve,Ae].join("|")+")",et=RegExp(Ne,"g"),tt=RegExp(Re,"g"),nt=RegExp(Le+"(?="+Le+")|"+Qe+Xe,"g"),rt=RegExp([Ue+"?"+Fe+"+"+Je+"(?="+[De,Ue,"$"].join("|")+")",We+"+"+Ge+"(?="+[De,Ue+He,"$"].join("|")+")",Ue+"?"+He+"+"+Je,Ue+"+"+Ge,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",je,$e].join("|"),"g"),ot=RegExp("["+qe+Te+Ee+Oe+"]"),it=/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/,at=["Array","Buffer","DataView","Date","Error","Float32Array","Float64Array","Function","Int8Array","Int16Array","Int32Array","Map","Math","Object","Promise","RegExp","Set","String","Symbol","TypeError","Uint8Array","Uint8ClampedArray","Uint16Array","Uint32Array","WeakMap","_","clearTimeout","isFinite","parseInt","setTimeout"],ut=-1,st={};st[Z]=st[F]=st[M]=st[L]=st[B]=st[z]=st[V]=st[U]=st[q]=!0,st[y]=st[b]=st[R]=st[w]=st[j]=st[k]=st[x]=st[S]=st[E]=st[_]=st[C]=st[O]=st[P]=st[N]=st[D]=!1;var lt={};lt[y]=lt[b]=lt[R]=lt[j]=lt[w]=lt[k]=lt[Z]=lt[F]=lt[M]=lt[L]=lt[B]=lt[E]=lt[_]=lt[C]=lt[O]=lt[P]=lt[N]=lt[A]=lt[z]=lt[V]=lt[U]=lt[q]=!0,lt[x]=lt[S]=lt[D]=!1;var ct={"\\":"\\","'":"'","\n":"n","\r":"r","\u2028":"u2028","\u2029":"u2029"},ft=parseFloat,dt=parseInt,pt="object"==typeof n.g&&n.g&&n.g.Object===Object&&n.g,ht="object"==typeof self&&self&&self.Object===Object&&self,vt=pt||ht||Function("return this")(),mt=t&&!t.nodeType&&t,gt=mt&&e&&!e.nodeType&&e,yt=gt&>.exports===mt,bt=yt&&pt.process,wt=function(){try{var e=gt&>.require&>.require("util").types;return e||bt&&bt.binding&&bt.binding("util")}catch(t){}}(),kt=wt&&wt.isArrayBuffer,xt=wt&&wt.isDate,St=wt&&wt.isMap,Tt=wt&&wt.isRegExp,Et=wt&&wt.isSet,_t=wt&&wt.isTypedArray;function Ct(e,t,n){switch(n.length){case 0:return e.call(t);case 1:return e.call(t,n[0]);case 2:return e.call(t,n[0],n[1]);case 3:return e.call(t,n[0],n[1],n[2])}return e.apply(t,n)}function It(e,t,n,r){for(var o=-1,i=null==e?0:e.length;++o-1}function Rt(e,t,n){for(var r=-1,o=null==e?0:e.length;++r-1;);return n}function rn(e,t){for(var n=e.length;n--&&Ut(t,e[n],0)>-1;);return n}var on=Gt({"\xc0":"A","\xc1":"A","\xc2":"A","\xc3":"A","\xc4":"A","\xc5":"A","\xe0":"a","\xe1":"a","\xe2":"a","\xe3":"a","\xe4":"a","\xe5":"a","\xc7":"C","\xe7":"c","\xd0":"D","\xf0":"d","\xc8":"E","\xc9":"E","\xca":"E","\xcb":"E","\xe8":"e","\xe9":"e","\xea":"e","\xeb":"e","\xcc":"I","\xcd":"I","\xce":"I","\xcf":"I","\xec":"i","\xed":"i","\xee":"i","\xef":"i","\xd1":"N","\xf1":"n","\xd2":"O","\xd3":"O","\xd4":"O","\xd5":"O","\xd6":"O","\xd8":"O","\xf2":"o","\xf3":"o","\xf4":"o","\xf5":"o","\xf6":"o","\xf8":"o","\xd9":"U","\xda":"U","\xdb":"U","\xdc":"U","\xf9":"u","\xfa":"u","\xfb":"u","\xfc":"u","\xdd":"Y","\xfd":"y","\xff":"y","\xc6":"Ae","\xe6":"ae","\xde":"Th","\xfe":"th","\xdf":"ss","\u0100":"A","\u0102":"A","\u0104":"A","\u0101":"a","\u0103":"a","\u0105":"a","\u0106":"C","\u0108":"C","\u010a":"C","\u010c":"C","\u0107":"c","\u0109":"c","\u010b":"c","\u010d":"c","\u010e":"D","\u0110":"D","\u010f":"d","\u0111":"d","\u0112":"E","\u0114":"E","\u0116":"E","\u0118":"E","\u011a":"E","\u0113":"e","\u0115":"e","\u0117":"e","\u0119":"e","\u011b":"e","\u011c":"G","\u011e":"G","\u0120":"G","\u0122":"G","\u011d":"g","\u011f":"g","\u0121":"g","\u0123":"g","\u0124":"H","\u0126":"H","\u0125":"h","\u0127":"h","\u0128":"I","\u012a":"I","\u012c":"I","\u012e":"I","\u0130":"I","\u0129":"i","\u012b":"i","\u012d":"i","\u012f":"i","\u0131":"i","\u0134":"J","\u0135":"j","\u0136":"K","\u0137":"k","\u0138":"k","\u0139":"L","\u013b":"L","\u013d":"L","\u013f":"L","\u0141":"L","\u013a":"l","\u013c":"l","\u013e":"l","\u0140":"l","\u0142":"l","\u0143":"N","\u0145":"N","\u0147":"N","\u014a":"N","\u0144":"n","\u0146":"n","\u0148":"n","\u014b":"n","\u014c":"O","\u014e":"O","\u0150":"O","\u014d":"o","\u014f":"o","\u0151":"o","\u0154":"R","\u0156":"R","\u0158":"R","\u0155":"r","\u0157":"r","\u0159":"r","\u015a":"S","\u015c":"S","\u015e":"S","\u0160":"S","\u015b":"s","\u015d":"s","\u015f":"s","\u0161":"s","\u0162":"T","\u0164":"T","\u0166":"T","\u0163":"t","\u0165":"t","\u0167":"t","\u0168":"U","\u016a":"U","\u016c":"U","\u016e":"U","\u0170":"U","\u0172":"U","\u0169":"u","\u016b":"u","\u016d":"u","\u016f":"u","\u0171":"u","\u0173":"u","\u0174":"W","\u0175":"w","\u0176":"Y","\u0177":"y","\u0178":"Y","\u0179":"Z","\u017b":"Z","\u017d":"Z","\u017a":"z","\u017c":"z","\u017e":"z","\u0132":"IJ","\u0133":"ij","\u0152":"Oe","\u0153":"oe","\u0149":"'n","\u017f":"s"}),an=Gt({"&":"&","<":"<",">":">",'"':""","'":"'"});function un(e){return"\\"+ct[e]}function sn(e){return ot.test(e)}function ln(e){var t=-1,n=Array(e.size);return e.forEach((function(e,r){n[++t]=[r,e]})),n}function cn(e,t){return function(n){return e(t(n))}}function fn(e,t){for(var n=-1,r=e.length,o=0,i=[];++n",""":'"',"'":"'"});var yn=function e(t){var n=(t=null==t?vt:yn.defaults(vt.Object(),t,yn.pick(vt,at))).Array,r=t.Date,ue=t.Error,Te=t.Function,Ee=t.Math,_e=t.Object,Ce=t.RegExp,Ie=t.String,Oe=t.TypeError,Pe=n.prototype,Ne=Te.prototype,Ae=_e.prototype,De=t["__core-js_shared__"],Re=Ne.toString,je=Ae.hasOwnProperty,Ze=0,Fe=function(){var e=/[^.]+$/.exec(De&&De.keys&&De.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}(),Me=Ae.toString,Le=Re.call(_e),Be=vt._,ze=Ce("^"+Re.call(je).replace(oe,"\\$&").replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g,"$1.*?")+"$"),Ve=yt?t.Buffer:o,Ue=t.Symbol,qe=t.Uint8Array,He=Ve?Ve.allocUnsafe:o,We=cn(_e.getPrototypeOf,_e),Je=_e.create,Ge=Ae.propertyIsEnumerable,Ye=Pe.splice,Ke=Ue?Ue.isConcatSpreadable:o,Xe=Ue?Ue.iterator:o,$e=Ue?Ue.toStringTag:o,Qe=function(){try{var e=di(_e,"defineProperty");return e({},"",{}),e}catch(t){}}(),nt=t.clearTimeout!==vt.clearTimeout&&t.clearTimeout,ot=r&&r.now!==vt.Date.now&&r.now,ct=t.setTimeout!==vt.setTimeout&&t.setTimeout,pt=Ee.ceil,ht=Ee.floor,mt=_e.getOwnPropertySymbols,gt=Ve?Ve.isBuffer:o,bt=t.isFinite,wt=Pe.join,Bt=cn(_e.keys,_e),Gt=Ee.max,bn=Ee.min,wn=r.now,kn=t.parseInt,xn=Ee.random,Sn=Pe.reverse,Tn=di(t,"DataView"),En=di(t,"Map"),_n=di(t,"Promise"),Cn=di(t,"Set"),In=di(t,"WeakMap"),On=di(_e,"create"),Pn=In&&new In,Nn={},An=Mi(Tn),Dn=Mi(En),Rn=Mi(_n),jn=Mi(Cn),Zn=Mi(In),Fn=Ue?Ue.prototype:o,Mn=Fn?Fn.valueOf:o,Ln=Fn?Fn.toString:o;function Bn(e){if(tu(e)&&!qa(e)&&!(e instanceof qn)){if(e instanceof Un)return e;if(je.call(e,"__wrapped__"))return Li(e)}return new Un(e)}var zn=function(){function e(){}return function(t){if(!eu(t))return{};if(Je)return Je(t);e.prototype=t;var n=new e;return e.prototype=o,n}}();function Vn(){}function Un(e,t){this.__wrapped__=e,this.__actions__=[],this.__chain__=!!t,this.__index__=0,this.__values__=o}function qn(e){this.__wrapped__=e,this.__actions__=[],this.__dir__=1,this.__filtered__=!1,this.__iteratees__=[],this.__takeCount__=m,this.__views__=[]}function Hn(e){var t=-1,n=null==e?0:e.length;for(this.clear();++t=t?e:t)),e}function sr(e,t,n,r,i,a){var u,s=1&t,l=2&t,c=4&t;if(n&&(u=i?n(e,r,i,a):n(e)),u!==o)return u;if(!eu(e))return e;var f=qa(e);if(f){if(u=function(e){var t=e.length,n=new e.constructor(t);t&&"string"==typeof e[0]&&je.call(e,"index")&&(n.index=e.index,n.input=e.input);return n}(e),!s)return Po(e,u)}else{var d=vi(e),p=d==S||d==T;if(Ga(e))return To(e,s);if(d==C||d==y||p&&!i){if(u=l||p?{}:gi(e),!s)return l?function(e,t){return No(e,hi(e),t)}(e,function(e,t){return e&&No(t,Au(t),e)}(u,e)):function(e,t){return No(e,pi(e),t)}(e,or(u,e))}else{if(!lt[d])return i?e:{};u=function(e,t,n){var r=e.constructor;switch(t){case R:return Eo(e);case w:case k:return new r(+e);case j:return function(e,t){var n=t?Eo(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.byteLength)}(e,n);case Z:case F:case M:case L:case B:case z:case V:case U:case q:return _o(e,n);case E:return new r;case _:case N:return new r(e);case O:return function(e){var t=new e.constructor(e.source,ve.exec(e));return t.lastIndex=e.lastIndex,t}(e);case P:return new r;case A:return o=e,Mn?_e(Mn.call(o)):{}}var o}(e,d,s)}}a||(a=new Yn);var h=a.get(e);if(h)return h;a.set(e,u),au(e)?e.forEach((function(r){u.add(sr(r,t,n,r,e,a))})):nu(e)&&e.forEach((function(r,o){u.set(o,sr(r,t,n,o,e,a))}));var v=f?o:(c?l?ii:oi:l?Au:Nu)(e);return Ot(v||e,(function(r,o){v&&(r=e[o=r]),tr(u,o,sr(r,t,n,o,e,a))})),u}function lr(e,t,n){var r=n.length;if(null==e)return!r;for(e=_e(e);r--;){var i=n[r],a=t[i],u=e[i];if(u===o&&!(i in e)||!a(u))return!1}return!0}function cr(e,t,n){if("function"!=typeof e)throw new Oe(i);return Ni((function(){e.apply(o,n)}),t)}function fr(e,t,n,r){var o=-1,i=Dt,a=!0,u=e.length,s=[],l=t.length;if(!u)return s;n&&(t=jt(t,Qt(n))),r?(i=Rt,a=!1):t.length>=200&&(i=tn,a=!1,t=new Gn(t));e:for(;++o-1},Wn.prototype.set=function(e,t){var n=this.__data__,r=nr(n,e);return r<0?(++this.size,n.push([e,t])):n[r][1]=t,this},Jn.prototype.clear=function(){this.size=0,this.__data__={hash:new Hn,map:new(En||Wn),string:new Hn}},Jn.prototype.delete=function(e){var t=ci(this,e).delete(e);return this.size-=t?1:0,t},Jn.prototype.get=function(e){return ci(this,e).get(e)},Jn.prototype.has=function(e){return ci(this,e).has(e)},Jn.prototype.set=function(e,t){var n=ci(this,e),r=n.size;return n.set(e,t),this.size+=n.size==r?0:1,this},Gn.prototype.add=Gn.prototype.push=function(e){return this.__data__.set(e,a),this},Gn.prototype.has=function(e){return this.__data__.has(e)},Yn.prototype.clear=function(){this.__data__=new Wn,this.size=0},Yn.prototype.delete=function(e){var t=this.__data__,n=t.delete(e);return this.size=t.size,n},Yn.prototype.get=function(e){return this.__data__.get(e)},Yn.prototype.has=function(e){return this.__data__.has(e)},Yn.prototype.set=function(e,t){var n=this.__data__;if(n instanceof Wn){var r=n.__data__;if(!En||r.length<199)return r.push([e,t]),this.size=++n.size,this;n=this.__data__=new Jn(r)}return n.set(e,t),this.size=n.size,this};var dr=Ro(wr),pr=Ro(kr,!0);function hr(e,t){var n=!0;return dr(e,(function(e,r,o){return n=!!t(e,r,o)})),n}function vr(e,t,n){for(var r=-1,i=e.length;++r0&&n(u)?t>1?gr(u,t-1,n,r,o):Zt(o,u):r||(o[o.length]=u)}return o}var yr=jo(),br=jo(!0);function wr(e,t){return e&&yr(e,t,Nu)}function kr(e,t){return e&&br(e,t,Nu)}function xr(e,t){return At(t,(function(t){return Xa(e[t])}))}function Sr(e,t){for(var n=0,r=(t=wo(t,e)).length;null!=e&&nt}function Cr(e,t){return null!=e&&je.call(e,t)}function Ir(e,t){return null!=e&&t in _e(e)}function Or(e,t,r){for(var i=r?Rt:Dt,a=e[0].length,u=e.length,s=u,l=n(u),c=1/0,f=[];s--;){var d=e[s];s&&t&&(d=jt(d,Qt(t))),c=bn(d.length,c),l[s]=!r&&(t||a>=120&&d.length>=120)?new Gn(s&&d):o}d=e[0];var p=-1,h=l[0];e:for(;++p=u?s:s*("desc"==n[r]?-1:1)}return e.index-t.index}(e,t,n)}))}function Hr(e,t,n){for(var r=-1,o=t.length,i={};++r-1;)u!==e&&Ye.call(u,s,1),Ye.call(e,s,1);return e}function Jr(e,t){for(var n=e?t.length:0,r=n-1;n--;){var o=t[n];if(n==r||o!==i){var i=o;bi(o)?Ye.call(e,o,1):fo(e,o)}}return e}function Gr(e,t){return e+ht(xn()*(t-e+1))}function Yr(e,t){var n="";if(!e||t<1||t>h)return n;do{t%2&&(n+=e),(t=ht(t/2))&&(e+=e)}while(t);return n}function Kr(e,t){return Ai(Ci(e,t,rs),e+"")}function Xr(e){return Xn(Bu(e))}function $r(e,t){var n=Bu(e);return ji(n,ur(t,0,n.length))}function Qr(e,t,n,r){if(!eu(e))return e;for(var i=-1,a=(t=wo(t,e)).length,u=a-1,s=e;null!=s&&++ii?0:i+t),(r=r>i?i:r)<0&&(r+=i),i=t>r?0:r-t>>>0,t>>>=0;for(var a=n(i);++o>>1,a=e[i];null!==a&&!su(a)&&(n?a<=t:a=200){var l=t?null:Ko(e);if(l)return dn(l);a=!1,o=tn,s=new Gn}else s=t?[]:u;e:for(;++r=r?e:ro(e,t,n)}var So=nt||function(e){return vt.clearTimeout(e)};function To(e,t){if(t)return e.slice();var n=e.length,r=He?He(n):new e.constructor(n);return e.copy(r),r}function Eo(e){var t=new e.constructor(e.byteLength);return new qe(t).set(new qe(e)),t}function _o(e,t){var n=t?Eo(e.buffer):e.buffer;return new e.constructor(n,e.byteOffset,e.length)}function Co(e,t){if(e!==t){var n=e!==o,r=null===e,i=e===e,a=su(e),u=t!==o,s=null===t,l=t===t,c=su(t);if(!s&&!c&&!a&&e>t||a&&u&&l&&!s&&!c||r&&u&&l||!n&&l||!i)return 1;if(!r&&!a&&!c&&e1?n[i-1]:o,u=i>2?n[2]:o;for(a=e.length>3&&"function"==typeof a?(i--,a):o,u&&wi(n[0],n[1],u)&&(a=i<3?o:a,i=1),t=_e(t);++r-1?i[a?t[u]:u]:o}}function Bo(e){return ri((function(t){var n=t.length,r=n,a=Un.prototype.thru;for(e&&t.reverse();r--;){var u=t[r];if("function"!=typeof u)throw new Oe(i);if(a&&!s&&"wrapper"==ui(u))var s=new Un([],!0)}for(r=s?r:n;++r1&&w.reverse(),p&&cs))return!1;var c=a.get(e),f=a.get(t);if(c&&f)return c==t&&f==e;var d=-1,p=!0,h=2&n?new Gn:o;for(a.set(e,t),a.set(t,e);++d-1&&e%1==0&&e1?"& ":"")+t[r],t=t.join(n>2?", ":" "),e.replace(se,"{\n/* [wrapped with "+t+"] */\n")}(r,function(e,t){return Ot(g,(function(n){var r="_."+n[0];t&n[1]&&!Dt(e,r)&&e.push(r)})),e.sort()}(function(e){var t=e.match(le);return t?t[1].split(ce):[]}(r),n)))}function Ri(e){var t=0,n=0;return function(){var r=wn(),i=16-(r-n);if(n=r,i>0){if(++t>=800)return arguments[0]}else t=0;return e.apply(o,arguments)}}function ji(e,t){var n=-1,r=e.length,i=r-1;for(t=t===o?r:t;++n1?e[t-1]:o;return n="function"==typeof n?(e.pop(),n):o,ia(e,n)}));function da(e){var t=Bn(e);return t.__chain__=!0,t}function pa(e,t){return t(e)}var ha=ri((function(e){var t=e.length,n=t?e[0]:0,r=this.__wrapped__,i=function(t){return ar(t,e)};return!(t>1||this.__actions__.length)&&r instanceof qn&&bi(n)?((r=r.slice(n,+n+(t?1:0))).__actions__.push({func:pa,args:[i],thisArg:o}),new Un(r,this.__chain__).thru((function(e){return t&&!e.length&&e.push(o),e}))):this.thru(i)}));var va=Ao((function(e,t,n){je.call(e,n)?++e[n]:ir(e,n,1)}));var ma=Lo(Ui),ga=Lo(qi);function ya(e,t){return(qa(e)?Ot:dr)(e,li(t,3))}function ba(e,t){return(qa(e)?Pt:pr)(e,li(t,3))}var wa=Ao((function(e,t,n){je.call(e,n)?e[n].push(t):ir(e,n,[t])}));var ka=Kr((function(e,t,r){var o=-1,i="function"==typeof t,a=Wa(e)?n(e.length):[];return dr(e,(function(e){a[++o]=i?Ct(t,e,r):Pr(e,t,r)})),a})),xa=Ao((function(e,t,n){ir(e,n,t)}));function Sa(e,t){return(qa(e)?jt:Lr)(e,li(t,3))}var Ta=Ao((function(e,t,n){e[n?0:1].push(t)}),(function(){return[[],[]]}));var Ea=Kr((function(e,t){if(null==e)return[];var n=t.length;return n>1&&wi(e,t[0],t[1])?t=[]:n>2&&wi(t[0],t[1],t[2])&&(t=[t[0]]),qr(e,gr(t,1),[])})),_a=ot||function(){return vt.Date.now()};function Ca(e,t,n){return t=n?o:t,t=e&&null==t?e.length:t,$o(e,f,o,o,o,o,t)}function Ia(e,t){var n;if("function"!=typeof t)throw new Oe(i);return e=hu(e),function(){return--e>0&&(n=t.apply(this,arguments)),e<=1&&(t=o),n}}var Oa=Kr((function(e,t,n){var r=1;if(n.length){var o=fn(n,si(Oa));r|=l}return $o(e,r,t,n,o)})),Pa=Kr((function(e,t,n){var r=3;if(n.length){var o=fn(n,si(Pa));r|=l}return $o(t,r,e,n,o)}));function Na(e,t,n){var r,a,u,s,l,c,f=0,d=!1,p=!1,h=!0;if("function"!=typeof e)throw new Oe(i);function v(t){var n=r,i=a;return r=a=o,f=t,s=e.apply(i,n)}function m(e){var n=e-c;return c===o||n>=t||n<0||p&&e-f>=u}function g(){var e=_a();if(m(e))return y(e);l=Ni(g,function(e){var n=t-(e-c);return p?bn(n,u-(e-f)):n}(e))}function y(e){return l=o,h&&r?v(e):(r=a=o,s)}function b(){var e=_a(),n=m(e);if(r=arguments,a=this,c=e,n){if(l===o)return function(e){return f=e,l=Ni(g,t),d?v(e):s}(c);if(p)return So(l),l=Ni(g,t),v(c)}return l===o&&(l=Ni(g,t)),s}return t=mu(t)||0,eu(n)&&(d=!!n.leading,u=(p="maxWait"in n)?Gt(mu(n.maxWait)||0,t):u,h="trailing"in n?!!n.trailing:h),b.cancel=function(){l!==o&&So(l),f=0,r=c=a=l=o},b.flush=function(){return l===o?s:y(_a())},b}var Aa=Kr((function(e,t){return cr(e,1,t)})),Da=Kr((function(e,t,n){return cr(e,mu(t)||0,n)}));function Ra(e,t){if("function"!=typeof e||null!=t&&"function"!=typeof t)throw new Oe(i);var n=function n(){var r=arguments,o=t?t.apply(this,r):r[0],i=n.cache;if(i.has(o))return i.get(o);var a=e.apply(this,r);return n.cache=i.set(o,a)||i,a};return n.cache=new(Ra.Cache||Jn),n}function ja(e){if("function"!=typeof e)throw new Oe(i);return function(){var t=arguments;switch(t.length){case 0:return!e.call(this);case 1:return!e.call(this,t[0]);case 2:return!e.call(this,t[0],t[1]);case 3:return!e.call(this,t[0],t[1],t[2])}return!e.apply(this,t)}}Ra.Cache=Jn;var Za=ko((function(e,t){var n=(t=1==t.length&&qa(t[0])?jt(t[0],Qt(li())):jt(gr(t,1),Qt(li()))).length;return Kr((function(r){for(var o=-1,i=bn(r.length,n);++o=t})),Ua=Nr(function(){return arguments}())?Nr:function(e){return tu(e)&&je.call(e,"callee")&&!Ge.call(e,"callee")},qa=n.isArray,Ha=kt?Qt(kt):function(e){return tu(e)&&Er(e)==R};function Wa(e){return null!=e&&Qa(e.length)&&!Xa(e)}function Ja(e){return tu(e)&&Wa(e)}var Ga=gt||ms,Ya=xt?Qt(xt):function(e){return tu(e)&&Er(e)==k};function Ka(e){if(!tu(e))return!1;var t=Er(e);return t==x||"[object DOMException]"==t||"string"==typeof e.message&&"string"==typeof e.name&&!ou(e)}function Xa(e){if(!eu(e))return!1;var t=Er(e);return t==S||t==T||"[object AsyncFunction]"==t||"[object Proxy]"==t}function $a(e){return"number"==typeof e&&e==hu(e)}function Qa(e){return"number"==typeof e&&e>-1&&e%1==0&&e<=h}function eu(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}function tu(e){return null!=e&&"object"==typeof e}var nu=St?Qt(St):function(e){return tu(e)&&vi(e)==E};function ru(e){return"number"==typeof e||tu(e)&&Er(e)==_}function ou(e){if(!tu(e)||Er(e)!=C)return!1;var t=We(e);if(null===t)return!0;var n=je.call(t,"constructor")&&t.constructor;return"function"==typeof n&&n instanceof n&&Re.call(n)==Le}var iu=Tt?Qt(Tt):function(e){return tu(e)&&Er(e)==O};var au=Et?Qt(Et):function(e){return tu(e)&&vi(e)==P};function uu(e){return"string"==typeof e||!qa(e)&&tu(e)&&Er(e)==N}function su(e){return"symbol"==typeof e||tu(e)&&Er(e)==A}var lu=_t?Qt(_t):function(e){return tu(e)&&Qa(e.length)&&!!st[Er(e)]};var cu=Jo(Mr),fu=Jo((function(e,t){return e<=t}));function du(e){if(!e)return[];if(Wa(e))return uu(e)?vn(e):Po(e);if(Xe&&e[Xe])return function(e){for(var t,n=[];!(t=e.next()).done;)n.push(t.value);return n}(e[Xe]());var t=vi(e);return(t==E?ln:t==P?dn:Bu)(e)}function pu(e){return e?(e=mu(e))===p||e===-1/0?17976931348623157e292*(e<0?-1:1):e===e?e:0:0===e?e:0}function hu(e){var t=pu(e),n=t%1;return t===t?n?t-n:t:0}function vu(e){return e?ur(hu(e),0,m):0}function mu(e){if("number"==typeof e)return e;if(su(e))return v;if(eu(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=eu(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=$t(e);var n=ge.test(e);return n||be.test(e)?dt(e.slice(2),n?2:8):me.test(e)?v:+e}function gu(e){return No(e,Au(e))}function yu(e){return null==e?"":lo(e)}var bu=Do((function(e,t){if(Ti(t)||Wa(t))No(t,Nu(t),e);else for(var n in t)je.call(t,n)&&tr(e,n,t[n])})),wu=Do((function(e,t){No(t,Au(t),e)})),ku=Do((function(e,t,n,r){No(t,Au(t),e,r)})),xu=Do((function(e,t,n,r){No(t,Nu(t),e,r)})),Su=ri(ar);var Tu=Kr((function(e,t){e=_e(e);var n=-1,r=t.length,i=r>2?t[2]:o;for(i&&wi(t[0],t[1],i)&&(r=1);++n1),t})),No(e,ii(e),n),r&&(n=sr(n,7,ti));for(var o=t.length;o--;)fo(n,t[o]);return n}));var Zu=ri((function(e,t){return null==e?{}:function(e,t){return Hr(e,t,(function(t,n){return Cu(e,n)}))}(e,t)}));function Fu(e,t){if(null==e)return{};var n=jt(ii(e),(function(e){return[e]}));return t=li(t),Hr(e,n,(function(e,n){return t(e,n[0])}))}var Mu=Xo(Nu),Lu=Xo(Au);function Bu(e){return null==e?[]:en(e,Nu(e))}var zu=Fo((function(e,t,n){return t=t.toLowerCase(),e+(n?Vu(t):t)}));function Vu(e){return Ku(yu(e).toLowerCase())}function Uu(e){return(e=yu(e))&&e.replace(ke,on).replace(tt,"")}var qu=Fo((function(e,t,n){return e+(n?"-":"")+t.toLowerCase()})),Hu=Fo((function(e,t,n){return e+(n?" ":"")+t.toLowerCase()})),Wu=Zo("toLowerCase");var Ju=Fo((function(e,t,n){return e+(n?"_":"")+t.toLowerCase()}));var Gu=Fo((function(e,t,n){return e+(n?" ":"")+Ku(t)}));var Yu=Fo((function(e,t,n){return e+(n?" ":"")+t.toUpperCase()})),Ku=Zo("toUpperCase");function Xu(e,t,n){return e=yu(e),(t=n?o:t)===o?function(e){return it.test(e)}(e)?function(e){return e.match(rt)||[]}(e):function(e){return e.match(fe)||[]}(e):e.match(t)||[]}var $u=Kr((function(e,t){try{return Ct(e,o,t)}catch(n){return Ka(n)?n:new ue(n)}})),Qu=ri((function(e,t){return Ot(t,(function(t){t=Fi(t),ir(e,t,Oa(e[t],e))})),e}));function es(e){return function(){return e}}var ts=Bo(),ns=Bo(!0);function rs(e){return e}function os(e){return jr("function"==typeof e?e:sr(e,1))}var is=Kr((function(e,t){return function(n){return Pr(n,e,t)}})),as=Kr((function(e,t){return function(n){return Pr(e,n,t)}}));function us(e,t,n){var r=Nu(t),o=xr(t,r);null!=n||eu(t)&&(o.length||!r.length)||(n=t,t=e,e=this,o=xr(t,Nu(t)));var i=!(eu(n)&&"chain"in n)||!!n.chain,a=Xa(e);return Ot(o,(function(n){var r=t[n];e[n]=r,a&&(e.prototype[n]=function(){var t=this.__chain__;if(i||t){var n=e(this.__wrapped__);return(n.__actions__=Po(this.__actions__)).push({func:r,args:arguments,thisArg:e}),n.__chain__=t,n}return r.apply(e,Zt([this.value()],arguments))})})),e}function ss(){}var ls=qo(jt),cs=qo(Nt),fs=qo(Lt);function ds(e){return ki(e)?Jt(Fi(e)):function(e){return function(t){return Sr(t,e)}}(e)}var ps=Wo(),hs=Wo(!0);function vs(){return[]}function ms(){return!1}var gs=Uo((function(e,t){return e+t}),0),ys=Yo("ceil"),bs=Uo((function(e,t){return e/t}),1),ws=Yo("floor");var ks=Uo((function(e,t){return e*t}),1),xs=Yo("round"),Ss=Uo((function(e,t){return e-t}),0);return Bn.after=function(e,t){if("function"!=typeof t)throw new Oe(i);return e=hu(e),function(){if(--e<1)return t.apply(this,arguments)}},Bn.ary=Ca,Bn.assign=bu,Bn.assignIn=wu,Bn.assignInWith=ku,Bn.assignWith=xu,Bn.at=Su,Bn.before=Ia,Bn.bind=Oa,Bn.bindAll=Qu,Bn.bindKey=Pa,Bn.castArray=function(){if(!arguments.length)return[];var e=arguments[0];return qa(e)?e:[e]},Bn.chain=da,Bn.chunk=function(e,t,r){t=(r?wi(e,t,r):t===o)?1:Gt(hu(t),0);var i=null==e?0:e.length;if(!i||t<1)return[];for(var a=0,u=0,s=n(pt(i/t));ai?0:i+n),(r=r===o||r>i?i:hu(r))<0&&(r+=i),r=n>r?0:vu(r);n>>0)?(e=yu(e))&&("string"==typeof t||null!=t&&!iu(t))&&!(t=lo(t))&&sn(e)?xo(vn(e),0,n):e.split(t,n):[]},Bn.spread=function(e,t){if("function"!=typeof e)throw new Oe(i);return t=null==t?0:Gt(hu(t),0),Kr((function(n){var r=n[t],o=xo(n,0,t);return r&&Zt(o,r),Ct(e,this,o)}))},Bn.tail=function(e){var t=null==e?0:e.length;return t?ro(e,1,t):[]},Bn.take=function(e,t,n){return e&&e.length?ro(e,0,(t=n||t===o?1:hu(t))<0?0:t):[]},Bn.takeRight=function(e,t,n){var r=null==e?0:e.length;return r?ro(e,(t=r-(t=n||t===o?1:hu(t)))<0?0:t,r):[]},Bn.takeRightWhile=function(e,t){return e&&e.length?ho(e,li(t,3),!1,!0):[]},Bn.takeWhile=function(e,t){return e&&e.length?ho(e,li(t,3)):[]},Bn.tap=function(e,t){return t(e),e},Bn.throttle=function(e,t,n){var r=!0,o=!0;if("function"!=typeof e)throw new Oe(i);return eu(n)&&(r="leading"in n?!!n.leading:r,o="trailing"in n?!!n.trailing:o),Na(e,t,{leading:r,maxWait:t,trailing:o})},Bn.thru=pa,Bn.toArray=du,Bn.toPairs=Mu,Bn.toPairsIn=Lu,Bn.toPath=function(e){return qa(e)?jt(e,Fi):su(e)?[e]:Po(Zi(yu(e)))},Bn.toPlainObject=gu,Bn.transform=function(e,t,n){var r=qa(e),o=r||Ga(e)||lu(e);if(t=li(t,4),null==n){var i=e&&e.constructor;n=o?r?new i:[]:eu(e)&&Xa(i)?zn(We(e)):{}}return(o?Ot:wr)(e,(function(e,r,o){return t(n,e,r,o)})),n},Bn.unary=function(e){return Ca(e,1)},Bn.union=ta,Bn.unionBy=na,Bn.unionWith=ra,Bn.uniq=function(e){return e&&e.length?co(e):[]},Bn.uniqBy=function(e,t){return e&&e.length?co(e,li(t,2)):[]},Bn.uniqWith=function(e,t){return t="function"==typeof t?t:o,e&&e.length?co(e,o,t):[]},Bn.unset=function(e,t){return null==e||fo(e,t)},Bn.unzip=oa,Bn.unzipWith=ia,Bn.update=function(e,t,n){return null==e?e:po(e,t,bo(n))},Bn.updateWith=function(e,t,n,r){return r="function"==typeof r?r:o,null==e?e:po(e,t,bo(n),r)},Bn.values=Bu,Bn.valuesIn=function(e){return null==e?[]:en(e,Au(e))},Bn.without=aa,Bn.words=Xu,Bn.wrap=function(e,t){return Fa(bo(t),e)},Bn.xor=ua,Bn.xorBy=sa,Bn.xorWith=la,Bn.zip=ca,Bn.zipObject=function(e,t){return go(e||[],t||[],tr)},Bn.zipObjectDeep=function(e,t){return go(e||[],t||[],Qr)},Bn.zipWith=fa,Bn.entries=Mu,Bn.entriesIn=Lu,Bn.extend=wu,Bn.extendWith=ku,us(Bn,Bn),Bn.add=gs,Bn.attempt=$u,Bn.camelCase=zu,Bn.capitalize=Vu,Bn.ceil=ys,Bn.clamp=function(e,t,n){return n===o&&(n=t,t=o),n!==o&&(n=(n=mu(n))===n?n:0),t!==o&&(t=(t=mu(t))===t?t:0),ur(mu(e),t,n)},Bn.clone=function(e){return sr(e,4)},Bn.cloneDeep=function(e){return sr(e,5)},Bn.cloneDeepWith=function(e,t){return sr(e,5,t="function"==typeof t?t:o)},Bn.cloneWith=function(e,t){return sr(e,4,t="function"==typeof t?t:o)},Bn.conformsTo=function(e,t){return null==t||lr(e,t,Nu(t))},Bn.deburr=Uu,Bn.defaultTo=function(e,t){return null==e||e!==e?t:e},Bn.divide=bs,Bn.endsWith=function(e,t,n){e=yu(e),t=lo(t);var r=e.length,i=n=n===o?r:ur(hu(n),0,r);return(n-=t.length)>=0&&e.slice(n,i)==t},Bn.eq=Ba,Bn.escape=function(e){return(e=yu(e))&&X.test(e)?e.replace(Y,an):e},Bn.escapeRegExp=function(e){return(e=yu(e))&&ie.test(e)?e.replace(oe,"\\$&"):e},Bn.every=function(e,t,n){var r=qa(e)?Nt:hr;return n&&wi(e,t,n)&&(t=o),r(e,li(t,3))},Bn.find=ma,Bn.findIndex=Ui,Bn.findKey=function(e,t){return zt(e,li(t,3),wr)},Bn.findLast=ga,Bn.findLastIndex=qi,Bn.findLastKey=function(e,t){return zt(e,li(t,3),kr)},Bn.floor=ws,Bn.forEach=ya,Bn.forEachRight=ba,Bn.forIn=function(e,t){return null==e?e:yr(e,li(t,3),Au)},Bn.forInRight=function(e,t){return null==e?e:br(e,li(t,3),Au)},Bn.forOwn=function(e,t){return e&&wr(e,li(t,3))},Bn.forOwnRight=function(e,t){return e&&kr(e,li(t,3))},Bn.get=_u,Bn.gt=za,Bn.gte=Va,Bn.has=function(e,t){return null!=e&&mi(e,t,Cr)},Bn.hasIn=Cu,Bn.head=Wi,Bn.identity=rs,Bn.includes=function(e,t,n,r){e=Wa(e)?e:Bu(e),n=n&&!r?hu(n):0;var o=e.length;return n<0&&(n=Gt(o+n,0)),uu(e)?n<=o&&e.indexOf(t,n)>-1:!!o&&Ut(e,t,n)>-1},Bn.indexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var o=null==n?0:hu(n);return o<0&&(o=Gt(r+o,0)),Ut(e,t,o)},Bn.inRange=function(e,t,n){return t=pu(t),n===o?(n=t,t=0):n=pu(n),function(e,t,n){return e>=bn(t,n)&&e=-9007199254740991&&e<=h},Bn.isSet=au,Bn.isString=uu,Bn.isSymbol=su,Bn.isTypedArray=lu,Bn.isUndefined=function(e){return e===o},Bn.isWeakMap=function(e){return tu(e)&&vi(e)==D},Bn.isWeakSet=function(e){return tu(e)&&"[object WeakSet]"==Er(e)},Bn.join=function(e,t){return null==e?"":wt.call(e,t)},Bn.kebabCase=qu,Bn.last=Ki,Bn.lastIndexOf=function(e,t,n){var r=null==e?0:e.length;if(!r)return-1;var i=r;return n!==o&&(i=(i=hu(n))<0?Gt(r+i,0):bn(i,r-1)),t===t?function(e,t,n){for(var r=n+1;r--;)if(e[r]===t)return r;return r}(e,t,i):Vt(e,Ht,i,!0)},Bn.lowerCase=Hu,Bn.lowerFirst=Wu,Bn.lt=cu,Bn.lte=fu,Bn.max=function(e){return e&&e.length?vr(e,rs,_r):o},Bn.maxBy=function(e,t){return e&&e.length?vr(e,li(t,2),_r):o},Bn.mean=function(e){return Wt(e,rs)},Bn.meanBy=function(e,t){return Wt(e,li(t,2))},Bn.min=function(e){return e&&e.length?vr(e,rs,Mr):o},Bn.minBy=function(e,t){return e&&e.length?vr(e,li(t,2),Mr):o},Bn.stubArray=vs,Bn.stubFalse=ms,Bn.stubObject=function(){return{}},Bn.stubString=function(){return""},Bn.stubTrue=function(){return!0},Bn.multiply=ks,Bn.nth=function(e,t){return e&&e.length?Ur(e,hu(t)):o},Bn.noConflict=function(){return vt._===this&&(vt._=Be),this},Bn.noop=ss,Bn.now=_a,Bn.pad=function(e,t,n){e=yu(e);var r=(t=hu(t))?hn(e):0;if(!t||r>=t)return e;var o=(t-r)/2;return Ho(ht(o),n)+e+Ho(pt(o),n)},Bn.padEnd=function(e,t,n){e=yu(e);var r=(t=hu(t))?hn(e):0;return t&&rt){var r=e;e=t,t=r}if(n||e%1||t%1){var i=xn();return bn(e+i*(t-e+ft("1e-"+((i+"").length-1))),t)}return Gr(e,t)},Bn.reduce=function(e,t,n){var r=qa(e)?Ft:Yt,o=arguments.length<3;return r(e,li(t,4),n,o,dr)},Bn.reduceRight=function(e,t,n){var r=qa(e)?Mt:Yt,o=arguments.length<3;return r(e,li(t,4),n,o,pr)},Bn.repeat=function(e,t,n){return t=(n?wi(e,t,n):t===o)?1:hu(t),Yr(yu(e),t)},Bn.replace=function(){var e=arguments,t=yu(e[0]);return e.length<3?t:t.replace(e[1],e[2])},Bn.result=function(e,t,n){var r=-1,i=(t=wo(t,e)).length;for(i||(i=1,e=o);++rh)return[];var n=m,r=bn(e,m);t=li(t),e-=m;for(var o=Xt(r,t);++n=a)return e;var s=n-hn(r);if(s<1)return r;var l=u?xo(u,0,s).join(""):e.slice(0,s);if(i===o)return l+r;if(u&&(s+=l.length-s),iu(i)){if(e.slice(s).search(i)){var c,f=l;for(i.global||(i=Ce(i.source,yu(ve.exec(i))+"g")),i.lastIndex=0;c=i.exec(f);)var d=c.index;l=l.slice(0,d===o?s:d)}}else if(e.indexOf(lo(i),s)!=s){var p=l.lastIndexOf(i);p>-1&&(l=l.slice(0,p))}return l+r},Bn.unescape=function(e){return(e=yu(e))&&K.test(e)?e.replace(G,gn):e},Bn.uniqueId=function(e){var t=++Ze;return yu(e)+t},Bn.upperCase=Yu,Bn.upperFirst=Ku,Bn.each=ya,Bn.eachRight=ba,Bn.first=Wi,us(Bn,function(){var e={};return wr(Bn,(function(t,n){je.call(Bn.prototype,n)||(e[n]=t)})),e}(),{chain:!1}),Bn.VERSION="4.17.21",Ot(["bind","bindKey","curry","curryRight","partial","partialRight"],(function(e){Bn[e].placeholder=Bn})),Ot(["drop","take"],(function(e,t){qn.prototype[e]=function(n){n=n===o?1:Gt(hu(n),0);var r=this.__filtered__&&!t?new qn(this):this.clone();return r.__filtered__?r.__takeCount__=bn(n,r.__takeCount__):r.__views__.push({size:bn(n,m),type:e+(r.__dir__<0?"Right":"")}),r},qn.prototype[e+"Right"]=function(t){return this.reverse()[e](t).reverse()}})),Ot(["filter","map","takeWhile"],(function(e,t){var n=t+1,r=1==n||3==n;qn.prototype[e]=function(e){var t=this.clone();return t.__iteratees__.push({iteratee:li(e,3),type:n}),t.__filtered__=t.__filtered__||r,t}})),Ot(["head","last"],(function(e,t){var n="take"+(t?"Right":"");qn.prototype[e]=function(){return this[n](1).value()[0]}})),Ot(["initial","tail"],(function(e,t){var n="drop"+(t?"":"Right");qn.prototype[e]=function(){return this.__filtered__?new qn(this):this[n](1)}})),qn.prototype.compact=function(){return this.filter(rs)},qn.prototype.find=function(e){return this.filter(e).head()},qn.prototype.findLast=function(e){return this.reverse().find(e)},qn.prototype.invokeMap=Kr((function(e,t){return"function"==typeof e?new qn(this):this.map((function(n){return Pr(n,e,t)}))})),qn.prototype.reject=function(e){return this.filter(ja(li(e)))},qn.prototype.slice=function(e,t){e=hu(e);var n=this;return n.__filtered__&&(e>0||t<0)?new qn(n):(e<0?n=n.takeRight(-e):e&&(n=n.drop(e)),t!==o&&(n=(t=hu(t))<0?n.dropRight(-t):n.take(t-e)),n)},qn.prototype.takeRightWhile=function(e){return this.reverse().takeWhile(e).reverse()},qn.prototype.toArray=function(){return this.take(m)},wr(qn.prototype,(function(e,t){var n=/^(?:filter|find|map|reject)|While$/.test(t),r=/^(?:head|last)$/.test(t),i=Bn[r?"take"+("last"==t?"Right":""):t],a=r||/^find/.test(t);i&&(Bn.prototype[t]=function(){var t=this.__wrapped__,u=r?[1]:arguments,s=t instanceof qn,l=u[0],c=s||qa(t),f=function(e){var t=i.apply(Bn,Zt([e],u));return r&&d?t[0]:t};c&&n&&"function"==typeof l&&1!=l.length&&(s=c=!1);var d=this.__chain__,p=!!this.__actions__.length,h=a&&!d,v=s&&!p;if(!a&&c){t=v?t:new qn(this);var m=e.apply(t,u);return m.__actions__.push({func:pa,args:[f],thisArg:o}),new Un(m,d)}return h&&v?e.apply(this,u):(m=this.thru(f),h?r?m.value()[0]:m.value():m)})})),Ot(["pop","push","shift","sort","splice","unshift"],(function(e){var t=Pe[e],n=/^(?:push|sort|unshift)$/.test(e)?"tap":"thru",r=/^(?:pop|shift)$/.test(e);Bn.prototype[e]=function(){var e=arguments;if(r&&!this.__chain__){var o=this.value();return t.apply(qa(o)?o:[],e)}return this[n]((function(n){return t.apply(qa(n)?n:[],e)}))}})),wr(qn.prototype,(function(e,t){var n=Bn[t];if(n){var r=n.name+"";je.call(Nn,r)||(Nn[r]=[]),Nn[r].push({name:t,func:n})}})),Nn[zo(o,2).name]=[{name:"wrapper",func:o}],qn.prototype.clone=function(){var e=new qn(this.__wrapped__);return e.__actions__=Po(this.__actions__),e.__dir__=this.__dir__,e.__filtered__=this.__filtered__,e.__iteratees__=Po(this.__iteratees__),e.__takeCount__=this.__takeCount__,e.__views__=Po(this.__views__),e},qn.prototype.reverse=function(){if(this.__filtered__){var e=new qn(this);e.__dir__=-1,e.__filtered__=!0}else(e=this.clone()).__dir__*=-1;return e},qn.prototype.value=function(){var e=this.__wrapped__.value(),t=this.__dir__,n=qa(e),r=t<0,o=n?e.length:0,i=function(e,t,n){var r=-1,o=n.length;for(;++r=this.__values__.length;return{done:e,value:e?o:this.__values__[this.__index__++]}},Bn.prototype.plant=function(e){for(var t,n=this;n instanceof Vn;){var r=Li(n);r.__index__=0,r.__values__=o,t?i.__wrapped__=r:t=r;var i=r;n=n.__wrapped__}return i.__wrapped__=e,t},Bn.prototype.reverse=function(){var e=this.__wrapped__;if(e instanceof qn){var t=e;return this.__actions__.length&&(t=new qn(this)),(t=t.reverse()).__actions__.push({func:pa,args:[ea],thisArg:o}),new Un(t,this.__chain__)}return this.thru(ea)},Bn.prototype.toJSON=Bn.prototype.valueOf=Bn.prototype.value=function(){return vo(this.__wrapped__,this.__actions__)},Bn.prototype.first=Bn.prototype.head,Xe&&(Bn.prototype[Xe]=function(){return this}),Bn}();vt._=yn,(r=function(){return yn}.call(t,n,t,e))===o||(e.exports=r)}.call(this)},4463:function(e,t,n){"use strict";var r=n(2791),o=n(5296);function i(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n