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