Browse Source

Merge stable branch

Antoine du Hamel 10 months ago
parent
commit
ae2ffb88cd

+ 6 - 0
.github/CONTRIBUTING.md

@@ -297,6 +297,12 @@ Now let’s create the version & tag:
 mkdir -p .git && npm version --workspaces-update=false --tag-version-prefix='@uppy/companion@' patch
 mkdir -p .git && npm version --workspaces-update=false --tag-version-prefix='@uppy/companion@' patch
 ```
 ```
 
 
+**Important:** Build Companion lib folder
+
+```bash
+yarn run build
+```
+
 Run a “dry-run” first:
 Run a “dry-run” first:
 
 
 ```bash
 ```bash

+ 20 - 0
CHANGELOG.md

@@ -350,6 +350,26 @@ Released: 2024-03-28
 - @uppy/vue: [v4.x] remove manual types (Antoine du Hamel / #4803)
 - @uppy/vue: [v4.x] remove manual types (Antoine du Hamel / #4803)
 - meta: prepare release workflow for beta versions (Antoine du Hamel)
 - meta: prepare release workflow for beta versions (Antoine du Hamel)
 
 
+## 3.26.0
+
+Released: 2024-06-04
+
+| Package                | Version | Package                | Version |
+| ---------------------- | ------- | ---------------------- | ------- |
+| @uppy/aws-s3-multipart |  3.12.0 | @uppy/webcam           |   3.4.2 |
+| @uppy/core             |  3.12.0 | uppy                   |  3.26.0 |
+| @uppy/transloadit      |   3.7.0 |                        |         |
+
+- meta: remove Companion's `prepublishOnly` (Mikael Finstad / #5220)
+- docs: document clearUploadedFiles (Merlijn Vos / #5204)
+- @uppy/webcam: add missing types for `recordedVideo` (Antoine du Hamel / #5208)
+- @uppy/core: check capabilities in clearUploadedFiles (Merlijn Vos / #5201)
+- @uppy/core: PartialTree - change the `maxTotalFileSize` error (Evgenia Karunus / #5203)
+- @uppy/transloadit: remove `updateNumberOfFilesInAssembly` (Merlijn Vos / #5202)
+- @uppy/aws-s3: resolve all headers on response (Merlijn Vos / #5195)
+- docs: Improve provider docs: OneDrive (Evgenia Karunus / #5196)
+
+
 ## 3.25.5
 ## 3.25.5
 
 
 Released: 2024-05-23
 Released: 2024-05-23

+ 9 - 1
docs/sources/companion-plugins/onedrive.mdx

@@ -100,7 +100,15 @@ If you are using Transloadit hosted Companion:
 https://api2.transloadit.com/companion/onedrive/redirect
 https://api2.transloadit.com/companion/onedrive/redirect
 ```
 ```
 
 
-Microsoft will give you an OAuth client ID and client secret.
+Go to the “Manifest” tab, and find the `"signInAudience"` key.  
+Change it to `"signInAudience": "AzureADandPersonalMicrosoftAccount"`, and click
+“Save”.
+
+Go to the “Overview” tab.  
+Copy the `Application (client) ID` field - this will be your Oauth client ID.
+
+Go to the “Certificates & secrets” tab, and click “+ New client secret”.  
+Copy the `Value` field - this will be your OAuth client secret.
 
 
 Configure the OneDrive key and secret in Companion. With the standalone
 Configure the OneDrive key and secret in Companion. With the standalone
 Companion server, specify environment variables:
 Companion server, specify environment variables:

+ 7 - 0
docs/uppy-core.mdx

@@ -701,6 +701,13 @@ that upload.
 uppy.removeFile('uppyteamkongjpg1501851828779');
 uppy.removeFile('uppyteamkongjpg1501851828779');
 ```
 ```
 
 
+#### `clear()`
+
+Clear the state. Can be useful for manually resetting Uppy after a successful
+upload.
+
+Upload plugins may choose to throw an error if called during an upload.
+
 #### `getFile(fileID)`
 #### `getFile(fileID)`
 
 
 Get a specific [Uppy file](#working-with-uppy-files) by its ID.
 Get a specific [Uppy file](#working-with-uppy-files) by its ID.

+ 0 - 52
e2e/cypress/integration/dashboard-transloadit.spec.ts

@@ -209,58 +209,6 @@ describe('Dashboard with Transloadit', () => {
     })
     })
   })
   })
 
 
-  // Not working, the upstream changes have not landed yet.
-  it.skip('should create assembly if there is still one file to upload', () => {
-    cy.get('@file-input').selectFile(
-      [
-        'cypress/fixtures/images/cat.jpg',
-        'cypress/fixtures/images/traffic.jpg',
-      ],
-      { force: true },
-    )
-    cy.get('.uppy-StatusBar-actionBtn--upload').click()
-
-    cy.window().then(({ uppy }) => {
-      // eslint-disable-next-line
-      // @ts-ignore fix me
-      expect(
-        Object.values(uppy.getPlugin('Transloadit').activeAssemblies).length,
-      ).to.equal(0)
-
-      const { files } = uppy.getState()
-      const [fileID] = Object.keys(files)
-      uppy.removeFile(fileID)
-
-      cy.wait('@createAssemblies').then(() => {
-        cy.wait('@resumable')
-        cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-      })
-    })
-  })
-
-  // Not working, the upstream changes have not landed yet.
-  it.skip('should complete upload if one gets cancelled mid-flight', () => {
-    cy.get('@file-input').selectFile(
-      [
-        'cypress/fixtures/images/cat.jpg',
-        'cypress/fixtures/images/traffic.jpg',
-      ],
-      { force: true },
-    )
-    cy.get('.uppy-StatusBar-actionBtn--upload').click()
-
-    cy.wait('@createAssemblies')
-    cy.wait('@resumable')
-
-    cy.window().then(({ uppy }) => {
-      const { files } = uppy.getState()
-      const [fileID] = Object.keys(files)
-      uppy.removeFile(fileID)
-
-      cy.get('.uppy-StatusBar-statusPrimary').should('contain', 'Complete')
-    })
-  })
-
   it('should not emit error if upload is cancelled right away', () => {
   it('should not emit error if upload is cancelled right away', () => {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', {
     cy.get('@file-input').selectFile('cypress/fixtures/images/cat.jpg', {
       force: true,
       force: true,

+ 16 - 5
packages/@uppy/aws-s3/src/index.ts

@@ -689,9 +689,20 @@ export default class AwsS3Multipart<
 
 
         onProgress?.({ loaded: size, lengthComputable: true })
         onProgress?.({ loaded: size, lengthComputable: true })
 
 
-        // NOTE This must be allowed by CORS.
-        const etag = xhr.getResponseHeader('ETag')
-        const location = xhr.getResponseHeader('Location')
+        // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/getAllResponseHeaders#examples
+        const arr = xhr
+          .getAllResponseHeaders()
+          .trim()
+          .split(/[\r\n]+/)
+        // @ts-expect-error null is allowed to avoid inherited properties
+        const headersMap: Record<string, string> = { __proto__: null }
+        for (const line of arr) {
+          const parts = line.split(': ')
+          const header = parts.shift()!
+          const value = parts.join(': ')
+          headersMap[header] = value
+        }
+        const { etag, location } = headersMap
 
 
         if (method.toUpperCase() === 'POST' && location === null) {
         if (method.toUpperCase() === 'POST' && location === null) {
           // Not being able to read the Location header is not a fatal error.
           // Not being able to read the Location header is not a fatal error.
@@ -711,8 +722,8 @@ export default class AwsS3Multipart<
 
 
         onComplete?.(etag)
         onComplete?.(etag)
         resolve({
         resolve({
-          ETag: etag,
-          ...(location ? { location } : undefined),
+          ...headersMap,
+          ETag: etag, // keep capitalised ETag for backwards compatiblity
         })
         })
       })
       })
 
 

+ 0 - 1
packages/@uppy/companion/package.json

@@ -103,7 +103,6 @@
   "scripts": {
   "scripts": {
     "build": "tsc -p .",
     "build": "tsc -p .",
     "deploy": "kubectl apply -f infra/kube/companion-kube.yml",
     "deploy": "kubectl apply -f infra/kube/companion-kube.yml",
-    "prepublishOnly": "yarn run build",
     "start": "node ./lib/standalone/start-server.js",
     "start": "node ./lib/standalone/start-server.js",
     "test": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand"
     "test": "NODE_OPTIONS=--experimental-vm-modules jest --runInBand"
   },
   },

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

@@ -56,6 +56,14 @@ Included in: Uppy v4.0.0-beta.1
 - @uppy/core: various type fixes (Antoine du Hamel / #4995)
 - @uppy/core: various type fixes (Antoine du Hamel / #4995)
 - @uppy/core,@uppy/provider-views: Fix breadcrumbs (Evgenia Karunus / #4986)
 - @uppy/core,@uppy/provider-views: Fix breadcrumbs (Evgenia Karunus / #4986)
 
 
+## 3.12.0
+
+Released: 2024-06-04
+Included in: Uppy v3.26.0
+
+- @uppy/core: check capabilities in clearUploadedFiles (Merlijn Vos / #5201)
+- @uppy/core: PartialTree - change the `maxTotalFileSize` error (Evgenia Karunus / #5203)
+
 ## 3.11.3
 ## 3.11.3
 
 
 Released: 2024-05-14
 Released: 2024-05-14

+ 9 - 17
packages/@uppy/core/src/Restricter.ts

@@ -98,25 +98,17 @@ class Restricter<M extends Meta, B extends Body> {
     }
     }
 
 
     if (maxTotalFileSize) {
     if (maxTotalFileSize) {
-      let totalFilesSize = existingFiles.reduce(
-        (total, f) => (total + (f.size ?? 0)) as number,
+      const totalFilesSize = [...existingFiles, ...addingFiles].reduce(
+        (total, f) => total + (f.size ?? 0),
         0,
         0,
       )
       )
-
-      for (const addingFile of addingFiles) {
-        if (addingFile.size != null) {
-          // We can't check maxTotalFileSize if the size is unknown.
-          totalFilesSize += addingFile.size
-
-          if (totalFilesSize > maxTotalFileSize) {
-            throw new RestrictionError(
-              this.getI18n()('exceedsSize', {
-                size: prettierBytes(maxTotalFileSize),
-                file: addingFile.name,
-              }),
-            )
-          }
-        }
+      if (totalFilesSize > maxTotalFileSize) {
+        throw new RestrictionError(
+          this.getI18n()('aggregateExceedsSize', {
+            sizeAllowed: prettierBytes(maxTotalFileSize),
+            size: prettierBytes(totalFilesSize),
+          }),
+        )
       }
       }
     }
     }
   }
   }

+ 5 - 3
packages/@uppy/core/src/Uppy.test.ts

@@ -492,7 +492,7 @@ describe('src/Core', () => {
 
 
     assert.throws(
     assert.throws(
       () => core.removeFile(fileIDs[0]),
       () => core.removeFile(fileIDs[0]),
-      /individualCancellation is disabled/,
+      /The installed uploader plugin does not allow removing files during an upload/,
     )
     )
 
 
     expect(core.getState().currentUploads[id]).toBeDefined()
     expect(core.getState().currentUploads[id]).toBeDefined()
@@ -2135,7 +2135,7 @@ describe('src/Core', () => {
     it('should enforce the maxTotalFileSize rule', () => {
     it('should enforce the maxTotalFileSize rule', () => {
       const core = new Core({
       const core = new Core({
         restrictions: {
         restrictions: {
-          maxTotalFileSize: 34000,
+          maxTotalFileSize: 20000,
         },
         },
       })
       })
 
 
@@ -2154,7 +2154,9 @@ describe('src/Core', () => {
           data: testImage,
           data: testImage,
         })
         })
       }).toThrowError(
       }).toThrowError(
-        new Error('foo1.jpg exceeds maximum allowed size of 33 KB'),
+        new Error(
+          'You selected 34 KB of files, but maximum allowed size is 20 KB',
+        ),
       )
       )
     })
     })
 
 

+ 13 - 1
packages/@uppy/core/src/Uppy.ts

@@ -557,6 +557,16 @@ export class Uppy<M extends Meta, B extends Body> {
   }
   }
 
 
   clear(): void {
   clear(): void {
+    const { capabilities, currentUploads } = this.getState()
+    if (
+      Object.keys(currentUploads).length > 0 &&
+      !capabilities.individualCancellation
+    ) {
+      throw new Error(
+        'The installed uploader plugin does not allow removing files during an upload.',
+      )
+    }
+
     this.setState({ ...defaultUploadState, files: {} })
     this.setState({ ...defaultUploadState, files: {} })
   }
   }
 
 
@@ -1130,7 +1140,9 @@ export class Uppy<M extends Meta, B extends Body> {
         newFileIDs.length !== currentUploads[uploadID].fileIDs.length &&
         newFileIDs.length !== currentUploads[uploadID].fileIDs.length &&
         !capabilities.individualCancellation
         !capabilities.individualCancellation
       ) {
       ) {
-        throw new Error('individualCancellation is disabled')
+        throw new Error(
+          'The installed uploader plugin does not allow removing files during an upload.',
+        )
       }
       }
 
 
       updatedUploads[uploadID] = {
       updatedUploads[uploadID] = {

+ 2 - 0
packages/@uppy/core/src/locale.ts

@@ -12,6 +12,8 @@ export default {
       0: 'You have to select at least %{smart_count} file',
       0: 'You have to select at least %{smart_count} file',
       1: 'You have to select at least %{smart_count} files',
       1: 'You have to select at least %{smart_count} files',
     },
     },
+    aggregateExceedsSize:
+      'You selected %{size} of files, but maximum allowed size is %{sizeAllowed}',
     exceedsSize: '%{file} exceeds maximum allowed size of %{size}',
     exceedsSize: '%{file} exceeds maximum allowed size of %{size}',
     missingRequiredMetaField: 'Missing required meta fields',
     missingRequiredMetaField: 'Missing required meta fields',
     missingRequiredMetaFieldOnFile:
     missingRequiredMetaFieldOnFile:

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

@@ -20,6 +20,14 @@ Released: 2024-03-28
 Included in: Uppy v4.0.0-beta.1
 Included in: Uppy v4.0.0-beta.1
 
 
 - @uppy/transloadit: migrate to TS (Merlijn Vos / #4987)
 - @uppy/transloadit: migrate to TS (Merlijn Vos / #4987)
+
+## 3.7.0
+
+Released: 2024-06-04
+Included in: Uppy v3.26.0
+
+- @uppy/transloadit: remove `updateNumberOfFilesInAssembly` (Merlijn Vos / #5202)
+
 ## 3.6.2
 ## 3.6.2
 
 
 Released: 2024-05-23
 Released: 2024-05-23

+ 0 - 24
packages/@uppy/transloadit/src/Client.ts

@@ -170,30 +170,6 @@ export default class Client<M extends Meta, B extends Body> {
     )
     )
   }
   }
 
 
-  /**
-   * Update the number of expected files in an already created assembly.
-   */
-  async updateNumberOfFilesInAssembly(
-    assembly: AssemblyResponse,
-    num_expected_upload_files: number,
-  ): Promise<AssemblyResponse> {
-    const url = new URL(assembly.assembly_ssl_url)
-    url.pathname = '/update_assemblies'
-    const body = JSON.stringify({
-      assembly_updates: [
-        {
-          assembly_id: assembly.assembly_id,
-          num_expected_upload_files,
-        },
-      ],
-    })
-    return this.#fetchJSON(url, {
-      method: 'POST',
-      headers: this.#headers,
-      body,
-    }).catch((err) => this.#reportError(err, { url, type: 'API_ERROR' }))
-  }
-
   /**
   /**
    * Cancel a running Assembly.
    * Cancel a running Assembly.
    */
    */

+ 5 - 23
packages/@uppy/transloadit/src/index.ts

@@ -395,17 +395,10 @@ export default class Transloadit<
         const files = this.uppy
         const files = this.uppy
           .getFiles()
           .getFiles()
           .filter(({ id }) => fileIDs.includes(id))
           .filter(({ id }) => fileIDs.includes(id))
-        if (files.length !== fileIDs.length) {
-          if (files.length === 0) {
-            // All files have been removed, cancelling.
-            await this.client.cancelAssembly(newAssembly)
-            return null
-          }
-          // At least one file has been removed.
-          await this.client.updateNumberOfFilesInAssembly(
-            newAssembly,
-            files.length,
-          )
+        if (files.length === 0) {
+          // All files have been removed, cancelling.
+          await this.client.cancelAssembly(newAssembly)
+          return null
         }
         }
 
 
         const assembly = new Assembly(newAssembly, this.#rateLimitedQueue)
         const assembly = new Assembly(newAssembly, this.#rateLimitedQueue)
@@ -441,22 +434,11 @@ export default class Transloadit<
         // TODO: this should not live inside a `file-removed` event but somewhere more deterministic.
         // TODO: this should not live inside a `file-removed` event but somewhere more deterministic.
         // Such as inside the function where the assembly has succeeded or cancelled.
         // Such as inside the function where the assembly has succeeded or cancelled.
         // For the use case of cancelling the assembly when needed, we should try to do that with just `cancel-all`.
         // For the use case of cancelling the assembly when needed, we should try to do that with just `cancel-all`.
-        const fileRemovedHandler = (fileRemoved: UppyFile<M, B>) => {
+        const fileRemovedHandler = () => {
           // If the assembly has successfully completed, we do not need these checks.
           // If the assembly has successfully completed, we do not need these checks.
           // Otherwise we may cancel an assembly after it already succeeded
           // Otherwise we may cancel an assembly after it already succeeded
           if (assembly.status?.ok === 'ASSEMBLY_COMPLETED') {
           if (assembly.status?.ok === 'ASSEMBLY_COMPLETED') {
             this.uppy.off('file-removed', fileRemovedHandler)
             this.uppy.off('file-removed', fileRemovedHandler)
-            return
-          }
-          if (fileRemoved.id in updatedFiles) {
-            delete updatedFiles[fileRemoved.id]
-            const nbOfRemainingFiles = Object.keys(updatedFiles).length
-
-            this.client
-              .updateNumberOfFilesInAssembly(newAssembly, nbOfRemainingFiles)
-              .catch(() => {
-                /* ignore potential errors */
-              })
           }
           }
         }
         }
         this.uppy.on('file-removed', fileRemovedHandler)
         this.uppy.on('file-removed', fileRemovedHandler)

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

@@ -15,6 +15,13 @@ Included in: Uppy v4.0.0-beta.1
 - @uppy/audio,@uppy/dashboard,@uppy/drop-target,@uppy/webcam: add missing exports (Antoine du Hamel / #5014)
 - @uppy/audio,@uppy/dashboard,@uppy/drop-target,@uppy/webcam: add missing exports (Antoine du Hamel / #5014)
 - @uppy/webcam: refactor to TypeScript (Antoine du Hamel / #4870)
 - @uppy/webcam: refactor to TypeScript (Antoine du Hamel / #4870)
 
 
+## 3.4.2
+
+Released: 2024-06-04
+Included in: Uppy v3.26.0
+
+- @uppy/webcam: add missing types for `recordedVideo` (Antoine du Hamel / #5208)
+
 ## 3.4.0
 ## 3.4.0
 
 
 Released: 2024-03-27
 Released: 2024-03-27

+ 1 - 1
packages/@uppy/webcam/src/CameraScreen.tsx

@@ -21,6 +21,7 @@ interface CameraScreenProps extends VideoSourceSelectProps {
 
 
   src: MediaStream | null
   src: MediaStream | null
   recording: boolean
   recording: boolean
+  recordedVideo: string | null
   modes: string[]
   modes: string[]
   supportsRecording: boolean
   supportsRecording: boolean
   showVideoSourceDropdown: boolean
   showVideoSourceDropdown: boolean
@@ -53,7 +54,6 @@ class CameraScreen extends Component<CameraScreenProps> {
   render(): ComponentChild {
   render(): ComponentChild {
     const {
     const {
       src,
       src,
-      // @ts-expect-error TODO: remove unused
       recordedVideo,
       recordedVideo,
       recording,
       recording,
       modes,
       modes,

+ 1 - 0
packages/@uppy/webcam/src/Webcam.tsx

@@ -81,6 +81,7 @@ interface WebcamState {
   recordingLengthSeconds: number
   recordingLengthSeconds: number
   videoSources: MediaDeviceInfo[]
   videoSources: MediaDeviceInfo[]
   currentDeviceId: string | MediaStreamTrack | null | undefined
   currentDeviceId: string | MediaStreamTrack | null | undefined
+  recordedVideo: null | string
   isRecording: boolean
   isRecording: boolean
   [key: string]: unknown
   [key: string]: unknown
 }
 }