feathers-solr
Advanced tools
Comparing version 1.1.16 to 2.1.0
430
lib/index.js
@@ -1,392 +0,76 @@ | ||
'use strict'; | ||
// const { _ } = require('@feathersjs/commons'); | ||
const { AdapterService } = require('@feathersjs/adapter-commons'); | ||
const Client = require('./client'); | ||
// const errors = require('@feathersjs/errors'); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
// const errorHandler = require("./error-handler"); | ||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); | ||
// const debug = require('debug')('feathers-solr'); | ||
exports.default = init; | ||
var _utils = require('./utils'); | ||
var _feathersErrors = require('feathers-errors'); | ||
var _feathersErrors2 = _interopRequireDefault(_feathersErrors); | ||
var _solr = require('./client/solr'); | ||
var _solr2 = _interopRequireDefault(_solr); | ||
var _debug = require('debug'); | ||
var _debug2 = _interopRequireDefault(_debug); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
var debug = (0, _debug2.default)('feathers-solr'); | ||
var Service = function () { | ||
function Service() { | ||
var _this = this; | ||
var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
_classCallCheck(this, Service); | ||
this.options = Object.assign({}, { | ||
host: 'http://localhost:8983/solr', | ||
core: '/gettingstarted', | ||
schema: false, | ||
migrate: 'safe', | ||
adminKey: false, | ||
idfield: 'id', | ||
managedScheme: true, | ||
/*commitStrategy softCommit: true, commit: true, commitWithin: 50000*/ | ||
commitStrategy: { | ||
softCommit: true, | ||
commitWithin: 50000, | ||
overwrite: true | ||
} | ||
}, opt); | ||
this.Solr = new _solr2.default({ | ||
host: this.options.host, | ||
core: this.options.core, | ||
managedScheme: this.options.managedScheme, | ||
commitStrategy: this.options.commitStrategy | ||
}); | ||
this.status().then(function () { | ||
_this.describe().then(function () { | ||
_this.define().catch(function (err) { | ||
debug('Service.define Error:', err); | ||
}); | ||
}).catch(function (err) { | ||
debug('Service.describe Error:', err); | ||
}); | ||
}).catch(function (err) { | ||
debug('Service.status Error:', err); | ||
}); | ||
} | ||
/** | ||
* Describe Solr Schema | ||
* @return {object]} Solr Schema | ||
*/ | ||
_createClass(Service, [{ | ||
key: 'describe', | ||
value: function describe() { | ||
return (0, _utils.describeSchema)(this); | ||
// Create the service. | ||
class Service extends AdapterService { | ||
constructor (options) { | ||
if (!options || !options.Model) { | ||
throw new Error('You must provide a Model (the initialized knex object)'); | ||
} | ||
/** | ||
* Define Sole Schema | ||
* @param {object} params Schema Filds | ||
* @return {[type]} | ||
*/ | ||
}, { | ||
key: 'define', | ||
value: function define(schema) { | ||
this.options.schema = Object.assign({}, this.options.schema, schema); | ||
return (0, _utils.defineSchema)(this); | ||
if (typeof options.name !== 'string') { | ||
throw new Error('No table name specified.'); | ||
} | ||
const { whitelist = [] } = options; | ||
/** | ||
* Solr Status | ||
* @return {object} Solr Status | ||
*/ | ||
super( | ||
Object.assign( | ||
{ | ||
id: 'id' | ||
}, | ||
options, | ||
{ | ||
whitelist: whitelist.concat(['$like', '$notlike', '$ilike', '$and']) | ||
} | ||
) | ||
); | ||
}, { | ||
key: 'status', | ||
value: function status() { | ||
var _this2 = this; | ||
this.table = options.name; | ||
this.schema = options.schema; | ||
} | ||
return new Promise(function (resolve, reject) { | ||
_this2.Solr.coreAdmin().status().then(function (res) { | ||
resolve(res); | ||
}).catch(function (err) { | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
get Model () { | ||
return this.options.Model; | ||
} | ||
/** | ||
* Adapter Find Method | ||
* @param {[type]} params User Query | ||
* @return {[type]} Promise | ||
*/ | ||
get client () { | ||
return this.Model; | ||
} | ||
}, { | ||
key: 'find', | ||
value: function find(params) { | ||
if (!_utils._.has(params.query, '$suggest')) { | ||
return this.search(params); | ||
} else { | ||
return this.suggest(params); | ||
} | ||
} | ||
_find (params = {}) { | ||
return this.Model.get('query', params); | ||
} | ||
/** | ||
* Adapter Custom Search Method | ||
* @param {object} params User Query | ||
* @return {object} Promise | ||
*/ | ||
_get (id, params = {}) { | ||
return this.Model.get(`query/${id}`, params); | ||
} | ||
}, { | ||
key: 'search', | ||
value: function search(params) { | ||
var _this3 = this; | ||
_create (data, params = {}) { | ||
return this.Model.post('update', data, params); | ||
} | ||
var _self = this; | ||
return new Promise(function (resolve, reject) { | ||
_this3.Solr.json((0, _utils.queryJson)(params, _self.options)).then(function (res) { | ||
debug('Service.find', params, res); | ||
resolve((0, _utils.responseFind)(params, _self.options, res)); | ||
}).catch(function (err) { | ||
debug('Service.find ERROR:', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
_patch (id, data, params = {}) { | ||
return this.Model.post('update', data, params); | ||
} | ||
/** | ||
* Adapter Custom Suggest Method | ||
* @param {object} params Query Object | ||
* @return {object} Promise | ||
*/ | ||
_remove (id, params = {}) { | ||
return this.Model.post('update', params); | ||
} | ||
}, { | ||
key: 'suggest', | ||
value: function suggest(params) { | ||
var _this4 = this; | ||
_update (id, data, params = {}) { | ||
return this.Model.post('update', data, params); | ||
} | ||
} | ||
var _self = this; | ||
return new Promise(function (resolve, reject) { | ||
_this4.Solr.suggest((0, _utils.querySuggest)(params, _self.options)).then(function (res) { | ||
debug('Service.suggest', params, res); | ||
resolve(res); | ||
}).catch(function (err) { | ||
debug('Service.suggest ERROR:', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Adapter Get Method | ||
* @param {[type]} params User Query | ||
* @return {[type]} Promise | ||
*/ | ||
}, { | ||
key: 'get', | ||
value: function get(id, params) { | ||
var _this5 = this; | ||
var _self = this; | ||
params = Object.assign({ query: {} }, params, { $limit: 1, $skip: 0 }); | ||
params.query[_self.options.idfield] = id; | ||
return new Promise(function (resolve, reject) { | ||
_this5.Solr.json((0, _utils.queryJson)(params, _self.options)).then(function (res) { | ||
var docs = (0, _utils.responseGet)(res); | ||
if (typeof docs !== 'undefined') { | ||
return resolve(docs); | ||
} else { | ||
return reject(new _feathersErrors2.default.NotFound('No record found for id \'' + id + '\'')); | ||
} | ||
}).catch(function (err) { | ||
console.log('err', err); | ||
return reject(new _feathersErrors2.default.NotFound('No record found for id \'' + id + '\'')); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Adapter Create Method | ||
* @param {[type]} params User Query | ||
* @return {[type]} Promise | ||
*/ | ||
}, { | ||
key: 'create', | ||
value: function create(data) { | ||
var _this6 = this; | ||
return new Promise(function (resolve, reject) { | ||
_this6.Solr.update(data).then(function (res) { | ||
if (res.responseHeader.status === 0) { | ||
resolve(data); | ||
} else { | ||
console.log('res', res); | ||
return reject(new _feathersErrors2.default.BadRequest(res)); | ||
} | ||
}).catch(function (err) { | ||
console.log('err', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Adapter Find Method | ||
* adapter.update(id, data, params) -> Promise | ||
* | ||
* @param {mixed} id [description] | ||
* @param {object} data Update Data | ||
* @param {object} params User Query | ||
* @return {object} Promise | ||
*/ | ||
}, { | ||
key: 'update', | ||
value: function update(id, data) { | ||
if (id === null || Array.isArray(data)) { | ||
return Promise.reject(new _feathersErrors2.default.BadRequest('You can not replace multiple instances. Did you mean \'patch\'?')); | ||
} | ||
var _self = this; | ||
data[_self.options.idfield] = id; | ||
return new Promise(function (resolve, reject) { | ||
_self.create(data).then(function () { | ||
resolve(data); | ||
}).catch(function (err) { | ||
debug('Service.update ERROR:', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* adapter.patch(id, data, params) -> Promise | ||
* Using update / overide the doc instead of atomic | ||
* field update http://yonik.com/solr/atomic-updates/ | ||
* @param {[type]} id [description] | ||
* @param {[type]} data [description] | ||
* @param {[type]} params [description] | ||
* @return {[type]} [description] | ||
*/ | ||
/** | ||
* adapter.patch(id, data, params) -> Promise | ||
* Atomic Field Update http://yonik.com/solr/atomic-updates/ | ||
* set – set or replace a particular value, or remove the value if null is specified as the new value | ||
* add – adds an additional value to a list | ||
* remove – removes a value (or a list of values) from a list | ||
* removeregex – removes from a list that match the given Java regular expression | ||
* inc – increments a numeric value by a specific amount (use a negative value to decrement) | ||
* @param {mixed} id ID Optional for single update | ||
* @param {object} data Patch Data | ||
* @param {mixed} query Query Optional for Multiple Updates | ||
* @return {object} Status | ||
*/ | ||
}, { | ||
key: 'patch', | ||
value: function patch(id, data, params) { | ||
var _self = this; | ||
return new Promise(function (resolve, reject) { | ||
if (id === null && (!_utils._.isObject(params) || _utils._.isEmpty(params))) { | ||
return reject(new _feathersErrors2.default.BadRequest('Missing Params')); | ||
} | ||
var patchData = (0, _utils.queryPatch)(data); | ||
var createData = []; | ||
if (id !== null) { | ||
patchData[_self.options.idfield] = id; | ||
createData.push(patchData); | ||
_self.create(createData).then(function (res) { | ||
return resolve(createData); | ||
}).catch(function (err) { | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
} else { | ||
var query = params.query || {}; | ||
query.$select = [_self.options.idfield]; | ||
_self.Solr.json((0, _utils.queryJson)({ query: query }, _self.options)).then(function (response) { | ||
response = (0, _utils.responseFind)(query, _self.options, response); | ||
if (response.data.length > 0) { | ||
response.data.forEach(function (doc, index) { | ||
var ref = {}; | ||
ref[_self.options.idfield] = doc[_self.options.idfield]; | ||
createData.push(Object.assign({}, patchData, ref)); | ||
}); | ||
_self.create(createData).then(function (res) { | ||
return resolve(createData); | ||
}).catch(function (err) { | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
} else { | ||
return resolve(createData); | ||
} | ||
}).catch(function (err) { | ||
debug('Service.patch find ERROR:', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
} | ||
}); | ||
} | ||
/** | ||
* Remove Data | ||
* - .remove('*') = {"delete": {"query": "*:*"},"commit": {}} | ||
* - .remove('*:*') = {"delete": {"query": "*:*"},"commit": {}} | ||
* - .remove('987987FGHJSD') = {"delete": "262","commit": {}} | ||
* - .remove(['987987FGHJSD','987987FGHJSD']) = {"delete": ["262"],"commit": {}} | ||
* - .remove(null,{'*':'*'}) = {"delete": {"query": "*:*"},"commit": {}} | ||
* - .remove(null,{id:257}) = {"delete": {"query": "id:257"},"commit": {}} | ||
* - .remove(null,{id:*}) = {"delete": {"query": "id:*"},"commit": {}} | ||
* - .remove(null,{other:*}) = {"delete": {"query": "other:257"},"commit": {}} | ||
* @param {[type]} id [description] | ||
* @param {[type]} params [description] | ||
* @return {[type]} [description] | ||
*/ | ||
}, { | ||
key: 'remove', | ||
value: function remove(id, params) { | ||
var _this7 = this; | ||
return new Promise(function (resolve, reject) { | ||
_this7.Solr.delete((0, _utils.queryDelete)(id, params || null)).then(function (res) { | ||
resolve(res); | ||
}).catch(function (err) { | ||
debug('Service.remove ERROR:', err); | ||
return reject(new _feathersErrors2.default.BadRequest(err)); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Get Solr Client and use additional functions | ||
* @return {[type]} [description] | ||
*/ | ||
}, { | ||
key: 'client', | ||
value: function client() { | ||
return this.Solr; | ||
} | ||
}]); | ||
return Service; | ||
}(); | ||
function init(options) { | ||
module.exports = function init (options) { | ||
return new Service(options); | ||
} | ||
}; | ||
init.Service = Service; | ||
module.exports = exports.default; | ||
module.exports.Service = Service; | ||
module.exports.Client = Client; |
103
package.json
{ | ||
"name": "feathers-solr", | ||
"description": "Solr Adapter for Feathersjs", | ||
"version": "1.1.16", | ||
"description": "A service plugin for Solr", | ||
"version": "2.1.0", | ||
"homepage": "https://github.com/sajov/feathers-solr", | ||
"main": "lib/", | ||
"keywords": [ | ||
"feathers", | ||
"feathers-plugin", | ||
"solr", | ||
"client", | ||
"adapter", | ||
"feathers", | ||
"feathers-plugin" | ||
"solr-client" | ||
], | ||
"license": "MIT", | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "https://github.com/sajov/feathers-solr/blob/master/LICENSE" | ||
} | ||
], | ||
"repository": { | ||
@@ -20,5 +23,3 @@ "type": "git", | ||
"author": { | ||
"name": "sajov", | ||
"email": "info@sajo-media.de", | ||
"url": "https://feathersjs.com" | ||
"name": "Sascha Jovanoski" | ||
}, | ||
@@ -30,24 +31,24 @@ "contributors": [], | ||
"engines": { | ||
"node": ">= 4.6.0" | ||
"node": ">= 6" | ||
}, | ||
"main": "lib/", | ||
"types": "types", | ||
"scripts": { | ||
"prepublish": "npm run compile", | ||
"NOpublish": "git push origin && git push origin --tags", | ||
"publish": "git push origin --tags && npm run changelog && git push origin", | ||
"changelog": "github_changelog_generator && git add CHANGELOG.md && git commit -am \"Updating changelog\"", | ||
"release:patch": "npm version patch && npm publish", | ||
"release:minor": "npm version minor && npm publish", | ||
"release:major": "npm version major && npm publish", | ||
"compile": "rimraf lib/ && babel -d lib/ src/", | ||
"watch": "babel --watch -d lib/ src/", | ||
"jshint": "jshint src/. test/. --config", | ||
"mocha": "mocha --recursive test/ --require babel-core/register", | ||
"test": "npm run compile && npm run jshint && npm run mocha", | ||
"test1": "npm run compile && npm run jshint && mocha --recursive test/adapter.test.js --require babel-core/register", | ||
"test2": "npm run compile && npm run jshint && mocha --recursive test/adapter.test.js --require babel-core/register", | ||
"test3": "npm run compile && npm run jshint && mocha --recursive test/client/configApi.test.js --require babel-core/register", | ||
"test4": "npm run compile && npm run jshint && mocha --recursive test/client/schemaApi.test.js --require babel-core/register", | ||
"test5": "npm run compile && npm run jshint && mocha --recursive test/query.test.js --require babel-core/register", | ||
"start": "npm run compile && node example/app", | ||
"test-cov": "node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha -- --require babel-core/register --colors test/*", | ||
"travis": "node_modules/.bin/istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require babel-core/register --colors --reporter spec test/" | ||
"lint": "semistandard --fix", | ||
"dtslint": "dtslint types", | ||
"mocha": "mocha --opts mocha.opts", | ||
"test": "npm run lint && npm run dtslint && npm run coverage", | ||
"example": "babel-node example/app", | ||
"coverage": "shx rm -rf *.sqlite && istanbul cover node_modules/mocha/bin/_mocha -- --opts mocha.opts" | ||
}, | ||
"semistandard": { | ||
"env": [ | ||
"mocha" | ||
] | ||
}, | ||
"directories": { | ||
@@ -57,40 +58,24 @@ "lib": "lib" | ||
"dependencies": { | ||
"@feathersjs/adapter-commons": "^4.3.8", | ||
"@feathersjs/commons": "^4.3.0", | ||
"@feathersjs/errors": "^4.3.4", | ||
"debug": "^4.1.1", | ||
"feathers-errors": "^2.9.2", | ||
"request": "^2.88.0", | ||
"request-promise": "^4.2.4" | ||
"is-plain-object": "^3.0.0", | ||
"node-fetch": "^2.6.0", | ||
"qs": "^6.9.1", | ||
"req-fast": "^0.2.19" | ||
}, | ||
"babel": { | ||
"presets": [ | ||
"es2015" | ||
] | ||
}, | ||
"eslintConfig": { | ||
"parser": "babel-eslint" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-core": "^6.26.3", | ||
"babel-eslint": "^10.0.3", | ||
"babel-plugin-add-module-exports": "^1.0.2", | ||
"babel-preset-es2015": "^6.24.1", | ||
"@feathersjs/adapter-tests": "^4.3.4", | ||
"@feathersjs/express": "^4.3.5", | ||
"@feathersjs/feathers": "^4.3.4", | ||
"body-parser": "^1.19.0", | ||
"chai": "^4.2.0", | ||
"coveralls": "^3.0.6", | ||
"faker": "^4.1.0", | ||
"feathers": "^2.2.4", | ||
"feathers-hooks": "^2.1.2", | ||
"feathers-rest": "^1.8.1", | ||
"feathers-socketio": "^2.0.1", | ||
"ink-docstrap": "^1.3.2", | ||
"istanbul": "1.0.0-alpha.2", | ||
"jsdoc-to-markdown": "^5.0.1", | ||
"jshint": "^2.10.2", | ||
"mocha": "^6.2.0", | ||
"mocha-coveralls-reporter": "^0.0.5", | ||
"mocha-lcov-reporter": "^1.3.0", | ||
"nsp": "^3.2.1", | ||
"rimraf": "^3.0.0", | ||
"supertest": "^4.0.2" | ||
"chai-as-promised": "^7.1.1", | ||
"dtslint": "^2.0.2", | ||
"istanbul": "^1.1.0-alpha.1", | ||
"loud-rejection": "^2.2.0", | ||
"mocha": "^6.2.1", | ||
"semistandard": "^14.2.0" | ||
} | ||
} |
686
README.md
@@ -1,544 +0,354 @@ | ||
# feathers-solr | ||
# feathers-knex | ||
[![Build Status](https://travis-ci.org/sajov/feathers-solr.png?branch=master)](https://travis-ci.org/sajov/feathers-solr) | ||
[![Coverage Status](https://coveralls.io/repos/github/sajov/feathers-solr/badge.svg?branch=master)](https://coveralls.io/github/sajov/feathers-solr?branch=master) | ||
[![dependencies Status](https://david-dm.org/sajov/feathers-solr/status.svg)](https://david-dm.org/sajov/feathers-solr) | ||
[![Known Vulnerabilities](https://snyk.io/test/npm/feathers-solr/badge.svg)](https://snyk.io/test/npm/feathers-solr) | ||
> Solr Adapter for Feathersjs. Can also used as a Solr-client. See [additional-client-methods](https://github.com/sajov/feathers-solr/blob/master/README.md#additional-client-methods) | ||
> Require >= Solr 5.x | ||
[![Greenkeeper badge](https://badges.greenkeeper.io/feathersjs-ecosystem/feathers-knex.svg)](https://greenkeeper.io/) | ||
## Online Demo | ||
[feather-solr](http://feathers-solr.sajo-media.de/) | ||
This demonstrate ease of a single query | ||
[![Build Status](https://travis-ci.org/feathersjs-ecosystem/feathers-knex.png?branch=master)](https://travis-ci.org/feathersjs-ecosystem/feathers-knex) | ||
[![Dependency Status](https://img.shields.io/david/feathersjs-ecosystem/feathers-knex.svg?style=flat-square)](https://david-dm.org/feathersjs-ecosystem/feathers-knex) | ||
[![Download Status](https://img.shields.io/npm/dm/feathers-knex.svg?style=flat-square)](https://www.npmjs.com/package/feathers-knex) | ||
## Installation | ||
A database adapter for [KnexJS](http://knexjs.org/), an SQL query builder for Postgres, MSSQL, MySQL, MariaDB, SQLite3, and Oracle. | ||
```bash | ||
npm install --save mysql knex feathers-knex | ||
``` | ||
npm install feathers-solr --save | ||
``` | ||
## Documentation | ||
> __Important:__ `feathers-knex` implements the [Feathers Common database adapter API](https://docs.feathersjs.com/api/databases/common.html) and [querying syntax](https://docs.feathersjs.com/api/databases/querying.html). | ||
Please refer to the [Feathers database adapter documentation](http://docs.feathersjs.com/databases/readme.html) for more details or directly at: | ||
<!-- --> | ||
- [Service methods](https://docs.feathersjs.com/api/databases/common.html#service-methods) - How to use a database adapter | ||
- [Pagination and Sorting](https://docs.feathersjs.com/api/databases/common.html#pagination) - How to use pagination and sorting for the database adapter | ||
- [Querying](https://docs.feathersjs.com/api/databases/querying.html) - The common adapter querying mechanism | ||
- [Extending](https://docs.feathersjs.com/api/databases/common.html#extending-adapters) - How to extend a database adapter | ||
> **Note:** You also need to [install the database driver](http://knexjs.org/#Installation-node) for the DB you want to use. | ||
## Getting Started | ||
## API | ||
### Install Solr | ||
### `service(options)` | ||
``` | ||
bin/solr start -e schemaless | ||
``` | ||
Returns a new service instance initialized with the given options. | ||
Use feathers-solr/bin/install-solr.sh for a kickstart installation. | ||
```js | ||
const knex = require('knex'); | ||
const service = require('feathers-knex'); | ||
const db = knex({ | ||
client: 'sqlite3', | ||
connection: { | ||
filename: './db.sqlite' | ||
} | ||
}); | ||
## Options | ||
// Create the schema | ||
db.schema.createTable('messages', table => { | ||
table.increments('id'); | ||
table.string('text'); | ||
}); | ||
| Option | Default | Description | | ||
| ---------------- | ---------------------------------------------------------- | ------------------------------------------------------------------ | | ||
| host | http://localhost:8983/solr | | | ||
| core | /gettingstarted | | | ||
| schema | false | {title: {type:"string"}} | | ||
| migrate | alter | *safe*, *alter* and *drop* (delete all data and reset schema) | | ||
| idfield | 'id' | Unique Document identifier | | ||
| commitStrategy | {softCommit: true, commitWithin: 50000, overwrite: true} | | | ||
| paginate | {default: 10, max: 100} | | | ||
## Managed Schema | ||
[Schemaless Mode](https://lucene.apache.org/solr/guide/6_6/schemaless-mode.html) is recommended. | ||
Use [Solr Field Types](https://cwiki.apache.org/confluence/display/solr/Solr+Field+Types) and [Field Type Definitions and Properties](https://cwiki.apache.org/confluence/display/solr/Field+Type+Definitions+and+Properties) to define Model properties | ||
```javascript | ||
{ | ||
title: { | ||
type: "text_general", // For more flexible searching. Default type is 'string' | ||
stored: true, // default, keep value visible in results | ||
indexed: true, // default, make it searchable | ||
multiValued: false, // default, true becomes an array field | ||
} | ||
} | ||
app.use('/messages', service({ | ||
Model: db, | ||
name: 'messages' | ||
})); | ||
app.use('/messages', service({ Model, name, id, events, paginate })); | ||
``` | ||
See your current schema definition | ||
__Options:__ | ||
``` | ||
http://localhost:8983/solr/gettingstarted/schema/ | ||
``` | ||
- `Model` (**required**) - The KnexJS database instance | ||
- `name` (**required**) - The name of the table | ||
- `schema` (*optional*) - The name of the schema table prefix (example: `schema.table`) | ||
- `id` (*optional*, default: `'id'`) - The name of the id field property. | ||
- `events` (*optional*) - A list of [custom service events](https://docs.feathersjs.com/api/events.html#custom-events) sent by this service | ||
- `paginate` (*optional*) - A [pagination object](https://docs.feathersjs.com/api/databases/common.html#pagination) containing a `default` and `max` page size | ||
- `multi` (*optional*) - Allow `create` with arrays and `update` and `remove` with `id` `null` to change multiple items. Can be `true` for all methods or an array of allowed methods (e.g. `[ 'remove', 'create' ]`) | ||
- `whitelist` (*optional*) - A list of additional query parameters to allow (e..g `[ '$regex', '$geoNear' ]`). Default is the supported `operators` | ||
### `adapter.createQuery(query)` | ||
Returns a KnexJS query with the [common filter criteria](https://docs.feathersjs.com/api/databases/querying.html) (without pagination) applied. | ||
## Complete Example | ||
### params.knex | ||
Here's an example of a Feathers server that uses `feathers-solr`. | ||
When making a [service method](https://docs.feathersjs.com/api/services.html) call, `params` can contain an `knex` property which allows to modify the options used to run the KnexJS query. See [customizing the query](#customizing-the-query) for an example. | ||
```javascript | ||
const feathers = require('feathers'); | ||
const rest = require('feathers-rest'); | ||
const hooks = require('feathers-hooks'); | ||
const bodyParser = require('body-parser'); | ||
const errorHandler = require('feathers-errors/handler'); | ||
const solr = require('feathers-solr'); | ||
## Example | ||
const Service = new solr.Service({ | ||
host: 'http://localhost:8983/solr', | ||
core: '/gettingstarted', | ||
schema:{ | ||
name: 'text_general', | ||
company: 'text_general', | ||
email: 'text_general', | ||
age: 'int', | ||
gender: 'string', | ||
color: { | ||
type: 'string', | ||
multiValued: true, | ||
}, | ||
address: { | ||
type: 'string', | ||
default: 'Düsseldorf' | ||
} | ||
}, | ||
paginate: { | ||
default: 10, | ||
max: 100 | ||
}); | ||
Here's a complete example of a Feathers server with a `messages` SQLite service. We are using the [Knex schema builder](http://knexjs.org/#Schema) and [SQLite](https://sqlite.org/) as the database. | ||
const app = feathers() | ||
.configure(rest()) | ||
.configure(hooks()) | ||
.use(bodyParser.json()) | ||
.use(bodyParser.urlencoded({ extended: true })) | ||
.use('/solr', Service()) | ||
.use(errorHandler()); | ||
``` | ||
$ npm install @feathersjs/feathers @feathersjs/errors @feathersjs/express @feathersjs/socketio feathers-knex knex sqlite3 | ||
``` | ||
In `app.js`: | ||
app.listen(3030); | ||
```js | ||
const feathers = require('@feathersjs/feathers'); | ||
const express = require('@feathersjs/express'); | ||
const socketio = require('@feathersjs/socketio'); | ||
console.log('Feathers app started on 127.0.0.1:3030'); | ||
const service = require('feathers-knex'); | ||
const knex = require('knex'); | ||
``` | ||
const db = knex({ | ||
client: 'sqlite3', | ||
connection: { | ||
filename: './db.sqlite' | ||
} | ||
}); | ||
// Create a feathers instance. | ||
const app = express(feathers()); | ||
// Turn on JSON parser for REST services | ||
app.use(express.json()); | ||
// Turn on URL-encoded parser for REST services | ||
app.use(express.urlencoded({ extended: true })); | ||
// Enable REST services | ||
app.configure(express.rest()); | ||
// Enable Socket.io services | ||
app.configure(socketio()); | ||
// Create Knex Feathers service with a default page size of 2 items | ||
// and a maximum size of 4 | ||
app.use('/messages', service({ | ||
Model: db, | ||
name: 'messages', | ||
paginate: { | ||
default: 2, | ||
max: 4 | ||
} | ||
})) | ||
app.use(express.errorHandler()); | ||
### Run Demo App | ||
// Clean up our data. This is optional and is here | ||
// because of our integration tests | ||
db.schema.dropTableIfExists('messages').then(() => { | ||
console.log('Dropped messages table'); | ||
``` | ||
node /example/app.js | ||
``` | ||
// Initialize your table | ||
return db.schema.createTable('messages', table => { | ||
console.log('Creating messages table'); | ||
table.increments('id'); | ||
table.string('text'); | ||
}); | ||
}).then(() => { | ||
// Create a dummy Message | ||
app.service('messages').create({ | ||
text: 'Message created on server' | ||
}).then(message => console.log('Created message', message)); | ||
}); | ||
## Support all Feathers Queries | ||
See [Feathers querying](https://docs.feathersjs.com/api/databases/querying.html) for more detail | ||
// Start the server. | ||
const port = 3030; | ||
## Supported Solr Queries | ||
### $search | ||
Simple query | ||
```javascript | ||
query: { | ||
$search: "John" | ||
} | ||
app.listen(port, () => { | ||
console.log(`Feathers server listening on port ${port}`); | ||
}); | ||
``` | ||
'$search' will try to match against Solr default search field '_text_' [Schemaless Mode](https://cwiki.apache.org/confluence/display/solr/Schemaless+Mode) | ||
Run the example with `node app` and go to [localhost:3030/messages](http://localhost:3030/messages). | ||
## Querying | ||
In addition to the [common querying mechanism](https://docs.feathersjs.com/api/databases/querying.html), this adapter also supports: | ||
More complex query with a default Solr configuration. | ||
### $and | ||
```javascript | ||
query: { | ||
$search: "John !Doe +age:[80 TO *]", // Search in default field _text_. See Solr copy field `copy:* to _text_` | ||
// $params: { | ||
// qf: "name^10 friends" define explicit fields to query and boost | ||
// } | ||
// or $search: "name:John^10 AND !name:Doe AND age:[80 TO *]", | ||
// or $search: "joh*", | ||
// or $search: '"john doe"', | ||
// or $search: 'jon~', | ||
} | ||
``` | ||
Find all records that match all of the given criteria. The following query retrieves all messages that have foo and bar attributes as true. | ||
### $params | ||
Add all kind of Solr query params! | ||
Combine huge Solr Features like *facets*, *stats*, *ranges*, *grouping* and more with the default response. | ||
This example will group the result. | ||
```javascript | ||
query: { | ||
$params: { | ||
group : true, | ||
"group.field" : "country", | ||
"group.format" : "simple", | ||
} | ||
} | ||
```js | ||
app.service('messages').find({ | ||
query: { | ||
$and: [ | ||
{foo: true}, | ||
{bar: true} | ||
] | ||
} | ||
}); | ||
``` | ||
Feathers Rest query | ||
Through the REST API: | ||
``` | ||
http://localhost:3030/solr?$params[group]=true&$params[group.field]=gender&$params[group.field]=age&$params[group.limit]=1&$params[group.format]=grouped&$select=id,age,gender | ||
/messages?$and[][foo]=true&$and[][bar]=true | ||
``` | ||
Feathers Result | ||
### $like | ||
```javascript | ||
{ | ||
"QTime": 0, | ||
"total": 0, | ||
"limit": 10, | ||
"skip": 0, | ||
"data": { | ||
"gender": { | ||
"matches": 50, | ||
"groups": [ | ||
{ | ||
"groupValue": "male", | ||
"doclist": { | ||
"numFound": 24, | ||
"start": 0, | ||
"docs": [ | ||
{ | ||
"id": "59501959f2786e0207a8b29f", | ||
"age": "45", | ||
"gender": "male" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"groupValue": "female", | ||
"doclist": { | ||
"numFound": 26, | ||
"start": 0, | ||
"docs": [ | ||
{ | ||
"id": "595019590a8632fecd292592", | ||
"age": "51", | ||
"gender": "female" | ||
} | ||
] | ||
} | ||
} | ||
] | ||
}, | ||
"age": { | ||
"matches": 50, | ||
"groups": [ | ||
{ | ||
"groupValue": "45", | ||
"doclist": { | ||
"numFound": 3, | ||
"start": 0, | ||
"docs": [ | ||
{ | ||
"id": "59501959f2786e0207a8b29f", | ||
"age": "45", | ||
"gender": "male" | ||
} | ||
] | ||
} | ||
}, | ||
{ | ||
"groupValue": "51", | ||
"doclist": { | ||
"numFound": 2, | ||
"start": 0, | ||
"docs": [ | ||
{ | ||
"id": "595019590a8632fecd292592", | ||
"age": "51", | ||
"gender": "female" | ||
} | ||
] | ||
} | ||
} | ||
] | ||
Find all records where the value matches the given string pattern. The following query retrieves all messages that start with `Hello`: | ||
```js | ||
app.service('messages').find({ | ||
query: { | ||
text: { | ||
$like: 'Hello%' | ||
} | ||
} | ||
} | ||
}); | ||
``` | ||
Through the REST API: | ||
### $facet Functions and Analytics | ||
See [Solr Facet Functions and Analytics](http://yonik.com/solr-facet-functions/) | ||
``` | ||
/messages?text[$like]=Hello% | ||
``` | ||
|Aggregation|Example|Effect| | ||
|--- |--- |--- | | ||
|sum|sum(sales)|summation of numeric values| | ||
|avg|avg(popularity)|average of numeric values| | ||
|sumsq|sumsq(rent)|sum of squares| | ||
|min|min(salary)|minimum value| | ||
|max|max(mul(price,popularity))|maximum value| | ||
|unique|unique(state)|number of unique values (count distinct)| | ||
|hll|hll(state)|number of unique values using the HyperLogLog algorithm| | ||
|percentile|percentile(salary,50,75,99,99.9)|calculates percentiles| | ||
### $notlike | ||
The opposite of `$like`; resulting in an SQL condition similar to this: `WHERE some_field NOT LIKE 'X'` | ||
```javascript | ||
query: { | ||
$facet: { | ||
age_avg : "avg(age)", | ||
age_sum : "sum(age)" | ||
```js | ||
app.service('messages').find({ | ||
query: { | ||
text: { | ||
$notlike: '%bar' | ||
} | ||
} | ||
} | ||
}); | ||
``` | ||
### $facet Ranges | ||
Add a facet type range | ||
Through the REST API: | ||
```javascript | ||
query: { | ||
$facet: { | ||
age_ranges: { | ||
type: "range", | ||
field: "age", | ||
start: 0, | ||
end: 100, | ||
gap: 25 | ||
} | ||
} | ||
} | ||
``` | ||
/messages?text[$notlike]=%bar | ||
``` | ||
Feathers Rest query | ||
### $ilike | ||
``` | ||
http://localhost:3030/solr?&$facet[age_ranges][type]=range&$facet[age_ranges][field]=age&$facet[age_ranges][start]=0&$facet[age_ranges][end]=100&$facet[age_ranges][gap]=25&$facet[age_avg]=avg(age)&$facet[age_sum]=sum(age) | ||
``` | ||
For PostgreSQL only, the keywork $ilike can be used instead of $like to make the match case insensitive. The following query retrieves all messages that start with `hello` (case insensitive): | ||
Feathers Result | ||
```javascript | ||
{ | ||
QTime: 0, | ||
total: 50, | ||
limit: 10, | ||
skip: 0, | ||
data: [...], | ||
facet: { | ||
age_avg: 29.44, | ||
age_sum: 1472, | ||
count: 54, | ||
age_ranges: { | ||
buckets: [{ | ||
val: 0, | ||
count: 4 | ||
}, { | ||
val: 25, | ||
count: 17 | ||
}, { | ||
val: 50, | ||
count: 15 | ||
}, { | ||
val: 75, | ||
count: 14 | ||
}] | ||
} | ||
```js | ||
app.service('messages').find({ | ||
query: { | ||
text: { | ||
$ilike: 'hello%' | ||
} | ||
} | ||
} | ||
}); | ||
``` | ||
See more query variants [JSON Facet API](http://yonik.com/json-facet-api/),[Solr Facet Functions and Analytics](http://yonik.com/solr-facet-functions/), [Solr Subfacets](http://yonik.com/solr-subfacets/), [Multi-Select Faceting](http://yonik.com/multi-select-faceting/) | ||
Through the REST API: | ||
### $suggest | ||
A custom response object for autocompleter suggestions. | ||
See example *app.js* for creating a custom searchcomponent and requesthandler including a spellcheck component | ||
``` | ||
query: { | ||
$suggest: 'Handmake', | ||
$params: {} // to plain solr parameter | ||
} | ||
/messages?text[$ilike]=hello% | ||
``` | ||
Feathers Rest query | ||
``` | ||
http://localhost:3030/solr?&$suggest=Handmake | ||
``` | ||
## Transaction Support | ||
Feathers Result | ||
This is a plain solr response | ||
The Knex adapter comes with three hooks that allows to run service method calls in a transaction. They can be used as application wide (`app.hooks.js`) hooks or per service like this: | ||
```javascript | ||
{ | ||
{ | ||
"responseHeader": { | ||
"status": 0, | ||
"QTime": 1 | ||
}, | ||
"spellcheck": { | ||
"suggestions": [ | ||
"handmake", { | ||
"numFound": 1, | ||
"startOffset": 0, | ||
"endOffset": 8, | ||
"origFreq": 0, | ||
"suggestion": [{ | ||
"word": "handmade", | ||
"freq": 1 | ||
}] | ||
} | ||
], | ||
"correctlySpelled": false, | ||
"collations": [ | ||
"collation", | ||
"handmade" | ||
] | ||
}, | ||
"suggest": { | ||
"suggest": { | ||
"Handmake": { | ||
"numFound": 1, | ||
"suggestions": [{ | ||
"term": "Handmade Wooden Keyboard", | ||
"weight": 0, | ||
"payload": "" | ||
}] | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// A common hooks file | ||
const { hooks } = require('feathers-knex'); | ||
``` | ||
const { transaction } = hooks; | ||
### $spellcheck | ||
This feature add a spellcheck component to the default find result | ||
``` | ||
query: { | ||
$search: "Handmake", | ||
$spellcheck:1, | ||
color: "sky blue", | ||
$limit: 10, | ||
module.exports = { | ||
before: { | ||
all: [ transaction.start() ], | ||
find: [], | ||
get: [], | ||
create: [], | ||
update: [], | ||
patch: [], | ||
remove: [] | ||
}, | ||
} | ||
after: { | ||
all: [ transaction.end() ], | ||
find: [], | ||
get: [], | ||
create: [], | ||
update: [], | ||
patch: [], | ||
remove: [] | ||
}, | ||
error: { | ||
all: [ transaction.rollback() ], | ||
find: [], | ||
get: [], | ||
create: [], | ||
update: [], | ||
patch: [], | ||
remove: [] | ||
} | ||
}; | ||
``` | ||
Feathers Rest query | ||
To use the transactions feature, you must ensure that the three hooks (start, end and rollback) are being used. | ||
``` | ||
http://localhost:3030/solr?$search=Handmake&color=Handmke&color="sky blue"&$limit=10&$spellcheck | ||
``` | ||
At the start of any request, a new transaction will be started. All the changes made during the request to the services that are using the `feathers-knex` will use the transaction. At the end of the request, if sucessful, the changes will be commited. If an error occurs, the changes will be forfeit, all the `creates`, `patches`, `updates` and `deletes` are not going to be commited. | ||
Feathers Result | ||
```javascript | ||
{ | ||
"QTime": 0, | ||
"total": 6, | ||
"limit": 10, | ||
"skip": 0, | ||
"data": [...], | ||
"spellcheck": { | ||
"suggestions": [ | ||
"handmake", { | ||
"numFound": 1, | ||
"startOffset": 0, | ||
"endOffset": 8, | ||
"origFreq": 0, | ||
"suggestion": [{ | ||
"word": "handmade", | ||
"freq": 1 | ||
}] | ||
} | ||
], | ||
"correctlySpelled": false, | ||
"collations": [ | ||
"collation", | ||
"handmade" | ||
] | ||
}, | ||
``` | ||
The object that contains `transaction` is stored in the `params.transaction` of each request. | ||
### Adapter.patch | ||
> __Important:__ If you call another Knex service within a hook and want to share the transaction you will have to pass `context.params.transaction` in the parameters of the service call. | ||
Support simple usage [Feathers Docs](https://docs.feathersjs.com/api/services.html#patchid-data-params) | ||
``` | ||
data: {views: 1}; | ||
Adapter.patch(id, data, params); | ||
``` | ||
## Customizing the query | ||
Support also advanced Solr Atomic Field Update [Solr Docs](https://lucene.apache.org/solr/guide/6_6/updating-parts-of-documents.html) | ||
In a `find` call, `params.knex` can be passed a KnexJS query (without pagination) to customize the find results. | ||
``` | ||
data: {views: {inc:1}}; // inc, set, add, remove, removeregex | ||
Adapter.patch(id, data, params); | ||
``` | ||
Combined with `.createQuery({ query: {...} })`, which returns a new KnexJS query with the [common filter criteria](https://docs.feathersjs.com/api/databases/querying.html) applied, this can be used to create more complex queries. The best way to customize the query is in a [before hook](https://docs.feathersjs.com/api/hooks.html) for `find`. | ||
```js | ||
app.service('messages').hooks({ | ||
before: { | ||
find(context) { | ||
const query = context.service.createQuery(context.params); | ||
| ------ | ||
// do something with query here | ||
query.orderBy('name', 'desc'); | ||
## Additional Client Methods | ||
context.params.knex = query; | ||
return context; | ||
} | ||
} | ||
}); | ||
``` | ||
## Configuring migrations | ||
| Solr Api's | Returns a Promise | ./client/requestHandler/ | | ||
| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------------------------- | | ||
| Ping | Adapter.client().ping() | Ping.js | | ||
| [JSON Request API](https://cwiki.apache.org/confluence/display/solr/JSON+Request+API) | Used by Adapter .find() .get() | JsonRequestApi.js | | ||
| [Update](https://cwiki.apache.org/confluence/display/solr/Uploading+Data+with+Index+Handlers#UploadingDatawithIndexHandlers-UpdateRequestHandlerConfiguration) | Used by Adapter .create(), .update() and .patch() | UpdateRequestHandlers.js | | ||
| [SearchHandlers]() | Adapter.client().search() | SearchHandlers.js | | ||
| [Schema API](https://cwiki.apache.org/confluence/display/solr/Managed+Resources) | Adapter.client().schema.method | SchemaApi.js | | ||
| [Config API](https://cwiki.apache.org/confluence/display/solr/Config+API#ConfigAPI-CreatingandUpdatingRequestHandlers) | | ConfigApi.js | | ||
| [CoreAdmin API](https://cwiki.apache.org/confluence/display/solr/CoreAdmin+API) | Adapter.client().coreAdmin.method | CoreAdminApi.js | | ||
| [Solr ConfigSets API](https://cwiki.apache.org/confluence/display/solr/ConfigSets+API) | Adapter.client().configSets.method | ConfigSetsApi.js | | ||
| [Solr Collections API](https://cwiki.apache.org/confluence/display/solr/Collections+API) | Adapter.client().collections.method | CollectionsApi.js | | ||
| [Solr Managed Resources](https://cwiki.apache.org/confluence/display/solr/Managed+Resources) | Adapter.client().resources.method | ManagedResources.js | | ||
| [Request Parameters API](https://cwiki.apache.org/confluence/display/solr/Request+Parameters+API) | Adapter.client().requestParameters.method | RequestParametersAPI.js | | ||
| ~~[Parallel SQL Interface](https://cwiki.apache.org/confluence/display/solr/Parallel+SQL+Interface)~~ | | ParalellSQL.js | | ||
| ~~[ReplicationHandlers](ReplicationHandlers)~~ | | ReplicationHandlers.js | | ||
| ~~[RealTime Get](https://cwiki.apache.org/confluence/display/solr/RealTime+Get)~~ | | RealTime.js | | ||
| ~~[ShardHandlers](https://cwiki.apache.org/confluence/display/solr/RequestHandlers+and+SearchComponents+in+SolrConfig)~~ | | ShardHandlers.js | | ||
| ~~[Solr BolbStore API](https://cwiki.apache.org/confluence/display/solr/Blob+Store+API)~~ | | BlobStoreApi.js | | ||
For using knex's migration CLI, we need to make the configuration available by the CLI. We can do that by providing a `knexfile.js` in the root folder with the following contents: | ||
Not all Solr API's implemented at the moment | ||
```js | ||
const app = require('./src/app') | ||
module.exports = app.get('postgres') | ||
``` | ||
## TODO | ||
* Write more Tests | ||
* Write more Docs | ||
* Implement Parallel SQL Interface | ||
* Implement ReplicationHandlers | ||
* Implement RealTime Get | ||
* Implement ShardHandlers | ||
* Implement Solr BolbStore API | ||
You will need to replace the `postgres` part with the adapter you are using. You will also need to add a `migrations` key to your feathersjs config under your database adapter. Optionally, add a `seeds` key if you will be using [seeds](http://knexjs.org/#Seeds-CLI). | ||
## Changelog | ||
```js | ||
// src/config/default.json | ||
... | ||
"postgres": { | ||
"client": "pg", | ||
"connection": "postgres://user:password@localhost:5432/database", | ||
"migrations": { | ||
"tableName": "knex_migrations" | ||
}, | ||
"seeds": { | ||
"directory": "../src/seeds" | ||
} | ||
} | ||
``` | ||
__1.1.15__ | ||
- add support $between param | ||
- add support for auth | ||
Then, by running: `knex migrate:make create-users`, a `migrations` directory will be created, with the new migration. | ||
__1.1.14__ | ||
- ... | ||
### Error handling | ||
__1.1.13__ | ||
- refactor describe | ||
- refactor define | ||
- add schema tests | ||
- edit docs | ||
As of version 4.0.0 `feathers-knex` only throws [Feathers Errors](https://docs.feathersjs.com/api/errors.html) with the message. On the server, the original error can be retrieved through a secure symbol via `error[require('feathers-knex').ERROR]` | ||
__1.1.12__ | ||
- refactor patch method | ||
```js | ||
const { ERROR } = require('feathers-knex'); | ||
try { | ||
await knexService.doSomething(); | ||
} catch(error) { | ||
// error is a FeathersError with just the message | ||
// Safely retrieve the Knex error | ||
const knexError = error[ERROR]; | ||
} | ||
``` | ||
... | ||
## License | ||
Copyright (c) 2015 | ||
Copyright (c) 2019 | ||
Licensed under the [MIT license](LICENSE). |
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
11
58595
8
15
213
355
3
+ Added@feathersjs/commons@^4.3.0
+ Added@feathersjs/errors@^4.3.4
+ Addedis-plain-object@^3.0.0
+ Addednode-fetch@^2.6.0
+ Addedqs@^6.9.1
+ Addedreq-fast@^0.2.19
+ Added@feathersjs/adapter-commons@4.5.17(transitive)
+ Added@feathersjs/commons@4.5.16(transitive)
+ Added@feathersjs/errors@4.5.17(transitive)
+ Added@feathersjs/feathers@4.5.17(transitive)
+ Addedcall-bind-apply-helpers@1.0.2(transitive)
+ Addedcall-bound@1.0.3(transitive)
+ Addeddebug@2.6.9(transitive)
+ Addeddunder-proto@1.0.1(transitive)
+ Addedes-define-property@1.0.1(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedes-object-atoms@1.1.1(transitive)
+ Addedevents@3.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedget-intrinsic@1.2.7(transitive)
+ Addedget-proto@1.0.1(transitive)
+ Addedgopd@1.2.0(transitive)
+ Addedhas-symbols@1.1.0(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addediconv-lite@0.4.24(transitive)
+ Addedis-plain-object@3.0.1(transitive)
+ Addedlodash.clonedeep@4.5.0(transitive)
+ Addedmath-intrinsics@1.1.0(transitive)
+ Addedms@2.0.0(transitive)
+ Addednode-fetch@2.7.0(transitive)
+ Addedobject-inspect@1.13.4(transitive)
+ Addedqs@6.14.0(transitive)
+ Addedreq-fast@0.2.21(transitive)
+ Addedside-channel@1.1.0(transitive)
+ Addedside-channel-list@1.0.0(transitive)
+ Addedside-channel-map@1.0.1(transitive)
+ Addedside-channel-weakmap@1.0.2(transitive)
+ Addedtr46@0.0.3(transitive)
+ Addedtunnel2@0.0.5(transitive)
+ Addeduberproto@2.0.6(transitive)
+ Addedurijs@1.19.11(transitive)
+ Addeduser-agents@1.1.450(transitive)
+ Addedwebidl-conversions@3.0.1(transitive)
+ Addedwhatwg-url@5.0.0(transitive)
- Removedfeathers-errors@^2.9.2
- Removedrequest@^2.88.0
- Removedrequest-promise@^4.2.4
- Removedajv@6.12.6(transitive)
- Removedasn1@0.2.6(transitive)
- Removedassert-plus@1.0.0(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedaws-sign2@0.7.0(transitive)
- Removedaws4@1.13.2(transitive)
- Removedbcrypt-pbkdf@1.0.2(transitive)
- Removedbluebird@3.7.2(transitive)
- Removedcaseless@0.12.0(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcore-util-is@1.0.2(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddebug@3.2.7(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedecc-jsbn@0.1.2(transitive)
- Removedextend@3.0.2(transitive)
- Removedextsprintf@1.3.0(transitive)
- Removedfast-deep-equal@3.1.3(transitive)
- Removedfast-json-stable-stringify@2.1.0(transitive)
- Removedfeathers-errors@2.9.2(transitive)
- Removedforever-agent@0.6.1(transitive)
- Removedform-data@2.3.3(transitive)
- Removedgetpass@0.1.7(transitive)
- Removedhar-schema@2.0.0(transitive)
- Removedhar-validator@5.1.5(transitive)
- Removedhttp-signature@1.2.0(transitive)
- Removedis-typedarray@1.0.0(transitive)
- Removedisstream@0.1.2(transitive)
- Removedjsbn@0.1.1(transitive)
- Removedjson-schema@0.4.0(transitive)
- Removedjson-schema-traverse@0.4.1(transitive)
- Removedjson-stringify-safe@5.0.1(transitive)
- Removedjsprim@1.4.2(transitive)
- Removedlodash@4.17.21(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedoauth-sign@0.9.0(transitive)
- Removedperformance-now@2.1.0(transitive)
- Removedpsl@1.15.0(transitive)
- Removedpunycode@2.3.1(transitive)
- Removedqs@6.5.3(transitive)
- Removedrequest@2.88.2(transitive)
- Removedrequest-promise@4.2.6(transitive)
- Removedrequest-promise-core@1.1.4(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsshpk@1.18.0(transitive)
- Removedstealthy-require@1.1.1(transitive)
- Removedtough-cookie@2.5.0(transitive)
- Removedtunnel-agent@0.6.0(transitive)
- Removedtweetnacl@0.14.5(transitive)
- Removeduri-js@4.4.1(transitive)
- Removeduuid@3.4.0(transitive)
- Removedverror@1.10.0(transitive)