nmr-predictor
Advanced tools
Comparing version 0.5.3 to 1.0.0
{ | ||
"name": "nmr-predictor", | ||
"version": "0.5.3", | ||
"version": "1.0.0", | ||
"description": "NMR chemical shift predictor", | ||
@@ -18,17 +18,12 @@ "keywords": [], | ||
"scripts": { | ||
"test": "npm run test-mocha", | ||
"test-mocha": "mocha --compilers js:babel-register --require should --reporter mocha-better-spec-reporter --recursive src/**/__tests__/**/*.js", | ||
"build": "cheminfo build --no-uglify" | ||
"eslint": "eslint src test --cache", | ||
"eslint-fix": "npm run eslint -- --fix", | ||
"test": "npm run test-mocha && npm run eslint", | ||
"test-mocha": "mocha --require should --reporter mocha-better-spec-reporter --recursive", | ||
"build": "cheminfo build" | ||
}, | ||
"devDependencies": { | ||
"babel-cli": "^6.2.0", | ||
"babel-core": "^6.2.1", | ||
"babel-plugin-transform-es2015-block-scoping": "^6.1.18", | ||
"babel-preset-es2015-node4": "^2.0.1", | ||
"babel-register": "^6.2.0", | ||
"babelify": "^7.2.0", | ||
"babili": "0.0.8", | ||
"cheminfo-tools": "^1.13.0", | ||
"cheminfo-tools": "^1.15.0", | ||
"eslint": "^3.9.1", | ||
"eslint-config-cheminfo": "^1.5.2", | ||
"eslint-config-cheminfo": "^1.7.0", | ||
"eslint-plugin-no-only-tests": "^1.1.0", | ||
@@ -40,9 +35,7 @@ "mocha": "^3.1.2", | ||
"dependencies": { | ||
"openchemlib-extended": "1.9.1", | ||
"ml-matrix": "^1.1.2", | ||
"ml-matrix": "^2.3.0", | ||
"new-array": "^1.0.0", | ||
"isomorphic-fetch": "2.2.1", | ||
"es6-promise": "4.0.5", | ||
"form-data": "2.1.2" | ||
"openchemlib-extended-minimal": "^2.0.0", | ||
"superagent": "^3.5.0" | ||
} | ||
} |
@@ -18,8 +18,6 @@ # nmr-predictor | ||
``` js | ||
```js | ||
'use strict'; | ||
const NmrPredictor = require('..'); | ||
const request = require('request'); | ||
const fs = require('fs'); | ||
const predictor = require('..'); | ||
@@ -49,8 +47,24 @@ const molfile = `Benzene, ethyl-, ID: C100414 | ||
// 1D proton prediction | ||
predictor.fetchProton().then(function () { | ||
console.log(predictor.proton(molfile)); | ||
}); | ||
var predictor = new NmrPredictor("spinus"); | ||
predictor.predict(molfile).then(prediction => { | ||
console.log(prediction); | ||
// 2D HSQC prediction | ||
Promise.all([ | ||
predictor.fetchProton(), | ||
predictor.fetchCarbon() | ||
]).then(function (dbs) { | ||
return predictor.twod(predictor.proton(molfile), predictor.carbon(molfile), molfile); | ||
}); | ||
// 2D HSQC with spinus | ||
Promise.all([ | ||
predictor.spinus(molfile), | ||
predictor.fetchCarbon() | ||
]).then(function (results) { | ||
return predictor.twod(results[0], predictor.carbon(molfile), molfile); | ||
}); | ||
``` | ||
## License | ||
@@ -60,4 +74,4 @@ | ||
[npm-image]: https://img.shields.io/npm/v/cheminfo-nmr-predictor.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/cheminfo-nmr-predictor | ||
[npm-image]: https://img.shields.io/npm/v/nmr-predictor.svg?style=flat-square | ||
[npm-url]: https://www.npmjs.com/package/nmr-predictor | ||
[travis-image]: https://img.shields.io/travis/cheminfo-js/nmr-predictor/master.svg?style=flat-square | ||
@@ -67,3 +81,3 @@ [travis-url]: https://travis-ci.org/cheminfo-js/nmr-predictor | ||
[david-url]: https://david-dm.org/cheminfo-js/nmr-predictor | ||
[download-image]: https://img.shields.io/npm/dm/cheminfo-nmr-predictor.svg?style=flat-square | ||
[download-url]: https://www.npmjs.com/package/cheminfo-nmr-predictor | ||
[download-image]: https://img.shields.io/npm/dm/nmr-predictor.svg?style=flat-square | ||
[download-url]: https://www.npmjs.com/package/nmr-predictor |
460
src/index.js
@@ -1,419 +0,81 @@ | ||
'use strict' | ||
/** | ||
* Created by acastillo on 7/5/16. | ||
*/ | ||
const OCLE = require('openchemlib-extended'); | ||
const Matrix = require('ml-matrix'); | ||
const newArray = require('new-array'); | ||
require('es6-promise').polyfill(); | ||
require('isomorphic-fetch'); | ||
const FormData = require('form-data'); | ||
'use strict'; | ||
const defaultOptions = {atomLabel:'H', ignoreLabile: true, use: 'median'}; | ||
const superagent = require('superagent'); | ||
class NmrPredictor { | ||
const normalizeOptions = require('./normalizeOptions'); | ||
const queryByHose = require('./queryByHose'); | ||
const spinus = require('./spinus'); | ||
const twoD = require('./twoD'); | ||
constructor(db) { | ||
this.db = db; | ||
} | ||
const defaultProtonUrl = 'https://raw.githubusercontent.com/cheminfo-js/nmr-predictor/master/data/h1.json'; | ||
const defaultCarbonUrl = 'https://raw.githubusercontent.com/cheminfo-js/nmr-predictor/master/data/nmrshiftdb2.json'; | ||
setDB(db) { | ||
this.db = db; | ||
} | ||
const databases = {}; | ||
spinus(molfile, options) { | ||
var mol = molfile; | ||
if(typeof molfile === 'string') { | ||
mol = OCLE.Molecule.fromMolfile(molfile); | ||
mol.addImplicitHydrogens(); | ||
} | ||
let opt = Object.assign({}, defaultOptions, options); | ||
return fromSpinus(mol, opt).then(prediction => {return group(prediction, opt)}); | ||
} | ||
protonSync(molfile, options) { | ||
if(!this.db) { | ||
this.db = {"H": fetchDB("H")}; | ||
} | ||
var mol = molfile; | ||
if(typeof molfile === 'string') { | ||
mol = OCLE.Molecule.fromMolfile(molfile); | ||
mol.addImplicitHydrogens(); | ||
} | ||
let opt = Object.assign({}, defaultOptions, options, {atomLabel: "H"}); | ||
return group(queryByHose(mol, this.db, opt), opt); | ||
} | ||
proton(molfile, options) { | ||
if(!this.db) | ||
this.db = {}; | ||
if(!this.db['H']) { | ||
return fetch('https://raw.githubusercontent.com/cheminfo-js/nmr-predictor/master/data/h1.json', {timeout: 5000}) | ||
.then(value => {return value.text()}).then(body => { | ||
this.db['H'] = JSON.parse(body); | ||
return this.protonSync(molfile, options); | ||
}); | ||
} else { | ||
let result = this.protonSync(molfile, options); | ||
return new Promise(function(resolve, reject){ | ||
resolve(result); | ||
}); | ||
} | ||
} | ||
carbonSync(molfile, options) { | ||
var mol = molfile; | ||
if(typeof molfile === 'string') { | ||
mol = OCLE.Molecule.fromMolfile(molfile); | ||
mol.addImplicitHydrogens(); | ||
} | ||
let opt = Object.assign({}, defaultOptions, options, {atomLabel: "C"}); | ||
return group(queryByHose(mol, this.db, opt), opt); | ||
} | ||
carbon(molfile, options) { | ||
if(!this.db) | ||
this.db = {}; | ||
if(!this.db['C']) { | ||
return fetch('https://raw.githubusercontent.com/cheminfo-js/nmr-predictor/master/data/nmrshiftdb2.json', | ||
{timeout: 20000}) | ||
.then(value => {return value.text()}).then(body => { | ||
this.db['C'] = JSON.parse(body); | ||
return this.carbonSync(molfile, options); | ||
}); | ||
} else { | ||
let result = this.carbonSync(molfile, options); | ||
return new Promise(function(resolve, reject){ | ||
resolve(result); | ||
}); | ||
} | ||
} | ||
twoD(dim1, dim2, molfile, opt) { | ||
var result = this.towDSync(dim1, dim2, molfile, opt); | ||
return new Promise(function(resolve, reject) { | ||
resolve(result); | ||
}); | ||
} | ||
towDSync(dim1, dim2, molfile, opt) { | ||
let mol = molfile; | ||
let fromAtomLabel = ''; | ||
let toAtomLabel = ''; | ||
if(dim1 && dim1.length > 0) { | ||
fromAtomLabel = dim1[0].atomLabel; | ||
} | ||
if(dim2 && dim2.length > 0) { | ||
toAtomLabel = dim2[0].atomLabel; | ||
} | ||
let options = Object.assign({}, {minLength: 1, maxLength:3}, opt, | ||
{fromLabel: fromAtomLabel, toLabel: toAtomLabel}); | ||
if(typeof molfile === 'string') { | ||
mol = OCLE.Molecule.fromMolfile(molfile); | ||
mol.addImplicitHydrogens(); | ||
} | ||
let paths = mol.getAllPaths(options); | ||
let idMap1 = {}; | ||
dim1.forEach(prediction => { | ||
idMap1[prediction["diaIDs"][0]] = prediction; | ||
}); | ||
let idMap2 = {}; | ||
dim2.forEach(prediction => { | ||
idMap2[prediction["diaIDs"][0]] = prediction; | ||
}); | ||
paths.forEach(element => { | ||
element.fromChemicalShift = idMap1[element.fromDiaID].delta; | ||
element.toChemicalShift = idMap2[element.toDiaID].delta; | ||
element.fromAtomLabel = fromAtomLabel; | ||
element.toAtomLabel = toAtomLabel; | ||
//@TODO Add the coupling constants in any case!!!!!! | ||
element.j = getCouplingConstant(idMap1, element.fromDiaID, element.toDiaID); | ||
}); | ||
return paths; | ||
} | ||
function fetchProton(url = defaultProtonUrl, dbName = 'proton') { | ||
return fetch(url, dbName, 'proton'); | ||
} | ||
function group(prediction, param) { | ||
if(param && param.group) { | ||
prediction.sort(function(a, b) { | ||
if(a.diaIDs[0] < b.diaIDs[0]) return -1; | ||
if(a.diaIDs[0] > b.diaIDs[0]) return 1; | ||
return 0; | ||
}); | ||
for(var i = prediction.length - 2; i >= 0; i--){ | ||
if(prediction[i].diaIDs[0] === prediction[i + 1].diaIDs[0]){ | ||
prediction[i].integral += prediction[i + 1].integral; | ||
prediction[i].atomIDs = prediction[i].atomIDs.concat(prediction[i + 1].atomIDs); | ||
prediction.splice(i + 1, 1); | ||
} | ||
} | ||
} | ||
return prediction; | ||
function fetchCarbon(url = defaultCarbonUrl, dbName = 'carbon') { | ||
return fetch(url, dbName, 'carbon'); | ||
} | ||
function getCouplingConstant(idMap, fromDiaID, toDiaID) { | ||
let j = idMap[fromDiaID].j; | ||
if(j) { | ||
let index = j.length - 1; | ||
while(index-- > 0) { | ||
if(j[index].diaID === toDiaID) { | ||
return j[index].coupling; | ||
} | ||
function fetch(url, dbName, type) { | ||
if (databases[dbName] && databases[dbName].type === type && databases[dbName].url === url) { | ||
if (databases[dbName].fetching) { | ||
return databases[dbName].fetching; | ||
} | ||
return Promise.resolve(databases[dbName].db); | ||
} | ||
return 0; | ||
} | ||
/** | ||
* This function towD shift for 1H-NMR, from a molfile by using the cheminfo reference data base. | ||
* @param molfile: string A molfile content | ||
* @returns +Object an array of NMRSignal1D | ||
*/ | ||
function queryByHose(mol, db, options) { | ||
var currentDB = null; | ||
const atomLabel = options.atomLabel || 'H'; | ||
const use = options.use; | ||
if (db) { | ||
currentDB = db[atomLabel]; | ||
} | ||
else { | ||
currentDB = [[], [], [], [], [], [], []]; | ||
} | ||
options.debug = options.debug || false; | ||
var algorithm = options.algorithm || 0; | ||
var levels = options.hoseLevels || [6, 5, 4, 3, 2]; | ||
var couplings = options.getCouplings || false; | ||
levels.sort(function(a, b) { | ||
return b - a; | ||
const database = { | ||
type, | ||
url, | ||
db: null, | ||
fetching: null | ||
}; | ||
databases[dbName] = database; | ||
const fetching = superagent.get(url).then((res) => { | ||
const db = res.body ? res.body : JSON.parse(res.text); | ||
database.db = db; | ||
database.fetching = false; | ||
return db; | ||
}).catch((e) => { | ||
delete databases[dbName]; | ||
throw e; | ||
}); | ||
var diaIDs = mol.getGroupedDiastereotopicAtomIDs({atomLabel: atomLabel}); | ||
var infoCOSY = [];//mol.getCouplings(); | ||
if(couplings) { | ||
// infoCOSY = mol.predictCouplings(); | ||
} | ||
var atoms = {}; | ||
var atomNumbers = []; | ||
var i, k, j, atom, hosesString; | ||
for (j = diaIDs.length - 1; j >= 0; j--) { | ||
hosesString = OCLE.Util.getHoseCodesFromDiastereotopicID(diaIDs[j].oclID, {maxSphereSize: levels[0], type: algorithm}); | ||
atom = { | ||
diaIDs: [diaIDs[j].oclID + ''] | ||
}; | ||
for(k = 0; k < levels.length; k++) { | ||
atom['hose'+levels[k]] = hosesString[levels[k]-1]+''; | ||
} | ||
for (k = diaIDs[j].atoms.length - 1; k >= 0; k--) { | ||
atoms[diaIDs[j].atoms[k]] = JSON.parse(JSON.stringify(atom)); | ||
atomNumbers.push(diaIDs[j].atoms[k]); | ||
} | ||
} | ||
//Now, we towD the chimical shift by using our copy of NMRShiftDB | ||
//var script2 = 'select chemicalShift FROM assignment where ';//hose5='dgH`EBYReZYiIjjjjj@OzP`NET''; | ||
var toReturn = new Array(atomNumbers.length); | ||
for (j = 0; j < atomNumbers.length; j++) { | ||
atom = atoms[atomNumbers[j]]; | ||
var res = null; | ||
k = 0; | ||
//A really simple query | ||
while(res === null && k < levels.length) { | ||
if(currentDB[levels[k]]) { | ||
res = currentDB[levels[k]][atom['hose' + levels[k]]]; | ||
} | ||
k++; | ||
} | ||
if (res == null) { | ||
res = { cs: null, ncs: 0, std: 0, min: 0, max: 0 };//Default values | ||
} | ||
atom.atomLabel = atomLabel; | ||
atom.level = levels[k-1]; | ||
atom.delta = res.cs; | ||
if(use === 'median' && res.median) | ||
atom.delta = res.median; | ||
else if (use === 'mean' && res.mean) | ||
atom.delta = res.mean; | ||
atom.integral = 1; | ||
atom.atomIDs = ['' + atomNumbers[j]]; | ||
atom.ncs = res.ncs; | ||
atom.std = res.std; | ||
atom.min = res.min; | ||
atom.max = res.max; | ||
atom.j = []; | ||
//Add the predicted couplings | ||
//console.log(atomNumbers[j]+' '+infoCOSY[0].atom1); | ||
for (i = infoCOSY.length - 1; i >= 0; i--) { | ||
if (infoCOSY[i].atom1 - 1 == atomNumbers[j] && infoCOSY[i].coupling > 2) { | ||
atom.j.push({ | ||
'assignment': infoCOSY[i].atom2 - 1 + '',//Put the diaID instead | ||
'diaID': infoCOSY[i].diaID2, | ||
'coupling': infoCOSY[i].coupling, | ||
'multiplicity': 'd' | ||
}); | ||
} | ||
} | ||
toReturn[j] = atom; | ||
} | ||
//TODO this will not work because getPaths is not implemented yet!!!! | ||
if(options.ignoreLabile) { | ||
var linksOH = mol.getAllPaths({ | ||
fromLabel: 'H', | ||
toLabel: 'O', | ||
minLength: 1, | ||
maxLength: 1 | ||
}); | ||
var linksNH = mol.getAllPaths({ | ||
fromLabel: 'H', | ||
toLabel: 'N', | ||
minLength: 1, | ||
maxLength: 1 | ||
}); | ||
for(j = toReturn.length-1; j >= 0; j--) { | ||
for(var k = 0; k < linksOH.length; k++) { | ||
if(toReturn[j].diaIDs[0] === linksOH[k].fromDiaID) { | ||
toReturn.splice(j, 1); | ||
break; | ||
} | ||
} | ||
} | ||
//console.log(h1pred.length); | ||
for(j = toReturn.length-1; j >= 0; j--) { | ||
for(var k = 0;k < linksNH.length; k++) { | ||
if(toReturn[j].diaIDs[0] === linksNH[k].fromDiaID) { | ||
toReturn.splice(j, 1); | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
return toReturn; | ||
database.fetching = fetching; | ||
return fetching; | ||
} | ||
function fromSpinus(mol, options){ | ||
let form = new FormData(); | ||
form.append('molfile', mol.toMolfile()); | ||
return fetch('https://www.nmrdb.org/service/predictor', { | ||
method: 'POST', | ||
body: form | ||
}).then(value => {return value.text()}).then(body => { | ||
//Convert to the ranges format and include the diaID for each atomID | ||
const data = spinusParser(body); | ||
const ids = data.ids; | ||
const jc = data.couplingConstants; | ||
const cs = data.chemicalShifts; | ||
const multiplicity = data.multiplicity; | ||
const integrals = data.integrals; | ||
const nspins = cs.length; | ||
const diaIDs = mol.getGroupedDiastereotopicAtomIDs({atomLabel: 'H'}); | ||
var result = new Array(nspins); | ||
var atoms = {}; | ||
var atomNumbers = []; | ||
var i, j, k, oclID, tmpCS; | ||
var csByOclID = {}; | ||
for (j = diaIDs.length - 1; j >= 0; j--) { | ||
oclID = diaIDs[j].oclID + ''; | ||
for (k = diaIDs[j].atoms.length - 1; k >= 0; k--) { | ||
atoms[diaIDs[j].atoms[k]] = oclID; | ||
atomNumbers.push(diaIDs[j].atoms[k]); | ||
if(!csByOclID[oclID]){ | ||
csByOclID[oclID] = {nc: 1, cs: cs[ids[diaIDs[j].atoms[k]]]}; | ||
} else { | ||
csByOclID[oclID].nc++; | ||
csByOclID[oclID].cs += cs[ids[diaIDs[j].atoms[k]]]; | ||
} | ||
} | ||
} | ||
//Average the entries for the equivalent protons | ||
var idsKeys = Object.keys(ids); | ||
for (i = 0; i < nspins; i++) { | ||
tmpCS = csByOclID[atoms[idsKeys[i]]].cs / csByOclID[atoms[idsKeys[i]]].nc; | ||
result[i] = {atomIDs: [idsKeys[i]], diaIDs: [atoms[idsKeys[i]]], integral: integrals[i], | ||
delta: tmpCS, atomLabel: 'H', j: []}; | ||
for (j = 0; j < nspins; j++) { | ||
if(jc[i][j] !== 0 ) { | ||
result[i].j.push({ | ||
'assignment': idsKeys[j], | ||
'diaID': atoms[idsKeys[j]], | ||
'coupling': jc[i][j], | ||
'multiplicity': multiplicityToString(multiplicity[j]) | ||
}); | ||
} | ||
} | ||
} | ||
return result; | ||
}).catch(ex => {return new Error('http request fail ' + ex)}); | ||
function proton(molecule, options) { | ||
[molecule, options] = normalizeOptions(molecule, options); | ||
const db = getDb(options.db || 'proton', 'proton'); | ||
options.atomLabel = 'H'; | ||
return queryByHose(molecule, db, options); | ||
} | ||
function multiplicityToString(mul) { | ||
switch(mul) { | ||
case 2: | ||
return 'd'; | ||
break; | ||
case 3: | ||
return 't'; | ||
break; | ||
case 4: | ||
return 'q'; | ||
break; | ||
default: | ||
return ''; | ||
} | ||
function carbon(molecule, options) { | ||
[molecule, options] = normalizeOptions(molecule, options); | ||
const db = getDb(options.db || 'carbon', 'carbon'); | ||
options.atomLabel = 'C'; | ||
return queryByHose(molecule, db, options); | ||
} | ||
function spinusParser(result){ | ||
var lines = result.split('\n'); | ||
var nspins = lines.length - 1; | ||
var cs = new Array(nspins); | ||
var integrals = new Array(nspins); | ||
var ids = {}; | ||
var jc = Matrix.zeros(nspins, nspins); | ||
var i, j; | ||
for (i = 0; i < nspins; i++) { | ||
var tokens = lines[i].split('\t'); | ||
cs[i] = +tokens[2]; | ||
ids[tokens[0] - 1] = i; | ||
integrals[i] = 1;//+tokens[5];//Is it always 1?? | ||
} | ||
for (i = 0; i < nspins; i++) { | ||
tokens = lines[i].split('\t'); | ||
var nCoup = (tokens.length - 4) / 3; | ||
for (j = 0; j < nCoup; j++) { | ||
var withID = tokens[4 + 3 * j] - 1; | ||
var idx = ids[withID]; | ||
jc[i][idx] = (+tokens[6 + 3 * j]); | ||
} | ||
} | ||
for (j = 0; j < nspins; j++) { | ||
for (i = j; i < nspins; i++) { | ||
jc[j][i] = jc[i][j]; | ||
} | ||
} | ||
return {ids, chemicalShifts: cs, integrals, couplingConstants: jc, multiplicity: newArray(nspins, 2)}; | ||
function getDb(option, type) { | ||
if (typeof option === 'object') return option; | ||
if (typeof option !== 'string') throw new TypeError('database option must be a string or array'); | ||
const db = databases[option]; | ||
if (!db) throw new Error(`database ${option} does not exist. Did you forget to fetch it?`); | ||
if (db.fetching) throw new Error(`database ${option} is not fetched yet`); | ||
if (db.type !== type) throw new Error(`database ${option} is of type ${db.type} instead of ${type}`); | ||
return db.db; | ||
} | ||
module.exports = NmrPredictor; | ||
module.exports = { | ||
fetchProton, | ||
fetchCarbon, | ||
proton, | ||
carbon, | ||
spinus, | ||
twoD | ||
}; |
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
4
7
0
80
1
3
18040
9
377
+ Addedsuperagent@^3.5.0
+ Addedcall-bind-apply-helpers@1.0.1(transitive)
+ Addedcall-bound@1.0.3(transitive)
+ Addedcomponent-emitter@1.3.1(transitive)
+ Addedcookiejar@2.1.4(transitive)
+ Addedcore-util-is@1.0.3(transitive)
+ Addeddebug@3.2.7(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)
+ Addedform-data@2.5.2(transitive)
+ Addedformidable@1.2.6(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)
+ Addedinherits@2.0.4(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedmath-intrinsics@1.1.0(transitive)
+ Addedmethods@1.1.2(transitive)
+ Addedmime@1.6.0(transitive)
+ Addedms@2.1.3(transitive)
+ Addedobject-inspect@1.13.3(transitive)
+ Addedopenchemlib@5.9.1(transitive)
+ Addedopenchemlib-extended@2.4.1(transitive)
+ Addedopenchemlib-extended-minimal@2.0.0(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedqs@6.14.0(transitive)
+ Addedreadable-stream@2.3.8(transitive)
+ Addedsafe-buffer@5.1.25.2.1(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)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedsuperagent@3.8.3(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
- Removedes6-promise@4.0.5
- Removedform-data@2.1.2
- Removedisomorphic-fetch@2.2.1
- Removedopenchemlib-extended@1.9.1
- Removedencoding@0.1.13(transitive)
- Removedes6-promise@4.0.5(transitive)
- Removedform-data@2.1.2(transitive)
- Removediconv-lite@0.6.3(transitive)
- Removedis-stream@1.1.0(transitive)
- Removedisomorphic-fetch@2.2.1(transitive)
- Removedml-array-utils@0.2.4(transitive)
- Removedml-binary-search@1.1.2(transitive)
- Removedml-matrix@1.4.0(transitive)
- Removednode-fetch@1.7.3(transitive)
- Removedopenchemlib@4.7.2(transitive)
- Removedopenchemlib-extended@1.9.1(transitive)
- Removedsafer-buffer@2.1.2(transitive)
- Removedwhatwg-fetch@3.6.20(transitive)
Updatedml-matrix@^2.3.0