Skip to content

Commit c0bc1db

Browse files
committed
2 parents dfe388b + 5fcdd8f commit c0bc1db

File tree

84 files changed

+5669
-3256
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+5669
-3256
lines changed

packages/@uppy/aws-s3-multipart/src/HTTPCommunicationQueue.ts

+427
Large diffs are not rendered by default.

packages/@uppy/aws-s3-multipart/src/MultipartUploader.js renamed to packages/@uppy/aws-s3-multipart/src/MultipartUploader.ts

+97-56
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,53 @@
1+
import type { Uppy } from '@uppy/core'
12
import { AbortController } from '@uppy/utils/lib/AbortController'
3+
import type { Meta, UppyFile } from '@uppy/utils/lib/UppyFile'
4+
import type { HTTPCommunicationQueue } from './HTTPCommunicationQueue'
5+
import type { Body } from './utils'
26

37
const MB = 1024 * 1024
48

9+
interface MultipartUploaderOptions<M extends Meta, B extends Body> {
10+
getChunkSize?: (file: { size: number }) => number
11+
onProgress?: (bytesUploaded: number, bytesTotal: number) => void
12+
onPartComplete?: (part: { PartNumber: number; ETag: string }) => void
13+
shouldUseMultipart?: boolean | ((file: UppyFile<M, B>) => boolean)
14+
onSuccess?: (result: B) => void
15+
onError?: (err: unknown) => void
16+
companionComm: HTTPCommunicationQueue<M, B>
17+
file: UppyFile<M, B>
18+
log: Uppy<M, B>['log']
19+
20+
uploadId: string
21+
key: string
22+
}
23+
524
const defaultOptions = {
6-
getChunkSize (file) {
25+
getChunkSize(file: { size: number }) {
726
return Math.ceil(file.size / 10000)
827
},
9-
onProgress () {},
10-
onPartComplete () {},
11-
onSuccess () {},
12-
onError (err) {
28+
onProgress() {},
29+
onPartComplete() {},
30+
onSuccess() {},
31+
onError(err: unknown) {
1332
throw err
1433
},
34+
} satisfies Partial<MultipartUploaderOptions<any, any>>
35+
36+
export interface Chunk {
37+
getData: () => Blob
38+
onProgress: (ev: ProgressEvent) => void
39+
onComplete: (etag: string) => void
40+
shouldUseMultipart: boolean
41+
setAsUploaded?: () => void
1542
}
1643

17-
function ensureInt (value) {
44+
function ensureInt<T>(value: T): T extends number | string ? number : never {
1845
if (typeof value === 'string') {
46+
// @ts-expect-error TS is not able to recognize it's fine.
1947
return parseInt(value, 10)
2048
}
2149
if (typeof value === 'number') {
50+
// @ts-expect-error TS is not able to recognize it's fine.
2251
return value
2352
}
2453
throw new TypeError('Expected a number')
@@ -32,47 +61,41 @@ export const pausingUploadReason = Symbol('pausing upload, not an actual error')
3261
* (based on the user-provided `shouldUseMultipart` option value) and to manage
3362
* the chunk splitting.
3463
*/
35-
class MultipartUploader {
64+
class MultipartUploader<M extends Meta, B extends Body> {
65+
options: MultipartUploaderOptions<M, B> &
66+
Required<Pick<MultipartUploaderOptions<M, B>, keyof typeof defaultOptions>>
67+
3668
#abortController = new AbortController()
3769

38-
/** @type {import("../types/chunk").Chunk[]} */
39-
#chunks
70+
#chunks: Array<Chunk | null>
4071

41-
/** @type {{ uploaded: number, etag?: string, done?: boolean }[]} */
42-
#chunkState
72+
#chunkState: { uploaded: number; etag?: string; done?: boolean }[]
4373

4474
/**
4575
* The (un-chunked) data to upload.
46-
*
47-
* @type {Blob}
4876
*/
49-
#data
77+
#data: Blob
5078

51-
/** @type {import("@uppy/core").UppyFile} */
52-
#file
79+
#file: UppyFile<M, B>
5380

54-
/** @type {boolean} */
5581
#uploadHasStarted = false
5682

57-
/** @type {(err?: Error | any) => void} */
58-
#onError
83+
#onError: (err: unknown) => void
5984

60-
/** @type {() => void} */
61-
#onSuccess
85+
#onSuccess: (result: B) => void
6286

63-
/** @type {import('../types/index').AwsS3MultipartOptions["shouldUseMultipart"]} */
64-
#shouldUseMultipart
87+
#shouldUseMultipart: MultipartUploaderOptions<M, B>['shouldUseMultipart']
6588

66-
/** @type {boolean} */
67-
#isRestoring
89+
#isRestoring: boolean
6890

69-
#onReject = (err) => (err?.cause === pausingUploadReason ? null : this.#onError(err))
91+
#onReject = (err: unknown) =>
92+
(err as any)?.cause === pausingUploadReason ? null : this.#onError(err)
7093

7194
#maxMultipartParts = 10_000
7295

7396
#minPartSize = 5 * MB
7497

75-
constructor (data, options) {
98+
constructor(data: Blob, options: MultipartUploaderOptions<M, B>) {
7699
this.options = {
77100
...defaultOptions,
78101
...options,
@@ -89,7 +112,7 @@ class MultipartUploader {
89112
// When we are restoring an upload, we already have an UploadId and a Key. Otherwise
90113
// we need to call `createMultipartUpload` to get an `uploadId` and a `key`.
91114
// Non-multipart uploads are not restorable.
92-
this.#isRestoring = options.uploadId && options.key
115+
this.#isRestoring = (options.uploadId && options.key) as any as boolean
93116

94117
this.#initChunks()
95118
}
@@ -98,15 +121,19 @@ class MultipartUploader {
98121
// and calculates the optimal part size. When using multipart part uploads every part except for the last has
99122
// to be at least 5 MB and there can be no more than 10K parts.
100123
// This means we sometimes need to change the preferred part size from the user in order to meet these requirements.
101-
#initChunks () {
124+
#initChunks() {
102125
const fileSize = this.#data.size
103-
const shouldUseMultipart = typeof this.#shouldUseMultipart === 'function'
104-
? this.#shouldUseMultipart(this.#file)
126+
const shouldUseMultipart =
127+
typeof this.#shouldUseMultipart === 'function' ?
128+
this.#shouldUseMultipart(this.#file)
105129
: Boolean(this.#shouldUseMultipart)
106130

107131
if (shouldUseMultipart && fileSize > this.#minPartSize) {
108132
// At least 5MB per request:
109-
let chunkSize = Math.max(this.options.getChunkSize(this.#data), this.#minPartSize)
133+
let chunkSize = Math.max(
134+
this.options.getChunkSize(this.#data),
135+
this.#minPartSize,
136+
)
110137
let arraySize = Math.floor(fileSize / chunkSize)
111138

112139
// At most 10k requests per file:
@@ -132,41 +159,48 @@ class MultipartUploader {
132159
shouldUseMultipart,
133160
}
134161
if (this.#isRestoring) {
135-
const size = offset + chunkSize > fileSize ? fileSize - offset : chunkSize
162+
const size =
163+
offset + chunkSize > fileSize ? fileSize - offset : chunkSize
136164
// setAsUploaded is called by listPart, to keep up-to-date the
137165
// quantity of data that is left to actually upload.
138-
this.#chunks[j].setAsUploaded = () => {
166+
this.#chunks[j]!.setAsUploaded = () => {
139167
this.#chunks[j] = null
140168
this.#chunkState[j].uploaded = size
141169
}
142170
}
143171
}
144172
} else {
145-
this.#chunks = [{
146-
getData: () => this.#data,
147-
onProgress: this.#onPartProgress(0),
148-
onComplete: this.#onPartComplete(0),
149-
shouldUseMultipart,
150-
}]
173+
this.#chunks = [
174+
{
175+
getData: () => this.#data,
176+
onProgress: this.#onPartProgress(0),
177+
onComplete: this.#onPartComplete(0),
178+
shouldUseMultipart,
179+
},
180+
]
151181
}
152182

153183
this.#chunkState = this.#chunks.map(() => ({ uploaded: 0 }))
154184
}
155185

156-
#createUpload () {
157-
this
158-
.options.companionComm.uploadFile(this.#file, this.#chunks, this.#abortController.signal)
186+
#createUpload() {
187+
this.options.companionComm
188+
.uploadFile(
189+
this.#file,
190+
this.#chunks as Chunk[],
191+
this.#abortController.signal,
192+
)
159193
.then(this.#onSuccess, this.#onReject)
160194
this.#uploadHasStarted = true
161195
}
162196

163-
#resumeUpload () {
164-
this
165-
.options.companionComm.resumeUploadFile(this.#file, this.#chunks, this.#abortController.signal)
197+
#resumeUpload() {
198+
this.options.companionComm
199+
.resumeUploadFile(this.#file, this.#chunks, this.#abortController.signal)
166200
.then(this.#onSuccess, this.#onReject)
167201
}
168202

169-
#onPartProgress = (index) => (ev) => {
203+
#onPartProgress = (index: number) => (ev: ProgressEvent) => {
170204
if (!ev.lengthComputable) return
171205

172206
this.#chunkState[index].uploaded = ensureInt(ev.loaded)
@@ -175,7 +209,7 @@ class MultipartUploader {
175209
this.options.onProgress(totalUploaded, this.#data.size)
176210
}
177211

178-
#onPartComplete = (index) => (etag) => {
212+
#onPartComplete = (index: number) => (etag: string) => {
179213
// This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers.
180214
this.#chunks[index] = null
181215
this.#chunkState[index].etag = etag
@@ -188,37 +222,44 @@ class MultipartUploader {
188222
this.options.onPartComplete(part)
189223
}
190224

191-
#abortUpload () {
225+
#abortUpload() {
192226
this.#abortController.abort()
193-
this.options.companionComm.abortFileUpload(this.#file).catch((err) => this.options.log(err))
227+
this.options.companionComm
228+
.abortFileUpload(this.#file)
229+
.catch((err: unknown) => this.options.log(err as Error))
194230
}
195231

196-
start () {
232+
start(): void {
197233
if (this.#uploadHasStarted) {
198-
if (!this.#abortController.signal.aborted) this.#abortController.abort(pausingUploadReason)
234+
if (!this.#abortController.signal.aborted)
235+
this.#abortController.abort(pausingUploadReason)
199236
this.#abortController = new AbortController()
200237
this.#resumeUpload()
201238
} else if (this.#isRestoring) {
202-
this.options.companionComm.restoreUploadFile(this.#file, { uploadId: this.options.uploadId, key: this.options.key })
239+
this.options.companionComm.restoreUploadFile(this.#file, {
240+
uploadId: this.options.uploadId,
241+
key: this.options.key,
242+
})
203243
this.#resumeUpload()
204244
} else {
205245
this.#createUpload()
206246
}
207247
}
208248

209-
pause () {
249+
pause(): void {
210250
this.#abortController.abort(pausingUploadReason)
211251
// Swap it out for a new controller, because this instance may be resumed later.
212252
this.#abortController = new AbortController()
213253
}
214254

215-
abort (opts = undefined) {
255+
abort(opts?: { really?: boolean }): void {
216256
if (opts?.really) this.#abortUpload()
217257
else this.pause()
218258
}
219259

220260
// TODO: remove this in the next major
221-
get chunkState () {
261+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
262+
get chunkState() {
222263
return this.#chunkState
223264
}
224265
}

packages/@uppy/aws-s3-multipart/src/createSignedURL.test.js

-77
This file was deleted.

0 commit comments

Comments
 (0)