Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

balena-request

Package Overview
Dependencies
Maintainers
1
Versions
177
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

balena-request - npm Package Compare versions

Comparing version 11.0.0-11-x-e4271637472eba7072f79301f00c6c2e5ff87f20 to 11.0.0-11-x-fc5adc4fbc8d4f9d7edeeab4a72f2838773c029d

54

build/progress.js

@@ -17,2 +17,11 @@ "use strict";

*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -35,3 +44,3 @@ exports.estimate = void 0;

* @param {Function} [onState] - on state callback (state)
* @returns {ReadableStream} progress stream
* @returns {NodeJS.ReadableStream} progress stream
*

@@ -69,3 +78,3 @@ * @example

*
* @returns {() => Promise<ReadableStream>} request stream
* @returns {(options) => Promise<NodeJS.ReadableStream>} request stream
*

@@ -80,24 +89,25 @@ * @example

return function (options) {
if (requestAsync == null) {
requestAsync = utils.getRequestAsync();
}
options.gzip = false;
options.headers['Accept-Encoding'] = 'gzip, deflate';
let reader = null;
if (options.signal != null) {
options.signal.addEventListener('abort', function () {
// We need to react to Abort events at this level, because otherwise our
// reader locks the stream and lower-level cancellation causes error.
if (reader) {
reader.cancel().catch(function () {
// ignore
});
return reader.releaseLock();
}
}, { once: true });
}
return requestAsync(options).then(function (response) {
return __awaiter(this, void 0, void 0, function* () {
if (requestAsync == null) {
requestAsync = utils.getRequestAsync();
}
options.gzip = false;
options.headers['Accept-Encoding'] = 'gzip, deflate';
let reader = null;
if (options.signal != null) {
options.signal.addEventListener('abort', function () {
// We need to react to Abort events at this level, because otherwise our
// reader locks the stream and lower-level cancellation causes error.
if (reader) {
reader.cancel().catch(function () {
// ignore
});
return reader.releaseLock();
}
}, { once: true });
}
const response = yield requestAsync(options);
let responseStream;
const output = new stream.PassThrough();
// @ts-ignore
// @ts-expect-error
output.response = response;

@@ -104,0 +114,0 @@ const responseLength = utils.getResponseLength(response);

"use strict";
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS201: Simplify complex destructure assignments
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/*
Copyright 2016-2020 Balena Ltd.

@@ -24,2 +17,11 @@

*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -29,5 +31,3 @@ /**

*/
const Promise = require("bluebird");
const urlLib = require("url");
const rindle = require("rindle");
const fetchReadableStream = require("fetch-readablestream");

@@ -55,41 +55,33 @@ const errors = require("balena-errors");

const prepareOptions = function (options) {
if (options == null) {
options = {};
}
options = Object.assign({
method: 'GET',
json: true,
strictSSL: true,
headers: {},
sendToken: true,
refreshToken: true,
retries,
}, options);
if (options.uri) {
options.url = options.uri;
delete options.uri;
}
if (urlLib.parse(options.url).protocol != null) {
delete options.baseUrl;
}
return Promise.try(function () {
return __awaiter(this, void 0, void 0, function* () {
if (options == null) {
options = {};
}
options = Object.assign({
method: 'GET',
json: true,
strictSSL: true,
headers: {},
sendToken: true,
refreshToken: true,
retries,
}, options);
if (options.uri) {
options.url = options.uri;
delete options.uri;
}
if (urlLib.parse(options.url).protocol != null) {
delete options.baseUrl;
}
// Only refresh if we have balena-auth, we're going to use it to send a
// token, and we haven't opted out of refresh
if (!(auth != null && options.sendToken && options.refreshToken)) {
return;
}
return utils.shouldRefreshKey(auth).then(function (shouldRefreshKey) {
if (!shouldRefreshKey) {
return;
if (auth != null && options.sendToken && options.refreshToken) {
const shouldRefreshKey = yield utils.shouldRefreshKey(auth);
if (shouldRefreshKey) {
yield exports.refreshToken(options);
}
return exports.refreshToken(options);
});
})
.then(function () {
if (options.sendToken) {
// @ts-expect-error
return utils.getAuthorizationHeader(auth);
}
})
.then(function (authorizationHeader) {
const authorizationHeader = options.sendToken
? yield utils.getAuthorizationHeader(auth)
: undefined;
if (authorizationHeader != null) {

@@ -115,15 +107,6 @@ options.headers.Authorization = authorizationHeader;

const interceptResponseError = (responseError) => interceptResponseOrError(Promise.reject(responseError));
var interceptRequestOrError = (initialPromise) => Promise.resolve(exports.interceptors.reduce(function (promise, { request, requestError }) {
if (request != null || requestError != null) {
return promise.then(request, requestError);
}
else {
return promise;
}
}, initialPromise));
var interceptResponseOrError = function (initialPromise) {
interceptors = exports.interceptors.slice().reverse();
return Promise.resolve(interceptors.reduce(function (promise, { response, responseError }) {
if (response != null || responseError != null) {
return promise.then(response, responseError);
var interceptRequestOrError = (initialPromise) => __awaiter(this, void 0, void 0, function* () {
return exports.interceptors.reduce(function (promise, { request, requestError }) {
if (request != null || requestError != null) {
return promise.then(request, requestError);
}

@@ -133,3 +116,16 @@ else {

}
}, initialPromise));
}, initialPromise);
});
var interceptResponseOrError = function (initialPromise) {
return __awaiter(this, void 0, void 0, function* () {
interceptors = exports.interceptors.slice().reverse();
return interceptors.reduce(function (promise, { response, responseError }) {
if (response != null || responseError != null) {
return promise.then(response, responseError);
}
else {
return promise;
}
}, initialPromise);
});
};

@@ -178,24 +174,31 @@ /**

exports.send = function (options) {
// Only set the default timeout when doing a normal HTTP
// request and not also when streaming since in the latter
// case we might cause unnecessary ESOCKETTIMEDOUT errors.
if (options.timeout == null) {
options.timeout = 59000;
}
return prepareOptions(options)
.then(interceptRequestOptions, interceptRequestError)
.then((opts) => requestAsync(opts).catch(function (error) {
error.requestOptions = opts;
throw error;
}))
.then((response) => utils.getBody(response, options.responseFormat).then(function (body) {
response = Object.assign({}, response, { body });
if (utils.isErrorCode(response.statusCode)) {
const responseError = utils.getErrorMessageFromResponse(response);
debugRequest(options, response);
throw new errors.BalenaRequestError(responseError, response.statusCode, options);
return __awaiter(this, void 0, void 0, function* () {
// Only set the default timeout when doing a normal HTTP
// request and not also when streaming since in the latter
// case we might cause unnecessary ESOCKETTIMEDOUT errors.
if (options.timeout == null) {
options.timeout = 59000;
}
return response;
}))
.then(interceptResponse, interceptResponseError);
return prepareOptions(options)
.then(interceptRequestOptions, interceptRequestError)
.then((opts) => __awaiter(this, void 0, void 0, function* () {
let response;
try {
response = yield requestAsync(opts);
}
catch (err) {
err.requestOptions = opts;
throw err;
}
const body = yield utils.getBody(response, options.responseFormat);
response = Object.assign(Object.assign({}, response), { body });
if (utils.isErrorCode(response.statusCode)) {
const responseError = utils.getErrorMessageFromResponse(response);
debugRequest(options, response);
throw new errors.BalenaRequestError(responseError, response.statusCode, options);
}
return response;
}))
.then(interceptResponse, interceptResponseError);
});
};

@@ -227,3 +230,3 @@ /**

*
* @returns {Promise<ReadableStream>} response
* @returns {Promise<NodeJS.ReadableStream>} response
*

@@ -245,4 +248,4 @@ * @example

.then(interceptRequestOptions, interceptRequestError)
.then(progress.estimate(requestStream, isBrowser))
.then(function (download) {
.then((opts) => __awaiter(this, void 0, void 0, function* () {
const download = yield progress.estimate(requestStream, isBrowser)(opts);
// @ts-expect-error

@@ -255,14 +258,21 @@ if (!utils.isErrorCode(download.response.statusCode)) {

}
// If status code is an error code, interpret
// the body of the request as an error.
return rindle.extract(download).then(function (data) {
const responseError = data || 'The request was unsuccessful';
// @ts-expect-error
debugRequest(options, download.response);
// @ts-expect-error
throw new errors.BalenaRequestError(responseError,
// @ts-expect-error
download.response.statusCode);
// If status code is an error code, interpret the body of the request as an error.
const chunks = [];
download.on('data', function (chunk) {
chunks.push(chunk);
});
})
yield new Promise((resolve, reject) => {
download.on('error', reject);
download.on('close', resolve);
download.on('end', resolve);
download.on('done', resolve);
});
const responseError = chunks.join() || 'The request was unsuccessful';
// @ts-expect-error
debugRequest(options, download.response);
// @ts-expect-error
throw new errors.BalenaRequestError(responseError,
// @ts-expect-error
download.response.statusCode);
}))
.then(interceptResponse, interceptResponseError);

@@ -328,3 +338,3 @@ };

*
* @returns {String} token - new token
* @returns {Promise<String>} token - new token
*

@@ -336,25 +346,29 @@ * @example

exports.refreshToken = function ({ baseUrl }) {
// Only refresh if we have balena-auth
if (auth == null) {
throw new Error('Auth module not provided in initializer');
}
return exports
.send({
url: '/whoami',
baseUrl,
refreshToken: false,
})
.catch({
code: 'BalenaRequestError',
statusCode: 401,
}, () => auth
.getKey()
.tap(auth.removeKey)
.then(function (key) {
throw new errors.BalenaExpiredToken(key);
}))
.get('body')
.tap(auth.setKey);
return __awaiter(this, void 0, void 0, function* () {
// Only refresh if we have balena-auth
if (auth == null) {
throw new Error('Auth module not provided in initializer');
}
let response;
try {
response = yield exports.send({
url: '/whoami',
baseUrl,
refreshToken: false,
});
}
catch (err) {
if (err.code === 'BalenaRequestError' && err.statusCode === 401) {
const expiredKey = yield auth.getKey();
yield auth.removeKey();
throw new errors.BalenaExpiredToken(expiredKey);
}
throw err;
}
const refreshedKey = response.body;
yield auth.setKey(refreshedKey);
return refreshedKey;
});
};
return exports;
};
"use strict";
/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS205: Consider reworking code to avoid use of IIFEs
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/*
Copyright 2016-2020 Balena Ltd.

@@ -25,5 +17,13 @@

*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getRequestAsync = exports.getBody = exports.debugRequest = exports.getResponseLength = exports.isResponseCompressed = exports.isErrorCode = exports.getErrorMessageFromResponse = exports.getAuthorizationHeader = exports.shouldRefreshKey = exports.TOKEN_REFRESH_INTERVAL = void 0;
const Promise = require("bluebird");
const { fetch: normalFetch, Headers: HeadersPonyfill, } = require('fetch-ponyfill')({ Promise });

@@ -57,14 +57,14 @@ const urlLib = require("url");

function shouldRefreshKey(auth) {
return auth.hasKey().then(function (hasKey) {
return __awaiter(this, void 0, void 0, function* () {
const hasKey = yield auth.hasKey();
if (!hasKey) {
return false;
}
return auth.getType().then(function (type) {
if (type !== token_1.TokenType.JWT) {
return false;
}
return auth.getAge().then((age) =>
// @ts-ignore
age >= exports.TOKEN_REFRESH_INTERVAL);
});
const type = yield auth.getType();
if (type !== token_1.TokenType.JWT) {
return false;
}
const age = yield auth.getAge();
// @ts-expect-error
return age >= exports.TOKEN_REFRESH_INTERVAL;
});

@@ -82,3 +82,3 @@ }

* @param {import('balena-auth').default} auth - an instance of `balena-auth`
* @returns {Promise<String>} authorization header
* @returns {Promise<string | undefined>} authorization header
*

@@ -90,13 +90,15 @@ * @example

*/
exports.getAuthorizationHeader = Promise.method(function (auth) {
if (auth == null) {
return;
}
return auth.hasKey().then(function (hasKey) {
exports.getAuthorizationHeader = function (auth) {
return __awaiter(this, void 0, void 0, function* () {
if (auth == null) {
return;
}
const hasKey = yield auth.hasKey();
if (!hasKey) {
return;
}
return auth.getKey().then((key) => `Bearer ${key}`);
const key = yield auth.getKey();
return `Bearer ${key}`;
});
});
};
/**

@@ -119,6 +121,7 @@ * @summary Get error message from response

function getErrorMessageFromResponse(response) {
var _a;
if (!response.body) {
return 'The request was unsuccessful';
}
const errorText = response.body.error != null ? response.body.error.text : undefined;
const errorText = (_a = response.body.error) === null || _a === void 0 ? void 0 : _a.text;
if (errorText != null) {

@@ -303,4 +306,3 @@ return errorText;

function getBody(response, responseFormat) {
// wrap in Bluebird promise for extra methods
return Promise.try(function () {
return __awaiter(this, void 0, void 0, function* () {
if (responseFormat === 'none') {

@@ -311,6 +313,3 @@ return null;

if (responseFormat === 'blob' ||
(responseFormat == null &&
(contentType != null
? contentType.includes('binary/octet-stream')
: undefined))) {
(responseFormat == null && (contentType === null || contentType === void 0 ? void 0 : contentType.includes('binary/octet-stream')))) {
// this is according to the standard

@@ -321,5 +320,5 @@ if (typeof response.blob === 'function') {

// https://github.com/bitinn/node-fetch/blob/master/lib/body.js#L66
// @ts-ignore
// @ts-expect-error
if (typeof response.buffer === 'function') {
// @ts-ignore
// @ts-expect-error
return response.buffer();

@@ -330,6 +329,3 @@ }

if (responseFormat === 'json' ||
(responseFormat == null &&
(contentType != null
? contentType.includes('application/json')
: undefined))) {
(responseFormat == null && (contentType === null || contentType === void 0 ? void 0 : contentType.includes('application/json')))) {
return response.json();

@@ -346,44 +342,53 @@ }

var requestAsync = function (fetch, options, retriesRemaining) {
const [url, opts] = Array.from(processRequestOptions(options));
if (retriesRemaining == null) {
retriesRemaining = opts.retries;
}
// When streaming, prefer using the native Headers object if available
if (fetch !== normalFetch && typeof Headers === 'function') {
// Edge's Headers(args) ctor doesn't work as expected when passed in a headers object
// from fetch-ponyfill, treating it as a plain object instead of using the iterator symbol.
// As a result when fetch-readablestream uses the native fetch on Edge, the headers sent
// to the server only contain a `map` property and not the actual headers that we want.
const nativeHeaders = new Headers();
opts.headers.forEach((value, name) => nativeHeaders.append(name, value));
opts.headers = nativeHeaders;
}
const requestTime = Date.now();
let p = fetch(url, opts);
if (opts.timeout && IS_BROWSER) {
p = p.timeout(opts.timeout);
}
p = p.then(function (response) {
if (opts.signal) {
handleAbortIfNotSupported(opts.signal, response);
return __awaiter(this, void 0, void 0, function* () {
const [url, opts] = processRequestOptions(options);
if (retriesRemaining == null) {
retriesRemaining = opts.retries;
}
const responseTime = Date.now();
response.duration = responseTime - requestTime;
response.statusCode = response.status;
response.request = {
headers: options.headers,
uri: urlLib.parse(url),
};
return response;
// When streaming, prefer using the native Headers object if available
if (fetch !== normalFetch && typeof Headers === 'function') {
// Edge's Headers(args) ctor doesn't work as expected when passed in a headers object
// from fetch-ponyfill, treating it as a plain object instead of using the iterator symbol.
// As a result when fetch-readablestream uses the native fetch on Edge, the headers sent
// to the server only contain a `map` property and not the actual headers that we want.
const nativeHeaders = new Headers();
opts.headers.forEach((value, name) => nativeHeaders.append(name, value));
opts.headers = nativeHeaders;
}
try {
const requestTime = Date.now();
let p = fetch(url, opts);
if (opts.timeout && IS_BROWSER) {
p = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('request timed out'));
}, opts.timeout);
p.then(resolve, reject);
});
}
const response = yield p;
if (opts.signal) {
handleAbortIfNotSupported(opts.signal, response);
}
const responseTime = Date.now();
response.duration = responseTime - requestTime;
response.statusCode = response.status;
response.request = {
headers: options.headers,
uri: urlLib.parse(url),
};
return response;
}
catch (err) {
if (retriesRemaining > 0) {
return yield requestAsync(fetch, options, retriesRemaining - 1);
}
throw err;
}
});
if (retriesRemaining > 0) {
return p.catch(() => requestAsync(fetch, options, retriesRemaining - 1));
}
else {
return p;
}
};
var handleAbortIfNotSupported = function (signal, response) {
const emulateAbort = (() => {
if (response.body != null ? response.body.cancel : undefined) {
var _a, _b;
if ((_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel) {
// We have an XHR-emulated stream - cancel kills the underlying XHR

@@ -395,3 +400,3 @@ // Context: https://github.com/jonnyreeves/fetch-readablestream/issues/6

}
else if (response.body != null ? response.body.destroy : undefined) {
else if ((_b = response.body) === null || _b === void 0 ? void 0 : _b.destroy) {
// We have a Node stream - destroy kills the stream, and seems to kill

@@ -398,0 +403,0 @@ // the underlying connection (hard to confirm - but it definitely stops streaming)

@@ -10,2 +10,7 @@ # Change Log

* Update fetch-ponyfill to 6.x [Pagan Gazzard]
* Remove rindle dependency [Pagan Gazzard]
* Update balena-auth to 4.x [Pagan Gazzard]
* Drop support for nodejs < 10 [Pagan Gazzard]
* Switch to returning native promises [Pagan Gazzard]
* Convert to type checked javascript [Pagan Gazzard]

@@ -12,0 +17,0 @@

@@ -35,3 +35,3 @@ /*

* @param {Function} [onState] - on state callback (state)
* @returns {ReadableStream} progress stream
* @returns {NodeJS.ReadableStream} progress stream
*

@@ -73,3 +73,3 @@ * @example

*
* @returns {() => Promise<ReadableStream>} request stream
* @returns {(options) => Promise<NodeJS.ReadableStream>} request stream
*

@@ -83,3 +83,3 @@ * @example

export function estimate(requestAsync, isBrowser) {
return function (options) {
return async function (options) {
if (requestAsync == null) {

@@ -111,46 +111,46 @@ requestAsync = utils.getRequestAsync();

return requestAsync(options).then(function (response) {
let responseStream;
const output = new stream.PassThrough();
// @ts-ignore
output.response = response;
const response = await requestAsync(options);
const responseLength = utils.getResponseLength(response);
const total = responseLength.uncompressed || responseLength.compressed;
let responseStream;
const output = new stream.PassThrough();
// @ts-expect-error
output.response = response;
if (response.body.getReader) {
// Convert browser (WHATWG) streams to Node streams
responseStream = webStreams.toNodeReadable(response.body);
reader = responseStream._reader;
} else {
responseStream = response.body;
}
const responseLength = utils.getResponseLength(response);
const total = responseLength.uncompressed || responseLength.compressed;
const progressStream = getProgressStream(total, (state) =>
output.emit('progress', state),
);
if (response.body.getReader) {
// Convert browser (WHATWG) streams to Node streams
responseStream = webStreams.toNodeReadable(response.body);
reader = responseStream._reader;
} else {
responseStream = response.body;
}
if (!isBrowser && utils.isResponseCompressed(response)) {
const gunzip = zlib.createGunzip();
const progressStream = getProgressStream(total, (state) =>
output.emit('progress', state),
);
// Uncompress after or before piping through progress
// depending on the response length available to us
if (
responseLength.compressed != null &&
responseLength.uncompressed == null
) {
responseStream.pipe(progressStream).pipe(gunzip).pipe(output);
} else {
responseStream.pipe(gunzip).pipe(progressStream).pipe(output);
}
if (!isBrowser && utils.isResponseCompressed(response)) {
const gunzip = zlib.createGunzip();
// Uncompress after or before piping through progress
// depending on the response length available to us
if (
responseLength.compressed != null &&
responseLength.uncompressed == null
) {
responseStream.pipe(progressStream).pipe(gunzip).pipe(output);
} else {
responseStream.pipe(progressStream).pipe(output);
responseStream.pipe(gunzip).pipe(progressStream).pipe(output);
}
} else {
responseStream.pipe(progressStream).pipe(output);
}
// Stream any request errors on downstream
responseStream.on('error', (e) => output.emit('error', e));
// Stream any request errors on downstream
responseStream.on('error', (e) => output.emit('error', e));
return output;
});
return output;
};
}
/*
* decaffeinate suggestions:
* DS102: Remove unnecessary code created because of implicit returns
* DS201: Simplify complex destructure assignments
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/*
Copyright 2016-2020 Balena Ltd.

@@ -28,5 +21,3 @@

import * as Promise from 'bluebird';
import * as urlLib from 'url';
import * as rindle from 'rindle';
import * as fetchReadableStream from 'fetch-readablestream';

@@ -63,3 +54,3 @@ import * as errors from 'balena-errors';

const prepareOptions = function (options) {
const prepareOptions = async function (options) {
if (options == null) {

@@ -89,41 +80,30 @@ options = {};

return Promise.try(function () {
// Only refresh if we have balena-auth, we're going to use it to send a
// token, and we haven't opted out of refresh
if (!(auth != null && options.sendToken && options.refreshToken)) {
return;
// Only refresh if we have balena-auth, we're going to use it to send a
// token, and we haven't opted out of refresh
if (auth != null && options.sendToken && options.refreshToken) {
const shouldRefreshKey = await utils.shouldRefreshKey(auth);
if (shouldRefreshKey) {
await exports.refreshToken(options);
}
}
const authorizationHeader = options.sendToken
? await utils.getAuthorizationHeader(auth)
: undefined;
return utils.shouldRefreshKey(auth).then(function (shouldRefreshKey) {
if (!shouldRefreshKey) {
return;
}
if (authorizationHeader != null) {
options.headers.Authorization = authorizationHeader;
}
return exports.refreshToken(options);
});
})
.then(function () {
if (options.sendToken) {
// @ts-expect-error
return utils.getAuthorizationHeader(auth);
}
})
.then(function (authorizationHeader) {
if (authorizationHeader != null) {
options.headers.Authorization = authorizationHeader;
}
if (typeof options.apiKey === 'string' && options.apiKey.length > 0) {
// Using `request` qs object results in dollar signs, or other
// special characters used to query our OData API, being escaped
// and thus leading to all sort of weird error.
// The workaround is to append the `apikey` query string manually
// to prevent affecting the rest of the query strings.
// See https://github.com/request/request/issues/2129
options.url += urlLib.parse(options.url).query != null ? '&' : '?';
options.url += `apikey=${options.apiKey}`;
}
if (typeof options.apiKey === 'string' && options.apiKey.length > 0) {
// Using `request` qs object results in dollar signs, or other
// special characters used to query our OData API, being escaped
// and thus leading to all sort of weird error.
// The workaround is to append the `apikey` query string manually
// to prevent affecting the rest of the query strings.
// See https://github.com/request/request/issues/2129
options.url += urlLib.parse(options.url).query != null ? '&' : '?';
options.url += `apikey=${options.apiKey}`;
}
return options;
});
return options;
};

@@ -143,28 +123,20 @@

var interceptRequestOrError = (initialPromise) =>
Promise.resolve(
exports.interceptors.reduce(function (
promise,
{ request, requestError },
) {
if (request != null || requestError != null) {
return promise.then(request, requestError);
} else {
return promise;
}
},
initialPromise),
);
var interceptRequestOrError = async (initialPromise) =>
exports.interceptors.reduce(function (promise, { request, requestError }) {
if (request != null || requestError != null) {
return promise.then(request, requestError);
} else {
return promise;
}
}, initialPromise);
var interceptResponseOrError = function (initialPromise) {
var interceptResponseOrError = async function (initialPromise) {
interceptors = exports.interceptors.slice().reverse();
return Promise.resolve(
interceptors.reduce(function (promise, { response, responseError }) {
if (response != null || responseError != null) {
return promise.then(response, responseError);
} else {
return promise;
}
}, initialPromise),
);
return interceptors.reduce(function (promise, { response, responseError }) {
if (response != null || responseError != null) {
return promise.then(response, responseError);
} else {
return promise;
}
}, initialPromise);
};

@@ -213,3 +185,3 @@

*/
exports.send = function (options) {
exports.send = async function (options) {
// Only set the default timeout when doing a normal HTTP

@@ -224,25 +196,26 @@ // request and not also when streaming since in the latter

.then(interceptRequestOptions, interceptRequestError)
.then((opts) =>
requestAsync(opts).catch(function (error) {
error.requestOptions = opts;
throw error;
}),
)
.then((response) =>
utils.getBody(response, options.responseFormat).then(function (body) {
response = Object.assign({}, response, { body });
.then(async (opts) => {
let response;
try {
response = await requestAsync(opts);
} catch (err) {
err.requestOptions = opts;
throw err;
}
if (utils.isErrorCode(response.statusCode)) {
const responseError = utils.getErrorMessageFromResponse(response);
debugRequest(options, response);
throw new errors.BalenaRequestError(
responseError,
response.statusCode,
options,
);
}
const body = await utils.getBody(response, options.responseFormat);
response = { ...response, body };
return response;
}),
)
if (utils.isErrorCode(response.statusCode)) {
const responseError = utils.getErrorMessageFromResponse(response);
debugRequest(options, response);
throw new errors.BalenaRequestError(
responseError,
response.statusCode,
options,
);
}
return response;
})
.then(interceptResponse, interceptResponseError);

@@ -276,3 +249,3 @@ };

*
* @returns {Promise<ReadableStream>} response
* @returns {Promise<NodeJS.ReadableStream>} response
*

@@ -295,4 +268,7 @@ * @example

.then(interceptRequestOptions, interceptRequestError)
.then(progress.estimate(requestStream, isBrowser))
.then(function (download) {
.then(async (opts) => {
const download = await progress.estimate(
requestStream,
isBrowser,
)(opts);
// @ts-expect-error

@@ -307,15 +283,22 @@ if (!utils.isErrorCode(download.response.statusCode)) {

// If status code is an error code, interpret
// the body of the request as an error.
return rindle.extract(download).then(function (data) {
const responseError = data || 'The request was unsuccessful';
// If status code is an error code, interpret the body of the request as an error.
const chunks = [];
download.on('data', function (chunk) {
chunks.push(chunk);
});
await new Promise((resolve, reject) => {
download.on('error', reject);
download.on('close', resolve);
download.on('end', resolve);
download.on('done', resolve);
});
const responseError = chunks.join() || 'The request was unsuccessful';
// @ts-expect-error
debugRequest(options, download.response);
// @ts-expect-error
throw new errors.BalenaRequestError(
responseError,
// @ts-expect-error
debugRequest(options, download.response);
// @ts-expect-error
throw new errors.BalenaRequestError(
responseError,
// @ts-expect-error
download.response.statusCode,
);
});
download.response.statusCode,
);
})

@@ -385,3 +368,3 @@ .then(interceptResponse, interceptResponseError);

*
* @returns {String} token - new token
* @returns {Promise<String>} token - new token
*

@@ -393,3 +376,3 @@ * @example

exports.refreshToken = function ({ baseUrl }) {
exports.refreshToken = async function ({ baseUrl }) {
// Only refresh if we have balena-auth

@@ -400,23 +383,20 @@ if (auth == null) {

return exports
.send({
let response;
try {
response = await exports.send({
url: '/whoami',
baseUrl,
refreshToken: false,
})
.catch(
{
code: 'BalenaRequestError',
statusCode: 401,
},
() =>
auth
.getKey()
.tap(auth.removeKey)
.then(function (key) {
throw new errors.BalenaExpiredToken(key);
}),
)
.get('body')
.tap(auth.setKey);
});
} catch (err) {
if (err.code === 'BalenaRequestError' && err.statusCode === 401) {
const expiredKey = await auth.getKey();
await auth.removeKey();
throw new errors.BalenaExpiredToken(expiredKey);
}
throw err;
}
const refreshedKey = response.body;
await auth.setKey(refreshedKey);
return refreshedKey;
};

@@ -423,0 +403,0 @@

/*
* decaffeinate suggestions:
* DS101: Remove unnecessary use of Array.from
* DS102: Remove unnecessary code created because of implicit returns
* DS205: Consider reworking code to avoid use of IIFEs
* DS207: Consider shorter variations of null checks
* Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
*/
/*
Copyright 2016-2020 Balena Ltd.

@@ -25,4 +17,2 @@

import * as Promise from 'bluebird';
const {

@@ -62,18 +52,14 @@ fetch: normalFetch,

*/
export function shouldRefreshKey(auth) {
return auth.hasKey().then(function (hasKey) {
if (!hasKey) {
return false;
}
return auth.getType().then(function (type) {
if (type !== TokenType.JWT) {
return false;
}
return auth.getAge().then(
(age) =>
// @ts-ignore
age >= TOKEN_REFRESH_INTERVAL,
);
});
});
export async function shouldRefreshKey(auth) {
const hasKey = await auth.hasKey();
if (!hasKey) {
return false;
}
const type = await auth.getType();
if (type !== TokenType.JWT) {
return false;
}
const age = await auth.getAge();
// @ts-expect-error
return age >= TOKEN_REFRESH_INTERVAL;
}

@@ -90,3 +76,3 @@

* @param {import('balena-auth').default} auth - an instance of `balena-auth`
* @returns {Promise<String>} authorization header
* @returns {Promise<string | undefined>} authorization header
*

@@ -98,13 +84,13 @@ * @example

*/
export let getAuthorizationHeader = Promise.method(function (auth) {
export let getAuthorizationHeader = async function (auth) {
if (auth == null) {
return;
}
return auth.hasKey().then(function (hasKey) {
if (!hasKey) {
return;
}
return auth.getKey().then((key) => `Bearer ${key}`);
});
});
const hasKey = await auth.hasKey();
if (!hasKey) {
return;
}
const key = await auth.getKey();
return `Bearer ${key}`;
};

@@ -132,4 +118,3 @@ /**

const errorText =
response.body.error != null ? response.body.error.text : undefined;
const errorText = response.body.error?.text;
if (errorText != null) {

@@ -337,52 +322,43 @@ return errorText;

*/
export function getBody(response, responseFormat) {
// wrap in Bluebird promise for extra methods
return Promise.try(function () {
if (responseFormat === 'none') {
return null;
}
export async function getBody(response, responseFormat) {
if (responseFormat === 'none') {
return null;
}
const contentType = response.headers.get('Content-Type');
const contentType = response.headers.get('Content-Type');
if (
responseFormat === 'blob' ||
(responseFormat == null &&
(contentType != null
? contentType.includes('binary/octet-stream')
: undefined))
) {
// this is according to the standard
if (typeof response.blob === 'function') {
return response.blob();
}
// https://github.com/bitinn/node-fetch/blob/master/lib/body.js#L66
// @ts-ignore
if (typeof response.buffer === 'function') {
// @ts-ignore
return response.buffer();
}
throw new Error(
'This `fetch` implementation does not support decoding binary streams.',
);
if (
responseFormat === 'blob' ||
(responseFormat == null && contentType?.includes('binary/octet-stream'))
) {
// this is according to the standard
if (typeof response.blob === 'function') {
return response.blob();
}
if (
responseFormat === 'json' ||
(responseFormat == null &&
(contentType != null
? contentType.includes('application/json')
: undefined))
) {
return response.json();
// https://github.com/bitinn/node-fetch/blob/master/lib/body.js#L66
// @ts-expect-error
if (typeof response.buffer === 'function') {
// @ts-expect-error
return response.buffer();
}
throw new Error(
'This `fetch` implementation does not support decoding binary streams.',
);
}
if (responseFormat == null || responseFormat === 'text') {
return response.text();
}
if (
responseFormat === 'json' ||
(responseFormat == null && contentType?.includes('application/json'))
) {
return response.json();
}
throw new errors.BalenaInvalidParameterError(
'responseFormat',
responseFormat,
);
});
if (responseFormat == null || responseFormat === 'text') {
return response.text();
}
throw new errors.BalenaInvalidParameterError(
'responseFormat',
responseFormat,
);
}

@@ -392,4 +368,4 @@

var requestAsync = function (fetch, options, retriesRemaining) {
const [url, opts] = Array.from(processRequestOptions(options));
var requestAsync = async function (fetch, options, retriesRemaining) {
const [url, opts] = processRequestOptions(options);
if (retriesRemaining == null) {

@@ -410,9 +386,16 @@ retriesRemaining = opts.retries;

const requestTime = Date.now();
let p = fetch(url, opts);
if (opts.timeout && IS_BROWSER) {
p = p.timeout(opts.timeout);
}
try {
const requestTime = Date.now();
let p = fetch(url, opts);
if (opts.timeout && IS_BROWSER) {
p = new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('request timed out'));
}, opts.timeout);
p.then(resolve, reject);
});
}
p = p.then(function (response) {
const response = await p;
if (opts.signal) {

@@ -430,8 +413,7 @@ handleAbortIfNotSupported(opts.signal, response);

return response;
});
if (retriesRemaining > 0) {
return p.catch(() => requestAsync(fetch, options, retriesRemaining - 1));
} else {
return p;
} catch (err) {
if (retriesRemaining > 0) {
return await requestAsync(fetch, options, retriesRemaining - 1);
}
throw err;
}

@@ -442,3 +424,3 @@ };

const emulateAbort = (() => {
if (response.body != null ? response.body.cancel : undefined) {
if (response.body?.cancel) {
// We have an XHR-emulated stream - cancel kills the underlying XHR

@@ -450,3 +432,3 @@ // Context: https://github.com/jonnyreeves/fetch-readablestream/issues/6

});
} else if (response.body != null ? response.body.destroy : undefined) {
} else if (response.body?.destroy) {
// We have a Node stream - destroy kills the stream, and seems to kill

@@ -453,0 +435,0 @@ // the underlying connection (hard to confirm - but it definitely stops streaming)

{
"name": "balena-request",
"version": "11.0.0-11-x-e4271637472eba7072f79301f00c6c2e5ff87f20",
"version": "11.0.0-11-x-fc5adc4fbc8d4f9d7edeeab4a72f2838773c029d",
"description": "Balena HTTP client",

@@ -36,4 +36,5 @@ "main": "build/request.js",

"@balena/lint": "^5.1.0",
"balena-auth": "^3.1.1",
"coffee-script": "~1.12.7",
"balena-auth": "^4.0.0",
"bluebird": "^3.7.2",
"coffeescript": "~1.12.7",
"global-tunnel-ng": "2.1.0",

@@ -49,2 +50,3 @@ "jsdoc-to-markdown": "^6.0.1",

"resin-config-karma": "^1.0.4",
"rindle": "^1.3.6",
"temp": "^0.8.4",

@@ -58,8 +60,6 @@ "timekeeper": "^1.0.0",

"balena-errors": "^4.4.0",
"bluebird": "^3.7.2",
"fetch-ponyfill": "^4.1.0",
"fetch-ponyfill": "^6.1.1",
"fetch-readablestream": "^0.2.0",
"progress-stream": "^2.0.0",
"qs": "^6.9.4",
"rindle": "^1.3.6"
"qs": "^6.9.4"
},

@@ -66,0 +66,0 @@ "peerDependencies": {

@@ -11,3 +11,3 @@ {

"sourceMap": false,
"target": "es2018",
"target": "es2015",
"noUnusedParameters": true,

@@ -14,0 +14,0 @@ "noUnusedLocals": true,

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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