From 2eddf210d3b3cea2f34ce97928afbeaf77cfd22a Mon Sep 17 00:00:00 2001 From: Martin Brennan Date: Thu, 26 Aug 2021 15:13:32 +1000 Subject: [PATCH] DEV: Revert uppy upgrade This rolls uppy back to the previous bundle that was used, which will break multipart functionality (which is not yet enabled anywhere). No other upload functionality should be affected by this change, it will be as if d295a16dab994c45e33e1df8da46c337ebd6a917 had not been merged. --- app/assets/javascripts/discourse-shims.js | 2 +- .../discourse/app/lib/uppy-checksum-plugin.js | 4 +- .../app/lib/uppy-media-optimization-plugin.js | 4 +- vendor/assets/javascripts/uppy.js | 14941 +++++++++------- 4 files changed, 8008 insertions(+), 6943 deletions(-) diff --git a/app/assets/javascripts/discourse-shims.js b/app/assets/javascripts/discourse-shims.js index 3f038c12d85..71ce8478e37 100644 --- a/app/assets/javascripts/discourse-shims.js +++ b/app/assets/javascripts/discourse-shims.js @@ -32,7 +32,7 @@ define("@popperjs/core", ["exports"], function (__exports__) { define("@uppy/core", ["exports"], function (__exports__) { __exports__.default = window.Uppy.Core; - __exports__.BasePlugin = window.Uppy.BasePlugin; + __exports__.Plugin = window.Uppy.Plugin; }); define("@uppy/aws-s3", ["exports"], function (__exports__) { diff --git a/app/assets/javascripts/discourse/app/lib/uppy-checksum-plugin.js b/app/assets/javascripts/discourse/app/lib/uppy-checksum-plugin.js index 3e74b36252f..4cd0cea0476 100644 --- a/app/assets/javascripts/discourse/app/lib/uppy-checksum-plugin.js +++ b/app/assets/javascripts/discourse/app/lib/uppy-checksum-plugin.js @@ -1,8 +1,8 @@ -import { BasePlugin } from "@uppy/core"; +import { Plugin } from "@uppy/core"; import { warn } from "@ember/debug"; import { Promise } from "rsvp"; -export default class UppyChecksum extends BasePlugin { +export default class UppyChecksum extends Plugin { constructor(uppy, opts) { super(uppy, opts); this.id = opts.id || "uppy-checksum"; diff --git a/app/assets/javascripts/discourse/app/lib/uppy-media-optimization-plugin.js b/app/assets/javascripts/discourse/app/lib/uppy-media-optimization-plugin.js index dcf3c3121ba..862a55a34d8 100644 --- a/app/assets/javascripts/discourse/app/lib/uppy-media-optimization-plugin.js +++ b/app/assets/javascripts/discourse/app/lib/uppy-media-optimization-plugin.js @@ -1,8 +1,8 @@ -import { BasePlugin } from "@uppy/core"; +import { Plugin } from "@uppy/core"; import { warn } from "@ember/debug"; import { Promise } from "rsvp"; -export default class UppyMediaOptimization extends BasePlugin { +export default class UppyMediaOptimization extends Plugin { constructor(uppy, opts) { super(uppy, opts); this.id = opts.id || "uppy-media-optimization"; diff --git a/vendor/assets/javascripts/uppy.js b/vendor/assets/javascripts/uppy.js index ef9e4517322..36cf75c3923 100644 --- a/vendor/assets/javascripts/uppy.js +++ b/vendor/assets/javascripts/uppy.js @@ -1,18 +1,4 @@ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i= need) { + break; + } + } + + candidates.forEach(function (index) { + _this3._uploadPartRetryable(index).then(function () { + // Continue uploading parts + _this3._uploadParts(); + }, function (err) { + _this3._onError(err); + }); + }); + }; + + _proto._retryable = function _retryable(_ref) { + var _this4 = this; + + var before = _ref.before, + attempt = _ref.attempt, + after = _ref.after; + var retryDelays = this.options.retryDelays; + var signal = this.abortController.signal; + if (before) before(); + + function shouldRetry(err) { + if (err.source && typeof err.source.status === 'number') { + var status = err.source.status; // 0 probably indicates network failure + + return status === 0 || status === 409 || status === 423 || status >= 500 && status < 600; + } + + return false; + } + + var doAttempt = function doAttempt(retryAttempt) { + return attempt().catch(function (err) { + if (_this4._aborted()) throw createAbortError(); + + if (shouldRetry(err) && retryAttempt < retryDelays.length) { + return delay(retryDelays[retryAttempt], { + signal: signal + }).then(function () { + return doAttempt(retryAttempt + 1); + }); + } + + throw err; + }); + }; + + return doAttempt(0).then(function (result) { + if (after) after(); + return result; + }, function (err) { + if (after) after(); + throw err; + }); + }; + + _proto._uploadPartRetryable = function _uploadPartRetryable(index) { + var _this5 = this; + + return this._retryable({ + before: function before() { + _this5.partsInProgress += 1; + }, + attempt: function attempt() { + return _this5._uploadPart(index); + }, + after: function after() { + _this5.partsInProgress -= 1; + } + }); + }; + + _proto._uploadPart = function _uploadPart(index) { + var _this6 = this; + + var body = this.chunks[index]; + this.chunkState[index].busy = true; + return Promise.resolve().then(function () { + return _this6.options.prepareUploadPart({ + key: _this6.key, + uploadId: _this6.uploadId, + body: body, + number: index + 1 + }); + }).then(function (result) { + var valid = typeof result === 'object' && result && typeof result.url === 'string'; + + if (!valid) { + throw new TypeError('AwsS3/Multipart: Got incorrect result from `prepareUploadPart()`, expected an object `{ url }`.'); + } + + return result; + }).then(function (_ref2) { + var url = _ref2.url, + headers = _ref2.headers; + + if (_this6._aborted()) { + _this6.chunkState[index].busy = false; + throw createAbortError(); + } + + return _this6._uploadPartBytes(index, url, headers); + }); + }; + + _proto._onPartProgress = function _onPartProgress(index, sent, total) { + this.chunkState[index].uploaded = ensureInt(sent); + var totalUploaded = this.chunkState.reduce(function (n, c) { + return n + c.uploaded; + }, 0); + this.options.onProgress(totalUploaded, this.file.size); + }; + + _proto._onPartComplete = function _onPartComplete(index, etag) { + this.chunkState[index].etag = etag; + this.chunkState[index].done = true; + var part = { + PartNumber: index + 1, + ETag: etag + }; + this.parts.push(part); + this.options.onPartComplete(part); + }; + + _proto._uploadPartBytes = function _uploadPartBytes(index, url, headers) { + var _this7 = this; + + var body = this.chunks[index]; + var signal = this.abortController.signal; + var defer; + var promise = new Promise(function (resolve, reject) { + defer = { + resolve: resolve, + reject: reject + }; + }); + var xhr = new XMLHttpRequest(); + xhr.open('PUT', url, true); + + if (headers) { + Object.keys(headers).map(function (key) { + xhr.setRequestHeader(key, headers[key]); + }); + } + + xhr.responseType = 'text'; + + function cleanup() { + signal.removeEventListener('abort', onabort); + } + + function onabort() { + xhr.abort(); + } + + signal.addEventListener('abort', onabort); + xhr.upload.addEventListener('progress', function (ev) { + if (!ev.lengthComputable) return; + + _this7._onPartProgress(index, ev.loaded, ev.total); + }); + xhr.addEventListener('abort', function (ev) { + cleanup(); + _this7.chunkState[index].busy = false; + defer.reject(createAbortError()); + }); + xhr.addEventListener('load', function (ev) { + cleanup(); + _this7.chunkState[index].busy = false; + + if (ev.target.status < 200 || ev.target.status >= 300) { + var error = new Error('Non 2xx'); + error.source = ev.target; + defer.reject(error); + return; + } + + _this7._onPartProgress(index, body.size, body.size); // NOTE This must be allowed by CORS. + + + var etag = ev.target.getResponseHeader('ETag'); + + if (etag === null) { + defer.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; + } + + _this7._onPartComplete(index, etag); + + defer.resolve(); + }); + xhr.addEventListener('error', function (ev) { + cleanup(); + _this7.chunkState[index].busy = false; + var error = new Error('Unknown error'); + error.source = ev.target; + defer.reject(error); + }); + xhr.send(body); + return promise; + }; + + _proto._completeUpload = function _completeUpload() { + var _this8 = this; + + // Parts may not have completed uploading in sorted order, if limit > 1. + this.parts.sort(function (a, b) { + return a.PartNumber - b.PartNumber; + }); + return Promise.resolve().then(function () { + return _this8.options.completeMultipartUpload({ + key: _this8.key, + uploadId: _this8.uploadId, + parts: _this8.parts + }); + }).then(function (result) { + _this8.options.onSuccess(result); + }, function (err) { + _this8._onError(err); + }); + }; + + _proto._abortUpload = function _abortUpload() { + var _this9 = this; + + this.abortController.abort(); + this.createdPromise.then(function () { + _this9.options.abortMultipartUpload({ + key: _this9.key, + uploadId: _this9.uploadId + }); + }, function () {// if the creation failed we do not need to abort + }); + }; + + _proto._onError = function _onError(err) { + if (err && err.name === 'AbortError') { + return; + } + + this.options.onError(err); + }; + + _proto.start = function start() { + this.isPaused = false; + + if (this.uploadId) { + this._resumeUpload(); + } else { + this._createUpload(); + } + }; + + _proto.pause = function pause() { + this.abortController.abort(); // Swap it out for a new controller, because this instance may be resumed later. + + this.abortController = new AbortController(); + this.isPaused = true; + }; + + _proto.abort = function abort(opts) { + if (opts === void 0) { + opts = {}; + } + + var really = opts.really || false; + if (!really) return this.pause(); + + this._abortUpload(); + }; + + return MultipartUploader; +}(); + +module.exports = MultipartUploader; +},{"@uppy/utils/lib/AbortController":21,"@uppy/utils/lib/delay":27}],3:[function(require,module,exports){ +var _class, _temp; + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +var _require = require('@uppy/core'), + Plugin = _require.Plugin; + +var _require2 = require('@uppy/companion-client'), + Socket = _require2.Socket, + Provider = _require2.Provider, + RequestClient = _require2.RequestClient; + +var EventTracker = require('@uppy/utils/lib/EventTracker'); + +var emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress'); + +var getSocketHost = require('@uppy/utils/lib/getSocketHost'); + +var RateLimitedQueue = require('@uppy/utils/lib/RateLimitedQueue'); + +var Uploader = require('./MultipartUploader'); + +function assertServerError(res) { + if (res && res.error) { + var error = new Error(res.message); + + _extends(error, res.error); + + throw error; + } + + return res; +} + +module.exports = (_temp = _class = /*#__PURE__*/function (_Plugin) { + _inheritsLoose(AwsS3Multipart, _Plugin); + + function AwsS3Multipart(uppy, opts) { + var _this; + + _this = _Plugin.call(this, uppy, opts) || this; + _this.type = 'uploader'; + _this.id = _this.opts.id || 'AwsS3Multipart'; + _this.title = 'AWS S3 Multipart'; + _this.client = new RequestClient(uppy, opts); + var defaultOptions = { + timeout: 30 * 1000, + limit: 0, + retryDelays: [0, 1000, 3000, 5000], + createMultipartUpload: _this.createMultipartUpload.bind(_assertThisInitialized(_this)), + listParts: _this.listParts.bind(_assertThisInitialized(_this)), + prepareUploadPart: _this.prepareUploadPart.bind(_assertThisInitialized(_this)), + abortMultipartUpload: _this.abortMultipartUpload.bind(_assertThisInitialized(_this)), + completeMultipartUpload: _this.completeMultipartUpload.bind(_assertThisInitialized(_this)) + }; + _this.opts = _extends({}, defaultOptions, opts); + _this.upload = _this.upload.bind(_assertThisInitialized(_this)); + _this.requests = new RateLimitedQueue(_this.opts.limit); + _this.uploaders = Object.create(null); + _this.uploaderEvents = Object.create(null); + _this.uploaderSockets = Object.create(null); + return _this; + } + /** + * Clean up all references for a file's upload: the MultipartUploader instance, + * any events related to the file, and the Companion WebSocket connection. + * + * Set `opts.abort` to tell S3 that the multipart upload is cancelled and must be removed. + * This should be done when the user cancels the upload, not when the upload is completed or errored. + */ + + + var _proto = AwsS3Multipart.prototype; + + _proto.resetUploaderReferences = function resetUploaderReferences(fileID, opts) { + if (opts === void 0) { + opts = {}; + } + + if (this.uploaders[fileID]) { + this.uploaders[fileID].abort({ + really: 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; + } + }; + + _proto.assertHost = function 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."); + } + }; + + _proto.createMultipartUpload = function createMultipartUpload(file) { + this.assertHost('createMultipartUpload'); + var metadata = {}; + Object.keys(file.meta).map(function (key) { + if (file.meta[key] != null) { + metadata[key] = file.meta[key].toString(); + } + }); + return this.client.post('s3/multipart', { + filename: file.name, + type: file.type, + metadata: metadata + }).then(assertServerError); + }; + + _proto.listParts = function listParts(file, _ref) { + var key = _ref.key, + uploadId = _ref.uploadId; + this.assertHost('listParts'); + var filename = encodeURIComponent(key); + return this.client.get("s3/multipart/" + uploadId + "?key=" + filename).then(assertServerError); + }; + + _proto.prepareUploadPart = function prepareUploadPart(file, _ref2) { + var key = _ref2.key, + uploadId = _ref2.uploadId, + number = _ref2.number; + this.assertHost('prepareUploadPart'); + var filename = encodeURIComponent(key); + return this.client.get("s3/multipart/" + uploadId + "/" + number + "?key=" + filename).then(assertServerError); + }; + + _proto.completeMultipartUpload = function completeMultipartUpload(file, _ref3) { + var key = _ref3.key, + uploadId = _ref3.uploadId, + parts = _ref3.parts; + this.assertHost('completeMultipartUpload'); + var filename = encodeURIComponent(key); + var uploadIdEnc = encodeURIComponent(uploadId); + return this.client.post("s3/multipart/" + uploadIdEnc + "/complete?key=" + filename, { + parts: parts + }).then(assertServerError); + }; + + _proto.abortMultipartUpload = function abortMultipartUpload(file, _ref4) { + var key = _ref4.key, + uploadId = _ref4.uploadId; + this.assertHost('abortMultipartUpload'); + var filename = encodeURIComponent(key); + var uploadIdEnc = encodeURIComponent(uploadId); + return this.client.delete("s3/multipart/" + uploadIdEnc + "?key=" + filename).then(assertServerError); + }; + + _proto.uploadFile = function uploadFile(file) { + var _this2 = this; + + return new Promise(function (resolve, reject) { + var onStart = function onStart(data) { + var cFile = _this2.uppy.getFile(file.id); + + _this2.uppy.setFileState(file.id, { + s3Multipart: _extends({}, cFile.s3Multipart, { + key: data.key, + uploadId: data.uploadId + }) + }); + }; + + var onProgress = function onProgress(bytesUploaded, bytesTotal) { + _this2.uppy.emit('upload-progress', file, { + uploader: _this2, + bytesUploaded: bytesUploaded, + bytesTotal: bytesTotal + }); + }; + + var onError = function onError(err) { + _this2.uppy.log(err); + + _this2.uppy.emit('upload-error', file, err); + + queuedRequest.done(); + + _this2.resetUploaderReferences(file.id); + + reject(err); + }; + + var onSuccess = function onSuccess(result) { + var uploadResp = { + body: _extends({}, result), + uploadURL: result.location + }; + queuedRequest.done(); + + _this2.resetUploaderReferences(file.id); + + var cFile = _this2.uppy.getFile(file.id); + + _this2.uppy.emit('upload-success', cFile || file, uploadResp); + + if (result.location) { + _this2.uppy.log("Download " + upload.file.name + " from " + result.location); + } + + resolve(upload); + }; + + var onPartComplete = function onPartComplete(part) { + var cFile = _this2.uppy.getFile(file.id); + + if (!cFile) { + return; + } + + _this2.uppy.emit('s3-multipart:part-uploaded', cFile, part); + }; + + var upload = new Uploader(file.data, _extends({ + // .bind to pass the file object to each handler. + createMultipartUpload: _this2.opts.createMultipartUpload.bind(_this2, file), + listParts: _this2.opts.listParts.bind(_this2, file), + prepareUploadPart: _this2.opts.prepareUploadPart.bind(_this2, file), + completeMultipartUpload: _this2.opts.completeMultipartUpload.bind(_this2, file), + abortMultipartUpload: _this2.opts.abortMultipartUpload.bind(_this2, file), + getChunkSize: _this2.opts.getChunkSize ? _this2.opts.getChunkSize.bind(_this2) : null, + onStart: onStart, + onProgress: onProgress, + onError: onError, + onSuccess: onSuccess, + onPartComplete: onPartComplete, + limit: _this2.opts.limit || 5, + retryDelays: _this2.opts.retryDelays || [] + }, file.s3Multipart)); + _this2.uploaders[file.id] = upload; + _this2.uploaderEvents[file.id] = new EventTracker(_this2.uppy); + + var queuedRequest = _this2.requests.run(function () { + if (!file.isPaused) { + upload.start(); + } // Don't do anything here, the caller will take care of cancelling the upload itself + // using resetUploaderReferences(). This is because resetUploaderReferences() has to be + // called when this request is still in the queue, and has not been started yet, too. At + // that point this cancellation function is not going to be called. + + + return function () {}; + }); + + _this2.onFileRemove(file.id, function (removed) { + queuedRequest.abort(); + + _this2.resetUploaderReferences(file.id, { + abort: true + }); + + resolve("upload " + removed.id + " was removed"); + }); + + _this2.onCancelAll(file.id, function () { + queuedRequest.abort(); + + _this2.resetUploaderReferences(file.id, { + abort: true + }); + + resolve("upload " + file.id + " was canceled"); + }); + + _this2.onFilePause(file.id, function (isPaused) { + if (isPaused) { + // Remove this file from the queue so another file can start in its place. + queuedRequest.abort(); + upload.pause(); + } else { + // Resuming an upload should be queued, else you could pause and then resume a queued upload to make it skip the queue. + queuedRequest.abort(); + queuedRequest = _this2.requests.run(function () { + upload.start(); + return function () {}; + }); + } + }); + + _this2.onPauseAll(file.id, function () { + queuedRequest.abort(); + upload.pause(); + }); + + _this2.onResumeAll(file.id, function () { + queuedRequest.abort(); + + if (file.error) { + upload.abort(); + } + + queuedRequest = _this2.requests.run(function () { + upload.start(); + return function () {}; + }); + }); // Don't double-emit upload-started for Golden Retriever-restored files that were already started + + + if (!file.progress.uploadStarted || !file.isRestored) { + _this2.uppy.emit('upload-started', file); + } + }); + }; + + _proto.uploadRemote = function uploadRemote(file) { + var _this3 = this; + + this.resetUploaderReferences(file.id); // Don't double-emit upload-started for Golden Retriever-restored files that were already started + + if (!file.progress.uploadStarted || !file.isRestored) { + this.uppy.emit('upload-started', file); + } + + if (file.serverToken) { + return this.connectToServerSocket(file); + } + + return new Promise(function (resolve, reject) { + var Client = file.remote.providerOptions.provider ? Provider : RequestClient; + var client = new Client(_this3.uppy, file.remote.providerOptions); + client.post(file.remote.url, _extends({}, file.remote.body, { + protocol: 's3-multipart', + size: file.data.size, + metadata: file.meta + })).then(function (res) { + _this3.uppy.setFileState(file.id, { + serverToken: res.token + }); + + file = _this3.uppy.getFile(file.id); + return file; + }).then(function (file) { + return _this3.connectToServerSocket(file); + }).then(function () { + resolve(); + }).catch(function (err) { + _this3.uppy.emit('upload-error', file, err); + + reject(err); + }); + }); + }; + + _proto.connectToServerSocket = function connectToServerSocket(file) { + var _this4 = this; + + return new Promise(function (resolve, reject) { + var token = file.serverToken; + var host = getSocketHost(file.remote.companionUrl); + var socket = new Socket({ + target: host + "/api/" + token, + autoOpen: false + }); + _this4.uploaderSockets[file.id] = socket; + _this4.uploaderEvents[file.id] = new EventTracker(_this4.uppy); + + _this4.onFileRemove(file.id, function (removed) { + queuedRequest.abort(); + socket.send('pause', {}); + + _this4.resetUploaderReferences(file.id, { + abort: true + }); + + resolve("upload " + file.id + " was removed"); + }); + + _this4.onFilePause(file.id, function (isPaused) { + if (isPaused) { + // Remove this file from the queue so another file can start in its place. + queuedRequest.abort(); + socket.send('pause', {}); + } else { + // Resuming an upload should be queued, else you could pause and then resume a queued upload to make it skip the queue. + queuedRequest.abort(); + queuedRequest = _this4.requests.run(function () { + socket.send('resume', {}); + return function () {}; + }); + } + }); + + _this4.onPauseAll(file.id, function () { + queuedRequest.abort(); + socket.send('pause', {}); + }); + + _this4.onCancelAll(file.id, function () { + queuedRequest.abort(); + socket.send('pause', {}); + + _this4.resetUploaderReferences(file.id); + + resolve("upload " + file.id + " was canceled"); + }); + + _this4.onResumeAll(file.id, function () { + queuedRequest.abort(); + + if (file.error) { + socket.send('pause', {}); + } + + queuedRequest = _this4.requests.run(function () { + socket.send('resume', {}); + }); + }); + + _this4.onRetry(file.id, function () { + // Only do the retry if the upload is actually in progress; + // else we could try to send these messages when the upload is still queued. + // We may need a better check for this since the socket may also be closed + // for other reasons, like network failures. + if (socket.isOpen) { + socket.send('pause', {}); + socket.send('resume', {}); + } + }); + + _this4.onRetryAll(file.id, function () { + if (socket.isOpen) { + socket.send('pause', {}); + socket.send('resume', {}); + } + }); + + socket.on('progress', function (progressData) { + return emitSocketProgress(_this4, progressData, file); + }); + socket.on('error', function (errData) { + _this4.uppy.emit('upload-error', file, new Error(errData.error)); + + _this4.resetUploaderReferences(file.id); + + queuedRequest.done(); + reject(new Error(errData.error)); + }); + socket.on('success', function (data) { + var uploadResp = { + uploadURL: data.url + }; + + _this4.uppy.emit('upload-success', file, uploadResp); + + _this4.resetUploaderReferences(file.id); + + queuedRequest.done(); + resolve(); + }); + + var queuedRequest = _this4.requests.run(function () { + socket.open(); + + if (file.isPaused) { + socket.send('pause', {}); + } + + return function () {}; + }); + }); + }; + + _proto.upload = function upload(fileIDs) { + var _this5 = this; + + if (fileIDs.length === 0) return Promise.resolve(); + var promises = fileIDs.map(function (id) { + var file = _this5.uppy.getFile(id); + + if (file.isRemote) { + return _this5.uploadRemote(file); + } + + return _this5.uploadFile(file); + }); + return Promise.all(promises); + }; + + _proto.onFileRemove = function onFileRemove(fileID, cb) { + this.uploaderEvents[fileID].on('file-removed', function (file) { + if (fileID === file.id) cb(file.id); + }); + }; + + _proto.onFilePause = function onFilePause(fileID, cb) { + this.uploaderEvents[fileID].on('upload-pause', function (targetFileID, isPaused) { + if (fileID === targetFileID) { + // const isPaused = this.uppy.pauseResume(fileID) + cb(isPaused); + } + }); + }; + + _proto.onRetry = function onRetry(fileID, cb) { + this.uploaderEvents[fileID].on('upload-retry', function (targetFileID) { + if (fileID === targetFileID) { + cb(); + } + }); + }; + + _proto.onRetryAll = function onRetryAll(fileID, cb) { + var _this6 = this; + + this.uploaderEvents[fileID].on('retry-all', function (filesToRetry) { + if (!_this6.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto.onPauseAll = function onPauseAll(fileID, cb) { + var _this7 = this; + + this.uploaderEvents[fileID].on('pause-all', function () { + if (!_this7.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto.onCancelAll = function onCancelAll(fileID, cb) { + var _this8 = this; + + this.uploaderEvents[fileID].on('cancel-all', function () { + if (!_this8.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto.onResumeAll = function onResumeAll(fileID, cb) { + var _this9 = this; + + this.uploaderEvents[fileID].on('resume-all', function () { + if (!_this9.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto.install = function install() { + var _this$uppy$getState = this.uppy.getState(), + capabilities = _this$uppy$getState.capabilities; + + this.uppy.setState({ + capabilities: _extends({}, capabilities, { + resumableUploads: true + }) + }); + this.uppy.addUploader(this.upload); + }; + + _proto.uninstall = function uninstall() { + var _this$uppy$getState2 = this.uppy.getState(), + capabilities = _this$uppy$getState2.capabilities; + + this.uppy.setState({ + capabilities: _extends({}, capabilities, { + resumableUploads: false + }) + }); + this.uppy.removeUploader(this.upload); + }; + + return AwsS3Multipart; +}(Plugin), _class.VERSION = "1.8.18", _temp); +},{"./MultipartUploader":2,"@uppy/companion-client":12,"@uppy/core":15,"@uppy/utils/lib/EventTracker":22,"@uppy/utils/lib/RateLimitedQueue":25,"@uppy/utils/lib/emitSocketProgress":28,"@uppy/utils/lib/getSocketHost":40}],4:[function(require,module,exports){ +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +var cuid = require('cuid'); + +var _require = require('@uppy/companion-client'), + Provider = _require.Provider, + RequestClient = _require.RequestClient, + Socket = _require.Socket; + +var emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress'); + +var getSocketHost = require('@uppy/utils/lib/getSocketHost'); + +var EventTracker = require('@uppy/utils/lib/EventTracker'); + +var ProgressTimeout = require('@uppy/utils/lib/ProgressTimeout'); + +var NetworkError = require('@uppy/utils/lib/NetworkError'); + +var isNetworkError = require('@uppy/utils/lib/isNetworkError'); // See XHRUpload + + +function buildResponseError(xhr, error) { + // No error message + if (!error) error = new Error('Upload error'); // Got an error message string + + if (typeof error === 'string') error = new Error(error); // Got something else + + if (!(error instanceof Error)) { + error = _extends(new Error('Upload error'), { + data: error + }); + } + + if (isNetworkError(xhr)) { + error = new NetworkError(error, xhr); + return error; + } + + error.request = xhr; + return error; +} // See XHRUpload + + +function setTypeInBlob(file) { + var dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type); + return dataWithUpdatedType; +} + +module.exports = /*#__PURE__*/function () { + function MiniXHRUpload(uppy, opts) { + this.uppy = uppy; + this.opts = _extends({ + validateStatus: function validateStatus(status, responseText, response) { + return status >= 200 && status < 300; + } + }, opts); + this.requests = opts.__queue; + this.uploaderEvents = Object.create(null); + this.i18n = opts.i18n; + } + + var _proto = MiniXHRUpload.prototype; + + _proto._getOptions = function _getOptions(file) { + var uppy = this.uppy; + var overrides = uppy.getState().xhrUpload; + + var opts = _extends({}, this.opts, overrides || {}, file.xhrUpload || {}, { + headers: {} + }); + + _extends(opts.headers, this.opts.headers); + + if (overrides) { + _extends(opts.headers, overrides.headers); + } + + if (file.xhrUpload) { + _extends(opts.headers, file.xhrUpload.headers); + } + + return opts; + }; + + _proto.uploadFile = function uploadFile(id, current, total) { + var file = this.uppy.getFile(id); + + if (file.error) { + throw new Error(file.error); + } else if (file.isRemote) { + return this._uploadRemoteFile(file, current, total); + } + + return this._uploadLocalFile(file, current, total); + }; + + _proto._addMetadata = function _addMetadata(formData, meta, opts) { + var metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default. + : Object.keys(meta); + metaFields.forEach(function (item) { + formData.append(item, meta[item]); + }); + }; + + _proto._createFormDataUpload = function _createFormDataUpload(file, opts) { + var formPost = new FormData(); + + this._addMetadata(formPost, file.meta, opts); + + var dataWithUpdatedType = setTypeInBlob(file); + + if (file.name) { + formPost.append(opts.fieldName, dataWithUpdatedType, file.meta.name); + } else { + formPost.append(opts.fieldName, dataWithUpdatedType); + } + + return formPost; + }; + + _proto._createBareUpload = function _createBareUpload(file, opts) { + return file.data; + }; + + _proto._onFileRemoved = function _onFileRemoved(fileID, cb) { + this.uploaderEvents[fileID].on('file-removed', function (file) { + if (fileID === file.id) cb(file.id); + }); + }; + + _proto._onRetry = function _onRetry(fileID, cb) { + this.uploaderEvents[fileID].on('upload-retry', function (targetFileID) { + if (fileID === targetFileID) { + cb(); + } + }); + }; + + _proto._onRetryAll = function _onRetryAll(fileID, cb) { + var _this = this; + + this.uploaderEvents[fileID].on('retry-all', function (filesToRetry) { + if (!_this.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto._onCancelAll = function _onCancelAll(fileID, cb) { + var _this2 = this; + + this.uploaderEvents[fileID].on('cancel-all', function () { + if (!_this2.uppy.getFile(fileID)) return; + cb(); + }); + }; + + _proto._uploadLocalFile = function _uploadLocalFile(file, current, total) { + var _this3 = this; + + var opts = this._getOptions(file); + + this.uppy.log("uploading " + current + " of " + total); + return new Promise(function (resolve, reject) { + // This is done in index.js in the S3 plugin. + // this.uppy.emit('upload-started', file) + var data = opts.formData ? _this3._createFormDataUpload(file, opts) : _this3._createBareUpload(file, opts); + var xhr = new XMLHttpRequest(); + _this3.uploaderEvents[file.id] = new EventTracker(_this3.uppy); + var timer = new ProgressTimeout(opts.timeout, function () { + xhr.abort(); + queuedRequest.done(); + var error = new Error(_this3.i18n('timedOut', { + seconds: Math.ceil(opts.timeout / 1000) + })); + + _this3.uppy.emit('upload-error', file, error); + + reject(error); + }); + var id = cuid(); + xhr.upload.addEventListener('loadstart', function (ev) { + _this3.uppy.log("[AwsS3/XHRUpload] " + id + " started"); + }); + xhr.upload.addEventListener('progress', function (ev) { + _this3.uppy.log("[AwsS3/XHRUpload] " + id + " progress: " + ev.loaded + " / " + ev.total); // Begin checking for timeouts when progress starts, instead of loading, + // to avoid timing out requests on browser concurrency queue + + + timer.progress(); + + if (ev.lengthComputable) { + _this3.uppy.emit('upload-progress', file, { + uploader: _this3, + bytesUploaded: ev.loaded, + bytesTotal: ev.total + }); + } + }); + xhr.addEventListener('load', function (ev) { + _this3.uppy.log("[AwsS3/XHRUpload] " + id + " finished"); + + timer.done(); + queuedRequest.done(); + + if (_this3.uploaderEvents[file.id]) { + _this3.uploaderEvents[file.id].remove(); + + _this3.uploaderEvents[file.id] = null; + } + + if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) { + var _body = opts.getResponseData(xhr.responseText, xhr); + + var uploadURL = _body[opts.responseUrlFieldName]; + var uploadResp = { + status: ev.target.status, + body: _body, + uploadURL: uploadURL + }; + + _this3.uppy.emit('upload-success', file, uploadResp); + + if (uploadURL) { + _this3.uppy.log("Download " + file.name + " from " + uploadURL); + } + + return resolve(file); + } + + var body = opts.getResponseData(xhr.responseText, xhr); + var error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); + var response = { + status: ev.target.status, + body: body + }; + + _this3.uppy.emit('upload-error', file, error, response); + + return reject(error); + }); + xhr.addEventListener('error', function (ev) { + _this3.uppy.log("[AwsS3/XHRUpload] " + id + " errored"); + + timer.done(); + queuedRequest.done(); + + if (_this3.uploaderEvents[file.id]) { + _this3.uploaderEvents[file.id].remove(); + + _this3.uploaderEvents[file.id] = null; + } + + var error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); + + _this3.uppy.emit('upload-error', file, error); + + return reject(error); + }); + xhr.open(opts.method.toUpperCase(), opts.endpoint, true); // IE10 does not allow setting `withCredentials` and `responseType` + // before `open()` is called. + + xhr.withCredentials = opts.withCredentials; + + if (opts.responseType !== '') { + xhr.responseType = opts.responseType; + } + + Object.keys(opts.headers).forEach(function (header) { + xhr.setRequestHeader(header, opts.headers[header]); + }); + + var queuedRequest = _this3.requests.run(function () { + xhr.send(data); + return function () { + timer.done(); + xhr.abort(); + }; + }, { + priority: 1 + }); + + _this3._onFileRemoved(file.id, function () { + queuedRequest.abort(); + reject(new Error('File removed')); + }); + + _this3._onCancelAll(file.id, function () { + queuedRequest.abort(); + reject(new Error('Upload cancelled')); + }); + }); + }; + + _proto._uploadRemoteFile = function _uploadRemoteFile(file, current, total) { + var _this4 = this; + + var opts = this._getOptions(file); + + return new Promise(function (resolve, reject) { + // This is done in index.js in the S3 plugin. + // this.uppy.emit('upload-started', file) + var fields = {}; + var metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default. + : Object.keys(file.meta); + metaFields.forEach(function (name) { + fields[name] = file.meta[name]; + }); + var Client = file.remote.providerOptions.provider ? Provider : RequestClient; + var client = new Client(_this4.uppy, file.remote.providerOptions); + client.post(file.remote.url, _extends({}, file.remote.body, { + endpoint: opts.endpoint, + size: file.data.size, + fieldname: opts.fieldName, + metadata: fields, + httpMethod: opts.method, + useFormData: opts.formData, + headers: opts.headers + })).then(function (res) { + var token = res.token; + var host = getSocketHost(file.remote.companionUrl); + var socket = new Socket({ + target: host + "/api/" + token, + autoOpen: false + }); + _this4.uploaderEvents[file.id] = new EventTracker(_this4.uppy); + + _this4._onFileRemoved(file.id, function () { + socket.send('pause', {}); + queuedRequest.abort(); + resolve("upload " + file.id + " was removed"); + }); + + _this4._onCancelAll(file.id, function () { + socket.send('pause', {}); + queuedRequest.abort(); + resolve("upload " + file.id + " was canceled"); + }); + + _this4._onRetry(file.id, function () { + socket.send('pause', {}); + socket.send('resume', {}); + }); + + _this4._onRetryAll(file.id, function () { + socket.send('pause', {}); + socket.send('resume', {}); + }); + + socket.on('progress', function (progressData) { + return emitSocketProgress(_this4, progressData, file); + }); + socket.on('success', function (data) { + var body = opts.getResponseData(data.response.responseText, data.response); + var uploadURL = body[opts.responseUrlFieldName]; + var uploadResp = { + status: data.response.status, + body: body, + uploadURL: uploadURL + }; + + _this4.uppy.emit('upload-success', file, uploadResp); + + queuedRequest.done(); + + if (_this4.uploaderEvents[file.id]) { + _this4.uploaderEvents[file.id].remove(); + + _this4.uploaderEvents[file.id] = null; + } + + return resolve(); + }); + socket.on('error', function (errData) { + var resp = errData.response; + var error = resp ? opts.getResponseError(resp.responseText, resp) : _extends(new Error(errData.error.message), { + cause: errData.error + }); + + _this4.uppy.emit('upload-error', file, error); + + queuedRequest.done(); + + if (_this4.uploaderEvents[file.id]) { + _this4.uploaderEvents[file.id].remove(); + + _this4.uploaderEvents[file.id] = null; + } + + reject(error); + }); + + var queuedRequest = _this4.requests.run(function () { + socket.open(); + + if (file.isPaused) { + socket.send('pause', {}); + } + + return function () { + return socket.close(); + }; + }); + }).catch(function (err) { + _this4.uppy.emit('upload-error', file, err); + + reject(err); + }); + }); + }; + + return MiniXHRUpload; +}(); +},{"@uppy/companion-client":12,"@uppy/utils/lib/EventTracker":22,"@uppy/utils/lib/NetworkError":23,"@uppy/utils/lib/ProgressTimeout":24,"@uppy/utils/lib/emitSocketProgress":28,"@uppy/utils/lib/getSocketHost":40,"@uppy/utils/lib/isNetworkError":44,"cuid":50}],5:[function(require,module,exports){ +var _class, _temp; + +function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; } + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +/** + * This plugin is currently a A Big Hack™! The core reason for that is how this plugin + * interacts with Uppy's current pipeline design. The pipeline can handle files in steps, + * including preprocessing, uploading, and postprocessing steps. This plugin initially + * was designed to do its work in a preprocessing step, and let XHRUpload deal with the + * actual file upload as an uploading step. However, Uppy runs steps on all files at once, + * sequentially: first, all files go through a preprocessing step, then, once they are all + * done, they go through the uploading step. + * + * For S3, this causes severely broken behaviour when users upload many files. The + * preprocessing step will request S3 upload URLs that are valid for a short time only, + * but it has to do this for _all_ files, which can take a long time if there are hundreds + * or even thousands of files. By the time the uploader step starts, the first URLs may + * already have expired. If not, the uploading might take such a long time that later URLs + * will expire before some files can be uploaded. + * + * The long-term solution to this problem is to change the upload pipeline so that files + * can be sent to the next step individually. That requires a breaking change, so it is + * planned for some future Uppy version. + * + * In the mean time, this plugin is stuck with a hackier approach: the necessary parts + * of the XHRUpload implementation were copied into this plugin, as the MiniXHRUpload + * class, and this plugin calls into it immediately once it receives an upload URL. + * This isn't as nicely modular as we'd like and requires us to maintain two copies of + * the XHRUpload code, but at least it's not horrifically broken :) + */ +// If global `URL` constructor is available, use it +var URL_ = typeof URL === 'function' ? URL : require('url-parse'); + +var _require = require('@uppy/core'), + Plugin = _require.Plugin; + +var Translator = require('@uppy/utils/lib/Translator'); + +var RateLimitedQueue = require('@uppy/utils/lib/RateLimitedQueue'); + +var settle = require('@uppy/utils/lib/settle'); + +var hasProperty = require('@uppy/utils/lib/hasProperty'); + +var _require2 = require('@uppy/companion-client'), + RequestClient = _require2.RequestClient; + +var qsStringify = require('qs-stringify'); + +var MiniXHRUpload = require('./MiniXHRUpload'); + +var isXml = require('./isXml'); + +function resolveUrl(origin, link) { + return origin ? new URL_(link, origin).toString() : new URL_(link).toString(); +} +/** + * Get the contents of a named tag in an XML source string. + * + * @param {string} source - The XML source string. + * @param {string} tagName - The name of the tag. + * @returns {string} The contents of the tag, or the empty string if the tag does not exist. + */ + + +function getXmlValue(source, tagName) { + var start = source.indexOf("<" + tagName + ">"); + var end = source.indexOf("", start); + return start !== -1 && end !== -1 ? source.slice(start + tagName.length + 2, end) : ''; +} + +function assertServerError(res) { + if (res && res.error) { + var error = new Error(res.message); + + _extends(error, res.error); + + throw error; + } + + return res; +} // warning deduplication flag: see `getResponseData()` XHRUpload option definition + + +var warnedSuccessActionStatus = false; +module.exports = (_temp = _class = /*#__PURE__*/function (_Plugin) { + _inheritsLoose(AwsS3, _Plugin); + + function AwsS3(uppy, opts) { + var _this; + + _this = _Plugin.call(this, uppy, opts) || this; + _this.type = 'uploader'; + _this.id = _this.opts.id || 'AwsS3'; + _this.title = 'AWS S3'; + _this.defaultLocale = { + strings: { + timedOut: 'Upload stalled for %{seconds} seconds, aborting.' + } + }; + var defaultOptions = { + timeout: 30 * 1000, + limit: 0, + metaFields: [], + // have to opt in + getUploadParameters: _this.getUploadParameters.bind(_assertThisInitialized(_this)) + }; + _this.opts = _extends({}, defaultOptions, opts); + + _this.i18nInit(); + + _this.client = new RequestClient(uppy, opts); + _this.handleUpload = _this.handleUpload.bind(_assertThisInitialized(_this)); + _this.requests = new RateLimitedQueue(_this.opts.limit); + return _this; + } + + var _proto = AwsS3.prototype; + + _proto.setOptions = function setOptions(newOpts) { + _Plugin.prototype.setOptions.call(this, newOpts); + + this.i18nInit(); + }; + + _proto.i18nInit = function i18nInit() { + this.translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]); + this.i18n = this.translator.translate.bind(this.translator); + this.setPluginState(); // so that UI re-renders and we see the updated locale + }; + + _proto.getUploadParameters = function getUploadParameters(file) { + if (!this.opts.companionUrl) { + throw new Error('Expected a `companionUrl` option containing a Companion address.'); + } + + var filename = file.meta.name; + var type = file.meta.type; + var metadata = {}; + this.opts.metaFields.forEach(function (key) { + if (file.meta[key] != null) { + metadata[key] = file.meta[key].toString(); + } + }); + var query = qsStringify({ + filename: filename, + type: type, + metadata: metadata + }); + return this.client.get("s3/params?" + query).then(assertServerError); + }; + + _proto.validateParameters = function validateParameters(file, params) { + var valid = typeof params === 'object' && params && typeof params.url === 'string' && (typeof params.fields === 'object' || params.fields == null); + + if (!valid) { + var 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."); + console.error(err); + throw err; + } + + var methodIsValid = params.method == null || /^(put|post)$/i.test(params.method); + + if (!methodIsValid) { + var _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."); + + console.error(_err); + throw _err; + } + }; + + _proto.handleUpload = function handleUpload(fileIDs) { + var _this2 = this; + + /** + * keep track of `getUploadParameters()` responses + * so we can cancel the calls individually using just a file ID + * + * @type {object.} + */ + var paramsPromises = Object.create(null); + + function onremove(file) { + var id = file.id; + + if (hasProperty(paramsPromises, id)) { + paramsPromises[id].abort(); + } + } + + this.uppy.on('file-removed', onremove); + fileIDs.forEach(function (id) { + var file = _this2.uppy.getFile(id); + + _this2.uppy.emit('upload-started', file); + }); + var getUploadParameters = this.requests.wrapPromiseFunction(function (file) { + return _this2.opts.getUploadParameters(file); + }); + var numberOfFiles = fileIDs.length; + return settle(fileIDs.map(function (id, index) { + paramsPromises[id] = getUploadParameters(_this2.uppy.getFile(id)); + return paramsPromises[id].then(function (params) { + delete paramsPromises[id]; + + var file = _this2.uppy.getFile(id); + + _this2.validateParameters(file, params); + + var _params$method = params.method, + method = _params$method === void 0 ? 'post' : _params$method, + url = params.url, + fields = params.fields, + headers = params.headers; + var xhrOpts = { + method: method, + formData: method.toLowerCase() === 'post', + endpoint: url, + metaFields: fields ? Object.keys(fields) : [] + }; + + if (headers) { + xhrOpts.headers = headers; + } + + _this2.uppy.setFileState(file.id, { + meta: _extends({}, file.meta, fields), + xhrUpload: xhrOpts + }); + + return _this2._uploader.uploadFile(file.id, index, numberOfFiles); + }).catch(function (error) { + delete paramsPromises[id]; + + var file = _this2.uppy.getFile(id); + + _this2.uppy.emit('upload-error', file, error); + }); + })).then(function (settled) { + // cleanup. + _this2.uppy.off('file-removed', onremove); + + return settled; + }); + }; + + _proto.install = function install() { + var uppy = this.uppy; + this.uppy.addUploader(this.handleUpload); // Get the response data from a successful XMLHttpRequest instance. + // `content` is the S3 response as a string. + // `xhr` is the XMLHttpRequest instance. + + function defaultGetResponseData(content, xhr) { + var opts = this; // If no response, we've hopefully done a PUT request to the file + // in the bucket on its full URL. + + 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; + } // The responseURL won't contain the object key. Give up. + + + return { + location: null + }; + } // responseURL is not available in older browsers. + + + if (!xhr.responseURL) { + return { + location: null + }; + } // Trim the query string because it's going to be a bunch of presign + // parameters for a PUT request—doing a GET request with those will + // always result in an error + + + return { + location: xhr.responseURL.replace(/\?.*$/, '') + }; + } + + return { + // Some S3 alternatives do not reply with an absolute URL. + // Eg DigitalOcean Spaces uses /$bucketName/xyz + location: resolveUrl(xhr.responseURL, getXmlValue(content, 'Location')), + bucket: getXmlValue(content, 'Bucket'), + key: getXmlValue(content, 'Key'), + etag: getXmlValue(content, 'ETag') + }; + } // Get the error data from a failed XMLHttpRequest instance. + // `content` is the S3 response as a string. + // `xhr` is the XMLHttpRequest instance. + + + function defaultGetResponseError(content, xhr) { + // If no response, we don't have a specific error message, use the default. + if (!isXml(content, xhr)) { + return; + } + + var error = getXmlValue(content, 'Message'); + return new Error(error); + } + + var xhrOptions = { + fieldName: 'file', + responseUrlFieldName: 'location', + timeout: this.opts.timeout, + // Share the rate limiting queue with XHRUpload. + __queue: this.requests, + responseType: 'text', + getResponseData: this.opts.getResponseData || defaultGetResponseData, + getResponseError: defaultGetResponseError + }; // Only for MiniXHRUpload, remove once we can depend on XHRUpload directly again + + xhrOptions.i18n = this.i18n; // Revert to `this.uppy.use(XHRUpload)` once the big comment block at the top of + // this file is solved + + this._uploader = new MiniXHRUpload(this.uppy, xhrOptions); + }; + + _proto.uninstall = function uninstall() { + this.uppy.removeUploader(this.handleUpload); + }; + + return AwsS3; +}(Plugin), _class.VERSION = "1.7.12", _temp); +},{"./MiniXHRUpload":4,"./isXml":6,"@uppy/companion-client":12,"@uppy/core":15,"@uppy/utils/lib/RateLimitedQueue":25,"@uppy/utils/lib/Translator":26,"@uppy/utils/lib/hasProperty":42,"@uppy/utils/lib/settle":46,"qs-stringify":58,"url-parse":61}],6:[function(require,module,exports){ +/** + * Remove parameters like `charset=utf-8` from the end of a mime type string. + * + * @param {string} mimeType - The mime type string that may have optional parameters. + * @returns {string} The "base" mime type, i.e. only 'category/type'. + */ +function removeMimeParameters(mimeType) { + return mimeType.replace(/;.*$/, ''); +} +/** + * Check if a response contains XML based on the response object and its text content. + * + * @param {string} content - The text body of the response. + * @param {object|XMLHttpRequest} xhr - The XHR object or response object from Companion. + * @returns {bool} Whether the content is (probably) XML. + */ + + +function isXml(content, xhr) { + var rawContentType = xhr.headers ? xhr.headers['content-type'] : xhr.getResponseHeader('Content-Type'); + + if (typeof rawContentType === 'string') { + var contentType = removeMimeParameters(rawContentType).toLowerCase(); + + if (contentType === 'application/xml' || contentType === 'text/xml') { + return true; + } // GCS uses text/html for some reason + // https://github.com/transloadit/uppy/issues/896 + + + if (contentType === 'text/html' && /^<\?xml /.test(content)) { + return true; + } + } + + return false; +} + +module.exports = isXml; +},{}],7:[function(require,module,exports){ +'use strict'; + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } + +function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); } + +function _construct(Parent, args, Class) { if (_isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } + +function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } } + +function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } + +var AuthError = /*#__PURE__*/function (_Error) { + _inheritsLoose(AuthError, _Error); + + function AuthError() { + var _this; + + _this = _Error.call(this, 'Authorization required') || this; + _this.name = 'AuthError'; + _this.isAuthError = true; + return _this; + } + + return AuthError; +}( /*#__PURE__*/_wrapNativeSuper(Error)); + +module.exports = AuthError; +},{}],8:[function(require,module,exports){ +'use strict'; + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +var qsStringify = require('qs-stringify'); + +var URL = require('url-parse'); + +var RequestClient = require('./RequestClient'); + +var tokenStorage = require('./tokenStorage'); + +var _getName = function _getName(id) { + return id.split('-').map(function (s) { + return s.charAt(0).toUpperCase() + s.slice(1); + }).join(' '); +}; + +module.exports = /*#__PURE__*/function (_RequestClient) { + _inheritsLoose(Provider, _RequestClient); + + function Provider(uppy, opts) { + var _this; + + _this = _RequestClient.call(this, uppy, opts) || this; + _this.provider = opts.provider; + _this.id = _this.provider; + _this.name = _this.opts.name || _getName(_this.id); + _this.pluginId = _this.opts.pluginId; + _this.tokenKey = "companion-" + _this.pluginId + "-auth-token"; + _this.companionKeysParams = _this.opts.companionKeysParams; + _this.preAuthToken = null; + return _this; + } + + var _proto = Provider.prototype; + + _proto.headers = function headers() { + var _this2 = this; + + return Promise.all([_RequestClient.prototype.headers.call(this), this.getAuthToken()]).then(function (_ref) { + var headers = _ref[0], + token = _ref[1]; + var authHeaders = {}; + + if (token) { + authHeaders['uppy-auth-token'] = token; + } + + if (_this2.companionKeysParams) { + authHeaders['uppy-credentials-params'] = btoa(JSON.stringify({ + params: _this2.companionKeysParams + })); + } + + return _extends({}, headers, authHeaders); + }); + }; + + _proto.onReceiveResponse = function onReceiveResponse(response) { + response = _RequestClient.prototype.onReceiveResponse.call(this, response); + var plugin = this.uppy.getPlugin(this.pluginId); + var oldAuthenticated = plugin.getPluginState().authenticated; + var authenticated = oldAuthenticated ? response.status !== 401 : response.status < 400; + plugin.setPluginState({ + authenticated: authenticated + }); + return response; + } // @todo(i.olarewaju) consider whether or not this method should be exposed + ; + + _proto.setAuthToken = function setAuthToken(token) { + return this.uppy.getPlugin(this.pluginId).storage.setItem(this.tokenKey, token); + }; + + _proto.getAuthToken = function getAuthToken() { + return this.uppy.getPlugin(this.pluginId).storage.getItem(this.tokenKey); + }; + + _proto.authUrl = function authUrl(queries) { + if (queries === void 0) { + queries = {}; + } + + if (this.preAuthToken) { + queries.uppyPreAuthToken = this.preAuthToken; + } + + var strigifiedQueries = qsStringify(queries); + strigifiedQueries = strigifiedQueries ? "?" + strigifiedQueries : strigifiedQueries; + return this.hostname + "/" + this.id + "/connect" + strigifiedQueries; + }; + + _proto.fileUrl = function fileUrl(id) { + return this.hostname + "/" + this.id + "/get/" + id; + }; + + _proto.fetchPreAuthToken = function fetchPreAuthToken() { + var _this3 = this; + + if (!this.companionKeysParams) { + return Promise.resolve(); + } + + return this.post(this.id + "/preauth/", { + params: this.companionKeysParams + }).then(function (res) { + _this3.preAuthToken = res.token; + }).catch(function (err) { + _this3.uppy.log("[CompanionClient] unable to fetch preAuthToken " + err, 'warning'); + }); + }; + + _proto.list = function list(directory) { + return this.get(this.id + "/list/" + (directory || '')); + }; + + _proto.logout = function logout() { + var _this4 = this; + + return this.get(this.id + "/logout").then(function (response) { + return Promise.all([response, _this4.uppy.getPlugin(_this4.pluginId).storage.removeItem(_this4.tokenKey)]); + }).then(function (_ref2) { + var response = _ref2[0]; + return response; + }); + }; + + Provider.initPlugin = function initPlugin(plugin, opts, defaultOpts) { + plugin.type = 'acquirer'; + plugin.files = []; + + if (defaultOpts) { + plugin.opts = _extends({}, defaultOpts, opts); + } + + if (opts.serverUrl || opts.serverPattern) { + throw new Error('`serverUrl` and `serverPattern` have been renamed to `companionUrl` and `companionAllowedHosts` respectively in the 0.30.5 release. Please consult the docs (for example, https://uppy.io/docs/instagram/ for the Instagram plugin) and use the updated options.`'); + } + + if (opts.companionAllowedHosts) { + var pattern = opts.companionAllowedHosts; // validate companionAllowedHosts param + + if (typeof pattern !== 'string' && !Array.isArray(pattern) && !(pattern instanceof RegExp)) { + throw new TypeError(plugin.id + ": the option \"companionAllowedHosts\" must be one of string, Array, RegExp"); + } + + plugin.opts.companionAllowedHosts = pattern; + } else { + // does not start with https:// + if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) { + plugin.opts.companionAllowedHosts = "https://" + opts.companionUrl.replace(/^\/\//, ''); + } else { + plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin; + } + } + + plugin.storage = plugin.opts.storage || tokenStorage; + }; + + return Provider; +}(RequestClient); +},{"./RequestClient":9,"./tokenStorage":13,"qs-stringify":58,"url-parse":61}],9:[function(require,module,exports){ +'use strict'; + +var _class, _temp; + +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } + +function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } + +var AuthError = require('./AuthError'); + +var fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError'); // Remove the trailing slash so we can always safely append /xyz. + + +function stripSlash(url) { + return url.replace(/\/$/, ''); +} + +module.exports = (_temp = _class = /*#__PURE__*/function () { + function RequestClient(uppy, opts) { + this.uppy = uppy; + this.opts = opts; + this.onReceiveResponse = this.onReceiveResponse.bind(this); + this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token']; + this.preflightDone = false; + } + + var _proto = RequestClient.prototype; + + _proto.headers = function headers() { + var userHeaders = this.opts.companionHeaders || this.opts.serverHeaders || {}; + return Promise.resolve(_extends({}, this.defaultHeaders, userHeaders)); + }; + + _proto._getPostResponseFunc = function _getPostResponseFunc(skip) { + var _this = this; + + return function (response) { + if (!skip) { + return _this.onReceiveResponse(response); + } + + return response; + }; + }; + + _proto.onReceiveResponse = function onReceiveResponse(response) { + var state = this.uppy.getState(); + var companion = state.companion || {}; + var host = this.opts.companionUrl; + var headers = response.headers; // Store the self-identified domain name for the Companion instance we just hit. + + if (headers.has('i-am') && headers.get('i-am') !== companion[host]) { + var _extends2; + + this.uppy.setState({ + companion: _extends({}, companion, (_extends2 = {}, _extends2[host] = headers.get('i-am'), _extends2)) + }); + } + + return response; + }; + + _proto._getUrl = function _getUrl(url) { + if (/^(https?:|)\/\//.test(url)) { + return url; + } + + return this.hostname + "/" + url; + }; + + _proto._json = function _json(res) { + if (res.status === 401) { + throw new AuthError(); + } + + if (res.status < 200 || res.status > 300) { + var errMsg = "Failed request with status: " + res.status + ". " + res.statusText; + return res.json().then(function (errData) { + errMsg = errData.message ? errMsg + " message: " + errData.message : errMsg; + errMsg = errData.requestId ? errMsg + " request-Id: " + errData.requestId : errMsg; + throw new Error(errMsg); + }).catch(function () { + throw new Error(errMsg); + }); + } + + return res.json(); + }; + + _proto.preflight = function preflight(path) { + var _this2 = this; + + if (this.preflightDone) { + return Promise.resolve(this.allowedHeaders.slice()); + } + + return fetch(this._getUrl(path), { + method: 'OPTIONS' + }).then(function (response) { + if (response.headers.has('access-control-allow-headers')) { + _this2.allowedHeaders = response.headers.get('access-control-allow-headers').split(',').map(function (headerName) { + return headerName.trim().toLowerCase(); + }); + } + + _this2.preflightDone = true; + return _this2.allowedHeaders.slice(); + }).catch(function (err) { + _this2.uppy.log("[CompanionClient] unable to make preflight request " + err, 'warning'); + + _this2.preflightDone = true; + return _this2.allowedHeaders.slice(); + }); + }; + + _proto.preflightAndHeaders = function preflightAndHeaders(path) { + var _this3 = this; + + return Promise.all([this.preflight(path), this.headers()]).then(function (_ref) { + var allowedHeaders = _ref[0], + headers = _ref[1]; + // filter to keep only allowed Headers + Object.keys(headers).forEach(function (header) { + if (allowedHeaders.indexOf(header.toLowerCase()) === -1) { + _this3.uppy.log("[CompanionClient] excluding unallowed header " + header); + + delete headers[header]; + } + }); + return headers; + }); + }; + + _proto.get = function get(path, skipPostResponse) { + var _this4 = this; + + return this.preflightAndHeaders(path).then(function (headers) { + return fetchWithNetworkError(_this4._getUrl(path), { + method: 'get', + headers: headers, + credentials: _this4.opts.companionCookiesRule || 'same-origin' + }); + }).then(this._getPostResponseFunc(skipPostResponse)).then(function (res) { + return _this4._json(res); + }).catch(function (err) { + if (!err.isAuthError) { + err.message = "Could not get " + _this4._getUrl(path) + ". " + err.message; + } + + return Promise.reject(err); + }); + }; + + _proto.post = function post(path, data, skipPostResponse) { + var _this5 = this; + + return this.preflightAndHeaders(path).then(function (headers) { + return fetchWithNetworkError(_this5._getUrl(path), { + method: 'post', + headers: headers, + credentials: _this5.opts.companionCookiesRule || 'same-origin', + body: JSON.stringify(data) + }); + }).then(this._getPostResponseFunc(skipPostResponse)).then(function (res) { + return _this5._json(res); + }).catch(function (err) { + if (!err.isAuthError) { + err.message = "Could not post " + _this5._getUrl(path) + ". " + err.message; + } + + return Promise.reject(err); + }); + }; + + _proto.delete = function _delete(path, data, skipPostResponse) { + var _this6 = this; + + return this.preflightAndHeaders(path).then(function (headers) { + return fetchWithNetworkError(_this6.hostname + "/" + path, { + method: 'delete', + headers: headers, + credentials: _this6.opts.companionCookiesRule || 'same-origin', + body: data ? JSON.stringify(data) : null + }); + }).then(this._getPostResponseFunc(skipPostResponse)).then(function (res) { + return _this6._json(res); + }).catch(function (err) { + if (!err.isAuthError) { + err.message = "Could not delete " + _this6._getUrl(path) + ". " + err.message; + } + + return Promise.reject(err); + }); + }; + + _createClass(RequestClient, [{ + key: "hostname", + get: function get() { + var _this$uppy$getState = this.uppy.getState(), + companion = _this$uppy$getState.companion; + + var host = this.opts.companionUrl; + return stripSlash(companion && companion[host] ? companion[host] : host); + } + }, { + key: "defaultHeaders", + get: function get() { + return { + Accept: 'application/json', + 'Content-Type': 'application/json', + 'Uppy-Versions': "@uppy/companion-client=" + RequestClient.VERSION + }; + } + }]); + + return RequestClient; +}(), _class.VERSION = "1.10.2", _temp); +},{"./AuthError":7,"@uppy/utils/lib/fetchWithNetworkError":29}],10:[function(require,module,exports){ +'use strict'; + +function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); } + +function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } + +var RequestClient = require('./RequestClient'); + +var _getName = function _getName(id) { + return id.split('-').map(function (s) { + return s.charAt(0).toUpperCase() + s.slice(1); + }).join(' '); +}; + +module.exports = /*#__PURE__*/function (_RequestClient) { + _inheritsLoose(SearchProvider, _RequestClient); + + function SearchProvider(uppy, opts) { + var _this; + + _this = _RequestClient.call(this, uppy, opts) || this; + _this.provider = opts.provider; + _this.id = _this.provider; + _this.name = _this.opts.name || _getName(_this.id); + _this.pluginId = _this.opts.pluginId; + return _this; + } + + var _proto = SearchProvider.prototype; + + _proto.fileUrl = function fileUrl(id) { + return this.hostname + "/search/" + this.id + "/get/" + id; + }; + + _proto.search = function search(text, queries) { + queries = queries ? "&" + queries : ''; + return this.get("search/" + this.id + "/list?q=" + encodeURIComponent(text) + queries); + }; + + return SearchProvider; +}(RequestClient); +},{"./RequestClient":9}],11:[function(require,module,exports){ +var ee = require('namespace-emitter'); + +module.exports = /*#__PURE__*/function () { + function UppySocket(opts) { + this.opts = opts; + this._queued = []; + this.isOpen = false; + this.emitter = ee(); + this._handleMessage = this._handleMessage.bind(this); + this.close = this.close.bind(this); + this.emit = this.emit.bind(this); + this.on = this.on.bind(this); + this.once = this.once.bind(this); + this.send = this.send.bind(this); + + if (!opts || opts.autoOpen !== false) { + this.open(); + } + } + + var _proto = UppySocket.prototype; + + _proto.open = function open() { + var _this = this; + + this.socket = new WebSocket(this.opts.target); + + this.socket.onopen = function (e) { + _this.isOpen = true; + + while (_this._queued.length > 0 && _this.isOpen) { + var first = _this._queued[0]; + + _this.send(first.action, first.payload); + + _this._queued = _this._queued.slice(1); + } + }; + + this.socket.onclose = function (e) { + _this.isOpen = false; + }; + + this.socket.onmessage = this._handleMessage; + }; + + _proto.close = function close() { + if (this.socket) { + this.socket.close(); + } + }; + + _proto.send = function send(action, payload) { + // attach uuid + if (!this.isOpen) { + this._queued.push({ + action: action, + payload: payload + }); + + return; + } + + this.socket.send(JSON.stringify({ + action: action, + payload: payload + })); + }; + + _proto.on = function on(action, handler) { + this.emitter.on(action, handler); + }; + + _proto.emit = function emit(action, payload) { + this.emitter.emit(action, payload); + }; + + _proto.once = function once(action, handler) { + this.emitter.once(action, handler); + }; + + _proto._handleMessage = function _handleMessage(e) { + try { + var message = JSON.parse(e.data); + this.emit(message.action, message.payload); + } catch (err) { + console.log(err); + } + }; + + return UppySocket; +}(); +},{"namespace-emitter":56}],12:[function(require,module,exports){ +'use strict'; +/** + * Manages communications with Companion + */ + +var RequestClient = require('./RequestClient'); + +var Provider = require('./Provider'); + +var SearchProvider = require('./SearchProvider'); + +var Socket = require('./Socket'); + +module.exports = { + RequestClient: RequestClient, + Provider: Provider, + SearchProvider: SearchProvider, + Socket: Socket +}; +},{"./Provider":8,"./RequestClient":9,"./SearchProvider":10,"./Socket":11}],13:[function(require,module,exports){ +'use strict'; +/** + * This module serves as an Async wrapper for LocalStorage + */ + +module.exports.setItem = function (key, value) { + return new Promise(function (resolve) { + localStorage.setItem(key, value); + resolve(); + }); +}; + +module.exports.getItem = function (key) { + return Promise.resolve(localStorage.getItem(key)); +}; + +module.exports.removeItem = function (key) { + return new Promise(function (resolve) { + localStorage.removeItem(key); + resolve(); + }); +}; +},{}],14:[function(require,module,exports){ +function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } + +var preact = require('preact'); + +var findDOMElement = require('@uppy/utils/lib/findDOMElement'); +/** + * Defer a frequent call to the microtask queue. + */ + + +function debounce(fn) { + var calling = null; + var latestArgs = null; + return function () { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + latestArgs = args; + + if (!calling) { + calling = Promise.resolve().then(function () { + calling = null; // At this point `args` may be different from the most + // recent state, if multiple calls happened since this task + // was queued. So we use the `latestArgs`, which definitely + // is the most recent call. + + return fn.apply(void 0, latestArgs); + }); + } + + return calling; + }; +} +/** + * Boilerplate that all Plugins share - and should not be used + * directly. It also shows which methods final plugins should implement/override, + * this deciding on structure. + * + * @param {object} main Uppy core object + * @param {object} object with plugin options + * @returns {Array|string} files or success/fail message + */ + + +module.exports = /*#__PURE__*/function () { + function Plugin(uppy, opts) { + this.uppy = uppy; + this.opts = opts || {}; + this.update = this.update.bind(this); + this.mount = this.mount.bind(this); + this.install = this.install.bind(this); + this.uninstall = this.uninstall.bind(this); + } + + var _proto = Plugin.prototype; + + _proto.getPluginState = function getPluginState() { + var _this$uppy$getState = this.uppy.getState(), + plugins = _this$uppy$getState.plugins; + + return plugins[this.id] || {}; + }; + + _proto.setPluginState = function setPluginState(update) { + var _extends2; + + var _this$uppy$getState2 = this.uppy.getState(), + plugins = _this$uppy$getState2.plugins; + + this.uppy.setState({ + plugins: _extends({}, plugins, (_extends2 = {}, _extends2[this.id] = _extends({}, plugins[this.id], update), _extends2)) + }); + }; + + _proto.setOptions = function setOptions(newOpts) { + this.opts = _extends({}, this.opts, newOpts); + this.setPluginState(); // so that UI re-renders with new options + }; + + _proto.update = function update(state) { + if (typeof this.el === 'undefined') { + return; + } + + if (this._updateUI) { + this._updateUI(state); + } + } // Called after every state update, after everything's mounted. Debounced. + ; + + _proto.afterUpdate = function afterUpdate() {} + /** + * Called when plugin is mounted, whether in DOM or into another plugin. + * Needed because sometimes plugins are mounted separately/after `install`, + * so this.el and this.parent might not be available in `install`. + * This is the case with @uppy/react plugins, for example. + */ + ; + + _proto.onMount = function onMount() {} + /** + * Check if supplied `target` is a DOM element or an `object`. + * If it’s an object — target is a plugin, and we search `plugins` + * for a plugin with same name and return its target. + * + * @param {string|object} target + * + */ + ; + + _proto.mount = function mount(target, plugin) { + var _this = this; + + var callerPluginName = plugin.id; + var targetElement = findDOMElement(target); + + if (targetElement) { + this.isTargetDOMEl = true; // API for plugins that require a synchronous rerender. + + this.rerender = function (state) { + // plugin could be removed, but this.rerender is debounced below, + // so it could still be called even after uppy.removePlugin or uppy.close + // hence the check + if (!_this.uppy.getPlugin(_this.id)) return; + _this.el = preact.render(_this.render(state), targetElement, _this.el); + + _this.afterUpdate(); + }; + + this._updateUI = debounce(this.rerender); + this.uppy.log("Installing " + callerPluginName + " to a DOM element '" + target + "'"); // clear everything inside the target container + + if (this.opts.replaceTargetContent) { + targetElement.innerHTML = ''; + } + + this.el = preact.render(this.render(this.uppy.getState()), targetElement); + this.onMount(); + return this.el; + } + + var targetPlugin; + + if (typeof target === 'object' && target instanceof Plugin) { + // Targeting a plugin *instance* + targetPlugin = target; + } else if (typeof target === 'function') { + // Targeting a plugin type + var Target = target; // Find the target plugin instance. + + this.uppy.iteratePlugins(function (plugin) { + if (plugin instanceof Target) { + targetPlugin = plugin; + return false; + } + }); + } + + if (targetPlugin) { + this.uppy.log("Installing " + callerPluginName + " to " + targetPlugin.id); + this.parent = targetPlugin; + this.el = targetPlugin.addTarget(plugin); + this.onMount(); + return this.el; + } + + this.uppy.log("Not installing " + callerPluginName); + var message = "Invalid target option given to " + callerPluginName + "."; + + if (typeof target === 'function') { + message += ' The given target is not a Plugin class. ' + 'Please check that you\'re not specifying a React Component instead of a plugin. ' + 'If you are using @uppy/* packages directly, make sure you have only 1 version of @uppy/core installed: ' + 'run `npm ls @uppy/core` on the command line and verify that all the versions match and are deduped correctly.'; + } else { + message += 'If you meant to target an HTML element, please make sure that the element exists. ' + 'Check that the