Coder Social home page Coder Social logo

xr's Introduction

NOTE: Package is mostly unsupported. Would recommend fetch or axios

xr

Really simple wrapper around XHR that provides a few bits of nice functionality, exposes the XHR object wherever relevant, and returns an ES6 Promise (or whatever Promise is set to globally, if you want to use something else).

The idea was to make a pragmatic library that's pre-configured for the 90% use case, but override-able for anyone that wants to do anything a bit off the beaten track.

For instance, the library is by default set up to send/receive JSON (with the associated headers and parser/dumper already set up), but if you wanted to use something like XML, it's easy enough to override that with a few lines.

It's lightweight, has no dependencies (other than having either Promise in the global namespace or provided via xr.config), and adds pretty much no overhead over the standard XHR API.

Install

npm install xr --save

Quickstart

import xr from 'xr';

const res = await xr.get('/api/items', { take: 5 });
logger.log(res.data);

const res = await xr.post('/api/item', { name: 'hello' });
logger.log('new item', res.data);

Extended syntax:

import xr from 'xr';

xr({
  method: xr.Methods.GET,
  url: '/api/items',
  params: {take: 5},
  events: {
    [xr.Events.PROGRESS]: (xhr, xhrProgressEvent) => {
      logger.log("xhr", xhr);
      logger.log("progress", xhrProgressEvent);
    },
  },
});

Custom promise:

xr.get('/url', {}, {
  promise: fn => new myPromiseClass(fn),
});

Raw mode (data is not dumped/loaded):

xr.put('/url', 'some data', {
  raw: true,
});

Custom dump/load:

xr.post('/url', { 'some': 'data' }, {
  dump: data => msgpack.encode(data),
  load: data => msgpack.decode(data),
});

Global configuration

One thing that I've always found irritating with libraries it that if you want to override the defaults, you have to do it per-request, or wrap the libraries.

With XR, this is simple, as you can globally configure the module for your project.

xr.configure({
  promise: fn => new myPromise(fn),
});

API is simple, for now consult source.

Features

  • Returns ES6 promises.
  • Has query parameter generation.
  • Supports events.

Alias Methods

You can do some quick aliases to requests, for instance:

xr.get('/my-url');

Requirements

There must be a polyfill or browser that supports at least the standard ES6 promise API (xr will use whatever's there).

License

See LICENSE

xr's People

Contributors

bronzehedwick avatar jaredklewis avatar jordanstephens avatar mtpbzz avatar radiosilence avatar runnez avatar tombyrer avatar toxicfork avatar wprater avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

xr's Issues

Question: how can I abort current request?

I'm writing some kind of autocomplete logic, so I need to abort current request if user presses other key, or something, but I can't find any public method to abort current xhr.

Response format changed / broke semver

Between version 0.1.10 and 0.1.11 a breaking change seems to have occurred:

Previously a request like:

xr.get('/something')

โ€” resolved with an object containing { data, response, status }. As of 0.1.11 it appears to just resolve with data at the top level (parsed JSON body).

Technically 0.x releases are fair game for breaking changes like that, but it seems like the type of thing that might have been worth at least a minor version bump given how many people are likely using default npm install --save behavior that allows that to be considered compatible. Also, documentation doesn't appear to have been updated

Ideally the old behavior could be restored (I can't find an obvious way to detect response status code), if not, docs should be updated to reflect the new syntax.

edit: here's a reduced test case just to confirm to myself that i'm not imagining this: https://jsfiddle.net/qsu8s7bk/

Event bindings executed after promise rejection

I've come across a bit of an edge-case, and I was hoping I could get a bit more insight into the decision for placing the xr event bindings before the opts event bindings?

The problem that I've encountered is that when a timeout occurs it is practically indistinguishable from a CORS error. The xhr object that's being passed back has virtually nothing that identifies which of the two scenarios caused the request to be rejected. Furthermore, the binding xr makes to the timeout event doesn't provide anything alongside the xhr object in the rejection.

I have attempted to bind my own listener using events in the args to the timeout event, however that callback is executed after the promise is rejected. Thus, if I have a catch afterwards, it executes before I have the opportunity to modify a flag variable to distinguish a timeout event from a CORS issue.

For example:

xr
.get(
  '//10.0.0.2',
  null,
  {
    timeout: 4000, // This only makes a difference with my PR, otherwise just wait for natural timeout
    events: {
      timeout: () => console.log('timeout')
    }
  })
.catch(o => console.log('error'))

I get:

error
timeout


Ideally, I'd like for my timeout binding to be fired before the promise is rejected or resolved. Especially since the xhr object is passed to the bound function, and allows me to tweak it so that I have a better idea of what when wrong when I eventually catch the failed request.

Is there a reason the optional bindings are executed after the promise is rejected/resolved? Or could the code be tweaked so that the optional bindings are executed first?

Expose xhr on resolve

I need to be able to read response headers when a promise is resolved. It would be really great if you could access the xhr on both resolve and reject.

I know it will change your API, but it would be nice to be consistent and return the same response structure in both cases. Have you thought about doing something like this:

xhr.addEventListener(Events.LOAD, function () {
  if (xhr.status >= 200 && xhr.status < 300) {
    // I'm omitting the bit for automatically parsing the response for the moment
    resolve(res(xhr));
  } else {
    reject(res(xhr));
  }
});

If you want to go this way, we will want to bring back the bit of code for automatically parsing the response. I wanted to omit it above as there are a couple of ways we could go about it. Off the top of my head, we could either leave it in the eventListener callback and pass the result as an optional _data arg to the res function. Something along these lines:

function res(xhr, _data) {
  return {
    status: xhr.status,
    response: _data || xhr.response,
    xhr: xhr
  };
}

function xr(args) {
  return promise(args, function (resolve, reject) {

    // omitted ...

    xhr.addEventListener(Events.LOAD, function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        var _data = null;
        if (xhr.responseText) {
          _data = opts.raw === true ? xhr.responseText : opts.load(xhr.responseText);
        }
        resolve(res(xhr, _data));
      } else {
        reject(res(xhr));
      }
    });

    // omitted ...
  });
}

Or perhaps a better solution would be to pass the opts object to res and let res take care of the parsing. Something like this:

function res(xhr, opts) {
  opts = opts || {};

  var response = null;

  if (xhr.responseText) {
    response = opts.raw === true ? xhr.responseText : opts.load(xhr.responseText);
  }

  return {
    status: xhr.status,
    response: response,
    xhr: xhr
  };
}

function xr(args) {
  return promise(args, function (resolve, reject) {

    // omitted ...

    xhr.addEventListener(Events.LOAD, function () {
      if (xhr.status >= 200 && xhr.status < 300) {
        resolve(res(xhr, opts));
      } else {
        reject(res(xhr));
      }
    });

    // omitted ...
  });
}

Let me know if you're interested in this approach, and I'll submit a PR.

Thanks!

Where is Object.assign used?

In the README, Object.assign is listed as a requirement, but I don't see any instance of its usage in the source. Forgive me if this is plainly obvious, but am I missing it or is it not a dependency after all?

Thanks!

JSHint finds lots of syntax errors

I've checked xr.js from both master, and from npm.

$ jshint xr.js
xr.js: line 1, col 24, Missing semicolon.
xr.js: line 1, col 24, Expected an identifier and instead saw '=='.
xr.js: line 1, col 26, Expected an operator and instead saw 'typeof'.
xr.js: line 1, col 26, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 32, Missing semicolon.
xr.js: line 1, col 195, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 196, Missing semicolon.
xr.js: line 1, col 377, Missing semicolon.
xr.js: line 1, col 418, Missing semicolon.
xr.js: line 1, col 451, Missing semicolon.
xr.js: line 1, col 534, Missing semicolon.
xr.js: line 1, col 607, Missing semicolon.
xr.js: line 1, col 651, Missing 'new' prefix when invoking a constructor.
xr.js: line 1, col 752, 'u' is already defined.
xr.js: line 1, col 869, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 870, Missing semicolon.
xr.js: line 1, col 879, Missing semicolon.
xr.js: line 1, col 905, Missing semicolon.
xr.js: line 1, col 976, Missing semicolon.
xr.js: line 1, col 1135, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1136, Missing semicolon.
xr.js: line 1, col 1204, Bad invocation.
xr.js: line 1, col 1332, Confusing use of '!'.
xr.js: line 1, col 1382, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1383, Missing semicolon.
xr.js: line 1, col 1396, Missing semicolon.
xr.js: line 1, col 1451, Missing semicolon.
xr.js: line 1, col 1506, Missing semicolon.
xr.js: line 1, col 1563, Missing semicolon.
xr.js: line 1, col 1564, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1661, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1673, 's' is already defined.
xr.js: line 1, col 1771, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1857, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 1858, Missing semicolon.
xr.js: line 1, col 1860, Missing semicolon.
xr.js: line 1, col 1902, Invalid typeof value 'symbol'
xr.js: line 1, col 1954, Missing semicolon.
xr.js: line 1, col 2045, Missing semicolon.
xr.js: line 1, col 2531, Missing '()' invoking a constructor.
xr.js: line 1, col 2545, Missing semicolon.
xr.js: line 1, col 2588, Missing semicolon.
xr.js: line 1, col 2764, Missing semicolon.
xr.js: line 1, col 2830, Missing semicolon.
xr.js: line 1, col 2898, Missing semicolon.
xr.js: line 1, col 2968, Missing semicolon.
xr.js: line 1, col 3028, Missing semicolon.
xr.js: line 1, col 3093, Missing semicolon.
xr.js: line 1, col 3108, Expected an assignment or function call and instead saw an expression.
xr.js: line 1, col 3109, Missing semicolon.
xr.js: line 1, col 3109, Too many errors. (100% scanned).

51 errors

Array params joined with comma

> require('querystring').stringify({x: [1,2,3]})
'x=1&x=2&x=3'

with xr

> require('xr').urlEncode({x: [1,2,3]})
'x=1%2C2%2C3'

Add xr to window if being used in browser with no module system

xr.js' immediately-invoked function only calls the factory if a module system is present. Could this be modified to add xr to the window if the library is being used in a browser without a module-loader? (When exports/amd et al. are undefined)

As xr is a minimal library it'd be great to us it as part of very lean JS applications. โœจ

typescript

just a suggestion, but would be cool to have this in typescript or at least a .d.ts file or it.

Hot update in built xr.js

What the fuck, men?!

!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports["xrl.js"]=t():e["xrl.js"]=t()}(this,function(){return function(e){function t(e){var t=document.getElementsByTagName("head")[0],n=document.createElement("script");n.type="text/javascript",n.charset="utf-8",n.src=p.p+""+e+"."+m+".hot-update.js",t.appendChild(n)}function n(e){if("undefined"==typeof XMLHttpRequest)return e(new Error("No browser support"));try{var t=new XMLHttpRequest,n=p.p+""+m+".hot-update.json";t.open("GET",n,!0),t.timeout=1e4,t.send(null)}catch(r){return e(r)}t.onreadystatechange=function(){if(4===t.readyState)if(0===t.status)e(new Error("Manifest request to "+n+" timed out."));else if(404===t.status)e();else if(200!==t.status&&304!==t.status)e(new Error("Manifest request to "+n+" failed."));else{try{var r=JSON.parse(t.responseText)}catch(o){return void e(o)}e(null,r)}}}function r(e){var t=H[e];if(!t)return p;var n=function(n){return t.hot.active?H[n]?(H[n].parents.indexOf(e)<0&&H[n].parents.push(e),t.children.indexOf(n)<0&&t.children.push(n)):w=[e]:(console.warn("[HMR] unexpected require("+n+") from disposed module "+e),w=[]),p(n)};for(var r in p)Object.prototype.hasOwnProperty.call(p,r)&&(n[r]=p[r]);return n.e=function(e,t){"ready"===x&&a("prepare"),b++,p.e(e,function(){function r(){b--,"prepare"===x&&(g[e]||u(e),0===b&&0===_&&d())}try{t.call(null,n)}finally{r()}})},n}function o(e){var t={_acceptedDependencies:{},_declinedDependencies:{},_selfAccepted:!1,_selfDeclined:!1,_disposeHandlers:[],active:!0,accept:function(e,n){if("undefined"==typeof e)t._selfAccepted=!0;else if("function"==typeof e)t._selfAccepted=e;else if("object"==typeof e)for(var r=0;r<e.length;r++)t._acceptedDependencies[e[r]]=n;else t._acceptedDependencies[e]=n},decline:function(e){if("undefined"==typeof e)t._selfDeclined=!0;else if("number"==typeof e)t._declinedDependencies[e]=!0;else for(var n=0;n<e.length;n++)t._declinedDependencies[e[n]]=!0},dispose:function(e){t._disposeHandlers.push(e)},addDisposeHandler:function(e){t._disposeHandlers.push(e)},removeDisposeHandler:function(e){var n=t._disposeHandlers.indexOf(e);n>=0&&t._disposeHandlers.splice(n,1)},check:s,apply:f,status:function(e){return e?void T.push(e):x},addStatusHandler:function(e){T.push(e)},removeStatusHandler:function(e){var t=T.indexOf(e);t>=0&&T.splice(t,1)},data:E[e]};return t}function a(e){x=e;for(var t=0;t<T.length;t++)T[t].call(null,e)}function i(e){var t=+e+""===e;return t?+e:e}function s(e,t){if("idle"!==x)throw new Error("check() is only allowed in idle status");"function"==typeof e?(O=!1,t=e):(O=e,t=t||function(e){if(e)throw e}),a("check"),n(function(e,n){if(e)return t(e);if(!n)return a("idle"),void t(null,null);j={},A={},g={};for(var r=0;r<n.c.length;r++)A[n.c[r]]=!0;y=n.h,a("prepare"),h=t,v={};var o=0;u(o),"prepare"===x&&0===b&&0===_&&d()})}function c(e,t){if(A[e]&&j[e]){j[e]=!1;for(var n in t)Object.prototype.hasOwnProperty.call(t,n)&&(v[n]=t[n]);0===--_&&0===b&&d()}}function u(e){A[e]?(j[e]=!0,_++,t(e)):g[e]=!0}function d(){a("ready");var e=h;if(h=null,e)if(O)f(O,e);else{var t=[];for(var n in v)Object.prototype.hasOwnProperty.call(v,n)&&t.push(i(n));e(null,t)}}function f(t,n){function r(e){for(var t=[e],n={},r=t.slice();r.length>0;){var a=r.pop(),e=H[a];if(e&&!e.hot._selfAccepted){if(e.hot._selfDeclined)return new Error("Aborted because of self decline: "+a);if(0===a)return;for(var i=0;i<e.parents.length;i++){var s=e.parents[i],c=H[s];if(c.hot._declinedDependencies[a])return new Error("Aborted because of declined dependency: "+a+" in "+s);t.indexOf(s)>=0||(c.hot._acceptedDependencies[a]?(n[s]||(n[s]=[]),o(n[s],[a])):(delete n[s],t.push(s),r.push(s)))}}}return[t,n]}function o(e,t){for(var n=0;n<t.length;n++){var r=t[n];e.indexOf(r)<0&&e.push(r)}}if("ready"!==x)throw new Error("apply() is only allowed in ready status");"function"==typeof t?(n=t,t={}):t&&"object"==typeof t?n=n||function(e){if(e)throw e}:(t={},n=n||function(e){if(e)throw e});var s={},c=[],u={};for(var d in v)if(Object.prototype.hasOwnProperty.call(v,d)){var f=i(d),l=r(f);if(!l){if(t.ignoreUnaccepted)continue;return a("abort"),n(new Error("Aborted because "+f+" is not accepted"))}if(l instanceof Error)return a("abort"),n(l);u[f]=v[f],o(c,l[0]);for(var f in l[1])Object.prototype.hasOwnProperty.call(l[1],f)&&(s[f]||(s[f]=[]),o(s[f],l[1][f]))}for(var h=[],O=0;O<c.length;O++){var f=c[O];H[f]&&H[f].hot._selfAccepted&&h.push({module:f,errorHandler:H[f].hot._selfAccepted})}a("dispose");for(var T=c.slice();T.length>0;){var f=T.pop(),_=H[f];if(_){for(var b={},g=_.hot._disposeHandlers,j=0;j<g.length;j++){var A=g[j];A(b)}E[f]=b,_.hot.active=!1,delete H[f];for(var j=0;j<_.children.length;j++){var P=H[_.children[j]];if(P){var R=P.parents.indexOf(f);R>=0&&P.parents.splice(R,1)}}}}for(var f in s)if(Object.prototype.hasOwnProperty.call(s,f))for(var _=H[f],D=s[f],j=0;j<D.length;j++){var S=D[j],R=_.children.indexOf(S);R>=0&&_.children.splice(R,1)}a("apply"),m=y;for(var f in u)Object.prototype.hasOwnProperty.call(u,f)&&(e[f]=u[f]);var L=null;for(var f in s)if(Object.prototype.hasOwnProperty.call(s,f)){for(var _=H[f],D=s[f],U=[],O=0;O<D.length;O++){var S=D[O],A=_.hot._acceptedDependencies[S];U.indexOf(A)>=0||U.push(A)}for(var O=0;O<U.length;O++){var A=U[O];try{A(s)}catch(C){L||(L=C)}}}for(var O=0;O<h.length;O++){var M=h[O],f=M.module;w=[f];try{p(f)}catch(C){if("function"==typeof M.errorHandler)try{M.errorHandler(C)}catch(C){L||(L=C)}else L||(L=C)}}return L?(a("fail"),n(L)):(a("idle"),void n(null,c))}function p(t){if(H[t])return H[t].exports;var n=H[t]={exports:{},id:t,loaded:!1,hot:o(t),parents:w,children:[]};return e[t].call(n.exports,n,n.exports,r(t)),n.loaded=!0,n.exports}var l=this.webpackHotUpdatexrl_js;this.webpackHotUpdatexrl_js=function(e,t){c(e,t),l&&l(e,t)};var h,v,y,O=!0,m="5cf8f21792e4ed1c74d8",E={},w=[],T=[],x="idle",_=0,b=0,g={},j={},A={},H={};return p.m=e,p.c=H,p.p="/",p.h=function(){return m},r(0)(0)}([/*!****************!*\

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.