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

Merge pull request #8257 from weseek/dev/6.2.x

Release v6.2.4
Yuki Takei 2 лет назад
Родитель
Сommit
a4631cd8fb
46 измененных файлов с 1207 добавлено и 300 удалено
  1. 1 1
      apps/app/.env.test
  2. 4 2
      apps/app/package.json
  3. 3 2
      apps/app/public/static/locales/en_US/admin.json
  4. 3 2
      apps/app/public/static/locales/ja_JP/admin.json
  5. 4 3
      apps/app/public/static/locales/zh_CN/admin.json
  6. 10 0
      apps/app/src/client/services/AdminGeneralSecurityContainer.js
  7. 3 3
      apps/app/src/components/Admin/Customize/CustomizePresentationSetting.tsx
  8. 14 1
      apps/app/src/components/Admin/Security/SecuritySetting.jsx
  9. 5 3
      apps/app/src/components/Common/Dropdown/PageItemControl.tsx
  10. 6 6
      apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginCard.tsx
  11. 1 1
      apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginsExtensionPageContents.tsx
  12. 7 2
      apps/app/src/server/events/user.ts
  13. 2 2
      apps/app/src/server/middlewares/certify-shared-page-attachment/retrieve-valid-share-link.ts
  14. 2 1
      apps/app/src/server/models/config.ts
  15. 1 0
      apps/app/src/server/models/page.ts
  16. 3 9
      apps/app/src/server/routes/apiv3/pages.js
  17. 12 3
      apps/app/src/server/routes/apiv3/security-settings/index.js
  18. 5 10
      apps/app/src/server/routes/apiv3/users.js
  19. 20 1
      apps/app/src/server/routes/page.js
  20. 106 23
      apps/app/src/server/service/page.ts
  21. 0 0
      apps/app/test-with-vite/.eslintrc.cjs
  22. 5 0
      apps/app/test-with-vite/download-mongo-binary/index.spec.ts
  23. 15 0
      apps/app/test-with-vite/download-mongo-binary/vitest.config.ts
  24. 4 0
      apps/app/test-with-vite/package.json
  25. 1 1
      apps/app/test-with-vite/setup/mongoms.ts
  26. 1 1
      apps/slackbot-proxy/package.json
  27. 4 4
      package.json
  28. 1 1
      packages/core/package.json
  29. 0 1
      packages/core/src/interfaces/page.ts
  30. 20 2
      packages/core/src/utils/page-path-utils/index.spec.ts
  31. 1 1
      packages/hackmd/package.json
  32. 1 1
      packages/presentation/package.json
  33. 3 0
      packages/preset-templates/dist/marp-example/ja_JP/meta.json
  34. 326 0
      packages/preset-templates/dist/marp-example/ja_JP/template.md
  35. 3 0
      packages/preset-templates/dist/marp-example/zh_CN/meta.json
  36. 325 0
      packages/preset-templates/dist/marp-example/zh_CN/template.md
  37. 1 1
      packages/preset-templates/package.json
  38. 1 1
      packages/preset-themes/package.json
  39. 1 1
      packages/remark-attachment-refs/package.json
  40. 1 1
      packages/remark-drawio/package.json
  41. 1 1
      packages/remark-growi-directive/package.json
  42. 1 1
      packages/remark-lsx/package.json
  43. 1 1
      packages/slack/package.json
  44. 1 1
      packages/ui/package.json
  45. 5 0
      packages/ui/src/components/Attachment.tsx
  46. 272 205
      yarn.lock

+ 1 - 1
apps/app/.env.test

@@ -5,5 +5,5 @@
 ## > To prevent accidentally leaking env variables to the client, only variables prefixed with
 ## > To prevent accidentally leaking env variables to the client, only variables prefixed with
 ## > VITE_ are exposed to your Vite-processed code. e.g. for the following env variables:
 ## > VITE_ are exposed to your Vite-processed code. e.g. for the following env variables:
 ##
 ##
-VITE_MONGOMS_VERSION="6.0.6"
+VITE_MONGOMS_VERSION="6.0.9"
 # VITE_MONGOMS_DEBUG=1
 # VITE_MONGOMS_DEBUG=1

+ 4 - 2
apps/app/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/app",
   "name": "@growi/app",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "license": "MIT",
   "license": "MIT",
   "scripts": {
   "scripts": {
     "//// for production": "",
     "//// for production": "",
@@ -40,6 +40,7 @@
     "reg:run": "reg-suit run",
     "reg:run": "reg-suit run",
     "vitest:run": "vitest run config src --coverage",
     "vitest:run": "vitest run config src --coverage",
     "vitest:run:integ": "vitest run -c vitest.config.integ.ts src --coverage",
     "vitest:run:integ": "vitest run -c vitest.config.integ.ts src --coverage",
+    "previtest:run:integ": "vitest run -c test-with-vite/download-mongo-binary/vitest.config.ts test-with-vite/download-mongo-binary",
     "//// misc": "",
     "//// misc": "",
     "console": "yarn cross-env NODE_ENV=development yarn ts-node --experimental-repl-await src/server/console.js",
     "console": "yarn cross-env NODE_ENV=development yarn ts-node --experimental-repl-await src/server/console.js",
     "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
     "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
@@ -218,6 +219,7 @@
     "@types/express": "^4.17.11",
     "@types/express": "^4.17.11",
     "@types/jest": "^29.5.2",
     "@types/jest": "^29.5.2",
     "@types/react-scroll": "^1.8.4",
     "@types/react-scroll": "^1.8.4",
+    "@vitest/coverage-v8": "^0.34.6",
     "autoprefixer": "^9.0.0",
     "autoprefixer": "^9.0.0",
     "babel-loader": "^8.2.5",
     "babel-loader": "^8.2.5",
     "bootstrap": "^4.6.1",
     "bootstrap": "^4.6.1",
@@ -239,7 +241,7 @@
     "jquery": "^3.7.0",
     "jquery": "^3.7.0",
     "load-css-file": "^1.0.0",
     "load-css-file": "^1.0.0",
     "material-icons": "^1.11.3",
     "material-icons": "^1.11.3",
-    "mongodb-memory-server": "^8.15.1",
+    "mongodb-memory-server-core": "^9.1.1",
     "morgan": "^1.10.0",
     "morgan": "^1.10.0",
     "null-loader": "^4.0.1",
     "null-loader": "^4.0.1",
     "penpal": "^4.0.0",
     "penpal": "^4.0.0",

+ 3 - 2
apps/app/public/static/locales/en_US/admin.json

@@ -47,8 +47,9 @@
     "anyone": "Anyone",
     "anyone": "Anyone",
     "user_homepage_deletion": {
     "user_homepage_deletion": {
       "user_homepage_deletion": "User homepage deletion",
       "user_homepage_deletion": "User homepage deletion",
-      "enable_user_homepage_deletion": "Complete deletion of user homepage, when user deletion",
-      "desc": "When deleting a user, the user homepage and its sub pages are also completely deleted."
+      "enable_user_homepage_deletion": "Enable user homepage deletion",
+      "enable_force_delete_user_homepage_on_user_deletion": "When you delete a user, the user's homepage and all its sub pages will be completely deleted",
+      "desc": "You will be able to delete a deleted user's homepage."
     },
     },
     "session": "Session",
     "session": "Session",
     "max_age": "Max age (msec)",
     "max_age": "Max age (msec)",

+ 3 - 2
apps/app/public/static/locales/ja_JP/admin.json

@@ -55,8 +55,9 @@
     "anyone": "誰でも可能",
     "anyone": "誰でも可能",
     "user_homepage_deletion": {
     "user_homepage_deletion": {
       "user_homepage_deletion": "ユーザーホームページの削除",
       "user_homepage_deletion": "ユーザーホームページの削除",
-      "enable_user_homepage_deletion": "ユーザー削除時にユーザーホームページを完全削除する",
-      "desc": "ユーザーを削除する際に、ユーザーホームページとその配下のページも完全削除されます。"
+      "enable_user_homepage_deletion": "ユーザーホームページの削除を有効化",
+      "enable_force_delete_user_homepage_on_user_deletion": "ユーザーを削除したとき、ユーザーホームページとその配下のページを完全削除する",
+      "desc": "削除済みユーザーのユーザーホームページを削除できるようになります。"
     },
     },
     "session": "セッション",
     "session": "セッション",
     "max_age": "有効期間 (ミリ秒)",
     "max_age": "有効期間 (ミリ秒)",

+ 4 - 3
apps/app/public/static/locales/zh_CN/admin.json

@@ -54,9 +54,10 @@
 		"admin_and_author": "管理员|作者",
 		"admin_and_author": "管理员|作者",
 		"anyone": "任何人",
 		"anyone": "任何人",
     "user_homepage_deletion": {
     "user_homepage_deletion": {
-      "user_homepage_deletion": "删除用户页面",
-      "enable_user_homepage_deletion": "用户删除时,完全删除用户主页",
-      "desc": "删除用户时,用户主页及其下属页面也会被完全删除。"
+      "user_homepage_deletion": "删除用户主页",
+      "enable_user_homepage_deletion": "启用用户主页删除功能",
+      "enable_force_delete_user_homepage_on_user_deletion": "删除用户时,该用户的主页及其所有子页面将被完全删除",
+      "desc": "您可以删除已删除用户的主页。"
     },
     },
     "session": "会议",
     "session": "会议",
     "max_age": "有效期间  (msec)",
     "max_age": "有效期间  (msec)",

+ 10 - 0
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -39,6 +39,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       isShowRestrictedByOwner: false,
       isShowRestrictedByOwner: false,
       isShowRestrictedByGroup: false,
       isShowRestrictedByGroup: false,
       isUsersHomepageDeletionEnabled: false,
       isUsersHomepageDeletionEnabled: false,
+      isForceDeleteUserHomepageOnUserDeletion: false,
       isLocalEnabled: false,
       isLocalEnabled: false,
       isLdapEnabled: false,
       isLdapEnabled: false,
       isSamlEnabled: false,
       isSamlEnabled: false,
@@ -75,6 +76,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
       isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
       isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
       isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
       isUsersHomepageDeletionEnabled: generalSetting.isUsersHomepageDeletionEnabled,
       isUsersHomepageDeletionEnabled: generalSetting.isUsersHomepageDeletionEnabled,
+      isForceDeleteUserHomepageOnUserDeletion: generalSetting.isForceDeleteUserHomepageOnUserDeletion,
       sessionMaxAge: generalSetting.sessionMaxAge,
       sessionMaxAge: generalSetting.sessionMaxAge,
       wikiMode: generalSetting.wikiMode,
       wikiMode: generalSetting.wikiMode,
       disableLinkSharing: shareLinkSetting.disableLinkSharing,
       disableLinkSharing: shareLinkSetting.disableLinkSharing,
@@ -202,6 +204,13 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ isUsersHomepageDeletionEnabled: !this.state.isUsersHomepageDeletionEnabled });
     this.setState({ isUsersHomepageDeletionEnabled: !this.state.isUsersHomepageDeletionEnabled });
   }
   }
 
 
+  /**
+   * Switch isForceDeleteUserHomepageOnUserDeletion
+   */
+  switchIsForceDeleteUserHomepageOnUserDeletion() {
+    this.setState({ isForceDeleteUserHomepageOnUserDeletion: !this.state.isForceDeleteUserHomepageOnUserDeletion });
+  }
+
   /**
   /**
    * Update restrictGuestMode
    * Update restrictGuestMode
    * @memberOf AdminGeneralSecuritySContainer
    * @memberOf AdminGeneralSecuritySContainer
@@ -219,6 +228,7 @@ export default class AdminGeneralSecurityContainer extends Container {
       hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
       hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
       hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
       hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
       isUsersHomepageDeletionEnabled: this.state.isUsersHomepageDeletionEnabled,
       isUsersHomepageDeletionEnabled: this.state.isUsersHomepageDeletionEnabled,
+      isForceDeleteUserHomepageOnUserDeletion: this.state.isForceDeleteUserHomepageOnUserDeletion,
     };
     };
 
 
     requestParams = await removeNullPropertyFromObject(requestParams);
     requestParams = await removeNullPropertyFromObject(requestParams);

+ 3 - 3
apps/app/src/components/Admin/Customize/CustomizePresentationSetting.tsx

@@ -17,9 +17,8 @@ type Props = {
 const CustomizePresentationSetting = (props: Props): JSX.Element => {
 const CustomizePresentationSetting = (props: Props): JSX.Element => {
   const { adminCustomizeContainer } = props;
   const { adminCustomizeContainer } = props;
 
 
-  console.log(adminCustomizeContainer);
-
   const { t } = useTranslation();
   const { t } = useTranslation();
+
   const onClickSubmit = useCallback(async() => {
   const onClickSubmit = useCallback(async() => {
     try {
     try {
       await adminCustomizeContainer.updateCustomizePresentation();
       await adminCustomizeContainer.updateCustomizePresentation();
@@ -28,7 +27,8 @@ const CustomizePresentationSetting = (props: Props): JSX.Element => {
     catch (err) {
     catch (err) {
       toastError(err);
       toastError(err);
     }
     }
-  }, [adminCustomizeContainer]);
+  }, [adminCustomizeContainer, t]);
+
   return (
   return (
     <React.Fragment>
     <React.Fragment>
       <h2 className="admin-setting-header">{t('admin:customize_settings.custom_presentation')}</h2>
       <h2 className="admin-setting-header">{t('admin:customize_settings.custom_presentation')}</h2>

+ 14 - 1
apps/app/src/components/Admin/Security/SecuritySetting.jsx

@@ -468,8 +468,21 @@ class SecuritySetting extends React.Component {
                 {t('security_settings.user_homepage_deletion.enable_user_homepage_deletion')}
                 {t('security_settings.user_homepage_deletion.enable_user_homepage_deletion')}
               </label>
               </label>
             </div>
             </div>
+            <div className="custom-control custom-switch custom-checkbox-success mt-2">
+              <input
+                type="checkbox"
+                className="form-check-input"
+                id="is-force-delete-user-homepage-on-user-deletion"
+                checked={adminGeneralSecurityContainer.state.isForceDeleteUserHomepageOnUserDeletion}
+                onChange={() => { adminGeneralSecurityContainer.switchIsForceDeleteUserHomepageOnUserDeletion() }}
+                disabled={!adminGeneralSecurityContainer.state.isUsersHomepageDeletionEnabled}
+              />
+              <label className="form-check-label" htmlFor="is-force-delete-user-homepage-on-user-deletion">
+                {t('security_settings.user_homepage_deletion.enable_force_delete_user_homepage_on_user_deletion')}
+              </label>
+            </div>
             <p
             <p
-              className="form-text text-muted small"
+              className="form-text text-muted small mt-2"
               dangerouslySetInnerHTML={{ __html: t('security_settings.user_homepage_deletion.desc') }}
               dangerouslySetInnerHTML={{ __html: t('security_settings.user_homepage_deletion.desc') }}
             />
             />
           </div>
           </div>

+ 5 - 3
apps/app/src/components/Common/Dropdown/PageItemControl.tsx

@@ -86,7 +86,8 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
     if (onClickRenameMenuItem == null) {
     if (onClickRenameMenuItem == null) {
       return;
       return;
     }
     }
-    if (!pageInfo?.isMovable) {
+
+    if (!pageInfo?.isDeletable) {
       logger.warn('This page could not be renamed.');
       logger.warn('This page could not be renamed.');
       return;
       return;
     }
     }
@@ -177,9 +178,10 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
         ) }
         ) }
 
 
         {/* Move/Rename */}
         {/* Move/Rename */}
-        { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && !isReadOnlyUser && pageInfo.isMovable && (
+        { !forceHideMenuItems?.includes(MenuItemType.RENAME) && isEnableActions && !isReadOnlyUser && (
           <DropdownItem
           <DropdownItem
             onClick={renameItemClickedHandler}
             onClick={renameItemClickedHandler}
+            disabled={!pageInfo.isDeletable}
             data-testid="open-page-move-rename-modal-btn"
             data-testid="open-page-move-rename-modal-btn"
             className="grw-page-control-dropdown-item"
             className="grw-page-control-dropdown-item"
           >
           >
@@ -231,7 +233,7 @@ const PageItemControlDropdownMenu = React.memo((props: DropdownMenuProps): JSX.E
 
 
         {/* divider */}
         {/* divider */}
         {/* Delete */}
         {/* Delete */}
-        { !forceHideMenuItems?.includes(MenuItemType.DELETE) && isEnableActions && !isReadOnlyUser && pageInfo.isMovable && (
+        { !forceHideMenuItems?.includes(MenuItemType.DELETE) && isEnableActions && !isReadOnlyUser && (
           <>
           <>
             { showDeviderBeforeDelete && <DropdownItem divider /> }
             { showDeviderBeforeDelete && <DropdownItem divider /> }
             <DropdownItem
             <DropdownItem

+ 6 - 6
apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginCard.tsx

@@ -12,7 +12,7 @@ type Props = {
   id: string,
   id: string,
   name: string,
   name: string,
   url: string,
   url: string,
-  isEnalbed: boolean,
+  isEnabled: boolean,
   desc?: string,
   desc?: string,
   onDelete: () => void,
   onDelete: () => void,
 }
 }
@@ -20,27 +20,27 @@ type Props = {
 export const PluginCard = (props: Props): JSX.Element => {
 export const PluginCard = (props: Props): JSX.Element => {
 
 
   const {
   const {
-    id, name, url, isEnalbed, desc,
+    id, name, url, isEnabled, desc,
   } = props;
   } = props;
 
 
   const { t } = useTranslation('admin');
   const { t } = useTranslation('admin');
 
 
   const PluginCardButton = (): JSX.Element => {
   const PluginCardButton = (): JSX.Element => {
-    const [isEnabled, setState] = useState<boolean>(isEnalbed);
+    const [_isEnabled, setIsEnabled] = useState<boolean>(isEnabled);
 
 
     const onChangeHandler = async() => {
     const onChangeHandler = async() => {
       try {
       try {
-        if (isEnabled) {
+        if (_isEnabled) {
           const reqUrl = `/plugins/${id}/deactivate`;
           const reqUrl = `/plugins/${id}/deactivate`;
           const res = await apiv3Put(reqUrl);
           const res = await apiv3Put(reqUrl);
-          setState(!isEnabled);
+          setIsEnabled(!_isEnabled);
           const pluginName = res.data.pluginName;
           const pluginName = res.data.pluginName;
           toastSuccess(t('toaster.deactivate_plugin_success', { pluginName }));
           toastSuccess(t('toaster.deactivate_plugin_success', { pluginName }));
         }
         }
         else {
         else {
           const reqUrl = `/plugins/${id}/activate`;
           const reqUrl = `/plugins/${id}/activate`;
           const res = await apiv3Put(reqUrl);
           const res = await apiv3Put(reqUrl);
-          setState(!isEnabled);
+          setIsEnabled(!_isEnabled);
           const pluginName = res.data.pluginName;
           const pluginName = res.data.pluginName;
           toastSuccess(t('toaster.activate_plugin_success', { pluginName }));
           toastSuccess(t('toaster.activate_plugin_success', { pluginName }));
         }
         }

+ 1 - 1
apps/app/src/features/growi-plugin/client/components/Admin/PluginsExtensionPageContents/PluginsExtensionPageContents.tsx

@@ -54,7 +54,7 @@ export const PluginsExtensionPageContents = (): JSX.Element => {
                     id={plugin._id}
                     id={plugin._id}
                     name={plugin.meta.name}
                     name={plugin.meta.name}
                     url={plugin.origin.url}
                     url={plugin.origin.url}
-                    isEnalbed={plugin.isEnabled}
+                    isEnabled={plugin.isEnabled}
                     desc={plugin.meta.desc}
                     desc={plugin.meta.desc}
                     onDelete={() => openPluginDeleteModal(plugin)}
                     onDelete={() => openPluginDeleteModal(plugin)}
                   />
                   />

+ 7 - 2
apps/app/src/server/events/user.ts

@@ -1,8 +1,10 @@
 import EventEmitter from 'events';
 import EventEmitter from 'events';
 
 
-import type { IUserHasId } from '@growi/core';
+import type { IPage, IUserHasId } from '@growi/core';
 import { pagePathUtils } from '@growi/core/dist/utils';
 import { pagePathUtils } from '@growi/core/dist/utils';
+import mongoose from 'mongoose';
 
 
+import type { PageModel } from '~/server/models/page';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:events:user');
 const logger = loggerFactory('growi:events:user');
@@ -23,11 +25,14 @@ class UserEvent extends EventEmitter {
       return;
       return;
     }
     }
 
 
-    const Page = this.crowi.model('Page');
+    const Page = mongoose.model<IPage, PageModel>('Page');
     const userHomepagePath = pagePathUtils.userHomepagePath(user);
     const userHomepagePath = pagePathUtils.userHomepagePath(user);
 
 
     let page = await Page.findByPath(userHomepagePath, true);
     let page = await Page.findByPath(userHomepagePath, true);
 
 
+    // TODO: Make it more type safe
+    // Since the type of page.creator is 'any', we resort to the following comparison,
+    // checking if page.creator.toString() is not equal to user._id.toString(). Our code covers null, string, or object types.
     if (page != null && page.creator != null && page.creator.toString() !== user._id.toString()) {
     if (page != null && page.creator != null && page.creator.toString() !== user._id.toString()) {
       await this.crowi.pageService.deleteCompletelyUserHomeBySystem(userHomepagePath);
       await this.crowi.pageService.deleteCompletelyUserHomeBySystem(userHomepagePath);
       page = null;
       page = null;

+ 2 - 2
apps/app/src/server/middlewares/certify-shared-page-attachment/retrieve-valid-share-link.ts

@@ -15,9 +15,9 @@ export const retrieveValidShareLinkByReferer = async(referer: ValidReferer): Pro
     return null;
     return null;
   }
   }
 
 
-  const shareLinkId = referer;
+  const { shareLinkId } = referer;
   const shareLink = await ShareLink.findOne({
   const shareLink = await ShareLink.findOne({
-    id: shareLinkId,
+    _id: shareLinkId,
   });
   });
   if (shareLink == null || shareLink.isExpired()) {
   if (shareLink == null || shareLink.isExpired()) {
     logger.info(`ShareLink ('${shareLinkId}') is not found or has already expired.`);
     logger.info(`ShareLink ('${shareLinkId}') is not found or has already expired.`);

+ 2 - 1
apps/app/src/server/models/config.ts

@@ -71,7 +71,8 @@ export const defaultCrowiConfigs: { [key: string]: any } = {
   'security:pageRecursiveDeletionAuthority' : undefined,
   'security:pageRecursiveDeletionAuthority' : undefined,
   'security:pageRecursiveCompleteDeletionAuthority' : undefined,
   'security:pageRecursiveCompleteDeletionAuthority' : undefined,
   'security:disableLinkSharing' : false,
   'security:disableLinkSharing' : false,
-  'security:isUsersHomepageDeletionEnabled': false,
+  'security:user-homepage-deletion:isEnabled': false,
+  'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion': false,
 
 
   'security:passport-local:isEnabled' : true,
   'security:passport-local:isEnabled' : true,
   'security:passport-ldap:isEnabled' : false,
   'security:passport-ldap:isEnabled' : false,

+ 1 - 0
apps/app/src/server/models/page.ts

@@ -65,6 +65,7 @@ export interface PageModel extends Model<PageDocument> {
   generateGrantCondition(
   generateGrantCondition(
     user, userGroups, includeAnyoneWithTheLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
     user, userGroups, includeAnyoneWithTheLink?: boolean, showPagesRestrictedByOwner?: boolean, showPagesRestrictedByGroup?: boolean,
   ): { $or: any[] }
   ): { $or: any[] }
+  removeLeafEmptyPagesRecursively(pageId: ObjectIdLike): Promise<void>
 
 
   PageQueryBuilder: typeof PageQueryBuilder
   PageQueryBuilder: typeof PageQueryBuilder
 
 

+ 3 - 9
apps/app/src/server/routes/apiv3/pages.js

@@ -906,18 +906,12 @@ module.exports = (crowi) => {
     }
     }
 
 
     let pagesCanBeDeleted;
     let pagesCanBeDeleted;
-    /*
-     * Delete Completely
-     */
     if (isCompletely) {
     if (isCompletely) {
-      pagesCanBeDeleted = crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user, isRecursively);
+      pagesCanBeDeleted = await crowi.pageService.filterPagesByCanDeleteCompletely(pagesToDelete, req.user, isRecursively);
     }
     }
-    /*
-     * Trash
-     */
     else {
     else {
-      pagesCanBeDeleted = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
-      pagesCanBeDeleted = crowi.pageService.filterPagesByCanDelete(pagesToDelete, req.user, isRecursively);
+      const filteredPages = pagesToDelete.filter(p => p.isEmpty || p.isUpdatable(pageIdToRevisionIdMap[p._id].toString()));
+      pagesCanBeDeleted = await crowi.pageService.filterPagesByCanDelete(filteredPages, req.user, isRecursively);
     }
     }
 
 
     if (pagesCanBeDeleted.length === 0) {
     if (pagesCanBeDeleted.length === 0) {

+ 12 - 3
apps/app/src/server/routes/apiv3/security-settings/index.js

@@ -31,6 +31,7 @@ const validator = {
     body('hideRestrictedByOwner').if(value => value != null).isBoolean(),
     body('hideRestrictedByOwner').if(value => value != null).isBoolean(),
     body('hideRestrictedByGroup').if(value => value != null).isBoolean(),
     body('hideRestrictedByGroup').if(value => value != null).isBoolean(),
     body('isUsersHomepageDeletionEnabled').if(value => value != null).isBoolean(),
     body('isUsersHomepageDeletionEnabled').if(value => value != null).isBoolean(),
+    body('isForceDeleteUserHomepageOnUserDeletion').if(value => value != null).isBoolean(),
   ],
   ],
   shareLinkSetting: [
   shareLinkSetting: [
     body('disableLinkSharing').if(value => value != null).isBoolean(),
     body('disableLinkSharing').if(value => value != null).isBoolean(),
@@ -358,7 +359,9 @@ module.exports = (crowi) => {
         pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
         pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
         hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
         hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
         hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
         hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
-        isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled'),
+        isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled'),
+        isForceDeleteUserHomepageOnUserDeletion:
+        await configManager.getConfig('crowi', 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion'),
         wikiMode: await configManager.getConfig('crowi', 'security:wikiMode'),
         wikiMode: await configManager.getConfig('crowi', 'security:wikiMode'),
         sessionMaxAge: await configManager.getConfig('crowi', 'security:sessionMaxAge'),
         sessionMaxAge: await configManager.getConfig('crowi', 'security:sessionMaxAge'),
       },
       },
@@ -626,7 +629,11 @@ module.exports = (crowi) => {
       'security:pageRecursiveCompleteDeletionAuthority': req.body.pageRecursiveCompleteDeletionAuthority,
       'security:pageRecursiveCompleteDeletionAuthority': req.body.pageRecursiveCompleteDeletionAuthority,
       'security:list-policy:hideRestrictedByOwner': req.body.hideRestrictedByOwner,
       'security:list-policy:hideRestrictedByOwner': req.body.hideRestrictedByOwner,
       'security:list-policy:hideRestrictedByGroup': req.body.hideRestrictedByGroup,
       'security:list-policy:hideRestrictedByGroup': req.body.hideRestrictedByGroup,
-      'security:isUsersHomepageDeletionEnabled': req.body.isUsersHomepageDeletionEnabled,
+      'security:user-homepage-deletion:isEnabled': req.body.isUsersHomepageDeletionEnabled,
+      // Validate user-homepage-deletion config
+      'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion': req.body.isUsersHomepageDeletionEnabled
+        ? req.body.isForceDeleteUserHomepageOnUserDeletion
+        : false,
     };
     };
 
 
     // Validate delete config
     // Validate delete config
@@ -655,7 +662,9 @@ module.exports = (crowi) => {
         pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
         pageRecursiveCompleteDeletionAuthority: await configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority'),
         hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
         hideRestrictedByOwner: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByOwner'),
         hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
         hideRestrictedByGroup: await configManager.getConfig('crowi', 'security:list-policy:hideRestrictedByGroup'),
-        isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled'),
+        isUsersHomepageDeletionEnabled: await configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled'),
+        isForceDeleteUserHomepageOnUserDeletion:
+        await configManager.getConfig('crowi', 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion'),
       };
       };
 
 
       const parameters = { action: SupportedAction.ACTION_ADMIN_SECURITY_SETTINGS_UPDATE };
       const parameters = { action: SupportedAction.ACTION_ADMIN_SECURITY_SETTINGS_UPDATE };

+ 5 - 10
apps/app/src/server/routes/apiv3/users.js

@@ -778,7 +778,7 @@ module.exports = (crowi) => {
    *        tags: [Users]
    *        tags: [Users]
    *        operationId: removeUser
    *        operationId: removeUser
    *        summary: /users/{id}/remove
    *        summary: /users/{id}/remove
-   *        description: Delete user and if isUsersHomepageDeletionEnabled delete user homepage and subpages
+   *        description: Delete user
    *        parameters:
    *        parameters:
    *          - name: id
    *          - name: id
    *            in: path
    *            in: path
@@ -788,7 +788,7 @@ module.exports = (crowi) => {
    *              type: string
    *              type: string
    *        responses:
    *        responses:
    *          200:
    *          200:
-   *            description: Deleting user success and if isUsersHomepageDeletionEnabled delete user homepage and subpages success
+   *            description: Deleting user success
    *            content:
    *            content:
    *              application/json:
    *              application/json:
    *                schema:
    *                schema:
@@ -796,16 +796,11 @@ module.exports = (crowi) => {
    *                    user:
    *                    user:
    *                      type: object
    *                      type: object
    *                      description: data of deleted user
    *                      description: data of deleted user
-   *                    userHomepagePath:
-   *                      type: string
-   *                      description: a user homepage path
-   *                    isUsersHomepageDeletionEnabled:
-   *                      type: boolean
-   *                      description: is users homepage deletion enabled
    */
    */
   router.delete('/:id/remove', loginRequiredStrictly, adminRequired, certifyUserOperationOtherThenYourOwn, addActivity, async(req, res) => {
   router.delete('/:id/remove', loginRequiredStrictly, adminRequired, certifyUserOperationOtherThenYourOwn, addActivity, async(req, res) => {
     const { id } = req.params;
     const { id } = req.params;
-    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:isUsersHomepageDeletionEnabled');
+    const isUsersHomepageDeletionEnabled = configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled');
+    const isForceDeleteUserHomepageOnUserDeletion = configManager.getConfig('crowi', 'security:user-homepage-deletion:isForceDeleteUserHomepageOnUserDeletion');
 
 
     try {
     try {
       const user = await User.findById(id);
       const user = await User.findById(id);
@@ -821,7 +816,7 @@ module.exports = (crowi) => {
 
 
       activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_USERS_REMOVE });
       activityEvent.emit('update', res.locals.activity._id, { action: SupportedAction.ACTION_ADMIN_USERS_REMOVE });
 
 
-      if (isUsersHomepageDeletionEnabled) {
+      if (isUsersHomepageDeletionEnabled && isForceDeleteUserHomepageOnUserDeletion) {
         crowi.pageService.deleteCompletelyUserHomeBySystem(homepagePath);
         crowi.pageService.deleteCompletelyUserHomeBySystem(homepagePath);
       }
       }
 
 

+ 20 - 1
apps/app/src/server/routes/page.js

@@ -136,7 +136,7 @@ module.exports = function(crowi, app) {
   const debug = require('debug')('growi:routes:page');
   const debug = require('debug')('growi:routes:page');
   const logger = loggerFactory('growi:routes:page');
   const logger = loggerFactory('growi:routes:page');
 
 
-  const { pathUtils } = require('@growi/core/dist/utils');
+  const { pathUtils, pagePathUtils } = require('@growi/core/dist/utils');
 
 
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
   const User = crowi.model('User');
   const User = crowi.model('User');
@@ -765,6 +765,16 @@ module.exports = function(crowi, app) {
         if (!crowi.pageService.canDeleteCompletely(page.path, creator, req.user, isRecursively)) {
         if (!crowi.pageService.canDeleteCompletely(page.path, creator, req.user, isRecursively)) {
           return res.json(ApiResponse.error('You can not delete this page completely', 'user_not_admin'));
           return res.json(ApiResponse.error('You can not delete this page completely', 'user_not_admin'));
         }
         }
+
+        if (pagePathUtils.isUsersHomepage(page.path)) {
+          if (!crowi.pageService.canDeleteUserHomepageByConfig()) {
+            return res.json(ApiResponse.error('Could not delete user homepage'));
+          }
+          if (!await crowi.pageService.isUsersHomepageOwnerAbsent(page.path)) {
+            return res.json(ApiResponse.error('Could not delete user homepage'));
+          }
+        }
+
         await crowi.pageService.deleteCompletely(page, req.user, options, isRecursively, false, activityParameters);
         await crowi.pageService.deleteCompletely(page, req.user, options, isRecursively, false, activityParameters);
       }
       }
       else {
       else {
@@ -782,6 +792,15 @@ module.exports = function(crowi, app) {
           return res.json(ApiResponse.error('You can not delete this page', 'user_not_admin'));
           return res.json(ApiResponse.error('You can not delete this page', 'user_not_admin'));
         }
         }
 
 
+        if (pagePathUtils.isUsersHomepage(page.path)) {
+          if (!crowi.pageService.canDeleteUserHomepageByConfig()) {
+            return res.json(ApiResponse.error('Could not delete user homepage'));
+          }
+          if (!await crowi.pageService.isUsersHomepageOwnerAbsent(page.path)) {
+            return res.json(ApiResponse.error('Could not delete user homepage'));
+          }
+        }
+
         await crowi.pageService.deletePage(page, req.user, options, isRecursively, activityParameters);
         await crowi.pageService.deletePage(page, req.user, options, isRecursively, activityParameters);
       }
       }
     }
     }

+ 106 - 23
apps/app/src/server/service/page.ts

@@ -2,14 +2,13 @@ import pathlib from 'path';
 import { Readable, Writable } from 'stream';
 import { Readable, Writable } from 'stream';
 
 
 import type {
 import type {
-  Ref, HasObjectId, IUserHasId,
+  Ref, HasObjectId, IUserHasId, IUser,
   IPage, IPageInfo, IPageInfoAll, IPageInfoForEntity, IPageWithMeta,
   IPage, IPageInfo, IPageInfoAll, IPageInfoForEntity, IPageWithMeta,
 } from '@growi/core';
 } from '@growi/core';
-import { PageGrant, PageStatus } from '@growi/core';
+import { PageGrant, PageStatus, getIdForRef } from '@growi/core';
 import {
 import {
   pagePathUtils, pathUtils,
   pagePathUtils, pathUtils,
 } from '@growi/core/dist/utils';
 } from '@growi/core/dist/utils';
-import { collectAncestorPaths } from '@growi/core/dist/utils/page-path-utils';
 import escapeStringRegexp from 'escape-string-regexp';
 import escapeStringRegexp from 'escape-string-regexp';
 import mongoose, { ObjectId, Cursor } from 'mongoose';
 import mongoose, { ObjectId, Cursor } from 'mongoose';
 import streamToPromise from 'stream-to-promise';
 import streamToPromise from 'stream-to-promise';
@@ -40,12 +39,14 @@ import ShareLink from '../models/share-link';
 import Subscription from '../models/subscription';
 import Subscription from '../models/subscription';
 import { V5ConversionError } from '../models/vo/v5-conversion-error';
 import { V5ConversionError } from '../models/vo/v5-conversion-error';
 
 
+import { configManager } from './config-manager';
+
 const debug = require('debug')('growi:services:page');
 const debug = require('debug')('growi:services:page');
 
 
 const logger = loggerFactory('growi:services:page');
 const logger = loggerFactory('growi:services:page');
 const {
 const {
-  isTrashPage, isTopPage, omitDuplicateAreaPageFromPages,
-  isMovablePage, canMoveByPath, isUsersProtectedPages, hasSlash, generateChildrenRegExp,
+  isTrashPage, isTopPage, omitDuplicateAreaPageFromPages, getUsernameByPath, collectAncestorPaths,
+  canMoveByPath, isUsersTopPage, isMovablePage, isUsersHomepage, hasSlash, generateChildrenRegExp,
 } = pagePathUtils;
 } = pagePathUtils;
 
 
 const { addTrailingSlash } = pathUtils;
 const { addTrailingSlash } = pathUtils;
@@ -152,6 +153,8 @@ class PageService {
 
 
     // init
     // init
     this.initPageEvent();
     this.initPageEvent();
+    this.canDeleteCompletely = this.canDeleteCompletely.bind(this);
+    this.canDelete = this.canDelete.bind(this);
   }
   }
 
 
   private initPageEvent() {
   private initPageEvent() {
@@ -164,7 +167,7 @@ class PageService {
   }
   }
 
 
   canDeleteCompletely(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
   canDeleteCompletely(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
-    if (operator == null || isTopPage(path) || isUsersProtectedPages(path)) return false;
+    if (operator == null || isTopPage(path) || isUsersTopPage(path)) return false;
 
 
     const pageCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority');
     const pageCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageCompleteDeletionAuthority');
     const pageRecursiveCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority');
     const pageRecursiveCompleteDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveCompleteDeletionAuthority');
@@ -175,7 +178,7 @@ class PageService {
   }
   }
 
 
   canDelete(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
   canDelete(path: string, creatorId: ObjectIdLike, operator: any | null, isRecursively: boolean): boolean {
-    if (operator == null || isUsersProtectedPages(path) || isTopPage(path)) return false;
+    if (operator == null || isTopPage(path) || isUsersTopPage(path)) return false;
 
 
     const pageDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageDeletionAuthority');
     const pageDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageDeletionAuthority');
     const pageRecursiveDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority');
     const pageRecursiveDeletionAuthority = this.crowi.configManager.getConfig('crowi', 'security:pageRecursiveDeletionAuthority');
@@ -185,6 +188,20 @@ class PageService {
     return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
     return this.canDeleteLogic(creatorId, operator, isRecursively, singleAuthority, recursiveAuthority);
   }
   }
 
 
+  canDeleteUserHomepageByConfig(): boolean {
+    return configManager.getConfig('crowi', 'security:user-homepage-deletion:isEnabled') ?? false;
+  }
+
+  async isUsersHomepageOwnerAbsent(path: string): Promise<boolean> {
+    const User = mongoose.model('User');
+    const username = getUsernameByPath(path);
+    if (username == null) {
+      throw new Error('Cannot found username by path');
+    }
+    const ownerExists = await User.exists({ username });
+    return ownerExists === null;
+  }
+
   private canDeleteLogic(
   private canDeleteLogic(
       creatorId: ObjectIdLike,
       creatorId: ObjectIdLike,
       operator,
       operator,
@@ -217,12 +234,58 @@ class PageService {
     return false;
     return false;
   }
   }
 
 
-  filterPagesByCanDeleteCompletely(pages, user, isRecursively: boolean) {
-    return pages.filter(p => p.isEmpty || this.canDeleteCompletely(p.path, p.creator, user, isRecursively));
+  private async getAbsenseUserHomeList(pages: PageDocument[]): Promise<string[]> {
+    const userHomepages = pages.filter(p => isUsersHomepage(p.path));
+
+    const User = mongoose.model<IUser>('User');
+    const usernames = userHomepages
+      .map(page => getUsernameByPath(page.path))
+      // see: https://zenn.dev/kimuson/articles/filter_safety_type_guard
+      .filter((username): username is Exclude<typeof username, null> => username !== null);
+    const existingUsernames = await User.distinct<string>('username', { username: { $in: usernames } });
+
+    return userHomepages.filter((page) => {
+      const username = getUsernameByPath(page.path);
+      if (username == null) {
+        throw new Error('Cannot found username by path');
+      }
+      return !existingUsernames.includes(username);
+    }).map(p => p.path);
+  }
+
+  private async filterPages(
+      pages: PageDocument[],
+      user: IUserHasId,
+      isRecursively: boolean,
+      canDeleteFunction: (path: string, creatorId: ObjectIdLike, operator: any, isRecursively: boolean) => boolean,
+  ): Promise<PageDocument[]> {
+    const filteredPages = pages.filter(p => p.isEmpty || canDeleteFunction(p.path, p.creator, user, isRecursively));
+
+    if (!this.canDeleteUserHomepageByConfig()) {
+      return filteredPages.filter(p => !isUsersHomepage(p.path));
+    }
+
+    // Confirmation of deletion of user homepages is an asynchronous process,
+    // so it is processed separately for performance optimization.
+    const absenseUserHomeList = await this.getAbsenseUserHomeList(filteredPages);
+
+    const excludeActiveUserHomepage = (path: string) => {
+      if (!isUsersHomepage(path)) {
+        return true;
+      }
+      return absenseUserHomeList.includes(path);
+    };
+
+    return filteredPages
+      .filter(p => excludeActiveUserHomepage(p.path));
+  }
+
+  async filterPagesByCanDeleteCompletely(pages: PageDocument[], user: IUserHasId, isRecursively: boolean): Promise<PageDocument[]> {
+    return this.filterPages(pages, user, isRecursively, this.canDeleteCompletely);
   }
   }
 
 
-  filterPagesByCanDelete(pages, user, isRecursively: boolean) {
-    return pages.filter(p => p.isEmpty || this.canDelete(p.path, p.creator, user, isRecursively));
+  async filterPagesByCanDelete(pages: PageDocument[], user: IUserHasId, isRecursively: boolean): Promise<PageDocument[]> {
+    return this.filterPages(pages, user, isRecursively, this.canDelete);
   }
   }
 
 
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
@@ -250,7 +313,6 @@ class PageService {
         meta: {
         meta: {
           isV5Compatible: isTopPage(page.path) || page.parent != null,
           isV5Compatible: isTopPage(page.path) || page.parent != null,
           isEmpty: page.isEmpty,
           isEmpty: page.isEmpty,
-          isMovable: false,
           isDeletable: false,
           isDeletable: false,
           isAbleToDeleteCompletely: false,
           isAbleToDeleteCompletely: false,
           isRevertible: false,
           isRevertible: false,
@@ -1385,10 +1447,19 @@ class PageService {
       throw new Error('This method does NOT support deleting trashed pages.');
       throw new Error('This method does NOT support deleting trashed pages.');
     }
     }
 
 
-    if (!isMovablePage(page.path)) {
+    if (isTopPage(page.path) || isUsersTopPage(page.path)) {
       throw new Error('Page is not deletable.');
       throw new Error('Page is not deletable.');
     }
     }
 
 
+    if (pagePathUtils.isUsersHomepage(page.path)) {
+      if (!this.crowi.pageService.canDeleteUserHomepageByConfig()) {
+        throw new Error('User Homepage is not deletable.');
+      }
+      if (!await this.crowi.pageService.isUsersHomepageOwnerAbsent(page.path)) {
+        throw new Error('User Homepage is not deletable.');
+      }
+    }
+
     const newPath = Page.getDeletedPageName(page.path);
     const newPath = Page.getDeletedPageName(page.path);
 
 
     const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, newPath);
     const canOperate = await this.crowi.pageOperationService.canOperate(isRecursively, page.path, newPath);
@@ -1972,24 +2043,38 @@ class PageService {
    * @throws {Error} - If an error occurs during the deletion process.
    * @throws {Error} - If an error occurs during the deletion process.
    */
    */
   async deleteCompletelyUserHomeBySystem(userHomepagePath: string): Promise<void> {
   async deleteCompletelyUserHomeBySystem(userHomepagePath: string): Promise<void> {
-    const Page = this.crowi.model('Page');
+    if (!isUsersHomepage(userHomepagePath)) {
+      const msg = 'input value is not user homepage path.';
+      logger.error(msg);
+      throw new Error(msg);
+    }
+
+    const Page = mongoose.model<IPage, PageModel>('Page');
     const userHomepage = await Page.findByPath(userHomepagePath, true);
     const userHomepage = await Page.findByPath(userHomepagePath, true);
 
 
     if (userHomepage == null) {
     if (userHomepage == null) {
-      logger.error('user homepage is not found.');
-      return;
+      const msg = 'user homepage is not found.';
+      logger.error(msg);
+      throw new Error(msg);
+    }
+
+    if (userHomepage.parent == null) {
+      const msg = 'user homepage parent is not found.';
+      logger.error(msg);
+      throw new Error(msg);
     }
     }
 
 
     const shouldUseV4Process = this.shouldUseV4Process(userHomepage);
     const shouldUseV4Process = this.shouldUseV4Process(userHomepage);
 
 
     const ids = [userHomepage._id];
     const ids = [userHomepage._id];
     const paths = [userHomepage.path];
     const paths = [userHomepage.path];
+    const parentId = getIdForRef(userHomepage.parent);
 
 
     try {
     try {
       if (!shouldUseV4Process) {
       if (!shouldUseV4Process) {
         // Ensure consistency of ancestors
         // Ensure consistency of ancestors
         const inc = userHomepage.isEmpty ? -userHomepage.descendantCount : -(userHomepage.descendantCount + 1);
         const inc = userHomepage.isEmpty ? -userHomepage.descendantCount : -(userHomepage.descendantCount + 1);
-        await this.updateDescendantCountOfAncestors(userHomepage.parent, inc, true);
+        await this.updateDescendantCountOfAncestors(parentId, inc, true);
       }
       }
 
 
       // Delete the user's homepage
       // Delete the user's homepage
@@ -1997,7 +2082,7 @@ class PageService {
 
 
       if (!shouldUseV4Process) {
       if (!shouldUseV4Process) {
         // Remove leaf empty pages
         // Remove leaf empty pages
-        await Page.removeLeafEmptyPagesRecursively(userHomepage.parent);
+        await Page.removeLeafEmptyPagesRecursively(parentId);
       }
       }
 
 
       if (!userHomepage.isEmpty) {
       if (!userHomepage.isEmpty) {
@@ -2010,7 +2095,7 @@ class PageService {
       // Find descendant pages with system deletion condition
       // Find descendant pages with system deletion condition
       const builder = new PageQueryBuilder(Page.find(), true)
       const builder = new PageQueryBuilder(Page.find(), true)
         .addConditionForSystemDeletion()
         .addConditionForSystemDeletion()
-        .addConditionToListOnlyDescendants(userHomepage.path);
+        .addConditionToListOnlyDescendants(userHomepage.path, {});
 
 
       // Stream processing to delete descendant pages
       // Stream processing to delete descendant pages
       // ────────┤ start │─────────
       // ────────┤ start │─────────
@@ -2384,13 +2469,12 @@ class PageService {
   }
   }
 
 
   constructBasicPageInfo(page: PageDocument, isGuestUser?: boolean): IPageInfo | IPageInfoForEntity {
   constructBasicPageInfo(page: PageDocument, isGuestUser?: boolean): IPageInfo | IPageInfoForEntity {
-    const isMovable = isGuestUser ? false : isMovablePage(page.path);
+    const isDeletable = !(isGuestUser || isTopPage(page.path) || isUsersTopPage(page.path));
 
 
     if (page.isEmpty) {
     if (page.isEmpty) {
       return {
       return {
         isV5Compatible: true,
         isV5Compatible: true,
         isEmpty: true,
         isEmpty: true,
-        isMovable,
         isDeletable: false,
         isDeletable: false,
         isAbleToDeleteCompletely: false,
         isAbleToDeleteCompletely: false,
         isRevertible: false,
         isRevertible: false,
@@ -2407,8 +2491,7 @@ class PageService {
       likerIds: this.extractStringIds(likers),
       likerIds: this.extractStringIds(likers),
       seenUserIds: this.extractStringIds(seenUsers),
       seenUserIds: this.extractStringIds(seenUsers),
       sumOfSeenUsers: page.seenUsers.length,
       sumOfSeenUsers: page.seenUsers.length,
-      isMovable,
-      isDeletable: isMovable,
+      isDeletable,
       isAbleToDeleteCompletely: false,
       isAbleToDeleteCompletely: false,
       isRevertible: isTrashPage(page.path),
       isRevertible: isTrashPage(page.path),
       contentAge: page.getContentAge(),
       contentAge: page.getContentAge(),

+ 0 - 0
apps/app/test-with-vite/.eslintrc.js → apps/app/test-with-vite/.eslintrc.cjs


+ 5 - 0
apps/app/test-with-vite/download-mongo-binary/index.spec.ts

@@ -0,0 +1,5 @@
+describe('Download mongo-binary', () => {
+  it('should be success', () => {
+    expect(true).toBeTruthy();
+  });
+});

+ 15 - 0
apps/app/test-with-vite/download-mongo-binary/vitest.config.ts

@@ -0,0 +1,15 @@
+import { defineConfig, mergeConfig } from 'vitest/config';
+
+import configShared from '../../vitest.config';
+
+export default mergeConfig(
+  configShared,
+  defineConfig({
+    test: {
+      hookTimeout: 60000, // increased for downloading MongoDB binary file
+      setupFiles: [
+        './test-with-vite/setup/mongoms.ts',
+      ],
+    },
+  }),
+);

+ 4 - 0
apps/app/test-with-vite/package.json

@@ -0,0 +1,4 @@
+{
+  "$schame": "http://json-schema.org/schema",
+  "type": "module"
+}

+ 1 - 1
apps/app/test-with-vite/setup/mongoms.ts

@@ -1,4 +1,4 @@
-import { MongoMemoryServer } from 'mongodb-memory-server';
+import { MongoMemoryServer } from 'mongodb-memory-server-core';
 import mongoose from 'mongoose';
 import mongoose from 'mongoose';
 
 
 import { mongoOptions } from '~/server/util/mongoose-utils';
 import { mongoOptions } from '~/server/util/mongoose-utils';

+ 1 - 1
apps/slackbot-proxy/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/slackbot-proxy",
   "name": "@growi/slackbot-proxy",
-  "version": "6.2.3-slackbot-proxy.0",
+  "version": "6.2.4-slackbot-proxy.0",
   "license": "MIT",
   "license": "MIT",
   "scripts": {
   "scripts": {
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",
     "build": "yarn tsc && tsc-alias -p tsconfig.build.json",

+ 4 - 4
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "growi",
   "name": "growi",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "Team collaboration software using markdown",
   "description": "Team collaboration software using markdown",
   "tags": [
   "tags": [
     "wiki",
     "wiki",
@@ -60,7 +60,7 @@
     "@typescript-eslint/eslint-plugin": "^5.59.7",
     "@typescript-eslint/eslint-plugin": "^5.59.7",
     "@typescript-eslint/parser": "^5.59.7",
     "@typescript-eslint/parser": "^5.59.7",
     "@vitejs/plugin-react": "^4.0.3",
     "@vitejs/plugin-react": "^4.0.3",
-    "@vitest/coverage-c8": "^0.31.1",
+    "@vitest/coverage-v8": "^0.34.6",
     "@vitest/ui": "^0.31.1",
     "@vitest/ui": "^0.31.1",
     "cypress": "^13.3.0",
     "cypress": "^13.3.0",
     "cypress-wait-until": "^2.0.1",
     "cypress-wait-until": "^2.0.1",
@@ -90,10 +90,10 @@
     "ts-node-dev": "^2.0.0",
     "ts-node-dev": "^2.0.0",
     "tsconfig-paths": "^3.9.0",
     "tsconfig-paths": "^3.9.0",
     "typescript": "~5.0.0",
     "typescript": "~5.0.0",
-    "vite": "^4.4.0",
+    "vite": "^4.5.0",
     "vite-plugin-dts": "^2.3.0",
     "vite-plugin-dts": "^2.3.0",
     "vite-tsconfig-paths": "^4.2.0",
     "vite-tsconfig-paths": "^4.2.0",
-    "vitest": "^0.31.4",
+    "vitest": "^0.34.6",
     "vitest-mock-extended": "^1.1.3"
     "vitest-mock-extended": "^1.1.3"
   },
   },
   "engines": {
   "engines": {

+ 1 - 1
packages/core/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/core",
   "name": "@growi/core",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI Core Libraries",
   "description": "GROWI Core Libraries",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 0 - 1
packages/core/src/interfaces/page.ts

@@ -74,7 +74,6 @@ export type IPageHasId = IPage & HasObjectId;
 export type IPageInfo = {
 export type IPageInfo = {
   isV5Compatible: boolean,
   isV5Compatible: boolean,
   isEmpty: boolean,
   isEmpty: boolean,
-  isMovable: boolean,
   isDeletable: boolean,
   isDeletable: boolean,
   isAbleToDeleteCompletely: boolean,
   isAbleToDeleteCompletely: boolean,
   isRevertible: boolean,
   isRevertible: boolean,

+ 20 - 2
packages/core/src/utils/page-path-utils/index.spec.ts

@@ -1,9 +1,9 @@
 import {
 import {
-  isMovablePage, convertToNewAffiliationPath, isCreatablePage, omitDuplicateAreaPathFromPaths, getUsernameByPath,
+  isMovablePage, isTopPage, isUsersProtectedPages, convertToNewAffiliationPath, isCreatablePage, omitDuplicateAreaPathFromPaths, getUsernameByPath,
 } from './index';
 } from './index';
 
 
 describe.concurrent('isMovablePage test', () => {
 describe.concurrent('isMovablePage test', () => {
-  test('should decide deletable or not', () => {
+  test('should decide movable or not', () => {
     expect(isMovablePage('/')).toBeFalsy();
     expect(isMovablePage('/')).toBeFalsy();
     expect(isMovablePage('/hoge')).toBeTruthy();
     expect(isMovablePage('/hoge')).toBeTruthy();
     expect(isMovablePage('/user')).toBeFalsy();
     expect(isMovablePage('/user')).toBeFalsy();
@@ -13,6 +13,24 @@ describe.concurrent('isMovablePage test', () => {
   });
   });
 });
 });
 
 
+describe.concurrent('isTopPage test', () => {
+  test('should decide deletable or not', () => {
+    expect(isTopPage('/')).toBeTruthy();
+    expect(isTopPage('/hoge')).toBeFalsy();
+    expect(isTopPage('/user/xxx/hoge')).toBeFalsy();
+  });
+});
+
+describe.concurrent('isUsersProtectedPages test', () => {
+  test('Should decide users protected pages or not', () => {
+    expect(isUsersProtectedPages('/hoge')).toBeFalsy();
+    expect(isUsersProtectedPages('/user')).toBeTruthy();
+    expect(isUsersProtectedPages('/user/xxx')).toBeTruthy();
+    expect(isUsersProtectedPages('/user/xxx123')).toBeTruthy();
+    expect(isUsersProtectedPages('/user/xxx/hoge')).toBeFalsy();
+  });
+});
+
 describe.concurrent('convertToNewAffiliationPath test', () => {
 describe.concurrent('convertToNewAffiliationPath test', () => {
   test.concurrent('Child path is not converted normally', () => {
   test.concurrent('Child path is not converted normally', () => {
     const result = convertToNewAffiliationPath('parent/', 'parent2/', 'parent/child');
     const result = convertToNewAffiliationPath('parent/', 'parent2/', 'parent/child');

+ 1 - 1
packages/hackmd/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/hackmd",
   "name": "@growi/hackmd",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI js and css files to use hackmd",
   "description": "GROWI js and css files to use hackmd",
   "license": "MIT",
   "license": "MIT",
   "type": "module",
   "type": "module",

+ 1 - 1
packages/presentation/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/presentation",
   "name": "@growi/presentation",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI plugin for presentation",
   "description": "GROWI plugin for presentation",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 3 - 0
packages/preset-templates/dist/marp-example/ja_JP/meta.json

@@ -0,0 +1,3 @@
+{
+  "title": "Marpを使ったプレゼンテーションの例"
+}

+ 326 - 0
packages/preset-templates/dist/marp-example/ja_JP/template.md

@@ -0,0 +1,326 @@
+---
+marp: true
+---
+
+Marp
+===
+
+![h:250](https://avatars1.githubusercontent.com/u/20685754?v=4)
+
+##### Markdown プレゼンテーションのエコシステム
+
+###### by Marp チーム ([@marp-team][marp-team])
+
+[marp-team]: https://github.com/marp-team
+[marpit]: https://github.com/marp-team/marpit
+[marp-core]: https://github.com/marp-team/marp-core
+[marp-cli]: https://github.com/marp-team/marp-cli
+[marp-vscode]: https://github.com/marp-team/marp-vscode
+
+---
+
+# 特徴
+
+- :memo: **Markdown 記法を使用したプレゼンテーションの作成** (CommonMark)
+- :factory: [Marpit framework][marpit] を基に構築: プレゼンテーションを作成するための新しく軽量なフレームワーク
+- :gear: [Marp Core][marp-core]: npm を介してコアエンジンやビルトインテーマを使いやすく
+- :tv: [Marp CLI][marp-cli]:  Markdown を HTML 、 PDF 、 PPTX 、画像に変換可能
+- :vs: [Marp for VS Code][marp-vscode]: 編集中のスライドをライブプレビュー表示
+- and more...
+
+---
+
+# スライドの記述のしかた
+
+やり方はとてもシンプルです。ハイフン記号 (e.g. `---`)でページを分割します。
+
+```markdown
+# スライド 1
+
+本文
+
+---
+
+# スライド 2
+
+本文
+```
+
+---
+
+# Directives
+
+Marp では美しいスライド作成を支援するため、**"Directives"** という拡張構文を用意しています。
+
+Markdown 記法の本文の前に挿入します:
+
+```
+---
+theme: default
+---
+```
+
+または本文のどこかに HTML 形式で記述します:
+
+```html
+<!-- theme: default -->
+```
+
+https://marpit.marp.app/directives
+
+---
+
+## [グローバル Directives](https://marpit.marp.app/directives?id=global-directives)
+
+- `theme` : テーマを選ぶ
+- `size` : スライドのサイズを `16:9` か `4:3` で選択する *(Marpit frameworkを除く)* 
+- [`headingDivider`](https://marpit.marp.app/directives?id=heading-divider) : 任意の見出しの前にスライドのページ区切りを挿入する
+
+```
+---
+theme: gaia
+size: 4:3
+---
+
+# 内容
+```
+
+> Marp では以下の [built-in themes in Marp Core](https://github.com/marp-team/marp-core/tree/master/themes#readme) が利用可能 : `default` `gaia` `uncover`.
+
+---
+
+## [ローカル Directives](https://marpit.marp.app/directives?id=local-directives)
+
+スライドページ毎に設定できる値一覧
+
+- `paginate` : `true`でページ数を表示する
+- `header` : ヘッダーの内容を指定する
+- `footer` : フッターの内容を指定する
+- `class` : 現在のスライドにHTMLのクラス設定をする
+- `color` : 文字色を指定する
+- `backgroundColor` : 背景色を指定する
+
+---
+
+### スポット Directives
+
+ローカル構文は**任意のページとそれ以降のページ**に適用されます。 
+
+`_class` のようにアンダーバーと接頭辞を使うことで一つのページに適用できます。
+
+![bg right 95%](https://marpit.marp.app/assets/directives.png)
+
+---
+
+### 活用例
+
+このページは  [defined in Marp built-in theme](https://github.com/marp-team/marp-core/tree/master/themes#readme) で色彩を反転させています。
+
+<!-- _class: invert -->
+
+```html
+<!-- _class: invert -->
+```
+
+---
+
+# [画像イメージのための構文](https://marpit.marp.app/image-syntax)
+
+以下のキーワードを使って、画像サイズの変更やフィルターを適用できます
+ : `width` (`w`) 、 `height` (`h`) 、 CSS のフィルター
+
+```markdown
+![width:100px height:100px](image.png)
+```
+
+```markdown
+![blur sepia:50%](filters.png)
+```
+
+[resizing image syntax](https://marpit.marp.app/image-syntax?id=resizing-image)  と  [a list of CSS filters](https://marpit.marp.app/image-syntax?id=image-filters) を指定してください。
+
+![w:100px h:100px](https://avatars1.githubusercontent.com/u/20685754?v=4) ![w:100 h:100 blur sepia:50%](https://avatars1.githubusercontent.com/u/20685754?v=4)
+
+---
+
+# [背景イメージのための構文](https://marpit.marp.app/image-syntax?id=slide-backgrounds)
+
+ `bg` でスライドに背景イメージを設定できます。
+
+```markdown
+![bg opacity](https://yhatt-marp-cli-example.netlify.com/assets/gradient.jpg)
+```
+
+![bg opacity](https://yhatt-marp-cli-example.netlify.com/assets/gradient.jpg)
+
+---
+
+## 複数の背景イメージを利用する ([Marpit's advanced backgrounds](https://marpit.marp.app/image-syntax?id=advanced-backgrounds))
+
+Marp では複数の背景イメージを利用できます。
+
+```markdown
+![bg blur:3px](https://fakeimg.pl/800x600/fff/ccc/?text=A)
+![bg blur:3px](https://fakeimg.pl/800x600/eee/ccc/?text=B)
+![bg blur:3px](https://fakeimg.pl/800x600/ddd/ccc/?text=C)
+```
+
+ `vertical` で、アライメントの方向も変更可能です。
+
+![bg blur:3px](https://fakeimg.pl/800x600/fff/ccc/?text=A)
+![bg blur:3px](https://fakeimg.pl/800x600/eee/ccc/?text=B)
+![bg blur:3px](https://fakeimg.pl/800x600/ddd/ccc/?text=C)
+
+---
+
+## [背景を分割する](https://marpit.marp.app/image-syntax?id=split-backgrounds)
+
+Marp では背景を分割する [Deckset](https://docs.deckset.com/English.lproj/Media/01-background-images.html#split-slides) を利用できます。
+
+ `bg` + `left` / `right` で背景イメージを配置するスぺースを指定可能です。
+
+```markdown
+![bg right](image.jpg)
+```
+
+![bg right](https://images.unsplash.com/photo-1568488789544-e37edf90eb67?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=720&ixlib=rb-1.2.1&q=80&w=640)
+
+<!-- _footer: "*Photo by [Mohamed Nohassi](https://unsplash.com/@coopery?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*" -->
+
+---
+
+## [階層リスト](https://marpit.marp.app/fragmented-list)
+
+Marp ではアスタリスク記号をつけることで、各コンテンツを階層リストとして分類します。 (_**HTML 形式のエクスポートのみ** by [Marp CLI][marp-cli] / [Marp for VS Code][marp-vscode]_)
+
+```markdown
+# 箇条書きリスト
+
+- 1
+- 2
+- 3
+
+---
+
+# 階層リスト
+
+* 1
+* 2
+* 3
+```
+
+---
+
+## 数式の設定 ( [Marp Core][marp-core] のみ)
+
+[KaTeX](https://katex.org/) math typesetting は $ax^2+bc+c$ のように [Pandoc's math syntax](https://pandoc.org/MANUAL.html#math) で利用できます。
+
+$$I_{xx}=\int\int_Ry^2f(x,y)\cdot{}dydx$$
+
+```tex
+$ax^2+bc+c$
+```
+```tex
+$$I_{xx}=\int\int_Ry^2f(x,y)\cdot{}dydx$$
+```
+
+---
+
+## オートスケーリング ( [Marp Core][marp-core] のみ)
+
+*Several built-in themes* はコードブロックと typesettings でオートスケーリングされます。
+
+```text
+Too long code block will be scaled-down automatically. ------------>
+```
+```text
+Too long code block will be scaled-down automatically. ------------------------>
+```
+```text
+Too long code block will be scaled-down automatically. ------------------------------------------------>
+```
+
+---
+
+##### <!--fit--> ヘッダーの自動調整 ( [Marp Core][marp-core] のみ)は
+##### <!--fit--> `<!--fit-->` のような注釈をヘッダーに入れることで利用可能です。
+
+<br />
+
+```html
+## <!--fit--> Auto-fitting header (only for Marp Core)
+```
+
+---
+
+## [CSS テーマ](https://marpit.marp.app/theme-css)
+
+Marp では各スライドのコンテナとして `<section>` を使います。その他は基本の Markdown 記法と同様です。 [Marp CLI][marp-cli] と [Marp for VS Code][marp-vscode] ではカスタムテーマを利用可能です。
+
+```css
+/* @theme your-theme */
+
+@import 'default';
+
+section {
+  /* Specify slide size */
+  width: 960px;
+  height: 720px;
+}
+
+h1 {
+  font-size: 30px;
+  color: #c33;
+}
+```
+
+---
+
+## [Markdown 記法のスタイル調整](https://marpit.marp.app/theme-css?id=tweak-style-through-markdown)
+
+Markdown 記法の `<style>` タグは theme CSS のコンテキストで機能します。
+
+```markdown
+---
+theme: default
+---
+
+<style>
+section {
+  background: yellow;
+}
+</style>
+
+Re-painted yellow background, ha-ha.
+```
+
+> `section.custom-class { ... }` のように class を活用することで、カスタムスタイルの追加も可能です。
+> `<!-- _class: custom-class -->` でスタイルを適用します。
+
+---
+
+## [特定範囲へのスタイル適用](https://marpit.marp.app/theme-css?id=scoped-style)
+
+現在のページの特定範囲へスタイルを適用したい場合は `<style scoped>` をご利用ください。
+
+```markdown
+<style scoped>
+a {
+  color: green;
+}
+</style>
+
+![Green link!](https://marp.app/)
+```
+
+<style scoped>
+a { color: green; }
+</style>
+
+---
+
+# スライド作成を楽しみましょう! :v: <!--fit-->
+
+##### ![w:1em h:1em](https://avatars1.githubusercontent.com/u/20685754?v=4)  Marp: Markdown  — https://marp.app/
+
+###### by Marp チーム ([@marp-team][marp-team])

+ 3 - 0
packages/preset-templates/dist/marp-example/zh_CN/meta.json

@@ -0,0 +1,3 @@
+{
+  "title": "使用 Marp 的演示示例"
+}

+ 325 - 0
packages/preset-templates/dist/marp-example/zh_CN/template.md

@@ -0,0 +1,325 @@
+---
+marp: true
+---
+
+Marp
+===
+
+![h:250](https://avatars1.githubusercontent.com/u/20685754?v=4)
+
+##### Markdown 演示生态系统
+
+###### by Marp 团队 ([@marp-team][marp-team])
+
+[marp-team]: https://github.com/marp-team
+[marpit]: https://github.com/marp-team/marpit
+[marp-core]: https://github.com/marp-team/marp-core
+[marp-cli]: https://github.com/marp-team/marp-cli
+[marp-vscode]: https://github.com/marp-team/marp-vscode
+
+---
+
+# 特点
+
+- :memo: **使用普通的 Markdown 编写投影片** (CommonMark)
+- :factory: 基于 [Marpit framework][marpit]: 一个全新的轻量级框架,用于创建投影片
+- :gear: [Marp Core][marp-core]: 通过 npm 轻松启动核心引擎并使用内置主题
+- :tv: [Marp CLI][marp-cli]: 将 Markdown 转换为 HTML、 PDF、 PPTX、 和图像
+- :vs: [Marp for VS Code][marp-vscode]: 在编辑时实时预览您的投影片
+- 等等...
+
+---
+
+# 如何编写幻灯片?
+
+通过水平标尺拆分页面 (e.g. `---`)。 这很简单。
+
+```markdown
+# 幻灯片 1
+
+张三李四
+
+---
+
+# 幻灯片 2
+
+张三李四
+```
+
+---
+
+# Directives
+
+Marp 引入了名为 **"Directives"** 的扩展语法,以支持创建漂亮的幻灯片。
+
+在 Markdown 正文之前插入:
+
+```
+---
+theme: default
+---
+```
+
+或者在正文的任意位置使用 HTML 进行描述:
+
+```html
+<!-- theme: default -->
+```
+
+https://marpit.marp.app/directives
+
+---
+
+## [全球 directives](https://marpit.marp.app/directives?id=global-directives)
+
+- `theme`: 选择主题
+- `size`: 选择幻灯片尺寸为 `16:9` 或 `4:3` *(不包括 Marpit framework)*
+- [`headingDivider`](https://marpit.marp.app/directives?id=heading-divider): 在任意标题之前插入幻灯片的分页符
+
+```
+---
+theme: gaia
+size: 4:3
+---
+
+# Content
+```
+
+> Marp 中有以下 [built-in themes in Marp Core](https://github.com/marp-team/marp-core/tree/master/themes#readme) 可供使用 : `default`, `gaia`, and `uncover`.
+
+---
+
+## [局部 directives](https://marpit.marp.app/directives?id=local-directives)
+
+每个幻灯片页面可设置的值列表
+
+- `paginate`: 使用 `true` 显示页数
+- `header`: 指定页眉内容
+- `footer`: 指定页脚内容
+- `class`: 为当前幻灯片设置 HTML 类
+- `color`: 指定文字颜色
+- `backgroundColor`: 指定背景颜色
+
+---
+
+### 点 directives
+
+局部 directives 将应用于 **defined page and following pages**.
+
+通过使用下划线和前缀,例如 `_class` ,可以将其应用于单个页面。
+
+![bg right 95%](https://marpit.marp.app/assets/directives.png)
+
+---
+
+### 例子
+
+该页面使用了反转色彩方案 [defined in Marp built-in theme](https://github.com/marp-team/marp-core/tree/master/themes#readme)。
+
+<!-- _class: invert -->
+
+```html
+<!-- _class: invert -->
+```
+
+---
+
+# [图像标记语言的语法](https://marpit.marp.app/image-syntax)
+
+您可以通过关键词调整图像大小并应用过滤器 : `width` (`w`), `height` (`h`), 和滤镜CSS关键词
+
+```markdown
+![width:100px height:100px](image.png)
+```
+
+```markdown
+![blur sepia:50%](filters.png)
+```
+
+请指定 [resizing image syntax](https://marpit.marp.app/image-syntax?id=resizing-image) 和 [a list of CSS filters](https://marpit.marp.app/image-syntax?id=image-filters)。
+
+![w:100px h:100px](https://avatars1.githubusercontent.com/u/20685754?v=4) ![w:100 h:100 blur sepia:50%](https://avatars1.githubusercontent.com/u/20685754?v=4)
+
+---
+
+# [背景图像的语法](https://marpit.marp.app/image-syntax?id=slide-backgrounds)
+
+您可以使用 `bg` 关键字为幻灯片设置背景图像。
+
+```markdown
+![bg opacity](https://yhatt-marp-cli-example.netlify.com/assets/gradient.jpg)
+```
+
+![bg opacity](https://yhatt-marp-cli-example.netlify.com/assets/gradient.jpg)
+
+---
+
+## 利用多个背景图像 ([Marpit's advanced backgrounds](https://marpit.marp.app/image-syntax?id=advanced-backgrounds))
+
+Marp 可以使用多个背景图像。
+
+```markdown
+![bg blur:3px](https://fakeimg.pl/800x600/fff/ccc/?text=A)
+![bg blur:3px](https://fakeimg.pl/800x600/eee/ccc/?text=B)
+![bg blur:3px](https://fakeimg.pl/800x600/ddd/ccc/?text=C)
+```
+
+还可以通过包含 `vertical` 关键字来更改对齐方向。
+
+![bg blur:3px](https://fakeimg.pl/800x600/fff/ccc/?text=A)
+![bg blur:3px](https://fakeimg.pl/800x600/eee/ccc/?text=B)
+![bg blur:3px](https://fakeimg.pl/800x600/ddd/ccc/?text=C)
+
+---
+
+## [分割背景](https://marpit.marp.app/image-syntax?id=split-backgrounds)
+
+Marp 可以使用 [Deckset](https://docs.deckset.com/English.lproj/Media/01-background-images.html#split-slides) 风格的分割背景。
+
+通过 `bg` + `left` / `right` 关键字为背景留出空间。
+
+```markdown
+![bg right](image.jpg)
+```
+
+![bg right](https://images.unsplash.com/photo-1568488789544-e37edf90eb67?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=720&ixlib=rb-1.2.1&q=80&w=640)
+
+<!-- _footer: "*Photo by [Mohamed Nohassi](https://unsplash.com/@coopery?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)*" -->
+
+---
+
+## [分段列表](https://marpit.marp.app/fragmented-list)
+
+Marp 将列表与星号标记解析为分段列表,以逐一显示内容。 (_**仅适用于导出的 HTML** by [Marp CLI][marp-cli] / [Marp for VS Code][marp-vscode]_)
+
+```markdown
+# 项目符号列表
+
+- 一
+- 二
+- 三
+
+---
+
+# 分段列表
+
+* 一
+* 二
+* 三
+```
+
+---
+
+## 数学排版 (仅适用于 [Marp Core][marp-core])
+
+使用 [KaTeX](https://katex.org/) 进行数学排版,例如 $ax^2+bc+c$ 可以与 [Pandoc's math syntax](https://pandoc.org/MANUAL.html#math) 一起使用。
+
+$$I_{xx}=\int\int_Ry^2f(x,y)\cdot{}dydx$$
+
+```tex
+$ax^2+bc+c$
+```
+```tex
+$$I_{xx}=\int\int_Ry^2f(x,y)\cdot{}dydx$$
+```
+
+---
+
+## 自动缩放 (仅适用于 [Marp Core][marp-core])
+
+*Several built-in themes* 支持对代码块和数学排版进行自动缩放。
+
+```text
+Too long code block will be scaled-down automatically. ------------>
+```
+```text
+Too long code block will be scaled-down automatically. ------------------------>
+```
+```text
+Too long code block will be scaled-down automatically. ------------------------------------------------>
+```
+
+---
+
+##### <!--fit--> 自动调整标题 (仅适用于 [Marp Core][marp-core])
+##### <!--fit--> 在标题中注释 `<!--fit-->` 即可使用。
+
+<br />
+
+```html
+## <!--fit--> Auto-fitting header (only for Marp Core)
+```
+
+---
+
+## [主题样式表](https://marpit.marp.app/theme-css)
+
+Marp 使用 `<section>` 作为每个幻灯片的容器。 其他的样式与普通的 Markdown 样式相同。定制主题可在 [Marp CLI][marp-cli] 和 [Marp for VS Code][marp-vscode] 中使用。
+
+```css
+/* @theme your-theme */
+
+@import 'default';
+
+section {
+  /* Specify slide size */
+  width: 960px;
+  height: 720px;
+}
+
+h1 {
+  font-size: 30px;
+  color: #c33;
+}
+```
+
+---
+
+## [在 Markdown 中微调样式](https://marpit.marp.app/theme-css?id=tweak-style-through-markdown)
+
+Markdown 中的 `<style>` 标签将在主题 CSS 的上下文中起作用。
+
+```markdown
+---
+theme: default
+---
+
+<style>
+section {
+  background: yellow;
+}
+</style>
+
+Re-painted yellow background, ha-ha.
+```
+
+> 您还可以通过类别添加自定义样式,例如 `section.custom-class { ... }` 。
+> 通过 `<!-- _class: custom-class -->` 应用样式。
+
+---
+
+## [局部样式](https://marpit.marp.app/theme-css?id=scoped-style)
+
+如果您想为当前页面设置一次性样式,可以使用 `<style scoped>` 。
+
+```markdown
+<style scoped>
+a {
+  color: green;
+}
+</style>
+
+![Green link!](https://marp.app/)
+```
+
+<style scoped>
+a { color: green; }
+</style>
+
+---
+
+# 尽情享受幻灯片创作吧! :v: <!--fit-->
+
+##### ![w:1em h:1em](https://avatars1.githubusercontent.com/u/20685754?v=4)  Marp: Markdown presentation ecosystem — https://marp.app/
+
+###### by Marp 团队 ([@marp-team][marp-team])

+ 1 - 1
packages/preset-templates/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/preset-templates",
   "name": "@growi/preset-templates",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "scripts": {
   "scripts": {
     "test": "vitest run",
     "test": "vitest run",
     "version": "yarn version --no-git-tag-version --preid=RC"
     "version": "yarn version --no-git-tag-version --preid=RC"

+ 1 - 1
packages/preset-themes/package.json

@@ -1,7 +1,7 @@
 {
 {
   "name": "@growi/preset-themes",
   "name": "@growi/preset-themes",
   "description": "GROWI preset themes",
   "description": "GROWI preset themes",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "license": "MIT",
   "license": "MIT",
   "main": "dist/libs/preset-themes.umd.js",
   "main": "dist/libs/preset-themes.umd.js",
   "module": "dist/libs/preset-themes.mjs",
   "module": "dist/libs/preset-themes.mjs",

+ 1 - 1
packages/remark-attachment-refs/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/remark-attachment-refs",
   "name": "@growi/remark-attachment-refs",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "description": "GROWI Plugin to add ref/refimg/refs/refsimg tags",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 1 - 1
packages/remark-drawio/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/remark-drawio",
   "name": "@growi/remark-drawio",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "remark plugin to draw diagrams with draw.io (diagrams.net)",
   "description": "remark plugin to draw diagrams with draw.io (diagrams.net)",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 1 - 1
packages/remark-growi-directive/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/remark-growi-directive",
   "name": "@growi/remark-growi-directive",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "remark plugin to support GROWI plugin (forked from remark-directive@2.0.1)",
   "description": "remark plugin to support GROWI plugin (forked from remark-directive@2.0.1)",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 1 - 1
packages/remark-lsx/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/remark-lsx",
   "name": "@growi/remark-lsx",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI plugin to list pages",
   "description": "GROWI plugin to list pages",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 1 - 1
packages/slack/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/slack",
   "name": "@growi/slack",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "license": "MIT",
   "license": "MIT",
   "type": "module",
   "type": "module",
   "main": "dist/index.cjs",
   "main": "dist/index.cjs",

+ 1 - 1
packages/ui/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/ui",
   "name": "@growi/ui",
-  "version": "6.2.3",
+  "version": "6.2.4-RC.0",
   "description": "GROWI UI Libraries",
   "description": "GROWI UI Libraries",
   "license": "MIT",
   "license": "MIT",
   "keywords": [
   "keywords": [

+ 5 - 0
packages/ui/src/components/Attachment.tsx

@@ -1,4 +1,5 @@
 import type { IAttachmentHasId } from '@growi/core';
 import type { IAttachmentHasId } from '@growi/core';
+import { format } from 'date-fns';
 
 
 import { UserPicture } from './UserPicture';
 import { UserPicture } from './UserPicture';
 
 
@@ -38,6 +39,9 @@ export const Attachment = (props: AttachmentProps): JSX.Element => {
     : '';
     : '';
   const fileType = <span className="attachment-filetype badge badge-pill badge-secondary">{attachment.fileFormat}</span>;
   const fileType = <span className="attachment-filetype badge badge-pill badge-secondary">{attachment.fileFormat}</span>;
   const fileInUse = (inUse) ? <span className="attachment-in-use badge badge-pill badge-info">In Use</span> : '';
   const fileInUse = (inUse) ? <span className="attachment-in-use badge badge-pill badge-info">In Use</span> : '';
+  // Should UserDate be used like PageRevisionTable ?
+  const formatType = 'yyyy/MM/dd HH:mm:ss';
+  const createdAt = format(new Date(attachment.createdAt), formatType);
 
 
   return (
   return (
     <div className="attachment mb-2">
     <div className="attachment mb-2">
@@ -48,6 +52,7 @@ export const Attachment = (props: AttachmentProps): JSX.Element => {
         <i className={formatIcon}></i> {attachment.originalName}
         <i className={formatIcon}></i> {attachment.originalName}
       </a>
       </a>
       <span className="mr-2">{fileType}</span>
       <span className="mr-2">{fileType}</span>
+      <span className="mr-2">{createdAt}</span>
       <span className="mr-2">{fileInUse}</span>
       <span className="mr-2">{fileInUse}</span>
       <span className="mr-2">{btnDownload}</span>
       <span className="mr-2">{btnDownload}</span>
       <span className="mr-2">{btnTrash}</span>
       <span className="mr-2">{btnTrash}</span>

+ 272 - 205
yarn.lock

@@ -2295,13 +2295,13 @@
     xdg-basedir "^4.0.0"
     xdg-basedir "^4.0.0"
 
 
 "@growi/core@link:packages/core":
 "@growi/core@link:packages/core":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     bson-objectid "^2.0.4"
     bson-objectid "^2.0.4"
     escape-string-regexp "^4.0.0"
     escape-string-regexp "^4.0.0"
 
 
 "@growi/hackmd@link:packages/hackmd":
 "@growi/hackmd@link:packages/hackmd":
-  version "6.2.3"
+  version "6.2.4-RC.0"
 
 
 "@growi/pluginkit@link:packages/pluginkit":
 "@growi/pluginkit@link:packages/pluginkit":
   version "0.1.0"
   version "0.1.0"
@@ -2310,18 +2310,18 @@
     extensible-custom-error "^0.0.7"
     extensible-custom-error "^0.0.7"
 
 
 "@growi/presentation@link:packages/presentation":
 "@growi/presentation@link:packages/presentation":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/core" "link:packages/core"
 
 
 "@growi/preset-templates@link:packages/preset-templates":
 "@growi/preset-templates@link:packages/preset-templates":
-  version "6.2.3"
+  version "6.2.4-RC.0"
 
 
 "@growi/preset-themes@link:packages/preset-themes":
 "@growi/preset-themes@link:packages/preset-themes":
-  version "6.2.3"
+  version "6.2.4-RC.0"
 
 
 "@growi/remark-attachment-refs@link:packages/remark-attachment-refs":
 "@growi/remark-attachment-refs@link:packages/remark-attachment-refs":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/core" "link:packages/core"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
@@ -2333,10 +2333,10 @@
     universal-bunyan "^0.9.2"
     universal-bunyan "^0.9.2"
 
 
 "@growi/remark-drawio@link:packages/remark-drawio":
 "@growi/remark-drawio@link:packages/remark-drawio":
-  version "6.2.3"
+  version "6.2.4-RC.0"
 
 
 "@growi/remark-growi-directive@link:packages/remark-growi-directive":
 "@growi/remark-growi-directive@link:packages/remark-growi-directive":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@types/mdast" "^3.0.0"
     "@types/mdast" "^3.0.0"
     "@types/unist" "^2.0.0"
     "@types/unist" "^2.0.0"
@@ -2353,7 +2353,7 @@
     uvu "^0.5.0"
     uvu "^0.5.0"
 
 
 "@growi/remark-lsx@link:packages/remark-lsx":
 "@growi/remark-lsx@link:packages/remark-lsx":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/core" "link:packages/core"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
     "@growi/remark-growi-directive" "link:packages/remark-growi-directive"
@@ -2365,7 +2365,7 @@
     swr "^2.0.3"
     swr "^2.0.3"
 
 
 "@growi/slack@link:packages/slack":
 "@growi/slack@link:packages/slack":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@slack/oauth" "^2.0.1"
     "@slack/oauth" "^2.0.1"
     "@slack/web-api" "^6.2.4"
     "@slack/web-api" "^6.2.4"
@@ -2381,7 +2381,7 @@
     url-join "^4.0.0"
     url-join "^4.0.0"
 
 
 "@growi/ui@link:packages/ui":
 "@growi/ui@link:packages/ui":
-  version "6.2.3"
+  version "6.2.4-RC.0"
   dependencies:
   dependencies:
     "@growi/core" "link:packages/core"
     "@growi/core" "link:packages/core"
 
 
@@ -2694,11 +2694,16 @@
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
   integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
 
 
-"@jridgewell/sourcemap-codec@1.4.14", "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13":
+"@jridgewell/sourcemap-codec@1.4.14":
   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"
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
   integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
 
 
+"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.13", "@jridgewell/sourcemap-codec@^1.4.15":
+  version "1.4.15"
+  resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32"
+  integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
+
 "@jridgewell/trace-mapping@0.3.9":
 "@jridgewell/trace-mapping@0.3.9":
   version "0.3.9"
   version "0.3.9"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
   resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9"
@@ -4095,51 +4100,56 @@
     "@babel/plugin-transform-react-jsx-source" "^7.22.5"
     "@babel/plugin-transform-react-jsx-source" "^7.22.5"
     react-refresh "^0.14.0"
     react-refresh "^0.14.0"
 
 
-"@vitest/coverage-c8@^0.31.1":
-  version "0.31.1"
-  resolved "https://registry.yarnpkg.com/@vitest/coverage-c8/-/coverage-c8-0.31.1.tgz#bc242d85404e02723e89e4126bec9c727146638a"
-  integrity sha512-6TkjQpmgYez7e3dbAUoYdRXxWN81BojCmUILJwgCy39uZFG33DsQ0rSRSZC9beAEdCZTpxR63nOvd9hxDQcJ0g==
+"@vitest/coverage-v8@^0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/coverage-v8/-/coverage-v8-0.34.6.tgz#931d9223fa738474e00c08f52b84e0f39cedb6d1"
+  integrity sha512-fivy/OK2d/EsJFoEoxHFEnNGTg+MmdZBAVK9Ka4qhXR2K3J0DS08vcGVwzDtXSuUMabLv4KtPcpSKkcMXFDViw==
   dependencies:
   dependencies:
     "@ampproject/remapping" "^2.2.1"
     "@ampproject/remapping" "^2.2.1"
-    c8 "^7.13.0"
-    magic-string "^0.30.0"
+    "@bcoe/v8-coverage" "^0.2.3"
+    istanbul-lib-coverage "^3.2.0"
+    istanbul-lib-report "^3.0.1"
+    istanbul-lib-source-maps "^4.0.1"
+    istanbul-reports "^3.1.5"
+    magic-string "^0.30.1"
     picocolors "^1.0.0"
     picocolors "^1.0.0"
-    std-env "^3.3.2"
+    std-env "^3.3.3"
+    test-exclude "^6.0.0"
+    v8-to-istanbul "^9.1.0"
 
 
-"@vitest/expect@0.31.4":
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.31.4.tgz#115c517404488bf3cb6ce4ac411c40d8e86891b8"
-  integrity sha512-tibyx8o7GUyGHZGyPgzwiaPaLDQ9MMuCOrc03BYT0nryUuhLbL7NV2r/q98iv5STlwMgaKuFJkgBW/8iPKwlSg==
+"@vitest/expect@0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/expect/-/expect-0.34.6.tgz#608a7b7a9aa3de0919db99b4cc087340a03ea77e"
+  integrity sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==
   dependencies:
   dependencies:
-    "@vitest/spy" "0.31.4"
-    "@vitest/utils" "0.31.4"
-    chai "^4.3.7"
+    "@vitest/spy" "0.34.6"
+    "@vitest/utils" "0.34.6"
+    chai "^4.3.10"
 
 
-"@vitest/runner@0.31.4":
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.31.4.tgz#e99abee89132a500d9726a53b58dfc9160db1078"
-  integrity sha512-Wgm6UER+gwq6zkyrm5/wbpXGF+g+UBB78asJlFkIOwyse0pz8lZoiC6SW5i4gPnls/zUcPLWS7Zog0LVepXnpg==
+"@vitest/runner@0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/runner/-/runner-0.34.6.tgz#6f43ca241fc96b2edf230db58bcde5b974b8dcaf"
+  integrity sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==
   dependencies:
   dependencies:
-    "@vitest/utils" "0.31.4"
-    concordance "^5.0.4"
+    "@vitest/utils" "0.34.6"
     p-limit "^4.0.0"
     p-limit "^4.0.0"
-    pathe "^1.1.0"
+    pathe "^1.1.1"
 
 
-"@vitest/snapshot@0.31.4":
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.31.4.tgz#59a42046fec4950a1ac70cf0ec64aada3b995559"
-  integrity sha512-LemvNumL3NdWSmfVAMpXILGyaXPkZbG5tyl6+RQSdcHnTj6hvA49UAI8jzez9oQyE/FWLKRSNqTGzsHuk89LRA==
+"@vitest/snapshot@0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/snapshot/-/snapshot-0.34.6.tgz#b4528cf683b60a3e8071cacbcb97d18b9d5e1d8b"
+  integrity sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==
   dependencies:
   dependencies:
-    magic-string "^0.30.0"
-    pathe "^1.1.0"
-    pretty-format "^27.5.1"
+    magic-string "^0.30.1"
+    pathe "^1.1.1"
+    pretty-format "^29.5.0"
 
 
-"@vitest/spy@0.31.4":
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.31.4.tgz#fce8e348cea32deff79996d116c67893b19cc47d"
-  integrity sha512-3ei5ZH1s3aqbEyftPAzSuunGICRuhE+IXOmpURFdkm5ybUADk+viyQfejNk6q8M5QGX8/EVKw+QWMEP3DTJDag==
+"@vitest/spy@0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/spy/-/spy-0.34.6.tgz#b5e8642a84aad12896c915bce9b3cc8cdaf821df"
+  integrity sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==
   dependencies:
   dependencies:
-    tinyspy "^2.1.0"
+    tinyspy "^2.1.1"
 
 
 "@vitest/ui@^0.31.1":
 "@vitest/ui@^0.31.1":
   version "0.31.1"
   version "0.31.1"
@@ -4163,14 +4173,14 @@
     loupe "^2.3.6"
     loupe "^2.3.6"
     pretty-format "^27.5.1"
     pretty-format "^27.5.1"
 
 
-"@vitest/utils@0.31.4":
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.31.4.tgz#5cfdcecfd604a7dbe3972cfe0f2b1e0af1246ad2"
-  integrity sha512-DobZbHacWznoGUfYU8XDPY78UubJxXfMNY1+SUdOp1NsI34eopSA6aZMeaGu10waSOeYwE8lxrd/pLfT0RMxjQ==
+"@vitest/utils@0.34.6":
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/@vitest/utils/-/utils-0.34.6.tgz#38a0a7eedddb8e7291af09a2409cb8a189516968"
+  integrity sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==
   dependencies:
   dependencies:
-    concordance "^5.0.4"
+    diff-sequences "^29.4.3"
     loupe "^2.3.6"
     loupe "^2.3.6"
-    pretty-format "^27.5.1"
+    pretty-format "^29.5.0"
 
 
 "@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.5":
 "@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.5":
   version "0.7.5"
   version "0.7.5"
@@ -4225,10 +4235,10 @@ acorn-walk@^8.0.0, acorn-walk@^8.1.1, acorn-walk@^8.2.0:
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
   resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1"
   integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
   integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
 
 
-acorn@^8.0.4, acorn@^8.4.1, acorn@^8.8.0, acorn@^8.8.2:
-  version "8.8.2"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
-  integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+acorn@^8.0.4, acorn@^8.10.0, acorn@^8.4.1, acorn@^8.8.0, acorn@^8.8.2, acorn@^8.9.0:
+  version "8.11.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
+  integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
 
 
 agent-base@6:
 agent-base@6:
   version "6.0.2"
   version "6.0.2"
@@ -4237,6 +4247,13 @@ agent-base@6:
   dependencies:
   dependencies:
     debug "4"
     debug "4"
 
 
+agent-base@^7.0.2:
+  version "7.1.0"
+  resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434"
+  integrity sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==
+  dependencies:
+    debug "^4.3.4"
+
 aggregate-error@^3.0.0:
 aggregate-error@^3.0.0:
   version "3.1.0"
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
   resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -4531,12 +4548,12 @@ async-canvas-to-blob@^1.0.3:
   resolved "https://registry.yarnpkg.com/async-canvas-to-blob/-/async-canvas-to-blob-1.0.3.tgz#dbea3ecdca99ecdf6d0340d645dc5342b5032be6"
   resolved "https://registry.yarnpkg.com/async-canvas-to-blob/-/async-canvas-to-blob-1.0.3.tgz#dbea3ecdca99ecdf6d0340d645dc5342b5032be6"
   integrity sha512-jXuowR9cJC9TzAyGv4sUh6ilOKuGUvjzJ1GAZMwgaa+q0rXO+SFVyo7GUUCp89mJ/OEVYlAT/gIx3Tlv0fChRw==
   integrity sha512-jXuowR9cJC9TzAyGv4sUh6ilOKuGUvjzJ1GAZMwgaa+q0rXO+SFVyo7GUUCp89mJ/OEVYlAT/gIx3Tlv0fChRw==
 
 
-async-mutex@^0.3.2:
-  version "0.3.2"
-  resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.3.2.tgz#1485eda5bda1b0ec7c8df1ac2e815757ad1831df"
-  integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==
+async-mutex@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f"
+  integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==
   dependencies:
   dependencies:
-    tslib "^2.3.1"
+    tslib "^2.4.0"
 
 
 async-retry@^1.3.1, async-retry@^1.3.3:
 async-retry@^1.3.1, async-retry@^1.3.3:
   version "1.3.3"
   version "1.3.3"
@@ -4665,6 +4682,11 @@ axobject-query@^2.2.0:
   resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
   resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
   integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
   integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
 
 
+b4a@^1.6.4:
+  version "1.6.4"
+  resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.6.4.tgz#ef1c1422cae5ce6535ec191baeed7567443f36c9"
+  integrity sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==
+
 babel-jest@^29.5.0:
 babel-jest@^29.5.0:
   version "29.5.0"
   version "29.5.0"
   resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5"
   resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.5.0.tgz#3fe3ddb109198e78b1c88f9ebdecd5e4fc2f50a5"
@@ -4980,6 +5002,11 @@ bson@^4.7.2:
   dependencies:
   dependencies:
     buffer "^5.6.0"
     buffer "^5.6.0"
 
 
+bson@^5.5.0:
+  version "5.5.1"
+  resolved "https://registry.yarnpkg.com/bson/-/bson-5.5.1.tgz#f5849d405711a7f23acdda9a442375df858e6833"
+  integrity sha512-ix0EwukN2EpC0SRWIj/7B5+A6uQMQy6KMREI9qQqvgpkV2frH63T0UDVd1SYedL6dNCmDBYB3QtXi4ISk9YT+g==
+
 buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
 buffer-crc32@^0.2.1, buffer-crc32@^0.2.13, buffer-crc32@~0.2.3:
   version "0.2.13"
   version "0.2.13"
   resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
   resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
@@ -5069,7 +5096,7 @@ bytes@3.1.0:
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
   resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
   integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
   integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
 
 
-c8@^7.0.0, c8@^7.13.0:
+c8@^7.0.0:
   version "7.13.0"
   version "7.13.0"
   resolved "https://registry.yarnpkg.com/c8/-/c8-7.13.0.tgz#a2a70a851278709df5a9247d62d7f3d4bcb5f2e4"
   resolved "https://registry.yarnpkg.com/c8/-/c8-7.13.0.tgz#a2a70a851278709df5a9247d62d7f3d4bcb5f2e4"
   integrity sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==
   integrity sha512-/NL4hQTv1gBL6J6ei80zu3IiTrmePDKXKXOTLpHvcIWZTVYQlDhVWjjWvkhICylE8EwwnMVzDZugCvdx0/DIIA==
@@ -5192,18 +5219,18 @@ ccount@^2.0.0:
   resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
   resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5"
   integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
   integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==
 
 
-chai@^4.3.7:
-  version "4.3.7"
-  resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51"
-  integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==
+chai@^4.3.10:
+  version "4.3.10"
+  resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.10.tgz#d784cec635e3b7e2ffb66446a63b4e33bd390384"
+  integrity sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==
   dependencies:
   dependencies:
     assertion-error "^1.1.0"
     assertion-error "^1.1.0"
-    check-error "^1.0.2"
-    deep-eql "^4.1.2"
-    get-func-name "^2.0.0"
-    loupe "^2.3.1"
+    check-error "^1.0.3"
+    deep-eql "^4.1.3"
+    get-func-name "^2.0.2"
+    loupe "^2.3.6"
     pathval "^1.1.1"
     pathval "^1.1.1"
-    type-detect "^4.0.5"
+    type-detect "^4.0.8"
 
 
 chainsaw@~0.1.0:
 chainsaw@~0.1.0:
   version "0.1.0"
   version "0.1.0"
@@ -5320,10 +5347,12 @@ charenc@~0.0.1:
   version "0.0.2"
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
 
 
-check-error@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
-  integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==
+check-error@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694"
+  integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==
+  dependencies:
+    get-func-name "^2.0.2"
 
 
 check-more-types@^2.24.0:
 check-more-types@^2.24.0:
   version "2.24.0"
   version "2.24.0"
@@ -5752,7 +5781,7 @@ content-type@~1.0.4:
   version "1.0.4"
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
   resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
 
 
-convert-source-map@^1.6.0, convert-source-map@^1.7.0:
+convert-source-map@^1.7.0:
   version "1.7.0"
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
   resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
   integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
   integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@@ -6419,7 +6448,7 @@ dedent@^0.7.0:
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
   resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
   integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
   integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=
 
 
-deep-eql@^4.1.2:
+deep-eql@^4.1.3:
   version "4.1.3"
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d"
   integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
   integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==
@@ -7557,6 +7586,11 @@ fast-diff@^1.2.0:
   resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
   resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
   integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
   integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
 
 
+fast-fifo@^1.1.0, fast-fifo@^1.2.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c"
+  integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==
+
 fast-glob@3, fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9:
 fast-glob@3, fast-glob@^3.1.1, fast-glob@^3.2.11, fast-glob@^3.2.12, fast-glob@^3.2.7, fast-glob@^3.2.9:
   version "3.2.12"
   version "3.2.12"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
   resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
@@ -7784,7 +7818,7 @@ follow-redirects@1.5.10:
   dependencies:
   dependencies:
     debug "=3.1.0"
     debug "=3.1.0"
 
 
-follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.15.2:
+follow-redirects@^1.14.0, follow-redirects@^1.14.4, follow-redirects@^1.15.3:
   version "1.15.3"
   version "1.15.3"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
   resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.3.tgz#fe2f3ef2690afce7e82ed0b44db08165b207123a"
   integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
   integrity sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==
@@ -7901,9 +7935,9 @@ fs.realpath@^1.0.0:
   integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
   integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
 
 
 fsevents@^2.3.2, fsevents@~2.3.2:
 fsevents@^2.3.2, fsevents@~2.3.2:
-  version "2.3.2"
-  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
-  integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+  version "2.3.3"
+  resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
+  integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
 
 
 fslightbox-react@^1.7.6:
 fslightbox-react@^1.7.6:
   version "1.7.6"
   version "1.7.6"
@@ -8001,7 +8035,7 @@ get-caller-file@^2.0.5:
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
   integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
 
 
-get-func-name@^2.0.0:
+get-func-name@^2.0.0, get-func-name@^2.0.2:
   version "2.0.2"
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
   resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41"
   integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
   integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==
@@ -8020,11 +8054,6 @@ get-package-type@^0.1.0:
   resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
   resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
   integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
   integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
 
 
-get-port@^5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
-  integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==
-
 get-stdin@^4.0.1:
 get-stdin@^4.0.1:
   version "4.0.1"
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
   resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
@@ -8755,7 +8784,7 @@ http2-client@^1.2.5:
   resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.3.tgz#90fc15d646cca86956b156d07c83947d57d659a9"
   resolved "https://registry.yarnpkg.com/http2-client/-/http2-client-1.3.3.tgz#90fc15d646cca86956b156d07c83947d57d659a9"
   integrity sha512-nUxLymWQ9pzkzTmir24p2RtsgruLmhje7lH3hLX1IpwvyTg77fW+1brenPPP3USAR+rQ36p5sTA/x7sjCJVkAA==
   integrity sha512-nUxLymWQ9pzkzTmir24p2RtsgruLmhje7lH3hLX1IpwvyTg77fW+1brenPPP3USAR+rQ36p5sTA/x7sjCJVkAA==
 
 
-https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
+https-proxy-agent@^5.0.0:
   version "5.0.1"
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
   integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
   integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
@@ -8763,6 +8792,14 @@ https-proxy-agent@^5.0.0, https-proxy-agent@^5.0.1:
     agent-base "6"
     agent-base "6"
     debug "4"
     debug "4"
 
 
+https-proxy-agent@^7.0.2:
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz#e2645b846b90e96c6e6f347fb5b2e41f1590b09b"
+  integrity sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==
+  dependencies:
+    agent-base "^7.0.2"
+    debug "4"
+
 human-signals@^1.1.1:
 human-signals@^1.1.1:
   version "1.1.1"
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
   resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
@@ -9393,28 +9430,28 @@ istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0:
     istanbul-lib-coverage "^3.2.0"
     istanbul-lib-coverage "^3.2.0"
     semver "^6.3.0"
     semver "^6.3.0"
 
 
-istanbul-lib-report@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6"
-  integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==
+istanbul-lib-report@^3.0.0, istanbul-lib-report@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz#908305bac9a5bd175ac6a74489eafd0fc2445a7d"
+  integrity sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==
   dependencies:
   dependencies:
     istanbul-lib-coverage "^3.0.0"
     istanbul-lib-coverage "^3.0.0"
-    make-dir "^3.0.0"
+    make-dir "^4.0.0"
     supports-color "^7.1.0"
     supports-color "^7.1.0"
 
 
-istanbul-lib-source-maps@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz#75743ce6d96bb86dc7ee4352cf6366a23f0b1ad9"
-  integrity sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==
+istanbul-lib-source-maps@^4.0.0, istanbul-lib-source-maps@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551"
+  integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==
   dependencies:
   dependencies:
     debug "^4.1.1"
     debug "^4.1.1"
     istanbul-lib-coverage "^3.0.0"
     istanbul-lib-coverage "^3.0.0"
     source-map "^0.6.1"
     source-map "^0.6.1"
 
 
-istanbul-reports@^3.1.3, istanbul-reports@^3.1.4:
-  version "3.1.5"
-  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae"
-  integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==
+istanbul-reports@^3.1.3, istanbul-reports@^3.1.4, istanbul-reports@^3.1.5:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.6.tgz#2544bcab4768154281a2f0870471902704ccaa1a"
+  integrity sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==
   dependencies:
   dependencies:
     html-escaper "^2.0.0"
     html-escaper "^2.0.0"
     istanbul-lib-report "^3.0.0"
     istanbul-lib-report "^3.0.0"
@@ -10447,7 +10484,7 @@ loud-rejection@^1.0.0:
     currently-unhandled "^0.4.1"
     currently-unhandled "^0.4.1"
     signal-exit "^3.0.0"
     signal-exit "^3.0.0"
 
 
-loupe@^2.3.1, loupe@^2.3.6:
+loupe@^2.3.6:
   version "2.3.6"
   version "2.3.6"
   resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53"
   resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53"
   integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==
   integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA==
@@ -10508,12 +10545,12 @@ magic-string@^0.29.0:
   dependencies:
   dependencies:
     "@jridgewell/sourcemap-codec" "^1.4.13"
     "@jridgewell/sourcemap-codec" "^1.4.13"
 
 
-magic-string@^0.30.0:
-  version "0.30.0"
-  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.0.tgz#fd58a4748c5c4547338a424e90fa5dd17f4de529"
-  integrity sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==
+magic-string@^0.30.1:
+  version "0.30.5"
+  resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.5.tgz#1994d980bd1c8835dc6e78db7cbd4ae4f24746f9"
+  integrity sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==
   dependencies:
   dependencies:
-    "@jridgewell/sourcemap-codec" "^1.4.13"
+    "@jridgewell/sourcemap-codec" "^1.4.15"
 
 
 make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
 make-dir@3.1.0, make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   version "3.1.0"
@@ -10530,6 +10567,13 @@ make-dir@^2.1.0:
     pify "^4.0.1"
     pify "^4.0.1"
     semver "^5.6.0"
     semver "^5.6.0"
 
 
+make-dir@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e"
+  integrity sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==
+  dependencies:
+    semver "^7.5.3"
+
 make-error@^1.1.1:
 make-error@^1.1.1:
   version "1.3.6"
   version "1.3.6"
   resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
   resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2"
@@ -10606,11 +10650,6 @@ md5-file@4.0.0:
   resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-4.0.0.tgz#f3f7ba1e2dd1144d5bf1de698d0e5f44a4409584"
   resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-4.0.0.tgz#f3f7ba1e2dd1144d5bf1de698d0e5f44a4409584"
   integrity sha512-UC0qFwyAjn4YdPpKaDNw6gNxRf7Mcx7jC1UGCY4boCzgvU2Aoc1mOGzTtrjjLKhM5ivsnhoKpQVxKPp+1j1qwg==
   integrity sha512-UC0qFwyAjn4YdPpKaDNw6gNxRf7Mcx7jC1UGCY4boCzgvU2Aoc1mOGzTtrjjLKhM5ivsnhoKpQVxKPp+1j1qwg==
 
 
-md5-file@^5.0.0:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/md5-file/-/md5-file-5.0.0.tgz#e519f631feca9c39e7f9ea1780b63c4745012e20"
-  integrity sha512-xbEFXCYVWrSx/gEKS1VPlg84h/4L20znVIulKw6kMfmBUAZNAnF00eczz9ICMl+/hjQGo5KSXRxbL/47X3rmMw==
-
 md5-hex@^3.0.1:
 md5-hex@^3.0.1:
   version "3.0.1"
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c"
   resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-3.0.1.tgz#be3741b510591434b2784d79e556eefc2c9a8e5c"
@@ -11363,15 +11402,15 @@ mkdirp@^2.1.6:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-2.1.6.tgz#964fbcb12b2d8c5d6fbc62a963ac95a273e2cc19"
   integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
   integrity sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==
 
 
-mlly@^1.2.0:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.2.1.tgz#cd50151f5712b651c5c379085157bcdff661133b"
-  integrity sha512-1aMEByaWgBPEbWV2BOPEMySRrzl7rIHXmQxam4DM8jVjalTQDjpN2ZKOLUrwyhfZQO7IXHml2StcHMhooDeEEQ==
+mlly@^1.2.0, mlly@^1.4.0:
+  version "1.4.2"
+  resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.2.tgz#7cf406aa319ff6563d25da6b36610a93f2a8007e"
+  integrity sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==
   dependencies:
   dependencies:
-    acorn "^8.8.2"
-    pathe "^1.1.0"
+    acorn "^8.10.0"
+    pathe "^1.1.1"
     pkg-types "^1.0.3"
     pkg-types "^1.0.3"
-    ufo "^1.1.2"
+    ufo "^1.3.0"
 
 
 mock-require@^3.0.3:
 mock-require@^3.0.3:
   version "3.0.3"
   version "3.0.3"
@@ -11398,35 +11437,24 @@ mongodb-connection-string-url@^2.5.4, mongodb-connection-string-url@^2.6.0:
     "@types/whatwg-url" "^8.2.1"
     "@types/whatwg-url" "^8.2.1"
     whatwg-url "^11.0.0"
     whatwg-url "^11.0.0"
 
 
-mongodb-memory-server-core@8.15.1:
-  version "8.15.1"
-  resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-8.15.1.tgz#41d79cffacdc0e119fe825044b11da12372fb208"
-  integrity sha512-U6ntro5DvUD71C2juKCzzc2Hul0qLYUpQLiXcXDcvtNDbGMoJgwKScdvptQkzQwUdICeZUfvZo8By7Mc09Umog==
+mongodb-memory-server-core@^9.1.1:
+  version "9.1.1"
+  resolved "https://registry.yarnpkg.com/mongodb-memory-server-core/-/mongodb-memory-server-core-9.1.1.tgz#9b1b2c0f881424d53d1a4704023f1d03cf70eb14"
+  integrity sha512-5toYR4A7DfV5k+Qf6L9FG86baID2rPP/JYwp8TPrdm8ZzfTfyHTwQwa2BzVpSwmLoVW5gXN0znYmXiE68mImMg==
   dependencies:
   dependencies:
-    async-mutex "^0.3.2"
+    async-mutex "^0.4.0"
     camelcase "^6.3.0"
     camelcase "^6.3.0"
     debug "^4.3.4"
     debug "^4.3.4"
     find-cache-dir "^3.3.2"
     find-cache-dir "^3.3.2"
-    follow-redirects "^1.15.2"
-    get-port "^5.1.1"
-    https-proxy-agent "^5.0.1"
-    md5-file "^5.0.0"
-    mongodb "^4.16.0"
+    follow-redirects "^1.15.3"
+    https-proxy-agent "^7.0.2"
+    mongodb "^5.9.1"
     new-find-package-json "^2.0.0"
     new-find-package-json "^2.0.0"
     semver "^7.5.4"
     semver "^7.5.4"
-    tar-stream "^2.1.4"
-    tslib "^2.6.1"
-    uuid "^9.0.0"
+    tar-stream "^3.0.0"
+    tslib "^2.6.2"
     yauzl "^2.10.0"
     yauzl "^2.10.0"
 
 
-mongodb-memory-server@^8.15.1:
-  version "8.15.1"
-  resolved "https://registry.yarnpkg.com/mongodb-memory-server/-/mongodb-memory-server-8.15.1.tgz#8cea839d3b3ae123028d129a96d66e37058fb2c4"
-  integrity sha512-nqIbM5oh1s46VV4InhqQdNFu5szx+xi6qT//87beQ10JCZHLG1nZ/SUMsXkKLNn9wvs19OAf5HwI60enK9ZOuA==
-  dependencies:
-    mongodb-memory-server-core "8.15.1"
-    tslib "^2.6.1"
-
 mongodb@4.16.0:
 mongodb@4.16.0:
   version "4.16.0"
   version "4.16.0"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.16.0.tgz#8b0043de7b577c6a7e0ce44a2ca7315b9c0a7927"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.16.0.tgz#8b0043de7b577c6a7e0ce44a2ca7315b9c0a7927"
@@ -11439,7 +11467,7 @@ mongodb@4.16.0:
     "@aws-sdk/credential-providers" "^3.186.0"
     "@aws-sdk/credential-providers" "^3.186.0"
     saslprep "^1.0.3"
     saslprep "^1.0.3"
 
 
-mongodb@^4.0.1, mongodb@^4.16.0:
+mongodb@^4.0.1:
   version "4.17.1"
   version "4.17.1"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.17.1.tgz#ccff6ddbda106d5e06c25b0e4df454fd36c5f819"
   resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-4.17.1.tgz#ccff6ddbda106d5e06c25b0e4df454fd36c5f819"
   integrity sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==
   integrity sha512-MBuyYiPUPRTqfH2dV0ya4dcr2E5N52ocBuZ8Sgg/M030nGF78v855B3Z27mZJnp8PxjnUquEnAtjOsphgMZOlQ==
@@ -11451,6 +11479,17 @@ mongodb@^4.0.1, mongodb@^4.16.0:
     "@aws-sdk/credential-providers" "^3.186.0"
     "@aws-sdk/credential-providers" "^3.186.0"
     "@mongodb-js/saslprep" "^1.1.0"
     "@mongodb-js/saslprep" "^1.1.0"
 
 
+mongodb@^5.9.1:
+  version "5.9.1"
+  resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.9.1.tgz#da03ea253b0972bf8097301fa5f65e34acad18fa"
+  integrity sha512-NBGA8AfJxGPeB12F73xXwozt8ZpeIPmCUeWRwl9xejozTXFes/3zaep9zhzs1B/nKKsw4P3I4iPfXl3K7s6g+Q==
+  dependencies:
+    bson "^5.5.0"
+    mongodb-connection-string-url "^2.6.0"
+    socks "^2.7.1"
+  optionalDependencies:
+    "@mongodb-js/saslprep" "^1.1.0"
+
 mongoose-gridfs@^1.2.42:
 mongoose-gridfs@^1.2.42:
   version "1.2.42"
   version "1.2.42"
   resolved "https://registry.yarnpkg.com/mongoose-gridfs/-/mongoose-gridfs-1.2.42.tgz#15f4ff25b9b4d7563d544cedd716fc326ad34961"
   resolved "https://registry.yarnpkg.com/mongoose-gridfs/-/mongoose-gridfs-1.2.42.tgz#15f4ff25b9b4d7563d544cedd716fc326ad34961"
@@ -11757,13 +11796,20 @@ node-fetch-h2@^2.3.0:
   dependencies:
   dependencies:
     http2-client "^1.2.5"
     http2-client "^1.2.5"
 
 
-node-fetch@2.6.7, node-fetch@^2.3.0, node-fetch@^2.6.1:
+node-fetch@2.6.7:
   version "2.6.7"
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
   dependencies:
   dependencies:
     whatwg-url "^5.0.0"
     whatwg-url "^5.0.0"
 
 
+node-fetch@^2.3.0, node-fetch@^2.6.1:
+  version "2.7.0"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
+  integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
+  dependencies:
+    whatwg-url "^5.0.0"
+
 node-forge@^0.10.0:
 node-forge@^0.10.0:
   version "0.10.0"
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@@ -12509,10 +12555,10 @@ path-type@^4.0.0:
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
   integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
 
 
-pathe@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.0.tgz#e2e13f6c62b31a3289af4ba19886c230f295ec03"
-  integrity sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==
+pathe@^1.1.0, pathe@^1.1.1:
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
+  integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
 
 
 pathval@^1.1.1:
 pathval@^1.1.1:
   version "1.1.1"
   version "1.1.1"
@@ -12701,7 +12747,7 @@ postcss@^7.0.0:
     picocolors "^0.2.1"
     picocolors "^0.2.1"
     source-map "^0.6.1"
     source-map "^0.6.1"
 
 
-postcss@^8.3.11, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.26, postcss@^8.4.31:
+postcss@^8.3.11, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.27, postcss@^8.4.31:
   version "8.4.31"
   version "8.4.31"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
   resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.31.tgz#92b451050a9f914da6755af352bdc0192508656d"
   integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
   integrity sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==
@@ -12899,6 +12945,11 @@ querystringify@^2.1.1:
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
   resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6"
   integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
   integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==
 
 
+queue-tick@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/queue-tick/-/queue-tick-1.0.1.tgz#f6f07ac82c1fd60f82e098b417a80e52f1f4c142"
+  integrity sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==
+
 quick-lru@^4.0.1:
 quick-lru@^4.0.1:
   version "4.0.1"
   version "4.0.1"
   resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
   resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
@@ -13926,10 +13977,10 @@ rollup-plugin-node-externals@^6.1.1:
   resolved "https://registry.yarnpkg.com/rollup-plugin-node-externals/-/rollup-plugin-node-externals-6.1.1.tgz#dff1a85073fe3c0b2c423b280259fe80392026a8"
   resolved "https://registry.yarnpkg.com/rollup-plugin-node-externals/-/rollup-plugin-node-externals-6.1.1.tgz#dff1a85073fe3c0b2c423b280259fe80392026a8"
   integrity sha512-127OFMkpH5rBVlRHRBDUMk1m1sGuzbGy7so5aj/IkpUb2r3+wOWjR/erUzd2ChEQWPsxsyQG6xpYYvPBAdcBRA==
   integrity sha512-127OFMkpH5rBVlRHRBDUMk1m1sGuzbGy7so5aj/IkpUb2r3+wOWjR/erUzd2ChEQWPsxsyQG6xpYYvPBAdcBRA==
 
 
-rollup@^3.25.2:
-  version "3.26.3"
-  resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.26.3.tgz#bbc8818cadd0aebca348dbb3d68d296d220967b8"
-  integrity sha512-7Tin0C8l86TkpcMtXvQu6saWH93nhG3dGQ1/+l5V2TDMceTxO7kDiK6GzbfLWNNxqJXm591PcEZUozZm51ogwQ==
+rollup@^3.27.1:
+  version "3.29.4"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.4.tgz#4d70c0f9834146df8705bfb69a9a19c9e1109981"
+  integrity sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==
   optionalDependencies:
   optionalDependencies:
     fsevents "~2.3.2"
     fsevents "~2.3.2"
 
 
@@ -14555,10 +14606,10 @@ statuses@2.0.1, statuses@>=2.0.0:
   version "1.5.0"
   version "1.5.0"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
   resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
 
 
-std-env@^3.3.2:
-  version "3.3.3"
-  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.3.3.tgz#a54f06eb245fdcfef53d56f3c0251f1d5c3d01fe"
-  integrity sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==
+std-env@^3.3.3:
+  version "3.5.0"
+  resolved "https://registry.yarnpkg.com/std-env/-/std-env-3.5.0.tgz#83010c9e29bd99bf6f605df87c19012d82d63b97"
+  integrity sha512-JGUEaALvL0Mf6JCfYnJOTcobY+Nc7sG/TemDRBqCA0wEr4DER7zDchaaixTlmOxAjG1uRJmX82EQcxwTQTkqVA==
 
 
 stealthy-require@^1.1.1:
 stealthy-require@^1.1.1:
   version "1.1.1"
   version "1.1.1"
@@ -14626,6 +14677,14 @@ streamsearch@^1.1.0:
   resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
   resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764"
   integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
   integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
 
 
+streamx@^2.15.0:
+  version "2.15.5"
+  resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.15.5.tgz#87bcef4dc7f0b883f9359671203344a4e004c7f1"
+  integrity sha512-9thPGMkKC2GctCzyCUjME3yR03x2xNo0GPKGkRw2UMYN+gqWa9uqpyNWhmsNCutU5zHmkUum0LsCRQTXUgUCAg==
+  dependencies:
+    fast-fifo "^1.1.0"
+    queue-tick "^1.0.1"
+
 string-argv@~0.3.1:
 string-argv@~0.3.1:
   version "0.3.1"
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
   resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.1.tgz#95e2fbec0427ae19184935f816d74aaa4c5c19da"
@@ -15064,7 +15123,7 @@ tape@^5.0.0:
     string.prototype.trim "^1.2.5"
     string.prototype.trim "^1.2.5"
     through "^2.3.8"
     through "^2.3.8"
 
 
-tar-stream@^2.1.4, tar-stream@^2.2.0:
+tar-stream@^2.2.0:
   version "2.2.0"
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
   resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
   integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
   integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
@@ -15075,6 +15134,15 @@ tar-stream@^2.1.4, tar-stream@^2.2.0:
     inherits "^2.0.3"
     inherits "^2.0.3"
     readable-stream "^3.1.1"
     readable-stream "^3.1.1"
 
 
+tar-stream@^3.0.0:
+  version "3.1.6"
+  resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.6.tgz#6520607b55a06f4a2e2e04db360ba7d338cc5bab"
+  integrity sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==
+  dependencies:
+    b4a "^1.6.4"
+    fast-fifo "^1.2.0"
+    streamx "^2.15.0"
+
 teeny-request@^7.0.0:
 teeny-request@^7.0.0:
   version "7.1.0"
   version "7.1.0"
   resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.0.tgz#be7593e62d5f2d656646a0c35fc7c3f18f6300f9"
   resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.1.0.tgz#be7593e62d5f2d656646a0c35fc7c3f18f6300f9"
@@ -15161,15 +15229,15 @@ tinybench@^2.5.0:
   resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5"
   resolved "https://registry.yarnpkg.com/tinybench/-/tinybench-2.5.0.tgz#4711c99bbf6f3e986f67eb722fed9cddb3a68ba5"
   integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==
   integrity sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==
 
 
-tinypool@^0.5.0:
-  version "0.5.0"
-  resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.5.0.tgz#3861c3069bf71e4f1f5aa2d2e6b3aaacc278961e"
-  integrity sha512-paHQtnrlS1QZYKF/GnLoOM/DN9fqaGOFbCbxzAhwniySnzl9Ebk8w73/dd34DAhe/obUbPAOldTyYXQZxnPBPQ==
+tinypool@^0.7.0:
+  version "0.7.0"
+  resolved "https://registry.yarnpkg.com/tinypool/-/tinypool-0.7.0.tgz#88053cc99b4a594382af23190c609d93fddf8021"
+  integrity sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==
 
 
-tinyspy@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.1.0.tgz#bd6875098f988728e6456cfd5ab8cc06498ecdeb"
-  integrity sha512-7eORpyqImoOvkQJCSkL0d0mB4NHHIFAy4b1u8PHdDa7SjGS2njzl6/lyGoZLm+eyYEtlUmFGE0rFj66SWxZgQQ==
+tinyspy@^2.1.1:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/tinyspy/-/tinyspy-2.2.0.tgz#9dc04b072746520b432f77ea2c2d17933de5d6ce"
+  integrity sha512-d2eda04AN/cPOR89F7Xv5bK/jrQEhmcLFe6HFldoeO9AJtps+fqEnh486vnT/8y4bw38pSyxDcTCAq+Ks2aJTg==
 
 
 tmp@0.0.x, tmp@^0.0.33:
 tmp@0.0.x, tmp@^0.0.33:
   version "0.0.33"
   version "0.0.33"
@@ -15396,7 +15464,7 @@ tsconfig@^7.0.0:
     strip-bom "^3.0.0"
     strip-bom "^3.0.0"
     strip-json-comments "^2.0.0"
     strip-json-comments "^2.0.0"
 
 
-"tslib@1 || 2", tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.1:
+"tslib@1 || 2", tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0, tslib@^2.5.0, tslib@^2.6.2:
   version "2.6.2"
   version "2.6.2"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
   integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@@ -15465,7 +15533,7 @@ type-coverage@^2.0.0:
     minimist "1"
     minimist "1"
     type-coverage-core "^2.22.0"
     type-coverage-core "^2.22.0"
 
 
-type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5:
+type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.8:
   version "4.0.8"
   version "4.0.8"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
@@ -15567,10 +15635,10 @@ uc.micro@^1.0.1, uc.micro@^1.0.5:
   resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
   resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac"
   integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
   integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
 
 
-ufo@^1.1.2:
-  version "1.1.2"
-  resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.1.2.tgz#d0d9e0fa09dece0c31ffd57bd363f030a35cfe76"
-  integrity sha512-TrY6DsjTQQgyS3E3dBaOXf0TpPD8u9FVrVYmKVegJuFw51n/YB9XPt+U6ydzFG5ZIN7+DIjPbNmXoBj9esYhgQ==
+ufo@^1.3.0:
+  version "1.3.2"
+  resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.2.tgz#c7d719d0628a1c80c006d2240e0d169f6e3c0496"
+  integrity sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==
 
 
 uglifycss@^0.0.29:
 uglifycss@^0.0.29:
   version "0.0.29"
   version "0.0.29"
@@ -15913,14 +15981,14 @@ v8-compile-cache@^2.3.0:
   resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
   resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
   integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
   integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
 
 
-v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1:
-  version "9.0.1"
-  resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4"
-  integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==
+v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1, v8-to-istanbul@^9.1.0:
+  version "9.1.3"
+  resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz#ea456604101cd18005ac2cae3cdd1aa058a6306b"
+  integrity sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==
   dependencies:
   dependencies:
     "@jridgewell/trace-mapping" "^0.3.12"
     "@jridgewell/trace-mapping" "^0.3.12"
     "@types/istanbul-lib-coverage" "^2.0.1"
     "@types/istanbul-lib-coverage" "^2.0.1"
-    convert-source-map "^1.6.0"
+    convert-source-map "^2.0.0"
 
 
 validate-npm-package-license@^3.0.1:
 validate-npm-package-license@^3.0.1:
   version "3.0.4"
   version "3.0.4"
@@ -15980,17 +16048,17 @@ vfile@^5.0.0, vfile@^5.1.0:
     unist-util-stringify-position "^3.0.0"
     unist-util-stringify-position "^3.0.0"
     vfile-message "^3.0.0"
     vfile-message "^3.0.0"
 
 
-vite-node@0.31.4:
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.31.4.tgz#0437f76c35fa83f0a868d3fb5896ca9e164291f5"
-  integrity sha512-uzL377GjJtTbuc5KQxVbDu2xfU/x0wVjUtXQR2ihS21q/NK6ROr4oG0rsSkBBddZUVCwzfx22in76/0ZZHXgkQ==
+vite-node@0.34.6:
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.34.6.tgz#34d19795de1498562bf21541a58edcd106328a17"
+  integrity sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==
   dependencies:
   dependencies:
     cac "^6.7.14"
     cac "^6.7.14"
     debug "^4.3.4"
     debug "^4.3.4"
-    mlly "^1.2.0"
-    pathe "^1.1.0"
+    mlly "^1.4.0"
+    pathe "^1.1.1"
     picocolors "^1.0.0"
     picocolors "^1.0.0"
-    vite "^3.0.0 || ^4.0.0"
+    vite "^3.0.0 || ^4.0.0 || ^5.0.0-0"
 
 
 vite-plugin-dts@^2.3.0:
 vite-plugin-dts@^2.3.0:
   version "2.3.0"
   version "2.3.0"
@@ -16017,14 +16085,14 @@ vite-tsconfig-paths@^4.2.0:
     globrex "^0.1.2"
     globrex "^0.1.2"
     tsconfck "^2.1.0"
     tsconfck "^2.1.0"
 
 
-"vite@^3.0.0 || ^4.0.0", vite@^4.4.0:
-  version "4.4.5"
-  resolved "https://registry.yarnpkg.com/vite/-/vite-4.4.5.tgz#ce9ae1a03841d2ec90f560744712495bf914f698"
-  integrity sha512-4m5kEtAWHYr0O1Fu7rZp64CfO1PsRGZlD3TAB32UmQlpd7qg15VF7ROqGN5CyqN7HFuwr7ICNM2+fDWRqFEKaA==
+"vite@^3.0.0 || ^4.0.0 || ^5.0.0-0", "vite@^3.1.0 || ^4.0.0 || ^5.0.0-0", vite@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.0.tgz#ec406295b4167ac3bc23e26f9c8ff559287cff26"
+  integrity sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==
   dependencies:
   dependencies:
     esbuild "^0.18.10"
     esbuild "^0.18.10"
-    postcss "^8.4.26"
-    rollup "^3.25.2"
+    postcss "^8.4.27"
+    rollup "^3.27.1"
   optionalDependencies:
   optionalDependencies:
     fsevents "~2.3.2"
     fsevents "~2.3.2"
 
 
@@ -16035,35 +16103,34 @@ vitest-mock-extended@^1.1.3:
   dependencies:
   dependencies:
     ts-essentials "^9.3.1"
     ts-essentials "^9.3.1"
 
 
-vitest@^0.31.4:
-  version "0.31.4"
-  resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.31.4.tgz#5abe02562675262949c10e40811f348a80f6b2a6"
-  integrity sha512-GoV0VQPmWrUFOZSg3RpQAPN+LPmHg2/gxlMNJlyxJihkz6qReHDV6b0pPDcqFLNEPya4tWJ1pgwUNP9MLmUfvQ==
+vitest@^0.34.6:
+  version "0.34.6"
+  resolved "https://registry.yarnpkg.com/vitest/-/vitest-0.34.6.tgz#44880feeeef493c04b7f795ed268f24a543250d7"
+  integrity sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==
   dependencies:
   dependencies:
     "@types/chai" "^4.3.5"
     "@types/chai" "^4.3.5"
     "@types/chai-subset" "^1.3.3"
     "@types/chai-subset" "^1.3.3"
     "@types/node" "*"
     "@types/node" "*"
-    "@vitest/expect" "0.31.4"
-    "@vitest/runner" "0.31.4"
-    "@vitest/snapshot" "0.31.4"
-    "@vitest/spy" "0.31.4"
-    "@vitest/utils" "0.31.4"
-    acorn "^8.8.2"
+    "@vitest/expect" "0.34.6"
+    "@vitest/runner" "0.34.6"
+    "@vitest/snapshot" "0.34.6"
+    "@vitest/spy" "0.34.6"
+    "@vitest/utils" "0.34.6"
+    acorn "^8.9.0"
     acorn-walk "^8.2.0"
     acorn-walk "^8.2.0"
     cac "^6.7.14"
     cac "^6.7.14"
-    chai "^4.3.7"
-    concordance "^5.0.4"
+    chai "^4.3.10"
     debug "^4.3.4"
     debug "^4.3.4"
     local-pkg "^0.4.3"
     local-pkg "^0.4.3"
-    magic-string "^0.30.0"
-    pathe "^1.1.0"
+    magic-string "^0.30.1"
+    pathe "^1.1.1"
     picocolors "^1.0.0"
     picocolors "^1.0.0"
-    std-env "^3.3.2"
+    std-env "^3.3.3"
     strip-literal "^1.0.1"
     strip-literal "^1.0.1"
     tinybench "^2.5.0"
     tinybench "^2.5.0"
-    tinypool "^0.5.0"
-    vite "^3.0.0 || ^4.0.0"
-    vite-node "0.31.4"
+    tinypool "^0.7.0"
+    vite "^3.1.0 || ^4.0.0 || ^5.0.0-0"
+    vite-node "0.34.6"
     why-is-node-running "^2.2.2"
     why-is-node-running "^2.2.2"
 
 
 void-elements@3.1.0:
 void-elements@3.1.0: