Browse Source

feat: site logo upload + extensions fix

Nicolas Giard 2 years ago
parent
commit
33547ddce9

+ 1 - 1
.gitignore

@@ -19,7 +19,7 @@ npm-debug.log*
 # Generated assets
 /assets
 /assets-legacy
-server/views/base.pug
+/server/views/base.pug
 
 # Webpack
 .webpack-cache

+ 2 - 0
package.json

@@ -154,6 +154,7 @@
     "pg-tsquery": "8.4.0",
     "pug": "3.0.2",
     "punycode": "2.1.1",
+    "puppeteer-core": "17.1.3",
     "qr-image": "3.2.0",
     "rate-limiter-flexible": "2.3.8",
     "remove-markdown": "0.3.0",
@@ -164,6 +165,7 @@
     "scim-query-filter-parser": "2.0.4",
     "semver": "7.3.7",
     "serve-favicon": "2.5.0",
+    "sharp": "0.31.0",
     "simple-git": "2.21.0",
     "socket.io": "4.5.2",
     "ssh2": "1.9.0",

+ 24 - 0
server/controllers/common.js

@@ -9,6 +9,7 @@ const path = require('path')
 /* global WIKI */
 
 const tmplCreateRegex = /^[0-9]+(,[0-9]+)?$/
+const siteAssetsPath = path.resolve(WIKI.ROOTPATH, WIKI.config.dataPath, 'assets')
 
 /**
  * Robots.txt
@@ -33,6 +34,29 @@ router.get('/healthz', (req, res, next) => {
   }
 })
 
+/**
+ * Site Asset
+ */
+router.get('/_site/:siteId?/:resource', async (req, res, next) => {
+  const site = req.params.siteId ? WIKI.sites[req.params.siteId] : await WIKI.models.sites.getSiteByHostname({ hostname: req.hostname })
+  if (!site) {
+    return res.status(404).send('Site Not Found')
+  }
+  switch (req.params.resource) {
+    case 'logo': {
+      if (site.config.assets.logo) {
+        res.sendFile(path.join(siteAssetsPath, `logo-${site.id}.${site.config.assets.logoExt}`))
+      } else {
+        res.sendFile(path.join(WIKI.ROOTPATH, 'assets/_assets/logo-wikijs.svg'))
+      }
+      break
+    }
+    default: {
+      return res.status(404).send('Invalid Site Resource')
+    }
+  }
+})
+
 /**
  * New v3 vue app
  */

+ 4 - 1
server/core/servers.js

@@ -6,6 +6,7 @@ const Promise = require('bluebird')
 const _ = require('lodash')
 const io = require('socket.io')
 const { ApolloServerPluginLandingPageGraphQLPlayground, ApolloServerPluginLandingPageProductionDefault } = require('apollo-server-core')
+const { graphqlUploadExpress } = require('graphql-upload')
 
 /* global WIKI */
 
@@ -132,7 +133,8 @@ module.exports = {
     const graphqlSchema = require('../graph')
     this.graph = new ApolloServer({
       schema: graphqlSchema,
-      uploads: false,
+      csrfPrevention: true,
+      cache: 'bounded',
       context: ({ req, res }) => ({ req, res }),
       plugins: [
         process.env.NODE_ENV === 'development' ? ApolloServerPluginLandingPageGraphQLPlayground({
@@ -145,6 +147,7 @@ module.exports = {
       ]
     })
     await this.graph.start()
+    WIKI.app.use(graphqlUploadExpress())
     this.graph.applyMiddleware({ app: WIKI.app, cors: false, path: '/_graphql' })
   },
   /**

+ 14 - 1
server/db/migrations/3.0.0.js

@@ -56,7 +56,7 @@ exports.up = async knex => {
     })
     // ASSET DATA --------------------------
     .createTable('assetData', table => {
-      table.uuid('id').notNullable().index()
+      table.uuid('id').notNullable().primary()
       table.binary('data').notNullable()
     })
     // ASSET FOLDERS -----------------------
@@ -485,6 +485,11 @@ exports.up = async knex => {
       locale: 'en',
       localeNamespacing: false,
       localeNamespaces: [],
+      assets: {
+        logo: false,
+        logoExt: 'svg',
+        favicon: false
+      },
       theme: {
         dark: false,
         colorPrimary: '#1976D2',
@@ -580,10 +585,18 @@ exports.up = async knex => {
     {
       id: userGuestId,
       email: 'guest@example.com',
+      auth: {},
       name: 'Guest',
       isSystem: true,
       isActive: true,
       isVerified: true,
+      meta: {},
+      prefs: {
+        timezone: 'America/New_York',
+        dateFormat: 'YYYY-MM-DD',
+        timeFormat: '12h',
+        darkMode: false
+      },
       localeCode: 'en'
     }
   ])

+ 0 - 1
server/graph/resolvers/hooks.js

@@ -6,7 +6,6 @@ const _ = require('lodash')
 module.exports = {
   Query: {
     async hooks () {
-      WIKI.logger.warn('Seriously man')
       return WIKI.models.hooks.query().orderBy('name')
     },
     async hookById (obj, args) {

+ 23 - 4
server/graph/resolvers/site.js

@@ -2,6 +2,8 @@ const graphHelper = require('../../helpers/graph')
 const _ = require('lodash')
 const CleanCSS = require('clean-css')
 const path = require('path')
+const fs = require('fs-extra')
+const { v4: uuid } = require('uuid')
 
 /* global WIKI */
 
@@ -154,24 +156,41 @@ module.exports = {
         if (!WIKI.extensions.ext.sharp.isInstalled) {
           throw new Error('This feature requires the Sharp extension but it is not installed.')
         }
-        console.info(mimetype)
         const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
-        const destPath = path.resolve(
+        const destFolder = path.resolve(
           process.cwd(),
           WIKI.config.dataPath,
-          `assets/logo.${destFormat}`
+          `assets`
         )
+        const destPath = path.join(destFolder, `logo-${args.id}.${destFormat}`)
+        await fs.ensureDir(destFolder)
+        // -> Resize
         await WIKI.extensions.ext.sharp.resize({
           format: destFormat,
           inputStream: createReadStream(),
           outputPath: destPath,
-          width: 100
+          height: 72
         })
+        // -> Save logo meta to DB
+        const site = await WIKI.models.sites.query().findById(args.id)
+        if (!site.config.assets.logo) {
+          site.config.assets.logo = uuid()
+        }
+        site.config.assets.logoExt = destFormat
+        await WIKI.models.sites.query().findById(args.id).patch({ config: site.config })
+        await WIKI.models.sites.reloadCache()
+        // -> Save image data to DB
+        const imgBuffer = await fs.readFile(destPath)
+        await WIKI.models.knex('assetData').insert({
+          id: site.config.assets.logo,
+          data: imgBuffer
+        }).onConflict('id').merge()
         WIKI.logger.info('New site logo processed successfully.')
         return {
           operation: graphHelper.generateSuccess('Site logo uploaded successfully')
         }
       } catch (err) {
+        WIKI.logger.warn(err)
         return graphHelper.generateError(err)
       }
     },

+ 2 - 2
server/graph/resolvers/system.js

@@ -42,7 +42,7 @@ module.exports = {
         await WIKI.extensions.ext[args.key].install()
         // TODO: broadcast ext install
         return {
-          status: graphHelper.generateSuccess('Extension installed successfully')
+          operation: graphHelper.generateSuccess('Extension installed successfully')
         }
       } catch (err) {
         return graphHelper.generateError(err)
@@ -55,7 +55,7 @@ module.exports = {
       await WIKI.configSvc.applyFlags()
       await WIKI.configSvc.saveToDb(['flags'])
       return {
-        status: graphHelper.generateSuccess('System Flags applied successfully')
+        operation: graphHelper.generateSuccess('System Flags applied successfully')
       }
     },
     async updateSystemSecurity (obj, args, context) {

+ 5 - 0
server/models/sites.js

@@ -84,6 +84,11 @@ module.exports = class Site extends Model {
         locale: 'en',
         localeNamespacing: false,
         localeNamespaces: [],
+        assets: {
+          logo: false,
+          logoExt: 'svg',
+          favicon: false
+        },
         theme: {
           dark: false,
           colorPrimary: '#1976D2',

+ 1 - 0
server/modules/extensions/git/ext.js

@@ -5,6 +5,7 @@ module.exports = {
   title: 'Git',
   description: 'Distributed version control system. Required for the Git storage module.',
   isInstalled: false,
+  isInstallable: false,
   async isCompatible () {
     return true
   },

+ 1 - 0
server/modules/extensions/pandoc/ext.js

@@ -9,6 +9,7 @@ module.exports = {
     return os.arch() === 'x64'
   },
   isInstalled: false,
+  isInstallable: false,
   async check () {
     try {
       await cmdExists('pandoc')

+ 22 - 3
server/modules/extensions/puppeteer/ext.js

@@ -1,5 +1,10 @@
-const cmdExists = require('command-exists')
 const os = require('os')
+const path = require('path')
+const util = require('util')
+const exec = util.promisify(require('child_process').exec)
+const fs = require('fs-extra')
+
+/* global WIKI */
 
 module.exports = {
   key: 'puppeteer',
@@ -9,13 +14,27 @@ module.exports = {
     return os.arch() === 'x64'
   },
   isInstalled: false,
+  isInstallable: true,
   async check () {
     try {
-      await cmdExists('pandoc')
-      this.isInstalled = true
+      this.isInstalled = await fs.pathExists(path.join(process.cwd(), 'node_modules/puppeteer-core/.local-chromium'))
     } catch (err) {
       this.isInstalled = false
     }
     return this.isInstalled
+  },
+  async install () {
+    try {
+      const { stdout, stderr } = await exec('node install.js', {
+        cwd: path.join(process.cwd(), 'node_modules/puppeteer-core'),
+        timeout: 300000,
+        windowsHide: true
+      })
+      this.isInstalled = true
+      WIKI.logger.info(stdout)
+      WIKI.logger.warn(stderr)
+    } catch (err) {
+      WIKI.logger.error(err)
+    }
   }
 }

+ 68 - 1
server/modules/extensions/sharp/ext.js

@@ -1,6 +1,9 @@
 const fs = require('fs-extra')
 const os = require('os')
 const path = require('path')
+const util = require('util')
+const exec = util.promisify(require('child_process').exec)
+const { pipeline } = require('stream/promises')
 
 /* global WIKI */
 
@@ -12,8 +15,72 @@ module.exports = {
     return os.arch() === 'x64'
   },
   isInstalled: false,
+  isInstallable: true,
   async check () {
-    this.isInstalled = await fs.pathExists(path.join(WIKI.ROOTPATH, 'node_modules/sharp'))
+    this.isInstalled = await fs.pathExists(path.join(process.cwd(), 'node_modules/sharp/wiki_installed.txt'))
     return this.isInstalled
+  },
+  async install () {
+    try {
+      const { stdout, stderr } = await exec('node install/libvips && node install/dll-copy', {
+        cwd: path.join(process.cwd(), 'node_modules/sharp'),
+        timeout: 120000,
+        windowsHide: true
+      })
+      await fs.ensureFile(path.join(process.cwd(), 'node_modules/sharp/wiki_installed.txt'))
+      this.isInstalled = true
+      WIKI.logger.info(stdout)
+      WIKI.logger.warn(stderr)
+    } catch (err) {
+      WIKI.logger.error(err)
+    }
+  },
+  sharp: null,
+  async load () {
+    if (!this.sharp) {
+      this.sharp = require('sharp')
+    }
+  },
+  /**
+   * RESIZE IMAGE
+   */
+  async resize ({
+    format = 'png',
+    inputStream = null,
+    inputPath = null,
+    outputStream = null,
+    outputPath = null,
+    width = null,
+    height = null,
+    fit = 'cover',
+    background = { r: 0, g: 0, b: 0, alpha: 0 }
+  }) {
+    this.load()
+
+    if (inputPath) {
+      inputStream = fs.createReadStream(inputPath)
+    }
+    if (!inputStream) {
+      throw new Error('Failed to open readable input stream for image resizing.')
+    }
+    if (outputPath) {
+      outputStream = fs.createWriteStream(outputPath)
+    }
+    if (!outputStream) {
+      throw new Error('Failed to open writable output stream for image resizing.')
+    }
+
+    if (format === 'svg') {
+      return pipeline([inputStream, outputStream])
+    } else {
+      const transformer = this.sharp().resize({
+        width,
+        height,
+        fit,
+        background
+      }).toFormat(format)
+
+      return pipeline([inputStream, transformer, outputStream])
+    }
   }
 }

+ 10 - 2
server/views/base.pug

@@ -54,20 +54,28 @@ html(lang=siteConfig.lang)
 
     //- CSS
     
+      
+    link(
+      type='text/css'
+      rel='stylesheet'
+      href='/_assets-legacy/css/app.629ebe3c082227dbee31.css'
+    )
+      
+    
 
     //- JS
     
       
     script(
       type='text/javascript'
-      src='/_assets-legacy/js/runtime.js'
+      src='/_assets-legacy/js/runtime.js?1662846772'
       )
       
     
       
     script(
       type='text/javascript'
-      src='/_assets-legacy/js/app.js'
+      src='/_assets-legacy/js/app.js?1662846772'
       )
       
     

+ 1 - 2
server/web.js

@@ -181,8 +181,7 @@ module.exports = async () => {
       lang: currentSite.config.locale,
       rtl: false, // TODO: handle RTL
       company: currentSite.config.company,
-      contentLicense: currentSite.config.contentLicense,
-      logoUrl: currentSite.config.logoUrl
+      contentLicense: currentSite.config.contentLicense
     }
     res.locals.langs = await WIKI.models.locales.getNavLocales({ cache: true })
     res.locals.analyticsCode = await WIKI.models.analytics.getCode({ cache: true })

+ 2 - 1
ux/quasar.config.js

@@ -97,7 +97,8 @@ module.exports = configure(function (/* ctx */) {
       open: false, // opens browser window automatically
       port: 3001,
       proxy: {
-        '/_graphql': 'http://localhost:3000/_graphql'
+        '/_graphql': 'http://localhost:3000/_graphql',
+        '/_site': 'http://localhost:3000'
       }
     },
 

+ 0 - 68
ux/src/layouts/AuthLayout.vue

@@ -9,73 +9,5 @@ q-layout(view='hHh lpr lff')
 </script>
 
 <style lang="scss">
-  .auth {
-    background-color: #FFF;
-    display: flex;
 
-    @at-root .body--dark & {
-      background-color: $dark-6;
-    }
-
-    &-content {
-      flex: 1 0 100%;
-      width: 100%;
-      max-width: 500px;
-      padding: 3rem 4rem;
-      display: flex;
-      flex-direction: column;
-      justify-content: center;
-      align-items: stretch;
-
-      @media (max-width: $breakpoint-xs-max) {
-        padding: 1rem 2rem;
-        max-width: 100vw;
-      }
-    }
-
-    &-logo {
-      img {
-        height: 72px;
-      }
-    }
-
-    &-site-title {
-      font-size: 1.875rem;
-      line-height: 2.25rem;
-      font-weight: 700;
-      margin: 0;
-      color: $blue-grey-9;
-
-      @at-root .body--dark & {
-        color: $blue-grey-1;
-      }
-    }
-
-    &-strategies {
-      display: grid;
-      grid-template-columns: repeat(auto-fit, minmax(45%, 1fr));
-      gap: 10px;
-    }
-
-    &-bg {
-      flex: 1;
-      flex-basis: 0;
-      position: relative;
-      height: 100vh;
-      overflow: hidden;
-
-      img {
-        position: relative;
-        width: 100%;
-        height: 100%;
-        object-fit: cover;
-        top: 0;
-        bottom: 0;
-        left: 0;
-        right: 0;
-        margin: 0;
-        padding: 0;
-      }
-    }
-  }
 </style>

+ 3 - 3
ux/src/pages/AdminExtensions.vue

@@ -174,7 +174,7 @@ async function install (ext) {
           installExtension (
             key: $key
           ) {
-            status {
+            operation {
               succeeded
               message
             }
@@ -185,7 +185,7 @@ async function install (ext) {
         key: ext.key
       }
     })
-    if (respRaw.data?.installExtension?.status?.succeeded) {
+    if (respRaw.data?.installExtension?.operation?.succeeded) {
       $q.notify({
         type: 'positive',
         message: t('admin.extensions.installSuccess')
@@ -193,7 +193,7 @@ async function install (ext) {
       ext.isInstalled = true
       // this.$forceUpdate()
     } else {
-      throw new Error(respRaw.data?.installExtension?.status?.message || 'An unexpected error occured')
+      throw new Error(respRaw.data?.installExtension?.operation?.message || 'An unexpected error occured')
     }
   } catch (err) {
     $q.notify({

+ 6 - 4
ux/src/pages/AdminGeneral.vue

@@ -255,16 +255,16 @@ q-page.admin-general
               dark
               style='height: 64px;'
               )
-              q-btn(dense, flat, to='/')
+              q-btn(dense, flat, v-if='adminStore.currentSiteId')
                 q-avatar(
                   v-if='state.config.logoText'
                   size='34px'
                   square
                   )
-                  img(src='/_assets/logo-wikijs.svg')
+                  img(:src='`/_site/` + adminStore.currentSiteId + `/logo?` + state.assetTimestamp')
                 img(
                   v-else
-                  src='https://m.media-amazon.com/images/G/01/audibleweb/arya/navigation/audible_logo._V517446980_.svg'
+                  :src='`/_site/` + adminStore.currentSiteId + `/logo?` + state.assetTimestamp'
                   style='height: 34px;'
                   )
               q-toolbar-title.text-h6(v-if='state.config.logoText') {{state.config.title}}
@@ -456,6 +456,7 @@ useMeta({
 
 const state = reactive({
   loading: 0,
+  assetTimestamp: (new Date()).toISOString(),
   config: {
     hostname: '',
     title: '',
@@ -678,7 +679,7 @@ async function uploadLogo () {
               id: $id
               image: $image
             ) {
-              status {
+              operation {
                 succeeded
                 slug
                 message
@@ -695,6 +696,7 @@ async function uploadLogo () {
         type: 'positive',
         message: t('admin.general.logoUploadSuccess')
       })
+      state.assetTimestamp = (new Date()).toISOString()
     } catch (err) {
       $q.notify({
         type: 'negative',

+ 72 - 2
ux/src/pages/Login.vue

@@ -2,8 +2,8 @@
 .auth
   .auth-content
     .auth-logo
-      img(src='/_assets/logo-wikijs.svg' :alt='siteStore.title')
-    h2.auth-site-title {{ siteStore.title }}
+      img(src='/_site/logo' :alt='siteStore.title')
+    h2.auth-site-title(v-if='siteStore.logoText') {{ siteStore.title }}
     p.text-grey-7 Login to continue
     auth-login-panel
   .auth-bg(aria-hidden="true")
@@ -412,5 +412,75 @@ onMounted(() => {
 </script>
 
 <style lang="scss">
+ .auth {
+    background-color: #FFF;
+    display: flex;
 
+    @at-root .body--dark & {
+      background-color: $dark-6;
+    }
+
+    &-content {
+      flex: 1 0 100%;
+      width: 100%;
+      max-width: 500px;
+      padding: 3rem 4rem;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      align-items: stretch;
+
+      @media (max-width: $breakpoint-xs-max) {
+        padding: 1rem 2rem;
+        max-width: 100vw;
+      }
+    }
+
+    &-logo {
+      margin-bottom: 6px;
+
+      img {
+        height: 72px;
+      }
+    }
+
+    &-site-title {
+      font-size: 1.875rem;
+      line-height: 2.25rem;
+      font-weight: 700;
+      margin: 0;
+      color: $blue-grey-9;
+
+      @at-root .body--dark & {
+        color: $blue-grey-1;
+      }
+    }
+
+    &-strategies {
+      display: grid;
+      grid-template-columns: repeat(auto-fit, minmax(45%, 1fr));
+      gap: 10px;
+    }
+
+    &-bg {
+      flex: 1;
+      flex-basis: 0;
+      position: relative;
+      height: 100vh;
+      overflow: hidden;
+
+      img {
+        position: relative;
+        width: 100%;
+        height: 100%;
+        object-fit: cover;
+        top: 0;
+        bottom: 0;
+        left: 0;
+        right: 0;
+        margin: 0;
+        padding: 0;
+      }
+    }
+  }
 </style>

+ 2 - 2
ux/src/stores/site.js

@@ -13,7 +13,7 @@ export const useSiteStore = defineStore('site', {
     dark: false,
     title: '',
     description: '',
-    logoUrl: '',
+    logoText: true,
     search: '',
     searchIsFocused: false,
     searchIsLoading: false,
@@ -98,7 +98,7 @@ export const useSiteStore = defineStore('site', {
           this.hostname = clone(siteInfo.hostname)
           this.title = clone(siteInfo.title)
           this.description = clone(siteInfo.description)
-          this.logoUrl = clone(siteInfo.logoUrl)
+          this.logoText = clone(siteInfo.logoText)
           this.company = clone(siteInfo.company)
           this.contentLicense = clone(siteInfo.contentLicense)
           this.theme = {

+ 202 - 13
yarn.lock

@@ -3368,6 +3368,13 @@
   dependencies:
     "@types/yargs-parser" "*"
 
+"@types/yauzl@^2.9.1":
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599"
+  integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==
+  dependencies:
+    "@types/node" "*"
+
 "@types/zen-observable@^0.8.0":
   version "0.8.0"
   resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
@@ -5166,7 +5173,7 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
-buffer@^5.5.0:
+buffer@^5.2.1, buffer@^5.5.0:
   version "5.7.1"
   resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
   integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
@@ -5928,6 +5935,14 @@ color-string@^1.4.0, color-string@^1.5.2:
     color-name "^1.0.0"
     simple-swizzle "^0.2.2"
 
+color-string@^1.9.0:
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
+  integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
+  dependencies:
+    color-name "^1.0.0"
+    simple-swizzle "^0.2.2"
+
 color@^0.11.0:
   version "0.11.4"
   resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764"
@@ -5961,6 +5976,14 @@ color@^3.0.0:
     color-convert "^1.9.1"
     color-string "^1.5.2"
 
+color@^4.2.3:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+  integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
+  dependencies:
+    color-convert "^2.0.1"
+    color-string "^1.9.0"
+
 colorette@2.0.19:
   version "2.0.19"
   resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
@@ -6363,6 +6386,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
+cross-fetch@3.1.5:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
+  integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
+  dependencies:
+    node-fetch "2.6.7"
+
 cross-fetch@^1.0.0:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-1.1.1.tgz#dede6865ae30f37eae62ac90ebb7bdac002b05a0"
@@ -7364,6 +7394,13 @@ decompress-response@^3.3.0:
   dependencies:
     mimic-response "^1.0.0"
 
+decompress-response@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
+  integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
+  dependencies:
+    mimic-response "^3.1.0"
+
 deep-extend@^0.5.1:
   version "0.5.1"
   resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.5.1.tgz#b894a9dd90d3023fbf1c55a394fb858eb2066f1f"
@@ -7507,11 +7544,21 @@ detect-libc@^1.0.2:
   resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
   integrity sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=
 
+detect-libc@^2.0.0, detect-libc@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd"
+  integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==
+
 detect-newline@^3.0.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
   integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==
 
+devtools-protocol@0.0.1036444:
+  version "0.0.1036444"
+  resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.1036444.tgz#a570d3cdde61527c82f9b03919847b8ac7b1c2b9"
+  integrity sha512-0y4f/T8H9lsESV9kKP1HDUXgHxCdniFeJh6Erq+FbdOEvp/Ydp9t8kcAAM5gOd17pMrTDlFWntoHtzzeTUWKNw==
+
 dezalgo@1.0.3:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/dezalgo/-/dezalgo-1.0.3.tgz#7f742de066fc748bc8db820569dddce49bf0d456"
@@ -8516,6 +8563,11 @@ expand-brackets@^2.1.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+expand-template@^2.0.3:
+  version "2.0.3"
+  resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c"
+  integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==
+
 expand-tilde@^2.0.0, expand-tilde@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
@@ -8664,6 +8716,17 @@ extglob@^2.0.4:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+extract-zip@2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
+  integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
+  dependencies:
+    debug "^4.1.1"
+    get-stream "^5.1.0"
+    yauzl "^2.10.0"
+  optionalDependencies:
+    "@types/yauzl" "^2.9.1"
+
 extract-zip@^1.7.0:
   version "1.7.0"
   resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927"
@@ -9292,6 +9355,11 @@ getpass@^0.1.1:
   dependencies:
     assert-plus "^1.0.0"
 
+github-from-package@0.0.0:
+  version "0.0.0"
+  resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce"
+  integrity sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==
+
 glob-parent@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -9956,6 +10024,14 @@ https-browserify@^1.0.0:
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
   integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
 
+https-proxy-agent@5.0.1:
+  version "5.0.1"
+  resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+  integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+  dependencies:
+    agent-base "6"
+    debug "4"
+
 https-proxy-agent@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
@@ -12346,6 +12422,11 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
   resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
   integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
 
+mimic-response@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
+  integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
+
 mini-css-extract-plugin@0.11.3:
   version "0.11.3"
   resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.11.3.tgz#15b0910a7f32e62ffde4a7430cfefbd700724ea6"
@@ -12396,6 +12477,11 @@ minimist@^1.1.1, minimist@^1.2.0:
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
   integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
 
+minimist@^1.2.3:
+  version "1.2.6"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+  integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
 minimist@^1.2.5:
   version "1.2.5"
   resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
@@ -12481,6 +12567,11 @@ mkdirp-classic@^0.5.2:
   resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.2.tgz#54c441ce4c96cd7790e10b41a87aa51068ecab2b"
   integrity sha512-ejdnDQcR75gwknmMw/tx02AuRs8jCtqFoFqDZMjiNxsu85sRIJVXDKHuLYvUUPRBUtV2FpSZa9bL1BUa3BdR2g==
 
+mkdirp-classic@^0.5.3:
+  version "0.5.3"
+  resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113"
+  integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==
+
 mkdirp@0.3.0:
   version "0.3.0"
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e"
@@ -12645,6 +12736,11 @@ nanomatch@^1.2.9:
     snapdragon "^0.8.1"
     to-regex "^3.0.1"
 
+napi-build-utils@^1.0.1:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
+  integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
+
 natural-compare@^1.4.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -12717,6 +12813,18 @@ node-2fa@1.1.2:
     notp "^2.0.3"
     thirty-two "0.0.2"
 
+node-abi@^3.3.0:
+  version "3.24.0"
+  resolved "https://registry.yarnpkg.com/node-abi/-/node-abi-3.24.0.tgz#b9d03393a49f2c7e147d0c99f180e680c27c1599"
+  integrity sha512-YPG3Co0luSu6GwOBsmIdGW6Wx0NyNDLg/hriIyDllVsNwnI6UeqaWShxC3lbH4LtEQUgoLP3XR1ndXiDAWvmRw==
+  dependencies:
+    semver "^7.3.5"
+
+node-addon-api@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.0.0.tgz#7d7e6f9ef89043befdb20c1989c905ebde18c501"
+  integrity sha512-CvkDw2OEnme7ybCykJpVcKH+uAOLV2qLqiyla128dN9TkEWfrYmxG6C2boDe5KcNQqZF3orkqzGgOMvZ/JNekA==
+
 node-cache@5.1.2:
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d"
@@ -12732,7 +12840,7 @@ node-fetch@1.7.3:
     encoding "^0.1.11"
     is-stream "^1.0.1"
 
-node-fetch@^2.6.1, node-fetch@^2.6.7:
+node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7:
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -15039,6 +15147,24 @@ postgres-interval@^1.1.0:
   dependencies:
     xtend "^4.0.0"
 
+prebuild-install@^7.1.1:
+  version "7.1.1"
+  resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
+  integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==
+  dependencies:
+    detect-libc "^2.0.0"
+    expand-template "^2.0.3"
+    github-from-package "0.0.0"
+    minimist "^1.2.3"
+    mkdirp-classic "^0.5.3"
+    napi-build-utils "^1.0.1"
+    node-abi "^3.3.0"
+    pump "^3.0.0"
+    rc "^1.2.7"
+    simple-get "^4.0.0"
+    tar-fs "^2.0.0"
+    tunnel-agent "^0.6.0"
+
 precond@0.2:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/precond/-/precond-0.2.3.tgz#aa9591bcaa24923f1e0f4849d240f47efc1075ac"
@@ -15119,7 +15245,7 @@ process@^0.11.10:
   resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
   integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
 
-progress@^2.0.0:
+progress@2.0.3, progress@^2.0.0:
   version "2.0.3"
   resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
   integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
@@ -15181,6 +15307,11 @@ proxy-addr@~2.0.7:
     forwarded "0.2.0"
     ipaddr.js "1.9.1"
 
+proxy-from-env@1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
 prr@~1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
@@ -15464,6 +15595,23 @@ pupa@^2.1.1:
   dependencies:
     escape-goat "^2.0.0"
 
+puppeteer-core@17.1.3:
+  version "17.1.3"
+  resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-17.1.3.tgz#3a8e16c9a251f3592e1ad06a11edd9a813df9698"
+  integrity sha512-gm3d5fTVEc+h7jVtT3Y1k8OL1awaOzln44UfsJhUJKH/tyO/wn/zOxyAHTQt9aX/yo37IS0dfisU3i3P8qvZnw==
+  dependencies:
+    cross-fetch "3.1.5"
+    debug "4.3.4"
+    devtools-protocol "0.0.1036444"
+    extract-zip "2.0.1"
+    https-proxy-agent "5.0.1"
+    progress "2.0.3"
+    proxy-from-env "1.1.0"
+    rimraf "3.0.2"
+    tar-fs "2.1.1"
+    unbzip2-stream "1.4.3"
+    ws "8.8.1"
+
 q@^1.1.2:
   version "1.5.1"
   resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@@ -16253,6 +16401,13 @@ rimraf@2.6.3:
   dependencies:
     glob "^7.1.3"
 
+rimraf@3.0.2, rimraf@^3.0.2:
+  version "3.0.2"
+  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+  dependencies:
+    glob "^7.1.3"
+
 rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.3:
   version "2.7.1"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
@@ -16267,13 +16422,6 @@ rimraf@^3.0.0:
   dependencies:
     glob "^7.1.3"
 
-rimraf@^3.0.2:
-  version "3.0.2"
-  resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
-  integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
-  dependencies:
-    glob "^7.1.3"
-
 rimraf@~2.4.0:
   version "2.4.5"
   resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.4.5.tgz#ee710ce5d93a8fdb856fb5ea8ff0e2d75934b2da"
@@ -16522,7 +16670,7 @@ semver@7.0.0:
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
   integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
 
-semver@7.3.7:
+semver@7.3.7, semver@^7.3.7:
   version "7.3.7"
   resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f"
   integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==
@@ -16679,6 +16827,20 @@ shallow-clone@^3.0.0:
   dependencies:
     kind-of "^6.0.2"
 
+sharp@0.31.0:
+  version "0.31.0"
+  resolved "https://registry.yarnpkg.com/sharp/-/sharp-0.31.0.tgz#ce9b5202a5667486721cf07fd5b52360b1c2275a"
+  integrity sha512-ft96f8WzGxavg0rkLpMw90MTPMUZDyf0tHjPPh8Ob59xt6KzX8EqtotcqZGUm7kwqpX2pmYiyYX2LL0IZ/FDEw==
+  dependencies:
+    color "^4.2.3"
+    detect-libc "^2.0.1"
+    node-addon-api "^5.0.0"
+    prebuild-install "^7.1.1"
+    semver "^7.3.7"
+    simple-get "^4.0.1"
+    tar-fs "^2.1.1"
+    tunnel-agent "^0.6.0"
+
 shebang-command@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
@@ -16732,6 +16894,20 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
+simple-concat@^1.0.0:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f"
+  integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==
+
+simple-get@^4.0.0, simple-get@^4.0.1:
+  version "4.0.1"
+  resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-4.0.1.tgz#4a39db549287c979d352112fa03fd99fd6bc3543"
+  integrity sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==
+  dependencies:
+    decompress-response "^6.0.0"
+    once "^1.3.1"
+    simple-concat "^1.0.0"
+
 simple-git@2.21.0:
   version "2.21.0"
   resolved "https://registry.yarnpkg.com/simple-git/-/simple-git-2.21.0.tgz#d25d3fdc6a139cd7f80f197541a6f9f6e9d4cbc8"
@@ -17457,7 +17633,7 @@ tapable@^1.0.0, tapable@^1.1.3:
   resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2"
   integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==
 
-tar-fs@2.1.1:
+tar-fs@2.1.1, tar-fs@^2.0.0, tar-fs@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784"
   integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==
@@ -17609,7 +17785,7 @@ through2@^2.0.0:
     readable-stream "~2.3.6"
     xtend "~4.0.1"
 
-through@2:
+through@2, through@^2.3.8:
   version "2.3.8"
   resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
   integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
@@ -17982,6 +18158,14 @@ unbox-primitive@^1.0.2:
     has-symbols "^1.0.3"
     which-boxed-primitive "^1.0.2"
 
+unbzip2-stream@1.4.3:
+  version "1.4.3"
+  resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7"
+  integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==
+  dependencies:
+    buffer "^5.2.1"
+    through "^2.3.8"
+
 undefsafe@^2.0.5:
   version "2.0.5"
   resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c"
@@ -18977,6 +19161,11 @@ write@1.0.3:
   dependencies:
     mkdirp "^0.5.1"
 
+ws@8.8.1:
+  version "8.8.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+  integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
 ws@^6.0.0:
   version "6.2.1"
   resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"