Przeglądaj źródła

Merge branch 'dev/5.0.x' into feat/81920-83419-fix-search-right-pane

NEEDLEMAN3\tatsu 4 lat temu
rodzic
commit
b90d6dd4db
67 zmienionych plików z 514 dodań i 466 usunięć
  1. 3 3
      .github/workflows/ci-slackbot-proxy.yml
  2. 5 26
      .github/workflows/ci.yml
  3. 2 2
      .github/workflows/list-unhealthy-branches.yml
  4. 1 1
      .github/workflows/release-slackbot-proxy.yml
  5. 2 2
      .github/workflows/release.yml
  6. 28 1
      CHANGELOG.md
  7. 1 3
      README.md
  8. 1 3
      README_JP.md
  9. 3 3
      package.json
  10. 1 1
      packages/app/bin/download-cdn-resources.ts
  11. 0 2
      packages/app/config/webpack.common.js
  12. 0 1
      packages/app/config/webpack.dev.dll.js
  13. 3 3
      packages/app/docker/Dockerfile
  14. 2 2
      packages/app/docker/README.md
  15. 3 4
      packages/app/package.json
  16. 1 1
      packages/app/src/client/services/ContextExtractor.tsx
  17. 0 1
      packages/app/src/client/services/PageContainer.js
  18. 1 1
      packages/app/src/client/util/interceptor/detach-code-blocks.js
  19. 1 1
      packages/app/src/client/util/interceptor/drawio-interceptor.js
  20. 1 1
      packages/app/src/components/PageEditor.jsx
  21. 1 1
      packages/app/src/components/PageEditor/MarkdownTableInterceptor.js
  22. 1 1
      packages/app/src/components/PageEditor/PreventMarkdownListInterceptor.js
  23. 1 1
      packages/app/src/components/SearchForm.jsx
  24. 1 1
      packages/app/src/components/SearchPage/DeleteSelectedPageGroup.tsx
  25. 21 9
      packages/app/src/components/SearchPage/SearchControl.tsx
  26. 1 1
      packages/app/src/components/SearchPage/SearchPageLayout.tsx
  27. 12 11
      packages/app/src/components/SearchPage/SearchResultListItem.tsx
  28. 6 1
      packages/app/src/components/SearchPage/SortControl.tsx
  29. 2 5
      packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx
  30. 1 1
      packages/app/src/server/models/GlobalNotificationSetting/index.js
  31. 32 20
      packages/app/src/server/models/obsolete-page.js
  32. 14 12
      packages/app/src/server/models/page.ts
  33. 1 1
      packages/app/src/server/routes/apiv3/app-settings.js
  34. 1 1
      packages/app/src/server/routes/apiv3/pages.js
  35. 9 7
      packages/app/src/server/routes/page.js
  36. 1 1
      packages/app/src/server/service/config-loader.ts
  37. 1 1
      packages/app/src/server/service/page.js
  38. 1 1
      packages/app/src/server/service/slack-command-handler/create-page-service.js
  39. 1 1
      packages/app/src/server/service/slack-command-handler/search.js
  40. 1 1
      packages/app/src/server/util/middlewares.js
  41. 1 1
      packages/app/src/server/util/swigFunctions.js
  42. 5 0
      packages/app/src/server/views/layout/layout.html
  43. 0 1
      packages/app/src/server/views/widget/page_content.html
  44. 1 1
      packages/app/src/services/cdn-resources-service.js
  45. 0 129
      packages/app/src/test/integration/service/page.test.js
  46. 194 0
      packages/app/src/test/integration/service/v5-migration.test.js
  47. 0 27
      packages/core/README.md
  48. 11 14
      packages/core/src/index.js
  49. 2 4
      packages/core/src/plugin/service/tag-cache-manager.js
  50. 1 3
      packages/core/src/service/localstorage-manager.js
  51. 1 3
      packages/core/src/utils/basic-interceptor.js
  52. 1 1
      packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPostRenderInterceptor.js
  53. 1 1
      packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPreRenderInterceptor.js
  54. 1 1
      packages/plugin-attachment-refs/src/client/js/util/RefsContext.js
  55. 1 1
      packages/plugin-attachment-refs/src/client/js/util/TagCacheManagerFactory.js
  56. 1 1
      packages/plugin-attachment-refs/src/server/routes/refs.js
  57. 1 1
      packages/plugin-lsx/src/client/js/components/Lsx.jsx
  58. 1 1
      packages/plugin-lsx/src/client/js/components/LsxPageList/LsxPage.jsx
  59. 1 1
      packages/plugin-lsx/src/client/js/util/Interceptor/LsxLogoutInterceptor.js
  60. 1 1
      packages/plugin-lsx/src/client/js/util/Interceptor/LsxPostRenderInterceptor.js
  61. 1 1
      packages/plugin-lsx/src/client/js/util/Interceptor/LsxPreRenderInterceptor.js
  62. 1 1
      packages/plugin-lsx/src/client/js/util/LsxContext.js
  63. 1 18
      packages/plugin-lsx/src/client/js/util/TagCacheManagerFactory.js
  64. 1 1
      packages/plugin-lsx/src/server/routes/lsx.js
  65. 3 3
      packages/slackbot-proxy/docker/Dockerfile
  66. 0 27
      packages/ui/README.md
  67. 114 83
      yarn.lock

+ 3 - 3
.github/workflows/ci-slackbot-proxy.yml

@@ -15,7 +15,7 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -58,7 +58,7 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     services:
     services:
       mysql:
       mysql:
@@ -118,7 +118,7 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     services:
     services:
       mysql:
       mysql:

+ 5 - 26
.github/workflows/ci.yml

@@ -15,7 +15,7 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -58,17 +58,13 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     services:
     services:
       mongodb:
       mongodb:
         image: mongo:4.4
         image: mongo:4.4
         ports:
         ports:
         - 27017/tcp
         - 27017/tcp
-      mongodb36:
-        image: mongo:3.6
-        ports:
-        - 27017/tcp
 
 
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -94,12 +90,6 @@ jobs:
         yarn test
         yarn test
       env:
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_test
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi_test
-    - name: yarn test with MongoDB 3.6
-      working-directory: ./packages/app
-      run: |
-        yarn test
-      env:
-        MONGO_URI: mongodb://localhost:${{ job.services.mongodb36.ports['27017'] }}/growi_test
 
 
     - name: Upload coverage report as artifact
     - name: Upload coverage report as artifact
       uses: actions/upload-artifact@v2
       uses: actions/upload-artifact@v2
@@ -123,7 +113,7 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [14.x]
+        node-version: [16.x]
 
 
     services:
     services:
       mongodb:
       mongodb:
@@ -173,17 +163,13 @@ jobs:
 
 
     strategy:
     strategy:
       matrix:
       matrix:
-        node-version: [12.x, 14.x]
+        node-version: [14.x, 16.x]
 
 
     services:
     services:
       mongodb:
       mongodb:
         image: mongo:4.4
         image: mongo:4.4
         ports:
         ports:
         - 27017/tcp
         - 27017/tcp
-      mongodb36:
-        image: mongo:3.6
-        ports:
-        - 27017/tcp
 
 
     steps:
     steps:
     - uses: actions/checkout@v2
     - uses: actions/checkout@v2
@@ -209,7 +195,7 @@ jobs:
       run: |
       run: |
         yarn lerna run build
         yarn lerna run build
       env:
       env:
-        ANALYZE_BUNDLE_SIZE: ${{ matrix.node-version == '14.x' }}
+        ANALYZE_BUNDLE_SIZE: ${{ matrix.node-version == '16.x' }}
     - name: lerna bootstrap --production
     - name: lerna bootstrap --production
       run: |
       run: |
         npx lerna bootstrap -- --production
         npx lerna bootstrap -- --production
@@ -229,13 +215,6 @@ jobs:
         yarn server:ci
         yarn server:ci
       env:
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
-    - name: yarn server:ci with MongoDB 3.6
-      working-directory: ./packages/app
-      run: |
-        cp config/ci/.env.local.for-ci .env.production.local
-        yarn server:ci
-      env:
-        MONGO_URI: mongodb://localhost:${{ job.services.mongodb36.ports['27017'] }}/growi-${{ steps.getdbname.outputs.suffix }}
 
 
     - name: Upload report as artifact
     - name: Upload report as artifact
       uses: actions/upload-artifact@v2
       uses: actions/upload-artifact@v2

+ 2 - 2
.github/workflows/list-unhealthy-branches.yml

@@ -14,9 +14,9 @@ jobs:
       with:
       with:
         fetch-depth: 0
         fetch-depth: 0
 
 
-    - uses: actions/setup-node@v2-beta
+    - uses: actions/setup-node@v2
       with:
       with:
-        node-version: '14'
+        node-version: '16'
 
 
     - name: List branches
     - name: List branches
       id: list-branches
       id: list-branches

+ 1 - 1
.github/workflows/release-slackbot-proxy.yml

@@ -108,7 +108,7 @@ jobs:
 
 
     - uses: actions/setup-node@v2
     - uses: actions/setup-node@v2
       with:
       with:
-        node-version: '14'
+        node-version: '16'
         cache: 'yarn'
         cache: 'yarn'
         cache-dependency-path: '**/yarn.lock'
         cache-dependency-path: '**/yarn.lock'
 
 

+ 2 - 2
.github/workflows/release.yml

@@ -24,7 +24,7 @@ jobs:
 
 
     - uses: actions/setup-node@v2
     - uses: actions/setup-node@v2
       with:
       with:
-        node-version: '14'
+        node-version: '16'
         cache: 'yarn'
         cache: 'yarn'
         cache-dependency-path: '**/yarn.lock'
         cache-dependency-path: '**/yarn.lock'
 
 
@@ -85,7 +85,7 @@ jobs:
 
 
     - uses: actions/setup-node@v2
     - uses: actions/setup-node@v2
       with:
       with:
-        node-version: '14'
+        node-version: '16'
         cache: 'yarn'
         cache: 'yarn'
         cache-dependency-path: '**/yarn.lock'
         cache-dependency-path: '**/yarn.lock'
 
 

+ 28 - 1
CHANGELOG.md

@@ -1,9 +1,36 @@
 # Changelog
 # Changelog
 
 
-## [Unreleased](https://github.com/weseek/growi/compare/v4.5.2...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v4.5.3...HEAD)
 
 
 *Please do not manually update this file. We've automated the process.*
 *Please do not manually update this file. We've automated the process.*
 
 
+## [v4.5.3](https://github.com/weseek/growi/compare/v4.5.2...v4.5.3) - 2021-12-17
+
+### 💎 Features
+
+- feat: user activation by email (#4862) @kaoritokashiki
+
+### 🚀 Improvement
+
+- imprv: Use SWR for isSlackEnabled (#4827) @stevenfukase
+- imprv: Disable rubber band scroll for Mac & iOS users (#4834) @hakumizuki
+- imprv: Omit atlaskit and implement sidebar only with original codes (#4598) @yuki-takei
+
+### 🐛 Bug Fixes
+
+- fix: GROWI Bot search command after transplanting search service from dev/5.0.x (#4916) @hakumizuki
+- fix: Set min-height to sidebar scroll target (#4884) @yuki-takei
+
+### 🧰 Maintenance
+
+- support: fix dependabot alert for kind-of (#4891) @LuqmanHakim-Grune
+- support: fix dependabot alert for ini (#4892) @LuqmanHakim-Grune
+- support: fix and debug mixin-deep dependabot alert (#4867) @LuqmanHakim-Grune
+- support: dependabot alert xmlhttprequest-ssl (#4878) @mudana-grune
+- support: Transplant search service from dev/5.0.x (#4869) @hakumizuki
+- support: dependabot alert set-value (#4864) @LuqmanHakim-Grune
+- ci(deps): bump aws-sdk from 2.179.0 to 2.1044.0 (#4821) @dependabot
+
 ## [v4.5.2](https://github.com/weseek/growi/compare/v4.5.1...v4.5.2) - 2021-12-06
 ## [v4.5.2](https://github.com/weseek/growi/compare/v4.5.1...v4.5.2) - 2021-12-06
 
 
 ### 🐛 Bug Fixes
 ### 🐛 Bug Fixes

+ 1 - 3
README.md

@@ -82,9 +82,7 @@ See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/ad
 
 
 ## Dependencies
 ## Dependencies
 
 
-- Node.js v12.x or v14.x
-- npm 6.x
-- yarn
+- Node.js v14.x or v16.x
 - MongoDB 4.x
 - MongoDB 4.x
 
 
 ### Optional Dependencies
 ### Optional Dependencies

+ 1 - 3
README_JP.md

@@ -81,9 +81,7 @@ Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/mig
 
 
 ## 依存関係
 ## 依存関係
 
 
-- Node.js v12.x or v14.x
-- npm 6.x
-- yarn
+- Node.js v14.x or v16.x
 - MongoDB 4.x
 - MongoDB 4.x
 
 
 ### オプションの依存関係
 ### オプションの依存関係

+ 3 - 3
package.json

@@ -75,8 +75,8 @@
     "typescript": "^4.2.3"
     "typescript": "^4.2.3"
   },
   },
   "engines": {
   "engines": {
-    "node": "^12 || ^14",
-    "npm": ">=6.11.3 <7",
-    "yarn": ">=1.19.1 <2"
+    "node": "^14 || ^16",
+    "npm": ">=6.14 <7 || >=8.1 < 9",
+    "yarn": ">=1.22 <2"
   }
   }
 }
 }

+ 1 - 1
packages/app/bin/download-cdn-resources.ts

@@ -3,7 +3,7 @@
  *
  *
  * @author Yuki Takei <yuki@weseek.co.jp>
  * @author Yuki Takei <yuki@weseek.co.jp>
  */
  */
-import { envUtils } from 'growi-commons';
+import { envUtils } from '@growi/core';
 
 
 import CdnResourcesDownloader from './cdn/cdn-resources-downloader';
 import CdnResourcesDownloader from './cdn/cdn-resources-downloader';
 import loggerFactory from '../src/utils/logger';
 import loggerFactory from '../src/utils/logger';

+ 0 - 2
packages/app/config/webpack.common.js

@@ -83,8 +83,6 @@ module.exports = (options) => {
           exclude: {
           exclude: {
             test: /node_modules/,
             test: /node_modules/,
             exclude: [ // include as a result
             exclude: [ // include as a result
-              { test: /node_modules\/growi-plugin-/ },
-              /node_modules\/growi-commons/,
               /node_modules\/codemirror/,
               /node_modules\/codemirror/,
             ],
             ],
           },
           },

+ 0 - 1
packages/app/config/webpack.dev.dll.js

@@ -17,7 +17,6 @@ module.exports = {
       'diff2html',
       'diff2html',
       'debug',
       'debug',
       'entities',
       'entities',
-      'growi-commons',
       'i18next', 'i18next-browser-languagedetector',
       'i18next', 'i18next-browser-languagedetector',
       'jquery-slimscroll',
       'jquery-slimscroll',
       'lodash', 'pako',
       'lodash', 'pako',

+ 3 - 3
packages/app/docker/Dockerfile

@@ -7,7 +7,7 @@ ARG flavor=default
 ##
 ##
 ## deps-resolver
 ## deps-resolver
 ##
 ##
-FROM node:14-slim AS deps-resolver
+FROM node:16-slim AS deps-resolver
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 
 ENV appDir /opt/growi
 ENV appDir /opt/growi
@@ -51,7 +51,7 @@ RUN tar cf node_modules.tar \
 ##
 ##
 ## prebuilder-default
 ## prebuilder-default
 ##
 ##
-FROM node:14-slim AS prebuilder-default
+FROM node:16-slim AS prebuilder-default
 
 
 ENV appDir /opt/growi
 ENV appDir /opt/growi
 
 
@@ -124,7 +124,7 @@ RUN tar cf packages.tar \
 ##
 ##
 ## release
 ## release
 ##
 ##
-FROM node:14-slim
+FROM node:16-slim
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 
 ENV NODE_ENV production
 ENV NODE_ENV production

+ 2 - 2
packages/app/docker/README.md

@@ -12,8 +12,8 @@ Supported tags and respective Dockerfile links
 
 
 * [`5.0.0`, `5.0`, `5`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.0/docker/Dockerfile)
 * [`5.0.0`, `5.0`, `5`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.0/docker/Dockerfile)
 * [`5.0.0-nocdn`, `5.0-nocdn`, `5-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.0/docker/Dockerfile)
 * [`5.0.0-nocdn`, `5.0-nocdn`, `5-nocdn`, `latest-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v5.0.0/docker/Dockerfile)
-* [`4.5.2`, `4.5`, `4` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.2/docker/Dockerfile)
-* [`4.5.2-nocdn`, `4.5-nocdn`, `4-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.2/docker/Dockerfile)
+* [`4.5.3`, `4.5`, `4` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.3/docker/Dockerfile)
+* [`4.5.3-nocdn`, `4.5-nocdn`, `4-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.5.3/docker/Dockerfile)
 * [`4.4.13`, `4.4` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.13/docker/Dockerfile)
 * [`4.4.13`, `4.4` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.13/docker/Dockerfile)
 * [`4.4.13-nocdn`, `4.4-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.13/docker/Dockerfile)
 * [`4.4.13-nocdn`, `4.4-nocdn` (Dockerfile)](https://github.com/weseek/growi/blob/v4.4.13/docker/Dockerfile)
 
 

+ 3 - 4
packages/app/package.json

@@ -63,8 +63,8 @@
     "@growi/plugin-lsx": "^5.0.0-RC.0",
     "@growi/plugin-lsx": "^5.0.0-RC.0",
     "@growi/plugin-pukiwiki-like-linker": "^5.0.0-RC.0",
     "@growi/plugin-pukiwiki-like-linker": "^5.0.0-RC.0",
     "@growi/slack": "^5.0.0-RC.0",
     "@growi/slack": "^5.0.0-RC.0",
-    "@promster/express": "^5.1.0",
-    "@promster/server": "^6.0.3",
+    "@promster/express": "^7.0.2",
+    "@promster/server": "^7.0.4",
     "@slack/events-api": "^3.0.0",
     "@slack/events-api": "^3.0.0",
     "@slack/web-api": "^6.2.4",
     "@slack/web-api": "^6.2.4",
     "@slack/webhook": "^6.0.0",
     "@slack/webhook": "^6.0.0",
@@ -99,7 +99,6 @@
     "express-validator": "^6.1.1",
     "express-validator": "^6.1.1",
     "express-webpack-assets": "^0.1.0",
     "express-webpack-assets": "^0.1.0",
     "graceful-fs": "^4.1.11",
     "graceful-fs": "^4.1.11",
-    "growi-commons": "^5.0.4",
     "helmet": "^4.6.0",
     "helmet": "^4.6.0",
     "http-errors": "~1.8.0",
     "http-errors": "~1.8.0",
     "i18next": "^20.3.2",
     "i18next": "^20.3.2",
@@ -148,7 +147,7 @@
     "unzipper": "^0.10.5",
     "unzipper": "^0.10.5",
     "url-join": "^4.0.0",
     "url-join": "^4.0.0",
     "validator": "^13.6.0",
     "validator": "^13.6.0",
-    "ws": "^7.4.6",
+    "ws": "^8.3.0",
     "xss": "^1.0.6"
     "xss": "^1.0.6"
   },
   },
   "// comments for defDependencies": {
   "// comments for defDependencies": {

+ 1 - 1
packages/app/src/client/services/ContextExtractor.tsx

@@ -60,7 +60,7 @@ const ContextExtractorOnce: FC = () => {
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
   const hasDraftOnHackmd = !!mainContent?.getAttribute('data-page-has-draft-on-hackmd');
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const creator = JSON.parse(mainContent?.getAttribute('data-page-creator') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
   const revisionAuthor = JSON.parse(mainContent?.getAttribute('data-page-revision-author') || jsonNull);
-  const targetAndAncestors = JSON.parse(mainContent?.getAttribute('data-target-and-ancestors') || jsonNull);
+  const targetAndAncestors = JSON.parse(document.getElementById('growi-pagetree-target-and-ancestors')?.textContent || jsonNull);
   const slackChannels = mainContent?.getAttribute('data-slack-channels') || '';
   const slackChannels = mainContent?.getAttribute('data-slack-channels') || '';
 
 
   /*
   /*

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

@@ -82,7 +82,6 @@ export default class PageContainer extends Container {
       templateTagData: mainContent.getAttribute('data-template-tags') || null,
       templateTagData: mainContent.getAttribute('data-template-tags') || null,
       shareLinksNumber: mainContent.getAttribute('data-share-links-number'),
       shareLinksNumber: mainContent.getAttribute('data-share-links-number'),
       shareLinkId: JSON.parse(mainContent.getAttribute('data-share-link-id') || null),
       shareLinkId: JSON.parse(mainContent.getAttribute('data-share-link-id') || null),
-      targetAndAncestors: JSON.parse(mainContent.getAttribute('data-target-and-ancestors') || null),
 
 
       // latest(on remote) information
       // latest(on remote) information
       remoteRevisionId: revisionId,
       remoteRevisionId: revisionId,

+ 1 - 1
packages/app/src/client/util/interceptor/detach-code-blocks.js

@@ -1,4 +1,4 @@
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 

+ 1 - 1
packages/app/src/client/util/interceptor/drawio-interceptor.js

@@ -2,7 +2,7 @@
 import React from 'react';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
 import { Provider } from 'unstated';
 import { Provider } from 'unstated';
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import Drawio from '~/components/Drawio';
 import Drawio from '~/components/Drawio';
 
 

+ 1 - 1
packages/app/src/components/PageEditor.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 import detectIndent from 'detect-indent';
 import detectIndent from 'detect-indent';
 
 
 import { throttle, debounce } from 'throttle-debounce';
 import { throttle, debounce } from 'throttle-debounce';
-import { envUtils } from 'growi-commons';
+import { envUtils } from '@growi/core';
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 
 import AppContainer from '~/client/services/AppContainer';
 import AppContainer from '~/client/services/AppContainer';

+ 1 - 1
packages/app/src/components/PageEditor/MarkdownTableInterceptor.js

@@ -1,4 +1,4 @@
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import mtu from './MarkdownTableUtil';
 import mtu from './MarkdownTableUtil';
 import MarkdownTable from '~/client/models/MarkdownTable';
 import MarkdownTable from '~/client/models/MarkdownTable';

+ 1 - 1
packages/app/src/components/PageEditor/PreventMarkdownListInterceptor.js

@@ -1,4 +1,4 @@
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import mlu from './MarkdownListUtil';
 import mlu from './MarkdownListUtil';
 
 

+ 1 - 1
packages/app/src/components/SearchForm.jsx

@@ -40,7 +40,7 @@ class SearchForm extends React.Component {
 
 
     // navigate to page
     // navigate to page
     if (page != null) {
     if (page != null) {
-      window.location = page.path;
+      window.location = page.pageData._id;
     }
     }
   }
   }
 
 

+ 1 - 1
packages/app/src/components/SearchPage/DeleteSelectedPageGroup.tsx

@@ -34,7 +34,7 @@ const DeleteSelectedPageGroup:FC<Props> = (props:Props) => {
         id="check-all-pages"
         id="check-all-pages"
         type="checkbox"
         type="checkbox"
         name="check-all-pages"
         name="check-all-pages"
-        className="custom-control custom-checkbox ml-2 align-self-center"
+        className="custom-control custom-checkbox align-self-center"
         disabled={props.isSelectAllCheckboxDisabled}
         disabled={props.isSelectAllCheckboxDisabled}
         onClick={onClickCheckbox}
         onClick={onClickCheckbox}
         checked={selectAllCheckboxType !== CheckboxType.NONE_CHECKED}
         checked={selectAllCheckboxType !== CheckboxType.NONE_CHECKED}

+ 21 - 9
packages/app/src/components/SearchPage/SearchControl.tsx

@@ -79,6 +79,16 @@ const SearchControl: FC <Props> = (props: Props) => {
     );
     );
   };
   };
 
 
+  const renderSortControl = () => {
+    return (
+      <SortControl
+        sort={props.sort}
+        order={props.order}
+        onChangeSortInvoked={onChangeSortInvoked}
+      />
+    );
+  };
+
   return (
   return (
     <div className="position-sticky fixed-top shadow-sm">
     <div className="position-sticky fixed-top shadow-sm">
       <div className="search-page-nav d-flex py-3 align-items-center">
       <div className="search-page-nav d-flex py-3 align-items-center">
@@ -89,16 +99,14 @@ const SearchControl: FC <Props> = (props: Props) => {
             onSearchFormChanged={props.onSearchInvoked}
             onSearchFormChanged={props.onSearchInvoked}
           />
           />
         </div>
         </div>
-        <div className="mr-4 d-flex">
-          <SortControl
-            sort={props.sort}
-            order={props.order}
-            onChangeSortInvoked={onChangeSortInvoked}
-          />
+
+        {/* sort option: show when screen is larger than lg */}
+        <div className="mr-4 d-lg-flex d-none">
+          {renderSortControl()}
         </div>
         </div>
       </div>
       </div>
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
       {/* TODO: replace the following elements deleteAll button , relevance button and include specificPath button component */}
-      <div className="search-control d-flex align-items-center py-md-2 py-3 px-md-3 px-3 border-bottom border-gray">
+      <div className="search-control d-flex align-items-center py-md-2 py-3 px-md-4 px-3 border-bottom border-gray">
         <div className="d-flex pl-md-2">
         <div className="d-flex pl-md-2">
           {/* Todo: design will be fixed in #80324. Function will be implemented in #77525 */}
           {/* Todo: design will be fixed in #80324. Function will be implemented in #77525 */}
           <DeleteSelectedPageGroup
           <DeleteSelectedPageGroup
@@ -108,8 +116,12 @@ const SearchControl: FC <Props> = (props: Props) => {
             onClickSelectAllCheckbox={props.onClickSelectAllCheckbox}
             onClickSelectAllCheckbox={props.onClickSelectAllCheckbox}
           />
           />
         </div>
         </div>
-        {/** filter option */}
-        <div className="d-lg-none ml-auto">
+        {/* sort option: show when screen is smaller than lg */}
+        <div className="mr-md-4 mr-2 d-flex d-lg-none ml-auto">
+          {renderSortControl()}
+        </div>
+        {/* filter option */}
+        <div className="d-lg-none">
           <button
           <button
             type="button"
             type="button"
             className="btn"
             className="btn"

+ 1 - 1
packages/app/src/components/SearchPage/SearchPageLayout.tsx

@@ -45,7 +45,7 @@ const SearchPageLayout: FC<Props> = (props: Props) => {
                 {/* Todo: replace "1-10" to the appropriate value */}
                 {/* Todo: replace "1-10" to the appropriate value */}
                 {renderShowingPageCountInfo()}
                 {renderShowingPageCountInfo()}
               </div>
               </div>
-              <div className="input-group search-result-select-group ml-4">
+              <div className="input-group search-result-select-group ml-4 d-lg-flex d-none">
                 <div className="input-group-prepend">
                 <div className="input-group-prepend">
                   <label className="input-group-text text-secondary" htmlFor="inputGroupSelect01">{t('search_result.number_of_list_to_display')}</label>
                   <label className="input-group-text text-secondary" htmlFor="inputGroupSelect01">{t('search_result.number_of_list_to_display')}</label>
                 </div>
                 </div>

+ 12 - 11
packages/app/src/components/SearchPage/SearchResultListItem.tsx

@@ -3,7 +3,7 @@ import React, { FC, memo } from 'react';
 import Clamp from 'react-multiline-clamp';
 import Clamp from 'react-multiline-clamp';
 
 
 import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui';
 import { UserPicture, PageListMeta, PagePathLabel } from '@growi/ui';
-import { pagePathUtils } from '@growi/core';
+import { pagePathUtils, DevidedPagePath } from '@growi/core';
 import { useIsDeviceSmallerThanMd } from '~/stores/ui';
 import { useIsDeviceSmallerThanMd } from '~/stores/ui';
 
 
 import { IPageSearchResultData } from '../../interfaces/search';
 import { IPageSearchResultData } from '../../interfaces/search';
@@ -30,8 +30,7 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
 
 
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
   const { data: isDeviceSmallerThanMd } = useIsDeviceSmallerThanMd();
 
 
-  // Add prefix 'id_' in pageId, because scrollspy of bootstrap doesn't work when the first letter of id attr of target component is numeral.
-  const pageId = `#${pageData._id}`;
+  const pagePath: DevidedPagePath = new DevidedPagePath(pageData.path, true);
 
 
   const pageTitle = (
   const pageTitle = (
     <PagePathLabel
     <PagePathLabel
@@ -56,9 +55,8 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
       key={pageData._id}
       key={pageData._id}
       className={`w-100 page-list-li search-result-item border-bottom ${responsiveListStyleClass}`}
       className={`w-100 page-list-li search-result-item border-bottom ${responsiveListStyleClass}`}
     >
     >
-      <a
-        className="d-block h-100"
-        href={pageId}
+      <div
+        className="h-100 text-break"
         onClick={() => onClickSearchResultItem != null && onClickSearchResultItem(pageData._id)}
         onClick={() => onClickSearchResultItem != null && onClickSearchResultItem(pageData._id)}
       >
       >
         <div className="d-flex h-100">
         <div className="d-flex h-100">
@@ -80,7 +78,7 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
             {/* page path */}
             {/* page path */}
             <h6 className="mb-1 py-1">
             <h6 className="mb-1 py-1">
               <i className="icon-fw icon-home"></i>
               <i className="icon-fw icon-home"></i>
-              {pagePathElem}
+              <a href={pagePath.isRoot ? pagePath.latter : pagePath.former}>{pagePathElem}</a>
             </h6>
             </h6>
             <div className="d-flex align-items-center mb-2">
             <div className="d-flex align-items-center mb-2">
               {/* Picture */}
               {/* Picture */}
@@ -88,9 +86,12 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
                 <UserPicture user={pageData.lastUpdateUser} size="sm" />
                 <UserPicture user={pageData.lastUpdateUser} size="sm" />
               </span>
               </span>
               {/* page title */}
               {/* page title */}
-              <span className="py-1 h5 mr-2 mb-0">
-                {pageTitle}
-              </span>
+              <Clamp lines={1}>
+                <span className="py-1 h5 mr-2 mb-0">
+                  <a href={`/${pageData._id}`}>{pageTitle}</a>
+                </span>
+              </Clamp>
+
               {/* page meta */}
               {/* page meta */}
               <div className="d-none d-md-flex item-meta py-0 px-1">
               <div className="d-none d-md-flex item-meta py-0 px-1">
                 <PageListMeta page={pageData} bookmarkCount={pageMeta.bookmarkCount} />
                 <PageListMeta page={pageData} bookmarkCount={pageMeta.bookmarkCount} />
@@ -119,7 +120,7 @@ const SearchResultListItem: FC<Props> = memo((props:Props) => {
           </div>
           </div>
         </div>
         </div>
         {/* TODO: adjust snippet position */}
         {/* TODO: adjust snippet position */}
-      </a>
+      </div>
     </li>
     </li>
   );
   );
 });
 });

+ 6 - 1
packages/app/src/components/SearchPage/SortControl.tsx

@@ -26,7 +26,12 @@ const SortControl: FC <Props> = (props: Props) => {
   };
   };
 
 
   const renderSortItem = (sort, order) => {
   const renderSortItem = (sort, order) => {
-    return <div className="d-flex align-items-center"><span className="mr-3">{t(`search_result.sort_axis.${sort}`)}</span>{renderOrderIcon(order)}</div>;
+    return (
+      <div className="d-flex align-items-center justify-content-between w-100">
+        <span className="mr-3">{t(`search_result.sort_axis.${sort}`)}</span>
+        {renderOrderIcon(order)}
+      </div>
+    );
   };
   };
 
 
   return (
   return (

+ 2 - 5
packages/app/src/components/Sidebar/PageTree/ItemsTree.tsx

@@ -1,4 +1,4 @@
-import React, { FC, useState } from 'react';
+import React, { FC } from 'react';
 
 
 import { IPageHasId } from '../../../interfaces/page';
 import { IPageHasId } from '../../../interfaces/page';
 import { ItemNode } from './ItemNode';
 import { ItemNode } from './ItemNode';
@@ -93,8 +93,6 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
   const { data: ancestorsChildrenData, error: error1 } = useSWRxPageAncestorsChildren(targetPath);
   const { data: ancestorsChildrenData, error: error1 } = useSWRxPageAncestorsChildren(targetPath);
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
   const { data: rootPageData, error: error2 } = useSWRxRootPage();
 
 
-  const [isRenderedCompletely, setRenderedCompletely] = useState(false);
-
   const DeleteModal = (
   const DeleteModal = (
     <PageDeleteModal
     <PageDeleteModal
       isOpen={isDeleteModalOpen}
       isOpen={isDeleteModalOpen}
@@ -114,9 +112,8 @@ const ItemsTree: FC<ItemsTreeProps> = (props: ItemsTreeProps) => {
   /*
   /*
    * Render completely
    * Render completely
    */
    */
-  if (!isRenderedCompletely && ancestorsChildrenData != null && rootPageData != null) {
+  if (ancestorsChildrenData != null && rootPageData != null) {
     const initialNode = generateInitialNodeAfterResponse(ancestorsChildrenData.ancestorsChildren, new ItemNode(rootPageData.rootPage));
     const initialNode = generateInitialNodeAfterResponse(ancestorsChildrenData.ancestorsChildren, new ItemNode(rootPageData.rootPage));
-    setRenderedCompletely(true); // render once
     return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetId, onClickDeleteByPage);
     return renderByInitialNode(initialNode, DeleteModal, isEnableActions, targetId, onClickDeleteByPage);
   }
   }
 
 

+ 1 - 1
packages/app/src/server/models/GlobalNotificationSetting/index.js

@@ -1,6 +1,6 @@
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
 const nodePath = require('path');
 const nodePath = require('path');
-const { pathUtils } = require('growi-commons');
+const { pathUtils } = require('@growi/core');
 
 
 /**
 /**
  * parent schema for GlobalNotificationSetting model
  * parent schema for GlobalNotificationSetting model

+ 32 - 20
packages/app/src/server/models/obsolete-page.js

@@ -12,7 +12,7 @@ const urljoin = require('url-join');
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
 const differenceInYears = require('date-fns/differenceInYears');
 const differenceInYears = require('date-fns/differenceInYears');
 
 
-const { pathUtils } = require('growi-commons');
+const { pathUtils } = require('@growi/core');
 const escapeStringRegexp = require('escape-string-regexp');
 const escapeStringRegexp = require('escape-string-regexp');
 
 
 const { isTopPage, isTrashPage } = pagePathUtils;
 const { isTopPage, isTrashPage } = pagePathUtils;
@@ -79,8 +79,17 @@ const populateDataToShowRevision = (page, userPublicFields) => {
 
 
 export class PageQueryBuilder {
 export class PageQueryBuilder {
 
 
-  constructor(query) {
+  constructor(query, includeEmpty = false) {
     this.query = query;
     this.query = query;
+    if (!includeEmpty) {
+      this.query = this.query
+        .and({
+          $or: [
+            { isEmpty: false },
+            { isEmpty: null }, // for v4 compatibility
+          ],
+        });
+    }
   }
   }
 
 
   addConditionToExcludeTrashed() {
   addConditionToExcludeTrashed() {
@@ -592,7 +601,7 @@ export const getPageSchema = (crowi) => {
    * @param {User} user User instance
    * @param {User} user User instance
    * @param {UserGroup[]} userGroups List of UserGroup instances
    * @param {UserGroup[]} userGroups List of UserGroup instances
    */
    */
-  pageSchema.statics.findByIdAndViewer = async function(id, user, userGroups) {
+  pageSchema.statics.findByIdAndViewer = async function(id, user, userGroups, includeEmpty = false) {
     const baseQuery = this.findOne({ _id: id });
     const baseQuery = this.findOne({ _id: id });
 
 
     let relatedUserGroups = userGroups;
     let relatedUserGroups = userGroups;
@@ -602,18 +611,21 @@ export const getPageSchema = (crowi) => {
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
     }
 
 
-    const queryBuilder = new PageQueryBuilder(baseQuery);
+    const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups, true);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups, true);
 
 
-    return await queryBuilder.query.exec();
+    return queryBuilder.query.exec();
   };
   };
 
 
   // find page by path
   // find page by path
-  pageSchema.statics.findByPath = function(path) {
+  pageSchema.statics.findByPath = function(path, includeEmpty = false) {
     if (path == null) {
     if (path == null) {
       return null;
       return null;
     }
     }
-    return this.findOne({ path });
+
+    const builder = new PageQueryBuilder(this.findOne({ path }), includeEmpty);
+
+    return builder.query.exec();
   };
   };
 
 
   /**
   /**
@@ -621,7 +633,7 @@ export const getPageSchema = (crowi) => {
    * @param {User} user User instance
    * @param {User} user User instance
    * @param {UserGroup[]} userGroups List of UserGroup instances
    * @param {UserGroup[]} userGroups List of UserGroup instances
    */
    */
-  pageSchema.statics.findAncestorByPathAndViewer = async function(path, user, userGroups) {
+  pageSchema.statics.findAncestorByPathAndViewer = async function(path, user, userGroups, includeEmpty = false) {
     if (path == null) {
     if (path == null) {
       throw new Error('path is required.');
       throw new Error('path is required.');
     }
     }
@@ -642,10 +654,10 @@ export const getPageSchema = (crowi) => {
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
       relatedUserGroups = await UserGroupRelation.findAllUserGroupIdsRelatedToUser(user);
     }
     }
 
 
-    const queryBuilder = new PageQueryBuilder(baseQuery);
+    const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups);
     queryBuilder.addConditionToFilteringByViewer(user, relatedUserGroups);
 
 
-    return await queryBuilder.query.exec();
+    return queryBuilder.query.exec();
   };
   };
 
 
   pageSchema.statics.findByRedirectTo = function(path) {
   pageSchema.statics.findByRedirectTo = function(path) {
@@ -655,22 +667,22 @@ export const getPageSchema = (crowi) => {
   /**
   /**
    * find pages that is match with `path` and its descendants
    * find pages that is match with `path` and its descendants
    */
    */
-  pageSchema.statics.findListWithDescendants = async function(path, user, option = {}) {
-    const builder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListWithDescendants = async function(path, user, option = {}, includeEmpty = false) {
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListWithDescendants(path, option);
     builder.addConditionToListWithDescendants(path, option);
 
 
-    return await findListFromBuilderAndViewer(builder, user, false, option);
+    return findListFromBuilderAndViewer(builder, user, false, option);
   };
   };
 
 
   /**
   /**
    * find pages that is match with `path` and its descendants whitch user is able to manage
    * find pages that is match with `path` and its descendants whitch user is able to manage
    */
    */
-  pageSchema.statics.findManageableListWithDescendants = async function(page, user, option = {}) {
+  pageSchema.statics.findManageableListWithDescendants = async function(page, user, option = {}, includeEmpty = false) {
     if (user == null) {
     if (user == null) {
       return null;
       return null;
     }
     }
 
 
-    const builder = new PageQueryBuilder(this.find());
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListWithDescendants(page.path, option);
     builder.addConditionToListWithDescendants(page.path, option);
     builder.addConditionToExcludeRedirect();
     builder.addConditionToExcludeRedirect();
 
 
@@ -691,11 +703,11 @@ export const getPageSchema = (crowi) => {
   /**
   /**
    * find pages that start with `path`
    * find pages that start with `path`
    */
    */
-  pageSchema.statics.findListByStartWith = async function(path, user, option) {
-    const builder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListByStartWith = async function(path, user, option, includeEmpty = false) {
+    const builder = new PageQueryBuilder(this.find(), includeEmpty);
     builder.addConditionToListByStartWith(path, option);
     builder.addConditionToListByStartWith(path, option);
 
 
-    return await findListFromBuilderAndViewer(builder, user, false, option);
+    return findListFromBuilderAndViewer(builder, user, false, option);
   };
   };
 
 
   /**
   /**
@@ -1102,8 +1114,8 @@ export const getPageSchema = (crowi) => {
     await this.removeRedirectOriginPageByPath(redirectPage.path);
     await this.removeRedirectOriginPageByPath(redirectPage.path);
   };
   };
 
 
-  pageSchema.statics.findListByPathsArray = async function(paths) {
-    const queryBuilder = new PageQueryBuilder(this.find());
+  pageSchema.statics.findListByPathsArray = async function(paths, includeEmpty = false) {
+    const queryBuilder = new PageQueryBuilder(this.find(), includeEmpty);
     queryBuilder.addConditionToListByPathsArray(paths);
     queryBuilder.addConditionToListByPathsArray(paths);
 
 
     return await queryBuilder.query.exec();
     return await queryBuilder.query.exec();

+ 14 - 12
packages/app/src/server/models/page.ts

@@ -41,7 +41,7 @@ export interface PageModel extends Model<PageDocument> {
   [x: string]: any; // for obsolete methods
   [x: string]: any; // for obsolete methods
   createEmptyPagesByPaths(paths: string[], publicOnly?: boolean): Promise<void>
   createEmptyPagesByPaths(paths: string[], publicOnly?: boolean): Promise<void>
   getParentIdAndFillAncestors(path: string): Promise<string | null>
   getParentIdAndFillAncestors(path: string): Promise<string | null>
-  findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?: boolean): Promise<PageDocument[]>
+  findByPathAndViewer(path: string | null, user, userGroups?, useFindOne?: boolean, includeEmpty?: boolean): Promise<PageDocument[]>
   findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
   findTargetAndAncestorsByPathOrId(pathOrId: string): Promise<TargetAndAncestorsResult>
   findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
   findChildrenByParentPathOrIdAndViewer(parentPathOrId: string, user, userGroups?): Promise<PageDocument[]>
   findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>
   findAncestorsChildrenByPathAndViewer(path: string, user, userGroups?): Promise<Record<string, PageDocument[]>>
@@ -143,7 +143,7 @@ const generateChildrenRegExp = (path: string): RegExp => {
  */
  */
 schema.statics.createEmptyPagesByPaths = async function(paths: string[], publicOnly = false): Promise<void> {
 schema.statics.createEmptyPagesByPaths = async function(paths: string[], publicOnly = false): Promise<void> {
   // find existing parents
   // find existing parents
-  const builder = new PageQueryBuilder(this.find(publicOnly ? { grant: GRANT_PUBLIC } : {}, { _id: 0, path: 1 }));
+  const builder = new PageQueryBuilder(this.find(publicOnly ? { grant: GRANT_PUBLIC } : {}, { _id: 0, path: 1 }), true);
   const existingPages = await builder
   const existingPages = await builder
     .addConditionToListByPathsArray(paths)
     .addConditionToListByPathsArray(paths)
     .query
     .query
@@ -165,7 +165,7 @@ schema.statics.createEmptyPagesByPaths = async function(paths: string[], publicO
 };
 };
 
 
 /*
 /*
- * Find the pages parent and update if the parent exists.
+ * Find the parent and update if the parent exists.
  * If not,
  * If not,
  *   - first   run createEmptyPagesByPaths with ancestor's paths to ensure all the ancestors exist
  *   - first   run createEmptyPagesByPaths with ancestor's paths to ensure all the ancestors exist
  *   - second  update ancestor pages' parent
  *   - second  update ancestor pages' parent
@@ -175,17 +175,20 @@ schema.statics.getParentIdAndFillAncestors = async function(path: string): Promi
   const parentPath = nodePath.dirname(path);
   const parentPath = nodePath.dirname(path);
 
 
   const parent = await this.findOne({ path: parentPath }); // find the oldest parent which must always be the true parent
   const parent = await this.findOne({ path: parentPath }); // find the oldest parent which must always be the true parent
-  if (parent != null) { // fill parents if parent is null
+  if (parent != null) {
     return parent._id;
     return parent._id;
   }
   }
 
 
+  /*
+   * Fill parents if parent is null
+   */
   const ancestorPaths = collectAncestorPaths(path); // paths of parents need to be created
   const ancestorPaths = collectAncestorPaths(path); // paths of parents need to be created
 
 
   // just create ancestors with empty pages
   // just create ancestors with empty pages
   await this.createEmptyPagesByPaths(ancestorPaths);
   await this.createEmptyPagesByPaths(ancestorPaths);
 
 
   // find ancestors
   // find ancestors
-  const builder = new PageQueryBuilder(this.find({}, { _id: 1, path: 1 }));
+  const builder = new PageQueryBuilder(this.find({}, { _id: 1, path: 1 }), true);
   const ancestors = await builder
   const ancestors = await builder
     .addConditionToListByPathsArray(ancestorPaths)
     .addConditionToListByPathsArray(ancestorPaths)
     .addConditionToSortPagesByDescPath()
     .addConditionToSortPagesByDescPath()
@@ -193,7 +196,6 @@ schema.statics.getParentIdAndFillAncestors = async function(path: string): Promi
     .lean()
     .lean()
     .exec();
     .exec();
 
 
-
   const ancestorsMap = new Map(); // Map<path, _id>
   const ancestorsMap = new Map(); // Map<path, _id>
   ancestors.forEach(page => ancestorsMap.set(page.path, page._id));
   ancestors.forEach(page => ancestorsMap.set(page.path, page._id));
 
 
@@ -234,14 +236,14 @@ const addViewerCondition = async(queryBuilder: PageQueryBuilder, user, userGroup
  * Find a page by path and viewer. Pass false to useFindOne to use findOne method.
  * Find a page by path and viewer. Pass false to useFindOne to use findOne method.
  */
  */
 schema.statics.findByPathAndViewer = async function(
 schema.statics.findByPathAndViewer = async function(
-    path: string | null, user, userGroups = null, useFindOne = true,
+    path: string | null, user, userGroups = null, useFindOne = true, includeEmpty = false,
 ): Promise<PageDocument | PageDocument[] | null> {
 ): Promise<PageDocument | PageDocument[] | null> {
   if (path == null) {
   if (path == null) {
     throw new Error('path is required.');
     throw new Error('path is required.');
   }
   }
 
 
   const baseQuery = useFindOne ? this.findOne({ path }) : this.find({ path });
   const baseQuery = useFindOne ? this.findOne({ path }) : this.find({ path });
-  const queryBuilder = new PageQueryBuilder(baseQuery);
+  const queryBuilder = new PageQueryBuilder(baseQuery, includeEmpty);
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
   return queryBuilder.query.exec();
   return queryBuilder.query.exec();
@@ -269,7 +271,7 @@ schema.statics.findTargetAndAncestorsByPathOrId = async function(pathOrId: strin
   ancestorPaths.push(path); // include target
   ancestorPaths.push(path); // include target
 
 
   // Do not populate
   // Do not populate
-  const queryBuilder = new PageQueryBuilder(this.find());
+  const queryBuilder = new PageQueryBuilder(this.find(), true);
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
   const _targetAndAncestors: PageDocument[] = await queryBuilder
   const _targetAndAncestors: PageDocument[] = await queryBuilder
@@ -298,11 +300,11 @@ schema.statics.findChildrenByParentPathOrIdAndViewer = async function(parentPath
   if (hasSlash(parentPathOrId)) {
   if (hasSlash(parentPathOrId)) {
     const path = parentPathOrId;
     const path = parentPathOrId;
     const regexp = generateChildrenRE2(path);
     const regexp = generateChildrenRE2(path);
-    queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }));
+    queryBuilder = new PageQueryBuilder(this.find({ path: { $regex: regexp.source } }), true);
   }
   }
   else {
   else {
     const parentId = parentPathOrId;
     const parentId = parentPathOrId;
-    queryBuilder = new PageQueryBuilder(this.find({ parent: parentId }));
+    queryBuilder = new PageQueryBuilder(this.find({ parent: parentId }), true);
   }
   }
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
 
 
@@ -318,7 +320,7 @@ schema.statics.findAncestorsChildrenByPathAndViewer = async function(path: strin
   const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
   const regexps = ancestorPaths.map(path => new RegExp(generateChildrenRegExp(path))); // cannot use re2
 
 
   // get pages at once
   // get pages at once
-  const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }));
+  const queryBuilder = new PageQueryBuilder(this.find({ path: { $in: regexps } }), true);
   await addViewerCondition(queryBuilder, user, userGroups);
   await addViewerCondition(queryBuilder, user, userGroups);
   const _pages = await queryBuilder
   const _pages = await queryBuilder
     .addConditionAsMigrated()
     .addConditionAsMigrated()

+ 1 - 1
packages/app/src/server/routes/apiv3/app-settings.js

@@ -7,7 +7,7 @@ const debug = require('debug')('growi:routes:admin');
 
 
 const express = require('express');
 const express = require('express');
 
 
-const { pathUtils } = require('growi-commons');
+const { pathUtils } = require('@growi/core');
 const { listLocaleIds } = require('~/utils/locale-utils');
 const { listLocaleIds } = require('~/utils/locale-utils');
 
 
 const router = express.Router();
 const router = express.Router();

+ 1 - 1
packages/app/src/server/routes/apiv3/pages.js

@@ -3,7 +3,7 @@ import loggerFactory from '~/utils/logger';
 
 
 const logger = loggerFactory('growi:routes:apiv3:pages'); // eslint-disable-line no-unused-vars
 const logger = loggerFactory('growi:routes:apiv3:pages'); // eslint-disable-line no-unused-vars
 const express = require('express');
 const express = require('express');
-const pathUtils = require('growi-commons').pathUtils;
+const { pathUtils } = require('@growi/core');
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
 
 
 const { body } = require('express-validator');
 const { body } = require('express-validator');

+ 9 - 7
packages/app/src/server/routes/page.js

@@ -138,7 +138,7 @@ module.exports = function(crowi, app) {
   const logger = loggerFactory('growi:routes:page');
   const logger = loggerFactory('growi:routes:page');
   const swig = require('swig-templates');
   const swig = require('swig-templates');
 
 
-  const pathUtils = require('growi-commons').pathUtils;
+  const { pathUtils } = require('@growi/core');
 
 
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
   const User = crowi.model('User');
   const User = crowi.model('User');
@@ -264,10 +264,10 @@ module.exports = function(crowi, app) {
     renderVars.pages = result.pages;
     renderVars.pages = result.pages;
   }
   }
 
 
-  async function addRenderVarsForPageTree(renderVars, path, user) {
-    const { targetAndAncestors, rootPage } = await Page.findTargetAndAncestorsByPathOrId(path, user);
+  async function addRenderVarsForPageTree(renderVars, pathOrId, user) {
+    const { targetAndAncestors, rootPage } = await Page.findTargetAndAncestorsByPathOrId(pathOrId, user);
 
 
-    if (targetAndAncestors.length === 0 && !isTopPage(path)) {
+    if (targetAndAncestors.length === 0 && pathOrId.includes('/') && !isTopPage(pathOrId)) {
       throw new Error('Ancestors must have at least one page.');
       throw new Error('Ancestors must have at least one page.');
     }
     }
 
 
@@ -291,6 +291,7 @@ module.exports = function(crowi, app) {
 
 
   async function _notFound(req, res) {
   async function _notFound(req, res) {
     const path = getPathFromRequest(req);
     const path = getPathFromRequest(req);
+    const pathOrId = req.params.id || path;
 
 
     let view;
     let view;
     const renderVars = { path };
     const renderVars = { path };
@@ -326,6 +327,7 @@ module.exports = function(crowi, app) {
     const limit = 50;
     const limit = 50;
     const offset = parseInt(req.query.offset) || 0;
     const offset = parseInt(req.query.offset) || 0;
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit, true);
     await addRenderVarsForDescendants(renderVars, path, req.user, offset, limit, true);
+    await addRenderVarsForPageTree(renderVars, pathOrId, req.user);
 
 
     return res.render(view, renderVars);
     return res.render(view, renderVars);
   }
   }
@@ -334,7 +336,7 @@ module.exports = function(crowi, app) {
     const id = req.params.id;
     const id = req.params.id;
     const { revisionId } = req.query;
     const { revisionId } = req.query;
 
 
-    let page = await Page.findByIdAndViewer(id, req.user);
+    let page = await Page.findByIdAndViewer(id, req.user, null, true, true);
 
 
     if (page == null) {
     if (page == null) {
       next();
       next();
@@ -395,7 +397,7 @@ module.exports = function(crowi, app) {
     const id = req.params.id;
     const id = req.params.id;
     const revisionId = req.query.revision;
     const revisionId = req.query.revision;
 
 
-    let page = await Page.findByIdAndViewer(id, req.user);
+    let page = await Page.findByIdAndViewer(id, req.user, null, true, true);
 
 
     if (page == null) {
     if (page == null) {
       // check the page is forbidden or just does not exist.
       // check the page is forbidden or just does not exist.
@@ -592,7 +594,7 @@ module.exports = function(crowi, app) {
    * redirector
    * redirector
    */
    */
   async function redirector(req, res, next, path) {
   async function redirector(req, res, next, path) {
-    const pages = await Page.findByPathAndViewer(path, req.user, null, false);
+    const pages = await Page.findByPathAndViewer(path, req.user, null, false, true);
     const { redirectFrom } = req.query;
     const { redirectFrom } = req.query;
 
 
     if (pages.length >= 2) {
     if (pages.length >= 2) {

+ 1 - 1
packages/app/src/server/service/config-loader.ts

@@ -1,4 +1,4 @@
-import { envUtils } from 'growi-commons';
+import { envUtils } from '@growi/core';
 
 
 import loggerFactory from '~/utils/logger';
 import loggerFactory from '~/utils/logger';
 
 

+ 1 - 1
packages/app/src/server/service/page.js

@@ -1001,7 +1001,7 @@ class PageService {
         await Page.createEmptyPagesByPaths(parentPaths, publicOnly);
         await Page.createEmptyPagesByPaths(parentPaths, publicOnly);
 
 
         // find parents again
         // find parents again
-        const builder = new PageQueryBuilder(Page.find({}, { _id: 1, path: 1 }));
+        const builder = new PageQueryBuilder(Page.find({}, { _id: 1, path: 1 }), true);
         const parents = await builder
         const parents = await builder
           .addConditionToListByPathsArray(parentPaths)
           .addConditionToListByPathsArray(parentPaths)
           .query
           .query

+ 1 - 1
packages/app/src/server/service/slack-command-handler/create-page-service.js

@@ -3,7 +3,7 @@ import loggerFactory from '~/utils/logger';
 const logger = loggerFactory('growi:service:CreatePageService');
 const logger = loggerFactory('growi:service:CreatePageService');
 const { reshapeContentsBody, respond, markdownSectionBlock } = require('@growi/slack');
 const { reshapeContentsBody, respond, markdownSectionBlock } = require('@growi/slack');
 const mongoose = require('mongoose');
 const mongoose = require('mongoose');
-const pathUtils = require('growi-commons').pathUtils;
+const { pathUtils } = require('@growi/core');
 
 
 class CreatePageService {
 class CreatePageService {
 
 

+ 1 - 1
packages/app/src/server/service/slack-command-handler/search.js

@@ -41,7 +41,7 @@ module.exports = (crowi) => {
 
 
     const { searchService } = crowi;
     const { searchService } = crowi;
     const options = { limit: PAGINGLIMIT, offset };
     const options = { limit: PAGINGLIMIT, offset };
-    const results = await searchService.searchKeyword(keywords, null, {}, options);
+    const [results] = await searchService.searchKeyword(keywords, null, {}, options);
     const resultsTotal = results.meta.total;
     const resultsTotal = results.meta.total;
 
 
     const pages = results.data.map((data) => {
     const pages = results.data.map((data) => {

+ 1 - 1
packages/app/src/server/util/middlewares.js

@@ -5,7 +5,7 @@ import loggerFactory from '~/utils/logger';
 // eslint-disable-next-line no-unused-vars
 // eslint-disable-next-line no-unused-vars
 
 
 const { formatDistanceStrict } = require('date-fns');
 const { formatDistanceStrict } = require('date-fns');
-const pathUtils = require('growi-commons').pathUtils;
+const { pathUtils } = require('@growi/core');
 const md5 = require('md5');
 const md5 = require('md5');
 const entities = require('entities');
 const entities = require('entities');
 
 

+ 1 - 1
packages/app/src/server/util/swigFunctions.js

@@ -2,7 +2,7 @@ module.exports = function(crowi, req, locals) {
   const debug = require('debug')('growi:lib:swigFunctions');
   const debug = require('debug')('growi:lib:swigFunctions');
   const stringWidth = require('string-width');
   const stringWidth = require('string-width');
 
 
-  const { pathUtils } = require('growi-commons');
+  const { pathUtils } = require('@growi/core');
 
 
   const Page = crowi.model('Page');
   const Page = crowi.model('Page');
   const User = crowi.model('User');
   const User = crowi.model('User');

+ 5 - 0
packages/app/src/server/views/layout/layout.html

@@ -125,6 +125,11 @@
   {{ userUISettings|json|safe }}
   {{ userUISettings|json|safe }}
   </script>
   </script>
 {% endif %}
 {% endif %}
+{% if targetAndAncestors != null %}
+  <script type="application/json" id="growi-pagetree-target-and-ancestors">
+  {{ targetAndAncestors|json|safe }}
+  </script>
+{% endif %}
 
 
 
 
 {% block custom_script %}
 {% block custom_script %}

+ 0 - 1
packages/app/src/server/views/widget/page_content.html

@@ -27,7 +27,6 @@
   data-page-user="{% if pageUser %}{{ pageUser|json }}{% else %}null{% endif %}"
   data-page-user="{% if pageUser %}{{ pageUser|json }}{% else %}null{% endif %}"
   data-share-links-number="{% if page %}{{ sharelinksNumber }}{% endif %}"
   data-share-links-number="{% if page %}{{ sharelinksNumber }}{% endif %}"
   data-share-link-id="{% if sharelink %}{{ sharelink._id|json }}{% endif %}"
   data-share-link-id="{% if sharelink %}{{ sharelink._id|json }}{% endif %}"
-  data-target-and-ancestors="{% if targetAndAncestors %}{{ targetAndAncestors|json }}{% endif %}"
   >
   >
 {% else %}
 {% else %}
 <div id="content-main" class="content-main d-flex"
 <div id="content-main" class="content-main d-flex"

+ 1 - 1
packages/app/src/services/cdn-resources-service.js

@@ -4,7 +4,7 @@ import { resolveFromRoot } from '~/utils/project-dir-utils';
 const { URL } = require('url');
 const { URL } = require('url');
 const urljoin = require('url-join');
 const urljoin = require('url-join');
 
 
-const { envUtils } = require('growi-commons');
+const { envUtils } = require('@growi/core');
 
 
 const cdnLocalScriptRoot = 'public/static/js/cdn';
 const cdnLocalScriptRoot = 'public/static/js/cdn';
 const cdnLocalScriptWebRoot = '/static/js/cdn';
 const cdnLocalScriptWebRoot = '/static/js/cdn';

+ 0 - 129
packages/app/src/test/integration/service/page.test.js

@@ -821,133 +821,4 @@ describe('PageService', () => {
     });
     });
   });
   });
 
 
-  describe('v5MigrationByPageIds()', () => {
-    test('should migrate all pages specified by pageIds', async() => {
-      jest.restoreAllMocks();
-
-      // initialize pages for test
-      const pages = await Page.insertMany([
-        {
-          path: '/private1',
-          grant: Page.GRANT_OWNER,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/dummyParent/private1',
-          grant: Page.GRANT_OWNER,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/dummyParent/private1/private2',
-          grant: Page.GRANT_OWNER,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/dummyParent/private1/private3',
-          grant: Page.GRANT_OWNER,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-      ]);
-
-      const pageIds = pages.map(page => page._id);
-      // migrate
-      await crowi.pageService.v5MigrationByPageIds(pageIds);
-
-      const migratedPages = await Page.find({
-        path: {
-          $in: ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'],
-        },
-      });
-      const migratedPagePaths = migratedPages.filter(doc => doc.parent != null).map(doc => doc.path);
-
-      const expected = ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'];
-
-      expect(migratedPagePaths.sort()).toStrictEqual(expected.sort());
-    });
-
-  });
-
-  describe('v5InitialMigration()', () => {
-    test('should migrate all public pages & replace private parents with empty pages', async() => {
-      jest.restoreAllMocks();
-
-      // initialize pages for test
-      const pages = await Page.insertMany([
-        {
-          path: '/publicA',
-          grant: Page.GRANT_PUBLIC,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/publicA/privateB',
-          grant: Page.GRANT_OWNER,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/publicA/privateB/publicC',
-          grant: Page.GRANT_PUBLIC,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/parenthesis/(a)[b]{c}d',
-          grant: Page.GRANT_PUBLIC,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-        {
-          path: '/parenthesis/(a)[b]{c}d/public',
-          grant: Page.GRANT_PUBLIC,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-        },
-      ]);
-
-      const parent = await Page.find({ path: '/' });
-      await Page.insertMany([
-        {
-          path: '/migratedD',
-          grant: Page.GRANT_PUBLIC,
-          creator: testUser1,
-          lastUpdateUser: testUser1,
-          parent: parent._id,
-        },
-      ]);
-
-      // migrate
-      await crowi.pageService.v5InitialMigration(Page.GRANT_PUBLIC);
-
-      const nMigratedPages = await Page.count({
-        path: {
-          $in: ['/publicA', '/publicA/privateB/publicC', '/parenthesis/(a)[b]{c}d', '/parenthesis/(a)[b]{c}d/public', '/migratedD'],
-        },
-        isEmpty: false,
-        parent: { $ne: null },
-      });
-      const nMigratedEmptyPages = await Page.count({
-        path: {
-          $in: ['/publicA/privateB', '/parenthesis'],
-        },
-        isEmpty: true,
-        parent: { $ne: null },
-      });
-      const nNonMigratedPages = await Page.count({
-        path: {
-          $in: ['/publicA/privateB'],
-        },
-        parent: null,
-      });
-
-      expect(nMigratedPages).toBe(5);
-      expect(nMigratedEmptyPages).toBe(2);
-      expect(nNonMigratedPages).toBe(1);
-    });
-  });
-
 });
 });

+ 194 - 0
packages/app/src/test/integration/service/v5-migration.test.js

@@ -0,0 +1,194 @@
+const mongoose = require('mongoose');
+
+const { getInstance } = require('../setup-crowi');
+
+describe('V5 page migration', () => {
+  let crowi;
+  let Page;
+
+  let testUser1;
+
+  beforeAll(async() => {
+    jest.restoreAllMocks();
+
+    crowi = await getInstance();
+    Page = mongoose.model('Page');
+  });
+
+
+  describe('v5MigrationByPageIds()', () => {
+    test('should migrate all pages specified by pageIds', async() => {
+      jest.restoreAllMocks();
+
+      // initialize pages for test
+      const pages = await Page.insertMany([
+        {
+          path: '/private1',
+          grant: Page.GRANT_OWNER,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/dummyParent/private1',
+          grant: Page.GRANT_OWNER,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/dummyParent/private1/private2',
+          grant: Page.GRANT_OWNER,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/dummyParent/private1/private3',
+          grant: Page.GRANT_OWNER,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+      ]);
+
+      const pageIds = pages.map(page => page._id);
+      // migrate
+      await crowi.pageService.v5MigrationByPageIds(pageIds);
+
+      const migratedPages = await Page.find({
+        path: {
+          $in: ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'],
+        },
+      });
+      const migratedPagePaths = migratedPages.filter(doc => doc.parent != null).map(doc => doc.path);
+
+      const expected = ['/private1', '/dummyParent', '/dummyParent/private1', '/dummyParent/private1/private2', '/dummyParent/private1/private3'];
+
+      expect(migratedPagePaths.sort()).toStrictEqual(expected.sort());
+    });
+
+  });
+
+  describe('v5InitialMigration()', () => {
+    let createPagePaths;
+    let allPossiblePagePaths;
+    beforeAll(async() => {
+      createPagePaths = [
+        '/publicA', '/publicA/privateB', '/publicA/privateB/publicC', '/parenthesis/(a)[b]{c}d', '/parenthesis/(a)[b]{c}d/public', '/migratedD',
+      ];
+      allPossiblePagePaths = [...createPagePaths, '/parenthesis', '/'];
+
+      // initialize pages for test
+      await Page.insertMany([
+        {
+          path: '/publicA',
+          grant: Page.GRANT_PUBLIC,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/publicA/privateB',
+          grant: Page.GRANT_OWNER,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/publicA/privateB/publicC',
+          grant: Page.GRANT_PUBLIC,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/parenthesis/(a)[b]{c}d',
+          grant: Page.GRANT_PUBLIC,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+        {
+          path: '/parenthesis/(a)[b]{c}d/public',
+          grant: Page.GRANT_PUBLIC,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+        },
+      ]);
+
+      const parent = await Page.find({ path: '/' });
+      await Page.insertMany([
+        {
+          path: '/migratedD',
+          grant: Page.GRANT_PUBLIC,
+          creator: testUser1,
+          lastUpdateUser: testUser1,
+          parent: parent._id,
+        },
+      ]);
+
+      // migrate
+      await crowi.pageService.v5InitialMigration(Page.GRANT_PUBLIC);
+    });
+
+    test('should migrate all public pages', async() => {
+      const migratedPages = await Page.find({
+        path: {
+          $in: allPossiblePagePaths,
+        },
+        parent: { $ne: null },
+      });
+      const migratedEmptyPages = await Page.find({
+        path: {
+          $in: allPossiblePagePaths,
+        },
+        isEmpty: true,
+        parent: { $ne: null },
+      });
+      const nonMigratedPages = await Page.find({
+        path: {
+          $in: allPossiblePagePaths,
+        },
+        parent: null,
+      });
+
+      const migratedPaths = migratedPages.map(page => page.path).sort();
+      const migratedEmptyPaths = migratedEmptyPages.map(page => page.path).sort();
+      const nonMigratedPaths = nonMigratedPages.map(page => page.path).sort();
+
+      const expectedMigratedPaths = allPossiblePagePaths.filter(path => path !== '/').sort();
+      const expectedMigratedEmptyPaths = ['/publicA/privateB', '/parenthesis'].sort();
+      const expectedNonMigratedPaths = ['/publicA/privateB', '/'].sort();
+
+      expect(migratedPaths).toStrictEqual(expectedMigratedPaths);
+      expect(migratedEmptyPaths).toStrictEqual(expectedMigratedEmptyPaths);
+      expect(nonMigratedPaths).toStrictEqual(expectedNonMigratedPaths);
+    });
+  });
+
+  test('replace private parents with empty pages', async() => {
+    const replacedPathPages = await Page.find({ path: '/publicA/privateB' }); // ex-private page
+
+    const _newEmptyPage = replacedPathPages.filter(page => page.parent != null)[0];
+    const newEmptyPage = {
+      path: _newEmptyPage.path,
+      grant: _newEmptyPage.grant,
+      isEmpty: _newEmptyPage.isEmpty,
+    };
+    const expectedNewEmptyPage = {
+      path: '/publicA/privateB',
+      grant: Page.GRANT_PUBLIC,
+      isEmpty: true,
+    };
+
+    const _privatePage = replacedPathPages.filter(page => page.parent == null)[0];
+    const privatePage = {
+      path: _privatePage.path,
+      grant: _privatePage.grant,
+      isEmpty: _privatePage.isEmpty,
+    };
+    const expectedPrivatePage = {
+      path: '/publicA/privateB',
+      grant: Page.GRANT_OWNER,
+      isEmpty: false,
+    };
+
+    expect(replacedPathPages.length).toBe(2);
+    expect(newEmptyPage).toStrictEqual(expectedNewEmptyPage);
+    expect(privatePage).toStrictEqual(expectedPrivatePage);
+  });
+
+});

+ 0 - 27
packages/core/README.md

@@ -1,27 +0,0 @@
-# growi-commons
-
-[![dependencies status](https://david-dm.org/weseek/growi-commons.svg)](https://david-dm.org/weseek/growi-commons)
-[![devDependencies Status](https://david-dm.org/weseek/growi-commons/dev-status.svg)](https://david-dm.org/weseek/growi-commons?type=dev)
-[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
-
-[GROWI](https://growi.org) Commons Libraries to develop GROWI and plugins
-
-
-Overview
---------
-
-growi-commons package is includes some functions, classes and modules to develop GROWI substance and GROWI plugins.
-
-Install
---------
-
-1. install plugin
-
-    ```
-    $ npm install --save growi-commons
-    ```
-
-Documentation
-------------
-
-See https://docs.growi.org/api/commons/

+ 11 - 14
packages/core/src/index.js

@@ -1,22 +1,19 @@
 import * as _pathUtils from './utils/path-utils';
 import * as _pathUtils from './utils/path-utils';
+import * as _envUtils from './utils/env-utils';
 import * as _pagePathUtils from './utils/page-path-utils';
 import * as _pagePathUtils from './utils/page-path-utils';
 import * as _templateChecker from './utils/template-checker';
 import * as _templateChecker from './utils/template-checker';
-
-// module.exports = {
-//   BasicInterceptor: require('./utils/basic-interceptor'),
-//   envUtils: require('./utils/env-utils'),
-//   // plugin
-//   customTagUtils: require('./plugin/util/custom-tag-utils'),
-//   TagCacheManager: require('./plugin/service/tag-cache-manager'),
-//   // service
-//   LocalStorageManager: require('./service/localstorage-manager'),
-// };
-
-export * from './plugin/interfaces/plugin-definition-v4';
-export * from './models/devided-page-path';
-export * from './utils/mongoose-utils';
+import * as _customTagUtils from './plugin/util/custom-tag-utils';
 
 
 // export utils
 // export utils
 export const pathUtils = _pathUtils;
 export const pathUtils = _pathUtils;
+export const envUtils = _envUtils;
 export const pagePathUtils = _pagePathUtils;
 export const pagePathUtils = _pagePathUtils;
 export const templateChecker = _templateChecker;
 export const templateChecker = _templateChecker;
+export const customTagUtils = _customTagUtils;
+
+export * from './plugin/interfaces/plugin-definition-v4';
+export * from './plugin/service/tag-cache-manager';
+export * from './models/devided-page-path';
+export * from './service/localstorage-manager';
+export * from './utils/basic-interceptor';
+export * from './utils/mongoose-utils';

+ 2 - 4
packages/core/src/plugin/service/tag-cache-manager.js

@@ -1,9 +1,9 @@
-const LocalStorageManager = require('../../service/localstorage-manager');
+import { LocalStorageManager } from '../../service/localstorage-manager';
 
 
 /**
 /**
  * Service Class for caching React state and TagContext
  * Service Class for caching React state and TagContext
  */
  */
-class TagCacheManager {
+export class TagCacheManager {
 
 
   /**
   /**
    * @callback generateCacheKey
    * @callback generateCacheKey
@@ -67,5 +67,3 @@ class TagCacheManager {
   }
   }
 
 
 }
 }
-
-module.exports = TagCacheManager;

+ 1 - 3
packages/core/src/service/localstorage-manager.js

@@ -1,5 +1,5 @@
 let _instance = null;
 let _instance = null;
-class LocalStorageManager {
+export class LocalStorageManager {
 
 
   static getInstance() {
   static getInstance() {
     if (_instance == null) {
     if (_instance == null) {
@@ -52,5 +52,3 @@ class LocalStorageManager {
   }
   }
 
 
 }
 }
-
-module.exports = LocalStorageManager;

+ 1 - 3
packages/core/src/utils/basic-interceptor.js

@@ -1,7 +1,7 @@
 /**
 /**
  * Basic Interceptor class
  * Basic Interceptor class
  */
  */
-class BasicInterceptor {
+export class BasicInterceptor {
 
 
   /**
   /**
    * getter for id
    * getter for id
@@ -43,5 +43,3 @@ class BasicInterceptor {
   }
   }
 
 
 }
 }
-
-module.exports = BasicInterceptor;

+ 1 - 1
packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPostRenderInterceptor.js

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
 
 
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import RefsContext from '../RefsContext';
 import RefsContext from '../RefsContext';
 import GalleryContext from '../GalleryContext';
 import GalleryContext from '../GalleryContext';

+ 1 - 1
packages/plugin-attachment-refs/src/client/js/util/Interceptor/RefsPreRenderInterceptor.js

@@ -1,4 +1,4 @@
-import { customTagUtils, BasicInterceptor } from 'growi-commons';
+import { customTagUtils, BasicInterceptor } from '@growi/core';
 
 
 import TagCacheManagerFactory from '../TagCacheManagerFactory';
 import TagCacheManagerFactory from '../TagCacheManagerFactory';
 
 

+ 1 - 1
packages/plugin-attachment-refs/src/client/js/util/RefsContext.js

@@ -1,6 +1,6 @@
 import * as url from 'url';
 import * as url from 'url';
 
 
-import { customTagUtils, pathUtils } from 'growi-commons';
+import { customTagUtils, pathUtils } from '@growi/core';
 
 
 const { TagContext, ArgsParser, OptionParser } = customTagUtils;
 const { TagContext, ArgsParser, OptionParser } = customTagUtils;
 
 

+ 1 - 1
packages/plugin-attachment-refs/src/client/js/util/TagCacheManagerFactory.js

@@ -1,4 +1,4 @@
-import { TagCacheManager } from 'growi-commons';
+import { TagCacheManager } from '@growi/core';
 
 
 const STATE_CACHE_NS = 'refs-state-cache';
 const STATE_CACHE_NS = 'refs-state-cache';
 
 

+ 1 - 1
packages/plugin-attachment-refs/src/server/routes/refs.js

@@ -1,6 +1,6 @@
 import loggerFactory from '../../utils/logger';
 import loggerFactory from '../../utils/logger';
 
 
-const { customTagUtils } = require('growi-commons');
+const { customTagUtils } = require('@growi/core');
 
 
 const { OptionParser } = customTagUtils;
 const { OptionParser } = customTagUtils;
 
 

+ 1 - 1
packages/plugin-lsx/src/client/js/components/Lsx.jsx

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
 
 
 import * as url from 'url';
 import * as url from 'url';
 
 
-import { pathUtils } from 'growi-commons';
+import { pathUtils } from '@growi/core';
 
 
 // eslint-disable-next-line no-unused-vars
 // eslint-disable-next-line no-unused-vars
 import styles from '../../css/index.css';
 import styles from '../../css/index.css';

+ 1 - 1
packages/plugin-lsx/src/client/js/components/LsxPageList/LsxPage.jsx

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import PropTypes from 'prop-types';
 import PropTypes from 'prop-types';
 
 
-import { pathUtils } from 'growi-commons';
+import { pathUtils } from '@growi/core';
 
 
 import { PageListMeta } from '@growi/ui';
 import { PageListMeta } from '@growi/ui';
 
 

+ 1 - 1
packages/plugin-lsx/src/client/js/util/Interceptor/LsxLogoutInterceptor.js

@@ -1,4 +1,4 @@
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import { TagCacheManagerFactory } from '../TagCacheManagerFactory';
 import { TagCacheManagerFactory } from '../TagCacheManagerFactory';
 
 

+ 1 - 1
packages/plugin-lsx/src/client/js/util/Interceptor/LsxPostRenderInterceptor.js

@@ -1,7 +1,7 @@
 import React from 'react';
 import React from 'react';
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
 
 
-import { BasicInterceptor } from 'growi-commons';
+import { BasicInterceptor } from '@growi/core';
 
 
 import { LsxContext } from '../LsxContext';
 import { LsxContext } from '../LsxContext';
 import { Lsx } from '../../components/Lsx';
 import { Lsx } from '../../components/Lsx';

+ 1 - 1
packages/plugin-lsx/src/client/js/util/Interceptor/LsxPreRenderInterceptor.js

@@ -1,5 +1,5 @@
 import ReactDOM from 'react-dom';
 import ReactDOM from 'react-dom';
-import { customTagUtils, BasicInterceptor } from 'growi-commons';
+import { customTagUtils, BasicInterceptor } from '@growi/core';
 
 
 /**
 /**
  * The interceptor for lsx
  * The interceptor for lsx

+ 1 - 1
packages/plugin-lsx/src/client/js/util/LsxContext.js

@@ -1,6 +1,6 @@
 import * as url from 'url';
 import * as url from 'url';
 
 
-import { customTagUtils, pathUtils } from 'growi-commons';
+import { customTagUtils, pathUtils } from '@growi/core';
 
 
 const { TagContext, ArgsParser, OptionParser } = customTagUtils;
 const { TagContext, ArgsParser, OptionParser } = customTagUtils;
 
 

+ 1 - 18
packages/plugin-lsx/src/client/js/util/TagCacheManagerFactory.js

@@ -1,29 +1,12 @@
-import { TagCacheManager } from 'growi-commons';
+import { TagCacheManager } from '@growi/core';
 
 
 const LSX_STATE_CACHE_NS = 'lsx-state-cache';
 const LSX_STATE_CACHE_NS = 'lsx-state-cache';
 
 
 
 
-// validate growi-commons version
-function validateGrowiCommonsVersion() {
-  // TagCacheManager was created on growi-commons@4.0.7
-  if (TagCacheManager == null) {
-    throw new Error(
-      'This version of \'growi-plugin-lsx\' requires \'growi-commons >= 4.0.7\'.\n'
-      + 'To resolve this, please process  either a) or b).\n'
-      + '\n'
-      + 'a) Use \'growi-plugin-lsx@3.0.0\'\n'
-      + 'b) Edit \'package.json\' of growi and upgrade \'growi-commons\' to v4.0.7 or above.',
-    );
-  }
-}
-
-
 let _instance;
 let _instance;
 export class TagCacheManagerFactory {
 export class TagCacheManagerFactory {
 
 
   static getInstance() {
   static getInstance() {
-    validateGrowiCommonsVersion();
-
     if (_instance == null) {
     if (_instance == null) {
       // create generateCacheKey implementation
       // create generateCacheKey implementation
       const generateCacheKey = (lsxContext) => {
       const generateCacheKey = (lsxContext) => {

+ 1 - 1
packages/plugin-lsx/src/server/routes/lsx.js

@@ -1,4 +1,4 @@
-const { customTagUtils } = require('growi-commons');
+const { customTagUtils } = require('@growi/core');
 
 
 const { OptionParser } = customTagUtils;
 const { OptionParser } = customTagUtils;
 
 

+ 3 - 3
packages/slackbot-proxy/docker/Dockerfile

@@ -3,7 +3,7 @@
 ##
 ##
 ## deps-resolver-base
 ## deps-resolver-base
 ##
 ##
-FROM node:14-slim AS deps-resolver-base
+FROM node:16-slim AS deps-resolver-base
 
 
 ENV appDir /opt
 ENV appDir /opt
 
 
@@ -46,7 +46,7 @@ RUN tar cf dependencies.tar \
 ##
 ##
 ## builder
 ## builder
 ##
 ##
-FROM node:14-slim AS builder
+FROM node:16-slim AS builder
 
 
 ENV appDir /opt
 ENV appDir /opt
 
 
@@ -83,7 +83,7 @@ RUN tar cf packages.tar \
 ##
 ##
 ## release
 ## release
 ##
 ##
-FROM node:14-slim
+FROM node:16-slim
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 LABEL maintainer Yuki Takei <yuki@weseek.co.jp>
 
 
 ENV NODE_ENV production
 ENV NODE_ENV production

+ 0 - 27
packages/ui/README.md

@@ -1,27 +0,0 @@
-# growi-commons
-
-[![dependencies status](https://david-dm.org/weseek/growi-commons.svg)](https://david-dm.org/weseek/growi-commons)
-[![devDependencies Status](https://david-dm.org/weseek/growi-commons/dev-status.svg)](https://david-dm.org/weseek/growi-commons?type=dev)
-[![MIT License](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](LICENSE)
-
-[GROWI](https://growi.org) Commons Libraries to develop GROWI and plugins
-
-
-Overview
---------
-
-growi-commons package is includes some functions, classes and modules to develop GROWI substance and GROWI plugins.
-
-Install
---------
-
-1. install plugin
-
-    ```
-    $ npm install --save growi-commons
-    ```
-
-Documentation
-------------
-
-See https://docs.growi.org/api/commons/

+ 114 - 83
yarn.lock

@@ -1856,6 +1856,21 @@
     google-libphonenumber ">=3.2.10"
     google-libphonenumber ">=3.2.10"
     lodash ">=4.17.15"
     lodash ">=4.17.15"
 
 
+"@mapbox/node-pre-gyp@^1.0.5":
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.8.tgz#32abc8a5c624bc4e46c43d84dfb8b26d33a96f58"
+  integrity sha512-CMGKi28CF+qlbXh26hDe6NxCd7amqeAzEqnS6IHeO6LoaKyM/n+Xw3HT1COdq8cuioOdlKdqn/hCmqPUOMOywg==
+  dependencies:
+    detect-libc "^1.0.3"
+    https-proxy-agent "^5.0.0"
+    make-dir "^3.1.0"
+    node-fetch "^2.6.5"
+    nopt "^5.0.0"
+    npmlog "^5.0.1"
+    rimraf "^3.0.2"
+    semver "^7.3.5"
+    tar "^6.1.11"
+
 "@nodelib/fs.scandir@2.1.3":
 "@nodelib/fs.scandir@2.1.3":
   version "2.1.3"
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@@ -2147,46 +2162,46 @@
   dependencies:
   dependencies:
     "@octokit/openapi-types" "^10.0.0"
     "@octokit/openapi-types" "^10.0.0"
 
 
-"@promster/express@^5.1.0":
-  version "5.1.0"
-  resolved "https://registry.yarnpkg.com/@promster/express/-/express-5.1.0.tgz#6b0b3e0bc713b7f76792c3a99babe06172652c8e"
-  integrity sha512-IFGtCzxByLMSLdqQK8AT1NIIQPqvOO1GfOSg25smlCVUZvnhgBQOj8MzWCuMNL4v6efeuQVVrZWmOA6Yo3w40Q==
+"@promster/express@^7.0.2":
+  version "7.0.2"
+  resolved "https://registry.yarnpkg.com/@promster/express/-/express-7.0.2.tgz#d60b10d373fd572275714426dc90a7049fd26d4f"
+  integrity sha512-HnNxb9bVZpUPbPU6/1UjACe2PeZEJmVw3tVxzZA8CdzGvPMwewe1e1TXNR9PASWRA/pJj6FBHHUPuTq0daEcgg==
   dependencies:
   dependencies:
-    "@promster/metrics" "^7.0.0"
+    "@promster/metrics" "^9.1.1"
     merge-options "3.0.4"
     merge-options "3.0.4"
     tslib "2.3.1"
     tslib "2.3.1"
 
 
-"@promster/metrics@^7.0.0":
-  version "7.0.0"
-  resolved "https://registry.yarnpkg.com/@promster/metrics/-/metrics-7.0.0.tgz#19ec55ab5472f5d0b690b15ffd7f88181e8b2fe7"
-  integrity sha512-SHZtSgETJETjGYdw7e9NVcpC7CxrSmxup+kbtvPN7Pgc9u35B5hYylOaIzj5r9HDwKBpARKIbDFYMs18LLBpQw==
+"@promster/metrics@^9.1.1", "@promster/metrics@^9.1.2":
+  version "9.1.2"
+  resolved "https://registry.yarnpkg.com/@promster/metrics/-/metrics-9.1.2.tgz#2468fae02adf87febdba8a25a4203111ea347f66"
+  integrity sha512-kKcawgTJahHUFjnX/TOr6vju+196rSKlGQUfjC6am6sBxqFbccsBW/nEPusUEUswvnC4zv+ddbNz4uLJcu1w7A==
   dependencies:
   dependencies:
     lodash.memoize "4.1.2"
     lodash.memoize "4.1.2"
     lodash.once "4.1.1"
     lodash.once "4.1.1"
     merge-options "3.0.4"
     merge-options "3.0.4"
     optional "0.1.4"
     optional "0.1.4"
-    ts-essentials "8.1.0"
+    ts-essentials "9.0.0"
     tslib "2.3.1"
     tslib "2.3.1"
     url "0.11.0"
     url "0.11.0"
-    url-value-parser "2.0.3"
+    url-value-parser "2.1.0"
   optionalDependencies:
   optionalDependencies:
-    "@sematext/gc-stats" "1.5.3"
+    "@sematext/gc-stats" "1.5.5"
 
 
-"@promster/server@^6.0.3":
-  version "6.0.3"
-  resolved "https://registry.yarnpkg.com/@promster/server/-/server-6.0.3.tgz#92409f97012dc7bd8c0437cb3cd95dae6ae78d91"
-  integrity sha512-d5VoeT2dwpe5Yk5BlFfjyXEa/ch4yq9AP/SymUM4df3nvdX0jnc/D2MuG/nZDKsMuRxUzf8bn38bXs71d3KJpA==
+"@promster/server@^7.0.4":
+  version "7.0.4"
+  resolved "https://registry.yarnpkg.com/@promster/server/-/server-7.0.4.tgz#b4c6babb6dd4d185c164020054a78f2d5da03755"
+  integrity sha512-JDOBVBe2Iqd29kLL5J3HuNyHuVhiX65sJdmZkY3GTBAOmmZFom/k+rT+JIBZRefhALhzU3pzMbpvxpvXlm0TOA==
   dependencies:
   dependencies:
-    "@promster/metrics" "^7.0.0"
+    "@promster/metrics" "^9.1.2"
     tslib "2.3.1"
     tslib "2.3.1"
 
 
-"@sematext/gc-stats@1.5.3":
-  version "1.5.3"
-  resolved "https://registry.yarnpkg.com/@sematext/gc-stats/-/gc-stats-1.5.3.tgz#66a1345b637b1d9e4f6415d9c82c13568d4afb3e"
-  integrity sha512-buh5fs+2ZIehfVY4+2mFZO5gxGXpX9mAOrR8duO3Spbb5y2aHJyvO0yxn2suWprKIPY1J337IndIdAf/WIyTYw==
+"@sematext/gc-stats@1.5.5":
+  version "1.5.5"
+  resolved "https://registry.yarnpkg.com/@sematext/gc-stats/-/gc-stats-1.5.5.tgz#3461e818454b95de26085b65f0d95417b9f183d6"
+  integrity sha512-OetiHJ7vVPy2xvM+qyTuGoDvnSy4B6JGEhoIRmAONhaa8oOL2NaBiBg2HEI+DQPwOpUeCTUVBV3/uiBt24Ru7Q==
   dependencies:
   dependencies:
-    nan "^2.14.2"
-    node-pre-gyp "^0.15.0"
+    "@mapbox/node-pre-gyp" "^1.0.5"
+    nan "^2.15.0"
 
 
 "@sindresorhus/is@^0.7.0":
 "@sindresorhus/is@^0.7.0":
   version "0.7.0"
   version "0.7.0"
@@ -6828,7 +6843,7 @@ detect-indent@^7.0.0:
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.0.tgz#cab58e6ab1129c669e2101181a6c677917d43577"
   resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-7.0.0.tgz#cab58e6ab1129c669e2101181a6c677917d43577"
   integrity sha512-/6kJlmVv6RDFPqaHC/ZDcU8bblYcoph2dUQ3kB47QqhkUEqXe3VZPELK9BaEMrC73qu+wn0AQ7iSteceN+yuMw==
   integrity sha512-/6kJlmVv6RDFPqaHC/ZDcU8bblYcoph2dUQ3kB47QqhkUEqXe3VZPELK9BaEMrC73qu+wn0AQ7iSteceN+yuMw==
 
 
-detect-libc@^1.0.2:
+detect-libc@^1.0.3:
   version "1.0.3"
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
   integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
   integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
@@ -8905,6 +8920,21 @@ functional-red-black-tree@^1.0.1:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
   resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
 
 
+gauge@^3.0.0:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
+  integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
+  dependencies:
+    aproba "^1.0.3 || ^2.0.0"
+    color-support "^1.1.2"
+    console-control-strings "^1.0.0"
+    has-unicode "^2.0.1"
+    object-assign "^4.1.1"
+    signal-exit "^3.0.0"
+    string-width "^4.2.3"
+    strip-ansi "^6.0.1"
+    wide-align "^1.1.2"
+
 gauge@^4.0.0:
 gauge@^4.0.0:
   version "4.0.0"
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8"
   resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.0.tgz#afba07aa0374a93c6219603b1fb83eaa2264d8f8"
@@ -9440,11 +9470,6 @@ grapheme-splitter@^1.0.4:
   resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
   resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
   integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
   integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
 
 
-growi-commons@^5.0.4:
-  version "5.0.4"
-  resolved "https://registry.yarnpkg.com/growi-commons/-/growi-commons-5.0.4.tgz#1235b7955a3f492803e8c714fef5c4a797e442b7"
-  integrity sha512-K282Pe97SnJgbZWAuMz9pNDTmvmw4JYPf/oYQaPmBsUjaxG4FDwd7+p5UFc5GqZUWcLwXvtJZQZMZEH/xpg+nA==
-
 growly@^1.3.0:
 growly@^1.3.0:
   version "1.3.0"
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
   resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -9988,7 +10013,7 @@ iconv-lite@0.4.23:
   dependencies:
   dependencies:
     safer-buffer ">= 2.1.2 < 3"
     safer-buffer ">= 2.1.2 < 3"
 
 
-iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
+iconv-lite@0.4.24, iconv-lite@^0.4.24:
   version "0.4.24"
   version "0.4.24"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
   dependencies:
   dependencies:
@@ -10022,7 +10047,7 @@ iferr@^0.1.5:
   version "0.1.5"
   version "0.1.5"
   resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
   resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501"
 
 
-ignore-walk@^3.0.1, ignore-walk@^3.0.3:
+ignore-walk@^3.0.3:
   version "3.0.3"
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
   resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37"
   integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
   integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==
@@ -12387,7 +12412,7 @@ make-dir@^3.0.0:
   dependencies:
   dependencies:
     semver "^6.0.0"
     semver "^6.0.0"
 
 
-make-dir@^3.0.2:
+make-dir@^3.0.2, make-dir@^3.1.0:
   version "3.1.0"
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
   integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
@@ -13331,7 +13356,7 @@ mkdirp@1.0.4, mkdirp@1.x, mkdirp@^1.0.0, mkdirp@^1.0.3, mkdirp@^1.0.4:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
 
-"mkdirp@>=0.5 0", mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.5, mkdirp@~0.5.1:
+"mkdirp@>=0.5 0", mkdirp@^0.5, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.5, mkdirp@~0.5.1:
   version "0.5.5"
   version "0.5.5"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
   integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -13628,7 +13653,7 @@ nan@^2.12.1, nan@^2.14.0:
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
   integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
   integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
 
 
-nan@^2.14.2, nan@^2.15.0:
+nan@^2.15.0:
   version "2.15.0"
   version "2.15.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.15.0.tgz#3f34a473ff18e15c1b5626b62903b5ad6e665fee"
   integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
   integrity sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==
@@ -13663,15 +13688,6 @@ ncp@~2.0.0:
   version "2.0.0"
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
   resolved "https://registry.yarnpkg.com/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3"
 
 
-needle@^2.5.0:
-  version "2.9.1"
-  resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684"
-  integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ==
-  dependencies:
-    debug "^3.2.6"
-    iconv-lite "^0.4.4"
-    sax "^1.2.4"
-
 negotiator@0.6.1:
 negotiator@0.6.1:
   version "0.6.1"
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
   resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9"
@@ -13782,6 +13798,13 @@ node-fetch@^2.6.1:
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
   integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
   integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
 
 
+node-fetch@^2.6.5:
+  version "2.6.6"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
+  integrity sha512-Z8/6vRlTUChSdIgMa51jxQ4lrw/Jy5SOW10ObaA47/RElsAN2c5Pn8bTgFGWn/ibwzXTE8qwr1Yzx28vsecXEA==
+  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"
@@ -13908,22 +13931,6 @@ node-object-hash@^1.2.0:
   resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94"
   resolved "https://registry.yarnpkg.com/node-object-hash/-/node-object-hash-1.4.2.tgz#385833d85b229902b75826224f6077be969a9e94"
   integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==
   integrity sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==
 
 
-node-pre-gyp@^0.15.0:
-  version "0.15.0"
-  resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.15.0.tgz#c2fc383276b74c7ffa842925241553e8b40f1087"
-  integrity sha512-7QcZa8/fpaU/BKenjcaeFF9hLz2+7S9AqyXFhlH/rilsQ/hPZKK32RtR5EQHJElgu+q5RfbJ34KriI79UWaorA==
-  dependencies:
-    detect-libc "^1.0.2"
-    mkdirp "^0.5.3"
-    needle "^2.5.0"
-    nopt "^4.0.1"
-    npm-packlist "^1.1.6"
-    npmlog "^4.0.2"
-    rc "^1.2.7"
-    rimraf "^2.6.1"
-    semver "^5.3.0"
-    tar "^4.4.2"
-
 node-readfiles@^0.2.0:
 node-readfiles@^0.2.0:
   version "0.2.0"
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/node-readfiles/-/node-readfiles-0.2.0.tgz#dbbd4af12134e2e635c245ef93ffcf6f60673a5d"
   resolved "https://registry.yarnpkg.com/node-readfiles/-/node-readfiles-0.2.0.tgz#dbbd4af12134e2e635c245ef93ffcf6f60673a5d"
@@ -14045,7 +14052,7 @@ normalize-url@^3.3.0:
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
   resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
   integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
   integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==
 
 
-npm-bundled@^1.0.1, npm-bundled@^1.1.1:
+npm-bundled@^1.1.1:
   version "1.1.1"
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
   resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b"
   integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
   integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==
@@ -14087,15 +14094,6 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0, npm-pack
     semver "^7.3.4"
     semver "^7.3.4"
     validate-npm-package-name "^3.0.0"
     validate-npm-package-name "^3.0.0"
 
 
-npm-packlist@^1.1.6:
-  version "1.4.8"
-  resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
-  integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==
-  dependencies:
-    ignore-walk "^3.0.1"
-    npm-bundled "^1.0.1"
-    npm-normalize-package-bin "^1.0.1"
-
 npm-packlist@^2.1.4:
 npm-packlist@^2.1.4:
   version "2.1.5"
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.5.tgz#43ef5bbb9f59b7c0ef91e0905f1dd707b4cfb33c"
   resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-2.1.5.tgz#43ef5bbb9f59b7c0ef91e0905f1dd707b4cfb33c"
@@ -14158,7 +14156,7 @@ npm-run-path@^4.0.1:
   dependencies:
   dependencies:
     path-key "^3.0.0"
     path-key "^3.0.0"
 
 
-npmlog@^4.0.2, npmlog@^4.1.2:
+npmlog@^4.1.2:
   version "4.1.2"
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
   integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
   integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -14168,6 +14166,16 @@ npmlog@^4.0.2, npmlog@^4.1.2:
     gauge "~2.7.3"
     gauge "~2.7.3"
     set-blocking "~2.0.0"
     set-blocking "~2.0.0"
 
 
+npmlog@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
+  integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
+  dependencies:
+    are-we-there-yet "^2.0.0"
+    console-control-strings "^1.1.0"
+    gauge "^3.0.0"
+    set-blocking "^2.0.0"
+
 npmlog@^6.0.0:
 npmlog@^6.0.0:
   version "6.0.0"
   version "6.0.0"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c"
   resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.0.tgz#ba9ef39413c3d936ea91553db7be49c34ad0520c"
@@ -16296,7 +16304,7 @@ rc-config-loader@^3.0.0:
     json5 "^2.1.1"
     json5 "^2.1.1"
     require-from-string "^2.0.2"
     require-from-string "^2.0.2"
 
 
-rc@>=1.2.8, rc@^1.0.1, rc@^1.1.6, rc@^1.2.7:
+rc@>=1.2.8, rc@^1.0.1, rc@^1.1.6:
   version "1.2.8"
   version "1.2.8"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
   resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
   integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==
@@ -17434,7 +17442,7 @@ sax@1.2.1:
   version "1.2.1"
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
 
 
-sax@>=0.6.0, sax@^1.2.1, sax@^1.2.4, sax@~1.2.4:
+sax@>=0.6.0, sax@^1.2.1, sax@~1.2.4:
   version "1.2.4"
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -17525,7 +17533,7 @@ semver@>=7.3.2:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938"
   integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
   integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==
 
 
-semver@^5.1.0, semver@^5.3.0:
+semver@^5.1.0:
   version "5.4.1"
   version "5.4.1"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
   resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e"
 
 
@@ -19204,7 +19212,7 @@ tar-stream@^2.2.0:
     inherits "^2.0.3"
     inherits "^2.0.3"
     readable-stream "^3.1.1"
     readable-stream "^3.1.1"
 
 
-tar@^4.4.12, tar@^4.4.2:
+tar@^4.4.12:
   version "4.4.19"
   version "4.4.19"
   resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"
   resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.19.tgz#2e4d7263df26f2b914dee10c825ab132123742f3"
   integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==
   integrity sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==
@@ -19229,7 +19237,7 @@ tar@^6.0.2, tar@^6.1.0:
     mkdirp "^1.0.3"
     mkdirp "^1.0.3"
     yallist "^4.0.0"
     yallist "^4.0.0"
 
 
-tar@^6.1.2:
+tar@^6.1.11, tar@^6.1.2:
   version "6.1.11"
   version "6.1.11"
   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
   resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.11.tgz#6760a38f003afa1b2ffd0ffe9e9abbd0eab3d621"
   integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
   integrity sha512-an/KZQzQUkZCkuoAA64hM92X0Urb6VpRhAFllDzz44U2mcD5scmT3zBc4VgVpkugF580+DQn8eAFSyoQt0tznA==
@@ -19887,6 +19895,11 @@ tr46@^3.0.0:
   dependencies:
   dependencies:
     punycode "^2.1.1"
     punycode "^2.1.1"
 
 
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+
 "traverse@>=0.3.0 <0.4":
 "traverse@>=0.3.0 <0.4":
   version "0.3.9"
   version "0.3.9"
   resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
   resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
@@ -19950,10 +19963,10 @@ tryer@^1.0.0:
   version "1.0.1"
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
   resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
 
 
-ts-essentials@8.1.0:
-  version "8.1.0"
-  resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-8.1.0.tgz#bc982b242db226b84c89477d3d42630ee2954513"
-  integrity sha512-34xALeQADWRYq9kbtprP4KdpTQ3v3BBIs/U4SpaP+C4++B8ijXY5NnILRJLchQVMzw7YBzKuGMUMrI9uT+ALVw==
+ts-essentials@9.0.0:
+  version "9.0.0"
+  resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-9.0.0.tgz#6196b7f390926429256c70951c8edd260e8e5097"
+  integrity sha512-pow5YBSknf/PyoDhr8vsb93+hZah/jSzhdHA3GMjSzUuZIDZH+rV7tvN5DCbN4hi7QJXdteDv8D9HdDCSwXBUw==
 
 
 ts-jest@^27.0.4:
 ts-jest@^27.0.4:
   version "27.0.4"
   version "27.0.4"
@@ -20631,10 +20644,10 @@ url-to-options@^1.0.1:
   resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
   resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
   integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=
   integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=
 
 
-url-value-parser@2.0.3:
-  version "2.0.3"
-  resolved "https://registry.yarnpkg.com/url-value-parser/-/url-value-parser-2.0.3.tgz#cd4b8d6754e458d65e8125260c09718d926e6e21"
-  integrity sha512-FjIX+Q9lYmDM9uYIGdMYfQW0uLbWVwN2NrL2ayAI7BTOvEwzH+VoDdNquwB9h4dFAx+u6mb0ONLa3sHD5DvyvA==
+url-value-parser@2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/url-value-parser/-/url-value-parser-2.1.0.tgz#fe1ae776122b2eea4bbf284896bbdcd7fc75e1fa"
+  integrity sha512-gIYPWXujdUdwd/9TGCHTf5Vvgw6lOxjE5Q/k+7WNByYyS0vW5WX0k+xuVlhvPq6gRNhzXVv/ezC+OfeAet5Kcw==
 
 
 url@0.10.3:
 url@0.10.3:
   version "0.10.3"
   version "0.10.3"
@@ -20914,6 +20927,11 @@ web-namespaces@^1.1.2:
   resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
   resolved "https://registry.yarnpkg.com/web-namespaces/-/web-namespaces-1.1.4.tgz#bc98a3de60dadd7faefc403d1076d529f5e030ec"
   integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==
   integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==
 
 
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+
 webidl-conversions@^5.0.0:
 webidl-conversions@^5.0.0:
   version "5.0.0"
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
@@ -21057,6 +21075,14 @@ whatwg-url@^11.0.0:
     tr46 "^3.0.0"
     tr46 "^3.0.0"
     webidl-conversions "^7.0.0"
     webidl-conversions "^7.0.0"
 
 
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
 whatwg-url@^8.0.0, whatwg-url@^8.5.0:
 whatwg-url@^8.0.0, whatwg-url@^8.5.0:
   version "8.7.0"
   version "8.7.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
@@ -21295,6 +21321,11 @@ ws@^7.4.6:
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
   integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
   integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
 
 
+ws@^8.3.0:
+  version "8.3.0"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d"
+  integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw==
+
 ws@~7.4.2:
 ws@~7.4.2:
   version "7.4.6"
   version "7.4.6"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"
   resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c"