Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

@uppy/aws-s3: remove legacy plugin #5070

Merged
merged 3 commits into from Apr 11, 2024
Merged

@uppy/aws-s3: remove legacy plugin #5070

merged 3 commits into from Apr 11, 2024

Conversation

aduh95
Copy link
Member

@aduh95 aduh95 commented Apr 10, 2024

We still keep @uppy/aws-s3-multipart to point to @uppy/aws-s3, and we should npm deprecate it when Uppy 4.0.0 is released.

Copy link
Contributor

Diff output files
diff --git a/packages/@uppy/aws-s3-multipart/lib/index.js b/packages/@uppy/aws-s3-multipart/lib/index.js
index 234780a..23eb6fd 100644
--- a/packages/@uppy/aws-s3-multipart/lib/index.js
+++ b/packages/@uppy/aws-s3-multipart/lib/index.js
@@ -1,677 +1,2 @@
-let _Symbol$for;
-function _classPrivateFieldLooseBase(receiver, privateKey) {
-  if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) {
-    throw new TypeError("attempted to use private field on non-instance");
-  }
-  return receiver;
-}
-var id = 0;
-function _classPrivateFieldLooseKey(name) {
-  return "__private_" + id++ + "_" + name;
-}
-import { RequestClient } from "@uppy/companion-client";
-import BasePlugin from "@uppy/core/lib/BasePlugin.js";
-import EventManager from "@uppy/core/lib/EventManager.js";
-import { createAbortError } from "@uppy/utils/lib/AbortController";
-import { filterFilesToEmitUploadStarted, filterNonFailedFiles } from "@uppy/utils/lib/fileFilters";
-import getAllowedMetaFields from "@uppy/utils/lib/getAllowedMetaFields";
-import { RateLimitedQueue } from "@uppy/utils/lib/RateLimitedQueue";
-import createSignedURL from "./createSignedURL.js";
-import { HTTPCommunicationQueue } from "./HTTPCommunicationQueue.js";
-import MultipartUploader from "./MultipartUploader.js";
-import { throwIfAborted } from "./utils.js";
-const packageJson = {
-  "version": "4.0.0-beta.1",
-};
-function assertServerError(res) {
-  if (res != null && res.error) {
-    const error = new Error(res.message);
-    Object.assign(error, res.error);
-    throw error;
-  }
-  return res;
-}
-function getExpiry(credentials) {
-  const expirationDate = credentials.Expiration;
-  if (expirationDate) {
-    const timeUntilExpiry = Math.floor((new Date(expirationDate) - Date.now()) / 1000);
-    if (timeUntilExpiry > 9) {
-      return timeUntilExpiry;
-    }
-  }
-  return undefined;
-}
-function getAllowedMetadata(_ref) {
-  let {
-    meta,
-    allowedMetaFields,
-    querify = false,
-  } = _ref;
-  const metaFields = allowedMetaFields != null ? allowedMetaFields : Object.keys(meta);
-  if (!meta) return {};
-  return Object.fromEntries(
-    metaFields.filter(key => meta[key] != null).map(key => {
-      const realKey = querify ? `metadata[${key}]` : key;
-      const value = String(meta[key]);
-      return [realKey, value];
-    }),
-  );
-}
-const defaultOptions = {
-  allowedMetaFields: true,
-  limit: 6,
-  getTemporarySecurityCredentials: false,
-  shouldUseMultipart: file => file.size !== 0,
-  retryDelays: [0, 1000, 3000, 5000],
-  companionHeaders: {},
-};
-var _companionCommunicationQueue = _classPrivateFieldLooseKey("companionCommunicationQueue");
-var _client = _classPrivateFieldLooseKey("client");
-var _cachedTemporaryCredentials = _classPrivateFieldLooseKey("cachedTemporaryCredentials");
-var _getTemporarySecurityCredentials = _classPrivateFieldLooseKey("getTemporarySecurityCredentials");
-var _setS3MultipartState = _classPrivateFieldLooseKey("setS3MultipartState");
-var _getFile = _classPrivateFieldLooseKey("getFile");
-var _uploadLocalFile = _classPrivateFieldLooseKey("uploadLocalFile");
-var _getCompanionClientArgs = _classPrivateFieldLooseKey("getCompanionClientArgs");
-var _upload = _classPrivateFieldLooseKey("upload");
-var _setCompanionHeaders = _classPrivateFieldLooseKey("setCompanionHeaders");
-var _setResumableUploadsCapability = _classPrivateFieldLooseKey("setResumableUploadsCapability");
-var _resetResumableCapability = _classPrivateFieldLooseKey("resetResumableCapability");
-_Symbol$for = Symbol.for("uppy test: getClient");
-export default class AwsS3Multipart extends BasePlugin {
-  constructor(uppy, opts) {
-    var _rateLimitedQueue;
-    super(uppy, {
-      ...defaultOptions,
-      uploadPartBytes: AwsS3Multipart.uploadPartBytes,
-      createMultipartUpload: null,
-      listParts: null,
-      abortMultipartUpload: null,
-      completeMultipartUpload: null,
-      signPart: null,
-      getUploadParameters: null,
-      ...opts,
-    });
-    Object.defineProperty(this, _getCompanionClientArgs, {
-      value: _getCompanionClientArgs2,
-    });
-    Object.defineProperty(this, _uploadLocalFile, {
-      value: _uploadLocalFile2,
-    });
-    Object.defineProperty(this, _getTemporarySecurityCredentials, {
-      value: _getTemporarySecurityCredentials2,
-    });
-    Object.defineProperty(this, _companionCommunicationQueue, {
-      writable: true,
-      value: void 0,
-    });
-    Object.defineProperty(this, _client, {
-      writable: true,
-      value: void 0,
-    });
-    Object.defineProperty(this, _cachedTemporaryCredentials, {
-      writable: true,
-      value: void 0,
-    });
-    Object.defineProperty(this, _setS3MultipartState, {
-      writable: true,
-      value: (file, _ref2) => {
-        let {
-          key,
-          uploadId,
-        } = _ref2;
-        const cFile = this.uppy.getFile(file.id);
-        if (cFile == null) {
-          return;
-        }
-        this.uppy.setFileState(file.id, {
-          s3Multipart: {
-            ...cFile.s3Multipart,
-            key,
-            uploadId,
-          },
-        });
-      },
-    });
-    Object.defineProperty(this, _getFile, {
-      writable: true,
-      value: file => {
-        return this.uppy.getFile(file.id) || file;
-      },
-    });
-    Object.defineProperty(this, _upload, {
-      writable: true,
-      value: async fileIDs => {
-        if (fileIDs.length === 0) return undefined;
-        const files = this.uppy.getFilesByIds(fileIDs);
-        const filesFiltered = filterNonFailedFiles(files);
-        const filesToEmit = filterFilesToEmitUploadStarted(filesFiltered);
-        this.uppy.emit("upload-start", filesToEmit);
-        const promises = filesFiltered.map(file => {
-          if (file.isRemote) {
-            const getQueue = () => this.requests;
-            _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](false);
-            const controller = new AbortController();
-            const removedHandler = removedFile => {
-              if (removedFile.id === file.id) controller.abort();
-            };
-            this.uppy.on("file-removed", removedHandler);
-            const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
-              file,
-              _classPrivateFieldLooseBase(this, _getCompanionClientArgs)[_getCompanionClientArgs](file),
-              {
-                signal: controller.signal,
-                getQueue,
-              },
-            );
-            this.requests.wrapSyncFunction(() => {
-              this.uppy.off("file-removed", removedHandler);
-            }, {
-              priority: -1,
-            })();
-            return uploadPromise;
-          }
-          return _classPrivateFieldLooseBase(this, _uploadLocalFile)[_uploadLocalFile](file);
-        });
-        const upload = await Promise.all(promises);
-        _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
-        return upload;
-      },
-    });
-    Object.defineProperty(this, _setCompanionHeaders, {
-      writable: true,
-      value: () => {
-        _classPrivateFieldLooseBase(this, _client)[_client].setCompanionHeaders(this.opts.companionHeaders);
-      },
-    });
-    Object.defineProperty(this, _setResumableUploadsCapability, {
-      writable: true,
-      value: boolean => {
-        const {
-          capabilities,
-        } = this.uppy.getState();
-        this.uppy.setState({
-          capabilities: {
-            ...capabilities,
-            resumableUploads: boolean,
-          },
-        });
-      },
-    });
-    Object.defineProperty(this, _resetResumableCapability, {
-      writable: true,
-      value: () => {
-        _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
-      },
-    });
-    this.type = "uploader";
-    this.id = this.opts.id || "AwsS3Multipart";
-    this.title = "AWS S3 Multipart";
-    _classPrivateFieldLooseBase(this, _client)[_client] = new RequestClient(uppy, opts);
-    const dynamicDefaultOptions = {
-      createMultipartUpload: this.createMultipartUpload,
-      listParts: this.listParts,
-      abortMultipartUpload: this.abortMultipartUpload,
-      completeMultipartUpload: this.completeMultipartUpload,
-      signPart: opts != null && opts.getTemporarySecurityCredentials ? this.createSignedURL : this.signPart,
-      getUploadParameters: opts != null && opts.getTemporarySecurityCredentials
-        ? this.createSignedURL
-        : this.getUploadParameters,
-    };
-    for (const key of Object.keys(dynamicDefaultOptions)) {
-      if (this.opts[key] == null) {
-        this.opts[key] = dynamicDefaultOptions[key].bind(this);
-      }
-    }
-    if ((opts == null ? void 0 : opts.prepareUploadParts) != null && opts.signPart == null) {
-      this.opts.signPart = async (file, _ref3) => {
-        let {
-          uploadId,
-          key,
-          partNumber,
-          body,
-          signal,
-        } = _ref3;
-        const {
-          presignedUrls,
-          headers,
-        } = await opts.prepareUploadParts(file, {
-          uploadId,
-          key,
-          parts: [{
-            number: partNumber,
-            chunk: body,
-          }],
-          signal,
-        });
-        return {
-          url: presignedUrls == null ? void 0 : presignedUrls[partNumber],
-          headers: headers == null ? void 0 : headers[partNumber],
-        };
-      };
-    }
-    this.requests = (_rateLimitedQueue = this.opts.rateLimitedQueue) != null
-      ? _rateLimitedQueue
-      : new RateLimitedQueue(this.opts.limit);
-    _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue] =
-      new HTTPCommunicationQueue(
-        this.requests,
-        this.opts,
-        _classPrivateFieldLooseBase(this, _setS3MultipartState)[_setS3MultipartState],
-        _classPrivateFieldLooseBase(this, _getFile)[_getFile],
-      );
-    this.uploaders = Object.create(null);
-    this.uploaderEvents = Object.create(null);
-    this.uploaderSockets = Object.create(null);
-  }
-  [_Symbol$for]() {
-    return _classPrivateFieldLooseBase(this, _client)[_client];
-  }
-  setOptions(newOptions) {
-    _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue].setOptions(
-      newOptions,
-    );
-    super.setOptions(newOptions);
-    _classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]();
-  }
-  resetUploaderReferences(fileID, opts) {
-    if (this.uploaders[fileID]) {
-      this.uploaders[fileID].abort({
-        really: (opts == null ? void 0 : opts.abort) || false,
-      });
-      this.uploaders[fileID] = null;
-    }
-    if (this.uploaderEvents[fileID]) {
-      this.uploaderEvents[fileID].remove();
-      this.uploaderEvents[fileID] = null;
-    }
-    if (this.uploaderSockets[fileID]) {
-      this.uploaderSockets[fileID].close();
-      this.uploaderSockets[fileID] = null;
-    }
-  }
-  assertHost(method) {
-    if (!this.opts.companionUrl) {
-      throw new Error(
-        `Expected a \`companionUrl\` option containing a Companion address, or if you are not using Companion, a custom \`${method}\` implementation.`,
-      );
-    }
-  }
-  createMultipartUpload(file, signal) {
-    this.assertHost("createMultipartUpload");
-    throwIfAborted(signal);
-    const allowedMetaFields = getAllowedMetaFields(this.opts.allowedMetaFields, file.meta);
-    const metadata = getAllowedMetadata({
-      meta: file.meta,
-      allowedMetaFields,
-    });
-    return _classPrivateFieldLooseBase(this, _client)[_client].post("s3/multipart", {
-      filename: file.name,
-      type: file.type,
-      metadata,
-    }, {
-      signal,
-    }).then(assertServerError);
-  }
-  listParts(file, _ref4, oldSignal) {
-    var _signal;
-    let {
-      key,
-      uploadId,
-      signal,
-    } = _ref4;
-    (_signal = signal) != null ? _signal : signal = oldSignal;
-    this.assertHost("listParts");
-    throwIfAborted(signal);
-    const filename = encodeURIComponent(key);
-    return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/multipart/${uploadId}?key=${filename}`, {
-      signal,
-    }).then(assertServerError);
-  }
-  completeMultipartUpload(file, _ref5, oldSignal) {
-    var _signal2;
-    let {
-      key,
-      uploadId,
-      parts,
-      signal,
-    } = _ref5;
-    (_signal2 = signal) != null ? _signal2 : signal = oldSignal;
-    this.assertHost("completeMultipartUpload");
-    throwIfAborted(signal);
-    const filename = encodeURIComponent(key);
-    const uploadIdEnc = encodeURIComponent(uploadId);
-    return _classPrivateFieldLooseBase(this, _client)[_client].post(
-      `s3/multipart/${uploadIdEnc}/complete?key=${filename}`,
-      {
-        parts,
-      },
-      {
-        signal,
-      },
-    ).then(assertServerError);
-  }
-  async createSignedURL(file, options) {
-    const data = await _classPrivateFieldLooseBase(this, _getTemporarySecurityCredentials)
-      [_getTemporarySecurityCredentials](options);
-    const expires = getExpiry(data.credentials) || 604800;
-    const {
-      uploadId,
-      key,
-      partNumber,
-    } = options;
-    return {
-      method: "PUT",
-      expires,
-      fields: {},
-      url: `${await createSignedURL({
-        accountKey: data.credentials.AccessKeyId,
-        accountSecret: data.credentials.SecretAccessKey,
-        sessionToken: data.credentials.SessionToken,
-        expires,
-        bucketName: data.bucket,
-        Region: data.region,
-        Key: key != null ? key : `${crypto.randomUUID()}-${file.name}`,
-        uploadId,
-        partNumber,
-      })}`,
-      headers: {
-        "Content-Type": file.type,
-      },
-    };
-  }
-  signPart(file, _ref6) {
-    let {
-      uploadId,
-      key,
-      partNumber,
-      signal,
-    } = _ref6;
-    this.assertHost("signPart");
-    throwIfAborted(signal);
-    if (uploadId == null || key == null || partNumber == null) {
-      throw new Error("Cannot sign without a key, an uploadId, and a partNumber");
-    }
-    const filename = encodeURIComponent(key);
-    return _classPrivateFieldLooseBase(this, _client)[_client].get(
-      `s3/multipart/${uploadId}/${partNumber}?key=${filename}`,
-      {
-        signal,
-      },
-    ).then(assertServerError);
-  }
-  abortMultipartUpload(file, _ref7, oldSignal) {
-    var _signal3;
-    let {
-      key,
-      uploadId,
-      signal,
-    } = _ref7;
-    (_signal3 = signal) != null ? _signal3 : signal = oldSignal;
-    this.assertHost("abortMultipartUpload");
-    const filename = encodeURIComponent(key);
-    const uploadIdEnc = encodeURIComponent(uploadId);
-    return _classPrivateFieldLooseBase(this, _client)[_client].delete(
-      `s3/multipart/${uploadIdEnc}?key=${filename}`,
-      undefined,
-      {
-        signal,
-      },
-    ).then(assertServerError);
-  }
-  getUploadParameters(file, options) {
-    const {
-      meta,
-    } = file;
-    const {
-      type,
-      name: filename,
-    } = meta;
-    const allowedMetaFields = getAllowedMetaFields(this.opts.allowedMetaFields, file.meta);
-    const metadata = getAllowedMetadata({
-      meta,
-      allowedMetaFields,
-      querify: true,
-    });
-    const query = new URLSearchParams({
-      filename,
-      type,
-      ...metadata,
-    });
-    return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/params?${query}`, options);
-  }
-  static async uploadPartBytes(_ref8) {
-    let {
-      signature: {
-        url,
-        expires,
-        headers,
-        method = "PUT",
-      },
-      body,
-      size = body.size,
-      onProgress,
-      onComplete,
-      signal,
-    } = _ref8;
-    throwIfAborted(signal);
-    if (url == null) {
-      throw new Error("Cannot upload to an undefined URL");
-    }
-    return new Promise((resolve, reject) => {
-      const xhr = new XMLHttpRequest();
-      xhr.open(method, url, true);
-      if (headers) {
-        Object.keys(headers).forEach(key => {
-          xhr.setRequestHeader(key, headers[key]);
-        });
-      }
-      xhr.responseType = "text";
-      if (typeof expires === "number") {
-        xhr.timeout = expires * 1000;
-      }
-      function onabort() {
-        xhr.abort();
-      }
-      function cleanup() {
-        signal == null || signal.removeEventListener("abort", onabort);
-      }
-      signal == null || signal.addEventListener("abort", onabort);
-      xhr.upload.addEventListener("progress", ev => {
-        onProgress(ev);
-      });
-      xhr.addEventListener("abort", () => {
-        cleanup();
-        reject(createAbortError());
-      });
-      xhr.addEventListener("timeout", () => {
-        cleanup();
-        const error = new Error("Request has expired");
-        error.source = {
-          status: 403,
-        };
-        reject(error);
-      });
-      xhr.addEventListener("load", ev => {
-        cleanup();
-        if (xhr.status === 403 && xhr.responseText.includes("<Message>Request has expired</Message>")) {
-          const error = new Error("Request has expired");
-          error.source = xhr;
-          reject(error);
-          return;
-        }
-        if (xhr.status < 200 || xhr.status >= 300) {
-          const error = new Error("Non 2xx");
-          error.source = xhr;
-          reject(error);
-          return;
-        }
-        onProgress == null || onProgress({
-          loaded: size,
-          lengthComputable: true,
-        });
-        const etag = xhr.getResponseHeader("ETag");
-        const location = xhr.getResponseHeader("Location");
-        if (method.toUpperCase() === "POST" && location === null) {
-          console.warn(
-            "AwsS3/Multipart: Could not read the Location header. This likely means CORS is not configured correctly on the S3 Bucket. See https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.",
-          );
-        }
-        if (etag === null) {
-          reject(
-            new Error(
-              "AwsS3/Multipart: Could not read the ETag header. This likely means CORS is not configured correctly on the S3 Bucket. See https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.",
-            ),
-          );
-          return;
-        }
-        onComplete == null || onComplete(etag);
-        resolve({
-          ETag: etag,
-          ...(location
-            ? {
-              location,
-            }
-            : undefined),
-        });
-      });
-      xhr.addEventListener("error", ev => {
-        cleanup();
-        const error = new Error("Unknown error");
-        error.source = ev.target;
-        reject(error);
-      });
-      xhr.send(body);
-    });
-  }
-  install() {
-    _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
-    this.uppy.addPreProcessor(_classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]);
-    this.uppy.addUploader(_classPrivateFieldLooseBase(this, _upload)[_upload]);
-    this.uppy.on("cancel-all", _classPrivateFieldLooseBase(this, _resetResumableCapability)[_resetResumableCapability]);
-  }
-  uninstall() {
-    this.uppy.removePreProcessor(_classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]);
-    this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _upload)[_upload]);
-    this.uppy.off(
-      "cancel-all",
-      _classPrivateFieldLooseBase(this, _resetResumableCapability)[_resetResumableCapability],
-    );
-  }
-}
-async function _getTemporarySecurityCredentials2(options) {
-  throwIfAborted(options == null ? void 0 : options.signal);
-  if (_classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] == null) {
-    if (this.opts.getTemporarySecurityCredentials === true) {
-      this.assertHost("getTemporarySecurityCredentials");
-      _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] =
-        _classPrivateFieldLooseBase(this, _client)[_client].get("s3/sts", options).then(assertServerError);
-    } else {
-      _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] = this.opts
-        .getTemporarySecurityCredentials(options);
-    }
-    _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] =
-      await _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials];
-    setTimeout(
-      () => {
-        _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] = null;
-      },
-      (getExpiry(
-        _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials].credentials,
-      ) || 0) * 500,
-    );
-  }
-  return _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials];
-}
-function _uploadLocalFile2(file) {
-  var _this = this;
-  return new Promise((resolve, reject) => {
-    const onProgress = (bytesUploaded, bytesTotal) => {
-      this.uppy.emit("upload-progress", this.uppy.getFile(file.id), {
-        uploader: this,
-        bytesUploaded,
-        bytesTotal,
-      });
-    };
-    const onError = err => {
-      this.uppy.log(err);
-      this.uppy.emit("upload-error", file, err);
-      this.resetUploaderReferences(file.id);
-      reject(err);
-    };
-    const onSuccess = result => {
-      const uploadResp = {
-        body: {
-          ...result,
-        },
-        status: 200,
-        uploadURL: result.location,
-      };
-      this.resetUploaderReferences(file.id);
-      this.uppy.emit("upload-success", _classPrivateFieldLooseBase(this, _getFile)[_getFile](file), uploadResp);
-      if (result.location) {
-        this.uppy.log(`Download ${file.name} from ${result.location}`);
-      }
-      resolve();
-    };
-    const upload = new MultipartUploader(file.data, {
-      companionComm: _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue],
-      log: function() {
-        return _this.uppy.log(...arguments);
-      },
-      getChunkSize: this.opts.getChunkSize ? this.opts.getChunkSize.bind(this) : null,
-      onProgress,
-      onError,
-      onSuccess,
-      onPartComplete: part => {
-        this.uppy.emit("s3-multipart:part-uploaded", _classPrivateFieldLooseBase(this, _getFile)[_getFile](file), part);
-      },
-      file,
-      shouldUseMultipart: this.opts.shouldUseMultipart,
-      ...file.s3Multipart,
-    });
-    this.uploaders[file.id] = upload;
-    const eventManager = new EventManager(this.uppy);
-    this.uploaderEvents[file.id] = eventManager;
-    eventManager.onFileRemove(file.id, removed => {
-      upload.abort();
-      this.resetUploaderReferences(file.id, {
-        abort: true,
-      });
-      resolve(`upload ${removed} was removed`);
-    });
-    eventManager.onCancelAll(file.id, options => {
-      if ((options == null ? void 0 : options.reason) === "user") {
-        upload.abort();
-        this.resetUploaderReferences(file.id, {
-          abort: true,
-        });
-      }
-      resolve(`upload ${file.id} was canceled`);
-    });
-    eventManager.onFilePause(file.id, isPaused => {
-      if (isPaused) {
-        upload.pause();
-      } else {
-        upload.start();
-      }
-    });
-    eventManager.onPauseAll(file.id, () => {
-      upload.pause();
-    });
-    eventManager.onResumeAll(file.id, () => {
-      upload.start();
-    });
-    upload.start();
-  });
-}
-function _getCompanionClientArgs2(file) {
-  var _file$remote;
-  return {
-    ...((_file$remote = file.remote) == null ? void 0 : _file$remote.body),
-    protocol: "s3-multipart",
-    size: file.data.size,
-    metadata: file.meta,
-  };
-}
-AwsS3Multipart.VERSION = packageJson.version;
+export * from "@uppy/aws-s3";
+export { default } from "@uppy/aws-s3";
diff --git a/packages/@uppy/aws-s3/lib/index.js b/packages/@uppy/aws-s3/lib/index.js
index bc29690..234780a 100644
--- a/packages/@uppy/aws-s3/lib/index.js
+++ b/packages/@uppy/aws-s3/lib/index.js
@@ -9,309 +9,669 @@ var id = 0;
 function _classPrivateFieldLooseKey(name) {
   return "__private_" + id++ + "_" + name;
 }
-import AwsS3Multipart from "@uppy/aws-s3-multipart";
 import { RequestClient } from "@uppy/companion-client";
 import BasePlugin from "@uppy/core/lib/BasePlugin.js";
+import EventManager from "@uppy/core/lib/EventManager.js";
+import { createAbortError } from "@uppy/utils/lib/AbortController";
 import { filterFilesToEmitUploadStarted, filterNonFailedFiles } from "@uppy/utils/lib/fileFilters";
-import { internalRateLimitedQueue, RateLimitedQueue } from "@uppy/utils/lib/RateLimitedQueue";
+import getAllowedMetaFields from "@uppy/utils/lib/getAllowedMetaFields";
+import { RateLimitedQueue } from "@uppy/utils/lib/RateLimitedQueue";
+import createSignedURL from "./createSignedURL.js";
+import { HTTPCommunicationQueue } from "./HTTPCommunicationQueue.js";
+import MultipartUploader from "./MultipartUploader.js";
+import { throwIfAborted } from "./utils.js";
 const packageJson = {
   "version": "4.0.0-beta.1",
 };
-import isXml from "./isXml.js";
-import locale from "./locale.js";
-import MiniXHRUpload from "./MiniXHRUpload.js";
-function resolveUrl(origin, link) {
-  if (!origin && !link.startsWith("https://") && !link.startsWith("http://")) {
-    link = `https://${link}`;
-  }
-  return new URL(link, origin || undefined).toString();
-}
-function getXmlValue(source, tagName) {
-  const start = source.indexOf(`<${tagName}>`);
-  const end = source.indexOf(`</${tagName}>`, start);
-  return start !== -1 && end !== -1 ? source.slice(start + tagName.length + 2, end) : "";
-}
 function assertServerError(res) {
-  if (res && res.error) {
+  if (res != null && res.error) {
     const error = new Error(res.message);
     Object.assign(error, res.error);
     throw error;
   }
   return res;
 }
-function validateParameters(file, params) {
-  const valid = params != null && typeof params.url === "string"
-    && (typeof params.fields === "object" || params.fields == null);
-  if (!valid) {
-    const err = new TypeError(
-      `AwsS3: got incorrect result from 'getUploadParameters()' for file '${file.name}', expected an object '{ url, method, fields, headers }' but got '${
-        JSON.stringify(params)
-      }' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`,
-    );
-    throw err;
-  }
-  const methodIsValid = params.method == null || /^p(u|os)t$/i.test(params.method);
-  if (!methodIsValid) {
-    const err = new TypeError(
-      `AwsS3: got incorrect method from 'getUploadParameters()' for file '${file.name}', expected  'PUT' or 'POST' but got '${params.method}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`,
-    );
-    throw err;
+function getExpiry(credentials) {
+  const expirationDate = credentials.Expiration;
+  if (expirationDate) {
+    const timeUntilExpiry = Math.floor((new Date(expirationDate) - Date.now()) / 1000);
+    if (timeUntilExpiry > 9) {
+      return timeUntilExpiry;
+    }
   }
+  return undefined;
 }
-function defaultGetResponseError(content, xhr) {
-  if (!isXml(content, xhr)) {
-    return undefined;
-  }
-  const error = getXmlValue(content, "Message");
-  return new Error(error);
+function getAllowedMetadata(_ref) {
+  let {
+    meta,
+    allowedMetaFields,
+    querify = false,
+  } = _ref;
+  const metaFields = allowedMetaFields != null ? allowedMetaFields : Object.keys(meta);
+  if (!meta) return {};
+  return Object.fromEntries(
+    metaFields.filter(key => meta[key] != null).map(key => {
+      const realKey = querify ? `metadata[${key}]` : key;
+      const value = String(meta[key]);
+      return [realKey, value];
+    }),
+  );
 }
-let warnedSuccessActionStatus = false;
+const defaultOptions = {
+  allowedMetaFields: true,
+  limit: 6,
+  getTemporarySecurityCredentials: false,
+  shouldUseMultipart: file => file.size !== 0,
+  retryDelays: [0, 1000, 3000, 5000],
+  companionHeaders: {},
+};
+var _companionCommunicationQueue = _classPrivateFieldLooseKey("companionCommunicationQueue");
 var _client = _classPrivateFieldLooseKey("client");
-var _requests = _classPrivateFieldLooseKey("requests");
-var _uploader = _classPrivateFieldLooseKey("uploader");
-var _handleUpload = _classPrivateFieldLooseKey("handleUpload");
-var _setCompanionHeaders = _classPrivateFieldLooseKey("setCompanionHeaders");
+var _cachedTemporaryCredentials = _classPrivateFieldLooseKey("cachedTemporaryCredentials");
+var _getTemporarySecurityCredentials = _classPrivateFieldLooseKey("getTemporarySecurityCredentials");
+var _setS3MultipartState = _classPrivateFieldLooseKey("setS3MultipartState");
+var _getFile = _classPrivateFieldLooseKey("getFile");
+var _uploadLocalFile = _classPrivateFieldLooseKey("uploadLocalFile");
 var _getCompanionClientArgs = _classPrivateFieldLooseKey("getCompanionClientArgs");
+var _upload = _classPrivateFieldLooseKey("upload");
+var _setCompanionHeaders = _classPrivateFieldLooseKey("setCompanionHeaders");
+var _setResumableUploadsCapability = _classPrivateFieldLooseKey("setResumableUploadsCapability");
+var _resetResumableCapability = _classPrivateFieldLooseKey("resetResumableCapability");
 _Symbol$for = Symbol.for("uppy test: getClient");
-export default class AwsS3 extends BasePlugin {
-  constructor(uppy, _opts) {
-    if ((_opts == null ? void 0 : _opts.shouldUseMultipart) != null) {
-      return new AwsS3Multipart(uppy, _opts);
-    }
-    super(uppy, _opts);
-    Object.defineProperty(this, _client, {
+export default class AwsS3Multipart extends BasePlugin {
+  constructor(uppy, opts) {
+    var _rateLimitedQueue;
+    super(uppy, {
+      ...defaultOptions,
+      uploadPartBytes: AwsS3Multipart.uploadPartBytes,
+      createMultipartUpload: null,
+      listParts: null,
+      abortMultipartUpload: null,
+      completeMultipartUpload: null,
+      signPart: null,
+      getUploadParameters: null,
+      ...opts,
+    });
+    Object.defineProperty(this, _getCompanionClientArgs, {
+      value: _getCompanionClientArgs2,
+    });
+    Object.defineProperty(this, _uploadLocalFile, {
+      value: _uploadLocalFile2,
+    });
+    Object.defineProperty(this, _getTemporarySecurityCredentials, {
+      value: _getTemporarySecurityCredentials2,
+    });
+    Object.defineProperty(this, _companionCommunicationQueue, {
       writable: true,
       value: void 0,
     });
-    Object.defineProperty(this, _requests, {
+    Object.defineProperty(this, _client, {
       writable: true,
       value: void 0,
     });
-    Object.defineProperty(this, _uploader, {
+    Object.defineProperty(this, _cachedTemporaryCredentials, {
       writable: true,
       value: void 0,
     });
-    Object.defineProperty(this, _handleUpload, {
+    Object.defineProperty(this, _setS3MultipartState, {
       writable: true,
-      value: async fileIDs => {
-        const paramsPromises = Object.create(null);
-        function onremove(file) {
-          var _paramsPromises$id;
-          const {
-            id,
-          } = file;
-          (_paramsPromises$id = paramsPromises[id]) == null || _paramsPromises$id.abort();
+      value: (file, _ref2) => {
+        let {
+          key,
+          uploadId,
+        } = _ref2;
+        const cFile = this.uppy.getFile(file.id);
+        if (cFile == null) {
+          return;
         }
-        this.uppy.on("file-removed", onremove);
+        this.uppy.setFileState(file.id, {
+          s3Multipart: {
+            ...cFile.s3Multipart,
+            key,
+            uploadId,
+          },
+        });
+      },
+    });
+    Object.defineProperty(this, _getFile, {
+      writable: true,
+      value: file => {
+        return this.uppy.getFile(file.id) || file;
+      },
+    });
+    Object.defineProperty(this, _upload, {
+      writable: true,
+      value: async fileIDs => {
+        if (fileIDs.length === 0) return undefined;
         const files = this.uppy.getFilesByIds(fileIDs);
         const filesFiltered = filterNonFailedFiles(files);
         const filesToEmit = filterFilesToEmitUploadStarted(filesFiltered);
         this.uppy.emit("upload-start", filesToEmit);
-        const getUploadParameters = _classPrivateFieldLooseBase(this, _requests)[_requests].wrapPromiseFunction(
-          file => {
-            return this.opts.getUploadParameters(file);
-          },
-        );
-        const numberOfFiles = fileIDs.length;
-        return Promise.allSettled(fileIDs.map((id, index) => {
-          paramsPromises[id] = getUploadParameters(this.uppy.getFile(id));
-          return paramsPromises[id].then(params => {
-            delete paramsPromises[id];
-            const file = this.uppy.getFile(id);
-            validateParameters(file, params);
-            const {
-              method = "POST",
-              url,
-              fields,
-              headers,
-            } = params;
-            const xhrOpts = {
-              method,
-              formData: method.toUpperCase() === "POST",
-              endpoint: url,
-              allowedMetaFields: fields ? Object.keys(fields) : [],
+        const promises = filesFiltered.map(file => {
+          if (file.isRemote) {
+            const getQueue = () => this.requests;
+            _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](false);
+            const controller = new AbortController();
+            const removedHandler = removedFile => {
+              if (removedFile.id === file.id) controller.abort();
             };
-            if (headers) {
-              xhrOpts.headers = headers;
-            }
-            this.uppy.setFileState(file.id, {
-              meta: {
-                ...file.meta,
-                ...fields,
+            this.uppy.on("file-removed", removedHandler);
+            const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
+              file,
+              _classPrivateFieldLooseBase(this, _getCompanionClientArgs)[_getCompanionClientArgs](file),
+              {
+                signal: controller.signal,
+                getQueue,
               },
-              xhrUpload: xhrOpts,
-            });
-            return this.uploadFile(file.id, index, numberOfFiles);
-          }).catch(error => {
-            delete paramsPromises[id];
-            const file = this.uppy.getFile(id);
-            this.uppy.emit("upload-error", file, error);
-            return Promise.reject(error);
-          });
-        })).finally(() => {
-          this.uppy.off("file-removed", onremove);
+            );
+            this.requests.wrapSyncFunction(() => {
+              this.uppy.off("file-removed", removedHandler);
+            }, {
+              priority: -1,
+            })();
+            return uploadPromise;
+          }
+          return _classPrivateFieldLooseBase(this, _uploadLocalFile)[_uploadLocalFile](file);
         });
+        const upload = await Promise.all(promises);
+        _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
+        return upload;
       },
     });
     Object.defineProperty(this, _setCompanionHeaders, {
       writable: true,
       value: () => {
         _classPrivateFieldLooseBase(this, _client)[_client].setCompanionHeaders(this.opts.companionHeaders);
-        return Promise.resolve();
       },
     });
-    Object.defineProperty(this, _getCompanionClientArgs, {
+    Object.defineProperty(this, _setResumableUploadsCapability, {
       writable: true,
-      value: file => {
-        const opts = _classPrivateFieldLooseBase(this, _uploader)[_uploader].getOptions(file);
-        const allowedMetaFields = Array.isArray(opts.allowedMetaFields)
-          ? opts.allowedMetaFields
-          : Object.keys(file.meta);
-        return {
-          ...file.remote.body,
-          protocol: "multipart",
-          endpoint: opts.endpoint,
-          size: file.data.size,
-          fieldname: opts.fieldName,
-          metadata: Object.fromEntries(allowedMetaFields.map(name => [name, file.meta[name]])),
-          httpMethod: opts.method,
-          useFormData: opts.formData,
-          headers: typeof opts.headers === "function" ? opts.headers(file) : opts.headers,
-        };
+      value: boolean => {
+        const {
+          capabilities,
+        } = this.uppy.getState();
+        this.uppy.setState({
+          capabilities: {
+            ...capabilities,
+            resumableUploads: boolean,
+          },
+        });
+      },
+    });
+    Object.defineProperty(this, _resetResumableCapability, {
+      writable: true,
+      value: () => {
+        _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
       },
     });
     this.type = "uploader";
-    this.id = this.opts.id || "AwsS3";
-    this.title = "AWS S3";
-    this.defaultLocale = locale;
-    const defaultOptions = {
-      timeout: 30 * 1000,
-      limit: 0,
-      allowedMetaFields: [],
-      getUploadParameters: this.getUploadParameters.bind(this),
-      shouldUseMultipart: false,
-      companionHeaders: {},
-    };
-    this.opts = {
-      ...defaultOptions,
-      ..._opts,
+    this.id = this.opts.id || "AwsS3Multipart";
+    this.title = "AWS S3 Multipart";
+    _classPrivateFieldLooseBase(this, _client)[_client] = new RequestClient(uppy, opts);
+    const dynamicDefaultOptions = {
+      createMultipartUpload: this.createMultipartUpload,
+      listParts: this.listParts,
+      abortMultipartUpload: this.abortMultipartUpload,
+      completeMultipartUpload: this.completeMultipartUpload,
+      signPart: opts != null && opts.getTemporarySecurityCredentials ? this.createSignedURL : this.signPart,
+      getUploadParameters: opts != null && opts.getTemporarySecurityCredentials
+        ? this.createSignedURL
+        : this.getUploadParameters,
     };
-    if ((_opts == null ? void 0 : _opts.allowedMetaFields) === undefined && "metaFields" in this.opts) {
-      throw new Error("The `metaFields` option has been renamed to `allowedMetaFields`.");
+    for (const key of Object.keys(dynamicDefaultOptions)) {
+      if (this.opts[key] == null) {
+        this.opts[key] = dynamicDefaultOptions[key].bind(this);
+      }
     }
-    this.i18nInit();
-    _classPrivateFieldLooseBase(this, _client)[_client] = new RequestClient(uppy, _opts);
-    _classPrivateFieldLooseBase(this, _requests)[_requests] = new RateLimitedQueue(this.opts.limit);
+    if ((opts == null ? void 0 : opts.prepareUploadParts) != null && opts.signPart == null) {
+      this.opts.signPart = async (file, _ref3) => {
+        let {
+          uploadId,
+          key,
+          partNumber,
+          body,
+          signal,
+        } = _ref3;
+        const {
+          presignedUrls,
+          headers,
+        } = await opts.prepareUploadParts(file, {
+          uploadId,
+          key,
+          parts: [{
+            number: partNumber,
+            chunk: body,
+          }],
+          signal,
+        });
+        return {
+          url: presignedUrls == null ? void 0 : presignedUrls[partNumber],
+          headers: headers == null ? void 0 : headers[partNumber],
+        };
+      };
+    }
+    this.requests = (_rateLimitedQueue = this.opts.rateLimitedQueue) != null
+      ? _rateLimitedQueue
+      : new RateLimitedQueue(this.opts.limit);
+    _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue] =
+      new HTTPCommunicationQueue(
+        this.requests,
+        this.opts,
+        _classPrivateFieldLooseBase(this, _setS3MultipartState)[_setS3MultipartState],
+        _classPrivateFieldLooseBase(this, _getFile)[_getFile],
+      );
+    this.uploaders = Object.create(null);
+    this.uploaderEvents = Object.create(null);
+    this.uploaderSockets = Object.create(null);
   }
   [_Symbol$for]() {
     return _classPrivateFieldLooseBase(this, _client)[_client];
   }
-  get client() {
-    return _classPrivateFieldLooseBase(this, _client)[_client];
+  setOptions(newOptions) {
+    _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue].setOptions(
+      newOptions,
+    );
+    super.setOptions(newOptions);
+    _classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]();
   }
-  set client(client) {
-    _classPrivateFieldLooseBase(this, _client)[_client] = client;
+  resetUploaderReferences(fileID, opts) {
+    if (this.uploaders[fileID]) {
+      this.uploaders[fileID].abort({
+        really: (opts == null ? void 0 : opts.abort) || false,
+      });
+      this.uploaders[fileID] = null;
+    }
+    if (this.uploaderEvents[fileID]) {
+      this.uploaderEvents[fileID].remove();
+      this.uploaderEvents[fileID] = null;
+    }
+    if (this.uploaderSockets[fileID]) {
+      this.uploaderSockets[fileID].close();
+      this.uploaderSockets[fileID] = null;
+    }
   }
-  getUploadParameters(file) {
+  assertHost(method) {
     if (!this.opts.companionUrl) {
-      throw new Error("Expected a `companionUrl` option containing a Companion address.");
+      throw new Error(
+        `Expected a \`companionUrl\` option containing a Companion address, or if you are not using Companion, a custom \`${method}\` implementation.`,
+      );
     }
-    const filename = file.meta.name;
+  }
+  createMultipartUpload(file, signal) {
+    this.assertHost("createMultipartUpload");
+    throwIfAborted(signal);
+    const allowedMetaFields = getAllowedMetaFields(this.opts.allowedMetaFields, file.meta);
+    const metadata = getAllowedMetadata({
+      meta: file.meta,
+      allowedMetaFields,
+    });
+    return _classPrivateFieldLooseBase(this, _client)[_client].post("s3/multipart", {
+      filename: file.name,
+      type: file.type,
+      metadata,
+    }, {
+      signal,
+    }).then(assertServerError);
+  }
+  listParts(file, _ref4, oldSignal) {
+    var _signal;
+    let {
+      key,
+      uploadId,
+      signal,
+    } = _ref4;
+    (_signal = signal) != null ? _signal : signal = oldSignal;
+    this.assertHost("listParts");
+    throwIfAborted(signal);
+    const filename = encodeURIComponent(key);
+    return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/multipart/${uploadId}?key=${filename}`, {
+      signal,
+    }).then(assertServerError);
+  }
+  completeMultipartUpload(file, _ref5, oldSignal) {
+    var _signal2;
+    let {
+      key,
+      uploadId,
+      parts,
+      signal,
+    } = _ref5;
+    (_signal2 = signal) != null ? _signal2 : signal = oldSignal;
+    this.assertHost("completeMultipartUpload");
+    throwIfAborted(signal);
+    const filename = encodeURIComponent(key);
+    const uploadIdEnc = encodeURIComponent(uploadId);
+    return _classPrivateFieldLooseBase(this, _client)[_client].post(
+      `s3/multipart/${uploadIdEnc}/complete?key=${filename}`,
+      {
+        parts,
+      },
+      {
+        signal,
+      },
+    ).then(assertServerError);
+  }
+  async createSignedURL(file, options) {
+    const data = await _classPrivateFieldLooseBase(this, _getTemporarySecurityCredentials)
+      [_getTemporarySecurityCredentials](options);
+    const expires = getExpiry(data.credentials) || 604800;
+    const {
+      uploadId,
+      key,
+      partNumber,
+    } = options;
+    return {
+      method: "PUT",
+      expires,
+      fields: {},
+      url: `${await createSignedURL({
+        accountKey: data.credentials.AccessKeyId,
+        accountSecret: data.credentials.SecretAccessKey,
+        sessionToken: data.credentials.SessionToken,
+        expires,
+        bucketName: data.bucket,
+        Region: data.region,
+        Key: key != null ? key : `${crypto.randomUUID()}-${file.name}`,
+        uploadId,
+        partNumber,
+      })}`,
+      headers: {
+        "Content-Type": file.type,
+      },
+    };
+  }
+  signPart(file, _ref6) {
+    let {
+      uploadId,
+      key,
+      partNumber,
+      signal,
+    } = _ref6;
+    this.assertHost("signPart");
+    throwIfAborted(signal);
+    if (uploadId == null || key == null || partNumber == null) {
+      throw new Error("Cannot sign without a key, an uploadId, and a partNumber");
+    }
+    const filename = encodeURIComponent(key);
+    return _classPrivateFieldLooseBase(this, _client)[_client].get(
+      `s3/multipart/${uploadId}/${partNumber}?key=${filename}`,
+      {
+        signal,
+      },
+    ).then(assertServerError);
+  }
+  abortMultipartUpload(file, _ref7, oldSignal) {
+    var _signal3;
+    let {
+      key,
+      uploadId,
+      signal,
+    } = _ref7;
+    (_signal3 = signal) != null ? _signal3 : signal = oldSignal;
+    this.assertHost("abortMultipartUpload");
+    const filename = encodeURIComponent(key);
+    const uploadIdEnc = encodeURIComponent(uploadId);
+    return _classPrivateFieldLooseBase(this, _client)[_client].delete(
+      `s3/multipart/${uploadIdEnc}?key=${filename}`,
+      undefined,
+      {
+        signal,
+      },
+    ).then(assertServerError);
+  }
+  getUploadParameters(file, options) {
+    const {
+      meta,
+    } = file;
     const {
       type,
-    } = file.meta;
-    const metadata = Object.fromEntries(
-      this.opts.allowedMetaFields.filter(key => file.meta[key] != null).map(
-        key => [`metadata[${key}]`, file.meta[key].toString()],
-      ),
-    );
+      name: filename,
+    } = meta;
+    const allowedMetaFields = getAllowedMetaFields(this.opts.allowedMetaFields, file.meta);
+    const metadata = getAllowedMetadata({
+      meta,
+      allowedMetaFields,
+      querify: true,
+    });
     const query = new URLSearchParams({
       filename,
       type,
       ...metadata,
     });
-    return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/params?${query}`).then(assertServerError);
+    return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/params?${query}`, options);
   }
-  uploadFile(id, current, total) {
-    const file = this.uppy.getFile(id);
-    this.uppy.log(`uploading ${current} of ${total}`);
-    if (file.error) throw new Error(file.error);
-    if (file.isRemote) {
-      const getQueue = () => _classPrivateFieldLooseBase(this, _requests)[_requests];
-      const controller = new AbortController();
-      const removedHandler = removedFile => {
-        if (removedFile.id === file.id) controller.abort();
-      };
-      this.uppy.on("file-removed", removedHandler);
-      const uploadPromise = this.uppy.getRequestClientForFile(file).uploadRemoteFile(
-        file,
-        _classPrivateFieldLooseBase(this, _getCompanionClientArgs)[_getCompanionClientArgs](file),
-        {
-          signal: controller.signal,
-          getQueue,
-        },
-      );
-      _classPrivateFieldLooseBase(this, _requests)[_requests].wrapSyncFunction(() => {
-        this.uppy.off("file-removed", removedHandler);
-      }, {
-        priority: -1,
-      })();
-      return uploadPromise;
+  static async uploadPartBytes(_ref8) {
+    let {
+      signature: {
+        url,
+        expires,
+        headers,
+        method = "PUT",
+      },
+      body,
+      size = body.size,
+      onProgress,
+      onComplete,
+      signal,
+    } = _ref8;
+    throwIfAborted(signal);
+    if (url == null) {
+      throw new Error("Cannot upload to an undefined URL");
     }
-    return _classPrivateFieldLooseBase(this, _uploader)[_uploader].uploadLocalFile(file, current, total);
-  }
-  install() {
-    const {
-      uppy,
-    } = this;
-    uppy.addPreProcessor(_classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]);
-    uppy.addUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]);
-    function defaultGetResponseData(content, xhr) {
-      const opts = this;
-      if (!isXml(content, xhr)) {
-        if (opts.method.toUpperCase() === "POST") {
-          if (!warnedSuccessActionStatus) {
-            uppy.log(
-              "[AwsS3] No response data found, make sure to set the success_action_status AWS SDK option to 201. See https://uppy.io/docs/aws-s3/#POST-Uploads",
-              "warning",
-            );
-            warnedSuccessActionStatus = true;
-          }
-          return {
-            location: null,
-          };
+    return new Promise((resolve, reject) => {
+      const xhr = new XMLHttpRequest();
+      xhr.open(method, url, true);
+      if (headers) {
+        Object.keys(headers).forEach(key => {
+          xhr.setRequestHeader(key, headers[key]);
+        });
+      }
+      xhr.responseType = "text";
+      if (typeof expires === "number") {
+        xhr.timeout = expires * 1000;
+      }
+      function onabort() {
+        xhr.abort();
+      }
+      function cleanup() {
+        signal == null || signal.removeEventListener("abort", onabort);
+      }
+      signal == null || signal.addEventListener("abort", onabort);
+      xhr.upload.addEventListener("progress", ev => {
+        onProgress(ev);
+      });
+      xhr.addEventListener("abort", () => {
+        cleanup();
+        reject(createAbortError());
+      });
+      xhr.addEventListener("timeout", () => {
+        cleanup();
+        const error = new Error("Request has expired");
+        error.source = {
+          status: 403,
+        };
+        reject(error);
+      });
+      xhr.addEventListener("load", ev => {
+        cleanup();
+        if (xhr.status === 403 && xhr.responseText.includes("<Message>Request has expired</Message>")) {
+          const error = new Error("Request has expired");
+          error.source = xhr;
+          reject(error);
+          return;
         }
-        if (!xhr.responseURL) {
-          return {
-            location: null,
-          };
+        if (xhr.status < 200 || xhr.status >= 300) {
+          const error = new Error("Non 2xx");
+          error.source = xhr;
+          reject(error);
+          return;
         }
-        return {
-          location: xhr.responseURL.replace(/\?.*$/, ""),
-        };
-      }
-      return {
-        location: resolveUrl(xhr.responseURL, getXmlValue(content, "Location")),
-        bucket: getXmlValue(content, "Bucket"),
-        key: getXmlValue(content, "Key"),
-        etag: getXmlValue(content, "ETag"),
-      };
-    }
-    const xhrOptions = {
-      fieldName: "file",
-      responseUrlFieldName: "location",
-      timeout: this.opts.timeout,
-      [internalRateLimitedQueue]: _classPrivateFieldLooseBase(this, _requests)[_requests],
-      responseType: "text",
-      getResponseData: this.opts.getResponseData || defaultGetResponseData,
-      getResponseError: defaultGetResponseError,
-    };
-    xhrOptions.i18n = this.i18n;
-    _classPrivateFieldLooseBase(this, _uploader)[_uploader] = new MiniXHRUpload(uppy, xhrOptions);
+        onProgress == null || onProgress({
+          loaded: size,
+          lengthComputable: true,
+        });
+        const etag = xhr.getResponseHeader("ETag");
+        const location = xhr.getResponseHeader("Location");
+        if (method.toUpperCase() === "POST" && location === null) {
+          console.warn(
+            "AwsS3/Multipart: Could not read the Location header. This likely means CORS is not configured correctly on the S3 Bucket. See https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.",
+          );
+        }
+        if (etag === null) {
+          reject(
+            new Error(
+              "AwsS3/Multipart: Could not read the ETag header. This likely means CORS is not configured correctly on the S3 Bucket. See https://uppy.io/docs/aws-s3-multipart#S3-Bucket-Configuration for instructions.",
+            ),
+          );
+          return;
+        }
+        onComplete == null || onComplete(etag);
+        resolve({
+          ETag: etag,
+          ...(location
+            ? {
+              location,
+            }
+            : undefined),
+        });
+      });
+      xhr.addEventListener("error", ev => {
+        cleanup();
+        const error = new Error("Unknown error");
+        error.source = ev.target;
+        reject(error);
+      });
+      xhr.send(body);
+    });
+  }
+  install() {
+    _classPrivateFieldLooseBase(this, _setResumableUploadsCapability)[_setResumableUploadsCapability](true);
+    this.uppy.addPreProcessor(_classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]);
+    this.uppy.addUploader(_classPrivateFieldLooseBase(this, _upload)[_upload]);
+    this.uppy.on("cancel-all", _classPrivateFieldLooseBase(this, _resetResumableCapability)[_resetResumableCapability]);
   }
   uninstall() {
     this.uppy.removePreProcessor(_classPrivateFieldLooseBase(this, _setCompanionHeaders)[_setCompanionHeaders]);
-    this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]);
+    this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _upload)[_upload]);
+    this.uppy.off(
+      "cancel-all",
+      _classPrivateFieldLooseBase(this, _resetResumableCapability)[_resetResumableCapability],
+    );
   }
 }
-AwsS3.VERSION = packageJson.version;
+async function _getTemporarySecurityCredentials2(options) {
+  throwIfAborted(options == null ? void 0 : options.signal);
+  if (_classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] == null) {
+    if (this.opts.getTemporarySecurityCredentials === true) {
+      this.assertHost("getTemporarySecurityCredentials");
+      _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] =
+        _classPrivateFieldLooseBase(this, _client)[_client].get("s3/sts", options).then(assertServerError);
+    } else {
+      _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] = this.opts
+        .getTemporarySecurityCredentials(options);
+    }
+    _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] =
+      await _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials];
+    setTimeout(
+      () => {
+        _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials] = null;
+      },
+      (getExpiry(
+        _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials].credentials,
+      ) || 0) * 500,
+    );
+  }
+  return _classPrivateFieldLooseBase(this, _cachedTemporaryCredentials)[_cachedTemporaryCredentials];
+}
+function _uploadLocalFile2(file) {
+  var _this = this;
+  return new Promise((resolve, reject) => {
+    const onProgress = (bytesUploaded, bytesTotal) => {
+      this.uppy.emit("upload-progress", this.uppy.getFile(file.id), {
+        uploader: this,
+        bytesUploaded,
+        bytesTotal,
+      });
+    };
+    const onError = err => {
+      this.uppy.log(err);
+      this.uppy.emit("upload-error", file, err);
+      this.resetUploaderReferences(file.id);
+      reject(err);
+    };
+    const onSuccess = result => {
+      const uploadResp = {
+        body: {
+          ...result,
+        },
+        status: 200,
+        uploadURL: result.location,
+      };
+      this.resetUploaderReferences(file.id);
+      this.uppy.emit("upload-success", _classPrivateFieldLooseBase(this, _getFile)[_getFile](file), uploadResp);
+      if (result.location) {
+        this.uppy.log(`Download ${file.name} from ${result.location}`);
+      }
+      resolve();
+    };
+    const upload = new MultipartUploader(file.data, {
+      companionComm: _classPrivateFieldLooseBase(this, _companionCommunicationQueue)[_companionCommunicationQueue],
+      log: function() {
+        return _this.uppy.log(...arguments);
+      },
+      getChunkSize: this.opts.getChunkSize ? this.opts.getChunkSize.bind(this) : null,
+      onProgress,
+      onError,
+      onSuccess,
+      onPartComplete: part => {
+        this.uppy.emit("s3-multipart:part-uploaded", _classPrivateFieldLooseBase(this, _getFile)[_getFile](file), part);
+      },
+      file,
+      shouldUseMultipart: this.opts.shouldUseMultipart,
+      ...file.s3Multipart,
+    });
+    this.uploaders[file.id] = upload;
+    const eventManager = new EventManager(this.uppy);
+    this.uploaderEvents[file.id] = eventManager;
+    eventManager.onFileRemove(file.id, removed => {
+      upload.abort();
+      this.resetUploaderReferences(file.id, {
+        abort: true,
+      });
+      resolve(`upload ${removed} was removed`);
+    });
+    eventManager.onCancelAll(file.id, options => {
+      if ((options == null ? void 0 : options.reason) === "user") {
+        upload.abort();
+        this.resetUploaderReferences(file.id, {
+          abort: true,
+        });
+      }
+      resolve(`upload ${file.id} was canceled`);
+    });
+    eventManager.onFilePause(file.id, isPaused => {
+      if (isPaused) {
+        upload.pause();
+      } else {
+        upload.start();
+      }
+    });
+    eventManager.onPauseAll(file.id, () => {
+      upload.pause();
+    });
+    eventManager.onResumeAll(file.id, () => {
+      upload.start();
+    });
+    upload.start();
+  });
+}
+function _getCompanionClientArgs2(file) {
+  var _file$remote;
+  return {
+    ...((_file$remote = file.remote) == null ? void 0 : _file$remote.body),
+    protocol: "s3-multipart",
+    size: file.data.size,
+    metadata: file.meta,
+  };
+}
+AwsS3Multipart.VERSION = packageJson.version;

This comment was marked as off-topic.

@aduh95 aduh95 requested a review from Murderlon April 10, 2024 14:14
@aduh95 aduh95 merged commit 04839fd into 4.x Apr 11, 2024
20 checks passed
@aduh95 aduh95 deleted the aws-rename-remove branch April 11, 2024 10:10
This was referenced Apr 11, 2024
github-actions bot added a commit that referenced this pull request Apr 11, 2024
| Package                   |      Version | Package                   |      Version |
| ------------------------- | ------------ | ------------------------- | ------------ |
| @uppy/angular             | 0.7.0-beta.2 | @uppy/instagram           | 4.0.0-beta.2 |
| @uppy/audio               | 2.0.0-beta.2 | @uppy/onedrive            | 4.0.0-beta.2 |
| @uppy/aws-s3              | 4.0.0-beta.2 | @uppy/progress-bar        | 4.0.0-beta.2 |
| @uppy/aws-s3-multipart    | 4.0.0-beta.2 | @uppy/provider-views      | 4.0.0-beta.2 |
| @uppy/box                 | 3.0.0-beta.2 | @uppy/react               | 4.0.0-beta.2 |
| @uppy/companion           | 5.0.0-beta.2 | @uppy/remote-sources      | 2.0.0-beta.2 |
| @uppy/companion-client    | 4.0.0-beta.2 | @uppy/screen-capture      | 4.0.0-beta.2 |
| @uppy/compressor          | 2.0.0-beta.2 | @uppy/status-bar          | 4.0.0-beta.2 |
| @uppy/core                | 4.0.0-beta.2 | @uppy/thumbnail-generator | 4.0.0-beta.2 |
| @uppy/dashboard           | 4.0.0-beta.2 | @uppy/transloadit         | 4.0.0-beta.2 |
| @uppy/drag-drop           | 4.0.0-beta.2 | @uppy/tus                 | 4.0.0-beta.2 |
| @uppy/drop-target         | 3.0.0-beta.2 | @uppy/unsplash            | 4.0.0-beta.2 |
| @uppy/dropbox             | 4.0.0-beta.2 | @uppy/url                 | 4.0.0-beta.2 |
| @uppy/facebook            | 4.0.0-beta.2 | @uppy/utils               | 6.0.0-beta.2 |
| @uppy/file-input          | 4.0.0-beta.2 | @uppy/webcam              | 4.0.0-beta.2 |
| @uppy/golden-retriever    | 4.0.0-beta.2 | @uppy/zoom                | 3.0.0-beta.2 |
| @uppy/google-drive        | 4.0.0-beta.2 | uppy                      | 4.0.0-beta.2 |

- @uppy/aws-s3: default to multipart depending on the size of input (Antoine du Hamel / #5076)
- @uppy/aws-s3: remove deprecated `prepareUploadParts` option (Antoine du Hamel / #5075)
- @uppy/core: use variadic arguments for `uppy.use` (Antoine du Hamel / #4888)
- @uppy/aws-s3: remove legacy plugin (Antoine du Hamel / #5070)
- @uppy/locales: do not build `dist/` folder (Merlijn Vos / #5055)
- @uppy/angular: fix Angular version requirement in peerDeps (Antoine du Hamel / #5067)
- @uppy/transloadit: remove deprecated options (Merlijn Vos / #5056)




| Package          | Version | Package          | Version |
| ---------------- | ------- | ---------------- | ------- |
| @uppy/companion  |  4.13.1 | uppy             |  3.24.1 |
| @uppy/file-input |   3.1.1 |                  |         |

- @uppy/companion: upgrade redis (Mikael Finstad / #5065)
- meta: fix `watch:*` scripts (Antoine du Hamel / #5046)
- meta: include more packages in `compare_diff` CI (Antoine du Hamel / #5044)
- @uppy/file-input: add missing export (Antoine du Hamel / #5045)
- meta: Bump express from 4.18.1 to 4.19.2 in /packages/@uppy/companion (dependabot[bot] / #5036)
- @uppy/companion: Bump express from 4.18.1 to 4.19.2 (dependabot[bot] / #5037)
@@ -1,93 +1,157 @@
# @uppy/aws-s3
# @uppy/aws-s3-multipart
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To not confuse people, this should be @uppy/aws-s3.

All commits below will still reference aws-s3-multipart though. Alternatively a comment in the changelog explaining the naming change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants