Forráskód Böngészése

Merge stable branch

Antoine du Hamel 2 éve
szülő
commit
a6029d0304
47 módosított fájl, 1159 hozzáadás és 191 törlés
  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/@uppy/audio/src/**/*.js',
-        'packages/@uppy/aws-s3/src/**/*.js',
         'packages/@uppy/aws-s3-multipart/src/**/*.js',
+        'packages/@uppy/aws-s3/src/**/*.js',
         'packages/@uppy/box/src/**/*.js',
         'packages/@uppy/companion-client/src/**/*.js',
         'packages/@uppy/compressor/src/**/*.js',
@@ -222,16 +222,17 @@ module.exports = {
         'packages/@uppy/provider-views/src/**/*.js',
         'packages/@uppy/react/src/**/*.js',
         'packages/@uppy/redux-dev-tools/src/**/*.js',
+        'packages/@uppy/remote-sources/src/**/*.js',
         'packages/@uppy/screen-capture/src/**/*.js',
         'packages/@uppy/status-bar/src/**/*.js',
         'packages/@uppy/store-default/src/**/*.js',
         'packages/@uppy/store-redux/src/**/*.js',
-        'packages/@uppy/svelte/src/**/*.js',
         'packages/@uppy/svelte/rollup.config.js',
+        'packages/@uppy/svelte/src/**/*.js',
         'packages/@uppy/thumbnail-generator/src/**/*.js',
+        'packages/@uppy/transloadit/src/**/*.js',
         'packages/@uppy/tus/src/**/*.js',
         'packages/@uppy/unsplash/src/**/*.js',
-        'packages/@uppy/transloadit/src/**/*.js',
         'packages/@uppy/url/src/**/*.js',
         'packages/@uppy/utils/src/**/*.js',
         'packages/@uppy/vue/src/**/*.js',

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

@@ -14,13 +14,8 @@ jobs:
       - name: Checkout sources
         uses: actions/checkout@v3
         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
         id: yarn-cache-dir-path
         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 >> 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
       - name: Open Pull Request
         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)
 
 
+## 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
 
 Released: 2022-05-30

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

@@ -1,5 +1,13 @@
 # @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
 
 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 {
   static VERSION = packageJson.version
 
+  #queueRequestSocketToken
+
+  #client
+
   constructor (uppy, opts) {
     super(uppy, opts)
     this.type = 'uploader'
     this.id = this.opts.id || 'AwsS3Multipart'
     this.title = 'AWS S3 Multipart'
-    this.client = new RequestClient(uppy, opts)
+    this.#client = new RequestClient(uppy, opts)
 
     const defaultOptions = {
       timeout: 30 * 1000,
@@ -36,6 +40,7 @@ export default class AwsS3Multipart extends BasePlugin {
       prepareUploadParts: this.prepareUploadParts.bind(this),
       abortMultipartUpload: this.abortMultipartUpload.bind(this),
       completeMultipartUpload: this.completeMultipartUpload.bind(this),
+      companionHeaders: {},
     }
 
     this.opts = { ...defaultOptions, ...opts }
@@ -47,8 +52,17 @@ export default class AwsS3Multipart extends BasePlugin {
     this.uploaders = Object.create(null)
     this.uploaderEvents = 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,
    * 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,
       type: file.type,
       metadata,
@@ -99,7 +113,7 @@ export default class AwsS3Multipart extends BasePlugin {
     this.assertHost('listParts')
 
     const filename = encodeURIComponent(key)
-    return this.client.get(`s3/multipart/${uploadId}?key=${filename}`)
+    return this.#client.get(`s3/multipart/${uploadId}?key=${filename}`)
       .then(assertServerError)
   }
 
@@ -107,7 +121,7 @@ export default class AwsS3Multipart extends BasePlugin {
     this.assertHost('prepareUploadParts')
 
     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)
   }
 
@@ -116,7 +130,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
     const filename = encodeURIComponent(key)
     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)
   }
 
@@ -125,7 +139,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
     const filename = encodeURIComponent(key)
     const uploadIdEnc = encodeURIComponent(uploadId)
-    return this.client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`)
+    return this.#client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`)
       .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)
 
     // 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)
     }
 
-    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)
-      }).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) {
@@ -322,7 +340,7 @@ export default class AwsS3Multipart extends BasePlugin {
 
       const token = file.serverToken
       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.uploaderEvents[file.id] = new EventTracker(this.uppy)
 
@@ -412,7 +430,6 @@ export default class AwsS3Multipart extends BasePlugin {
       })
 
       queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
           socket.send('pause', {})
         }
@@ -436,6 +453,11 @@ export default class AwsS3Multipart extends BasePlugin {
     return Promise.all(promises)
   }
 
+  #setCompanionHeaders = () => {
+    this.#client.setCompanionHeaders(this.opts.companionHeaders)
+    return Promise.resolve()
+  }
+
   onFileRemove (fileID, cb) {
     this.uploaderEvents[fileID].on('file-removed', (file) => {
       if (fileID === file.id) cb(file.id)
@@ -495,6 +517,7 @@ export default class AwsS3Multipart extends BasePlugin {
         resumableUploads: true,
       },
     })
+    this.uppy.addPreProcessor(this.#setCompanionHeaders)
     this.uppy.addUploader(this.upload)
   }
 
@@ -506,6 +529,7 @@ export default class AwsS3Multipart extends BasePlugin {
         resumableUploads: false,
       },
     })
+    this.uppy.removePreProcessor(this.#setCompanionHeaders)
     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)
     })
   })
+
+  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
 
+## 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
 
 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
 
 export default class MiniXHRUpload {
+  #queueRequestSocketToken
+
   constructor (uppy, opts) {
     this.uppy = uppy
     this.opts = {
@@ -65,6 +67,8 @@ export default class MiniXHRUpload {
     this.requests = opts[internalRateLimitedQueue]
     this.uploaderEvents = Object.create(null)
     this.i18n = opts.i18n
+
+    this.#queueRequestSocketToken = this.requests.wrapPromiseFunction(this.#requestSocketToken)
   }
 
   #getOptions (file) {
@@ -243,19 +247,21 @@ export default class MiniXHRUpload {
     })
   }
 
-  #uploadRemoteFile (file) {
+  #requestSocketToken = async (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)
       ? opts.metaFields
-    // Send along all fields by default.
+      // Send along all fields by default.
       : 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,
       endpoint: opts.endpoint,
       size: file.data.size,
@@ -264,14 +270,34 @@ export default class MiniXHRUpload {
       httpMethod: opts.method,
       useFormData: opts.formData,
       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 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)
 
       const queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
           socket.send('pause', {})
         }
@@ -341,6 +367,6 @@ export default class MiniXHRUpload {
     }).catch((err) => {
       this.uppy.emit('upload-error', file, 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,
       metaFields: [], // have to opt in
       getUploadParameters: this.getUploadParameters.bind(this),
+      companionHeaders: {},
     }
 
     this.opts = { ...defaultOptions, ...opts }
@@ -128,6 +129,13 @@ export default class AwsS3 extends BasePlugin {
     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) {
     if (!this.opts.companionUrl) {
       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 () {
     const { uppy } = this
+    uppy.addPreProcessor(this.#setCompanionHeaders)
     uppy.addUploader(this.#handleUpload)
 
     // Get the response data from a successful XMLHttpRequest instance.
@@ -279,6 +293,7 @@ export default class AwsS3 extends BasePlugin {
   }
 
   uninstall () {
+    this.uppy.removePreProcessor(this.#setCompanionHeaders)
     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()
     })
   })
+
+  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 {
   static VERSION = packageJson.version
 
+  #companionHeaders
+
   #getPostResponseFunc = skip => response => (skip ? response : this.onReceiveResponse(response))
 
   constructor (uppy, opts) {
@@ -43,8 +45,15 @@ export default class RequestClient {
     this.onReceiveResponse = this.onReceiveResponse.bind(this)
     this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token']
     this.preflightDone = false
+    this.#companionHeaders = opts?.companionHeaders
+  }
+
+  setCompanionHeaders (headers) {
+    this.#companionHeaders = headers
   }
 
+  [Symbol.for('uppy test: getCompanionHeaders')] () { return this.#companionHeaders }
+
   get hostname () {
     const { companion } = this.uppy.getState()
     const host = this.opts.companionUrl
@@ -58,10 +67,9 @@ export default class RequestClient {
   }
 
   headers () {
-    const userHeaders = this.opts.companionHeaders || {}
     return Promise.resolve({
       ...RequestClient.defaultHeaders,
-      ...userHeaders,
+      ...this.#companionHeaders,
     })
   }
 

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

@@ -1,5 +1,12 @@
 # @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
 
 Released: 2022-05-30

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

@@ -967,7 +967,7 @@ class Uppy {
         if (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)
       } else {
         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)
 
+## 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
 
 Released: 2022-05-30

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

@@ -18,8 +18,9 @@ export default function useUppy (factory) {
   useEffect(() => {
     return () => {
       uppy.current.close({ reason: 'unmount' })
+      uppy.current = undefined
     }
-  }, [])
+  }, [uppy])
 
   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
 
+## 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
 
 Released: 2022-05-30

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

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

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

@@ -1,3 +1,4 @@
+/* eslint-disable global-require */
 const Transloadit = require('@uppy/transloadit')
 const has = require('@uppy/utils/lib/hasProperty')
 
@@ -39,6 +40,7 @@ function addRemoteProvider (uppy, name, opts) {
     companionAllowedHosts: Transloadit.COMPANION_PATTERN,
   }
 
+  // eslint-disable-next-line no-shadow
   remoteProviderOptionNames.forEach((name) => {
     if (has(opts, name)) providerOptions[name] = opts[name]
   })
@@ -68,6 +70,7 @@ function addLocalProvider (uppy, name, opts) {
   const Provider = localProviders[name]
   const providerOptions = {}
 
+  // eslint-disable-next-line no-shadow
   localProviderOptionNames.forEach((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) {
   if (!opts) throw new TypeError('robodog.form: must provide an options object')
 
+  // eslint-disable-next-line no-param-reassign
   opts = {
     ...opts,
     locale: mergeDefaultLocale(defaultLocaleStrings, opts.locale),

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

@@ -8,5 +8,7 @@ module.exports = {
   form,
   pick,
   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,
 }

+ 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)
 
+## 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
 
 Released: 2022-05-30

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

@@ -835,12 +835,14 @@ export default class Transloadit extends BasePlugin {
 }
 
 export {
-  COMPANION,
   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
 // 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.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
 
+## 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
 
 Released: 2022-05-30

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

@@ -57,6 +57,8 @@ export default class Tus extends BasePlugin {
 
   #retryDelayIterator
 
+  #queueRequestSocketToken
+
   /**
    * @param {Uppy} uppy
    * @param {TusOptions} opts
@@ -97,6 +99,7 @@ export default class Tus extends BasePlugin {
 
     this.handleResetProgress = this.handleResetProgress.bind(this)
     this.handleUpload = this.handleUpload.bind(this)
+    this.#queueRequestSocketToken = this.requests.wrapPromiseFunction(this.#requestSocketToken)
   }
 
   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 }
+
     if (file.tus) {
       // Install file-specific upload overrides.
       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 {
-      // !! 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) {
       this.uppy.emit('upload-error', file, err)
       throw err
@@ -482,7 +490,7 @@ export default class Tus extends BasePlugin {
     return new Promise((resolve, reject) => {
       const token = file.serverToken
       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.uploaderEvents[file.id] = new EventTracker(this.uppy)
 
@@ -591,7 +599,6 @@ export default class Tus extends BasePlugin {
       })
 
       queuedRequest = this.requests.run(() => {
-        socket.open()
         if (file.isPaused) {
           socket.send('pause', {})
         }

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

@@ -20,7 +20,7 @@ export interface TusOptions extends PluginOptions, TusUploadOptions {
     limit?: number
     useFastRemoteRetry?: 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> {}

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

@@ -1,5 +1,13 @@
 # @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
 
 Released: 2022-05-30

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

@@ -48,7 +48,8 @@ function checkIfCorrectURL (url) {
 }
 
 function getFileNameFromUrl (url) {
-  return url.substring(url.lastIndexOf('/') + 1)
+  const { pathname } = new URL(url)
+  return pathname.substring(pathname.lastIndexOf('/') + 1)
 }
 /**
  * Url
@@ -104,7 +105,7 @@ export default class Url extends UIPlugin {
       })
   }
 
-  async addFile (protocollessUrl) {
+  async addFile (protocollessUrl, optionalMeta = undefined) {
     const url = this.addProtocolToURL(protocollessUrl)
     if (!this.checkIfCorrectURL(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 tagFile = {
+        meta: optionalMeta,
         source: this.id,
         name: this.getFileNameFromUrl(url),
         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 UrlLocale from './generatedLocale'
 
@@ -9,7 +9,7 @@ export interface UrlOptions extends PluginOptions, RequestClientOptions {
 }
 
 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

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

@@ -1,5 +1,12 @@
 # @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
 
 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`)
         timer.done()
         queuedRequest.done()
@@ -260,12 +260,12 @@ export default class XHRUpload extends BasePlugin {
           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 uploadURL = body[opts.responseUrlFieldName]
 
           const uploadResp = {
-            status: ev.target.status,
+            status: xhr.status,
             body,
             uploadURL,
           }
@@ -282,7 +282,7 @@ export default class XHRUpload extends BasePlugin {
         const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr))
 
         const response = {
-          status: ev.target.status,
+          status: xhr.status,
           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 Instagram } from '@uppy/instagram'
 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 Unsplash } from '@uppy/unsplash'
 export { default as Url } from '@uppy/url'

+ 14 - 18
private/dev/Dashboard.js

@@ -2,15 +2,7 @@
 /* eslint-disable import/no-extraneous-dependencies */
 import Uppy from '@uppy/core'
 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 ScreenCapture from '@uppy/screen-capture'
 import GoldenRetriever from '@uppy/golden-retriever'
@@ -83,15 +75,19 @@ export default () => {
       proudlyDisplayPoweredByUppy: true,
       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, {
       target: Dashboard,
       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 prompts from 'prompts'
+import { TARGET_BRANCH } from './config.js'
 
 const ROOT = new  URL('../../', import.meta.url)
 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`,
     )
     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,
       )}.`,
     )
@@ -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`,
       )
       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,
         )}.`,
       )

+ 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 { 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 { 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 { 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/

+ 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 { 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/

+ 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
   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":
   version: 7.17.5
   resolution: "@babel/core@npm:7.17.5"
@@ -671,29 +694,6 @@ __metadata:
   languageName: node
   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":
   version: 7.16.3
   resolution: "@babel/eslint-parser@npm:7.16.3"
@@ -4667,6 +4667,37 @@ __metadata:
   languageName: node
   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":
   version: 24.9.0
   resolution: "@jest/fake-timers@npm:24.9.0"
@@ -4692,6 +4723,20 @@ __metadata:
   languageName: node
   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":
   version: 27.4.2
   resolution: "@jest/globals@npm:27.4.2"
@@ -4703,6 +4748,17 @@ __metadata:
   languageName: node
   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":
   version: 27.4.2
   resolution: "@jest/reporters@npm:27.4.2"
@@ -4741,6 +4797,15 @@ __metadata:
   languageName: node
   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":
   version: 24.9.0
   resolution: "@jest/source-map@npm:24.9.0"
@@ -4844,6 +4909,29 @@ __metadata:
   languageName: node
   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":
   version: 24.9.0
   resolution: "@jest/types@npm:24.9.0"
@@ -4906,6 +4994,20 @@ __metadata:
   languageName: node
   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":
   version: 0.3.1
   resolution: "@jridgewell/gen-mapping@npm:0.3.1"
@@ -4955,7 +5057,7 @@ __metadata:
   languageName: node
   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
   resolution: "@jridgewell/trace-mapping@npm:0.3.13"
   dependencies:
@@ -6534,6 +6636,13 @@ __metadata:
   languageName: node
   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":
   version: 0.14.0
   resolution: "@sindresorhus/is@npm:0.14.0"
@@ -6559,6 +6668,15 @@ __metadata:
   languageName: node
   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":
   version: 0.3.3
   resolution: "@sitespeed.io/tracium@npm:0.3.3"
@@ -8498,7 +8616,7 @@ __metadata:
   languageName: node
   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
   resolution: "@types/graceful-fs@npm:4.1.5"
   dependencies:
@@ -9225,6 +9343,15 @@ __metadata:
   languageName: node
   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":
   version: 2.9.2
   resolution: "@types/yauzl@npm:2.9.2"
@@ -10320,6 +10447,27 @@ __metadata:
   languageName: unknown
   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":
   version: 0.0.0-use.local
   resolution: "@uppy/robodog@workspace:packages/@uppy/robodog"
@@ -12825,7 +12973,7 @@ __metadata:
   languageName: node
   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
   resolution: "babel-plugin-istanbul@npm:6.1.1"
   dependencies:
@@ -14415,9 +14563,9 @@ __metadata:
   linkType: hard
 
 "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
   linkType: hard
 
@@ -16989,6 +17137,13 @@ __metadata:
   languageName: node
   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":
   version: 4.0.2
   resolution: "diff@npm:4.0.2"
@@ -17496,9 +17651,9 @@ __metadata:
   linkType: hard
 
 "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
   linkType: hard
 
@@ -19732,6 +19887,19 @@ __metadata:
   languageName: node
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 27.4.3
   resolution: "jest@npm:27.4.3"
@@ -31073,7 +31385,7 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   linkType: hard
 
-"pirates@npm:^4.0.5":
+"pirates@npm:^4.0.4, pirates@npm:^4.0.5":
   version: 4.0.5
   resolution: "pirates@npm:4.0.5"
   checksum: c9994e61b85260bec6c4fc0307016340d9b0c4f4b6550a957afaaff0c9b1ad58fbbea5cfcf083860a25cb27a375442e2b0edf52e2e1e40e69934e08dcc52d227
@@ -32523,6 +32835,18 @@ hexo-filter-github-emojis@arturi/hexo-filter-github-emojis:
   languageName: node
   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":
   version: 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
   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":
   version: 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
   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":
   version: 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
   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":
   version: 1.0.3
   resolution: "write@npm:1.0.3"