(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<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ // Adapted from https://github.com/Flet/prettier-bytes/ // Changing 1000 bytes to 1024, so we can keep uppercase KB vs kB // ISC License (c) Dan Flettre https://github.com/Flet/prettier-bytes/blob/master/LICENSE module.exports = function prettierBytes (num) { if (typeof num !== 'number' || isNaN(num)) { throw new TypeError('Expected a number, got ' + typeof num) } var neg = num < 0 var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] if (neg) { num = -num } if (num < 1) { return (neg ? '-' : '') + num + ' B' } var exponent = Math.min(Math.floor(Math.log(num) / Math.log(1024)), units.length - 1) num = Number(num / Math.pow(1024, exponent)) var unit = units[exponent] if (num >= 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){ "use strict"; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const { AbortController, createAbortError } = require('@uppy/utils/lib/AbortController'); const delay = require('@uppy/utils/lib/delay'); const MB = 1024 * 1024; const defaultOptions = { limit: 1, retryDelays: [0, 1000, 3000, 5000], getChunkSize(file) { return Math.ceil(file.size / 10000); }, onStart() {}, onProgress() {}, onPartComplete() {}, onSuccess() {}, 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 _aborted = /*#__PURE__*/_classPrivateFieldLooseKey("aborted"); var _initChunks = /*#__PURE__*/_classPrivateFieldLooseKey("initChunks"); var _createUpload = /*#__PURE__*/_classPrivateFieldLooseKey("createUpload"); var _resumeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("resumeUpload"); var _uploadParts = /*#__PURE__*/_classPrivateFieldLooseKey("uploadParts"); var _retryable = /*#__PURE__*/_classPrivateFieldLooseKey("retryable"); var _prepareUploadParts = /*#__PURE__*/_classPrivateFieldLooseKey("prepareUploadParts"); var _uploadPartRetryable = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPartRetryable"); var _uploadPart = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPart"); var _onPartProgress = /*#__PURE__*/_classPrivateFieldLooseKey("onPartProgress"); var _onPartComplete = /*#__PURE__*/_classPrivateFieldLooseKey("onPartComplete"); var _uploadPartBytes = /*#__PURE__*/_classPrivateFieldLooseKey("uploadPartBytes"); var _completeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("completeUpload"); var _abortUpload = /*#__PURE__*/_classPrivateFieldLooseKey("abortUpload"); var _onError = /*#__PURE__*/_classPrivateFieldLooseKey("onError"); class MultipartUploader { constructor(file, options) { Object.defineProperty(this, _onError, { value: _onError2 }); Object.defineProperty(this, _abortUpload, { value: _abortUpload2 }); Object.defineProperty(this, _completeUpload, { value: _completeUpload2 }); Object.defineProperty(this, _uploadPartBytes, { value: _uploadPartBytes2 }); Object.defineProperty(this, _onPartComplete, { value: _onPartComplete2 }); Object.defineProperty(this, _onPartProgress, { value: _onPartProgress2 }); Object.defineProperty(this, _uploadPart, { value: _uploadPart2 }); Object.defineProperty(this, _uploadPartRetryable, { value: _uploadPartRetryable2 }); Object.defineProperty(this, _prepareUploadParts, { value: _prepareUploadParts2 }); Object.defineProperty(this, _retryable, { value: _retryable2 }); Object.defineProperty(this, _uploadParts, { value: _uploadParts2 }); Object.defineProperty(this, _resumeUpload, { value: _resumeUpload2 }); Object.defineProperty(this, _createUpload, { value: _createUpload2 }); Object.defineProperty(this, _initChunks, { value: _initChunks2 }); Object.defineProperty(this, _aborted, { value: _aborted2 }); this.options = { ...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.lockedCandidatesForBatch = []; _classPrivateFieldLooseBase(this, _initChunks)[_initChunks](); this.createdPromise.catch(() => {}); // silence uncaught rejection warning } /** * Was this upload aborted? * * If yes, we may need to throw an AbortError. * * @returns {boolean} */ start() { this.isPaused = false; if (this.uploadId) { _classPrivateFieldLooseBase(this, _resumeUpload)[_resumeUpload](); } else { _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](); } } 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; } abort(opts = undefined) { if (opts != null && opts.really) _classPrivateFieldLooseBase(this, _abortUpload)[_abortUpload]();else this.pause(); } } function _aborted2() { return this.abortController.signal.aborted; } function _initChunks2() { const chunks = []; const desiredChunkSize = this.options.getChunkSize(this.file); // at least 5MB per request, at most 10k requests const minChunkSize = Math.max(5 * MB, Math.ceil(this.file.size / 10000)); const 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 (let i = 0; i < this.file.size; i += chunkSize) { const end = Math.min(this.file.size, i + chunkSize); chunks.push(this.file.slice(i, end)); } } this.chunks = chunks; this.chunkState = chunks.map(() => ({ uploaded: 0, busy: false, done: false })); } function _createUpload2() { this.createdPromise = Promise.resolve().then(() => this.options.createMultipartUpload()); return this.createdPromise.then(result => { if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError(); const 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); _classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts](); }).catch(err => { _classPrivateFieldLooseBase(this, _onError)[_onError](err); }); } async function _resumeUpload2() { try { const parts = await this.options.listParts({ uploadId: this.uploadId, key: this.key }); if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError(); parts.forEach(part => { const i = part.PartNumber - 1; this.chunkState[i] = { uploaded: ensureInt(part.Size), etag: part.ETag, done: true }; // Only add if we did not yet know about this part. if (!this.parts.some(p => p.PartNumber === part.PartNumber)) { this.parts.push({ PartNumber: part.PartNumber, ETag: part.ETag }); } }); _classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts](); } catch (err) { _classPrivateFieldLooseBase(this, _onError)[_onError](err); } } function _uploadParts2() { if (this.isPaused) return; // All parts are uploaded. if (this.chunkState.every(state => state.done)) { _classPrivateFieldLooseBase(this, _completeUpload)[_completeUpload](); return; } // For a 100MB file, with the default min chunk size of 5MB and a limit of 10: // // Total 20 parts // --------- // Need 1 is 10 // Need 2 is 5 // Need 3 is 5 const need = this.options.limit - this.partsInProgress; const completeChunks = this.chunkState.filter(state => state.done).length; const remainingChunks = this.chunks.length - completeChunks; let minNeeded = Math.ceil(this.options.limit / 2); if (minNeeded > remainingChunks) { minNeeded = remainingChunks; } if (need < minNeeded) return; const candidates = []; for (let i = 0; i < this.chunkState.length; i++) { // eslint-disable-next-line no-continue if (this.lockedCandidatesForBatch.includes(i)) continue; const state = this.chunkState[i]; // eslint-disable-next-line no-continue if (state.done || state.busy) continue; candidates.push(i); if (candidates.length >= need) { break; } } if (candidates.length === 0) return; _classPrivateFieldLooseBase(this, _prepareUploadParts)[_prepareUploadParts](candidates).then(result => { candidates.forEach(index => { const partNumber = index + 1; const prePreparedPart = { url: result.presignedUrls[partNumber], headers: result.headers }; _classPrivateFieldLooseBase(this, _uploadPartRetryable)[_uploadPartRetryable](index, prePreparedPart).then(() => { _classPrivateFieldLooseBase(this, _uploadParts)[_uploadParts](); }, err => { _classPrivateFieldLooseBase(this, _onError)[_onError](err); }); }); }); } function _retryable2({ before, attempt, after }) { const { retryDelays } = this.options; const { signal } = this.abortController; if (before) before(); function shouldRetry(err) { if (err.source && typeof err.source.status === 'number') { const { status } = err.source; // 0 probably indicates network failure return status === 0 || status === 409 || status === 423 || status >= 500 && status < 600; } return false; } const doAttempt = retryAttempt => attempt().catch(err => { if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) throw createAbortError(); if (shouldRetry(err) && retryAttempt < retryDelays.length) { return delay(retryDelays[retryAttempt], { signal }).then(() => doAttempt(retryAttempt + 1)); } throw err; }); return doAttempt(0).then(result => { if (after) after(); return result; }, err => { if (after) after(); throw err; }); } async function _prepareUploadParts2(candidates) { this.lockedCandidatesForBatch.push(...candidates); const result = await _classPrivateFieldLooseBase(this, _retryable)[_retryable]({ attempt: () => this.options.prepareUploadParts({ key: this.key, uploadId: this.uploadId, partNumbers: candidates.map(index => index + 1) }) }); if (typeof (result == null ? void 0 : result.presignedUrls) !== 'object') { throw new TypeError('AwsS3/Multipart: Got incorrect result from `prepareUploadParts()`, expected an object `{ presignedUrls }`.'); } return result; } function _uploadPartRetryable2(index, prePreparedPart) { return _classPrivateFieldLooseBase(this, _retryable)[_retryable]({ before: () => { this.partsInProgress += 1; }, attempt: () => _classPrivateFieldLooseBase(this, _uploadPart)[_uploadPart](index, prePreparedPart), after: () => { this.partsInProgress -= 1; } }); } function _uploadPart2(index, prePreparedPart) { this.chunkState[index].busy = true; const valid = typeof (prePreparedPart == null ? void 0 : prePreparedPart.url) === 'string'; if (!valid) { throw new TypeError('AwsS3/Multipart: Got incorrect result for `prePreparedPart`, expected an object `{ url }`.'); } const { url, headers } = prePreparedPart; if (_classPrivateFieldLooseBase(this, _aborted)[_aborted]()) { this.chunkState[index].busy = false; throw createAbortError(); } return _classPrivateFieldLooseBase(this, _uploadPartBytes)[_uploadPartBytes](index, url, headers); } function _onPartProgress2(index, sent) { this.chunkState[index].uploaded = ensureInt(sent); const totalUploaded = this.chunkState.reduce((n, c) => n + c.uploaded, 0); this.options.onProgress(totalUploaded, this.file.size); } function _onPartComplete2(index, etag) { this.chunkState[index].etag = etag; this.chunkState[index].done = true; const part = { PartNumber: index + 1, ETag: etag }; this.parts.push(part); this.options.onPartComplete(part); } function _uploadPartBytes2(index, url, headers) { const body = this.chunks[index]; const { signal } = this.abortController; let defer; const promise = new Promise((resolve, reject) => { defer = { resolve, reject }; }); const xhr = new XMLHttpRequest(); xhr.open('PUT', url, true); if (headers) { Object.keys(headers).forEach(key => { xhr.setRequestHeader(key, headers[key]); }); } xhr.responseType = 'text'; function cleanup() { // eslint-disable-next-line no-use-before-define signal.removeEventListener('abort', onabort); } function onabort() { xhr.abort(); } signal.addEventListener('abort', onabort); xhr.upload.addEventListener('progress', ev => { if (!ev.lengthComputable) return; _classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](index, ev.loaded, ev.total); }); xhr.addEventListener('abort', () => { cleanup(); this.chunkState[index].busy = false; defer.reject(createAbortError()); }); xhr.addEventListener('load', ev => { cleanup(); this.chunkState[index].busy = false; if (ev.target.status < 200 || ev.target.status >= 300) { const error = new Error('Non 2xx'); error.source = ev.target; defer.reject(error); return; } // This avoids the net::ERR_OUT_OF_MEMORY in Chromium Browsers. this.chunks[index] = null; _classPrivateFieldLooseBase(this, _onPartProgress)[_onPartProgress](index, body.size, body.size); // NOTE This must be allowed by CORS. const 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; } _classPrivateFieldLooseBase(this, _onPartComplete)[_onPartComplete](index, etag); defer.resolve(); }); xhr.addEventListener('error', ev => { cleanup(); this.chunkState[index].busy = false; const error = new Error('Unknown error'); error.source = ev.target; defer.reject(error); }); xhr.send(body); return promise; } async function _completeUpload2() { // Parts may not have completed uploading in sorted order, if limit > 1. this.parts.sort((a, b) => a.PartNumber - b.PartNumber); try { const result = await this.options.completeMultipartUpload({ key: this.key, uploadId: this.uploadId, parts: this.parts }); this.options.onSuccess(result); } catch (err) { _classPrivateFieldLooseBase(this, _onError)[_onError](err); } } function _abortUpload2() { this.abortController.abort(); this.createdPromise.then(() => { this.options.abortMultipartUpload({ key: this.key, uploadId: this.uploadId }); }, () => {// if the creation failed we do not need to abort }); } function _onError2(err) { if (err && err.name === 'AbortError') { return; } this.options.onError(err); } module.exports = MultipartUploader; },{"@uppy/utils/lib/AbortController":23,"@uppy/utils/lib/delay":29}],3:[function(require,module,exports){ "use strict"; var _class, _temp; const BasePlugin = require('@uppy/core/lib/BasePlugin'); const { Socket, Provider, RequestClient } = require('@uppy/companion-client'); const EventTracker = require('@uppy/utils/lib/EventTracker'); const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress'); const getSocketHost = require('@uppy/utils/lib/getSocketHost'); const { RateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue'); const Uploader = require('./MultipartUploader'); function assertServerError(res) { if (res && res.error) { const error = new Error(res.message); Object.assign(error, res.error); throw error; } return res; } module.exports = (_temp = _class = class AwsS3Multipart extends BasePlugin { constructor(uppy, opts) { super(uppy, opts); this.type = 'uploader'; this.id = this.opts.id || 'AwsS3Multipart'; this.title = 'AWS S3 Multipart'; this.client = new RequestClient(uppy, opts); const defaultOptions = { timeout: 30 * 1000, limit: 0, retryDelays: [0, 1000, 3000, 5000], createMultipartUpload: this.createMultipartUpload.bind(this), listParts: this.listParts.bind(this), prepareUploadParts: this.prepareUploadParts.bind(this), abortMultipartUpload: this.abortMultipartUpload.bind(this), completeMultipartUpload: this.completeMultipartUpload.bind(this) }; this.opts = { ...defaultOptions, ...opts }; this.upload = this.upload.bind(this); this.requests = new RateLimitedQueue(this.opts.limit); this.uploaders = Object.create(null); this.uploaderEvents = Object.create(null); this.uploaderSockets = Object.create(null); } /** * 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. */ resetUploaderReferences(fileID, 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; } } assertHost(method) { if (!this.opts.companionUrl) { throw new Error(`Expected a \`companionUrl\` option containing a Companion address, or if you are not using Companion, a custom \`${method}\` implementation.`); } } createMultipartUpload(file) { this.assertHost('createMultipartUpload'); const metadata = {}; Object.keys(file.meta).forEach(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 }).then(assertServerError); } listParts(file, { key, uploadId }) { this.assertHost('listParts'); const filename = encodeURIComponent(key); return this.client.get(`s3/multipart/${uploadId}?key=${filename}`).then(assertServerError); } prepareUploadParts(file, { key, uploadId, partNumbers }) { this.assertHost('prepareUploadParts'); const filename = encodeURIComponent(key); return this.client.get(`s3/multipart/${uploadId}/batch?key=${filename}&partNumbers=${partNumbers.join(',')}`).then(assertServerError); } completeMultipartUpload(file, { key, uploadId, parts }) { this.assertHost('completeMultipartUpload'); const filename = encodeURIComponent(key); const uploadIdEnc = encodeURIComponent(uploadId); return this.client.post(`s3/multipart/${uploadIdEnc}/complete?key=${filename}`, { parts }).then(assertServerError); } abortMultipartUpload(file, { key, uploadId }) { this.assertHost('abortMultipartUpload'); const filename = encodeURIComponent(key); const uploadIdEnc = encodeURIComponent(uploadId); return this.client.delete(`s3/multipart/${uploadIdEnc}?key=${filename}`).then(assertServerError); } uploadFile(file) { return new Promise((resolve, reject) => { const onStart = data => { const cFile = this.uppy.getFile(file.id); this.uppy.setFileState(file.id, { s3Multipart: { ...cFile.s3Multipart, key: data.key, uploadId: data.uploadId } }); }; const onProgress = (bytesUploaded, bytesTotal) => { this.uppy.emit('upload-progress', file, { uploader: this, bytesUploaded, bytesTotal }); }; const onError = err => { this.uppy.log(err); this.uppy.emit('upload-error', file, err); queuedRequest.done(); this.resetUploaderReferences(file.id); reject(err); }; const onSuccess = result => { const uploadResp = { body: { ...result }, uploadURL: result.location }; queuedRequest.done(); this.resetUploaderReferences(file.id); const cFile = this.uppy.getFile(file.id); this.uppy.emit('upload-success', cFile || file, uploadResp); if (result.location) { this.uppy.log(`Download ${upload.file.name} from ${result.location}`); } resolve(upload); }; const onPartComplete = part => { const cFile = this.uppy.getFile(file.id); if (!cFile) { return; } this.uppy.emit('s3-multipart:part-uploaded', cFile, part); }; const upload = new Uploader(file.data, { // .bind to pass the file object to each handler. createMultipartUpload: this.opts.createMultipartUpload.bind(this, file), listParts: this.opts.listParts.bind(this, file), prepareUploadParts: this.opts.prepareUploadParts.bind(this, file), completeMultipartUpload: this.opts.completeMultipartUpload.bind(this, file), abortMultipartUpload: this.opts.abortMultipartUpload.bind(this, file), getChunkSize: this.opts.getChunkSize ? this.opts.getChunkSize.bind(this) : null, onStart, onProgress, onError, onSuccess, onPartComplete, limit: this.opts.limit || 5, retryDelays: this.opts.retryDelays || [], ...file.s3Multipart }); this.uploaders[file.id] = upload; this.uploaderEvents[file.id] = new EventTracker(this.uppy); let queuedRequest = this.requests.run(() => { 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 () => {}; }); this.onFileRemove(file.id, removed => { queuedRequest.abort(); this.resetUploaderReferences(file.id, { abort: true }); resolve(`upload ${removed.id} was removed`); }); this.onCancelAll(file.id, () => { queuedRequest.abort(); this.resetUploaderReferences(file.id, { abort: true }); resolve(`upload ${file.id} was canceled`); }); this.onFilePause(file.id, 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 = this.requests.run(() => { upload.start(); return () => {}; }); } }); this.onPauseAll(file.id, () => { queuedRequest.abort(); upload.pause(); }); this.onResumeAll(file.id, () => { queuedRequest.abort(); if (file.error) { upload.abort(); } queuedRequest = this.requests.run(() => { upload.start(); return () => {}; }); }); // 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); } }); } uploadRemote(file) { 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((resolve, reject) => { const Client = file.remote.providerOptions.provider ? Provider : RequestClient; const client = new Client(this.uppy, file.remote.providerOptions); client.post(file.remote.url, { ...file.remote.body, protocol: 's3-multipart', size: file.data.size, metadata: file.meta }).then(res => { this.uppy.setFileState(file.id, { serverToken: res.token }); file = this.uppy.getFile(file.id); return file; }).then(file => { return this.connectToServerSocket(file); }).then(() => { resolve(); }).catch(err => { this.uppy.emit('upload-error', file, err); reject(err); }); }); } connectToServerSocket(file) { return new Promise((resolve, reject) => { const token = file.serverToken; const host = getSocketHost(file.remote.companionUrl); const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false }); this.uploaderSockets[file.id] = socket; this.uploaderEvents[file.id] = new EventTracker(this.uppy); this.onFileRemove(file.id, () => { queuedRequest.abort(); socket.send('pause', {}); this.resetUploaderReferences(file.id, { abort: true }); resolve(`upload ${file.id} was removed`); }); this.onFilePause(file.id, 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 = this.requests.run(() => { socket.send('resume', {}); return () => {}; }); } }); this.onPauseAll(file.id, () => { queuedRequest.abort(); socket.send('pause', {}); }); this.onCancelAll(file.id, () => { queuedRequest.abort(); socket.send('pause', {}); this.resetUploaderReferences(file.id); resolve(`upload ${file.id} was canceled`); }); this.onResumeAll(file.id, () => { queuedRequest.abort(); if (file.error) { socket.send('pause', {}); } queuedRequest = this.requests.run(() => { socket.send('resume', {}); }); }); this.onRetry(file.id, () => { // 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', {}); } }); this.onRetryAll(file.id, () => { if (socket.isOpen) { socket.send('pause', {}); socket.send('resume', {}); } }); socket.on('progress', progressData => emitSocketProgress(this, progressData, file)); socket.on('error', errData => { this.uppy.emit('upload-error', file, new Error(errData.error)); this.resetUploaderReferences(file.id); queuedRequest.done(); reject(new Error(errData.error)); }); socket.on('success', data => { const uploadResp = { uploadURL: data.url }; this.uppy.emit('upload-success', file, uploadResp); this.resetUploaderReferences(file.id); queuedRequest.done(); resolve(); }); let queuedRequest = this.requests.run(() => { socket.open(); if (file.isPaused) { socket.send('pause', {}); } return () => {}; }); }); } upload(fileIDs) { if (fileIDs.length === 0) return Promise.resolve(); const promises = fileIDs.map(id => { const file = this.uppy.getFile(id); if (file.isRemote) { return this.uploadRemote(file); } return this.uploadFile(file); }); return Promise.all(promises); } onFileRemove(fileID, cb) { this.uploaderEvents[fileID].on('file-removed', file => { if (fileID === file.id) cb(file.id); }); } onFilePause(fileID, cb) { this.uploaderEvents[fileID].on('upload-pause', (targetFileID, isPaused) => { if (fileID === targetFileID) { // const isPaused = this.uppy.pauseResume(fileID) cb(isPaused); } }); } onRetry(fileID, cb) { this.uploaderEvents[fileID].on('upload-retry', targetFileID => { if (fileID === targetFileID) { cb(); } }); } onRetryAll(fileID, cb) { this.uploaderEvents[fileID].on('retry-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } onPauseAll(fileID, cb) { this.uploaderEvents[fileID].on('pause-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } onCancelAll(fileID, cb) { this.uploaderEvents[fileID].on('cancel-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } onResumeAll(fileID, cb) { this.uploaderEvents[fileID].on('resume-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } install() { const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, resumableUploads: true } }); this.uppy.addUploader(this.upload); } uninstall() { const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, resumableUploads: false } }); this.uppy.removeUploader(this.upload); } }, _class.VERSION = "2.1.1", _temp); },{"./MultipartUploader":2,"@uppy/companion-client":12,"@uppy/core/lib/BasePlugin":14,"@uppy/utils/lib/EventTracker":24,"@uppy/utils/lib/RateLimitedQueue":27,"@uppy/utils/lib/emitSocketProgress":30,"@uppy/utils/lib/getSocketHost":41}],4:[function(require,module,exports){ "use strict"; var _getOptions, _addEventHandlerForFile, _addEventHandlerIfFileStillExists, _uploadLocalFile, _uploadRemoteFile; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const { nanoid } = require('nanoid'); const { Provider, RequestClient, Socket } = require('@uppy/companion-client'); const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress'); const getSocketHost = require('@uppy/utils/lib/getSocketHost'); const EventTracker = require('@uppy/utils/lib/EventTracker'); const ProgressTimeout = require('@uppy/utils/lib/ProgressTimeout'); const NetworkError = require('@uppy/utils/lib/NetworkError'); const isNetworkError = require('@uppy/utils/lib/isNetworkError'); const { internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue'); // See XHRUpload function buildResponseError(xhr, error) { if (isNetworkError(xhr)) return new NetworkError(error, xhr); // TODO: when we drop support for browsers that do not support this syntax, use: // return new Error('Upload error', { cause: error, request: xhr }) const err = new Error('Upload error'); err.cause = error; err.request = xhr; return err; } // See XHRUpload function setTypeInBlob(file) { const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type); return dataWithUpdatedType; } function addMetadata(formData, meta, opts) { const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default. : Object.keys(meta); metaFields.forEach(item => { formData.append(item, meta[item]); }); } function createFormDataUpload(file, opts) { const formPost = new FormData(); addMetadata(formPost, file.meta, opts); const dataWithUpdatedType = setTypeInBlob(file); if (file.name) { formPost.append(opts.fieldName, dataWithUpdatedType, file.meta.name); } else { formPost.append(opts.fieldName, dataWithUpdatedType); } return formPost; } const createBareUpload = file => file.data; module.exports = (_getOptions = /*#__PURE__*/_classPrivateFieldLooseKey("getOptions"), _addEventHandlerForFile = /*#__PURE__*/_classPrivateFieldLooseKey("addEventHandlerForFile"), _addEventHandlerIfFileStillExists = /*#__PURE__*/_classPrivateFieldLooseKey("addEventHandlerIfFileStillExists"), _uploadLocalFile = /*#__PURE__*/_classPrivateFieldLooseKey("uploadLocalFile"), _uploadRemoteFile = /*#__PURE__*/_classPrivateFieldLooseKey("uploadRemoteFile"), class MiniXHRUpload { constructor(_uppy, _opts) { Object.defineProperty(this, _uploadRemoteFile, { value: _uploadRemoteFile2 }); Object.defineProperty(this, _uploadLocalFile, { value: _uploadLocalFile2 }); Object.defineProperty(this, _addEventHandlerIfFileStillExists, { value: _addEventHandlerIfFileStillExists2 }); Object.defineProperty(this, _addEventHandlerForFile, { value: _addEventHandlerForFile2 }); Object.defineProperty(this, _getOptions, { value: _getOptions2 }); this.uppy = _uppy; this.opts = { validateStatus(status) { return status >= 200 && status < 300; }, ..._opts }; this.requests = _opts[internalRateLimitedQueue]; this.uploaderEvents = Object.create(null); this.i18n = _opts.i18n; } uploadFile(id, current, total) { const file = this.uppy.getFile(id); if (file.error) { throw new Error(file.error); } else if (file.isRemote) { return _classPrivateFieldLooseBase(this, _uploadRemoteFile)[_uploadRemoteFile](file, current, total); } return _classPrivateFieldLooseBase(this, _uploadLocalFile)[_uploadLocalFile](file, current, total); } }); function _getOptions2(file) { var _file$xhrUpload; const { uppy } = this; const overrides = uppy.getState().xhrUpload; const opts = { ...this.opts, ...(overrides || {}), ...(file.xhrUpload || {}), headers: { ...this.opts.headers, ...(overrides == null ? void 0 : overrides.headers), ...((_file$xhrUpload = file.xhrUpload) == null ? void 0 : _file$xhrUpload.headers) } }; return opts; } function _addEventHandlerForFile2(eventName, fileID, eventHandler) { this.uploaderEvents[fileID].on(eventName, targetFileID => { if (fileID === targetFileID) eventHandler(); }); } function _addEventHandlerIfFileStillExists2(eventName, fileID, eventHandler) { this.uploaderEvents[fileID].on(eventName, () => { if (this.uppy.getFile(fileID)) eventHandler(); }); } function _uploadLocalFile2(file, current, total) { const opts = _classPrivateFieldLooseBase(this, _getOptions)[_getOptions](file); this.uppy.log(`uploading ${current} of ${total}`); return new Promise((resolve, reject) => { // This is done in index.js in the S3 plugin. // this.uppy.emit('upload-started', file) const data = opts.formData ? createFormDataUpload(file, opts) : createBareUpload(file, opts); const xhr = new XMLHttpRequest(); this.uploaderEvents[file.id] = new EventTracker(this.uppy); const timer = new ProgressTimeout(opts.timeout, () => { xhr.abort(); // eslint-disable-next-line no-use-before-define queuedRequest.done(); const error = new Error(this.i18n('timedOut', { seconds: Math.ceil(opts.timeout / 1000) })); this.uppy.emit('upload-error', file, error); reject(error); }); const id = nanoid(); xhr.upload.addEventListener('loadstart', () => { this.uppy.log(`[AwsS3/XHRUpload] ${id} started`); }); xhr.upload.addEventListener('progress', ev => { this.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) { this.uppy.emit('upload-progress', file, { uploader: this, bytesUploaded: ev.loaded, bytesTotal: ev.total }); } }); xhr.addEventListener('load', ev => { this.uppy.log(`[AwsS3/XHRUpload] ${id} finished`); timer.done(); // eslint-disable-next-line no-use-before-define queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) { const body = opts.getResponseData(xhr.responseText, xhr); const uploadURL = body[opts.responseUrlFieldName]; const uploadResp = { status: ev.target.status, body, uploadURL }; this.uppy.emit('upload-success', file, uploadResp); if (uploadURL) { this.uppy.log(`Download ${file.name} from ${uploadURL}`); } return resolve(file); } const body = opts.getResponseData(xhr.responseText, xhr); const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); const response = { status: ev.target.status, body }; this.uppy.emit('upload-error', file, error, response); return reject(error); }); xhr.addEventListener('error', () => { this.uppy.log(`[AwsS3/XHRUpload] ${id} errored`); timer.done(); // eslint-disable-next-line no-use-before-define queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); this.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. It’s important to set withCredentials // to a boolean, otherwise React Native crashes xhr.withCredentials = Boolean(opts.withCredentials); if (opts.responseType !== '') { xhr.responseType = opts.responseType; } Object.keys(opts.headers).forEach(header => { xhr.setRequestHeader(header, opts.headers[header]); }); const queuedRequest = this.requests.run(() => { xhr.send(data); return () => { // eslint-disable-next-line no-use-before-define timer.done(); xhr.abort(); }; }, { priority: 1 }); _classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('file-removed', file.id, () => { queuedRequest.abort(); reject(new Error('File removed')); }); _classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('cancel-all', file.id, () => { queuedRequest.abort(); reject(new Error('Upload cancelled')); }); }); } function _uploadRemoteFile2(file) { const opts = _classPrivateFieldLooseBase(this, _getOptions)[_getOptions](file); // This is done in index.js in the S3 plugin. // this.uppy.emit('upload-started', file) const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default. : Object.keys(file.meta); const Client = file.remote.providerOptions.provider ? Provider : RequestClient; const client = new Client(this.uppy, file.remote.providerOptions); return client.post(file.remote.url, { ...file.remote.body, endpoint: opts.endpoint, size: file.data.size, fieldname: opts.fieldName, metadata: Object.fromEntries(metaFields.map(name => [name, file.meta[name]])), httpMethod: opts.method, useFormData: opts.formData, headers: opts.headers }).then(res => new Promise((resolve, reject) => { const { token } = res; const host = getSocketHost(file.remote.companionUrl); const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false }); this.uploaderEvents[file.id] = new EventTracker(this.uppy); const queuedRequest = this.requests.run(() => { socket.open(); if (file.isPaused) { socket.send('pause', {}); } return () => socket.close(); }); _classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('file-removed', file.id, () => { socket.send('pause', {}); queuedRequest.abort(); resolve(`upload ${file.id} was removed`); }); _classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('cancel-all', file.id, () => { socket.send('pause', {}); queuedRequest.abort(); resolve(`upload ${file.id} was canceled`); }); _classPrivateFieldLooseBase(this, _addEventHandlerForFile)[_addEventHandlerForFile]('upload-retry', file.id, () => { socket.send('pause', {}); socket.send('resume', {}); }); _classPrivateFieldLooseBase(this, _addEventHandlerIfFileStillExists)[_addEventHandlerIfFileStillExists]('retry-all', file.id, () => { socket.send('pause', {}); socket.send('resume', {}); }); socket.on('progress', progressData => emitSocketProgress(this, progressData, file)); socket.on('success', data => { const body = opts.getResponseData(data.response.responseText, data.response); const uploadURL = body[opts.responseUrlFieldName]; const uploadResp = { status: data.response.status, body, uploadURL, bytesUploaded: data.bytesUploaded }; this.uppy.emit('upload-success', file, uploadResp); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } return resolve(); }); socket.on('error', errData => { const resp = errData.response; const error = resp ? opts.getResponseError(resp.responseText, resp) : Object.assign(new Error(errData.error.message), { cause: errData.error }); this.uppy.emit('upload-error', file, error); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } reject(error); }); }).catch(err => { this.uppy.emit('upload-error', file, err); return Promise.reject(err); })); } },{"@uppy/companion-client":12,"@uppy/utils/lib/EventTracker":24,"@uppy/utils/lib/NetworkError":25,"@uppy/utils/lib/ProgressTimeout":26,"@uppy/utils/lib/RateLimitedQueue":27,"@uppy/utils/lib/emitSocketProgress":30,"@uppy/utils/lib/getSocketHost":41,"@uppy/utils/lib/isNetworkError":45,"nanoid":53}],5:[function(require,module,exports){ "use strict"; var _class, _client, _requests, _uploader, _handleUpload, _temp; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } /** * 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 :) */ const BasePlugin = require('@uppy/core/lib/BasePlugin'); const { RateLimitedQueue, internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue'); const settle = require('@uppy/utils/lib/settle'); const { RequestClient } = require('@uppy/companion-client'); const MiniXHRUpload = require('./MiniXHRUpload'); const isXml = require('./isXml'); function resolveUrl(origin, link) { return new URL(link, origin || undefined).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) { const start = source.indexOf(`<${tagName}>`); const end = source.indexOf(`</${tagName}>`, start); return start !== -1 && end !== -1 ? source.slice(start + tagName.length + 2, end) : ''; } function assertServerError(res) { if (res && res.error) { const error = new Error(res.message); Object.assign(error, res.error); throw error; } return res; } function validateParameters(file, params) { const valid = params != null && typeof params.url === 'string' && (typeof params.fields === 'object' || params.fields == null); if (!valid) { const err = new TypeError(`AwsS3: got incorrect result from 'getUploadParameters()' for file '${file.name}', expected an object '{ url, method, fields, headers }' but got '${JSON.stringify(params)}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`); throw err; } const methodIsValid = params.method == null || /^p(u|os)t$/i.test(params.method); if (!methodIsValid) { const err = new TypeError(`AwsS3: got incorrect method from 'getUploadParameters()' for file '${file.name}', expected 'put' or 'post' but got '${params.method}' instead.\nSee https://uppy.io/docs/aws-s3/#getUploadParameters-file for more on the expected format.`); throw err; } } // 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 undefined; } const error = getXmlValue(content, 'Message'); return new Error(error); } // warning deduplication flag: see `getResponseData()` XHRUpload option definition let warnedSuccessActionStatus = false; module.exports = (_temp = (_client = /*#__PURE__*/_classPrivateFieldLooseKey("client"), _requests = /*#__PURE__*/_classPrivateFieldLooseKey("requests"), _uploader = /*#__PURE__*/_classPrivateFieldLooseKey("uploader"), _handleUpload = /*#__PURE__*/_classPrivateFieldLooseKey("handleUpload"), _class = class AwsS3 extends BasePlugin { // eslint-disable-next-line global-require constructor(uppy, opts) { super(uppy, opts); Object.defineProperty(this, _client, { writable: true, value: void 0 }); Object.defineProperty(this, _requests, { writable: true, value: void 0 }); Object.defineProperty(this, _uploader, { writable: true, value: void 0 }); Object.defineProperty(this, _handleUpload, { writable: true, value: fileIDs => { /** * keep track of `getUploadParameters()` responses * so we can cancel the calls individually using just a file ID * * @type {object.<string, Promise>} */ const paramsPromises = Object.create(null); function onremove(file) { var _paramsPromises$id; const { id } = file; (_paramsPromises$id = paramsPromises[id]) == null ? void 0 : _paramsPromises$id.abort(); } this.uppy.on('file-removed', onremove); fileIDs.forEach(id => { const file = this.uppy.getFile(id); this.uppy.emit('upload-started', file); }); const getUploadParameters = _classPrivateFieldLooseBase(this, _requests)[_requests].wrapPromiseFunction(file => { return this.opts.getUploadParameters(file); }); const numberOfFiles = fileIDs.length; return settle(fileIDs.map((id, index) => { paramsPromises[id] = getUploadParameters(this.uppy.getFile(id)); return paramsPromises[id].then(params => { delete paramsPromises[id]; const file = this.uppy.getFile(id); validateParameters(file, params); const { method = 'post', url, fields, headers } = params; const xhrOpts = { method, formData: method.toLowerCase() === 'post', endpoint: url, metaFields: fields ? Object.keys(fields) : [] }; if (headers) { xhrOpts.headers = headers; } this.uppy.setFileState(file.id, { meta: { ...file.meta, ...fields }, xhrUpload: xhrOpts }); return _classPrivateFieldLooseBase(this, _uploader)[_uploader].uploadFile(file.id, index, numberOfFiles); }).catch(error => { delete paramsPromises[id]; const file = this.uppy.getFile(id); this.uppy.emit('upload-error', file, error); }); })).finally(() => { // cleanup. this.uppy.off('file-removed', onremove); }); } }); this.type = 'uploader'; this.id = this.opts.id || 'AwsS3'; this.title = 'AWS S3'; this.defaultLocale = { strings: { timedOut: 'Upload stalled for %{seconds} seconds, aborting.' } }; const defaultOptions = { timeout: 30 * 1000, limit: 0, metaFields: [], // have to opt in getUploadParameters: this.getUploadParameters.bind(this) }; this.opts = { ...defaultOptions, ...opts }; // TODO: remove i18n once we can depend on XHRUpload instead of MiniXHRUpload this.i18nInit(); _classPrivateFieldLooseBase(this, _client)[_client] = new RequestClient(uppy, opts); _classPrivateFieldLooseBase(this, _requests)[_requests] = new RateLimitedQueue(this.opts.limit); } getUploadParameters(file) { if (!this.opts.companionUrl) { throw new Error('Expected a `companionUrl` option containing a Companion address.'); } const filename = file.meta.name; const { type } = file.meta; const metadata = Object.fromEntries(this.opts.metaFields.filter(key => file.meta[key] != null).map(key => [`metadata[${key}]`, file.meta[key].toString()])); const query = new URLSearchParams({ filename, type, ...metadata }); return _classPrivateFieldLooseBase(this, _client)[_client].get(`s3/params?${query}`).then(assertServerError); } install() { const { uppy } = this; uppy.addUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_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) { const 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') }; } const xhrOptions = { fieldName: 'file', responseUrlFieldName: 'location', timeout: this.opts.timeout, // Share the rate limiting queue with XHRUpload. [internalRateLimitedQueue]: _classPrivateFieldLooseBase(this, _requests)[_requests], responseType: 'text', getResponseData: this.opts.getResponseData || defaultGetResponseData, getResponseError: defaultGetResponseError }; // TODO: remove i18n once we can depend on XHRUpload instead of MiniXHRUpload xhrOptions.i18n = this.i18n; // Revert to `uppy.use(XHRUpload)` once the big comment block at the top of // this file is solved _classPrivateFieldLooseBase(this, _uploader)[_uploader] = new MiniXHRUpload(uppy, xhrOptions); } uninstall() { this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]); } }), _class.VERSION = "2.0.5", _temp); },{"./MiniXHRUpload":4,"./isXml":6,"@uppy/companion-client":12,"@uppy/core/lib/BasePlugin":14,"@uppy/utils/lib/RateLimitedQueue":27,"@uppy/utils/lib/settle":47}],6:[function(require,module,exports){ "use strict"; /** * 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) { const rawContentType = xhr.headers ? xhr.headers['content-type'] : xhr.getResponseHeader('Content-Type'); if (typeof rawContentType === 'string') { const 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'; class AuthError extends Error { constructor() { super('Authorization required'); this.name = 'AuthError'; this.isAuthError = true; } } module.exports = AuthError; },{}],8:[function(require,module,exports){ 'use strict'; const RequestClient = require('./RequestClient'); const tokenStorage = require('./tokenStorage'); const getName = id => { return id.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' '); }; module.exports = class Provider extends RequestClient { constructor(uppy, opts) { super(uppy, opts); 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; } headers() { return Promise.all([super.headers(), this.getAuthToken()]).then(([headers, token]) => { const authHeaders = {}; if (token) { authHeaders['uppy-auth-token'] = token; } if (this.companionKeysParams) { authHeaders['uppy-credentials-params'] = btoa(JSON.stringify({ params: this.companionKeysParams })); } return { ...headers, ...authHeaders }; }); } onReceiveResponse(response) { response = super.onReceiveResponse(response); const plugin = this.uppy.getPlugin(this.pluginId); const oldAuthenticated = plugin.getPluginState().authenticated; const authenticated = oldAuthenticated ? response.status !== 401 : response.status < 400; plugin.setPluginState({ authenticated }); return response; } setAuthToken(token) { return this.uppy.getPlugin(this.pluginId).storage.setItem(this.tokenKey, token); } getAuthToken() { return this.uppy.getPlugin(this.pluginId).storage.getItem(this.tokenKey); } authUrl(queries = {}) { if (this.preAuthToken) { queries.uppyPreAuthToken = this.preAuthToken; } return `${this.hostname}/${this.id}/connect?${new URLSearchParams(queries)}`; } fileUrl(id) { return `${this.hostname}/${this.id}/get/${id}`; } fetchPreAuthToken() { if (!this.companionKeysParams) { return Promise.resolve(); } return this.post(`${this.id}/preauth/`, { params: this.companionKeysParams }).then(res => { this.preAuthToken = res.token; }).catch(err => { this.uppy.log(`[CompanionClient] unable to fetch preAuthToken ${err}`, 'warning'); }); } list(directory) { return this.get(`${this.id}/list/${directory || ''}`); } logout() { return this.get(`${this.id}/logout`).then(response => Promise.all([response, this.uppy.getPlugin(this.pluginId).storage.removeItem(this.tokenKey)])).then(([response]) => response); } static initPlugin(plugin, opts, defaultOpts) { plugin.type = 'acquirer'; plugin.files = []; if (defaultOpts) { plugin.opts = { ...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) { const 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 if (/^(?!https?:\/\/).*$/i.test(opts.companionUrl)) { // does not start with https:// plugin.opts.companionAllowedHosts = `https://${opts.companionUrl.replace(/^\/\//, '')}`; } else { plugin.opts.companionAllowedHosts = new URL(opts.companionUrl).origin; } plugin.storage = plugin.opts.storage || tokenStorage; } }; },{"./RequestClient":9,"./tokenStorage":13}],9:[function(require,module,exports){ 'use strict'; var _class, _getPostResponseFunc, _getUrl, _errorHandler, _temp; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const fetchWithNetworkError = require('@uppy/utils/lib/fetchWithNetworkError'); const AuthError = require('./AuthError'); // Remove the trailing slash so we can always safely append /xyz. function stripSlash(url) { return url.replace(/\/$/, ''); } async function handleJSONResponse(res) { if (res.status === 401) { throw new AuthError(); } const jsonPromise = res.json(); if (res.status < 200 || res.status > 300) { let errMsg = `Failed request with status: ${res.status}. ${res.statusText}`; try { const errData = await jsonPromise; errMsg = errData.message ? `${errMsg} message: ${errData.message}` : errMsg; errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg; } finally { // eslint-disable-next-line no-unsafe-finally throw new Error(errMsg); } } return jsonPromise; } module.exports = (_temp = (_getPostResponseFunc = /*#__PURE__*/_classPrivateFieldLooseKey("getPostResponseFunc"), _getUrl = /*#__PURE__*/_classPrivateFieldLooseKey("getUrl"), _errorHandler = /*#__PURE__*/_classPrivateFieldLooseKey("errorHandler"), _class = class RequestClient { // eslint-disable-next-line global-require constructor(uppy, opts) { Object.defineProperty(this, _errorHandler, { value: _errorHandler2 }); Object.defineProperty(this, _getUrl, { value: _getUrl2 }); Object.defineProperty(this, _getPostResponseFunc, { writable: true, value: skip => response => skip ? response : this.onReceiveResponse(response) }); this.uppy = uppy; this.opts = opts; this.onReceiveResponse = this.onReceiveResponse.bind(this); this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token']; this.preflightDone = false; } get hostname() { const { companion } = this.uppy.getState(); const host = this.opts.companionUrl; return stripSlash(companion && companion[host] ? companion[host] : host); } headers() { const userHeaders = this.opts.companionHeaders || {}; return Promise.resolve({ ...RequestClient.defaultHeaders, ...userHeaders }); } onReceiveResponse(response) { const state = this.uppy.getState(); const companion = state.companion || {}; const host = this.opts.companionUrl; const { headers } = response; // Store the self-identified domain name for the Companion instance we just hit. if (headers.has('i-am') && headers.get('i-am') !== companion[host]) { this.uppy.setState({ companion: { ...companion, [host]: headers.get('i-am') } }); } return response; } preflight(path) { if (this.preflightDone) { return Promise.resolve(this.allowedHeaders.slice()); } return fetch(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), { method: 'OPTIONS' }).then(response => { if (response.headers.has('access-control-allow-headers')) { this.allowedHeaders = response.headers.get('access-control-allow-headers').split(',').map(headerName => headerName.trim().toLowerCase()); } this.preflightDone = true; return this.allowedHeaders.slice(); }).catch(err => { this.uppy.log(`[CompanionClient] unable to make preflight request ${err}`, 'warning'); this.preflightDone = true; return this.allowedHeaders.slice(); }); } preflightAndHeaders(path) { return Promise.all([this.preflight(path), this.headers()]).then(([allowedHeaders, headers]) => { // filter to keep only allowed Headers Object.keys(headers).forEach(header => { if (!allowedHeaders.includes(header.toLowerCase())) { this.uppy.log(`[CompanionClient] excluding disallowed header ${header}`); delete headers[header]; // eslint-disable-line no-param-reassign } }); return headers; }); } get(path, skipPostResponse) { const method = 'get'; return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), { method, headers, credentials: this.opts.companionCookiesRule || 'same-origin' })).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path)); } post(path, data, skipPostResponse) { const method = 'post'; return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), { method, headers, credentials: this.opts.companionCookiesRule || 'same-origin', body: JSON.stringify(data) })).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path)); } delete(path, data, skipPostResponse) { const method = 'delete'; return this.preflightAndHeaders(path).then(headers => fetchWithNetworkError(`${this.hostname}/${path}`, { method, headers, credentials: this.opts.companionCookiesRule || 'same-origin', body: data ? JSON.stringify(data) : null })).then(_classPrivateFieldLooseBase(this, _getPostResponseFunc)[_getPostResponseFunc](skipPostResponse)).then(handleJSONResponse).catch(_classPrivateFieldLooseBase(this, _errorHandler)[_errorHandler](method, path)); } }), _class.VERSION = "2.0.3", _class.defaultHeaders = { Accept: 'application/json', 'Content-Type': 'application/json', 'Uppy-Versions': `@uppy/companion-client=${_class.VERSION}` }, _temp); function _getUrl2(url) { if (/^(https?:|)\/\//.test(url)) { return url; } return `${this.hostname}/${url}`; } function _errorHandler2(method, path) { return err => { var _err; if (!((_err = err) != null && _err.isAuthError)) { const error = new Error(`Could not ${method} ${_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path)}`); error.cause = err; err = error; // eslint-disable-line no-param-reassign } return Promise.reject(err); }; } },{"./AuthError":7,"@uppy/utils/lib/fetchWithNetworkError":31}],10:[function(require,module,exports){ 'use strict'; const RequestClient = require('./RequestClient'); const getName = id => { return id.split('-').map(s => s.charAt(0).toUpperCase() + s.slice(1)).join(' '); }; module.exports = class SearchProvider extends RequestClient { constructor(uppy, opts) { super(uppy, opts); this.provider = opts.provider; this.id = this.provider; this.name = this.opts.name || getName(this.id); this.pluginId = this.opts.pluginId; } fileUrl(id) { return `${this.hostname}/search/${this.id}/get/${id}`; } search(text, queries) { queries = queries ? `&${queries}` : ''; return this.get(`search/${this.id}/list?q=${encodeURIComponent(text)}${queries}`); } }; },{"./RequestClient":9}],11:[function(require,module,exports){ "use strict"; var _queued, _emitter, _isOpen, _socket, _handleMessage; let _Symbol$for, _Symbol$for2; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const ee = require('namespace-emitter'); module.exports = (_queued = /*#__PURE__*/_classPrivateFieldLooseKey("queued"), _emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter"), _isOpen = /*#__PURE__*/_classPrivateFieldLooseKey("isOpen"), _socket = /*#__PURE__*/_classPrivateFieldLooseKey("socket"), _handleMessage = /*#__PURE__*/_classPrivateFieldLooseKey("handleMessage"), _Symbol$for = Symbol.for('uppy test: getSocket'), _Symbol$for2 = Symbol.for('uppy test: getQueued'), class UppySocket { constructor(opts) { Object.defineProperty(this, _queued, { writable: true, value: [] }); Object.defineProperty(this, _emitter, { writable: true, value: ee() }); Object.defineProperty(this, _isOpen, { writable: true, value: false }); Object.defineProperty(this, _socket, { writable: true, value: void 0 }); Object.defineProperty(this, _handleMessage, { writable: true, value: e => { try { const message = JSON.parse(e.data); this.emit(message.action, message.payload); } catch (err) { // TODO: use a more robust error handler. console.log(err); // eslint-disable-line no-console } } }); this.opts = opts; if (!opts || opts.autoOpen !== false) { this.open(); } } get isOpen() { return _classPrivateFieldLooseBase(this, _isOpen)[_isOpen]; } [_Symbol$for]() { return _classPrivateFieldLooseBase(this, _socket)[_socket]; } [_Symbol$for2]() { return _classPrivateFieldLooseBase(this, _queued)[_queued]; } open() { _classPrivateFieldLooseBase(this, _socket)[_socket] = new WebSocket(this.opts.target); _classPrivateFieldLooseBase(this, _socket)[_socket].onopen = () => { _classPrivateFieldLooseBase(this, _isOpen)[_isOpen] = true; while (_classPrivateFieldLooseBase(this, _queued)[_queued].length > 0 && _classPrivateFieldLooseBase(this, _isOpen)[_isOpen]) { const first = _classPrivateFieldLooseBase(this, _queued)[_queued].shift(); this.send(first.action, first.payload); } }; _classPrivateFieldLooseBase(this, _socket)[_socket].onclose = () => { _classPrivateFieldLooseBase(this, _isOpen)[_isOpen] = false; }; _classPrivateFieldLooseBase(this, _socket)[_socket].onmessage = _classPrivateFieldLooseBase(this, _handleMessage)[_handleMessage]; } close() { var _classPrivateFieldLoo; (_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _socket)[_socket]) == null ? void 0 : _classPrivateFieldLoo.close(); } send(action, payload) { // attach uuid if (!_classPrivateFieldLooseBase(this, _isOpen)[_isOpen]) { _classPrivateFieldLooseBase(this, _queued)[_queued].push({ action, payload }); return; } _classPrivateFieldLooseBase(this, _socket)[_socket].send(JSON.stringify({ action, payload })); } on(action, handler) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].on(action, handler); } emit(action, payload) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].emit(action, payload); } once(action, handler) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].once(action, handler); } }); },{"namespace-emitter":52}],12:[function(require,module,exports){ 'use strict'; /** * Manages communications with Companion */ const RequestClient = require('./RequestClient'); const Provider = require('./Provider'); const SearchProvider = require('./SearchProvider'); const Socket = require('./Socket'); module.exports = { RequestClient, Provider, SearchProvider, 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 = (key, value) => { return new Promise(resolve => { localStorage.setItem(key, value); resolve(); }); }; module.exports.getItem = key => { return Promise.resolve(localStorage.getItem(key)); }; module.exports.removeItem = key => { return new Promise(resolve => { localStorage.removeItem(key); resolve(); }); }; },{}],14:[function(require,module,exports){ "use strict"; /** * Core plugin logic that all plugins share. * * BasePlugin does not contain DOM rendering so it can be used for plugins * without a user interface. * * See `Plugin` for the extended version with Preact rendering for interfaces. */ const Translator = require('@uppy/utils/lib/Translator'); module.exports = class BasePlugin { constructor(uppy, opts = {}) { this.uppy = uppy; this.opts = opts; } getPluginState() { const { plugins } = this.uppy.getState(); return plugins[this.id] || {}; } setPluginState(update) { const { plugins } = this.uppy.getState(); this.uppy.setState({ plugins: { ...plugins, [this.id]: { ...plugins[this.id], ...update } } }); } setOptions(newOpts) { this.opts = { ...this.opts, ...newOpts }; this.setPluginState(); // so that UI re-renders with new options this.i18nInit(); } i18nInit() { const translator = new Translator([this.defaultLocale, this.uppy.locale, this.opts.locale]); this.i18n = translator.translate.bind(translator); this.i18nArray = translator.translateArray.bind(translator); this.setPluginState(); // so that UI re-renders and we see the updated locale } /** * Extendable methods * ================== * These methods are here to serve as an overview of the extendable methods as well as * making them not conditional in use, such as `if (this.afterUpdate)`. */ // eslint-disable-next-line class-methods-use-this addTarget() { throw new Error('Extend the addTarget method to add your plugin to another plugin\'s target'); } // eslint-disable-next-line class-methods-use-this install() {} // eslint-disable-next-line class-methods-use-this uninstall() {} /** * 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. */ render() { throw new Error('Extend the render method to add your plugin to a DOM element'); } // eslint-disable-next-line class-methods-use-this update() {} // Called after every state update, after everything's mounted. Debounced. // eslint-disable-next-line class-methods-use-this afterUpdate() {} }; },{"@uppy/utils/lib/Translator":28}],15:[function(require,module,exports){ "use strict"; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const { render } = require('preact'); const findDOMElement = require('@uppy/utils/lib/findDOMElement'); const BasePlugin = require('./BasePlugin'); /** * Defer a frequent call to the microtask queue. * * @param {() => T} fn * @returns {Promise<T>} */ function debounce(fn) { let calling = null; let latestArgs = null; return (...args) => { latestArgs = args; if (!calling) { calling = Promise.resolve().then(() => { 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(...latestArgs); }); } return calling; }; } /** * UIPlugin is the extended version of BasePlugin to incorporate rendering with Preact. * Use this for plugins that need a user interface. * * For plugins without an user interface, see BasePlugin. */ var _updateUI = /*#__PURE__*/_classPrivateFieldLooseKey("updateUI"); class UIPlugin extends BasePlugin { constructor(...args) { super(...args); Object.defineProperty(this, _updateUI, { writable: true, value: void 0 }); } /** * 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. */ mount(target, plugin) { const callerPluginName = plugin.id; const targetElement = findDOMElement(target); if (targetElement) { this.isTargetDOMEl = true; // When target is <body> with a single <div> element, // Preact thinks it’s the Uppy root element in there when doing a diff, // and destroys it. So we are creating a fragment (could be empty div) const uppyRootElement = document.createDocumentFragment(); // API for plugins that require a synchronous rerender. _classPrivateFieldLooseBase(this, _updateUI)[_updateUI] = debounce(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; render(this.render(state), uppyRootElement); this.afterUpdate(); }); this.uppy.log(`Installing ${callerPluginName} to a DOM element '${target}'`); if (this.opts.replaceTargetContent) { // Doing render(h(null), targetElement), which should have been // a better way, since because the component might need to do additional cleanup when it is removed, // stopped working — Preact just adds null into target, not replacing targetElement.innerHTML = ''; } render(this.render(this.uppy.getState()), uppyRootElement); this.el = uppyRootElement.firstElementChild; targetElement.appendChild(uppyRootElement); this.onMount(); return this.el; } let targetPlugin; if (typeof target === 'object' && target instanceof UIPlugin) { // Targeting a plugin *instance* targetPlugin = target; } else if (typeof target === 'function') { // Targeting a plugin type const Target = target; // Find the target plugin instance. this.uppy.iteratePlugins(p => { if (p instanceof Target) { targetPlugin = p; 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}`); let 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 <script> tag initializing Uppy is right before the closing </body> tag at the end of the page. ' + '(see https://github.com/transloadit/uppy/issues/1042)\n\n' + 'If you meant to target a plugin, please confirm that your `import` statements or `require` calls are correct.'; } throw new Error(message); } update(state) { if (this.el != null) { var _classPrivateFieldLoo, _classPrivateFieldLoo2; (_classPrivateFieldLoo = (_classPrivateFieldLoo2 = _classPrivateFieldLooseBase(this, _updateUI))[_updateUI]) == null ? void 0 : _classPrivateFieldLoo.call(_classPrivateFieldLoo2, state); } } unmount() { if (this.isTargetDOMEl) { var _this$el; (_this$el = this.el) == null ? void 0 : _this$el.remove(); } this.onUnmount(); } // eslint-disable-next-line class-methods-use-this onMount() {} // eslint-disable-next-line class-methods-use-this onUnmount() {} } module.exports = UIPlugin; },{"./BasePlugin":14,"@uppy/utils/lib/findDOMElement":32,"preact":55}],16:[function(require,module,exports){ /* global AggregateError */ 'use strict'; let _Symbol$for, _Symbol$for2; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const Translator = require('@uppy/utils/lib/Translator'); const ee = require('namespace-emitter'); const { nanoid } = require('nanoid'); const throttle = require('lodash.throttle'); const prettierBytes = require('@transloadit/prettier-bytes'); const match = require('mime-match'); const DefaultStore = require('@uppy/store-default'); const getFileType = require('@uppy/utils/lib/getFileType'); const getFileNameAndExtension = require('@uppy/utils/lib/getFileNameAndExtension'); const generateFileID = require('@uppy/utils/lib/generateFileID'); const supportsUploadProgress = require('./supportsUploadProgress'); const getFileName = require('./getFileName'); const { justErrorsLogger, debugLogger } = require('./loggers'); // Exported from here. class RestrictionError extends Error { constructor(...args) { super(...args); this.isRestriction = true; } } if (typeof AggregateError === 'undefined') { // eslint-disable-next-line no-global-assign globalThis.AggregateError = class AggregateError extends Error { constructor(message, errors) { super(message); this.errors = errors; } }; } class AggregateRestrictionError extends AggregateError { constructor(...args) { super(...args); this.isRestriction = true; } } /** * Uppy Core module. * Manages plugins, state updates, acts as an event bus, * adds/removes files and metadata. */ var _plugins = /*#__PURE__*/_classPrivateFieldLooseKey("plugins"); var _storeUnsubscribe = /*#__PURE__*/_classPrivateFieldLooseKey("storeUnsubscribe"); var _emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter"); var _preProcessors = /*#__PURE__*/_classPrivateFieldLooseKey("preProcessors"); var _uploaders = /*#__PURE__*/_classPrivateFieldLooseKey("uploaders"); var _postProcessors = /*#__PURE__*/_classPrivateFieldLooseKey("postProcessors"); var _checkRestrictions = /*#__PURE__*/_classPrivateFieldLooseKey("checkRestrictions"); var _checkMinNumberOfFiles = /*#__PURE__*/_classPrivateFieldLooseKey("checkMinNumberOfFiles"); var _checkRequiredMetaFields = /*#__PURE__*/_classPrivateFieldLooseKey("checkRequiredMetaFields"); var _showOrLogErrorAndThrow = /*#__PURE__*/_classPrivateFieldLooseKey("showOrLogErrorAndThrow"); var _assertNewUploadAllowed = /*#__PURE__*/_classPrivateFieldLooseKey("assertNewUploadAllowed"); var _checkAndCreateFileStateObject = /*#__PURE__*/_classPrivateFieldLooseKey("checkAndCreateFileStateObject"); var _startIfAutoProceed = /*#__PURE__*/_classPrivateFieldLooseKey("startIfAutoProceed"); var _addListeners = /*#__PURE__*/_classPrivateFieldLooseKey("addListeners"); var _updateOnlineStatus = /*#__PURE__*/_classPrivateFieldLooseKey("updateOnlineStatus"); var _createUpload = /*#__PURE__*/_classPrivateFieldLooseKey("createUpload"); var _getUpload = /*#__PURE__*/_classPrivateFieldLooseKey("getUpload"); var _removeUpload = /*#__PURE__*/_classPrivateFieldLooseKey("removeUpload"); var _runUpload = /*#__PURE__*/_classPrivateFieldLooseKey("runUpload"); _Symbol$for = Symbol.for('uppy test: getPlugins'); _Symbol$for2 = Symbol.for('uppy test: createUpload'); class Uppy { // eslint-disable-next-line global-require /** @type {Record<string, BasePlugin[]>} */ /** * Instantiate Uppy * * @param {object} opts — Uppy options */ constructor(_opts) { Object.defineProperty(this, _runUpload, { value: _runUpload2 }); Object.defineProperty(this, _removeUpload, { value: _removeUpload2 }); Object.defineProperty(this, _getUpload, { value: _getUpload2 }); Object.defineProperty(this, _createUpload, { value: _createUpload2 }); Object.defineProperty(this, _addListeners, { value: _addListeners2 }); Object.defineProperty(this, _startIfAutoProceed, { value: _startIfAutoProceed2 }); Object.defineProperty(this, _checkAndCreateFileStateObject, { value: _checkAndCreateFileStateObject2 }); Object.defineProperty(this, _assertNewUploadAllowed, { value: _assertNewUploadAllowed2 }); Object.defineProperty(this, _showOrLogErrorAndThrow, { value: _showOrLogErrorAndThrow2 }); Object.defineProperty(this, _checkRequiredMetaFields, { value: _checkRequiredMetaFields2 }); Object.defineProperty(this, _checkMinNumberOfFiles, { value: _checkMinNumberOfFiles2 }); Object.defineProperty(this, _checkRestrictions, { value: _checkRestrictions2 }); Object.defineProperty(this, _plugins, { writable: true, value: Object.create(null) }); Object.defineProperty(this, _storeUnsubscribe, { writable: true, value: void 0 }); Object.defineProperty(this, _emitter, { writable: true, value: ee() }); Object.defineProperty(this, _preProcessors, { writable: true, value: new Set() }); Object.defineProperty(this, _uploaders, { writable: true, value: new Set() }); Object.defineProperty(this, _postProcessors, { writable: true, value: new Set() }); Object.defineProperty(this, _updateOnlineStatus, { writable: true, value: this.updateOnlineStatus.bind(this) }); this.defaultLocale = { strings: { addBulkFilesFailed: { 0: 'Failed to add %{smart_count} file due to an internal error', 1: 'Failed to add %{smart_count} files due to internal errors' }, youCanOnlyUploadX: { 0: 'You can only upload %{smart_count} file', 1: 'You can only upload %{smart_count} files' }, youHaveToAtLeastSelectX: { 0: 'You have to select at least %{smart_count} file', 1: 'You have to select at least %{smart_count} files' }, exceedsSize: '%{file} exceeds maximum allowed size of %{size}', missingRequiredMetaField: 'Missing required meta fields', missingRequiredMetaFieldOnFile: 'Missing required meta fields in %{fileName}', inferiorSize: 'This file is smaller than the allowed size of %{size}', youCanOnlyUploadFileTypes: 'You can only upload: %{types}', noMoreFilesAllowed: 'Cannot add more files', noDuplicates: 'Cannot add the duplicate file \'%{fileName}\', it already exists', companionError: 'Connection with Companion failed', authAborted: 'Authentication aborted', companionUnauthorizeHint: 'To unauthorize to your %{provider} account, please go to %{url}', failedToUpload: 'Failed to upload %{file}', noInternetConnection: 'No Internet connection', connectedToInternet: 'Connected to the Internet', // Strings for remote providers noFilesFound: 'You have no files or folders here', selectX: { 0: 'Select %{smart_count}', 1: 'Select %{smart_count}' }, allFilesFromFolderNamed: 'All files from folder %{name}', openFolderNamed: 'Open folder %{name}', cancel: 'Cancel', logOut: 'Log out', filter: 'Filter', resetFilter: 'Reset filter', loading: 'Loading...', authenticateWithTitle: 'Please authenticate with %{pluginName} to select files', authenticateWith: 'Connect to %{pluginName}', signInWithGoogle: 'Sign in with Google', searchImages: 'Search for images', enterTextToSearch: 'Enter text to search for images', backToSearch: 'Back to Search', emptyFolderAdded: 'No files were added from empty folder', folderAlreadyAdded: 'The folder "%{folder}" was already added', folderAdded: { 0: 'Added %{smart_count} file from %{folder}', 1: 'Added %{smart_count} files from %{folder}' } } }; const defaultOptions = { id: 'uppy', autoProceed: false, /** * @deprecated The method should not be used */ allowMultipleUploads: true, allowMultipleUploadBatches: true, debug: false, restrictions: { maxFileSize: null, minFileSize: null, maxTotalFileSize: null, maxNumberOfFiles: null, minNumberOfFiles: null, allowedFileTypes: null, requiredMetaFields: [] }, meta: {}, onBeforeFileAdded: currentFile => currentFile, onBeforeUpload: files => files, store: DefaultStore(), logger: justErrorsLogger, infoTimeout: 5000 }; // Merge default options with the ones set by user, // making sure to merge restrictions too this.opts = { ...defaultOptions, ..._opts, restrictions: { ...defaultOptions.restrictions, ...(_opts && _opts.restrictions) } }; // Support debug: true for backwards-compatability, unless logger is set in opts // opts instead of this.opts to avoid comparing objects — we set logger: justErrorsLogger in defaultOptions if (_opts && _opts.logger && _opts.debug) { this.log('You are using a custom `logger`, but also set `debug: true`, which uses built-in logger to output logs to console. Ignoring `debug: true` and using your custom `logger`.', 'warning'); } else if (_opts && _opts.debug) { this.opts.logger = debugLogger; } this.log(`Using Core v${this.constructor.VERSION}`); if (this.opts.restrictions.allowedFileTypes && this.opts.restrictions.allowedFileTypes !== null && !Array.isArray(this.opts.restrictions.allowedFileTypes)) { throw new TypeError('`restrictions.allowedFileTypes` must be an array'); } this.i18nInit(); // ___Why throttle at 500ms? // - We must throttle at >250ms for superfocus in Dashboard to work well // (because animation takes 0.25s, and we want to wait for all animations to be over before refocusing). // [Practical Check]: if thottle is at 100ms, then if you are uploading a file, // and click 'ADD MORE FILES', - focus won't activate in Firefox. // - We must throttle at around >500ms to avoid performance lags. // [Practical Check] Firefox, try to upload a big file for a prolonged period of time. Laptop will start to heat up. this.calculateProgress = throttle(this.calculateProgress.bind(this), 500, { leading: true, trailing: true }); this.store = this.opts.store; this.setState({ plugins: {}, files: {}, currentUploads: {}, allowNewUpload: true, capabilities: { uploadProgress: supportsUploadProgress(), individualCancellation: true, resumableUploads: false }, totalProgress: 0, meta: { ...this.opts.meta }, info: [], recoveredState: null }); _classPrivateFieldLooseBase(this, _storeUnsubscribe)[_storeUnsubscribe] = this.store.subscribe((prevState, nextState, patch) => { this.emit('state-update', prevState, nextState, patch); this.updateAll(nextState); }); // Exposing uppy object on window for debugging and testing if (this.opts.debug && typeof window !== 'undefined') { window[this.opts.id] = this; } _classPrivateFieldLooseBase(this, _addListeners)[_addListeners](); } emit(event, ...args) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].emit(event, ...args); } on(event, callback) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].on(event, callback); return this; } once(event, callback) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].once(event, callback); return this; } off(event, callback) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].off(event, callback); return this; } /** * Iterate on all plugins and run `update` on them. * Called each time state changes. * */ updateAll(state) { this.iteratePlugins(plugin => { plugin.update(state); }); } /** * Updates state with a patch * * @param {object} patch {foo: 'bar'} */ setState(patch) { this.store.setState(patch); } /** * Returns current state. * * @returns {object} */ getState() { return this.store.getState(); } /** * Back compat for when uppy.state is used instead of uppy.getState(). * * @deprecated */ get state() { // Here, state is a non-enumerable property. return this.getState(); } /** * Shorthand to set state for a specific file. */ setFileState(fileID, state) { if (!this.getState().files[fileID]) { throw new Error(`Can’t set state for ${fileID} (the file could have been removed)`); } this.setState({ files: { ...this.getState().files, [fileID]: { ...this.getState().files[fileID], ...state } } }); } i18nInit() { const translator = new Translator([this.defaultLocale, this.opts.locale]); this.i18n = translator.translate.bind(translator); this.i18nArray = translator.translateArray.bind(translator); this.locale = translator.locale; } setOptions(newOpts) { this.opts = { ...this.opts, ...newOpts, restrictions: { ...this.opts.restrictions, ...(newOpts && newOpts.restrictions) } }; if (newOpts.meta) { this.setMeta(newOpts.meta); } this.i18nInit(); if (newOpts.locale) { this.iteratePlugins(plugin => { plugin.setOptions(); }); } // Note: this is not the preact `setState`, it's an internal function that has the same name. this.setState(); // so that UI re-renders with new options } resetProgress() { const defaultProgress = { percentage: 0, bytesUploaded: 0, uploadComplete: false, uploadStarted: null }; const files = { ...this.getState().files }; const updatedFiles = {}; Object.keys(files).forEach(fileID => { const updatedFile = { ...files[fileID] }; updatedFile.progress = { ...updatedFile.progress, ...defaultProgress }; updatedFiles[fileID] = updatedFile; }); this.setState({ files: updatedFiles, totalProgress: 0 }); this.emit('reset-progress'); } addPreProcessor(fn) { _classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors].add(fn); } removePreProcessor(fn) { return _classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors].delete(fn); } addPostProcessor(fn) { _classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].add(fn); } removePostProcessor(fn) { return _classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].delete(fn); } addUploader(fn) { _classPrivateFieldLooseBase(this, _uploaders)[_uploaders].add(fn); } removeUploader(fn) { return _classPrivateFieldLooseBase(this, _uploaders)[_uploaders].delete(fn); } setMeta(data) { const updatedMeta = { ...this.getState().meta, ...data }; const updatedFiles = { ...this.getState().files }; Object.keys(updatedFiles).forEach(fileID => { updatedFiles[fileID] = { ...updatedFiles[fileID], meta: { ...updatedFiles[fileID].meta, ...data } }; }); this.log('Adding metadata:'); this.log(data); this.setState({ meta: updatedMeta, files: updatedFiles }); } setFileMeta(fileID, data) { const updatedFiles = { ...this.getState().files }; if (!updatedFiles[fileID]) { this.log('Was trying to set metadata for a file that has been removed: ', fileID); return; } const newMeta = { ...updatedFiles[fileID].meta, ...data }; updatedFiles[fileID] = { ...updatedFiles[fileID], meta: newMeta }; this.setState({ files: updatedFiles }); } /** * Get a file object. * * @param {string} fileID The ID of the file object to return. */ getFile(fileID) { return this.getState().files[fileID]; } /** * Get all files in an array. */ getFiles() { const { files } = this.getState(); return Object.values(files); } getObjectOfFilesPerState() { const { files: filesObject, totalProgress, error } = this.getState(); const files = Object.values(filesObject); const inProgressFiles = files.filter(({ progress }) => !progress.uploadComplete && progress.uploadStarted); const newFiles = files.filter(file => !file.progress.uploadStarted); const startedFiles = files.filter(file => file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess); const uploadStartedFiles = files.filter(file => file.progress.uploadStarted); const pausedFiles = files.filter(file => file.isPaused); const completeFiles = files.filter(file => file.progress.uploadComplete); const erroredFiles = files.filter(file => file.error); const inProgressNotPausedFiles = inProgressFiles.filter(file => !file.isPaused); const processingFiles = files.filter(file => file.progress.preprocess || file.progress.postprocess); return { newFiles, startedFiles, uploadStartedFiles, pausedFiles, completeFiles, erroredFiles, inProgressFiles, inProgressNotPausedFiles, processingFiles, isUploadStarted: uploadStartedFiles.length > 0, isAllComplete: totalProgress === 100 && completeFiles.length === files.length && processingFiles.length === 0, isAllErrored: !!error && erroredFiles.length === files.length, isAllPaused: inProgressFiles.length !== 0 && pausedFiles.length === inProgressFiles.length, isUploadInProgress: inProgressFiles.length > 0, isSomeGhost: files.some(file => file.isGhost) }; } /** * A public wrapper for _checkRestrictions — checks if a file passes a set of restrictions. * For use in UI pluigins (like Providers), to disallow selecting files that won’t pass restrictions. * * @param {object} file object to check * @param {Array} [files] array to check maxNumberOfFiles and maxTotalFileSize * @returns {object} { result: true/false, reason: why file didn’t pass restrictions } */ validateRestrictions(file, files) { try { _classPrivateFieldLooseBase(this, _checkRestrictions)[_checkRestrictions](file, files); return { result: true }; } catch (err) { return { result: false, reason: err.message }; } } /** * Check if file passes a set of restrictions set in options: maxFileSize, minFileSize, * maxNumberOfFiles and allowedFileTypes. * * @param {object} file object to check * @param {Array} [files] array to check maxNumberOfFiles and maxTotalFileSize * @private */ checkIfFileAlreadyExists(fileID) { const { files } = this.getState(); if (files[fileID] && !files[fileID].isGhost) { return true; } return false; } /** * Create a file state object based on user-provided `addFile()` options. * * Note this is extremely side-effectful and should only be done when a file state object * will be added to state immediately afterward! * * The `files` value is passed in because it may be updated by the caller without updating the store. */ /** * Add a new file to `state.files`. This will run `onBeforeFileAdded`, * try to guess file type in a clever way, check file against restrictions, * and start an upload if `autoProceed === true`. * * @param {object} file object to add * @returns {string} id for the added file */ addFile(file) { _classPrivateFieldLooseBase(this, _assertNewUploadAllowed)[_assertNewUploadAllowed](file); const { files } = this.getState(); let newFile = _classPrivateFieldLooseBase(this, _checkAndCreateFileStateObject)[_checkAndCreateFileStateObject](files, file); // Users are asked to re-select recovered files without data, // and to keep the progress, meta and everthing else, we only replace said data if (files[newFile.id] && files[newFile.id].isGhost) { newFile = { ...files[newFile.id], data: file.data, isGhost: false }; this.log(`Replaced the blob in the restored ghost file: ${newFile.name}, ${newFile.id}`); } this.setState({ files: { ...files, [newFile.id]: newFile } }); this.emit('file-added', newFile); this.emit('files-added', [newFile]); this.log(`Added file: ${newFile.name}, ${newFile.id}, mime type: ${newFile.type}`); _classPrivateFieldLooseBase(this, _startIfAutoProceed)[_startIfAutoProceed](); return newFile.id; } /** * Add multiple files to `state.files`. See the `addFile()` documentation. * * If an error occurs while adding a file, it is logged and the user is notified. * This is good for UI plugins, but not for programmatic use. * Programmatic users should usually still use `addFile()` on individual files. */ addFiles(fileDescriptors) { _classPrivateFieldLooseBase(this, _assertNewUploadAllowed)[_assertNewUploadAllowed](); // create a copy of the files object only once const files = { ...this.getState().files }; const newFiles = []; const errors = []; for (let i = 0; i < fileDescriptors.length; i++) { try { let newFile = _classPrivateFieldLooseBase(this, _checkAndCreateFileStateObject)[_checkAndCreateFileStateObject](files, fileDescriptors[i]); // Users are asked to re-select recovered files without data, // and to keep the progress, meta and everthing else, we only replace said data if (files[newFile.id] && files[newFile.id].isGhost) { newFile = { ...files[newFile.id], data: fileDescriptors[i].data, isGhost: false }; this.log(`Replaced blob in a ghost file: ${newFile.name}, ${newFile.id}`); } files[newFile.id] = newFile; newFiles.push(newFile); } catch (err) { if (!err.isRestriction) { errors.push(err); } } } this.setState({ files }); newFiles.forEach(newFile => { this.emit('file-added', newFile); }); this.emit('files-added', newFiles); if (newFiles.length > 5) { this.log(`Added batch of ${newFiles.length} files`); } else { Object.keys(newFiles).forEach(fileID => { this.log(`Added file: ${newFiles[fileID].name}\n id: ${newFiles[fileID].id}\n type: ${newFiles[fileID].type}`); }); } if (newFiles.length > 0) { _classPrivateFieldLooseBase(this, _startIfAutoProceed)[_startIfAutoProceed](); } if (errors.length > 0) { let message = 'Multiple errors occurred while adding files:\n'; errors.forEach(subError => { message += `\n * ${subError.message}`; }); this.info({ message: this.i18n('addBulkFilesFailed', { smart_count: errors.length }), details: message }, 'error', this.opts.infoTimeout); if (typeof AggregateError === 'function') { throw new AggregateError(errors, message); } else { const err = new Error(message); err.errors = errors; throw err; } } } removeFiles(fileIDs, reason) { const { files, currentUploads } = this.getState(); const updatedFiles = { ...files }; const updatedUploads = { ...currentUploads }; const removedFiles = Object.create(null); fileIDs.forEach(fileID => { if (files[fileID]) { removedFiles[fileID] = files[fileID]; delete updatedFiles[fileID]; } }); // Remove files from the `fileIDs` list in each upload. function fileIsNotRemoved(uploadFileID) { return removedFiles[uploadFileID] === undefined; } Object.keys(updatedUploads).forEach(uploadID => { const newFileIDs = currentUploads[uploadID].fileIDs.filter(fileIsNotRemoved); // Remove the upload if no files are associated with it anymore. if (newFileIDs.length === 0) { delete updatedUploads[uploadID]; return; } updatedUploads[uploadID] = { ...currentUploads[uploadID], fileIDs: newFileIDs }; }); const stateUpdate = { currentUploads: updatedUploads, files: updatedFiles }; // If all files were removed - allow new uploads, // and clear recoveredState if (Object.keys(updatedFiles).length === 0) { stateUpdate.allowNewUpload = true; stateUpdate.error = null; stateUpdate.recoveredState = null; } this.setState(stateUpdate); this.calculateTotalProgress(); const removedFileIDs = Object.keys(removedFiles); removedFileIDs.forEach(fileID => { this.emit('file-removed', removedFiles[fileID], reason); }); if (removedFileIDs.length > 5) { this.log(`Removed ${removedFileIDs.length} files`); } else { this.log(`Removed files: ${removedFileIDs.join(', ')}`); } } removeFile(fileID, reason = null) { this.removeFiles([fileID], reason); } pauseResume(fileID) { if (!this.getState().capabilities.resumableUploads || this.getFile(fileID).uploadComplete) { return undefined; } const wasPaused = this.getFile(fileID).isPaused || false; const isPaused = !wasPaused; this.setFileState(fileID, { isPaused }); this.emit('upload-pause', fileID, isPaused); return isPaused; } pauseAll() { const updatedFiles = { ...this.getState().files }; const inProgressUpdatedFiles = Object.keys(updatedFiles).filter(file => { return !updatedFiles[file].progress.uploadComplete && updatedFiles[file].progress.uploadStarted; }); inProgressUpdatedFiles.forEach(file => { const updatedFile = { ...updatedFiles[file], isPaused: true }; updatedFiles[file] = updatedFile; }); this.setState({ files: updatedFiles }); this.emit('pause-all'); } resumeAll() { const updatedFiles = { ...this.getState().files }; const inProgressUpdatedFiles = Object.keys(updatedFiles).filter(file => { return !updatedFiles[file].progress.uploadComplete && updatedFiles[file].progress.uploadStarted; }); inProgressUpdatedFiles.forEach(file => { const updatedFile = { ...updatedFiles[file], isPaused: false, error: null }; updatedFiles[file] = updatedFile; }); this.setState({ files: updatedFiles }); this.emit('resume-all'); } retryAll() { const updatedFiles = { ...this.getState().files }; const filesToRetry = Object.keys(updatedFiles).filter(file => { return updatedFiles[file].error; }); filesToRetry.forEach(file => { const updatedFile = { ...updatedFiles[file], isPaused: false, error: null }; updatedFiles[file] = updatedFile; }); this.setState({ files: updatedFiles, error: null }); this.emit('retry-all', filesToRetry); if (filesToRetry.length === 0) { return Promise.resolve({ successful: [], failed: [] }); } const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](filesToRetry, { forceAllowNewUpload: true // create new upload even if allowNewUpload: false }); return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID); } cancelAll() { this.emit('cancel-all'); const { files } = this.getState(); const fileIDs = Object.keys(files); if (fileIDs.length) { this.removeFiles(fileIDs, 'cancel-all'); } this.setState({ totalProgress: 0, error: null, recoveredState: null }); } retryUpload(fileID) { this.setFileState(fileID, { error: null, isPaused: false }); this.emit('upload-retry', fileID); const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload]([fileID], { forceAllowNewUpload: true // create new upload even if allowNewUpload: false }); return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID); } reset() { this.cancelAll(); } logout() { this.iteratePlugins(plugin => { if (plugin.provider && plugin.provider.logout) { plugin.provider.logout(); } }); } calculateProgress(file, data) { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } // bytesTotal may be null or zero; in that case we can't divide by it const canHavePercentage = Number.isFinite(data.bytesTotal) && data.bytesTotal > 0; this.setFileState(file.id, { progress: { ...this.getFile(file.id).progress, bytesUploaded: data.bytesUploaded, bytesTotal: data.bytesTotal, percentage: canHavePercentage ? Math.round(data.bytesUploaded / data.bytesTotal * 100) : 0 } }); this.calculateTotalProgress(); } calculateTotalProgress() { // calculate total progress, using the number of files currently uploading, // multiplied by 100 and the summ of individual progress of each file const files = this.getFiles(); const inProgress = files.filter(file => { return file.progress.uploadStarted || file.progress.preprocess || file.progress.postprocess; }); if (inProgress.length === 0) { this.emit('progress', 0); this.setState({ totalProgress: 0 }); return; } const sizedFiles = inProgress.filter(file => file.progress.bytesTotal != null); const unsizedFiles = inProgress.filter(file => file.progress.bytesTotal == null); if (sizedFiles.length === 0) { const progressMax = inProgress.length * 100; const currentProgress = unsizedFiles.reduce((acc, file) => { return acc + file.progress.percentage; }, 0); const totalProgress = Math.round(currentProgress / progressMax * 100); this.setState({ totalProgress }); return; } let totalSize = sizedFiles.reduce((acc, file) => { return acc + file.progress.bytesTotal; }, 0); const averageSize = totalSize / sizedFiles.length; totalSize += averageSize * unsizedFiles.length; let uploadedSize = 0; sizedFiles.forEach(file => { uploadedSize += file.progress.bytesUploaded; }); unsizedFiles.forEach(file => { uploadedSize += averageSize * (file.progress.percentage || 0) / 100; }); let totalProgress = totalSize === 0 ? 0 : Math.round(uploadedSize / totalSize * 100); // hot fix, because: // uploadedSize ended up larger than totalSize, resulting in 1325% total if (totalProgress > 100) { totalProgress = 100; } this.setState({ totalProgress }); this.emit('progress', totalProgress); } /** * Registers listeners for all global actions, like: * `error`, `file-removed`, `upload-progress` */ updateOnlineStatus() { const online = typeof window.navigator.onLine !== 'undefined' ? window.navigator.onLine : true; if (!online) { this.emit('is-offline'); this.info(this.i18n('noInternetConnection'), 'error', 0); this.wasOffline = true; } else { this.emit('is-online'); if (this.wasOffline) { this.emit('back-online'); this.info(this.i18n('connectedToInternet'), 'success', 3000); this.wasOffline = false; } } } getID() { return this.opts.id; } /** * Registers a plugin with Core. * * @param {object} Plugin object * @param {object} [opts] object with options to be passed to Plugin * @returns {object} self for chaining */ // eslint-disable-next-line no-shadow use(Plugin, opts) { if (typeof Plugin !== 'function') { const msg = `Expected a plugin class, but got ${Plugin === null ? 'null' : typeof Plugin}.` + ' Please verify that the plugin was imported and spelled correctly.'; throw new TypeError(msg); } // Instantiate const plugin = new Plugin(this, opts); const pluginId = plugin.id; if (!pluginId) { throw new Error('Your plugin must have an id'); } if (!plugin.type) { throw new Error('Your plugin must have a type'); } const existsPluginAlready = this.getPlugin(pluginId); if (existsPluginAlready) { const msg = `Already found a plugin named '${existsPluginAlready.id}'. ` + `Tried to use: '${pluginId}'.\n` + 'Uppy plugins must have unique `id` options. See https://uppy.io/docs/plugins/#id.'; throw new Error(msg); } if (Plugin.VERSION) { this.log(`Using ${pluginId} v${Plugin.VERSION}`); } if (plugin.type in _classPrivateFieldLooseBase(this, _plugins)[_plugins]) { _classPrivateFieldLooseBase(this, _plugins)[_plugins][plugin.type].push(plugin); } else { _classPrivateFieldLooseBase(this, _plugins)[_plugins][plugin.type] = [plugin]; } plugin.install(); return this; } /** * Find one Plugin by name. * * @param {string} id plugin id * @returns {BasePlugin|undefined} */ getPlugin(id) { for (const plugins of Object.values(_classPrivateFieldLooseBase(this, _plugins)[_plugins])) { const foundPlugin = plugins.find(plugin => plugin.id === id); if (foundPlugin != null) return foundPlugin; } return undefined; } [_Symbol$for](type) { return _classPrivateFieldLooseBase(this, _plugins)[_plugins][type]; } /** * Iterate through all `use`d plugins. * * @param {Function} method that will be run on each plugin */ iteratePlugins(method) { Object.values(_classPrivateFieldLooseBase(this, _plugins)[_plugins]).flat(1).forEach(method); } /** * Uninstall and remove a plugin. * * @param {object} instance The plugin instance to remove. */ removePlugin(instance) { this.log(`Removing plugin ${instance.id}`); this.emit('plugin-remove', instance); if (instance.uninstall) { instance.uninstall(); } const list = _classPrivateFieldLooseBase(this, _plugins)[_plugins][instance.type]; // list.indexOf failed here, because Vue3 converted the plugin instance // to a Proxy object, which failed the strict comparison test: // obj !== objProxy const index = list.findIndex(item => item.id === instance.id); if (index !== -1) { list.splice(index, 1); } const state = this.getState(); const updatedState = { plugins: { ...state.plugins, [instance.id]: undefined } }; this.setState(updatedState); } /** * Uninstall all plugins and close down this Uppy instance. */ close() { this.log(`Closing Uppy instance ${this.opts.id}: removing all files and uninstalling plugins`); this.reset(); _classPrivateFieldLooseBase(this, _storeUnsubscribe)[_storeUnsubscribe](); this.iteratePlugins(plugin => { this.removePlugin(plugin); }); if (typeof window !== 'undefined' && window.removeEventListener) { window.removeEventListener('online', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]); window.removeEventListener('offline', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]); } } hideInfo() { const { info } = this.getState(); this.setState({ info: info.slice(1) }); this.emit('info-hidden'); } /** * Set info message in `state.info`, so that UI plugins like `Informer` * can display the message. * * @param {string | object} message Message to be displayed by the informer * @param {string} [type] * @param {number} [duration] */ info(message, type = 'info', duration = 3000) { const isComplexMessage = typeof message === 'object'; this.setState({ info: [...this.getState().info, { type, message: isComplexMessage ? message.message : message, details: isComplexMessage ? message.details : null }] }); setTimeout(() => this.hideInfo(), duration); this.emit('info-visible'); } /** * Passes messages to a function, provided in `opts.logger`. * If `opts.logger: Uppy.debugLogger` or `opts.debug: true`, logs to the browser console. * * @param {string|object} message to log * @param {string} [type] optional `error` or `warning` */ log(message, type) { const { logger } = this.opts; switch (type) { case 'error': logger.error(message); break; case 'warning': logger.warn(message); break; default: logger.debug(message); break; } } /** * Restore an upload by its ID. */ restore(uploadID) { this.log(`Core: attempting to restore upload "${uploadID}"`); if (!this.getState().currentUploads[uploadID]) { _classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID); return Promise.reject(new Error('Nonexistent upload')); } return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID); } /** * Create an upload for a bunch of files. * * @param {Array<string>} fileIDs File IDs to include in this upload. * @returns {string} ID of this upload. */ [_Symbol$for2](...args) { return _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](...args); } /** * Add data to an upload's result object. * * @param {string} uploadID The ID of the upload. * @param {object} data Data properties to add to the result object. */ addResultData(uploadID, data) { if (!_classPrivateFieldLooseBase(this, _getUpload)[_getUpload](uploadID)) { this.log(`Not setting result for an upload that has been removed: ${uploadID}`); return; } const { currentUploads } = this.getState(); const currentUpload = { ...currentUploads[uploadID], result: { ...currentUploads[uploadID].result, ...data } }; this.setState({ currentUploads: { ...currentUploads, [uploadID]: currentUpload } }); } /** * Remove an upload, eg. if it has been canceled or completed. * * @param {string} uploadID The ID of the upload. */ /** * Start an upload for all the files that are not currently being uploaded. * * @returns {Promise} */ upload() { var _classPrivateFieldLoo; if (!((_classPrivateFieldLoo = _classPrivateFieldLooseBase(this, _plugins)[_plugins].uploader) != null && _classPrivateFieldLoo.length)) { this.log('No uploader type plugins are used', 'warning'); } let { files } = this.getState(); const onBeforeUploadResult = this.opts.onBeforeUpload(files); if (onBeforeUploadResult === false) { return Promise.reject(new Error('Not starting the upload because onBeforeUpload returned false')); } if (onBeforeUploadResult && typeof onBeforeUploadResult === 'object') { files = onBeforeUploadResult; // Updating files in state, because uploader plugins receive file IDs, // and then fetch the actual file object from state this.setState({ files }); } return Promise.resolve().then(() => { _classPrivateFieldLooseBase(this, _checkMinNumberOfFiles)[_checkMinNumberOfFiles](files); _classPrivateFieldLooseBase(this, _checkRequiredMetaFields)[_checkRequiredMetaFields](files); }).catch(err => { _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err); }).then(() => { const { currentUploads } = this.getState(); // get a list of files that are currently assigned to uploads const currentlyUploadingFiles = Object.values(currentUploads).flatMap(curr => curr.fileIDs); const waitingFileIDs = []; Object.keys(files).forEach(fileID => { const file = this.getFile(fileID); // if the file hasn't started uploading and hasn't already been assigned to an upload.. if (!file.progress.uploadStarted && currentlyUploadingFiles.indexOf(fileID) === -1) { waitingFileIDs.push(file.id); } }); const uploadID = _classPrivateFieldLooseBase(this, _createUpload)[_createUpload](waitingFileIDs); return _classPrivateFieldLooseBase(this, _runUpload)[_runUpload](uploadID); }).catch(err => { _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, { showInformer: false }); }); } } function _checkRestrictions2(file, files = this.getFiles()) { const { maxFileSize, minFileSize, maxTotalFileSize, maxNumberOfFiles, allowedFileTypes } = this.opts.restrictions; if (maxNumberOfFiles) { if (files.length + 1 > maxNumberOfFiles) { throw new RestrictionError(`${this.i18n('youCanOnlyUploadX', { smart_count: maxNumberOfFiles })}`); } } if (allowedFileTypes) { const isCorrectFileType = allowedFileTypes.some(type => { // check if this is a mime-type if (type.indexOf('/') > -1) { if (!file.type) return false; return match(file.type.replace(/;.*?$/, ''), type); } // otherwise this is likely an extension if (type[0] === '.' && file.extension) { return file.extension.toLowerCase() === type.substr(1).toLowerCase(); } return false; }); if (!isCorrectFileType) { const allowedFileTypesString = allowedFileTypes.join(', '); throw new RestrictionError(this.i18n('youCanOnlyUploadFileTypes', { types: allowedFileTypesString })); } } // We can't check maxTotalFileSize if the size is unknown. if (maxTotalFileSize && file.size != null) { let totalFilesSize = 0; totalFilesSize += file.size; files.forEach(f => { totalFilesSize += f.size; }); if (totalFilesSize > maxTotalFileSize) { throw new RestrictionError(this.i18n('exceedsSize', { size: prettierBytes(maxTotalFileSize), file: file.name })); } } // We can't check maxFileSize if the size is unknown. if (maxFileSize && file.size != null) { if (file.size > maxFileSize) { throw new RestrictionError(this.i18n('exceedsSize', { size: prettierBytes(maxFileSize), file: file.name })); } } // We can't check minFileSize if the size is unknown. if (minFileSize && file.size != null) { if (file.size < minFileSize) { throw new RestrictionError(this.i18n('inferiorSize', { size: prettierBytes(minFileSize) })); } } } function _checkMinNumberOfFiles2(files) { const { minNumberOfFiles } = this.opts.restrictions; if (Object.keys(files).length < minNumberOfFiles) { throw new RestrictionError(`${this.i18n('youHaveToAtLeastSelectX', { smart_count: minNumberOfFiles })}`); } } function _checkRequiredMetaFields2(files) { const { requiredMetaFields } = this.opts.restrictions; const { hasOwnProperty } = Object.prototype; const errors = []; for (const fileID of Object.keys(files)) { const file = this.getFile(fileID); for (let i = 0; i < requiredMetaFields.length; i++) { if (!hasOwnProperty.call(file.meta, requiredMetaFields[i]) || file.meta[requiredMetaFields[i]] === '') { const err = new RestrictionError(`${this.i18n('missingRequiredMetaFieldOnFile', { fileName: file.name })}`); errors.push(err); _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, { file, showInformer: false, throwErr: false }); } } } if (errors.length) { throw new AggregateRestrictionError(`${this.i18n('missingRequiredMetaField')}`, errors); } } function _showOrLogErrorAndThrow2(err, { showInformer = true, file = null, throwErr = true } = {}) { const message = typeof err === 'object' ? err.message : err; const details = typeof err === 'object' && err.details ? err.details : ''; // Restriction errors should be logged, but not as errors, // as they are expected and shown in the UI. let logMessageWithDetails = message; if (details) { logMessageWithDetails += ` ${details}`; } if (err.isRestriction) { this.log(logMessageWithDetails); this.emit('restriction-failed', file, err); } else { this.log(logMessageWithDetails, 'error'); } // Sometimes informer has to be shown manually by the developer, // for example, in `onBeforeFileAdded`. if (showInformer) { this.info({ message, details }, 'error', this.opts.infoTimeout); } if (throwErr) { throw typeof err === 'object' ? err : new Error(err); } } function _assertNewUploadAllowed2(file) { const { allowNewUpload } = this.getState(); if (allowNewUpload === false) { _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](new RestrictionError(this.i18n('noMoreFilesAllowed')), { file }); } } function _checkAndCreateFileStateObject2(files, fileDescriptor) { const fileType = getFileType(fileDescriptor); const fileName = getFileName(fileType, fileDescriptor); const fileExtension = getFileNameAndExtension(fileName).extension; const isRemote = Boolean(fileDescriptor.isRemote); const fileID = generateFileID({ ...fileDescriptor, type: fileType }); if (this.checkIfFileAlreadyExists(fileID)) { const error = new RestrictionError(this.i18n('noDuplicates', { fileName })); _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](error, { file: fileDescriptor }); } const meta = fileDescriptor.meta || {}; meta.name = fileName; meta.type = fileType; // `null` means the size is unknown. const size = Number.isFinite(fileDescriptor.data.size) ? fileDescriptor.data.size : null; let newFile = { source: fileDescriptor.source || '', id: fileID, name: fileName, extension: fileExtension || '', meta: { ...this.getState().meta, ...meta }, type: fileType, data: fileDescriptor.data, progress: { percentage: 0, bytesUploaded: 0, bytesTotal: size, uploadComplete: false, uploadStarted: null }, size, isRemote, remote: fileDescriptor.remote || '', preview: fileDescriptor.preview }; const onBeforeFileAddedResult = this.opts.onBeforeFileAdded(newFile, files); if (onBeforeFileAddedResult === false) { // Don’t show UI info for this error, as it should be done by the developer _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](new RestrictionError('Cannot add the file because onBeforeFileAdded returned false.'), { showInformer: false, fileDescriptor }); } else if (typeof onBeforeFileAddedResult === 'object' && onBeforeFileAddedResult !== null) { newFile = onBeforeFileAddedResult; } try { const filesArray = Object.keys(files).map(i => files[i]); _classPrivateFieldLooseBase(this, _checkRestrictions)[_checkRestrictions](newFile, filesArray); } catch (err) { _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](err, { file: newFile }); } return newFile; } function _startIfAutoProceed2() { if (this.opts.autoProceed && !this.scheduledAutoProceed) { this.scheduledAutoProceed = setTimeout(() => { this.scheduledAutoProceed = null; this.upload().catch(err => { if (!err.isRestriction) { this.log(err.stack || err.message || err); } }); }, 4); } } function _addListeners2() { /** * @param {Error} error * @param {object} [file] * @param {object} [response] */ const errorHandler = (error, file, response) => { let errorMsg = error.message || 'Unknown error'; if (error.details) { errorMsg += ` ${error.details}`; } this.setState({ error: errorMsg }); if (file != null && file.id in this.getState().files) { this.setFileState(file.id, { error: errorMsg, response }); } }; this.on('error', errorHandler); this.on('upload-error', (file, error, response) => { errorHandler(error, file, response); if (typeof error === 'object' && error.message) { const newError = new Error(error.message); newError.details = error.message; if (error.details) { newError.details += ` ${error.details}`; } newError.message = this.i18n('failedToUpload', { file: file.name }); _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](newError, { throwErr: false }); } else { _classPrivateFieldLooseBase(this, _showOrLogErrorAndThrow)[_showOrLogErrorAndThrow](error, { throwErr: false }); } }); this.on('upload', () => { this.setState({ error: null }); }); this.on('upload-started', file => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } this.setFileState(file.id, { progress: { uploadStarted: Date.now(), uploadComplete: false, percentage: 0, bytesUploaded: 0, bytesTotal: file.size } }); }); this.on('upload-progress', this.calculateProgress); this.on('upload-success', (file, uploadResp) => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } const currentProgress = this.getFile(file.id).progress; this.setFileState(file.id, { progress: { ...currentProgress, postprocess: _classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors].size > 0 ? { mode: 'indeterminate' } : null, uploadComplete: true, percentage: 100, bytesUploaded: currentProgress.bytesTotal }, response: uploadResp, uploadURL: uploadResp.uploadURL, isPaused: false }); // Remote providers sometimes don't tell us the file size, // but we can know how many bytes we uploaded once the upload is complete. if (file.size == null) { this.setFileState(file.id, { size: uploadResp.bytesUploaded || currentProgress.bytesTotal }); } this.calculateTotalProgress(); }); this.on('preprocess-progress', (file, progress) => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } this.setFileState(file.id, { progress: { ...this.getFile(file.id).progress, preprocess: progress } }); }); this.on('preprocess-complete', file => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } const files = { ...this.getState().files }; files[file.id] = { ...files[file.id], progress: { ...files[file.id].progress } }; delete files[file.id].progress.preprocess; this.setState({ files }); }); this.on('postprocess-progress', (file, progress) => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } this.setFileState(file.id, { progress: { ...this.getState().files[file.id].progress, postprocess: progress } }); }); this.on('postprocess-complete', file => { if (!this.getFile(file.id)) { this.log(`Not setting progress for a file that has been removed: ${file.id}`); return; } const files = { ...this.getState().files }; files[file.id] = { ...files[file.id], progress: { ...files[file.id].progress } }; delete files[file.id].progress.postprocess; this.setState({ files }); }); this.on('restored', () => { // Files may have changed--ensure progress is still accurate. this.calculateTotalProgress(); }); // show informer if offline if (typeof window !== 'undefined' && window.addEventListener) { window.addEventListener('online', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]); window.addEventListener('offline', _classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus]); setTimeout(_classPrivateFieldLooseBase(this, _updateOnlineStatus)[_updateOnlineStatus], 3000); } } function _createUpload2(fileIDs, opts = {}) { // uppy.retryAll sets this to true — when retrying we want to ignore `allowNewUpload: false` const { forceAllowNewUpload = false } = opts; const { allowNewUpload, currentUploads } = this.getState(); if (!allowNewUpload && !forceAllowNewUpload) { throw new Error('Cannot create a new upload: already uploading.'); } const uploadID = nanoid(); this.emit('upload', { id: uploadID, fileIDs }); this.setState({ allowNewUpload: this.opts.allowMultipleUploadBatches !== false && this.opts.allowMultipleUploads !== false, currentUploads: { ...currentUploads, [uploadID]: { fileIDs, step: 0, result: {} } } }); return uploadID; } function _getUpload2(uploadID) { const { currentUploads } = this.getState(); return currentUploads[uploadID]; } function _removeUpload2(uploadID) { const currentUploads = { ...this.getState().currentUploads }; delete currentUploads[uploadID]; this.setState({ currentUploads }); } async function _runUpload2(uploadID) { let { currentUploads } = this.getState(); let currentUpload = currentUploads[uploadID]; const restoreStep = currentUpload.step || 0; const steps = [..._classPrivateFieldLooseBase(this, _preProcessors)[_preProcessors], ..._classPrivateFieldLooseBase(this, _uploaders)[_uploaders], ..._classPrivateFieldLooseBase(this, _postProcessors)[_postProcessors]]; try { for (let step = restoreStep; step < steps.length; step++) { if (!currentUpload) { break; } const fn = steps[step]; const updatedUpload = { ...currentUpload, step }; this.setState({ currentUploads: { ...currentUploads, [uploadID]: updatedUpload } }); // TODO give this the `updatedUpload` object as its only parameter maybe? // Otherwise when more metadata may be added to the upload this would keep getting more parameters await fn(updatedUpload.fileIDs, uploadID); // Update currentUpload value in case it was modified asynchronously. currentUploads = this.getState().currentUploads; currentUpload = currentUploads[uploadID]; } } catch (err) { this.emit('error', err); _classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID); throw err; } // Set result data. if (currentUpload) { // Mark postprocessing step as complete if necessary; this addresses a case where we might get // stuck in the postprocessing UI while the upload is fully complete. // If the postprocessing steps do not do any work, they may not emit postprocessing events at // all, and never mark the postprocessing as complete. This is fine on its own but we // introduced code in the @uppy/core upload-success handler to prepare postprocessing progress // state if any postprocessors are registered. That is to avoid a "flash of completed state" // before the postprocessing plugins can emit events. // // So, just in case an upload with postprocessing plugins *has* completed *without* emitting // postprocessing completion, we do it instead. currentUpload.fileIDs.forEach(fileID => { const file = this.getFile(fileID); if (file && file.progress.postprocess) { this.emit('postprocess-complete', file); } }); const files = currentUpload.fileIDs.map(fileID => this.getFile(fileID)); const successful = files.filter(file => !file.error); const failed = files.filter(file => file.error); await this.addResultData(uploadID, { successful, failed, uploadID }); // Update currentUpload value in case it was modified asynchronously. currentUploads = this.getState().currentUploads; currentUpload = currentUploads[uploadID]; } // Emit completion events. // This is in a separate function so that the `currentUploads` variable // always refers to the latest state. In the handler right above it refers // to an outdated object without the `.result` property. let result; if (currentUpload) { result = currentUpload.result; this.emit('complete', result); _classPrivateFieldLooseBase(this, _removeUpload)[_removeUpload](uploadID); } if (result == null) { this.log(`Not setting result for an upload that has been removed: ${uploadID}`); } return result; } Uppy.VERSION = "2.1.1"; module.exports = Uppy; },{"./getFileName":17,"./loggers":19,"./supportsUploadProgress":20,"@transloadit/prettier-bytes":1,"@uppy/store-default":22,"@uppy/utils/lib/Translator":28,"@uppy/utils/lib/generateFileID":33,"@uppy/utils/lib/getFileNameAndExtension":39,"@uppy/utils/lib/getFileType":40,"lodash.throttle":50,"mime-match":51,"namespace-emitter":52,"nanoid":53}],17:[function(require,module,exports){ "use strict"; module.exports = function getFileName(fileType, fileDescriptor) { if (fileDescriptor.name) { return fileDescriptor.name; } if (fileType.split('/')[0] === 'image') { return `${fileType.split('/')[0]}.${fileType.split('/')[1]}`; } return 'noname'; }; },{}],18:[function(require,module,exports){ 'use strict'; const Uppy = require('./Uppy'); const UIPlugin = require('./UIPlugin'); const BasePlugin = require('./BasePlugin'); const { debugLogger } = require('./loggers'); module.exports = Uppy; module.exports.Uppy = Uppy; module.exports.UIPlugin = UIPlugin; module.exports.BasePlugin = BasePlugin; module.exports.debugLogger = debugLogger; },{"./BasePlugin":14,"./UIPlugin":15,"./Uppy":16,"./loggers":19}],19:[function(require,module,exports){ "use strict"; /* eslint-disable no-console */ const getTimeStamp = require('@uppy/utils/lib/getTimeStamp'); // Swallow all logs, except errors. // default if logger is not set or debug: false const justErrorsLogger = { debug: () => {}, warn: () => {}, error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args) }; // Print logs to console with namespace + timestamp, // set by logger: Uppy.debugLogger or debug: true const debugLogger = { debug: (...args) => console.debug(`[Uppy] [${getTimeStamp()}]`, ...args), warn: (...args) => console.warn(`[Uppy] [${getTimeStamp()}]`, ...args), error: (...args) => console.error(`[Uppy] [${getTimeStamp()}]`, ...args) }; module.exports = { justErrorsLogger, debugLogger }; },{"@uppy/utils/lib/getTimeStamp":42}],20:[function(require,module,exports){ "use strict"; // Edge 15.x does not fire 'progress' events on uploads. // See https://github.com/transloadit/uppy/issues/945 // And https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12224510/ module.exports = function supportsUploadProgress(userAgent) { // Allow passing in userAgent for tests if (userAgent == null) { userAgent = typeof navigator !== 'undefined' ? navigator.userAgent : null; } // Assume it works because basically everything supports progress events. if (!userAgent) return true; const m = /Edge\/(\d+\.\d+)/.exec(userAgent); if (!m) return true; const edgeVersion = m[1]; let [major, minor] = edgeVersion.split('.'); major = parseInt(major, 10); minor = parseInt(minor, 10); // Worked before: // Edge 40.15063.0.0 // Microsoft EdgeHTML 15.15063 if (major < 15 || major === 15 && minor < 15063) { return true; } // Fixed in: // Microsoft EdgeHTML 18.18218 if (major > 18 || major === 18 && minor >= 18218) { return true; } // other versions don't work. return false; }; },{}],21:[function(require,module,exports){ "use strict"; var _class, _temp; const BasePlugin = require('@uppy/core/lib/BasePlugin'); const getDroppedFiles = require('@uppy/utils/lib/getDroppedFiles'); const toArray = require('@uppy/utils/lib/toArray'); /** * Drop Target plugin * */ module.exports = (_temp = _class = class DropTarget extends BasePlugin { constructor(uppy, opts) { super(uppy, opts); this.addFiles = files => { const descriptors = files.map(file => ({ source: this.id, name: file.name, type: file.type, data: file, meta: { // path of the file relative to the ancestor directory the user selected. // e.g. 'docs/Old Prague/airbnb.pdf' relativePath: file.relativePath || null } })); try { this.uppy.addFiles(descriptors); } catch (err) { this.uppy.log(err); } }; this.handleDrop = async event => { var _this$opts$onDrop, _this$opts; event.preventDefault(); event.stopPropagation(); clearTimeout(this.removeDragOverClassTimeout); // 2. Remove dragover class event.currentTarget.classList.remove('uppy-is-drag-over'); this.setPluginState({ isDraggingOver: false }); // 3. Add all dropped files this.uppy.log('[DropTarget] Files were dropped'); const logDropError = error => { this.uppy.log(error, 'error'); }; const files = await getDroppedFiles(event.dataTransfer, { logDropError }); this.addFiles(files); (_this$opts$onDrop = (_this$opts = this.opts).onDrop) == null ? void 0 : _this$opts$onDrop.call(_this$opts, event); }; this.handleDragOver = event => { var _this$opts$onDragOver, _this$opts2; event.preventDefault(); event.stopPropagation(); // 1. Add a small (+) icon on drop // (and prevent browsers from interpreting this as files being _moved_ into the browser, // https://github.com/transloadit/uppy/issues/1978) event.dataTransfer.dropEffect = 'copy'; clearTimeout(this.removeDragOverClassTimeout); event.currentTarget.classList.add('uppy-is-drag-over'); this.setPluginState({ isDraggingOver: true }); (_this$opts$onDragOver = (_this$opts2 = this.opts).onDragOver) == null ? void 0 : _this$opts$onDragOver.call(_this$opts2, event); }; this.handleDragLeave = event => { var _this$opts$onDragLeav, _this$opts3; event.preventDefault(); event.stopPropagation(); const { currentTarget } = event; clearTimeout(this.removeDragOverClassTimeout); // Timeout against flickering, this solution is taken from drag-drop library. // Solution with 'pointer-events: none' didn't work across browsers. this.removeDragOverClassTimeout = setTimeout(() => { currentTarget.classList.remove('uppy-is-drag-over'); this.setPluginState({ isDraggingOver: false }); }, 50); (_this$opts$onDragLeav = (_this$opts3 = this.opts).onDragLeave) == null ? void 0 : _this$opts$onDragLeav.call(_this$opts3, event); }; this.addListeners = () => { const { target } = this.opts; if (target instanceof Element) { this.nodes = [target]; } else if (typeof target === 'string') { this.nodes = toArray(document.querySelectorAll(target)); } if (!this.nodes && !this.nodes.length > 0) { throw new Error(`"${target}" does not match any HTML elements`); } this.nodes.forEach(node => { node.addEventListener('dragover', this.handleDragOver, false); node.addEventListener('dragleave', this.handleDragLeave, false); node.addEventListener('drop', this.handleDrop, false); }); }; this.removeListeners = () => { if (this.nodes) { this.nodes.forEach(node => { node.removeEventListener('dragover', this.handleDragOver, false); node.removeEventListener('dragleave', this.handleDragLeave, false); node.removeEventListener('drop', this.handleDrop, false); }); } }; this.type = 'acquirer'; this.id = this.opts.id || 'DropTarget'; this.title = 'Drop Target'; // Default options const defaultOpts = { target: null }; // Merge default options with the ones set by user this.opts = { ...defaultOpts, ...opts }; this.removeDragOverClassTimeout = null; } install() { this.setPluginState({ isDraggingOver: false }); this.addListeners(); } uninstall() { this.removeListeners(); } }, _class.VERSION = "1.1.1", _temp); },{"@uppy/core/lib/BasePlugin":14,"@uppy/utils/lib/getDroppedFiles":34,"@uppy/utils/lib/toArray":48}],22:[function(require,module,exports){ "use strict"; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } var _publish = /*#__PURE__*/_classPrivateFieldLooseKey("publish"); /** * Default store that keeps state in a simple object. */ class DefaultStore { constructor() { Object.defineProperty(this, _publish, { value: _publish2 }); this.state = {}; this.callbacks = []; } getState() { return this.state; } setState(patch) { const prevState = { ...this.state }; const nextState = { ...this.state, ...patch }; this.state = nextState; _classPrivateFieldLooseBase(this, _publish)[_publish](prevState, nextState, patch); } subscribe(listener) { this.callbacks.push(listener); return () => { // Remove the listener. this.callbacks.splice(this.callbacks.indexOf(listener), 1); }; } } function _publish2(...args) { this.callbacks.forEach(listener => { listener(...args); }); } DefaultStore.VERSION = "2.0.2"; module.exports = function defaultStore() { return new DefaultStore(); }; },{}],23:[function(require,module,exports){ "use strict"; /** * Little AbortController proxy module so we can swap out the implementation easily later. */ exports.AbortController = globalThis.AbortController; exports.AbortSignal = globalThis.AbortSignal; exports.createAbortError = (message = 'Aborted') => new DOMException(message, 'AbortError'); },{}],24:[function(require,module,exports){ "use strict"; var _emitter, _events; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } /** * Create a wrapper around an event emitter with a `remove` method to remove * all events that were added using the wrapped emitter. */ module.exports = (_emitter = /*#__PURE__*/_classPrivateFieldLooseKey("emitter"), _events = /*#__PURE__*/_classPrivateFieldLooseKey("events"), class EventTracker { constructor(emitter) { Object.defineProperty(this, _emitter, { writable: true, value: void 0 }); Object.defineProperty(this, _events, { writable: true, value: [] }); _classPrivateFieldLooseBase(this, _emitter)[_emitter] = emitter; } on(event, fn) { _classPrivateFieldLooseBase(this, _events)[_events].push([event, fn]); return _classPrivateFieldLooseBase(this, _emitter)[_emitter].on(event, fn); } remove() { for (const [event, fn] of _classPrivateFieldLooseBase(this, _events)[_events].splice(0)) { _classPrivateFieldLooseBase(this, _emitter)[_emitter].off(event, fn); } } }); },{}],25:[function(require,module,exports){ "use strict"; class NetworkError extends Error { constructor(error, xhr = null) { super(`This looks like a network error, the endpoint might be blocked by an internet provider or a firewall.`); this.cause = error; this.isNetworkError = true; this.request = xhr; } } module.exports = NetworkError; },{}],26:[function(require,module,exports){ "use strict"; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } var _aliveTimer = /*#__PURE__*/_classPrivateFieldLooseKey("aliveTimer"); var _isDone = /*#__PURE__*/_classPrivateFieldLooseKey("isDone"); var _onTimedOut = /*#__PURE__*/_classPrivateFieldLooseKey("onTimedOut"); var _timeout = /*#__PURE__*/_classPrivateFieldLooseKey("timeout"); /** * Helper to abort upload requests if there has not been any progress for `timeout` ms. * Create an instance using `timer = new ProgressTimeout(10000, onTimeout)` * Call `timer.progress()` to signal that there has been progress of any kind. * Call `timer.done()` when the upload has completed. */ class ProgressTimeout { constructor(timeout, timeoutHandler) { Object.defineProperty(this, _aliveTimer, { writable: true, value: void 0 }); Object.defineProperty(this, _isDone, { writable: true, value: false }); Object.defineProperty(this, _onTimedOut, { writable: true, value: void 0 }); Object.defineProperty(this, _timeout, { writable: true, value: void 0 }); _classPrivateFieldLooseBase(this, _timeout)[_timeout] = timeout; _classPrivateFieldLooseBase(this, _onTimedOut)[_onTimedOut] = timeoutHandler; } progress() { // Some browsers fire another progress event when the upload is // cancelled, so we have to ignore progress after the timer was // told to stop. if (_classPrivateFieldLooseBase(this, _isDone)[_isDone]) return; if (_classPrivateFieldLooseBase(this, _timeout)[_timeout] > 0) { clearTimeout(_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer]); _classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer] = setTimeout(_classPrivateFieldLooseBase(this, _onTimedOut)[_onTimedOut], _classPrivateFieldLooseBase(this, _timeout)[_timeout]); } } done() { if (!_classPrivateFieldLooseBase(this, _isDone)[_isDone]) { clearTimeout(_classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer]); _classPrivateFieldLooseBase(this, _aliveTimer)[_aliveTimer] = null; _classPrivateFieldLooseBase(this, _isDone)[_isDone] = true; } } } module.exports = ProgressTimeout; },{}],27:[function(require,module,exports){ "use strict"; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } function createCancelError() { return new Error('Cancelled'); } var _activeRequests = /*#__PURE__*/_classPrivateFieldLooseKey("activeRequests"); var _queuedHandlers = /*#__PURE__*/_classPrivateFieldLooseKey("queuedHandlers"); var _call = /*#__PURE__*/_classPrivateFieldLooseKey("call"); var _queueNext = /*#__PURE__*/_classPrivateFieldLooseKey("queueNext"); var _next = /*#__PURE__*/_classPrivateFieldLooseKey("next"); var _queue = /*#__PURE__*/_classPrivateFieldLooseKey("queue"); var _dequeue = /*#__PURE__*/_classPrivateFieldLooseKey("dequeue"); class RateLimitedQueue { constructor(limit) { Object.defineProperty(this, _dequeue, { value: _dequeue2 }); Object.defineProperty(this, _queue, { value: _queue2 }); Object.defineProperty(this, _next, { value: _next2 }); Object.defineProperty(this, _queueNext, { value: _queueNext2 }); Object.defineProperty(this, _call, { value: _call2 }); Object.defineProperty(this, _activeRequests, { writable: true, value: 0 }); Object.defineProperty(this, _queuedHandlers, { writable: true, value: [] }); if (typeof limit !== 'number' || limit === 0) { this.limit = Infinity; } else { this.limit = limit; } } run(fn, queueOptions) { if (_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] < this.limit) { return _classPrivateFieldLooseBase(this, _call)[_call](fn); } return _classPrivateFieldLooseBase(this, _queue)[_queue](fn, queueOptions); } wrapPromiseFunction(fn, queueOptions) { return (...args) => { let queuedRequest; const outerPromise = new Promise((resolve, reject) => { queuedRequest = this.run(() => { let cancelError; let innerPromise; try { innerPromise = Promise.resolve(fn(...args)); } catch (err) { innerPromise = Promise.reject(err); } innerPromise.then(result => { if (cancelError) { reject(cancelError); } else { queuedRequest.done(); resolve(result); } }, err => { if (cancelError) { reject(cancelError); } else { queuedRequest.done(); reject(err); } }); return () => { cancelError = createCancelError(); }; }, queueOptions); }); outerPromise.abort = () => { queuedRequest.abort(); }; return outerPromise; }; } } function _call2(fn) { _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] += 1; let done = false; let cancelActive; try { cancelActive = fn(); } catch (err) { _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1; throw err; } return { abort: () => { if (done) return; done = true; _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1; cancelActive(); _classPrivateFieldLooseBase(this, _queueNext)[_queueNext](); }, done: () => { if (done) return; done = true; _classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] -= 1; _classPrivateFieldLooseBase(this, _queueNext)[_queueNext](); } }; } function _queueNext2() { // Do it soon but not immediately, this allows clearing out the entire queue synchronously // one by one without continuously _advancing_ it (and starting new tasks before immediately // aborting them) queueMicrotask(() => _classPrivateFieldLooseBase(this, _next)[_next]()); } function _next2() { if (_classPrivateFieldLooseBase(this, _activeRequests)[_activeRequests] >= this.limit) { return; } if (_classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].length === 0) { return; } // Dispatch the next request, and update the abort/done handlers // so that cancelling it does the Right Thing (and doesn't just try // to dequeue an already-running request). const next = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].shift(); const handler = _classPrivateFieldLooseBase(this, _call)[_call](next.fn); next.abort = handler.abort; next.done = handler.done; } function _queue2(fn, options = {}) { const handler = { fn, priority: options.priority || 0, abort: () => { _classPrivateFieldLooseBase(this, _dequeue)[_dequeue](handler); }, done: () => { throw new Error('Cannot mark a queued request as done: this indicates a bug'); } }; const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].findIndex(other => { return handler.priority > other.priority; }); if (index === -1) { _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].push(handler); } else { _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 0, handler); } return handler; } function _dequeue2(handler) { const index = _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].indexOf(handler); if (index !== -1) { _classPrivateFieldLooseBase(this, _queuedHandlers)[_queuedHandlers].splice(index, 1); } } module.exports = { RateLimitedQueue, internalRateLimitedQueue: Symbol('__queue') }; },{}],28:[function(require,module,exports){ "use strict"; var _apply; function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } var id = 0; function _classPrivateFieldLooseKey(name) { return "__private_" + id++ + "_" + name; } const has = require('./hasProperty'); function insertReplacement(source, rx, replacement) { const newParts = []; source.forEach(chunk => { // When the source contains multiple placeholders for interpolation, // we should ignore chunks that are not strings, because those // can be JSX objects and will be otherwise incorrectly turned into strings. // Without this condition we’d get this: [object Object] hello [object Object] my <button> if (typeof chunk !== 'string') { return newParts.push(chunk); } return rx[Symbol.split](chunk).forEach((raw, i, list) => { if (raw !== '') { newParts.push(raw); } // Interlace with the `replacement` value if (i < list.length - 1) { newParts.push(replacement); } }); }); return newParts; } /** * Takes a string with placeholder variables like `%{smart_count} file selected` * and replaces it with values from options `{smart_count: 5}` * * @license https://github.com/airbnb/polyglot.js/blob/master/LICENSE * taken from https://github.com/airbnb/polyglot.js/blob/master/lib/polyglot.js#L299 * * @param {string} phrase that needs interpolation, with placeholders * @param {object} options with values that will be used to replace placeholders * @returns {any[]} interpolated */ function interpolate(phrase, options) { const dollarRegex = /\$/g; const dollarBillsYall = '$$$$'; let interpolated = [phrase]; if (options == null) return interpolated; for (const arg of Object.keys(options)) { if (arg !== '_') { // Ensure replacement value is escaped to prevent special $-prefixed // regex replace tokens. the "$$$$" is needed because each "$" needs to // be escaped with "$" itself, and we need two in the resulting output. let replacement = options[arg]; if (typeof replacement === 'string') { replacement = dollarRegex[Symbol.replace](replacement, dollarBillsYall); } // We create a new `RegExp` each time instead of using a more-efficient // string replace so that the same argument can be replaced multiple times // in the same phrase. interpolated = insertReplacement(interpolated, new RegExp(`%\\{${arg}\\}`, 'g'), replacement); } } return interpolated; } /** * Translates strings with interpolation & pluralization support. * Extensible with custom dictionaries and pluralization functions. * * Borrows heavily from and inspired by Polyglot https://github.com/airbnb/polyglot.js, * basically a stripped-down version of it. Differences: pluralization functions are not hardcoded * and can be easily added among with dictionaries, nested objects are used for pluralization * as opposed to `||||` delimeter * * Usage example: `translator.translate('files_chosen', {smart_count: 3})` */ module.exports = (_apply = /*#__PURE__*/_classPrivateFieldLooseKey("apply"), class Translator { /** * @param {object|Array<object>} locales - locale or list of locales. */ constructor(locales) { Object.defineProperty(this, _apply, { value: _apply2 }); this.locale = { strings: {}, pluralize(n) { if (n === 1) { return 0; } return 1; } }; if (Array.isArray(locales)) { locales.forEach(_classPrivateFieldLooseBase(this, _apply)[_apply], this); } else { _classPrivateFieldLooseBase(this, _apply)[_apply](locales); } } /** * Public translate method * * @param {string} key * @param {object} options with values that will be used later to replace placeholders in string * @returns {string} translated (and interpolated) */ translate(key, options) { return this.translateArray(key, options).join(''); } /** * Get a translation and return the translated and interpolated parts as an array. * * @param {string} key * @param {object} options with values that will be used to replace placeholders * @returns {Array} The translated and interpolated parts, in order. */ translateArray(key, options) { if (!has(this.locale.strings, key)) { throw new Error(`missing string: ${key}`); } const string = this.locale.strings[key]; const hasPluralForms = typeof string === 'object'; if (hasPluralForms) { if (options && typeof options.smart_count !== 'undefined') { const plural = this.locale.pluralize(options.smart_count); return interpolate(string[plural], options); } throw new Error('Attempted to use a string with plural forms, but no value was given for %{smart_count}'); } return interpolate(string, options); } }); function _apply2(locale) { if (!(locale != null && locale.strings)) { return; } const prevLocale = this.locale; this.locale = { ...prevLocale, strings: { ...prevLocale.strings, ...locale.strings } }; this.locale.pluralize = locale.pluralize || prevLocale.pluralize; } },{"./hasProperty":43}],29:[function(require,module,exports){ "use strict"; const { createAbortError } = require('./AbortController'); /** * Return a Promise that resolves after `ms` milliseconds. * * @param {number} ms - Number of milliseconds to wait. * @param {{ signal?: AbortSignal }} [opts] - An abort signal that can be used to cancel the delay early. * @returns {Promise<void>} A Promise that resolves after the given amount of `ms`. */ module.exports = function delay(ms, opts) { return new Promise((resolve, reject) => { var _opts$signal, _opts$signal2; if (opts != null && (_opts$signal = opts.signal) != null && _opts$signal.aborted) { return reject(createAbortError()); } const timeout = setTimeout(() => { cleanup(); // eslint-disable-line no-use-before-define resolve(); }, ms); function onabort() { clearTimeout(timeout); cleanup(); // eslint-disable-line no-use-before-define reject(createAbortError()); } opts == null ? void 0 : (_opts$signal2 = opts.signal) == null ? void 0 : _opts$signal2.addEventListener('abort', onabort); function cleanup() { var _opts$signal3; opts == null ? void 0 : (_opts$signal3 = opts.signal) == null ? void 0 : _opts$signal3.removeEventListener('abort', onabort); } return undefined; }); }; },{"./AbortController":23}],30:[function(require,module,exports){ "use strict"; const throttle = require('lodash.throttle'); function emitSocketProgress(uploader, progressData, file) { const { progress, bytesUploaded, bytesTotal } = progressData; if (progress) { uploader.uppy.log(`Upload progress: ${progress}`); uploader.uppy.emit('upload-progress', file, { uploader, bytesUploaded, bytesTotal }); } } module.exports = throttle(emitSocketProgress, 300, { leading: true, trailing: true }); },{"lodash.throttle":50}],31:[function(require,module,exports){ "use strict"; const NetworkError = require('./NetworkError'); /** * Wrapper around window.fetch that throws a NetworkError when appropriate */ module.exports = function fetchWithNetworkError(...options) { return fetch(...options).catch(err => { if (err.name === 'AbortError') { throw err; } else { throw new NetworkError(err); } }); }; },{"./NetworkError":25}],32:[function(require,module,exports){ "use strict"; const isDOMElement = require('./isDOMElement'); /** * Find a DOM element. * * @param {Node|string} element * @returns {Node|null} */ module.exports = function findDOMElement(element, context = document) { if (typeof element === 'string') { return context.querySelector(element); } if (isDOMElement(element)) { return element; } return null; }; },{"./isDOMElement":44}],33:[function(require,module,exports){ "use strict"; function encodeCharacter(character) { return character.charCodeAt(0).toString(32); } function encodeFilename(name) { let suffix = ''; return name.replace(/[^A-Z0-9]/ig, character => { suffix += `-${encodeCharacter(character)}`; return '/'; }) + suffix; } /** * Takes a file object and turns it into fileID, by converting file.name to lowercase, * removing extra characters and adding type, size and lastModified * * @param {object} file * @returns {string} the fileID */ module.exports = function generateFileID(file) { // It's tempting to do `[items].filter(Boolean).join('-')` here, but that // is slower! simple string concatenation is fast let id = 'uppy'; if (typeof file.name === 'string') { id += `-${encodeFilename(file.name.toLowerCase())}`; } if (file.type !== undefined) { id += `-${file.type}`; } if (file.meta && typeof file.meta.relativePath === 'string') { id += `-${encodeFilename(file.meta.relativePath.toLowerCase())}`; } if (file.data.size !== undefined) { id += `-${file.data.size}`; } if (file.data.lastModified !== undefined) { id += `-${file.data.lastModified}`; } return id; }; },{}],34:[function(require,module,exports){ "use strict"; const webkitGetAsEntryApi = require('./utils/webkitGetAsEntryApi/index'); const fallbackApi = require('./utils/fallbackApi'); /** * Returns a promise that resolves to the array of dropped files (if a folder is * dropped, and browser supports folder parsing - promise resolves to the flat * array of all files in all directories). * Each file has .relativePath prop appended to it (e.g. "/docs/Prague/ticket_from_prague_to_ufa.pdf") * if browser supports it. Otherwise it's undefined. * * @param {DataTransfer} dataTransfer * @param {Function} logDropError - a function that's called every time some * folder or some file error out (e.g. because of the folder name being too long * on Windows). Notice that resulting promise will always be resolved anyway. * * @returns {Promise} - Array<File> */ module.exports = function getDroppedFiles(dataTransfer, { logDropError = () => {} } = {}) { var _dataTransfer$items; // Get all files from all subdirs. Works (at least) in Chrome, Mozilla, and Safari if ((_dataTransfer$items = dataTransfer.items) != null && _dataTransfer$items[0] && 'webkitGetAsEntry' in dataTransfer.items[0]) { return webkitGetAsEntryApi(dataTransfer, logDropError); // Otherwise just return all first-order files } return fallbackApi(dataTransfer); }; },{"./utils/fallbackApi":35,"./utils/webkitGetAsEntryApi/index":38}],35:[function(require,module,exports){ "use strict"; const toArray = require('../../toArray'); // .files fallback, should be implemented in any browser module.exports = function fallbackApi(dataTransfer) { const files = toArray(dataTransfer.files); return Promise.resolve(files); }; },{"../../toArray":48}],36:[function(require,module,exports){ "use strict"; /** * Recursive function, calls the original callback() when the directory is entirely parsed. * * @param {FileSystemDirectoryReader} directoryReader * @param {Array} oldEntries * @param {Function} logDropError * @param {Function} callback - called with ([ all files and directories in that directoryReader ]) */ module.exports = function getFilesAndDirectoriesFromDirectory(directoryReader, oldEntries, logDropError, { onSuccess }) { directoryReader.readEntries(entries => { const newEntries = [...oldEntries, ...entries]; // According to the FileSystem API spec, getFilesAndDirectoriesFromDirectory() // must be called until it calls the onSuccess with an empty array. if (entries.length) { setTimeout(() => { getFilesAndDirectoriesFromDirectory(directoryReader, newEntries, logDropError, { onSuccess }); }, 0); // Done iterating this particular directory } else { onSuccess(newEntries); } }, // Make sure we resolve on error anyway, it's fine if only one directory couldn't be parsed! error => { logDropError(error); onSuccess(oldEntries); }); }; },{}],37:[function(require,module,exports){ "use strict"; /** * Get the relative path from the FileEntry#fullPath, because File#webkitRelativePath is always '', at least onDrop. * * @param {FileEntry} fileEntry * * @returns {string|null} - if file is not in a folder - return null (this is to * be consistent with .relativePath-s of files selected from My Device). If file * is in a folder - return its fullPath, e.g. '/simpsons/hi.jpeg'. */ module.exports = function getRelativePath(fileEntry) { // fileEntry.fullPath - "/simpsons/hi.jpeg" or undefined (for browsers that don't support it) // fileEntry.name - "hi.jpeg" if (!fileEntry.fullPath || fileEntry.fullPath === `/${fileEntry.name}`) { return null; } return fileEntry.fullPath; }; },{}],38:[function(require,module,exports){ "use strict"; const toArray = require('../../../toArray'); const getRelativePath = require('./getRelativePath'); const getFilesAndDirectoriesFromDirectory = require('./getFilesAndDirectoriesFromDirectory'); module.exports = function webkitGetAsEntryApi(dataTransfer, logDropError) { const files = []; const rootPromises = []; /** * Returns a resolved promise, when :files array is enhanced * * @param {(FileSystemFileEntry|FileSystemDirectoryEntry)} entry * @returns {Promise} - empty promise that resolves when :files is enhanced with a file */ const createPromiseToAddFileOrParseDirectory = entry => new Promise(resolve => { // This is a base call if (entry.isFile) { // Creates a new File object which can be used to read the file. entry.file(file => { // eslint-disable-next-line no-param-reassign file.relativePath = getRelativePath(entry); files.push(file); resolve(); }, // Make sure we resolve on error anyway, it's fine if only one file couldn't be read! error => { logDropError(error); resolve(); }); // This is a recursive call } else if (entry.isDirectory) { const directoryReader = entry.createReader(); getFilesAndDirectoriesFromDirectory(directoryReader, [], logDropError, { onSuccess: entries => resolve(Promise.all(entries.map(createPromiseToAddFileOrParseDirectory))) }); } }); // For each dropped item, - make sure it's a file/directory, and start deepening in! toArray(dataTransfer.items).forEach(item => { const entry = item.webkitGetAsEntry(); // :entry can be null when we drop the url e.g. if (entry) { rootPromises.push(createPromiseToAddFileOrParseDirectory(entry)); } }); return Promise.all(rootPromises).then(() => files); }; },{"../../../toArray":48,"./getFilesAndDirectoriesFromDirectory":36,"./getRelativePath":37}],39:[function(require,module,exports){ "use strict"; /** * Takes a full filename string and returns an object {name, extension} * * @param {string} fullFileName * @returns {object} {name, extension} */ module.exports = function getFileNameAndExtension(fullFileName) { const lastDot = fullFileName.lastIndexOf('.'); // these count as no extension: "no-dot", "trailing-dot." if (lastDot === -1 || lastDot === fullFileName.length - 1) { return { name: fullFileName, extension: undefined }; } return { name: fullFileName.slice(0, lastDot), extension: fullFileName.slice(lastDot + 1) }; }; },{}],40:[function(require,module,exports){ "use strict"; const getFileNameAndExtension = require('./getFileNameAndExtension'); const mimeTypes = require('./mimeTypes'); module.exports = function getFileType(file) { var _getFileNameAndExtens; if (file.type) return file.type; const fileExtension = file.name ? (_getFileNameAndExtens = getFileNameAndExtension(file.name).extension) == null ? void 0 : _getFileNameAndExtens.toLowerCase() : null; if (fileExtension && fileExtension in mimeTypes) { // else, see if we can map extension to a mime type return mimeTypes[fileExtension]; } // if all fails, fall back to a generic byte stream type return 'application/octet-stream'; }; },{"./getFileNameAndExtension":39,"./mimeTypes":46}],41:[function(require,module,exports){ "use strict"; module.exports = function getSocketHost(url) { // get the host domain const regex = /^(?:https?:\/\/|\/\/)?(?:[^@\n]+@)?(?:www\.)?([^\n]+)/i; const host = regex.exec(url)[1]; const socketProtocol = /^http:\/\//i.test(url) ? 'ws' : 'wss'; return `${socketProtocol}://${host}`; }; },{}],42:[function(require,module,exports){ "use strict"; /** * Adds zero to strings shorter than two characters. * * @param {number} number * @returns {string} */ function pad(number) { return number < 10 ? `0${number}` : number.toString(); } /** * Returns a timestamp in the format of `hours:minutes:seconds` */ module.exports = function getTimeStamp() { const date = new Date(); const hours = pad(date.getHours()); const minutes = pad(date.getMinutes()); const seconds = pad(date.getSeconds()); return `${hours}:${minutes}:${seconds}`; }; },{}],43:[function(require,module,exports){ "use strict"; module.exports = function has(object, key) { return Object.prototype.hasOwnProperty.call(object, key); }; },{}],44:[function(require,module,exports){ "use strict"; /** * Check if an object is a DOM element. Duck-typing based on `nodeType`. * * @param {*} obj */ module.exports = function isDOMElement(obj) { return (obj == null ? void 0 : obj.nodeType) === Node.ELEMENT_NODE; }; },{}],45:[function(require,module,exports){ "use strict"; function isNetworkError(xhr) { if (!xhr) { return false; } return xhr.readyState !== 0 && xhr.readyState !== 4 || xhr.status === 0; } module.exports = isNetworkError; },{}],46:[function(require,module,exports){ "use strict"; // ___Why not add the mime-types package? // It's 19.7kB gzipped, and we only need mime types for well-known extensions (for file previews). // ___Where to take new extensions from? // https://github.com/jshttp/mime-db/blob/master/db.json module.exports = { md: 'text/markdown', markdown: 'text/markdown', mp4: 'video/mp4', mp3: 'audio/mp3', svg: 'image/svg+xml', jpg: 'image/jpeg', png: 'image/png', gif: 'image/gif', heic: 'image/heic', heif: 'image/heif', yaml: 'text/yaml', yml: 'text/yaml', csv: 'text/csv', tsv: 'text/tab-separated-values', tab: 'text/tab-separated-values', avi: 'video/x-msvideo', mks: 'video/x-matroska', mkv: 'video/x-matroska', mov: 'video/quicktime', doc: 'application/msword', docm: 'application/vnd.ms-word.document.macroenabled.12', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', dot: 'application/msword', dotm: 'application/vnd.ms-word.template.macroenabled.12', dotx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', xla: 'application/vnd.ms-excel', xlam: 'application/vnd.ms-excel.addin.macroenabled.12', xlc: 'application/vnd.ms-excel', xlf: 'application/x-xliff+xml', xlm: 'application/vnd.ms-excel', xls: 'application/vnd.ms-excel', xlsb: 'application/vnd.ms-excel.sheet.binary.macroenabled.12', xlsm: 'application/vnd.ms-excel.sheet.macroenabled.12', xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', xlt: 'application/vnd.ms-excel', xltm: 'application/vnd.ms-excel.template.macroenabled.12', xltx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', xlw: 'application/vnd.ms-excel', txt: 'text/plain', text: 'text/plain', conf: 'text/plain', log: 'text/plain', pdf: 'application/pdf', zip: 'application/zip', '7z': 'application/x-7z-compressed', rar: 'application/x-rar-compressed', tar: 'application/x-tar', gz: 'application/gzip', dmg: 'application/x-apple-diskimage' }; },{}],47:[function(require,module,exports){ "use strict"; module.exports = function settle(promises) { const resolutions = []; const rejections = []; function resolved(value) { resolutions.push(value); } function rejected(error) { rejections.push(error); } const wait = Promise.all(promises.map(promise => promise.then(resolved, rejected))); return wait.then(() => { return { successful: resolutions, failed: rejections }; }); }; },{}],48:[function(require,module,exports){ "use strict"; /** * Converts list into array */ module.exports = Array.from; },{}],49:[function(require,module,exports){ "use strict"; var _class, _temp; const BasePlugin = require('@uppy/core/lib/BasePlugin'); const { nanoid } = require('nanoid'); const { Provider, RequestClient, Socket } = require('@uppy/companion-client'); const emitSocketProgress = require('@uppy/utils/lib/emitSocketProgress'); const getSocketHost = require('@uppy/utils/lib/getSocketHost'); const settle = require('@uppy/utils/lib/settle'); const EventTracker = require('@uppy/utils/lib/EventTracker'); const ProgressTimeout = require('@uppy/utils/lib/ProgressTimeout'); const { RateLimitedQueue, internalRateLimitedQueue } = require('@uppy/utils/lib/RateLimitedQueue'); const NetworkError = require('@uppy/utils/lib/NetworkError'); const isNetworkError = require('@uppy/utils/lib/isNetworkError'); function buildResponseError(xhr, err) { let error = err; // 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 = Object.assign(new Error('Upload error'), { data: error }); } if (isNetworkError(xhr)) { error = new NetworkError(error, xhr); return error; } error.request = xhr; return error; } /** * Set `data.type` in the blob to `file.meta.type`, * because we might have detected a more accurate file type in Uppy * https://stackoverflow.com/a/50875615 * * @param {object} file File object with `data`, `size` and `meta` properties * @returns {object} blob updated with the new `type` set from `file.meta.type` */ function setTypeInBlob(file) { const dataWithUpdatedType = file.data.slice(0, file.data.size, file.meta.type); return dataWithUpdatedType; } module.exports = (_temp = _class = class XHRUpload extends BasePlugin { // eslint-disable-next-line global-require constructor(uppy, opts) { super(uppy, opts); this.type = 'uploader'; this.id = this.opts.id || 'XHRUpload'; this.title = 'XHRUpload'; this.defaultLocale = { strings: { timedOut: 'Upload stalled for %{seconds} seconds, aborting.' } }; // Default options const defaultOptions = { formData: true, fieldName: opts.bundle ? 'files[]' : 'file', method: 'post', metaFields: null, responseUrlFieldName: 'url', bundle: false, headers: {}, timeout: 30 * 1000, limit: 5, withCredentials: false, responseType: '', /** * @typedef respObj * @property {string} responseText * @property {number} status * @property {string} statusText * @property {object.<string, string>} headers * * @param {string} responseText the response body string * @param {XMLHttpRequest | respObj} response the response object (XHR or similar) */ getResponseData(responseText) { let parsedResponse = {}; try { parsedResponse = JSON.parse(responseText); } catch (err) { uppy.log(err); } return parsedResponse; }, /** * * @param {string} responseText the response body string * @param {XMLHttpRequest | respObj} response the response object (XHR or similar) */ getResponseError(_, response) { let error = new Error('Upload error'); if (isNetworkError(response)) { error = new NetworkError(error, response); } return error; }, /** * Check if the response from the upload endpoint indicates that the upload was successful. * * @param {number} status the response status code */ validateStatus(status) { return status >= 200 && status < 300; } }; this.opts = { ...defaultOptions, ...opts }; this.i18nInit(); this.handleUpload = this.handleUpload.bind(this); // Simultaneous upload limiting is shared across all uploads with this plugin. if (internalRateLimitedQueue in this.opts) { this.requests = this.opts[internalRateLimitedQueue]; } else { this.requests = new RateLimitedQueue(this.opts.limit); } if (this.opts.bundle && !this.opts.formData) { throw new Error('`opts.formData` must be true when `opts.bundle` is enabled.'); } this.uploaderEvents = Object.create(null); } getOptions(file) { const overrides = this.uppy.getState().xhrUpload; const { headers } = this.opts; const opts = { ...this.opts, ...(overrides || {}), ...(file.xhrUpload || {}), headers: {} }; // Support for `headers` as a function, only in the XHRUpload settings. // Options set by other plugins in Uppy state or on the files themselves are still merged in afterward. // // ```js // headers: (file) => ({ expires: file.meta.expires }) // ``` if (typeof headers === 'function') { opts.headers = headers(file); } else { Object.assign(opts.headers, this.opts.headers); } if (overrides) { Object.assign(opts.headers, overrides.headers); } if (file.xhrUpload) { Object.assign(opts.headers, file.xhrUpload.headers); } return opts; } // eslint-disable-next-line class-methods-use-this addMetadata(formData, meta, opts) { const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields : Object.keys(meta); // Send along all fields by default. metaFields.forEach(item => { formData.append(item, meta[item]); }); } createFormDataUpload(file, opts) { const formPost = new FormData(); this.addMetadata(formPost, file.meta, opts); const dataWithUpdatedType = setTypeInBlob(file); if (file.name) { formPost.append(opts.fieldName, dataWithUpdatedType, file.meta.name); } else { formPost.append(opts.fieldName, dataWithUpdatedType); } return formPost; } createBundledUpload(files, opts) { const formPost = new FormData(); const { meta } = this.uppy.getState(); this.addMetadata(formPost, meta, opts); files.forEach(file => { const options = this.getOptions(file); const dataWithUpdatedType = setTypeInBlob(file); if (file.name) { formPost.append(options.fieldName, dataWithUpdatedType, file.name); } else { formPost.append(options.fieldName, dataWithUpdatedType); } }); return formPost; } upload(file, current, total) { const opts = this.getOptions(file); this.uppy.log(`uploading ${current} of ${total}`); return new Promise((resolve, reject) => { this.uppy.emit('upload-started', file); const data = opts.formData ? this.createFormDataUpload(file, opts) : file.data; const xhr = new XMLHttpRequest(); this.uploaderEvents[file.id] = new EventTracker(this.uppy); const timer = new ProgressTimeout(opts.timeout, () => { xhr.abort(); queuedRequest.done(); const error = new Error(this.i18n('timedOut', { seconds: Math.ceil(opts.timeout / 1000) })); this.uppy.emit('upload-error', file, error); reject(error); }); const id = nanoid(); xhr.upload.addEventListener('loadstart', () => { this.uppy.log(`[XHRUpload] ${id} started`); }); xhr.upload.addEventListener('progress', ev => { this.uppy.log(`[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) { this.uppy.emit('upload-progress', file, { uploader: this, bytesUploaded: ev.loaded, bytesTotal: ev.total }); } }); xhr.addEventListener('load', ev => { this.uppy.log(`[XHRUpload] ${id} finished`); timer.done(); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } if (opts.validateStatus(ev.target.status, xhr.responseText, xhr)) { const body = opts.getResponseData(xhr.responseText, xhr); const uploadURL = body[opts.responseUrlFieldName]; const uploadResp = { status: ev.target.status, body, uploadURL }; this.uppy.emit('upload-success', file, uploadResp); if (uploadURL) { this.uppy.log(`Download ${file.name} from ${uploadURL}`); } return resolve(file); } const body = opts.getResponseData(xhr.responseText, xhr); const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); const response = { status: ev.target.status, body }; this.uppy.emit('upload-error', file, error, response); return reject(error); }); xhr.addEventListener('error', () => { this.uppy.log(`[XHRUpload] ${id} errored`); timer.done(); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } const error = buildResponseError(xhr, opts.getResponseError(xhr.responseText, xhr)); this.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; } const queuedRequest = this.requests.run(() => { this.uppy.emit('upload-started', file); // When using an authentication system like JWT, the bearer token goes as a header. This // header needs to be fresh each time the token is refreshed so computing and setting the // headers just before the upload starts enables this kind of authentication to work properly. // Otherwise, half-way through the list of uploads the token could be stale and the upload would fail. const currentOpts = this.getOptions(file); Object.keys(currentOpts.headers).forEach(header => { xhr.setRequestHeader(header, currentOpts.headers[header]); }); xhr.send(data); return () => { timer.done(); xhr.abort(); }; }); this.onFileRemove(file.id, () => { queuedRequest.abort(); reject(new Error('File removed')); }); this.onCancelAll(file.id, () => { queuedRequest.abort(); reject(new Error('Upload cancelled')); }); }); } uploadRemote(file) { const opts = this.getOptions(file); return new Promise((resolve, reject) => { const fields = {}; const metaFields = Array.isArray(opts.metaFields) ? opts.metaFields // Send along all fields by default. : Object.keys(file.meta); metaFields.forEach(name => { fields[name] = file.meta[name]; }); const Client = file.remote.providerOptions.provider ? Provider : RequestClient; const client = new Client(this.uppy, file.remote.providerOptions); client.post(file.remote.url, { ...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(res => { const { token } = res; const host = getSocketHost(file.remote.companionUrl); const socket = new Socket({ target: `${host}/api/${token}`, autoOpen: false }); this.uploaderEvents[file.id] = new EventTracker(this.uppy); this.onFileRemove(file.id, () => { socket.send('pause', {}); queuedRequest.abort(); resolve(`upload ${file.id} was removed`); }); this.onCancelAll(file.id, () => { socket.send('pause', {}); queuedRequest.abort(); resolve(`upload ${file.id} was canceled`); }); this.onRetry(file.id, () => { socket.send('pause', {}); socket.send('resume', {}); }); this.onRetryAll(file.id, () => { socket.send('pause', {}); socket.send('resume', {}); }); socket.on('progress', progressData => emitSocketProgress(this, progressData, file)); socket.on('success', data => { const body = opts.getResponseData(data.response.responseText, data.response); const uploadURL = body[opts.responseUrlFieldName]; const uploadResp = { status: data.response.status, body, uploadURL }; this.uppy.emit('upload-success', file, uploadResp); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } return resolve(); }); socket.on('error', errData => { const resp = errData.response; const error = resp ? opts.getResponseError(resp.responseText, resp) : Object.assign(new Error(errData.error.message), { cause: errData.error }); this.uppy.emit('upload-error', file, error); queuedRequest.done(); if (this.uploaderEvents[file.id]) { this.uploaderEvents[file.id].remove(); this.uploaderEvents[file.id] = null; } reject(error); }); const queuedRequest = this.requests.run(() => { socket.open(); if (file.isPaused) { socket.send('pause', {}); } return () => socket.close(); }); }).catch(err => { this.uppy.emit('upload-error', file, err); reject(err); }); }); } uploadBundle(files) { return new Promise((resolve, reject) => { const { endpoint } = this.opts; const { method } = this.opts; const optsFromState = this.uppy.getState().xhrUpload; const formData = this.createBundledUpload(files, { ...this.opts, ...(optsFromState || {}) }); const xhr = new XMLHttpRequest(); const timer = new ProgressTimeout(this.opts.timeout, () => { xhr.abort(); const error = new Error(this.i18n('timedOut', { seconds: Math.ceil(this.opts.timeout / 1000) })); emitError(error); reject(error); }); const emitError = error => { files.forEach(file => { this.uppy.emit('upload-error', file, error); }); }; xhr.upload.addEventListener('loadstart', () => { this.uppy.log('[XHRUpload] started uploading bundle'); timer.progress(); }); xhr.upload.addEventListener('progress', ev => { timer.progress(); if (!ev.lengthComputable) return; files.forEach(file => { this.uppy.emit('upload-progress', file, { uploader: this, bytesUploaded: ev.loaded / ev.total * file.size, bytesTotal: file.size }); }); }); xhr.addEventListener('load', ev => { timer.done(); if (this.opts.validateStatus(ev.target.status, xhr.responseText, xhr)) { const body = this.opts.getResponseData(xhr.responseText, xhr); const uploadResp = { status: ev.target.status, body }; files.forEach(file => { this.uppy.emit('upload-success', file, uploadResp); }); return resolve(); } const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error'); error.request = xhr; emitError(error); return reject(error); }); xhr.addEventListener('error', () => { timer.done(); const error = this.opts.getResponseError(xhr.responseText, xhr) || new Error('Upload error'); emitError(error); return reject(error); }); this.uppy.on('cancel-all', () => { timer.done(); xhr.abort(); }); xhr.open(method.toUpperCase(), endpoint, true); // IE10 does not allow setting `withCredentials` and `responseType` // before `open()` is called. xhr.withCredentials = this.opts.withCredentials; if (this.opts.responseType !== '') { xhr.responseType = this.opts.responseType; } Object.keys(this.opts.headers).forEach(header => { xhr.setRequestHeader(header, this.opts.headers[header]); }); xhr.send(formData); files.forEach(file => { this.uppy.emit('upload-started', file); }); }); } uploadFiles(files) { const promises = files.map((file, i) => { const current = parseInt(i, 10) + 1; const total = files.length; if (file.error) { return Promise.reject(new Error(file.error)); } if (file.isRemote) { return this.uploadRemote(file, current, total); } return this.upload(file, current, total); }); return settle(promises); } onFileRemove(fileID, cb) { this.uploaderEvents[fileID].on('file-removed', file => { if (fileID === file.id) cb(file.id); }); } onRetry(fileID, cb) { this.uploaderEvents[fileID].on('upload-retry', targetFileID => { if (fileID === targetFileID) { cb(); } }); } onRetryAll(fileID, cb) { this.uploaderEvents[fileID].on('retry-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } onCancelAll(fileID, cb) { this.uploaderEvents[fileID].on('cancel-all', () => { if (!this.uppy.getFile(fileID)) return; cb(); }); } handleUpload(fileIDs) { if (fileIDs.length === 0) { this.uppy.log('[XHRUpload] No files to upload!'); return Promise.resolve(); } // No limit configured by the user, and no RateLimitedQueue passed in by a "parent" plugin // (basically just AwsS3) using the internal symbol if (this.opts.limit === 0 && !this.opts[internalRateLimitedQueue]) { this.uppy.log('[XHRUpload] When uploading multiple files at once, consider setting the `limit` option (to `10` for example), to limit the number of concurrent uploads, which helps prevent memory and network issues: https://uppy.io/docs/xhr-upload/#limit-0', 'warning'); } this.uppy.log('[XHRUpload] Uploading...'); const files = fileIDs.map(fileID => this.uppy.getFile(fileID)); if (this.opts.bundle) { // if bundle: true, we don’t support remote uploads const isSomeFileRemote = files.some(file => file.isRemote); if (isSomeFileRemote) { throw new Error('Can’t upload remote files when the `bundle: true` option is set'); } if (typeof this.opts.headers === 'function') { throw new TypeError('`headers` may not be a function when the `bundle: true` option is set'); } return this.uploadBundle(files); } return this.uploadFiles(files).then(() => null); } install() { if (this.opts.bundle) { const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, individualCancellation: false } }); } this.uppy.addUploader(this.handleUpload); } uninstall() { if (this.opts.bundle) { const { capabilities } = this.uppy.getState(); this.uppy.setState({ capabilities: { ...capabilities, individualCancellation: true } }); } this.uppy.removeUploader(this.handleUpload); } }, _class.VERSION = "2.0.5", _temp); },{"@uppy/companion-client":12,"@uppy/core/lib/BasePlugin":14,"@uppy/utils/lib/EventTracker":24,"@uppy/utils/lib/NetworkError":25,"@uppy/utils/lib/ProgressTimeout":26,"@uppy/utils/lib/RateLimitedQueue":27,"@uppy/utils/lib/emitSocketProgress":30,"@uppy/utils/lib/getSocketHost":41,"@uppy/utils/lib/isNetworkError":45,"@uppy/utils/lib/settle":47,"nanoid":53}],50:[function(require,module,exports){ (function (global){(function (){ /** * lodash (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors <https://jquery.org/> * Released under MIT license <https://lodash.com/license> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as the `TypeError` message for "Functions" methods. */ var FUNC_ERROR_TEXT = 'Expected a function'; /** Used as references for various `Number` constants. */ var NAN = 0 / 0; /** `Object#toString` result references. */ var symbolTag = '[object Symbol]'; /** Used to match leading and trailing whitespace. */ var reTrim = /^\s+|\s+$/g; /** Used to detect bad signed hexadecimal string values. */ var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; /** Used to detect binary string values. */ var reIsBinary = /^0b[01]+$/i; /** Used to detect octal string values. */ var reIsOctal = /^0o[0-7]+$/i; /** Built-in method references without a dependency on `root`. */ var freeParseInt = parseInt; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof global == 'object' && global && global.Object === Object && global; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** Used for built-in method references. */ var objectProto = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString = objectProto.toString; /* Built-in method references for those with the same name as other `lodash` methods. */ var nativeMax = Math.max, nativeMin = Math.min; /** * Gets the timestamp of the number of milliseconds that have elapsed since * the Unix epoch (1 January 1970 00:00:00 UTC). * * @static * @memberOf _ * @since 2.4.0 * @category Date * @returns {number} Returns the timestamp. * @example * * _.defer(function(stamp) { * console.log(_.now() - stamp); * }, _.now()); * // => Logs the number of milliseconds it took for the deferred invocation. */ var now = function() { return root.Date.now(); }; /** * Creates a debounced function that delays invoking `func` until after `wait` * milliseconds have elapsed since the last time the debounced function was * invoked. The debounced function comes with a `cancel` method to cancel * delayed `func` invocations and a `flush` method to immediately invoke them. * Provide `options` to indicate whether `func` should be invoked on the * leading and/or trailing edge of the `wait` timeout. The `func` is invoked * with the last arguments provided to the debounced function. Subsequent * calls to the debounced function return the result of the last `func` * invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the debounced function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.debounce` and `_.throttle`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to debounce. * @param {number} [wait=0] The number of milliseconds to delay. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=false] * Specify invoking on the leading edge of the timeout. * @param {number} [options.maxWait] * The maximum time `func` is allowed to be delayed before it's invoked. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new debounced function. * @example * * // Avoid costly calculations while the window size is in flux. * jQuery(window).on('resize', _.debounce(calculateLayout, 150)); * * // Invoke `sendMail` when clicked, debouncing subsequent calls. * jQuery(element).on('click', _.debounce(sendMail, 300, { * 'leading': true, * 'trailing': false * })); * * // Ensure `batchLog` is invoked once after 1 second of debounced calls. * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 }); * var source = new EventSource('/stream'); * jQuery(source).on('message', debounced); * * // Cancel the trailing debounced invocation. * jQuery(window).on('popstate', debounced.cancel); */ function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { // Reset any `maxWait` timer. lastInvokeTime = time; // Start the timer for the trailing edge. timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, result = wait - timeSinceLastCall; return maxing ? nativeMin(result, maxWait - timeSinceLastInvoke) : result; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } // Restart the timer. timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; } function flush() { return timerId === undefined ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === undefined) { return leadingEdge(lastCallTime); } if (maxing) { // Handle invocations in a tight loop. timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } /** * Creates a throttled function that only invokes `func` at most once per * every `wait` milliseconds. The throttled function comes with a `cancel` * method to cancel delayed `func` invocations and a `flush` method to * immediately invoke them. Provide `options` to indicate whether `func` * should be invoked on the leading and/or trailing edge of the `wait` * timeout. The `func` is invoked with the last arguments provided to the * throttled function. Subsequent calls to the throttled function return the * result of the last `func` invocation. * * **Note:** If `leading` and `trailing` options are `true`, `func` is * invoked on the trailing edge of the timeout only if the throttled function * is invoked more than once during the `wait` timeout. * * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred * until to the next tick, similar to `setTimeout` with a timeout of `0`. * * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) * for details over the differences between `_.throttle` and `_.debounce`. * * @static * @memberOf _ * @since 0.1.0 * @category Function * @param {Function} func The function to throttle. * @param {number} [wait=0] The number of milliseconds to throttle invocations to. * @param {Object} [options={}] The options object. * @param {boolean} [options.leading=true] * Specify invoking on the leading edge of the timeout. * @param {boolean} [options.trailing=true] * Specify invoking on the trailing edge of the timeout. * @returns {Function} Returns the new throttled function. * @example * * // Avoid excessively updating the position while scrolling. * jQuery(window).on('scroll', _.throttle(updatePosition, 100)); * * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. * var throttled = _.throttle(renewToken, 300000, { 'trailing': false }); * jQuery(element).on('click', throttled); * * // Cancel the trailing throttled invocation. * jQuery(window).on('popstate', throttled.cancel); */ function throttle(func, wait, options) { var leading = true, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } if (isObject(options)) { leading = 'leading' in options ? !!options.leading : leading; trailing = 'trailing' in options ? !!options.trailing : trailing; } return debounce(func, wait, { 'leading': leading, 'maxWait': wait, 'trailing': trailing }); } /** * Checks if `value` is the * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an object, else `false`. * @example * * _.isObject({}); * // => true * * _.isObject([1, 2, 3]); * // => true * * _.isObject(_.noop); * // => true * * _.isObject(null); * // => false */ function isObject(value) { var type = typeof value; return !!value && (type == 'object' || type == 'function'); } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return !!value && typeof value == 'object'; } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol(value) { return typeof value == 'symbol' || (isObjectLike(value) && objectToString.call(value) == symbolTag); } /** * Converts `value` to a number. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {number} Returns the number. * @example * * _.toNumber(3.2); * // => 3.2 * * _.toNumber(Number.MIN_VALUE); * // => 5e-324 * * _.toNumber(Infinity); * // => Infinity * * _.toNumber('3.2'); * // => 3.2 */ function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value); } module.exports = throttle; }).call(this)}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) },{}],51:[function(require,module,exports){ var wildcard = require('wildcard'); var reMimePartSplit = /[\/\+\.]/; /** # mime-match A simple function to checker whether a target mime type matches a mime-type pattern (e.g. image/jpeg matches image/jpeg OR image/*). ## Example Usage <<< example.js **/ module.exports = function(target, pattern) { function test(pattern) { var result = wildcard(pattern, target, reMimePartSplit); // ensure that we have a valid mime type (should have two parts) return result && result.length >= 2; } return pattern ? test(pattern.split(';')[0]) : test; }; },{"wildcard":57}],52:[function(require,module,exports){ /** * Create an event emitter with namespaces * @name createNamespaceEmitter * @example * var emitter = require('./index')() * * emitter.on('*', function () { * console.log('all events emitted', this.event) * }) * * emitter.on('example', function () { * console.log('example event emitted') * }) */ module.exports = function createNamespaceEmitter () { var emitter = {} var _fns = emitter._fns = {} /** * Emit an event. Optionally namespace the event. Handlers are fired in the order in which they were added with exact matches taking precedence. Separate the namespace and event with a `:` * @name emit * @param {String} event – the name of the event, with optional namespace * @param {...*} data – up to 6 arguments that are passed to the event listener * @example * emitter.emit('example') * emitter.emit('demo:test') * emitter.emit('data', { example: true}, 'a string', 1) */ emitter.emit = function emit (event, arg1, arg2, arg3, arg4, arg5, arg6) { var toEmit = getListeners(event) if (toEmit.length) { emitAll(event, toEmit, [arg1, arg2, arg3, arg4, arg5, arg6]) } } /** * Create en event listener. * @name on * @param {String} event * @param {Function} fn * @example * emitter.on('example', function () {}) * emitter.on('demo', function () {}) */ emitter.on = function on (event, fn) { if (!_fns[event]) { _fns[event] = [] } _fns[event].push(fn) } /** * Create en event listener that fires once. * @name once * @param {String} event * @param {Function} fn * @example * emitter.once('example', function () {}) * emitter.once('demo', function () {}) */ emitter.once = function once (event, fn) { function one () { fn.apply(this, arguments) emitter.off(event, one) } this.on(event, one) } /** * Stop listening to an event. Stop all listeners on an event by only passing the event name. Stop a single listener by passing that event handler as a callback. * You must be explicit about what will be unsubscribed: `emitter.off('demo')` will unsubscribe an `emitter.on('demo')` listener, * `emitter.off('demo:example')` will unsubscribe an `emitter.on('demo:example')` listener * @name off * @param {String} event * @param {Function} [fn] – the specific handler * @example * emitter.off('example') * emitter.off('demo', function () {}) */ emitter.off = function off (event, fn) { var keep = [] if (event && fn) { var fns = this._fns[event] var i = 0 var l = fns ? fns.length : 0 for (i; i < l; i++) { if (fns[i] !== fn) { keep.push(fns[i]) } } } keep.length ? this._fns[event] = keep : delete this._fns[event] } function getListeners (e) { var out = _fns[e] ? _fns[e] : [] var idx = e.indexOf(':') var args = (idx === -1) ? [e] : [e.substring(0, idx), e.substring(idx + 1)] var keys = Object.keys(_fns) var i = 0 var l = keys.length for (i; i < l; i++) { var key = keys[i] if (key === '*') { out = out.concat(_fns[key]) } if (args.length === 2 && args[0] === key) { out = out.concat(_fns[key]) break } } return out } function emitAll (e, fns, args) { var i = 0 var l = fns.length for (i; i < l; i++) { if (!fns[i]) break fns[i].event = e fns[i].apply(fns[i], args) } } return emitter } },{}],53:[function(require,module,exports){ (function (process){(function (){ // This file replaces `index.js` in bundlers like webpack or Rollup, // according to `browser` config in `package.json`. let { urlAlphabet } = require('./url-alphabet/index.cjs') if (process.env.NODE_ENV !== 'production') { // All bundlers will remove this block in the production bundle. if ( typeof navigator !== 'undefined' && navigator.product === 'ReactNative' && typeof crypto === 'undefined' ) { throw new Error( 'React Native does not have a built-in secure random generator. ' + 'If you don’t need unpredictable IDs use `nanoid/non-secure`. ' + 'For secure IDs, import `react-native-get-random-values` ' + 'before Nano ID.' ) } if (typeof msCrypto !== 'undefined' && typeof crypto === 'undefined') { throw new Error( 'Import file with `if (!window.crypto) window.crypto = window.msCrypto`' + ' before importing Nano ID to fix IE 11 support' ) } if (typeof crypto === 'undefined') { throw new Error( 'Your browser does not have secure random generator. ' + 'If you don’t need unpredictable IDs, you can use nanoid/non-secure.' ) } } let random = bytes => crypto.getRandomValues(new Uint8Array(bytes)) let customRandom = (alphabet, size, getRandom) => { // First, a bitmask is necessary to generate the ID. The bitmask makes bytes // values closer to the alphabet size. The bitmask calculates the closest // `2^31 - 1` number, which exceeds the alphabet size. // For example, the bitmask for the alphabet size 30 is 31 (00011111). // `Math.clz32` is not used, because it is not available in browsers. let mask = (2 << (Math.log(alphabet.length - 1) / Math.LN2)) - 1 // Though, the bitmask solution is not perfect since the bytes exceeding // the alphabet size are refused. Therefore, to reliably generate the ID, // the random bytes redundancy has to be satisfied. // Note: every hardware random generator call is performance expensive, // because the system call for entropy collection takes a lot of time. // So, to avoid additional system calls, extra bytes are requested in advance. // Next, a step determines how many random bytes to generate. // The number of random bytes gets decided upon the ID size, mask, // alphabet size, and magic number 1.6 (using 1.6 peaks at performance // according to benchmarks). // `-~f => Math.ceil(f)` if f is a float // `-~i => i + 1` if i is an integer let step = -~((1.6 * mask * size) / alphabet.length) return () => { let id = '' while (true) { let bytes = getRandom(step) // A compact alternative for `for (var i = 0; i < step; i++)`. let j = step while (j--) { // Adding `|| ''` refuses a random byte that exceeds the alphabet size. id += alphabet[bytes[j] & mask] || '' if (id.length === size) return id } } } } let customAlphabet = (alphabet, size) => customRandom(alphabet, size, random) let nanoid = (size = 21) => { let id = '' let bytes = crypto.getRandomValues(new Uint8Array(size)) // A compact alternative for `for (var i = 0; i < step; i++)`. while (size--) { // It is incorrect to use bytes exceeding the alphabet size. // The following mask reduces the random byte in the 0-255 value // range to the 0-63 value range. Therefore, adding hacks, such // as empty string fallback or magic numbers, is unneccessary because // the bitmask trims bytes down to the alphabet size. let byte = bytes[size] & 63 if (byte < 36) { // `0-9a-z` id += byte.toString(36) } else if (byte < 62) { // `A-Z` id += (byte - 26).toString(36).toUpperCase() } else if (byte < 63) { id += '_' } else { id += '-' } } return id } module.exports = { nanoid, customAlphabet, customRandom, urlAlphabet, random } }).call(this)}).call(this,require('_process')) },{"./url-alphabet/index.cjs":54,"_process":56}],54:[function(require,module,exports){ // This alphabet uses `A-Za-z0-9_-` symbols. The genetic algorithm helped // optimize the gzip compression for this alphabet. let urlAlphabet = 'ModuleSymbhasOwnPr-0123456789ABCDEFGHNRVfgctiUvz_KqYTJkLxpZXIjQW' module.exports = { urlAlphabet } },{}],55:[function(require,module,exports){ var n,l,u,t,i,o,r,f,e={},c=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function a(n,l){for(var u in l)n[u]=l[u];return n}function p(n){var l=n.parentNode;l&&l.removeChild(n)}function v(l,u,t){var i,o,r,f={};for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];if(arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),"function"==typeof l&&null!=l.defaultProps)for(r in l.defaultProps)void 0===f[r]&&(f[r]=l.defaultProps[r]);return h(l,f,i,o,null)}function h(n,t,i,o,r){var f={type:n,props:t,key:i,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==r?++u:r};return null!=l.vnode&&l.vnode(f),f}function y(n){return n.children}function d(n,l){this.props=n,this.context=l}function _(n,l){if(null==l)return n.__?_(n.__,n.__.__k.indexOf(n)+1):null;for(var u;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e)return u.__e;return"function"==typeof n.type?_(n):null}function k(n){var l,u;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,l=0;l<n.__k.length;l++)if(null!=(u=n.__k[l])&&null!=u.__e){n.__e=n.__c.base=u.__e;break}return k(n)}}function x(n){(!n.__d&&(n.__d=!0)&&i.push(n)&&!b.__r++||r!==l.debounceRendering)&&((r=l.debounceRendering)||o)(b)}function b(){for(var n;b.__r=i.length;)n=i.sort(function(n,l){return n.__v.__b-l.__v.__b}),i=[],n.some(function(n){var l,u,t,i,o,r;n.__d&&(o=(i=(l=n).__v).__e,(r=l.__P)&&(u=[],(t=a({},i)).__v=i.__v+1,I(r,i,t,l.__n,void 0!==r.ownerSVGElement,null!=i.__h?[o]:null,u,null==o?_(i):o,i.__h),T(u,i),i.__e!=o&&k(i)))})}function m(n,l,u,t,i,o,r,f,s,a){var p,v,d,k,x,b,m,A=t&&t.__k||c,P=A.length;for(u.__k=[],p=0;p<l.length;p++)if(null!=(k=u.__k[p]=null==(k=l[p])||"boolean"==typeof k?null:"string"==typeof k||"number"==typeof k||"bigint"==typeof k?h(null,k,null,null,k):Array.isArray(k)?h(y,{children:k},null,null,null):k.__b>0?h(k.type,k.props,k.key,null,k.__v):k)){if(k.__=u,k.__b=u.__b+1,null===(d=A[p])||d&&k.key==d.key&&k.type===d.type)A[p]=void 0;else for(v=0;v<P;v++){if((d=A[v])&&k.key==d.key&&k.type===d.type){A[v]=void 0;break}d=null}I(n,k,d=d||e,i,o,r,f,s,a),x=k.__e,(v=k.ref)&&d.ref!=v&&(m||(m=[]),d.ref&&m.push(d.ref,null,k),m.push(v,k.__c||x,k)),null!=x?(null==b&&(b=x),"function"==typeof k.type&&null!=k.__k&&k.__k===d.__k?k.__d=s=g(k,s,n):s=w(n,k,d,A,x,s),a||"option"!==u.type?"function"==typeof u.type&&(u.__d=s):n.value=""):s&&d.__e==s&&s.parentNode!=n&&(s=_(d))}for(u.__e=b,p=P;p--;)null!=A[p]&&("function"==typeof u.type&&null!=A[p].__e&&A[p].__e==u.__d&&(u.__d=_(t,p+1)),L(A[p],A[p]));if(m)for(p=0;p<m.length;p++)z(m[p],m[++p],m[++p])}function g(n,l,u){var t,i;for(t=0;t<n.__k.length;t++)(i=n.__k[t])&&(i.__=n,l="function"==typeof i.type?g(i,l,u):w(u,i,i,n.__k,i.__e,l));return l}function w(n,l,u,t,i,o){var r,f,e;if(void 0!==l.__d)r=l.__d,l.__d=void 0;else if(null==u||i!=o||null==i.parentNode)n:if(null==o||o.parentNode!==n)n.appendChild(i),r=null;else{for(f=o,e=0;(f=f.nextSibling)&&e<t.length;e+=2)if(f==i)break n;n.insertBefore(i,o),r=o}return void 0!==r?r:i.nextSibling}function A(n,l,u,t,i){var o;for(o in u)"children"===o||"key"===o||o in l||C(n,o,null,u[o],t);for(o in l)i&&"function"!=typeof l[o]||"children"===o||"key"===o||"value"===o||"checked"===o||u[o]===l[o]||C(n,o,l[o],u[o],t)}function P(n,l,u){"-"===l[0]?n.setProperty(l,u):n[l]=null==u?"":"number"!=typeof u||s.test(l)?u:u+"px"}function C(n,l,u,t,i){var o;n:if("style"===l)if("string"==typeof u)n.style.cssText=u;else{if("string"==typeof t&&(n.style.cssText=t=""),t)for(l in t)u&&l in u||P(n.style,l,"");if(u)for(l in u)t&&u[l]===t[l]||P(n.style,l,u[l])}else if("o"===l[0]&&"n"===l[1])o=l!==(l=l.replace(/Capture$/,"")),l=l.toLowerCase()in n?l.toLowerCase().slice(2):l.slice(2),n.l||(n.l={}),n.l[l+o]=u,u?t||n.addEventListener(l,o?H:$,o):n.removeEventListener(l,o?H:$,o);else if("dangerouslySetInnerHTML"!==l){if(i)l=l.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==l&&"list"!==l&&"form"!==l&&"tabIndex"!==l&&"download"!==l&&l in n)try{n[l]=null==u?"":u;break n}catch(n){}"function"==typeof u||(null!=u&&(!1!==u||"a"===l[0]&&"r"===l[1])?n.setAttribute(l,u):n.removeAttribute(l))}}function $(n){this.l[n.type+!1](l.event?l.event(n):n)}function H(n){this.l[n.type+!0](l.event?l.event(n):n)}function I(n,u,t,i,o,r,f,e,c){var s,p,v,h,_,k,x,b,g,w,A,P=u.type;if(void 0!==u.constructor)return null;null!=t.__h&&(c=t.__h,e=u.__e=t.__e,u.__h=null,r=[e]),(s=l.__b)&&s(u);try{n:if("function"==typeof P){if(b=u.props,g=(s=P.contextType)&&i[s.__c],w=s?g?g.props.value:s.__:i,t.__c?x=(p=u.__c=t.__c).__=p.__E:("prototype"in P&&P.prototype.render?u.__c=p=new P(b,w):(u.__c=p=new d(b,w),p.constructor=P,p.render=M),g&&g.sub(p),p.props=b,p.state||(p.state={}),p.context=w,p.__n=i,v=p.__d=!0,p.__h=[]),null==p.__s&&(p.__s=p.state),null!=P.getDerivedStateFromProps&&(p.__s==p.state&&(p.__s=a({},p.__s)),a(p.__s,P.getDerivedStateFromProps(b,p.__s))),h=p.props,_=p.state,v)null==P.getDerivedStateFromProps&&null!=p.componentWillMount&&p.componentWillMount(),null!=p.componentDidMount&&p.__h.push(p.componentDidMount);else{if(null==P.getDerivedStateFromProps&&b!==h&&null!=p.componentWillReceiveProps&&p.componentWillReceiveProps(b,w),!p.__e&&null!=p.shouldComponentUpdate&&!1===p.shouldComponentUpdate(b,p.__s,w)||u.__v===t.__v){p.props=b,p.state=p.__s,u.__v!==t.__v&&(p.__d=!1),p.__v=u,u.__e=t.__e,u.__k=t.__k,u.__k.forEach(function(n){n&&(n.__=u)}),p.__h.length&&f.push(p);break n}null!=p.componentWillUpdate&&p.componentWillUpdate(b,p.__s,w),null!=p.componentDidUpdate&&p.__h.push(function(){p.componentDidUpdate(h,_,k)})}p.context=w,p.props=b,p.state=p.__s,(s=l.__r)&&s(u),p.__d=!1,p.__v=u,p.__P=n,s=p.render(p.props,p.state,p.context),p.state=p.__s,null!=p.getChildContext&&(i=a(a({},i),p.getChildContext())),v||null==p.getSnapshotBeforeUpdate||(k=p.getSnapshotBeforeUpdate(h,_)),A=null!=s&&s.type===y&&null==s.key?s.props.children:s,m(n,Array.isArray(A)?A:[A],u,t,i,o,r,f,e,c),p.base=u.__e,u.__h=null,p.__h.length&&f.push(p),x&&(p.__E=p.__=null),p.__e=!1}else null==r&&u.__v===t.__v?(u.__k=t.__k,u.__e=t.__e):u.__e=j(t.__e,u,t,i,o,r,f,c);(s=l.diffed)&&s(u)}catch(n){u.__v=null,(c||null!=r)&&(u.__e=e,u.__h=!!c,r[r.indexOf(e)]=null),l.__e(n,u,t)}}function T(n,u){l.__c&&l.__c(u,n),n.some(function(u){try{n=u.__h,u.__h=[],n.some(function(n){n.call(u)})}catch(n){l.__e(n,u.__v)}})}function j(l,u,t,i,o,r,f,c){var s,a,v,h=t.props,y=u.props,d=u.type,k=0;if("svg"===d&&(o=!0),null!=r)for(;k<r.length;k++)if((s=r[k])&&(s===l||(d?s.localName==d:3==s.nodeType))){l=s,r[k]=null;break}if(null==l){if(null===d)return document.createTextNode(y);l=o?document.createElementNS("http://www.w3.org/2000/svg",d):document.createElement(d,y.is&&y),r=null,c=!1}if(null===d)h===y||c&&l.data===y||(l.data=y);else{if(r=r&&n.call(l.childNodes),a=(h=t.props||e).dangerouslySetInnerHTML,v=y.dangerouslySetInnerHTML,!c){if(null!=r)for(h={},k=0;k<l.attributes.length;k++)h[l.attributes[k].name]=l.attributes[k].value;(v||a)&&(v&&(a&&v.__html==a.__html||v.__html===l.innerHTML)||(l.innerHTML=v&&v.__html||""))}if(A(l,y,h,o,c),v)u.__k=[];else if(k=u.props.children,m(l,Array.isArray(k)?k:[k],u,t,i,o&&"foreignObject"!==d,r,f,r?r[0]:t.__k&&_(t,0),c),null!=r)for(k=r.length;k--;)null!=r[k]&&p(r[k]);c||("value"in y&&void 0!==(k=y.value)&&(k!==l.value||"progress"===d&&!k)&&C(l,"value",k,h.value,!1),"checked"in y&&void 0!==(k=y.checked)&&k!==l.checked&&C(l,"checked",k,h.checked,!1))}return l}function z(n,u,t){try{"function"==typeof n?n(u):n.current=u}catch(n){l.__e(n,t)}}function L(n,u,t){var i,o;if(l.unmount&&l.unmount(n),(i=n.ref)&&(i.current&&i.current!==n.__e||z(i,null,u)),null!=(i=n.__c)){if(i.componentWillUnmount)try{i.componentWillUnmount()}catch(n){l.__e(n,u)}i.base=i.__P=null}if(i=n.__k)for(o=0;o<i.length;o++)i[o]&&L(i[o],u,"function"!=typeof n.type);t||null==n.__e||p(n.__e),n.__e=n.__d=void 0}function M(n,l,u){return this.constructor(n,u)}function N(u,t,i){var o,r,f;l.__&&l.__(u,t),r=(o="function"==typeof i)?null:i&&i.__k||t.__k,f=[],I(t,u=(!o&&i||t).__k=v(y,null,[u]),r||e,e,void 0!==t.ownerSVGElement,!o&&i?[i]:r?null:t.firstChild?n.call(t.childNodes):null,f,!o&&i?i:r?r.__e:t.firstChild,o),T(f,u)}n=c.slice,l={__e:function(n,l){for(var u,t,i;l=l.__;)if((u=l.__c)&&!u.__)try{if((t=u.constructor)&&null!=t.getDerivedStateFromError&&(u.setState(t.getDerivedStateFromError(n)),i=u.__d),null!=u.componentDidCatch&&(u.componentDidCatch(n),i=u.__d),i)return u.__E=u}catch(l){n=l}throw n}},u=0,t=function(n){return null!=n&&void 0===n.constructor},d.prototype.setState=function(n,l){var u;u=null!=this.__s&&this.__s!==this.state?this.__s:this.__s=a({},this.state),"function"==typeof n&&(n=n(a({},u),this.props)),n&&a(u,n),null!=n&&this.__v&&(l&&this.__h.push(l),x(this))},d.prototype.forceUpdate=function(n){this.__v&&(this.__e=!0,n&&this.__h.push(n),x(this))},d.prototype.render=y,i=[],o="function"==typeof Promise?Promise.prototype.then.bind(Promise.resolve()):setTimeout,b.__r=0,f=0,exports.render=N,exports.hydrate=function n(l,u){N(l,u,n)},exports.createElement=v,exports.h=v,exports.Fragment=y,exports.createRef=function(){return{current:null}},exports.isValidElement=t,exports.Component=d,exports.cloneElement=function(l,u,t){var i,o,r,f=a({},l.props);for(r in u)"key"==r?i=u[r]:"ref"==r?o=u[r]:f[r]=u[r];return arguments.length>2&&(f.children=arguments.length>3?n.call(arguments,2):t),h(l.type,f,i||l.key,o||l.ref,null)},exports.createContext=function(n,l){var u={__c:l="__cC"+f++,__:n,Consumer:function(n,l){return n.children(l)},Provider:function(n){var u,t;return this.getChildContext||(u=[],(t={})[l]=this,this.getChildContext=function(){return t},this.shouldComponentUpdate=function(n){this.props.value!==n.value&&u.some(x)},this.sub=function(n){u.push(n);var l=n.componentWillUnmount;n.componentWillUnmount=function(){u.splice(u.indexOf(n),1),l&&l.call(n)}}),n.children}};return u.Provider.__=u.Consumer.contextType=u},exports.toChildArray=function n(l,u){return u=u||[],null==l||"boolean"==typeof l||(Array.isArray(l)?l.some(function(l){n(l,u)}):u.push(l)),u},exports.options=l; },{}],56:[function(require,module,exports){ // shim for using process in browser var process = module.exports = {}; // cached from whatever global is present so that test runners that stub it // don't break things. But we need to wrap it in a try catch in case it is // wrapped in strict mode code which doesn't define any globals. It's inside a // function because try/catches deoptimize in certain engines. var cachedSetTimeout; var cachedClearTimeout; function defaultSetTimout() { throw new Error('setTimeout has not been defined'); } function defaultClearTimeout () { throw new Error('clearTimeout has not been defined'); } (function () { try { if (typeof setTimeout === 'function') { cachedSetTimeout = setTimeout; } else { cachedSetTimeout = defaultSetTimout; } } catch (e) { cachedSetTimeout = defaultSetTimout; } try { if (typeof clearTimeout === 'function') { cachedClearTimeout = clearTimeout; } else { cachedClearTimeout = defaultClearTimeout; } } catch (e) { cachedClearTimeout = defaultClearTimeout; } } ()) function runTimeout(fun) { if (cachedSetTimeout === setTimeout) { //normal enviroments in sane situations return setTimeout(fun, 0); } // if setTimeout wasn't available but was latter defined if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { cachedSetTimeout = setTimeout; return setTimeout(fun, 0); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedSetTimeout(fun, 0); } catch(e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedSetTimeout.call(null, fun, 0); } catch(e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error return cachedSetTimeout.call(this, fun, 0); } } } function runClearTimeout(marker) { if (cachedClearTimeout === clearTimeout) { //normal enviroments in sane situations return clearTimeout(marker); } // if clearTimeout wasn't available but was latter defined if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { cachedClearTimeout = clearTimeout; return clearTimeout(marker); } try { // when when somebody has screwed with setTimeout but no I.E. maddness return cachedClearTimeout(marker); } catch (e){ try { // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally return cachedClearTimeout.call(null, marker); } catch (e){ // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. // Some versions of I.E. have different rules for clearTimeout vs setTimeout return cachedClearTimeout.call(this, marker); } } } var queue = []; var draining = false; var currentQueue; var queueIndex = -1; function cleanUpNextTick() { if (!draining || !currentQueue) { return; } draining = false; if (currentQueue.length) { queue = currentQueue.concat(queue); } else { queueIndex = -1; } if (queue.length) { drainQueue(); } } function drainQueue() { if (draining) { return; } var timeout = runTimeout(cleanUpNextTick); draining = true; var len = queue.length; while(len) { currentQueue = queue; queue = []; while (++queueIndex < len) { if (currentQueue) { currentQueue[queueIndex].run(); } } queueIndex = -1; len = queue.length; } currentQueue = null; draining = false; runClearTimeout(timeout); } process.nextTick = function (fun) { var args = new Array(arguments.length - 1); if (arguments.length > 1) { for (var i = 1; i < arguments.length; i++) { args[i - 1] = arguments[i]; } } queue.push(new Item(fun, args)); if (queue.length === 1 && !draining) { runTimeout(drainQueue); } }; // v8 likes predictible objects function Item(fun, array) { this.fun = fun; this.array = array; } Item.prototype.run = function () { this.fun.apply(null, this.array); }; process.title = 'browser'; process.browser = true; process.env = {}; process.argv = []; process.version = ''; // empty string to avoid regexp issues process.versions = {}; function noop() {} process.on = noop; process.addListener = noop; process.once = noop; process.off = noop; process.removeListener = noop; process.removeAllListeners = noop; process.emit = noop; process.prependListener = noop; process.prependOnceListener = noop; process.listeners = function (name) { return [] } process.binding = function (name) { throw new Error('process.binding is not supported'); }; process.cwd = function () { return '/' }; process.chdir = function (dir) { throw new Error('process.chdir is not supported'); }; process.umask = function() { return 0; }; },{}],57:[function(require,module,exports){ /* jshint node: true */ 'use strict'; /** # wildcard Very simple wildcard matching, which is designed to provide the same functionality that is found in the [eve](https://github.com/adobe-webplatform/eve) eventing library. ## Usage It works with strings: <<< examples/strings.js Arrays: <<< examples/arrays.js Objects (matching against keys): <<< examples/objects.js While the library works in Node, if you are are looking for file-based wildcard matching then you should have a look at: <https://github.com/isaacs/node-glob> **/ function WildcardMatcher(text, separator) { this.text = text = text || ''; this.hasWild = ~text.indexOf('*'); this.separator = separator; this.parts = text.split(separator); } WildcardMatcher.prototype.match = function(input) { var matches = true; var parts = this.parts; var ii; var partsCount = parts.length; var testParts; if (typeof input == 'string' || input instanceof String) { if (!this.hasWild && this.text != input) { matches = false; } else { testParts = (input || '').split(this.separator); for (ii = 0; matches && ii < partsCount; ii++) { if (parts[ii] === '*') { continue; } else if (ii < testParts.length) { matches = parts[ii] === testParts[ii]; } else { matches = false; } } // If matches, then return the component parts matches = matches && testParts; } } else if (typeof input.splice == 'function') { matches = []; for (ii = input.length; ii--; ) { if (this.match(input[ii])) { matches[matches.length] = input[ii]; } } } else if (typeof input == 'object') { matches = {}; for (var key in input) { if (this.match(key)) { matches[key] = input[key]; } } } return matches; }; module.exports = function(text, test, separator) { var matcher = new WildcardMatcher(text, separator || /[\/\.]/); if (typeof test != 'undefined') { return matcher.match(test); } return matcher; }; },{}],58:[function(require,module,exports){ // 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.XHRUpload = require('@uppy/xhr-upload') Uppy.AwsS3 = require('@uppy/aws-s3') Uppy.AwsS3Multipart = require('@uppy/aws-s3-multipart') Uppy.DropTarget = require('@uppy/drop-target') Uppy.Utils = { delay: require('@uppy/utils/lib/delay'), EventTracker: require('@uppy/utils/lib/EventTracker'), AbortControllerLib: require('@uppy/utils/lib/AbortController') } },{"@uppy/aws-s3":5,"@uppy/aws-s3-multipart":3,"@uppy/core":18,"@uppy/drop-target":21,"@uppy/utils/lib/AbortController":23,"@uppy/utils/lib/EventTracker":24,"@uppy/utils/lib/delay":29,"@uppy/xhr-upload":49}]},{},[58]);