@uppy/companion-client
Advanced tools
Comparing version 3.0.2 to 3.1.0
# @uppy/companion-client | ||
## 3.1.0 | ||
Released: 2022-11-10 | ||
Included in: Uppy v3.3.0 | ||
- @uppy/companion-client: add support for `AbortSignal` (Antoine du Hamel / #4201) | ||
- @uppy/companion-client: prevent preflight race condition (Mikael Finstad / #4182) | ||
## 3.0.2 | ||
@@ -4,0 +12,0 @@ |
@@ -22,26 +22,23 @@ 'use strict'; | ||
headers() { | ||
return Promise.all([super.headers(), this.getAuthToken()]).then(_ref => { | ||
let [headers, token] = _ref; | ||
const authHeaders = {}; | ||
async headers() { | ||
const [headers, token] = await Promise.all([super.headers(), this.getAuthToken()]); | ||
const authHeaders = {}; | ||
if (token) { | ||
authHeaders['uppy-auth-token'] = token; | ||
} | ||
if (token) { | ||
authHeaders['uppy-auth-token'] = token; | ||
} | ||
if (this.companionKeysParams) { | ||
authHeaders['uppy-credentials-params'] = btoa(JSON.stringify({ | ||
params: this.companionKeysParams | ||
})); | ||
} | ||
if (this.companionKeysParams) { | ||
authHeaders['uppy-credentials-params'] = btoa(JSON.stringify({ | ||
params: this.companionKeysParams | ||
})); | ||
} | ||
return { ...headers, | ||
...authHeaders | ||
}; | ||
}); | ||
return { ...headers, | ||
...authHeaders | ||
}; | ||
} | ||
onReceiveResponse(response) { | ||
response = super.onReceiveResponse(response); // eslint-disable-line no-param-reassign | ||
super.onReceiveResponse(response); | ||
const plugin = this.uppy.getPlugin(this.pluginId); | ||
@@ -117,4 +114,4 @@ const oldAuthenticated = plugin.getPluginState().authenticated; | ||
logout() { | ||
return this.get(`${this.id}/logout`).then(response => Promise.all([response, this.uppy.getPlugin(this.pluginId).storage.removeItem(this.tokenKey)])).then(_ref2 => { | ||
let [response] = _ref2; | ||
return this.get(`${this.id}/logout`).then(response => Promise.all([response, this.uppy.getPlugin(this.pluginId).storage.removeItem(this.tokenKey)])).then(_ref => { | ||
let [response] = _ref; | ||
return response; | ||
@@ -121,0 +118,0 @@ }); |
@@ -15,3 +15,3 @@ 'use strict'; | ||
const packageJson = { | ||
"version": "3.0.2" | ||
"version": "3.1.0" | ||
}; // Remove the trailing slash so we can always safely append /xyz. | ||
@@ -30,25 +30,27 @@ | ||
if (res.status < 200 || res.status > 300) { | ||
let errMsg = `Failed request with status: ${res.status}. ${res.statusText}`; | ||
if (res.ok) { | ||
return jsonPromise; | ||
} | ||
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); | ||
} | ||
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; | ||
} catch { | ||
/* if the response contains invalid JSON, let's ignore the error */ | ||
} | ||
return jsonPromise; | ||
} | ||
throw new Error(errMsg); | ||
} // todo pull out into core instead? | ||
const allowedHeadersCache = new Map(); | ||
var _companionHeaders = /*#__PURE__*/_classPrivateFieldLooseKey("companionHeaders"); | ||
var _getPostResponseFunc = /*#__PURE__*/_classPrivateFieldLooseKey("getPostResponseFunc"); | ||
var _getUrl = /*#__PURE__*/_classPrivateFieldLooseKey("getUrl"); | ||
var _errorHandler = /*#__PURE__*/_classPrivateFieldLooseKey("errorHandler"); | ||
var _request = /*#__PURE__*/_classPrivateFieldLooseKey("request"); | ||
@@ -58,4 +60,4 @@ _Symbol$for = Symbol.for('uppy test: getCompanionHeaders'); | ||
constructor(uppy, opts) { | ||
Object.defineProperty(this, _errorHandler, { | ||
value: _errorHandler2 | ||
Object.defineProperty(this, _request, { | ||
value: _request2 | ||
}); | ||
@@ -69,11 +71,5 @@ Object.defineProperty(this, _getUrl, { | ||
}); | ||
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; | ||
_classPrivateFieldLooseBase(this, _companionHeaders)[_companionHeaders] = opts == null ? void 0 : opts.companionHeaders; | ||
@@ -98,15 +94,20 @@ } | ||
headers() { | ||
return Promise.resolve({ ...RequestClient.defaultHeaders, | ||
async headers() { | ||
const defaultHeaders = { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}` | ||
}; | ||
return { ...defaultHeaders, | ||
..._classPrivateFieldLooseBase(this, _companionHeaders)[_companionHeaders] | ||
}); | ||
}; | ||
} | ||
onReceiveResponse(response) { | ||
onReceiveResponse(_ref) { | ||
let { | ||
headers | ||
} = _ref; | ||
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. | ||
const host = this.opts.companionUrl; // Store the self-identified domain name for the Companion instance we just hit. | ||
@@ -120,68 +121,114 @@ if (headers.has('i-am') && headers.get('i-am') !== companion[host]) { | ||
} | ||
} | ||
return response; | ||
/* | ||
Preflight was added to avoid breaking change between older Companion-client versions and | ||
newer Companion versions and vice-versa. Usually the break will manifest via CORS errors because a | ||
version of companion-client could be sending certain headers to a version of Companion server that | ||
does not support those headers. In which case, the default preflight would lead to CORS. | ||
So to avoid those errors, we do preflight ourselves, to see what headers the Companion server | ||
we are communicating with allows. And based on that, companion-client knows what headers to | ||
send and what headers to not send. | ||
The preflight only happens once throughout the life-cycle of a certain | ||
Companion-client <-> Companion-server pair (allowedHeadersCache). | ||
Subsequent requests use the cached result of the preflight. | ||
However if there is an error retrieving the allowed headers, we will try again next time | ||
*/ | ||
async preflight(path) { | ||
const allowedHeadersCached = allowedHeadersCache.get(this.hostname); | ||
if (allowedHeadersCached != null) return allowedHeadersCached; | ||
const fallbackAllowedHeaders = ['accept', 'content-type', 'uppy-auth-token']; | ||
const promise = (async () => { | ||
try { | ||
const response = await fetch(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), { | ||
method: 'OPTIONS' | ||
}); | ||
const header = response.headers.get('access-control-allow-headers'); | ||
if (header == null) { | ||
allowedHeadersCache.set(this.hostname, fallbackAllowedHeaders); | ||
return fallbackAllowedHeaders; | ||
} | ||
this.uppy.log(`[CompanionClient] adding allowed preflight headers to companion cache: ${this.hostname} ${header}`); | ||
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'); // If the user gets a network error or similar, we should try preflight | ||
// again next time, or else we might get incorrect behaviour. | ||
allowedHeadersCache.delete(this.hostname); // re-fetch next time | ||
return fallbackAllowedHeaders; | ||
} | ||
})(); | ||
allowedHeadersCache.set(this.hostname, promise); | ||
return promise; | ||
} | ||
preflight(path) { | ||
if (this.preflightDone) { | ||
return Promise.resolve(this.allowedHeaders.slice()); | ||
} | ||
async preflightAndHeaders(path) { | ||
const [allowedHeaders, headers] = await Promise.all([this.preflight(path), this.headers()]); // filter to keep only allowed Headers | ||
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()); | ||
return Object.fromEntries(Object.entries(headers).filter(_ref2 => { | ||
let [header] = _ref2; | ||
if (!allowedHeaders.includes(header.toLowerCase())) { | ||
this.uppy.log(`[CompanionClient] excluding disallowed header ${header}`); | ||
return false; | ||
} | ||
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(); | ||
}); | ||
return true; | ||
})); | ||
} | ||
preflightAndHeaders(path) { | ||
return Promise.all([this.preflight(path), this.headers()]).then(_ref => { | ||
let [allowedHeaders, headers] = _ref; | ||
// 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; | ||
async get(path, options) { | ||
if (options === void 0) { | ||
options = undefined; | ||
} | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { | ||
skipPostResponse: options | ||
}; | ||
return _classPrivateFieldLooseBase(this, _request)[_request]({ ...options, | ||
path | ||
}); | ||
} | ||
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)); | ||
} | ||
async post(path, data, options) { | ||
if (options === void 0) { | ||
options = undefined; | ||
} | ||
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)); | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { | ||
skipPostResponse: options | ||
}; | ||
return _classPrivateFieldLooseBase(this, _request)[_request]({ ...options, | ||
path, | ||
method: 'POST', | ||
data | ||
}); | ||
} | ||
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)); | ||
async delete(path, data, options) { | ||
if (data === void 0) { | ||
data = undefined; | ||
} | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { | ||
skipPostResponse: options | ||
}; | ||
return _classPrivateFieldLooseBase(this, _request)[_request]({ ...options, | ||
path, | ||
method: 'DELETE', | ||
data | ||
}); | ||
} | ||
@@ -199,22 +246,30 @@ | ||
function _errorHandler2(method, path) { | ||
return err => { | ||
var _err; | ||
async function _request2(_ref3) { | ||
let { | ||
path, | ||
method = 'GET', | ||
data, | ||
skipPostResponse, | ||
signal | ||
} = _ref3; | ||
if (!((_err = err) != null && _err.isAuthError)) { | ||
// eslint-disable-next-line no-param-reassign | ||
err = new ErrorWithCause(`Could not ${method} ${_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path)}`, { | ||
cause: err | ||
}); | ||
} | ||
return Promise.reject(err); | ||
}; | ||
try { | ||
const headers = await this.preflightAndHeaders(path); | ||
const response = await fetchWithNetworkError(_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path), { | ||
method, | ||
signal, | ||
headers, | ||
credentials: this.opts.companionCookiesRule || 'same-origin', | ||
body: data ? JSON.stringify(data) : null | ||
}); | ||
if (!skipPostResponse) this.onReceiveResponse(response); | ||
return handleJSONResponse(response); | ||
} catch (err) { | ||
if (err != null && err.isAuthError) throw err; | ||
throw new ErrorWithCause(`Could not ${method} ${_classPrivateFieldLooseBase(this, _getUrl)[_getUrl](path)}`, { | ||
cause: err | ||
}); | ||
} | ||
} | ||
RequestClient.VERSION = packageJson.version; | ||
RequestClient.defaultHeaders = { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}` | ||
}; | ||
RequestClient.VERSION = packageJson.version; |
{ | ||
"name": "@uppy/companion-client", | ||
"description": "Client library for communication with Companion. Intended for use in Uppy plugins.", | ||
"version": "3.0.2", | ||
"version": "3.1.0", | ||
"license": "MIT", | ||
@@ -25,3 +25,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@uppy/utils": "^5.0.2", | ||
"@uppy/utils": "^5.1.0", | ||
"namespace-emitter": "^2.0.1" | ||
@@ -28,0 +28,0 @@ }, |
@@ -22,21 +22,19 @@ 'use strict' | ||
headers () { | ||
return Promise.all([super.headers(), this.getAuthToken()]) | ||
.then(([headers, token]) => { | ||
const authHeaders = {} | ||
if (token) { | ||
authHeaders['uppy-auth-token'] = token | ||
} | ||
async headers () { | ||
const [headers, token] = await Promise.all([super.headers(), this.getAuthToken()]) | ||
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 } | ||
}) | ||
if (this.companionKeysParams) { | ||
authHeaders['uppy-credentials-params'] = btoa( | ||
JSON.stringify({ params: this.companionKeysParams }), | ||
) | ||
} | ||
return { ...headers, ...authHeaders } | ||
} | ||
onReceiveResponse (response) { | ||
response = super.onReceiveResponse(response) // eslint-disable-line no-param-reassign | ||
super.onReceiveResponse(response) | ||
const plugin = this.uppy.getPlugin(this.pluginId) | ||
@@ -43,0 +41,0 @@ const oldAuthenticated = plugin.getPluginState().authenticated |
@@ -20,17 +20,18 @@ 'use strict' | ||
const jsonPromise = res.json() | ||
if (res.ok) { | ||
return jsonPromise | ||
} | ||
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 | ||
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 | ||
} catch { /* if the response contains invalid JSON, let's ignore the error */ } | ||
throw new Error(errMsg) | ||
} | ||
// todo pull out into core instead? | ||
const allowedHeadersCache = new Map() | ||
export default class RequestClient { | ||
@@ -41,4 +42,2 @@ static VERSION = packageJson.version | ||
#getPostResponseFunc = skip => response => (skip ? response : this.onReceiveResponse(response)) | ||
constructor (uppy, opts) { | ||
@@ -48,4 +47,2 @@ this.uppy = uppy | ||
this.onReceiveResponse = this.onReceiveResponse.bind(this) | ||
this.allowedHeaders = ['accept', 'content-type', 'uppy-auth-token'] | ||
this.preflightDone = false | ||
this.#companionHeaders = opts?.companionHeaders | ||
@@ -66,20 +63,20 @@ } | ||
static defaultHeaders = { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}`, | ||
} | ||
async headers () { | ||
const defaultHeaders = { | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
'Uppy-Versions': `@uppy/companion-client=${RequestClient.VERSION}`, | ||
} | ||
headers () { | ||
return Promise.resolve({ | ||
...RequestClient.defaultHeaders, | ||
return { | ||
...defaultHeaders, | ||
...this.#companionHeaders, | ||
}) | ||
} | ||
} | ||
onReceiveResponse (response) { | ||
onReceiveResponse ({ headers }) { | ||
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. | ||
@@ -91,3 +88,2 @@ if (headers.has('i-am') && headers.get('i-am') !== companion[host]) { | ||
} | ||
return response | ||
} | ||
@@ -102,90 +98,100 @@ | ||
#errorHandler (method, path) { | ||
return (err) => { | ||
if (!err?.isAuthError) { | ||
// eslint-disable-next-line no-param-reassign | ||
err = new ErrorWithCause(`Could not ${method} ${this.#getUrl(path)}`, { cause: err }) | ||
} | ||
return Promise.reject(err) | ||
} | ||
} | ||
/* | ||
Preflight was added to avoid breaking change between older Companion-client versions and | ||
newer Companion versions and vice-versa. Usually the break will manifest via CORS errors because a | ||
version of companion-client could be sending certain headers to a version of Companion server that | ||
does not support those headers. In which case, the default preflight would lead to CORS. | ||
So to avoid those errors, we do preflight ourselves, to see what headers the Companion server | ||
we are communicating with allows. And based on that, companion-client knows what headers to | ||
send and what headers to not send. | ||
preflight (path) { | ||
if (this.preflightDone) { | ||
return Promise.resolve(this.allowedHeaders.slice()) | ||
} | ||
The preflight only happens once throughout the life-cycle of a certain | ||
Companion-client <-> Companion-server pair (allowedHeadersCache). | ||
Subsequent requests use the cached result of the preflight. | ||
However if there is an error retrieving the allowed headers, we will try again next time | ||
*/ | ||
async preflight (path) { | ||
const allowedHeadersCached = allowedHeadersCache.get(this.hostname) | ||
if (allowedHeadersCached != null) return allowedHeadersCached | ||
return fetch(this.#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()) | ||
const fallbackAllowedHeaders = ['accept', 'content-type', 'uppy-auth-token'] | ||
const promise = (async () => { | ||
try { | ||
const response = await fetch(this.#getUrl(path), { method: 'OPTIONS' }) | ||
const header = response.headers.get('access-control-allow-headers') | ||
if (header == null) { | ||
allowedHeadersCache.set(this.hostname, fallbackAllowedHeaders) | ||
return fallbackAllowedHeaders | ||
} | ||
this.preflightDone = true | ||
return this.allowedHeaders.slice() | ||
}) | ||
.catch((err) => { | ||
this.uppy.log(`[CompanionClient] adding allowed preflight headers to companion cache: ${this.hostname} ${header}`) | ||
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.preflightDone = true | ||
return this.allowedHeaders.slice() | ||
}) | ||
// If the user gets a network error or similar, we should try preflight | ||
// again next time, or else we might get incorrect behaviour. | ||
allowedHeadersCache.delete(this.hostname) // re-fetch next time | ||
return fallbackAllowedHeaders | ||
} | ||
})() | ||
allowedHeadersCache.set(this.hostname, promise) | ||
return promise | ||
} | ||
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 | ||
}) | ||
async preflightAndHeaders (path) { | ||
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 | ||
})) | ||
} | ||
get (path, skipPostResponse) { | ||
const method = 'get' | ||
return this.preflightAndHeaders(path) | ||
.then((headers) => fetchWithNetworkError(this.#getUrl(path), { | ||
async #request ({ path, method = 'GET', data, skipPostResponse, signal }) { | ||
try { | ||
const headers = await this.preflightAndHeaders(path) | ||
const response = await fetchWithNetworkError(this.#getUrl(path), { | ||
method, | ||
signal, | ||
headers, | ||
credentials: this.opts.companionCookiesRule || 'same-origin', | ||
})) | ||
.then(this.#getPostResponseFunc(skipPostResponse)) | ||
.then(handleJSONResponse) | ||
.catch(this.#errorHandler(method, path)) | ||
body: data ? JSON.stringify(data) : null, | ||
}) | ||
if (!skipPostResponse) this.onReceiveResponse(response) | ||
return handleJSONResponse(response) | ||
} catch (err) { | ||
if (err?.isAuthError) throw err | ||
throw new ErrorWithCause(`Could not ${method} ${this.#getUrl(path)}`, { cause: err }) | ||
} | ||
} | ||
post (path, data, skipPostResponse) { | ||
const method = 'post' | ||
return this.preflightAndHeaders(path) | ||
.then((headers) => fetchWithNetworkError(this.#getUrl(path), { | ||
method, | ||
headers, | ||
credentials: this.opts.companionCookiesRule || 'same-origin', | ||
body: JSON.stringify(data), | ||
})) | ||
.then(this.#getPostResponseFunc(skipPostResponse)) | ||
.then(handleJSONResponse) | ||
.catch(this.#errorHandler(method, path)) | ||
async get (path, options = undefined) { | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { skipPostResponse: options } | ||
return this.#request({ ...options, 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(this.#getPostResponseFunc(skipPostResponse)) | ||
.then(handleJSONResponse) | ||
.catch(this.#errorHandler(method, path)) | ||
async post (path, data, options = undefined) { | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { skipPostResponse: options } | ||
return this.#request({ ...options, path, method: 'POST', data }) | ||
} | ||
async delete (path, data = undefined, options) { | ||
// TODO: remove boolean support for options that was added for backward compatibility. | ||
// eslint-disable-next-line no-param-reassign | ||
if (typeof options === 'boolean') options = { skipPostResponse: options } | ||
return this.#request({ ...options, path, method: 'DELETE', data }) | ||
} | ||
} |
@@ -13,16 +13,36 @@ import type { Uppy } from '@uppy/core' | ||
type CompanionHeaders = Record<string, string> | ||
export interface RequestClientOptions { | ||
companionUrl: string | ||
companionHeaders?: Record<string, unknown> | ||
companionHeaders?: CompanionHeaders | ||
companionCookiesRule?: RequestCredentials | ||
} | ||
type RequestOptions = { | ||
skipPostResponse?: boolean, | ||
signal?: AbortSignal, | ||
} | ||
export class RequestClient { | ||
constructor (uppy: Uppy, opts: RequestClientOptions) | ||
get (path: string): Promise<any> | ||
readonly hostname: string | ||
post (path: string, data: Record<string, unknown>): Promise<any> | ||
setCompanionHeaders(headers: CompanionHeaders): void | ||
delete (path: string, data: Record<string, unknown>): Promise<any> | ||
get<T = unknown> (path: string, options?: RequestOptions): Promise<T> | ||
/** @deprecated use option bag instead */ | ||
get<T = unknown> (path: string, skipPostResponse: boolean): Promise<T> | ||
post<T = unknown> (path: string, data: Record<string, unknown>, options?: RequestOptions): Promise<T> | ||
/** @deprecated use option bag instead */ | ||
post<T = unknown> (path: string, data: Record<string, unknown>, skipPostResponse: boolean): Promise<T> | ||
delete<T = unknown> (path: string, data?: Record<string, unknown>, options?: RequestOptions): Promise<T> | ||
/** @deprecated use option bag instead */ | ||
delete<T = unknown> (path: string, data: Record<string, unknown>, skipPostResponse: boolean): Promise<T> | ||
} | ||
@@ -29,0 +49,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
78681
1112
Updated@uppy/utils@^5.1.0