@verdaccio/ui-components 5.0.0-next-9.21 → 5.0.0-next-9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/Theme/ResetStyles.js.map +1 -1
- package/build/Theme/ResetStyles.mjs.map +1 -1
- package/build/Theme/StyleBaseline.js.map +1 -1
- package/build/Theme/StyleBaseline.mjs.map +1 -1
- package/build/Theme/ThemeProvider.js.map +1 -1
- package/build/Theme/ThemeProvider.mjs.map +1 -1
- package/build/Theme/colors.js.map +1 -1
- package/build/Theme/modes.js.map +1 -1
- package/build/Theme/theme.js.map +1 -1
- package/build/api/use-data-mutation.d.ts +1 -1
- package/build/api/use-data-mutation.js.map +1 -1
- package/build/api/use-data-mutation.mjs.map +1 -1
- package/build/api/use-data.d.ts +1 -1
- package/build/api/use-data.js.map +1 -1
- package/build/api/use-data.mjs.map +1 -1
- package/build/components/ActionBar/ActionBar.js.map +1 -1
- package/build/components/ActionBar/ActionBar.mjs.map +1 -1
- package/build/components/ActionBar/ActionBarAction.js.map +1 -1
- package/build/components/ActionBar/index.mjs +1 -2
- package/build/components/AppRoute/AppRoute.js.map +1 -1
- package/build/components/AppRoute/AppRoute.mjs.map +1 -1
- package/build/components/Author/Author.js.map +1 -1
- package/build/components/Author/index.mjs +1 -2
- package/build/components/Author/styles.js.map +1 -1
- package/build/components/CopyClipboard/CopyToClipBoard.js.map +1 -1
- package/build/components/Dependencies/Dependencies.js.map +1 -1
- package/build/components/Dependencies/DependencyBlock.js.map +1 -1
- package/build/components/Dependencies/DependencyBlock.mjs.map +1 -1
- package/build/components/Dependencies/index.mjs +1 -2
- package/build/components/Deprecated/Deprecated.js.map +1 -1
- package/build/components/Deprecated/index.mjs +1 -2
- package/build/components/Developers/Developers.js.map +1 -1
- package/build/components/Developers/Developers.mjs.map +1 -1
- package/build/components/Developers/Title.js.map +1 -1
- package/build/components/Distribution/Dist.js.map +1 -1
- package/build/components/Distribution/Dist.mjs.map +1 -1
- package/build/components/Distribution/index.mjs +1 -2
- package/build/components/Distribution/styles.js.map +1 -1
- package/build/components/Engines/Engines.js.map +1 -1
- package/build/components/Engines/index.mjs +1 -2
- package/build/components/Engines/styles.js.map +1 -1
- package/build/components/ErrorBoundary/ErrorBoundary.js.map +1 -1
- package/build/components/ErrorBoundary/index.mjs +1 -2
- package/build/components/Forbidden/Forbidden.js.map +1 -1
- package/build/components/Forbidden/index.mjs +1 -2
- package/build/components/FundButton/FundButton.js.map +1 -1
- package/build/components/FundButton/FundButton.mjs.map +1 -1
- package/build/components/FundButton/index.mjs +1 -2
- package/build/components/GenericError/GenericError.js.map +1 -1
- package/build/components/GenericError/index.mjs +1 -2
- package/build/components/HeaderInfoDialog/HeaderInfoDialog.js.map +1 -1
- package/build/components/HeaderInfoDialog/index.mjs +1 -2
- package/build/components/Heading/Heading.js.map +1 -1
- package/build/components/Heading/Heading.mjs.map +1 -1
- package/build/components/Heading/index.mjs +1 -2
- package/build/components/Help/Help.js.map +1 -1
- package/build/components/Help/Help.mjs.map +1 -1
- package/build/components/Help/index.mjs +1 -2
- package/build/components/Help/styles.js.map +1 -1
- package/build/components/Icons/DevsIcons/CommonJS2.js.map +1 -1
- package/build/components/Icons/DevsIcons/CommonJS2.mjs.map +1 -1
- package/build/components/Icons/DevsIcons/ES6Module2.js.map +1 -1
- package/build/components/Icons/DevsIcons/ES6Module2.mjs.map +1 -1
- package/build/components/Icons/DevsIcons/Git2.js.map +1 -1
- package/build/components/Icons/DevsIcons/Git2.mjs.map +1 -1
- package/build/components/Icons/DevsIcons/NodeJS2.js.map +1 -1
- package/build/components/Icons/DevsIcons/NodeJS2.mjs.map +1 -1
- package/build/components/Icons/DevsIcons/TypeScript2.js.map +1 -1
- package/build/components/Icons/DevsIcons/TypeScript2.mjs.map +1 -1
- package/build/components/Icons/Earth.js.map +1 -1
- package/build/components/Icons/FileBinary.js.map +1 -1
- package/build/components/Icons/Law.js.map +1 -1
- package/build/components/Icons/License.d.ts +2 -4
- package/build/components/Icons/License.js.map +1 -1
- package/build/components/Icons/License.mjs.map +1 -1
- package/build/components/Icons/Managers/Npm2.js.map +1 -1
- package/build/components/Icons/Managers/Npm2.mjs.map +1 -1
- package/build/components/Icons/Managers/Pnpm2.js.map +1 -1
- package/build/components/Icons/Managers/Pnpm2.mjs.map +1 -1
- package/build/components/Icons/Managers/Yarn2.js.map +1 -1
- package/build/components/Icons/Managers/Yarn2.mjs.map +1 -1
- package/build/components/Icons/SvgIcon.js.map +1 -1
- package/build/components/Icons/Time.js.map +1 -1
- package/build/components/Icons/Version.js.map +1 -1
- package/build/components/Install/Install.js.map +1 -1
- package/build/components/Install/Install.mjs.map +1 -1
- package/build/components/Install/InstallListItem.js.map +1 -1
- package/build/components/Install/index.mjs +1 -2
- package/build/components/Keywords/KeywordListItems.js.map +1 -1
- package/build/components/Keywords/Keywords.js.map +1 -1
- package/build/components/Keywords/index.mjs +1 -2
- package/build/components/Label/Label.js.map +1 -1
- package/build/components/Label/index.mjs +1 -2
- package/build/components/Link/Link.js.map +1 -1
- package/build/components/Link/Link.mjs.map +1 -1
- package/build/components/Link/index.mjs +1 -2
- package/build/components/LinkExternal/LinkExternal.js.map +1 -1
- package/build/components/LinkExternal/index.mjs +1 -2
- package/build/components/Loading/Loading.js.map +1 -1
- package/build/components/Loading/Spinner/Spinner.js.map +1 -1
- package/build/components/Loading/Spinner/index.mjs +1 -2
- package/build/components/Loading/index.mjs +1 -2
- package/build/components/Loading/styles.js.map +1 -1
- package/build/components/LoginDialog/LoginDialog.d.ts +1 -0
- package/build/components/LoginDialog/LoginDialog.js +11 -2
- package/build/components/LoginDialog/LoginDialog.js.map +1 -1
- package/build/components/LoginDialog/LoginDialog.mjs +10 -2
- package/build/components/LoginDialog/LoginDialog.mjs.map +1 -1
- package/build/components/LoginDialog/LoginDialogCloseButton.js.map +1 -1
- package/build/components/LoginDialog/LoginDialogForm.js.map +1 -1
- package/build/components/LoginDialog/LoginDialogFormError.js.map +1 -1
- package/build/components/LoginDialog/index.mjs +1 -2
- package/build/components/LoginForm/Login.js +0 -23
- package/build/components/LoginForm/Login.js.map +1 -1
- package/build/components/LoginForm/Login.mjs +0 -21
- package/build/components/LoginForm/Login.mjs.map +1 -1
- package/build/components/LoginForm/PasswordField.js.map +1 -1
- package/build/components/LoginForm/UsernameField.js.map +1 -1
- package/build/components/LoginForm/styles.js.map +1 -1
- package/build/components/LoginForm/useLoginForm.js.map +1 -1
- package/build/components/Logo/Logo.js.map +1 -1
- package/build/components/Logo/Logo.mjs.map +1 -1
- package/build/components/Logo/index.mjs +1 -2
- package/build/components/MenuItem/MenuItem.js.map +1 -1
- package/build/components/MenuItem/MenuItem.mjs.map +1 -1
- package/build/components/MenuItem/index.mjs +1 -2
- package/build/components/NoItems/NoItems.js.map +1 -1
- package/build/components/NoItems/index.mjs +1 -2
- package/build/components/NotFound/NotFound.js.map +1 -1
- package/build/components/NotFound/index.mjs +1 -2
- package/build/components/Package/Package.js.map +1 -1
- package/build/components/Package/Package.mjs.map +1 -1
- package/build/components/Package/index.mjs +1 -2
- package/build/components/Package/styles.js.map +1 -1
- package/build/components/Package/styles.mjs.map +1 -1
- package/build/components/PackageList/PackageList.js.map +1 -1
- package/build/components/PackageList/PackageList.mjs.map +1 -1
- package/build/components/PackageList/index.mjs +1 -2
- package/build/components/Person/Person.js.map +1 -1
- package/build/components/Person/Person.mjs.map +1 -1
- package/build/components/Person/PersonTooltip.js.map +1 -1
- package/build/components/Person/PersonTooltip.mjs.map +1 -1
- package/build/components/Person/index.mjs +1 -2
- package/build/components/Person/utils.mjs.map +1 -1
- package/build/components/RawViewer/RawViewer.js.map +1 -1
- package/build/components/RawViewer/index.mjs +1 -2
- package/build/components/Readme/Readme.js.map +1 -1
- package/build/components/Readme/index.mjs +1 -2
- package/build/components/Readme/utils.js.map +1 -1
- package/build/components/RegistryInfoDialog/RegistryInfoDialog.js.map +1 -1
- package/build/components/RegistryInfoDialog/index.mjs +1 -2
- package/build/components/RegistryInfoDialog/styles.js.map +1 -1
- package/build/components/Repository/Repository.js.map +1 -1
- package/build/components/Repository/Repository.mjs.map +1 -1
- package/build/components/Repository/index.mjs +1 -2
- package/build/components/Search/AutoComplete/AutoComplete.js.map +1 -1
- package/build/components/Search/AutoComplete/index.mjs +1 -2
- package/build/components/Search/AutoComplete/styles.js.map +1 -1
- package/build/components/Search/Search.js.map +1 -1
- package/build/components/Search/Search.mjs.map +1 -1
- package/build/components/Search/SearchItem.js.map +1 -1
- package/build/components/Search/index.mjs +1 -2
- package/build/components/Search/styles.js.map +1 -1
- package/build/components/SettingsMenu/SettingsMenu.js.map +1 -1
- package/build/components/SettingsMenu/SettingsMenu.mjs.map +1 -1
- package/build/components/SettingsMenu/index.mjs +1 -2
- package/build/components/SideBarTitle/SideBarTitle.js.map +1 -1
- package/build/components/SideBarTitle/SideBarTitle.mjs.map +1 -1
- package/build/components/SideBarTitle/index.mjs +1 -2
- package/build/components/UpLinks/UpLinks.js.map +1 -1
- package/build/components/UpLinks/UpLinks.mjs.map +1 -1
- package/build/components/UpLinks/UplinkLink.js.map +1 -1
- package/build/components/UpLinks/UplinkLink.mjs.map +1 -1
- package/build/components/UpLinks/index.mjs +1 -2
- package/build/components/UpLinks/styles.js.map +1 -1
- package/build/components/UpLinks/styles.mjs.map +1 -1
- package/build/components/Versions/HistoryList.js.map +1 -1
- package/build/components/Versions/HistoryList.mjs.map +1 -1
- package/build/components/Versions/TagList.js.map +1 -1
- package/build/components/Versions/Versions.js.map +1 -1
- package/build/components/Versions/Versions.mjs.map +1 -1
- package/build/components/Versions/index.mjs +1 -2
- package/build/components/Versions/styles.js.map +1 -1
- package/build/components/Versions/styles.mjs.map +1 -1
- package/build/hooks/useLocalStorage.js.map +1 -1
- package/build/layouts/Security/Dialog.js.map +1 -1
- package/build/layouts/Version/Version.js.map +1 -1
- package/build/layouts/Version/Version.mjs.map +1 -1
- package/build/layouts/Version/index.mjs +1 -2
- package/build/pages/Front/Home.js.map +1 -1
- package/build/pages/Front/index.mjs +1 -2
- package/build/pages/Security/AddUser.js.map +1 -1
- package/build/pages/Security/ChangePassword.js.map +1 -1
- package/build/pages/Security/Login.js.map +1 -1
- package/build/pages/Security/Success.js.map +1 -1
- package/build/pages/Stage/StageDetail.js.map +1 -1
- package/build/pages/Stage/StageList.js.map +1 -1
- package/build/pages/Version/Version.js.map +1 -1
- package/build/pages/Version/index.mjs +1 -2
- package/build/providers/AppConfigurationProvider/AppConfigurationProvider.js.map +1 -1
- package/build/providers/AppConfigurationProvider/index.mjs +1 -2
- package/build/providers/AuthProvider/AuthProvider.js +1 -1
- package/build/providers/AuthProvider/AuthProvider.js.map +1 -1
- package/build/providers/AuthProvider/AuthProvider.mjs +1 -1
- package/build/providers/AuthProvider/AuthProvider.mjs.map +1 -1
- package/build/providers/AuthProvider/index.mjs +1 -2
- package/build/providers/AuthProvider/utils.js.map +1 -1
- package/build/providers/DownloadProvider/DownloadProvider.js.map +1 -1
- package/build/providers/DownloadProvider/index.mjs +1 -2
- package/build/providers/ManifestsProvider/ManifestsProvider.js +1 -1
- package/build/providers/ManifestsProvider/ManifestsProvider.js.map +1 -1
- package/build/providers/ManifestsProvider/ManifestsProvider.mjs +1 -1
- package/build/providers/ManifestsProvider/ManifestsProvider.mjs.map +1 -1
- package/build/providers/ManifestsProvider/index.mjs +1 -2
- package/build/providers/PersistenceSettingProvider/PersistenceSettingProvider.js.map +1 -1
- package/build/providers/PersistenceSettingProvider/index.mjs +1 -2
- package/build/providers/SearchProvider/SearchProvider.js +1 -1
- package/build/providers/SearchProvider/SearchProvider.js.map +1 -1
- package/build/providers/SearchProvider/SearchProvider.mjs +1 -1
- package/build/providers/SearchProvider/SearchProvider.mjs.map +1 -1
- package/build/providers/SearchProvider/index.mjs +1 -2
- package/build/providers/TranslatorProvider/TranslatorProvider.js.map +1 -1
- package/build/providers/TranslatorProvider/index.mjs +1 -2
- package/build/providers/VersionProvider/VersionProvider.js +1 -1
- package/build/providers/VersionProvider/VersionProvider.js.map +1 -1
- package/build/providers/VersionProvider/VersionProvider.mjs +1 -1
- package/build/providers/VersionProvider/VersionProvider.mjs.map +1 -1
- package/build/providers/VersionProvider/index.mjs +1 -2
- package/build/sections/Detail/ContainerContent.js.map +1 -1
- package/build/sections/Detail/ContainerContent.mjs.map +1 -1
- package/build/sections/Detail/Detail.js.map +1 -1
- package/build/sections/Detail/Detail.mjs.map +1 -1
- package/build/sections/Detail/ReadmeSection.js.map +1 -1
- package/build/sections/Detail/Tabs.js.map +1 -1
- package/build/sections/Detail/Tabs.mjs.map +1 -1
- package/build/sections/Detail/index.mjs +1 -2
- package/build/sections/Footer/Footer.js.map +1 -1
- package/build/sections/Footer/Footer.mjs.map +1 -1
- package/build/sections/Footer/index.mjs +1 -2
- package/build/sections/Footer/styles.js.map +1 -1
- package/build/sections/Header/Header.js.map +1 -1
- package/build/sections/Header/Header.mjs.map +1 -1
- package/build/sections/Header/HeaderGreetings.js.map +1 -1
- package/build/sections/Header/HeaderGreetings.mjs.map +1 -1
- package/build/sections/Header/HeaderLeft.js.map +1 -1
- package/build/sections/Header/HeaderLeft.mjs.map +1 -1
- package/build/sections/Header/HeaderMenu.js +10 -0
- package/build/sections/Header/HeaderMenu.js.map +1 -1
- package/build/sections/Header/HeaderMenu.mjs +10 -0
- package/build/sections/Header/HeaderMenu.mjs.map +1 -1
- package/build/sections/Header/HeaderRight.js.map +1 -1
- package/build/sections/Header/HeaderRight.mjs.map +1 -1
- package/build/sections/Header/HeaderSettingsDialog.js.map +1 -1
- package/build/sections/Header/HeaderToolTip.js.map +1 -1
- package/build/sections/Header/HeaderToolTipIcon.js.map +1 -1
- package/build/sections/Header/HeaderToolTipIcon.mjs.map +1 -1
- package/build/sections/Header/LanguageSwitch.js.map +1 -1
- package/build/sections/Header/LanguageSwitch.mjs.map +1 -1
- package/build/sections/Header/RegistryInfoContent/RegistryInfoContent.js.map +1 -1
- package/build/sections/Header/RegistryInfoContent/RegistryInfoContent.mjs.map +1 -1
- package/build/sections/Header/RegistryInfoContent/index.mjs +1 -2
- package/build/sections/Header/RegistryInfoContent/styles.js.map +1 -1
- package/build/sections/Header/index.mjs +1 -2
- package/build/sections/Header/styles.js.map +1 -1
- package/build/sections/Home/Home.js.map +1 -1
- package/build/sections/Home/index.mjs +1 -2
- package/build/sections/Security/AddUser.js.map +1 -1
- package/build/sections/Security/AddUser.mjs.map +1 -1
- package/build/sections/Security/ChangePassword.js +41 -16
- package/build/sections/Security/ChangePassword.js.map +1 -1
- package/build/sections/Security/ChangePassword.mjs +41 -16
- package/build/sections/Security/ChangePassword.mjs.map +1 -1
- package/build/sections/Security/Login.js.map +1 -1
- package/build/sections/Security/Login.mjs.map +1 -1
- package/build/sections/Security/Success.js.map +1 -1
- package/build/sections/Security/styles.js.map +1 -1
- package/build/sections/Security/utils.js.map +1 -1
- package/build/sections/SideBar/Sidebar.js.map +1 -1
- package/build/sections/SideBar/Sidebar.mjs.map +1 -1
- package/build/sections/SideBar/index.mjs +1 -2
- package/build/sections/Stage/StageActions.js.map +1 -1
- package/build/sections/Stage/StageActions.mjs.map +1 -1
- package/build/sections/Stage/StageDetail.js.map +1 -1
- package/build/sections/Stage/StageDetail.mjs.map +1 -1
- package/build/sections/Stage/StageList.js.map +1 -1
- package/build/sections/Stage/StageList.mjs.map +1 -1
- package/build/sections/Stage/useRequireSession.js.map +1 -1
- package/build/sections/Stage/useRequireSession.mjs.map +1 -1
- package/build/sections/Stage/useStage.js.map +1 -1
- package/build/sections/Stage/useStage.mjs.map +1 -1
- package/build/store/api.js.map +1 -1
- package/build/store/api.mjs.map +1 -1
- package/build/store/index.mjs +1 -2
- package/build/store/storage.js.map +1 -1
- package/build/utils/loadable.js.map +1 -1
- package/build/utils/schemas.d.ts +0 -2
- package/build/utils/schemas.js +0 -1
- package/build/utils/schemas.js.map +1 -1
- package/build/utils/schemas.mjs +0 -1
- package/build/utils/schemas.mjs.map +1 -1
- package/build/utils/token.js.map +1 -1
- package/build/utils/url.js.map +1 -1
- package/build/utils/url.mjs.map +1 -1
- package/build/utils/utils.js.map +1 -1
- package/package.json +21 -21
- package/build/store/routes.d.ts +0 -12
- package/build/store/routes.js +0 -18
- package/build/store/routes.js.map +0 -1
- package/build/store/routes.mjs +0 -18
- package/build/store/routes.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Login.mjs","names":[],"sources":["../../../src/sections/Security/Login.tsx"],"sourcesContent":["import { yupResolver } from '@hookform/resolvers/yup';\nimport { Link, Typography } from '@mui/material';\nimport React, { useCallback, useRef } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { Link as RouterLink, useLocation, useNavigate } from 'react-router';\n\nimport { useDataMutation } from '../../api/use-data-mutation';\nimport type { LoginFormValues } from '../../components/LoginForm/Login';\nimport LoginForm from '../../components/LoginForm/Login';\nimport LoginFormHeader from '../../components/LoginForm/styles';\nimport NotFound from '../../components/NotFound';\nimport { getConfiguration } from '../../configuration';\nimport SecurityLayout from '../../layouts/Security/Dialog';\nimport { useAuth } from '../../providers/AuthProvider/AuthProvider';\nimport type { LoginBody } from '../../providers/AuthProvider/types';\nimport { authErrorMessage } from '../../providers/AuthProvider/utils';\nimport { saveAuth } from '../../store/storage';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { Route } from '../../utils';\nimport { loginSchema } from '../../utils/schemas';\nimport { MessageType } from './Success';\nimport { SecurityContainer, SecurityForm } from './styles';\nimport { getSecurityUrlParams } from './utils';\n\nconst configuration = getConfiguration();\nconst basePath = stripTrailingSlash(configuration.base);\n\nconst Login: React.FC = () => {\n const { t } = useTranslation();\n const location = useLocation();\n const navigate = useNavigate();\n const { setUserState } = useAuth();\n const { next } = getSecurityUrlParams(location);\n const createUserEnabled = configuration?.flags?.createUser;\n const addUserLink = Route.ADD_USER + (next ? '?next=' + next : '');\n\n const { trigger } = useDataMutation<LoginBody>(basePath, next, 'POST');\n\n const form = useForm<LoginFormValues>({\n mode: 'onChange',\n resolver: yupResolver(loginSchema),\n });\n\n const {\n setError,\n handleSubmit,\n register,\n formState: { isValid, isSubmitting, errors },\n } = form;\n\n const handleLogin = useCallback(\n async (body: { username: string; password: string }) => {\n return await trigger(body);\n },\n [trigger]\n );\n\n const onSuccess = useCallback(() => {\n navigate(`${Route.SUCCESS}?messageType=${MessageType.Login}`);\n }, [navigate]);\n\n // `disabled={isSubmitting}` only applies after a re-render; clicks landing in\n // the same React batch would still fire duplicate requests\n const inFlight = useRef(false);\n\n const onSubmit = useCallback(\n async (data: LoginFormValues) => {\n if (inFlight.current) {\n return;\n }\n inFlight.current = true;\n try {\n const result = await handleLogin?.(data);\n if (!result || !result.username || !result.token) {\n // a 2xx with an unexpected body must not pass as a successful login\n throw new Error('login response is missing the token');\n }\n saveAuth(result.username, result.token);\n // AuthProvider reads storage only once, so the header needs the state update\n setUserState?.({ username: result.username, token: result.token });\n onSuccess();\n } catch (err: any) {\n // only a 401 means wrong credentials; a dead server, 500 or 429 must\n // not claim the credentials were invalid\n const message =\n err?.code === 401\n ? t('security.error.invalid-credentials')\n : authErrorMessage(err, t('security.error.unable-to-login'));\n setError('root', { type: 'server', message });\n } finally {\n inFlight.current = false;\n }\n },\n [handleLogin, setError, setUserState, onSuccess, t]\n );\n\n return !next ? (\n <NotFound />\n ) : (\n <SecurityLayout>\n <SecurityContainer>\n {/* container only: LoginForm renders the real <form>, and a <form>\n cannot be a descendant of another <form> */}\n <SecurityForm as=\"div\">\n <LoginFormHeader />\n <LoginForm\n errors={errors}\n handleSubmit={handleSubmit}\n isSubmitting={isSubmitting}\n isValid={isValid}\n onSubmit={onSubmit}\n register={register}\n />\n {createUserEnabled && (\n <Typography align=\"center\" sx={{ mt: 2, fontSize: 12 }} variant=\"body2\">\n {t('security.login.noUserQuestion')}\n <Link component={RouterLink} sx={{ ml: 1 }} to={addUserLink}>\n {t('security.login.createUser')}\n </Link>\n </Typography>\n )}\n </SecurityForm>\n </SecurityContainer>\n </SecurityLayout>\n );\n};\n\nexport default Login;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAM,gBAAgB,iBAAiB;AACvC,IAAM,WAAW,mBAAmB,cAAc,IAAI;AAEtD,IAAM,cAAwB;CAC5B,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,iBAAiB,QAAQ;CACjC,MAAM,EAAE,SAAS,qBAAqB,QAAQ;CAC9C,MAAM,oBAAoB,eAAe,OAAO;CAChD,MAAM,
|
|
1
|
+
{"version":3,"file":"Login.mjs","names":["Route","Link","RouterLink"],"sources":["../../../src/sections/Security/Login.tsx"],"sourcesContent":["import { yupResolver } from '@hookform/resolvers/yup';\nimport { Link, Typography } from '@mui/material';\nimport React, { useCallback, useRef } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { Link as RouterLink, useLocation, useNavigate } from 'react-router';\n\nimport { useDataMutation } from '../../api/use-data-mutation';\nimport type { LoginFormValues } from '../../components/LoginForm/Login';\nimport LoginForm from '../../components/LoginForm/Login';\nimport LoginFormHeader from '../../components/LoginForm/styles';\nimport NotFound from '../../components/NotFound';\nimport { getConfiguration } from '../../configuration';\nimport SecurityLayout from '../../layouts/Security/Dialog';\nimport { useAuth } from '../../providers/AuthProvider/AuthProvider';\nimport type { LoginBody } from '../../providers/AuthProvider/types';\nimport { authErrorMessage } from '../../providers/AuthProvider/utils';\nimport { saveAuth } from '../../store/storage';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { Route } from '../../utils';\nimport { loginSchema } from '../../utils/schemas';\nimport { MessageType } from './Success';\nimport { SecurityContainer, SecurityForm } from './styles';\nimport { getSecurityUrlParams } from './utils';\n\nconst configuration = getConfiguration();\nconst basePath = stripTrailingSlash(configuration.base);\n\nconst Login: React.FC = () => {\n const { t } = useTranslation();\n const location = useLocation();\n const navigate = useNavigate();\n const { setUserState } = useAuth();\n const { next } = getSecurityUrlParams(location);\n const createUserEnabled = configuration?.flags?.createUser;\n const addUserLink = Route.ADD_USER + (next ? '?next=' + next : '');\n\n const { trigger } = useDataMutation<LoginBody>(basePath, next, 'POST');\n\n const form = useForm<LoginFormValues>({\n mode: 'onChange',\n resolver: yupResolver(loginSchema),\n });\n\n const {\n setError,\n handleSubmit,\n register,\n formState: { isValid, isSubmitting, errors },\n } = form;\n\n const handleLogin = useCallback(\n async (body: { username: string; password: string }) => {\n return await trigger(body);\n },\n [trigger]\n );\n\n const onSuccess = useCallback(() => {\n navigate(`${Route.SUCCESS}?messageType=${MessageType.Login}`);\n }, [navigate]);\n\n // `disabled={isSubmitting}` only applies after a re-render; clicks landing in\n // the same React batch would still fire duplicate requests\n const inFlight = useRef(false);\n\n const onSubmit = useCallback(\n async (data: LoginFormValues) => {\n if (inFlight.current) {\n return;\n }\n inFlight.current = true;\n try {\n const result = await handleLogin?.(data);\n if (!result || !result.username || !result.token) {\n // a 2xx with an unexpected body must not pass as a successful login\n throw new Error('login response is missing the token');\n }\n saveAuth(result.username, result.token);\n // AuthProvider reads storage only once, so the header needs the state update\n setUserState?.({ username: result.username, token: result.token });\n onSuccess();\n } catch (err: any) {\n // only a 401 means wrong credentials; a dead server, 500 or 429 must\n // not claim the credentials were invalid\n const message =\n err?.code === 401\n ? t('security.error.invalid-credentials')\n : authErrorMessage(err, t('security.error.unable-to-login'));\n setError('root', { type: 'server', message });\n } finally {\n inFlight.current = false;\n }\n },\n [handleLogin, setError, setUserState, onSuccess, t]\n );\n\n return !next ? (\n <NotFound />\n ) : (\n <SecurityLayout>\n <SecurityContainer>\n {/* container only: LoginForm renders the real <form>, and a <form>\n cannot be a descendant of another <form> */}\n <SecurityForm as=\"div\">\n <LoginFormHeader />\n <LoginForm\n errors={errors}\n handleSubmit={handleSubmit}\n isSubmitting={isSubmitting}\n isValid={isValid}\n onSubmit={onSubmit}\n register={register}\n />\n {createUserEnabled && (\n <Typography align=\"center\" sx={{ mt: 2, fontSize: 12 }} variant=\"body2\">\n {t('security.login.noUserQuestion')}\n <Link component={RouterLink} sx={{ ml: 1 }} to={addUserLink}>\n {t('security.login.createUser')}\n </Link>\n </Typography>\n )}\n </SecurityForm>\n </SecurityContainer>\n </SecurityLayout>\n );\n};\n\nexport default Login;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,IAAM,gBAAgB,iBAAiB;AACvC,IAAM,WAAW,mBAAmB,cAAc,IAAI;AAEtD,IAAM,cAAwB;CAC5B,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,iBAAiB,QAAQ;CACjC,MAAM,EAAE,SAAS,qBAAqB,QAAQ;CAC9C,MAAM,oBAAoB,eAAe,OAAO;CAChD,MAAM,cAAcA,QAAM,YAAY,OAAO,WAAW,OAAO;CAE/D,MAAM,EAAE,YAAY,gBAA2B,UAAU,MAAM,MAAM;CAOrE,MAAM,EACJ,UACA,cACA,UACA,WAAW,EAAE,SAAS,cAAc,aATzB,QAAyB;EACpC,MAAM;EACN,UAAU,YAAY,WAAW;CACnC,CAOI;CAEJ,MAAM,cAAc,YAClB,OAAO,SAAiD;EACtD,OAAO,MAAM,QAAQ,IAAI;CAC3B,GACA,CAAC,OAAO,CACV;CAEA,MAAM,YAAY,kBAAkB;EAClC,SAAS,GAAGA,QAAM,QAAQ,eAAe,YAAY,OAAO;CAC9D,GAAG,CAAC,QAAQ,CAAC;CAIb,MAAM,WAAW,OAAO,KAAK;CAE7B,MAAM,WAAW,YACf,OAAO,SAA0B;EAC/B,IAAI,SAAS,SACX;EAEF,SAAS,UAAU;EACnB,IAAI;GACF,MAAM,SAAS,MAAM,cAAc,IAAI;GACvC,IAAI,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,OAAO,OAEzC,MAAM,IAAI,MAAM,qCAAqC;GAEvD,SAAS,OAAO,UAAU,OAAO,KAAK;GAEtC,eAAe;IAAE,UAAU,OAAO;IAAU,OAAO,OAAO;GAAM,CAAC;GACjE,UAAU;EACZ,SAAS,KAAU;GAGjB,MAAM,UACJ,KAAK,SAAS,MACV,EAAE,oCAAoC,IACtC,iBAAiB,KAAK,EAAE,gCAAgC,CAAC;GAC/D,SAAS,QAAQ;IAAE,MAAM;IAAU;GAAQ,CAAC;EAC9C,UAAU;GACR,SAAS,UAAU;EACrB;CACF,GACA;EAAC;EAAa;EAAU;EAAc;EAAW;CAAC,CACpD;CAEA,OAAO,CAAC,OACN,oBAAC,UAAD,CAAW,CAAA,IAEX,oBAAC,gBAAD,EAAA,UACE,oBAAC,mBAAD,EAAA,UAGE,qBAAC,cAAD;EAAc,IAAG;EAAjB,UAAA;GACE,oBAAC,iBAAD,CAAkB,CAAA;GAClB,oBAAC,WAAD;IACU;IACM;IACA;IACL;IACC;IACA;GACX,CAAA;GACA,qBACC,qBAAC,YAAD;IAAY,OAAM;IAAS,IAAI;KAAE,IAAI;KAAG,UAAU;IAAG;IAAG,SAAQ;IAAhE,UAAA,CACG,EAAE,+BAA+B,GAClC,oBAACC,QAAD;KAAM,WAAWC;KAAY,IAAI,EAAE,IAAI,EAAE;KAAG,IAAI;KAC7C,UAAA,EAAE,2BAA2B;IAC1B,CAAA,CACI;;EAEF;CACG,CAAA,EAAA,CAAA,EACL,CAAA;AAEpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Success.js","names":[],"sources":["../../../src/sections/Security/Success.tsx"],"sourcesContent":["import CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport { Button, Typography } from '@mui/material';\nimport React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useLocation, useNavigate } from 'react-router';\n\nimport SecurityLayout from '../../layouts/Security/Dialog';\nimport { SecurityContainer, SecurityForm } from './styles';\n\nexport enum MessageType {\n Login = 'Login',\n ChangePassword = 'ChangePassword',\n AddUser = 'AddUser',\n Success = 'Success',\n}\n\nconst Success: React.FC = () => {\n const { t } = useTranslation();\n const location = useLocation();\n const navigate = useNavigate();\n const queryParams = new URLSearchParams(location.search);\n const messageType = queryParams.get('messageType') as MessageType;\n const message = !messageType\n ? t('security.success.messageSuccess')\n : t(`security.success.message${messageType}`);\n\n const handleClose = () => {\n // navigate() keeps the router basename; window.location.href = '/' left the\n // registry entirely when it is served under a sub-path\n navigate('/');\n };\n\n return (\n <SecurityLayout>\n <SecurityContainer>\n {/* container only: this page has no form to submit */}\n <SecurityForm as=\"div\">\n <CheckCircleIcon color=\"success\" />\n <Typography component=\"h1\" gutterBottom={true} variant=\"h4\">\n {t('security.success.title')}\n </Typography>\n <Typography color=\"text.secondary\" paragraph={true} variant=\"body1\">\n {message}\n </Typography>\n <Button color=\"primary\" onClick={handleClose} sx={{ mt: 2 }} variant=\"contained\">\n {t('security.success.submit')}\n </Button>\n </SecurityForm>\n </SecurityContainer>\n </SecurityLayout>\n );\n};\n\nexport default Success;\n"],"mappings":";;;;;;;;;;;;AASA,IAAY,cAAL,yBAAA,aAAA;CACL,YAAA,WAAA;CACA,YAAA,oBAAA;CACA,YAAA,aAAA;CACA,YAAA,aAAA;;AACF,EAAA,CAAA,CAAA;AAEA,IAAM,gBAA0B;CAC9B,MAAM,EAAE,OAAA,
|
|
1
|
+
{"version":3,"file":"Success.js","names":["useTranslation","useLocation","useNavigate","SecurityLayout","SecurityContainer","SecurityForm","CheckCircleIcon","Typography","Button"],"sources":["../../../src/sections/Security/Success.tsx"],"sourcesContent":["import CheckCircleIcon from '@mui/icons-material/CheckCircle';\nimport { Button, Typography } from '@mui/material';\nimport React from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useLocation, useNavigate } from 'react-router';\n\nimport SecurityLayout from '../../layouts/Security/Dialog';\nimport { SecurityContainer, SecurityForm } from './styles';\n\nexport enum MessageType {\n Login = 'Login',\n ChangePassword = 'ChangePassword',\n AddUser = 'AddUser',\n Success = 'Success',\n}\n\nconst Success: React.FC = () => {\n const { t } = useTranslation();\n const location = useLocation();\n const navigate = useNavigate();\n const queryParams = new URLSearchParams(location.search);\n const messageType = queryParams.get('messageType') as MessageType;\n const message = !messageType\n ? t('security.success.messageSuccess')\n : t(`security.success.message${messageType}`);\n\n const handleClose = () => {\n // navigate() keeps the router basename; window.location.href = '/' left the\n // registry entirely when it is served under a sub-path\n navigate('/');\n };\n\n return (\n <SecurityLayout>\n <SecurityContainer>\n {/* container only: this page has no form to submit */}\n <SecurityForm as=\"div\">\n <CheckCircleIcon color=\"success\" />\n <Typography component=\"h1\" gutterBottom={true} variant=\"h4\">\n {t('security.success.title')}\n </Typography>\n <Typography color=\"text.secondary\" paragraph={true} variant=\"body1\">\n {message}\n </Typography>\n <Button color=\"primary\" onClick={handleClose} sx={{ mt: 2 }} variant=\"contained\">\n {t('security.success.submit')}\n </Button>\n </SecurityForm>\n </SecurityContainer>\n </SecurityLayout>\n );\n};\n\nexport default Success;\n"],"mappings":";;;;;;;;;;;;AASA,IAAY,cAAL,yBAAA,aAAA;CACL,YAAA,WAAA;CACA,YAAA,oBAAA;CACA,YAAA,aAAA;CACA,YAAA,aAAA;;AACF,EAAA,CAAA,CAAA;AAEA,IAAM,gBAA0B;CAC9B,MAAM,EAAE,OAAA,GAAMA,cAAAA,eAAAA,CAAe;CAC7B,MAAM,YAAA,GAAWC,aAAAA,YAAAA,CAAY;CAC7B,MAAM,YAAA,GAAWC,aAAAA,YAAAA,CAAY;CAE7B,MAAM,cAAc,IADI,gBAAgB,SAAS,MAC7B,CAAA,CAAY,IAAI,aAAa;CACjD,MAAM,UAAU,CAAC,cACb,EAAE,iCAAiC,IACnC,EAAE,2BAA2B,aAAa;CAE9C,MAAM,oBAAoB;EAGxB,SAAS,GAAG;CACd;CAEA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,eAAAA,SAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,eAAAA,mBAAD,EAAA,UAEE,iBAAA,GAAA,kBAAA,KAAA,CAACC,eAAAA,cAAD;EAAc,IAAG;EAAjB,UAAA;GACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,gCAAAA,SAAD,EAAiB,OAAM,UAAW,CAAA;GAClC,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,YAAD;IAAY,WAAU;IAAK,cAAc;IAAM,SAAQ;IACpD,UAAA,EAAE,wBAAwB;GACjB,CAAA;GACZ,iBAAA,GAAA,kBAAA,IAAA,CAACA,cAAAA,YAAD;IAAY,OAAM;IAAiB,WAAW;IAAM,SAAQ;IACzD,UAAA;GACS,CAAA;GACZ,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,QAAD;IAAQ,OAAM;IAAU,SAAS;IAAa,IAAI,EAAE,IAAI,EAAE;IAAG,SAAQ;IAClE,UAAA,EAAE,yBAAyB;GACtB,CAAA;EACI;CACG,CAAA,EAAA,CAAA,EACL,CAAA;AAEpB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"styles.js","names":[],"sources":["../../../src/sections/Security/styles.ts"],"sourcesContent":["import { Box, TextField } from '@mui/material';\nimport { styled } from '@mui/material/styles';\n\nexport const SecurityContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100%',\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n}));\n\nexport const SecurityForm = styled('form')(({ theme }) => ({\n width: '100%',\n maxWidth: 420,\n padding: theme.spacing(4),\n backgroundColor: theme.palette.background.paper,\n borderRadius: theme.shape.borderRadius,\n boxShadow: theme.shadows[3],\n}));\n\nexport const SecurityTextField = styled(TextField)(({ theme }) => ({\n width: '100%',\n marginTop: theme.spacing(1),\n marginBottom: theme.spacing(1),\n}));\n"],"mappings":";;;AAGA,IAAa,qBAAA,
|
|
1
|
+
{"version":3,"file":"styles.js","names":["styled","Box","TextField"],"sources":["../../../src/sections/Security/styles.ts"],"sourcesContent":["import { Box, TextField } from '@mui/material';\nimport { styled } from '@mui/material/styles';\n\nexport const SecurityContainer = styled(Box)(({ theme }) => ({\n display: 'flex',\n flexDirection: 'column',\n alignItems: 'center',\n justifyContent: 'center',\n minHeight: '100%',\n padding: theme.spacing(2),\n backgroundColor: theme.palette.background.default,\n}));\n\nexport const SecurityForm = styled('form')(({ theme }) => ({\n width: '100%',\n maxWidth: 420,\n padding: theme.spacing(4),\n backgroundColor: theme.palette.background.paper,\n borderRadius: theme.shape.borderRadius,\n boxShadow: theme.shadows[3],\n}));\n\nexport const SecurityTextField = styled(TextField)(({ theme }) => ({\n width: '100%',\n marginTop: theme.spacing(1),\n marginBottom: theme.spacing(1),\n}));\n"],"mappings":";;;AAGA,IAAa,qBAAA,GAAoBA,qBAAAA,OAAAA,CAAOC,cAAAA,GAAG,CAAC,EAAE,EAAE,aAAa;CAC3D,SAAS;CACT,eAAe;CACf,YAAY;CACZ,gBAAgB;CAChB,WAAW;CACX,SAAS,MAAM,QAAQ,CAAC;CACxB,iBAAiB,MAAM,QAAQ,WAAW;AAC5C,EAAE;AAEF,IAAa,gBAAA,GAAeD,qBAAAA,OAAAA,CAAO,MAAM,CAAC,EAAE,EAAE,aAAa;CACzD,OAAO;CACP,UAAU;CACV,SAAS,MAAM,QAAQ,CAAC;CACxB,iBAAiB,MAAM,QAAQ,WAAW;CAC1C,cAAc,MAAM,MAAM;CAC1B,WAAW,MAAM,QAAQ;AAC3B,EAAE;AAEF,IAAa,qBAAA,GAAoBA,qBAAAA,OAAAA,CAAOE,cAAAA,SAAS,CAAC,EAAE,EAAE,aAAa;CACjE,OAAO;CACP,WAAW,MAAM,QAAQ,CAAC;CAC1B,cAAc,MAAM,QAAQ,CAAC;AAC/B,EAAE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../src/sections/Security/utils.ts"],"sourcesContent":["import { isEmpty } from 'lodash-es';\n\nimport { Route } from '../../utils';\n\nexport interface SecurityUrlParams {\n readonly next: string;\n readonly user: string;\n}\n\nexport interface LocationLike {\n readonly search: string;\n}\n\nexport function getSecurityUrlParams(location: LocationLike): SecurityUrlParams {\n const queryParams = new URLSearchParams(location.search);\n\n // Get next parameter to match v1 URLs\n let next = queryParams.get('next') || '';\n if (next.length > 0 && (!next.startsWith(Route.LOGIN_API) || !next.match(/\\/[-0-9a-f]{36}$/))) {\n next = '';\n }\n\n // Ignore user parameter if it does not match expected format (prevent XSS attacks)\n let user = queryParams.get('user') || '';\n if (user.length > 0 && !user.match(/^[a-zA-Z0-9-_]{1,255}$/)) {\n user = '';\n }\n\n return { next, user };\n}\n\nexport interface TranslationFunction {\n (key: string, options?: Record<string, unknown>): string;\n}\n\nexport interface DispatchAction {\n (payload: { type: string; description: string }): void;\n}\n\nexport const validateCredentials = (\n username: string,\n password: string,\n t: TranslationFunction,\n dispatchAction: DispatchAction\n): boolean => {\n if (!username || !password || isEmpty(username) || isEmpty(password)) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-or-password-cant-be-empty'),\n });\n return false;\n }\n\n if (username.length < 2) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-min-length', { length: 2 }),\n });\n return false;\n }\n\n // Validate username contains only url-safe characters\n if (!username.match(/^[-a-zA-Z0-9_.!~*'()@]+$/)) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-must-be-url-safe'),\n });\n return false;\n }\n\n // Note: password rules should be checked in auth plugin\n return true;\n};\n"],"mappings":";;;;AAaA,SAAgB,qBAAqB,UAA2C;CAC9E,MAAM,cAAc,IAAI,gBAAgB,SAAS,MAAM;CAGvD,IAAI,OAAO,YAAY,IAAI,MAAM,KAAK;CACtC,IAAI,KAAK,SAAS,MAAM,CAAC,KAAK,
|
|
1
|
+
{"version":3,"file":"utils.js","names":["Route"],"sources":["../../../src/sections/Security/utils.ts"],"sourcesContent":["import { isEmpty } from 'lodash-es';\n\nimport { Route } from '../../utils';\n\nexport interface SecurityUrlParams {\n readonly next: string;\n readonly user: string;\n}\n\nexport interface LocationLike {\n readonly search: string;\n}\n\nexport function getSecurityUrlParams(location: LocationLike): SecurityUrlParams {\n const queryParams = new URLSearchParams(location.search);\n\n // Get next parameter to match v1 URLs\n let next = queryParams.get('next') || '';\n if (next.length > 0 && (!next.startsWith(Route.LOGIN_API) || !next.match(/\\/[-0-9a-f]{36}$/))) {\n next = '';\n }\n\n // Ignore user parameter if it does not match expected format (prevent XSS attacks)\n let user = queryParams.get('user') || '';\n if (user.length > 0 && !user.match(/^[a-zA-Z0-9-_]{1,255}$/)) {\n user = '';\n }\n\n return { next, user };\n}\n\nexport interface TranslationFunction {\n (key: string, options?: Record<string, unknown>): string;\n}\n\nexport interface DispatchAction {\n (payload: { type: string; description: string }): void;\n}\n\nexport const validateCredentials = (\n username: string,\n password: string,\n t: TranslationFunction,\n dispatchAction: DispatchAction\n): boolean => {\n if (!username || !password || isEmpty(username) || isEmpty(password)) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-or-password-cant-be-empty'),\n });\n return false;\n }\n\n if (username.length < 2) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-min-length', { length: 2 }),\n });\n return false;\n }\n\n // Validate username contains only url-safe characters\n if (!username.match(/^[-a-zA-Z0-9_.!~*'()@]+$/)) {\n dispatchAction({\n type: 'error',\n description: t('security.error.username-must-be-url-safe'),\n });\n return false;\n }\n\n // Note: password rules should be checked in auth plugin\n return true;\n};\n"],"mappings":";;;;AAaA,SAAgB,qBAAqB,UAA2C;CAC9E,MAAM,cAAc,IAAI,gBAAgB,SAAS,MAAM;CAGvD,IAAI,OAAO,YAAY,IAAI,MAAM,KAAK;CACtC,IAAI,KAAK,SAAS,MAAM,CAAC,KAAK,WAAWA,eAAAA,MAAM,SAAS,KAAK,CAAC,KAAK,MAAM,kBAAkB,IACzF,OAAO;CAIT,IAAI,OAAO,YAAY,IAAI,MAAM,KAAK;CACtC,IAAI,KAAK,SAAS,KAAK,CAAC,KAAK,MAAM,wBAAwB,GACzD,OAAO;CAGT,OAAO;EAAE;EAAM;CAAK;AACtB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.js","names":[],"sources":["../../../src/sections/SideBar/Sidebar.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport React from 'react';\n\nimport ActionBar from '../../components/ActionBar';\nimport Author from '../../components/Author';\nimport Developers, { DeveloperType } from '../../components/Developers';\nimport Dist from '../../components/Distribution';\nimport Engines from '../../components/Engines';\nimport FundButton from '../../components/FundButton';\nimport Install from '../../components/Install';\nimport Keywords from '../../components/Keywords';\nimport Repository from '../../components/Repository';\nimport SideBarTitle from '../../components/SideBarTitle';\nimport { DownloadProvider, useConfig, useVersion } from '../../providers';\nimport type { ModuleType, PackageMetaInterface } from '../../types/packageMeta';\n\nconst getModuleTypes = (manifest: PackageMetaInterface) => {\n if (!manifest.latest) return [];\n\n const moduleTypes: ModuleType[] = [manifest.latest.type || 'commonjs'];\n\n if (manifest.latest.main) {\n moduleTypes.push('commonjs');\n }\n\n return Array.from(new Set(moduleTypes));\n};\n\nconst DetailSidebar: React.FC = () => {\n const { packageMeta, packageName, packageVersion } = useVersion();\n\n const { configOptions } = useConfig();\n const version = packageVersion || packageMeta?.latest.version || '';\n const time = packageMeta?.time ? packageMeta.time[version] : '';\n if (!packageMeta || !packageName) {\n return null;\n }\n\n return (\n <Paper data-testid=\"sidebar\" sx={{ position: 'sticky', top: 0, p: 2, ml: { xs: 0, md: 2 } }}>\n <SideBarTitle\n description={packageMeta.latest?.description}\n hasTypes={typeof packageMeta.latest?.types === 'string'}\n isLatest={typeof packageVersion === 'undefined'}\n moduleTypes={getModuleTypes(packageMeta)}\n packageName={packageName}\n time={time}\n version={version}\n />\n <DownloadProvider>\n <ActionBar\n packageMeta={packageMeta}\n showDownloadTarball={configOptions.showDownloadTarball}\n showRaw={configOptions.showRaw}\n />\n </DownloadProvider>\n <FundButton packageMeta={packageMeta} />\n <Install configOptions={configOptions} packageMeta={packageMeta} packageName={packageName} />\n <Repository packageMeta={packageMeta} />\n <Engines packageMeta={packageMeta} />\n <Dist packageMeta={packageMeta} />\n <Keywords packageMeta={packageMeta} />\n <Author packageMeta={packageMeta} />\n <Developers packageMeta={packageMeta} type={DeveloperType.MAINTAINERS} />\n <Developers packageMeta={packageMeta} type={DeveloperType.CONTRIBUTORS} />\n </Paper>\n );\n};\n\nexport default DetailSidebar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,IAAM,kBAAkB,aAAmC;CACzD,IAAI,CAAC,SAAS,QAAQ,OAAO,CAAC;CAE9B,MAAM,cAA4B,CAAC,SAAS,OAAO,QAAQ,UAAU;CAErE,IAAI,SAAS,OAAO,MAClB,YAAY,KAAK,UAAU;CAG7B,OAAO,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC;AACxC;AAEA,IAAM,sBAAgC;CACpC,MAAM,EAAE,aAAa,aAAa,
|
|
1
|
+
{"version":3,"file":"Sidebar.js","names":["useVersion","useConfig","Paper","SideBarTitle","DownloadProvider","ActionBar","FundButton","Install","Repository","Engines","Dist","Keywords","Author","Developers","DeveloperType"],"sources":["../../../src/sections/SideBar/Sidebar.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport React from 'react';\n\nimport ActionBar from '../../components/ActionBar';\nimport Author from '../../components/Author';\nimport Developers, { DeveloperType } from '../../components/Developers';\nimport Dist from '../../components/Distribution';\nimport Engines from '../../components/Engines';\nimport FundButton from '../../components/FundButton';\nimport Install from '../../components/Install';\nimport Keywords from '../../components/Keywords';\nimport Repository from '../../components/Repository';\nimport SideBarTitle from '../../components/SideBarTitle';\nimport { DownloadProvider, useConfig, useVersion } from '../../providers';\nimport type { ModuleType, PackageMetaInterface } from '../../types/packageMeta';\n\nconst getModuleTypes = (manifest: PackageMetaInterface) => {\n if (!manifest.latest) return [];\n\n const moduleTypes: ModuleType[] = [manifest.latest.type || 'commonjs'];\n\n if (manifest.latest.main) {\n moduleTypes.push('commonjs');\n }\n\n return Array.from(new Set(moduleTypes));\n};\n\nconst DetailSidebar: React.FC = () => {\n const { packageMeta, packageName, packageVersion } = useVersion();\n\n const { configOptions } = useConfig();\n const version = packageVersion || packageMeta?.latest.version || '';\n const time = packageMeta?.time ? packageMeta.time[version] : '';\n if (!packageMeta || !packageName) {\n return null;\n }\n\n return (\n <Paper data-testid=\"sidebar\" sx={{ position: 'sticky', top: 0, p: 2, ml: { xs: 0, md: 2 } }}>\n <SideBarTitle\n description={packageMeta.latest?.description}\n hasTypes={typeof packageMeta.latest?.types === 'string'}\n isLatest={typeof packageVersion === 'undefined'}\n moduleTypes={getModuleTypes(packageMeta)}\n packageName={packageName}\n time={time}\n version={version}\n />\n <DownloadProvider>\n <ActionBar\n packageMeta={packageMeta}\n showDownloadTarball={configOptions.showDownloadTarball}\n showRaw={configOptions.showRaw}\n />\n </DownloadProvider>\n <FundButton packageMeta={packageMeta} />\n <Install configOptions={configOptions} packageMeta={packageMeta} packageName={packageName} />\n <Repository packageMeta={packageMeta} />\n <Engines packageMeta={packageMeta} />\n <Dist packageMeta={packageMeta} />\n <Keywords packageMeta={packageMeta} />\n <Author packageMeta={packageMeta} />\n <Developers packageMeta={packageMeta} type={DeveloperType.MAINTAINERS} />\n <Developers packageMeta={packageMeta} type={DeveloperType.CONTRIBUTORS} />\n </Paper>\n );\n};\n\nexport default DetailSidebar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,IAAM,kBAAkB,aAAmC;CACzD,IAAI,CAAC,SAAS,QAAQ,OAAO,CAAC;CAE9B,MAAM,cAA4B,CAAC,SAAS,OAAO,QAAQ,UAAU;CAErE,IAAI,SAAS,OAAO,MAClB,YAAY,KAAK,UAAU;CAG7B,OAAO,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC;AACxC;AAEA,IAAM,sBAAgC;CACpC,MAAM,EAAE,aAAa,aAAa,mBAAmBA,wBAAAA,WAAW;CAEhE,MAAM,EAAE,kBAAkBC,iCAAAA,UAAU;CACpC,MAAM,UAAU,kBAAkB,aAAa,OAAO,WAAW;CACjE,MAAM,OAAO,aAAa,OAAO,YAAY,KAAK,WAAW;CAC7D,IAAI,CAAC,eAAe,CAAC,aACnB,OAAO;CAGT,OACE,iBAAA,GAAA,kBAAA,KAAA,CAACC,oBAAAA,SAAD;EAAO,eAAY;EAAU,IAAI;GAAE,UAAU;GAAU,KAAK;GAAG,GAAG;GAAG,IAAI;IAAE,IAAI;IAAG,IAAI;GAAE;EAAE;EAA1F,UAAA;GACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,qBAAAA,SAAD;IACE,aAAa,YAAY,QAAQ;IACjC,UAAU,OAAO,YAAY,QAAQ,UAAU;IAC/C,UAAU,OAAO,mBAAmB;IACpC,aAAa,eAAe,WAAW;IAC1B;IACP;IACG;GACV,CAAA;GACD,iBAAA,GAAA,kBAAA,IAAA,CAACC,yBAAAA,kBAAD,EAAA,UACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,kBAAAA,SAAD;IACe;IACb,qBAAqB,cAAc;IACnC,SAAS,cAAc;GACxB,CAAA,EACe,CAAA;GAClB,iBAAA,GAAA,kBAAA,IAAA,CAACC,mBAAAA,SAAD,EAAyB,YAAc,CAAA;GACvC,iBAAA,GAAA,kBAAA,IAAA,CAACC,gBAAAA,SAAD;IAAwB;IAA4B;IAA0B;GAAc,CAAA;GAC5F,iBAAA,GAAA,kBAAA,IAAA,CAACC,mBAAAA,SAAD,EAAyB,YAAc,CAAA;GACvC,iBAAA,GAAA,kBAAA,IAAA,CAACC,gBAAAA,SAAD,EAAsB,YAAc,CAAA;GACpC,iBAAA,GAAA,kBAAA,IAAA,CAACC,aAAAA,SAAD,EAAmB,YAAc,CAAA;GACjC,iBAAA,GAAA,kBAAA,IAAA,CAACC,iBAAAA,SAAD,EAAuB,YAAc,CAAA;GACrC,iBAAA,GAAA,kBAAA,IAAA,CAACC,eAAAA,SAAD,EAAqB,YAAc,CAAA;GACnC,iBAAA,GAAA,kBAAA,IAAA,CAACC,mBAAAA,SAAD;IAAyB;IAAa,MAAMC,sBAAAA,cAAc;GAAc,CAAA;GACxE,iBAAA,GAAA,kBAAA,IAAA,CAACD,mBAAAA,SAAD;IAAyB;IAAa,MAAMC,sBAAAA,cAAc;GAAe,CAAA;EACpE;;AAEX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sidebar.mjs","names":[],"sources":["../../../src/sections/SideBar/Sidebar.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport React from 'react';\n\nimport ActionBar from '../../components/ActionBar';\nimport Author from '../../components/Author';\nimport Developers, { DeveloperType } from '../../components/Developers';\nimport Dist from '../../components/Distribution';\nimport Engines from '../../components/Engines';\nimport FundButton from '../../components/FundButton';\nimport Install from '../../components/Install';\nimport Keywords from '../../components/Keywords';\nimport Repository from '../../components/Repository';\nimport SideBarTitle from '../../components/SideBarTitle';\nimport { DownloadProvider, useConfig, useVersion } from '../../providers';\nimport type { ModuleType, PackageMetaInterface } from '../../types/packageMeta';\n\nconst getModuleTypes = (manifest: PackageMetaInterface) => {\n if (!manifest.latest) return [];\n\n const moduleTypes: ModuleType[] = [manifest.latest.type || 'commonjs'];\n\n if (manifest.latest.main) {\n moduleTypes.push('commonjs');\n }\n\n return Array.from(new Set(moduleTypes));\n};\n\nconst DetailSidebar: React.FC = () => {\n const { packageMeta, packageName, packageVersion } = useVersion();\n\n const { configOptions } = useConfig();\n const version = packageVersion || packageMeta?.latest.version || '';\n const time = packageMeta?.time ? packageMeta.time[version] : '';\n if (!packageMeta || !packageName) {\n return null;\n }\n\n return (\n <Paper data-testid=\"sidebar\" sx={{ position: 'sticky', top: 0, p: 2, ml: { xs: 0, md: 2 } }}>\n <SideBarTitle\n description={packageMeta.latest?.description}\n hasTypes={typeof packageMeta.latest?.types === 'string'}\n isLatest={typeof packageVersion === 'undefined'}\n moduleTypes={getModuleTypes(packageMeta)}\n packageName={packageName}\n time={time}\n version={version}\n />\n <DownloadProvider>\n <ActionBar\n packageMeta={packageMeta}\n showDownloadTarball={configOptions.showDownloadTarball}\n showRaw={configOptions.showRaw}\n />\n </DownloadProvider>\n <FundButton packageMeta={packageMeta} />\n <Install configOptions={configOptions} packageMeta={packageMeta} packageName={packageName} />\n <Repository packageMeta={packageMeta} />\n <Engines packageMeta={packageMeta} />\n <Dist packageMeta={packageMeta} />\n <Keywords packageMeta={packageMeta} />\n <Author packageMeta={packageMeta} />\n <Developers packageMeta={packageMeta} type={DeveloperType.MAINTAINERS} />\n <Developers packageMeta={packageMeta} type={DeveloperType.CONTRIBUTORS} />\n </Paper>\n );\n};\n\nexport default DetailSidebar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,IAAM,kBAAkB,aAAmC;CACzD,IAAI,CAAC,SAAS,QAAQ,OAAO,CAAC;CAE9B,MAAM,cAA4B,CAAC,SAAS,OAAO,QAAQ,UAAU;CAErE,IAAI,SAAS,OAAO,MAClB,YAAY,KAAK,UAAU;CAG7B,OAAO,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC;AACxC;AAEA,IAAM,sBAAgC;CACpC,MAAM,EAAE,aAAa,aAAa,mBAAmB,WAAW;CAEhE,MAAM,EAAE,kBAAkB,UAAU;CACpC,MAAM,UAAU,kBAAkB,aAAa,OAAO,WAAW;CACjE,MAAM,OAAO,aAAa,OAAO,YAAY,KAAK,WAAW;CAC7D,IAAI,CAAC,eAAe,CAAC,aACnB,OAAO;CAGT,OACE,qBAAC,OAAD;EAAO,eAAY;EAAU,IAAI;GAAE,UAAU;GAAU,KAAK;GAAG,GAAG;GAAG,IAAI;IAAE,IAAI;IAAG,IAAI;GAAE;EAAE;EAA1F,UAAA;GACE,
|
|
1
|
+
{"version":3,"file":"Sidebar.mjs","names":["SideBarTitle","Engines"],"sources":["../../../src/sections/SideBar/Sidebar.tsx"],"sourcesContent":["import Paper from '@mui/material/Paper';\nimport React from 'react';\n\nimport ActionBar from '../../components/ActionBar';\nimport Author from '../../components/Author';\nimport Developers, { DeveloperType } from '../../components/Developers';\nimport Dist from '../../components/Distribution';\nimport Engines from '../../components/Engines';\nimport FundButton from '../../components/FundButton';\nimport Install from '../../components/Install';\nimport Keywords from '../../components/Keywords';\nimport Repository from '../../components/Repository';\nimport SideBarTitle from '../../components/SideBarTitle';\nimport { DownloadProvider, useConfig, useVersion } from '../../providers';\nimport type { ModuleType, PackageMetaInterface } from '../../types/packageMeta';\n\nconst getModuleTypes = (manifest: PackageMetaInterface) => {\n if (!manifest.latest) return [];\n\n const moduleTypes: ModuleType[] = [manifest.latest.type || 'commonjs'];\n\n if (manifest.latest.main) {\n moduleTypes.push('commonjs');\n }\n\n return Array.from(new Set(moduleTypes));\n};\n\nconst DetailSidebar: React.FC = () => {\n const { packageMeta, packageName, packageVersion } = useVersion();\n\n const { configOptions } = useConfig();\n const version = packageVersion || packageMeta?.latest.version || '';\n const time = packageMeta?.time ? packageMeta.time[version] : '';\n if (!packageMeta || !packageName) {\n return null;\n }\n\n return (\n <Paper data-testid=\"sidebar\" sx={{ position: 'sticky', top: 0, p: 2, ml: { xs: 0, md: 2 } }}>\n <SideBarTitle\n description={packageMeta.latest?.description}\n hasTypes={typeof packageMeta.latest?.types === 'string'}\n isLatest={typeof packageVersion === 'undefined'}\n moduleTypes={getModuleTypes(packageMeta)}\n packageName={packageName}\n time={time}\n version={version}\n />\n <DownloadProvider>\n <ActionBar\n packageMeta={packageMeta}\n showDownloadTarball={configOptions.showDownloadTarball}\n showRaw={configOptions.showRaw}\n />\n </DownloadProvider>\n <FundButton packageMeta={packageMeta} />\n <Install configOptions={configOptions} packageMeta={packageMeta} packageName={packageName} />\n <Repository packageMeta={packageMeta} />\n <Engines packageMeta={packageMeta} />\n <Dist packageMeta={packageMeta} />\n <Keywords packageMeta={packageMeta} />\n <Author packageMeta={packageMeta} />\n <Developers packageMeta={packageMeta} type={DeveloperType.MAINTAINERS} />\n <Developers packageMeta={packageMeta} type={DeveloperType.CONTRIBUTORS} />\n </Paper>\n );\n};\n\nexport default DetailSidebar;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgBA,IAAM,kBAAkB,aAAmC;CACzD,IAAI,CAAC,SAAS,QAAQ,OAAO,CAAC;CAE9B,MAAM,cAA4B,CAAC,SAAS,OAAO,QAAQ,UAAU;CAErE,IAAI,SAAS,OAAO,MAClB,YAAY,KAAK,UAAU;CAG7B,OAAO,MAAM,KAAK,IAAI,IAAI,WAAW,CAAC;AACxC;AAEA,IAAM,sBAAgC;CACpC,MAAM,EAAE,aAAa,aAAa,mBAAmB,WAAW;CAEhE,MAAM,EAAE,kBAAkB,UAAU;CACpC,MAAM,UAAU,kBAAkB,aAAa,OAAO,WAAW;CACjE,MAAM,OAAO,aAAa,OAAO,YAAY,KAAK,WAAW;CAC7D,IAAI,CAAC,eAAe,CAAC,aACnB,OAAO;CAGT,OACE,qBAAC,OAAD;EAAO,eAAY;EAAU,IAAI;GAAE,UAAU;GAAU,KAAK;GAAG,GAAG;GAAG,IAAI;IAAE,IAAI;IAAG,IAAI;GAAE;EAAE;EAA1F,UAAA;GACE,oBAACA,oBAAD;IACE,aAAa,YAAY,QAAQ;IACjC,UAAU,OAAO,YAAY,QAAQ,UAAU;IAC/C,UAAU,OAAO,mBAAmB;IACpC,aAAa,eAAe,WAAW;IAC1B;IACP;IACG;GACV,CAAA;GACD,oBAAC,kBAAD,EAAA,UACE,oBAAC,WAAD;IACe;IACb,qBAAqB,cAAc;IACnC,SAAS,cAAc;GACxB,CAAA,EACe,CAAA;GAClB,oBAAC,YAAD,EAAyB,YAAc,CAAA;GACvC,oBAAC,SAAD;IAAwB;IAA4B;IAA0B;GAAc,CAAA;GAC5F,oBAAC,YAAD,EAAyB,YAAc,CAAA;GACvC,oBAACC,QAAD,EAAsB,YAAc,CAAA;GACpC,oBAAC,MAAD,EAAmB,YAAc,CAAA;GACjC,oBAAC,UAAD,EAAuB,YAAc,CAAA;GACrC,oBAAC,QAAD,EAAqB,YAAc,CAAA;GACnC,oBAAC,YAAD;IAAyB;IAAa,MAAM,cAAc;GAAc,CAAA;GACxE,oBAAC,YAAD;IAAyB;IAAa,MAAM,cAAc;GAAe,CAAA;EACpE;;AAEX"}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { DetailSidebar as default };
|
|
1
|
+
export { default } from "./Sidebar.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageActions.js","names":[],"sources":["../../../src/sections/Stage/StageActions.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { StagePackageVersion } from './types';\nimport { approveStagedVersion, rejectStagedVersion } from './useStage';\n\ntype PendingAction = 'approve' | 'reject' | null;\n\ninterface Props {\n item: StagePackageVersion;\n /** Called after a successful approve/reject so the caller can refresh. */\n onDone: () => void;\n}\n\n/**\n * Approve / reject buttons with a confirmation step.\n *\n * Both actions are irreversible from the UI: approving publishes the version\n * for real, rejecting deletes the staged record and its tarball.\n */\nconst StageActions: React.FC<Props> = ({ item, onDone }) => {\n const { t } = useTranslation();\n const [pending, setPending] = useState<PendingAction>(null);\n const [isBusy, setIsBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const close = useCallback(() => {\n if (!isBusy) {\n setPending(null);\n setError(null);\n }\n }, [isBusy]);\n\n const confirm = useCallback(async () => {\n if (!pending) {\n return;\n }\n setIsBusy(true);\n setError(null);\n try {\n if (pending === 'approve') {\n await approveStagedVersion(item.id);\n } else {\n await rejectStagedVersion(item.id);\n }\n setPending(null);\n onDone();\n } catch (err: any) {\n // 409 here means the version got published by another route meanwhile,\n // and the staged item is deliberately kept so it can still be inspected\n setError(err?.message ?? t('stage.error.action'));\n } finally {\n setIsBusy(false);\n }\n }, [item.id, onDone, pending, t]);\n\n return (\n <>\n <Button\n color=\"primary\"\n data-testid={`stage-approve-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('approve')}\n size=\"small\"\n variant=\"outlined\"\n >\n {t('stage.action.approve')}\n </Button>\n <Button\n color=\"error\"\n data-testid={`stage-reject-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('reject')}\n size=\"small\"\n >\n {t('stage.action.reject')}\n </Button>\n\n <Dialog onClose={close} open={pending !== null}>\n <DialogTitle>\n {pending === 'approve' ? t('stage.confirm.approveTitle') : t('stage.confirm.rejectTitle')}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {pending === 'approve'\n ? t('stage.confirm.approveBody', {\n package: item.packageName,\n version: item.version,\n tag: item.tag,\n })\n : t('stage.confirm.rejectBody', {\n package: item.packageName,\n version: item.version,\n })}\n </DialogContentText>\n {error && (\n <Typography color=\"error\" marginTop={2} role=\"alert\">\n {error}\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button disabled={isBusy} onClick={close}>\n {t('button.cancel')}\n </Button>\n <Button\n color={pending === 'approve' ? 'primary' : 'error'}\n data-testid=\"stage-confirm\"\n disabled={isBusy}\n onClick={confirm}\n variant=\"contained\"\n >\n {pending === 'approve' ? t('stage.action.approve') : t('stage.action.reject')}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n\nexport default StageActions;\n"],"mappings":";;;;;;;;;;;;;;AA6BA,IAAM,gBAAiC,EAAE,MAAM,aAAa;CAC1D,MAAM,EAAE,OAAA,
|
|
1
|
+
{"version":3,"file":"StageActions.js","names":["useTranslation","useState","useCallback","approveStagedVersion","rejectStagedVersion","_Fragment","Button","Dialog","DialogTitle","DialogContent","DialogContentText","Typography","DialogActions"],"sources":["../../../src/sections/Stage/StageActions.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { StagePackageVersion } from './types';\nimport { approveStagedVersion, rejectStagedVersion } from './useStage';\n\ntype PendingAction = 'approve' | 'reject' | null;\n\ninterface Props {\n item: StagePackageVersion;\n /** Called after a successful approve/reject so the caller can refresh. */\n onDone: () => void;\n}\n\n/**\n * Approve / reject buttons with a confirmation step.\n *\n * Both actions are irreversible from the UI: approving publishes the version\n * for real, rejecting deletes the staged record and its tarball.\n */\nconst StageActions: React.FC<Props> = ({ item, onDone }) => {\n const { t } = useTranslation();\n const [pending, setPending] = useState<PendingAction>(null);\n const [isBusy, setIsBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const close = useCallback(() => {\n if (!isBusy) {\n setPending(null);\n setError(null);\n }\n }, [isBusy]);\n\n const confirm = useCallback(async () => {\n if (!pending) {\n return;\n }\n setIsBusy(true);\n setError(null);\n try {\n if (pending === 'approve') {\n await approveStagedVersion(item.id);\n } else {\n await rejectStagedVersion(item.id);\n }\n setPending(null);\n onDone();\n } catch (err: any) {\n // 409 here means the version got published by another route meanwhile,\n // and the staged item is deliberately kept so it can still be inspected\n setError(err?.message ?? t('stage.error.action'));\n } finally {\n setIsBusy(false);\n }\n }, [item.id, onDone, pending, t]);\n\n return (\n <>\n <Button\n color=\"primary\"\n data-testid={`stage-approve-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('approve')}\n size=\"small\"\n variant=\"outlined\"\n >\n {t('stage.action.approve')}\n </Button>\n <Button\n color=\"error\"\n data-testid={`stage-reject-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('reject')}\n size=\"small\"\n >\n {t('stage.action.reject')}\n </Button>\n\n <Dialog onClose={close} open={pending !== null}>\n <DialogTitle>\n {pending === 'approve' ? t('stage.confirm.approveTitle') : t('stage.confirm.rejectTitle')}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {pending === 'approve'\n ? t('stage.confirm.approveBody', {\n package: item.packageName,\n version: item.version,\n tag: item.tag,\n })\n : t('stage.confirm.rejectBody', {\n package: item.packageName,\n version: item.version,\n })}\n </DialogContentText>\n {error && (\n <Typography color=\"error\" marginTop={2} role=\"alert\">\n {error}\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button disabled={isBusy} onClick={close}>\n {t('button.cancel')}\n </Button>\n <Button\n color={pending === 'approve' ? 'primary' : 'error'}\n data-testid=\"stage-confirm\"\n disabled={isBusy}\n onClick={confirm}\n variant=\"contained\"\n >\n {pending === 'approve' ? t('stage.action.approve') : t('stage.action.reject')}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n\nexport default StageActions;\n"],"mappings":";;;;;;;;;;;;;;AA6BA,IAAM,gBAAiC,EAAE,MAAM,aAAa;CAC1D,MAAM,EAAE,OAAA,GAAMA,cAAAA,eAAAA,CAAe;CAC7B,MAAM,CAAC,SAAS,eAAA,GAAcC,MAAAA,SAAAA,CAAwB,IAAI;CAC1D,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAS,KAAK;CAC1C,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAwB,IAAI;CAEtD,MAAM,SAAA,GAAQC,MAAAA,YAAAA,OAAkB;EAC9B,IAAI,CAAC,QAAQ;GACX,WAAW,IAAI;GACf,SAAS,IAAI;EACf;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,WAAA,GAAUA,MAAAA,YAAAA,CAAY,YAAY;EACtC,IAAI,CAAC,SACH;EAEF,UAAU,IAAI;EACd,SAAS,IAAI;EACb,IAAI;GACF,IAAI,YAAY,WACd,MAAMC,iBAAAA,qBAAqB,KAAK,EAAE;QAElC,MAAMC,iBAAAA,oBAAoB,KAAK,EAAE;GAEnC,WAAW,IAAI;GACf,OAAO;EACT,SAAS,KAAU;GAGjB,SAAS,KAAK,WAAW,EAAE,oBAAoB,CAAC;EAClD,UAAU;GACR,UAAU,KAAK;EACjB;CACF,GAAG;EAAC,KAAK;EAAI;EAAQ;EAAS;CAAC,CAAC;CAEhC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAAC,kBAAAA,UAAA,EAAA,UAAA;EACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,QAAD;GACE,OAAM;GACN,eAAa,iBAAiB,KAAK;GACnC,UAAU;GACV,eAAe,WAAW,SAAS;GACnC,MAAK;GACL,SAAQ;GAEP,UAAA,EAAE,sBAAsB;EACnB,CAAA;EACR,iBAAA,GAAA,kBAAA,IAAA,CAACA,cAAAA,QAAD;GACE,OAAM;GACN,eAAa,gBAAgB,KAAK;GAClC,UAAU;GACV,eAAe,WAAW,QAAQ;GAClC,MAAK;GAEJ,UAAA,EAAE,qBAAqB;EAClB,CAAA;EAER,iBAAA,GAAA,kBAAA,KAAA,CAACC,cAAAA,QAAD;GAAQ,SAAS;GAAO,MAAM,YAAY;GAA1C,UAAA;IACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,aAAD,EAAA,UACG,YAAY,YAAY,EAAE,4BAA4B,IAAI,EAAE,2BAA2B,EAC7E,CAAA;IACb,iBAAA,GAAA,kBAAA,KAAA,CAACC,cAAAA,eAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,mBAAD,EAAA,UACG,YAAY,YACT,EAAE,6BAA6B;KAC7B,SAAS,KAAK;KACd,SAAS,KAAK;KACd,KAAK,KAAK;IACZ,CAAC,IACD,EAAE,4BAA4B;KAC5B,SAAS,KAAK;KACd,SAAS,KAAK;IAChB,CAAC,EACY,CAAA,GAClB,SACC,iBAAA,GAAA,kBAAA,IAAA,CAACC,cAAAA,YAAD;KAAY,OAAM;KAAQ,WAAW;KAAG,MAAK;KAC1C,UAAA;IACS,CAAA,CAED,EAAA,CAAA;IACf,iBAAA,GAAA,kBAAA,KAAA,CAACC,cAAAA,eAAD,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACN,cAAAA,QAAD;KAAQ,UAAU;KAAQ,SAAS;KAChC,UAAA,EAAE,eAAe;IACZ,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAACA,cAAAA,QAAD;KACE,OAAO,YAAY,YAAY,YAAY;KAC3C,eAAY;KACZ,UAAU;KACV,SAAS;KACT,SAAQ;KAEP,UAAA,YAAY,YAAY,EAAE,sBAAsB,IAAI,EAAE,qBAAqB;IACtE,CAAA,CACK,EAAA,CAAA;GACT;;CACR,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageActions.mjs","names":[],"sources":["../../../src/sections/Stage/StageActions.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { StagePackageVersion } from './types';\nimport { approveStagedVersion, rejectStagedVersion } from './useStage';\n\ntype PendingAction = 'approve' | 'reject' | null;\n\ninterface Props {\n item: StagePackageVersion;\n /** Called after a successful approve/reject so the caller can refresh. */\n onDone: () => void;\n}\n\n/**\n * Approve / reject buttons with a confirmation step.\n *\n * Both actions are irreversible from the UI: approving publishes the version\n * for real, rejecting deletes the staged record and its tarball.\n */\nconst StageActions: React.FC<Props> = ({ item, onDone }) => {\n const { t } = useTranslation();\n const [pending, setPending] = useState<PendingAction>(null);\n const [isBusy, setIsBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const close = useCallback(() => {\n if (!isBusy) {\n setPending(null);\n setError(null);\n }\n }, [isBusy]);\n\n const confirm = useCallback(async () => {\n if (!pending) {\n return;\n }\n setIsBusy(true);\n setError(null);\n try {\n if (pending === 'approve') {\n await approveStagedVersion(item.id);\n } else {\n await rejectStagedVersion(item.id);\n }\n setPending(null);\n onDone();\n } catch (err: any) {\n // 409 here means the version got published by another route meanwhile,\n // and the staged item is deliberately kept so it can still be inspected\n setError(err?.message ?? t('stage.error.action'));\n } finally {\n setIsBusy(false);\n }\n }, [item.id, onDone, pending, t]);\n\n return (\n <>\n <Button\n color=\"primary\"\n data-testid={`stage-approve-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('approve')}\n size=\"small\"\n variant=\"outlined\"\n >\n {t('stage.action.approve')}\n </Button>\n <Button\n color=\"error\"\n data-testid={`stage-reject-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('reject')}\n size=\"small\"\n >\n {t('stage.action.reject')}\n </Button>\n\n <Dialog onClose={close} open={pending !== null}>\n <DialogTitle>\n {pending === 'approve' ? t('stage.confirm.approveTitle') : t('stage.confirm.rejectTitle')}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {pending === 'approve'\n ? t('stage.confirm.approveBody', {\n package: item.packageName,\n version: item.version,\n tag: item.tag,\n })\n : t('stage.confirm.rejectBody', {\n package: item.packageName,\n version: item.version,\n })}\n </DialogContentText>\n {error && (\n <Typography color=\"error\" marginTop={2} role=\"alert\">\n {error}\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button disabled={isBusy} onClick={close}>\n {t('button.cancel')}\n </Button>\n <Button\n color={pending === 'approve' ? 'primary' : 'error'}\n data-testid=\"stage-confirm\"\n disabled={isBusy}\n onClick={confirm}\n variant=\"contained\"\n >\n {pending === 'approve' ? t('stage.action.approve') : t('stage.action.reject')}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n\nexport default StageActions;\n"],"mappings":";;;;;;;;;;;;AA6BA,IAAM,gBAAiC,EAAE,MAAM,aAAa;CAC1D,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,CAAC,SAAS,cAAc,SAAwB,IAAI;CAC1D,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CAEtD,MAAM,QAAQ,kBAAkB;EAC9B,IAAI,CAAC,QAAQ;GACX,WAAW,IAAI;GACf,SAAS,IAAI;EACf;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,UAAU,YAAY,YAAY;EACtC,IAAI,CAAC,SACH;EAEF,UAAU,IAAI;EACd,SAAS,IAAI;EACb,IAAI;GACF,IAAI,YAAY,WACd,MAAM,qBAAqB,KAAK,EAAE;QAElC,MAAM,oBAAoB,KAAK,EAAE;GAEnC,WAAW,IAAI;GACf,OAAO;EACT,SAAS,KAAU;GAGjB,SAAS,KAAK,WAAW,EAAE,oBAAoB,CAAC;EAClD,UAAU;GACR,UAAU,KAAK;EACjB;CACF,GAAG;EAAC,KAAK;EAAI;EAAQ;EAAS;CAAC,CAAC;CAEhC,OACE,
|
|
1
|
+
{"version":3,"file":"StageActions.mjs","names":["_Fragment"],"sources":["../../../src/sections/Stage/StageActions.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogActions,\n DialogContent,\n DialogContentText,\n DialogTitle,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nimport type { StagePackageVersion } from './types';\nimport { approveStagedVersion, rejectStagedVersion } from './useStage';\n\ntype PendingAction = 'approve' | 'reject' | null;\n\ninterface Props {\n item: StagePackageVersion;\n /** Called after a successful approve/reject so the caller can refresh. */\n onDone: () => void;\n}\n\n/**\n * Approve / reject buttons with a confirmation step.\n *\n * Both actions are irreversible from the UI: approving publishes the version\n * for real, rejecting deletes the staged record and its tarball.\n */\nconst StageActions: React.FC<Props> = ({ item, onDone }) => {\n const { t } = useTranslation();\n const [pending, setPending] = useState<PendingAction>(null);\n const [isBusy, setIsBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const close = useCallback(() => {\n if (!isBusy) {\n setPending(null);\n setError(null);\n }\n }, [isBusy]);\n\n const confirm = useCallback(async () => {\n if (!pending) {\n return;\n }\n setIsBusy(true);\n setError(null);\n try {\n if (pending === 'approve') {\n await approveStagedVersion(item.id);\n } else {\n await rejectStagedVersion(item.id);\n }\n setPending(null);\n onDone();\n } catch (err: any) {\n // 409 here means the version got published by another route meanwhile,\n // and the staged item is deliberately kept so it can still be inspected\n setError(err?.message ?? t('stage.error.action'));\n } finally {\n setIsBusy(false);\n }\n }, [item.id, onDone, pending, t]);\n\n return (\n <>\n <Button\n color=\"primary\"\n data-testid={`stage-approve-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('approve')}\n size=\"small\"\n variant=\"outlined\"\n >\n {t('stage.action.approve')}\n </Button>\n <Button\n color=\"error\"\n data-testid={`stage-reject-${item.id}`}\n disabled={isBusy}\n onClick={() => setPending('reject')}\n size=\"small\"\n >\n {t('stage.action.reject')}\n </Button>\n\n <Dialog onClose={close} open={pending !== null}>\n <DialogTitle>\n {pending === 'approve' ? t('stage.confirm.approveTitle') : t('stage.confirm.rejectTitle')}\n </DialogTitle>\n <DialogContent>\n <DialogContentText>\n {pending === 'approve'\n ? t('stage.confirm.approveBody', {\n package: item.packageName,\n version: item.version,\n tag: item.tag,\n })\n : t('stage.confirm.rejectBody', {\n package: item.packageName,\n version: item.version,\n })}\n </DialogContentText>\n {error && (\n <Typography color=\"error\" marginTop={2} role=\"alert\">\n {error}\n </Typography>\n )}\n </DialogContent>\n <DialogActions>\n <Button disabled={isBusy} onClick={close}>\n {t('button.cancel')}\n </Button>\n <Button\n color={pending === 'approve' ? 'primary' : 'error'}\n data-testid=\"stage-confirm\"\n disabled={isBusy}\n onClick={confirm}\n variant=\"contained\"\n >\n {pending === 'approve' ? t('stage.action.approve') : t('stage.action.reject')}\n </Button>\n </DialogActions>\n </Dialog>\n </>\n );\n};\n\nexport default StageActions;\n"],"mappings":";;;;;;;;;;;;AA6BA,IAAM,gBAAiC,EAAE,MAAM,aAAa;CAC1D,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,CAAC,SAAS,cAAc,SAAwB,IAAI;CAC1D,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAC1C,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CAEtD,MAAM,QAAQ,kBAAkB;EAC9B,IAAI,CAAC,QAAQ;GACX,WAAW,IAAI;GACf,SAAS,IAAI;EACf;CACF,GAAG,CAAC,MAAM,CAAC;CAEX,MAAM,UAAU,YAAY,YAAY;EACtC,IAAI,CAAC,SACH;EAEF,UAAU,IAAI;EACd,SAAS,IAAI;EACb,IAAI;GACF,IAAI,YAAY,WACd,MAAM,qBAAqB,KAAK,EAAE;QAElC,MAAM,oBAAoB,KAAK,EAAE;GAEnC,WAAW,IAAI;GACf,OAAO;EACT,SAAS,KAAU;GAGjB,SAAS,KAAK,WAAW,EAAE,oBAAoB,CAAC;EAClD,UAAU;GACR,UAAU,KAAK;EACjB;CACF,GAAG;EAAC,KAAK;EAAI;EAAQ;EAAS;CAAC,CAAC;CAEhC,OACE,qBAAAA,YAAA,EAAA,UAAA;EACE,oBAAC,QAAD;GACE,OAAM;GACN,eAAa,iBAAiB,KAAK;GACnC,UAAU;GACV,eAAe,WAAW,SAAS;GACnC,MAAK;GACL,SAAQ;GAEP,UAAA,EAAE,sBAAsB;EACnB,CAAA;EACR,oBAAC,QAAD;GACE,OAAM;GACN,eAAa,gBAAgB,KAAK;GAClC,UAAU;GACV,eAAe,WAAW,QAAQ;GAClC,MAAK;GAEJ,UAAA,EAAE,qBAAqB;EAClB,CAAA;EAER,qBAAC,QAAD;GAAQ,SAAS;GAAO,MAAM,YAAY;GAA1C,UAAA;IACE,oBAAC,aAAD,EAAA,UACG,YAAY,YAAY,EAAE,4BAA4B,IAAI,EAAE,2BAA2B,EAC7E,CAAA;IACb,qBAAC,eAAD,EAAA,UAAA,CACE,oBAAC,mBAAD,EAAA,UACG,YAAY,YACT,EAAE,6BAA6B;KAC7B,SAAS,KAAK;KACd,SAAS,KAAK;KACd,KAAK,KAAK;IACZ,CAAC,IACD,EAAE,4BAA4B;KAC5B,SAAS,KAAK;KACd,SAAS,KAAK;IAChB,CAAC,EACY,CAAA,GAClB,SACC,oBAAC,YAAD;KAAY,OAAM;KAAQ,WAAW;KAAG,MAAK;KAC1C,UAAA;IACS,CAAA,CAED,EAAA,CAAA;IACf,qBAAC,eAAD,EAAA,UAAA,CACE,oBAAC,QAAD;KAAQ,UAAU;KAAQ,SAAS;KAChC,UAAA,EAAE,eAAe;IACZ,CAAA,GACR,oBAAC,QAAD;KACE,OAAO,YAAY,YAAY,YAAY;KAC3C,eAAY;KACZ,UAAU;KACV,SAAS;KACT,SAAQ;KAEP,UAAA,YAAY,YAAY,EAAE,sBAAsB,IAAI,EAAE,qBAAqB;IACtE,CAAA,CACK,EAAA,CAAA;GACT;;CACR,EAAA,CAAA;AAEN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageDetail.js","names":[],"sources":["../../../src/sections/Stage/StageDetail.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate, useParams } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { downloadStagedTarball, useStageItem } from './useStage';\nimport { useRequireSession } from './useRequireSession';\n\nconst StageDetail: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { stageId } = useParams<{ stageId: string }>();\n const isLoggedIn = useRequireSession();\n const { data, error, isLoading } = useStageItem(stageId);\n\n const handleDownload = useCallback(async () => {\n if (data) {\n await downloadStagedTarball(data);\n }\n }, [data]);\n\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error || !data) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.notFound')}\n </Typography>\n </Box>\n );\n }\n\n const rows: [string, React.ReactNode][] = [\n [t('stage.column.package'), data.packageName],\n [t('stage.column.version'), data.version],\n [t('stage.column.tag'), <Chip key=\"tag\" label={data.tag} size=\"small\" />],\n [t('stage.column.stagedBy'), `${data.actor} (${data.actorType})`],\n [t('stage.column.date'), new Date(data.createdAt).toLocaleString()],\n [t('stage.column.access'), data.access],\n [t('stage.column.shasum'), data.shasum],\n ];\n\n return (\n <Box padding={2}>\n <Button onClick={() => navigate(Route.STAGE)} size=\"small\">\n {t('stage.backToList')}\n </Button>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {data.packageName}@{data.version}\n </Typography>\n\n <Paper sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table data-testid=\"stage-detail\" size=\"small\">\n <TableBody>\n {rows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\" sx={{ fontWeight: 600, width: '30%' }}>\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Paper>\n\n <Box display=\"flex\" gap={1} marginTop={2}>\n <Button onClick={handleDownload} size=\"small\" variant=\"outlined\">\n {t('stage.action.download')}\n </Button>\n <StageActions item={data} onDone={() => navigate(Route.STAGE)} />\n </Box>\n </Box>\n );\n};\n\nexport default StageDetail;\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAM,oBAA8B;CAClC,MAAM,EAAE,OAAA,
|
|
1
|
+
{"version":3,"file":"StageDetail.js","names":["useTranslation","useNavigate","useParams","useRequireSession","useStageItem","useCallback","downloadStagedTarball","Box","CircularProgress","Typography","Chip","Button","Route","Paper","Table","TableBody","TableRow","TableCell","StageActions"],"sources":["../../../src/sections/Stage/StageDetail.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate, useParams } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { downloadStagedTarball, useStageItem } from './useStage';\nimport { useRequireSession } from './useRequireSession';\n\nconst StageDetail: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { stageId } = useParams<{ stageId: string }>();\n const isLoggedIn = useRequireSession();\n const { data, error, isLoading } = useStageItem(stageId);\n\n const handleDownload = useCallback(async () => {\n if (data) {\n await downloadStagedTarball(data);\n }\n }, [data]);\n\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error || !data) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.notFound')}\n </Typography>\n </Box>\n );\n }\n\n const rows: [string, React.ReactNode][] = [\n [t('stage.column.package'), data.packageName],\n [t('stage.column.version'), data.version],\n [t('stage.column.tag'), <Chip key=\"tag\" label={data.tag} size=\"small\" />],\n [t('stage.column.stagedBy'), `${data.actor} (${data.actorType})`],\n [t('stage.column.date'), new Date(data.createdAt).toLocaleString()],\n [t('stage.column.access'), data.access],\n [t('stage.column.shasum'), data.shasum],\n ];\n\n return (\n <Box padding={2}>\n <Button onClick={() => navigate(Route.STAGE)} size=\"small\">\n {t('stage.backToList')}\n </Button>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {data.packageName}@{data.version}\n </Typography>\n\n <Paper sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table data-testid=\"stage-detail\" size=\"small\">\n <TableBody>\n {rows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\" sx={{ fontWeight: 600, width: '30%' }}>\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Paper>\n\n <Box display=\"flex\" gap={1} marginTop={2}>\n <Button onClick={handleDownload} size=\"small\" variant=\"outlined\">\n {t('stage.action.download')}\n </Button>\n <StageActions item={data} onDone={() => navigate(Route.STAGE)} />\n </Box>\n </Box>\n );\n};\n\nexport default StageDetail;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageDetail.mjs","names":[],"sources":["../../../src/sections/Stage/StageDetail.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate, useParams } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { downloadStagedTarball, useStageItem } from './useStage';\nimport { useRequireSession } from './useRequireSession';\n\nconst StageDetail: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { stageId } = useParams<{ stageId: string }>();\n const isLoggedIn = useRequireSession();\n const { data, error, isLoading } = useStageItem(stageId);\n\n const handleDownload = useCallback(async () => {\n if (data) {\n await downloadStagedTarball(data);\n }\n }, [data]);\n\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error || !data) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.notFound')}\n </Typography>\n </Box>\n );\n }\n\n const rows: [string, React.ReactNode][] = [\n [t('stage.column.package'), data.packageName],\n [t('stage.column.version'), data.version],\n [t('stage.column.tag'), <Chip key=\"tag\" label={data.tag} size=\"small\" />],\n [t('stage.column.stagedBy'), `${data.actor} (${data.actorType})`],\n [t('stage.column.date'), new Date(data.createdAt).toLocaleString()],\n [t('stage.column.access'), data.access],\n [t('stage.column.shasum'), data.shasum],\n ];\n\n return (\n <Box padding={2}>\n <Button onClick={() => navigate(Route.STAGE)} size=\"small\">\n {t('stage.backToList')}\n </Button>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {data.packageName}@{data.version}\n </Typography>\n\n <Paper sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table data-testid=\"stage-detail\" size=\"small\">\n <TableBody>\n {rows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\" sx={{ fontWeight: 600, width: '30%' }}>\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Paper>\n\n <Box display=\"flex\" gap={1} marginTop={2}>\n <Button onClick={handleDownload} size=\"small\" variant=\"outlined\">\n {t('stage.action.download')}\n </Button>\n <StageActions item={data} onDone={() => navigate(Route.STAGE)} />\n </Box>\n </Box>\n );\n};\n\nexport default StageDetail;\n"],"mappings":";;;;;;;;;;;AAqBA,IAAM,oBAA8B;CAClC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,YAAY,UAA+B;CACnD,MAAM,aAAa,kBAAkB;CACrC,MAAM,EAAE,MAAM,OAAO,cAAc,aAAa,OAAO;CAEvD,MAAM,iBAAiB,YAAY,YAAY;EAC7C,IAAI,MACF,MAAM,sBAAsB,IAAI;CAEpC,GAAG,CAAC,IAAI,CAAC;CAET,IAAI,CAAC,YACH,OAAO;CAGT,IAAI,WACF,OACE,oBAAC,KAAD;EAAK,SAAQ;EAAO,gBAAe;EAAS,SAAS;EACnD,UAAA,oBAAC,kBAAD,CAAmB,CAAA;CAChB,CAAA;CAIT,IAAI,SAAS,CAAC,MACZ,OACE,oBAAC,KAAD;EAAK,SAAS;EACZ,UAAA,oBAAC,YAAD;GAAY,OAAM;GAAQ,MAAK;GAC5B,UAAA,EAAE,sBAAsB;EACf,CAAA;CACT,CAAA;CAIT,MAAM,OAAoC;EACxC,CAAC,EAAE,sBAAsB,GAAG,KAAK,WAAW;EAC5C,CAAC,EAAE,sBAAsB,GAAG,KAAK,OAAO;EACxC,CAAC,EAAE,kBAAkB,GAAG,oBAAC,MAAD;GAAgB,OAAO,KAAK;GAAK,MAAK;EAAS,GAArC,KAAqC,CAAC;EACxE,CAAC,EAAE,uBAAuB,GAAG,GAAG,KAAK,MAAM,IAAI,KAAK,UAAU,EAAE;EAChE,CAAC,EAAE,mBAAmB,GAAG,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,eAAe,CAAC;EAClE,CAAC,EAAE,qBAAqB,GAAG,KAAK,MAAM;EACtC,CAAC,EAAE,qBAAqB,GAAG,KAAK,MAAM;CACxC;CAEA,OACE,qBAAC,KAAD;EAAK,SAAS;EAAd,UAAA;GACE,oBAAC,QAAD;IAAQ,eAAe,
|
|
1
|
+
{"version":3,"file":"StageDetail.mjs","names":["Route"],"sources":["../../../src/sections/Stage/StageDetail.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate, useParams } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { downloadStagedTarball, useStageItem } from './useStage';\nimport { useRequireSession } from './useRequireSession';\n\nconst StageDetail: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const { stageId } = useParams<{ stageId: string }>();\n const isLoggedIn = useRequireSession();\n const { data, error, isLoading } = useStageItem(stageId);\n\n const handleDownload = useCallback(async () => {\n if (data) {\n await downloadStagedTarball(data);\n }\n }, [data]);\n\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error || !data) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.notFound')}\n </Typography>\n </Box>\n );\n }\n\n const rows: [string, React.ReactNode][] = [\n [t('stage.column.package'), data.packageName],\n [t('stage.column.version'), data.version],\n [t('stage.column.tag'), <Chip key=\"tag\" label={data.tag} size=\"small\" />],\n [t('stage.column.stagedBy'), `${data.actor} (${data.actorType})`],\n [t('stage.column.date'), new Date(data.createdAt).toLocaleString()],\n [t('stage.column.access'), data.access],\n [t('stage.column.shasum'), data.shasum],\n ];\n\n return (\n <Box padding={2}>\n <Button onClick={() => navigate(Route.STAGE)} size=\"small\">\n {t('stage.backToList')}\n </Button>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {data.packageName}@{data.version}\n </Typography>\n\n <Paper sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table data-testid=\"stage-detail\" size=\"small\">\n <TableBody>\n {rows.map(([label, value]) => (\n <TableRow key={label}>\n <TableCell component=\"th\" scope=\"row\" sx={{ fontWeight: 600, width: '30%' }}>\n {label}\n </TableCell>\n <TableCell sx={{ wordBreak: 'break-all' }}>{value}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </Paper>\n\n <Box display=\"flex\" gap={1} marginTop={2}>\n <Button onClick={handleDownload} size=\"small\" variant=\"outlined\">\n {t('stage.action.download')}\n </Button>\n <StageActions item={data} onDone={() => navigate(Route.STAGE)} />\n </Box>\n </Box>\n );\n};\n\nexport default StageDetail;\n"],"mappings":";;;;;;;;;;;AAqBA,IAAM,oBAA8B;CAClC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,EAAE,YAAY,UAA+B;CACnD,MAAM,aAAa,kBAAkB;CACrC,MAAM,EAAE,MAAM,OAAO,cAAc,aAAa,OAAO;CAEvD,MAAM,iBAAiB,YAAY,YAAY;EAC7C,IAAI,MACF,MAAM,sBAAsB,IAAI;CAEpC,GAAG,CAAC,IAAI,CAAC;CAET,IAAI,CAAC,YACH,OAAO;CAGT,IAAI,WACF,OACE,oBAAC,KAAD;EAAK,SAAQ;EAAO,gBAAe;EAAS,SAAS;EACnD,UAAA,oBAAC,kBAAD,CAAmB,CAAA;CAChB,CAAA;CAIT,IAAI,SAAS,CAAC,MACZ,OACE,oBAAC,KAAD;EAAK,SAAS;EACZ,UAAA,oBAAC,YAAD;GAAY,OAAM;GAAQ,MAAK;GAC5B,UAAA,EAAE,sBAAsB;EACf,CAAA;CACT,CAAA;CAIT,MAAM,OAAoC;EACxC,CAAC,EAAE,sBAAsB,GAAG,KAAK,WAAW;EAC5C,CAAC,EAAE,sBAAsB,GAAG,KAAK,OAAO;EACxC,CAAC,EAAE,kBAAkB,GAAG,oBAAC,MAAD;GAAgB,OAAO,KAAK;GAAK,MAAK;EAAS,GAArC,KAAqC,CAAC;EACxE,CAAC,EAAE,uBAAuB,GAAG,GAAG,KAAK,MAAM,IAAI,KAAK,UAAU,EAAE;EAChE,CAAC,EAAE,mBAAmB,GAAG,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC,eAAe,CAAC;EAClE,CAAC,EAAE,qBAAqB,GAAG,KAAK,MAAM;EACtC,CAAC,EAAE,qBAAqB,GAAG,KAAK,MAAM;CACxC;CAEA,OACE,qBAAC,KAAD;EAAK,SAAS;EAAd,UAAA;GACE,oBAAC,QAAD;IAAQ,eAAe,SAASA,QAAM,KAAK;IAAG,MAAK;IAChD,UAAA,EAAE,kBAAkB;GACf,CAAA;GACR,qBAAC,YAAD;IAAY,WAAU;IAAK,cAAc;IAAM,SAAQ;IAAvD,UAAA;KACG,KAAK;KAAY;KAAE,KAAK;IACf;;GAEZ,oBAAC,OAAD;IAAO,IAAI;KAAE,WAAW;KAAG,WAAW;IAAO;IAC3C,UAAA,oBAAC,OAAD;KAAO,eAAY;KAAe,MAAK;KACrC,UAAA,oBAAC,WAAD,EAAA,UACG,KAAK,KAAK,CAAC,OAAO,WACjB,qBAAC,UAAD,EAAA,UAAA,CACE,oBAAC,WAAD;MAAW,WAAU;MAAK,OAAM;MAAM,IAAI;OAAE,YAAY;OAAK,OAAO;MAAM;MACvE,UAAA;KACQ,CAAA,GACX,oBAAC,WAAD;MAAW,IAAI,EAAE,WAAW,YAAY;MAAI,UAAA;KAAiB,CAAA,CACrD,EAAA,GALK,KAKL,CACX,EACQ,CAAA;IACN,CAAA;GACF,CAAA;GAEP,qBAAC,KAAD;IAAK,SAAQ;IAAO,KAAK;IAAG,WAAW;IAAvC,UAAA,CACE,oBAAC,QAAD;KAAQ,SAAS;KAAgB,MAAK;KAAQ,SAAQ;KACnD,UAAA,EAAE,uBAAuB;IACpB,CAAA,GACR,oBAAC,cAAD;KAAc,MAAM;KAAM,cAAc,SAASA,QAAM,KAAK;IAAI,CAAA,CAC7D;;EACF;;AAET"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageList.js","names":[],"sources":["../../../src/sections/Stage/StageList.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TablePagination,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { useRequireSession } from './useRequireSession';\nimport type { StagePackageVersion } from './types';\nimport { downloadStagedTarball, useStageList } from './useStage';\n\nconst StageList: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const isLoggedIn = useRequireSession();\n const [page, setPage] = useState(0);\n const [perPage, setPerPage] = useState(10);\n const [actionError, setActionError] = useState(false);\n const { data, error, isLoading, mutate } = useStageList(page, perPage);\n\n const handleDownload = useCallback(async (item: StagePackageVersion) => {\n try {\n setActionError(false);\n await downloadStagedTarball(item);\n } catch (err) {\n console.error('Error downloading staged tarball:', err);\n setActionError(true);\n }\n }, []);\n\n // the redirect lands on the next effect; rendering the table meanwhile would\n // only show an empty state that is about to disappear\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.list')}\n </Typography>\n </Box>\n );\n }\n\n const items = data?.items ?? [];\n\n return (\n <Box padding={2}>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {t('stage.title')}\n </Typography>\n <Typography color=\"text.secondary\" gutterBottom={true} variant=\"body2\">\n {t('stage.description')}\n </Typography>\n\n {actionError && (\n <Typography color=\"error\" role=\"alert\" variant=\"body2\">\n {t('stage.error.action')}\n </Typography>\n )}\n\n {items.length === 0 ? (\n <Typography color=\"text.secondary\" marginTop={2}>\n {t('stage.empty')}\n </Typography>\n ) : (\n <TableContainer component={Paper} sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table aria-label={t('stage.title')} data-testid=\"stage-table\" size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{t('stage.column.package')}</TableCell>\n <TableCell>{t('stage.column.version')}</TableCell>\n <TableCell>{t('stage.column.tag')}</TableCell>\n <TableCell>{t('stage.column.stagedBy')}</TableCell>\n <TableCell>{t('stage.column.date')}</TableCell>\n <TableCell align=\"right\">{t('stage.column.actions')}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {items.map((item) => (\n <TableRow data-testid={`stage-row-${item.id}`} hover={true} key={item.id}>\n <TableCell>\n <Button\n onClick={() => navigate(Route.STAGE_DETAIL.replace(':stageId', item.id))}\n size=\"small\"\n sx={{ textTransform: 'none' }}\n >\n {item.packageName}\n </Button>\n </TableCell>\n <TableCell>{item.version}</TableCell>\n <TableCell>\n <Chip label={item.tag} size=\"small\" />\n </TableCell>\n <TableCell>{item.actor}</TableCell>\n <TableCell>{new Date(item.createdAt).toLocaleString()}</TableCell>\n <TableCell align=\"right\">\n <Box alignItems=\"center\" display=\"flex\" gap={1} justifyContent=\"flex-end\">\n <Button\n data-testid={`stage-download-${item.id}`}\n onClick={() => handleDownload(item)}\n size=\"small\"\n >\n {t('stage.action.download')}\n </Button>\n <StageActions item={item} onDone={() => mutate()} />\n </Box>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <TablePagination\n component=\"div\"\n count={data?.total ?? 0}\n onPageChange={(_event, nextPage) => setPage(nextPage)}\n onRowsPerPageChange={(event) => {\n setPerPage(Number.parseInt(event.target.value, 10));\n setPage(0);\n }}\n page={page}\n rowsPerPage={perPage}\n rowsPerPageOptions={[10, 25, 50, 100]}\n />\n </TableContainer>\n )}\n </Box>\n );\n};\n\nexport default StageList;\n"],"mappings":";;;;;;;;;;;;;AAyBA,IAAM,kBAA4B;CAChC,MAAM,EAAE,OAAA,
|
|
1
|
+
{"version":3,"file":"StageList.js","names":["useTranslation","useNavigate","useRequireSession","useState","useStageList","useCallback","downloadStagedTarball","Box","CircularProgress","Typography","TableContainer","Paper","Table","TableHead","TableRow","TableCell","TableBody","Button","Route","Chip","StageActions","TablePagination"],"sources":["../../../src/sections/Stage/StageList.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TablePagination,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { useRequireSession } from './useRequireSession';\nimport type { StagePackageVersion } from './types';\nimport { downloadStagedTarball, useStageList } from './useStage';\n\nconst StageList: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const isLoggedIn = useRequireSession();\n const [page, setPage] = useState(0);\n const [perPage, setPerPage] = useState(10);\n const [actionError, setActionError] = useState(false);\n const { data, error, isLoading, mutate } = useStageList(page, perPage);\n\n const handleDownload = useCallback(async (item: StagePackageVersion) => {\n try {\n setActionError(false);\n await downloadStagedTarball(item);\n } catch (err) {\n console.error('Error downloading staged tarball:', err);\n setActionError(true);\n }\n }, []);\n\n // the redirect lands on the next effect; rendering the table meanwhile would\n // only show an empty state that is about to disappear\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.list')}\n </Typography>\n </Box>\n );\n }\n\n const items = data?.items ?? [];\n\n return (\n <Box padding={2}>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {t('stage.title')}\n </Typography>\n <Typography color=\"text.secondary\" gutterBottom={true} variant=\"body2\">\n {t('stage.description')}\n </Typography>\n\n {actionError && (\n <Typography color=\"error\" role=\"alert\" variant=\"body2\">\n {t('stage.error.action')}\n </Typography>\n )}\n\n {items.length === 0 ? (\n <Typography color=\"text.secondary\" marginTop={2}>\n {t('stage.empty')}\n </Typography>\n ) : (\n <TableContainer component={Paper} sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table aria-label={t('stage.title')} data-testid=\"stage-table\" size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{t('stage.column.package')}</TableCell>\n <TableCell>{t('stage.column.version')}</TableCell>\n <TableCell>{t('stage.column.tag')}</TableCell>\n <TableCell>{t('stage.column.stagedBy')}</TableCell>\n <TableCell>{t('stage.column.date')}</TableCell>\n <TableCell align=\"right\">{t('stage.column.actions')}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {items.map((item) => (\n <TableRow data-testid={`stage-row-${item.id}`} hover={true} key={item.id}>\n <TableCell>\n <Button\n onClick={() => navigate(Route.STAGE_DETAIL.replace(':stageId', item.id))}\n size=\"small\"\n sx={{ textTransform: 'none' }}\n >\n {item.packageName}\n </Button>\n </TableCell>\n <TableCell>{item.version}</TableCell>\n <TableCell>\n <Chip label={item.tag} size=\"small\" />\n </TableCell>\n <TableCell>{item.actor}</TableCell>\n <TableCell>{new Date(item.createdAt).toLocaleString()}</TableCell>\n <TableCell align=\"right\">\n <Box alignItems=\"center\" display=\"flex\" gap={1} justifyContent=\"flex-end\">\n <Button\n data-testid={`stage-download-${item.id}`}\n onClick={() => handleDownload(item)}\n size=\"small\"\n >\n {t('stage.action.download')}\n </Button>\n <StageActions item={item} onDone={() => mutate()} />\n </Box>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <TablePagination\n component=\"div\"\n count={data?.total ?? 0}\n onPageChange={(_event, nextPage) => setPage(nextPage)}\n onRowsPerPageChange={(event) => {\n setPerPage(Number.parseInt(event.target.value, 10));\n setPage(0);\n }}\n page={page}\n rowsPerPage={perPage}\n rowsPerPageOptions={[10, 25, 50, 100]}\n />\n </TableContainer>\n )}\n </Box>\n );\n};\n\nexport default StageList;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StageList.mjs","names":[],"sources":["../../../src/sections/Stage/StageList.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TablePagination,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { useRequireSession } from './useRequireSession';\nimport type { StagePackageVersion } from './types';\nimport { downloadStagedTarball, useStageList } from './useStage';\n\nconst StageList: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const isLoggedIn = useRequireSession();\n const [page, setPage] = useState(0);\n const [perPage, setPerPage] = useState(10);\n const [actionError, setActionError] = useState(false);\n const { data, error, isLoading, mutate } = useStageList(page, perPage);\n\n const handleDownload = useCallback(async (item: StagePackageVersion) => {\n try {\n setActionError(false);\n await downloadStagedTarball(item);\n } catch (err) {\n console.error('Error downloading staged tarball:', err);\n setActionError(true);\n }\n }, []);\n\n // the redirect lands on the next effect; rendering the table meanwhile would\n // only show an empty state that is about to disappear\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.list')}\n </Typography>\n </Box>\n );\n }\n\n const items = data?.items ?? [];\n\n return (\n <Box padding={2}>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {t('stage.title')}\n </Typography>\n <Typography color=\"text.secondary\" gutterBottom={true} variant=\"body2\">\n {t('stage.description')}\n </Typography>\n\n {actionError && (\n <Typography color=\"error\" role=\"alert\" variant=\"body2\">\n {t('stage.error.action')}\n </Typography>\n )}\n\n {items.length === 0 ? (\n <Typography color=\"text.secondary\" marginTop={2}>\n {t('stage.empty')}\n </Typography>\n ) : (\n <TableContainer component={Paper} sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table aria-label={t('stage.title')} data-testid=\"stage-table\" size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{t('stage.column.package')}</TableCell>\n <TableCell>{t('stage.column.version')}</TableCell>\n <TableCell>{t('stage.column.tag')}</TableCell>\n <TableCell>{t('stage.column.stagedBy')}</TableCell>\n <TableCell>{t('stage.column.date')}</TableCell>\n <TableCell align=\"right\">{t('stage.column.actions')}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {items.map((item) => (\n <TableRow data-testid={`stage-row-${item.id}`} hover={true} key={item.id}>\n <TableCell>\n <Button\n onClick={() => navigate(Route.STAGE_DETAIL.replace(':stageId', item.id))}\n size=\"small\"\n sx={{ textTransform: 'none' }}\n >\n {item.packageName}\n </Button>\n </TableCell>\n <TableCell>{item.version}</TableCell>\n <TableCell>\n <Chip label={item.tag} size=\"small\" />\n </TableCell>\n <TableCell>{item.actor}</TableCell>\n <TableCell>{new Date(item.createdAt).toLocaleString()}</TableCell>\n <TableCell align=\"right\">\n <Box alignItems=\"center\" display=\"flex\" gap={1} justifyContent=\"flex-end\">\n <Button\n data-testid={`stage-download-${item.id}`}\n onClick={() => handleDownload(item)}\n size=\"small\"\n >\n {t('stage.action.download')}\n </Button>\n <StageActions item={item} onDone={() => mutate()} />\n </Box>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <TablePagination\n component=\"div\"\n count={data?.total ?? 0}\n onPageChange={(_event, nextPage) => setPage(nextPage)}\n onRowsPerPageChange={(event) => {\n setPerPage(Number.parseInt(event.target.value, 10));\n setPage(0);\n }}\n page={page}\n rowsPerPage={perPage}\n rowsPerPageOptions={[10, 25, 50, 100]}\n />\n </TableContainer>\n )}\n </Box>\n );\n};\n\nexport default StageList;\n"],"mappings":";;;;;;;;;;;AAyBA,IAAM,kBAA4B;CAChC,MAAM,EAAE,MAAM,eAAe;CAC7B,MAAM,WAAW,YAAY;CAC7B,MAAM,aAAa,kBAAkB;CACrC,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC;CAClC,MAAM,CAAC,SAAS,cAAc,SAAS,EAAE;CACzC,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,EAAE,MAAM,OAAO,WAAW,WAAW,aAAa,MAAM,OAAO;CAErE,MAAM,iBAAiB,YAAY,OAAO,SAA8B;EACtE,IAAI;GACF,eAAe,KAAK;GACpB,MAAM,sBAAsB,IAAI;EAClC,SAAS,KAAK;GACZ,QAAQ,MAAM,qCAAqC,GAAG;GACtD,eAAe,IAAI;EACrB;CACF,GAAG,CAAC,CAAC;CAIL,IAAI,CAAC,YACH,OAAO;CAGT,IAAI,WACF,OACE,oBAAC,KAAD;EAAK,SAAQ;EAAO,gBAAe;EAAS,SAAS;EACnD,UAAA,oBAAC,kBAAD,CAAmB,CAAA;CAChB,CAAA;CAIT,IAAI,OACF,OACE,oBAAC,KAAD;EAAK,SAAS;EACZ,UAAA,oBAAC,YAAD;GAAY,OAAM;GAAQ,MAAK;GAC5B,UAAA,EAAE,kBAAkB;EACX,CAAA;CACT,CAAA;CAIT,MAAM,QAAQ,MAAM,SAAS,CAAC;CAE9B,OACE,qBAAC,KAAD;EAAK,SAAS;EAAd,UAAA;GACE,oBAAC,YAAD;IAAY,WAAU;IAAK,cAAc;IAAM,SAAQ;IACpD,UAAA,EAAE,aAAa;GACN,CAAA;GACZ,oBAAC,YAAD;IAAY,OAAM;IAAiB,cAAc;IAAM,SAAQ;IAC5D,UAAA,EAAE,mBAAmB;GACZ,CAAA;GAEX,eACC,oBAAC,YAAD;IAAY,OAAM;IAAQ,MAAK;IAAQ,SAAQ;IAC5C,UAAA,EAAE,oBAAoB;GACb,CAAA;GAGb,MAAM,WAAW,IAChB,oBAAC,YAAD;IAAY,OAAM;IAAiB,WAAW;IAC3C,UAAA,EAAE,aAAa;GACN,CAAA,IAEZ,qBAAC,gBAAD;IAAgB,WAAW;IAAO,IAAI;KAAE,WAAW;KAAG,WAAW;IAAO;IAAxE,UAAA,CACE,qBAAC,OAAD;KAAO,cAAY,EAAE,aAAa;KAAG,eAAY;KAAc,MAAK;KAApE,UAAA,CACE,oBAAC,WAAD,EAAA,UACE,qBAAC,UAAD,EAAA,UAAA;MACE,oBAAC,WAAD,EAAA,UAAY,EAAE,sBAAsB,EAAa,CAAA;MACjD,oBAAC,WAAD,EAAA,UAAY,EAAE,sBAAsB,EAAa,CAAA;MACjD,oBAAC,WAAD,EAAA,UAAY,EAAE,kBAAkB,EAAa,CAAA;MAC7C,oBAAC,WAAD,EAAA,UAAY,EAAE,uBAAuB,EAAa,CAAA;MAClD,oBAAC,WAAD,EAAA,UAAY,EAAE,mBAAmB,EAAa,CAAA;MAC9C,oBAAC,WAAD;OAAW,OAAM;OAAS,UAAA,EAAE,sBAAsB;MAAa,CAAA;KACvD,EAAA,CAAA,EACD,CAAA,GACX,oBAAC,WAAD,EAAA,UACG,MAAM,KAAK,SACV,qBAAC,UAAD;MAAU,eAAa,aAAa,KAAK;MAAM,OAAO;MAAtD,UAAA;OACE,oBAAC,WAAD,EAAA,UACE,oBAAC,QAAD;QACE,eAAe,
|
|
1
|
+
{"version":3,"file":"StageList.mjs","names":["Route"],"sources":["../../../src/sections/Stage/StageList.tsx"],"sourcesContent":["import {\n Box,\n Button,\n Chip,\n CircularProgress,\n Paper,\n Table,\n TableBody,\n TableCell,\n TableContainer,\n TableHead,\n TablePagination,\n TableRow,\n Typography,\n} from '@mui/material';\nimport React, { useCallback, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { useNavigate } from 'react-router';\n\nimport { Route } from '../../utils';\nimport StageActions from './StageActions';\nimport { useRequireSession } from './useRequireSession';\nimport type { StagePackageVersion } from './types';\nimport { downloadStagedTarball, useStageList } from './useStage';\n\nconst StageList: React.FC = () => {\n const { t } = useTranslation();\n const navigate = useNavigate();\n const isLoggedIn = useRequireSession();\n const [page, setPage] = useState(0);\n const [perPage, setPerPage] = useState(10);\n const [actionError, setActionError] = useState(false);\n const { data, error, isLoading, mutate } = useStageList(page, perPage);\n\n const handleDownload = useCallback(async (item: StagePackageVersion) => {\n try {\n setActionError(false);\n await downloadStagedTarball(item);\n } catch (err) {\n console.error('Error downloading staged tarball:', err);\n setActionError(true);\n }\n }, []);\n\n // the redirect lands on the next effect; rendering the table meanwhile would\n // only show an empty state that is about to disappear\n if (!isLoggedIn) {\n return null;\n }\n\n if (isLoading) {\n return (\n <Box display=\"flex\" justifyContent=\"center\" padding={4}>\n <CircularProgress />\n </Box>\n );\n }\n\n if (error) {\n return (\n <Box padding={2}>\n <Typography color=\"error\" role=\"alert\">\n {t('stage.error.list')}\n </Typography>\n </Box>\n );\n }\n\n const items = data?.items ?? [];\n\n return (\n <Box padding={2}>\n <Typography component=\"h1\" gutterBottom={true} variant=\"h5\">\n {t('stage.title')}\n </Typography>\n <Typography color=\"text.secondary\" gutterBottom={true} variant=\"body2\">\n {t('stage.description')}\n </Typography>\n\n {actionError && (\n <Typography color=\"error\" role=\"alert\" variant=\"body2\">\n {t('stage.error.action')}\n </Typography>\n )}\n\n {items.length === 0 ? (\n <Typography color=\"text.secondary\" marginTop={2}>\n {t('stage.empty')}\n </Typography>\n ) : (\n <TableContainer component={Paper} sx={{ marginTop: 2, overflowX: 'auto' }}>\n <Table aria-label={t('stage.title')} data-testid=\"stage-table\" size=\"small\">\n <TableHead>\n <TableRow>\n <TableCell>{t('stage.column.package')}</TableCell>\n <TableCell>{t('stage.column.version')}</TableCell>\n <TableCell>{t('stage.column.tag')}</TableCell>\n <TableCell>{t('stage.column.stagedBy')}</TableCell>\n <TableCell>{t('stage.column.date')}</TableCell>\n <TableCell align=\"right\">{t('stage.column.actions')}</TableCell>\n </TableRow>\n </TableHead>\n <TableBody>\n {items.map((item) => (\n <TableRow data-testid={`stage-row-${item.id}`} hover={true} key={item.id}>\n <TableCell>\n <Button\n onClick={() => navigate(Route.STAGE_DETAIL.replace(':stageId', item.id))}\n size=\"small\"\n sx={{ textTransform: 'none' }}\n >\n {item.packageName}\n </Button>\n </TableCell>\n <TableCell>{item.version}</TableCell>\n <TableCell>\n <Chip label={item.tag} size=\"small\" />\n </TableCell>\n <TableCell>{item.actor}</TableCell>\n <TableCell>{new Date(item.createdAt).toLocaleString()}</TableCell>\n <TableCell align=\"right\">\n <Box alignItems=\"center\" display=\"flex\" gap={1} justifyContent=\"flex-end\">\n <Button\n data-testid={`stage-download-${item.id}`}\n onClick={() => handleDownload(item)}\n size=\"small\"\n >\n {t('stage.action.download')}\n </Button>\n <StageActions item={item} onDone={() => mutate()} />\n </Box>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n <TablePagination\n component=\"div\"\n count={data?.total ?? 0}\n onPageChange={(_event, nextPage) => setPage(nextPage)}\n onRowsPerPageChange={(event) => {\n setPerPage(Number.parseInt(event.target.value, 10));\n setPage(0);\n }}\n page={page}\n rowsPerPage={perPage}\n rowsPerPageOptions={[10, 25, 50, 100]}\n />\n </TableContainer>\n )}\n </Box>\n );\n};\n\nexport default StageList;\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRequireSession.js","names":[],"sources":["../../../src/sections/Stage/useRequireSession.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useNavigate } from 'react-router';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport { Route } from '../../utils';\n\n/**\n * Send anonymous visitors home instead of letting them sit on a page that\n * cannot load.\n *\n * Every stage endpoint requires authentication, so rendering the view without a\n * session only produces a `401` and an error message that says nothing useful.\n * Login lives in a header dialog rather than on a route, so the way back is the\n * home page.\n *\n * @returns whether there is a session; callers should render nothing when false,\n * since the redirect happens on the next effect and the view would otherwise\n * fire a request it cannot complete.\n */\nexport function useRequireSession(): boolean {\n const { userState } = useAuth();\n const navigate = useNavigate();\n const isLoggedIn = Boolean(userState?.token);\n\n useEffect(() => {\n if (!isLoggedIn) {\n navigate(Route.ROOT);\n }\n }, [isLoggedIn, navigate]);\n\n return isLoggedIn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBAA6B;CAC3C,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"useRequireSession.js","names":["useAuth","useNavigate","Route"],"sources":["../../../src/sections/Stage/useRequireSession.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useNavigate } from 'react-router';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport { Route } from '../../utils';\n\n/**\n * Send anonymous visitors home instead of letting them sit on a page that\n * cannot load.\n *\n * Every stage endpoint requires authentication, so rendering the view without a\n * session only produces a `401` and an error message that says nothing useful.\n * Login lives in a header dialog rather than on a route, so the way back is the\n * home page.\n *\n * @returns whether there is a session; callers should render nothing when false,\n * since the redirect happens on the next effect and the view would otherwise\n * fire a request it cannot complete.\n */\nexport function useRequireSession(): boolean {\n const { userState } = useAuth();\n const navigate = useNavigate();\n const isLoggedIn = Boolean(userState?.token);\n\n useEffect(() => {\n if (!isLoggedIn) {\n navigate(Route.ROOT);\n }\n }, [isLoggedIn, navigate]);\n\n return isLoggedIn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBAA6B;CAC3C,MAAM,EAAE,cAAcA,qBAAAA,QAAQ;CAC9B,MAAM,YAAA,GAAWC,aAAAA,YAAAA,CAAY;CAC7B,MAAM,aAAa,QAAQ,WAAW,KAAK;CAE3C,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,YACH,SAASC,eAAAA,MAAM,IAAI;CAEvB,GAAG,CAAC,YAAY,QAAQ,CAAC;CAEzB,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useRequireSession.mjs","names":[],"sources":["../../../src/sections/Stage/useRequireSession.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useNavigate } from 'react-router';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport { Route } from '../../utils';\n\n/**\n * Send anonymous visitors home instead of letting them sit on a page that\n * cannot load.\n *\n * Every stage endpoint requires authentication, so rendering the view without a\n * session only produces a `401` and an error message that says nothing useful.\n * Login lives in a header dialog rather than on a route, so the way back is the\n * home page.\n *\n * @returns whether there is a session; callers should render nothing when false,\n * since the redirect happens on the next effect and the view would otherwise\n * fire a request it cannot complete.\n */\nexport function useRequireSession(): boolean {\n const { userState } = useAuth();\n const navigate = useNavigate();\n const isLoggedIn = Boolean(userState?.token);\n\n useEffect(() => {\n if (!isLoggedIn) {\n navigate(Route.ROOT);\n }\n }, [isLoggedIn, navigate]);\n\n return isLoggedIn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBAA6B;CAC3C,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,WAAW,YAAY;CAC7B,MAAM,aAAa,QAAQ,WAAW,KAAK;CAE3C,gBAAgB;EACd,IAAI,CAAC,YACH,
|
|
1
|
+
{"version":3,"file":"useRequireSession.mjs","names":["Route"],"sources":["../../../src/sections/Stage/useRequireSession.ts"],"sourcesContent":["import { useEffect } from 'react';\nimport { useNavigate } from 'react-router';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport { Route } from '../../utils';\n\n/**\n * Send anonymous visitors home instead of letting them sit on a page that\n * cannot load.\n *\n * Every stage endpoint requires authentication, so rendering the view without a\n * session only produces a `401` and an error message that says nothing useful.\n * Login lives in a header dialog rather than on a route, so the way back is the\n * home page.\n *\n * @returns whether there is a session; callers should render nothing when false,\n * since the redirect happens on the next effect and the view would otherwise\n * fire a request it cannot complete.\n */\nexport function useRequireSession(): boolean {\n const { userState } = useAuth();\n const navigate = useNavigate();\n const isLoggedIn = Boolean(userState?.token);\n\n useEffect(() => {\n if (!isLoggedIn) {\n navigate(Route.ROOT);\n }\n }, [isLoggedIn, navigate]);\n\n return isLoggedIn;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAmBA,SAAgB,oBAA6B;CAC3C,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,WAAW,YAAY;CAC7B,MAAM,aAAa,QAAQ,WAAW,KAAK;CAE3C,gBAAgB;EACd,IAAI,CAAC,YACH,SAASA,QAAM,IAAI;CAEvB,GAAG,CAAC,YAAY,QAAQ,CAAC;CAEzB,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStage.js","names":[],"sources":["../../../src/sections/Stage/useStage.ts"],"sourcesContent":["import useSWR from 'swr';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport API from '../../store/api';\nimport { getConfiguration } from '../../configuration';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { APIRoute } from '../../utils/routes';\nimport { downloadFile } from '../../utils/url';\nimport type { StagePackageList, StagePackageVersion } from './types';\n\nfunction stageUrl(path = ''): string {\n const basePath = stripTrailingSlash(getConfiguration().base);\n return `${basePath}${APIRoute.STAGE}${path}`;\n}\n\n/**\n * Page through the staged versions the logged in user is allowed to see.\n *\n * The registry filters by permission before paginating, so `total` always\n * matches what this user can actually reach.\n */\nexport function useStageList(page: number, perPage: number) {\n const { userState } = useAuth();\n const url = `${stageUrl()}?page=${page}&perPage=${perPage}`;\n // a null key tells SWR not to fetch: without a session the request can only\n // come back 401\n const { data, error, isLoading, mutate } = useSWR<StagePackageList>(\n userState?.token ? url : null,\n () => API.request<StagePackageList>(url)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** A single staged version. */\nexport function useStageItem(stageId?: string) {\n const { userState } = useAuth();\n const url = stageId && userState?.token ? stageUrl(`/${stageId}`) : null;\n const { data, error, isLoading, mutate } = useSWR<StagePackageVersion>(url, () =>\n API.request<StagePackageVersion>(url as string)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** Approve a staged version, publishing it for real. */\nexport function approveStagedVersion(stageId: string): Promise<{ message: string }> {\n return API.request<{ message: string }>(stageUrl(`/${stageId}/approve`), 'POST');\n}\n\n/** Reject a staged version, dropping it and its tarball. */\nexport function rejectStagedVersion(stageId: string): Promise<void> {\n return API.request<void>(stageUrl(`/${stageId}`), 'DELETE');\n}\n\n/**\n * Download the staged tarball.\n *\n * The endpoint is authenticated, so a plain `<a href>` would get a 401: the\n * blob has to be fetched through the API client, which attaches the bearer\n * token, and then handed to the browser.\n */\nexport async function downloadStagedTarball(item: StagePackageVersion): Promise<void> {\n const blob = await API.request<Blob>(stageUrl(`/${item.id}/tarball`), 'GET', {\n headers: { accept: 'application/octet-stream' },\n });\n const safeName = item.packageName.replace('@', '').replace('/', '-');\n downloadFile(blob, `${safeName}-${item.version}.tgz`);\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAS,SAAS,OAAO,IAAY;CAEnC,OAAO,
|
|
1
|
+
{"version":3,"file":"useStage.js","names":["stripTrailingSlash","getConfiguration","APIRoute","useAuth","useSWR","API"],"sources":["../../../src/sections/Stage/useStage.ts"],"sourcesContent":["import useSWR from 'swr';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport API from '../../store/api';\nimport { getConfiguration } from '../../configuration';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { APIRoute } from '../../utils/routes';\nimport { downloadFile } from '../../utils/url';\nimport type { StagePackageList, StagePackageVersion } from './types';\n\nfunction stageUrl(path = ''): string {\n const basePath = stripTrailingSlash(getConfiguration().base);\n return `${basePath}${APIRoute.STAGE}${path}`;\n}\n\n/**\n * Page through the staged versions the logged in user is allowed to see.\n *\n * The registry filters by permission before paginating, so `total` always\n * matches what this user can actually reach.\n */\nexport function useStageList(page: number, perPage: number) {\n const { userState } = useAuth();\n const url = `${stageUrl()}?page=${page}&perPage=${perPage}`;\n // a null key tells SWR not to fetch: without a session the request can only\n // come back 401\n const { data, error, isLoading, mutate } = useSWR<StagePackageList>(\n userState?.token ? url : null,\n () => API.request<StagePackageList>(url)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** A single staged version. */\nexport function useStageItem(stageId?: string) {\n const { userState } = useAuth();\n const url = stageId && userState?.token ? stageUrl(`/${stageId}`) : null;\n const { data, error, isLoading, mutate } = useSWR<StagePackageVersion>(url, () =>\n API.request<StagePackageVersion>(url as string)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** Approve a staged version, publishing it for real. */\nexport function approveStagedVersion(stageId: string): Promise<{ message: string }> {\n return API.request<{ message: string }>(stageUrl(`/${stageId}/approve`), 'POST');\n}\n\n/** Reject a staged version, dropping it and its tarball. */\nexport function rejectStagedVersion(stageId: string): Promise<void> {\n return API.request<void>(stageUrl(`/${stageId}`), 'DELETE');\n}\n\n/**\n * Download the staged tarball.\n *\n * The endpoint is authenticated, so a plain `<a href>` would get a 401: the\n * blob has to be fetched through the API client, which attaches the bearer\n * token, and then handed to the browser.\n */\nexport async function downloadStagedTarball(item: StagePackageVersion): Promise<void> {\n const blob = await API.request<Blob>(stageUrl(`/${item.id}/tarball`), 'GET', {\n headers: { accept: 'application/octet-stream' },\n });\n const safeName = item.packageName.replace('@', '').replace('/', '-');\n downloadFile(blob, `${safeName}-${item.version}.tgz`);\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAS,SAAS,OAAO,IAAY;CAEnC,OAAO,GADUA,cAAAA,mBAAmBC,sBAAAA,iBAAiB,CAAC,CAAC,IAC7C,IAAWC,eAAAA,SAAS,QAAQ;AACxC;;;;;;;AAQA,SAAgB,aAAa,MAAc,SAAiB;CAC1D,MAAM,EAAE,cAAcC,qBAAAA,QAAQ;CAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,QAAQ,KAAK,WAAW;CAGlD,MAAM,EAAE,MAAM,OAAO,WAAW,YAAA,GAAWC,IAAAA,QAAAA,CACzC,WAAW,QAAQ,MAAM,YACnBC,YAAAA,QAAI,QAA0B,GAAG,CACzC;CAEA,OAAO;EAAE;EAAM;EAAO;EAAW;CAAO;AAC1C;;AAGA,SAAgB,aAAa,SAAkB;CAC7C,MAAM,EAAE,cAAcF,qBAAAA,QAAQ;CAC9B,MAAM,MAAM,WAAW,WAAW,QAAQ,SAAS,IAAI,SAAS,IAAI;CACpE,MAAM,EAAE,MAAM,OAAO,WAAW,YAAA,GAAWC,IAAAA,QAAAA,CAA4B,WACrEC,YAAAA,QAAI,QAA6B,GAAa,CAChD;CAEA,OAAO;EAAE;EAAM;EAAO;EAAW;CAAO;AAC1C;;AAGA,SAAgB,qBAAqB,SAA+C;CAClF,OAAOA,YAAAA,QAAI,QAA6B,SAAS,IAAI,QAAQ,SAAS,GAAG,MAAM;AACjF;;AAGA,SAAgB,oBAAoB,SAAgC;CAClE,OAAOA,YAAAA,QAAI,QAAc,SAAS,IAAI,SAAS,GAAG,QAAQ;AAC5D;;;;;;;;AASA,eAAsB,sBAAsB,MAA0C;CACpF,MAAM,OAAO,MAAMA,YAAAA,QAAI,QAAc,SAAS,IAAI,KAAK,GAAG,SAAS,GAAG,OAAO,EAC3E,SAAS,EAAE,QAAQ,2BAA2B,EAChD,CAAC;CACD,MAAM,WAAW,KAAK,YAAY,QAAQ,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,GAAG;CACnE,YAAA,aAAa,MAAM,GAAG,SAAS,GAAG,KAAK,QAAQ,KAAK;AACtD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStage.mjs","names":[],"sources":["../../../src/sections/Stage/useStage.ts"],"sourcesContent":["import useSWR from 'swr';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport API from '../../store/api';\nimport { getConfiguration } from '../../configuration';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { APIRoute } from '../../utils/routes';\nimport { downloadFile } from '../../utils/url';\nimport type { StagePackageList, StagePackageVersion } from './types';\n\nfunction stageUrl(path = ''): string {\n const basePath = stripTrailingSlash(getConfiguration().base);\n return `${basePath}${APIRoute.STAGE}${path}`;\n}\n\n/**\n * Page through the staged versions the logged in user is allowed to see.\n *\n * The registry filters by permission before paginating, so `total` always\n * matches what this user can actually reach.\n */\nexport function useStageList(page: number, perPage: number) {\n const { userState } = useAuth();\n const url = `${stageUrl()}?page=${page}&perPage=${perPage}`;\n // a null key tells SWR not to fetch: without a session the request can only\n // come back 401\n const { data, error, isLoading, mutate } = useSWR<StagePackageList>(\n userState?.token ? url : null,\n () => API.request<StagePackageList>(url)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** A single staged version. */\nexport function useStageItem(stageId?: string) {\n const { userState } = useAuth();\n const url = stageId && userState?.token ? stageUrl(`/${stageId}`) : null;\n const { data, error, isLoading, mutate } = useSWR<StagePackageVersion>(url, () =>\n API.request<StagePackageVersion>(url as string)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** Approve a staged version, publishing it for real. */\nexport function approveStagedVersion(stageId: string): Promise<{ message: string }> {\n return API.request<{ message: string }>(stageUrl(`/${stageId}/approve`), 'POST');\n}\n\n/** Reject a staged version, dropping it and its tarball. */\nexport function rejectStagedVersion(stageId: string): Promise<void> {\n return API.request<void>(stageUrl(`/${stageId}`), 'DELETE');\n}\n\n/**\n * Download the staged tarball.\n *\n * The endpoint is authenticated, so a plain `<a href>` would get a 401: the\n * blob has to be fetched through the API client, which attaches the bearer\n * token, and then handed to the browser.\n */\nexport async function downloadStagedTarball(item: StagePackageVersion): Promise<void> {\n const blob = await API.request<Blob>(stageUrl(`/${item.id}/tarball`), 'GET', {\n headers: { accept: 'application/octet-stream' },\n });\n const safeName = item.packageName.replace('@', '').replace('/', '-');\n downloadFile(blob, `${safeName}-${item.version}.tgz`);\n}\n"],"mappings":";;;;;;;;;AAUA,SAAS,SAAS,OAAO,IAAY;CAEnC,OAAO,GADU,mBAAmB,iBAAiB,CAAC,CAAC,IAC7C,IAAW,SAAS,QAAQ;AACxC;;;;;;;AAQA,SAAgB,aAAa,MAAc,SAAiB;CAC1D,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,QAAQ,KAAK,WAAW;CAGlD,MAAM,EAAE,MAAM,OAAO,WAAW,WAAW,OACzC,WAAW,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"useStage.mjs","names":["API"],"sources":["../../../src/sections/Stage/useStage.ts"],"sourcesContent":["import useSWR from 'swr';\n\nimport { useAuth } from '../../providers/AuthProvider';\nimport API from '../../store/api';\nimport { getConfiguration } from '../../configuration';\nimport { stripTrailingSlash } from '../../store/utils';\nimport { APIRoute } from '../../utils/routes';\nimport { downloadFile } from '../../utils/url';\nimport type { StagePackageList, StagePackageVersion } from './types';\n\nfunction stageUrl(path = ''): string {\n const basePath = stripTrailingSlash(getConfiguration().base);\n return `${basePath}${APIRoute.STAGE}${path}`;\n}\n\n/**\n * Page through the staged versions the logged in user is allowed to see.\n *\n * The registry filters by permission before paginating, so `total` always\n * matches what this user can actually reach.\n */\nexport function useStageList(page: number, perPage: number) {\n const { userState } = useAuth();\n const url = `${stageUrl()}?page=${page}&perPage=${perPage}`;\n // a null key tells SWR not to fetch: without a session the request can only\n // come back 401\n const { data, error, isLoading, mutate } = useSWR<StagePackageList>(\n userState?.token ? url : null,\n () => API.request<StagePackageList>(url)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** A single staged version. */\nexport function useStageItem(stageId?: string) {\n const { userState } = useAuth();\n const url = stageId && userState?.token ? stageUrl(`/${stageId}`) : null;\n const { data, error, isLoading, mutate } = useSWR<StagePackageVersion>(url, () =>\n API.request<StagePackageVersion>(url as string)\n );\n\n return { data, error, isLoading, mutate };\n}\n\n/** Approve a staged version, publishing it for real. */\nexport function approveStagedVersion(stageId: string): Promise<{ message: string }> {\n return API.request<{ message: string }>(stageUrl(`/${stageId}/approve`), 'POST');\n}\n\n/** Reject a staged version, dropping it and its tarball. */\nexport function rejectStagedVersion(stageId: string): Promise<void> {\n return API.request<void>(stageUrl(`/${stageId}`), 'DELETE');\n}\n\n/**\n * Download the staged tarball.\n *\n * The endpoint is authenticated, so a plain `<a href>` would get a 401: the\n * blob has to be fetched through the API client, which attaches the bearer\n * token, and then handed to the browser.\n */\nexport async function downloadStagedTarball(item: StagePackageVersion): Promise<void> {\n const blob = await API.request<Blob>(stageUrl(`/${item.id}/tarball`), 'GET', {\n headers: { accept: 'application/octet-stream' },\n });\n const safeName = item.packageName.replace('@', '').replace('/', '-');\n downloadFile(blob, `${safeName}-${item.version}.tgz`);\n}\n"],"mappings":";;;;;;;;;AAUA,SAAS,SAAS,OAAO,IAAY;CAEnC,OAAO,GADU,mBAAmB,iBAAiB,CAAC,CAAC,IAC7C,IAAW,SAAS,QAAQ;AACxC;;;;;;;AAQA,SAAgB,aAAa,MAAc,SAAiB;CAC1D,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,QAAQ,KAAK,WAAW;CAGlD,MAAM,EAAE,MAAM,OAAO,WAAW,WAAW,OACzC,WAAW,QAAQ,MAAM,YACnBA,YAAI,QAA0B,GAAG,CACzC;CAEA,OAAO;EAAE;EAAM;EAAO;EAAW;CAAO;AAC1C;;AAGA,SAAgB,aAAa,SAAkB;CAC7C,MAAM,EAAE,cAAc,QAAQ;CAC9B,MAAM,MAAM,WAAW,WAAW,QAAQ,SAAS,IAAI,SAAS,IAAI;CACpE,MAAM,EAAE,MAAM,OAAO,WAAW,WAAW,OAA4B,WACrEA,YAAI,QAA6B,GAAa,CAChD;CAEA,OAAO;EAAE;EAAM;EAAO;EAAW;CAAO;AAC1C;;AAGA,SAAgB,qBAAqB,SAA+C;CAClF,OAAOA,YAAI,QAA6B,SAAS,IAAI,QAAQ,SAAS,GAAG,MAAM;AACjF;;AAGA,SAAgB,oBAAoB,SAAgC;CAClE,OAAOA,YAAI,QAAc,SAAS,IAAI,SAAS,GAAG,QAAQ;AAC5D;;;;;;;;AASA,eAAsB,sBAAsB,MAA0C;CACpF,MAAM,OAAO,MAAMA,YAAI,QAAc,SAAS,IAAI,KAAK,GAAG,SAAS,GAAG,OAAO,EAC3E,SAAS,EAAE,QAAQ,2BAA2B,EAChD,CAAC;CACD,MAAM,WAAW,KAAK,YAAY,QAAQ,KAAK,EAAE,CAAC,CAAC,QAAQ,KAAK,GAAG;CACnE,aAAa,MAAM,GAAG,SAAS,GAAG,KAAK,QAAQ,KAAK;AACtD"}
|
package/build/store/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","names":[],"sources":["../../src/store/api.ts"],"sourcesContent":["import storage from './storage';\n\nexport class CustomError extends Error {\n code: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = 'CustomError';\n this.code = code ?? 500;\n }\n}\n\n/**\n * Handles response according to content type\n * @param {object} response\n * @returns {promise}\n */\nexport function handleResponseType(response: Response): Promise<[boolean, any, number]> {\n if (response.headers) {\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/pdf')) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n // binary payloads (e.g. staged tarballs, served without a .tgz url) must\n // never be decoded as text: invalid utf-8 bytes become U+FFFD and the\n // downloaded archive is corrupted\n if (\n contentType?.includes('application/octet-stream') ||\n contentType?.includes('application/gzip')\n ) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n if (contentType?.includes('application/json')) {\n return Promise.all([response.ok, response.json(), response.status]);\n }\n // it includes all text types\n if (contentType?.includes('text/')) {\n return Promise.all([response.ok, response.text(), response.status]);\n }\n\n // unfortunately on download files there is no header available\n if (response.url && response.url.endsWith('.tgz') === true) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n }\n\n // error handling\n return Promise.all([response.ok, response.text(), response.status]);\n}\n\nconst AuthHeader = 'Authorization';\n\nclass API {\n public request<T>(\n url: string,\n method = 'GET',\n options: RequestInit = { headers: {} }\n ): Promise<T> {\n const token = storage.getItem('token');\n const headers = new Headers(options.headers);\n\n if (token && headers.has(AuthHeader) === false) {\n headers.set(AuthHeader, `Bearer ${token}`);\n }\n\n headers.set('x-client', 'verdaccio-ui');\n // assign unconditionally: doing it only inside the token branch dropped\n // the x-client header for anonymous requests\n options.headers = headers;\n\n return new Promise((resolve, reject) => {\n fetch(url, {\n method,\n credentials: 'same-origin',\n signal: options.signal,\n ...options,\n })\n .then(handleResponseType)\n .then((response) => {\n const [ok, data, status] = response;\n if (ok === true) {\n resolve(data);\n } else {\n const message =\n typeof data === 'string' ? data : (data?.error ?? data?.message ?? 'Unknown error');\n reject(new CustomError(message, status));\n }\n })\n .catch((error) => {\n reject(error);\n });\n });\n }\n}\n\nexport default new API();\n"],"mappings":";;AAEA,IAAa,cAAb,cAAiC,MAAM;CACrC;CAEA,YAAY,SAAiB,MAAe;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;AAOA,SAAgB,mBAAmB,UAAqD;CACtF,IAAI,SAAS,SAAS;EACpB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;EACvD,IAAI,aAAa,SAAS,iBAAiB,GACzC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAKpE,IACE,aAAa,SAAS,0BAA0B,KAChD,aAAa,SAAS,kBAAkB,GAExC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAEpE,IAAI,aAAa,SAAS,kBAAkB,GAC1C,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAGpE,IAAI,aAAa,SAAS,OAAO,GAC/B,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAIpE,IAAI,SAAS,OAAO,SAAS,IAAI,SAAS,MAAM,MAAM,MACpD,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;CAEtE;CAGA,OAAO,QAAQ,IAAI;EAAC,SAAS;EAAI,SAAS,KAAK;EAAG,SAAS;CAAM,CAAC;AACpE;AAEA,IAAM,aAAa;AAEnB,IAAM,MAAN,MAAU;CACR,QACE,KACA,SAAS,OACT,UAAuB,EAAE,SAAS,CAAC,EAAE,GACzB;EACZ,MAAM,
|
|
1
|
+
{"version":3,"file":"api.js","names":["storage"],"sources":["../../src/store/api.ts"],"sourcesContent":["import storage from './storage';\n\nexport class CustomError extends Error {\n code: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = 'CustomError';\n this.code = code ?? 500;\n }\n}\n\n/**\n * Handles response according to content type\n * @param {object} response\n * @returns {promise}\n */\nexport function handleResponseType(response: Response): Promise<[boolean, any, number]> {\n if (response.headers) {\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/pdf')) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n // binary payloads (e.g. staged tarballs, served without a .tgz url) must\n // never be decoded as text: invalid utf-8 bytes become U+FFFD and the\n // downloaded archive is corrupted\n if (\n contentType?.includes('application/octet-stream') ||\n contentType?.includes('application/gzip')\n ) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n if (contentType?.includes('application/json')) {\n return Promise.all([response.ok, response.json(), response.status]);\n }\n // it includes all text types\n if (contentType?.includes('text/')) {\n return Promise.all([response.ok, response.text(), response.status]);\n }\n\n // unfortunately on download files there is no header available\n if (response.url && response.url.endsWith('.tgz') === true) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n }\n\n // error handling\n return Promise.all([response.ok, response.text(), response.status]);\n}\n\nconst AuthHeader = 'Authorization';\n\nclass API {\n public request<T>(\n url: string,\n method = 'GET',\n options: RequestInit = { headers: {} }\n ): Promise<T> {\n const token = storage.getItem('token');\n const headers = new Headers(options.headers);\n\n if (token && headers.has(AuthHeader) === false) {\n headers.set(AuthHeader, `Bearer ${token}`);\n }\n\n headers.set('x-client', 'verdaccio-ui');\n // assign unconditionally: doing it only inside the token branch dropped\n // the x-client header for anonymous requests\n options.headers = headers;\n\n return new Promise((resolve, reject) => {\n fetch(url, {\n method,\n credentials: 'same-origin',\n signal: options.signal,\n ...options,\n })\n .then(handleResponseType)\n .then((response) => {\n const [ok, data, status] = response;\n if (ok === true) {\n resolve(data);\n } else {\n const message =\n typeof data === 'string' ? data : (data?.error ?? data?.message ?? 'Unknown error');\n reject(new CustomError(message, status));\n }\n })\n .catch((error) => {\n reject(error);\n });\n });\n }\n}\n\nexport default new API();\n"],"mappings":";;AAEA,IAAa,cAAb,cAAiC,MAAM;CACrC;CAEA,YAAY,SAAiB,MAAe;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;AAOA,SAAgB,mBAAmB,UAAqD;CACtF,IAAI,SAAS,SAAS;EACpB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;EACvD,IAAI,aAAa,SAAS,iBAAiB,GACzC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAKpE,IACE,aAAa,SAAS,0BAA0B,KAChD,aAAa,SAAS,kBAAkB,GAExC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAEpE,IAAI,aAAa,SAAS,kBAAkB,GAC1C,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAGpE,IAAI,aAAa,SAAS,OAAO,GAC/B,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAIpE,IAAI,SAAS,OAAO,SAAS,IAAI,SAAS,MAAM,MAAM,MACpD,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;CAEtE;CAGA,OAAO,QAAQ,IAAI;EAAC,SAAS;EAAI,SAAS,KAAK;EAAG,SAAS;CAAM,CAAC;AACpE;AAEA,IAAM,aAAa;AAEnB,IAAM,MAAN,MAAU;CACR,QACE,KACA,SAAS,OACT,UAAuB,EAAE,SAAS,CAAC,EAAE,GACzB;EACZ,MAAM,QAAQA,gBAAAA,QAAQ,QAAQ,OAAO;EACrC,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;EAE3C,IAAI,SAAS,QAAQ,IAAI,UAAU,MAAM,OACvC,QAAQ,IAAI,YAAY,UAAU,OAAO;EAG3C,QAAQ,IAAI,YAAY,cAAc;EAGtC,QAAQ,UAAU;EAElB,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,KAAK;IACT;IACA,aAAa;IACb,QAAQ,QAAQ;IAChB,GAAG;GACL,CAAC,CAAC,CACC,KAAK,kBAAkB,CAAC,CACxB,MAAM,aAAa;IAClB,MAAM,CAAC,IAAI,MAAM,UAAU;IAC3B,IAAI,OAAO,MACT,QAAQ,IAAI;SAIZ,OAAO,IAAI,YADT,OAAO,SAAS,WAAW,OAAQ,MAAM,SAAS,MAAM,WAAW,iBACrC,MAAM,CAAC;GAE3C,CAAC,CAAC,CACD,OAAO,UAAU;IAChB,OAAO,KAAK;GACd,CAAC;EACL,CAAC;CACH;AACF;AAEA,IAAA,cAAe,IAAI,IAAI"}
|
package/build/store/api.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.mjs","names":[],"sources":["../../src/store/api.ts"],"sourcesContent":["import storage from './storage';\n\nexport class CustomError extends Error {\n code: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = 'CustomError';\n this.code = code ?? 500;\n }\n}\n\n/**\n * Handles response according to content type\n * @param {object} response\n * @returns {promise}\n */\nexport function handleResponseType(response: Response): Promise<[boolean, any, number]> {\n if (response.headers) {\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/pdf')) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n // binary payloads (e.g. staged tarballs, served without a .tgz url) must\n // never be decoded as text: invalid utf-8 bytes become U+FFFD and the\n // downloaded archive is corrupted\n if (\n contentType?.includes('application/octet-stream') ||\n contentType?.includes('application/gzip')\n ) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n if (contentType?.includes('application/json')) {\n return Promise.all([response.ok, response.json(), response.status]);\n }\n // it includes all text types\n if (contentType?.includes('text/')) {\n return Promise.all([response.ok, response.text(), response.status]);\n }\n\n // unfortunately on download files there is no header available\n if (response.url && response.url.endsWith('.tgz') === true) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n }\n\n // error handling\n return Promise.all([response.ok, response.text(), response.status]);\n}\n\nconst AuthHeader = 'Authorization';\n\nclass API {\n public request<T>(\n url: string,\n method = 'GET',\n options: RequestInit = { headers: {} }\n ): Promise<T> {\n const token = storage.getItem('token');\n const headers = new Headers(options.headers);\n\n if (token && headers.has(AuthHeader) === false) {\n headers.set(AuthHeader, `Bearer ${token}`);\n }\n\n headers.set('x-client', 'verdaccio-ui');\n // assign unconditionally: doing it only inside the token branch dropped\n // the x-client header for anonymous requests\n options.headers = headers;\n\n return new Promise((resolve, reject) => {\n fetch(url, {\n method,\n credentials: 'same-origin',\n signal: options.signal,\n ...options,\n })\n .then(handleResponseType)\n .then((response) => {\n const [ok, data, status] = response;\n if (ok === true) {\n resolve(data);\n } else {\n const message =\n typeof data === 'string' ? data : (data?.error ?? data?.message ?? 'Unknown error');\n reject(new CustomError(message, status));\n }\n })\n .catch((error) => {\n reject(error);\n });\n });\n }\n}\n\nexport default new API();\n"],"mappings":";;AAEA,IAAa,cAAb,cAAiC,MAAM;CACrC;CAEA,YAAY,SAAiB,MAAe;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;AAOA,SAAgB,mBAAmB,UAAqD;CACtF,IAAI,SAAS,SAAS;EACpB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;EACvD,IAAI,aAAa,SAAS,iBAAiB,GACzC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAKpE,IACE,aAAa,SAAS,0BAA0B,KAChD,aAAa,SAAS,kBAAkB,GAExC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAEpE,IAAI,aAAa,SAAS,kBAAkB,GAC1C,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAGpE,IAAI,aAAa,SAAS,OAAO,GAC/B,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAIpE,IAAI,SAAS,OAAO,SAAS,IAAI,SAAS,MAAM,MAAM,MACpD,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;CAEtE;CAGA,OAAO,QAAQ,IAAI;EAAC,SAAS;EAAI,SAAS,KAAK;EAAG,SAAS;CAAM,CAAC;AACpE;AAEA,IAAM,aAAa;AAEnB,IAAM,MAAN,MAAU;CACR,QACE,KACA,SAAS,OACT,UAAuB,EAAE,SAAS,CAAC,EAAE,GACzB;EACZ,MAAM,
|
|
1
|
+
{"version":3,"file":"api.mjs","names":["storage"],"sources":["../../src/store/api.ts"],"sourcesContent":["import storage from './storage';\n\nexport class CustomError extends Error {\n code: number;\n\n constructor(message: string, code?: number) {\n super(message);\n this.name = 'CustomError';\n this.code = code ?? 500;\n }\n}\n\n/**\n * Handles response according to content type\n * @param {object} response\n * @returns {promise}\n */\nexport function handleResponseType(response: Response): Promise<[boolean, any, number]> {\n if (response.headers) {\n const contentType = response.headers.get('Content-Type');\n if (contentType?.includes('application/pdf')) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n // binary payloads (e.g. staged tarballs, served without a .tgz url) must\n // never be decoded as text: invalid utf-8 bytes become U+FFFD and the\n // downloaded archive is corrupted\n if (\n contentType?.includes('application/octet-stream') ||\n contentType?.includes('application/gzip')\n ) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n if (contentType?.includes('application/json')) {\n return Promise.all([response.ok, response.json(), response.status]);\n }\n // it includes all text types\n if (contentType?.includes('text/')) {\n return Promise.all([response.ok, response.text(), response.status]);\n }\n\n // unfortunately on download files there is no header available\n if (response.url && response.url.endsWith('.tgz') === true) {\n return Promise.all([response.ok, response.blob(), response.status]);\n }\n }\n\n // error handling\n return Promise.all([response.ok, response.text(), response.status]);\n}\n\nconst AuthHeader = 'Authorization';\n\nclass API {\n public request<T>(\n url: string,\n method = 'GET',\n options: RequestInit = { headers: {} }\n ): Promise<T> {\n const token = storage.getItem('token');\n const headers = new Headers(options.headers);\n\n if (token && headers.has(AuthHeader) === false) {\n headers.set(AuthHeader, `Bearer ${token}`);\n }\n\n headers.set('x-client', 'verdaccio-ui');\n // assign unconditionally: doing it only inside the token branch dropped\n // the x-client header for anonymous requests\n options.headers = headers;\n\n return new Promise((resolve, reject) => {\n fetch(url, {\n method,\n credentials: 'same-origin',\n signal: options.signal,\n ...options,\n })\n .then(handleResponseType)\n .then((response) => {\n const [ok, data, status] = response;\n if (ok === true) {\n resolve(data);\n } else {\n const message =\n typeof data === 'string' ? data : (data?.error ?? data?.message ?? 'Unknown error');\n reject(new CustomError(message, status));\n }\n })\n .catch((error) => {\n reject(error);\n });\n });\n }\n}\n\nexport default new API();\n"],"mappings":";;AAEA,IAAa,cAAb,cAAiC,MAAM;CACrC;CAEA,YAAY,SAAiB,MAAe;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO,QAAQ;CACtB;AACF;;;;;;AAOA,SAAgB,mBAAmB,UAAqD;CACtF,IAAI,SAAS,SAAS;EACpB,MAAM,cAAc,SAAS,QAAQ,IAAI,cAAc;EACvD,IAAI,aAAa,SAAS,iBAAiB,GACzC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAKpE,IACE,aAAa,SAAS,0BAA0B,KAChD,aAAa,SAAS,kBAAkB,GAExC,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAEpE,IAAI,aAAa,SAAS,kBAAkB,GAC1C,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAGpE,IAAI,aAAa,SAAS,OAAO,GAC/B,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;EAIpE,IAAI,SAAS,OAAO,SAAS,IAAI,SAAS,MAAM,MAAM,MACpD,OAAO,QAAQ,IAAI;GAAC,SAAS;GAAI,SAAS,KAAK;GAAG,SAAS;EAAM,CAAC;CAEtE;CAGA,OAAO,QAAQ,IAAI;EAAC,SAAS;EAAI,SAAS,KAAK;EAAG,SAAS;CAAM,CAAC;AACpE;AAEA,IAAM,aAAa;AAEnB,IAAM,MAAN,MAAU;CACR,QACE,KACA,SAAS,OACT,UAAuB,EAAE,SAAS,CAAC,EAAE,GACzB;EACZ,MAAM,QAAQA,gBAAQ,QAAQ,OAAO;EACrC,MAAM,UAAU,IAAI,QAAQ,QAAQ,OAAO;EAE3C,IAAI,SAAS,QAAQ,IAAI,UAAU,MAAM,OACvC,QAAQ,IAAI,YAAY,UAAU,OAAO;EAG3C,QAAQ,IAAI,YAAY,cAAc;EAGtC,QAAQ,UAAU;EAElB,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,KAAK;IACT;IACA,aAAa;IACb,QAAQ,QAAQ;IAChB,GAAG;GACL,CAAC,CAAC,CACC,KAAK,kBAAkB,CAAC,CACxB,MAAM,aAAa;IAClB,MAAM,CAAC,IAAI,MAAM,UAAU;IAC3B,IAAI,OAAO,MACT,QAAQ,IAAI;SAIZ,OAAO,IAAI,YADT,OAAO,SAAS,WAAW,OAAQ,MAAM,SAAS,MAAM,WAAW,iBACrC,MAAM,CAAC;GAE3C,CAAC,CAAC,CACD,OAAO,UAAU;IAChB,OAAO,KAAK;GACd,CAAC;EACL,CAAC;CACH;AACF;AAEA,IAAA,cAAe,IAAI,IAAI"}
|
package/build/store/index.mjs
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
|
|
2
|
-
export { api_default as api };
|
|
1
|
+
export { default as api } from "./api.mjs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","names":[],"sources":["../../src/store/storage.ts"],"sourcesContent":["import memoryStorage from 'localstorage-memory';\n\nlet storage: Storage;\ntry {\n localStorage.setItem('__TEST__', '');\n localStorage.removeItem('__TEST__');\n storage = localStorage;\n} catch {\n // Fall back to memory storage if localStorage is not available\n storage = memoryStorage;\n}\n\nexport function saveAuth(username: string, token: string): void {\n storage.setItem('username', username);\n storage.setItem('token', token);\n}\n\nexport function clearAuth(): void {\n storage.removeItem('username');\n storage.removeItem('token');\n}\n\nexport function getAuth(): { username: string | null; token: string | null } {\n return {\n username: storage.getItem('username'),\n token: storage.getItem('token'),\n };\n}\n\nexport default storage;\n"],"mappings":";;;;AAEA,IAAI;AACJ,IAAI;CACF,aAAa,QAAQ,YAAY,EAAE;CACnC,aAAa,WAAW,UAAU;CAClC,UAAU;AACZ,QAAQ;CAEN,
|
|
1
|
+
{"version":3,"file":"storage.js","names":["memoryStorage"],"sources":["../../src/store/storage.ts"],"sourcesContent":["import memoryStorage from 'localstorage-memory';\n\nlet storage: Storage;\ntry {\n localStorage.setItem('__TEST__', '');\n localStorage.removeItem('__TEST__');\n storage = localStorage;\n} catch {\n // Fall back to memory storage if localStorage is not available\n storage = memoryStorage;\n}\n\nexport function saveAuth(username: string, token: string): void {\n storage.setItem('username', username);\n storage.setItem('token', token);\n}\n\nexport function clearAuth(): void {\n storage.removeItem('username');\n storage.removeItem('token');\n}\n\nexport function getAuth(): { username: string | null; token: string | null } {\n return {\n username: storage.getItem('username'),\n token: storage.getItem('token'),\n };\n}\n\nexport default storage;\n"],"mappings":";;;;AAEA,IAAI;AACJ,IAAI;CACF,aAAa,QAAQ,YAAY,EAAE;CACnC,aAAa,WAAW,UAAU;CAClC,UAAU;AACZ,QAAQ;CAEN,UAAUA,oBAAAA;AACZ;AAEA,SAAgB,SAAS,UAAkB,OAAqB;CAC9D,QAAQ,QAAQ,YAAY,QAAQ;CACpC,QAAQ,QAAQ,SAAS,KAAK;AAChC;AAEA,SAAgB,YAAkB;CAChC,QAAQ,WAAW,UAAU;CAC7B,QAAQ,WAAW,OAAO;AAC5B;AAEA,SAAgB,UAA6D;CAC3E,OAAO;EACL,UAAU,QAAQ,QAAQ,UAAU;EACpC,OAAO,QAAQ,QAAQ,OAAO;CAChC;AACF;AAEA,IAAA,kBAAe"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadable.js","names":[],"sources":["../../src/utils/loadable.tsx"],"sourcesContent":["import React, { Suspense, lazy } from 'react';\n\n/**\n * With the combination of a bundler, enable to code split a package.\n * \n * @example\n ```jsx\n const VersionPage = loadable(() => import(/'../pages/Version'));\n ```\n @category HOC\n */\nexport default (importCallback: any) => {\n const TargetComponent = lazy(importCallback);\n return (props: any) => (\n <Suspense fallback={null}>\n <TargetComponent {...props} />\n </Suspense>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAWA,IAAA,oBAAgB,mBAAwB;CACtC,MAAM,mBAAA,
|
|
1
|
+
{"version":3,"file":"loadable.js","names":["lazy","Suspense"],"sources":["../../src/utils/loadable.tsx"],"sourcesContent":["import React, { Suspense, lazy } from 'react';\n\n/**\n * With the combination of a bundler, enable to code split a package.\n * \n * @example\n ```jsx\n const VersionPage = loadable(() => import(/'../pages/Version'));\n ```\n @category HOC\n */\nexport default (importCallback: any) => {\n const TargetComponent = lazy(importCallback);\n return (props: any) => (\n <Suspense fallback={null}>\n <TargetComponent {...props} />\n </Suspense>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;AAWA,IAAA,oBAAgB,mBAAwB;CACtC,MAAM,mBAAA,GAAkBA,MAAAA,KAAAA,CAAK,cAAc;CAC3C,QAAQ,UACN,iBAAA,GAAA,kBAAA,IAAA,CAACC,MAAAA,UAAD;EAAU,UAAU;EAClB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,iBAAD,EAAiB,GAAI,MAAQ,CAAA;CACrB,CAAA;AAEd"}
|
package/build/utils/schemas.d.ts
CHANGED
|
@@ -22,12 +22,10 @@ export declare const addUserSchema: yup.ObjectSchema<{
|
|
|
22
22
|
}, "">;
|
|
23
23
|
export type AddUserFormValues = yup.InferType<typeof addUserSchema>;
|
|
24
24
|
export declare const changePasswordSchema: yup.ObjectSchema<{
|
|
25
|
-
username: string;
|
|
26
25
|
oldPassword: string;
|
|
27
26
|
newPassword: string;
|
|
28
27
|
confirmPassword: string;
|
|
29
28
|
}, yup.AnyObject, {
|
|
30
|
-
username: undefined;
|
|
31
29
|
oldPassword: undefined;
|
|
32
30
|
newPassword: undefined;
|
|
33
31
|
confirmPassword: undefined;
|
package/build/utils/schemas.js
CHANGED
|
@@ -13,7 +13,6 @@ var addUserSchema = yup.object({
|
|
|
13
13
|
email: yup.string().email("form-validation.invalid-email").required("form-validation.required-field")
|
|
14
14
|
});
|
|
15
15
|
var changePasswordSchema = yup.object({
|
|
16
|
-
username: usernameSchema,
|
|
17
16
|
oldPassword: passwordSchema,
|
|
18
17
|
newPassword: passwordSchema,
|
|
19
18
|
confirmPassword: yup.string().required("form-validation.required-field").oneOf([yup.ref("newPassword")], "security.error.password-mismatch")
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schemas.js","names":[],"sources":["../../src/utils/schemas.ts"],"sourcesContent":["import * as yup from 'yup';\n\nexport const USERNAME_MIN_LENGTH = 2;\nexport const PASSWORD_MIN_LENGTH = 2;\n\n// All messages are i18n keys; the form fields translate them on render with\n// t(message, { length }) so they follow the selected language.\nexport const usernameSchema = yup\n .string()\n .required('form-validation.required-field')\n .min(USERNAME_MIN_LENGTH, 'security.error.username-min-length')\n .matches(/^[-a-zA-Z0-9_.!~*'()@]+$/, 'security.error.username-must-be-url-safe');\n\nexport const passwordSchema = yup\n .string()\n .required('form-validation.required-field')\n .min(PASSWORD_MIN_LENGTH, 'form-validation.required-min-length');\n\nexport const loginSchema = yup.object({\n username: usernameSchema,\n password: passwordSchema,\n});\n\nexport type LoginFormValues = yup.InferType<typeof loginSchema>;\n\nexport const addUserSchema = yup.object({\n username: usernameSchema,\n password: passwordSchema,\n email: yup\n .string()\n .email('form-validation.invalid-email')\n .required('form-validation.required-field'),\n});\n\nexport type AddUserFormValues = yup.InferType<typeof addUserSchema>;\n\nexport const changePasswordSchema = yup.object({\n
|
|
1
|
+
{"version":3,"file":"schemas.js","names":[],"sources":["../../src/utils/schemas.ts"],"sourcesContent":["import * as yup from 'yup';\n\nexport const USERNAME_MIN_LENGTH = 2;\nexport const PASSWORD_MIN_LENGTH = 2;\n\n// All messages are i18n keys; the form fields translate them on render with\n// t(message, { length }) so they follow the selected language.\nexport const usernameSchema = yup\n .string()\n .required('form-validation.required-field')\n .min(USERNAME_MIN_LENGTH, 'security.error.username-min-length')\n .matches(/^[-a-zA-Z0-9_.!~*'()@]+$/, 'security.error.username-must-be-url-safe');\n\nexport const passwordSchema = yup\n .string()\n .required('form-validation.required-field')\n .min(PASSWORD_MIN_LENGTH, 'form-validation.required-min-length');\n\nexport const loginSchema = yup.object({\n username: usernameSchema,\n password: passwordSchema,\n});\n\nexport type LoginFormValues = yup.InferType<typeof loginSchema>;\n\nexport const addUserSchema = yup.object({\n username: usernameSchema,\n password: passwordSchema,\n email: yup\n .string()\n .email('form-validation.invalid-email')\n .required('form-validation.required-field'),\n});\n\nexport type AddUserFormValues = yup.InferType<typeof addUserSchema>;\n\nexport const changePasswordSchema = yup.object({\n oldPassword: passwordSchema,\n newPassword: passwordSchema,\n confirmPassword: yup\n .string()\n .required('form-validation.required-field')\n .oneOf([yup.ref('newPassword')], 'security.error.password-mismatch'),\n});\n\nexport type ChangePasswordFormValues = yup.InferType<typeof changePasswordSchema>;\n"],"mappings":";;;AAOA,IAAa,iBAAiB,IAC3B,OAAO,CAAC,CACR,SAAS,gCAAgC,CAAC,CAC1C,IAAA,GAAyB,oCAAoC,CAAC,CAC9D,QAAQ,4BAA4B,0CAA0C;AAEjF,IAAa,iBAAiB,IAC3B,OAAO,CAAC,CACR,SAAS,gCAAgC,CAAC,CAC1C,IAAA,GAAyB,qCAAqC;AAEjE,IAAa,cAAc,IAAI,OAAO;CACpC,UAAU;CACV,UAAU;AACZ,CAAC;AAID,IAAa,gBAAgB,IAAI,OAAO;CACtC,UAAU;CACV,UAAU;CACV,OAAO,IACJ,OAAO,CAAC,CACR,MAAM,+BAA+B,CAAC,CACtC,SAAS,gCAAgC;AAC9C,CAAC;AAID,IAAa,uBAAuB,IAAI,OAAO;CAC7C,aAAa;CACb,aAAa;CACb,iBAAiB,IACd,OAAO,CAAC,CACR,SAAS,gCAAgC,CAAC,CAC1C,MAAM,CAAC,IAAI,IAAI,aAAa,CAAC,GAAG,kCAAkC;AACvE,CAAC"}
|
package/build/utils/schemas.mjs
CHANGED
|
@@ -11,7 +11,6 @@ var addUserSchema = yup.object({
|
|
|
11
11
|
email: yup.string().email("form-validation.invalid-email").required("form-validation.required-field")
|
|
12
12
|
});
|
|
13
13
|
var changePasswordSchema = yup.object({
|
|
14
|
-
username: usernameSchema,
|
|
15
14
|
oldPassword: passwordSchema,
|
|
16
15
|
newPassword: passwordSchema,
|
|
17
16
|
confirmPassword: yup.string().required("form-validation.required-field").oneOf([yup.ref("newPassword")], "security.error.password-mismatch")
|