
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
earbuds-http
Advanced tools
An HTTP library to standardize ajax requests to a Transplace API.
npm install earbuds-http
earbuds-http isn't currently pre-compiled, so you may need to modify your webpack config to deal with it.
// webpack.base.conf.js
module.exports = {
entry: [ /* ... */ ],
resolve: [ /* ... */ ],
module: {
rules: {
{ /* eslint rules */ },
{ /* vue-loader rules */ },
{
/* babel rules */
test: /\.js$/,
loader: 'babel-loader',
include: [
resolve('src'),
resolve('test'),
// transpile all packages named earbuds{whatever}
new RegExp(resolve(['node_modules', 'earbuds.*']), 'i'),
]
}
}
}
};
earbuds-http is built on top of axios. Anything that axios does Earbuds-http should do, too.
earbuds-http adds caching and custom server response handling to base axios.
https://github.com/mzabriskie/axios
ebHttp.get(url, [options])ebHttp.post(url, data, [options])params - the URL parameters to be sent with the request. Must be a plain object or a URLSearchParams object
params: { ID: 12345 },
data - the data to be sent as the request body
Only applicable for request methods 'PUT', 'POST', and 'PATCH'
When no transformRequest is set, must be of one of the following types:
data: { firstName: 'Fred' },cache - whether or not to cache the response. boolean.
{cache: true}
cacheTimeToLive - how long the data should be cached. the value is milliseconds until the cache should expire
{cacheTimeToLive: 5000}
updateCache - if true, the current cached value is ignored and the new value is saved in it's place
{cacheTimeToLive: true}
see https://github.com/mzabriskie/axios#request-config
Extend earbuds-http by using axios's extension points (which were inspired by angular's $http module) https://github.com/mzabriskie/axios#interceptors
earbuds-http correctly handles api entity responses from the server.
An ApiEntity response looks like this:
{
success: true,
errors: [ ],
tid: null,
data: {
domainId: null,
accountId: -1,
accountName: null,
// ...
}
}
earbuds-http handles automatically handles the success flag. If true, the promise is resolved with the contents of the data field. Otherwise, the promise is rejected with a list of errors.
Currently the tid (transaction-id) field is not managed.
Add a response interceptor to catch the AuthenticationError and handle it
// signInRequiredResponseInterceptor.js
import AuthenticationError from 'earbuds-http/src/errors/AuthenticationError';
// todo - confirm that this is the best generic login URL to try
const LOGON_URL_BEST_GUESS = process.env.NODE_ENV !== 'production'
? '/security/baseLogon.do'
: '/00-tms-logon-redirect.php';
function handleSignInRequired(error) {
// Returning a non-resolving promise prevents the api request from resolving
// at all. E.g. if this happens in a beforeEnter navigation guard in the
// router, it will stop the page from loading while waiting for the redirect
// to resolve.
return new Promise(() => {
if (error.response && error.response.data && error.response.data.redirect) {
window.location.href = error.response.data.redirect;
} else {
console.warn('ERROR - SIGN IN REQUIRED', 'No redirect url provided. Trying best guess.', LOGON_URL_BEST_GUESS);
window.location.href = LOGON_URL_BEST_GUESS;
}
});
}
export default function rejectedHandler(err) {
if (err && (err instanceof AuthenticationError || err.name === 'AuthenticationError')) {
return handleSignInRequired(err);
}
return err;
}
// BaseApi.js
import ebHttp from 'earbuds-http';
import combineUrls from 'axios/lib/helpers/combineURLs';
import signInRequiredResponseInterceptor from './signInRequiredResponseInterceptor';
const TMS_API_URL = '/tms/api/v1/';
class BaseTmsApi {
constructor(url) {
const baseUrl = combineUrls(TMS_API_URL, url);
this.ebHttp = ebHttp.create({
baseURL: baseUrl,
headers: { 'Content-Type': 'application/json', Accept: 'application/json' }
});
// Handle sign-in required errors
this.ebHttp.interceptors.response.use(null, signInRequiredResponseInterceptor);
}
}
export { BaseTmsApi };
export default BaseTmsApi;
Both get and post calls can be cached.
var url = '/user/123/details';
var a = instance.get(url, {cache:true});
var b = instance.get(url, {cache:true});
var c = instance.get(url, {params: {includeAddress: true}, cache: true});
var d = instance.get(url, {params: {includeAddress: true}, cache: true});
a === b; // true
c === d; // true
a === c; // false. Although the url is the same, the options are not.
Subsequent calls to that url with the same options will used cached data instead of hitting the server.
You can automatically expire cached data after a specified number of milliseconds by including the cacheTimeToLive parameter.
var url = '/user/123/details';
var a = instance.get(url, {cache:true, cacheTimeToLive: 10000});
setTimeout(function () {
var c = instance.get(url, {cache:true, cacheTimeToLive: 10000});
a === c; // true
}, 5000);
setTimeout(function () {
var d = instance.get(url, {cache:true, cacheTimeToLive: 10000});
a === d; // false
}, 20000);
// userApi.js
import ebHttp from 'earbuds-http';
const BASE_URL = './api/user';
export default class Api {
_httpInstance;
constructor(url) {
if (!url) {
throw new Error('url must be provided');
}
this._httpInstance = ebHttp.create({
baseURL: path.join(BASE_URL, url)
});
}
get http() {
return this._httpInstance;
}
getSecurityContext() {
// url -> ./api/user/securityContext/
// cached for the entirety of the javascript session
return this.http.get('/securityContext', { cache: true });
}
getUserDetail(userId) {
// url -> ./api/user/userDetail/userId
// not cached at all
return this.http.get(`/userDetail/${userId}`, { cache: false });
}
getMapData(showMapPaths, mapStyle) {
// url -> ./api/user/getMapData/userId?showMapPaths=true&mapStyle=SQUARE
// cached for 10 seconds
return this.http.get('/userDetail/${userId}', {
params: {showMapPaths, mapStyle},
cache: true,
cacheTimeToLive: 10000
});
}
}
// consumer.js
import userApi from './userApi';
userApi.getSecurityContext()
.then(securityContext => console.log(securityContext))
.catch(errors => toastr.danger(errors.join(', ')));
// ...
You can develop npm packages locally without having to push to test your changes
https://docs.npmjs.com/cli/link
$ npm link$ npm link earbuds-httpOnce you're done developing, use npm version to push your changes
https://docs.npmjs.com/cli/version
$ npm version [major|minor|patch]$ npm install earbuds-http$ npm upgrade earbuds-httpFAQs
An http module for Transplace web applications
We found that earbuds-http demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.