From d52c2ac2a6a53ccde1a29aa7ff878f1c43ceac2a Mon Sep 17 00:00:00 2001 From: ivan-aksamentov Date: Sun, 26 Feb 2023 07:21:40 +0100 Subject: [PATCH 1/5] chore: scaffold web app --- .dockerignore | 5 +- .nvmrc | 1 + .vercelignore | 21 + docker-dev | 41 + docker/docker-dev.dockerfile | 91 + vercel.json | 8 + web/.browserslistrc | 10 + web/.editorconfig | 17 + web/.env.example | 20 + web/.env.vercel | 20 + web/.eslintignore | 16 + web/.eslintrc.cjs | 264 + web/.gitignore | 12 + web/.prettierignore | 18 + web/.prettierrc | 9 + web/.yarnrc | 16 + web/config/dotenv/index.js | 6 + web/config/i18next/i18next.config.cjs | 66 + web/config/jest/jest.config.js | 35 + web/config/jest/jest.eslint.config.js | 33 + web/config/jest/jest.tests.config.js | 53 + web/config/jest/mocks/fileMock.js | 1 + web/config/jest/mocks/mockPopperJS.js | 11 + .../jest/mocks/mockReactChildrenUtilities.js | 3 + web/config/jest/mocks/mockUseDebounce.js | 3 + web/config/jest/setupDotenv.js | 1 + web/config/next/lib/CustomWebpackConfig.ts | 9 + web/config/next/lib/EmitFilePlugin.js | 141 + web/config/next/lib/addWebpackConfig.ts | 19 + web/config/next/lib/addWebpackLoader.ts | 15 + web/config/next/lib/addWebpackPlugin.ts | 25 + web/config/next/lib/getEnvVars.ts | 38 + .../next/loaders/removeDebugPackageLoader.cjs | 22 + web/config/next/next.config.ts | 183 + web/config/next/withCopy.ts | 11 + web/config/next/withExtraWatch.ts | 17 + web/config/next/withFriendlyChunkNames.ts | 17 + web/config/next/withFriendlyConsole.ts | 43 + web/config/next/withIgnore.ts | 19 + web/config/next/withResolve.ts | 24 + web/config/next/withRobotsTxt.ts | 15 + web/config/next/withSvg.ts | 19 + web/config/next/withTypeChecking.ts | 86 + web/config/next/withUrlAsset.ts | 10 + web/config/next/withWebpackWatchPoll.ts | 11 + web/config/next/withoutDebugPackage.ts | 16 + web/config/next/withoutMinification.ts | 13 + web/config/nodemon/dev.json | 28 + web/config/nodemon/eslint.fix.json | 29 + web/config/nodemon/eslint.json | 29 + web/config/nodemon/lint.fix.json | 27 + web/config/nodemon/lint.json | 27 + web/config/nodemon/prod.json | 37 + web/config/nodemon/profile.json | 37 + web/config/nodemon/serve.json | 17 + web/infra/find-lambda-at-edge-logs.sh | 12 + .../iam-role-trust-for-lambda-at-edge.json | 15 + web/infra/s3-cors-permissions.json | 9 + .../lambda-at-edge/OriginRequest.lambda.js | 44 + .../lambda-at-edge/ViewerResponse.lambda.js | 99 + web/json-autotranslate.json | 4 + web/lib/clear.js | 6 + web/lib/findModuleRoot.d.ts | 8 + web/lib/findModuleRoot.js | 18 + web/lib/getBuildNumber.ts | 5 + web/lib/getBuildUrl.ts | 5 + web/lib/getDomain.ts | 36 + web/lib/getGitBranch.ts | 28 + web/lib/getGitCommitHash.ts | 29 + web/lib/getenv.d.ts | 3 + web/lib/getenv.js | 47 + web/next-env.d.ts | 5 + web/next.config.mjs | 3 + web/package.json | 330 + web/postcss.config.json | 16 + web/public/.well-known/dnt-policy-1.0.txt | 218 + web/public/browserconfig.xml | 12 + web/public/favicon.ico | Bin 0 -> 15086 bytes web/public/favicon.png | Bin 0 -> 12410 bytes web/public/favicon.svg | 1 + web/public/icons/android-chrome-144x144.png | Bin 0 -> 6843 bytes web/public/icons/android-chrome-192x192.png | Bin 0 -> 9191 bytes web/public/icons/android-chrome-256x256.png | Bin 0 -> 12410 bytes web/public/icons/android-chrome-36x36.png | Bin 0 -> 1564 bytes web/public/icons/android-chrome-384x384.png | Bin 0 -> 19073 bytes web/public/icons/android-chrome-48x48.png | Bin 0 -> 1913 bytes web/public/icons/android-chrome-512x512.png | Bin 0 -> 25912 bytes web/public/icons/android-chrome-72x72.png | Bin 0 -> 3566 bytes web/public/icons/android-chrome-96x96.png | Bin 0 -> 4652 bytes .../apple-touch-icon-114x114-precomposed.png | Bin 0 -> 2949 bytes web/public/icons/apple-touch-icon-114x114.png | Bin 0 -> 2593 bytes .../apple-touch-icon-120x120-precomposed.png | Bin 0 -> 3015 bytes web/public/icons/apple-touch-icon-120x120.png | Bin 0 -> 2674 bytes .../apple-touch-icon-144x144-precomposed.png | Bin 0 -> 3545 bytes web/public/icons/apple-touch-icon-144x144.png | Bin 0 -> 3080 bytes .../apple-touch-icon-152x152-precomposed.png | Bin 0 -> 3780 bytes web/public/icons/apple-touch-icon-152x152.png | Bin 0 -> 3261 bytes .../apple-touch-icon-180x180-precomposed.png | Bin 0 -> 7122 bytes web/public/icons/apple-touch-icon-180x180.png | Bin 0 -> 3807 bytes .../apple-touch-icon-57x57-precomposed.png | Bin 0 -> 1708 bytes web/public/icons/apple-touch-icon-57x57.png | Bin 0 -> 1527 bytes .../apple-touch-icon-60x60-precomposed.png | Bin 0 -> 1767 bytes web/public/icons/apple-touch-icon-60x60.png | Bin 0 -> 1577 bytes .../apple-touch-icon-72x72-precomposed.png | Bin 0 -> 1997 bytes web/public/icons/apple-touch-icon-72x72.png | Bin 0 -> 1782 bytes .../apple-touch-icon-76x76-precomposed.png | Bin 0 -> 2158 bytes web/public/icons/apple-touch-icon-76x76.png | Bin 0 -> 1915 bytes .../icons/apple-touch-icon-precomposed.png | Bin 0 -> 7122 bytes web/public/icons/apple-touch-icon.png | Bin 0 -> 3807 bytes web/public/icons/favicon-128x128.png | Bin 0 -> 5123 bytes web/public/icons/favicon-16x16.png | Bin 0 -> 896 bytes web/public/icons/favicon-196x196.png | Bin 0 -> 9107 bytes web/public/icons/favicon-32x32.png | Bin 0 -> 1430 bytes web/public/icons/favicon-96x96.png | Bin 0 -> 3860 bytes web/public/icons/mstile-144x144.png | Bin 0 -> 6785 bytes web/public/icons/mstile-150x150.png | Bin 0 -> 6819 bytes web/public/icons/mstile-310x150.png | Bin 0 -> 7418 bytes web/public/icons/mstile-310x310.png | Bin 0 -> 14802 bytes web/public/icons/mstile-70x70.png | Bin 0 -> 4763 bytes web/public/icons/safari-pinned-tab.svg | 34 + web/public/manifest.json | 22 + web/src/assets/images/biozentrum.svg | 1 + web/src/assets/images/biozentrum_square.svg | 1 + web/src/assets/images/logo.svg | 1 + web/src/assets/images/neherlab.svg | 1 + web/src/assets/images/nextjs.svg | 1 + web/src/assets/images/sib.svg | 20 + web/src/assets/images/unibas.svg | 1 + web/src/assets/images/vercel.svg | 1 + web/src/assets/social/social-1200x630.png | Bin 0 -> 696 bytes web/src/components/Common/Checkbox.tsx | 122 + web/src/components/Common/ColoredBox.tsx | 11 + web/src/components/Common/ColoredCircle.tsx | 11 + web/src/components/Common/CountryFlag.tsx | 51 + web/src/components/Common/Dropdown.tsx | 99 + .../components/Common/DropdownWithSearch.tsx | 55 + web/src/components/Common/Link.tsx | 106 + web/src/components/Common/MdxComponents.tsx | 38 + web/src/components/Common/Plausible.tsx | 37 + web/src/components/Error/ErrorBoundary.tsx | 16 + web/src/components/Error/ErrorContent.tsx | 46 + .../Error/ErrorContentExplanation.tsx | 19 + web/src/components/Error/ErrorPage.tsx | 71 + web/src/components/Error/ErrorStyles.tsx | 20 + .../Error/error-types/ErrorGeneric.tsx | 21 + web/src/components/Home/HomePage.tsx | 30 + web/src/components/Layout/Footer.tsx | 129 + .../components/Layout/LanguageSwitcher.tsx | 76 + web/src/components/Layout/Layout.tsx | 36 + web/src/components/Layout/Loading.tsx | 60 + web/src/components/Layout/NavigationBar.tsx | 65 + web/src/components/Layout/PageContainer.tsx | 27 + web/src/constants.ts | 27 + web/src/helpers/ErrorInternal.ts | 9 + web/src/helpers/format.ts | 57 + web/src/helpers/getCopyrightYearRange.ts | 11 + web/src/helpers/getVersionString.ts | 27 + web/src/helpers/notUndefined.ts | 13 + web/src/helpers/safeZip.ts | 13 + web/src/helpers/sanitizeError.ts | 12 + web/src/helpers/setOperations.ts | 3 + web/src/helpers/takeFirstMaybe.ts | 16 + web/src/helpers/types.ts | 24 + web/src/helpers/useTranslationSafe.ts | 23 + web/src/hooks/useReloadPage.ts | 10 + web/src/hooks/useToggle.ts | 18 + web/src/i18n/additional_keys.json | 1 + web/src/i18n/detectLocale.ts | 40 + web/src/i18n/getMdxContent.tsx | 31 + web/src/i18n/i18n.ts | 192 + web/src/i18n/resources/ar/common.json | 1 + web/src/i18n/resources/de/common.json | 1 + web/src/i18n/resources/el/common.json | 1 + web/src/i18n/resources/en/common.json | 1 + web/src/i18n/resources/es/common.json | 1 + web/src/i18n/resources/fa/common.json | 1 + web/src/i18n/resources/fr/common.json | 1 + web/src/i18n/resources/he/common.json | 1 + web/src/i18n/resources/hi/common.json | 1 + web/src/i18n/resources/id/common.json | 1 + web/src/i18n/resources/it/common.json | 1 + web/src/i18n/resources/ja/common.json | 1 + web/src/i18n/resources/ko/common.json | 1 + web/src/i18n/resources/nl/common.json | 1 + web/src/i18n/resources/pt/common.json | 1 + web/src/i18n/resources/ru/common.json | 1 + web/src/i18n/resources/ta/common.json | 1 + web/src/i18n/resources/th/common.json | 1 + web/src/i18n/resources/tr/common.json | 1 + web/src/i18n/resources/ur/common.json | 1 + web/src/i18n/resources/vi/common.json | 1 + web/src/i18n/resources/zh/common.json | 1 + web/src/pages/404.tsx | 10 + web/src/pages/500.tsx | 8 + web/src/pages/_app.tsx | 71 + web/src/pages/_document.tsx | 137 + web/src/pages/_error.tsx | 1 + web/src/pages/index.tsx | 11 + web/src/seo/seoMetadata.ts | 23 + web/src/state/locale.state.ts | 26 + web/src/state/utils/isDefaultValue.ts | 5 + web/src/state/utils/localStorage.ts | 6 + web/src/styles/_mixins.scss | 13 + web/src/styles/_theme.scss | 417 + web/src/styles/_variables.scss | 118 + web/src/styles/bootstrap.scss | 48 + web/src/styles/font-family-system.scss | 25 + web/src/styles/global.scss | 65 + web/src/theme.ts | 196 + web/src/types.ts | 1 + web/src/types/@next/mdx.d.ts | 1 + .../@nuxt/friendly-errors-webpack-plugin.d.ts | 1 + web/src/types/all-contributorsrc.d.ts | 12 + web/src/types/allow-methods.d.ts | 7 + web/src/types/assets.d.ts | 19 + web/src/types/core-js.d.ts | 12 + .../types/country-flag-icons/react/3x2.d.ts | 5 + web/src/types/fasy.d.ts | 50 + web/src/types/fs-extra/esm.d.ts | 3 + web/src/types/images.d.ts | 1 + web/src/types/intersection-observer.d.ts | 1 + web/src/types/iso-3166-1-alpha-2.d.ts | 15 + web/src/types/jest.d.ts | 27 + web/src/types/json-module.d.ts | 4 + web/src/types/md-module.d.ts | 4 + web/src/types/mdx-module.d.ts | 4 + web/src/types/react-file-icon.d.ts | 177 + web/src/types/remark-images.d.ts | 1 + web/src/types/remark-slug.d.ts | 1 + web/src/types/styled.d.ts | 6 + web/src/types/svg-module.d.ts | 6 + web/tools/addLocaleKeys.ts | 25 + web/tools/fixLocales.ts | 165 + web/tools/monkeyPatch.ts | 170 + web/tools/server/dataServer.ts | 85 + web/tools/server/server.ts | 92 + web/tsconfig.eslint.json | 19 + web/tsconfig.json | 54 + web/tsconfig.ts-node.json | 40 + web/yarn.lock | 13614 ++++++++++++++++ 240 files changed, 20297 insertions(+), 1 deletion(-) create mode 100644 .nvmrc create mode 100644 .vercelignore create mode 100755 docker-dev create mode 100644 docker/docker-dev.dockerfile create mode 100644 vercel.json create mode 100644 web/.browserslistrc create mode 100644 web/.editorconfig create mode 100644 web/.env.example create mode 100644 web/.env.vercel create mode 100644 web/.eslintignore create mode 100644 web/.eslintrc.cjs create mode 100644 web/.gitignore create mode 100644 web/.prettierignore create mode 100644 web/.prettierrc create mode 100644 web/.yarnrc create mode 100644 web/config/dotenv/index.js create mode 100644 web/config/i18next/i18next.config.cjs create mode 100644 web/config/jest/jest.config.js create mode 100644 web/config/jest/jest.eslint.config.js create mode 100644 web/config/jest/jest.tests.config.js create mode 100644 web/config/jest/mocks/fileMock.js create mode 100644 web/config/jest/mocks/mockPopperJS.js create mode 100644 web/config/jest/mocks/mockReactChildrenUtilities.js create mode 100644 web/config/jest/mocks/mockUseDebounce.js create mode 100644 web/config/jest/setupDotenv.js create mode 100644 web/config/next/lib/CustomWebpackConfig.ts create mode 100644 web/config/next/lib/EmitFilePlugin.js create mode 100644 web/config/next/lib/addWebpackConfig.ts create mode 100644 web/config/next/lib/addWebpackLoader.ts create mode 100644 web/config/next/lib/addWebpackPlugin.ts create mode 100644 web/config/next/lib/getEnvVars.ts create mode 100644 web/config/next/loaders/removeDebugPackageLoader.cjs create mode 100644 web/config/next/next.config.ts create mode 100644 web/config/next/withCopy.ts create mode 100644 web/config/next/withExtraWatch.ts create mode 100644 web/config/next/withFriendlyChunkNames.ts create mode 100644 web/config/next/withFriendlyConsole.ts create mode 100644 web/config/next/withIgnore.ts create mode 100644 web/config/next/withResolve.ts create mode 100644 web/config/next/withRobotsTxt.ts create mode 100644 web/config/next/withSvg.ts create mode 100644 web/config/next/withTypeChecking.ts create mode 100644 web/config/next/withUrlAsset.ts create mode 100644 web/config/next/withWebpackWatchPoll.ts create mode 100644 web/config/next/withoutDebugPackage.ts create mode 100644 web/config/next/withoutMinification.ts create mode 100644 web/config/nodemon/dev.json create mode 100644 web/config/nodemon/eslint.fix.json create mode 100644 web/config/nodemon/eslint.json create mode 100644 web/config/nodemon/lint.fix.json create mode 100644 web/config/nodemon/lint.json create mode 100644 web/config/nodemon/prod.json create mode 100644 web/config/nodemon/profile.json create mode 100644 web/config/nodemon/serve.json create mode 100755 web/infra/find-lambda-at-edge-logs.sh create mode 100644 web/infra/iam-role-trust-for-lambda-at-edge.json create mode 100644 web/infra/s3-cors-permissions.json create mode 100644 web/infra/web/lambda-at-edge/OriginRequest.lambda.js create mode 100644 web/infra/web/lambda-at-edge/ViewerResponse.lambda.js create mode 100644 web/json-autotranslate.json create mode 100644 web/lib/clear.js create mode 100644 web/lib/findModuleRoot.d.ts create mode 100644 web/lib/findModuleRoot.js create mode 100644 web/lib/getBuildNumber.ts create mode 100644 web/lib/getBuildUrl.ts create mode 100644 web/lib/getDomain.ts create mode 100644 web/lib/getGitBranch.ts create mode 100644 web/lib/getGitCommitHash.ts create mode 100644 web/lib/getenv.d.ts create mode 100644 web/lib/getenv.js create mode 100644 web/next-env.d.ts create mode 100644 web/next.config.mjs create mode 100644 web/package.json create mode 100644 web/postcss.config.json create mode 100644 web/public/.well-known/dnt-policy-1.0.txt create mode 100644 web/public/browserconfig.xml create mode 100644 web/public/favicon.ico create mode 100644 web/public/favicon.png create mode 100644 web/public/favicon.svg create mode 100644 web/public/icons/android-chrome-144x144.png create mode 100644 web/public/icons/android-chrome-192x192.png create mode 100644 web/public/icons/android-chrome-256x256.png create mode 100644 web/public/icons/android-chrome-36x36.png create mode 100644 web/public/icons/android-chrome-384x384.png create mode 100644 web/public/icons/android-chrome-48x48.png create mode 100644 web/public/icons/android-chrome-512x512.png create mode 100644 web/public/icons/android-chrome-72x72.png create mode 100644 web/public/icons/android-chrome-96x96.png create mode 100644 web/public/icons/apple-touch-icon-114x114-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-114x114.png create mode 100644 web/public/icons/apple-touch-icon-120x120-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-120x120.png create mode 100644 web/public/icons/apple-touch-icon-144x144-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-144x144.png create mode 100644 web/public/icons/apple-touch-icon-152x152-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-152x152.png create mode 100644 web/public/icons/apple-touch-icon-180x180-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-180x180.png create mode 100644 web/public/icons/apple-touch-icon-57x57-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-57x57.png create mode 100644 web/public/icons/apple-touch-icon-60x60-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-60x60.png create mode 100644 web/public/icons/apple-touch-icon-72x72-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-72x72.png create mode 100644 web/public/icons/apple-touch-icon-76x76-precomposed.png create mode 100644 web/public/icons/apple-touch-icon-76x76.png create mode 100644 web/public/icons/apple-touch-icon-precomposed.png create mode 100644 web/public/icons/apple-touch-icon.png create mode 100644 web/public/icons/favicon-128x128.png create mode 100644 web/public/icons/favicon-16x16.png create mode 100644 web/public/icons/favicon-196x196.png create mode 100644 web/public/icons/favicon-32x32.png create mode 100644 web/public/icons/favicon-96x96.png create mode 100644 web/public/icons/mstile-144x144.png create mode 100644 web/public/icons/mstile-150x150.png create mode 100644 web/public/icons/mstile-310x150.png create mode 100644 web/public/icons/mstile-310x310.png create mode 100644 web/public/icons/mstile-70x70.png create mode 100644 web/public/icons/safari-pinned-tab.svg create mode 100644 web/public/manifest.json create mode 100644 web/src/assets/images/biozentrum.svg create mode 100644 web/src/assets/images/biozentrum_square.svg create mode 100644 web/src/assets/images/logo.svg create mode 100644 web/src/assets/images/neherlab.svg create mode 100644 web/src/assets/images/nextjs.svg create mode 100644 web/src/assets/images/sib.svg create mode 100644 web/src/assets/images/unibas.svg create mode 100644 web/src/assets/images/vercel.svg create mode 100644 web/src/assets/social/social-1200x630.png create mode 100644 web/src/components/Common/Checkbox.tsx create mode 100644 web/src/components/Common/ColoredBox.tsx create mode 100644 web/src/components/Common/ColoredCircle.tsx create mode 100644 web/src/components/Common/CountryFlag.tsx create mode 100644 web/src/components/Common/Dropdown.tsx create mode 100644 web/src/components/Common/DropdownWithSearch.tsx create mode 100644 web/src/components/Common/Link.tsx create mode 100644 web/src/components/Common/MdxComponents.tsx create mode 100644 web/src/components/Common/Plausible.tsx create mode 100644 web/src/components/Error/ErrorBoundary.tsx create mode 100644 web/src/components/Error/ErrorContent.tsx create mode 100644 web/src/components/Error/ErrorContentExplanation.tsx create mode 100644 web/src/components/Error/ErrorPage.tsx create mode 100644 web/src/components/Error/ErrorStyles.tsx create mode 100644 web/src/components/Error/error-types/ErrorGeneric.tsx create mode 100644 web/src/components/Home/HomePage.tsx create mode 100644 web/src/components/Layout/Footer.tsx create mode 100644 web/src/components/Layout/LanguageSwitcher.tsx create mode 100644 web/src/components/Layout/Layout.tsx create mode 100644 web/src/components/Layout/Loading.tsx create mode 100644 web/src/components/Layout/NavigationBar.tsx create mode 100644 web/src/components/Layout/PageContainer.tsx create mode 100644 web/src/constants.ts create mode 100644 web/src/helpers/ErrorInternal.ts create mode 100644 web/src/helpers/format.ts create mode 100644 web/src/helpers/getCopyrightYearRange.ts create mode 100644 web/src/helpers/getVersionString.ts create mode 100644 web/src/helpers/notUndefined.ts create mode 100644 web/src/helpers/safeZip.ts create mode 100644 web/src/helpers/sanitizeError.ts create mode 100644 web/src/helpers/setOperations.ts create mode 100644 web/src/helpers/takeFirstMaybe.ts create mode 100644 web/src/helpers/types.ts create mode 100644 web/src/helpers/useTranslationSafe.ts create mode 100644 web/src/hooks/useReloadPage.ts create mode 100644 web/src/hooks/useToggle.ts create mode 100644 web/src/i18n/additional_keys.json create mode 100644 web/src/i18n/detectLocale.ts create mode 100644 web/src/i18n/getMdxContent.tsx create mode 100644 web/src/i18n/i18n.ts create mode 100644 web/src/i18n/resources/ar/common.json create mode 100644 web/src/i18n/resources/de/common.json create mode 100644 web/src/i18n/resources/el/common.json create mode 100644 web/src/i18n/resources/en/common.json create mode 100644 web/src/i18n/resources/es/common.json create mode 100644 web/src/i18n/resources/fa/common.json create mode 100644 web/src/i18n/resources/fr/common.json create mode 100644 web/src/i18n/resources/he/common.json create mode 100644 web/src/i18n/resources/hi/common.json create mode 100644 web/src/i18n/resources/id/common.json create mode 100644 web/src/i18n/resources/it/common.json create mode 100644 web/src/i18n/resources/ja/common.json create mode 100644 web/src/i18n/resources/ko/common.json create mode 100644 web/src/i18n/resources/nl/common.json create mode 100644 web/src/i18n/resources/pt/common.json create mode 100644 web/src/i18n/resources/ru/common.json create mode 100644 web/src/i18n/resources/ta/common.json create mode 100644 web/src/i18n/resources/th/common.json create mode 100644 web/src/i18n/resources/tr/common.json create mode 100644 web/src/i18n/resources/ur/common.json create mode 100644 web/src/i18n/resources/vi/common.json create mode 100644 web/src/i18n/resources/zh/common.json create mode 100644 web/src/pages/404.tsx create mode 100644 web/src/pages/500.tsx create mode 100644 web/src/pages/_app.tsx create mode 100644 web/src/pages/_document.tsx create mode 100644 web/src/pages/_error.tsx create mode 100644 web/src/pages/index.tsx create mode 100644 web/src/seo/seoMetadata.ts create mode 100644 web/src/state/locale.state.ts create mode 100644 web/src/state/utils/isDefaultValue.ts create mode 100644 web/src/state/utils/localStorage.ts create mode 100644 web/src/styles/_mixins.scss create mode 100644 web/src/styles/_theme.scss create mode 100644 web/src/styles/_variables.scss create mode 100644 web/src/styles/bootstrap.scss create mode 100644 web/src/styles/font-family-system.scss create mode 100644 web/src/styles/global.scss create mode 100644 web/src/theme.ts create mode 100644 web/src/types.ts create mode 100644 web/src/types/@next/mdx.d.ts create mode 100644 web/src/types/@nuxt/friendly-errors-webpack-plugin.d.ts create mode 100644 web/src/types/all-contributorsrc.d.ts create mode 100644 web/src/types/allow-methods.d.ts create mode 100644 web/src/types/assets.d.ts create mode 100644 web/src/types/core-js.d.ts create mode 100644 web/src/types/country-flag-icons/react/3x2.d.ts create mode 100644 web/src/types/fasy.d.ts create mode 100644 web/src/types/fs-extra/esm.d.ts create mode 100644 web/src/types/images.d.ts create mode 100644 web/src/types/intersection-observer.d.ts create mode 100644 web/src/types/iso-3166-1-alpha-2.d.ts create mode 100644 web/src/types/jest.d.ts create mode 100644 web/src/types/json-module.d.ts create mode 100644 web/src/types/md-module.d.ts create mode 100644 web/src/types/mdx-module.d.ts create mode 100644 web/src/types/react-file-icon.d.ts create mode 100644 web/src/types/remark-images.d.ts create mode 100644 web/src/types/remark-slug.d.ts create mode 100644 web/src/types/styled.d.ts create mode 100644 web/src/types/svg-module.d.ts create mode 100644 web/tools/addLocaleKeys.ts create mode 100644 web/tools/fixLocales.ts create mode 100644 web/tools/monkeyPatch.ts create mode 100644 web/tools/server/dataServer.ts create mode 100644 web/tools/server/server.ts create mode 100644 web/tsconfig.eslint.json create mode 100644 web/tsconfig.json create mode 100644 web/tsconfig.ts-node.json create mode 100644 web/yarn.lock diff --git a/.dockerignore b/.dockerignore index 7d6f6c59..66c17e07 100644 --- a/.dockerignore +++ b/.dockerignore @@ -16,4 +16,7 @@ /pangraph.tar.gz /vendor /playgrounds -/script \ No newline at end of file +/script +\.build/ +\.cache/ +node_modules/ diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 00000000..72e4a483 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +18.14.2 diff --git a/.vercelignore b/.vercelignore new file mode 100644 index 00000000..60317849 --- /dev/null +++ b/.vercelignore @@ -0,0 +1,21 @@ +/.cache +/.dep +/.env +/.github +/.idea +/.local +/.vscode* +/Dockerfile +/data +/deps/minimap2/build +/deps/minimap2/products +/docs +/example_datasets +/pangraph +/pangraph.tar.gz +/vendor +/playgrounds +/script +\.build/ +\.cache/ +node_modules/ diff --git a/docker-dev b/docker-dev new file mode 100755 index 00000000..2007493f --- /dev/null +++ b/docker-dev @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +set -euo pipefail + +export NICE="nice -14 ionice -c2 -n3" + +DOCKER_REPO="neherlab/pangraph" +DOCKER_IMAGE_NAME_SAFE="${DOCKER_REPO//\//-}" +DOCKER_CONTAINER_NAME="${DOCKER_IMAGE_NAME_SAFE}-$(date +%s)" + +USER="user" +GROUP="group" + +DEFAULT_COMMAND="cd web && yarn install && yarn dev" + +${NICE} docker build -q \ + --file="docker/docker-dev.dockerfile" \ + --tag="${DOCKER_REPO}" \ + --network=host \ + --build-arg="UID=$(id -u)" \ + --build-arg="GID=$(id -g)" \ + --build-arg="USER=${USER}" \ + --build-arg="GROUP=${GROUP}" \ + "$(pwd)" >/dev/null + +${NICE} docker run -it --rm \ + --network=host \ + --init \ + --name="${DOCKER_CONTAINER_NAME}" \ + --hostname="${DOCKER_IMAGE_NAME_SAFE}" \ + --user="$(id -u):$(id -g)" \ + --volume="$(pwd):/workdir" \ + --workdir="/workdir" \ + --env="UID=$(id -u)" \ + --env="GID=$(id -g)" \ + --env="USER=${USER}" \ + --env="GROUP=${GROUP}" \ + --env="PS1=\${USER}@\${HOST}" \ + --ulimit core=0 \ + "${DOCKER_REPO}" \ + bash -c "set -euo pipefail; ${*:-${DEFAULT_COMMAND}}" diff --git a/docker/docker-dev.dockerfile b/docker/docker-dev.dockerfile new file mode 100644 index 00000000..2e9e28a2 --- /dev/null +++ b/docker/docker-dev.dockerfile @@ -0,0 +1,91 @@ +# Freeze base image version to +# ubuntu:20.04 (pushed 2023-02-01T17:52:39.31842Z) +# https://hub.docker.com/layers/ubuntu/library/ubuntu/20.04/images/sha256-bffb6799d706144f263f4b91e1226745ffb5643ea0ea89c2f709208e8d70c999 +FROM ubuntu@sha256:bffb6799d706144f263f4b91e1226745ffb5643ea0ea89c2f709208e8d70c999 + +SHELL ["bash", "-euxo", "pipefail", "-c"] + +ARG NODEMON_VERSION="2.0.15" +ARG YARN_VERSION="1.22.18" + +RUN set -euxo pipefail >/dev/null \ +&& export DEBIAN_FRONTEND=noninteractive \ +&& apt-get update -qq --yes \ +&& apt-get install -qq --no-install-recommends --yes \ + bash \ + bash-completion \ + build-essential \ + ca-certificates \ + curl \ + git \ + gnupg \ + python3 \ + python3-pip \ + python3-setuptools \ + python3-wheel \ + sudo \ + time \ +>/dev/null \ +&& apt-get clean autoclean >/dev/null \ +&& apt-get autoremove --yes >/dev/null \ +&& rm -rf /var/lib/apt/lists/* + +ARG USER=user +ARG GROUP=user +ARG UID +ARG GID + +ENV USER=$USER +ENV GROUP=$GROUP +ENV UID=$UID +ENV GID=$GID +ENV TERM="xterm-256color" +ENV HOME="/home/${USER}" +ENV NODE_DIR="/opt/node" +ENV PATH="${NODE_DIR}/bin:${HOME}/.local/bin:${PATH}" + + +# Install Node.js +COPY .nvmrc / +RUN set -eux >dev/null \ +&& mkdir -p "${NODE_DIR}" \ +&& cd "${NODE_DIR}" \ +&& NODE_VERSION=$(cat /.nvmrc) \ +&& curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" | tar -xJ --strip-components=1 \ +&& npm install -g nodemon@${NODEMON_VERSION} yarn@${YARN_VERSION} >/dev/null + + +# Calm down the (in)famous chatter from yarn +RUN set -euxo pipefail >/dev/null \ +&& sed -i'' "s/this.reporter.warn(this.reporter.lang('incompatibleResolutionVersion', pattern, reqPattern));//g" "${NODE_DIR}/lib/node_modules/yarn/lib/cli.js" \ +&& sed -i'' "s/_this2\.reporter.warn(_this2\.reporter.lang('ignoredScripts'));//g" "${NODE_DIR}/lib/node_modules/yarn/lib/cli.js" \ +&& sed -i'' 's/_this3\.reporter\.warn(_this3\.reporter\.lang(peerError.*;//g' "/opt/node/lib/node_modules/yarn/lib/cli.js" + + +# Make a user and group +RUN set -euxo pipefail >/dev/null \ +&& \ + if [ -z "$(getent group ${GID})" ]; then \ + addgroup --system --gid ${GID} ${GROUP}; \ + else \ + groupmod -n ${GROUP} $(getent group ${GID} | cut -d: -f1); \ + fi \ +&& \ + if [ -z "$(getent passwd ${UID})" ]; then \ + useradd \ + --system \ + --create-home --home-dir ${HOME} \ + --shell /bin/bash \ + --gid ${GROUP} \ + --groups sudo \ + --uid ${UID} \ + ${USER}; \ + fi \ +&& sed -i /etc/sudoers -re 's/^%sudo.*/%sudo ALL=(ALL:ALL) NOPASSWD: ALL/g' \ +&& sed -i /etc/sudoers -re 's/^root.*/root ALL=(ALL:ALL) NOPASSWD: ALL/g' \ +&& sed -i /etc/sudoers -re 's/^#includedir.*/## **Removed the include directive** ##"/g' \ +&& echo "foo ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers \ +&& touch ${HOME}/.hushlogin \ +&& chown -R ${UID}:${GID} "${HOME}" + +USER ${USER} diff --git a/vercel.json b/vercel.json new file mode 100644 index 00000000..1323cdac --- /dev/null +++ b/vercel.json @@ -0,0 +1,8 @@ +{ + "rewrites": [ + { + "source": "/(.*)", + "destination": "/index.html" + } + ] +} diff --git a/web/.browserslistrc b/web/.browserslistrc new file mode 100644 index 00000000..347e11d3 --- /dev/null +++ b/web/.browserslistrc @@ -0,0 +1,10 @@ +[production] +defaults +cover 99.5% +since 2015 +ie >= 10 +safari >= 10 + +[development] +last 5 Chrome versions +last 5 Firefox versions diff --git a/web/.editorconfig b/web/.editorconfig new file mode 100644 index 00000000..eb3edb17 --- /dev/null +++ b/web/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space +insert_final_newline = true +max_line_length = 120 +trim_trailing_whitespace = true + +[{*.txt,*.rst,*.md,*.mdx,*.tex}] +max_line_length = 9999999 +trim_trailing_whitespace = false + +[{Makefile,*.makefile,go.mod,go.sum,*.go}] +indent_style = tab diff --git a/web/.env.example b/web/.env.example new file mode 100644 index 00000000..2b9ca3c9 --- /dev/null +++ b/web/.env.example @@ -0,0 +1,20 @@ +FULL_DOMAIN=autodetect +DATA_ROOT_URL=/data + +WEB_PORT_DEV=3000 +WEB_PORT_PROD=8080 +WEB_PORT_ANALYZE=8888 + +NEXT_TELEMETRY_DISABLED=0 +RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false + +DEV_ENABLE_TYPE_CHECKS=1 +DEV_ENABLE_ESLINT=1 + +PROD_ENABLE_SOURCE_MAPS=1 +PROD_ENABLE_TYPE_CHECKS=1 +PROD_ENABLE_ESLINT=1 + +PROFILE=0 + +WATCH_POLL=0 diff --git a/web/.env.vercel b/web/.env.vercel new file mode 100644 index 00000000..79584de3 --- /dev/null +++ b/web/.env.vercel @@ -0,0 +1,20 @@ +FULL_DOMAIN=autodetect +DATA_ROOT_URL=/data + +WEB_PORT_DEV=3000 +WEB_PORT_PROD=null +WEB_PORT_ANALYZE=8888 + +NEXT_TELEMETRY_DISABLED=0 +RECOIL_DUPLICATE_ATOM_KEY_CHECKING_ENABLED=false + +DEV_ENABLE_TYPE_CHECKS=1 +DEV_ENABLE_ESLINT=1 + +PROD_ENABLE_SOURCE_MAPS=1 +PROD_ENABLE_TYPE_CHECKS=0 +PROD_ENABLE_ESLINT=0 + +PROFILE=0 + +WATCH_POLL=0 diff --git a/web/.eslintignore b/web/.eslintignore new file mode 100644 index 00000000..3bad4a62 --- /dev/null +++ b/web/.eslintignore @@ -0,0 +1,16 @@ +3rdparty +\.build +\.cache +\.env +\.eslintrc.js +\.github +\.idea +\.ignore +\.reports +\.vscode +config/next/lib/EmitFilePlugin.js +infra/lambda-at-edge/basicAuth.js +node_modules +public +styles +tsconfig.json diff --git a/web/.eslintrc.cjs b/web/.eslintrc.cjs new file mode 100644 index 00000000..67afd63d --- /dev/null +++ b/web/.eslintrc.cjs @@ -0,0 +1,264 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + sourceType: 'module', + ecmaVersion: 'latest', + ecmaFeatures: { + jsx: true, + globalReturn: false, + }, + project: './tsconfig.eslint.json', + warnOnUnsupportedTypeScriptVersion: true, + }, + globals: {}, + extends: [ + 'eslint:recommended', + + 'airbnb', + 'airbnb-typescript', + 'airbnb/hooks', + 'react-app', + + 'next/core-web-vitals', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + 'plugin:array-func/all', + 'plugin:import/errors', + 'plugin:import/typescript', + 'plugin:import/warnings', + 'plugin:jest/recommended', + 'plugin:jest/style', + 'plugin:jsx-a11y/recommended', + 'plugin:lodash/recommended', + 'plugin:promise/recommended', + 'plugin:react/recommended', + 'plugin:react-perf/all', + 'plugin:security/recommended', + 'plugin:sonarjs/recommended', + 'plugin:unicorn/recommended', + + 'plugin:prettier/recommended', + ], + plugins: [ + 'array-func', + 'cflint', + 'import', + 'jest', + 'jsx-a11y', + 'lodash', + 'no-loops', + 'no-secrets', + 'node', + 'only-ascii', + 'promise', + 'react', + 'react-hooks', + 'react-perf', + 'security', + 'sonarjs', + 'unicorn', + + 'only-warn', + + '@typescript-eslint', + + 'prettier', + ], + reportUnusedDisableDirectives: true, + rules: { + '@next/next/no-img-element': 'off', + '@next/next/no-title-in-document-head': 'off', + '@typescript-eslint/array-type': 'off', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-module-boundary-types': 'off', + '@typescript-eslint/lines-between-class-members': 'off', + '@typescript-eslint/naming-convention': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-shadow': 'off', + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + '@typescript-eslint/unbound-method': ['off'], + 'array-func/no-unnecessary-this-arg': 'off', + 'array-func/prefer-array-from': 'off', + 'camelcase': 'off', + 'cflint/no-substr': 'warn', + 'cflint/no-this-assignment': 'warn', + 'import/extensions': [ + 'warn', + 'ignorePackages', + { + js: 'never', + jsx: 'never', + mjs: 'never', + ts: 'never', + tsx: 'never', + }, + ], + 'import/no-extraneous-dependencies': ['warn', { devDependencies: true }], + 'import/no-unresolved': 'off', + 'import/no-webpack-loader-syntax': 'off', + 'import/order': 'warn', + 'import/prefer-default-export': 'off', + 'jest/consistent-test-it': 'warn', + 'jest/expect-expect': 'warn', + 'jest/no-done-callback': 'warn', + 'jsx-a11y/label-has-associated-control': ['warn', { assert: 'either' }], + 'lodash/chaining': 'off', + 'lodash/import-scope': 'off', + 'lodash/prefer-constant': 'off', + 'lodash/prefer-lodash-chain': 'off', + 'lodash/prefer-lodash-method': 'off', + 'lodash/prefer-lodash-typecheck': 'off', + 'lodash/prefer-noop': 'off', + 'lodash/prop-shorthand': 'off', + 'max-classes-per-file': 'off', + 'no-console': ['warn', { allow: ['info', 'warn', 'error'] }], + 'no-loops/no-loops': 'warn', + 'no-param-reassign': ['warn', { ignorePropertyModificationsFor: ['draft'] }], + 'no-secrets/no-secrets': ['warn', { tolerance: 5 }], + 'no-shadow': 'off', + 'only-ascii/only-ascii': 'warn', + 'prefer-for-of': 'off', + 'prettier/prettier': 'warn', + 'react/jsx-curly-brace-presence': 'off', + 'react/jsx-filename-extension': ['warn', { extensions: ['.js', '.jsx', '.ts', '.tsx'] }], + 'react/jsx-props-no-spreading': 'off', + 'react/no-unused-prop-types': 'off', + 'react/prop-types': 'off', + 'react/react-in-jsx-scope': 'off', + 'react/require-default-props': 'off', + 'react/state-in-constructor': 'off', + 'security/detect-non-literal-fs-filename': 'off', + 'security/detect-object-injection': 'off', + 'sonarjs/cognitive-complexity': ['warn', 20], + 'unicorn/escape-case': 'off', + 'unicorn/filename-case': 'off', + 'unicorn/import-style': 'off', + 'unicorn/new-for-builtins': 'off', + 'unicorn/no-abusive-eslint-disable': 'warn', + 'unicorn/no-array-callback-reference': 'off', + 'unicorn/no-array-for-each': 'off', + 'unicorn/no-array-method-this-argument': 'off', + 'unicorn/no-array-reduce': 'off', + 'unicorn/no-fn-reference-in-iterator': 'off', + 'unicorn/no-negated-condition': 'off', + 'unicorn/no-null': 'off', + 'unicorn/no-reduce': 'off', + 'unicorn/no-typeof-undefined': 'off', + 'unicorn/no-useless-undefined': 'off', + 'unicorn/no-zero-fractions': 'off', + 'unicorn/prefer-node-protocol': 'off', + 'unicorn/prefer-query-selector': 'off', + 'unicorn/prefer-spread': 'off', + 'unicorn/prevent-abbreviations': 'off', + + 'lines-between-class-members': ['warn', 'always', { exceptAfterSingleLine: true }], + + 'require-await': 'off', + '@typescript-eslint/require-await': 'off', + + 'no-unused-expressions': 'off', + '@typescript-eslint/no-unused-expressions': 'warn', + + '@typescript-eslint/no-duplicate-imports': 'off', + }, + env: { + browser: true, + es6: true, + jest: true, + node: true, + }, + settings: { + 'react': { + version: 'detect', + }, + 'import/parsers': { + '@typescript-eslint/parser': ['.js', '.jsx', '.ts', '.tsx'], + }, + 'import/resolver': { + typescript: { + alwaysTryTypes: true, + }, + }, + }, + overrides: [ + { + files: ['src/pages/**/*', 'src/types/**/*'], + rules: { + 'no-restricted-exports': 'off', + }, + }, + { + files: ['*.d.ts'], + rules: { + '@typescript-eslint/ban-types': ['warn', { extendDefaults: true, types: { object: false } }], + '@typescript-eslint/no-explicit-any': 'off', + '@typescript-eslint/no-unused-vars': 'off', + 'import/no-duplicates': 'off', + 'no-useless-constructor': 'off', + 'react/prefer-stateless-function': 'off', + }, + }, + { + files: [ + '.eslintrc.js', + 'babel-node.config.js', + 'config/**/*.js', + 'config/**/*.ts', + 'config/jest/mocks/**/*.js', + 'infra/**/*.js', + 'jest-runner-eslint.config.js', + 'jest.config.js', + 'lib/EnvVarError.js', + 'lib/findModuleRoot.js', + 'lib/getenv.js', + 'next.config.mjs', + 'postcss.config.cjs', + 'tools/**/*.js', + 'tools/**/*.ts', + 'webpack.config.js', + ], + rules: { + '@typescript-eslint/no-unsafe-argument': 'off', + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + 'global-require': 'off', + 'import/extensions': 'off', + 'import/no-anonymous-default-export': 'off', + 'import/no-import-module-exports': 'off', + 'security/detect-child-process': 'off', + 'sonarjs/cognitive-complexity': ['warn', 50], + 'unicorn/prefer-module': 'off', + }, + }, + { + files: ['config/jest/mocks/**/*.js'], + rules: { + 'no-constructor-return': 'off', + 'react/display-name': 'off', + }, + }, + { + files: ['**/*.test.*', '**/__test__/**', '**/__tests__/**', '**/test/**', '**/tests/**'], + rules: { + '@typescript-eslint/no-unsafe-assignment': 'off', + '@typescript-eslint/no-unsafe-call': 'off', + '@typescript-eslint/no-unsafe-member-access': 'off', + '@typescript-eslint/no-unsafe-return': 'off', + '@typescript-eslint/restrict-template-expressions': 'off', + 'sonarjs/no-duplicate-string': 'off', + 'sonarjs/no-identical-functions': 'off', + }, + }, + ], +} diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 00000000..6a2f200b --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,12 @@ +*.log +.DS_Store +\.build +\.cache +\.env +\.idea +\.ignore +\.json-autotranslate-cache/ +\.reports +\.vscode +dist/ +node_modules diff --git a/web/.prettierignore b/web/.prettierignore new file mode 100644 index 00000000..4315e0f6 --- /dev/null +++ b/web/.prettierignore @@ -0,0 +1,18 @@ +*.lock +*.log +\.build +\.cache +\.editorconfig +\.env +\.git +\.gitignore +\.idea +\.ignore +\.prettierignore +\.reports +\.vscode +node_modules +src/assets/data +tools/augur_profile +/data +/public diff --git a/web/.prettierrc b/web/.prettierrc new file mode 100644 index 00000000..bc719f53 --- /dev/null +++ b/web/.prettierrc @@ -0,0 +1,9 @@ +{ + "arrowParens": "always", + "printWidth": 120, + "proseWrap": "always", + "quoteProps": "consistent", + "semi": false, + "singleQuote": true, + "trailingComma": "all" +} diff --git a/web/.yarnrc b/web/.yarnrc new file mode 100644 index 00000000..2a99e5de --- /dev/null +++ b/web/.yarnrc @@ -0,0 +1,16 @@ +# Don't check for updates of yarn itself +disable-self-update-check true + +# Install exact versions (not prefixed with '^') +save-prefix "" + +# Don't run post-install scripts (for security reasons) +--install.ignore-scripts true +--add.ignore-scripts true +--remove.ignore-scripts true + +# Don't check Node version +ignore-engines true + +# Keep yarn cache locally for faster and more reproducible builds +cache-folder ".cache/yarn" diff --git a/web/config/dotenv/index.js b/web/config/dotenv/index.js new file mode 100644 index 00000000..23862785 --- /dev/null +++ b/web/config/dotenv/index.js @@ -0,0 +1,6 @@ +import { join } from 'path' +import { config } from 'dotenv' +import { findModuleRoot } from '../../lib/findModuleRoot' + +const { moduleRoot } = findModuleRoot() +config({ path: join(moduleRoot, '.env') }) diff --git a/web/config/i18next/i18next.config.cjs b/web/config/i18next/i18next.config.cjs new file mode 100644 index 00000000..b6fd483f --- /dev/null +++ b/web/config/i18next/i18next.config.cjs @@ -0,0 +1,66 @@ +module.exports = { + contextSeparator: '_', + // Key separator used in your translation keys + + createOldCatalogs: false, + // Save the \_old files + + defaultNamespace: 'translation', + // Default namespace used in your i18next config + + // defaultValue: '', + // Default value to give to empty keys + + indentation: 2, + // Indentation of the catalog files + + keepRemoved: false, + // Keep keys from the catalog that are no longer in code + + keySeparator: false, + // Key separator used in your translation keys + // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. + + // see below for more details + lexers: { + js: ['JavascriptLexer'], // if you're writing jsx inside .js files, change this to JsxLexer + ts: ['JavascriptLexer'], + jsx: ['JsxLexer'], + tsx: ['JsxLexer'], + + default: ['JavascriptLexer'], + }, + + lineEnding: '\n', + // Control the line ending. See options at https://github.com/ryanve/eol + + locales: ['en'], + // An array of the locales in your applications + + namespaceSeparator: false, + // Namespace separator used in your translation keys + // If you want to use plain english keys, separators such as `.` and `:` will conflict. You might want to set `keySeparator: false` and `namespaceSeparator: false`. That way, `t('Status: Loading...')` will not think that there are a namespace and three separator dots for instance. + + output: 'src/i18n/resources/$LOCALE/common.json', + // Supports $LOCALE and $NAMESPACE injection + // Supports JSON (.json) and YAML (.yml) file formats + // Where to write the locale files relative to process.cwd() + + input: ['../../src/**/*.{ts,tsx,js,jsx}', '!../../src/i18n/**'], + // An array of globs that describe where to look for source files + // relative to the location of the configuration file + + reactNamespace: false, + // For react file, extract the defaultNamespace - https://react.i18next.com/components/translate-hoc.html + // Ignored when parsing a `.jsx` file and namespace is extracted from that file. + + sort: true, + // Whether or not to sort the catalog + + useKeysAsDefaultValue: true, + // Whether to use the keys as the default value; ex. "Hello": "Hello", "World": "World" + // The option `defaultValue` will not work if this is set to true + + verbose: false, + // Display info about the parsing including some stats +} diff --git a/web/config/jest/jest.config.js b/web/config/jest/jest.config.js new file mode 100644 index 00000000..768d36ea --- /dev/null +++ b/web/config/jest/jest.config.js @@ -0,0 +1,35 @@ +require('../dotenv') + +const path = require('path') + +const { findModuleRoot } = require('../../lib/findModuleRoot') + +const { moduleRoot } = findModuleRoot() + +const shouldRunEslint = process.env.WITH_ESLINT === '1' + +module.exports = { + rootDir: moduleRoot, + + projects: [require('./jest.tests.config.js'), shouldRunEslint && require('./jest.eslint.config.js')].filter(Boolean), + + coverageDirectory: path.join(moduleRoot, '.reports', 'coverage'), + + collectCoverageFrom: [ + '!/**/*.d.ts', + '!/**/node_modules/**/*', + '/src/**/*.{js,jsx,ts,tsx}', + '!/src/index.polyfilled.{js,jsx,ts,tsx}', + '!/src/locales/**/*', + ], + + coverageThreshold: { + global: { + // TODO: write more tests? + // branches: 33, + // functions: 33, + // lines: 33, + // statements: 33, + }, + }, +} diff --git a/web/config/jest/jest.eslint.config.js b/web/config/jest/jest.eslint.config.js new file mode 100644 index 00000000..1bacd879 --- /dev/null +++ b/web/config/jest/jest.eslint.config.js @@ -0,0 +1,33 @@ +require('../dotenv') + +const { findModuleRoot } = require('../../lib/findModuleRoot') + +const { moduleRoot } = findModuleRoot() + +module.exports = { + rootDir: moduleRoot, + roots: ['/src'], + runner: 'jest-runner-eslint', + displayName: { name: 'lint', color: 'blue' }, + testEnvironment: 'jest-environment-jsdom', + preset: 'ts-jest', + globals: { + 'ts-jest': { + babelConfig: true, + diagnostics: { + pathRegex: /(\/__tests?__\/.*|([./])(test|spec))\.[jt]sx?$/, + warnOnly: true, + }, + }, + }, + testMatch: [ + '/src/**/*.(spec|test).{js,jsx,ts,tsx}', + '/src/**/__test__/**/*.{js,jsx,ts,tsx}', + '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', + '/src/**/test/**/*.{js,jsx,ts,tsx}', + '/src/**/tests/**/*.{js,jsx,ts,tsx}', + ], + watchPlugins: [ + 'jest-runner-eslint/watch-fix', // prettier-ignore + ], +} diff --git a/web/config/jest/jest.tests.config.js b/web/config/jest/jest.tests.config.js new file mode 100644 index 00000000..04ceed53 --- /dev/null +++ b/web/config/jest/jest.tests.config.js @@ -0,0 +1,53 @@ +require('../dotenv') + +const { findModuleRoot } = require('../../lib/findModuleRoot') + +const { moduleRoot } = findModuleRoot() + +module.exports = { + rootDir: moduleRoot, + roots: ['/src'], + displayName: { name: 'test', color: 'cyan' }, + testEnvironment: 'jest-environment-jsdom', + preset: 'ts-jest', + globals: { + 'ts-jest': { + babelConfig: true, + diagnostics: { + pathRegex: /(\/__tests?__\/.*|([./])(test|spec))\.[jt]sx?$/, + warnOnly: true, + }, + }, + }, + transform: { + '^.+\\.[t|j]sx?$': 'ts-jest', + '^.+\\.(md|mdx)$': 'jest-transformer-mdx', + '\\.(txt|fasta|csv|tsv)': 'jest-raw-loader', + }, + testMatch: [ + '/src/**/*.(spec|test).{js,jsx,ts,tsx}', + '/src/**/__test__/**/*.{js,jsx,ts,tsx}', + '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', + '/src/**/test/**/*.{js,jsx,ts,tsx}', + '/src/**/tests/**/*.{js,jsx,ts,tsx}', + ], + transformIgnorePatterns: ['node_modules/(?!(d3-scale)/)'], + moduleNameMapper: { + '^src/(.*)': '/src/$1', + '\\.(eot|otf|webp|ttf|woff\\d?|svg|png|jpe?g|gif)$': '/config/jest/mocks/fileMock.js', + '\\.(css|scss)$': 'identity-obj-proxy', + 'react-children-utilities': '/config/jest/mocks/mockReactChildrenUtilities.js', + 'react-i18next': '/config/jest/mocks/mockReactI18next.js', + 'popper-js': '/config/jest/mockPopperJS.js', + 'use-debounce': '/config/jest/mocks/mockUseDebounce.js', + }, + setupFiles: ['core-js', 'regenerator-runtime'], + setupFilesAfterEnv: [ + '/config/jest/setupDotenv.js', + 'jest-chain', + 'jest-extended', + 'jest-axe/extend-expect', + '@testing-library/jest-dom/extend-expect', + ], + watchPlugins: ['jest-watch-typeahead/filename', 'jest-watch-typeahead/testname'], +} diff --git a/web/config/jest/mocks/fileMock.js b/web/config/jest/mocks/fileMock.js new file mode 100644 index 00000000..7e27f6ad --- /dev/null +++ b/web/config/jest/mocks/fileMock.js @@ -0,0 +1 @@ +module.exports = __filename diff --git a/web/config/jest/mocks/mockPopperJS.js b/web/config/jest/mocks/mockPopperJS.js new file mode 100644 index 00000000..5775760e --- /dev/null +++ b/web/config/jest/mocks/mockPopperJS.js @@ -0,0 +1,11 @@ +// mock popper.js (reactstrap dependency via react-popper) +// see: https://github.com/popperjs/popper-core/issues/478#issuecomment-341494703 + +export default class { + constructor() { + return { + destroy: () => null, + scheduleUpdate: () => null, + } + } +} diff --git a/web/config/jest/mocks/mockReactChildrenUtilities.js b/web/config/jest/mocks/mockReactChildrenUtilities.js new file mode 100644 index 00000000..45c02e40 --- /dev/null +++ b/web/config/jest/mocks/mockReactChildrenUtilities.js @@ -0,0 +1,3 @@ +module.exports = { + onlyText: (text) => text, +} diff --git a/web/config/jest/mocks/mockUseDebounce.js b/web/config/jest/mocks/mockUseDebounce.js new file mode 100644 index 00000000..dc200b53 --- /dev/null +++ b/web/config/jest/mocks/mockUseDebounce.js @@ -0,0 +1,3 @@ +export function useDebouncedCallback(f) { + return [f] +} diff --git a/web/config/jest/setupDotenv.js b/web/config/jest/setupDotenv.js new file mode 100644 index 00000000..76486cae --- /dev/null +++ b/web/config/jest/setupDotenv.js @@ -0,0 +1 @@ +require('../dotenv') diff --git a/web/config/next/lib/CustomWebpackConfig.ts b/web/config/next/lib/CustomWebpackConfig.ts new file mode 100644 index 00000000..b7a6c7a8 --- /dev/null +++ b/web/config/next/lib/CustomWebpackConfig.ts @@ -0,0 +1,9 @@ +import type { NextConfig } from 'next' +import type { WebpackConfigContext } from 'next/dist/server/config-shared' +import type { Configuration } from 'webpack' + +export type CustomWebpackConfig = ( + nextConfig: NextConfig, + webpackConfig: Configuration, + options: WebpackConfigContext, +) => Configuration diff --git a/web/config/next/lib/EmitFilePlugin.js b/web/config/next/lib/EmitFilePlugin.js new file mode 100644 index 00000000..9ea456f8 --- /dev/null +++ b/web/config/next/lib/EmitFilePlugin.js @@ -0,0 +1,141 @@ +// Taken from https://github.com/Kir-Antipov/emit-file-webpack-plugin/blob/a17e94f434c9185d659e18806d49f3c6bc73d706/index.js + +/** + * @author Kir_Antipov + * See LICENSE.md file in root directory for full license. + */ +import { Buffer } from 'buffer' +import path from 'path' +import webpack from 'webpack' + +const version = +webpack.version.split('.')[0] +// Webpack 5 exposes the sources property to ensure the right version of webpack-sources is used. +// require('webpack-sources') approach may result in the "Cannot find module 'webpack-sources'" error. +const { Source, RawSource } = webpack.sources || require('webpack-sources') + +/** + * @typedef {object} EmitFilePluginOptions + * + * @property {string} [path] + * OPTIONAL: defaults to the Webpack output path. + * Output path. + * Can be relative (to Webpack output path) or absolute. + * + * @property {string} filename + * REQUIRED. + * Name of the file to add to assets. + * + * @property {boolean} [hash] + * OPTIONAL: defaults to false. + * Adds the compilation hash to the filename. You can either choose within the filename + * where the hash is inserted by adding `[hash]` i.e. `test.[hash].js` or the hash will be + * appended to the end of the file i.e. `test.js?hash`. + * + * @property {number} [stage] + * OPTIONAL: defaults to the webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL. + * Asset processing stage. + * + * @property {string|Buffer|Source|((assets: Record) => (string|Buffer|Source))|((assets: Record) => (Promise))} content + * REQUIRED. + * File content. Can be either a string, a buffer, or a (asynchronous) function. + * If the resulting object is not a string or a buffer, it will be converted to JSON. + */ + +/** + * Webpack plugin to emit files. + * + * @param {EmitFilePluginOptions} options The EmitFilePlugin config. + */ +function EmitFilePlugin(options) { + if (!options) { + throw new Error(`${EmitFilePlugin.name}: Please provide 'options' for the ${EmitFilePlugin.name} config.`) + } + + if (!options.filename) { + throw new Error(`${EmitFilePlugin.name}: Please provide 'options.filename' in the ${EmitFilePlugin.name} config.`) + } + + if (!options.content && options.content !== '') { + throw new Error(`${EmitFilePlugin.name}: Please provide 'options.content' in the ${EmitFilePlugin.name} config.`) + } + + if (typeof options.stage == 'number' && version < 5) { + console.warn(`${EmitFilePlugin.name}: 'options.stage' is only available for Webpack version 5 and higher.`) + } + + this.options = options +} + +/** + * Plugin entry point. + * + * @param {webpack.Compiler} compiler The compiler. + */ +EmitFilePlugin.prototype.apply = function (compiler) { + if (version < 4) { + compiler.plugin('emit', (compilation, callback) => emitFile(this.options, compilation, callback, callback)) + } else if (version === 4) { + compiler.hooks.emit.tapAsync(EmitFilePlugin.name, (compilation, callback) => + emitFile(this.options, compilation, callback, callback), + ) + } else { + compiler.hooks.thisCompilation.tap(EmitFilePlugin.name, (compilation) => { + compilation.hooks.processAssets.tapPromise( + { + name: EmitFilePlugin.name, + stage: + typeof this.options.stage == 'number' + ? this.options.stage + : webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONAL, + }, + () => new Promise((resolve, reject) => emitFile(this.options, compilation, resolve, reject)), + ) + }) + } +} + +/** + * @param {EmitFilePluginOptions} options + * @param {webpack.Compilation} compilation + * @param {() => void} resolve + */ +function emitFile(options, compilation, resolve) { + const outputPath = options.path || compilation.options.output.path + + let filename = options.filename + + if (options.hash) { + const hash = compilation.hash || '' + if (filename.includes('[hash]')) { + filename = filename.replace('[hash]', hash) + } else if (hash) { + filename = `${filename}?${hash}` + } + } + + const outputPathAndFilename = path.resolve(compilation.options.output.path, outputPath, filename) + + const relativeOutputPath = path.relative(compilation.options.output.path, outputPathAndFilename) + + const contentOrPromise = typeof options.content == 'function' ? options.content(compilation.assets) : options.content + + const contentPromise = + contentOrPromise instanceof Promise ? contentOrPromise : new Promise((resolve) => resolve(contentOrPromise)) + + contentPromise.then((content) => { + const source = + content instanceof Source + ? content + : new RawSource(typeof content == 'string' || content instanceof Buffer ? content : JSON.stringify(content)) + + if (version < 5) { + compilation.assets[relativeOutputPath] = source + } else { + compilation.emitAsset(relativeOutputPath, source) + } + + resolve() + }) +} + +export default EmitFilePlugin diff --git a/web/config/next/lib/addWebpackConfig.ts b/web/config/next/lib/addWebpackConfig.ts new file mode 100644 index 00000000..2e5df5f8 --- /dev/null +++ b/web/config/next/lib/addWebpackConfig.ts @@ -0,0 +1,19 @@ +import type { NextConfig } from 'next' +import type { WebpackConfigContext } from 'next/dist/server/config-shared' +import type { Configuration } from 'webpack' + +import { CustomWebpackConfig } from './CustomWebpackConfig' + +export function addWebpackConfig(nextConfig: NextConfig, customWebpackConfig: CustomWebpackConfig) { + const webpack = (webpackConfig: Configuration, options: WebpackConfigContext) => { + const newConfig = customWebpackConfig(nextConfig, webpackConfig, options) + + if (typeof nextConfig.webpack === 'function') { + return nextConfig.webpack(newConfig, options) + } + + return newConfig + } + + return { ...nextConfig, webpack } +} diff --git a/web/config/next/lib/addWebpackLoader.ts b/web/config/next/lib/addWebpackLoader.ts new file mode 100644 index 00000000..d970d0ec --- /dev/null +++ b/web/config/next/lib/addWebpackLoader.ts @@ -0,0 +1,15 @@ +import type { NextConfig } from 'next' +import type { WebpackConfigContext } from 'next/dist/server/config-shared' +import type { Configuration, RuleSetRule } from 'webpack' + +import { addWebpackConfig } from './addWebpackConfig' + +export type GetLoaderFunction = (webpackConfig: Configuration, options: WebpackConfigContext) => RuleSetRule + +export function addWebpackLoader(nextConfig: NextConfig, getLoader: GetLoaderFunction) { + return addWebpackConfig(nextConfig, (nextConfig, webpackConfig, options) => { + const loader = getLoader(webpackConfig, options) + webpackConfig?.module?.rules?.push(loader) + return webpackConfig + }) +} diff --git a/web/config/next/lib/addWebpackPlugin.ts b/web/config/next/lib/addWebpackPlugin.ts new file mode 100644 index 00000000..6ea4bf36 --- /dev/null +++ b/web/config/next/lib/addWebpackPlugin.ts @@ -0,0 +1,25 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { NextConfig } from 'next' +import type { WebpackConfigContext } from 'next/dist/server/config-shared' +import type { Compiler, Configuration, WebpackPluginFunction, WebpackPluginInstance } from 'webpack' + +import { addWebpackConfig } from './addWebpackConfig' + +export function addWebpackPlugin( + nextConfig: NextConfig, + plugin: WebpackPluginInstance | WebpackPluginFunction | ((this: Compiler, compiler: Compiler) => void) | any, +) { + return addWebpackConfig( + nextConfig, + (nextConfig: NextConfig, webpackConfig: Configuration, { isServer }: WebpackConfigContext) => { + if (!isServer) { + if (webpackConfig?.plugins) { + webpackConfig.plugins.push(plugin) + } else { + return { plugins: [plugin] } + } + } + return webpackConfig + }, + ) +} diff --git a/web/config/next/lib/getEnvVars.ts b/web/config/next/lib/getEnvVars.ts new file mode 100644 index 00000000..3c87df5f --- /dev/null +++ b/web/config/next/lib/getEnvVars.ts @@ -0,0 +1,38 @@ +import { getbool, getenv } from '../../../lib/getenv' +import { getDomain } from '../../../lib/getDomain' + +export function getEnvVars() { + const NODE_ENV = getenv('NODE_ENV') + const PRODUCTION = NODE_ENV === 'production' + const PROFILE = getbool('PROFILE') + const DOMAIN = getDomain() + const DOMAIN_STRIPPED = DOMAIN.replace('https://', '').replace('http://', '') + const WATCH_POLL = getbool('WATCH_POLL', false) + const DATA_ROOT_URL = getenv('DATA_ROOT_URL') + + const common = { + NODE_ENV, + PRODUCTION, + PROFILE, + DOMAIN, + DOMAIN_STRIPPED, + WATCH_POLL, + DATA_ROOT_URL, + } + + if (PRODUCTION) { + return { + ...common, + ENABLE_SOURCE_MAPS: getbool('PROD_ENABLE_SOURCE_MAPS'), + ENABLE_ESLINT: getbool('PROD_ENABLE_ESLINT'), + ENABLE_TYPE_CHECKS: getbool('PROD_ENABLE_TYPE_CHECKS'), + } + } + + return { + ...common, + ENABLE_SOURCE_MAPS: true, + ENABLE_ESLINT: getbool('DEV_ENABLE_ESLINT'), + ENABLE_TYPE_CHECKS: getbool('DEV_ENABLE_TYPE_CHECKS'), + } +} diff --git a/web/config/next/loaders/removeDebugPackageLoader.cjs b/web/config/next/loaders/removeDebugPackageLoader.cjs new file mode 100644 index 00000000..787ce8fb --- /dev/null +++ b/web/config/next/loaders/removeDebugPackageLoader.cjs @@ -0,0 +1,22 @@ +function stripDebugRequire(source) { + return source + .replace( + /__importDefault\s*\(\s*require\s*\(\s*["']\s*debug\s*["']\s*\)\s*\)/g, + '{ default() { return function () {} } }\n', + ) + .replace( + /_interopRequireDefault\s*\(\s*require\s*\(\s*["']\s*debug\s*["']\s*\)\s*\)/g, + '{ default() { return function () {} } }\n', + ) + .replace(/.*?require\(\s*["']debug["']\s*\).*/g, '(function(){return function () {}})\n') + .replace(/import\s+(.+)\s+from\s+["']debug["']/g, 'const $1 = (function(){ return function () {} })\n') +} + +/** + * Webpack loader which removes imports and requires of the `debug` package from JavaScript. + * This is especially important in presence of WebWorkers: `debug` package uses `windows` global, which is not + * present in WebWorker environment. + */ +export default function removeDebugPackageLoader(source) { + this.callback(null, stripDebugRequire(source)) +} diff --git a/web/config/next/next.config.ts b/web/config/next/next.config.ts new file mode 100644 index 00000000..34cdd290 --- /dev/null +++ b/web/config/next/next.config.ts @@ -0,0 +1,183 @@ +import type { NextConfig } from 'next' +import path from 'path' +import { uniq } from 'lodash-es' +import getWithMDX from '@next/mdx' +import remarkBreaks from 'remark-breaks' +import remarkMath from 'remark-math' +import remarkToc from 'remark-toc' +import remarkSlug from 'remark-slug' +import remarkImages from 'remark-images' + +import { findModuleRoot } from '../../lib/findModuleRoot' +import { getGitBranch } from '../../lib/getGitBranch' +import { getBuildNumber } from '../../lib/getBuildNumber' +import { getBuildUrl } from '../../lib/getBuildUrl' +import { getGitCommitHash } from '../../lib/getGitCommitHash' +import { getEnvVars } from './lib/getEnvVars' + +import getWithExtraWatch from './withExtraWatch' +import getWithFriendlyConsole from './withFriendlyConsole' +import { getWithRobotsTxt } from './withRobotsTxt' +import getWithTypeChecking from './withTypeChecking' +import withoutDebugPackage from './withoutDebugPackage' +import withSvg from './withSvg' +import withIgnore from './withIgnore' +import withoutMinification from './withoutMinification' +import withFriendlyChunkNames from './withFriendlyChunkNames' +import withResolve from './withResolve' +import withWebpackWatchPoll from './withWebpackWatchPoll' +import withUrlAsset from './withUrlAsset' + +const { + PRODUCTION, + PROFILE, + ENABLE_SOURCE_MAPS, + ENABLE_ESLINT, + ENABLE_TYPE_CHECKS, + DOMAIN, + DOMAIN_STRIPPED, + WATCH_POLL, + DATA_ROOT_URL, +} = getEnvVars() + +const BRANCH_NAME = getGitBranch() + +const { pkg, moduleRoot } = findModuleRoot() + +const clientEnv = { + BRANCH_NAME, + PACKAGE_VERSION: pkg.version ?? '', + BUILD_NUMBER: getBuildNumber(), + TRAVIS_BUILD_WEB_URL: getBuildUrl(), + COMMIT_HASH: getGitCommitHash(), + DOMAIN, + DOMAIN_STRIPPED, + DATA_ROOT_URL, +} + +const transpilationListDev = [ + // prettier-ignore + 'd3-scale', +] + +const transpilationListProd = uniq([ + // prettier-ignore + ...transpilationListDev, + 'debug', + 'lodash', + 'react-share', + 'recharts', + 'semver', +]) + +const nextConfig: NextConfig = { + distDir: `.build/${process.env.NODE_ENV}/tmp`, + pageExtensions: ['js', 'jsx', 'ts', 'tsx', 'md', 'mdx', 'all-contributorsrc'], + onDemandEntries: { + maxInactiveAge: 180 * 1000, + pagesBufferLength: 5, + }, + reactStrictMode: true, + cleanDistDir: true, + experimental: { + legacyBrowsers: true, + newNextLinkBehavior: true, + scrollRestoration: true, + swcMinify: true, + }, + swcMinify: true, + productionBrowserSourceMaps: ENABLE_SOURCE_MAPS, + excludeDefaultMomentLocales: true, + devIndicators: { + buildActivity: false, + }, + typescript: { + ignoreBuildErrors: true, + }, + eslint: { + ignoreDuringBuilds: true, + }, + compiler: { + styledComponents: true, + }, + env: clientEnv, + poweredByHeader: false, + webpack(config) { + config.experiments.topLevelAwait = true + return config + }, + transpilePackages: PRODUCTION ? transpilationListProd : transpilationListDev, + async rewrites() { + return [{ source: '/:any*', destination: '/' }] + }, +} + +const withMDX = getWithMDX({ + extension: /\.mdx?$/, + options: { + remarkPlugins: [ + remarkBreaks, + remarkImages, + remarkMath, + remarkSlug, + [remarkToc, { tight: true }], + + // [ + // require('remark-autolink-headings'), + // { + // behavior: 'prepend', + // content: { + // type: 'element', + // tagName: 'i', + // properties: { className: ['bi', 'bi-link-45deg', 'mdx-link-icon'] }, + // }, + // }, + // ], + ], + rehypePlugins: [], + }, +}) + +const withFriendlyConsole = getWithFriendlyConsole({ + clearConsole: false, + projectRoot: path.resolve(moduleRoot), +}) + +const withExtraWatch = getWithExtraWatch({ + files: [path.join(moduleRoot, 'src/types/**/*.d.ts')], + dirs: [], +}) + +const withTypeChecking = getWithTypeChecking({ + typeChecking: ENABLE_TYPE_CHECKS, + eslint: ENABLE_ESLINT, + memoryLimit: 4096, +}) + +const withRobotsTxt = getWithRobotsTxt(`User-agent: *\nDisallow:${BRANCH_NAME === 'release' ? '' : ' *'}\n`) + +export default function config(phase: string, defaultConfig: NextConfig) { + const plugins = [ + withIgnore, + withExtraWatch, + withSvg, + withFriendlyConsole, + withMDX, + withTypeChecking, + PROFILE && withoutMinification, + WATCH_POLL && withWebpackWatchPoll, + withFriendlyChunkNames, + withResolve, + withRobotsTxt, + withUrlAsset, + PRODUCTION && withoutDebugPackage, + ].filter(Boolean) + + return plugins.reduce( + (acc, plugin) => { + const update = plugin(acc) + return typeof update === 'function' ? update(phase, defaultConfig) : update + }, + { ...nextConfig }, + ) +} diff --git a/web/config/next/withCopy.ts b/web/config/next/withCopy.ts new file mode 100644 index 00000000..cc067ed8 --- /dev/null +++ b/web/config/next/withCopy.ts @@ -0,0 +1,11 @@ +import { NextConfig } from 'next' +import CopyPlugin, { PluginOptions as CopyPluginOptions } from 'copy-webpack-plugin' +import { addWebpackPlugin } from './lib/addWebpackPlugin' + +const getWithCopy = (options: CopyPluginOptions) => (nextConfig: NextConfig) => { + return addWebpackPlugin(nextConfig, new CopyPlugin(options)) +} + +export default getWithCopy + +export { type PluginOptions as CopyPluginOptions } from 'copy-webpack-plugin' diff --git a/web/config/next/withExtraWatch.ts b/web/config/next/withExtraWatch.ts new file mode 100644 index 00000000..34033efc --- /dev/null +++ b/web/config/next/withExtraWatch.ts @@ -0,0 +1,17 @@ +import type { NextConfig } from 'next' +import ExtraWatchWebpackPlugin from 'extra-watch-webpack-plugin' + +import { addWebpackPlugin } from './lib/addWebpackPlugin' + +export interface WithExtraWatchOptions { + files: string[] + dirs: string[] +} + +const getWithExtraWatch = + ({ files, dirs }: WithExtraWatchOptions) => + (nextConfig: NextConfig) => { + return addWebpackPlugin(nextConfig, new ExtraWatchWebpackPlugin({ files, dirs })) + } + +export default getWithExtraWatch diff --git a/web/config/next/withFriendlyChunkNames.ts b/web/config/next/withFriendlyChunkNames.ts new file mode 100644 index 00000000..b75373fd --- /dev/null +++ b/web/config/next/withFriendlyChunkNames.ts @@ -0,0 +1,17 @@ +import { unset } from 'lodash-es' + +import type { NextConfig } from 'next' +import { addWebpackConfig } from './lib/addWebpackConfig' + +export default function withFriendlyChunkNames(nextConfig: NextConfig) { + return addWebpackConfig(nextConfig, (nextConfig, webpackConfig, _options) => { + if ( + typeof webpackConfig.optimization?.splitChunks !== 'boolean' && + webpackConfig.optimization?.splitChunks?.cacheGroups + ) { + unset(webpackConfig, 'optimization.splitChunks.cacheGroups.lib.name') + unset(webpackConfig, 'optimization.splitChunks.cacheGroups.shared.name') + } + return webpackConfig + }) +} diff --git a/web/config/next/withFriendlyConsole.ts b/web/config/next/withFriendlyConsole.ts new file mode 100644 index 00000000..b46cd87d --- /dev/null +++ b/web/config/next/withFriendlyConsole.ts @@ -0,0 +1,43 @@ +import FriendlyErrorsWebpackPlugin from '@nuxt/friendly-errors-webpack-plugin' +import type { NextConfig } from 'next' +import { addWebpackPlugin } from './lib/addWebpackPlugin' + +interface FriendlyErrorsWebpackPluginError { + message: string + file: string +} + +function cleanup() { + return (error: FriendlyErrorsWebpackPluginError) => ({ + ...error, + message: error.message.replace(/.*ERROR in.*\n/, '').replace(/.*WARNING in.*\n/, ''), + }) +} + +function stripProjectRoot(projectRoot: string) { + return (error: FriendlyErrorsWebpackPluginError) => ({ + ...error, + message: error && error.message && error.message.replace(`${projectRoot}/`, ''), + file: error && error.file && error.file.replace(`${projectRoot}/`, ''), + }) +} + +export interface WithFriendlyConsoleParams { + clearConsole: boolean + projectRoot: string +} + +const getWithFriendlyConsole = + ({ clearConsole, projectRoot }: WithFriendlyConsoleParams) => + (nextConfig: NextConfig) => { + return addWebpackPlugin( + nextConfig, + new FriendlyErrorsWebpackPlugin({ + clearConsole, + additionalTransformers: [cleanup(), stripProjectRoot(projectRoot)], + additionalFormatters: [], + }), + ) + } + +export default getWithFriendlyConsole diff --git a/web/config/next/withIgnore.ts b/web/config/next/withIgnore.ts new file mode 100644 index 00000000..a9fb25cb --- /dev/null +++ b/web/config/next/withIgnore.ts @@ -0,0 +1,19 @@ +import webpack from 'webpack' +import type { NextConfig } from 'next' + +import { addWebpackPlugin } from './lib/addWebpackPlugin' + +export default function withIgnore(nextConfig: NextConfig) { + return addWebpackPlugin( + nextConfig, + new webpack.IgnorePlugin({ + checkResource: (resource: string) => { + return ( + resource.endsWith('awesomplete.css') || + resource.includes('core-js/library') || + resource.includes('babel-runtime') + ) + }, + }), + ) +} diff --git a/web/config/next/withResolve.ts b/web/config/next/withResolve.ts new file mode 100644 index 00000000..efbae348 --- /dev/null +++ b/web/config/next/withResolve.ts @@ -0,0 +1,24 @@ +import path from 'path' + +import type { NextConfig } from 'next' +import { findModuleRoot } from '../../lib/findModuleRoot' +import { addWebpackConfig } from './lib/addWebpackConfig' + +const { moduleRoot } = findModuleRoot() + +export default function withResolve(nextConfig: NextConfig) { + return addWebpackConfig(nextConfig, (nextConfig, webpackConfig, options) => { + webpackConfig.resolve = { + ...webpackConfig.resolve, + modules: [ + ...(webpackConfig.resolve?.modules ?? []), + path.resolve(moduleRoot), + path.resolve(moduleRoot, '..'), + path.resolve(moduleRoot, 'src'), + path.resolve(moduleRoot, 'node_modules'), + ], + } + + return webpackConfig + }) +} diff --git a/web/config/next/withRobotsTxt.ts b/web/config/next/withRobotsTxt.ts new file mode 100644 index 00000000..cccd7491 --- /dev/null +++ b/web/config/next/withRobotsTxt.ts @@ -0,0 +1,15 @@ +import { NextConfig } from 'next' +import { addWebpackPlugin } from './lib/addWebpackPlugin' +import EmitFilePlugin from './lib/EmitFilePlugin' + +export const getWithRobotsTxt = (content: string) => (nextConfig: NextConfig) => { + return addWebpackPlugin( + nextConfig, + new EmitFilePlugin({ + path: '.', + filename: 'robots.txt', + content, + hash: false, + }), + ) +} diff --git a/web/config/next/withSvg.ts b/web/config/next/withSvg.ts new file mode 100644 index 00000000..e4a0ddeb --- /dev/null +++ b/web/config/next/withSvg.ts @@ -0,0 +1,19 @@ +import type { NextConfig } from 'next' + +import { addWebpackLoader } from './lib/addWebpackLoader' + +export default function withSvg(nextConfig: NextConfig) { + return addWebpackLoader(nextConfig, (_webpackConfig, _context) => ({ + test: /\.svg$/i, + issuer: /\.(ts|tsx|js|jsx|md|mdx|scss|sass|css)$/, + use: [ + { + loader: '@svgr/webpack', + options: { + removeViewbox: false, + typescript: false, + }, + }, + ], + })) +} diff --git a/web/config/next/withTypeChecking.ts b/web/config/next/withTypeChecking.ts new file mode 100644 index 00000000..cab90e54 --- /dev/null +++ b/web/config/next/withTypeChecking.ts @@ -0,0 +1,86 @@ +import path from 'path' +import { readJSONSync } from 'fs-extra/esm' +import ForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin' +import ESLintWebpackPlugin from 'eslint-webpack-plugin' +import type { NextConfig } from 'next' +import { addWebpackPlugin } from './lib/addWebpackPlugin' +import { findModuleRoot } from '../../lib/findModuleRoot' + +const { moduleRoot } = findModuleRoot() + +export interface GetWithTypeCheckingParams { + eslint: boolean + typeChecking: boolean + memoryLimit?: number + exclude?: string[] +} + +const getWithTypeChecking = + ({ eslint, typeChecking, memoryLimit = 512, exclude }: GetWithTypeCheckingParams) => + (nextConfig: NextConfig) => { + let config = nextConfig + + if (!typeChecking && !eslint) { + return config + } + + if (typeChecking) { + const tsConfig = readJSONSync('tsconfig.json') + + config = addWebpackPlugin( + config, + new ForkTsCheckerWebpackPlugin({ + issue: { + exclude: exclude?.map((file) => ({ origin: 'typescript', file })), + }, + + typescript: { + configFile: path.join(moduleRoot, 'tsconfig.json'), + memoryLimit, + mode: 'write-references', + diagnosticOptions: { syntactic: true, semantic: true, declaration: true, global: true }, + configOverwrite: { + compilerOptions: { + ...tsConfig.compilerOptions, + allowJs: false, + skipLibCheck: true, + sourceMap: false, + inlineSourceMap: false, + declarationMap: false, + tsBuildInfoFile: '.cache/.tsbuildinfo.webpackplugin', + }, + include: [ + 'lib/**/*.js', + 'lib/**/*.jsx', + 'lib/**/*.ts', + 'lib/**/*.tsx', + 'src/**/*.js', + 'src/**/*.jsx', + 'src/**/*.ts', + 'src/**/*.tsx', + ], + exclude: [...tsConfig.exclude, ...(exclude ?? [])], + }, + }, + + formatter: 'codeframe', + }), + ) + } + + if (eslint) { + config = addWebpackPlugin( + config, + new ESLintWebpackPlugin({ + threads: true, + files: [path.join(moduleRoot, 'src/**/*.{js,jsx,ts,tsx}')], + cache: false, + formatter: 'codeframe', + }), + ) + } + + return config + } + +export default getWithTypeChecking diff --git a/web/config/next/withUrlAsset.ts b/web/config/next/withUrlAsset.ts new file mode 100644 index 00000000..21d38fab --- /dev/null +++ b/web/config/next/withUrlAsset.ts @@ -0,0 +1,10 @@ +import type { NextConfig } from 'next' + +import { addWebpackLoader } from './lib/addWebpackLoader' + +export default function withUrlAsset(nextConfig: NextConfig) { + return addWebpackLoader(nextConfig, (_webpackConfig, _context) => ({ + type: 'asset', + resourceQuery: /url/, // *.svg?url + })) +} diff --git a/web/config/next/withWebpackWatchPoll.ts b/web/config/next/withWebpackWatchPoll.ts new file mode 100644 index 00000000..31af879c --- /dev/null +++ b/web/config/next/withWebpackWatchPoll.ts @@ -0,0 +1,11 @@ +import { set } from 'lodash-es' + +import type { NextConfig } from 'next' +import { addWebpackConfig } from './lib/addWebpackConfig' + +export default function withWebpackWatchPoll(nextConfig: NextConfig) { + return addWebpackConfig(nextConfig, (nextConfig, webpackConfig, options) => { + set(webpackConfig, 'watchOptions.poll', 1000) + return webpackConfig + }) +} diff --git a/web/config/next/withoutDebugPackage.ts b/web/config/next/withoutDebugPackage.ts new file mode 100644 index 00000000..ee993f41 --- /dev/null +++ b/web/config/next/withoutDebugPackage.ts @@ -0,0 +1,16 @@ +import path from 'path' +import type { NextConfig } from 'next' +import { addWebpackLoader } from './lib/addWebpackLoader' + +const THIS_DIR = new URL('.', import.meta.url).pathname + +export default function withoutDebugPackage(nextConfig: NextConfig) { + return addWebpackLoader(nextConfig, (_webpackConfig, _context) => ({ + test: /\.(ts|tsx|ctx|mtx|js|jsx|cjs|mjs|)$/i, + use: [ + { + loader: path.resolve(THIS_DIR, 'loaders', 'removeDebugPackageLoader.cjs'), + }, + ], + })) +} diff --git a/web/config/next/withoutMinification.ts b/web/config/next/withoutMinification.ts new file mode 100644 index 00000000..b87bf0a9 --- /dev/null +++ b/web/config/next/withoutMinification.ts @@ -0,0 +1,13 @@ +import { NextConfig } from 'next' +import { addWebpackConfig } from './lib/addWebpackConfig' + +export default function withoutMinification(nextConfig: NextConfig) { + return addWebpackConfig(nextConfig, (nextConfig, webpackConfig, options) => { + if (webpackConfig.optimization) { + webpackConfig.optimization.minimizer = [] + } else { + webpackConfig.optimization = { minimizer: [] } + } + return webpackConfig + }) +} diff --git a/web/config/nodemon/dev.json b/web/config/nodemon/dev.json new file mode 100644 index 00000000..82fbdc4d --- /dev/null +++ b/web/config/nodemon/dev.json @@ -0,0 +1,28 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "next.config.js", + "package.json", + "postcss.config.js", + "schemas", + "stylelint.config.js", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn dev:start || cd ." +} diff --git a/web/config/nodemon/eslint.fix.json b/web/config/nodemon/eslint.fix.json new file mode 100644 index 00000000..5871f274 --- /dev/null +++ b/web/config/nodemon/eslint.fix.json @@ -0,0 +1,29 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "cypress", + "jest.config.js", + "package.json", + "postcss.config.js", + "src", + "stylelint.config.js", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn clear && yarn eslint:fix && echo '\u2714 OK' || echo '\u274C FAILED' || cd ." +} diff --git a/web/config/nodemon/eslint.json b/web/config/nodemon/eslint.json new file mode 100644 index 00000000..cfd50a9b --- /dev/null +++ b/web/config/nodemon/eslint.json @@ -0,0 +1,29 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "cypress", + "jest.config.js", + "package.json", + "postcss.config.js", + "src", + "stylelint.config.js", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn clear && yarn eslint && echo '\u2714 OK' || echo '\u274C FAILED' || cd ." +} diff --git a/web/config/nodemon/lint.fix.json b/web/config/nodemon/lint.fix.json new file mode 100644 index 00000000..c6957500 --- /dev/null +++ b/web/config/nodemon/lint.fix.json @@ -0,0 +1,27 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "cypress", + "jest.config.js", + "package.json", + "src", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn clear && yarn lint:fix && echo '\u2714 OK' || echo '\u274C FAILED' || cd ." +} diff --git a/web/config/nodemon/lint.json b/web/config/nodemon/lint.json new file mode 100644 index 00000000..8fe3388c --- /dev/null +++ b/web/config/nodemon/lint.json @@ -0,0 +1,27 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "cypress", + "jest.config.js", + "package.json", + "src", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn clear && yarn lint && echo '\u2714 OK' || echo '\u274C FAILED' || cd ." +} diff --git a/web/config/nodemon/prod.json b/web/config/nodemon/prod.json new file mode 100644 index 00000000..4d75ef28 --- /dev/null +++ b/web/config/nodemon/prod.json @@ -0,0 +1,37 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "package.json", + "postcss.config.js", + "schemas", + "src", + "static", + "stylelint.config.js", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": [ + "**/*.test.*s", + "**/*.test.*sx", + "**/__tests__", + ".build", + "config/jest", + "node_modules", + "src/.generated" + ], + "exec": "yarn prod:start || cd ." +} diff --git a/web/config/nodemon/profile.json b/web/config/nodemon/profile.json new file mode 100644 index 00000000..3f444088 --- /dev/null +++ b/web/config/nodemon/profile.json @@ -0,0 +1,37 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + "../.env", + ".browserslistrc", + ".editorconfig", + ".env", + ".eslintignore", + ".eslintrc.js", + ".prettierignore", + ".prettierrc", + "babel.config.js", + "config", + "package.json", + "postcss.config.js", + "schemas", + "src", + "static", + "stylelint.config.js", + "tsconfig.json", + "webpack.config.js", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": [ + "**/*.test.*s", + "**/*.test.*sx", + "**/__tests__", + ".build", + "config/jest", + "node_modules", + "src/.generated" + ], + "exec": "run-s -c clear prod:clean prod:start:profile || cd ." +} diff --git a/web/config/nodemon/serve.json b/web/config/nodemon/serve.json new file mode 100644 index 00000000..cc9fbfcf --- /dev/null +++ b/web/config/nodemon/serve.json @@ -0,0 +1,17 @@ +{ + "quiet": true, + "legacyWatch": false, + "delay": 0.1, + "watch": [ + ".env", + "babel.config.js", + "infra/lambda-at-edge/modifyOutgoingHeaders.lambda.js", + "package.json", + "tools/server", + "tsconfig.json", + "yarn.lock" + ], + "ext": "js,jsx,ts,tsx,json,flow,css,scss,sass,styl,html,ejs,d,txt,yml,sh", + "ignore": ["**/*.test.*s", "**/*.test.*sx", "**/__tests__", ".build", "config/jest", "node_modules"], + "exec": "yarn prod:serve:nowatch || cd ." +} diff --git a/web/infra/find-lambda-at-edge-logs.sh b/web/infra/find-lambda-at-edge-logs.sh new file mode 100755 index 00000000..b9c8fe44 --- /dev/null +++ b/web/infra/find-lambda-at-edge-logs.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Inspired by https://stackoverflow.com/a/54096479/526860 + +FUNCTION_NAME=$1 + +for region in $(aws --output text ec2 describe-regions | cut -f 4); do + for loggroup in $(aws --output text logs describe-log-groups --log-group-name "/aws/lambda/us-east-1.$FUNCTION_NAME" --region $region --query 'logGroups[].logGroupName'); do + printf "$region\tconsole.aws.amazon.com/cloudwatch/home?region=$region#logsV2:log-groups/log-group/\$252Faws\$252Flambda\$252Fus-east-1.$FUNCTION_NAME\n" + done +done diff --git a/web/infra/iam-role-trust-for-lambda-at-edge.json b/web/infra/iam-role-trust-for-lambda-at-edge.json new file mode 100644 index 00000000..e39c7310 --- /dev/null +++ b/web/infra/iam-role-trust-for-lambda-at-edge.json @@ -0,0 +1,15 @@ +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com", + "edgelambda.amazonaws.com" + ] + }, + "Action": "sts:AssumeRole" + } + ] +} diff --git a/web/infra/s3-cors-permissions.json b/web/infra/s3-cors-permissions.json new file mode 100644 index 00000000..d705ef49 --- /dev/null +++ b/web/infra/s3-cors-permissions.json @@ -0,0 +1,9 @@ +[ + { + "AllowedHeaders": ["*"], + "AllowedMethods": ["GET", "HEAD"], + "AllowedOrigins": ["*"], + "ExposeHeaders": [], + "MaxAgeSeconds": 3000 + } +] diff --git a/web/infra/web/lambda-at-edge/OriginRequest.lambda.js b/web/infra/web/lambda-at-edge/OriginRequest.lambda.js new file mode 100644 index 00000000..902e15aa --- /dev/null +++ b/web/infra/web/lambda-at-edge/OriginRequest.lambda.js @@ -0,0 +1,44 @@ +/* eslint-disable prefer-destructuring */ +// Implements rewrite of non-compressed to .gz or .br URLs using AWS +// Lambda@Edge. This is useful if you have precompressed your files. +// +// Usage: +// Create an AWS Lambda function and attach it to "Origin Request" event of a +// Cloudfront distribution + +const ARCHIVE_EXTS = ['.7z', '.br', '.bz2', '.gz', '.lzma', '.xz', '.zip', '.zst'] + +function getHeader(headers, headerName) { + const header = headers[headerName.toLowerCase()] + if (!header || !header[0] || !header[0].value) { + return undefined + } + return header[0].value +} + +function acceptsEncoding(headers, encoding) { + const ae = getHeader(headers, 'Accept-Encoding') + if (!ae || typeof ae != 'string') { + return false + } + return ae.split(',').some((e) => e.trim().toLowerCase().startsWith(encoding.toLowerCase())) +} + +function handler(event, context, callback) { + const request = event.Records[0].cf.request + const headers = request.headers + + // If not an archive file (which are not precompressed), rewrite the URL to + // get the corresponding .gz file + if (ARCHIVE_EXTS.every((ext) => !request.uri.endsWith(ext))) { + if (acceptsEncoding(headers, 'br')) { + request.uri += '.br' + } else if (acceptsEncoding(headers, 'gzip')) { + request.uri += '.gz' + } + } + + callback(null, request) +} + +exports.handler = handler diff --git a/web/infra/web/lambda-at-edge/ViewerResponse.lambda.js b/web/infra/web/lambda-at-edge/ViewerResponse.lambda.js new file mode 100644 index 00000000..92bfc51e --- /dev/null +++ b/web/infra/web/lambda-at-edge/ViewerResponse.lambda.js @@ -0,0 +1,99 @@ +// Adds additional headers to the response, including security headers. +// Suited for websites. +// +// See also: +// - https://securityheaders.com/ +// +// Usage: Create an AWS Lambda@Edge function and attach it to "Viewer Response" +// event of a Cloudfront distribution + +const FEATURE_POLICY = { + accelerometer: `'none'`, + camera: `'none'`, + geolocation: `'none'`, + gyroscope: `'none'`, + magnetometer: `'none'`, + microphone: `'none'`, + payment: `'none'`, + usb: `'none'`, +} + +function generateFeaturePolicyHeader(featurePolicyObject) { + return Object.entries(featurePolicyObject) + .map(([policy, value]) => `${policy} ${value}`) + .join('; ') +} + +const PERMISSIONS_POLICY = { + 'accelerometer': '()', + 'camera': '()', + 'geolocation': '()', + 'gyroscope': '()', + 'magnetometer': '()', + 'microphone': '()', + 'payment': '()', + 'usb': '()', + 'interest-cohort': '()', +} + +function generatePermissionsPolicyHeader(permissionsPolicyObject) { + return Object.entries(permissionsPolicyObject) + .map(([policy, value]) => `${policy}=${value}`) + .join(', ') +} + +const NEW_HEADERS = { + 'Content-Security-Policy': `default-src 'self' *.pangenome.org; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: *.pangenome.org plausible.io maxcdn.bootstrapcdn.com; style-src 'self' 'unsafe-inline' maxcdn.bootstrapcdn.com fonts.googleapis.com; font-src 'self' maxcdn.bootstrapcdn.com fonts.googleapis.com fonts.gstatic.com;img-src 'self' data:; connect-src *; frame-src 'self' player.vimeo.com`, + 'Referrer-Policy': 'no-referrer', + 'Strict-Transport-Security': 'max-age=15768000; includeSubDomains; preload', + 'X-Content-Type-Options': 'nosniff', + 'X-DNS-Prefetch-Control': 'off', + 'X-Download-Options': 'noopen', + 'X-Frame-Options': 'SAMEORIGIN', + 'X-XSS-Protection': '1; mode=block', + 'Feature-Policy': generateFeaturePolicyHeader(FEATURE_POLICY), + 'Permissions-Policy': generatePermissionsPolicyHeader(PERMISSIONS_POLICY), +} + +function addHeaders(headersObject) { + return Object.fromEntries( + Object.entries(headersObject).map(([header, value]) => [header.toLowerCase(), [{ key: header, value }]]), + ) +} + +const HEADERS_TO_REMOVE = new Set(['server', 'via']) + +function filterHeaders(headers) { + return Object.entries(headers).reduce((result, [key, value]) => { + if (HEADERS_TO_REMOVE.has(key.toLowerCase())) { + return result + } + + if (key.toLowerCase().includes('powered-by')) { + return result + } + + return { ...result, [key.toLowerCase()]: value } + }, {}) +} + +function modifyHeaders({ response }) { + let newHeaders = addHeaders(NEW_HEADERS) + + newHeaders = { + ...response.headers, + ...newHeaders, + } + + newHeaders = filterHeaders(newHeaders) + + return newHeaders +} + +exports.handler = (event, context, callback) => { + const { request, response } = event.Records[0].cf + response.headers = modifyHeaders({ request, response }) + callback(null, response) +} + +exports.modifyHeaders = modifyHeaders diff --git a/web/json-autotranslate.json b/web/json-autotranslate.json new file mode 100644 index 00000000..e93f8e85 --- /dev/null +++ b/web/json-autotranslate.json @@ -0,0 +1,4 @@ +{ + "region": "us-east-1", + "maxAttempts": 10 +} diff --git a/web/lib/clear.js b/web/lib/clear.js new file mode 100644 index 00000000..3851d79c --- /dev/null +++ b/web/lib/clear.js @@ -0,0 +1,6 @@ +// Taken from jest: +// https://github.com/facebook/jest/blob/v19.0.2/packages/jest-cli/src/constants.js#L15 +const isWindows = process.platform === 'win32' +const CLEAR = isWindows ? '\u001Bc' : '\u001B[2J\u001B[3J\u001B[H' + +process.stdout.write(CLEAR) diff --git a/web/lib/findModuleRoot.d.ts b/web/lib/findModuleRoot.d.ts new file mode 100644 index 00000000..0bb4b57e --- /dev/null +++ b/web/lib/findModuleRoot.d.ts @@ -0,0 +1,8 @@ +import { JSONSchemaForNPMPackageJsonFiles } from '@schemastore/package' + +export interface FindModuleRootResult { + moduleRoot: string + pkg: JSONSchemaForNPMPackageJsonFiles +} + +export declare function findModuleRoot(maxDepth: number = 10): FindModuleRootResult diff --git a/web/lib/findModuleRoot.js b/web/lib/findModuleRoot.js new file mode 100644 index 00000000..02da8099 --- /dev/null +++ b/web/lib/findModuleRoot.js @@ -0,0 +1,18 @@ +/* eslint-disable no-loops/no-loops,no-param-reassign,no-plusplus */ +import fs from 'fs-extra' +import path from 'path' + +const THIS_DIR = new URL('.', import.meta.url).pathname + +export function findModuleRoot(maxDepth = 10) { + let moduleRoot = THIS_DIR + while (--maxDepth) { + moduleRoot = path.resolve(moduleRoot, '..') + const file = path.join(moduleRoot, 'package.json') + if (fs.existsSync(file)) { + const pkg = fs.readJsonSync(file) + return { moduleRoot, pkg } + } + } + throw new Error('Module root not found') +} diff --git a/web/lib/getBuildNumber.ts b/web/lib/getBuildNumber.ts new file mode 100644 index 00000000..2e423974 --- /dev/null +++ b/web/lib/getBuildNumber.ts @@ -0,0 +1,5 @@ +import { getenv } from './getenv' + +export function getBuildNumber() { + return getenv('TRAVIS_BUILD_NUMBER', null) ?? getenv('BUILD_ID', null) +} diff --git a/web/lib/getBuildUrl.ts b/web/lib/getBuildUrl.ts new file mode 100644 index 00000000..57d9c839 --- /dev/null +++ b/web/lib/getBuildUrl.ts @@ -0,0 +1,5 @@ +import { getenv } from './getenv' + +export function getBuildUrl() { + return getenv('TRAVIS_BUILD_WEB_URL', null) +} diff --git a/web/lib/getDomain.ts b/web/lib/getDomain.ts new file mode 100644 index 00000000..01dca9b8 --- /dev/null +++ b/web/lib/getDomain.ts @@ -0,0 +1,36 @@ +import { isNil } from 'lodash-es' +import { getenv } from './getenv' + +const WEB_PORT_DEV = getenv('WEB_PORT_DEV', null) +const WEB_PORT_PROD = getenv('WEB_PORT_PROD', null) +const devDomain = `http://localhost:${WEB_PORT_DEV}` +const prodDomain = `http://localhost:${WEB_PORT_PROD}` + +const ENV_VARS = [ + // prettier-ignore + 'VERCEL_URL', + 'NOW_URL', + 'ZEIT_URL', + 'DEPLOY_PRIME_URL', + 'DEPLOY_URL', + 'URL', +] + +export function getenvFirst(vars: string[]): string | undefined { + return vars.map((v) => getenv(v, null)).find((v) => !isNil(v)) +} + +function sanitizeDomain(domain: string) { + return domain.startsWith('http') ? domain : `https://${domain}` +} + +export function getDomain() { + const domain = getenv('FULL_DOMAIN') + if (domain === 'autodetect') { + if (process.env.NODE_ENV === 'development') { + return devDomain + } + return sanitizeDomain(getenvFirst(ENV_VARS) ?? prodDomain) + } + return sanitizeDomain(domain) +} diff --git a/web/lib/getGitBranch.ts b/web/lib/getGitBranch.ts new file mode 100644 index 00000000..2f5a4455 --- /dev/null +++ b/web/lib/getGitBranch.ts @@ -0,0 +1,28 @@ +import { execSync } from 'child_process' + +import { getenv } from './getenv' + +export function getGitCommitHashLocal() { + try { + return execSync('git rev-parse --abbrev-ref HEAD').toString().trim() + } catch { + return undefined + } +} + +export function getGitBranch() { + return ( + getenv('GIT_BRANCH', null) ?? + getenv('BRANCH', null) ?? + getenv('TRAVIS_BRANCH', null) ?? + getenv('NOW_GITHUB_COMMIT_REF', null) ?? + getenv('VERCEL_GITHUB_COMMIT_REF', null) ?? + getenv('VERCEL_GITLAB_COMMIT_REF', null) ?? + getenv('VERCEL_BITBUCKET_COMMIT_REF', null) ?? + getenv('ZEIT_GITHUB_COMMIT_REF', null) ?? + getenv('ZEIT_GITLAB_COMMIT_REF', null) ?? + getenv('ZEIT_BITBUCKET_COMMIT_REF', null) ?? + getGitCommitHashLocal() ?? + '' + ) +} diff --git a/web/lib/getGitCommitHash.ts b/web/lib/getGitCommitHash.ts new file mode 100644 index 00000000..0311d911 --- /dev/null +++ b/web/lib/getGitCommitHash.ts @@ -0,0 +1,29 @@ +import { execSync } from 'child_process' + +import { getenv } from './getenv' + +export function getGitCommitHashLocal() { + try { + return execSync('git rev-parse HEAD').toString().trim() + } catch { + return undefined + } +} + +export function getGitCommitHash() { + return ( + getenv('GIT_COMMIT', null) ?? + getenv('GIT_COMMIT_HASH', null) ?? + getenv('TRAVIS_COMMIT', null) ?? + getenv('NOW_GITHUB_COMMIT_SHA', null) ?? + getenv('GITHUB_SHA', null) ?? + getenv('COMMIT_REF', null) ?? + getenv('VERCEL_GITHUB_COMMIT_SHA', null) ?? + getenv('VERCEL_GITLAB_COMMIT_SHA', null) ?? + getenv('VERCEL_BITBUCKET_COMMIT_SHA', null) ?? + getenv('ZEIT_GITHUB_COMMIT_SHA', null) ?? + getenv('ZEIT_GITLAB_COMMIT_SHA', null) ?? + getenv('ZEIT_BITBUCKET_COMMIT_SHA', null) ?? + getGitCommitHashLocal() + ) +} diff --git a/web/lib/getenv.d.ts b/web/lib/getenv.d.ts new file mode 100644 index 00000000..040f8be0 --- /dev/null +++ b/web/lib/getenv.d.ts @@ -0,0 +1,3 @@ +export function getenv(key: string, defaultValue?: string | null): string + +export function getbool(key: string, defaultValue?: boolean | null): boolean diff --git a/web/lib/getenv.js b/web/lib/getenv.js new file mode 100644 index 00000000..3821e680 --- /dev/null +++ b/web/lib/getenv.js @@ -0,0 +1,47 @@ +import '../config/dotenv' + +export default class EnvVarError extends TypeError { + constructor(key, value) { + super(` + When reading an environment variable "${key}" (as \`process.env.${key}\`): + it was expected to find a valid string, but found \`${value}\`. + + Have you followed the instructions in Developer's guide? + + There might have been additions to the list of environement variables required. + Verify that your \`.env\` file has all the variables present in \`.env.example\`: + + diff --color .env.example .env + + In simple cases you might just copy the example: + + cp .env.example .env + + `) + } +} + +export function getenv(key, defaultValue) { + const value = process.env[key] + if (!value) { + if (typeof defaultValue !== 'undefined') { + return defaultValue + } + + throw new EnvVarError(key, value) + } + return value +} + +export function getbool(key, defaultValue) { + const value = process.env[key] + if (!value) { + if (typeof defaultValue !== 'undefined') { + return defaultValue + } + + throw new EnvVarError(key, value) + } + + return value === '1' || value === 'true' || value === 'yes' +} diff --git a/web/next-env.d.ts b/web/next-env.d.ts new file mode 100644 index 00000000..4f11a03d --- /dev/null +++ b/web/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/web/next.config.mjs b/web/next.config.mjs new file mode 100644 index 00000000..80f1f429 --- /dev/null +++ b/web/next.config.mjs @@ -0,0 +1,3 @@ +import config from './config/next/next.config.ts' + +export default config diff --git a/web/package.json b/web/package.json new file mode 100644 index 00000000..d71dd21c --- /dev/null +++ b/web/package.json @@ -0,0 +1,330 @@ +{ + "name": "@neherlab/pangraph", + "version": "0.1.0", + "description": "Align large sets of closely related genomes into a graph data structure", + "license": "MIT", + "type": "module", + "scripts": { + "clear": "node ./lib/clear.js", + "dev": "nodemon --config config/nodemon/dev.json", + "dev:start": "yarn install && run-s clear dev:clean monkey-patch dev:nowatch || cd .", + "dev:nowatch": "yarn next dev --hostname 0.0.0.0 --port 3000", + "dev:clean": "rimraf '.build/development/{*,.*}' 'node_modules/.cache'", + "prod": "yarn prod:watch", + "prod:build": "yarn install && run-s clear prod:clean monkey-patch next:build next:export", + "prod:build:ci": "yarn run prod:build", + "prod:build:vercel": "cp .env.vercel .env && yarn install && yarn prod:build && cp .build/production/tmp/routes-manifest.json .build/production/web/", + "next": "yarn run:node node_modules/next/dist/bin/next.js", + "next:build": "yarn next build", + "next:export": "yarn next export --threads 4 -o \".build/production/web\" && cp .build/production/tmp/robots.txt .build/production/web/robots.txt", + "next:build:profile": "PROFILE=1 yarn next:build --profile", + "prod:clean": "rimraf '.build/production/{*,.*}' 'node_modules/.cache'", + "prod:serve:nowatch": "yarn run:node ./tools/server/server.ts", + "prod:serve": "nodemon --config config/nodemon/serve.json", + "prod:watch": "nodemon --config config/nodemon/prod.json", + "prod:start": "yarn install && run-s -c prod:build prod:serve || cd .", + "prod:build:profile": "yarn run monkey-patch && yarn run next:build:profile && yarn run next:export", + "prod:watch:profile": "nodemon --config config/nodemon/profile.json", + "prod:start:profile": "yarn prod:build:profile && yarn prod:serve || cd .", + "analyze": "cross-env ANALYZE=1 yarn prod:build", + "analyze:clean": "rimraf '.build/analyze/*' 'node_modules/.cache'", + "analyze:watch": "nodemon --config config/nodemon/analyze.json", + "lint": "run-s -c eslint tsc", + "lint:fix": "run-s -c eslint:fix tsc", + "lint:watch": "nodemon --config config/nodemon/lint.json", + "lint:fix:watch": "nodemon --config config/nodemon/lint.fix.json", + "lint:ci": "run-s -c eslint:ci tsc", + "eslint": "eslint --format codeframe \"**/*.{js,jsx,ts,tsx}\"", + "eslint:i18n": "eslint --config .eslintrc.i18next.js --format codeframe \"src/{components,pages}/**/*.{js,jsx,ts,tsx}\"", + "eslint:fix": "yarn eslint --fix", + "eslint:watch": "nodemon --config config/nodemon/eslint.json", + "eslint:fix:watch": "nodemon --config config/nodemon/eslint.fix.json", + "eslint:ci": "yarn eslint --max-warnings=0", + "tsc": "tsc --project tsconfig.json --noEmit", + "tsc:watch": "yarn tsc --watch", + "format": "prettier --check \"**/*.{js,jsx,ts,tsx,json,html,css,less,scss,yml,yaml}\"", + "format:fix": "yarn format --write", + "test": "yarn test:nowatch --watch --verbose ", + "test:nowatch": "jest --config=config/jest/jest.config.js --passWithNoTests", + "test:lint": "WITH_ESLINT=1 yarn test", + "test:lint:nowatch": "WITH_ESLINT=1 yarn test:nowatch", + "test:debug": "node --inspect-brk node_modules/.bin/jest --config=config/jest/jest.config.js --runInBand --watch", + "test:coverage": "yarn test:nowatch --coverage", + "run:node": "cross-env NODE_OPTIONS='--max_old_space_size=8192 --trace-warnings' yarn ts-node-esm --cwdMode -P tsconfig.ts-node.json", + "monkey-patch": "yarn run:node tools/monkeyPatch.ts", + "serve:data": "yarn run:node ./tools/server/dataServer.ts", + "i18n:extract": "yarn i18next -c config/i18next/i18next.config.cjs", + "i18n:addkeys": "yarn run:node tools/addLocaleKeys.ts", + "i18n:fix": "yarn run:node tools/fixLocales.ts", + "i18n:translate": "json-autotranslate --config=json-autotranslate.json --input=src/i18n/resources --cache=.cache/json-autotranslate --service=amazon-translate --matcher=i18next --fix-inconsistencies --delete-unused-strings" + }, + "dependencies": { + "@mdx-js/react": "2.3.0", + "@react-spring/core": "9.7.0", + "@tanstack/query-core": "4.24.10", + "@tanstack/react-query": "4.24.10", + "@tanstack/react-query-devtools": "4.24.10", + "animate.css": "4.1.1", + "autoprefixer": "10.4.13", + "axios": "1.3.4", + "bootstrap": "4.5.2", + "bootstrap-icons": "1.10.3", + "classnames": "2.3.2", + "core-js": "3.28.0", + "countries-list": "2.6.1", + "country-flag-icons": "1.5.5", + "fast-copy": "3.0.1", + "fast-memoize": "2.5.2", + "fasy": "9.0.2", + "fuse.js": "6.6.2", + "i18next": "22.4.10", + "is-absolute-url": "4.0.1", + "iso-3166-1-alpha-2": "1.0.1", + "lodash-es": "4.17.21", + "luxon": "3.2.1", + "marked": "4.2.12", + "merge-anything": "5.1.4", + "next": "13.2.1", + "next-progress": "2.2.0", + "numbro": "2.3.6", + "papaparse": "5.3.2", + "polished": "4.2.2", + "pretty-bytes": "6.1.0", + "prop-types": "15.8.1", + "rc-slider": "10.1.1", + "react": "18.2.0", + "react-aspect-ratio": "1.1.4", + "react-copy-to-clipboard": "5.1.0", + "react-dom": "18.2.0", + "react-error-boundary": "3.1.4", + "react-helmet": "6.1.0", + "react-i18next": "12.1.5", + "react-icons": "4.7.1", + "react-loader-spinner": "5.3.4", + "react-reactstrap-pagination": "2.0.3", + "react-resize-detector": "8.0.4", + "react-select": "5.7.0", + "react-share": "4.4.1", + "react-toggle": "4.1.3", + "reactstrap": "8.10.1", + "recharts": "2.4.3", + "recoil": "0.7.6", + "recoil-persist": "4.2.0", + "reflect-metadata": "0.1.13", + "regenerator-runtime": "0.13.11", + "route-parser": "0.0.5", + "serialize-javascript": "6.0.1", + "styled-components": "5.3.6", + "transliteration": "2.3.5", + "typeface-droid-sans-mono": "0.0.44", + "typeface-open-sans": "1.1.13", + "url-join": "5.0.0", + "use-sync-external-store": "1.2.0" + }, + "devDependencies": { + "@mdx-js/loader": "2.3.0", + "@next/eslint-plugin-next": "13.2.1", + "@next/mdx": "13.2.1", + "@nuxt/friendly-errors-webpack-plugin": "2.5.2", + "@schemastore/package": "0.0.8", + "@svgr/webpack": "6.5.1", + "@swc/core": "1.3.36", + "@testing-library/jest-dom": "5.16.5", + "@testing-library/react": "14.0.0", + "@testing-library/user-event": "14.4.3", + "@types/classnames": "2.3.0", + "@types/compression": "1.7.2", + "@types/copy-webpack-plugin": "8.0.1", + "@types/express": "4.17.17", + "@types/extra-watch-webpack-plugin": "1.0.3", + "@types/friendly-errors-webpack-plugin": "0.1.4", + "@types/fs-extra": "11.0.1", + "@types/jest": "29.4.0", + "@types/jest-axe": "3.5.5", + "@types/lodash-es": "4.17.6", + "@types/luxon": "3.2.0", + "@types/morgan": "1.9.4", + "@types/node": "18.14.1", + "@types/papaparse": "5.3.7", + "@types/react": "18.0.28", + "@types/react-copy-to-clipboard": "5.0.4", + "@types/react-dom": "18.0.11", + "@types/react-loader-spinner": "3.1.3", + "@types/react-toggle": "4.0.3", + "@types/rimraf": "3.0.2", + "@types/route-parser": "0.1.4", + "@types/serialize-javascript": "5.0.2", + "@types/styled-components": "5.1.26", + "@types/url-join": "4.0.1", + "@types/use-sync-external-store": "0.0.3", + "@typescript-eslint/eslint-plugin": "5.53.0", + "@typescript-eslint/parser": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "allow-methods": "4.1.3", + "compression": "1.7.4", + "copy-webpack-plugin": "11.0.0", + "cross-env": "7.0.3", + "css-loader": "6.7.3", + "dotenv": "16.0.3", + "eslint": "8.34.0", + "eslint-config-airbnb": "19.0.4", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-airbnb-typescript": "17.0.0", + "eslint-config-next": "13.2.1", + "eslint-config-prettier": "8.6.0", + "eslint-config-react-app": "7.0.1", + "eslint-formatter-codeframe": "7.32.1", + "eslint-import-resolver-typescript": "3.5.3", + "eslint-plugin-array-func": "3.1.8", + "eslint-plugin-cflint": "1.0.0", + "eslint-plugin-cypress": "2.12.1", + "eslint-plugin-flowtype": "8.0.3", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-jest": "27.2.1", + "eslint-plugin-jest-dom": "4.0.3", + "eslint-plugin-json": "3.1.0", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-lodash": "7.4.0", + "eslint-plugin-no-loops": "0.3.0", + "eslint-plugin-no-secrets": "0.8.9", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-only-ascii": "0.0.0", + "eslint-plugin-only-warn": "1.1.0", + "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-promise": "6.1.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-perf": "3.3.1", + "eslint-plugin-security": "1.7.1", + "eslint-plugin-sonarjs": "0.18.0", + "eslint-plugin-unicorn": "45.0.2", + "eslint-webpack-plugin": "4.0.0", + "express": "4.18.2", + "express-static-gzip": "2.1.7", + "extra-watch-webpack-plugin": "1.0.3", + "file-loader": "6.2.0", + "fork-ts-checker-webpack-plugin": "7.3.0", + "fs-extra": "11.1.0", + "glob": "8.1.0", + "i18next-parser": "7.7.0", + "identity-obj-proxy": "3.0.0", + "is-interactive": "2.0.0", + "jest": "29.4.3", + "jest-axe": "7.0.0", + "jest-chain": "1.1.6", + "jest-extended": "3.2.4", + "jest-raw-loader": "1.0.1", + "jest-runner-eslint": "1.1.0", + "jest-styled-components": "7.1.1", + "jest-transformer-mdx": "3.3.0", + "jest-watch-typeahead": "2.2.2", + "json-autotranslate": "1.10.5", + "json-loader": "0.5.7", + "morgan": "1.10.0", + "nodemon": "2.0.20", + "npm-run-all": "4.1.5", + "postcss-flexbugs-fixes": "5.0.2", + "postcss-loader": "7.0.2", + "postcss-preset-env": "8.0.1", + "prettier": "2.8.4", + "raw-loader": "4.0.2", + "remark-autolink-headings": "7.0.1", + "remark-breaks": "3.0.2", + "remark-images": "3.1.0", + "remark-math": "5.1.1", + "remark-slug": "7.0.1", + "remark-toc": "8.0.1", + "rimraf": "4.1.2", + "sass": "1.58.3", + "source-map-loader": "4.0.1", + "style-loader": "3.3.1", + "svgo": "3.0.2", + "ts-essentials": "9.3.0", + "ts-node": "10.9.1", + "ts-toolbelt": "9.6.0", + "typescript": "4.9.5", + "url-loader": "4.1.1", + "utility-types": "3.10.0", + "webpack": "5.75.0", + "webpack-cli": "5.0.1" + }, + "resolutions": { + "@mdx-js/loader": "2.3.0", + "@mdx-js/react": "2.3.0", + "@next/mdx": "13.2.1", + "@react-spring/core": "9.7.0", + "@tanstack/query-core": "4.24.10", + "@tanstack/react-query": "4.24.10", + "@tanstack/react-query-devtools": "4.24.10", + "@typescript-eslint/eslint-plugin": "5.53.0", + "@typescript-eslint/parser": "5.53.0", + "@typescript-eslint/typescript-estree": "5.53.0", + "axios": "1.3.4", + "bootstrap": "5.2.3", + "classnames": "2.3.2", + "core-js": "3.28.0", + "css-loader": "6.7.3", + "eslint": "8.34.0", + "eslint-config-airbnb": "19.0.4", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-airbnb-typescript": "17.0.0", + "eslint-config-next": "13.2.1", + "eslint-config-prettier": "8.6.0", + "eslint-config-react-app": "7.0.1", + "eslint-formatter-codeframe": "7.32.1", + "eslint-import-resolver-ts": "0.4.2", + "eslint-import-resolver-typescript": "3.5.3", + "eslint-plugin-array-func": "3.1.8", + "eslint-plugin-cflint": "1.0.0", + "eslint-plugin-cypress": "2.12.1", + "eslint-plugin-flowtype": "8.0.3", + "eslint-plugin-import": "2.27.5", + "eslint-plugin-jest": "27.2.1", + "eslint-plugin-jest-dom": "4.0.3", + "eslint-plugin-json": "3.1.0", + "eslint-plugin-jsx-a11y": "6.7.1", + "eslint-plugin-lodash": "7.4.0", + "eslint-plugin-no-loops": "0.3.0", + "eslint-plugin-no-secrets": "0.8.9", + "eslint-plugin-node": "11.1.0", + "eslint-plugin-only-ascii": "0.0.0", + "eslint-plugin-only-warn": "1.1.0", + "eslint-plugin-prettier": "4.2.1", + "eslint-plugin-promise": "6.1.1", + "eslint-plugin-react": "7.32.2", + "eslint-plugin-react-hooks": "4.6.0", + "eslint-plugin-react-perf": "3.3.1", + "eslint-plugin-security": "1.7.1", + "eslint-plugin-sonarjs": "0.18.0", + "eslint-plugin-unicorn": "45.0.2", + "file-loader": "6.2.0", + "fs-extra": "11.1.0", + "i18next": "22.4.10", + "json-loader": "0.5.7", + "lodash-es": "4.17.21", + "luxon": "3.2.1", + "next": "13.2.1", + "papaparse": "5.3.2", + "prettier": "2.8.4", + "raw-loader": "4.0.2", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-error-boundary": "3.1.4", + "react-i18next": "12.1.5", + "reactstrap": "8.10.1", + "recharts": "2.4.3", + "recoil": "0.7.6", + "reflect-metadata": "0.1.13", + "regenerator-runtime": "0.13.11", + "sass": "1.58.3", + "source-map-loader": "4.0.1", + "style-loader": "3.3.1", + "styled-components": "5.3.6", + "ts-node": "10.9.1", + "typescript": "4.9.5", + "url-loader": "4.1.1", + "use-sync-external-store": "1.2.0", + "webpack": "5.75.0", + "webpack-cli": "5.0.1" + } +} diff --git a/web/postcss.config.json b/web/postcss.config.json new file mode 100644 index 00000000..6edf1255 --- /dev/null +++ b/web/postcss.config.json @@ -0,0 +1,16 @@ +{ + "plugins": { + "postcss-flexbugs-fixes": {}, + "postcss-preset-env": { + "stage": 2, + "features": { + "custom-properties": {"preserve": false} + }, + "autoprefixer": { + "remove": false, + "grid": "autoplace", + "flexbox": "no-2009" + } + } + } +} diff --git a/web/public/.well-known/dnt-policy-1.0.txt b/web/public/.well-known/dnt-policy-1.0.txt new file mode 100644 index 00000000..ad946d1f --- /dev/null +++ b/web/public/.well-known/dnt-policy-1.0.txt @@ -0,0 +1,218 @@ +Do Not Track Compliance Policy + +Version 1.0 + +This domain complies with user opt-outs from tracking via the "Do Not Track" +or "DNT" header [http://www.w3.org/TR/tracking-dnt/]. This file will always +be posted via HTTPS at https://example-domain.com/.well-known/dnt-policy.txt +to indicate this fact. + +SCOPE + +This policy document allows an operator of a Fully Qualified Domain Name +("domain") to declare that it respects Do Not Track as a meaningful privacy +opt-out of tracking, so that privacy-protecting software can better determine +whether to block or anonymize communications with this domain. This policy is +intended first and foremost to be posted on domains that publish ads, widgets, +images, scripts and other third-party embedded hypertext (for instance on +widgets.example.com), but it can be posted on any domain, including those users +visit directly (such as www.example.com). The policy may be applied to some +domains used by a company, site, or service, and not to others. Do Not Track +may be sent by any client that uses the HTTP protocol, including websites, +mobile apps, and smart devices like TVs. Do Not Track also works with all +protocols able to read HTTP headers, including SPDY. + +NOTE: This policy contains both Requirements and Exceptions. Where possible +terms are defined in the text, but a few additional definitions are included +at the end. + +REQUIREMENTS + +When this domain receives Web requests from a user who enables DNT by actively +choosing an opt-out setting in their browser or by installing software that is +primarily designed to protect privacy ("DNT User"), we will take the following +measures with respect to those users' data, subject to the Exceptions, also +listed below: + +1. END USER IDENTIFIERS: + + a. If a DNT User has logged in to our service, all user identifiers, such as + unique or nearly unique cookies, "supercookies" and fingerprints are + discarded as soon as the HTTP(S) response is issued. + + Data structures which associate user identifiers with accounts may be + employed to recognize logged in users per Exception 4 below, but may not + be associated with records of the user's activities unless otherwise + excepted. + + b. If a DNT User is not logged in to our service, we will take steps to ensure + that no user identifiers are transmitted to us at all. + +2. LOG RETENTION: + + a. Logs with DNT Users' identifiers removed (but including IP addresses and + User Agent strings) may be retained for a period of 10 days or less, + unless an Exception (below) applies. This period of time balances privacy + concerns with the need to ensure that log processing systems have time to + operate; that operations engineers have time to monitor and fix technical + and performance problems; and that security and data aggregation systems + have time to operate. + + b. These logs will not be used for any other purposes. + +3. OTHER DOMAINS: + + a. If this domain transfers identifiable user data about DNT Users to + contractors, affiliates or other parties, or embeds from or posts data to + other domains, we will either: + + b. ensure that the operators of those domains abide by this policy overall + by posting it at /.well-known/dnt-policy.txt via HTTPS on the domains in + question, + + OR + + ensure that the recipient's policies and practices require the recipient + to respect the policy for our DNT Users' data. + + OR + + obtain a contractual commitment from the recipient to respect this policy + for our DNT Users' data. + + NOTE: if an “Other Domain” does not receive identifiable user information + from the domain because such information has been removed, because the + Other Domain does not log that information, or for some other reason, these + requirements do not apply. + + c. "Identifiable" means any records which are not Anonymized or otherwise + covered by the Exceptions below. + +4. PERIODIC REASSERTION OF COMPLIANCE: + + At least once every 12 months, we will take reasonable steps commensurate + with the size of our organization and the nature of our service to confirm + our ongoing compliance with this document, and we will publicly reassert our + compliance. + +5. USER NOTIFICATION: + + a. If we are required by law to retain or disclose user identifiers, we will + attempt to provide the users with notice (unless we are prohibited or it + would be futile) that a request for their information has been made in + order to give the users an opportunity to object to the retention or + disclosure. + + b. We will attempt to provide this notice by email, if the users have given + us an email address, and by postal mail if the users have provided a + postal address. + + c. If the users do not challenge the disclosure request, we may be legally + required to turn over their information. + + d. We may delay notice if we, in good faith, believe that an emergency + involving danger of death or serious physical injury to any person + requires disclosure without delay of information relating to the + emergency. + +EXCEPTIONS + +Data from DNT Users collected by this domain may be logged or retained only in +the following specific situations: + +1. CONSENT / "OPT BACK IN" + + a. DNT Users are opting out from tracking across the Web. It is possible + that for some feature or functionality, we will need to ask a DNT User to + "opt back in" to be tracked by us across the entire Web. + + b. If we do that, we will take reasonable steps to verify that the users who + select this option have genuinely intended to opt back in to tracking. + One way to do this is by performing scientifically reasonable user + studies with a representative sample of our users, but smaller + organizations can satisfy this requirement by other means. + + c. Where we believe that we have opt back in consent, our server will + send a tracking value status header "Tk: C" as described in section 6.2 + of the W3C Tracking Preference Expression draft: + + http://www.w3.org/TR/tracking-dnt/#tracking-status-value + +2. TRANSACTIONS + + If a DNT User actively and knowingly enters a transaction with our + services (for instance, clicking on a clearly-labeled advertisement, + posting content to a widget, or purchasing an item), we will retain + necessary data for as long as required to perform the transaction. This + may for example include keeping auditing information for clicks on + advertising links; keeping a copy of posted content and the name of the + posting user; keeping server-side session IDs to recognize logged in + users; or keeping a copy of the physical address to which a purchased + item will be shipped. By their nature, some transactions will require data + to be retained indefinitely. + +3. TECHNICAL AND SECURITY LOGGING: + + a. If, during the processing of the initial request (for unique identifiers) + or during the subsequent 10 days (for IP addresses and User Agent strings), + we obtain specific information that causes our employees or systems to + believe that a request is, or is likely to be, part of a security attack, + spam submission, or fraudulent transaction, then logs of those requests + are not subject to this policy. + + b. If we encounter technical problems with our site, then, in rare + circumstances, we may retain logs for longer than 10 days, if that is + necessary to diagnose and fix those problems, but this practice will not be + routinized and we will strive to delete such logs as soon as possible. + +4. AGGREGATION: + + a. We may retain and share anonymized datasets, such as aggregate records of + readership patterns; statistical models of user behavior; graphs of system + variables; data structures to count active users on monthly or yearly + bases; database tables mapping authentication cookies to logged in + accounts; non-unique data structures constructed within browsers for tasks + such as ad frequency capping or conversion tracking; or logs with truncated + and/or encrypted IP addresses and simplified User Agent strings. + + b. "Anonymized" means we have conducted risk mitigation to ensure + that the dataset, plus any additional information that is in our + possession or likely to be available to us, does not allow the + reconstruction of reading habits, online or offline activity of groups of + fewer than 5000 individuals or devices. + + c. If we generate anonymized datasets under this exception we will publicly + document our anonymization methods in sufficient detail to allow outside + experts to evaluate the effectiveness of those methods. + +5. ERRORS: + +From time to time, there may be errors by which user data is temporarily +logged or retained in violation of this policy. If such errors are +inadvertent, rare, and made in good faith, they do not constitute a breach +of this policy. We will delete such data as soon as practicable after we +become aware of any error and take steps to ensure that it is deleted by any +third-party who may have had access to the data. + +ADDITIONAL DEFINITIONS + +"Fully Qualified Domain Name" means a domain name that addresses a computer +connected to the Internet. For instance, example1.com; www.example1.com; +ads.example1.com; and widgets.example2.com are all distinct FQDNs. + +"Supercookie" means any technology other than an HTTP Cookie which can be used +by a server to associate identifiers with the clients that visit it. Examples +of supercookies include Flash LSO cookies, DOM storage, HTML5 storage, or +tricks to store information in caches or etags. + +"Risk mitigation" means an engineering process that evaluates the possibility +and likelihood of various adverse outcomes, considers the available methods of +making those adverse outcomes less likely, and deploys sufficient mitigations +to bring the probability and harm from adverse outcomes below an acceptable +threshold. + +"Reading habits" includes amongst other things lists of visited DNS names, if +those domains pertain to specific topics or activities, but records of visited +DNS names are not reading habits if those domain names serve content of a very +diverse and general nature, thereby revealing minimal information about the +opinions, interests or activities of the user. diff --git a/web/public/browserconfig.xml b/web/public/browserconfig.xml new file mode 100644 index 00000000..55954cd6 --- /dev/null +++ b/web/public/browserconfig.xml @@ -0,0 +1,12 @@ + + + + + + + + + #492c7c + + + diff --git a/web/public/favicon.ico b/web/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e0b09acbb984747b3b1d54e1c0019b0ac5f53832 GIT binary patch literal 15086 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 literal 0 HcmV?d00001 diff --git a/web/public/favicon.svg b/web/public/favicon.svg new file mode 100644 index 00000000..f291b7be --- /dev/null +++ b/web/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/public/icons/android-chrome-144x144.png b/web/public/icons/android-chrome-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..09e6ab4ce1e466abfbbc83e9bfa4365caade98bb GIT binary patch literal 6843 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 Lt0Ge-1r7W^x`yIk literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-192x192.png b/web/public/icons/android-chrome-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..d2853b28d549c1582adf34e27f0600345b15fe24 GIT binary patch literal 9191 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-36x36.png b/web/public/icons/android-chrome-36x36.png new file mode 100644 index 0000000000000000000000000000000000000000..735d214419f211048cf8f2dee27f630c41340619 GIT binary patch literal 1564 zcmZ`%c{tQt82$-QvfNv^Sz7K?gl2grOBikvnw1!1DJ9D+w#;A{nHhd$&z3FQSPF#- zgAB4{-@0~%mfPZ1QVC_bx4OUaUw_=^Ip6ut_n!Ct-t(O2OR%-UD#)tK0sx?Z$61o0 ztRW5=Nob#b=2ZzLDTcYVIRMnA%Khsu4b?DD9N8KG!VUuf7!3fM5CpyjfM7HLEVuyx zCIJP0Oy`Uv9UU0oB#(22%fbbgmcB6;d}y;4O?kN@89F6#^tyK_S;$$2ZL(e9m!(Vr{UkfdCFp7+(W`XXR1Qg%HGA@dmmCr4H z)!CjFkpH~3wK}yT$|KVm6IGvCmE@O8$0v9b6C>O*$Od40QH`fnd}rk3nyday>nt2*IdnF z?DWAPN=h+!y|273&^~#tsUUWlso2Ss{Yna$!yCt($RW&_AcL@FY@93ehJ!{*|js|F+(+EB5Bg*y`jjhm7uIqYiupCN_I;P=9C)6 z8`?6C%6pDzhbY56K7F&taGK^^goY7|4_jBbAq)n`QKpAZ9bO$C9A4cN(iw72`576+ z#D{`6k8aalWKZ9_^EEyF2tru8 z8<;zLsUw7ox=<@Uzzbo8YK1hc-0YerkVzG!1O6l-cf@tSNm&kKx^IT7f?fNdg#q?v zdgR(w?`7!4sbWZz0wBf6$Jl}>c&=fAO~9-NB}H(_K4~LFouPn~LfC@zW^=sFS1s#? z>tuv|u|%xR*gI`j>{|p2rRkJ_h%+8(a$D-Of*qR-(uo{$#9LhNkG!k;o2qEgVsREd z8u4_zs}r8P2VJTC;qUl;&ZvWm>ODxkFz(_nqv30X@)VQNZ&Lfw%S*e)*85+Hghf7# zCas*%!BJ889Y>ke`CzlI6_tX7p{G?>HA8pEw{AOpt~jl~=DsUw+_x?P}osu?%Y zg~(cF2WOw~Y(4n&cmBFsPjzXp{w$KFyxJJx7omUn3DwXQoY?&5*Dis2-Mty_7hvkR ziK)Tqx=mH(Ya&XXdVV(>j-2@Vtk}%iax1#N&a(8cH*!O>_88m%_fa`h*{ zMJCmoe(C=OUh$GhNT4QmuxHZQ9QQyPz~OLE-u}Kpp6)CfiW$hr|7fZXk(|OP&TO(f z2gV9y(!Kp?F!m)D4d%^adjddc!K-C?nE*#^?ZMMS!}xhJAbCg*bE{sVqXpH2V( literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-384x384.png b/web/public/icons/android-chrome-384x384.png new file mode 100644 index 0000000000000000000000000000000000000000..4dc46eb686015899482638bfd2d37dd721478365 GIT binary patch literal 19073 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-48x48.png b/web/public/icons/android-chrome-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..317eabab974dd351ec6187539077e507e0d761da GIT binary patch literal 1913 zcmZ`)cU05o8vY?-SfU;oZleqZ5(oq-aFNvjNk}3ILzV)PFvAd1LKeg@!`?_3WmQ0i z3=tCuL$M&UC>3czD}o4=YQ-zXLg_DZPw%;Z+;hI~eZTiP&-1+RAMZII-;rRyU;dap z008?jXcP&|2+5U|2BXicTnCu;23X^*0iZQYVarzrtf78r5*`4US^&UJ0D#XR#C;0@ z-+;mu9{@1B2>@ylr6Qs^0PG2N#5vo71bb~J7S6%I*k2B7_ko3RFa`{a?ms0Pugl#n z?O-e<(jTN@VQecX*ItK%fq&8JGSL6!gWx}&Y_u*1t;@z}gn}v;&<8HjV)VG^T_I>C z0a+kzcZM(ce_HR(BZ(4o76HZt-=G|?1xh$LeNe?h!zE;_J_C#n5~gGxtS;LQ&OpOh zm@l?=dR!1B8gc1v*}QA{E+#jE98$e)lENI*f4bkb_^_`n_x2TwgwDdoh5p{*`n$on zRB>tZeCn*I`x+{Frn5^}+&ol!w=tvc^@HAp2fgm*@q@S8lL87$*cD9AoJTcn zMfCD$x9pUln*-IYQ|&?rI46Nz@Sw7Fw4oz4u`NXb7mb*KbBw+mk}!9kqaW7 zGIAnIxxV@J{99N(&iA+4JS`GtySkmfy20?sneXi>VO0!Nx4Hc*K9yRG)8k}@m(2I~ zlI>FPh}a)mI)7{zW`+s!X=RrT!jy?@m*u`s&)UO@xP zD|fD2T$}t|UQ}sUNpn?vbx+xy>3gD=V)5;?+RmcJt5%5}`3*Jk)fr&|f@%#z`vHYE#og`IFXK>pt(MSX*;HXEg@aNw?=cq)Cjf9`d`*c!f zohz$5^;F0G-ydD6+kZs`Uxz%|$6=hv4;?<@>6R_(oLl>K^N-bgm(@leD`y>N$4qK{ z&~^?AIUhFjTy<#Hg%4UwU30KUjfMVDE!k?h2()_?xa9+qE9IF}rT~Hv8U}9*S=w_< z#>gOS-1!^;NGoGd*3Rg`g2H8Q9P!BDrID!wqBg%?OQ$AKBmRj^{&pSg0KVo)71=?f zP2;QwS$n2UnKB~ayxb;^nSf5?`vunw8!(u|HcBV-vWW}s>cZ}VqS`xPAGcDEtMLU?HQb?7Xwyd{I*1npNC7+W_V#w_ z9Xez&I=5fX?D<%7`Xmm4XD_g1T|0^tb?PfTQkLDtK`Q~L`#(Lk-mgOD+uGC7^K)JDct=zqP4=bDWLL zDI1BJ5!%VjPr|Gth1D(E*!NHYI92(1&c!ietf(ozUMc4PG&$=v0S;<|2xUJUkPz$QfCI_TWy&6Ozposah3SBkd3R--Ti;S`??6l z(1NvNe|_S-yVwWW%E7(Ie4ku1V|+`-cmugCz4a(4-b)8Px@?axK4L#=j~X*DvM&bG`8-%%#~A|KNqN7R*hVp z@Rn=dCg(KTyg#U#%`b(iW5;Xt|Gaac%K$<8pntH5{`;zrQSBMwCM-*3T5uwdm(*Hg z6163-pQhqaB{(kNzrkrXfih6qC>9AOAIGSpBd=p0e~|D2i+JkSwj#Y6u9 literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-512x512.png b/web/public/icons/android-chrome-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..26fe679441f0be69239feb8fb5bcb997884a9db8 GIT binary patch literal 25912 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 LdgwQ5wqgGds>`)= literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-72x72.png b/web/public/icons/android-chrome-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..802d8a5234f1496bc03ea58d4392f5dcae9f6f3b GIT binary patch literal 3566 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00006 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/android-chrome-96x96.png b/web/public/icons/android-chrome-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..bac20252486cc8c546a2b7b73446225d19069fb7 GIT binary patch literal 4652 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

##Z(6)C^df&N)Z6&%7ivg@3cUw3C!?U}az z+~;FukMWRWRvd$pW7lyN(q0x=f8bribsj$Qm;G}C38&h^jqLJ6?`ddGRj8V0`eA6; zZ=-S&wU=jiA-~8!i4N0@P;qpaerRcf(48}meolyUzUMzqfSu9Vjaj;xLOwc%1OSH@*1D$1+sEQ<*fMX361_`&a(;ruTN;%6C3fOJK-=| zM*kWUMJ5^q<$oV{&y8)`3P`N<&!az+ZINTau{0VWV{mCzsr{;U6*TYK7s537qVxM!6Y<R-8@)-qR74-@a-wuyAnb+yWpB7Z%}%3-b#< z)Psvji3v-I!TI2DDLDLZ%#ro~Avidh+gN)4|AfQ=jqzIo!@mr=PL>!?Q&$VX)6-MH z#?j8r%+%RJz{%Arbw`@<_R>=yJwuF+sVB_Y)ydMv-U5d4cD8`oIAhEJz$+s*OVEA(J})8joI{T)mlkUp%+rzZly X#vN+RzP#AS+Z2GRq9(Ff9v$>QMwy=t literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-114x114-precomposed.png b/web/public/icons/apple-touch-icon-114x114-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..69cc00b03a53eba444f250d45d974348a79dfe6a GIT binary patch literal 2949 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 u96*9iNWQTT?hW`hWFK~$f literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-114x114.png b/web/public/icons/apple-touch-icon-114x114.png new file mode 100644 index 0000000000000000000000000000000000000000..20db0e713708b175b1dedd9e2990ae1fc1104f73 GIT binary patch literal 2593 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%0A?B0E!T~O4J-!rqKn20& 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-144x144-precomposed.png b/web/public/icons/apple-touch-icon-144x144-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..1c7703c5f51c9d196c395ce0c288a222ea585b91 GIT binary patch literal 3545 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 Q=PLlXt}*n5&Li}H0DYmC!~g&Q literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-144x144.png b/web/public/icons/apple-touch-icon-144x144.png new file mode 100644 index 0000000000000000000000000000000000000000..2bfc286884b74e6d8b2f41079c8ce564f134d242 GIT binary patch literal 3080 zcmV+j4EOViP)004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0007u 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 f)fiH0{{R3FC5Sl00004XF*Lt006O% z3;baP0000WV@Og>004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0007@ zP)t-sNi2LxEPF^Rd`K*ONh^FvEPF^Rdr2#MO)!2=F@H-feRfxmsEDP>sI%a{zU9Nf z*tfdAov(FQk5)K>ka3%weW0d?rJa7EQ8RydT9MGMx8%XUa8iv9mWJZXef1!NteSeCTt9?wP>obKfnP#~%&WChG=RjSve&k{-o3q0GJj=9 ziNBw(e_)eXJA-UajDlp9vXZKZXqR+WkGYwytBR(+p0I^xmcyd4(XqI(kg3zMxL!Yn zW=V;xji|w(v9pt^b5)MJo37x#y}O#O&#kt?p|P!vr_Zgod0Ualr?gKpf7G+Nn0cOp zW0j+Wq)ae=VMB((p|H7_t!Yb&#G|o?XqS|Bos)E&rG=!toUhQYx5T5edt8#nrn0Pz zr?{7_YfX!8P>h3RmV924pn##2be(rukcMZLYD|i$iKb^siK>gI$fmQ!q_U-kr2qf_ zYs=4<00001VoOIv-=1mG4FCWD2XskIMF-~s8U-{c3Z?w#000QLNkln_RVcAS(a0qc=Ck8`Zz;@SQ`Sl!}bse0TV(SEaDzMIj_jHTOpTU0}h zx825sRA>U9V!WJb@e2?a&kE%m94=1I=jpBp_)y~=7C~_6s~qf6?i1LmPN8ELM|x04 z5ihiN3&Bzw@QP@Sx*zYzD^|c9#aJgNc5vngM1b*E$D~f3lVv%jOIKR@KvL?1-MXiC z?SVDL)HpG`(t64Yd=Eury?Q_FQ-po`K7usH&;k(IKE0nz&_6887?3H`7?{PI8KON7 zq8X%wM1zOO8l*gmGKZb4VA90UY`~ybK$J5~*5NT^b)r%L>Ys)~0wW|TH%~Ue$j4E{ z+?d)#5gP^VkCyTcdCBqMj_VK2&7K+`Ls_GPGDW!(-lOHF0fLhWj(MR!RYum+l53zT&}pj_vm z!=&|)*jS+40F@hA3ny8YeVIx#eX9h@?oj!PlOEdyZEpt3o=~YBTr8e3?^T=?3)6rB z&^DL}t%AB?_2A7d4FTC}TTz58EI4Y$W20$ZG1N7H7c*WD3CnnUNp)GaUD*f(C3a%9+cm@}GgWUw)kONmd^N)@jcFh} zq+k!Rw=}%y+};E=+ncHPb|kCvm8>=~=&I!GuBhc&$p-floFIiNUHVW3!d z_=8RZr)#B064`8`P-b9Wo;~x!qcR%1b|UPsc1N{_tJD5MJT3WHGscjcu|xWZfq^lO zSE39t5^MF@l>Dz>tiL!(Vi2!3n44p-rgvE;FjBL?zLTA}4Hu4gdD zfGjnKeX`L4uM%Mk{$w@!)aj_^+nd@l4HaiL+h<^mx{(nZXVX88Ld$n95ZxzM>lL4U zE{Ybtia1R=Z%8Y%DeRRCrJ9cGWVwi5NI|{Y=e0LVXLY3V_S6)R*gjEnQ4OurU)0(# zYYws$9hkzxFe*Ec)V5dq|6@;mxnTIdL$#P0q%F)Mp)W~rQkx^%b}Wz;iHm{pDjcc` znTug@Nu|*gh&LEt0+tNX!=B4zE^$SYW&&Q><^xi3$X9866?hs9h#ePV5jpJnTBWff z@N^Xr`{o`J%TmXvzTo8k6d>0B-ua7Ks5=sTgqua(gm?@GlIhn3AHM~}Sa#R2@Zgp% zT4hj^nw?TRdqvUU#wCnvq&p5hPX@%;_KL6`LxFTh--VvP2gKS05R*`2>nt+FuMP>| zQbe5?VhE5GF(sFRA%BVhi^!V1u98spzHiXPjtU?aoK%{pvPLo*fl}C%%h)dH{1*h)&gR;1b8e2(-eDK<)G{)>;S~B@TY{^8ReKpBEAfRSv5a$t9ZD82%O`&Cz5f>WIqWjw1Pi zCbphGF*Ueirm}Q$#&wY*0(&+;@+YPuX1mNH9F!wG>FpG+JpP|R5I@CimpOz)qmHU9 z-{$WpicYM$BnJkjDw3aQV%UShc+j~bRDm; z6fycsa=fEPkzA*EP2x@kz41misD4u<^v>fbu?~yTFfR}i%fphbbWIlUCW77&nLtP= z=UIQJcqLyD?XjpXt0gRQ!;)(hu`|MXMUDTZgvB3W3B8oQi8BeKij2~g-z6-<$3_#W z?OGr2WZ^;!rD{TBa!8OwIn3;o0v{MhSu>$ACnWfjBDR7vg(#y5LuS;fW@d0@ypH

&Mnp$k3wv~gY0_Zl$c}D4G(@>ir=Q~J$(POb4e2FoBG)x}@uK1h&Wa}TkyjTwo zG|ADB#+O)37&MEVXuSUAy1Z{zk9vLmHe+6X>^Hicps(pp#>D<}mX`sl4bPz*V>l3C z4$NRj-!t7Y;MH=MCoeB*6s8Na<`NyRyaJxQu*VT~7scGtR%Q#=xe@;M7hMC`*A95y zXyxiJhT}!Lf35C9XiXi&cU6Gt%%A>8TwBI7>*001R)MObuX zVRU6WV{&C-bY%cCFfubOFfuJNHdHV*IyEvnG%zbLFgh?WDmwM{0000bbVXQnWMOn= zI&E)cX=ZrK74o@OxI=I!KnM~D?hr_D4;EaLMK&z%&fD+( zdq3Wp(^IFrr+R8?y8Bk&iqq0i#KI)U1ONa_SxHU@A)EgzAaulCViKJXAy6Hq)ujQT z;UoCL8VymWu~pJh2Y^5(00@l)fICD}C=396c>rMF3IIfN0D#1^pi^5M(SdHGswf9M z|92I4SEM6aUc6IQmw&N`OpYeN`u_8T7XXm^D9cIf1*{wvLOjg|3x|I^?oPw9d{tZB zj2k&ksaE-Vc+d$z*`Zh%B|O_Ef)s)&Z;Ayof9sUlv^B1bEf9Zg6xyb7%OYH6V2UFE z)mJgt(VC%0a65C1Hgc|}al)Gw_9mPJ=I)0n(-ip6YeOnFD#olv&o|&lx4%3>9^sbH zd7*eoN9FdwKGH$r(=w_RW2l+I)HDhqA%GP3!2PrekpeQ1BTv>?PG6@6O-E**t_{Yj zA;FNQF}IX1roIwup0gz6kCWFa`0KKU-UH%T#cHcwI>_XUY-lqUW*G{bypyJ%QBi;% zlr61<3doy%b%rFh(k^F|&|~CwMYy&VTA4rj&q8wMM{@u2tAuZ{z^%pRy*`D%jCJtV@@{8=F|`*Cc@W>|f_k%GaNpCEVT# z?Vtour^2F6i?s>tVb`g&8R7Ua>p=EQY+$i9Aa7W=o5p^Md|e#^bXv8;pvO#b z%I+7UfLC9Y&8#<5rPjwD;ikcDOE^T^mM%Fz?P6P%L_O6m)Tl(~8k8lyR8GUFr2o6Z z7%KVRvCtG1{HVR~*O@BO7&hgyo)=|gvmmDT;p$G(>eo(y348&FjsE+MG#W2fll-z2 zUV#)U=bvXb-}(B~EKqeET~0`$G6khx8*VV|WD~~d0R2or^5v7P-E)y2inOgv>u)!+ zSc_e8vDYNcqpjG+PU9NbKOI_%RQ4Vh`x3Qk%u>`T>B`XcqF|2r)+L}BKAiogS!^29 znucqia5I8iT2411D z@#{DU*k;e)+Aw0+7xjNnyHLaWX3^MVWpQU$cqZVUZLQrOjjuq^TIj_a;qa9_TWvAFwqU2yH8HBlazx_}rly_OmKjGe8(yOBH&;S!*6VrpX|>nMXwwi;U+32K zF1C}KMe=&<_p}Fs8UuwSX@xsKlilrESV!d)hxI3-6Ny+qYpd4SO_=b4UyWv=Bo@Ph zq|6ia^&x3>b(KnzbqG6e?qdXP;92$A`~sU#6NZmeh_ zQ!v-nXza;o>t{GZi+Z|I#@T%c-Gk=iDT6QB^Lxpx)6zn9z}ypPH4d@SXpD@xjmOq) zQzij21En4O`do$cFyPkn~)BT-E+b zaArFx;lIf8ZL+ zEUB3FWxgw+L%mjNs&#)Uj>qh8mD)OXIrGBi`qFY-d|tN1i++Xa0h(%NWh=b#$%$rL z+wOKWGRAf&G4ytpmxcb8$6F+k)d2v-VBO89pq?a$ zv8s0eM-ypv|2mqLH2=%v*)gqayvKmD6`hMIY(q7@-P4OQ>?emxJiqpj5?t(gQnXdC3O(4f5#ItXxul3LWO4!wWL*5T8mg2^SZKWKrhM&z}1Qo2ererm3&y zg)d_Iqph?|RI0CVjq|dYoJzMNFloNkkKlX6&Wdb^kOfxZpF*#weTNySEOp(XWTf$ElL-HF&a1ZRl-mNWx z^u5|jJ$HgMz7b(!#qi>xqo|kX9t}`0QXXS z;D=DW{VEw6BgZ=ywbQk8cj5O#?E9Ui!!bGHMILQQ+9~+&a2a7EZRK$j{B))TM(5Sp z8m0669Cw`!4ZzTlqnu^7l10zm`0*I+(IXz0lMjUaymQcCS`o4COg${qf*()ngQZq7 zt;@HF@Ghz1d_>f~I{6inlVAPW`;p=g3ZLSZN`!gljHeSq13~*8VP}_v{Va3r0u)1< zA^TxdQtWWaKlQ=VKD)C%sA0$HWL;dC2H3;LPoCI&+=-(-n&>G0w75JLUr|CLjrA;~ z71OR-juO4=M+Pr%9ZoBRmLk{q3eMaoCK55w)B)OE$5B%!;vPMg0WRhBoivXUM+O7$ zS*eF(qL0N%9boyF9FZezIx-;XKYX{m+YK1EB~t5s{J;O9P4XZY8yb-sjKL@*r8Y4t zVyjfXl9F9DIHyqkNwqVP=0=j>*A`&+FKrf17=5o$n`bmF{)ObzpuS<1=((4vmU?39c=fmiXk+l8*5T1>D_IlA-*e^H&3b04-`Jkw|3?E3W>WiE5hMX?99iFNl~r{#Bx%YtYI- zMGcd!?Rz}QA9dJeWvL?xQ&I^<-AlElHtz8%w+B$r-&fKu6z}*>45kUQMlj~d$q%ZX? z$nUu7jYhJpADGLGa=2`K7CHriXaqwW&^ONdZf`XdY=>eaOBS`qf0u>G(zd!e_?SiN zfe$@B+t06DnA`)dA0#;oxg-QJ@Uz1cZ0^vW;gS4zHz)b>gdY) zCsB5Q^3WFn&!vpPd#S>%B@5~$k@|tOT8r0KEZa{whc78U##dp^f9Ov#u({TeH_G59 zX><8K$PlfmvCZR$KN*zH6@r_*rCPKaN^up-jSI22!lcc_G*huw#^^+B>p~x7LUOq9 zc;~anRGLmZUhLgQOEff^Lf;lkl(INlLd}4hlip5kH5y2LJf)OLoS1Dw-QYbXN z=8o{)1Qj<1SRK^+zR03<>G9S@&CyZpB&qIoajU`;pBV14)=6K&AqYdfcih}EHm0Xt z`|39C$8*1k{5Vb%4V_bZ{+?@VR&kX|$eAYF>?>DoGgzMxm8(_W@AC>NOSx;vlfVoD zypDRR(S!U9M=a1JXG9~faa1k8Ym>{+%a&*_?Ud6$KR*l{F~BX6^(9;>!AF--6G_7 z#9}DE!`t9=7r)bSGg0A^!&x&Gmr`Ogpl$0-EUPtEZZL5`eR}&=! zxecVPf>zU>n|t-E`sUhElJ9#C640>C(DVk>>)r-x`&A*TfCnPA?hTc&RykdH{xs}b zSLA5mrh}6+Cgb5vQX(_Z@YCb5#qfN|i-&Yb?D!HxL%^nY$q3k7{qXWbK+US24Z7N{ z>?T*AsO786(iV5pw)HCWKN;yYvH}0JNYG!rKPey|>bLY`??lSfiND6S?DYuoN7vQe z{ifwYE?sAmo6zYyj{4)N?$|;hM@RV8z9|f0YrxL@+XDF2&cP_Idihj7 zq>3&DFW62~c@ck(!n95B-n1L(aZeB#ux00&n1>tuWE>p)4)+73NyuJ0r)h8KWa>GP z^m?<4xXWKZ)bmWHszi&FSpyr&?QvBM|7MsO;*!LQYHkGUyK?UnDx?zQC6!{>_`21x zWZ?lIJTtvdrr^2?3YUI@E+B&Ln`4sI(_ta5zq|Pc|4moze-$Cm9G$-cyk_|$ONY_3 z%}#xEL3a!J)m8-7lN-5)_9Lw4W(Qh1pG$oRLq6a_q0)(Fm)2HR2jo%Lz z<)2H2UxPG@DKF8dmqo~*Md)Qe;YjRaSv_BWWL@~7#gfSH*e%>7XNKNED(Tkd=wDV@ zw|$LsJt(Umi7RZcK*Ut87Ccgh#QVM21J}9`Y3n4$@b8DVRgqHCl*cISHRE`fBZoTXvY3zi2*iEcKK5 zYFZm)p{&a50hs8rH#bW^`-19mFSchV&}P^v)6TMb|72+MstYY9{!J~VTng?Ww3 zz|k>7`S;&u!u|AP10NNsaX9YG{aPOw9Yh)&z^%BX0@R)D(@}Zzed8{Z6On0e*~i8Z zafzZLr!*X0C9y;Ks&hT_^S(ua(<=olhtozMgRF=3l?`6i>A*1jQ2xIn`CWeT?8G4# zOK)a39UaC@8DFCJ=PWFrzEmYcMj0x}Hry@#rWbIqd|adsp|RJ<5~p5)V{v@#&$_^g zn(*bjbY7yXwJ6xr+vgC7?TkHS_ie zW)s<2&8z(J{nVfFxklphc5#^xI^9p76}5n+y~s`HWD6rRr5Mn^JUF7#%-{M{#62a0 zgQY&+xe-pQZR~meVOk8!bl{cW+`jVRTU;5`#bc$xVlo%woXDj$@qKTYf%q&KF$>s)503xgm^&cdQGg#r_FY}9Dhj;bh2M5wd0@%jErYG7CwgIGBJJo zt=(;%!!o?9{x{hUexPQ#Dy&)lES|C*OaMNN1Di~FV9}z&<$tdMp}+VynxA_(HW{nc z-@i{c!AX$Mxb?ID6tz(Ig`K@oUUN&s|J2ASm!PkQUH8A<0+GWZw3;DWA)V#=9WcKXG{DH5$JcJupmgw5HKk%(rCW^NfVLf5AX!rvk*1epAMjD~5^&Bk%RVmwE%a zBYxl=Chi3v8PSuYgUkY3iG|V$N4vmtIanp0Pu_IyTqYTdD>Y=z^tcRTn$fIKNs1!` zNBAk*Ni}H%WfSp*=mBZbZi3iR<6lf+=On`|RK#Fllo*u+BM+uFGhcls*ZnkGrfdN> zHp)TL7+TcUtlN=-huQh`3`z5_-=2rzc5IOU!NfND|OWI*GUX2X9n{;A&WYtNsw}OMl8X` zZ#`T|sb{VO8#2j@WuL6#7N&I)I~8eo$!;M6Ek|r#`~i*0Q$7k7@stTKx~qd;N0nQ0 z{|Ts_Cv2$gtq+a7Py4IZ&R}qO+$}&0qxdN!awCvyFrC^_;@8Nz!#it%C&(38h1l^MotWnvPQBSnuiJWtsc<)rUfrrmI@7`KC=NzHCEwVO>$;YI_I3wfl zmxL+mI4wiQLX;)WtTb)%joz+vKywW_>>_-8)JsW&!u`0N?Vl)E5M)k<$Kb>{t8$2s zJy1}HRq^QG5T|i2vg~j>l_yPW6dA5x+M3$BI$-M5BT4~>54i8d&jdN+M?@H8GSn2) z>F}b0^Jp4BsB#|QkNxThz}_P4@(~|ufJRhZuG-LM#G7stTy|^dO`PUnM9N$0YNZ4^ zs)l?bsAf!g@jDIj}+Hc&T`S-x!0a+s&1Trp(vuc%RQz$H4MCawtb zRX}z-!HA)>T=def#??Pw>^1w)3+k)tS<3{w*u}hN;bu$&QPP+6g!p@Y85wp=>$qB` zx-|R0zmb;k3J z`{1U4Q<7Iz5>qn4wdmBH>L(v(=ETAfU{xg!&;B9lK3F&}r|?~bC?1sOYp}sy=gU4b zf%g4Agc|#Ur~K)j(Lf6Ij8ZgRIFnUZ+7jfBP4dpDqaeOjxBsSN$E7lnkE_at>xDh# zulu5i3f|z%_wx@v>$U+$w}O%1kaw#}ww2hKobaDt; zHj2FYGPC}r&!5wN1rI@Bh`lwBTJp4%2bc`qJ#VBdEag zGB$$0N=dbjn34k5`J?kA$4?otfFdI4d6$sXfE0@pbySsXSYaNfjf=`dIRGxxGMoU) zg#n4YcY5bG-NY)=&=O#to$%5VSowY27|NGM93ACk$n_%=#ZwNBRL_I4Qn5xg6IDF_ zZN#pGuH4HBK0C0&uv`pYzVw$D`2CSWGy8*HBJ@l(?p)25Uq8sol&R|4eT^GV52 zO61SsZ%+kqkOSo5#GFHCHJ2!bh5=^&E+HL=@c@pA)*uzQ0b`VrHtu9xO~gNqVXwa{ zN#M@GxiusNmu{N%(x!tZ55d?je5@-N=_S_maXg3_2!=5?zcmm1##(AJKvL&h4Hee> z^=uthTpBVWSz|g9vodVU!D>_6nEaRk5hpz_wB-=Z-8V`q@IQth5o8o`JR zMKDl-l;OM{sBJ`?PaQvkF6Bb$BmNNyfSZUVv~GnDu6Kl66gVM&6!;1aN*Q%&SrFL;dq5JrRc0;enf799u9UvEV>}8Ep=)rgI#HILgLyn36u^fqTAjkT(Boppe zf$8Xxs~_~hN-2f9-T8B%hpcdG^FtjXBgObm-sqj3^*eh}TW@=W0Qh+L_&Is_IQazi zcwURX<`Wg*W#{1$<>5(Gf0Fq>3ho|u&JK|OU*Qo~;R&H&7^r9TPRH7x#?#xw!P(88 z<{iY-p2pepoh<+a6hO~jg1ih^SjIHRCzN+|02F#Kmo1opoxi34u!r+ rk7j)7_sGD+0Oi1o@ld5=6)s?(1n=FM%004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0008D zP)t-sNi2LwEPF^Sd`T;ONi2IwD|<*Qdq^vMPcna9K7?jSh-gZQSv-SGE`4`ckgSZS z&8)WM!N29hz}L3AoPD5BGJjV&f_7JrlXRS*fTF63rly6Zd0LTGIDtwneS%_?)U&y{ znXXnif{t&S#Ga$;lI94F@KYDoR4suqJyJsPK@8Zy>(WO&#ktIXqSy{ zns8B!i)@&RY?)X)gRzjQiE5a?pRb5&m#>eh$ELH0X_(Njw^B5K;=jIXOpCXdtj?{q zhG&+=q_V-Fv4&@tyPB?rW|r8ux~z?;gk_f3wYr34mDaSmZ%~bMRF2cKxYDq=(5|k*VOmzDzHE!lAKBE`7hBuwX)m z+qt`{i>JPvua0h-dt8#prn6Kxfon~RgJqU{UXr(#tet(JwUw)~lB(LcyS$vQz@M;N zJ%o5#k=?tzW=V;6S&(*EkZn(k!=ka5c%E=ljlP|)UqXeHcAc({s8~9JtBR&%Mu(t) zp`(MOT0DbpP>j>DxL!eplysePQ;mIIlXX{*mv^3;d!Lkboor5v-Mqc5i>H!uoM%ai zxR|VtZ<>~No|t)_r-!Ajji>+r|3lgVP5=M^0b)x>MCQS4S_J?A00(qQO+^Ri0vZK0 zDSe{MaR2}c#7RU!RCwCuo%>@I#TCc*zVawy$f_+MxezqrAs{c25FV05K%*_j6sSOz zfUQ(~)fBX}w!TnAj0u9GrZyDIGYuo>$_qF$)IWv2A&g{%w z+J1jZX6Eks+_`7Z&g1N^<7iYQ8gra@>W_q1lKvy^L{rjfN0;J>cpz6IUTVsM$`bNz zV@`Qlsd-bx5dv}ny@V=Pw_Yl4id*oC$L)J~LPJ+2A~uQR30)B^YRgTu_D|djwE-ih zojYcxV~jetv5T=eYLopDDt!8@+#qsB2KvOpFip1!tCs;yvUbJQ*EunZqXZqpgAB9r zQ;?CC9xAsy8jEXikyBuz@&BM`iKe2VT%=^vXZ5Q(EoxL$er3pM+Ncc;C!vO(UNx-R zP3ep?&thx7`nBOBMpm6Yd{hnfm`V{qzMqq$$GCabf{L-{oLlFWSJ#glPXkc!P5GZ^ z!bBJ9ygc%x$xMYi<$M}~q2X#;6;oNXX}R+0Gh9wpGbu-o5@Se2{cshS+8 zxyAL@a5Mc(^8br5)D)-kq9V-hqQxW_$tXsPk4rFA%Y;U^s4gKLKOyzA$7td4Qq0tP znH!+XNd~0)ns^}^5roL=>of}n)&agjhpt;nGZ8qTAP;1(phLTD`umQiD~rI*ZU5%7 z<%04RL>tX;Vairp^Le-!gQfc z%-pdy-(vhauJ&50l_Fp&zVjU+^W^V>Cf9M*Bgkg_1B43ndZBNe4x4 zzcz@VH?VEEvB(W{J3bw%HUYj-zYD)P{|a3E~-Lr z#y~UpHp6h6A1N9O{1%e>F$TJWYm)51ZGNI=D;Ds-w0??#?&O-B#<~5Ks@7O3PY>?G zKzDOZ?%|+s)55I2h&>JiwQx=NEB(ES#@MK*KFsEzw{T5v!cFSP>%Jm|m>NvSME7w` zw%{iB>tGJEg|!tEO~EZ5;Jjm-vNcT;uLlFW@oElwJ%5X!B zp2HpGq15cAOJ>czfEs2W`C&{n3^iC>-@GSXzG{20IX(MF+zJyOcJd1&JMb|c z>C9H_B=mHco#b7e80k^AK?T2y@fbSB_SRfQi?rErTE-zYvJLo2!tLk~yC2WT?A!(Lxj3bqQ}*|5c4uDX9WtWUPXy6Sx)fn+3HP~Q(4 zOy{ewfd@FiFbe{>el;%=LI1MQ;9wV5`ztuWJFt_jd`8-|x{quWF}u&^>&M>D*ZvxO zzIP9*{Ed{^^qHu94Sz_;?Dl!}74>{&6Ntn$xPHt~DZ3FzJ>n&w6GKnT)er3F>w2Cq ze10vrL;71e!)a*M+2J8Ev-?7}y0TkP_hSCjl{>j{)Ju|vLtGOVcKbkQZBOqKR8?L9)h!EeD9*g>9q0U2K6Q0F2dps<0d|k^ zJ6DXuW^yFn6sNo$eLgG+LB|VrJ5M;>zQkSa-cnM{yVbaI(Vp`{K@jy6NG{#xIFE~o zTSgv&=`-&wfkV|JFWhJ;W_WrZSWgXe&Oo#cwR`{AHtHnzv3(!3Et%EVx3~SRt&8QR zRT?!9blI3xyuxF;4{Za<2xihuOT}#Lty4=4DAJ<1ns~XAG@p+qMo3V+{R?d1&GiLM2!m=*VnVa@jz%@ED;1;Dh$RV?JIuGHDHlKTkZ#SbY>8r?_Y03EqyghlOS6GFh zKQa#OT~_3vp-YeR3NK-qI}R9!CTR|YYxYXfqFB9gjKZZ*W_SuS9S6M*-PglHUtqBp zWg<1qqRHQffEN%X^9Vu+xU$6fnSz7HH&9@Z4(@5@ zPPfm!QgnMh^)^(xm_QeJDl(3czc=f87FyFy6|xFk(Su7%FXv0qhRfQZql6VP-)aY7 z%Maxa3^L3;x6+PD7C!ZY`~zmA?ePkCu^m0tFf-TDK~HL%4d5nug(&xme&;Mr{9T>_ zQ4eMmAM*;QS$1ivIsjZgi;+!%Tu!}(ohoYDV+Ev}@0E^WpuZ>qbJ6c{0@6L0D@6M( z|1bAd?4y@O$%>zGx9RGij(5bu^;A$vXi(k!#D z_xkrWGjI84w(JDES!;Dz3gqwV%sF>FTgE<@LEHW7VyWbp_CmhgYCoHDWo7?RDF3cz z5^*y-SZf&O27M1D6sc%D_Wc!@>&U-S1<9if^kJjSirMtcHbukRj&qC+(v+-RvQ%#$ zn#3UXKl3ch+--D;H}EX<3D(?=_^b|@WR~b?ag*&unV&P+(K3TOAE$3B25oe)RO&1g zclBpQm^Yea4)X-u&;6NOO~&YO55IoZ7kXT%;}#bo+KGOWNBo#2?m<=-X~-dJyiLsf zh4p+!KSc#U%YZPgbU*2#uv83Mb5U0}3sQ>zZEy-sc#N(02|?LGeDm_)b>@(jh8&`= zx%$f^0C~}+Mt8#XJ&$n0{0r~M2Ip7WPMSnk9gg~4iw2s9yi;Km*fIL>X=4iE8zp!* zRz{Ii@_l*#d(z;}*55y)#U)Gnjy+|SSv)N?zCO`f5YhXqTEW)-eogb9ZQ@;crlSb) zvFOrWVUpS0+nfFiUw~)q;4#xN)xg3w;%rzUaiGrR`#*@<`BiC+0*?$u|JTBk@?2xeX=r zPc#;dXbo-u0fG>G>Xdb=ZU6uPC3HntbYx+4WjbSWWnpw>05UK#Gc7POEiyJ#Ff}?g zGCDLdD=;uRFffcw{z3o%03~!qSaf7zbY(hiZ)9m^c>ppnGBYhOGA%MTR4_FfF7004NL@DLLt zYa;*vOwcGqlonzCZ9`q{R6QlhwV-z`5EBRh4=Rj5C+KTAC2)PF(? z@P38G*Z4EifsMz*f?FaSj9Hg=cdT8yIG}pfuFkNp{_K3 z?%mE(aT1cXI-&`4rD+7c^eZ*>Q~~$<`u^I^sRs)6-TQKWSER?S)UdKy=>Rd9rTn4$ zjcPCzQ<@fDzM@vY?2$`(9RqdUYmYSTEb(|p-^1#T_@LX*#L`BZaQ%@+!WO56m)$FD zNxxL45!?@Vqd#x!tI2L+UFYY;RsGc6e;r*K>B&eAVNc&zsGEAf2`G9lmaL~(R zJg@3TLsxm*x95wP#M)O66g}MbY+OYcgubd#%W67v@Rga-9BxMaWVei&SQG7Cz|Cmj zd|$u6zt0(#4{1++m>d-z4Y-%TUas)eIrED{AiM5NnpW3IycmLLiB(euw8Vgn#vl!T zJ7BHv2_{=I;s8MRJQ@**MLnzK=YBJD;mFfZ=bpLY6w!x3j*g|M$l>Br!$c8VXvj2C zfzF*{2_0jc+wu2()a`z%F@pEPqRspBd4h@052fISL#G}2i@MC9L&u6yD84V4JmgB4Lr3?UhdrrnixbCAbSyN6`AMq`bR*&mtH)liV z&<35n#Ey67j&CBwH`1N?CbbNN)$#Zk$(J3~*)LGudM%|Lj!}Ii7p26 zH2wLfOF8I7#5DN#E5E|BXSRts{OojS%5sOwO-VOguN6~zI2Vl zY6Fqnl#GR|!P|VUM_LAac#gMFc->3}Sx%kARi&R$?wa zQY+{D+b?NNsan)GT}Svgg@ z?bKwM!r-TUfZ3mLcqD&sMfe>1HgHF#dzSmb1iT6SV!P13X}b!;j%xJYSLXLRxcv4K z4&KxW0PVSP(s)fVk*gNNHbeO_Ns;uYXEv`F*v7BC^PxyEC1G2BNc!+4j{gP!^S&wh zxzw{eWkas5-7EIZpC*dpo0l;+ZP)#nlzz(C?lY)y=ngjQIb!Qizh>UVBq1)9cgyD6 zx4HHIh!#-%`{qN;)}I_jeLWiW#V0<$6P9#xa5UZX$=XU6e$`{$&UF#6xmG)Ni6yZ# z>wK3;5^aiP`Jh=$XHgBa-!~vR+veCd@sD_h5UNS=J84cDJA7Po@=Lk^SA+pmEk~CR zRV+Rc?u*1*b?`|;bM1G5$w)kzm_SbUP0C8v0^kmD_i%x@ySRH}Azr>-?!F#AP7sJM z1hRpDl<+@+^bBIk)x5tC)aX;*XahJ37Eg{!$OUC)Wn4{3O9qkiGLu0mndBq@xY@Y2 zbJ*}kjHBa=OXEMHH=+PtJ7d=*V+%Wri#$tR3lM_g`Ix9{C&hxs=VzXe{y1tgYB0_~ b2}50htz+iooglAT?Fs;m3`g_?U19zQ+b#b@ literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-57x57.png b/web/public/icons/apple-touch-icon-57x57.png new file mode 100644 index 0000000000000000000000000000000000000000..3d8e5729c29caa3644fc03a6204bcc28bc54c0c6 GIT binary patch literal 1527 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0005Y 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-72x72.png b/web/public/icons/apple-touch-icon-72x72.png new file mode 100644 index 0000000000000000000000000000000000000000..4a3f9e1cb6998cfa98fa12b9f291d77b00abeba3 GIT binary patch literal 1782 zcmV004R>004l5008;`004mK004C`008P>0026e000+ooVrmw0005? zP)t-sNi2LwEPO~Sdr2#NNi2IvD||>Sdr2#MQZ#@{Eq#Dtl*FU4;J&`)!@$+Exn@a; zPBDIhWRli)>DeYE6n{Mu<{1fWDls%&WC(Op3yx zu~9RBM5ec} z8Co7>`s~{-6Tu`^C8Sj=RIa8v%A?4Vr+2^ zLAp8#ySRwph$A*k(7o#vtf{z7eu{IWWjredIB_zLwfJ8~wc&EISS#Cxuj8smjvF|C zE8dh~P$*Xn_9i6y#C1uMmzth29z!Ad|?I+1S9(O42Nt7`!62bgWWMzz4K z161Dtuu(76ZI)1^DhB{H0jji~5NQT#3!o67R{dDhMYRp62LT-dbXfi|*sh87C>I?& z0@R~`S^*scbiCt)c7ZWl5l;g36a%LLbOspelBP`;wnW`M!0lzIPd&0wzvLFg5{$Ot z0l6OsDL5nd^;rsPhNwTR4R;z9{9I(xk4WHrrE1ECI+@=VD`c?lma3MZ3m3s1l_txj zf)3`7AuDuQnfhbN<5y%ib711Cta&XELomq4JE;hbUKe>QZbBw>Lo~IUw~#vCDJj}# zs55;VsRQkorsm$sQ{1v}J64BGOYuW@qcZpI&&;;SGJ|ty8B$AoDE$F26Eo)LymYt^ z!wU9}~U2}*xY$OjNA#|gO!w^&0wBNOVgprf@a z5xR)vGNDg6mcVE9nbN;V&R6x$iZpdAB}Dc?wnu;aZb?X;(htfHOE3Ek7D}YI^Rrdx zCn3Ms$bO@AqOT#U4j8BWR6>@SP%}nHP`%GY=Lvi2qrrGKK@w33>Ft-z?56Cgx@G{a;ABePT>%h=S&#LUDT#0SfONT5nC Y0O}VJbn-$ql>h($07*qoM6N<$g6w4~b^rhX literal 0 HcmV?d00001 diff --git a/web/public/icons/apple-touch-icon-76x76-precomposed.png b/web/public/icons/apple-touch-icon-76x76-precomposed.png new file mode 100644 index 0000000000000000000000000000000000000000..45e977535467e3eabb6b2eabca5e5d7b15c003cd GIT binary patch literal 2158 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 Y@Jd{;RG<|Mp00i_>zopr0G5MT3IG5A literal 0 HcmV?d00001 diff --git a/web/public/icons/favicon-96x96.png b/web/public/icons/favicon-96x96.png new file mode 100644 index 0000000000000000000000000000000000000000..bf7fc46a68f47479baf6c6aad18d60cb00cc0486 GIT binary patch literal 3860 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 literal 0 HcmV?d00001 diff --git a/web/public/icons/mstile-310x310.png b/web/public/icons/mstile-310x310.png new file mode 100644 index 0000000000000000000000000000000000000000..caf4d18e8a817900b0de63924e3f528e1254cab2 GIT binary patch literal 14802 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 WN{>F!lu5fj0zl{(Yk$;4M*JV_lD^IW literal 0 HcmV?d00001 diff --git a/web/public/icons/safari-pinned-tab.svg b/web/public/icons/safari-pinned-tab.svg new file mode 100644 index 00000000..6b06b022 --- /dev/null +++ b/web/public/icons/safari-pinned-tab.svg @@ -0,0 +1,34 @@ + + + + +Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + + + diff --git a/web/public/manifest.json b/web/public/manifest.json new file mode 100644 index 00000000..47b0ca97 --- /dev/null +++ b/web/public/manifest.json @@ -0,0 +1,22 @@ +{ + "short_name": "Pangraph", + "name": "Pangraph", + "description": "", + "theme_color": "#e5c3c0", + "background_color": "#492c7c", + "start_url": "/?source=pwa", + "display": "standalone", + "scope": "/", + "icons": [ + { + "src": "/icons/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/icons/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] +} diff --git a/web/src/assets/images/biozentrum.svg b/web/src/assets/images/biozentrum.svg new file mode 100644 index 00000000..e9314662 --- /dev/null +++ b/web/src/assets/images/biozentrum.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/images/biozentrum_square.svg b/web/src/assets/images/biozentrum_square.svg new file mode 100644 index 00000000..39c88174 --- /dev/null +++ b/web/src/assets/images/biozentrum_square.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/images/logo.svg b/web/src/assets/images/logo.svg new file mode 100644 index 00000000..4f39bcbb --- /dev/null +++ b/web/src/assets/images/logo.svg @@ -0,0 +1 @@ + diff --git a/web/src/assets/images/neherlab.svg b/web/src/assets/images/neherlab.svg new file mode 100644 index 00000000..f7baba77 --- /dev/null +++ b/web/src/assets/images/neherlab.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/images/nextjs.svg b/web/src/assets/images/nextjs.svg new file mode 100644 index 00000000..8f5c90e3 --- /dev/null +++ b/web/src/assets/images/nextjs.svg @@ -0,0 +1 @@ + diff --git a/web/src/assets/images/sib.svg b/web/src/assets/images/sib.svg new file mode 100644 index 00000000..0eeb3b32 --- /dev/null +++ b/web/src/assets/images/sib.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + diff --git a/web/src/assets/images/unibas.svg b/web/src/assets/images/unibas.svg new file mode 100644 index 00000000..6ccf2da6 --- /dev/null +++ b/web/src/assets/images/unibas.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/src/assets/images/vercel.svg b/web/src/assets/images/vercel.svg new file mode 100644 index 00000000..7d199d32 --- /dev/null +++ b/web/src/assets/images/vercel.svg @@ -0,0 +1 @@ +> diff --git a/web/src/assets/social/social-1200x630.png b/web/src/assets/social/social-1200x630.png new file mode 100644 index 0000000000000000000000000000000000000000..1fdb8ad10a074edb815a108ab38c5b12b0244dc6 GIT binary patch literal 696 zcmeAS@N?(olHy`uVBq!ia0y~yVA;UHz*NQv6ktfVcKZmVSkfJR9T^xl_H+M9WCijS zl0AZa85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+uenM@oty! z5+IMg#M9T6{W%jqpC;!eqZ2O~7#I(Dx;TbZ+* +} + +export function Checkbox({ title, checked, setChecked, children }: CheckboxProps) { + const onChange = useCallback(() => { + setChecked((checkedPrev) => !checkedPrev) + }, [setChecked]) + + return ( + + + + ) +} + +export interface CheckboxWithTextProps extends Omit { + label: string +} + +export function CheckboxWithText({ label, title, checked, setChecked }: CheckboxWithTextProps) { + const onChange = useCallback(() => { + setChecked((checkedPrev) => !checkedPrev) + }, [setChecked]) + + return ( + + + + ) +} + +export interface CheckboxWithIconProps extends Omit { + label: string + icon: ReactNode +} + +export function CheckboxWithIcon({ title, label, icon, checked, setChecked }: CheckboxWithIconProps) { + return ( + + {icon} + {label} + + ) +} + +export enum CheckboxState { + Checked, + Unchecked, + Indeterminate, +} + +export interface CheckboxIndeterminateProps extends StrictOmit { + state?: CheckboxState + onChange?(state: CheckboxState): void +} + +/** Checkbox with 3 states: checked, unchecked, indeterminate */ +export function CheckboxIndeterminate({ state, onChange, ...restProps }: CheckboxIndeterminateProps) { + const inputRef = useRef(null) + + const handleOnChange = useCallback(() => { + if (state === CheckboxState.Checked) { + return onChange?.(CheckboxState.Unchecked) + } + return onChange?.(CheckboxState.Checked) + }, [onChange, state]) + + useEffect(() => { + if (inputRef?.current) { + inputRef.current.indeterminate = state === CheckboxState.Indeterminate + } + }, [state]) + + const checked = useMemo(() => state === CheckboxState.Checked, [state]) + + return +} + +export interface CheckboxIndeterminateWithTextProps extends Omit { + label: string +} + +export function CheckboxIndeterminateWithText({ label, title, ...restProps }: CheckboxIndeterminateWithTextProps) { + return ( + + + + ) +} + +const FormGroup = styled(FormGroupBase)` + overflow-x: hidden; +` + +const Label = styled(LabelBase)` + white-space: nowrap; + overflow-x: hidden; + text-overflow: ellipsis; + display: block; +` + +const CheckboxText = styled.span` + margin-left: 0.3rem; +` diff --git a/web/src/components/Common/ColoredBox.tsx b/web/src/components/Common/ColoredBox.tsx new file mode 100644 index 00000000..29e77630 --- /dev/null +++ b/web/src/components/Common/ColoredBox.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components' + +export const ColoredBox = styled.div<{ $color: string; $size: number; $aspect?: number }>` + display: inline-block; + padding: 0; + margin: auto; + background-color: ${(props) => props.$color}; + width: ${(props) => props.$size * (props.$aspect ?? 1)}px; + height: ${(props) => props.$size}px; + border-radius: 2px; +` diff --git a/web/src/components/Common/ColoredCircle.tsx b/web/src/components/Common/ColoredCircle.tsx new file mode 100644 index 00000000..d0b07644 --- /dev/null +++ b/web/src/components/Common/ColoredCircle.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components' + +export const ColoredCircle = styled.div<{ $color: string; $size: number }>` + display: inline-block; + margin: auto; + margin-right: ${(props) => props.$size / 2}px; + background-color: ${(props) => props.$color}; + width: ${(props) => props.$size}px; + height: ${(props) => props.$size}px; + border-radius: ${(props) => props.$size / 2}px; +` diff --git a/web/src/components/Common/CountryFlag.tsx b/web/src/components/Common/CountryFlag.tsx new file mode 100644 index 00000000..dc801d73 --- /dev/null +++ b/web/src/components/Common/CountryFlag.tsx @@ -0,0 +1,51 @@ +import React, { ReactElement } from 'react' +import styled from 'styled-components' +import iso3311a2 from 'iso-3166-1-alpha-2' +import Flags from 'country-flag-icons/react/3x2' + +export const FlagWrapper = styled.div` + height: calc(1em + 2px); + width: calc(1.33em + 2px); + border: 0.5px solid #aaaa; + display: flex; + + > * { + width: 100%; + height: 100%; + } +` + +export const missingCountryCodes: Record = { + 'Bolivia': 'BO', + 'Bonaire': 'BQ', + 'Brunei': 'BN', + 'Cabo Verde': 'CV', + 'Curacao': 'CW', + 'Democratic Republic of the Congo': 'CD', + 'Eswatini': 'SZ', + 'Iran': 'IR', + 'Kosovo': 'XK', + 'Laos': 'LA', + 'Moldova': 'MD', + 'North Macedonia': 'MK', + 'Republic of the Congo': 'CD', + 'Russia': 'RU', + 'Saint Martin': 'SX', + 'Sint Maarten': 'SX', + 'South Korea': 'KR', + 'Taiwan': 'TW', + 'USA': 'US', + 'Venezuela': 'VE', + 'Vietnam': 'VN', +} + +export function getFlag(country: string): ReactElement | null { + const countryCode = missingCountryCodes[country] ?? iso3311a2.getCode(country) ?? '?' + + const Flag = Flags[countryCode] + if (Flag) { + return + } + + return null +} diff --git a/web/src/components/Common/Dropdown.tsx b/web/src/components/Common/Dropdown.tsx new file mode 100644 index 00000000..6a52a0cf --- /dev/null +++ b/web/src/components/Common/Dropdown.tsx @@ -0,0 +1,99 @@ +import React, { ReactNode, useCallback, useMemo, useState } from 'react' +import styled from 'styled-components' +import { rgba, darken } from 'polished' +import { + Dropdown as DropdownBase, + DropdownToggle as DropdownToggleBase, + DropdownMenu as DropdownMenuBase, + DropdownItem as DropdownItemBase, + DropdownProps as DropdownBaseProps, +} from 'reactstrap' +import { MdArrowDropDown } from 'react-icons/md' + +export interface DropdownEntry { + key: string + value: ReactNode +} + +export interface DropdownProps extends DropdownBaseProps { + entries: DropdownEntry[] + currentEntry: DropdownEntry + setCurrentEntry(key: DropdownEntry): void +} + +export function Dropdown({ currentEntry, setCurrentEntry, entries, ...restProps }: DropdownProps) { + const [dropdownOpen, setDropdownOpen] = useState(false) + const toggle = useCallback(() => setDropdownOpen((prevState) => !prevState), []) + const onClick = useCallback((entry: DropdownEntry) => () => setCurrentEntry(entry), [setCurrentEntry]) + const menuItems = useMemo(() => { + return entries.map((entry) => { + return ( + + {entry.value} + + ) + }) + }, [currentEntry, entries, onClick]) + + return ( + + + {currentEntry.value} + + + {menuItems} + + ) +} + +const DropdownStyled = styled(DropdownBase)` + display: flex; + border: ${(props) => rgba(props.theme.primary, 0.5)} solid 1px; + border-radius: 3px; + box-shadow: ${(props) => props.theme.shadows.lighter}; + + color: ${(props) => props.theme.bodyColor}; + + :hover { + color: ${(props) => props.theme.bodyColor}; + } + + & > a { + // min-width: 200px; + } +` + +const DropdownToggle = styled(DropdownToggleBase)` + display: flex; + color: ${(props) => props.theme.bodyColor}; +` + +const DropdownToggleText = styled.span` + flex: 1; + color: ${(props) => props.theme.bodyColor}; + + :hover { + color: ${(props) => props.theme.bodyColor}; + } +` + +const DropdownCaret = styled(MdArrowDropDown)` + margin: 0 auto; + width: 22px; + height: 22px; +` + +const DropdownMenu = styled(DropdownMenuBase)` + color: ${(props) => props.theme.bodyColor}; + background-color: ${(props) => props.theme.bodyBg}; + box-shadow: ${(props) => props.theme.shadows.blurredMedium}; + max-height: 70vh; + overflow-y: scroll; +` + +const DropdownItem = styled(DropdownItemBase)<{ active: boolean }>` + :hover { + color: ${({ active, theme }) => (active ? theme.white : theme.bodyColor)}; + background: ${({ active, theme }) => (active ? darken(0.025)(theme.primary) : theme.gray200)}; + } +` diff --git a/web/src/components/Common/DropdownWithSearch.tsx b/web/src/components/Common/DropdownWithSearch.tsx new file mode 100644 index 00000000..be6e6d1b --- /dev/null +++ b/web/src/components/Common/DropdownWithSearch.tsx @@ -0,0 +1,55 @@ +import React, { useCallback } from 'react' +import Select from 'react-select' +import type { ActionMeta, OnChangeValue } from 'react-select/dist/declarations/src/types' +import type { StateManagerProps } from 'react-select/dist/declarations/src/useStateManager' + +export type IsMultiValue = false + +export interface DropdownWithSearchProps extends StateManagerProps, IsMultiValue> { + defaultOption?: DropdownOption + onValueChange?(value: string): void + onOptionChange?(option: DropdownOption): void +} + +export function DropdownWithSearch({ + options, + defaultOption, + value, + onOptionChange, + onValueChange, + ...restProps +}: DropdownWithSearchProps) { + const handleChange = useCallback( + (option: OnChangeValue, IsMultiValue>, _actionMeta: ActionMeta>) => { + if (option) { + onValueChange?.(option.value) + onOptionChange?.(option) + } + }, + [onOptionChange, onValueChange], + ) + + return ( +