Browse Source

Merge stable branch

Antoine du Hamel 2 years ago
parent
commit
a6029d0304
47 changed files with 1159 additions and 191 deletions
  1. 4 3
      .eslintrc.js
  2. 4 7
      .github/workflows/release-beta-candidate.yml
  3. 41 0
      CHANGELOG.md
  4. 8 0
      packages/@uppy/aws-s3-multipart/CHANGELOG.md
  5. 59 35
      packages/@uppy/aws-s3-multipart/src/index.js
  6. 31 0
      packages/@uppy/aws-s3-multipart/src/index.test.js
  7. 7 0
      packages/@uppy/aws-s3/CHANGELOG.md
  8. 39 13
      packages/@uppy/aws-s3/src/MiniXHRUpload.js
  9. 15 0
      packages/@uppy/aws-s3/src/index.js
  10. 31 0
      packages/@uppy/aws-s3/src/index.test.js
  11. 10 2
      packages/@uppy/companion-client/src/RequestClient.js
  12. 7 0
      packages/@uppy/core/CHANGELOG.md
  13. 1 1
      packages/@uppy/core/src/Uppy.js
  14. 7 0
      packages/@uppy/react/CHANGELOG.md
  15. 2 1
      packages/@uppy/react/src/useUppy.js
  16. 8 0
      packages/@uppy/remote-sources/CHANGELOG.md
  17. 21 0
      packages/@uppy/remote-sources/LICENSE
  18. 35 0
      packages/@uppy/remote-sources/README.md
  19. 51 0
      packages/@uppy/remote-sources/package.json
  20. 83 0
      packages/@uppy/remote-sources/src/index.js
  21. 42 0
      packages/@uppy/remote-sources/src/index.test.js
  22. 13 0
      packages/@uppy/remote-sources/types/index.d.ts
  23. 11 0
      packages/@uppy/remote-sources/types/index.test-d.ts
  24. 7 0
      packages/@uppy/robodog/CHANGELOG.md
  25. 2 2
      packages/@uppy/robodog/src/TransloaditResultsPlugin.js
  26. 3 0
      packages/@uppy/robodog/src/addProviders.js
  27. 1 0
      packages/@uppy/robodog/src/form.js
  28. 2 0
      packages/@uppy/robodog/src/index.js
  29. 7 0
      packages/@uppy/transloadit/CHANGELOG.md
  30. 5 3
      packages/@uppy/transloadit/src/index.js
  31. 8 0
      packages/@uppy/tus/CHANGELOG.md
  32. 36 29
      packages/@uppy/tus/src/index.js
  33. 1 1
      packages/@uppy/tus/types/index.d.ts
  34. 8 0
      packages/@uppy/url/CHANGELOG.md
  35. 4 2
      packages/@uppy/url/src/Url.jsx
  36. 2 2
      packages/@uppy/url/types/index.d.ts
  37. 7 0
      packages/@uppy/xhr-upload/CHANGELOG.md
  38. 4 4
      packages/@uppy/xhr-upload/src/index.js
  39. 1 0
      packages/uppy/index.mjs
  40. 14 18
      private/dev/Dashboard.js
  41. 3 2
      private/release/choose-semverness.js
  42. 21 16
      website/src/docs/react-dragdrop.md
  43. 11 6
      website/src/docs/react-fileinput.md
  44. 11 5
      website/src/docs/react-progressbar.md
  45. 12 6
      website/src/docs/react-statusbar.md
  46. 78 0
      website/src/docs/remote-sources.md
  47. 381 33
      yarn.lock

+ 4 - 3
.eslintrc.js

@@ -197,8 +197,8 @@ module.exports = {
 
 
         // Packages that have switched to ESM sources:
         // Packages that have switched to ESM sources:
         'packages/@uppy/audio/src/**/*.js',
         'packages/@uppy/audio/src/**/*.js',
-        'packages/@uppy/aws-s3/src/**/*.js',
         'packages/@uppy/aws-s3-multipart/src/**/*.js',
         'packages/@uppy/aws-s3-multipart/src/**/*.js',
+        'packages/@uppy/aws-s3/src/**/*.js',
         'packages/@uppy/box/src/**/*.js',
         'packages/@uppy/box/src/**/*.js',
         'packages/@uppy/companion-client/src/**/*.js',
         'packages/@uppy/companion-client/src/**/*.js',
         'packages/@uppy/compressor/src/**/*.js',
         'packages/@uppy/compressor/src/**/*.js',
@@ -222,16 +222,17 @@ module.exports = {
         'packages/@uppy/provider-views/src/**/*.js',
         'packages/@uppy/provider-views/src/**/*.js',
         'packages/@uppy/react/src/**/*.js',
         'packages/@uppy/react/src/**/*.js',
         'packages/@uppy/redux-dev-tools/src/**/*.js',
         'packages/@uppy/redux-dev-tools/src/**/*.js',
+        'packages/@uppy/remote-sources/src/**/*.js',
         'packages/@uppy/screen-capture/src/**/*.js',
         'packages/@uppy/screen-capture/src/**/*.js',
         'packages/@uppy/status-bar/src/**/*.js',
         'packages/@uppy/status-bar/src/**/*.js',
         'packages/@uppy/store-default/src/**/*.js',
         'packages/@uppy/store-default/src/**/*.js',
         'packages/@uppy/store-redux/src/**/*.js',
         'packages/@uppy/store-redux/src/**/*.js',
-        'packages/@uppy/svelte/src/**/*.js',
         'packages/@uppy/svelte/rollup.config.js',
         'packages/@uppy/svelte/rollup.config.js',
+        'packages/@uppy/svelte/src/**/*.js',
         'packages/@uppy/thumbnail-generator/src/**/*.js',
         'packages/@uppy/thumbnail-generator/src/**/*.js',
+        'packages/@uppy/transloadit/src/**/*.js',
         'packages/@uppy/tus/src/**/*.js',
         'packages/@uppy/tus/src/**/*.js',
         'packages/@uppy/unsplash/src/**/*.js',
         'packages/@uppy/unsplash/src/**/*.js',
-        'packages/@uppy/transloadit/src/**/*.js',
         'packages/@uppy/url/src/**/*.js',
         'packages/@uppy/url/src/**/*.js',
         'packages/@uppy/utils/src/**/*.js',
         'packages/@uppy/utils/src/**/*.js',
         'packages/@uppy/vue/src/**/*.js',
         'packages/@uppy/vue/src/**/*.js',

+ 4 - 7
.github/workflows/release-beta-candidate.yml

@@ -14,13 +14,8 @@ jobs:
       - name: Checkout sources
       - name: Checkout sources
         uses: actions/checkout@v3
         uses: actions/checkout@v3
         with:
         with:
-          branch: release
-      - name: Rebase
-        run: |
-          git fetch origin ${{ env.BETA_BRANCH }} --depth=1
-          git config --global user.email "actions@github.com"
-          git config --global user.name "GitHub Actions"
-          git rebase FETCH_HEAD
+          branch: release-beta
+          fetch-depth: 3 # the prepare commit, the merge commit, and the base ones.
       - name: Get yarn cache directory path
       - name: Get yarn cache directory path
         id: yarn-cache-dir-path
         id: yarn-cache-dir-path
         run: echo "::set-output name=dir::$(corepack yarn config get cacheFolder)"
         run: echo "::set-output name=dir::$(corepack yarn config get cacheFolder)"
@@ -56,6 +51,8 @@ jobs:
           echo "This is a release candidate for the following packages:" >> commitMessage
           echo "This is a release candidate for the following packages:" >> commitMessage
           echo >> commitMessage
           echo >> commitMessage
           jq -r 'map("- `"+.ident+"`: "+.oldVersion+" -> "+.newVersion) | join("\n") ' < releases.json >> commitMessage
           jq -r 'map("- `"+.ident+"`: "+.oldVersion+" -> "+.newVersion) | join("\n") ' < releases.json >> commitMessage
+          git config --global user.email "actions@github.com"
+          git config --global user.name "GitHub Actions"
           git commit -n --amend --file commitMessage
           git commit -n --amend --file commitMessage
       - name: Open Pull Request
       - name: Open Pull Request
         id: pr_opening
         id: pr_opening

+ 41 - 0
CHANGELOG.md

@@ -50,6 +50,47 @@ Released: 2022-05-30
 - @uppy/transloadit: remove IE 10 hack (Antoine du Hamel / #3777)
 - @uppy/transloadit: remove IE 10 hack (Antoine du Hamel / #3777)
 
 
 
 
+## 2.12.1
+
+Released: 2022-06-09
+
+| Package           | Version | Package           | Version |
+| ----------------- | ------- | ----------------- | ------- |
+| @uppy/transloadit |   2.3.1 | uppy              |  2.12.1 |
+| @uppy/robodog     |   2.8.1 |                   |         |
+
+- @uppy/transloadit: fix `COMPANION_PATTERN` export (Antoine du Hamel / #3820)
+- meta: fix URL generation in the release script (Antoine du Hamel)
+
+
+## 2.12.0
+
+Released: 2022-06-07
+
+| Package                | Version | Package                | Version |
+| ---------------------- | ------- | ---------------------- | ------- |
+| @uppy/aws-s3           |   2.2.1 | @uppy/tus              |   2.4.1 |
+| @uppy/aws-s3-multipart |   2.4.1 | @uppy/url              |   2.2.0 |
+| @uppy/companion-client |   2.2.1 | @uppy/xhr-upload       |   2.1.2 |
+| @uppy/core             |   2.3.1 | @uppy/robodog          |   2.8.0 |
+| @uppy/react            |   2.2.2 | uppy                   |  2.12.0 |
+| @uppy/remote-sources   |   0.1.0 |                        |         |
+
+- @uppy/remote-sources: Add @uppy/remote-sources preset/plugin (Artur Paikin / #3676)
+- @uppy/react: Reset uppy instance when React component is unmounted (Tomasz Pęksa / #3814)
+- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
+- @uppy/xhr-upload: replace `ev.target.status` with `xhr.status` (Wes Sankey / #3782)
+- @uppy/core: fix `TypeError` when file was deleted (Antoine du Hamel / #3811)
+- @uppy/robodog: fix linter warnings (Antoine du Hamel / #3808)
+- meta: fix GHA workflow for prereleases (Antoine du Hamel)
+- @uppy/aws-s3-multipart: allow `companionHeaders` to be modified with `setOptions` (Paulo Lemos Neto / #3770)
+- @uppy/url: enable passing optional meta data to `addFile` (Brad Edelman / #3788)
+- @uppy/url: fix `getFileNameFromUrl` (Brad Edelman / #3804)
+- @uppy/tus: make onShouldRetry type optional (Merlijn Vos / #3800)
+- doc: fix React examples (Antoine du Hamel / #3799)
+- meta: add GHA workflow for prereleases (Antoine du Hamel)
+
+
 ## 2.11.0
 ## 2.11.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 8 - 0
packages/@uppy/aws-s3-multipart/CHANGELOG.md

@@ -1,5 +1,13 @@
 # @uppy/aws-s3-multipart
 # @uppy/aws-s3-multipart
 
 
+## 2.4.1
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
+- @uppy/aws-s3-multipart: allow `companionHeaders` to be modified with `setOptions` (Paulo Lemos Neto / #3770)
+
 ## 2.4.0
 ## 2.4.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 59 - 35
packages/@uppy/aws-s3-multipart/src/index.js

@@ -20,12 +20,16 @@ function assertServerError (res) {
 export default class AwsS3Multipart extends BasePlugin {
 export default class AwsS3Multipart extends BasePlugin {
   static VERSION = packageJson.version
   static VERSION = packageJson.version
 
 
+  #queueRequestSocketToken
+
+  #client
+
   constructor (uppy, opts) {
   constructor (uppy, opts) {
     super(uppy, opts)
     super(uppy, opts)
     this.type = 'uploader'
     this.type = 'uploader'
     this.id = this.opts.id || 'AwsS3Multipart'
     this.id = this.opts.id || 'AwsS3Multipart'
     this.title = 'AWS S3 Multipart'
     this.title = 'AWS S3 Multipart'
-    this.client = new RequestClient(uppy, opts)
+    this.#client = new RequestClient(uppy, opts)
 
 
     const defaultOptions = {
     const defaultOptions = {
       timeout: 30 * 1000,
       timeout: 30 * 1000,
@@ -36,6 +40,7 @@ export default class AwsS3Multipart extends BasePlugin {
       prepareUploadParts: this.prepareUploadParts.bind(this),
       prepareUploadParts: this.prepareUploadParts.bind(this),
       abortMultipartUpload: this.abortMultipartUpload.bind(this),
       abortMultipartUpload: this.abortMultipartUpload.bind(this),
       completeMultipartUpload: this.completeMultipartUpload.bind(this),
       completeMultipartUpload: this.completeMultipartUpload.bind(this),
+      companionHeaders: {},
     }
     }
 
 
     this.opts = { ...defaultOptions, ...opts }
     this.opts = { ...defaultOptions, ...opts }
@@ -47,8 +52,17 @@ export default class AwsS3Multipart extends BasePlugin {
     this.uploaders = Object.create(null)
     this.uploaders = Object.create(null)
     this.uploaderEvents = Object.create(null)
     this.uploaderEvents = Object.create(null)
     this.uploaderSockets = Object.create(null)
     this.uploaderSockets = Object.create(null)
+
+    this.#queueRequestSocketToken = this.requests.wrapPromiseFunction(this.#requestSocketToken)
   }
   }
 
 
+  [Symbol.for('uppy test: getClient')] () { return this.#client }
+
+  // TODO: remove getter and setter for #client on the next major release
+  get client () { return this.#client }
+
+  set client (client) { this.#client = client }
+
   /**
   /**
    * Clean up all references for a file's upload: the MultipartUploader instance,
    * Clean up all references for a file's upload: the MultipartUploader instance,
    * any events related to the file, and the Companion WebSocket connection.
    * any events related to the file, and the Companion WebSocket connection.
@@ -88,7 +102,7 @@ export default class AwsS3Multipart extends BasePlugin {
       }
       }
     })
     })
 
 
-    return this.client.post('s3/multipart', {
+    return this.#client.post('s3/multipart', {
       filename: file.name,
       filename: file.name,
       type: file.type,
       type: file.type,
       metadata,
       metadata,
@@ -99,7 +113,7 @@ export default class AwsS3Multipart extends BasePlugin {
     this.assertHost('listParts')
     this.assertHost('listParts')
 
 
     const filename = encodeURIComponent(key)
     const filename = encodeURIComponent(key)
-    return this.client.get(`s3/multipart/${uploadId}?key=${filename}`)
+    return this.#client.get(`s3/multipart/${uploadId}?key=${filename}`)
       .then(assertServerError)
       .then(assertServerError)
   }
   }
 
 
@@ -107,7 +121,7 @@ export default class AwsS3Multipart extends BasePlugin {
     this.assertHost('prepareUploadParts')
     this.assertHost('prepareUploadParts')
 
 
     const filename = encodeURIComponent(key)
     const filename = encodeURIComponent(key)
-    return this.client.get(`s3/multipart/${uploadId}/batch?key=${filename}&partNumbers=${partNumbers.join(',')}`)
+    return this.#client.get(`s3/multipart/${uploadId}/batch?key=${filename}&partNumbers=${partNumbers.join(',')}`)
       .then(assertServerError)
       .then(assertServerError)
   }
   }
 
 
@@ -116,7 +130,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
 
     const filename = encodeURIComponent(key)
     const filename = encodeURIComponent(key)
     const uploadIdEnc = encodeURIComponent(uploadId)
     const uploadIdEnc = encodeURIComponent(uploadId)
-    return this.client.post(`s3/multipart/${uploadIdEnc}/complete?key=${filename}`, { parts })
+    return this.#client.post(`s3/multipart/${uploadIdEnc}/complete?key=${filename}`, { parts })
       .then(assertServerError)
       .then(assertServerError)
   }
   }
 
 
@@ -125,7 +139,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
 
     const filename = encodeURIComponent(key)
     const filename = encodeURIComponent(key)
     const uploadIdEnc = encodeURIComponent(uploadId)
     const uploadIdEnc = encodeURIComponent(uploadId)
-    return this.client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`)
+    return this.#client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`)
       .then(assertServerError)
       .then(assertServerError)
   }
   }
 
 
@@ -279,7 +293,26 @@ export default class AwsS3Multipart extends BasePlugin {
     })
     })
   }
   }
 
 
-  uploadRemote (file) {
+  #requestSocketToken = async (file) => {
+    const Client = file.remote.providerOptions.provider ? Provider : RequestClient
+    const client = new Client(this.uppy, file.remote.providerOptions)
+    const opts = { ...this.opts }
+
+    if (file.tus) {
+      // Install file-specific upload overrides.
+      Object.assign(opts, file.tus)
+    }
+
+    const res = await client.post(file.remote.url, {
+      ...file.remote.body,
+      protocol: 's3-multipart',
+      size: file.data.size,
+      metadata: file.meta,
+    })
+    return res.token
+  }
+
+  async uploadRemote (file) {
     this.resetUploaderReferences(file.id)
     this.resetUploaderReferences(file.id)
 
 
     // Don't double-emit upload-started for Golden Retriever-restored files that were already started
     // Don't double-emit upload-started for Golden Retriever-restored files that were already started
@@ -287,33 +320,18 @@ export default class AwsS3Multipart extends BasePlugin {
       this.uppy.emit('upload-started', file)
       this.uppy.emit('upload-started', file)
     }
     }
 
 
-    if (file.serverToken) {
-      return this.connectToServerSocket(file)
-    }
-
-    return new Promise((resolve, reject) => {
-      const Client = file.remote.providerOptions.provider ? Provider : RequestClient
-      const client = new Client(this.uppy, file.remote.providerOptions)
-      client.post(
-        file.remote.url,
-        {
-          ...file.remote.body,
-          protocol: 's3-multipart',
-          size: file.data.size,
-          metadata: file.meta,
-        },
-      ).then((res) => {
-        this.uppy.setFileState(file.id, { serverToken: res.token })
-        // eslint-disable-next-line no-param-reassign
-        file = this.uppy.getFile(file.id)
+    try {
+      if (file.serverToken) {
         return this.connectToServerSocket(file)
         return this.connectToServerSocket(file)
-      }).then(() => {
-        resolve()
-      }).catch((err) => {
-        this.uppy.emit('upload-error', file, err)
-        reject(err)
-      })
-    })
+      }
+      const serverToken = await this.#queueRequestSocketToken(file)
+
+      this.uppy.setFileState(file.id, { serverToken })
+      return this.connectToServerSocket(this.uppy.getFile(file.id))
+    } catch (err) {
+      this.uppy.emit('upload-error', file, err)
+      throw err
+    }
   }
   }
 
 
   connectToServerSocket (file) {
   connectToServerSocket (file) {
@@ -322,7 +340,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
 
       const token = file.serverToken
       const token = file.serverToken
       const host = getSocketHost(file.remote.companionUrl)
       const host = getSocketHost(file.remote.companionUrl)
-      const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false })
+      const socket = new Socket({ target: `${host}/api/${token}` })
       this.uploaderSockets[file.id] = socket
       this.uploaderSockets[file.id] = socket
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
 
 
@@ -412,7 +430,6 @@ export default class AwsS3Multipart extends BasePlugin {
       })
       })
 
 
       queuedRequest = this.requests.run(() => {
       queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
         if (file.isPaused) {
           socket.send('pause', {})
           socket.send('pause', {})
         }
         }
@@ -436,6 +453,11 @@ export default class AwsS3Multipart extends BasePlugin {
     return Promise.all(promises)
     return Promise.all(promises)
   }
   }
 
 
+  #setCompanionHeaders = () => {
+    this.#client.setCompanionHeaders(this.opts.companionHeaders)
+    return Promise.resolve()
+  }
+
   onFileRemove (fileID, cb) {
   onFileRemove (fileID, cb) {
     this.uploaderEvents[fileID].on('file-removed', (file) => {
     this.uploaderEvents[fileID].on('file-removed', (file) => {
       if (fileID === file.id) cb(file.id)
       if (fileID === file.id) cb(file.id)
@@ -495,6 +517,7 @@ export default class AwsS3Multipart extends BasePlugin {
         resumableUploads: true,
         resumableUploads: true,
       },
       },
     })
     })
+    this.uppy.addPreProcessor(this.#setCompanionHeaders)
     this.uppy.addUploader(this.upload)
     this.uppy.addUploader(this.upload)
   }
   }
 
 
@@ -506,6 +529,7 @@ export default class AwsS3Multipart extends BasePlugin {
         resumableUploads: false,
         resumableUploads: false,
       },
       },
     })
     })
+    this.uppy.removePreProcessor(this.#setCompanionHeaders)
     this.uppy.removeUploader(this.upload)
     this.uppy.removeUploader(this.upload)
   }
   }
 }
 }

+ 31 - 0
packages/@uppy/aws-s3-multipart/src/index.test.js

@@ -227,4 +227,35 @@ describe('AwsS3Multipart', () => {
       expect(awsS3Multipart.opts.prepareUploadParts.mock.calls.length).toEqual(2)
       expect(awsS3Multipart.opts.prepareUploadParts.mock.calls.length).toEqual(2)
     })
     })
   })
   })
+
+  describe('dynamic companionHeader', () => {
+    let core
+    let awsS3Multipart
+    const oldToken = 'old token'
+    const newToken = 'new token'
+
+    beforeEach(() => {
+      core = new Core()
+      core.use(AwsS3Multipart, {
+        companionHeaders: {
+          authorization: oldToken,
+        },
+      })
+      awsS3Multipart = core.getPlugin('AwsS3Multipart')
+    })
+
+    it('companionHeader is updated before uploading file', async () => {
+      awsS3Multipart.setOptions({
+        companionHeaders: {
+          authorization: newToken,
+        },
+      })
+
+      await core.upload()
+
+      const client = awsS3Multipart[Symbol.for('uppy test: getClient')]()
+
+      expect(client[Symbol.for('uppy test: getCompanionHeaders')]().authorization).toEqual(newToken)
+    })
+  })
 })
 })

+ 7 - 0
packages/@uppy/aws-s3/CHANGELOG.md

@@ -1,5 +1,12 @@
 # @uppy/aws-s3
 # @uppy/aws-s3
 
 
+## 2.2.1
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
+
 ## 2.2.0
 ## 2.2.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 39 - 13
packages/@uppy/aws-s3/src/MiniXHRUpload.js

@@ -53,6 +53,8 @@ function createFormDataUpload (file, opts) {
 const createBareUpload = file => file.data
 const createBareUpload = file => file.data
 
 
 export default class MiniXHRUpload {
 export default class MiniXHRUpload {
+  #queueRequestSocketToken
+
   constructor (uppy, opts) {
   constructor (uppy, opts) {
     this.uppy = uppy
     this.uppy = uppy
     this.opts = {
     this.opts = {
@@ -65,6 +67,8 @@ export default class MiniXHRUpload {
     this.requests = opts[internalRateLimitedQueue]
     this.requests = opts[internalRateLimitedQueue]
     this.uploaderEvents = Object.create(null)
     this.uploaderEvents = Object.create(null)
     this.i18n = opts.i18n
     this.i18n = opts.i18n
+
+    this.#queueRequestSocketToken = this.requests.wrapPromiseFunction(this.#requestSocketToken)
   }
   }
 
 
   #getOptions (file) {
   #getOptions (file) {
@@ -243,19 +247,21 @@ export default class MiniXHRUpload {
     })
     })
   }
   }
 
 
-  #uploadRemoteFile (file) {
+  #requestSocketToken = async (file) => {
     const opts = this.#getOptions(file)
     const opts = this.#getOptions(file)
-    // This is done in index.js in the S3 plugin.
-    // this.uppy.emit('upload-started', file)
-
+    const Client = file.remote.providerOptions.provider ? Provider : RequestClient
+    const client = new Client(this.uppy, file.remote.providerOptions)
     const metaFields = Array.isArray(opts.metaFields)
     const metaFields = Array.isArray(opts.metaFields)
       ? opts.metaFields
       ? opts.metaFields
-    // Send along all fields by default.
+      // Send along all fields by default.
       : Object.keys(file.meta)
       : Object.keys(file.meta)
 
 
-    const Client = file.remote.providerOptions.provider ? Provider : RequestClient
-    const client = new Client(this.uppy, file.remote.providerOptions)
-    return client.post(file.remote.url, {
+    if (file.tus) {
+      // Install file-specific upload overrides.
+      Object.assign(opts, file.tus)
+    }
+
+    const res = await client.post(file.remote.url, {
       ...file.remote.body,
       ...file.remote.body,
       endpoint: opts.endpoint,
       endpoint: opts.endpoint,
       size: file.data.size,
       size: file.data.size,
@@ -264,14 +270,34 @@ export default class MiniXHRUpload {
       httpMethod: opts.method,
       httpMethod: opts.method,
       useFormData: opts.formData,
       useFormData: opts.formData,
       headers: opts.headers,
       headers: opts.headers,
-    }).then(res => new Promise((resolve, reject) => {
-      const { token } = res
+    })
+    return res.token
+  }
+
+  async #uploadRemoteFile (file) {
+    try {
+      if (file.serverToken) {
+        return this.connectToServerSocket(file)
+      }
+      const serverToken = await this.#queueRequestSocketToken(file)
+
+      this.uppy.setFileState(file.id, { serverToken })
+      return this.connectToServerSocket(this.uppy.getFile(file.id))
+    } catch (err) {
+      this.uppy.emit('upload-error', file, err)
+      throw err
+    }
+  }
+
+  connectToServerSocket (file) {
+    return new Promise((resolve, reject) => {
+      const opts = this.#getOptions(file)
+      const token = file.serverToken
       const host = getSocketHost(file.remote.companionUrl)
       const host = getSocketHost(file.remote.companionUrl)
-      const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false })
+      const socket = new Socket({ target: `${host}/api/${token}` })
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
 
 
       const queuedRequest = this.requests.run(() => {
       const queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
         if (file.isPaused) {
           socket.send('pause', {})
           socket.send('pause', {})
         }
         }
@@ -341,6 +367,6 @@ export default class MiniXHRUpload {
     }).catch((err) => {
     }).catch((err) => {
       this.uppy.emit('upload-error', file, err)
       this.uppy.emit('upload-error', file, err)
       return Promise.reject(err)
       return Promise.reject(err)
-    }))
+    })
   }
   }
 }
 }

+ 15 - 0
packages/@uppy/aws-s3/src/index.js

@@ -117,6 +117,7 @@ export default class AwsS3 extends BasePlugin {
       limit: 0,
       limit: 0,
       metaFields: [], // have to opt in
       metaFields: [], // have to opt in
       getUploadParameters: this.getUploadParameters.bind(this),
       getUploadParameters: this.getUploadParameters.bind(this),
+      companionHeaders: {},
     }
     }
 
 
     this.opts = { ...defaultOptions, ...opts }
     this.opts = { ...defaultOptions, ...opts }
@@ -128,6 +129,13 @@ export default class AwsS3 extends BasePlugin {
     this.#requests = new RateLimitedQueue(this.opts.limit)
     this.#requests = new RateLimitedQueue(this.opts.limit)
   }
   }
 
 
+  [Symbol.for('uppy test: getClient')] () { return this.#client }
+
+  // TODO: remove getter and setter for #client on the next major release
+  get client () { return this.#client }
+
+  set client (client) { this.#client = client }
+
   getUploadParameters (file) {
   getUploadParameters (file) {
     if (!this.opts.companionUrl) {
     if (!this.opts.companionUrl) {
       throw new Error('Expected a `companionUrl` option containing a Companion address.')
       throw new Error('Expected a `companionUrl` option containing a Companion address.')
@@ -216,8 +224,14 @@ export default class AwsS3 extends BasePlugin {
     })
     })
   }
   }
 
 
+  #setCompanionHeaders = () => {
+    this.#client.setCompanionHeaders(this.opts.companionHeaders)
+    return Promise.resolve()
+  }
+
   install () {
   install () {
     const { uppy } = this
     const { uppy } = this
+    uppy.addPreProcessor(this.#setCompanionHeaders)
     uppy.addUploader(this.#handleUpload)
     uppy.addUploader(this.#handleUpload)
 
 
     // Get the response data from a successful XMLHttpRequest instance.
     // Get the response data from a successful XMLHttpRequest instance.
@@ -279,6 +293,7 @@ export default class AwsS3 extends BasePlugin {
   }
   }
 
 
   uninstall () {
   uninstall () {
+    this.uppy.removePreProcessor(this.#setCompanionHeaders)
     this.uppy.removeUploader(this.#handleUpload)
     this.uppy.removeUploader(this.#handleUpload)
   }
   }
 }
 }

+ 31 - 0
packages/@uppy/aws-s3/src/index.test.js

@@ -35,4 +35,35 @@ describe('AwsS3', () => {
       expect(() => awsS3.opts.getUploadParameters(file)).not.toThrow()
       expect(() => awsS3.opts.getUploadParameters(file)).not.toThrow()
     })
     })
   })
   })
+
+  describe('dynamic companionHeader', () => {
+    let core
+    let awsS3
+    const oldToken = 'old token'
+    const newToken = 'new token'
+
+    beforeEach(() => {
+      core = new Core()
+      core.use(AwsS3, {
+        companionHeaders: {
+          authorization: oldToken,
+        },
+      })
+      awsS3 = core.getPlugin('AwsS3')
+    })
+
+    it('companionHeader is updated before uploading file', async () => {
+      awsS3.setOptions({
+        companionHeaders: {
+          authorization: newToken,
+        },
+      })
+
+      await core.upload()
+
+      const client = awsS3[Symbol.for('uppy test: getClient')]()
+
+      expect(client[Symbol.for('uppy test: getCompanionHeaders')]().authorization).toEqual(newToken)
+    })
+  })
 })
 })

+ 10 - 2
packages/@uppy/companion-client/src/RequestClient.js

@@ -35,6 +35,8 @@ async function handleJSONResponse (res) {
 export default class RequestClient {
 export default class RequestClient {
   static VERSION = packageJson.version
   static VERSION = packageJson.version
 
 
+  #companionHeaders
+
   #getPostResponseFunc = skip => response => (skip ? response : this.onReceiveResponse(response))
   #getPostResponseFunc = skip => response => (skip ? response : this.onReceiveResponse(response))
 
 
   constructor (uppy, opts) {
   constructor (uppy, opts) {
@@ -43,8 +45,15 @@ export default class RequestClient {
     this.onReceiveResponse = this.onReceiveResponse.bind(this)
     this.onReceiveResponse = this.onReceiveResponse.bind(this)
     this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token']
     this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token']
     this.preflightDone = false
     this.preflightDone = false
+    this.#companionHeaders = opts?.companionHeaders
+  }
+
+  setCompanionHeaders (headers) {
+    this.#companionHeaders = headers
   }
   }
 
 
+  [Symbol.for('uppy test: getCompanionHeaders')] () { return this.#companionHeaders }
+
   get hostname () {
   get hostname () {
     const { companion } = this.uppy.getState()
     const { companion } = this.uppy.getState()
     const host = this.opts.companionUrl
     const host = this.opts.companionUrl
@@ -58,10 +67,9 @@ export default class RequestClient {
   }
   }
 
 
   headers () {
   headers () {
-    const userHeaders = this.opts.companionHeaders || {}
     return Promise.resolve({
     return Promise.resolve({
       ...RequestClient.defaultHeaders,
       ...RequestClient.defaultHeaders,
-      ...userHeaders,
+      ...this.#companionHeaders,
     })
     })
   }
   }
 
 

+ 7 - 0
packages/@uppy/core/CHANGELOG.md

@@ -1,5 +1,12 @@
 # @uppy/core
 # @uppy/core
 
 
+## 2.3.1
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/core: fix `TypeError` when file was deleted (Antoine du Hamel / #3811)
+
 ## 2.3.0
 ## 2.3.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 1 - 1
packages/@uppy/core/src/Uppy.js

@@ -967,7 +967,7 @@ class Uppy {
         if (error.details) {
         if (error.details) {
           newError.details += ` ${error.details}`
           newError.details += ` ${error.details}`
         }
         }
-        newError.message = this.i18n('failedToUpload', { file: file.name })
+        newError.message = this.i18n('failedToUpload', { file: file?.name })
         this.#informAndEmit(newError)
         this.#informAndEmit(newError)
       } else {
       } else {
         this.#informAndEmit(error)
         this.#informAndEmit(error)

+ 7 - 0
packages/@uppy/react/CHANGELOG.md

@@ -7,6 +7,13 @@ Included in: Uppy v3.0.0-beta
 
 
 - @uppy/react: refactor to ESM (Antoine du Hamel / #3780)
 - @uppy/react: refactor to ESM (Antoine du Hamel / #3780)
 
 
+## 2.2.2
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/react: Reset uppy instance when React component is unmounted (Tomasz Pęksa / #3814)
+
 ## 2.2.1
 ## 2.2.1
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 2 - 1
packages/@uppy/react/src/useUppy.js

@@ -18,8 +18,9 @@ export default function useUppy (factory) {
   useEffect(() => {
   useEffect(() => {
     return () => {
     return () => {
       uppy.current.close({ reason: 'unmount' })
       uppy.current.close({ reason: 'unmount' })
+      uppy.current = undefined
     }
     }
-  }, [])
+  }, [uppy])
 
 
   return uppy.current
   return uppy.current
 }
 }

+ 8 - 0
packages/@uppy/remote-sources/CHANGELOG.md

@@ -0,0 +1,8 @@
+# @uppy/remote-sources
+
+## 0.1.0
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/remote-sources: Add @uppy/remote-sources preset/plugin (Artur Paikin / #3676)

+ 21 - 0
packages/@uppy/remote-sources/LICENSE

@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2022 Transloadit
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 35 - 0
packages/@uppy/remote-sources/README.md

@@ -0,0 +1,35 @@
+# @uppy/remote-sources
+
+<img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="120" alt="Uppy logo: a superman puppy in a pink suit" align="right">
+
+<a href="https://www.npmjs.com/package/@uppy/remote-sources"><img src="https://img.shields.io/npm/v/@uppy/compressor.svg?style=flat-square"></a> <img src="https://github.com/transloadit/uppy/workflows/Tests/badge.svg" alt="CI status for Uppy tests"> <img src="https://github.com/transloadit/uppy/workflows/Companion/badge.svg" alt="CI status for Companion tests"> <img src="https://github.com/transloadit/uppy/workflows/End-to-end%20tests/badge.svg" alt="CI status for browser tests">
+
+## Example
+
+```js
+import Uppy from '@uppy/core'
+import RemoteSources from '@uppy/remote-sources'
+
+const uppy = new Uppy()
+uppy.use(RemoteSources, {
+  companionUrl: 'https://your-companion-url',
+})
+```
+
+## Installation
+
+```bash
+npm install @uppy/remote-sources
+# or
+yarn add @uppy/remote-sources
+```
+
+Alternatively, you can also use this plugin in a pre-built bundle from Transloadit’s CDN: Edgly. In that case `Uppy.RemoteSources` will attach itself to the global `window.Uppy` object. See the [main Uppy documentation](https://uppy.io/docs/#Installation) for instructions.
+
+## Documentation
+
+Documentation for this plugin can be found on the [Uppy website](https://uppy.io/docs/remote-sources).
+
+## License
+
+[The MIT License](./LICENSE).

+ 51 - 0
packages/@uppy/remote-sources/package.json

@@ -0,0 +1,51 @@
+{
+  "name": "@uppy/remote-sources",
+  "description": "Uppy plugin that includes all remote sources that Uppy+Companion offer, like Instagram, Google Drive, Dropox, Box, Unsplash, Url etc",
+  "version": "0.1.0",
+  "license": "MIT",
+  "main": "lib/index.js",
+  "types": "types/index.d.ts",
+  "type": "module",
+  "keywords": [
+    "file uploader",
+    "instagram",
+    "google-drive",
+    "facebook",
+    "dropbox",
+    "onedrive",
+    "zoom",
+    "unsplash",
+    "box",
+    "url"
+  ],
+  "homepage": "https://uppy.io",
+  "bugs": {
+    "url": "https://github.com/transloadit/uppy/issues"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/transloadit/uppy.git"
+  },
+  "dependencies": {
+    "@uppy/box": "workspace:^",
+    "@uppy/dashboard": "workspace:^",
+    "@uppy/dropbox": "workspace:^",
+    "@uppy/facebook": "workspace:^",
+    "@uppy/google-drive": "workspace:^",
+    "@uppy/instagram": "workspace:^",
+    "@uppy/onedrive": "workspace:^",
+    "@uppy/unsplash": "workspace:^",
+    "@uppy/url": "workspace:^",
+    "@uppy/zoom": "workspace:^"
+  },
+  "peerDependencies": {
+    "@uppy/core": "workspace:^"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "devDependencies": {
+    "@jest/globals": "^28.1.0",
+    "resize-observer-polyfill": "^1.5.1"
+  }
+}

+ 83 - 0
packages/@uppy/remote-sources/src/index.js

@@ -0,0 +1,83 @@
+import { BasePlugin } from '@uppy/core'
+import Dashboard from '@uppy/dashboard'
+import Dropbox from '@uppy/dropbox'
+import GoogleDrive from '@uppy/google-drive'
+import Instagram from '@uppy/instagram'
+import Facebook from '@uppy/facebook'
+import OneDrive from '@uppy/onedrive'
+import Box from '@uppy/box'
+import Unsplash from '@uppy/unsplash'
+import Url from '@uppy/url'
+import Zoom from '@uppy/zoom'
+
+import packageJson from '../package.json'
+
+const availablePlugins = [
+  Box,
+  Dropbox,
+  Facebook,
+  GoogleDrive,
+  Instagram,
+  OneDrive,
+  Unsplash,
+  Url,
+  Zoom,
+]
+
+export default class RemoteSources extends BasePlugin {
+  static VERSION = packageJson.version
+
+  #installedPlugins = new Set()
+
+  constructor (uppy, opts) {
+    super(uppy, opts)
+    this.id = this.opts.id || 'RemoteSources'
+    this.type = 'acquirer'
+
+    const defaultOptions = {
+      sources: [
+        'Box',
+        'Dropbox',
+        'Facebook',
+        'GoogleDrive',
+        'Instagram',
+        'OneDrive',
+        'Unsplash',
+        'Url',
+      ],
+      target: Dashboard,
+    }
+    this.opts = { ...defaultOptions, ...opts }
+
+    if (this.opts.companionUrl == null) {
+      throw new Error('Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl')
+    }
+  }
+
+  setOptions (newOpts) {
+    this.uninstall()
+    super.setOptions(newOpts)
+    this.install()
+  }
+
+  install () {
+    this.opts.sources.forEach((pluginId) => {
+      const optsForRemoteSourcePlugin = { ...this.opts, sources: undefined }
+      const plugin = availablePlugins.find(p => p.name === pluginId)
+      if (plugin == null) {
+        const pluginNames = availablePlugins.map(p => p.name)
+        const formatter = new Intl.ListFormat('en', { style: 'long', type: 'disjunction' })
+        throw new Error(`Invalid plugin: "${pluginId}" is not one of: ${formatter.format(pluginNames)}.`)
+      }
+      this.uppy.use(plugin, optsForRemoteSourcePlugin)
+      this.#installedPlugins.add(plugin)
+    })
+  }
+
+  uninstall () {
+    for (const plugin of this.#installedPlugins) {
+      this.uppy.removePlugin(plugin)
+    }
+    this.#installedPlugins.clear()
+  }
+}

+ 42 - 0
packages/@uppy/remote-sources/src/index.test.js

@@ -0,0 +1,42 @@
+import { describe, expect, it } from '@jest/globals'
+import resizeObserverPolyfill from 'resize-observer-polyfill'
+import Core from '@uppy/core'
+import Dashboard from '@uppy/dashboard'
+import RemoteSources from './index.js'
+
+describe('RemoteSources', () => {
+  beforeAll(() => {
+    globalThis.ResizeObserver = resizeObserverPolyfill.default || resizeObserverPolyfill
+  })
+
+  afterAll(() => {
+    delete globalThis.ResizeObserver
+  })
+
+  it('should install RemoteSources with default options', () => {
+    expect(() => {
+      const core = new Core()
+      core.use(Dashboard)
+      core.use(RemoteSources, { companionUrl: 'https://example.com' })
+    }).not.toThrow()
+  })
+
+  it('should throw when a companionUrl is not specified', () => {
+    expect(() => {
+      const core = new Core()
+      core.use(Dashboard)
+      core.use(RemoteSources, { sources: ['Webcam'] })
+    }).toThrow(new Error('Please specify companionUrl for RemoteSources to work, see https://uppy.io/docs/remote-sources#companionUrl'))
+  })
+
+  it('should throw when trying to use a plugin which is not included in RemoteSources', () => {
+    expect(() => {
+      const core = new Core()
+      core.use(Dashboard)
+      core.use(RemoteSources, {
+        companionUrl: 'https://example.com',
+        sources: ['Webcam'],
+      })
+    }).toThrow('Invalid plugin: "Webcam" is not one of: Box, Dropbox, Facebook, GoogleDrive, Instagram, OneDrive, Unsplash, Url, or Zoom.')
+  })
+})

+ 13 - 0
packages/@uppy/remote-sources/types/index.d.ts

@@ -0,0 +1,13 @@
+import type { PluginOptions, BasePlugin, PluginTarget } from '@uppy/core'
+import type { RequestClientOptions } from '@uppy/companion-client'
+
+interface RemoteTargetOptions extends PluginOptions, RequestClientOptions {
+  target?: PluginTarget
+  sources?: Array<string>
+  title?: string
+  companionUrl: string
+}
+
+declare class RemoteTarget extends BasePlugin<RemoteTargetOptions> {}
+
+export default RemoteTarget

+ 11 - 0
packages/@uppy/remote-sources/types/index.test-d.ts

@@ -0,0 +1,11 @@
+import Uppy from '@uppy/core'
+import RemoteSources from '..'
+
+{
+  const uppy = new Uppy()
+  uppy.use(RemoteSources, {
+    sources: ['Instagram', 'Url'],
+    companionUrl: '',
+    companionCookiesRule: 'same-origin',
+  })
+}

+ 7 - 0
packages/@uppy/robodog/CHANGELOG.md

@@ -1,5 +1,12 @@
 # @uppy/robodog
 # @uppy/robodog
 
 
+## 2.8.0
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/robodog: fix linter warnings (Antoine du Hamel / #3808)
+
 ## 2.7.0
 ## 2.7.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 2 - 2
packages/@uppy/robodog/src/TransloaditResultsPlugin.js

@@ -26,9 +26,9 @@ class TransloaditResultsPlugin extends BasePlugin {
     assemblies.forEach((assembly) => {
     assemblies.forEach((assembly) => {
       Object.keys(assembly.results).forEach((stepName) => {
       Object.keys(assembly.results).forEach((stepName) => {
         const results = assembly.results[stepName]
         const results = assembly.results[stepName]
-        results.forEach((result) => {
+        results.forEach((resultObject) => {
           assemblyResults.push({
           assemblyResults.push({
-            ...result,
+            ...resultObject,
             assemblyId: assembly.assembly_id,
             assemblyId: assembly.assembly_id,
             stepName,
             stepName,
           })
           })

+ 3 - 0
packages/@uppy/robodog/src/addProviders.js

@@ -1,3 +1,4 @@
+/* eslint-disable global-require */
 const Transloadit = require('@uppy/transloadit')
 const Transloadit = require('@uppy/transloadit')
 const has = require('@uppy/utils/lib/hasProperty')
 const has = require('@uppy/utils/lib/hasProperty')
 
 
@@ -39,6 +40,7 @@ function addRemoteProvider (uppy, name, opts) {
     companionAllowedHosts: Transloadit.COMPANION_PATTERN,
     companionAllowedHosts: Transloadit.COMPANION_PATTERN,
   }
   }
 
 
+  // eslint-disable-next-line no-shadow
   remoteProviderOptionNames.forEach((name) => {
   remoteProviderOptionNames.forEach((name) => {
     if (has(opts, name)) providerOptions[name] = opts[name]
     if (has(opts, name)) providerOptions[name] = opts[name]
   })
   })
@@ -68,6 +70,7 @@ function addLocalProvider (uppy, name, opts) {
   const Provider = localProviders[name]
   const Provider = localProviders[name]
   const providerOptions = {}
   const providerOptions = {}
 
 
+  // eslint-disable-next-line no-shadow
   localProviderOptionNames.forEach((name) => {
   localProviderOptionNames.forEach((name) => {
     if (has(opts, name)) providerOptions[name] = opts[name]
     if (has(opts, name)) providerOptions[name] = opts[name]
   })
   })

+ 1 - 0
packages/@uppy/robodog/src/form.js

@@ -24,6 +24,7 @@ function mergeDefaultLocale (defaults, userProvided = {}) {
 function form (target, opts) {
 function form (target, opts) {
   if (!opts) throw new TypeError('robodog.form: must provide an options object')
   if (!opts) throw new TypeError('robodog.form: must provide an options object')
 
 
+  // eslint-disable-next-line no-param-reassign
   opts = {
   opts = {
     ...opts,
     ...opts,
     locale: mergeDefaultLocale(defaultLocaleStrings, opts.locale),
     locale: mergeDefaultLocale(defaultLocaleStrings, opts.locale),

+ 2 - 0
packages/@uppy/robodog/src/index.js

@@ -8,5 +8,7 @@ module.exports = {
   form,
   form,
   pick,
   pick,
   upload,
   upload,
+  // We need to keep the require here because we're using `babel-plugin-inline-package-json`.
+  // eslint-disable-next-line global-require
   VERSION: require('../package.json').version,
   VERSION: require('../package.json').version,
 }
 }

+ 7 - 0
packages/@uppy/transloadit/CHANGELOG.md

@@ -7,6 +7,13 @@ Included in: Uppy v3.0.0-beta
 
 
 - @uppy/transloadit: remove IE 10 hack (Antoine du Hamel / #3777)
 - @uppy/transloadit: remove IE 10 hack (Antoine du Hamel / #3777)
 
 
+## 2.3.1
+
+Released: 2022-06-09
+Included in: Uppy v2.12.1
+
+- @uppy/transloadit: fix `COMPANION_PATTERN` export (Antoine du Hamel / #3820)
+
 ## 2.3.0
 ## 2.3.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 5 - 3
packages/@uppy/transloadit/src/index.js

@@ -835,12 +835,14 @@ export default class Transloadit extends BasePlugin {
 }
 }
 
 
 export {
 export {
-  COMPANION,
   ALLOWED_COMPANION_PATTERN,
   ALLOWED_COMPANION_PATTERN,
+  COMPANION,
+  ALLOWED_COMPANION_PATTERN as COMPANION_PATTERN,
 }
 }
 
 
-// Backward compatibility: we want `COMPANION` and `ALLOWED_COMPANION_PATTERN`
+// Backward compatibility: we want `COMPANION` and `COMPANION_PATTERN`
 // to keep being accessible as static properties of `Transloadit` to avoid a
 // to keep being accessible as static properties of `Transloadit` to avoid a
 // breaking change.
 // breaking change.
-Transloadit.COMPANION = COMPANION // TODO: remove this line on the next major
 Transloadit.ALLOWED_COMPANION_PATTERN = ALLOWED_COMPANION_PATTERN // TODO: remove this line on the next major
 Transloadit.ALLOWED_COMPANION_PATTERN = ALLOWED_COMPANION_PATTERN // TODO: remove this line on the next major
+Transloadit.COMPANION = COMPANION // TODO: remove this line on the next major
+Transloadit.COMPANION_PATTERN = ALLOWED_COMPANION_PATTERN // TODO: remove this line on the next major

+ 8 - 0
packages/@uppy/tus/CHANGELOG.md

@@ -1,5 +1,13 @@
 # @uppy/tus
 # @uppy/tus
 
 
+## 2.4.1
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/tus: queue socket token requests for remote files (Merlijn Vos / #3797)
+- @uppy/tus: make onShouldRetry type optional (Merlijn Vos / #3800)
+
 ## 2.4.0
 ## 2.4.0
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 36 - 29
packages/@uppy/tus/src/index.js

@@ -57,6 +57,8 @@ export default class Tus extends BasePlugin {
 
 
   #retryDelayIterator
   #retryDelayIterator
 
 
+  #queueRequestSocketToken
+
   /**
   /**
    * @param {Uppy} uppy
    * @param {Uppy} uppy
    * @param {TusOptions} opts
    * @param {TusOptions} opts
@@ -97,6 +99,7 @@ export default class Tus extends BasePlugin {
 
 
     this.handleResetProgress = this.handleResetProgress.bind(this)
     this.handleResetProgress = this.handleResetProgress.bind(this)
     this.handleUpload = this.handleUpload.bind(this)
     this.handleUpload = this.handleUpload.bind(this)
+    this.#queueRequestSocketToken = this.requests.wrapPromiseFunction(this.#requestSocketToken)
   }
   }
 
 
   handleResetProgress () {
   handleResetProgress () {
@@ -427,43 +430,48 @@ export default class Tus extends BasePlugin {
     })
     })
   }
   }
 
 
-  /**
-   * @param {UppyFile} file for use with upload
-   * @returns {Promise<void>}
-   */
-  async uploadRemote (file) {
-    this.resetUploaderReferences(file.id)
-
+  #requestSocketToken = async (file) => {
+    const Client = file.remote.providerOptions.provider ? Provider : RequestClient
+    const client = new Client(this.uppy, file.remote.providerOptions)
     const opts = { ...this.opts }
     const opts = { ...this.opts }
+
     if (file.tus) {
     if (file.tus) {
       // Install file-specific upload overrides.
       // Install file-specific upload overrides.
       Object.assign(opts, file.tus)
       Object.assign(opts, file.tus)
     }
     }
 
 
-    this.uppy.emit('upload-started', file)
-    this.uppy.log(file.remote.url)
+    const res = await client.post(file.remote.url, {
+      ...file.remote.body,
+      endpoint: opts.endpoint,
+      uploadUrl: opts.uploadUrl,
+      protocol: 'tus',
+      size: file.data.size,
+      headers: opts.headers,
+      metadata: file.meta,
+    })
+    return res.token
+  }
 
 
-    if (file.serverToken) {
-      await this.connectToServerSocket(file)
-      return
-    }
+  /**
+   * @param {UppyFile} file for use with upload
+   * @returns {Promise<void>}
+   */
+  async uploadRemote (file) {
+    this.resetUploaderReferences(file.id)
 
 
-    const Client = file.remote.providerOptions.provider ? Provider : RequestClient
-    const client = new Client(this.uppy, file.remote.providerOptions)
+    // Don't double-emit upload-started for Golden Retriever-restored files that were already started
+    if (!file.progress.uploadStarted || !file.isRestored) {
+      this.uppy.emit('upload-started', file)
+    }
 
 
     try {
     try {
-      // !! cancellation is NOT supported at this stage yet
-      const res = await client.post(file.remote.url, {
-        ...file.remote.body,
-        endpoint: opts.endpoint,
-        uploadUrl: opts.uploadUrl,
-        protocol: 'tus',
-        size: file.data.size,
-        headers: opts.headers,
-        metadata: file.meta,
-      })
-      this.uppy.setFileState(file.id, { serverToken: res.token })
-      await this.connectToServerSocket(this.uppy.getFile(file.id))
+      if (file.serverToken) {
+        return this.connectToServerSocket(file)
+      }
+      const serverToken = await this.#queueRequestSocketToken(file)
+
+      this.uppy.setFileState(file.id, { serverToken })
+      return this.connectToServerSocket(this.uppy.getFile(file.id))
     } catch (err) {
     } catch (err) {
       this.uppy.emit('upload-error', file, err)
       this.uppy.emit('upload-error', file, err)
       throw err
       throw err
@@ -482,7 +490,7 @@ export default class Tus extends BasePlugin {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       const token = file.serverToken
       const token = file.serverToken
       const host = getSocketHost(file.remote.companionUrl)
       const host = getSocketHost(file.remote.companionUrl)
-      const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false })
+      const socket = new Socket({ target: `${host}/api/${token}` })
       this.uploaderSockets[file.id] = socket
       this.uploaderSockets[file.id] = socket
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
       this.uploaderEvents[file.id] = new EventTracker(this.uppy)
 
 
@@ -591,7 +599,6 @@ export default class Tus extends BasePlugin {
       })
       })
 
 
       queuedRequest = this.requests.run(() => {
       queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
         if (file.isPaused) {
           socket.send('pause', {})
           socket.send('pause', {})
         }
         }

+ 1 - 1
packages/@uppy/tus/types/index.d.ts

@@ -20,7 +20,7 @@ export interface TusOptions extends PluginOptions, TusUploadOptions {
     limit?: number
     limit?: number
     useFastRemoteRetry?: boolean
     useFastRemoteRetry?: boolean
     withCredentials?: boolean
     withCredentials?: boolean
-    onShouldRetry: (err: Error | undefined, retryAttempt: number, options: TusOptions, next: Next) => boolean
+    onShouldRetry?: (err: Error | undefined, retryAttempt: number, options: TusOptions, next: Next) => boolean
   }
   }
 
 
 declare class Tus extends BasePlugin<TusOptions> {}
 declare class Tus extends BasePlugin<TusOptions> {}

+ 8 - 0
packages/@uppy/url/CHANGELOG.md

@@ -1,5 +1,13 @@
 # @uppy/url
 # @uppy/url
 
 
+## 2.2.0
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/url: enable passing optional meta data to `addFile` (Brad Edelman / #3788)
+- @uppy/url: fix `getFileNameFromUrl` (Brad Edelman / #3804)
+
 ## 2.1.1
 ## 2.1.1
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 4 - 2
packages/@uppy/url/src/Url.jsx

@@ -48,7 +48,8 @@ function checkIfCorrectURL (url) {
 }
 }
 
 
 function getFileNameFromUrl (url) {
 function getFileNameFromUrl (url) {
-  return url.substring(url.lastIndexOf('/') + 1)
+  const { pathname } = new URL(url)
+  return pathname.substring(pathname.lastIndexOf('/') + 1)
 }
 }
 /**
 /**
  * Url
  * Url
@@ -104,7 +105,7 @@ export default class Url extends UIPlugin {
       })
       })
   }
   }
 
 
-  async addFile (protocollessUrl) {
+  async addFile (protocollessUrl, optionalMeta = undefined) {
     const url = this.addProtocolToURL(protocollessUrl)
     const url = this.addProtocolToURL(protocollessUrl)
     if (!this.checkIfCorrectURL(url)) {
     if (!this.checkIfCorrectURL(url)) {
       this.uppy.log(`[URL] Incorrect URL entered: ${url}`)
       this.uppy.log(`[URL] Incorrect URL entered: ${url}`)
@@ -116,6 +117,7 @@ export default class Url extends UIPlugin {
       const meta = await this.getMeta(url)
       const meta = await this.getMeta(url)
 
 
       const tagFile = {
       const tagFile = {
+        meta: optionalMeta,
         source: this.id,
         source: this.id,
         name: this.getFileNameFromUrl(url),
         name: this.getFileNameFromUrl(url),
         type: meta.type,
         type: meta.type,

+ 2 - 2
packages/@uppy/url/types/index.d.ts

@@ -1,4 +1,4 @@
-import type { PluginOptions, UIPlugin, PluginTarget } from '@uppy/core'
+import type { PluginOptions, UIPlugin, PluginTarget, IndexedObject } from '@uppy/core'
 import type { RequestClientOptions } from '@uppy/companion-client'
 import type { RequestClientOptions } from '@uppy/companion-client'
 import UrlLocale from './generatedLocale'
 import UrlLocale from './generatedLocale'
 
 
@@ -9,7 +9,7 @@ export interface UrlOptions extends PluginOptions, RequestClientOptions {
 }
 }
 
 
 declare class Url extends UIPlugin<UrlOptions> {
 declare class Url extends UIPlugin<UrlOptions> {
-  public addFile(url: string): undefined | string | never
+  public addFile(url: string, meta?: IndexedObject<any>): undefined | string | never
 }
 }
 
 
 export default Url
 export default Url

+ 7 - 0
packages/@uppy/xhr-upload/CHANGELOG.md

@@ -1,5 +1,12 @@
 # @uppy/xhr-upload
 # @uppy/xhr-upload
 
 
+## 2.1.2
+
+Released: 2022-06-07
+Included in: Uppy v2.12.0
+
+- @uppy/xhr-upload: replace `ev.target.status` with `xhr.status` (Wes Sankey / #3782)
+
 ## 2.1.1
 ## 2.1.1
 
 
 Released: 2022-05-30
 Released: 2022-05-30

+ 4 - 4
packages/@uppy/xhr-upload/src/index.js

@@ -251,7 +251,7 @@ export default class XHRUpload extends BasePlugin {
         }
         }
       })
       })
 
 
-      xhr.addEventListener('load', (ev) => {
+      xhr.addEventListener('load', () => {
         this.uppy.log(`[XHRUpload] ${id} finished`)
         this.uppy.log(`[XHRUpload] ${id} finished`)
         timer.done()
         timer.done()
         queuedRequest.done()
         queuedRequest.done()
@@ -260,12 +260,12 @@ export default class XHRUpload extends BasePlugin {
           this.uploaderEvents[file.id] = null
           this.uploaderEvents[file.id] = null
         }
         }
 
 
-        if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) {
+        if (opts.validateStatus(xhr.status, xhr.responseText, xhr)) {
           const body = opts.getResponseData(xhr.responseText, xhr)
           const body = opts.getResponseData(xhr.responseText, xhr)
           const uploadURL = body[opts.responseUrlFieldName]
           const uploadURL = body[opts.responseUrlFieldName]
 
 
           const uploadResp = {
           const uploadResp = {
-            status: ev.target.status,
+            status: xhr.status,
             body,
             body,
             uploadURL,
             uploadURL,
           }
           }
@@ -282,7 +282,7 @@ export default class XHRUpload extends BasePlugin {
         const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
         const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
 
 
         const response = {
         const response = {
-          status: ev.target.status,
+          status: xhr.status,
           body,
           body,
         }
         }
 
 

+ 1 - 0
packages/uppy/index.mjs

@@ -29,6 +29,7 @@ export { default as Facebook } from '@uppy/facebook'
 export { default as GoogleDrive } from '@uppy/google-drive'
 export { default as GoogleDrive } from '@uppy/google-drive'
 export { default as Instagram } from '@uppy/instagram'
 export { default as Instagram } from '@uppy/instagram'
 export { default as OneDrive } from '@uppy/onedrive'
 export { default as OneDrive } from '@uppy/onedrive'
+export { default as RemoteSources } from '@uppy/remote-sources'
 export { default as ScreenCapture } from '@uppy/screen-capture'
 export { default as ScreenCapture } from '@uppy/screen-capture'
 export { default as Unsplash } from '@uppy/unsplash'
 export { default as Unsplash } from '@uppy/unsplash'
 export { default as Url } from '@uppy/url'
 export { default as Url } from '@uppy/url'

+ 14 - 18
private/dev/Dashboard.js

@@ -2,15 +2,7 @@
 /* eslint-disable import/no-extraneous-dependencies */
 /* eslint-disable import/no-extraneous-dependencies */
 import Uppy from '@uppy/core'
 import Uppy from '@uppy/core'
 import Dashboard from '@uppy/dashboard'
 import Dashboard from '@uppy/dashboard'
-import Instagram from '@uppy/instagram'
-import Facebook from '@uppy/facebook'
-import OneDrive from '@uppy/onedrive'
-import Dropbox from '@uppy/dropbox'
-import Box from '@uppy/box'
-import GoogleDrive from '@uppy/google-drive'
-import Unsplash from '@uppy/unsplash'
-import Zoom from '@uppy/zoom'
-import Url from '@uppy/url'
+import RemoteSources from '@uppy/remote-sources'
 import Webcam from '@uppy/webcam'
 import Webcam from '@uppy/webcam'
 import ScreenCapture from '@uppy/screen-capture'
 import ScreenCapture from '@uppy/screen-capture'
 import GoldenRetriever from '@uppy/golden-retriever'
 import GoldenRetriever from '@uppy/golden-retriever'
@@ -83,15 +75,19 @@ export default () => {
       proudlyDisplayPoweredByUppy: true,
       proudlyDisplayPoweredByUppy: true,
       note: '2 files, images and video only',
       note: '2 files, images and video only',
     })
     })
-    .use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Instagram, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Dropbox, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Box, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Facebook, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(OneDrive, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Zoom, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Url, { target: Dashboard, companionUrl: COMPANION_URL })
-    .use(Unsplash, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(GoogleDrive, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Instagram, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Dropbox, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Box, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Facebook, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(OneDrive, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Zoom, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Url, { target: Dashboard, companionUrl: COMPANION_URL })
+    // .use(Unsplash, { target: Dashboard, companionUrl: COMPANION_URL })
+    .use(RemoteSources, {
+      companionUrl: COMPANION_URL,
+      sources: ['Box', 'Dropbox', 'Facebook', 'GoogleDrive', 'Instagram', 'OneDrive', 'Unsplash', 'Url'],
+    })
     .use(Webcam, {
     .use(Webcam, {
       target: Dashboard,
       target: Dashboard,
       showVideoSourceDropdown: true,
       showVideoSourceDropdown: true,

+ 3 - 2
private/release/choose-semverness.js

@@ -4,6 +4,7 @@ import { createWriteStream, mkdirSync, readFileSync } from 'node:fs'
 import { spawnSync } from 'node:child_process'
 import { spawnSync } from 'node:child_process'
 
 
 import prompts from 'prompts'
 import prompts from 'prompts'
+import { TARGET_BRANCH } from './config.js'
 
 
 const ROOT = new  URL('../../', import.meta.url)
 const ROOT = new  URL('../../', import.meta.url)
 const PACKAGES_FOLDER = new URL('./packages/', ROOT)
 const PACKAGES_FOLDER = new URL('./packages/', ROOT)
@@ -87,7 +88,7 @@ export default async function pickSemverness (
       `\nHere are the commits that landed on ${name} since previous release:\n${stdout}\n`,
       `\nHere are the commits that landed on ${name} since previous release:\n${stdout}\n`,
     )
     )
     console.log(
     console.log(
-      `Check the web UI at https://github.com/transloadit/uppy/tree/main/${encodeURI(
+      `Check the web UI at https://github.com/transloadit/uppy/tree/${TARGET_BRANCH}/${encodeURI(
         location,
         location,
       )}.`,
       )}.`,
     )
     )
@@ -147,7 +148,7 @@ export default async function pickSemverness (
         `Here are the commits that landed on @uppy/robodog since previous release:\n\n${stdout}\n`,
         `Here are the commits that landed on @uppy/robodog since previous release:\n\n${stdout}\n`,
       )
       )
       console.log(
       console.log(
-        `Check the web UI at https://github.com/transloadit/uppy/tree/main/${encodeURI(
+        `Check the web UI at https://github.com/transloadit/uppy/tree/${TARGET_BRANCH}/${encodeURI(
           location,
           location,
         )}.`,
         )}.`,
       )
       )

+ 21 - 16
website/src/docs/react-dragdrop.md

@@ -44,20 +44,25 @@ The `<DragDrop />` component supports all [DragDrop](/docs/drag-drop/) options a
 import React from 'react'
 import React from 'react'
 import { DragDrop } from '@uppy/react'
 import { DragDrop } from '@uppy/react'
 
 
-  <DragDrop
-    width="100%"
-    height="100%"
-    note="Images up to 200×200px"
-    // assuming `this.uppy` contains an Uppy instance:
-    uppy={this.uppy}
-    locale={{
-      strings: {
-      // Text to show on the droppable area.
-      // `%{browse}` is replaced with a link that opens the system file selection dialog.
-        dropHereOr: 'Drop here or %{browse}',
-        // Used as the label for the link that opens the system file selection dialog.
-        browse: 'browse',
-      },
-    }}
-  />
+export default function MyComponent (props) {
+  const { uppy } = props
+  return  (
+    <DragDrop
+      width="100%"
+      height="100%"
+      note="Images up to 200×200px"
+    // assuming `props.uppy` contains an Uppy instance:
+      uppy={uppy}
+      locale={{
+        strings: {
+          // Text to show on the droppable area.
+          // `%{browse}` is replaced with a link that opens the system file selection dialog.
+          dropHereOr: 'Drop here or %{browse}',
+          // Used as the label for the link that opens the system file selection dialog.
+          browse: 'browse',
+        },
+      }}
+    />
+  )
+}
 ```
 ```

+ 11 - 6
website/src/docs/react-fileinput.md

@@ -44,10 +44,15 @@ The `<FileInput />` component supports all [FileInput](/docs/file-input/) option
 import React from 'react'
 import React from 'react'
 import { FileInput } from '@uppy/react'
 import { FileInput } from '@uppy/react'
 
 
-  <FileInput
-    // assuming `this.uppy` contains an Uppy instance:
-    uppy={this.uppy}
-    pretty
-    inputName="files[]"
-  />
+export default function MyComponent (props) {
+  const { uppy } = props
+  return (
+    <FileInput
+    // assuming `props.uppy` contains an Uppy instance:
+      uppy={uppy}
+      pretty
+      inputName="files[]"
+    />
+  )
+}
 ```
 ```

+ 11 - 5
website/src/docs/react-progressbar.md

@@ -44,11 +44,17 @@ The `<ProgressBar />` component supports all [`@uppy/progress-bar`][] options as
 import React from 'react'
 import React from 'react'
 import { ProgressBar } from '@uppy/react'
 import { ProgressBar } from '@uppy/react'
 
 
-  <ProgressBar
-    uppy={uppy}
-    fixed
-    hideAfterFinish
-  />
+export default function MyComponent (props) {
+  const { uppy } = props
+  return (
+    <ProgressBar
+      // assuming `props.uppy` contains an Uppy instance:
+      uppy={uppy}
+      fixed
+      hideAfterFinish
+    />
+  )
+}
 ```
 ```
 
 
 [`@uppy/progress-bar`]: /docs/progress-bar/
 [`@uppy/progress-bar`]: /docs/progress-bar/

+ 12 - 6
website/src/docs/react-statusbar.md

@@ -44,12 +44,18 @@ The `<StatusBar />` component supports all [`@uppy/status-bar`][] options as pro
 import React from 'react'
 import React from 'react'
 import { StatusBar } from '@uppy/react'
 import { StatusBar } from '@uppy/react'
 
 
-  <StatusBar
-    uppy={uppy}
-    hideUploadButton
-    hideAfterFinish={false}
-    showProgressDetails
-  />
+export default function MyComponent (props) {
+  const { uppy } = props
+  return (
+    <StatusBar
+      // assuming `props.uppy` contains an Uppy instance:
+      uppy={uppy}
+      hideUploadButton
+      hideAfterFinish={false}
+      showProgressDetails
+    />
+  )
+}
 ```
 ```
 
 
 [`@uppy/status-bar`]: /docs/status-bar/
 [`@uppy/status-bar`]: /docs/status-bar/

+ 78 - 0
website/src/docs/remote-sources.md

@@ -0,0 +1,78 @@
+---
+type: docs
+order: 10
+title: "Remote Sources"
+module: "@uppy/remote-sources"
+permalink: docs/remote-sources/
+category: "Miscellaneous"
+tagline: "Uppy plugin that includes all remote sources that Uppy+Companion offer, like Instagram, Google Drive, Dropox, Box, Unsplash, Url etc"
+---
+
+`@uppy/remote-sources` is a preset plugin to add all the available remote sources, such Instagram, Google Drive, Dropbox, and others to Uppy Dashboard in one package.
+
+> Note: Remote Sources requires Dashboard and automatically installs all its plugins to it.
+
+```js
+import Uppy from '@uppy/core'
+import Dashbaord from '@uppy/dashboard'
+import RemoteSources from '@uppy/compressor'
+
+const uppy = new Uppy()
+uppy.use(Dashboard)
+uppy.use(RemoteSources, {
+  companionUrl: 'https://your-companion-url',
+})
+```
+
+## Installation
+
+This plugin is published as the `@uppy/remote-sources` package.
+
+```shell
+npm install @uppy/remote-sources
+```
+
+In the [CDN package](/docs/#With-a-script-tag), the plugin class is available on the `Uppy` global object:
+
+```js
+const { RemoteSources } = Uppy
+```
+
+## Options
+
+### `id`
+
+A unique identifier for this plugin (`string`, default: `RemoteSources`).
+
+### `sources`
+
+List of remote sources that will be enabled (`array`, default: `['Box', 'Dropbox', 'Facebook', 'GoogleDrive','Instagram', 'OneDrive', 'Unsplash', 'Url']`).
+
+You don’t need to specify them manually or change them, but if you want to alter the order in which they appear in the Dashboard, or disable some sources, this option is for you.
+
+```js
+uppy.use(RemoteSources, {
+  companionUrl: 'https://your-companion-url',
+  sources: ['Instagram', 'GoogleDrive', 'Unsplash', 'Url'],
+})
+```
+
+### `companionUrl`
+
+URL to a [Companion](/docs/companion) instance (`string`, default: `null`).
+
+### `companionHeaders`
+
+Custom headers that should be sent along to [Companion](/docs/companion) on every request (`object`, default: `{}`).
+
+### `companionAllowedHosts`
+
+The valid and authorized URL(s) from which OAuth responses should be accepted (`string | RegExp | Array<string | RegExp>`, Default: `companionUrl`)
+
+This value can be a `String`, a `Regex` pattern, or an `Array` of both.
+
+This is useful when you have your [Companion](/docs/companion) running on several hosts. Otherwise, the default value, which is `companionUrl`, should do fine.
+
+### `companionCookiesRule`
+
+This option correlates to the [RequestCredentials value](https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials), which tells the plugin whether to send cookies to [Companion](/docs/companion) (`string`, default: `same-origin`).

+ 381 - 33
yarn.lock

@@ -648,6 +648,29 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@babel/core@npm:^7.11.6, @babel/core@npm:^7.14.3, @babel/core@npm:^7.17.9":
+  version: 7.18.2
+  resolution: "@babel/core@npm:7.18.2"
+  dependencies:
+    "@ampproject/remapping": ^2.1.0
+    "@babel/code-frame": ^7.16.7
+    "@babel/generator": ^7.18.2
+    "@babel/helper-compilation-targets": ^7.18.2
+    "@babel/helper-module-transforms": ^7.18.0
+    "@babel/helpers": ^7.18.2
+    "@babel/parser": ^7.18.0
+    "@babel/template": ^7.16.7
+    "@babel/traverse": ^7.18.2
+    "@babel/types": ^7.18.2
+    convert-source-map: ^1.7.0
+    debug: ^4.1.0
+    gensync: ^1.0.0-beta.2
+    json5: ^2.2.1
+    semver: ^6.3.0
+  checksum: 14a4142c12e004cd2477b7610408d5788ee5dd821ee9e4de204cbb72d9c399d858d9deabc3d49914d5d7c2927548160c19bdc7524b1a9f6acc1ec96a8d9848dd
+  languageName: node
+  linkType: hard
+
 "@babel/core@npm:^7.12.10, @babel/core@npm:^7.17.5":
 "@babel/core@npm:^7.12.10, @babel/core@npm:^7.17.5":
   version: 7.17.5
   version: 7.17.5
   resolution: "@babel/core@npm:7.17.5"
   resolution: "@babel/core@npm:7.17.5"
@@ -671,29 +694,6 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
-"@babel/core@npm:^7.14.3, @babel/core@npm:^7.17.9":
-  version: 7.18.2
-  resolution: "@babel/core@npm:7.18.2"
-  dependencies:
-    "@ampproject/remapping": ^2.1.0
-    "@babel/code-frame": ^7.16.7
-    "@babel/generator": ^7.18.2
-    "@babel/helper-compilation-targets": ^7.18.2
-    "@babel/helper-module-transforms": ^7.18.0
-    "@babel/helpers": ^7.18.2
-    "@babel/parser": ^7.18.0
-    "@babel/template": ^7.16.7
-    "@babel/traverse": ^7.18.2
-    "@babel/types": ^7.18.2
-    convert-source-map: ^1.7.0
-    debug: ^4.1.0
-    gensync: ^1.0.0-beta.2
-    json5: ^2.2.1
-    semver: ^6.3.0
-  checksum: 14a4142c12e004cd2477b7610408d5788ee5dd821ee9e4de204cbb72d9c399d858d9deabc3d49914d5d7c2927548160c19bdc7524b1a9f6acc1ec96a8d9848dd
-  languageName: node
-  linkType: hard
-
 "@babel/eslint-parser@npm:^7.11.3":
 "@babel/eslint-parser@npm:^7.11.3":
   version: 7.16.3
   version: 7.16.3
   resolution: "@babel/eslint-parser@npm:7.16.3"
   resolution: "@babel/eslint-parser@npm:7.16.3"
@@ -4667,6 +4667,37 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/environment@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/environment@npm:28.1.0"
+  dependencies:
+    "@jest/fake-timers": ^28.1.0
+    "@jest/types": ^28.1.0
+    "@types/node": "*"
+    jest-mock: ^28.1.0
+  checksum: 376904d6626bb439f96a56ca9d400e1b6b4a5bafb751820fec649238e35cb7d0b9619223ade86c2906e97fae8da03a7b9561c55c1f5850afe9856db89185d754
+  languageName: node
+  linkType: hard
+
+"@jest/expect-utils@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/expect-utils@npm:28.1.0"
+  dependencies:
+    jest-get-type: ^28.0.2
+  checksum: 5b8b463682bd35ae71868020c87dc654ebed65ded4e74ea3c24bd9e1ab4637a7790c8b78c26cdcb832dd227b9981e8dd24eb3b742891637c24c2a3e38ba153e8
+  languageName: node
+  linkType: hard
+
+"@jest/expect@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/expect@npm:28.1.0"
+  dependencies:
+    expect: ^28.1.0
+    jest-snapshot: ^28.1.0
+  checksum: e596bc2a2d02d66cb3e23982c6a48cfe24aa31932f594db7de6966db6c0b58f7aad3836a71debb8aeda6178116c35160e11ded42a355a94457f6402cbb2186e3
+  languageName: node
+  linkType: hard
+
 "@jest/fake-timers@npm:^24.9.0":
 "@jest/fake-timers@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "@jest/fake-timers@npm:24.9.0"
   resolution: "@jest/fake-timers@npm:24.9.0"
@@ -4692,6 +4723,20 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/fake-timers@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/fake-timers@npm:28.1.0"
+  dependencies:
+    "@jest/types": ^28.1.0
+    "@sinonjs/fake-timers": ^9.1.1
+    "@types/node": "*"
+    jest-message-util: ^28.1.0
+    jest-mock: ^28.1.0
+    jest-util: ^28.1.0
+  checksum: d24375bcd52873f1e602ff02ffe57c6866570b95ec0be167a4734d051047b2c6b3dab69b2a301a390a0ca2de2ad89fd2b23e991c09a1a3b70b1dd4763c8681c7
+  languageName: node
+  linkType: hard
+
 "@jest/globals@npm:^27.4.2":
 "@jest/globals@npm:^27.4.2":
   version: 27.4.2
   version: 27.4.2
   resolution: "@jest/globals@npm:27.4.2"
   resolution: "@jest/globals@npm:27.4.2"
@@ -4703,6 +4748,17 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/globals@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/globals@npm:28.1.0"
+  dependencies:
+    "@jest/environment": ^28.1.0
+    "@jest/expect": ^28.1.0
+    "@jest/types": ^28.1.0
+  checksum: dce822edd1810430ce381235f714be705a9c774c00bf109d9d5df0dc4868371da62520832df99e83635ee1fc1fa4241cf617821b4e3b1a8bcd3fcd91aa8a75a7
+  languageName: node
+  linkType: hard
+
 "@jest/reporters@npm:^27.4.2":
 "@jest/reporters@npm:^27.4.2":
   version: 27.4.2
   version: 27.4.2
   resolution: "@jest/reporters@npm:27.4.2"
   resolution: "@jest/reporters@npm:27.4.2"
@@ -4741,6 +4797,15 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/schemas@npm:^28.0.2":
+  version: 28.0.2
+  resolution: "@jest/schemas@npm:28.0.2"
+  dependencies:
+    "@sinclair/typebox": ^0.23.3
+  checksum: 6a177e97b112c99f377697fe803a34f4489b92cd07949876250c69edc9029c7cbda771fcbb03caebd20ffbcfa89b9c22b4dc9d1e9a7fbc9873185459b48ba780
+  languageName: node
+  linkType: hard
+
 "@jest/source-map@npm:^24.9.0":
 "@jest/source-map@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "@jest/source-map@npm:24.9.0"
   resolution: "@jest/source-map@npm:24.9.0"
@@ -4844,6 +4909,29 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/transform@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/transform@npm:28.1.0"
+  dependencies:
+    "@babel/core": ^7.11.6
+    "@jest/types": ^28.1.0
+    "@jridgewell/trace-mapping": ^0.3.7
+    babel-plugin-istanbul: ^6.1.1
+    chalk: ^4.0.0
+    convert-source-map: ^1.4.0
+    fast-json-stable-stringify: ^2.0.0
+    graceful-fs: ^4.2.9
+    jest-haste-map: ^28.1.0
+    jest-regex-util: ^28.0.2
+    jest-util: ^28.1.0
+    micromatch: ^4.0.4
+    pirates: ^4.0.4
+    slash: ^3.0.0
+    write-file-atomic: ^4.0.1
+  checksum: f7417409c466fa1b4d8f9f7d365c8c1ed07e709e8712279180a87e9da8520ab06518de270b290148034d93f666d7826449b5e40cac34cc5f7225980e8991f2ba
+  languageName: node
+  linkType: hard
+
 "@jest/types@npm:^24.9.0":
 "@jest/types@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "@jest/types@npm:24.9.0"
   resolution: "@jest/types@npm:24.9.0"
@@ -4906,6 +4994,20 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@jest/types@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "@jest/types@npm:28.1.0"
+  dependencies:
+    "@jest/schemas": ^28.0.2
+    "@types/istanbul-lib-coverage": ^2.0.0
+    "@types/istanbul-reports": ^3.0.0
+    "@types/node": "*"
+    "@types/yargs": ^17.0.8
+    chalk: ^4.0.0
+  checksum: 22705aed92a76d45465a6c51147bc71c1fbd300b912ebad2769e3ff7fd51c1938017e29fcea52e00c00dab7130697359b2a2c2be6ee601e37c8b1042a2c4040e
+  languageName: node
+  linkType: hard
+
 "@jridgewell/gen-mapping@npm:^0.3.0":
 "@jridgewell/gen-mapping@npm:^0.3.0":
   version: 0.3.1
   version: 0.3.1
   resolution: "@jridgewell/gen-mapping@npm:0.3.1"
   resolution: "@jridgewell/gen-mapping@npm:0.3.1"
@@ -4955,7 +5057,7 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
-"@jridgewell/trace-mapping@npm:^0.3.9":
+"@jridgewell/trace-mapping@npm:^0.3.7, @jridgewell/trace-mapping@npm:^0.3.9":
   version: 0.3.13
   version: 0.3.13
   resolution: "@jridgewell/trace-mapping@npm:0.3.13"
   resolution: "@jridgewell/trace-mapping@npm:0.3.13"
   dependencies:
   dependencies:
@@ -6534,6 +6636,13 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@sinclair/typebox@npm:^0.23.3":
+  version: 0.23.5
+  resolution: "@sinclair/typebox@npm:0.23.5"
+  checksum: c96056d35d9cb862aeb635ff8873e2e7633e668dd544e162aee2690a82c970d0b3f90aa2b3501fe374dfa8e792388559a3e3a86712b23ebaef10061add534f47
+  languageName: node
+  linkType: hard
+
 "@sindresorhus/is@npm:^0.14.0":
 "@sindresorhus/is@npm:^0.14.0":
   version: 0.14.0
   version: 0.14.0
   resolution: "@sindresorhus/is@npm:0.14.0"
   resolution: "@sindresorhus/is@npm:0.14.0"
@@ -6559,6 +6668,15 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@sinonjs/fake-timers@npm:^9.1.1":
+  version: 9.1.2
+  resolution: "@sinonjs/fake-timers@npm:9.1.2"
+  dependencies:
+    "@sinonjs/commons": ^1.7.0
+  checksum: 7d3aef54e17c1073101cb64d953157c19d62a40e261a30923fa1ee337b049c5f29cc47b1f0c477880f42b5659848ba9ab897607ac8ea4acd5c30ddcfac57fca6
+  languageName: node
+  linkType: hard
+
 "@sitespeed.io/tracium@npm:^0.3.3":
 "@sitespeed.io/tracium@npm:^0.3.3":
   version: 0.3.3
   version: 0.3.3
   resolution: "@sitespeed.io/tracium@npm:0.3.3"
   resolution: "@sitespeed.io/tracium@npm:0.3.3"
@@ -8498,7 +8616,7 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
-"@types/graceful-fs@npm:^4.1.2":
+"@types/graceful-fs@npm:^4.1.2, @types/graceful-fs@npm:^4.1.3":
   version: 4.1.5
   version: 4.1.5
   resolution: "@types/graceful-fs@npm:4.1.5"
   resolution: "@types/graceful-fs@npm:4.1.5"
   dependencies:
   dependencies:
@@ -9225,6 +9343,15 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"@types/yargs@npm:^17.0.8":
+  version: 17.0.10
+  resolution: "@types/yargs@npm:17.0.10"
+  dependencies:
+    "@types/yargs-parser": "*"
+  checksum: f0673cbfc08e17239dc58952a88350d6c4db04a027a28a06fbad27d87b670e909f9cd9e66f9c64cebdd5071d1096261e33454a55868395f125297e5c50992ca8
+  languageName: node
+  linkType: hard
+
 "@types/yauzl@npm:^2.9.1":
 "@types/yauzl@npm:^2.9.1":
   version: 2.9.2
   version: 2.9.2
   resolution: "@types/yauzl@npm:2.9.2"
   resolution: "@types/yauzl@npm:2.9.2"
@@ -10320,6 +10447,27 @@ __metadata:
   languageName: unknown
   languageName: unknown
   linkType: soft
   linkType: soft
 
 
+"@uppy/remote-sources@workspace:packages/@uppy/remote-sources":
+  version: 0.0.0-use.local
+  resolution: "@uppy/remote-sources@workspace:packages/@uppy/remote-sources"
+  dependencies:
+    "@jest/globals": ^28.1.0
+    "@uppy/box": "workspace:^"
+    "@uppy/dashboard": "workspace:^"
+    "@uppy/dropbox": "workspace:^"
+    "@uppy/facebook": "workspace:^"
+    "@uppy/google-drive": "workspace:^"
+    "@uppy/instagram": "workspace:^"
+    "@uppy/onedrive": "workspace:^"
+    "@uppy/unsplash": "workspace:^"
+    "@uppy/url": "workspace:^"
+    "@uppy/zoom": "workspace:^"
+    resize-observer-polyfill: ^1.5.1
+  peerDependencies:
+    "@uppy/core": "workspace:^"
+  languageName: unknown
+  linkType: soft
+
 "@uppy/robodog@workspace:*, @uppy/robodog@workspace:packages/@uppy/robodog":
 "@uppy/robodog@workspace:*, @uppy/robodog@workspace:packages/@uppy/robodog":
   version: 0.0.0-use.local
   version: 0.0.0-use.local
   resolution: "@uppy/robodog@workspace:packages/@uppy/robodog"
   resolution: "@uppy/robodog@workspace:packages/@uppy/robodog"
@@ -12825,7 +12973,7 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
-"babel-plugin-istanbul@npm:6.1.1, babel-plugin-istanbul@npm:^6.0.0":
+"babel-plugin-istanbul@npm:6.1.1, babel-plugin-istanbul@npm:^6.0.0, babel-plugin-istanbul@npm:^6.1.1":
   version: 6.1.1
   version: 6.1.1
   resolution: "babel-plugin-istanbul@npm:6.1.1"
   resolution: "babel-plugin-istanbul@npm:6.1.1"
   dependencies:
   dependencies:
@@ -14415,9 +14563,9 @@ __metadata:
   linkType: hard
   linkType: hard
 
 
 "caniuse-lite@npm:^1.0.30001332":
 "caniuse-lite@npm:^1.0.30001332":
-  version: 1.0.30001344
-  resolution: "caniuse-lite@npm:1.0.30001344"
-  checksum: 9dba66f796dc98632dced4c5d487d0fad219e137a27c634eec68520f2e598a613e3371b9207e15a078689a629128eca898793e37fc98841821ab481bddad51b9
+  version: 1.0.30001346
+  resolution: "caniuse-lite@npm:1.0.30001346"
+  checksum: 951590454ffa4e2e7b772558dc593cd08604b44c83741e1188166298f54c34387f4bf34f5141a35de4a43028c012484240ad15c896e48bf4eac70dd7076a4449
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
@@ -16989,6 +17137,13 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"diff-sequences@npm:^28.0.2":
+  version: 28.0.2
+  resolution: "diff-sequences@npm:28.0.2"
+  checksum: 482360a8ec93333ea61bc93a800a1bee37c943b94a48fa1597825076adcad24620b44a0d3aa8f3d190584a4156c4b3315028453ca33e1174001fae3cdaa7f8f8
+  languageName: node
+  linkType: hard
+
 "diff@npm:^4.0.1":
 "diff@npm:^4.0.1":
   version: 4.0.2
   version: 4.0.2
   resolution: "diff@npm:4.0.2"
   resolution: "diff@npm:4.0.2"
@@ -17496,9 +17651,9 @@ __metadata:
   linkType: hard
   linkType: hard
 
 
 "electron-to-chromium@npm:^1.4.118":
 "electron-to-chromium@npm:^1.4.118":
-  version: 1.4.142
-  resolution: "electron-to-chromium@npm:1.4.142"
-  checksum: 53c6105ef81923a4dba0a53fdd1a010a492ad051eb527969e981ce78aee5ebdc729e7b16733e6cfb7b934ad23b84172d0677abaa5d9cd0ae71cd4cf986e4ab2e
+  version: 1.4.147
+  resolution: "electron-to-chromium@npm:1.4.147"
+  checksum: a714da8ac6842887e98886026b8eeaee0d2fd6d57f5707b0fc2a2916c1b9d026ca8deeef529fd3b069e96f719495a7467b01a508b881fd90d95aa204a7a92000
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
@@ -19732,6 +19887,19 @@ __metadata:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"expect@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "expect@npm:28.1.0"
+  dependencies:
+    "@jest/expect-utils": ^28.1.0
+    jest-get-type: ^28.0.2
+    jest-matcher-utils: ^28.1.0
+    jest-message-util: ^28.1.0
+    jest-util: ^28.1.0
+  checksum: 53bfa2e094a7d5b270ce9a8dafc5432d51bb369287502acd373b66fe01072260bacd1f83bf741d5de49b008406781ab879a0247f5f6fc10d3f32fbe5a3ccfbdf
+  languageName: node
+  linkType: hard
+
 "expo-application@npm:~3.2.0":
 "expo-application@npm:~3.2.0":
   version: 3.2.0
   version: 3.2.0
   resolution: "expo-application@npm:3.2.0"
   resolution: "expo-application@npm:3.2.0"
@@ -24523,6 +24691,18 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-diff@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-diff@npm:28.1.0"
+  dependencies:
+    chalk: ^4.0.0
+    diff-sequences: ^28.0.2
+    jest-get-type: ^28.0.2
+    pretty-format: ^28.1.0
+  checksum: 4d90d9d18ba1d28f5520fa206831e9e8199facf28c6d2b4967c7e4cd1ee78e7e826187babdeb02073f79a1d2c186520d73f77fa29877c6547b0a79392d08a513
+  languageName: node
+  linkType: hard
+
 "jest-docblock@npm:^27.4.0":
 "jest-docblock@npm:^27.4.0":
   version: 27.4.0
   version: 27.4.0
   resolution: "jest-docblock@npm:27.4.0"
   resolution: "jest-docblock@npm:27.4.0"
@@ -24588,6 +24768,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-get-type@npm:^28.0.2":
+  version: 28.0.2
+  resolution: "jest-get-type@npm:28.0.2"
+  checksum: 5281d7c89bc8156605f6d15784f45074f4548501195c26e9b188742768f72d40948252d13230ea905b5349038865a1a8eeff0e614cc530ff289dfc41fe843abd
+  languageName: node
+  linkType: hard
+
 "jest-haste-map@npm:^24.9.0":
 "jest-haste-map@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "jest-haste-map@npm:24.9.0"
   resolution: "jest-haste-map@npm:24.9.0"
@@ -24660,6 +24847,29 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-haste-map@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-haste-map@npm:28.1.0"
+  dependencies:
+    "@jest/types": ^28.1.0
+    "@types/graceful-fs": ^4.1.3
+    "@types/node": "*"
+    anymatch: ^3.0.3
+    fb-watchman: ^2.0.0
+    fsevents: ^2.3.2
+    graceful-fs: ^4.2.9
+    jest-regex-util: ^28.0.2
+    jest-util: ^28.1.0
+    jest-worker: ^28.1.0
+    micromatch: ^4.0.4
+    walker: ^1.0.7
+  dependenciesMeta:
+    fsevents:
+      optional: true
+  checksum: 128c2d1aa39610febfc9fe66bbc40bb847d89da3e1646ed1bbe63e90bd4c930d1798d20aef8d928fda8e5b0570f05f1cbb263030ebe776c01bb86dd5174434da
+  languageName: node
+  linkType: hard
+
 "jest-jasmine2@npm:^27.4.2":
 "jest-jasmine2@npm:^27.4.2":
   version: 27.4.2
   version: 27.4.2
   resolution: "jest-jasmine2@npm:27.4.2"
   resolution: "jest-jasmine2@npm:27.4.2"
@@ -24708,6 +24918,18 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-matcher-utils@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-matcher-utils@npm:28.1.0"
+  dependencies:
+    chalk: ^4.0.0
+    jest-diff: ^28.1.0
+    jest-get-type: ^28.0.2
+    pretty-format: ^28.1.0
+  checksum: 60e3e83fff67402972b101135d44443981d6519008e435b567f197220f330ec38356f905b6872348d082f0a2a4089612f63d2c72f55ee3c718de6b0ef03f4d6d
+  languageName: node
+  linkType: hard
+
 "jest-message-util@npm:^24.9.0":
 "jest-message-util@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "jest-message-util@npm:24.9.0"
   resolution: "jest-message-util@npm:24.9.0"
@@ -24741,6 +24963,23 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-message-util@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-message-util@npm:28.1.0"
+  dependencies:
+    "@babel/code-frame": ^7.12.13
+    "@jest/types": ^28.1.0
+    "@types/stack-utils": ^2.0.0
+    chalk: ^4.0.0
+    graceful-fs: ^4.2.9
+    micromatch: ^4.0.4
+    pretty-format: ^28.1.0
+    slash: ^3.0.0
+    stack-utils: ^2.0.3
+  checksum: a224f9dbb53b5ad857918938f94c6e5d9c64ccdd42e0780b3b485d66bd93c82cff7dd91fbe274273efb69533d79808f9c98622b23d70ec027e8619a20e283773
+  languageName: node
+  linkType: hard
+
 "jest-mock@npm:^24.9.0":
 "jest-mock@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "jest-mock@npm:24.9.0"
   resolution: "jest-mock@npm:24.9.0"
@@ -24770,6 +25009,16 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-mock@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-mock@npm:28.1.0"
+  dependencies:
+    "@jest/types": ^28.1.0
+    "@types/node": "*"
+  checksum: 013428db82f418059314588e5d02a2a8f6697940ffeb1b1a23f61e9b94b1dca3ea0061d91f284e217bf0ce0e5251ff8f2f182a393cecd1ec6788d766cc18ded4
+  languageName: node
+  linkType: hard
+
 "jest-pnp-resolver@npm:^1.2.2":
 "jest-pnp-resolver@npm:^1.2.2":
   version: 1.2.2
   version: 1.2.2
   resolution: "jest-pnp-resolver@npm:1.2.2"
   resolution: "jest-pnp-resolver@npm:1.2.2"
@@ -24796,6 +25045,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-regex-util@npm:^28.0.2":
+  version: 28.0.2
+  resolution: "jest-regex-util@npm:28.0.2"
+  checksum: 0ea8c5c82ec88bc85e273c0ec82e0c0f35f7a1e2d055070e50f0cc2a2177f848eec55f73e37ae0d045c3db5014c42b2f90ac62c1ab3fdb354d2abd66a9e08add
+  languageName: node
+  linkType: hard
+
 "jest-resolve-dependencies@npm:^27.4.2":
 "jest-resolve-dependencies@npm:^27.4.2":
   version: 27.4.2
   version: 27.4.2
   resolution: "jest-resolve-dependencies@npm:27.4.2"
   resolution: "jest-resolve-dependencies@npm:27.4.2"
@@ -24948,6 +25204,37 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-snapshot@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-snapshot@npm:28.1.0"
+  dependencies:
+    "@babel/core": ^7.11.6
+    "@babel/generator": ^7.7.2
+    "@babel/plugin-syntax-typescript": ^7.7.2
+    "@babel/traverse": ^7.7.2
+    "@babel/types": ^7.3.3
+    "@jest/expect-utils": ^28.1.0
+    "@jest/transform": ^28.1.0
+    "@jest/types": ^28.1.0
+    "@types/babel__traverse": ^7.0.6
+    "@types/prettier": ^2.1.5
+    babel-preset-current-node-syntax: ^1.0.0
+    chalk: ^4.0.0
+    expect: ^28.1.0
+    graceful-fs: ^4.2.9
+    jest-diff: ^28.1.0
+    jest-get-type: ^28.0.2
+    jest-haste-map: ^28.1.0
+    jest-matcher-utils: ^28.1.0
+    jest-message-util: ^28.1.0
+    jest-util: ^28.1.0
+    natural-compare: ^1.4.0
+    pretty-format: ^28.1.0
+    semver: ^7.3.5
+  checksum: 73695484cf4e2af9d0dbb8bc1e851f6d6217cc740aa93b521012c253fbbd9dc1ce11b147ac3e18cac8358b4b64fe36a1b8a6d1a3083c9d275dd937281faad818
+  languageName: node
+  linkType: hard
+
 "jest-util@npm:^24.9.0":
 "jest-util@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "jest-util@npm:24.9.0"
   resolution: "jest-util@npm:24.9.0"
@@ -24996,6 +25283,20 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-util@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-util@npm:28.1.0"
+  dependencies:
+    "@jest/types": ^28.1.0
+    "@types/node": "*"
+    chalk: ^4.0.0
+    ci-info: ^3.2.0
+    graceful-fs: ^4.2.9
+    picomatch: ^2.2.3
+  checksum: 14c2ee1c24c6efa2d7adfe81ece8b9bbda78fa871f40bed80db72726166e96f7fb22bf1d9fb1689fb433b9bcd748027eb1ee5f0851a12f1aa1c49ee0bd4d7508
+  languageName: node
+  linkType: hard
+
 "jest-validate@npm:^24.9.0":
 "jest-validate@npm:^24.9.0":
   version: 24.9.0
   version: 24.9.0
   resolution: "jest-validate@npm:24.9.0"
   resolution: "jest-validate@npm:24.9.0"
@@ -25082,6 +25383,17 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"jest-worker@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "jest-worker@npm:28.1.0"
+  dependencies:
+    "@types/node": "*"
+    merge-stream: ^2.0.0
+    supports-color: ^8.0.0
+  checksum: 44b6cfb03752543e2462f143ca5c9642206f20813068ef0461e793bb8feda85f643ee906d96a0a57728e1a2fb5b89386fd34e44289568b1cee5815c115e7ee02
+  languageName: node
+  linkType: hard
+
 "jest@npm:^27.0.6":
 "jest@npm:^27.0.6":
   version: 27.4.3
   version: 27.4.3
   resolution: "jest@npm:27.4.3"
   resolution: "jest@npm:27.4.3"
@@ -31073,7 +31385,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
-"pirates@npm:^4.0.5":
+"pirates@npm:^4.0.4, pirates@npm:^4.0.5":
   version: 4.0.5
   version: 4.0.5
   resolution: "pirates@npm:4.0.5"
   resolution: "pirates@npm:4.0.5"
   checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227
   checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227
@@ -32523,6 +32835,18 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"pretty-format@npm:^28.1.0":
+  version: 28.1.0
+  resolution: "pretty-format@npm:28.1.0"
+  dependencies:
+    "@jest/schemas": ^28.0.2
+    ansi-regex: ^5.0.1
+    ansi-styles: ^5.0.0
+    react-is: ^18.0.0
+  checksum: c1018099f8f800693449df96c05c243d94e01f7429b6617e1064a1a69b4d715637fc3c579061fbc31548b87d92af74a7933c6eb3856da6f30b29c0ff67004ce0
+  languageName: node
+  linkType: hard
+
 "pretty-format@npm:^3.8.0":
 "pretty-format@npm:^3.8.0":
   version: 3.8.0
   version: 3.8.0
   resolution: "pretty-format@npm:3.8.0"
   resolution: "pretty-format@npm:3.8.0"
@@ -33255,6 +33579,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"react-is@npm:^18.0.0":
+  version: 18.1.0
+  resolution: "react-is@npm:18.1.0"
+  checksum: d206a0fe6790851bff168727bfb896de02c5591695afb0c441163e8630136a3e13ee1a7ddd59fdccddcc93968b4721ae112c10f790b194b03b35a3dc13a355ef
+  languageName: node
+  linkType: hard
+
 "react-native-safe-area-context@npm:3.2.0":
 "react-native-safe-area-context@npm:3.2.0":
   version: 3.2.0
   version: 3.2.0
   resolution: "react-native-safe-area-context@npm:3.2.0"
   resolution: "react-native-safe-area-context@npm:3.2.0"
@@ -35931,6 +36262,13 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"signal-exit@npm:^3.0.7":
+  version: 3.0.7
+  resolution: "signal-exit@npm:3.0.7"
+  checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318
+  languageName: node
+  linkType: hard
+
 "simple-concat@npm:^1.0.0":
 "simple-concat@npm:^1.0.0":
   version: 1.0.1
   version: 1.0.1
   resolution: "simple-concat@npm:1.0.1"
   resolution: "simple-concat@npm:1.0.1"
@@ -41520,6 +41858,16 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   languageName: node
   linkType: hard
   linkType: hard
 
 
+"write-file-atomic@npm:^4.0.1":
+  version: 4.0.1
+  resolution: "write-file-atomic@npm:4.0.1"
+  dependencies:
+    imurmurhash: ^0.1.4
+    signal-exit: ^3.0.7
+  checksum: 8f780232533ca6223c63c9b9c01c4386ca8c625ebe5017a9ed17d037aec19462ae17109e0aa155bff5966ee4ae7a27b67a99f55caf3f32ffd84155e9da3929fc
+  languageName: node
+  linkType: hard
+
 "write@npm:1.0.3":
 "write@npm:1.0.3":
   version: 1.0.3
   version: 1.0.3
   resolution: "write@npm:1.0.3"
   resolution: "write@npm:1.0.3"