Просмотр исходного кода

Merge branch 'support/apply-nextjs-2' into imprv/100017-next-NotFoundPage

yuken 3 лет назад
Родитель
Сommit
6a06d3d3d7
35 измененных файлов с 779 добавлено и 338 удалено
  1. 2 2
      packages/app/next.config.js
  2. 4 2
      packages/app/package.json
  3. 0 0
      packages/app/public/static/locales/en_US/admin.json
  4. 0 0
      packages/app/public/static/locales/ja_JP/admin.json
  5. 0 0
      packages/app/public/static/locales/zh_CN/admin.json
  6. 1 0
      packages/app/src/client/services/PageContainer.js
  7. 1 1
      packages/app/src/components/Admin/SlackIntegration/BotTypeCard.jsx
  8. 3 2
      packages/app/src/components/Admin/SlackIntegration/ConfirmBotChangeModal.jsx
  9. 2 0
      packages/app/src/components/Layout/BasicLayout.tsx
  10. 0 199
      packages/app/src/components/PageAttachment.jsx
  11. 151 0
      packages/app/src/components/PageAttachment.tsx
  12. 3 1
      packages/app/src/components/PagePresentationModal.jsx
  13. 4 2
      packages/app/src/components/PagePresentationModal.module.scss
  14. 10 1
      packages/app/src/interfaces/attachment.ts
  15. 3 3
      packages/app/src/interfaces/crowi-request.ts
  16. 0 4
      packages/app/src/interfaces/user-ui-settings.ts
  17. 74 36
      packages/app/src/pages/[[...path]].page.tsx
  18. 1 1
      packages/app/src/pages/_app.page.tsx
  19. 0 0
      packages/app/src/pages/_search.page.tsx
  20. 37 20
      packages/app/src/pages/admin/[[...path]].page.tsx
  21. 1 1
      packages/app/src/pages/installer.page.tsx
  22. 1 1
      packages/app/src/pages/utils/commons.ts
  23. 13 0
      packages/app/src/pages/utils/objectid-transformer.ts
  24. 7 5
      packages/app/src/server/middlewares/inject-user-ui-settings-to-localvars.ts
  25. 4 2
      packages/app/src/server/models/user-ui-settings.ts
  26. 3 3
      packages/app/src/server/routes/apiv3/attachment.js
  27. 4 6
      packages/app/src/server/routes/index.js
  28. 0 2
      packages/app/src/services/renderer/renderer.tsx
  29. 52 0
      packages/app/src/stores/attachment.tsx
  30. 4 0
      packages/app/src/stores/context.tsx
  31. 2 2
      packages/app/src/stores/page.tsx
  32. 27 1
      packages/app/src/utils/axios.ts
  33. 1 0
      packages/core/src/index.ts
  34. 3 1
      packages/slackbot-proxy/src/controllers/slack.ts
  35. 361 40
      yarn.lock

+ 2 - 2
packages/app/next.config.js

@@ -1,5 +1,6 @@
 import eazyLogger from 'eazy-logger';
 import eazyLogger from 'eazy-logger';
 import { I18NextHMRPlugin } from 'i18next-hmr/plugin';
 import { I18NextHMRPlugin } from 'i18next-hmr/plugin';
+import { withSuperjson } from 'next-superjson';
 import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
 import { WebpackManifestPlugin } from 'webpack-manifest-plugin';
 
 
 import { i18n, localePath } from './src/next-i18next.config';
 import { i18n, localePath } from './src/next-i18next.config';
@@ -63,7 +64,6 @@ const nextConfig = {
 
 
   /** @param config {import('next').NextConfig} */
   /** @param config {import('next').NextConfig} */
   webpack(config, options) {
   webpack(config, options) {
-
     // Avoid "Module not found: Can't resolve 'fs'"
     // Avoid "Module not found: Can't resolve 'fs'"
     // See: https://stackoverflow.com/a/68511591
     // See: https://stackoverflow.com/a/68511591
     if (!options.isServer) {
     if (!options.isServer) {
@@ -98,4 +98,4 @@ const nextConfig = {
 
 
 };
 };
 
 
-module.exports = withTM(nextConfig);
+module.exports = withSuperjson()(withTM(nextConfig));

+ 4 - 2
packages/app/package.json

@@ -127,7 +127,6 @@
     "next": "^12.1.6",
     "next": "^12.1.6",
     "next-i18next": "^11.0.0",
     "next-i18next": "^11.0.0",
     "next-themes": "^0.2.0",
     "next-themes": "^0.2.0",
-    "next-transpile-modules": "^9.0.0",
     "nocache": "^3.0.1",
     "nocache": "^3.0.1",
     "nodemailer": "^6.6.2",
     "nodemailer": "^6.6.2",
     "nodemailer-ses-transport": "~1.5.0",
     "nodemailer-ses-transport": "~1.5.0",
@@ -160,12 +159,12 @@
     "rehype-toc": "^3.0.2",
     "rehype-toc": "^3.0.2",
     "remark-breaks": "^3.0.2",
     "remark-breaks": "^3.0.2",
     "remark-emoji": "^3.0.2",
     "remark-emoji": "^3.0.2",
-    "remark-footnotes": "^4.0.1",
     "remark-gfm": "^3.0.1",
     "remark-gfm": "^3.0.1",
     "rimraf": "^3.0.0",
     "rimraf": "^3.0.0",
     "socket.io": "^4.2.0",
     "socket.io": "^4.2.0",
     "stream-to-promise": "^3.0.0",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
     "string-width": "=4.2.2",
+    "superjson": "^1.9.1",
     "swagger-jsdoc": "^6.1.0",
     "swagger-jsdoc": "^6.1.0",
     "swig-templates": "^2.0.2",
     "swig-templates": "^2.0.2",
     "uglifycss": "^0.0.29",
     "uglifycss": "^0.0.29",
@@ -189,6 +188,7 @@
     "@types/jquery": "^3.5.8",
     "@types/jquery": "^3.5.8",
     "@types/multer": "^1.4.5",
     "@types/multer": "^1.4.5",
     "autoprefixer": "^9.0.0",
     "autoprefixer": "^9.0.0",
+    "babel-loader": "^8.2.5",
     "bootstrap": "^4.6.1",
     "bootstrap": "^4.6.1",
     "browser-sync": "^2.27.7",
     "browser-sync": "^2.27.7",
     "bunyan-debug": "^2.0.0",
     "bunyan-debug": "^2.0.0",
@@ -214,6 +214,8 @@
     "markdown-table": "^1.1.1",
     "markdown-table": "^1.1.1",
     "material-icons": "^1.11.3",
     "material-icons": "^1.11.3",
     "morgan": "^1.10.0",
     "morgan": "^1.10.0",
+    "next-superjson": "^0.0.4",
+    "next-transpile-modules": "^9.0.0",
     "normalize-path": "^3.0.0",
     "normalize-path": "^3.0.0",
     "penpal": "^4.0.0",
     "penpal": "^4.0.0",
     "plantuml-encoder": "^1.2.5",
     "plantuml-encoder": "^1.2.5",

+ 0 - 0
packages/app/public/static/locales/en_US/admin/admin.json → packages/app/public/static/locales/en_US/admin.json


+ 0 - 0
packages/app/public/static/locales/ja_JP/admin/admin.json → packages/app/public/static/locales/ja_JP/admin.json


+ 0 - 0
packages/app/public/static/locales/zh_CN/admin/admin.json → packages/app/public/static/locales/zh_CN/admin.json


+ 1 - 0
packages/app/src/client/services/PageContainer.js

@@ -135,6 +135,7 @@ export default class PageContainer extends Container {
 
 
   /**
   /**
    * initialize state for markdown data
    * initialize state for markdown data
+   * [Already SWRized]
    */
    */
   initStateMarkdown() {
   initStateMarkdown() {
     let pageContent = '';
     let pageContent = '';

+ 1 - 1
packages/app/src/components/Admin/SlackIntegration/BotTypeCard.jsx

@@ -31,7 +31,7 @@ const botDetails = {
 };
 };
 
 
 const BotTypeCard = (props) => {
 const BotTypeCard = (props) => {
-  const { t } = useTranslation('admin');
+  const { t } = useTranslation();
 
 
   const isBotTypeOfficial = props.botType === SlackbotType.OFFICIAL;
   const isBotTypeOfficial = props.botType === SlackbotType.OFFICIAL;
 
 

+ 3 - 2
packages/app/src/components/Admin/SlackIntegration/ConfirmBotChangeModal.jsx

@@ -1,12 +1,13 @@
 import React from 'react';
 import React from 'react';
-import PropTypes from 'prop-types';
+
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import PropTypes from 'prop-types';
 import {
 import {
   Modal, ModalHeader, ModalBody, ModalFooter,
   Modal, ModalHeader, ModalBody, ModalFooter,
 } from 'reactstrap';
 } from 'reactstrap';
 
 
 const ConfirmBotChangeModal = (props) => {
 const ConfirmBotChangeModal = (props) => {
-  const { t } = useTranslation('admin');
+  const { t } = useTranslation();
 
 
   const handleCancelButton = () => {
   const handleCancelButton = () => {
     if (props.onCancelClick != null) {
     if (props.onCancelClick != null) {

+ 2 - 0
packages/app/src/components/Layout/BasicLayout.tsx

@@ -26,6 +26,7 @@ export const BasicLayout = ({ children, title, className }: Props): JSX.Element
   const PageDuplicateModal = dynamic(() => import('../PageDuplicateModal'), { ssr: false });
   const PageDuplicateModal = dynamic(() => import('../PageDuplicateModal'), { ssr: false });
   const PageDeleteModal = dynamic(() => import('../PageDeleteModal'), { ssr: false });
   const PageDeleteModal = dynamic(() => import('../PageDeleteModal'), { ssr: false });
   const PageRenameModal = dynamic(() => import('../PageRenameModal'), { ssr: false });
   const PageRenameModal = dynamic(() => import('../PageRenameModal'), { ssr: false });
+  const PagePresentationModal = dynamic(() => import('../PagePresentationModal'), { ssr: false });
 
 
   return (
   return (
     <RawLayout title={title} className={className}>
     <RawLayout title={title} className={className}>
@@ -47,6 +48,7 @@ export const BasicLayout = ({ children, title, className }: Props): JSX.Element
       <PageDuplicateModal />
       <PageDuplicateModal />
       <PageDeleteModal />
       <PageDeleteModal />
       <PageRenameModal />
       <PageRenameModal />
+      <PagePresentationModal />
       {/* <HotkeysManager /> */}
       {/* <HotkeysManager /> */}
 
 
       <ShortcutsModal />
       <ShortcutsModal />

+ 0 - 199
packages/app/src/components/PageAttachment.jsx

@@ -1,199 +0,0 @@
-/* eslint-disable react/no-access-state-in-setstate */
-import React from 'react';
-
-import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
-
-import PageContainer from '~/client/services/PageContainer';
-import { apiPost } from '~/client/util/apiv1-client';
-import { apiv3Get } from '~/client/util/apiv3-client';
-import { useIsGuestUser } from '~/stores/context';
-
-import DeleteAttachmentModal from './PageAttachment/DeleteAttachmentModal';
-import PageAttachmentList from './PageAttachment/PageAttachmentList';
-import PaginationWrapper from './PaginationWrapper';
-import { withUnstatedContainers } from './UnstatedUtils';
-
-
-class PageAttachment extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      activePage: 1,
-      totalAttachments: 0,
-      limit: Infinity,
-      attachments: [],
-      inUse: {},
-      attachmentToDelete: null,
-      deleting: false,
-      deleteError: '',
-    };
-
-    this.handlePage = this.handlePage.bind(this);
-    this.onAttachmentDeleteClicked = this.onAttachmentDeleteClicked.bind(this);
-    this.onAttachmentDeleteClickedConfirm = this.onAttachmentDeleteClickedConfirm.bind(this);
-  }
-
-
-  async handlePage(selectedPage) {
-    const { pageId } = this.props.pageContainer.state;
-    const page = selectedPage;
-
-    if (!pageId) { return }
-
-    const res = await apiv3Get('/attachment/list', { pageId, page });
-    const attachments = res.data.paginateResult.docs;
-    const totalAttachments = res.data.paginateResult.totalDocs;
-    const pagingLimit = res.data.paginateResult.limit;
-
-    const inUse = {};
-
-    for (const attachment of attachments) {
-      inUse[attachment._id] = this.checkIfFileInUse(attachment);
-    }
-    this.setState({
-      activePage: selectedPage,
-      totalAttachments,
-      limit: pagingLimit,
-      attachments,
-      inUse,
-    });
-  }
-
-
-  async componentDidMount() {
-    await this.handlePage(1);
-    this.setState({
-      activePage: 1,
-    });
-  }
-
-  checkIfFileInUse(attachment) {
-    const { markdown } = this.props.pageContainer.state;
-
-    if (markdown.match(attachment._id)) {
-      return true;
-    }
-    return false;
-  }
-
-  onAttachmentDeleteClicked(attachment) {
-    this.setState({
-      attachmentToDelete: attachment,
-    });
-  }
-
-  onAttachmentDeleteClickedConfirm(attachment) {
-    const attachmentId = attachment._id;
-    this.setState({
-      deleting: true,
-    });
-
-    apiPost('/attachments.remove', { attachment_id: attachmentId })
-      .then((res) => {
-        this.setState({
-          attachments: this.state.attachments.filter((at) => {
-            // comparing ObjectId
-            // eslint-disable-next-line eqeqeq
-            return at._id != attachmentId;
-          }),
-          attachmentToDelete: null,
-          deleting: false,
-        });
-      }).catch((err) => {
-        this.setState({
-          deleteError: 'Something went wrong.',
-          deleting: false,
-        });
-      });
-  }
-
-
-  render() {
-    const { t, isGuestUser } = this.props;
-
-    if (this.state.attachments.length === 0) {
-      return (
-        <div data-testid="page-attachment">
-          {t('No_attachments_yet')}
-        </div>
-      );
-    }
-
-    let deleteAttachmentModal = '';
-    if (!isGuestUser) {
-      const attachmentToDelete = this.state.attachmentToDelete;
-      const deleteModalClose = () => {
-        this.setState({ attachmentToDelete: null, deleteError: '' });
-      };
-      const showModal = attachmentToDelete !== null;
-
-      let deleteInUse = null;
-      if (attachmentToDelete !== null) {
-        deleteInUse = this.state.inUse[attachmentToDelete._id] || false;
-      }
-
-      deleteAttachmentModal = (
-        <DeleteAttachmentModal
-          isOpen={showModal}
-          animation="false"
-          toggle={deleteModalClose}
-          attachmentToDelete={attachmentToDelete}
-          inUse={deleteInUse}
-          deleting={this.state.deleting}
-          deleteError={this.state.deleteError}
-          onAttachmentDeleteClickedConfirm={this.onAttachmentDeleteClickedConfirm}
-        />
-      );
-    }
-
-    return (
-      <div data-testid="page-attachment">
-        <PageAttachmentList
-          attachments={this.state.attachments}
-          inUse={this.state.inUse}
-          onAttachmentDeleteClicked={this.onAttachmentDeleteClicked}
-          isUserLoggedIn={!isGuestUser}
-        />
-
-        {deleteAttachmentModal}
-
-        <PaginationWrapper
-          activePage={this.state.activePage}
-          changePage={this.handlePage}
-          totalItemsCount={this.state.totalAttachments}
-          pagingLimit={this.state.limit}
-          align="center"
-        />
-      </div>
-    );
-  }
-
-}
-
-PageAttachment.propTypes = {
-  t: PropTypes.func.isRequired,
-  pageContainer: PropTypes.instanceOf(PageContainer).isRequired,
-
-  isGuestUser: PropTypes.bool.isRequired,
-};
-
-/**
- * Wrapper component for using unstated
- */
-const PageAttachmentUnstatedWrapper = withUnstatedContainers(PageAttachment, [PageContainer]);
-
-const PageAttachmentWrapper = (props) => {
-  const { t } = useTranslation();
-  const { data: isGuestUser } = useIsGuestUser();
-
-  if (isGuestUser == null) {
-    return <></>;
-  }
-
-  return <PageAttachmentUnstatedWrapper {...props} t={t} isGuestUser={isGuestUser} />;
-};
-
-export default PageAttachmentWrapper;

+ 151 - 0
packages/app/src/components/PageAttachment.tsx

@@ -0,0 +1,151 @@
+import React, { useCallback, useEffect, useState } from 'react';
+
+import { useTranslation } from 'next-i18next';
+
+import { useSWRxAttachments } from '~/stores/attachment';
+import { useEditingMarkdown, useCurrentPageId, useIsGuestUser } from '~/stores/context';
+
+import DeleteAttachmentModal from './PageAttachment/DeleteAttachmentModal';
+import PageAttachmentList from './PageAttachment/PageAttachmentList';
+import PaginationWrapper from './PaginationWrapper';
+
+// Utility
+const checkIfFileInUse = (markdown: string, attachment) => {
+  return markdown.match(attachment._id);
+};
+
+// Custom hook that handles processes related to inUseAttachments
+const useInUseAttachments = (attachments) => {
+  const { data: markdown } = useEditingMarkdown();
+  const [inUse, setInUse] = useState<any>({});
+
+  // Update inUse when either of attachments or markdown is updated
+  useEffect(() => {
+    if (markdown == null) {
+      return;
+    }
+
+    const newInUse = {};
+
+    for (const attachment of attachments) {
+      newInUse[attachment._id] = checkIfFileInUse(markdown, attachment);
+    }
+
+    setInUse(newInUse);
+  }, [attachments, markdown]);
+
+  return inUse;
+};
+
+const PageAttachment = (): JSX.Element => {
+  const { t } = useTranslation();
+
+  // Static SWRs
+  const { data: pageId } = useCurrentPageId();
+  const { data: isGuestUser } = useIsGuestUser();
+
+  // States
+  const [pageNumber, setPageNumber] = useState(1);
+  const [attachmentToDelete, setAttachmentToDelete] = useState<any>(undefined);
+  const [deleting, setDeleting] = useState(false);
+  const [deleteError, setDeleteError] = useState('');
+
+  // SWRs
+  const { data: dataAttachments, remove } = useSWRxAttachments(pageId, pageNumber);
+  const {
+    attachments = [],
+    totalAttachments = 0,
+    limit,
+  } = dataAttachments ?? {};
+
+  // Custom hooks
+  const inUseAttachments = useInUseAttachments(attachments);
+
+  // Methods
+  const onChangePageHandler = useCallback((newPageNumber: number) => {
+    setPageNumber(newPageNumber);
+  }, []);
+
+  const onAttachmentDeleteClicked = useCallback((attachment) => {
+    setAttachmentToDelete(attachment);
+  }, []);
+
+  const onAttachmentDeleteClickedConfirmHandler = useCallback(async(attachment) => {
+    setDeleting(true);
+
+    try {
+      await remove({ attachment_id: attachment._id });
+
+      setAttachmentToDelete(null);
+      setDeleting(false);
+    }
+    catch {
+      setDeleteError('Something went wrong.');
+      setDeleting(false);
+    }
+  }, [remove]);
+
+  const onToggleHandler = useCallback(() => {
+    setAttachmentToDelete(null);
+    setDeleteError('');
+  }, []);
+
+  // Renderers
+  const renderDeleteAttachmentModal = useCallback(() => {
+    if (isGuestUser) {
+      return <></>;
+    }
+
+    if (attachments.length === 0) {
+      return (
+        <div data-testid="page-attachment">
+          {t('No_attachments_yet')}
+        </div>
+      );
+    }
+
+    let deleteInUse = null;
+    if (attachmentToDelete != null) {
+      deleteInUse = inUseAttachments[attachmentToDelete._id] || false;
+    }
+
+    const isOpen = attachmentToDelete != null;
+
+    return (
+      <DeleteAttachmentModal
+        isOpen={isOpen}
+        animation="false"
+        toggle={onToggleHandler}
+        attachmentToDelete={attachmentToDelete}
+        inUse={deleteInUse}
+        deleting={deleting}
+        deleteError={deleteError}
+        onAttachmentDeleteClickedConfirm={onAttachmentDeleteClickedConfirmHandler}
+      />
+    );
+  // eslint-disable-next-line max-len
+  }, [attachmentToDelete, attachments.length, deleteError, deleting, inUseAttachments, isGuestUser, onAttachmentDeleteClickedConfirmHandler, onToggleHandler, t]);
+
+  return (
+    <div data-testid="page-attachment">
+      <PageAttachmentList
+        attachments={attachments}
+        inUse={inUseAttachments}
+        onAttachmentDeleteClicked={onAttachmentDeleteClicked}
+        isUserLoggedIn={!isGuestUser}
+      />
+
+      {renderDeleteAttachmentModal()}
+
+      <PaginationWrapper
+        activePage={pageNumber}
+        changePage={onChangePageHandler}
+        totalItemsCount={totalAttachments}
+        pagingLimit={limit}
+        align="center"
+      />
+    </div>
+  );
+};
+
+export default PageAttachment;

+ 3 - 1
packages/app/src/components/PagePresentationModal.jsx

@@ -6,6 +6,8 @@ import {
 
 
 import { usePagePresentationModal } from '~/stores/modal';
 import { usePagePresentationModal } from '~/stores/modal';
 
 
+import styles from './PagePresentationModal.module.scss';
+
 const PagePresentationModal = () => {
 const PagePresentationModal = () => {
 
 
   const { data: presentationData, close: closePresentationModal } = usePagePresentationModal();
   const { data: presentationData, close: closePresentationModal } = usePagePresentationModal();
@@ -15,7 +17,7 @@ const PagePresentationModal = () => {
       isOpen={presentationData.isOpened}
       isOpen={presentationData.isOpened}
       toggle={closePresentationModal}
       toggle={closePresentationModal}
       data-testid="page-presentation-modal"
       data-testid="page-presentation-modal"
-      className="grw-presentation-modal"
+      className={`grw-presentation-modal ${styles['grw-presentation-modal']}`}
       unmountOnClose={false}
       unmountOnClose={false}
     >
     >
       <ModalBody className="modal-body">
       <ModalBody className="modal-body">

+ 4 - 2
packages/app/src/styles/_page-presentation.scss → packages/app/src/components/PagePresentationModal.module.scss

@@ -1,5 +1,7 @@
-.grw-presentation-modal {
-  @include expand-modal-fullscreen(false, false);
+@use '~/styles/mixins' as mi;
+
+.grw-presentation-modal :global {
+  @include mi.expand-modal-fullscreen(false, false);
 
 
   .modal-body {
   .modal-body {
     background: black;
     background: black;

+ 10 - 1
packages/app/src/interfaces/attachment.ts

@@ -1 +1,10 @@
-export type { IAttachment } from '@growi/core';
+import type { IAttachment } from '@growi/core';
+
+import type { PaginateResult } from './mongoose-utils';
+
+
+export type IResAttachmentList = {
+  data: {
+    paginateResult: PaginateResult<IAttachment>
+  }
+};

+ 3 - 3
packages/app/src/interfaces/crowi-request.ts

@@ -1,10 +1,10 @@
 import { Request } from 'express';
 import { Request } from 'express';
 
 
-import { IUserHasId } from './user';
+import { IUser, IUserHasId } from './user';
 
 
-export interface CrowiRequest extends Request {
+export interface CrowiRequest<U extends IUser = IUserHasId> extends Request {
 
 
-  user?: IUserHasId,
+  user?: U,
 
 
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   // eslint-disable-next-line @typescript-eslint/no-explicit-any
   crowi: any,
   crowi: any,

+ 0 - 4
packages/app/src/interfaces/user-ui-settings.ts

@@ -1,10 +1,6 @@
-import { Ref } from '@growi/core';
-
 import { SidebarContentsType } from './ui';
 import { SidebarContentsType } from './ui';
-import { IUser } from './user';
 
 
 export interface IUserUISettings {
 export interface IUserUISettings {
-  user: Ref<IUser> | null;
   isSidebarCollapsed: boolean,
   isSidebarCollapsed: boolean,
   currentSidebarContents: SidebarContentsType,
   currentSidebarContents: SidebarContentsType,
   currentProductNavWidth: number,
   currentProductNavWidth: number,

+ 74 - 36
packages/app/src/pages/[[...path]].page.tsx

@@ -1,9 +1,10 @@
 import React, { useEffect } from 'react';
 import React, { useEffect } from 'react';
 
 
+
 import EventEmitter from 'events';
 import EventEmitter from 'events';
 
 
 import {
 import {
-  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, isClient, pagePathUtils, pathUtils,
+  IDataWithMeta, IPageInfoForEntity, IPagePopulatedToShowRevision, isClient, isIPageInfoForEntity, isServer, IUser, IUserHasId, pagePathUtils, pathUtils,
 } from '@growi/core';
 } from '@growi/core';
 import ExtensibleCustomError from 'extensible-custom-error';
 import ExtensibleCustomError from 'extensible-custom-error';
 import {
 import {
@@ -13,6 +14,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import Head from 'next/head';
 import Head from 'next/head';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
+import superjson from 'superjson';
 
 
 import { PageAlerts } from '~/components/PageAlert/PageAlerts';
 import { PageAlerts } from '~/components/PageAlert/PageAlerts';
 // import { PageComments } from '~/components/PageComment/PageComments';
 // import { PageComments } from '~/components/PageComment/PageComments';
@@ -25,10 +27,11 @@ import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CustomWindow } from '~/interfaces/global';
 import { CustomWindow } from '~/interfaces/global';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { RendererConfig } from '~/interfaces/services/renderer';
 import { ISidebarConfig } from '~/interfaces/sidebar-config';
 import { ISidebarConfig } from '~/interfaces/sidebar-config';
+import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { PageModel, PageDocument } from '~/server/models/page';
 import { PageModel, PageDocument } from '~/server/models/page';
-import UserUISettings, { UserUISettingsDocument } from '~/server/models/user-ui-settings';
+import UserUISettings from '~/server/models/user-ui-settings';
 import Xss from '~/services/xss';
 import Xss from '~/services/xss';
-import { useSWRxCurrentPage, useSWRxPageInfo, useSWRxPage } from '~/stores/page';
+import { useSWRxCurrentPage, useSWRxPageInfo } from '~/stores/page';
 import {
 import {
   usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
   usePreferDrawerModeByUser, usePreferDrawerModeOnEditByUser, useSidebarCollapsed, useCurrentSidebarContents, useCurrentProductNavWidth,
 } from '~/stores/ui';
 } from '~/stores/ui';
@@ -55,23 +58,56 @@ import {
   useHackmdUri,
   useHackmdUri,
   useIsAclEnabled, useIsUserPage, useIsNotCreatable,
   useIsAclEnabled, useIsUserPage, useIsNotCreatable,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
   useCsrfToken, useIsSearchScopeChildrenAsDefault, useCurrentPageId, useCurrentPathname,
-  useIsSlackConfigured, useIsBlinkedHeaderAtBoot, useRendererConfig,
+  useIsSlackConfigured, useIsBlinkedHeaderAtBoot, useRendererConfig, useEditingMarkdown,
 } from '../stores/context';
 } from '../stores/context';
 import { useXss } from '../stores/xss';
 import { useXss } from '../stores/xss';
 
 
 import {
 import {
   CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
   CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
-} from './commons';
+} from './utils/commons';
+import { registerTransformerForObjectId } from './utils/objectid-transformer';
 // import { useCurrentPageSWR } from '../stores/page';
 // import { useCurrentPageSWR } from '../stores/page';
 
 
 
 
 const logger = loggerFactory('growi:pages:all');
 const logger = loggerFactory('growi:pages:all');
+
 const {
 const {
   isPermalink: _isPermalink, isUsersHomePage, isTrashPage: _isTrashPage, isUserPage, isCreatablePage,
   isPermalink: _isPermalink, isUsersHomePage, isTrashPage: _isTrashPage, isUserPage, isCreatablePage,
 } = pagePathUtils;
 } = pagePathUtils;
 const { removeHeadingSlash } = pathUtils;
 const { removeHeadingSlash } = pathUtils;
 
 
 
 
+type IPageToShowRevisionWithMeta = IDataWithMeta<IPagePopulatedToShowRevision & PageDocument, IPageInfoForEntity>;
+type IPageToShowRevisionWithMetaSerialized = IDataWithMeta<string, string>;
+
+// register custom serializer
+registerTransformerForObjectId();
+
+superjson.registerCustom<IPageToShowRevisionWithMeta, IPageToShowRevisionWithMetaSerialized>(
+  {
+    isApplicable: (v): v is IPageToShowRevisionWithMeta => {
+      return v?.data != null
+        && v?.data.toObject != null
+        && v?.meta != null
+        && isIPageInfoForEntity(v.meta);
+    },
+    serialize: (v) => {
+      return {
+        data: superjson.stringify(v.data.toObject()),
+        meta: superjson.stringify(v.meta),
+      };
+    },
+    deserialize: (v) => {
+      return {
+        data: superjson.parse(v.data),
+        meta: v.meta != null ? superjson.parse(v.meta) : undefined,
+      };
+    },
+  },
+  'IPageToShowRevisionWithMetaTransformer',
+);
+
+
 const IdenticalPathPage = (): JSX.Element => {
 const IdenticalPathPage = (): JSX.Element => {
   const IdenticalPathPage = dynamic(() => import('../components/IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
   const IdenticalPathPage = dynamic(() => import('../components/IdenticalPathPage').then(mod => mod.IdenticalPathPage), { ssr: false });
   return <IdenticalPathPage />;
   return <IdenticalPathPage />;
@@ -82,12 +118,10 @@ const PutbackPageModal = (): JSX.Element => {
   return <PutbackPageModal />;
   return <PutbackPageModal />;
 };
 };
 
 
-type IPageToShowRevisionWithMeta = IDataWithMeta<IPagePopulatedToShowRevision, IPageInfoForEntity>;
-
 type Props = CommonProps & {
 type Props = CommonProps & {
-  currentUser: string,
+  currentUser: IUser,
 
 
-  pageWithMetaStr: string,
+  pageWithMeta: IPageToShowRevisionWithMeta,
   // pageUser?: any,
   // pageUser?: any,
   // redirectTo?: string;
   // redirectTo?: string;
   // redirectFrom?: string;
   // redirectFrom?: string;
@@ -127,7 +161,7 @@ type Props = CommonProps & {
   rendererConfig: RendererConfig,
   rendererConfig: RendererConfig,
 
 
   // UI
   // UI
-  userUISettings: UserUISettingsDocument | null
+  userUISettings?: IUserUISettings
   // Sidebar
   // Sidebar
   sidebarConfig: ISidebarConfig,
   sidebarConfig: ISidebarConfig,
 };
 };
@@ -139,7 +173,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   const UnsavedAlertDialog = dynamic(() => import('./UnsavedAlertDialog'), { ssr: false });
   const UnsavedAlertDialog = dynamic(() => import('./UnsavedAlertDialog'), { ssr: false });
   const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher'), { ssr: false });
   const GrowiSubNavigationSwitcher = dynamic(() => import('../components/Navbar/GrowiSubNavigationSwitcher'), { ssr: false });
 
 
-  const { data: currentUser } = useCurrentUser(props.currentUser != null ? JSON.parse(props.currentUser) : null);
+  const { data: currentUser } = useCurrentUser(props.currentUser ?? null);
 
 
   // register global EventEmitter
   // register global EventEmitter
   if (isClient()) {
   if (isClient()) {
@@ -196,10 +230,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
 
 
   // const { data: editorMode } = useEditorMode();
   // const { data: editorMode } = useEditorMode();
 
 
-  let pageWithMeta: IPageToShowRevisionWithMeta | undefined;
-  if (props.pageWithMetaStr != null) {
-    pageWithMeta = JSON.parse(props.pageWithMetaStr) as IPageToShowRevisionWithMeta;
-  }
+  const { pageWithMeta, userUISettings } = props;
 
 
   let shouldRenderPutbackPageModal = false;
   let shouldRenderPutbackPageModal = false;
   if (pageWithMeta != null) {
   if (pageWithMeta != null) {
@@ -215,6 +246,7 @@ const GrowiPage: NextPage<Props> = (props: Props) => {
   useIsNotCreatable(props.isForbidden || !isCreatablePage(pageWithMeta?.data.path ?? '')); // TODO: need to include props.isIdentical
   useIsNotCreatable(props.isForbidden || !isCreatablePage(pageWithMeta?.data.path ?? '')); // TODO: need to include props.isIdentical
   useCurrentPagePath(pageWithMeta?.data.path);
   useCurrentPagePath(pageWithMeta?.data.path);
   useCurrentPathname(props.currentPathname);
   useCurrentPathname(props.currentPathname);
+  useEditingMarkdown(pageWithMeta?.data.revision.body);
 
 
   // sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
   // sync pathname by Shallow Routing https://nextjs.org/docs/routing/shallow-routing
   useEffect(() => {
   useEffect(() => {
@@ -319,7 +351,7 @@ class MultiplePagesHitsError extends ExtensibleCustomError {
 
 
 }
 }
 
 
-async function getPageData(context: GetServerSidePropsContext, props: Props): Promise<IPageToShowRevisionWithMeta|null> {
+async function injectPageData(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
   const { revisionId } = req.query;
   const { revisionId } = req.query;
@@ -342,8 +374,8 @@ async function getPageData(context: GetServerSidePropsContext, props: Props): Pr
     }
     }
   }
   }
 
 
-  const result: IPageToShowRevisionWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, currentPathname, user, true); // includeEmpty = true, isSharedPage = false
-  const page = result?.data as unknown as PageDocument;
+  const pageWithMeta: IPageToShowRevisionWithMeta = await pageService.findPageAndMetaDataByViewer(pageId, currentPathname, user, true); // includeEmpty = true, isSharedPage = false
+  const page = pageWithMeta?.data as unknown as PageDocument;
 
 
   // populate & check if the revision is latest
   // populate & check if the revision is latest
   if (page != null) {
   if (page != null) {
@@ -352,10 +384,20 @@ async function getPageData(context: GetServerSidePropsContext, props: Props): Pr
     props.isLatestRevision = page.isLatestRevision();
     props.isLatestRevision = page.isLatestRevision();
   }
   }
 
 
-  return result;
+  props.pageWithMeta = pageWithMeta;
+}
+
+async function injectUserUISettings(context: GetServerSidePropsContext, props: Props): Promise<void> {
+  const req = context.req as CrowiRequest<IUserHasId & any>;
+  const { user } = req;
+
+  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
+  if (userUISettings != null) {
+    props.userUISettings = userUISettings.toObject();
+  }
 }
 }
 
 
-async function injectRoutingInformation(context: GetServerSidePropsContext, props: Props, pageWithMeta: IPageToShowRevisionWithMeta|null): Promise<void> {
+async function injectRoutingInformation(context: GetServerSidePropsContext, props: Props): Promise<void> {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
   const Page = crowi.model('Page') as PageModel;
   const Page = crowi.model('Page') as PageModel;
@@ -364,7 +406,7 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
   const pageId = getPageIdFromPathname(currentPathname);
   const pageId = getPageIdFromPathname(currentPathname);
   const isPermalink = _isPermalink(currentPathname);
   const isPermalink = _isPermalink(currentPathname);
 
 
-  const page = pageWithMeta?.data;
+  const page = props.pageWithMeta?.data;
 
 
   if (props.isIdenticalPathPage) {
   if (props.isIdenticalPathPage) {
     // TBD
     // TBD
@@ -408,7 +450,7 @@ async function injectRoutingInformation(context: GetServerSidePropsContext, prop
 //   }
 //   }
 // }
 // }
 
 
-async function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): Promise<void> {
+function injectServerConfigurations(context: GetServerSidePropsContext, props: Props): void {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
   const {
   const {
@@ -477,7 +519,7 @@ async function injectNextI18NextConfigurations(context: GetServerSidePropsContex
 }
 }
 
 
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
-  const req: CrowiRequest = context.req as CrowiRequest;
+  const req = context.req as CrowiRequest<IUserHasId & any>;
   const { user } = req;
   const { user } = req;
 
 
   const result = await getServerSideCommonProps(context);
   const result = await getServerSideCommonProps(context);
@@ -490,10 +532,13 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   }
   }
 
 
   const props: Props = result.props as Props;
   const props: Props = result.props as Props;
-  let pageWithMeta;
+
+  if (user != null) {
+    props.currentUser = user.toObject();
+  }
+
   try {
   try {
-    pageWithMeta = await getPageData(context, props);
-    props.pageWithMetaStr = JSON.stringify(pageWithMeta);
+    await injectPageData(context, props);
   }
   }
   catch (err) {
   catch (err) {
     if (err instanceof MultiplePagesHitsError) {
     if (err instanceof MultiplePagesHitsError) {
@@ -504,17 +549,10 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     }
     }
   }
   }
 
 
-  injectRoutingInformation(context, props, pageWithMeta);
+  await injectUserUISettings(context, props);
+  await injectRoutingInformation(context, props);
   injectServerConfigurations(context, props);
   injectServerConfigurations(context, props);
-  injectNextI18NextConfigurations(context, props, ['translation']);
-
-  if (user != null) {
-    props.currentUser = JSON.stringify(user);
-  }
-
-  // UI
-  const userUISettings = user == null ? null : await UserUISettings.findOne({ user: user._id }).exec();
-  props.userUISettings = JSON.parse(JSON.stringify(userUISettings));
+  await injectNextI18NextConfigurations(context, props, ['translation']);
 
 
   return {
   return {
     props,
     props,

+ 1 - 1
packages/app/src/pages/_app.page.tsx

@@ -16,7 +16,7 @@ import {
   useAppTitle, useConfidential, useGrowiTheme, useGrowiVersion, useSiteUrl,
   useAppTitle, useConfidential, useGrowiTheme, useGrowiVersion, useSiteUrl,
 } from '../stores/context';
 } from '../stores/context';
 
 
-import { CommonProps } from './commons';
+import { CommonProps } from './utils/commons';
 // import { useInterceptorManager } from '~/stores/interceptor';
 // import { useInterceptorManager } from '~/stores/interceptor';
 
 
 const isDev = process.env.NODE_ENV === 'development';
 const isDev = process.env.NODE_ENV === 'development';

+ 0 - 0
packages/app/src/pages/search.page.tsx → packages/app/src/pages/_search.page.tsx


+ 37 - 20
packages/app/src/pages/admin/[[...path]].page.tsx

@@ -4,32 +4,40 @@ import {
   NextPage, GetServerSideProps, GetServerSidePropsContext,
   NextPage, GetServerSideProps, GetServerSidePropsContext,
 } from 'next';
 } from 'next';
 import { useTranslation } from 'next-i18next';
 import { useTranslation } from 'next-i18next';
+import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
 import dynamic from 'next/dynamic';
 import dynamic from 'next/dynamic';
 import { useRouter } from 'next/router';
 import { useRouter } from 'next/router';
 
 
-import AdminHome from '~/components/Admin/AdminHome/AdminHome';
-import AppSettingsPageContents from '~/components/Admin/App/AppSettingsPageContents';
-import { AuditLogManagement } from '~/components/Admin/AuditLogManagement';
-import ElasticsearchManagement from '~/components/Admin/ElasticsearchManagement/ElasticsearchManagement';
-import ExportArchiveDataPage from '~/components/Admin/ExportArchiveDataPage';
-import DataImportPageContents from '~/components/Admin/ImportData/ImportDataPageContents';
-import LegacySlackIntegration from '~/components/Admin/LegacySlackIntegration/LegacySlackIntegration';
-import MarkDownSettingContents from '~/components/Admin/MarkdownSetting/MarkDownSettingContents';
-import NotificationSetting from '~/components/Admin/Notification/NotificationSetting';
-import SecurityManagementContents from '~/components/Admin/Security/SecurityManagementContents';
-import SlackIntegration from '~/components/Admin/SlackIntegration/SlackIntegration';
-import UserGroupPage from '~/components/Admin/UserGroup/UserGroupPage';
-import UserManagement from '~/components/Admin/UserManagement';
-import AdminLayout from '~/components/Layout/AdminLayout';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CrowiRequest } from '~/interfaces/crowi-request';
-import { CommonProps, getServerSideCommonProps, useCustomTitle } from '~/pages/commons';
 import PluginUtils from '~/server/plugins/plugin-utils';
 import PluginUtils from '~/server/plugins/plugin-utils';
 import ConfigLoader from '~/server/service/config-loader';
 import ConfigLoader from '~/server/service/config-loader';
 import {
 import {
   useCurrentUser, /* useSearchServiceConfigured, */ useIsSearchServiceReachable, useSiteUrl,
   useCurrentUser, /* useSearchServiceConfigured, */ useIsSearchServiceReachable, useSiteUrl,
 } from '~/stores/context';
 } from '~/stores/context';
+
+import {
+  CommonProps, getServerSideCommonProps, useCustomTitle, getNextI18NextConfig,
+} from '../utils/commons';
 // import { useEnvVars } from '~/stores/admin-context';
 // import { useEnvVars } from '~/stores/admin-context';
 
 
+const AdminHome = dynamic(() => import('../../components/Admin/AdminHome/AdminHome'), { ssr: false });
+const AppSettingsPageContents = dynamic(() => import('../../components/Admin/App/AppSettingsPageContents'), { ssr: false });
+const SecurityManagementContents = dynamic(() => import('../../components/Admin/Notification/NotificationSetting'), { ssr: false });
+const MarkDownSettingContents = dynamic(() => import('../../components/Admin/MarkdownSetting/MarkDownSettingContents'), { ssr: false });
+const CustomizeSettingContents = dynamic(() => import('../../components/Admin/Customize/Customize'), { ssr: false });
+const DataImportPageContents = dynamic(() => import('../../components/Admin/ImportData/ImportDataPageContents'), { ssr: false });
+const ExportArchiveDataPage = dynamic(() => import('../../components/Admin/ExportArchiveDataPage'), { ssr: false });
+const NotificationSetting = dynamic(() => import('../../components/Admin/Notification/NotificationSetting'), { ssr: false });
+const SlackIntegration = dynamic(() => import('../../components/Admin/SlackIntegration/SlackIntegration'), { ssr: false });
+const LegacySlackIntegration = dynamic(() => import('../../components/Admin/LegacySlackIntegration/LegacySlackIntegration'), { ssr: false });
+const UserManagement = dynamic(() => import('../../components/Admin/UserManagement'), { ssr: false });
+const UserGroupPage = dynamic(() => import('../../components/Admin/UserGroup/UserGroupPage'), { ssr: false });
+const ElasticsearchManagement = dynamic(() => import('../../components/Admin/ElasticsearchManagement/ElasticsearchManagement'), { ssr: false });
+// named export
+const AuditLogManagement = dynamic(() => import('../../components/Admin/AuditLogManagement').then(module => module.AuditLogManagement));
+
+const AdminLayout = dynamic(() => import('../../components/Layout/AdminLayout'), { ssr: false });
+
 const pluginUtils = new PluginUtils();
 const pluginUtils = new PluginUtils();
 
 
 type Props = CommonProps & {
 type Props = CommonProps & {
@@ -45,18 +53,15 @@ type Props = CommonProps & {
   isSearchServiceReachable: boolean,
   isSearchServiceReachable: boolean,
 
 
   siteUrl: string,
   siteUrl: string,
-
 };
 };
 
 
 const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
 const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
 
 
-  const { t } = useTranslation();
+  const { t } = useTranslation('admin');
   const router = useRouter();
   const router = useRouter();
   const path = router.query.path || 'home';
   const path = router.query.path || 'home';
   const name = Array.isArray(path) ? path[0] : path;
   const name = Array.isArray(path) ? path[0] : path;
 
 
-  const CustomizeSettingContents = dynamic(() => import('../../components/Admin/Customize/Customize'), { ssr: false });
-
   const adminPagesMap = {
   const adminPagesMap = {
     home: {
     home: {
       title: useCustomTitle(props, t('Wiki Management Home Page')),
       title: useCustomTitle(props, t('Wiki Management Home Page')),
@@ -145,6 +150,17 @@ const AdminMarkdownSettingsPage: NextPage<Props> = (props: Props) => {
   );
   );
 };
 };
 
 
+/**
+ * for Server Side Translations
+ * @param context
+ * @param props
+ * @param namespacesRequired
+ */
+async function injectNextI18NextConfigurations(context: GetServerSidePropsContext, props: Props, namespacesRequired?: string[] | undefined): Promise<void> {
+  const nextI18NextConfig = await getNextI18NextConfig(serverSideTranslations, context, namespacesRequired);
+  props._nextI18Next = nextI18NextConfig._nextI18Next;
+}
+
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
 export const getServerSideProps: GetServerSideProps = async(context: GetServerSidePropsContext) => {
   const req: CrowiRequest = context.req as CrowiRequest;
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
   const { crowi } = req;
@@ -153,7 +169,6 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
   } = crowi;
   } = crowi;
 
 
   const { user } = req;
   const { user } = req;
-
   const result = await getServerSideCommonProps(context);
   const result = await getServerSideCommonProps(context);
 
 
   // check for presence
   // check for presence
@@ -167,6 +182,8 @@ export const getServerSideProps: GetServerSideProps = async(context: GetServerSi
     props.currentUser = JSON.stringify(user);
     props.currentUser = JSON.stringify(user);
   }
   }
 
 
+  injectNextI18NextConfigurations(context, props, ['admin']);
+
   props.siteUrl = appService.getSiteUrl();
   props.siteUrl = appService.getSiteUrl();
   props.nodeVersion = crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version.version : null;
   props.nodeVersion = crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version.version : null;
   props.npmVersion = crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version.version : null;
   props.npmVersion = crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version.version : null;

+ 1 - 1
packages/app/src/pages/installer.page.tsx

@@ -17,7 +17,7 @@ import {
 
 
 import {
 import {
   CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
   CommonProps, getNextI18NextConfig, getServerSideCommonProps, useCustomTitle,
-} from './commons';
+} from './utils/commons';
 
 
 
 
 const { isTrashPage: _isTrashPage } = pagePathUtils;
 const { isTrashPage: _isTrashPage } = pagePathUtils;

+ 1 - 1
packages/app/src/pages/commons.ts → packages/app/src/pages/utils/commons.ts

@@ -5,7 +5,7 @@ import { SSRConfig, UserConfig } from 'next-i18next';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { CrowiRequest } from '~/interfaces/crowi-request';
 import { GrowiThemes } from '~/interfaces/theme';
 import { GrowiThemes } from '~/interfaces/theme';
 
 
-import * as nextI18NextConfig from '../next-i18next.config';
+import * as nextI18NextConfig from '../../next-i18next.config';
 
 
 export type CommonProps = {
 export type CommonProps = {
   namespacesRequired: string[], // i18next
   namespacesRequired: string[], // i18next

+ 13 - 0
packages/app/src/pages/utils/objectid-transformer.ts

@@ -0,0 +1,13 @@
+import { Types as MongooseTypes } from 'mongoose';
+import superjson from 'superjson';
+
+export const registerTransformerForObjectId = (): void => {
+  superjson.registerCustom<MongooseTypes.ObjectId|string, string>(
+    {
+      isApplicable: (v): v is MongooseTypes.ObjectId => v instanceof MongooseTypes.ObjectId,
+      serialize: v => (v instanceof MongooseTypes.ObjectId ? v.toHexString() : v),
+      deserialize: v => v,
+    },
+    'ObjectidTransformer',
+  );
+};

+ 7 - 5
packages/app/src/server/middlewares/inject-user-ui-settings-to-localvars.ts

@@ -5,16 +5,18 @@ import UserUISettings from '../models/user-ui-settings';
 
 
 const logger = loggerFactory('growi:middleware:inject-user-ui-settings-to-localvars');
 const logger = loggerFactory('growi:middleware:inject-user-ui-settings-to-localvars');
 
 
-async function getSettings(userId: string): Promise<Partial<IUserUISettings> | null> {
+async function getSettings(userId: string): Promise<IUserUISettings | null> {
   const doc = await UserUISettings.findOne({ user: userId }).exec();
   const doc = await UserUISettings.findOne({ user: userId }).exec();
 
 
-  let partialDoc: Partial<IUserUISettings> | null = null;
+  let userUISettings: IUserUISettings | null = null;
   if (doc != null) {
   if (doc != null) {
-    partialDoc = doc.toObject();
-    delete partialDoc.user;
+    const obj = doc.toObject();
+    // omit user property
+    // eslint-disable-next-line @typescript-eslint/no-unused-vars
+    userUISettings = (({ user, ...rest }) => rest)(obj);
   }
   }
 
 
-  return partialDoc;
+  return userUISettings;
 }
 }
 
 
 module.exports = () => {
 module.exports = () => {

+ 4 - 2
packages/app/src/server/models/user-ui-settings.ts

@@ -1,14 +1,16 @@
+import { getOrCreateModel, Ref, IUser } from '@growi/core';
 import {
 import {
   Schema, Model, Document,
   Schema, Model, Document,
 } from 'mongoose';
 } from 'mongoose';
 
 
-import { getOrCreateModel } from '@growi/core';
 
 
 import { SidebarContentsType } from '~/interfaces/ui';
 import { SidebarContentsType } from '~/interfaces/ui';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 import { IUserUISettings } from '~/interfaces/user-ui-settings';
 
 
 
 
-export interface UserUISettingsDocument extends IUserUISettings, Document {}
+export interface UserUISettingsDocument extends IUserUISettings, Document {
+  user: Ref<IUser>,
+}
 export type UserUISettingsModel = Model<UserUISettingsDocument>
 export type UserUISettingsModel = Model<UserUISettingsDocument>
 
 
 const schema = new Schema<UserUISettingsDocument, UserUISettingsModel>({
 const schema = new Schema<UserUISettingsDocument, UserUISettingsModel>({

+ 3 - 3
packages/app/src/server/routes/apiv3/attachment.js

@@ -28,7 +28,7 @@ module.exports = (crowi) => {
   const validator = {
   const validator = {
     retrieveAttachments: [
     retrieveAttachments: [
       query('pageId').isMongoId().withMessage('pageId is required'),
       query('pageId').isMongoId().withMessage('pageId is required'),
-      query('page').optional().isInt().withMessage('page must be a number'),
+      query('pageNumber').optional().isInt().withMessage('pageNumber must be a number'),
       query('limit').optional().isInt({ max: 100 }).withMessage('You should set less than 100 or not to set limit.'),
       query('limit').optional().isInt({ max: 100 }).withMessage('You should set less than 100 or not to set limit.'),
     ],
     ],
   };
   };
@@ -53,8 +53,8 @@ module.exports = (crowi) => {
   router.get('/list', accessTokenParser, loginRequired, validator.retrieveAttachments, apiV3FormValidator, async(req, res) => {
   router.get('/list', accessTokenParser, loginRequired, validator.retrieveAttachments, apiV3FormValidator, async(req, res) => {
 
 
     const limit = req.query.limit || await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS') || 10;
     const limit = req.query.limit || await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS') || 10;
-    const page = req.query.page || 1;
-    const offset = (page - 1) * limit;
+    const pageNumber = req.query.pageNumber || 1;
+    const offset = (pageNumber - 1) * limit;
 
 
     try {
     try {
       const pageId = req.query.pageId;
       const pageId = req.query.pageId;

+ 4 - 6
packages/app/src/server/routes/index.js

@@ -76,7 +76,7 @@ module.exports = function(crowi, app) {
 
 
   app.get('/_next/*'                  , next.delegateToNext);
   app.get('/_next/*'                  , next.delegateToNext);
 
 
-  app.get('/'                         , applicationInstalled, unavailableWhenMaintenanceMode, loginRequired, autoReconnectToSearch, injectUserUISettings, next.delegateToNext);
+  app.get('/'                         , applicationInstalled, unavailableWhenMaintenanceMode, loginRequired, autoReconnectToSearch, next.delegateToNext);
 
 
   app.get('/login/error/:reason'      , applicationInstalled, login.error);
   app.get('/login/error/:reason'      , applicationInstalled, login.error);
   app.get('/login'                    , applicationInstalled, login.preLogin, login.login);
   app.get('/login'                    , applicationInstalled, login.preLogin, login.login);
@@ -219,7 +219,7 @@ module.exports = function(crowi, app) {
   app.get('/attachment/:pageId/:fileName'       , loginRequired, attachment.api.obsoletedGetForMongoDB); // DEPRECATED: remains for backward compatibility for v3.3.x or below
   app.get('/attachment/:pageId/:fileName'       , loginRequired, attachment.api.obsoletedGetForMongoDB); // DEPRECATED: remains for backward compatibility for v3.3.x or below
   app.get('/download/:id([0-9a-z]{24})'         , loginRequired, attachment.api.download);
   app.get('/download/:id([0-9a-z]{24})'         , loginRequired, attachment.api.download);
 
 
-  app.get('/_search'                            , loginRequired, injectUserUISettings, search.searchPage);
+  app.get('/_search'                            , loginRequired, next.delegateToNext);
 
 
   app.get('/trash$'                   , loginRequired, injectUserUISettings, page.trashPageShowWrapper);
   app.get('/trash$'                   , loginRequired, injectUserUISettings, page.trashPageShowWrapper);
   app.get('/trash/$'                  , loginRequired, (req, res) => res.redirect('/trash'));
   app.get('/trash/$'                  , loginRequired, (req, res) => res.redirect('/trash'));
@@ -249,9 +249,7 @@ module.exports = function(crowi, app) {
 
 
   app.use('/ogp', express.Router().get('/:pageId([0-9a-z]{0,})', loginRequired, ogp.pageIdRequired, ogp.ogpValidator, ogp.renderOgp));
   app.use('/ogp', express.Router().get('/:pageId([0-9a-z]{0,})', loginRequired, ogp.pageIdRequired, ogp.ogpValidator, ogp.renderOgp));
 
 
-  app.get('/:id([0-9a-z]{24})'       , loginRequired , injectUserUISettings, next.delegateToNext);
-
-  app.get('/*/$'                   , loginRequired , injectUserUISettings, next.delegateToNext);
-  app.get('/*'                     , loginRequired , autoReconnectToSearch, injectUserUISettings, next.delegateToNext);
+  app.get('/*/$'                   , loginRequired, next.delegateToNext);
+  app.get('/*'                     , loginRequired, autoReconnectToSearch, next.delegateToNext);
 
 
 };
 };

+ 0 - 2
packages/app/src/services/renderer/renderer.tsx

@@ -5,7 +5,6 @@ import slug from 'rehype-slug';
 import toc, { HtmlElementNode } from 'rehype-toc';
 import toc, { HtmlElementNode } from 'rehype-toc';
 import breaks from 'remark-breaks';
 import breaks from 'remark-breaks';
 import emoji from 'remark-emoji';
 import emoji from 'remark-emoji';
-import footnotes from 'remark-footnotes';
 import gfm from 'remark-gfm';
 import gfm from 'remark-gfm';
 
 
 import { Header } from '~/components/ReactMarkdownComponents/Header';
 import { Header } from '~/components/ReactMarkdownComponents/Header';
@@ -244,7 +243,6 @@ export const generateViewOptions = (
 
 
   // add remark plugins
   // add remark plugins
   if (remarkPlugins != null) {
   if (remarkPlugins != null) {
-    remarkPlugins.push(footnotes);
     remarkPlugins.push(emoji);
     remarkPlugins.push(emoji);
     if (config.isEnabledLinebreaks) {
     if (config.isEnabledLinebreaks) {
       remarkPlugins.push(breaks);
       remarkPlugins.push(breaks);

+ 52 - 0
packages/app/src/stores/attachment.tsx

@@ -0,0 +1,52 @@
+import { useCallback } from 'react';
+
+import {
+  IAttachment, Nullable, SWRResponseWithUtils, withUtils,
+} from '@growi/core';
+import useSWR from 'swr';
+
+import { apiGet, apiPost } from '~/client/util/apiv1-client';
+import { IResAttachmentList } from '~/interfaces/attachment';
+
+type Util = {
+  remove(body: { attachment_id: string }): Promise<void>
+};
+
+type IDataAttachmentList = {
+  attachments: IAttachment[]
+  totalAttachments: number
+  limit: number
+};
+
+export const useSWRxAttachments = (pageId?: Nullable<string>, pageNumber?: number): SWRResponseWithUtils<Util, IDataAttachmentList, Error> => {
+  const shouldFetch = pageId != null && pageNumber != null;
+
+  const fetcher = useCallback(async(endpoint) => {
+    const res = await apiGet<IResAttachmentList>(endpoint, { pageId, pageNumber });
+    return {
+      attachments: res.data.paginateResult.docs,
+      totalAttachments: res.data.paginateResult.totalDocs,
+      limit: res.data.paginateResult.limit,
+    };
+  }, [pageId, pageNumber]);
+
+  const swrResponse = useSWR(
+    shouldFetch ? ['/attachments/list', pageId, pageNumber] : null,
+    fetcher,
+  );
+
+  // Utils
+  const remove = useCallback(async(body: { attachment_id: string }) => {
+    const { mutate } = swrResponse;
+
+    try {
+      await apiPost('/attachments.remove', body);
+      mutate();
+    }
+    catch (err) {
+      throw err;
+    }
+  }, [swrResponse]);
+
+  return withUtils<Util, IDataAttachmentList, Error>(swrResponse, { remove });
+};

+ 4 - 0
packages/app/src/stores/context.tsx

@@ -232,6 +232,10 @@ export const useIsBlinkedHeaderAtBoot = (initialData?: boolean): SWRResponse<boo
   return useStaticSWR('isBlinkedAtBoot', initialData);
   return useStaticSWR('isBlinkedAtBoot', initialData);
 };
 };
 
 
+export const useEditingMarkdown = (initialData?: string): SWRResponse<string, Error> => {
+  return useStaticSWR('currentMarkdown', initialData);
+};
+
 
 
 /** **********************************************************
 /** **********************************************************
  *                     Computed contexts
  *                     Computed contexts

+ 2 - 2
packages/app/src/stores/page.tsx

@@ -17,14 +17,14 @@ import { useCurrentPageId } from './context';
 export const useSWRxPage = (pageId?: string|null, shareLinkId?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
 export const useSWRxPage = (pageId?: string|null, shareLinkId?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
   return useSWR<IPagePopulatedToShowRevision, Error>(
   return useSWR<IPagePopulatedToShowRevision, Error>(
     pageId != null ? ['/page', pageId, shareLinkId] : null,
     pageId != null ? ['/page', pageId, shareLinkId] : null,
-    (endpoint, pageId, shareLinkId) => apiv3Get(endpoint, { pageId, shareLinkId }).then(result => result.data.page),
+    (endpoint, pageId, shareLinkId) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { pageId, shareLinkId }).then(result => result.data.page),
   );
   );
 };
 };
 
 
 export const useSWRxPageByPath = (path?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
 export const useSWRxPageByPath = (path?: string): SWRResponse<IPagePopulatedToShowRevision, Error> => {
   return useSWR<IPagePopulatedToShowRevision, Error>(
   return useSWR<IPagePopulatedToShowRevision, Error>(
     path != null ? ['/page', path] : null,
     path != null ? ['/page', path] : null,
-    (endpoint, path) => apiv3Get(endpoint, { path }).then(result => result.data.page),
+    (endpoint, path) => apiv3Get<{ page: IPagePopulatedToShowRevision }>(endpoint, { path }).then(result => result.data.page),
   );
   );
 };
 };
 
 

+ 27 - 1
packages/app/src/utils/axios.ts

@@ -1,9 +1,35 @@
 // eslint-disable-next-line no-restricted-imports
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
 import axios from 'axios';
+import parseISO from 'date-fns/parseISO';
+import isIsoDate from 'is-iso-date';
 
 
-export default axios.create({
+const customAxios = axios.create({
   headers: {
   headers: {
     'X-Requested-With': 'XMLHttpRequest',
     'X-Requested-With': 'XMLHttpRequest',
     'Content-Type': 'application/json',
     'Content-Type': 'application/json',
   },
   },
 });
 });
+
+// add an interceptor to convert ISODate
+const convertDates = (body: any): void => {
+  if (body === null || body === undefined || typeof body !== 'object') {
+    return body;
+  }
+
+  for (const key of Object.keys(body)) {
+    const value = body[key];
+    if (isIsoDate(value)) {
+      body[key] = parseISO(value);
+    }
+    else if (typeof value === 'object') {
+      // eslint-disable-next-line @typescript-eslint/no-unused-vars
+      convertDates(value);
+    }
+  }
+};
+customAxios.interceptors.response.use((response) => {
+  convertDates(response.data);
+  return response;
+});
+
+export default customAxios;

+ 1 - 0
packages/core/src/index.ts

@@ -27,3 +27,4 @@ export * from './service/localstorage-manager';
 export * from './utils/basic-interceptor';
 export * from './utils/basic-interceptor';
 export * from './utils/browser-utils';
 export * from './utils/browser-utils';
 export * from './utils/mongoose-utils';
 export * from './utils/mongoose-utils';
+export * from './utils/with-utils';

+ 3 - 1
packages/slackbot-proxy/src/controllers/slack.ts

@@ -1,5 +1,7 @@
 
 
 
 
+import { ServerResponse } from 'http';
+
 import {
 import {
   markdownSectionBlock, GrowiCommand, parseSlashCommand, respondRejectedErrors, generateWebClient,
   markdownSectionBlock, GrowiCommand, parseSlashCommand, respondRejectedErrors, generateWebClient,
   InvalidGrowiCommandError, requiredScopes, REQUEST_TIMEOUT_FOR_PTOG,
   InvalidGrowiCommandError, requiredScopes, REQUEST_TIMEOUT_FOR_PTOG,
@@ -423,7 +425,7 @@ export class SlackCtrl {
   }
   }
 
 
   @Get('/oauth_redirect')
   @Get('/oauth_redirect')
-  async handleOauthRedirect(@Req() req: Req, @Res() serverRes: Res, @Res() platformRes: PlatformResponse): Promise<void|string> {
+  async handleOauthRedirect(@Req() req: Req, @Res() serverRes: ServerResponse, @Res() platformRes: PlatformResponse): Promise<void|string> {
 
 
     // create 'Add to Slack' url
     // create 'Add to Slack' url
     const addToSlackUrl = await this.installerService.installer.generateInstallUrl({
     const addToSlackUrl = await this.installerService.installer.generateInstallUrl({

+ 361 - 40
yarn.lock

@@ -12,6 +12,14 @@
     loader-utils "^1.2.3"
     loader-utils "^1.2.3"
     lodash "^4.17.15"
     lodash "^4.17.15"
 
 
+"@ampproject/remapping@^2.1.0":
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+  integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
+  dependencies:
+    "@jridgewell/gen-mapping" "^0.1.0"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
 "@apidevtools/json-schema-ref-parser@^9.0.6":
 "@apidevtools/json-schema-ref-parser@^9.0.6":
   version "9.0.9"
   version "9.0.9"
   resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b"
   resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b"
@@ -974,6 +982,13 @@
   dependencies:
   dependencies:
     "@babel/highlight" "^7.14.5"
     "@babel/highlight" "^7.14.5"
 
 
+"@babel/code-frame@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+  integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+  dependencies:
+    "@babel/highlight" "^7.18.6"
+
 "@babel/code-frame@^7.5.5":
 "@babel/code-frame@^7.5.5":
   version "7.5.5"
   version "7.5.5"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
   resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
@@ -993,6 +1008,11 @@
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.9.tgz#ac7996ceaafcf8f410119c8af0d1db4cf914a210"
   resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.14.9.tgz#ac7996ceaafcf8f410119c8af0d1db4cf914a210"
   integrity sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw==
   integrity sha512-p3QjZmMGHDGdpcwEYYWu7i7oJShJvtgMjJeb0W95PPhSm++3lm8YXYOh45Y6iCN9PkZLTZ7CIX5nFrp7pw7TXw==
 
 
+"@babel/compat-data@^7.18.8":
+  version "7.18.8"
+  resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
+  integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
+
 "@babel/core@^7.1.0":
 "@babel/core@^7.1.0":
   version "7.4.5"
   version "7.4.5"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.4.5.tgz#081f97e8ffca65a9b4b0fdc7e274e703f000c06a"
@@ -1013,6 +1033,27 @@
     semver "^5.4.1"
     semver "^5.4.1"
     source-map "^0.5.0"
     source-map "^0.5.0"
 
 
+"@babel/core@^7.13.15":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59"
+  integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
+  dependencies:
+    "@ampproject/remapping" "^2.1.0"
+    "@babel/code-frame" "^7.18.6"
+    "@babel/generator" "^7.18.9"
+    "@babel/helper-compilation-targets" "^7.18.9"
+    "@babel/helper-module-transforms" "^7.18.9"
+    "@babel/helpers" "^7.18.9"
+    "@babel/parser" "^7.18.9"
+    "@babel/template" "^7.18.6"
+    "@babel/traverse" "^7.18.9"
+    "@babel/types" "^7.18.9"
+    convert-source-map "^1.7.0"
+    debug "^4.1.0"
+    gensync "^1.0.0-beta.2"
+    json5 "^2.2.1"
+    semver "^6.3.0"
+
 "@babel/core@^7.7.2":
 "@babel/core@^7.7.2":
   version "7.14.8"
   version "7.14.8"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010"
   resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010"
@@ -1064,6 +1105,15 @@
     jsesc "^2.5.1"
     jsesc "^2.5.1"
     source-map "^0.5.0"
     source-map "^0.5.0"
 
 
+"@babel/generator@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5"
+  integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==
+  dependencies:
+    "@babel/types" "^7.18.9"
+    "@jridgewell/gen-mapping" "^0.3.2"
+    jsesc "^2.5.1"
+
 "@babel/generator@^7.4.4":
 "@babel/generator@^7.4.4":
   version "7.4.4"
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041"
   resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.4.4.tgz#174a215eb843fc392c7edcaabeaa873de6e8f041"
@@ -1105,6 +1155,21 @@
     browserslist "^4.16.6"
     browserslist "^4.16.6"
     semver "^6.3.0"
     semver "^6.3.0"
 
 
+"@babel/helper-compilation-targets@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
+  integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
+  dependencies:
+    "@babel/compat-data" "^7.18.8"
+    "@babel/helper-validator-option" "^7.18.6"
+    browserslist "^4.20.2"
+    semver "^6.3.0"
+
+"@babel/helper-environment-visitor@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
+  integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
+
 "@babel/helper-function-name@^7.1.0":
 "@babel/helper-function-name@^7.1.0":
   version "7.1.0"
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz#a0ceb01685f73355d4360c1247f582bfafc8ff53"
@@ -1122,6 +1187,14 @@
     "@babel/template" "^7.14.5"
     "@babel/template" "^7.14.5"
     "@babel/types" "^7.14.5"
     "@babel/types" "^7.14.5"
 
 
+"@babel/helper-function-name@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
+  integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
+  dependencies:
+    "@babel/template" "^7.18.6"
+    "@babel/types" "^7.18.9"
+
 "@babel/helper-function-name@^7.7.4":
 "@babel/helper-function-name@^7.7.4":
   version "7.7.4"
   version "7.7.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e"
   resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.7.4.tgz#ab6e041e7135d436d8f0a3eca15de5b67a341a2e"
@@ -1174,6 +1247,13 @@
   dependencies:
   dependencies:
     "@babel/types" "^7.14.5"
     "@babel/types" "^7.14.5"
 
 
+"@babel/helper-hoist-variables@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
+  integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
+  dependencies:
+    "@babel/types" "^7.18.6"
+
 "@babel/helper-member-expression-to-functions@^7.14.5":
 "@babel/helper-member-expression-to-functions@^7.14.5":
   version "7.14.7"
   version "7.14.7"
   resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970"
   resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.14.7.tgz#97e56244beb94211fe277bd818e3a329c66f7970"
@@ -1181,6 +1261,13 @@
   dependencies:
   dependencies:
     "@babel/types" "^7.14.5"
     "@babel/types" "^7.14.5"
 
 
+"@babel/helper-module-imports@^7.13.12", "@babel/helper-module-imports@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
+  integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
+  dependencies:
+    "@babel/types" "^7.18.6"
+
 "@babel/helper-module-imports@^7.14.5":
 "@babel/helper-module-imports@^7.14.5":
   version "7.14.5"
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
   resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.14.5.tgz#6d1a44df6a38c957aa7c312da076429f11b422f3"
@@ -1202,6 +1289,20 @@
     "@babel/traverse" "^7.14.8"
     "@babel/traverse" "^7.14.8"
     "@babel/types" "^7.14.8"
     "@babel/types" "^7.14.8"
 
 
+"@babel/helper-module-transforms@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
+  integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
+  dependencies:
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-module-imports" "^7.18.6"
+    "@babel/helper-simple-access" "^7.18.6"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/helper-validator-identifier" "^7.18.6"
+    "@babel/template" "^7.18.6"
+    "@babel/traverse" "^7.18.9"
+    "@babel/types" "^7.18.9"
+
 "@babel/helper-optimise-call-expression@^7.14.5":
 "@babel/helper-optimise-call-expression@^7.14.5":
   version "7.14.5"
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
   resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.14.5.tgz#f27395a8619e0665b3f0364cddb41c25d71b499c"
@@ -1219,6 +1320,11 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz#5ac822ce97eec46741ab70a517971e443a70c5a9"
   integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
   integrity sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==
 
 
+"@babel/helper-plugin-utils@^7.18.6":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
+  integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
+
 "@babel/helper-plugin-utils@^7.8.0":
 "@babel/helper-plugin-utils@^7.8.0":
   version "7.8.3"
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
   resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
@@ -1241,6 +1347,13 @@
   dependencies:
   dependencies:
     "@babel/types" "^7.14.8"
     "@babel/types" "^7.14.8"
 
 
+"@babel/helper-simple-access@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
+  integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==
+  dependencies:
+    "@babel/types" "^7.18.6"
+
 "@babel/helper-split-export-declaration@^7.14.5":
 "@babel/helper-split-export-declaration@^7.14.5":
   version "7.14.5"
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.14.5.tgz#22b23a54ef51c2b7605d851930c1976dd0bc693a"
@@ -1248,6 +1361,13 @@
   dependencies:
   dependencies:
     "@babel/types" "^7.14.5"
     "@babel/types" "^7.14.5"
 
 
+"@babel/helper-split-export-declaration@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
+  integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
+  dependencies:
+    "@babel/types" "^7.18.6"
+
 "@babel/helper-split-export-declaration@^7.4.4":
 "@babel/helper-split-export-declaration@^7.4.4":
   version "7.4.4"
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
   resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz#ff94894a340be78f53f06af038b205c49d993677"
@@ -1284,11 +1404,21 @@
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
   integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
   integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
 
 
+"@babel/helper-validator-identifier@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
+  integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
+
 "@babel/helper-validator-option@^7.14.5":
 "@babel/helper-validator-option@^7.14.5":
   version "7.14.5"
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
   resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.14.5.tgz#6e72a1fff18d5dfcb878e1e62f1a021c4b72d5a3"
   integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
   integrity sha512-OX8D5eeX4XwcroVW45NMvoYaIuFI+GQpA2a8Gi+X/U/cDUIRsV37qQfF905F0htTRCREQIB4KqPeaveRJUl3Ow==
 
 
+"@babel/helper-validator-option@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
+  integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
+
 "@babel/helpers@^7.14.8":
 "@babel/helpers@^7.14.8":
   version "7.14.8"
   version "7.14.8"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77"
@@ -1298,6 +1428,15 @@
     "@babel/traverse" "^7.14.8"
     "@babel/traverse" "^7.14.8"
     "@babel/types" "^7.14.8"
     "@babel/types" "^7.14.8"
 
 
+"@babel/helpers@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
+  integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
+  dependencies:
+    "@babel/template" "^7.18.6"
+    "@babel/traverse" "^7.18.9"
+    "@babel/types" "^7.18.9"
+
 "@babel/helpers@^7.4.4":
 "@babel/helpers@^7.4.4":
   version "7.4.4"
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.4.4.tgz#868b0ef59c1dd4e78744562d5ce1b59c89f2f2a5"
@@ -1342,6 +1481,15 @@
     chalk "^2.0.0"
     chalk "^2.0.0"
     js-tokens "^4.0.0"
     js-tokens "^4.0.0"
 
 
+"@babel/highlight@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+  integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    chalk "^2.0.0"
+    js-tokens "^4.0.0"
+
 "@babel/highlight@^7.8.3":
 "@babel/highlight@^7.8.3":
   version "7.8.3"
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
   resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
@@ -1361,6 +1509,11 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.9.tgz#596c1ad67608070058ebf8df50c1eaf65db895a4"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.14.9.tgz#596c1ad67608070058ebf8df50c1eaf65db895a4"
   integrity sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ==
   integrity sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ==
 
 
+"@babel/parser@^7.18.6", "@babel/parser@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
+  integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
+
 "@babel/parser@^7.4.0":
 "@babel/parser@^7.4.0":
   version "7.4.2"
   version "7.4.2"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.2.tgz#b4521a400cb5a871eab3890787b4bc1326d38d91"
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.2.tgz#b4521a400cb5a871eab3890787b4bc1326d38d91"
@@ -1410,6 +1563,13 @@
   dependencies:
   dependencies:
     "@babel/helper-plugin-utils" "^7.8.0"
     "@babel/helper-plugin-utils" "^7.8.0"
 
 
+"@babel/plugin-syntax-jsx@^7.12.13":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0"
+  integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.6"
+
 "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
 "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
   version "7.10.4"
   version "7.10.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
@@ -1459,6 +1619,13 @@
   dependencies:
   dependencies:
     "@babel/helper-plugin-utils" "^7.14.5"
     "@babel/helper-plugin-utils" "^7.14.5"
 
 
+"@babel/plugin-syntax-typescript@^7.12.13":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285"
+  integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.18.6"
+
 "@babel/plugin-syntax-typescript@^7.7.2":
 "@babel/plugin-syntax-typescript@^7.7.2":
   version "7.14.5"
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.14.5.tgz#b82c6ce471b165b5ce420cf92914d6fb46225716"
@@ -1546,6 +1713,15 @@
     "@babel/parser" "^7.14.5"
     "@babel/parser" "^7.14.5"
     "@babel/types" "^7.14.5"
     "@babel/types" "^7.14.5"
 
 
+"@babel/template@^7.18.6":
+  version "7.18.6"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31"
+  integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==
+  dependencies:
+    "@babel/code-frame" "^7.18.6"
+    "@babel/parser" "^7.18.6"
+    "@babel/types" "^7.18.6"
+
 "@babel/template@^7.4.4":
 "@babel/template@^7.4.4":
   version "7.4.4"
   version "7.4.4"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.4.4.tgz#f4b88d1225689a08f5bc3a17483545be9e4ed237"
@@ -1603,6 +1779,22 @@
     debug "^4.1.0"
     debug "^4.1.0"
     globals "^11.1.0"
     globals "^11.1.0"
 
 
+"@babel/traverse@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98"
+  integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==
+  dependencies:
+    "@babel/code-frame" "^7.18.6"
+    "@babel/generator" "^7.18.9"
+    "@babel/helper-environment-visitor" "^7.18.9"
+    "@babel/helper-function-name" "^7.18.9"
+    "@babel/helper-hoist-variables" "^7.18.6"
+    "@babel/helper-split-export-declaration" "^7.18.6"
+    "@babel/parser" "^7.18.9"
+    "@babel/types" "^7.18.9"
+    debug "^4.1.0"
+    globals "^11.1.0"
+
 "@babel/traverse@^7.7.4":
 "@babel/traverse@^7.7.4":
   version "7.7.4"
   version "7.7.4"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558"
   resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.7.4.tgz#9c1e7c60fb679fe4fcfaa42500833333c2058558"
@@ -1641,6 +1833,14 @@
     lodash "^4.17.11"
     lodash "^4.17.11"
     to-fast-properties "^2.0.0"
     to-fast-properties "^2.0.0"
 
 
+"@babel/types@^7.13.17", "@babel/types@^7.18.6", "@babel/types@^7.18.9":
+  version "7.18.9"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f"
+  integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.18.6"
+    to-fast-properties "^2.0.0"
+
 "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.14.9", "@babel/types@^7.3.3":
 "@babel/types@^7.14.5", "@babel/types@^7.14.8", "@babel/types@^7.14.9", "@babel/types@^7.3.3":
   version "7.14.9"
   version "7.14.9"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.9.tgz#f2b19c3f2f77c5708d67fe8f6046e9cea2b5036d"
   resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.9.tgz#f2b19c3f2f77c5708d67fe8f6046e9cea2b5036d"
@@ -2121,11 +2321,33 @@
     comment-json "^4.1.0"
     comment-json "^4.1.0"
     find-up "^4.1.0"
     find-up "^4.1.0"
 
 
+"@jridgewell/gen-mapping@^0.1.0":
+  version "0.1.1"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+  integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.0"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@jridgewell/gen-mapping@^0.3.2":
+  version "0.3.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+  integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+  dependencies:
+    "@jridgewell/set-array" "^1.0.1"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+    "@jridgewell/trace-mapping" "^0.3.9"
+
 "@jridgewell/resolve-uri@^3.0.3":
 "@jridgewell/resolve-uri@^3.0.3":
   version "3.0.8"
   version "3.0.8"
   resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1"
   resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.8.tgz#687cc2bbf243f4e9a868ecf2262318e2658873a1"
   integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==
   integrity sha512-YK5G9LaddzGbcucK4c8h5tWFmMPBvRZ/uyWmN1/SbBdIvqGUdWGkJ5BAaccgs6XbzVLsqbPJrBSFwKv3kT9i7w==
 
 
+"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
+  version "1.1.2"
+  resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+  integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
 "@jridgewell/sourcemap-codec@^1.4.10":
 "@jridgewell/sourcemap-codec@^1.4.10":
   version "1.4.14"
   version "1.4.14"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
   resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
@@ -2139,6 +2361,14 @@
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/resolve-uri" "^3.0.3"
     "@jridgewell/sourcemap-codec" "^1.4.10"
     "@jridgewell/sourcemap-codec" "^1.4.10"
 
 
+"@jridgewell/trace-mapping@^0.3.9":
+  version "0.3.14"
+  resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
+  integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
+  dependencies:
+    "@jridgewell/resolve-uri" "^3.0.3"
+    "@jridgewell/sourcemap-codec" "^1.4.10"
+
 "@jsdevtools/ono@^7.1.3":
 "@jsdevtools/ono@^7.1.3":
   version "7.1.3"
   version "7.1.3"
   resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
   resolved "https://registry.yarnpkg.com/@jsdevtools/ono/-/ono-7.1.3.tgz#9df03bbd7c696a5c58885c34aa06da41c8543796"
@@ -4054,16 +4284,16 @@
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0"
   integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
   integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==
 
 
+"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.9":
+  version "7.0.11"
+  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
+  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
+
 "@types/json-schema@^7.0.6":
 "@types/json-schema@^7.0.6":
   version "7.0.9"
   version "7.0.9"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
   resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
   integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
   integrity sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==
 
 
-"@types/json-schema@^7.0.9":
-  version "7.0.11"
-  resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
-  integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
-
 "@types/json5@^0.0.29":
 "@types/json5@^0.0.29":
   version "0.0.29"
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -4640,6 +4870,11 @@ aggregate-error@^3.0.0:
     clean-stack "^2.0.0"
     clean-stack "^2.0.0"
     indent-string "^4.0.0"
     indent-string "^4.0.0"
 
 
+ajv-keywords@^3.5.2:
+  version "3.5.2"
+  resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
+  integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
+
 ajv@^5.5.2:
 ajv@^5.5.2:
   version "5.5.2"
   version "5.5.2"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
@@ -5259,6 +5494,16 @@ babel-jest@^27.0.6:
     graceful-fs "^4.2.4"
     graceful-fs "^4.2.4"
     slash "^3.0.0"
     slash "^3.0.0"
 
 
+babel-loader@^8.2.2, babel-loader@^8.2.5:
+  version "8.2.5"
+  resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.2.5.tgz#d45f585e654d5a5d90f5350a779d7647c5ed512e"
+  integrity sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==
+  dependencies:
+    find-cache-dir "^3.3.1"
+    loader-utils "^2.0.0"
+    make-dir "^3.1.0"
+    schema-utils "^2.6.5"
+
 babel-plugin-istanbul@^6.0.0:
 babel-plugin-istanbul@^6.0.0:
   version "6.0.0"
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"
   resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz#e159ccdc9af95e0b570c75b4573b7c34d671d765"
@@ -5280,6 +5525,15 @@ babel-plugin-jest-hoist@^27.0.6:
     "@types/babel__core" "^7.0.0"
     "@types/babel__core" "^7.0.0"
     "@types/babel__traverse" "^7.0.6"
     "@types/babel__traverse" "^7.0.6"
 
 
+babel-plugin-superjson-next@^0.4.2:
+  version "0.4.3"
+  resolved "https://registry.yarnpkg.com/babel-plugin-superjson-next/-/babel-plugin-superjson-next-0.4.3.tgz#3d35d36443ad1e4fe4529a4567f264391d2a93fd"
+  integrity sha512-wfPsTPnEn1YaEkmaoomN4Z/Hm6nWVWFkASdetP/Ju2aPz/8XguAJwuThZIBH9dMTtXnyGXnbJufPQ0wRha0QcA==
+  dependencies:
+    "@babel/helper-module-imports" "^7.13.12"
+    "@babel/types" "^7.13.17"
+    hoist-non-react-statics "^3.3.2"
+
 babel-preset-current-node-syntax@^1.0.0:
 babel-preset-current-node-syntax@^1.0.0:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
   resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
@@ -5720,6 +5974,16 @@ browserslist@^4.16.6:
     node-releases "^2.0.1"
     node-releases "^2.0.1"
     picocolors "^1.0.0"
     picocolors "^1.0.0"
 
 
+browserslist@^4.20.2:
+  version "4.21.2"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf"
+  integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==
+  dependencies:
+    caniuse-lite "^1.0.30001366"
+    electron-to-chromium "^1.4.188"
+    node-releases "^2.0.6"
+    update-browserslist-db "^1.0.4"
+
 bs-logger@0.x:
 bs-logger@0.x:
   version "0.2.6"
   version "0.2.6"
   resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
   resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
@@ -6034,6 +6298,11 @@ caniuse-lite@^1.0.30001332:
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz#95c5efdb64148bb4870771749b9a619304755ce5"
   resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001354.tgz#95c5efdb64148bb4870771749b9a619304755ce5"
   integrity sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg==
   integrity sha512-mImKeCkyGDAHNywYFA4bqnLAzTUvVkqPvhY4DV47X+Gl2c5Z8c3KNETnXp14GQt11LvxE8AwjzGxJ+rsikiOzg==
 
 
+caniuse-lite@^1.0.30001366:
+  version "1.0.30001367"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz#2b97fe472e8fa29c78c5970615d7cd2ee414108a"
+  integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw==
+
 capital-case@^1.0.3, capital-case@^1.0.4:
 capital-case@^1.0.3, capital-case@^1.0.4:
   version "1.0.4"
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
   resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
@@ -6641,6 +6910,11 @@ common-tags@^1.8.0:
   resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
   resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.2.tgz#94ebb3c076d26032745fd54face7f688ef5ac9c6"
   integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==
   integrity sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==
 
 
+commondir@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
+  integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==
+
 compare-func@^2.0.0:
 compare-func@^2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3"
   resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3"
@@ -7009,6 +7283,13 @@ copy-anything@^2.0.1:
   dependencies:
   dependencies:
     is-what "^3.14.1"
     is-what "^3.14.1"
 
 
+copy-anything@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/copy-anything/-/copy-anything-3.0.2.tgz#7189171ff5e1893b2287e8bf574b8cd448ed50b1"
+  integrity sha512-CzATjGXzUQ0EvuvgOCI6A4BGOo2bcVx8B+eC2nF862iv9fopnPQwlrbACakNCHRIJbCSBj+J/9JeDf60k64MkA==
+  dependencies:
+    is-what "^4.1.6"
+
 copy-descriptor@^0.1.0:
 copy-descriptor@^0.1.0:
   version "0.1.1"
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
   resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d"
@@ -7966,6 +8247,11 @@ electron-to-chromium@^1.3.878:
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.884.tgz#0cd8c3a80271fd84a81f284c60fb3c9ecb33c166"
   resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.884.tgz#0cd8c3a80271fd84a81f284c60fb3c9ecb33c166"
   integrity sha512-kOaCAa+biA98PwH5BpCkeUeTL6mCeg8p3Q3OhqzPyqhu/5QUnWAN2wr/3IK8xMQxIV76kfoQpP+Bn/wij/jXrg==
   integrity sha512-kOaCAa+biA98PwH5BpCkeUeTL6mCeg8p3Q3OhqzPyqhu/5QUnWAN2wr/3IK8xMQxIV76kfoQpP+Bn/wij/jXrg==
 
 
+electron-to-chromium@^1.4.188:
+  version "1.4.195"
+  resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz#139b2d95a42a3f17df217589723a1deac71d1473"
+  integrity sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg==
+
 emittery@^0.8.1:
 emittery@^0.8.1:
   version "0.8.1"
   version "0.8.1"
   resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
   resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
@@ -9400,6 +9686,15 @@ finalhandler@~1.1.2:
     statuses "~1.5.0"
     statuses "~1.5.0"
     unpipe "~1.0.0"
     unpipe "~1.0.0"
 
 
+find-cache-dir@^3.3.1:
+  version "3.3.2"
+  resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b"
+  integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==
+  dependencies:
+    commondir "^1.0.1"
+    make-dir "^3.0.2"
+    pkg-dir "^4.1.0"
+
 find-index@^0.1.1:
 find-index@^0.1.1:
   version "0.1.1"
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4"
   resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4"
@@ -11699,6 +11994,11 @@ is-what@^3.14.1:
   resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
   resolved "https://registry.yarnpkg.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1"
   integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
   integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==
 
 
+is-what@^4.1.6:
+  version "4.1.7"
+  resolved "https://registry.yarnpkg.com/is-what/-/is-what-4.1.7.tgz#c41dc1d2d2d6a9285c624c2505f61849c8b1f9cc"
+  integrity sha512-DBVOQNiPKnGMxRMLIYSwERAS5MVY1B7xYiGnpgctsOFvVDz9f9PFXXxMcTOHuoqYp4NK9qFYQaIC1NRRxLMpBQ==
+
 is-whitespace-character@^1.0.0:
 is-whitespace-character@^1.0.0:
   version "1.0.3"
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac"
   resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.3.tgz#b3ad9546d916d7d3ffa78204bca0c26b56257fac"
@@ -12472,6 +12772,11 @@ json5@^2.1.0:
   dependencies:
   dependencies:
     minimist "^1.2.0"
     minimist "^1.2.0"
 
 
+json5@^2.2.1:
+  version "2.2.1"
+  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
+  integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
+
 jsonfile@^3.0.0:
 jsonfile@^3.0.0:
   version "3.0.1"
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
   resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-3.0.1.tgz#a5ecc6f65f53f662c4415c7675a0331d0992ec66"
@@ -12903,6 +13208,15 @@ loader-utils@^1.2.3:
     emojis-list "^3.0.0"
     emojis-list "^3.0.0"
     json5 "^1.0.1"
     json5 "^1.0.1"
 
 
+loader-utils@^2.0.0:
+  version "2.0.2"
+  resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.2.tgz#d6e3b4fb81870721ae4e0868ab11dd638368c129"
+  integrity sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==
+  dependencies:
+    big.js "^5.2.2"
+    emojis-list "^3.0.0"
+    json5 "^2.1.2"
+
 localtunnel@^2.0.1:
 localtunnel@^2.0.1:
   version "2.0.2"
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-2.0.2.tgz#528d50087151c4790f89c2db374fe7b0a48501f0"
   resolved "https://registry.yarnpkg.com/localtunnel/-/localtunnel-2.0.2.tgz#528d50087151c4790f89c2db374fe7b0a48501f0"
@@ -13201,7 +13515,7 @@ macos-release@^2.2.0:
   resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2"
   resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.0.tgz#067c2c88b5f3fb3c56a375b2ec93826220fa1ff2"
   integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==
   integrity sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==
 
 
-make-dir@3.1.0, make-dir@^3.1.0:
+make-dir@3.1.0, make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -13413,15 +13727,6 @@ mdast-util-footnote@^0.1.0:
     mdast-util-to-markdown "^0.6.0"
     mdast-util-to-markdown "^0.6.0"
     micromark "~2.11.0"
     micromark "~2.11.0"
 
 
-mdast-util-footnote@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/mdast-util-footnote/-/mdast-util-footnote-1.1.0.tgz#c81e583e2de6a3dd4e05a59e83639fb67b0c5686"
-  integrity sha512-M8tHiqL+7eS/ASWWs+nnmMKRLO1A1JnllNRzF2gLT4gpzLGvRiE0IugPI+/zfpi93IrtrxmlFolTdRPtWZocaA==
-  dependencies:
-    "@types/mdast" "^3.0.0"
-    mdast-util-to-markdown "^1.0.0"
-    micromark-util-normalize-identifier "^1.0.0"
-
 mdast-util-from-markdown@^0.8.0:
 mdast-util-from-markdown@^0.8.0:
   version "0.8.5"
   version "0.8.5"
   resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c"
   resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-0.8.5.tgz#d1ef2ca42bc377ecb0463a987910dae89bd9a28c"
@@ -13774,20 +14079,6 @@ micromark-extension-footnote@^0.3.0:
   dependencies:
   dependencies:
     micromark "~2.11.0"
     micromark "~2.11.0"
 
 
-micromark-extension-footnote@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/micromark-extension-footnote/-/micromark-extension-footnote-1.0.2.tgz#534c42211315406b43625fa29573c7094d5122c2"
-  integrity sha512-sD7t/hooONLnbPYgcQpBcTjh7mPEcD2R/jRS5DLYDNm0XbngbaJZ01F1aI3v6VXVMdAu3XtFKUecNRlXbgGk/Q==
-  dependencies:
-    micromark-core-commonmark "^1.0.0"
-    micromark-factory-space "^1.0.0"
-    micromark-util-character "^1.0.0"
-    micromark-util-chunked "^1.0.0"
-    micromark-util-normalize-identifier "^1.0.0"
-    micromark-util-resolve-all "^1.0.0"
-    micromark-util-symbol "^1.0.0"
-    uvu "^0.5.0"
-
 micromark-extension-frontmatter@^0.2.0:
 micromark-extension-frontmatter@^0.2.0:
   version "0.2.2"
   version "0.2.2"
   resolved "https://registry.yarnpkg.com/micromark-extension-frontmatter/-/micromark-extension-frontmatter-0.2.2.tgz#61b8e92e9213e1d3c13f5a59e7862f5ca98dfa53"
   resolved "https://registry.yarnpkg.com/micromark-extension-frontmatter/-/micromark-extension-frontmatter-0.2.2.tgz#61b8e92e9213e1d3c13f5a59e7862f5ca98dfa53"
@@ -14831,6 +15122,17 @@ next-i18next@^11.0.0:
     i18next-fs-backend "^1.1.4"
     i18next-fs-backend "^1.1.4"
     react-i18next "^11.16.2"
     react-i18next "^11.16.2"
 
 
+next-superjson@^0.0.4:
+  version "0.0.4"
+  resolved "https://registry.yarnpkg.com/next-superjson/-/next-superjson-0.0.4.tgz#273c41482cc3d2914cba6c05230ca473456a1cea"
+  integrity sha512-PYtoHbPcZYED8Vm9YCIQIZi/arANNnf6grwjkPuJXzWdY1TxJxrn9dCPmVj6ALvPn9YcDThwEA9WvHq/NyzMvw==
+  dependencies:
+    "@babel/core" "^7.13.15"
+    "@babel/plugin-syntax-jsx" "^7.12.13"
+    "@babel/plugin-syntax-typescript" "^7.12.13"
+    babel-loader "^8.2.2"
+    babel-plugin-superjson-next "^0.4.2"
+
 next-themes@^0.2.0:
 next-themes@^0.2.0:
   version "0.2.0"
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.2.0.tgz#fdc507f61e95b3ae513dee8d4783bcec8c02e3a3"
   resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.2.0.tgz#fdc507f61e95b3ae513dee8d4783bcec8c02e3a3"
@@ -15028,6 +15330,11 @@ node-releases@^2.0.1:
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
   resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
   integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
   integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
 
 
+node-releases@^2.0.6:
+  version "2.0.6"
+  resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
+  integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
+
 nodemailer-ses-transport@~1.5.0:
 nodemailer-ses-transport@~1.5.0:
   version "1.5.1"
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/nodemailer-ses-transport/-/nodemailer-ses-transport-1.5.1.tgz#dc0598c1bf53e8652e632e8f31692ce022d7dea9"
   resolved "https://registry.yarnpkg.com/nodemailer-ses-transport/-/nodemailer-ses-transport-1.5.1.tgz#dc0598c1bf53e8652e632e8f31692ce022d7dea9"
@@ -16283,7 +16590,7 @@ pixelmatch@^5.2.1:
   dependencies:
   dependencies:
     pngjs "^4.0.1"
     pngjs "^4.0.1"
 
 
-pkg-dir@^4.2.0:
+pkg-dir@^4.1.0, pkg-dir@^4.2.0:
   version "4.2.0"
   version "4.2.0"
   resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
   resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3"
   integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
   integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==
@@ -17704,16 +18011,6 @@ remark-footnotes@^3.0.0:
     mdast-util-footnote "^0.1.0"
     mdast-util-footnote "^0.1.0"
     micromark-extension-footnote "^0.3.0"
     micromark-extension-footnote "^0.3.0"
 
 
-remark-footnotes@^4.0.1:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/remark-footnotes/-/remark-footnotes-4.0.1.tgz#17d3c87ddc240924e8b4590b34a3d79aa8ede50c"
-  integrity sha512-He6YzQFk/Wu2KgfjI80EyPXjt/G+WFaYfUH+xapqPQBdm3aTdEyzosXXv9a2FbTxGqgOfJ4q/TCB46v+wofRpQ==
-  dependencies:
-    "@types/mdast" "^3.0.0"
-    mdast-util-footnote "^1.0.0"
-    micromark-extension-footnote "^1.0.0"
-    unified "^10.0.0"
-
 remark-frontmatter@^1.3.3:
 remark-frontmatter@^1.3.3:
   version "1.3.3"
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/remark-frontmatter/-/remark-frontmatter-1.3.3.tgz#67ec63c89da5a84bb793ecec166e11b4eb47af10"
   resolved "https://registry.yarnpkg.com/remark-frontmatter/-/remark-frontmatter-1.3.3.tgz#67ec63c89da5a84bb793ecec166e11b4eb47af10"
@@ -18222,6 +18519,15 @@ scheduler@^0.23.0:
   dependencies:
   dependencies:
     loose-envify "^1.1.0"
     loose-envify "^1.1.0"
 
 
+schema-utils@^2.6.5:
+  version "2.7.1"
+  resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7"
+  integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==
+  dependencies:
+    "@types/json-schema" "^7.0.5"
+    ajv "^6.12.4"
+    ajv-keywords "^3.5.2"
+
 scroll-into-view-if-needed@^2.2.20:
 scroll-into-view-if-needed@^2.2.20:
   version "2.2.29"
   version "2.2.29"
   resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
   resolved "https://registry.yarnpkg.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-2.2.29.tgz#551791a84b7e2287706511f8c68161e4990ab885"
@@ -19594,6 +19900,13 @@ subarg@^1.0.0:
   dependencies:
   dependencies:
     minimist "^1.1.0"
     minimist "^1.1.0"
 
 
+superjson@^1.9.1:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/superjson/-/superjson-1.9.1.tgz#e23bd2e8cf0f4ade131d6d769754cac7eaa8ab34"
+  integrity sha512-oT3HA2nPKlU1+5taFgz/HDy+GEaY+CWEbLzaRJVD4gZ7zMVVC4GDNFdgvAZt6/VuIk6D2R7RtPAiCHwmdzlMmg==
+  dependencies:
+    copy-anything "^3.0.2"
+
 supports-color@^2.0.0:
 supports-color@^2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
@@ -21102,6 +21415,14 @@ upath@^2.0.1:
   resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
   resolved "https://registry.yarnpkg.com/upath/-/upath-2.0.1.tgz#50c73dea68d6f6b990f51d279ce6081665d61a8b"
   integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
   integrity sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==
 
 
+update-browserslist-db@^1.0.4:
+  version "1.0.5"
+  resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
+  integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
+  dependencies:
+    escalade "^3.1.1"
+    picocolors "^1.0.0"
+
 upper-case-first@^2.0.2:
 upper-case-first@^2.0.2:
   version "2.0.2"
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
   resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"