Comparing version 0.9.3 to 0.10.0
{ | ||
"name": "sajari", | ||
"version": "0.9.3", | ||
"description": "JavaScript SDK for access to the Sajari search API", | ||
"author": { | ||
"name": "mish15", | ||
"url": "https://www.sajari.com" | ||
"version": "0.10.0", | ||
"description": "JavaScript SDK for the Sajari search API", | ||
"scripts": { | ||
"build": "webpack -p", | ||
"dev": "webpack -p -w", | ||
"size": "node scripts/buildSize.js" | ||
}, | ||
"main": "src/js/api.js", | ||
"main": "sajari.js", | ||
"files": [ | ||
"README.md", | ||
"LICENSE", | ||
"sajari.js", | ||
"src/" | ||
], | ||
"repository": { | ||
@@ -21,3 +28,5 @@ "type": "git", | ||
"realtime", | ||
"autocomplete" | ||
"autocomplete", | ||
"instant", | ||
"hosted" | ||
], | ||
@@ -32,26 +41,35 @@ "homepage": "https://github.com/sajari/sajari-sdk-js", | ||
], | ||
"dependencies": { | ||
"js-cookie": "2.1.3" | ||
}, | ||
"devDependencies": { | ||
"browserify": "^13.0.0", | ||
"gulp": "^3.9.0", | ||
"gulp-concat": "^2.3.4", | ||
"gulp-jsbeautifier": "^1.0.1", | ||
"gulp-jshint": "^2.0.0", | ||
"gulp-jslint": "^0.2.2", | ||
"gulp-shell": "^0.5.1", | ||
"gulp-tape": "0.0.7", | ||
"gulp-uglify": "^0.3.1", | ||
"gulp-watch": "^4.3.5", | ||
"jshint": "^2.8.0", | ||
"jshint-stylish": "^2.1.0", | ||
"tap-colorize": "^1.2.0", | ||
"tape": "^4.4.0", | ||
"vinyl-buffer": "^1.0.0", | ||
"vinyl-source-stream": "^1.1.0" | ||
"babel-core": "^6.11.4", | ||
"babel-loader": "^6.2.4", | ||
"babel-preset-es2015": "^6.9.0", | ||
"babel-preset-stage-0": "^6.5.0", | ||
"babel-register": "^6.11.6", | ||
"eslint": "^3.3.0", | ||
"eslint-config-airbnb": "^10.0.0", | ||
"eslint-plugin-import": "^1.12.0", | ||
"eslint-plugin-jsx-a11y": "^2.0.1", | ||
"eslint-plugin-react": "^6.0.0", | ||
"gzip-size": "^3.0.0", | ||
"isparta": "^4.0.0", | ||
"istanbul": "^0.4.4", | ||
"mocha": "^3.0.0", | ||
"webpack": "^1.13.1" | ||
}, | ||
"dependencies": { | ||
"loglevel": "^1.4.0", | ||
"superagent": "^1.6.1", | ||
"superagent-jsonp": "0.0.6" | ||
}, | ||
"author": [ | ||
{ | ||
"name": "Trent Billington", | ||
"email": "tbillington@sajari.com", | ||
"url": "https://www.sajari.com" | ||
}, | ||
{ | ||
"name": "David Howden", | ||
"email": "dhowden@sajari.com", | ||
"url": "https://www.sajari.com" | ||
} | ||
], | ||
"license": "MIT" | ||
} |
539
README.md
@@ -1,22 +0,38 @@ | ||
# sajari-sdk-js | ||
Sajari JavaScript SDK for integration into web and nodejs applications | ||
# Sajari Javascript SDK | ||
![npm](https://img.shields.io/npm/v/sajari.svg?style=flat-square) ![license](http://img.shields.io/badge/license-MIT-green.svg?style=flat-square) | ||
The Sajari Javascript SDK provides web integration for browsers. | ||
[Sajari Search](https://www.sajari.com) is a hosted search and recommendation service supporting instant search, faceted search, recommendations and custom matching algorithms | ||
This module is to interact with the raw API. If you want automated indexing, profiling and convenience functions for rendering HTML, please check out [sajari-website](https://github.com/sajari/sajari-sdk-website) instead. | ||
This module is for querying the search service. If you want automated indexing, profiling and convenience functions for rendering HTML, please check out [sajari-website](https://github.com/sajari/sajari-sdk-website) instead. | ||
[![Version][version-svg]][package-url] [![License][license-image]][license-url] [![Downloads][downloads-image]][downloads-url] | ||
## Table of Contents | ||
[license-image]: http://img.shields.io/badge/license-MIT-green.svg?style=flat-square | ||
[license-url]: LICENSE.txt | ||
[downloads-image]: https://img.shields.io/npm/dm/sajari.svg?style=flat-square | ||
[downloads-url]: http://npm-stat.com/charts.html?package=sajari | ||
[version-svg]: https://img.shields.io/npm/v/sajari.svg?style=flat-square | ||
[package-url]: https://npmjs.org/package/sajari | ||
* [Setup](#setup) | ||
* [Npm, Browserify, Webpack](#npm-browserify-webpack) | ||
* [Getting Started](#getting-started) | ||
* Usage | ||
* [Body](#body) | ||
* [Page](#page) | ||
* [Results Per Page](#resultsperpage) | ||
* [Filter](#filter) | ||
* [Sorting](#sorting) | ||
* [Aggregates](#aggregates) | ||
* [Instance Boosts](#instance-boosts) | ||
* [Field Boosts](#field-boosts) | ||
* [Tokens](#tokens) | ||
* [Pos Neg](#pos-neg) | ||
* [Click](#click) | ||
* [Results](#results) | ||
* [Reset ID]($reset-id) | ||
* [License](#license) | ||
* [Browser Support](#browser-support) | ||
This library is [UMD](https://github.com/umdjs/umd) compatible, you can use it with any module loader. It can be used with nodejs, or integrated into browser applications (read requests). | ||
## Setup | ||
To install: | ||
The library is 7.2KB minified and 2.8KB gzipped. | ||
##### NPM, Browserify, webpack, node.js | ||
### NPM, Browserify, webpack | ||
``` | ||
@@ -26,181 +42,408 @@ npm install sajari --save | ||
##### Quick start for browsers (read requests only): | ||
## Getting Started | ||
```javascript | ||
import { Api, Query, body } from 'sajari' | ||
Note: browsers use the "jsonp" option to make cross domain AJAX requests. A key-secret is not required, the companyId-collectionId combination will check for allowed domains and authorize accordingly. If you get 401 errors, make sure the calling domain is added in your control panel for this collection. | ||
const api = new Api('project', 'collection') | ||
const query = new Query() | ||
```js | ||
var sajari = require('sajari'); | ||
api = new sajari('companyId', 'collectionId', { | ||
jsonp: true | ||
}); | ||
query.body([ | ||
body("foo bar") | ||
]) | ||
var query = api.query({ | ||
'q': 'whatever' | ||
api.search(query, (err, res) => { | ||
console.log(err, res) | ||
}) | ||
``` | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
The `Api` object handles the requesting and callbacks. If you need to override the default address, you can supply an extra parameter to `Api`: | ||
```javascript | ||
new Api('project', 'collection', 'http://localhost:8000') | ||
``` | ||
##### Quick start for server side: | ||
The `Query` object handles the query state. Use the methods on it to define your queries. | ||
Note: server side integrations can use the private key-secret combination to access both read and write requests. Do NOT use your private key-secret in a browser based application. | ||
## Body | ||
```js | ||
var sajari = require('sajari'); | ||
api = new sajari('companyId', 'collectionId', { | ||
basicauth : { | ||
user : 'key', | ||
pass : 'secret' | ||
} | ||
); | ||
`body` is the text to search for in the collection. It takes a string, and an optional decimal number for the weighting. | ||
var query = api.query({ | ||
'q': 'whatever' | ||
}) | ||
The `query.body()` method takes an array of `body`. This is useful if you would like to weigh some text differently. | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
### Simple example | ||
```javascript | ||
import { body } from 'sajari' | ||
query.body([ | ||
body('computer parts') | ||
]) | ||
``` | ||
Notes: | ||
- You don't need to keep initializing the `api`, you only need to do this once unless you want to change config, collection, etc. | ||
- The `args` object is very generic and supports anything in our [API spec](https://www.sajari.com/api-documentation#attributes) | ||
- Some of our parameters are more complex, the `query` object helps [encode args](#args) | ||
### Expanded example | ||
### Query object | ||
```javascript | ||
import { body } from 'sajari' | ||
A new `query` object can be initialized with `opts`: | ||
query.body([ | ||
body('red computer parts', 1), | ||
body('laptop', 0.8), | ||
body('desktop', 0.8), | ||
]) | ||
``` | ||
`opts` can be an object: | ||
```js | ||
var query = api.query({ | ||
q : 'something', | ||
custom1 : 'group A', | ||
cols: ['title', 'description', 'url'] | ||
}); | ||
## Page | ||
To use pagination, set the page value on the query via the `query.page()` method. | ||
### Example | ||
```javascript | ||
query.page(2) | ||
``` | ||
`opts` can also be a query string: | ||
```js | ||
var query = api.query('something'); | ||
## ResultsPerPage | ||
To set the maximum number of results to be returned by one query use `query.resultsPerPage()`. Use in conjunction with page to support pagination. | ||
### Example | ||
```javascript | ||
query.resultsPerPage(20) | ||
``` | ||
Sajari has many [supported attributes](https://www.sajari.com/api-documentation#attributes) with query methods. These can also be chained: | ||
```js | ||
var query = api.query('something') | ||
.filter("this", "~", "that") | ||
.scale("lat", 50, 5, 1, 0) | ||
.scale("lng", 100, 5, 1, 0) | ||
.filter("location", "contains", "usa") | ||
.meta("category", "electronics") | ||
.attr("custom1", "abc") | ||
.page(3) | ||
.maxresults(5) | ||
.cols(["title", "description", "url"]); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
## Filter | ||
Filters let you exclude documents from the result set. A query only has 1 filter attached to it, but it is still possible to construct arbitrarily nested filters to satisfy any query logic. | ||
Field filters act on a value in a field, resulting in a true or false result. | ||
| Query Filter | | ||
| :-- | | ||
| `FILTER_OP_EQ` | | ||
| `FILTER_OP_NOT_EQ` | | ||
| `FILTER_OP_GT` | | ||
| `FILTER_OP_GT_EQ` | | ||
| `FILTER_OP_LT` | | ||
| `FILTER_OP_LT_EQ` | | ||
| `FILTER_OP_CONTAINS` | | ||
| `FILTER_OP_NOT_CONTAIN` | | ||
| `FILTER_OP_PREFIX` | | ||
| `FILTER_OP_SUFFIX` | | ||
Combinator filters act on an array of filters, also resulting in a true of false result. | ||
| Query Filter Combinator | | ||
| :-- | | ||
| `COMB_FILTER_OP_ALL` | | ||
| `COMB_FILTER_OP_ANY` | | ||
| `COMB_FILTER_OP_ONE` | | ||
| `COMB_FILTER_OP_NONE` | | ||
### Filter Example | ||
```javascript | ||
import { fieldFilter, FILTER_OP_LT } from 'sajari' | ||
query.filter( | ||
fieldFilter('price', 100, FILTER_OP_LT) | ||
) | ||
``` | ||
In the above case, the query is passed directly to the search function, which handles the appropriate encoding for you. | ||
### Combinator Filter Example | ||
```javascript | ||
import { fieldFIlter, FILTER_OP_LT, FILTER_OP_GT_EQ, combinatorFilter } from 'sajari' | ||
### Search | ||
query.filter( | ||
combinatorFilter([ | ||
fieldFilter('price', 100, FILTER_OP_LT), | ||
fieldFilter('stock', 3, FILTER_OP_GT_EQ) | ||
], COMB_FILTER_OP_ALL) | ||
) | ||
``` | ||
Sajari supports multiple types of searches, which are all relatively interchangeable and use the same API endpoint: | ||
## Sorting | ||
- **instant** - used for "as you type" style keyword searches. Partial words are extended to full queries. Spelling errors are corrected if the query cannot be extended as is, etc. | ||
- **match** - keyword queries are augmented with meta data, which is used to boost results in various ways. Unlike filters, this does not exclude any results, but rather changes their ranking. | ||
- **document** - full documents can be used as queries themselves. | ||
- **faceted** - generally used with keyword style searches to get aggregate information about matching results meta | ||
Sorts allow you to order your results based on their fields. Queries can take multiple sorts, using successive sorts to resolve ties. | ||
All searches can also be filtered, scaled (based on numeric meta). | ||
| Sort Order | | ||
| :-- | | ||
| `SORT_ASCENDING` | | ||
| `SORT_DESCENDING` | | ||
Instant search example (should be triggered using the keyup event or similar): | ||
```js | ||
var query = api.query('di'); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
## Sort Example | ||
```javascript | ||
import { SORT_ASCENDING } from 'sajari' | ||
query.sort([ | ||
sort('price', SORT_ASCENDING) | ||
]) | ||
``` | ||
Match example (search must have "jaguar", prefer color="red" & category="cars"): | ||
```js | ||
var query = api.query('jaguar') | ||
.meta("color", "red") | ||
.meta("category", "cars"); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
## Expanded Sort Example | ||
```javascript | ||
import { SORT_ASCENDING, SORT_DESCENDING } from 'sajari' | ||
query.sort([ | ||
sort('rating', SORT_DESCENDING), | ||
sort('price', SORT_ASCENDING), | ||
sort('performance', SORT_DESCENDING), | ||
]) | ||
``` | ||
Field Facet example (top 10 categories and colors for docs matching the "jaguar" query): | ||
```js | ||
var query = api.query('jaguar') | ||
.facetfields(['category', 'color'], 10); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
## Aggregates | ||
Aggregates give you information about your data. | ||
There are 3 types of aggregates. Metric and Count both work on fields, while Bucket works on Filters. | ||
- Metric - statistical information such as minimum, maximum, count, average, and sum. | ||
- Count - the counts for each value. | ||
- Bucket - counts of categories that match the supplied filters | ||
| Metric Aggregate Type | | ||
| :-- | | ||
| `METRIC_TYPE_MAX` | | ||
| `METRIC_TYPE_MIN` | | ||
| `METRIC_TYPE_AVG` | | ||
| `METRIC_TYPE_SUM` | | ||
### Metric example | ||
```javascript | ||
import { metricAggregate, METRIC_TYPE_MAX, METRIC_TYPE_MIN, METRIC_TYPE_AVG, METRIC_TYPE_SUM } from 'sajari' | ||
query.aggregates([ | ||
metricAggregate('Most expensive part', 'price', METRIC_TYPE_MAX), | ||
metricAggregate('Least expensive part', 'price', METRIC_TYPE_MIN), | ||
metricAggregate('Average price of part', 'price', METRIC_TYPE_AVG), | ||
metricAggregate('Number of parts available', 'quantity', METRIC_TYPE_SUM), | ||
]) | ||
``` | ||
Metric Facet example (get the count for price brackets of 10,000 from 0 - 200,000 for all docs matching the "jaguar" query): | ||
```js | ||
var query = api.query('jaguar') | ||
.filter("color", "=", "red") | ||
.metricfacet('price', 0, 200000, 10000); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
### Count example | ||
```javascript | ||
import { countAggregate } from 'sajari' | ||
query.aggregates([ | ||
countAggregate('Number of parts by manufacturer', 'manufacturer') | ||
]) | ||
``` | ||
### Bucket example | ||
### Recommendations | ||
```javascript | ||
import { bucketAggregate, bucket, fieldFilter, FILTER_OP_LT, FILTER_OP_GT_EQ, FILTER_OP_GT, combinatorFilter, COMB_FILTER_OP_ALL } from 'sajari' | ||
Sajari supports two main groups types of recommendations: | ||
query.aggregates([ | ||
bucketAggregate( | ||
'Price groups', | ||
[ | ||
bucket('$0 - $99', | ||
fieldFilter('price', 100, FILTER_OP_LT) | ||
) | ||
bucket('$100 - $199', | ||
combinatorFilter([ | ||
fieldFilter('price', 100, FILTER_OP_GT_EQ), | ||
fieldFilter('price', 200, FILTER_OP_LT), | ||
], COMB_FILTER_ALL) | ||
), | ||
bucket('$200+', | ||
fieldFilter('price', 200, FILTER_OP_GT) | ||
), | ||
] | ||
) | ||
]) | ||
``` | ||
1. Website recommendations - Typically they require information from the current web page, visitor profile, etc. So although they are very analogous to the "search" function, we would advise those looking for website recommendations to use [sajari-website](https://github.com/sajari/sajari-sdk-website) instead, as that module integrates into the DOM, handles user profile cookies, etc. | ||
## Instance boosts | ||
2. Custom recommendations - These typically use the "search" function, but include "meta" parameters to help power the recommendation. The way information is used in the recommendation algorithm is [highly configurable](https://www.sajari.com/configuration#fields). | ||
Instance boosts can influence the scoring of indexed fields. This is commonly used to make the title or keywords of a page play a larger role. | ||
Example below: | ||
### Field Instance Boost Example | ||
```js | ||
var query = api.query() | ||
.meta("category", "electronics") | ||
.meta("price", 25.00) | ||
.meta("segment", "luxury") | ||
.meta("brand", "samsung") | ||
.meta("tags", ["phone", "oled", "silver"]) | ||
.filter("sku", "!=", "J12345") | ||
.maxresults(5); | ||
api.search(query, function success(res) { | ||
console.log(res); | ||
}, function failure(err) { | ||
console.log(err); | ||
}); | ||
```javascript | ||
import { fieldInstanceBoost } from 'sajari' | ||
query.instanceBoosts([ | ||
fieldInstanceBoost('title', 1.5) | ||
]) | ||
``` | ||
### Score Instance Boost Example | ||
```javascript | ||
import { scoreInstanceBoost } from 'sajari' | ||
query.instanceBoosts([ | ||
scoreInstanceBoost(2) | ||
]) | ||
``` | ||
## Field boosts | ||
Field boosts allow you to influence the scoring of results based on the data in certain meta fields. In theory they are similar to filters that influence the score rather than exclude/include documents. | ||
The most obvious boost is a filter boost. It applies a boost if the document matches the filter. | ||
### Filter Field Boost Example | ||
```javascript | ||
import { filterFieldBoost, fieldFilter, FILTER_OP_LT } from 'sajari' | ||
query.fieldBoosts([ | ||
filterFieldBoost(fieldFilter('price', 100, FILTER_OP_LT), 2) | ||
]) | ||
``` | ||
### Additive Field Boost Example | ||
```javascript | ||
import { additiveFieldBoost, filterFieldBoost, fieldFilter, FILTER_OP_LT } from 'sajari' | ||
query.fieldBoosts([ | ||
additiveFieldBoost(filterFieldBoost(fieldFilter('price', 100, FILTER_OP_LT), 2), 0.5) | ||
]) | ||
``` | ||
If you had latitude and longitude fields, geo-boosting is a good option to get location-aware results. | ||
### Geo Field Boost Example | ||
Boost results within 50km of Sydney. | ||
| Geo Boost Regions | | ||
| :-- | | ||
| `GEO_FIELD_BOOST_REGION_INSIDE` | | ||
| `GEO_FIELD_BOOST_REGION_OUTSIDE` | | ||
```javascript | ||
query.fieldBoosts([ | ||
geoFieldBoost('lat', 'lng', -33.8688, 151.2093, 50, 2, GEO_FIELD_BOOST_REGION_INSIDE) | ||
]); | ||
``` | ||
If you would like to scale a value based on arbitrary points, you can use the interval boost. | ||
### Interval Field Boost Example | ||
This will scale the score based on a sliding scale defined through points. | ||
```javascript | ||
import { intervalFieldBoost, pointValue } from 'sajari' | ||
query.fieldBoosts([ | ||
intervalFieldBoost('performance', [ | ||
pointValue(0, 0.5), | ||
pointValue(80, 1), | ||
pointValue(100, 1.5), | ||
]) | ||
]) | ||
``` | ||
### Distance Field Boost Example | ||
Distance boosts let you boost a result, with values closer to the ref given a higher boost (up to the specified boost value). In this example, a value of 50 would get 2x boost, value 60 would get 1.5x, value of 70 or higher would get 1x. | ||
```javascript | ||
import { distanceFieldBoost } from 'sajari' | ||
query.fieldBoosts([ | ||
distanceFieldBoost(30, 70, 50, 'price', 2) | ||
]) | ||
``` | ||
### Element Field Boost Example | ||
Element field boosts can be applied to string arrays. | ||
```javascript | ||
import { elementFieldBoost } from 'sajari' | ||
query.fieldBoosts([ | ||
elementFieldBoost('keywords', ['sale', 'deal']) | ||
]) | ||
``` | ||
### Text Field Boost Example | ||
Boost results with the word 'reviews' in the 'description' field. | ||
```javascript | ||
import { textFieldBoost } from 'sajari' | ||
query.fieldBoosts([ | ||
textFieldBoost('description', 'reviews') | ||
]) | ||
``` | ||
## Tokens | ||
### Pos Neg | ||
The argument to `posNeg` is the field to use. It must be unique. | ||
```javascript | ||
query.posNeg('url') | ||
``` | ||
### Click | ||
The argument to `click` is the field to use. It must be unique. | ||
```javascript | ||
query.click('url') | ||
``` | ||
## Results | ||
The results that come back from a successful search look like this | ||
```javascript | ||
{ | ||
reads: "1000", // Engine read 1000 documents | ||
totalResults: "50", // 50 documents matches the query | ||
time: "2.2ms", // Time taken | ||
aggregates: {...}, // An object describing the results of the various aggregates applied to the query | ||
results: [ | ||
{ | ||
meta: { | ||
_id: "49913-3c39-7e62-7b81-3ec5a156", // Auto generated unique id for the document | ||
title: "New Computer Part Sale!", | ||
url: "/awesome_part.html", | ||
description: "Super awesome part, does x, y, z...", | ||
price: 59.99, | ||
keywords: ['sale', 'deal', 'part'], | ||
... | ||
}, | ||
score: 0.4, // Score of the document with boosts applied | ||
rawScore: 0.4 // Score of the document without boosts applied | ||
}, | ||
... | ||
] | ||
} | ||
``` | ||
The `results` property is an array of objects, each containing their score, and meta fields. | ||
## Reset ID | ||
This method is used if you would like the next search you perform to count as a different query. This has more to do with stats and won't directly affect your query in any way. | ||
```javascript | ||
// ... Some searches | ||
query.resetID() // You have determined that from now on, the query is sufficiently different to be classified as a new query for tracking purposes | ||
// ... Some searches | ||
``` | ||
## License | ||
We use the [MIT license](./LICENSE) | ||
## Browser Support | ||
This library uses the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). Fetch is available on all evergreen browsers (Chrome, Firefox, Edge), see [here](http://caniuse.com/#feat=fetch) for a more complete overview. We recommend using [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) to increase compatibility across other browsers and [Node.js](https://nodejs.org). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
1
15
449
28403
6
246
4
+ Addedjs-cookie@2.1.3
+ Addedjs-cookie@2.1.3(transitive)
- Removedloglevel@^1.4.0
- Removedsuperagent@^1.6.1
- Removedsuperagent-jsonp@0.0.6
- Removedasync@1.5.2(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcomponent-emitter@1.2.1(transitive)
- Removedcookiejar@2.0.6(transitive)
- Removedcore-util-is@1.0.3(transitive)
- Removeddebug@2.6.9(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedextend@3.0.0(transitive)
- Removedform-data@1.0.0-rc3(transitive)
- Removedformidable@1.0.17(transitive)
- Removedinherits@2.0.4(transitive)
- Removedisarray@0.0.1(transitive)
- Removedloglevel@1.9.2(transitive)
- Removedmethods@1.1.2(transitive)
- Removedmime@1.3.4(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedms@2.0.0(transitive)
- Removedqs@2.3.3(transitive)
- Removedreadable-stream@1.0.27-1(transitive)
- Removedreduce-component@1.0.1(transitive)
- Removedstring_decoder@0.10.31(transitive)
- Removedsuperagent@1.8.5(transitive)
- Removedsuperagent-jsonp@0.0.6(transitive)