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

api-reach

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

api-reach - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

dist/__index.js

4

CHANGELOG.md

@@ -6,2 +6,6 @@ All notable changes to this project will be documented in this file.

## [0.7.0] - 2020-03-14
### Fixed
- support for both native ESM and CommonJS fallback
## [0.6.0] - 2020-03-01

@@ -8,0 +12,0 @@ ### Added

17

dist/errors.js

@@ -8,8 +8,6 @@ "use strict";

var _dist = _interopRequireDefault(require("better-custom-error/dist"));
var _betterCustomError = _interopRequireDefault(require("better-custom-error"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const createError = _dist.default.default || _dist.default; // to let it work as bare es modules and common js with babel
/**

@@ -30,4 +28,3 @@ * Base/generic error type

*/
const HttpError = createError("HttpError");
const HttpError = (0, _betterCustomError.default)("HttpError");
/**

@@ -42,3 +39,3 @@ * Response 4xx error

exports.HttpError = HttpError;
const ClientHttpError = createError("ClientHttpError", HttpError);
const ClientHttpError = (0, _betterCustomError.default)("ClientHttpError", HttpError);
/**

@@ -53,3 +50,3 @@ * Response 5xx error

exports.ClientHttpError = ClientHttpError;
const ServerHttpError = createError("ServerHttpError", HttpError);
const ServerHttpError = (0, _betterCustomError.default)("ServerHttpError", HttpError);
/**

@@ -65,3 +62,3 @@ * Response timeout error

exports.ServerHttpError = ServerHttpError;
const TimeoutHttpError = createError("TimeoutHttpError", ServerHttpError);
const TimeoutHttpError = (0, _betterCustomError.default)("TimeoutHttpError", ServerHttpError);
/**

@@ -77,3 +74,3 @@ * Response aborted error

exports.TimeoutHttpError = TimeoutHttpError;
const AbortedHttpError = createError("AbortedHttpError", ClientHttpError);
const AbortedHttpError = (0, _betterCustomError.default)("AbortedHttpError", ClientHttpError);
/**

@@ -87,3 +84,3 @@ * Response data type was different than expected

exports.AbortedHttpError = AbortedHttpError;
const ResponseDataTypeMismatchError = createError("ResponseDataTypeMismatchError");
const ResponseDataTypeMismatchError = (0, _betterCustomError.default)("ResponseDataTypeMismatchError");
exports.ResponseDataTypeMismatchError = ResponseDataTypeMismatchError;

@@ -1,495 +0,6 @@

"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _lightIsomorphicFetch = _interopRequireDefault(require("light-isomorphic-fetch"));
var _qs = _interopRequireDefault(require("qs"));
var _urlJoin = _interopRequireDefault(require("url-join"));
var _isomorphicAbortController = _interopRequireDefault(require("isomorphic-abort-controller"));
var _Timeout = _interopRequireDefault(require("oop-timers/src/Timeout"));
var _errors = require("./errors");
var _response = _interopRequireDefault(require("./response"));
var _matchStatus = require("./response/matchStatus");
var _request = _interopRequireDefault(require("./request"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// eslint-disable-line max-lines
const stringify = _qs.default.stringify;
// Types:
// text
// json
// raw (binary)
// stream
/**
* @typedef {Object} ApiOptions
* @property {string} type - expected data type
* @property {Object} headers - headers to be send with each request
* @property {number} retry - how many times should request try to get a successful response. Can be overridden with
* retryPolicy. 1 = no retry, 2 = one retry, etc.
* @property {number} retryInterval - time between retries. Can be overriden with retryWaitPolicy
* @property {function} retryPolicy - function that decides if request should be retried
* @property {function} retryWaitPolicy - function that decides how much time to wait before next retry
* @property {number} timeout - timeout for each request
* @property {number} totalTimeout - total timeout in which all, including retried requests should be fulfilled
* (this includes wait time between, so timeout=100, wait=200, totalTimeout=350 means that retry will have only 50ms)
*/
const contentTypeMap = {
json: "application/json; charset=utf-8",
text: "application/x-www-form-urlencoded"
}; // const optionsWhitelist = [
// "method",
// "mode",
// "cache",
// "credentials",
// "headers",
// "redirect",
// "referrer",
// "body",
// ];
let URLParser = URL;
const safeUrlParse = url => {
try {
return new URLParser(url);
} catch (e) {
// eslint-disable-line no-unused-vars
return null;
}
};
const globalOptions = {
// eslint-disable-line object-shorthand
retry: 1,
retryInterval: 100,
retryPolicy({
count
}) {
return count <= this.retry;
},
retryWaitPolicy() {
return this.retryInterval;
},
timeout: 30000,
totalTimeout: 60000
};
const wait = time => new Promise(resolve => setTimeout(resolve, time));
const noop = () => undefined;
const createAbortError = ({
isTimeouted,
isGlobalTimeouted,
lastError,
errorDetails
}) => {
const useTimeoutError = isTimeouted || isGlobalTimeouted;
if (useTimeoutError) {
return new _errors.TimeoutHttpError(`Request aborted because of timeout`, lastError, errorDetails);
}
return new _errors.AbortedHttpError(`Request aborted`, lastError, errorDetails);
};
class ApiClient {
/**
* @class ApiClient
* @param {ApiOptions} options - options that will override defaults
*/
constructor(options) {
// @todo validate them?
this._options = options || {};
}
_getType(options) {
return options.type || this._options.type || "json"; // @todo do not hardcode type here
}
_getContentType(options) {
const type = this._getType(options);
return contentTypeMap[type]; // @todo handle unknown type
}
_getBody(options, body) {
const type = this._getType(options);
if (type === "json") {
return JSON.stringify(body);
}
if (type === "text") {
if (typeof body === "string") {
return body;
}
return stringify(body);
}
return ""; // @todo throw?
}
_buildFetchOptions(options, body) {
const globalHeaders = this._options.headers;
const localHeaders = options.headers;
const contentType = {};
const bodyOptions = {};
if (body != null) {
contentType["Content-Type"] = this._getContentType(options);
bodyOptions.body = this._getBody(options, body);
}
return { // @todo filter only known options
...globalOptions,
...this._options,
...options,
...bodyOptions,
headers: { ...globalHeaders,
// @todo handle same header but with different case
...localHeaders,
// @todo handle multiple headers
...contentType
}
};
}
_buildUrlBase(url, base) {
const parsedBase = safeUrlParse(base);
if (!parsedBase || !parsedBase.host) {
// @todo throw an Error ?
return url;
}
const parsedUrl = safeUrlParse(url);
if (parsedUrl && parsedUrl.base) {
// base is valid full url and given url is also full url
throw new Error("Cannot use absolute url with base url."); // @todo throw custom type?
}
return (0, _urlJoin.default)(base, url);
}
_buildUrl(originalUrl, queryParams, fetchOptions) {
const url = this._buildUrlBase(originalUrl, fetchOptions.base);
if (!queryParams) {
return url;
}
const hasQS = url.includes("?");
const appendChar = hasQS ? "&" : "?"; // @todo extract existing query params from string and include for stringify ?
// @todo add support for string query params
return url + appendChar + stringify(queryParams);
}
/**
* Sends a GET request
*
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
get(url, queryParams, options) {
return this.request("GET", url, queryParams, null, options);
}
/**
* Sends a POST request
*
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {string|Object} [body] - request body. Used as-is when string or stringified according to given data
* `type` when Object
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
post(url, queryParams, body, options) {
return this.request("POST", url, queryParams, body, options);
}
/**
* Sends a PATCH request
*
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {string|Object} [body] - request body. Used as-is when string or stringified according to given data
* `type` when Object
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
patch(url, queryParams, body, options) {
return this.request("PATCH", url, queryParams, body, options);
}
/**
* Sends a DELETE request
*
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {string|Object} [body] - request body. Used as-is when string or stringified according to given data
* `type` when Object
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
delete(url, queryParams, body, options) {
return this.request("DELETE", url, queryParams, body, options);
}
/**
* Sends a HEAD request
*
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {string|Object} [body] - request body. Used as-is when string or stringified according to given data
* `type` when Object
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
head(url, queryParams, body, options) {
return this.request("HEAD", url, queryParams, body, options);
}
/**
* Sends a custom method request
*
* @param {string} method - method to use
* @param {string} url - absolute url or relative that will be joined with base url
* @param {Object|null} [queryParams] - query params that will be added to `url`
* @param {string|Object} [body] - request body. Used as-is when string or stringified according to given data
* `type` when Object
* @param {ApiOptions} [options] - options that will override defaults and options specified in the constructor
* @returns {Promise<Response>}
* @throws {ClientHttpError}
* @throws {ServerHttpError}
* @throws {ResponseDataTypeMismatchError}
* @throws {AbortedHttpError}
* @throws {TimeoutHttpError}
* @throws {Error}
*/
request(method, url, queryParams, body, options = {}) {
// eslint-disable-line max-lines-per-function
const start = Date.now();
const fineOptions = this._buildFetchOptions(options || {}, body);
let currentController,
globalTimeout,
aborted = false,
isGlobalTimeouted = false;
const globalBreak = () => {
isGlobalTimeouted = true;
future.abort(); // eslint-disable-line no-use-before-define
};
const future = new Promise((resolve, reject) => {
// eslint-disable-line max-lines-per-function
let count = 0,
lastError = null;
return (async () => {
// eslint-disable-line max-statements, max-lines-per-function
while (fineOptions.retryPolicy({
count: ++count
})) {
let isTimeouted = false;
currentController = new _isomorphicAbortController.default();
const singleTimeout = new _Timeout.default(() => {
// eslint-disable-line no-loop-func
isTimeouted = true;
currentController.abort();
}, fineOptions.timeout);
try {
if (count > 1) {
const waitTime = fineOptions.retryWaitPolicy({
count
});
if (!globalTimeout || fineOptions.totalTimeout > Date.now() - start + waitTime) {
await wait(waitTime);
} else {
globalTimeout.stop();
globalBreak();
}
}
if (aborted) {
const errorDetails = {
tries: count - 1,
while: "waiting",
timeout: isTimeouted,
globalTimeout: isGlobalTimeouted
};
lastError = createAbortError({
isTimeouted,
isGlobalTimeouted,
lastError,
errorDetails
});
break;
}
singleTimeout.start();
return await this._request(method, url, queryParams, fineOptions, currentController.signal);
} catch (e) {
if (e.name === "AbortError") {
const errorDetails = {
tries: count,
while: "connection",
timeout: isTimeouted,
globalTimeout: isGlobalTimeouted
};
lastError = createAbortError({
isTimeouted,
isGlobalTimeouted,
lastError,
errorDetails
}); // it should not try again if:
// globally timeouted
// aborted by user (abort didn't happened via timeout)
if (isGlobalTimeouted || !isTimeouted) {
break;
}
continue;
}
lastError = e;
} finally {
singleTimeout.stop();
}
}
throw lastError ? lastError : new Error("No error thrown"); // @todo what to do if no error saved?
})().then(resolve, reject);
});
future.finally(() => {
globalTimeout && globalTimeout.stop();
}).catch(noop); // noop is required here, as finally is creating new promise branch and throws if errors occurs
// and each branch should handle error separately (even if that is the same error)
future.abort = () => {
aborted = true;
currentController && currentController.abort();
};
if (fineOptions.totalTimeout > 0 && Number.isFinite(fineOptions.totalTimeout)) {
globalTimeout = new _Timeout.default(globalBreak, fineOptions.totalTimeout, true);
}
return future;
}
async _request(method, originalUrl, queryParams, options, signal) {
const fetchOptions = { ...options,
method: method.toUpperCase()
};
const url = this._buildUrl(originalUrl, queryParams, fetchOptions);
const request = new _request.default(url, fetchOptions, originalUrl, queryParams);
const result = await (0, _lightIsomorphicFetch.default)(request.url, { ...request.options,
signal
});
const type = this._getType(options || {});
const response = await (0, _response.default)(result, type, request);
if ("rawBody" in response) {
throw new _errors.ResponseDataTypeMismatchError("Unexpected type of data received", {
response: response,
expectedType: type
});
}
if ((0, _matchStatus.isClientError)(result.status)) {
throw new _errors.ClientHttpError(result.statusText, {
response
});
}
if ((0, _matchStatus.isServerError)(result.status)) {
throw new _errors.ServerHttpError(result.statusText, {
response
});
}
return response;
}
}
/**
* Sets global ApiClient configuration that is shared between instances
*
* @param {Object} options
* @param {function} options.URL - `URL`-compatible URL parser, see: https://is.gd/Wbyu4k and https://is.gd/FziUWo
*/
ApiClient.configure = options => {
URLParser = options.URL;
};
var _default = ApiClient;
exports.default = _default;
const m = require("./__index.js");
const def = m.default;
module.exports = def || {};
Object.keys(m).filter(key => key !== "default").forEach(key => {
module.exports[key] = m[key];
});
{
"name": "api-reach",
"version": "0.6.0",
"version": "0.7.0",
"repository": "https://github.com/dzek69/api-reach.git",
"author": "Jacek Nowacki @dzek69 <git-public@dzek.eu>",
"license": "MIT",
"main": "src/index.mjs",
"scripts": {
"test": "cross-env NODE_ENV=testing mocha 'src/**/*.spec.js'",
"docs": "node build-scripts/docs && jsdoc -r src README.md -t node_modules/docdash -d ./docs -u ./tutorials -c jsdoc.json && node build-scripts/docs.after",
"transpile": "node build-scripts/transpile && babel src -d dist --ignore **/*.spec.js",
"prepublishOnly": "npm run lint && npm run test && npm run docs",
"prepack": "npm run transpile",
"lint": "cross-env eslint --report-unused-disable-directives 'src/**/*.js' 'src/*.js' 'src/**/*.mjs' 'src/*.mjs'",
"lint:fix": "npm run lint -- --fix"
"test": "cross-env NODE_ENV=testing mocha --require ./test/bootstrap.cjs 'src/**/*.spec.mjs'",
"docs": "node build-scripts/docs.mjs && jsdoc -r src README.md -t node_modules/docdash -d ./docs -u ./tutorials -c jsdoc.json && node build-scripts/docs.after.mjs",
"transpile": "node build-scripts/transpile.mjs && babel src -d dist --ignore **/*.spec.mjs && node build-scripts/transpile.after.mjs",
"prepublishOnly": "yarn lint && yarn test && yarn docs",
"prepack": "yarn transpile",
"lint": "cross-env eslint --report-unused-disable-directives 'src/**/*.mjs' 'src/*.mjs'",
"lint:fix": "yarn lint --fix"
},
"main": "dist/index.js",
"module": "src/index.mjs",
"exports": {
".": {
"require": "./dist/index.js",
"default": "./src/index.mjs"
}
},
"devDependencies": {
"@babel/cli": "^7.5.5",
"@babel/core": "^7.5.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.5.5",
"@babel/register": "^7.5.5",
"@babel/cli": "^7.8.4",
"@babel/core": "^7.8.7",
"@babel/polyfill": "^7.8.7",
"@babel/preset-env": "^7.8.7",
"@babel/register": "^7.8.6",
"@dzek69/eslint-config-base": "^1.0.1",
"babel-plugin-rewire": "^1.2.0",
"cross-env": "^5.2.0",
"docdash": "^1.1.1",
"eslint": "^6.2.2",
"fs-extra": "^7.0.1",
"husky": "^3.0.4",
"cross-env": "^7.0.2",
"docdash": "^1.2.0",
"eslint": "^6.8.0",
"fs-extra": "^8.1.0",
"husky": "^4.2.3",
"jsdoc": "^3.6.3",
"mocha": "^6.2.0",
"mocha": "^6.2.2",
"must": "^0.13.4",
"node-fetch": "^2.3.0"
"node-fetch": "^2.3.0",
"babel-plugin-module-extension": "^0.1.1"
},
"dependencies": {
"abort-controller": "^2.0.2",
"better-custom-error": "^1.0.0",
"better-custom-error": "^3.0.1",
"isomorphic-abort-controller": "^1.0.0",
"light-isomorphic-fetch": "^1.0.0",
"oop-timers": "^1.0.0",
"oop-timers": "^3.0.1",
"qs": "^6.6.0",

@@ -53,4 +61,5 @@ "url-join": "^4.0.0"

"libraryTemplate": {
"version": "1.9.0"
"version": "2.0.5",
"fixDefaultForCommonJS": true
}
}

@@ -19,3 +19,2 @@ # api-reach

- add files upload support
- add binary support
- handle multiple same header

@@ -22,0 +21,0 @@ - handle same header, different letters case

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

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

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