(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":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