npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@videojs/xhr

v2.7.0

Published

small xhr abstraction

Downloads

1,715,120

Readme

xhr

Originally forked from naugtur/xhr.

A small XMLHttpRequest wrapper. Designed for use with browserify, webpack etc.

API is a subset of request so you can write code that works in both node.js and the browser by using require('request') in your code and telling your browser bundler to load xhr instead of request.

For browserify, add a browser field to your package.json:

"browser": {
  "request": "@videojs/xhr"
}

For webpack, add a resolve.alias field to your configuration:

"resolve": {
  "alias": {
    "request$": "@videojs/xhr"
  }
}

Browser support: IE8+ and everything else.

Installation

npm install @videojs/xhr

Example

var xhr = require("@videojs/xhr")

xhr({
    method: "post",
    body: someJSONString,
    uri: "/foo",
    headers: {
        "Content-Type": "application/json"
    }
}, function (err, resp, body) {
    // check resp.statusCode
})

var req = xhr(options, callback)

type XhrOptions = String | {
    useXDR: Boolean?,
    sync: Boolean?,
    uri: String,
    url: String,
    method: String?,
    timeout: Number?,
    headers: Object?,
    body: String? | Object?,
    json: Boolean? | Object?,
    username: String?,
    password: String?,
    withCredentials: Boolean?,
    responseType: String?,
    beforeSend: Function?
}
xhr := (XhrOptions, Callback<Response>) => Request

the returned object is either an XMLHttpRequest instance or an XDomainRequest instance (if on IE8/IE9 && options.useXDR is set to true)

XhrCallback

Your callback will be called once with the arguments ( Error, response , body ) where the response is an object:

{
    body: Object||String,
    statusCode: Number,
    method: String,
    headers: {},
    url: String,
    rawRequest: xhr
}

Your callback will be called with an Error if there is an error in the browser that prevents sending the request. A HTTP 500 response is not going to cause an error to be returned.

Other signatures

  • var req = xhr(url, callback) - a simple string instead of the options. In this case, a GET request will be made to that url.

  • var req = xhr(url, options, callback) - the above may also be called with the standard set of options.

Convience methods

  • var req = xhr.{post, put, patch, del, head, get}(url, callback)
  • var req = xhr.{post, put, patch, del, head, get}(options, callback)
  • var req = xhr.{post, put, patch, del, head, get}(url, options, callback)

The xhr module has convience functions attached that will make requests with the given method. Each function is named after its method, with the exception of DELETE which is called xhr.del for compatibility.

The method shorthands may be combined with the url-first form of xhr for succinct and descriptive requests. For example,

xhr.post('/post-to-me', function(err, resp) {
  console.log(resp.body)
})

or

xhr.del('/delete-me', { headers: { my: 'auth' } }, function (err, resp) {
  console.log(resp.statusCode);
})

Options

options.method

Specify the method the XMLHttpRequest should be opened with. Passed to XMLHttpRequest.open. Defaults to "GET"

options.useXDR

Specify whether this is a cross origin (CORS) request for IE<10. Switches IE to use XDomainRequest instead of XMLHttpRequest. Ignored in other browsers.

Note that headers cannot be set on an XDomainRequest instance.

options.sync

Specify whether this is a synchrounous request. Note that when this is true the callback will be called synchronously. In most cases this option should not be used. Only use if you know what you are doing!

options.body

Pass in body to be send across the XMLHttpRequest. Generally should be a string. But anything that's valid as a parameter to XMLHttpRequest.send should work (Buffer for file, etc.).

If options.json is true, then this must be a JSON-serializable object. options.body is passed to JSON.stringify and sent.

options.uri or options.url

The uri to send a request to. Passed to XMLHttpRequest.open. options.url and options.uri are aliases for each other.

options.headers

An object of headers that should be set on the request. The key, value pair is passed to XMLHttpRequest.setRequestHeader

options.timeout

Number of miliseconds to wait for response. Defaults to 0 (no timeout). Ignored when options.sync is true.

options.json

Set to true to send request as application/json (see options.body) and parse response from JSON.

For backwards compatibility options.json can also be a valid JSON-serializable value to be sent to the server. Additionally the response body is still parsed as JSON

For sending booleans as JSON body see FAQ

options.withCredentials

Specify whether user credentials are to be included in a cross-origin request. Sets XMLHttpRequest.withCredentials. Defaults to false.

A wildcard * cannot be used in the Access-Control-Allow-Origin header when withCredentials is true. The header needs to specify your origin explicitly or browser will abort the request.

options.responseType

Determines the data type of the response. Sets XMLHttpRequest.responseType. For example, a responseType of document will return a parsed Document object as the response.body for an XML resource.

options.beforeSend

A function being called right before the send method of the XMLHttpRequest or XDomainRequest instance is called. The XMLHttpRequest or XDomainRequest instance is passed as an argument.

options.xhr

Pass an XMLHttpRequest object (or something that acts like one) to use instead of constructing a new one using the XMLHttpRequest or XDomainRequest constructors. Useful for testing.

Helpers

This module exposes the following helpers on the xhr object.

xhr.httpHandler(callback, decodeResponseBody) => XhrCallback

httpHandler is a wrapper for the [XhrCallback][] which returns an error for HTTP Status Codes 4xx and 5xx. Given a callback, it'll either return an error with the response body as the error's cause, or return the response body.

Usage like so:

xhr({
    uri: "https://example.com/foo",
    responseType: 'arraybuffer'
}, xhr.httpHandler(function(err, responseBody) {

  // we got an error if the XHR errored out or if the status code was 4xx/5xx
  if (err) {
    // error cause is coming soon to JavaScript https://github.com/tc39/proposal-error-cause
    throw new Error(err, {cause: err.cause});
  }

  // this will log an ArrayBuffer
  console.log(responseBody);
});
xhr({
    uri: "https://example.com/foo",
    responseType: 'arraybuffer'
}, xhr.httpHandler(function(err, responseBody) {

  if (err) {
    throw new Error(err, {cause: err.cause});
  }

  // in this case, responseBody will be a String
  console.log(responseBody);
},

// passing true as the second argument will cause httpHandler try and decode the response body into a string
true)

xhr.requestInterceptorsStorage and xhr.responseInterceptorsStorage

have the following API:

export interface NetworkRequest {
  headers: Record<string, string>;
  uri: string;
  metadata: Record<string, unknown>;
  body?: unknown;
  retry?: Retry;
  timeout?: number;
}

export interface NetworkResponse {
  headers: Record<string, string>;
  responseUrl: string;
  body?: unknown;
  responseType?: XMLHttpRequestResponseType;
}

export type Interceptor<T> = (payload: T) => T;

export interface InterceptorsStorage<T> {
  enable(): void;
  disable(): void;
  getIsEnabled(): boolean;
  reset(): void;
  addInterceptor(type: string, interceptor: Interceptor<T>): boolean;
  removeInterceptor(type: string, interceptor: Interceptor<T>): boolean;
  clearInterceptorsByType(type: string): boolean;
  clear(): boolean;
  getForType(type: string): Set<Interceptor<T>>;
  execute(type: string, payload: T): T;
}

Usage:

xhr.requestInterceptorsStorage.enable();
xhr.responseInterceptorsStorage.enable();

xhr.requestInterceptorsStorage.addInterceptor('segment', (request) => {
  // read / update NetworkRequest
  return request;
});

xhr.responseInterceptorsStorage.addInterceptor('segement', (response) => {
  // read / update NetworkResponse
  return response;
});

xhr({
  uri: 'https://host/segment',
  responseType: 'arraybuffer',
  requestType: 'segement'
}, function(err, response, responseBody) {
  // your callback
});

xhr.retryManager

has the following API

export interface Retry {
  getCurrentDelay(): number;
  getCurrentMinPossibleDelay(): number;
  getCurrentMaxPossibleDelay(): number;
  getCurrentFuzzedDelay(): number;
  shouldRetry(): boolean;
  moveToNextAttempt(): void;
}

export interface RetryOptions {
  maxAttempts?: number;
  delayFactor?: number;
  fuzzFactor?: number;
  initialDelay?: number;
}

export interface RetryManager {
  enable(): void;
  disable(): void;
  reset(): void;
  getMaxAttempts(): number;
  setMaxAttempts(maxAttempts: number): void;
  getDelayFactor(): number;
  setDelayFactor(delayFactor: number): void;
  getFuzzFactor(): number;
  setFuzzFactor(fuzzFactor: number): void;
  getInitialDelay(): number;
  setInitialDelay(initialDelay: number): void;
  createRetry(options?: RetryOptions): Retry;
}

Usage:

xhr.retryManager.enable();

xhr.retryManager.setMaxAttempts(2);

xhr({
  uri: 'https://host/segment',
  responseType: 'arraybuffer',
  retry: xhr.retryManager.createRetry(),
}, function(err, response, responseBody) {
  // your callback
});

// or override values for specific request:
xhr({
  uri: 'https://host/segment',
  responseType: 'arraybuffer',
  retry: xhr.retryManager.createRetry({ maxAttempts: 3 }),
}, function(err, response, responseBody) {
  // your callback
});


// you can combine interceptors/retry APIs if you dont have direct acces to the request:
xhr.requestInterceptorsStorage.addInterceptor('segment', (request) => {
  // read / update NetworkRequest
  request.retry = xhr.retryManager.createRetry();
  return request;
});

FAQ

  • Why is my server's JSON response not parsed? I returned the right content-type.
    • See options.json - you can set it to true on a GET request to tell xhr to parse the response body.
    • Without options.json body is returned as-is (a string or when responseType is set and the browser supports it - a result of parsing JSON or XML)
  • How do I send an object or array as POST body?
    • options.body should be a string. You need to serialize your object before passing to xhr for sending.
    • To serialize to JSON you can use options.json:true with options.body for convenience - then xhr will do the serialization and set content-type accordingly.
  • Where's stream API? .pipe() etc.
    • Not implemented. You can't reasonably have that in the browser.
  • Why can't I send "true" as body by passing it as options.json anymore?
    • Accepting true as a value was a bug. Despite what JSON.stringify does, the string "true" is not valid JSON. If you're sending booleans as JSON, please consider wrapping them in an object or array to save yourself from more trouble in the future. To bring back the old behavior, hardcode options.json to true and set options.body to your boolean value.
  • How do I add an onprogress listener?
    • use beforeSend function for non-standard things that are browser specific. In this case:
    xhr({
      ...
      beforeSend: function(xhrObject){
        xhrObject.onprogress = function(){}
      }
    })

Mocking Requests

You can override the constructor used to create new requests for testing. When you're making a new request:

xhr({ xhr: new MockXMLHttpRequest() })

or you can override the constructors used to create requests at the module level:

xhr.XMLHttpRequest = MockXMLHttpRequest
xhr.XDomainRequest = MockXDomainRequest

MIT Licenced