isotopic-distribution
Advanced tools
Comparing version 3.1.3 to 3.2.0
768
lib/index.js
@@ -1,753 +0,17 @@ | ||
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
var chemicalElements = require('chemical-elements'); | ||
var mfParser = require('mf-parser'); | ||
var mfUtilities = require('mf-utilities'); | ||
var spectrumGenerator = require('spectrum-generator'); | ||
function closestPointX(array, target) { | ||
let low = 0; | ||
let high = array.length - 1; | ||
let middle = 0; | ||
while (high - low > 1) { | ||
middle = low + ((high - low) >> 1); | ||
if (array[middle].x < target) { | ||
low = middle; | ||
} else if (array[middle].x > target) { | ||
high = middle; | ||
} else { | ||
return array[middle]; | ||
"use strict"; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
var desc = Object.getOwnPropertyDescriptor(m, k); | ||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { | ||
desc = { enumerable: true, get: function() { return m[k]; } }; | ||
} | ||
} | ||
if (low < array.length - 1) { | ||
if (Math.abs(target - array[low].x) < Math.abs(array[low + 1].x - target)) { | ||
return array[low]; | ||
} else { | ||
return array[low + 1]; | ||
} | ||
} else { | ||
return array[low]; | ||
} | ||
} | ||
/** | ||
* Join x values if there are similar | ||
*/ | ||
function joinX(self, threshold = Number.EPSILON) { | ||
// when we join we will use the center of mass | ||
if (self.array.length === 0) return []; | ||
self.sortX(); | ||
let current = self.array[0]; | ||
let result = [current]; | ||
for (let i = 1; i < self.array.length; i++) { | ||
const item = self.array[i]; | ||
if (item.x - current.x <= threshold) { | ||
// weighted sum | ||
current.x = | ||
(item.y / (current.y + item.y)) * (item.x - current.x) + current.x; | ||
current.y += item.y; | ||
} else { | ||
current = { | ||
x: item.x, | ||
y: item.y, | ||
}; | ||
if (item.composition) current.composition = item.composition; | ||
result.push(current); | ||
} | ||
} | ||
self.array = result; | ||
self.ySorted = false; | ||
return self; | ||
} | ||
function multiply(a, b, options = {}) { | ||
const { minY = 1e-8, maxLines = 5000, deltaX = 1e-2 } = options; | ||
const result = new a.constructor(); | ||
a.sortY(); | ||
b.sortY(); | ||
for (let entryA of a.array) { | ||
for (let entryB of b.array) { | ||
let y = entryA.y * entryB.y; | ||
if (y > minY) { | ||
const composition = calculateComposition(entryA, entryB); | ||
if (composition) { | ||
result.push({ x: entryA.x + entryB.x, y, composition }); | ||
} else { | ||
result.push({ x: entryA.x + entryB.x, y }); | ||
} | ||
} | ||
if (result.length > maxLines * 2) { | ||
result.joinX(deltaX); | ||
result.topY(maxLines); | ||
} | ||
} | ||
} | ||
result.joinX(deltaX); | ||
result.topY(maxLines); | ||
a.move(result); | ||
return a; | ||
} | ||
function calculateComposition(entryA, entryB) { | ||
if (!entryA.composition || !entryB.composition) return; | ||
let toReturn = {}; | ||
const keys = [ | ||
...new Set( | ||
Object.keys(entryA.composition).concat(Object.keys(entryB.composition)), | ||
), | ||
]; | ||
for (let key of keys) { | ||
toReturn[key] = | ||
(entryA.composition[key] || 0) + (entryB.composition[key] || 0); | ||
} | ||
return toReturn; | ||
} | ||
// https://en.wikipedia.org/wiki/Exponentiation_by_squaring | ||
function power(array, p, options = {}) { | ||
if (p <= 0) throw new Error('power must be larger than 0'); | ||
if (p === 1) return array; | ||
if (p === 2) { | ||
return array.square(); | ||
} | ||
p--; | ||
let base = array.copy(); // linear time | ||
while (p !== 0) { | ||
if ((p & 1) !== 0) { | ||
array.multiply(base, options); // executed <= log2(p) times | ||
} | ||
p >>= 1; | ||
if (p !== 0) base.square(options); // executed <= log2(p) times | ||
} | ||
return array; | ||
} | ||
/** | ||
* Internal class to deal with isotopic distribution calculations | ||
*/ | ||
class Distribution { | ||
constructor(array = []) { | ||
this.array = array; | ||
this.cache = getEmptyCache(); | ||
} | ||
emptyCache() { | ||
if (this.cache.isEmpty) return; | ||
this.cache = getEmptyCache(); | ||
} | ||
get length() { | ||
return this.array.length; | ||
} | ||
get xs() { | ||
return this.array.map((p) => p.x); | ||
} | ||
get ys() { | ||
return this.array.map((p) => p.y); | ||
} | ||
get sumY() { | ||
if (!isNaN(this.cache.sumY)) return this.cache.sumY; | ||
let sumY = 0; | ||
for (let item of this.array) { | ||
sumY += item.y; | ||
} | ||
this.cache.sumY = sumY; | ||
this.cache.isEmpty = false; | ||
return sumY; | ||
} | ||
get minX() { | ||
if (!isNaN(this.cache.minX)) return this.cache.minX; | ||
let minX = Number.POSITIVE_INFINITY; | ||
for (let item of this.array) { | ||
if (item.x < minX) { | ||
minX = item.x; | ||
} | ||
} | ||
this.cache.minX = minX; | ||
this.cache.isEmpty = false; | ||
return minX; | ||
} | ||
get maxX() { | ||
if (!isNaN(this.cache.maxX)) return this.cache.maxX; | ||
let maxX = Number.NEGATIVE_INFINITY; | ||
for (let item of this.array) { | ||
if (item.x > maxX) { | ||
maxX = item.x; | ||
} | ||
} | ||
this.cache.maxX = maxX; | ||
this.cache.isEmpty = false; | ||
return maxX; | ||
} | ||
get minY() { | ||
if (!isNaN(this.cache.minY)) return this.cache.minY; | ||
let minY = Number.POSITIVE_INFINITY; | ||
for (let item of this.array) { | ||
if (item.y < minY) { | ||
minY = item.y; | ||
} | ||
} | ||
this.cache.minY = minY; | ||
this.cache.isEmpty = false; | ||
return minY; | ||
} | ||
get maxY() { | ||
if (!isNaN(this.cache.maxY)) return this.cache.maxY; | ||
let maxY = Number.NEGATIVE_INFINITY; | ||
for (let item of this.array) { | ||
if (item.y > maxY) { | ||
maxY = item.y; | ||
} | ||
} | ||
this.cache.maxY = maxY; | ||
this.cache.isEmpty = false; | ||
return maxY; | ||
} | ||
multiplyY(value) { | ||
for (const item of this.array) { | ||
item.y *= value; | ||
} | ||
} | ||
setArray(array) { | ||
this.array = array; | ||
this.emptyCache(); | ||
} | ||
move(other) { | ||
this.array = other.array; | ||
this.emptyCache(); | ||
} | ||
push(point) { | ||
this.array.push(point); | ||
this.emptyCache(); | ||
} | ||
/** | ||
* Sort by ASCENDING x values | ||
* @returns {Distribution} | ||
*/ | ||
sortX() { | ||
this.cache.ySorted = false; | ||
if (this.cache.xSorted) return this; | ||
this.array.sort((a, b) => a.x - b.x); | ||
this.cache.xSorted = true; | ||
this.cache.isEmpty = false; | ||
return this; | ||
} | ||
/** | ||
* Sort by DESCENDING y values | ||
* @returns {Distribution} | ||
*/ | ||
sortY() { | ||
this.cache.xSorted = false; | ||
if (this.cache.ySorted) return this; | ||
this.array.sort((a, b) => b.y - a.y); | ||
this.cache.ySorted = true; | ||
this.cache.isEmpty = false; | ||
return this; | ||
} | ||
normalize() { | ||
const sum = this.sumY; | ||
for (let item of this.array) { | ||
item.y /= sum; | ||
} | ||
return this; | ||
} | ||
/** | ||
* Only keep a defined number of peaks | ||
* @param {number} limit | ||
* @returns | ||
*/ | ||
topY(limit) { | ||
if (!limit) return this; | ||
if (this.array.length <= limit) return this; | ||
this.sortY(); | ||
this.array.splice(limit); | ||
return this; | ||
} | ||
/** | ||
* remove all the peaks under a defined relative threshold | ||
* @param {number} [relativeValue=0] Should be between 0 and 1. 0 means no peak will be removed, 1 means all peaks will be removed | ||
*/ | ||
threshold(relativeValue = 0) { | ||
if (!relativeValue) return this; | ||
const maxY = this.maxY; | ||
const threshold = maxY * relativeValue; | ||
this.array = this.array.filter((point) => point.y >= threshold); | ||
} | ||
square(options = {}) { | ||
return this.multiply(this, options); | ||
} | ||
multiply(b, options) { | ||
return multiply(this, b, options); | ||
} | ||
power(p, options) { | ||
return power(this, p, options); | ||
} | ||
copy() { | ||
let distCopy = new Distribution(); | ||
distCopy.cache = { ...this.cache }; | ||
distCopy.array = JSON.parse(JSON.stringify(this.array)); | ||
return distCopy; | ||
} | ||
maxToOne() { | ||
if (this.array.length === 0) return this; | ||
let currentMax = this.maxY; | ||
for (let item of this.array) { | ||
item.y /= currentMax; | ||
} | ||
return this; | ||
} | ||
joinX(threshold) { | ||
return joinX(this, threshold); | ||
} | ||
append(distribution) { | ||
for (let item of distribution.array) { | ||
this.array.push(item); | ||
} | ||
this.emptyCache(); | ||
} | ||
closestPointX(target) { | ||
this.sortX(); | ||
return closestPointX(this.array, target); | ||
} | ||
} | ||
function getEmptyCache() { | ||
return { | ||
isEmpty: true, | ||
xSorted: false, | ||
ySorted: false, | ||
minX: NaN, | ||
maxX: NaN, | ||
minY: NaN, | ||
maxY: NaN, | ||
sumY: NaN, | ||
}; | ||
} | ||
function getDerivedCompositionInfo(composition) { | ||
const shortComposition = {}; | ||
let label = ''; | ||
let shortLabel = ''; | ||
for (let key in composition) { | ||
let isotopeLabel = ''; | ||
for (let i = 0; i < key.length; i++) { | ||
if (mfParser.superscript[key[i]]) { | ||
isotopeLabel += mfParser.superscript[key[i]]; | ||
} else { | ||
isotopeLabel += key[i]; | ||
} | ||
} | ||
if (composition[key] > 1) { | ||
const number = String(composition[key]); | ||
for (let i = 0; i < number.length; i++) { | ||
isotopeLabel += mfParser.subscript[number[i]]; | ||
} | ||
} | ||
label += isotopeLabel; | ||
if (chemicalElements.stableIsotopesObject[key].mostAbundant) continue; | ||
shortLabel += isotopeLabel; | ||
shortComposition[key] = composition[key]; | ||
} | ||
return { label, shortComposition, shortLabel }; | ||
} | ||
const MINIMAL_FWHM = 1e-8; | ||
/** | ||
* An object containing two arrays | ||
* @typedef {object} XY | ||
* @property {number[]} x - The x array | ||
* @property {number[]} y - The y array | ||
*/ | ||
/** | ||
* A class that allows to manage isotopic distribution | ||
*/ | ||
class IsotopicDistribution { | ||
/** | ||
* Class that manage isotopic distribution | ||
* @param {string|array} value - Molecular formula or an array of parts | ||
* @param {object} [options={}] | ||
* @param {string} [options.ionizations=''] - string containing a comma separated list of modifications | ||
* @param {number} [options.fwhm=0.01] - Amount of Dalton under which 2 peaks are joined | ||
* @param {number} [options.maxLines=5000] - Maximal number of lines during calculations | ||
* @param {number} [options.minY=1e-8] - Minimal signal height during calculations | ||
* @param {boolean} [options.ensureCase=false] - Ensure uppercase / lowercase | ||
* @param {number} [options.threshold] - We can filter the result based on the relative height of the peaks | ||
* @param {number} [options.limit] - We may define the maximum number of peaks to keep | ||
* @param {boolean} [options.allowNeutral=true] - Should we keep the distribution if the molecule has no charge | ||
*/ | ||
constructor(value, options = {}) { | ||
this.threshold = options.threshold; | ||
this.limit = options.limit; | ||
if (Array.isArray(value)) { | ||
this.parts = JSON.parse(JSON.stringify(value)); | ||
for (let part of this.parts) { | ||
part.confidence = 0; | ||
part.isotopesInfo = new mfParser.MF( | ||
`${part.mf}(${part.ionization.mf})`, | ||
).getIsotopesInfo(); | ||
} | ||
} else { | ||
let mf = new mfParser.MF(value, { ensureCase: options.ensureCase }); | ||
let mfInfo = mf.getInfo(); | ||
let ionizations = mfUtilities.preprocessIonizations(options.ionizations); | ||
let parts = mfInfo.parts || [mfInfo]; | ||
this.parts = []; | ||
for (let partOriginal of parts) { | ||
// we calculate information for each part | ||
for (const ionization of ionizations) { | ||
let part = JSON.parse(JSON.stringify(partOriginal)); | ||
part.em = part.monoisotopicMass; // TODO: To remove !!! we change the name !? | ||
part.isotopesInfo = new mfParser.MF( | ||
`${part.mf}(${ionization.mf})`, | ||
).getIsotopesInfo(); | ||
part.confidence = 0; | ||
let msInfo = mfUtilities.getMsInfo(part, { | ||
ionization, | ||
}); | ||
part.ionization = msInfo.ionization; | ||
part.ms = msInfo.ms; | ||
this.parts.push(part); | ||
} | ||
} | ||
} | ||
this.cachedDistribution = undefined; | ||
this.fwhm = options.fwhm === undefined ? 0.01 : options.fwhm; | ||
// if fwhm is under 1e-8 there are some artifacts in the spectra | ||
if (this.fwhm < MINIMAL_FWHM) this.fwhm = MINIMAL_FWHM; | ||
this.minY = options.minY === undefined ? MINIMAL_FWHM : options.minY; | ||
this.maxLines = options.maxLines || 5000; | ||
this.allowNeutral = | ||
options.allowNeutral === undefined ? true : options.allowNeutral; | ||
} | ||
getParts() { | ||
return this.parts; | ||
} | ||
/** | ||
* @return {Distribution} returns the total distribution (for all parts) | ||
*/ | ||
getDistribution() { | ||
if (this.cachedDistribution) return this.cachedDistribution; | ||
let options = { | ||
maxLines: this.maxLines, | ||
minY: this.minY, | ||
deltaX: this.fwhm, | ||
}; | ||
let finalDistribution = new Distribution(); | ||
this.confidence = 0; | ||
// TODO need to cache each part without ionization | ||
// in case of many ionization we don't need to recalculate everything ! | ||
for (let part of this.parts) { | ||
let totalDistribution = new Distribution([ | ||
{ | ||
x: 0, | ||
y: 1, | ||
composition: this.fwhm === MINIMAL_FWHM ? {} : undefined, | ||
}, | ||
]); | ||
let charge = part.ms.charge; | ||
let absoluteCharge = Math.abs(charge); | ||
if (charge || this.allowNeutral) { | ||
for (let isotope of part.isotopesInfo.isotopes) { | ||
if (isotope.number < 0) return { array: [] }; | ||
if (isotope.number > 0) { | ||
const newDistribution = JSON.parse( | ||
JSON.stringify(isotope.distribution), | ||
); | ||
if (this.fwhm === MINIMAL_FWHM) { | ||
// add composition | ||
for (const entry of newDistribution) { | ||
entry.composition = { [Math.round(entry.x) + isotope.atom]: 1 }; | ||
} | ||
} | ||
let distribution = new Distribution(newDistribution); | ||
distribution.power(isotope.number, options); | ||
totalDistribution.multiply(distribution, options); | ||
} | ||
} | ||
this.confidence = 0; | ||
for (const item of totalDistribution.array) { | ||
this.confidence += item.y; | ||
} | ||
// we finally deal with the charge | ||
if (charge) { | ||
totalDistribution.array.forEach((e) => { | ||
e.x = (e.x - chemicalElements.ELECTRON_MASS * charge) / absoluteCharge; | ||
}); | ||
} | ||
if (totalDistribution.array && totalDistribution.array.length > 0) { | ||
totalDistribution.sortX(); | ||
part.fromX = totalDistribution.array[0].x; | ||
part.toX = | ||
totalDistribution.array[totalDistribution.array.length - 1].x; | ||
} | ||
if (part?.ms.similarity?.factor) { | ||
totalDistribution.multiplyY(part.ms.similarity.factor); | ||
} else if ( | ||
part.ms?.target?.intensity && | ||
part.ms?.target?.intensity !== 1 | ||
) { | ||
// intensity is the value of the monoisotopic mass ! | ||
// need to find the intensity of the peak corresponding | ||
// to the monoisotopic mass | ||
if (part.ms.target.mass) { | ||
let target = totalDistribution.closestPointX(part.ms.target.mass); | ||
totalDistribution.multiplyY(part.ms.target.intensity / target.y); | ||
} else { | ||
totalDistribution.multiplyY(part.ms.target.intensity); | ||
} | ||
} else if (part?.intensity && part?.intensity !== 1) { | ||
totalDistribution.multiplyY(part.intensity); | ||
} | ||
part.isotopicDistribution = totalDistribution.array; | ||
if (finalDistribution.array.length === 0) { | ||
finalDistribution = totalDistribution; | ||
} else { | ||
finalDistribution.append(totalDistribution); | ||
} | ||
} | ||
} | ||
if (finalDistribution) finalDistribution.joinX(this.fwhm); | ||
// if there is a threshold we will deal with it | ||
// and we will correct the confidence | ||
if (this.threshold || this.limit) { | ||
const sumBefore = finalDistribution.sumY; | ||
if (this.threshold) finalDistribution.threshold(this.threshold); | ||
if (this.limit) { | ||
finalDistribution.topY(this.limit); | ||
finalDistribution.sortX(); | ||
} | ||
const sumAfter = finalDistribution.sumY; | ||
this.confidence = (this.confidence * sumAfter) / sumBefore; | ||
} | ||
for (let entry of finalDistribution.array) { | ||
if (!entry.composition) continue; | ||
Object.assign(entry, getDerivedCompositionInfo(entry.composition)); | ||
} | ||
this.confidence /= this.parts.length; | ||
this.cachedDistribution = finalDistribution; | ||
return finalDistribution; | ||
} | ||
getCSV() { | ||
return this.getText({ delimiter: ', ' }); | ||
} | ||
getTSV() { | ||
return this.getText({ delimiter: '\t' }); | ||
} | ||
getTable(options = {}) { | ||
const { maxValue, xLabel = 'x', yLabel = 'y' } = options; | ||
let points = this.getDistribution().array; | ||
let factor = 1; | ||
if (maxValue) { | ||
let maxY = this.getMaxY(points); | ||
factor = maxValue / maxY; | ||
} | ||
return points.map((point) => { | ||
let newPoint = {}; | ||
newPoint[xLabel] = point.x; | ||
newPoint[yLabel] = point.y * factor; | ||
return newPoint; | ||
}); | ||
} | ||
getText(options = {}) { | ||
const { delimiter = '\t', numberDecimals = 3 } = options; | ||
let points = this.getDistribution().array; | ||
let csv = []; | ||
for (let point of points) { | ||
csv.push( | ||
`${point.x.toFixed(5)}${delimiter}${(point.y * 100).toFixed( | ||
numberDecimals, | ||
)}`, | ||
); | ||
} | ||
return csv.join('\n'); | ||
} | ||
getMaxY(points) { | ||
let maxY = points[0].y; | ||
for (let point of points) { | ||
if (point.y > maxY) maxY = point.y; | ||
} | ||
return maxY; | ||
} | ||
getSumY(points) { | ||
let sumY = 0; | ||
for (let point of points) { | ||
sumY += point.y; | ||
} | ||
return sumY; | ||
} | ||
/** | ||
* Returns the isotopic distribution as an array of peaks | ||
* @param {object} [options={}] | ||
* @param {number} [options.maxValue=100] | ||
* @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored | ||
* @return {Array<any>} an object containing at least the 2 properties: x:[] and y:[] | ||
*/ | ||
getPeaks(options = {}) { | ||
const { maxValue = 100, sumValue } = options; | ||
let peaks = this.getDistribution().array; | ||
if (peaks.length === 0) return []; | ||
let factor = 1; | ||
if (sumValue) { | ||
let sumY = this.getSumY(peaks); | ||
factor = sumY / sumValue; | ||
} else if (maxValue) { | ||
let maxY = this.getMaxY(peaks); | ||
factor = maxY / maxValue; | ||
} | ||
if (factor !== 1) { | ||
// we need to copy the array because we prefer no side effects | ||
peaks = JSON.parse(JSON.stringify(peaks)); | ||
for (const peak of peaks) { | ||
peak.y = peak.y / factor; | ||
} | ||
} | ||
return peaks; | ||
} | ||
/** | ||
* Returns the isotopic distirubtion | ||
* @param {object} [options={}] | ||
* @param {number} [options.maxValue=100] | ||
* @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored | ||
* @return {XY} an object containing at least the 2 properties: x:[] and y:[] | ||
*/ | ||
getXY(options = {}) { | ||
let peaks = this.getPeaks(options); | ||
if (peaks.length === 0) { | ||
return { x: [], y: [] }; | ||
} | ||
const result = { | ||
x: peaks.map((a) => a.x), | ||
y: peaks.map((a) => a.y), | ||
}; | ||
for (let key of Object.keys(peaks[0]).filter( | ||
(k) => k !== 'x' && k !== 'y', | ||
)) { | ||
result[key] = peaks.map((a) => a[key]); | ||
} | ||
return result; | ||
} | ||
/** | ||
* Returns the isotopic distirubtion | ||
* @param {object} [options={}] | ||
* @param {number} [options.maxValue=100] | ||
* @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored | ||
* @return {import('cheminfo-types').MeasurementXYVariables} an object containing at least the 2 properties: x:[] and y:[] | ||
*/ | ||
getVariables(options = {}) { | ||
const xy = this.getXY(options); | ||
return { | ||
x: { data: xy.x, label: 'm/z', units: 'u' }, | ||
y: { data: xy.y, label: 'Relative intensity', units: '%' }, | ||
}; | ||
} | ||
/** | ||
* Returns the isotopic distribution as the sum of gaussian | ||
* @param {object} [options={}] | ||
* @param {number} [options.gaussianWidth=10] | ||
* @param {number} [options.threshold=0.00001] // minimal height to return point | ||
* @param {number} [options.maxLength=1e6] // minimal height to return point | ||
* @param {number} [options.maxValue] // rescale Y to reach maxValue | ||
* @param {function} [options.peakWidthFct=(mz)=>(this.fwhm)] | ||
* @return {XY} isotopic distribution as an object containing 2 properties: x:[] and y:[] | ||
*/ | ||
getGaussian(options = {}) { | ||
const { | ||
peakWidthFct = () => this.fwhm, | ||
threshold = 0.00001, | ||
gaussianWidth = 10, | ||
maxValue, | ||
maxLength = 1e6, | ||
} = options; | ||
let points = this.getTable({ maxValue }); | ||
if (points.length === 0) return { x: [], y: [] }; | ||
const from = Math.floor(options.from || points[0].x - 2); | ||
const to = Math.ceil(options.to || points[points.length - 1].x + 2); | ||
const nbPoints = Math.round(((to - from) * gaussianWidth) / this.fwhm + 1); | ||
if (nbPoints > maxLength) { | ||
throw Error( | ||
`Number of points is over the maxLength: ${nbPoints}>${maxLength}`, | ||
); | ||
} | ||
let gaussianOptions = { | ||
from, | ||
to, | ||
nbPoints, | ||
peakWidthFct, | ||
}; | ||
let spectrumGenerator$1 = new spectrumGenerator.SpectrumGenerator(gaussianOptions); | ||
for (let point of points) { | ||
spectrumGenerator$1.addPeak([point.x, point.y]); | ||
} | ||
let spectrum = spectrumGenerator$1.getSpectrum({ threshold }); | ||
return spectrum; | ||
} | ||
} | ||
exports.IsotopicDistribution = IsotopicDistribution; | ||
Object.defineProperty(o, k2, desc); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
__exportStar(require("./IsotopicDistribution"), exports); |
{ | ||
"name": "isotopic-distribution", | ||
"version": "3.1.3", | ||
"version": "3.2.0", | ||
"description": "Calculate the isotopic distribution of a molecular formula", | ||
@@ -23,5 +23,5 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"chemical-elements": "^2.0.4", | ||
"mf-parser": "^3.1.1", | ||
"mf-utilities": "^3.1.1", | ||
"chemical-elements": "^2.1.0", | ||
"mf-parser": "^3.2.0", | ||
"mf-utilities": "^3.2.0", | ||
"spectrum-generator": "^8.0.11" | ||
@@ -32,3 +32,3 @@ }, | ||
}, | ||
"gitHead": "b9f99ec6f05ce6b0034d35f4ae0452ae653f90c2" | ||
"gitHead": "28dae91d3b42556a23097ee08acfe4061f276ed0" | ||
} |
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
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
55995
27
1607
1
Updatedchemical-elements@^2.1.0
Updatedmf-parser@^3.2.0
Updatedmf-utilities@^3.2.0