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

papi

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

papi - npm Package Compare versions

Comparing version 0.29.1 to 1.0.0-alpha.1

.github/workflows/nodejs.yml

918

lib/client.js

@@ -1,233 +0,209 @@

/**
* HTTP client.
*/
'use strict';
/**
* Module dependencies.
*/
const events = require('events');
const http = require('http');
const https = require('https');
const url = require('url');
const util = require('util');
var events = require('events');
var http = require('http');
var https = require('https');
var url = require('url');
var util = require('util');
const constants = require('./constants');
const errors = require('./errors');
const meta = require('../package.json');
const utils = require('./utils');
var constants = require('./constants');
var errors = require('./errors');
var meta = require('../package.json');
var utils = require('./utils');
/**
* Client
*/
class Client extends events.EventEmitter {
constructor(opts) {
super();
function Client(opts) {
if (!(this instanceof Client)) {
return new Client(opts);
}
opts = opts || {};
events.EventEmitter.call(this);
if (typeof opts === 'string') {
opts = { baseUrl: opts };
} else {
opts = utils.merge(opts);
}
opts = opts || {};
if (!opts.baseUrl) {
throw new errors.ValidationError('baseUrl required');
}
if (typeof opts === 'string') {
opts = { baseUrl: opts };
} else {
opts = utils.merge(opts);
}
if (!(opts.baseUrl instanceof url.Url)) {
if (typeof opts.baseUrl !== 'string') {
throw new errors.ValidationError('baseUrl must be a string: ' +
opts.baseUrl);
}
if (!opts.baseUrl) {
throw errors.Validation('baseUrl required');
}
if (!(opts.baseUrl instanceof url.Url)) {
if (typeof opts.baseUrl !== 'string') {
throw errors.Validation('baseUrl must be a string: ' + opts.baseUrl);
opts.baseUrl = url.parse(opts.baseUrl);
}
opts.baseUrl = url.parse(opts.baseUrl);
}
const path = opts.baseUrl.pathname;
opts.baseUrl = utils.pick(opts.baseUrl,
'auth', 'hostname', 'port', 'protocol');
opts.baseUrl.path = path;
var path = opts.baseUrl.pathname;
opts.baseUrl = utils.pick(opts.baseUrl,
'auth', 'hostname', 'port', 'protocol');
opts.baseUrl.path = path;
if (opts.baseUrl.path === '/') {
opts.baseUrl.path = '';
} else if (opts.baseUrl.path[opts.baseUrl.path.length - 1] === '/') {
throw new errors.ValidationError(
'baseUrl must not end with a forward slash');
}
if (opts.baseUrl.path === '/') {
opts.baseUrl.path = '';
} else if (opts.baseUrl.path[opts.baseUrl.path.length - 1] === '/') {
throw errors.Validation('baseUrl must not end with a forward slash');
}
opts.headers = utils.mergeHeaders(opts.headers);
if (opts.tags) {
if (Array.isArray(opts.tags)) {
opts.tags = opts.tags.slice(0);
opts.headers = utils.mergeHeaders(opts.headers);
if (opts.tags) {
if (Array.isArray(opts.tags)) {
opts.tags = opts.tags.slice(0);
} else {
throw new errors.ValidationError('tags must be an array');
}
} else {
throw errors.Validation('tags must be an array');
opts.tags = [];
}
} else {
opts.tags = [];
}
if (opts.name && !~opts.tags.indexOf(opts.name)) {
opts.tags.push(opts.name);
}
if (opts.name && !~opts.tags.indexOf(opts.name)) {
opts.tags.push(opts.name);
}
opts.encoders = utils.merge(constants.ENCODERS, opts.encoders);
opts.decoders = utils.merge(constants.DECODERS, opts.decoders);
opts.encoders = utils.merge(constants.ENCODERS, opts.encoders);
opts.decoders = utils.merge(constants.DECODERS, opts.decoders);
this._opts = opts;
this._exts = {};
}
util.inherits(Client, events.EventEmitter);
/**
* Add information to error
*/
Client.prototype._err = function(err, opts) {
if (!err) return err;
if (!(err instanceof Error)) err = new Error(err);
if (opts && opts.name) {
err.message = util.format('%s: %s', opts.name, err.message);
this._opts = opts;
this._exts = {};
}
if (this._opts.name) {
err.message = util.format('%s: %s', this._opts.name, err.message);
}
/**
* Add information to error
*/
_err(err, opts) {
if (!err) return err;
return err;
};
if (!(err instanceof Error)) err = new Error(err);
/**
* Register an extension
*/
if (opts && opts.name) {
err.message = util.format('%s: %s', opts.name, err.message);
}
Client.prototype._ext = function(eventName, callback) {
if (!eventName || typeof eventName !== 'string') {
throw this._err(errors.Validation('extension eventName required'));
}
if (this._opts.name) {
err.message = util.format('%s: %s', this._opts.name, err.message);
}
if (typeof callback !== 'function') {
throw this._err(errors.Validation('extension callback required'));
return err;
}
if (!this._exts[eventName]) this._exts[eventName] = [];
/**
* Register an extension
*/
_ext(eventName, callback) {
if (!eventName || typeof eventName !== 'string') {
throw this._err(new errors.ValidationError(
'extension eventName required'));
}
this._exts[eventName].push(callback);
};
if (typeof callback !== 'function') {
throw this._err(new errors.ValidationError(
'extension callback required'));
}
/**
* Register a plugin
*/
if (!this._exts[eventName]) this._exts[eventName] = [];
Client.prototype._plugin = function(plugin, options) {
if (!plugin) {
throw this._err(errors.Validation('plugin required'));
this._exts[eventName].push(callback);
}
if (typeof plugin.register !== 'function') {
throw this._err(errors.Validation('plugin must have register function'));
}
/**
* Register a plugin
*/
_plugin(plugin, options) {
if (!plugin) {
throw this._err(new errors.ValidationError('plugin required'));
}
var attributes = plugin.register.attributes;
if (typeof plugin.register !== 'function') {
throw this._err(new errors.ValidationError(
'plugin must have register function'));
}
if (!attributes) {
throw this._err(errors.Validation('plugin attributes required'));
}
const attributes = plugin.register.attributes;
if (!attributes.name) {
throw this._err(errors.Validation('plugin attributes name required'));
}
if (!attributes) {
throw this._err(new errors.ValidationError('plugin attributes required'));
}
if (!attributes.version) {
throw this._err(errors.Validation('plugin attributes version required'));
}
if (!attributes.name) {
throw this._err(new errors.ValidationError(
'plugin attributes name required'));
}
return plugin.register(this, options || {});
};
if (!attributes.version) {
throw this._err(new errors.ValidationError(
'plugin attributes version required'));
}
/**
* Log request events
*/
Client.prototype._log = function(tags, data) {
return this.emit('log', tags, data);
};
/**
* Encode
*/
Client.prototype._encode = function(mime, value) {
if (!this._opts.encoders[mime]) {
throw errors.Codec('unknown encoder: ' + mime);
return plugin.register(this, options || {});
}
try {
return this._opts.encoders[mime](value);
} catch (err) {
err.message = 'encode (' + mime + ') failed: ' + err.message;
throw errors.Codec(err);
/**
* Log request events
*/
_log(tags, data) {
return this.emit('log', tags, data);
}
};
/**
* Decode
*/
/**
* Encode
*/
_encode(mime, value) {
if (!this._opts.encoders[mime]) {
throw new errors.CodecError('unknown encoder: ' + mime);
}
Client.prototype._decode = function(mime, value) {
if (!this._opts.decoders[mime]) {
throw errors.Codec('unknown decoder: ' + mime);
try {
return this._opts.encoders[mime](value);
} catch (err) {
err.message = 'encode (' + mime + ') failed: ' + err.message;
throw new errors.CodecError(err);
}
}
try {
return this._opts.decoders[mime](value);
} catch (err) {
err.message = 'decode (' + mime + ') failed: ' + err.message;
throw errors.Codec(err);
}
};
/**
* Decode
*/
_decode(mime, value) {
if (!this._opts.decoders[mime]) {
throw new errors.CodecError('unknown decoder: ' + mime);
}
/**
* Push ext list
*/
Client.prototype.__push = function(request, name) {
if (this._exts[name]) {
request._stack.push.apply(request._stack, this._exts[name]);
try {
return this._opts.decoders[mime](value);
} catch (err) {
err.message = 'decode (' + mime + ') failed: ' + err.message;
throw new errors.CodecError(err);
}
}
if (request.opts && request.opts.exts && request.opts.exts[name]) {
if (Array.isArray(request.opts.exts[name])) {
request._stack.push.apply(request._stack, request.opts.exts[name]);
} else {
request._stack.push(request.opts.exts[name]);
/**
* Push ext list
*/
__push(request, name) {
if (this._exts[name]) {
request._stack.push.apply(request._stack, this._exts[name]);
}
if (request.opts && request.opts.exts && request.opts.exts[name]) {
if (Array.isArray(request.opts.exts[name])) {
request._stack.push.apply(request._stack, request.opts.exts[name]);
} else {
request._stack.push(request.opts.exts[name]);
}
}
}
};
/**
* Run request pipeline
*/
/**
* Run client request
*/
_request(opts) {
if (!opts) opts = {};
Client.prototype._request = function(opts) {
var self = this;
var request;
if (this.__request) {
request = this.__request;
opts = request.opts;
self = request._client;
} else {
request = {
_args: Array.prototype.slice.call(arguments),
_client: this,
const request = {
opts: opts,

@@ -237,11 +213,2 @@ state: {},

if (!opts) opts = request.opts = {};
if (request._args.length > 1) {
request._callback = request._args[request._args.length - 1];
} else {
return self.emit('error', self._err(
errors.Validation('callback required'), opts));
}
// if ctx is an event emitter we use it to abort requests when done is

@@ -254,3 +221,3 @@ // emitted

// combine global and request tags
opts.tags = (opts.tags || []).concat(self._opts.tags);
opts.tags = (opts.tags || []).concat(this._opts.tags);

@@ -267,8 +234,8 @@ // inject request name into tags if not already defined

// restart request
request.retry = function() {
request.retry = () => {
if (request._retryable === false) {
throw errors.Validation('request is not retryable');
throw new errors.ValidationError('request is not retryable');
}
self._log(['papi', 'request', 'retry'].concat(request.opts.tags));
this._log(['papi', 'request', 'retry'].concat(request.opts.tags));

@@ -281,3 +248,3 @@ delete request.body;

self._request.call({ __request: request });
this.__pipeline(request);
};

@@ -287,111 +254,109 @@

self.__push(request, 'onCreate');
this.__push(request, 'onCreate');
request._stack.push(self.__create);
request._stack.push(this.__create);
self.__push(request, 'onRequest');
this.__push(request, 'onRequest');
request._stack.push(self.__execute);
request._stack.push(this.__execute);
self.__push(request, 'onResponse');
this.__push(request, 'onResponse');
request._stack.push.apply(
request._stack,
request._args.slice(1, request._args.length - 1)
);
}
request._stack.push.apply(request._stack,
Array.prototype.slice.call(arguments, 1));
var i = 0;
function next(err) {
if (err) return request._callback(self._err(err, opts));
return new Promise((resolve, reject) => {
request._resolve = resolve;
request._reject = reject;
// middlware can call next(false, args...) to stop middleware
if (err === false) {
return request._callback.apply(null,
Array.prototype.slice.call(arguments, 1));
}
var fn = request._stack[i++];
if (fn) {
fn.call(self, request, next);
} else {
request._callback.call(self, self._err(request.err, opts), request.res);
}
this.__pipeline(request);
});
}
next();
};
/**
* Run request pipeline
*/
__pipeline(request) {
const self = this;
/**
* Create HTTP request
*/
let i = 0;
function next(err, value, maybeValue) {
if (err) return request._reject(self._err(err, request.opts));
Client.prototype.__create = function(request, next) {
var self = this;
// middlware can call next(false, value) to stop middleware
if (err === false) {
// basic backwards compatibility for old interface
if (value instanceof Error) {
return request._reject(value);
} else if (!value && maybeValue) {
value = maybeValue;
}
var opts = request.opts;
var path = opts.path;
return request._resolve(value);
}
if (typeof path !== 'string') {
return next(errors.Validation('path required'));
const fn = request._stack[i++];
if (fn) {
fn.call(self, request, next);
} else if (request.err) {
request._reject(self._err(request.err, request.opts));
} else {
request._resolve(request.res);
}
}
next();
}
var headers = utils.mergeHeaders(self._opts.headers, opts.headers);
/**
* Create HTTP request
*/
__create(request, next) {
const opts = request.opts;
let path = opts.path;
// path
try {
path = path.replace(/\{(\w+)\}/g, function(src, dst) {
if (!opts.params.hasOwnProperty(dst)) {
throw errors.Validation('missing param: ' + dst);
}
if (typeof path !== 'string') {
return next(new errors.ValidationError('path required'));
}
var part = opts.params[dst] || '';
const headers = utils.mergeHeaders(this._opts.headers, opts.headers);
// optionally disable param encoding
return part.encode === false && part.toString ?
part.toString() : encodeURIComponent(part);
});
} catch (err) {
return next(err);
}
// path
try {
path = path.replace(/\{(\w+)\}/g, (src, dst) => {
if (!opts.params.hasOwnProperty(dst)) {
throw new errors.ValidationError('missing param: ' + dst);
}
// query
if (!utils.isEmpty(opts.query)) {
try {
path += '?' + self._encode('application/x-www-form-urlencoded',
opts.query).toString();
let part = opts.params[dst] || '';
// optionally disable param encoding
return part.encode === false && part.toString ?
part.toString() : encodeURIComponent(part);
});
} catch (err) {
return next(err);
}
}
// body
if (opts.body !== undefined) {
var mime = constants.MIME_ALIAS[opts.type] ||
headers['content-type'] ||
constants.MIME_ALIAS[self._opts.type];
var isFunction = typeof opts.body === 'function';
if (isFunction) {
// query
if (!utils.isEmpty(opts.query)) {
try {
request.body = opts.body();
path += '?' + this._encode('application/x-www-form-urlencoded',
opts.query).toString();
} catch (err) {
return next(err);
}
} else {
request.body = opts.body;
}
var isBuffer = Buffer.isBuffer(request.body);
var isStream = utils.isReadableStream(request.body);
// body
if (opts.body !== undefined) {
let mime = constants.MIME_ALIAS[opts.type] ||
headers['content-type'] ||
constants.MIME_ALIAS[this._opts.type];
if (!isBuffer && !isStream && !mime) {
return next(errors.Validation('type required'));
}
let isFunction = typeof opts.body === 'function';
if (!isBuffer && !isStream) {
if (self._opts.encoders[mime]) {
if (isFunction) {
try {
request.body = this._encode(mime, request.body);
request.body = opts.body();
} catch (err) {

@@ -401,262 +366,295 @@ return next(err);

} else {
return next(errors.Codec('type is unknown: ' + mime));
request.body = opts.body;
}
}
if (!headers['content-type'] && mime) {
headers['content-type'] = mime + '; charset=' + constants.CHARSET;
}
const isBuffer = Buffer.isBuffer(request.body);
const isStream = utils.isReadableStream(request.body);
if (isStream) {
if (!isFunction) request._retryable = false;
} else {
headers['content-length'] = request.body.length;
}
} else if (!~constants.EXCLUDE_CONTENT_LENGTH.indexOf(opts.method)) {
headers['content-length'] = 0;
}
if (!isBuffer && !isStream && !mime) {
return next(new errors.ValidationError('type required'));
}
// response pipe
if (opts.pipe) {
var isPipeFunction = typeof opts.pipe === 'function';
if (!isBuffer && !isStream) {
if (this._opts.encoders[mime]) {
try {
request.body = this._encode(mime, request.body);
} catch (err) {
return next(err);
}
} else {
return next(new errors.CodecError('type is unknown: ' + mime));
}
}
if (isPipeFunction) {
try {
request.pipe = opts.pipe();
} catch (err) {
return next(err);
if (!headers['content-type'] && mime) {
headers['content-type'] = mime + '; charset=' + constants.CHARSET;
}
} else {
request.pipe = opts.pipe;
request._retryable = false;
if (isStream) {
if (!isFunction) request._retryable = false;
} else {
headers['content-length'] = request.body.length;
}
} else if (!~constants.EXCLUDE_CONTENT_LENGTH.indexOf(
(opts.method || '').toUpperCase())) {
headers['content-length'] = 0;
}
if (!utils.isWritableStream(request.pipe)) {
return next(errors.Validation('pipe must be a writable stream'));
}
}
// response pipe
if (opts.pipe) {
const isPipeFunction = typeof opts.pipe === 'function';
// build http.request options
request.req = utils.merge(
utils.pick(self._opts, constants.CLIENT_OPTIONS),
utils.pick(self._opts.baseUrl, 'auth', 'hostname', 'port', 'path'),
utils.pick(opts, constants.REQUEST_OPTIONS),
{ headers: headers }
);
if (isPipeFunction) {
try {
request.pipe = opts.pipe();
} catch (err) {
return next(err);
}
} else {
request.pipe = opts.pipe;
// append request path to baseUrl
request.req.path += path;
request._retryable = false;
}
// pick http transport
if (self._opts.baseUrl.protocol === 'https:') {
request.transport = https;
if (!request.req.port) request.req.port = 443;
} else {
request.transport = http;
if (!request.req.port) request.req.port = 80;
}
if (!utils.isWritableStream(request.pipe)) {
return next(new errors.ValidationError(
'pipe must be a writable stream'));
}
}
if (request.req.auth === null) delete request.req.auth;
// build http.request options
request.req = utils.merge(
utils.pick(this._opts, constants.CLIENT_OPTIONS),
utils.pick(this._opts.baseUrl, 'auth', 'hostname', 'port', 'path'),
utils.pick(opts, constants.REQUEST_OPTIONS),
{ headers: headers }
);
next();
};
// append request path to baseUrl
request.req.path += path;
/**
* Execute HTTP request
*/
// pick http transport
if (this._opts.baseUrl.protocol === 'https:') {
request.transport = https;
if (!request.req.port) request.req.port = 443;
} else {
request.transport = http;
if (!request.req.port) request.req.port = 80;
}
Client.prototype.__execute = function(request, next) {
var self = this;
if (request.req.auth === null) delete request.req.auth;
if (request.ctx) {
if (request.ctx.canceled === true) {
return next(errors.Validation('ctx already canceled'));
} else if (request.ctx.finished === true) {
return next(errors.Validation('ctx already finished'));
}
next();
}
var done = false;
/**
* Execute HTTP request
*/
__execute(request, next) {
if (request.ctx) {
if (request.ctx.canceled === true) {
return next(new errors.ValidationError('ctx already canceled'));
} else if (request.ctx.finished === true) {
return next(new errors.ValidationError('ctx already finished'));
}
}
var opts = request.opts;
let done = false;
var abort;
var timeoutId;
var timeout = opts.hasOwnProperty('timeout') ?
opts.timeout : self._opts.timeout;
const opts = request.opts;
self._log(['papi', 'request'].concat(opts.tags), request.req);
let abort;
let timeoutId;
let timeout = opts.hasOwnProperty('timeout') ?
opts.timeout : this._opts.timeout;
var req = request.transport.request(request.req);
this._log(['papi', 'request'].concat(opts.tags), request.req);
var userAgent = req.getHeader('user-agent');
const req = request.transport.request(request.req);
if (userAgent === undefined) {
req.setHeader('user-agent', 'papi/' + meta.version);
} else if (userAgent === null) {
req.removeHeader('user-agent');
}
const userAgent = req.getHeader('user-agent');
req.on('error', function(err) {
self._log(['papi', 'request', 'error'].concat(opts.tags), err);
if (userAgent === undefined) {
req.setHeader('user-agent', 'papi/' + meta.version);
} else if (userAgent === null) {
req.removeHeader('user-agent');
}
if (done) return;
done = true;
req.on('error', err => {
this._log(['papi', 'request', 'error'].concat(opts.tags), err);
if (abort) request.ctx.removeListener('cancel', abort);
if (timeoutId) clearTimeout(timeoutId);
if (done) return;
done = true;
request.err = err;
next();
});
if (abort) request.ctx.removeListener('cancel', abort);
if (timeoutId) clearTimeout(timeoutId);
if (request.ctx) {
abort = function() {
req.abort();
req.emit('error', errors.Abort('request aborted'));
};
request.err = err;
next();
});
request.ctx.once('cancel', abort);
}
if (request.ctx) {
abort = () => {
req.abort();
req.emit('error', new errors.AbortError('request aborted'));
};
// set request and absolute timeout
if (timeout && timeout > 0) {
timeoutId = setTimeout(function() {
req.emit('timeout');
req.abort();
}, timeout);
request.ctx.once('cancel', abort);
}
req.setTimeout(timeout);
}
// set request and absolute timeout
if (timeout && timeout > 0) {
timeoutId = setTimeout(() => {
req.emit('timeout');
req.abort();
}, timeout);
req.on('timeout', function(err) {
self._log(['papi', 'request', 'error', 'timeout'].concat(opts.tags));
if (err) {
err = errors.Timeout(err);
} else {
err = errors.Timeout('request timed out (' + timeout + 'ms)');
req.setTimeout(timeout);
}
req.emit('error', err);
});
req.on('response', function(res) {
var chunks = [];
var bodyLength = 0;
self._log(['papi', 'response'].concat(opts.tags), {
method: opts.method,
path: req.path,
statusCode: res.statusCode,
headers: res.headers,
remoteAddress: res.connection && res.connection.remoteAddress,
remotePort: res.connection && res.connection.remotePort,
req.on('timeout', err => {
this._log(['papi', 'request', 'error', 'timeout'].concat(opts.tags));
if (err) {
err = new errors.TimeoutError(err);
} else {
err = new errors.TimeoutError('request timed out (' + timeout + 'ms)');
}
req.emit('error', err);
});
request.res = res;
req.on('response', (res) => {
let chunks = [];
let bodyLength = 0;
if (request.pipe) {
res.pipe(request.pipe);
} else {
res.on('data', function(chunk) {
chunks.push(chunk);
bodyLength += chunk.length;
this._log(['papi', 'response'].concat(opts.tags), {
method: opts.method,
path: req.path,
statusCode: res.statusCode,
headers: res.headers,
remoteAddress: res.socket && res.socket.remoteAddress,
remotePort: res.socket && res.socket.remotePort,
});
}
res.on('end', function() {
if (done) return;
done = true;
request.res = res;
if (abort) request.ctx.removeListener('cancel', abort);
if (timeoutId) clearTimeout(timeoutId);
if (request.pipe) {
res.pipe(request.pipe);
} else {
res.on('data', (chunk) => {
chunks.push(chunk);
bodyLength += chunk.length;
});
}
// body content mime
var mime;
res.on('end', () => {
if (done) return;
done = true;
// decode body
if (bodyLength) {
res.body = Buffer.concat(chunks, bodyLength);
if (abort) request.ctx.removeListener('cancel', abort);
if (timeoutId) clearTimeout(timeoutId);
// don't decode if user explicitly asks for buffer
if (!opts.buffer) {
mime = (res.headers['content-type'] || '').split(';')[0].trim();
// body content mime
let mime;
if (self._opts.decoders[mime]) {
try {
res.body = self._decode(mime, res.body);
} catch (err) {
request.err = err;
return next();
// decode body
if (bodyLength) {
res.body = Buffer.concat(chunks, bodyLength);
// don't decode if user explicitly asks for buffer
if (!opts.buffer) {
mime = (res.headers['content-type'] || '').split(';')[0].trim();
if (this._opts.decoders[mime]) {
try {
res.body = this._decode(mime, res.body);
} catch (err) {
request.err = err;
return next();
}
}
}
}
}
// any non-200 is consider an error
if (Math.floor(res.statusCode / 100) !== 2) {
var err = errors.Response();
// any non-200 is consider an error
if (Math.floor(res.statusCode / 100) !== 2) {
var message;
if (res.body && mime === 'text/plain' && res.body.length < 80) {
err.message = res.body;
}
if (res.body && mime === 'text/plain' && res.body.length < 80) {
message = res.body;
}
if (!err.message) {
if (http.STATUS_CODES[res.statusCode]) {
err.message = http.STATUS_CODES[res.statusCode].toLowerCase();
} else {
err.message = 'request failed: ' + res.statusCode;
if (!message) {
if (http.STATUS_CODES[res.statusCode]) {
message = http.STATUS_CODES[res.statusCode].toLowerCase();
} else {
message = 'request failed: ' + res.statusCode;
}
}
request.err = new errors.ResponseError(message, res);
}
err.statusCode = res.statusCode;
request.err = err;
}
next();
next();
});
});
});
if (utils.isReadableStream(request.body)) {
request.body.pipe(req);
} else {
req.end(request.body);
if (utils.isReadableStream(request.body)) {
request.body.pipe(req);
} else {
req.end(request.body);
}
}
};
/**
* Shortcuts
*/
__shortcut(method, callerArgs) {
let args;
let opts = callerArgs[0];
constants.METHODS.forEach(function(method) {
var reqMethod = method.toUpperCase();
if (typeof opts === 'string') {
args = Array.prototype.slice.call(callerArgs);
args[0] = opts = { path: opts };
} else if (!opts) {
args = Array.prototype.slice.call(callerArgs);
args[0] = opts = {};
} else {
args = callerArgs;
}
Client.prototype['_' + method] = function(opts) {
var args;
opts.method = method;
if (typeof opts === 'string') {
opts = { path: opts, method: reqMethod };
return this._request.apply(this, args);
}
args = Array.prototype.slice.call(arguments);
args[0] = opts;
_options() {
return this.__shortcut('OPTIONS', arguments);
}
return this._request.apply(this, args);
} else if (!opts) {
args = Array.prototype.slice.call(arguments);
args[0] = {};
_get() {
return this.__shortcut('GET', arguments);
}
return this._request.apply(this, args);
}
_head() {
return this.__shortcut('HEAD', arguments);
}
opts.method = reqMethod;
_post() {
return this.__shortcut('POST', arguments);
}
return this._request.apply(this, arguments);
};
});
_put() {
return this.__shortcut('PUT', arguments);
}
/**
* Module exports.
*/
_delete() {
return this.__shortcut('DELETE', arguments);
}
_del() {
return this.__shortcut('DELETE', arguments);
}
_patch() {
return this.__shortcut('PATCH', arguments);
}
}
exports.Client = Client;

@@ -1,13 +0,5 @@

/**
* Encoders/Decoders
*/
'use strict';
/**
* Module dependencies.
*/
const querystring = require('querystring');
var querystring = require('querystring');
/**

@@ -17,12 +9,7 @@ * Text

var text = {};
text.encode = function(data) {
return Buffer.from(data, 'utf8');
const text = {
encode: data => Buffer.from(data, 'utf8'),
decode: data => Buffer.isBuffer(data) ? data.toString() : data,
};
text.decode = function(data) {
return Buffer.isBuffer(data) ? data.toString() : data;
};
/**

@@ -32,12 +19,7 @@ * JSON

var json = {};
json.encode = function(data) {
return text.encode(JSON.stringify(data));
const json = {
encode: data => text.encode(JSON.stringify(data)),
decode: data => JSON.parse(text.decode(data)),
};
json.decode = function(data) {
return JSON.parse(text.decode(data));
};
/**

@@ -47,18 +29,9 @@ * Form

var form = {};
form.encode = function(data) {
return text.encode(querystring.stringify(data));
const form = {
encode: data => text.encode(querystring.stringify(data)),
decode: data => querystring.parse(text.decode(data)),
};
form.decode = function(data) {
return querystring.parse(text.decode(data));
};
/**
* Module exports.
*/
exports.json = json;
exports.form = form;
exports.text = text;
'use strict';
/**
* Module dependencies.
*/
const codecs = require('./codecs');
var codecs = require('./codecs');
/**
* Constants
*/
exports.CHARSET = 'utf-8';
exports.ENCODERS = {
exports.ENCODERS = Object.freeze({
'application/json': codecs.json.encode,
'application/x-www-form-urlencoded': codecs.form.encode,
'text/plain': codecs.text.encode,
};
});
exports.DECODERS = {
exports.DECODERS = Object.freeze({
'application/json': codecs.json.decode,

@@ -27,15 +19,5 @@ 'application/x-www-form-urlencoded': codecs.form.decode,

'text/plain': codecs.text.decode,
};
});
exports.METHODS = [
'options',
'get',
'head',
'post',
'put',
'delete',
'patch',
];
exports.MIME_ALIAS = {
exports.MIME_ALIAS = Object.freeze({
form: 'application/x-www-form-urlencoded',

@@ -46,11 +28,11 @@ json: 'application/json',

text: 'text/plain',
};
});
exports.EXCLUDE_CONTENT_LENGTH = [
exports.EXCLUDE_CONTENT_LENGTH = Object.freeze([
'GET',
'HEAD',
'OPTIONS',
];
]);
exports.CLIENT_OPTIONS = [
exports.CLIENT_OPTIONS = Object.freeze([
'agent',

@@ -74,6 +56,6 @@ // tls

'sessionIdContext',
];
]);
exports.REQUEST_OPTIONS = exports.CLIENT_OPTIONS.concat([
exports.REQUEST_OPTIONS = Object.freeze(exports.CLIENT_OPTIONS.concat([
'method',
]);
]));

@@ -1,90 +0,67 @@

/**
* Errors
*/
'use strict';
/**
* Create
*/
class PapiError extends Error {
constructor(message) {
if (message instanceof Error) {
super(message.message);
this.cause = message;
} else {
super(message);
}
function create(message) {
var error = message instanceof Error ?
message :
new Error(message ? message : undefined);
error.isPapi = true;
return error;
this.isPapi = true;
}
}
/**
* Codec
*/
class CodecError extends PapiError {
constructor(message) {
super(message);
function codec(message) {
var error = create(message);
error.isCodec = true;
return error;
this.isCodec = true;
}
}
/**
* Response
*/
class ResponseError extends PapiError {
constructor(message, response) {
super(message);
function response(message) {
var error = create(message);
this.isResponse = true;
error.isResponse = true;
this.response = response;
}
return error;
get statusCode() {
return this.response.statusCode;
}
}
/**
* Abort
*/
class AbortError extends PapiError {
constructor(message) {
super(message);
function abort(message) {
var error = create(message);
error.isAbort = true;
return error;
this.isAbort = true;
}
}
/**
* Timeout
*/
class TimeoutError extends PapiError {
constructor(message) {
super(message);
function timeout(message) {
var error = create(message);
error.isTimeout = true;
return error;
this.isTimeout = true;
}
}
/**
* Validation
*/
class ValidationError extends PapiError {
constructor(message) {
super(message);
function validation(message) {
var error = create(message);
error.isValidation = true;
return error;
this.isValidation = true;
}
}
/**
* Module exports.
*/
exports.Codec = codec;
exports.Response = response;
exports.Abort = abort;
exports.Timeout = timeout;
exports.Validation = validation;
exports.create = create;
exports.PapiError = PapiError;
exports.CodecError = CodecError;
exports.ResponseError = ResponseError;
exports.AbortError = AbortError;
exports.TimeoutError = TimeoutError;
exports.ValidationError = ValidationError;
'use strict';
/**
* Module dependencies.
*/
const Client = require('./client').Client;
const codecs = require('./codecs');
const errors = require('./errors');
const shortcuts = require('./shortcuts');
const tools = require('./tools');
var Client = require('./client').Client;
var codecs = require('./codecs');
var shortcuts = require('./shortcuts');
var tools = require('./tools');
exports.Client = Client;
/**
* Module exports.
*/
exports.PapiError = errors.PapiError;
exports.CodecError = errors.CodecError;
exports.ResponseError = errors.ResponseError;
exports.AbortError = errors.AbortError;
exports.TimeoutError = errors.TimeoutError;
exports.ValidationError = errors.ValidationError;
exports.Client = Client;
exports.request = shortcuts.request;

@@ -19,0 +19,0 @@ exports.get = shortcuts.method('GET');

'use strict';
/**
* Module dependencies.
*/
const url = require('url');
var url = require('url');
const Client = require('./client').Client;
const errors = require('./errors');
var Client = require('./client').Client;
var errors = require('./errors');
/**

@@ -23,31 +19,20 @@ * Request.

try {
if (!opts.url) {
throw errors.Validation('url required');
}
if (!opts.url) {
return Promise.reject(new errors.ValidationError('url required'));
}
if (typeof opts.url !== 'string') {
throw errors.Validation('url must be a string');
}
if (typeof opts.url !== 'string') {
return Promise.reject(new errors.ValidationError('url must be a string'));
}
var baseUrl = url.parse(opts.url);
const baseUrl = url.parse(opts.url);
opts.path = baseUrl.pathname.replace('%7B', '{').replace('%7D', '}');
baseUrl.pathname = '';
opts.path = baseUrl.pathname.replace('%7B', '{').replace('%7D', '}');
baseUrl.pathname = '';
var client = new Client({ baseUrl: baseUrl });
const client = new Client({ baseUrl: baseUrl });
delete opts.url;
delete opts.url;
client._request.apply(client, arguments);
} catch (err) {
var callback = arguments[arguments.length - 1];
if (typeof callback !== 'function') {
err.message = 'no callback: ' + err.message;
throw err;
}
callback(err);
}
return client._request.apply(client, arguments);
}

@@ -69,11 +54,7 @@

request.apply(null, arguments);
return request.apply(null, arguments);
};
}
/**
* Module exports.
*/
exports.method = method;
exports.request = request;

@@ -1,7 +0,5 @@

/**
* Random useful tools.
*/
'use strict';
const util = require('util');
/**

@@ -26,5 +24,7 @@ * Walk "standard" library

Object.keys(obj.prototype).forEach(function(key) {
var v = obj.prototype[key];
Object.getOwnPropertyNames(obj.prototype).forEach(key => {
if (key === 'constructor') return;
const v = obj.prototype[key];
if (!key.match(/^[a-z]+/)) return;

@@ -38,9 +38,9 @@ if (!tree.methods) tree.methods = {};

var meta = obj.meta || {};
const meta = obj.meta || {};
tree.methods[key].type = meta[key] && meta[key].type || 'callback';
tree.methods[key].type = meta[key] && meta[key].type || 'promise';
});
Object.keys(obj).forEach(function(key) {
var v = obj[key];
Object.keys(obj).forEach(key => {
const v = obj[key];

@@ -62,54 +62,15 @@ if (!key.match(/^[A-Z]+/)) return;

/**
* Callback wrapper
* Converts promise methods to callbacks
*/
function fromCallback(fn) {
return new Promise(function(resolve, reject) {
try {
return fn(function(err, data) {
if (err) return reject(err);
return resolve(data);
});
} catch (err) {
return reject(err);
}
});
}
/**
* Wrap callbacks with promises
*/
function promisify(client, wrapper) {
function callbackify(client) {
if (!client) throw new Error('client required');
if (!wrapper) {
if (global.Promise) {
wrapper = fromCallback;
} else {
throw new Error('wrapper required');
}
} else if (typeof wrapper !== 'function') {
throw new Error('wrapper must be a function');
}
var patch = function(client, tree) {
Object.keys(tree.methods).forEach(function(key) {
var method = tree.methods[key];
var fn = client[method.name];
const patch = (client, tree) => {
Object.keys(tree.methods).forEach((key) => {
const method = tree.methods[key];
const fn = client[method.name];
if (method.type === 'callback' && !client[method.name]._wrapCallback) {
client[method.name] = function() {
// use callback if provided
if (typeof arguments[arguments.length - 1] === 'function') {
return fn.apply(client, arguments);
}
// otherwise return promise
var args = Array.prototype.slice.call(arguments);
return wrapper(function(callback) {
args.push(callback);
return fn.apply(client, args);
});
};
client[method.name]._wrapped = true;
if (method.type === 'promise') {
client[method.name] = util.callbackify(fn);
}

@@ -119,4 +80,4 @@ });

if (tree.objects) {
Object.keys(tree.objects).forEach(function(key) {
var clientKey = key[0].toLowerCase() + key.slice(1);
Object.keys(tree.objects).forEach((key) => {
const clientKey = key[0].toLowerCase() + key.slice(1);
patch(client[clientKey], tree.objects[key]);

@@ -130,7 +91,3 @@ });

/**
* Module exports.
*/
exports.promisify = promisify;
exports.callbackify = callbackify;
exports.walk = walk;

@@ -1,5 +0,1 @@

/**
* Helper functions
*/
'use strict';

@@ -14,3 +10,3 @@

for (var p in obj) {
for (let p in obj) {
if (obj.hasOwnProperty(p)) return false;

@@ -53,16 +49,16 @@ }

function merge() {
var data = {};
const data = {};
if (!arguments.length) return data;
for (var i = 0; i < arguments.length; i++) {
const arg = arguments[i];
var args = Array.prototype.slice.call(arguments, 0);
if (arg != null) {
for (const key in arg) {
if (Object.prototype.hasOwnProperty.call(arg, key)) {
data[key] = arg[key];
}
}
}
}
args.forEach(function(obj) {
if (!obj) return;
Object.keys(obj).forEach(function(key) {
data[key] = obj[key];
});
});
return data;

@@ -76,16 +72,16 @@ }

function mergeHeaders() {
var data = {};
const data = {};
if (!arguments.length) return data;
for (var i = 0; i < arguments.length; i++) {
const arg = arguments[i];
var args = Array.prototype.slice.call(arguments, 0);
if (arg != null) {
for (const key in arg) {
if (Object.prototype.hasOwnProperty.call(arg, key)) {
data[key.toLowerCase()] = arg[key];
}
}
}
}
args.forEach(function(obj) {
if (!obj) return;
Object.keys(obj).forEach(function(key) {
data[key.toLowerCase()] = obj[key];
});
});
return data;

@@ -99,24 +95,23 @@ }

function pick(obj) {
var args = Array.prototype.slice.call(arguments);
args.shift();
let args = arguments;
let start = 1;
if (args.length === 1 && Array.isArray(args[0])) {
args = args[0];
if (args.length === 2 && Array.isArray(args[1])) {
args = args[1];
start = 0;
}
var result = {};
const data = {};
args.forEach(function(name) {
if (obj.hasOwnProperty(name)) {
result[name] = obj[name];
for (var i = start; i < args.length; i++) {
const key = args[i];
if (obj.hasOwnProperty(key)) {
data[key] = obj[key];
}
});
}
return result;
return data;
}
/**
* Module exports.
*/
exports.isEmpty = isEmpty;

@@ -123,0 +118,0 @@ exports.isReadableStream = isReadableStream;

{
"name": "papi",
"version": "0.29.1",
"version": "1.0.0-alpha.1",
"description": "Build HTTP API clients",
"main": "lib",
"devDependencies": {
"async": "^2.6.1",
"bluebird": "^3.5.1",
"debug": "^3.1.0",
"istanbul": "^0.4.5",
"debug": "^4.1.1",
"jscs": "^3.0.7",
"jshint": "^2.9.5",
"lodash": "^4.17.10",
"mocha": "^5.2.0",
"nock": "^9.3.2",
"mocha": "^8.3.0",
"nock": "^13.0.7",
"nyc": "^15.1.0",
"request": "^2.87.0",
"should": "^13.2.1",
"sinon": "^1.10.3"
"sinon": "^9.2.4"
},
"engines": {
"node": ">=10.0.0"
},
"scripts": {
"cover": "istanbul cover _mocha -- --recursive && open coverage/lcov-report/index.html",
"bench": "BENCHMARK=true mocha test/benchmark.js",
"test": "jshint lib test && jscs lib test && istanbul cover _mocha -- --recursive --check-leaks --globals Promise && istanbul check-coverage --statements 100 --functions 100 --branches 100 --lines 100"
"cover": "nyc --reporter=lcov _mocha --recursive && open coverage/lcov-report/index.html",
"test": "jshint lib test && jscs lib test && nyc _mocha --recursive --check-leaks --globals Promise && nyc check-coverage --statements 100 --functions 100 --branches 100 --lines 100"
},

@@ -25,0 +25,0 @@ "repository": {

@@ -1,4 +0,4 @@

# Papi [![Build Status](https://travis-ci.org/silas/node-papi.png?branch=master)](https://travis-ci.org/silas/node-papi)
# Papi
This is a module for building HTTP API clients.
This is a library for building HTTP API clients.

@@ -19,4 +19,4 @@ * [Documentation](#documentation)

Your client should inherit the prototype methods from this constructor and call
it in your client's constructor.
Your client should extend the Papi `Client` class and call `super(options)`
with the appropriate options.

@@ -42,18 +42,17 @@ Options

const papi = require('papi');
const util = require('util');
function GitHub(opts) {
opts = opts || {};
opts.baseUrl = 'https://api.github.com';
opts.header = { accept: 'application/vnd.github.v3+json' };
opts.timeout = 15 * 1000;
class GitHub extends papi.Client {
constructor(opts) {
opts = opts || {};
opts.baseUrl = 'https://api.github.com';
opts.header = { accept: 'application/vnd.github.v3+json' };
opts.timeout = 15 * 1000;
papi.Client.call(this, opts);
super(opts);
}
}
util.inherits(GitHub, papi.Client);
```
<a name="client-request"></a>
### client.\_request(request, [callback...], callback)
### client.\_request(request, [middleware...])

@@ -68,4 +67,3 @@ Make an HTTP request.

* request (Object): request options
* callback... (Function&lt;request, next&gt;, optional): middleware functions that can mutate `request.err` or `request.res`. Call `next` without arguments to continue execution, `next(err)` to break with an error, or `next(false, arguments...)` to trigger the final callback with the given arguments
* callback (Function&lt;err, res&gt;): request callback function
* middlware (Function&lt;request, next&gt;, optional): middleware functions that can mutate `request.err` or `request.res`. Call `next` without arguments to continue execution, `next(err)` to break with an error, or `next(false, value)` to return immediately.

@@ -85,3 +83,3 @@ Request

There are also `_get`, `_head`, `_post`, `_put`, `_delete` (`_del`), `_patch`,
There are also `_get`, `_head`, `_post`, `_put`, `_delete`, `_patch`,
and `_options` shortcuts with the same method signature as `_request`.

@@ -92,14 +90,17 @@

``` javascript
GitHub.prototype.gists = function(username, callback) {
const opts = {
path: '/users/{username}/gists',
params: { username: username },
};
class GitHub extends papi.Client {
constructor() {
// see example constructor above
}
this._get(opts, (err, res) => {
if (err) return callback(err);
async gists(username) {
const opts = {
path: '/users/{username}/gists',
params: { username: username },
};
callback(null, res.body);
});
};
const res = await this._get(opts);
return res.body;
}
}
```

@@ -110,4 +111,3 @@

```
statusCode 200
body [ { url: 'https://api.github.com/gists/9458207',
[ { url: 'https://api.github.com/gists/9458207',
...

@@ -214,11 +214,7 @@ ```

``` javascript
const papi = require('papi');
async function show() {
const res = await papi.get('https://api.github.com/users/silas/gists');
papi.get('https://api.github.com/users/silas/gists', (err, res) => {
if (err) throw err;
res.body.forEach(function(gist) {
console.log(gist.url);
});
});
res.body.forEach(gist => console.log(gist.url));
}
```

@@ -230,8 +226,3 @@

``` javascript
/**
* Module dependencies.
*/
const papi = require('papi');
const util = require('util');

@@ -241,71 +232,61 @@ /**

*/
class GitHub extends papi.Client {
constructor(opts) {
opts = opts || {};
function GitHub(opts) {
opts = opts || {};
if (!opts.baseUrl) {
opts.baseUrl = 'https://api.github.com';
}
if (!opts.headers) {
opts.headers = {};
}
if (!opts.headers.accept) {
opts.headers.accept = 'application/vnd.github.v3+json';
}
if (!opts.headers['user-agent']) {
opts.headers['user-agent'] = 'PapiGitHub/0.1.0';
}
if (opts.tags) {
opts.tags = ['github'].concat(opts.tags);
} else {
opts.tags = ['github'];
}
if (!opts.timeout) {
opts.timeout = 60 * 1000;
}
if (!opts.baseUrl) {
opts.baseUrl = 'https://api.github.com';
super(opts);
if (opts.debug) {
this.on('log', console.log);
}
}
if (!opts.headers) {
opts.headers = {};
}
if (!opts.headers.accept) {
opts.headers.accept = 'application/vnd.github.v3+json';
}
if (!opts.headers['user-agent']) {
opts.headers['user-agent'] = 'PapiGitHub/0.1.0';
}
if (opts.tags) {
opts.tags = ['github'].concat(opts.tags);
} else {
opts.tags = ['github'];
}
if (!opts.timeout) {
opts.timeout = 60 * 1000;
}
papi.Client.call(this, opts);
/**
* Get user gists
*/
async gists(username) {
const opts = {
path: '/users/{username}/gists',
params: { username: username },
};
if (opts.debug) {
this.on('log', console.log);
const res = await this._get(opts);
return res.body;
}
}
util.inherits(GitHub, papi.Client);
/**
* Get user gists
*/
GitHub.prototype.gists = function(username, callback) {
const opts = {
path: '/users/{username}/gists',
params: { username: username },
};
return this._get(opts, callback);
};
/**
* Print gists for user `silas`
*/
function main() {
// Print gists for user `silas`
async function main() {
const github = new GitHub({ debug: true });
github.gists('silas', (err, res) => {
if (err) throw err;
const gists = await github.gists('silas');
console.log('----');
console.log('----');
res.body.forEach(function(gist) {
if (gist.description) console.log(gist.description);
});
gists.forEach(function(gist) {
if (gist.description) console.log(gist.description);
});
}
/**
* Initialize
*/
if (require.main === module) {

@@ -312,0 +293,0 @@ main();

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