Socket
Socket
Sign inDemoInstall

@uppy/companion-client

Package Overview
Dependencies
Maintainers
6
Versions
88
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@uppy/companion-client - npm Package Compare versions

Comparing version 3.3.0 to 3.4.0

7

CHANGELOG.md
# @uppy/companion-client
## 3.4.0
Released: 2023-09-05
Included in: Uppy v3.15.0
- @uppy/aws-s3-multipart,@uppy/aws-s3,@uppy/companion-client,@uppy/core,@uppy/tus,@uppy/utils,@uppy/xhr-upload: Move remote file upload logic into companion-client (Merlijn Vos / #4573)
## 3.3.0

@@ -4,0 +11,0 @@

4

lib/Provider.js

@@ -32,4 +32,4 @@ 'use strict';

export default class Provider extends RequestClient {
constructor(uppy, opts) {
super(uppy, opts);
constructor(uppy, opts, getQueue) {
super(uppy, opts, getQueue);
Object.defineProperty(this, _removeAuthToken, {

@@ -36,0 +36,0 @@ value: _removeAuthToken2

@@ -9,5 +9,9 @@ 'use strict';

import ErrorWithCause from '@uppy/utils/lib/ErrorWithCause';
import emitSocketProgress from '@uppy/utils/lib/emitSocketProgress';
import getSocketHost from '@uppy/utils/lib/getSocketHost';
import EventManager from '@uppy/utils/lib/EventManager';
import AuthError from './AuthError.js';
import Socket from './Socket.js';
const packageJson = {
"version": "3.3.0"
"version": "3.4.0"
}; // Remove the trailing slash so we can always safely append /xyz.

@@ -30,3 +34,5 @@ function stripSlash(url) {

errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg;
} catch {/* if the response contains invalid JSON, let's ignore the error */}
} catch {
/* if the response contains invalid JSON, let's ignore the error */
}
throw new Error(errMsg);

@@ -39,5 +45,6 @@ }

var _getUrl = /*#__PURE__*/_classPrivateFieldLooseKey("getUrl");
var _requestSocketToken = /*#__PURE__*/_classPrivateFieldLooseKey("requestSocketToken");
_Symbol$for = Symbol.for('uppy test: getCompanionHeaders');
export default class RequestClient {
constructor(uppy, opts) {
constructor(uppy, opts, getQueue) {
Object.defineProperty(this, _getUrl, {

@@ -50,4 +57,18 @@ value: _getUrl2

});
Object.defineProperty(this, _requestSocketToken, {
writable: true,
value: async (file, postBody) => {
if (file.remote.url == null) {
throw new Error('Cannot connect to an undefined URL');
}
const res = await this.post(file.remote.url, {
...file.remote.body,
...postBody
});
return res.token;
}
});
this.uppy = uppy;
this.opts = opts;
this.getQueue = getQueue;
this.onReceiveResponse = this.onReceiveResponse.bind(this);

@@ -226,2 +247,150 @@ _classPrivateFieldLooseBase(this, _companionHeaders)[_companionHeaders] = opts == null ? void 0 : opts.companionHeaders;

}
async uploadRemoteFile(file, reqBody, options) {
if (options === void 0) {
options = {};
}
try {
if (file.serverToken) {
return await this.connectToServerSocket(file, this.getQueue());
}
const queueRequestSocketToken = this.getQueue().wrapPromiseFunction(_classPrivateFieldLooseBase(this, _requestSocketToken)[_requestSocketToken], {
priority: -1
});
const serverToken = await queueRequestSocketToken(file, reqBody).abortOn(options.signal);
if (!this.uppy.getState().files[file.id]) return undefined;
this.uppy.setFileState(file.id, {
serverToken
});
return await this.connectToServerSocket(this.uppy.getFile(file.id), this.getQueue());
} catch (err) {
var _err$cause;
if ((err == null || (_err$cause = err.cause) == null ? void 0 : _err$cause.name) === 'AbortError') {
// The file upload was aborted, it’s not an error
return undefined;
}
this.uppy.setFileState(file.id, {
serverToken: undefined
});
this.uppy.emit('upload-error', file, err);
throw err;
}
}
/**
* @param {UppyFile} file
*/
async connectToServerSocket(file, queue) {
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
});
const eventManager = new EventManager(this.uppy);
let queuedRequest;
eventManager.onFileRemove(file.id, () => {
socket.send('cancel', {});
queuedRequest.abort();
resolve(`upload ${file.id} was removed`);
});
eventManager.onPause(file.id, isPaused => {
if (isPaused) {
// Remove this file from the queue so another file can start in its place.
socket.send('pause', {});
queuedRequest.abort();
} 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 = queue.run(() => {
socket.open();
socket.send('resume', {});
return () => {};
});
}
});
eventManager.onPauseAll(file.id, () => {
socket.send('pause', {});
queuedRequest.abort();
});
eventManager.onCancelAll(file.id, function (_temp) {
let {
reason
} = _temp === void 0 ? {} : _temp;
if (reason === 'user') {
socket.send('cancel', {});
queuedRequest.abort();
}
resolve(`upload ${file.id} was canceled`);
});
eventManager.onResumeAll(file.id, () => {
queuedRequest.abort();
if (file.error) {
socket.send('pause', {});
}
queuedRequest = queue.run(() => {
socket.open();
socket.send('resume', {});
return () => {};
});
});
eventManager.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', {});
}
});
eventManager.onRetryAll(file.id, () => {
// See the comment in the onRetry() call
if (socket.isOpen) {
socket.send('pause', {});
socket.send('resume', {});
}
});
socket.on('progress', progressData => emitSocketProgress(this, progressData, file));
socket.on('error', errData => {
const {
message
} = errData.error;
const error = Object.assign(new Error(message), {
cause: errData.error
});
// If the remote retry optimisation should not be used,
// close the socket—this will tell companion to clear state and delete the file.
if (!this.opts.useFastRemoteRetry) {
// Remove the serverToken so that a new one will be created for the retry.
this.uppy.setFileState(file.id, {
serverToken: null
});
} else {
socket.close();
}
this.uppy.emit('upload-error', file, error);
queuedRequest.done();
reject(error);
});
socket.on('success', data => {
const uploadResp = {
uploadURL: data.url
};
this.uppy.emit('upload-success', file, uploadResp);
queuedRequest.done();
socket.close();
resolve();
});
queuedRequest = queue.run(() => {
if (file.isPaused) {
socket.send('pause', {});
} else {
socket.open();
}
return () => {};
});
});
}
}

@@ -228,0 +397,0 @@ function _getUrl2(url) {

{
"name": "@uppy/companion-client",
"description": "Client library for communication with Companion. Intended for use in Uppy plugins.",
"version": "3.3.0",
"version": "3.4.0",
"license": "MIT",

@@ -25,3 +25,3 @@ "main": "lib/index.js",

"dependencies": {
"@uppy/utils": "^5.4.3",
"@uppy/utils": "^5.5.0",
"namespace-emitter": "^2.0.1"

@@ -28,0 +28,0 @@ },

@@ -33,4 +33,4 @@ 'use strict'

constructor (uppy, opts) {
super(uppy, opts)
constructor (uppy, opts, getQueue) {
super(uppy, opts, getQueue)
this.provider = opts.provider

@@ -37,0 +37,0 @@ this.id = this.provider

@@ -5,3 +5,8 @@ 'use strict'

import ErrorWithCause from '@uppy/utils/lib/ErrorWithCause'
import emitSocketProgress from '@uppy/utils/lib/emitSocketProgress'
import getSocketHost from '@uppy/utils/lib/getSocketHost'
import EventManager from '@uppy/utils/lib/EventManager'
import AuthError from './AuthError.js'
import Socket from './Socket.js'

@@ -29,4 +34,8 @@ import packageJson from '../package.json'

errMsg = errData.message ? `${errMsg} message: ${errData.message}` : errMsg
errMsg = errData.requestId ? `${errMsg} request-Id: ${errData.requestId}` : errMsg
} catch { /* if the response contains invalid JSON, let's ignore the error */ }
errMsg = errData.requestId
? `${errMsg} request-Id: ${errData.requestId}`
: errMsg
} catch {
/* if the response contains invalid JSON, let's ignore the error */
}
throw new Error(errMsg)

@@ -43,5 +52,6 @@ }

constructor (uppy, opts) {
constructor (uppy, opts, getQueue) {
this.uppy = uppy
this.opts = opts
this.getQueue = getQueue
this.onReceiveResponse = this.onReceiveResponse.bind(this)

@@ -55,3 +65,5 @@ this.#companionHeaders = opts?.companionHeaders

[Symbol.for('uppy test: getCompanionHeaders')] () { return this.#companionHeaders }
[Symbol.for('uppy test: getCompanionHeaders')] () {
return this.#companionHeaders
}

@@ -115,3 +127,7 @@ get hostname () {

const fallbackAllowedHeaders = ['accept', 'content-type', 'uppy-auth-token']
const fallbackAllowedHeaders = [
'accept',
'content-type',
'uppy-auth-token',
]

@@ -128,9 +144,16 @@ const promise = (async () => {

this.uppy.log(`[CompanionClient] adding allowed preflight headers to companion cache: ${this.hostname} ${header}`)
this.uppy.log(
`[CompanionClient] adding allowed preflight headers to companion cache: ${this.hostname} ${header}`,
)
const allowedHeaders = header.split(',').map((headerName) => headerName.trim().toLowerCase())
const allowedHeaders = header
.split(',')
.map((headerName) => headerName.trim().toLowerCase())
allowedHeadersCache.set(this.hostname, allowedHeaders)
return allowedHeaders
} catch (err) {
this.uppy.log(`[CompanionClient] unable to make preflight request ${err}`, 'warning')
this.uppy.log(
`[CompanionClient] unable to make preflight request ${err}`,
'warning',
)
// If the user gets a network error or similar, we should try preflight

@@ -148,11 +171,18 @@ // again next time, or else we might get incorrect behaviour.

async preflightAndHeaders (path) {
const [allowedHeaders, headers] = await Promise.all([this.preflight(path), this.headers()])
const [allowedHeaders, headers] = await Promise.all([
this.preflight(path),
this.headers(),
])
// filter to keep only allowed Headers
return Object.fromEntries(Object.entries(headers).filter(([header]) => {
if (!allowedHeaders.includes(header.toLowerCase())) {
this.uppy.log(`[CompanionClient] excluding disallowed header ${header}`)
return false
}
return true
}))
return Object.fromEntries(
Object.entries(headers).filter(([header]) => {
if (!allowedHeaders.includes(header.toLowerCase())) {
this.uppy.log(
`[CompanionClient] excluding disallowed header ${header}`,
)
return false
}
return true
}),
)
}

@@ -175,3 +205,5 @@

if (err?.isAuthError) throw err
throw new ErrorWithCause(`Could not ${method} ${this.#getUrl(path)}`, { cause: err })
throw new ErrorWithCause(`Could not ${method} ${this.#getUrl(path)}`, {
cause: err,
})
}

@@ -200,2 +232,178 @@ }

}
async uploadRemoteFile (file, reqBody, options = {}) {
try {
if (file.serverToken) {
return await this.connectToServerSocket(file, this.getQueue())
}
const queueRequestSocketToken = this.getQueue().wrapPromiseFunction(
this.#requestSocketToken,
{ priority: -1 },
)
const serverToken = await queueRequestSocketToken(file, reqBody).abortOn(
options.signal,
)
if (!this.uppy.getState().files[file.id]) return undefined
this.uppy.setFileState(file.id, { serverToken })
return await this.connectToServerSocket(
this.uppy.getFile(file.id),
this.getQueue(),
)
} catch (err) {
if (err?.cause?.name === 'AbortError') {
// The file upload was aborted, it’s not an error
return undefined
}
this.uppy.setFileState(file.id, { serverToken: undefined })
this.uppy.emit('upload-error', file, err)
throw err
}
}
#requestSocketToken = async (file, postBody) => {
if (file.remote.url == null) {
throw new Error('Cannot connect to an undefined URL')
}
const res = await this.post(file.remote.url, {
...file.remote.body,
...postBody,
})
return res.token
}
/**
* @param {UppyFile} file
*/
async connectToServerSocket (file, queue) {
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,
})
const eventManager = new EventManager(this.uppy)
let queuedRequest
eventManager.onFileRemove(file.id, () => {
socket.send('cancel', {})
queuedRequest.abort()
resolve(`upload ${file.id} was removed`)
})
eventManager.onPause(file.id, (isPaused) => {
if (isPaused) {
// Remove this file from the queue so another file can start in its place.
socket.send('pause', {})
queuedRequest.abort()
} 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 = queue.run(() => {
socket.open()
socket.send('resume', {})
return () => {}
})
}
})
eventManager.onPauseAll(file.id, () => {
socket.send('pause', {})
queuedRequest.abort()
})
eventManager.onCancelAll(file.id, ({ reason } = {}) => {
if (reason === 'user') {
socket.send('cancel', {})
queuedRequest.abort()
}
resolve(`upload ${file.id} was canceled`)
})
eventManager.onResumeAll(file.id, () => {
queuedRequest.abort()
if (file.error) {
socket.send('pause', {})
}
queuedRequest = queue.run(() => {
socket.open()
socket.send('resume', {})
return () => {}
})
})
eventManager.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', {})
}
})
eventManager.onRetryAll(file.id, () => {
// See the comment in the onRetry() call
if (socket.isOpen) {
socket.send('pause', {})
socket.send('resume', {})
}
})
socket.on('progress', (progressData) => emitSocketProgress(this, progressData, file))
socket.on('error', (errData) => {
const { message } = errData.error
const error = Object.assign(new Error(message), {
cause: errData.error,
})
// If the remote retry optimisation should not be used,
// close the socket—this will tell companion to clear state and delete the file.
if (!this.opts.useFastRemoteRetry) {
// Remove the serverToken so that a new one will be created for the retry.
this.uppy.setFileState(file.id, {
serverToken: null,
})
} else {
socket.close()
}
this.uppy.emit('upload-error', file, error)
queuedRequest.done()
reject(error)
})
socket.on('success', (data) => {
const uploadResp = {
uploadURL: data.url,
}
this.uppy.emit('upload-success', file, uploadResp)
queuedRequest.done()
socket.close()
resolve()
})
queuedRequest = queue.run(() => {
if (file.isPaused) {
socket.send('pause', {})
} else {
socket.open()
}
return () => {}
})
})
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc