Преглед на файлове

@uppy/aws-s3: use default `Body` generic & export `AwsBody` (#5353)

Merlijn Vos преди 9 месеца
родител
ревизия
1ef39db22c

+ 30 - 0
docs/uploader/aws-s3-multipart.mdx

@@ -169,6 +169,36 @@ const uppy = new Uppy()
 	});
 ```
 
+### Use with TypeScript
+
+Uppy always puts the response to an upload in `file.response.body`. If you want
+this to be type safe with `@uppy/aws-s3`, you can import the `AwsBody` type and
+pass it as the second genric to `Uppy`.
+
+```ts {3,12} showLineNumbers
+import Uppy from '@uppy/core';
+import Dashboard from '@uppy/dashboard';
+import AwsS3, { type AwsBody } from '@uppy/aws-s3';
+
+import '@uppy/core/dist/style.min.css';
+import '@uppy/dashboard/dist/style.min.css';
+
+// Set this to `any` or `Record<string, unknown>`
+// if you do not set any metadata yourself
+type Meta = { license: string };
+
+const uppy = new Uppy<Meta, AwsBody>()
+	.use(Dashboard, { inline: true, target: 'body' })
+	.use(AwsS3, { endpoint: '...' });
+
+const id = uppy.addFile(/* ... */);
+
+await uppy.upload();
+
+const body = uppy.getFile(id).response.body!;
+const { location } = body; // This is now type safe
+```
+
 ## API
 
 ### Options

+ 2 - 2
packages/@uppy/aws-s3/src/HTTPCommunicationQueue.ts

@@ -1,4 +1,4 @@
-import type { Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
+import type { Meta, Body, UppyFile } from '@uppy/utils/lib/UppyFile'
 import type {
   RateLimitedQueue,
   WrapPromiseFunctionType,
@@ -6,7 +6,7 @@ import type {
 import { pausingUploadReason, type Chunk } from './MultipartUploader.ts'
 import type AwsS3Multipart from './index.js'
 import { throwIfAborted } from './utils.ts'
-import type { Body, UploadPartBytesResult, UploadResult } from './utils.js'
+import type { UploadPartBytesResult, UploadResult } from './utils.js'
 import type { AwsS3MultipartOptions, uploadPartBytes } from './index.js'
 
 function removeMetadataFromURL(urlString: string) {

+ 1 - 2
packages/@uppy/aws-s3/src/MultipartUploader.ts

@@ -1,8 +1,7 @@
 import type { Uppy } from '@uppy/core'
 import { AbortController } from '@uppy/utils/lib/AbortController'
-import type { Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
+import type { Meta, Body, UppyFile } from '@uppy/utils/lib/UppyFile'
 import type { HTTPCommunicationQueue } from './HTTPCommunicationQueue.js'
-import type { Body } from './utils.js'
 
 const MB = 1024 * 1024
 

+ 20 - 26
packages/@uppy/aws-s3/src/index.test.ts

@@ -3,8 +3,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
 import 'whatwg-fetch'
 import nock from 'nock'
 import Core from '@uppy/core'
-import AwsS3Multipart from './index.ts'
-import type { Body } from './utils.js'
+import AwsS3Multipart, { type AwsBody } from './index.ts'
 
 const KB = 1024
 const MB = KB * KB
@@ -13,20 +12,18 @@ describe('AwsS3Multipart', () => {
   beforeEach(() => nock.disableNetConnect())
 
   it('Registers AwsS3Multipart upload plugin', () => {
-    const core = new Core<any, Body>()
-    core.use(AwsS3Multipart)
+    const core = new Core().use(AwsS3Multipart)
 
     // @ts-expect-error private property
     const pluginNames = core[Symbol.for('uppy test: getPlugins')](
       'uploader',
-    ).map((plugin: AwsS3Multipart<any, Body>) => plugin.constructor.name)
+    ).map((plugin: AwsS3Multipart<any, AwsBody>) => plugin.constructor.name)
     expect(pluginNames).toContain('AwsS3Multipart')
   })
 
   describe('companionUrl assertion', () => {
     it('Throws an error for main functions if configured without companionUrl', () => {
-      const core = new Core<any, Body>()
-      core.use(AwsS3Multipart)
+      const core = new Core().use(AwsS3Multipart)
       const awsS3Multipart = core.getPlugin('AwsS3Multipart')!
 
       const err = 'Expected a `endpoint` option'
@@ -47,8 +44,7 @@ describe('AwsS3Multipart', () => {
 
   describe('non-multipart upload', () => {
     it('should handle POST uploads', async () => {
-      const core = new Core<any, Body>()
-      core.use(AwsS3Multipart, {
+      const core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: false,
         limit: 0,
         getUploadParameters: () => ({
@@ -103,11 +99,11 @@ describe('AwsS3Multipart', () => {
   })
 
   describe('without companionUrl (custom main functions)', () => {
-    let core: Core<any, Body>
-    let awsS3Multipart: AwsS3Multipart<any, Body>
+    let core: Core<any, AwsBody>
+    let awsS3Multipart: AwsS3Multipart<any, AwsBody>
 
     beforeEach(() => {
-      core = new Core<any, Body>()
+      core = new Core<any, AwsBody>()
       core.use(AwsS3Multipart, {
         shouldUseMultipart: true,
         limit: 0,
@@ -254,7 +250,7 @@ describe('AwsS3Multipart', () => {
     })
 
     it('retries uploadPartBytes when it fails once', async () => {
-      const core = new Core<any, Body>().use(AwsS3Multipart, {
+      const core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: true,
         createMultipartUpload,
         completeMultipartUpload: vi.fn(async () => ({ location: 'test' })),
@@ -287,7 +283,7 @@ describe('AwsS3Multipart', () => {
     })
 
     it('calls `upload-error` when uploadPartBytes fails after all retries', async () => {
-      const core = new Core<any, Body>().use(AwsS3Multipart, {
+      const core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: true,
         retryDelays: [10],
         createMultipartUpload,
@@ -328,7 +324,7 @@ describe('AwsS3Multipart', () => {
     const newToken = 'new token'
 
     beforeEach(() => {
-      core = new Core<any, Body>()
+      core = new Core()
       core.use(AwsS3Multipart, {
         endpoint: '',
         headers: {
@@ -358,19 +354,17 @@ describe('AwsS3Multipart', () => {
   })
 
   describe('dynamic companionHeader using setOption', () => {
-    let core: Core<any, Body>
-    let awsS3Multipart: AwsS3Multipart<any, Body>
+    let core: Core<any, AwsBody>
+    let awsS3Multipart: AwsS3Multipart<any, AwsBody>
     const newToken = 'new token'
 
     it('companionHeader is updated before uploading file', async () => {
-      core = new Core<any, Body>()
+      core = new Core()
       core.use(AwsS3Multipart)
       /* Set up preprocessor */
       core.addPreProcessor(() => {
-        awsS3Multipart = core.getPlugin('AwsS3Multipart') as AwsS3Multipart<
-          any,
-          Body
-        >
+        awsS3Multipart =
+          core.getPlugin<AwsS3Multipart<any, AwsBody>>('AwsS3Multipart')!
         awsS3Multipart.setOptions({
           endpoint: 'http://localhost',
           headers: {
@@ -391,7 +385,7 @@ describe('AwsS3Multipart', () => {
   })
 
   describe('file metadata across custom main functions', () => {
-    let core: Core<any, Body>
+    let core: Core
     const createMultipartUpload = vi.fn((file) => {
       core.setFileMeta(file.id, {
         ...file.meta,
@@ -459,7 +453,7 @@ describe('AwsS3Multipart', () => {
     })
 
     it('preserves file metadata if upload is completed', async () => {
-      core = new Core<any, Body>().use(AwsS3Multipart, {
+      core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: true,
         createMultipartUpload,
         signPart,
@@ -520,7 +514,7 @@ describe('AwsS3Multipart', () => {
         }
       })
 
-      core = new Core<any, Body>().use(AwsS3Multipart, {
+      core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: true,
         createMultipartUpload,
         signPart: signPartWithAbort,
@@ -590,7 +584,7 @@ describe('AwsS3Multipart', () => {
         }
       })
 
-      core = new Core<any, Body>().use(AwsS3Multipart, {
+      core = new Core().use(AwsS3Multipart, {
         shouldUseMultipart: true,
         createMultipartUpload,
         signPart: signPartWithPause,

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

@@ -4,7 +4,7 @@ import BasePlugin, {
 } from '@uppy/core/lib/BasePlugin.js'
 import { RequestClient } from '@uppy/companion-client'
 import type { RequestOptions } from '@uppy/utils/lib/CompanionClientProvider'
-import type { Body as _Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
+import type { Body, Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
 import type { Uppy } from '@uppy/core'
 import EventManager from '@uppy/core/lib/EventManager.js'
 import { RateLimitedQueue } from '@uppy/utils/lib/RateLimitedQueue'
@@ -21,7 +21,6 @@ import type {
   UploadResultWithSignal,
   MultipartUploadResultWithSignal,
   UploadPartBytesResult,
-  Body,
 } from './utils.js'
 import createSignedURL from './createSignedURL.ts'
 import { HTTPCommunicationQueue } from './HTTPCommunicationQueue.ts'
@@ -33,13 +32,13 @@ interface MultipartFile<M extends Meta, B extends Body> extends UppyFile<M, B> {
   s3Multipart: UploadResult
 }
 
-type PartUploadedCallback<M extends Meta, B extends _Body> = (
+type PartUploadedCallback<M extends Meta, B extends Body> = (
   file: UppyFile<M, B>,
   part: { PartNumber: number; ETag: string },
 ) => void
 
 declare module '@uppy/core' {
-  export interface UppyEventMap<M extends Meta, B extends _Body> {
+  export interface UppyEventMap<M extends Meta, B extends Body> {
     's3-multipart:part-uploaded': PartUploadedCallback<M, B>
   }
 }
@@ -289,6 +288,8 @@ const defaultOptions = {
   retryDelays: [0, 1000, 3000, 5000],
 } satisfies Partial<AwsS3MultipartOptions<any, any>>
 
+export type { AwsBody } from './utils.ts'
+
 export default class AwsS3Multipart<
   M extends Meta,
   B extends Body,
@@ -839,7 +840,7 @@ export default class AwsS3Multipart<
             ...result,
           },
           status: 200,
-          uploadURL: result.location,
+          uploadURL: result.location as string,
         }
 
         this.resetUploaderReferences(file.id)

+ 2 - 2
packages/@uppy/aws-s3/src/utils.ts

@@ -1,5 +1,5 @@
 import { createAbortError } from '@uppy/utils/lib/AbortController'
-import type { Body as _Body } from '@uppy/utils/lib/UppyFile'
+import type { Body } from '@uppy/utils/lib/UppyFile'
 
 import type { AwsS3Part } from './index.js'
 
@@ -23,6 +23,6 @@ export type UploadPartBytesResult = {
   location?: string
 }
 
-export interface Body extends _Body {
+export interface AwsBody extends Body {
   location: string
 }