@uppy/xhr-upload
Advanced tools
Comparing version 3.1.1 to 3.2.0
# @uppy/xhr-upload | ||
## 3.2.0 | ||
Released: 2023-04-18 | ||
Included in: Uppy v3.8.0 | ||
- @uppy/xhr-upload: fix type in README.md (Top Master / #4416) | ||
## 3.1.1 | ||
@@ -4,0 +11,0 @@ |
619
lib/index.js
@@ -12,3 +12,2 @@ function _classPrivateFieldLooseBase(receiver, privateKey) { if (!Object.prototype.hasOwnProperty.call(receiver, privateKey)) { throw new TypeError("attempted to use private field on non-instance"); } return receiver; } | ||
import getSocketHost from '@uppy/utils/lib/getSocketHost'; | ||
import settle from '@uppy/utils/lib/settle'; | ||
import EventTracker from '@uppy/utils/lib/EventTracker'; | ||
@@ -19,4 +18,5 @@ import ProgressTimeout from '@uppy/utils/lib/ProgressTimeout'; | ||
import isNetworkError from '@uppy/utils/lib/isNetworkError'; | ||
import { filterNonFailedFiles, filterFilesToEmitUploadStarted } from '@uppy/utils/lib/fileFilters'; | ||
const packageJson = { | ||
"version": "3.1.1" | ||
"version": "3.2.0" | ||
}; | ||
@@ -63,4 +63,14 @@ import locale from './locale.js'; | ||
var _upload = /*#__PURE__*/_classPrivateFieldLooseKey("upload"); | ||
var _requestSocketToken = /*#__PURE__*/_classPrivateFieldLooseKey("requestSocketToken"); | ||
var _uploadRemote = /*#__PURE__*/_classPrivateFieldLooseKey("uploadRemote"); | ||
var _uploadBundle = /*#__PURE__*/_classPrivateFieldLooseKey("uploadBundle"); | ||
var _uploadFiles = /*#__PURE__*/_classPrivateFieldLooseKey("uploadFiles"); | ||
var _handleUpload = /*#__PURE__*/_classPrivateFieldLooseKey("handleUpload"); | ||
export default class XHRUpload extends BasePlugin { | ||
@@ -70,2 +80,14 @@ // eslint-disable-next-line global-require | ||
super(uppy, _opts); | ||
Object.defineProperty(this, _uploadFiles, { | ||
value: _uploadFiles2 | ||
}); | ||
Object.defineProperty(this, _uploadBundle, { | ||
value: _uploadBundle2 | ||
}); | ||
Object.defineProperty(this, _uploadRemote, { | ||
value: _uploadRemote2 | ||
}); | ||
Object.defineProperty(this, _upload, { | ||
value: _upload2 | ||
}); | ||
Object.defineProperty(this, _queueRequestSocketToken, { | ||
@@ -96,2 +118,40 @@ writable: true, | ||
}); | ||
Object.defineProperty(this, _handleUpload, { | ||
writable: true, | ||
value: async fileIDs => { | ||
if (fileIDs.length === 0) { | ||
this.uppy.log('[XHRUpload] No files to upload!'); | ||
return; | ||
} // 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 = this.uppy.getFilesByIds(fileIDs); | ||
const filesFiltered = filterNonFailedFiles(files); | ||
const filesToEmit = filterFilesToEmitUploadStarted(filesFiltered); | ||
this.uppy.emit('upload-start', filesToEmit); | ||
if (this.opts.bundle) { | ||
// if bundle: true, we don’t support remote uploads | ||
const isSomeFileRemote = filesFiltered.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'); | ||
} | ||
await _classPrivateFieldLooseBase(this, _uploadBundle)[_uploadBundle](filesFiltered); | ||
} else { | ||
await _classPrivateFieldLooseBase(this, _uploadFiles)[_uploadFiles](filesFiltered); | ||
} | ||
} | ||
}); | ||
this.type = 'uploader'; | ||
@@ -158,4 +218,3 @@ this.id = this.opts.id || 'XHRUpload'; | ||
}; | ||
this.i18nInit(); | ||
this.handleUpload = this.handleUpload.bind(this); // Simultaneous upload limiting is shared across all uploads with this plugin. | ||
this.i18nInit(); // Simultaneous upload limiting is shared across all uploads with this plugin. | ||
@@ -257,154 +316,2 @@ if (internalRateLimitedQueue in this.opts) { | ||
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); | ||
let queuedRequest; | ||
const timer = new ProgressTimeout(opts.timeout, () => { | ||
const error = new Error(this.i18n('uploadStalled', { | ||
seconds: Math.ceil(opts.timeout / 1000) | ||
})); | ||
this.uppy.emit('upload-stalled', error, [file]); | ||
}); | ||
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', () => { | ||
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(xhr.status, xhr.responseText, xhr)) { | ||
const body = opts.getResponseData(xhr.responseText, xhr); | ||
const uploadURL = body[opts.responseUrlFieldName]; | ||
const uploadResp = { | ||
status: xhr.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: xhr.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; | ||
} | ||
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, _ref => { | ||
let { | ||
reason | ||
} = _ref; | ||
if (reason === 'user') { | ||
queuedRequest.abort(); | ||
} | ||
reject(new Error('Upload cancelled')); | ||
}); | ||
}); | ||
} | ||
// NOTE! Keep this duplicated code in sync with other plugins | ||
// TODO we should probably abstract this into a common function | ||
async uploadRemote(file) { | ||
// TODO: we could rewrite this to use server-sent events instead of creating WebSockets. | ||
try { | ||
this.uppy.emit('upload-started', file); | ||
if (file.serverToken) { | ||
return await this.connectToServerSocket(file); | ||
} | ||
const serverToken = await _classPrivateFieldLooseBase(this, _queueRequestSocketToken)[_queueRequestSocketToken](file); | ||
if (!this.uppy.getState().files[file.id]) return undefined; | ||
this.uppy.setFileState(file.id, { | ||
serverToken | ||
}); | ||
return await this.connectToServerSocket(this.uppy.getFile(file.id)); | ||
} catch (err) { | ||
this.uppy.setFileState(file.id, { | ||
serverToken: undefined | ||
}); | ||
this.uppy.emit('upload-error', file, err); | ||
throw err; | ||
} | ||
} | ||
async connectToServerSocket(file) { | ||
@@ -523,114 +430,2 @@ return new Promise((resolve, reject) => { | ||
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 emitError = error => { | ||
files.forEach(file => { | ||
this.uppy.emit('upload-error', file, error); | ||
}); | ||
}; | ||
const timer = new ProgressTimeout(this.opts.timeout, () => { | ||
const error = new Error(this.i18n('uploadStalled', { | ||
seconds: Math.ceil(this.opts.timeout / 1000) | ||
})); | ||
this.uppy.emit('upload-stalled', error, files); | ||
}); | ||
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', function (_temp2) { | ||
let { | ||
reason | ||
} = _temp2 === void 0 ? {} : _temp2; | ||
if (reason !== 'user') return; | ||
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) { | ||
@@ -666,35 +461,2 @@ this.uploaderEvents[fileID].on('file-removed', file => { | ||
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() { | ||
@@ -712,3 +474,3 @@ if (this.opts.bundle) { | ||
this.uppy.addUploader(this.handleUpload); | ||
this.uppy.addUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]); | ||
} | ||
@@ -728,6 +490,257 @@ | ||
this.uppy.removeUploader(this.handleUpload); | ||
this.uppy.removeUploader(_classPrivateFieldLooseBase(this, _handleUpload)[_handleUpload]); | ||
} | ||
} | ||
async function _upload2(file, current, total) { | ||
const opts = this.getOptions(file); | ||
this.uppy.log(`uploading ${current} of ${total}`); | ||
return new Promise((resolve, reject) => { | ||
const data = opts.formData ? this.createFormDataUpload(file, opts) : file.data; | ||
const xhr = new XMLHttpRequest(); | ||
this.uploaderEvents[file.id] = new EventTracker(this.uppy); | ||
let queuedRequest; | ||
const timer = new ProgressTimeout(opts.timeout, () => { | ||
const error = new Error(this.i18n('uploadStalled', { | ||
seconds: Math.ceil(opts.timeout / 1000) | ||
})); | ||
this.uppy.emit('upload-stalled', error, [file]); | ||
}); | ||
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', () => { | ||
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(xhr.status, xhr.responseText, xhr)) { | ||
const body = opts.getResponseData(xhr.responseText, xhr); | ||
const uploadURL = body[opts.responseUrlFieldName]; | ||
const uploadResp = { | ||
status: xhr.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: xhr.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; | ||
} | ||
queuedRequest = this.requests.run(() => { | ||
// 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, _ref => { | ||
let { | ||
reason | ||
} = _ref; | ||
if (reason === 'user') { | ||
queuedRequest.abort(); | ||
} | ||
reject(new Error('Upload cancelled')); | ||
}); | ||
}); | ||
} | ||
async function _uploadRemote2(file) { | ||
// TODO: we could rewrite this to use server-sent events instead of creating WebSockets. | ||
try { | ||
if (file.serverToken) { | ||
return await this.connectToServerSocket(file); | ||
} | ||
const serverToken = await _classPrivateFieldLooseBase(this, _queueRequestSocketToken)[_queueRequestSocketToken](file); | ||
if (!this.uppy.getState().files[file.id]) return undefined; | ||
this.uppy.setFileState(file.id, { | ||
serverToken | ||
}); | ||
return await this.connectToServerSocket(this.uppy.getFile(file.id)); | ||
} catch (err) { | ||
this.uppy.setFileState(file.id, { | ||
serverToken: undefined | ||
}); | ||
this.uppy.emit('upload-error', file, err); | ||
throw err; | ||
} | ||
} | ||
function _uploadBundle2(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 emitError = error => { | ||
files.forEach(file => { | ||
this.uppy.emit('upload-error', file, error); | ||
}); | ||
}; | ||
const timer = new ProgressTimeout(this.opts.timeout, () => { | ||
const error = new Error(this.i18n('uploadStalled', { | ||
seconds: Math.ceil(this.opts.timeout / 1000) | ||
})); | ||
this.uppy.emit('upload-stalled', error, files); | ||
}); | ||
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', function (_temp2) { | ||
let { | ||
reason | ||
} = _temp2 === void 0 ? {} : _temp2; | ||
if (reason !== 'user') return; | ||
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); | ||
}); | ||
} | ||
async function _uploadFiles2(files) { | ||
await Promise.allSettled(files.map((file, i) => { | ||
const current = parseInt(i, 10) + 1; | ||
const total = files.length; | ||
if (file.isRemote) { | ||
return _classPrivateFieldLooseBase(this, _uploadRemote)[_uploadRemote](file, current, total); | ||
} | ||
return _classPrivateFieldLooseBase(this, _upload)[_upload](file, current, total); | ||
})); | ||
} | ||
XHRUpload.VERSION = packageJson.version; |
{ | ||
"name": "@uppy/xhr-upload", | ||
"description": "Plain and simple classic HTML multipart form uploads with Uppy, as well as uploads using the HTTP PUT method.", | ||
"version": "3.1.1", | ||
"version": "3.2.0", | ||
"license": "MIT", | ||
@@ -28,4 +28,4 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@uppy/companion-client": "^3.1.2", | ||
"@uppy/utils": "^5.2.0", | ||
"@uppy/companion-client": "^3.1.3", | ||
"@uppy/utils": "^5.3.0", | ||
"nanoid": "^4.0.0" | ||
@@ -38,4 +38,4 @@ }, | ||
"peerDependencies": { | ||
"@uppy/core": "^3.1.2" | ||
"@uppy/core": "^3.2.0" | ||
} | ||
} |
# @uppy/xhr-upload | ||
<img src="https://uppy.io/images/logos/uppy-dog-head-arrow.svg" width="120" alt="Uppy logo: a superman puppy in a pink suit" align="right"> | ||
<img src="https://uppy.io/img/logo.svg" width="120" alt="Uppy logo: a smiling puppy above a pink upwards arrow" align="right"> | ||
@@ -21,3 +21,3 @@ [![npm version](https://img.shields.io/npm/v/@uppy/xhr-upload.svg?style=flat-square)](https://www.npmjs.com/package/@uppy/xhr-upload) | ||
const uppy = new Uppy() | ||
uppy.use(Uppy, { | ||
uppy.use(XHRUpload, { | ||
// Options | ||
@@ -24,0 +24,0 @@ }) |
@@ -6,3 +6,2 @@ import BasePlugin from '@uppy/core/lib/BasePlugin.js' | ||
import getSocketHost from '@uppy/utils/lib/getSocketHost' | ||
import settle from '@uppy/utils/lib/settle' | ||
import EventTracker from '@uppy/utils/lib/EventTracker' | ||
@@ -13,2 +12,3 @@ import ProgressTimeout from '@uppy/utils/lib/ProgressTimeout' | ||
import isNetworkError from '@uppy/utils/lib/isNetworkError' | ||
import { filterNonFailedFiles, filterFilesToEmitUploadStarted } from '@uppy/utils/lib/fileFilters' | ||
@@ -118,4 +118,2 @@ import packageJson from '../package.json' | ||
this.handleUpload = this.handleUpload.bind(this) | ||
// Simultaneous upload limiting is shared across all uploads with this plugin. | ||
@@ -220,3 +218,3 @@ if (internalRateLimitedQueue in this.opts) { | ||
upload (file, current, total) { | ||
async #upload (file, current, total) { | ||
const opts = this.getOptions(file) | ||
@@ -226,4 +224,2 @@ | ||
return new Promise((resolve, reject) => { | ||
this.uppy.emit('upload-started', file) | ||
const data = opts.formData | ||
@@ -325,4 +321,2 @@ ? this.createFormDataUpload(file, opts) | ||
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 | ||
@@ -384,6 +378,5 @@ // header needs to be fresh each time the token is refreshed so computing and setting the | ||
// TODO we should probably abstract this into a common function | ||
async uploadRemote (file) { | ||
async #uploadRemote (file) { | ||
// TODO: we could rewrite this to use server-sent events instead of creating WebSockets. | ||
try { | ||
this.uppy.emit('upload-started', file) | ||
if (file.serverToken) { | ||
@@ -506,3 +499,3 @@ return await this.connectToServerSocket(file) | ||
uploadBundle (files) { | ||
#uploadBundle (files) { | ||
return new Promise((resolve, reject) => { | ||
@@ -598,23 +591,15 @@ const { endpoint } = this.opts | ||
xhr.send(formData) | ||
files.forEach((file) => { | ||
this.uppy.emit('upload-started', file) | ||
}) | ||
}) | ||
} | ||
uploadFiles (files) { | ||
const promises = files.map((file, i) => { | ||
async #uploadFiles (files) { | ||
await Promise.allSettled(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) | ||
if (file.isRemote) { | ||
return this.#uploadRemote(file, current, total) | ||
} | ||
return this.upload(file, current, total) | ||
}) | ||
return settle(promises) | ||
return this.#upload(file, current, total) | ||
})) | ||
} | ||
@@ -650,6 +635,6 @@ | ||
handleUpload (fileIDs) { | ||
#handleUpload = async (fileIDs) => { | ||
if (fileIDs.length === 0) { | ||
this.uppy.log('[XHRUpload] No files to upload!') | ||
return Promise.resolve() | ||
return | ||
} | ||
@@ -667,7 +652,11 @@ | ||
this.uppy.log('[XHRUpload] Uploading...') | ||
const files = fileIDs.map((fileID) => this.uppy.getFile(fileID)) | ||
const files = this.uppy.getFilesByIds(fileIDs) | ||
const filesFiltered = filterNonFailedFiles(files) | ||
const filesToEmit = filterFilesToEmitUploadStarted(filesFiltered) | ||
this.uppy.emit('upload-start', filesToEmit) | ||
if (this.opts.bundle) { | ||
// if bundle: true, we don’t support remote uploads | ||
const isSomeFileRemote = files.some(file => file.isRemote) | ||
const isSomeFileRemote = filesFiltered.some(file => file.isRemote) | ||
if (isSomeFileRemote) { | ||
@@ -681,6 +670,6 @@ throw new Error('Can’t upload remote files when the `bundle: true` option is set') | ||
return this.uploadBundle(files) | ||
await this.#uploadBundle(filesFiltered) | ||
} else { | ||
await this.#uploadFiles(filesFiltered) | ||
} | ||
return this.uploadFiles(files).then(() => null) | ||
} | ||
@@ -699,3 +688,3 @@ | ||
this.uppy.addUploader(this.handleUpload) | ||
this.uppy.addUploader(this.#handleUpload) | ||
} | ||
@@ -714,4 +703,4 @@ | ||
this.uppy.removeUploader(this.handleUpload) | ||
this.uppy.removeUploader(this.#handleUpload) | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
1362
102646
Updated@uppy/utils@^5.3.0