diff --git a/app/assets/javascripts/discourse-shims.js b/app/assets/javascripts/discourse-shims.js
index b63d4a6fce8..8a0cf56cb3d 100644
--- a/app/assets/javascripts/discourse-shims.js
+++ b/app/assets/javascripts/discourse-shims.js
@@ -44,3 +44,11 @@ define("@popperjs/core", ["exports"], function (__exports__) {
__exports__.defaultModifiers = window.Popper.defaultModifiers;
__exports__.popperGenerator = window.Popper.popperGenerator;
});
+
+define("uppy", ["exports"], function (__exports__) {
+ __exports__.default = window.Uppy.Core;
+ __exports__.Plugin = window.Uppy.Plugin;
+ __exports__.XHRUpload = window.Uppy.XHRUpload;
+ __exports__.AwsS3 = window.Uppy.AwsS3;
+ __exports__.AwsS3Multipart = window.Uppy.AwsS3Multipart;
+});
diff --git a/app/assets/javascripts/vendor.js b/app/assets/javascripts/vendor.js
index f28366faa38..1e0b0c6d1cf 100644
--- a/app/assets/javascripts/vendor.js
+++ b/app/assets/javascripts/vendor.js
@@ -22,6 +22,7 @@
//= require mousetrap-global-bind.js
//= require rsvp.js
//= require show-html.js
+//= require uppy.js
//= require buffered-proxy
//= require jquery.autoellipsis-1.0.10
//= require virtual-dom
diff --git a/lib/tasks/javascript.rake b/lib/tasks/javascript.rake
index 834a3804c31..510a1ff0b78 100644
--- a/lib/tasks/javascript.rake
+++ b/lib/tasks/javascript.rake
@@ -217,7 +217,10 @@ def dependencies
public: true,
skip_versioning: true
},
-
+ {
+ source: 'custom-uppy-build.js',
+ destination: 'uppy.js'
+ }
]
end
@@ -339,7 +342,14 @@ task 'javascript:update' => 'clean_up' do
# lodash.js needs building
if src.include? "lodash.js"
puts "Building custom lodash.js build"
- system('yarn run lodash include="each,filter,map,range,first,isEmpty,chain,extend,every,omit,merge,union,sortBy,uniq,intersection,reject,compact,reduce,debounce,throttle,values,pick,keys,flatten,min,max,isArray,delay,isString,isEqual,without,invoke,clone,findIndex,find,groupBy" minus="template" -d -o "node_modules/lodash.js"')
+ system('yarn run lodash include="escapeRegExp,each,filter,map,range,first,isEmpty,chain,extend,every,omit,merge,union,sortBy,uniq,intersection,reject,compact,reduce,debounce,throttle,values,pick,keys,flatten,min,max,isArray,delay,isString,isEqual,without,invoke,clone,findIndex,find,groupBy" minus="template" -d -o "node_modules/lodash.js"')
+ end
+
+ # we need a custom build of uppy because we cannot import
+ # their modules easily, using browserify to do so
+ if src.include? "custom-uppy-build"
+ puts "Building custom uppy using browserify"
+ system("yarn run browserify #{vendor_js}/custom-uppy.js -o node_modules/custom-uppy-build.js")
end
unless File.exists?(dest)
diff --git a/package.json b/package.json
index 0b8fddf98df..327e022022b 100644
--- a/package.json
+++ b/package.json
@@ -10,6 +10,10 @@
"@highlightjs/cdn-assets": "^10.6.0",
"@json-editor/json-editor": "^2.5.2",
"@popperjs/core": "v2.0.6",
+ "@uppy/aws-s3": "^1.7.12",
+ "@uppy/aws-s3-multipart": "^1.8.18",
+ "@uppy/core": "^1.19.2",
+ "@uppy/xhr-upload": "^1.7.5",
"ace-builds": "1.4.12",
"blueimp-file-upload": "10.13.0",
"bootbox": "3.2.0",
@@ -42,6 +46,7 @@
"devDependencies": {
"@arkweid/lefthook": "^0.7.2",
"@mixer/parallel-prettier": "^2.0.1",
+ "browserify": "^17.0.0",
"chrome-launcher": "^0.12.0",
"chrome-remote-interface": "^0.25",
"lodash-cli": "https://github.com/lodash-archive/lodash-cli.git",
diff --git a/vendor/assets/javascripts/custom-uppy.js b/vendor/assets/javascripts/custom-uppy.js
new file mode 100644
index 00000000000..8044448f189
--- /dev/null
+++ b/vendor/assets/javascripts/custom-uppy.js
@@ -0,0 +1,11 @@
+// We need a custom build of Uppy because we do not use webpack for
+// our JS modules/build. The only way to get what you want from Uppy
+// is to use the webpack modules or to include the entire Uppy project
+// including all plugins in a single JS file. This way we can just
+// use the plugins we actually want.
+window.Uppy = {}
+Uppy.Core = require('@uppy/core')
+Uppy.Plugin = Uppy.Core.Plugin
+Uppy.XHRUpload = require('@uppy/xhr-upload')
+Uppy.AwsS3 = require('@uppy/aws-s3')
+Uppy.AwsS3Multipart = require('@uppy/aws-s3-multipart')
diff --git a/vendor/assets/javascripts/lodash.js b/vendor/assets/javascripts/lodash.js
index 9f352389c12..b8f8a9b2fe3 100644
--- a/vendor/assets/javascripts/lodash.js
+++ b/vendor/assets/javascripts/lodash.js
@@ -1,7 +1,7 @@
/**
* @license
* Lodash (Custom Build)
- * Build: `lodash include="each,filter,map,range,first,isEmpty,chain,extend,every,omit,merge,union,sortBy,uniq,intersection,reject,compact,reduce,debounce,throttle,values,pick,keys,flatten,min,max,isArray,delay,isString,isEqual,without,invoke,clone,findIndex,find,groupBy" minus="template" -d -o node_modules/lodash.js`
+ * Build: `lodash include="escapeRegExp,each,filter,map,range,first,isEmpty,chain,extend,every,omit,merge,union,sortBy,uniq,intersection,reject,compact,reduce,debounce,throttle,values,pick,keys,flatten,min,max,isArray,delay,isString,isEqual,without,invoke,clone,findIndex,find,groupBy" minus="template" -d -o node_modules/lodash.js`
* Copyright OpenJS Foundation and other contributors
* Released under MIT license
* Based on Underscore.js 1.8.3
@@ -125,7 +125,8 @@
* Used to match `RegExp`
* [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
*/
- var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+ var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
+ reHasRegExpChar = RegExp(reRegExpChar.source);
/** Used to match leading whitespace. */
var reTrimStart = /^\s+/;
@@ -7373,6 +7374,30 @@
/*------------------------------------------------------------------------*/
+ /**
+ * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
+ * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
+ *
+ * @static
+ * @memberOf _
+ * @since 3.0.0
+ * @category String
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escapeRegExp('[lodash](https://lodash.com/)');
+ * // => '\[lodash\]\(https://lodash\.com/\)'
+ */
+ function escapeRegExp(string) {
+ string = toString(string);
+ return (string && reHasRegExpChar.test(string))
+ ? string.replace(reRegExpChar, '\\$&')
+ : string;
+ }
+
+ /*------------------------------------------------------------------------*/
+
/**
* Creates a function that returns `value`.
*
@@ -7759,6 +7784,7 @@
// Add methods that return unwrapped values in chain sequences.
lodash.clone = clone;
lodash.eq = eq;
+ lodash.escapeRegExp = escapeRegExp;
lodash.every = every;
lodash.find = find;
lodash.findIndex = findIndex;
diff --git a/vendor/assets/javascripts/uppy.js b/vendor/assets/javascripts/uppy.js
new file mode 100644
index 00000000000..db07801d276
--- /dev/null
+++ b/vendor/assets/javascripts/uppy.js
@@ -0,0 +1,8576 @@
+(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= 10 || num % 1 === 0) {
+ // Do not show decimals when the number is two-digit, or if the number has no
+ // decimal component.
+ return (neg ? '-' : '') + num.toFixed(0) + ' ' + unit
+ } else {
+ return (neg ? '-' : '') + num.toFixed(1) + ' ' + unit
+ }
+}
+
+},{}],2:[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 _require = require('@uppy/utils/lib/AbortController'),
+ AbortController = _require.AbortController,
+ createAbortError = _require.createAbortError;
+
+var delay = require('@uppy/utils/lib/delay');
+
+var MB = 1024 * 1024;
+var defaultOptions = {
+ limit: 1,
+ retryDelays: [0, 1000, 3000, 5000],
+ getChunkSize: function getChunkSize(file) {
+ return Math.ceil(file.size / 10000);
+ },
+ onStart: function onStart() {},
+ onProgress: function onProgress() {},
+ onPartComplete: function onPartComplete() {},
+ onSuccess: function onSuccess() {},
+ onError: function onError(err) {
+ throw err;
+ }
+};
+
+function ensureInt(value) {
+ if (typeof value === 'string') {
+ return parseInt(value, 10);
+ }
+
+ if (typeof value === 'number') {
+ return value;
+ }
+
+ throw new TypeError('Expected a number');
+}
+
+var MultipartUploader = /*#__PURE__*/function () {
+ function MultipartUploader(file, options) {
+ this.options = _extends({}, defaultOptions, options); // Use default `getChunkSize` if it was null or something
+
+ if (!this.options.getChunkSize) {
+ this.options.getChunkSize = defaultOptions.getChunkSize;
+ }
+
+ this.file = file;
+ this.abortController = new AbortController();
+ this.key = this.options.key || null;
+ this.uploadId = this.options.uploadId || null;
+ this.parts = []; // Do `this.createdPromise.then(OP)` to execute an operation `OP` _only_ if the
+ // upload was created already. That also ensures that the sequencing is right
+ // (so the `OP` definitely happens if the upload is created).
+ //
+ // This mostly exists to make `_abortUpload` work well: only sending the abort request if
+ // the upload was already created, and if the createMultipartUpload request is still in flight,
+ // aborting it immediately after it finishes.
+
+ this.createdPromise = Promise.reject(); // eslint-disable-line prefer-promise-reject-errors
+
+ this.isPaused = false;
+ this.partsInProgress = 0;
+ this.chunks = null;
+ this.chunkState = null;
+
+ this._initChunks();
+
+ this.createdPromise.catch(function () {}); // silence uncaught rejection warning
+ }
+ /**
+ * Was this upload aborted?
+ *
+ * If yes, we may need to throw an AbortError.
+ *
+ * @returns {boolean}
+ */
+
+
+ var _proto = MultipartUploader.prototype;
+
+ _proto._aborted = function _aborted() {
+ return this.abortController.signal.aborted;
+ };
+
+ _proto._initChunks = function _initChunks() {
+ var chunks = [];
+ var desiredChunkSize = this.options.getChunkSize(this.file); // at least 5MB per request, at most 10k requests
+
+ var minChunkSize = Math.max(5 * MB, Math.ceil(this.file.size / 10000));
+ var chunkSize = Math.max(desiredChunkSize, minChunkSize); // Upload zero-sized files in one zero-sized chunk
+
+ if (this.file.size === 0) {
+ chunks.push(this.file);
+ } else {
+ for (var i = 0; i < this.file.size; i += chunkSize) {
+ var end = Math.min(this.file.size, i + chunkSize);
+ chunks.push(this.file.slice(i, end));
+ }
+ }
+
+ this.chunks = chunks;
+ this.chunkState = chunks.map(function () {
+ return {
+ uploaded: 0,
+ busy: false,
+ done: false
+ };
+ });
+ };
+
+ _proto._createUpload = function _createUpload() {
+ var _this = this;
+
+ this.createdPromise = Promise.resolve().then(function () {
+ return _this.options.createMultipartUpload();
+ });
+ return this.createdPromise.then(function (result) {
+ if (_this._aborted()) throw createAbortError();
+ var valid = typeof result === 'object' && result && typeof result.uploadId === 'string' && typeof result.key === 'string';
+
+ if (!valid) {
+ throw new TypeError('AwsS3/Multipart: Got incorrect result from `createMultipartUpload()`, expected an object `{ uploadId, key }`.');
+ }
+
+ _this.key = result.key;
+ _this.uploadId = result.uploadId;
+
+ _this.options.onStart(result);
+
+ _this._uploadParts();
+ }).catch(function (err) {
+ _this._onError(err);
+ });
+ };
+
+ _proto._resumeUpload = function _resumeUpload() {
+ var _this2 = this;
+
+ return Promise.resolve().then(function () {
+ return _this2.options.listParts({
+ uploadId: _this2.uploadId,
+ key: _this2.key
+ });
+ }).then(function (parts) {
+ if (_this2._aborted()) throw createAbortError();
+ parts.forEach(function (part) {
+ var i = part.PartNumber - 1;
+ _this2.chunkState[i] = {
+ uploaded: ensureInt(part.Size),
+ etag: part.ETag,
+ done: true
+ }; // Only add if we did not yet know about this part.
+
+ if (!_this2.parts.some(function (p) {
+ return p.PartNumber === part.PartNumber;
+ })) {
+ _this2.parts.push({
+ PartNumber: part.PartNumber,
+ ETag: part.ETag
+ });
+ }
+ });
+
+ _this2._uploadParts();
+ }).catch(function (err) {
+ _this2._onError(err);
+ });
+ };
+
+ _proto._uploadParts = function _uploadParts() {
+ var _this3 = this;
+
+ if (this.isPaused) return;
+ var need = this.options.limit - this.partsInProgress;
+ if (need === 0) return; // All parts are uploaded.
+
+ if (this.chunkState.every(function (state) {
+ return state.done;
+ })) {
+ this._completeUpload();
+
+ return;
+ }
+
+ var candidates = [];
+
+ for (var i = 0; i < this.chunkState.length; i++) {
+ var state = this.chunkState[i];
+ if (state.done || state.busy) continue;
+ candidates.push(i);
+
+ if (candidates.length >= 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":20,"@uppy/utils/lib/delay":26}],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":21,"@uppy/utils/lib/RateLimitedQueue":24,"@uppy/utils/lib/emitSocketProgress":27,"@uppy/utils/lib/getSocketHost":34}],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":21,"@uppy/utils/lib/NetworkError":22,"@uppy/utils/lib/ProgressTimeout":23,"@uppy/utils/lib/emitSocketProgress":27,"@uppy/utils/lib/getSocketHost":34,"@uppy/utils/lib/isNetworkError":38,"cuid":43}],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("" + tagName + ">", 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":24,"@uppy/utils/lib/Translator":25,"@uppy/utils/lib/hasProperty":36,"@uppy/utils/lib/settle":40,"qs-stringify":51,"url-parse":54}],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":51,"url-parse":54}],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":28}],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":49}],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