mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
remove ramda (#8932)
This commit is contained in:
parent
a71cc2b137
commit
cd4903f65e
@ -149,7 +149,6 @@
|
||||
"pump": "^3.0.0",
|
||||
"punycode": "^2.1.1",
|
||||
"qrcode-generator": "1.4.1",
|
||||
"ramda": "^0.24.1",
|
||||
"react": "^16.12.0",
|
||||
"react-dnd": "^3.0.2",
|
||||
"react-dnd-html5-backend": "^7.4.4",
|
||||
|
@ -6,7 +6,6 @@ import { compose } from 'redux'
|
||||
import * as actions from '../../../store/actions'
|
||||
import { Dropdown, DropdownMenuItem } from './components/dropdown'
|
||||
import NetworkDropdownIcon from './components/network-dropdown-icon'
|
||||
import R from 'ramda'
|
||||
import { NETWORKS_ROUTE } from '../../../helpers/constants/routes'
|
||||
|
||||
// classes from nodes of the toggle element.
|
||||
@ -219,7 +218,7 @@ class NetworkDropdown extends Component {
|
||||
onClickOutside={(event) => {
|
||||
const { classList } = event.target
|
||||
const isInClassList = (className) => classList.contains(className)
|
||||
const notToggleElementIndex = R.findIndex(isInClassList)(notToggleElementClassnames)
|
||||
const notToggleElementIndex = notToggleElementClassnames.findIndex(isInClassList)
|
||||
|
||||
if (notToggleElementIndex === -1) {
|
||||
this.props.hideNetworkDropdown()
|
||||
|
@ -1,7 +1,6 @@
|
||||
import classnames from 'classnames'
|
||||
import PropTypes from 'prop-types'
|
||||
import React, { Component } from 'react'
|
||||
import R from 'ramda'
|
||||
|
||||
class SimpleDropdown extends Component {
|
||||
static propTypes = {
|
||||
@ -18,7 +17,7 @@ class SimpleDropdown extends Component {
|
||||
getDisplayValue () {
|
||||
const { selectedOption, options } = this.props
|
||||
const matchesOption = (option) => option.value === selectedOption
|
||||
const matchingOption = R.find(matchesOption)(options)
|
||||
const matchingOption = options.find(matchesOption)
|
||||
return matchingOption
|
||||
? matchingOption.displayValue || matchingOption.value
|
||||
: selectedOption
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { connect } from 'react-redux'
|
||||
import { pipe, partialRight } from 'ramda'
|
||||
import GasModalPageContainer from './gas-modal-page-container.component'
|
||||
import {
|
||||
hideModal,
|
||||
@ -308,23 +307,18 @@ function calcCustomGasLimit (customGasLimitInHex) {
|
||||
}
|
||||
|
||||
function addHexWEIsToRenderableEth (aHexWEI, bHexWEI) {
|
||||
return pipe(
|
||||
addHexWEIsToDec,
|
||||
formatETHFee
|
||||
)(aHexWEI, bHexWEI)
|
||||
return formatETHFee(addHexWEIsToDec(aHexWEI, bHexWEI))
|
||||
}
|
||||
|
||||
function subtractHexWEIsFromRenderableEth (aHexWEI, bHexWei) {
|
||||
return pipe(
|
||||
subtractHexWEIsToDec,
|
||||
formatETHFee
|
||||
)(aHexWEI, bHexWei)
|
||||
function subtractHexWEIsFromRenderableEth (aHexWEI, bHexWEI) {
|
||||
return formatETHFee(subtractHexWEIsToDec(aHexWEI, bHexWEI))
|
||||
}
|
||||
|
||||
function addHexWEIsToRenderableFiat (aHexWEI, bHexWEI, convertedCurrency, conversionRate) {
|
||||
return pipe(
|
||||
addHexWEIsToDec,
|
||||
partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]),
|
||||
partialRight(formatCurrency, [convertedCurrency]),
|
||||
)(aHexWEI, bHexWEI)
|
||||
const ethTotal = ethTotalToConvertedCurrency(
|
||||
addHexWEIsToDec(aHexWEI, bHexWEI),
|
||||
convertedCurrency,
|
||||
conversionRate
|
||||
)
|
||||
return formatCurrency(ethTotal, convertedCurrency)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { connect } from 'react-redux'
|
||||
import R from 'ramda'
|
||||
import { findLastIndex } from 'lodash'
|
||||
import TransactionActivityLog from './transaction-activity-log.component'
|
||||
import { conversionRateSelector, getNativeCurrency } from '../../../selectors'
|
||||
import { combineTransactionHistories } from './transaction-activity-log.util'
|
||||
@ -27,8 +27,8 @@ const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
} = ownProps
|
||||
|
||||
const activities = combineTransactionHistories(transactions)
|
||||
const inlineRetryIndex = R.findLastIndex(matchesEventKey(TRANSACTION_RESUBMITTED_EVENT))(activities)
|
||||
const inlineCancelIndex = R.findLastIndex(matchesEventKey(TRANSACTION_CANCEL_ATTEMPTED_EVENT))(activities)
|
||||
const inlineRetryIndex = findLastIndex(activities, matchesEventKey(TRANSACTION_RESUBMITTED_EVENT))
|
||||
const inlineCancelIndex = findLastIndex(activities, matchesEventKey(TRANSACTION_CANCEL_ATTEMPTED_EVENT))
|
||||
|
||||
return {
|
||||
...stateProps,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { clone, uniqBy, flatten } from 'ramda'
|
||||
import { uniqBy, cloneDeep, flatten } from 'lodash'
|
||||
import BigNumber from 'bignumber.js'
|
||||
import {
|
||||
loadLocalStorageData,
|
||||
@ -138,10 +138,10 @@ export default function reducer (state = initState, action) {
|
||||
case RESET_CUSTOM_DATA:
|
||||
return {
|
||||
...state,
|
||||
customData: clone(initState.customData),
|
||||
customData: cloneDeep(initState.customData),
|
||||
}
|
||||
case RESET_CUSTOM_GAS_STATE:
|
||||
return clone(initState)
|
||||
return cloneDeep(initState)
|
||||
default:
|
||||
return state
|
||||
}
|
||||
@ -393,7 +393,7 @@ export function fetchGasEstimates (blockTime) {
|
||||
.then((r) => r.json())
|
||||
.then((r) => {
|
||||
const estimatedPricesAndTimes = r.map(({ expectedTime, expectedWait, gasprice }) => ({ expectedTime, expectedWait, gasprice }))
|
||||
const estimatedTimeWithUniquePrices = uniqBy(({ expectedTime }) => expectedTime, estimatedPricesAndTimes)
|
||||
const estimatedTimeWithUniquePrices = uniqBy(estimatedPricesAndTimes, ({ expectedTime }) => expectedTime)
|
||||
|
||||
const withSupplementalTimeEstimates = flatten(estimatedTimeWithUniquePrices.map(({ expectedWait, gasprice }, i, arr) => {
|
||||
const next = arr[i + 1]
|
||||
|
@ -17,9 +17,8 @@
|
||||
* @returns {(number | string | BN)}
|
||||
*
|
||||
* The utility passes value along with the options as a single object to the `converter` function.
|
||||
* `converter` uses Ramda.js to apply a composition of conditional setters to the `value` property, depending
|
||||
* on the accompanying options. Some of these conditional setters are selected via key-value maps, where
|
||||
* the keys are specified in the options parameters and the values are setter functions.
|
||||
* `converter` conditional modifies the supplied `value` property, depending
|
||||
* on the accompanying options.
|
||||
*/
|
||||
|
||||
import BigNumber from 'bignumber.js'
|
||||
@ -27,11 +26,6 @@ import BigNumber from 'bignumber.js'
|
||||
import ethUtil, { stripHexPrefix } from 'ethereumjs-util'
|
||||
|
||||
const BN = ethUtil.BN
|
||||
import R from 'ramda'
|
||||
|
||||
BigNumber.config({
|
||||
ROUNDING_MODE: BigNumber.ROUND_HALF_DOWN,
|
||||
})
|
||||
|
||||
// Big Number Constants
|
||||
const BIG_NUMBER_WEI_MULTIPLIER = new BigNumber('1000000000000000000')
|
||||
@ -60,60 +54,78 @@ const baseChange = {
|
||||
BN: (n) => new BN(n.toString(16)),
|
||||
}
|
||||
|
||||
// Individual Setters
|
||||
const convert = R.invoker(1, 'times')
|
||||
const round = R.invoker(2, 'round')(R.__, BigNumber.ROUND_HALF_DOWN)
|
||||
const roundDown = R.invoker(2, 'round')(R.__, BigNumber.ROUND_DOWN)
|
||||
const invertConversionRate = (conversionRate) => () => new BigNumber(1.0).div(conversionRate)
|
||||
const decToBigNumberViaString = () => R.pipe(String, toBigNumber['dec'])
|
||||
/**
|
||||
* Defines the base type of numeric value
|
||||
* @typedef {('hex' | 'dec' | 'BN')} NumericBase
|
||||
*/
|
||||
|
||||
// Predicates
|
||||
const fromAndToCurrencyPropsNotEqual = R.compose(
|
||||
R.not,
|
||||
R.eqBy(R.__, 'fromCurrency', 'toCurrency'),
|
||||
R.flip(R.prop)
|
||||
)
|
||||
/**
|
||||
* Defines which type of denomination a value is in
|
||||
* @typedef {('WEI' | 'GWEI' | 'ETH')} EthDenomination
|
||||
*/
|
||||
|
||||
// Lens
|
||||
const valuePropertyLens = R.over(R.lensProp('value'))
|
||||
const conversionRateLens = R.over(R.lensProp('conversionRate'))
|
||||
/**
|
||||
* Utility method to convert a value between denominations, formats and currencies.
|
||||
* @param {Object} input
|
||||
* @param {string | BigNumber} input.value
|
||||
* @param {NumericBase} input.fromNumericBase
|
||||
* @param {EthDenomination} [input.fromDenomination]
|
||||
* @param {string} [input.fromCurrency]
|
||||
* @param {NumericBase} input.toNumericBase
|
||||
* @param {EthDenomination} [input.toDenomination]
|
||||
* @param {string} [input.toCurrency]
|
||||
* @param {number} [input.numberOfDecimals]
|
||||
* @param {number} [input.conversionRate]
|
||||
* @param {boolean} [input.invertConversionRate]
|
||||
* @param {string} [input.roundDown]
|
||||
*/
|
||||
const converter = ({
|
||||
value,
|
||||
fromNumericBase,
|
||||
fromDenomination,
|
||||
fromCurrency,
|
||||
toNumericBase,
|
||||
toDenomination,
|
||||
toCurrency,
|
||||
numberOfDecimals,
|
||||
conversionRate,
|
||||
invertConversionRate,
|
||||
roundDown,
|
||||
}) => {
|
||||
let convertedValue = fromNumericBase ? toBigNumber[fromNumericBase](value) : value
|
||||
|
||||
// conditional conversionRate setting wrapper
|
||||
const whenPredSetCRWithPropAndSetter = (pred, prop, setter) => R.when(
|
||||
pred,
|
||||
R.converge(
|
||||
conversionRateLens,
|
||||
[R.pipe(R.prop(prop), setter), R.identity]
|
||||
)
|
||||
)
|
||||
if (fromDenomination) {
|
||||
convertedValue = toNormalizedDenomination[fromDenomination](convertedValue)
|
||||
}
|
||||
|
||||
// conditional 'value' setting wrappers
|
||||
const whenPredSetWithPropAndSetter = (pred, prop, setter) => R.when(
|
||||
pred,
|
||||
R.converge(
|
||||
valuePropertyLens,
|
||||
[R.pipe(R.prop(prop), setter), R.identity]
|
||||
)
|
||||
)
|
||||
const whenPropApplySetterMap = (prop, setterMap) => whenPredSetWithPropAndSetter(
|
||||
R.prop(prop),
|
||||
prop,
|
||||
R.prop(R.__, setterMap)
|
||||
)
|
||||
if (fromCurrency !== toCurrency) {
|
||||
if (conversionRate == null) {
|
||||
throw new Error(`Converting from ${fromCurrency} to ${toCurrency} requires a conversionRate, but one was not provided`)
|
||||
}
|
||||
let rate = toBigNumber.dec(conversionRate)
|
||||
if (invertConversionRate) {
|
||||
rate = new BigNumber(1.0).div(conversionRate)
|
||||
}
|
||||
convertedValue = convertedValue.times(rate)
|
||||
}
|
||||
|
||||
// Conversion utility function
|
||||
const converter = R.pipe(
|
||||
whenPredSetCRWithPropAndSetter(R.prop('conversionRate'), 'conversionRate', decToBigNumberViaString),
|
||||
whenPredSetCRWithPropAndSetter(R.prop('invertConversionRate'), 'conversionRate', invertConversionRate),
|
||||
whenPropApplySetterMap('fromNumericBase', toBigNumber),
|
||||
whenPropApplySetterMap('fromDenomination', toNormalizedDenomination),
|
||||
whenPredSetWithPropAndSetter(fromAndToCurrencyPropsNotEqual, 'conversionRate', convert),
|
||||
whenPropApplySetterMap('toDenomination', toSpecifiedDenomination),
|
||||
whenPredSetWithPropAndSetter(R.prop('numberOfDecimals'), 'numberOfDecimals', round),
|
||||
whenPredSetWithPropAndSetter(R.prop('roundDown'), 'roundDown', roundDown),
|
||||
whenPropApplySetterMap('toNumericBase', baseChange),
|
||||
R.view(R.lensProp('value'))
|
||||
)
|
||||
if (toDenomination) {
|
||||
convertedValue = toSpecifiedDenomination[toDenomination](convertedValue)
|
||||
}
|
||||
|
||||
if (numberOfDecimals) {
|
||||
convertedValue = convertedValue.round(numberOfDecimals, BigNumber.ROUND_HALF_DOWN)
|
||||
}
|
||||
|
||||
if (roundDown) {
|
||||
convertedValue = convertedValue.round(roundDown, BigNumber.ROUND_DOWN)
|
||||
}
|
||||
|
||||
if (toNumericBase) {
|
||||
convertedValue = baseChange[toNumericBase](convertedValue)
|
||||
}
|
||||
return convertedValue
|
||||
}
|
||||
|
||||
const conversionUtil = (value, {
|
||||
fromCurrency = null,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import assert from 'assert'
|
||||
import { addCurrencies } from './conversion-util'
|
||||
|
||||
import { addCurrencies, conversionUtil } from './conversion-util'
|
||||
import BigNumber from 'bignumber.js'
|
||||
|
||||
describe('conversion utils', function () {
|
||||
describe('addCurrencies()', function () {
|
||||
@ -19,4 +19,41 @@ describe('conversion utils', function () {
|
||||
assert.equal(result.toNumber(), 0.4444444444444444)
|
||||
})
|
||||
})
|
||||
|
||||
describe('conversionUtil', function () {
|
||||
it('Returns expected types', function () {
|
||||
const conv1 = conversionUtil(1000000000000000000, { fromNumericBase: 'dec', toNumericBase: 'hex' })
|
||||
const conv2 = conversionUtil(1, { fromNumericBase: 'dec', fromDenomination: 'ETH', toDenomination: 'WEI' })
|
||||
assert(typeof conv1 === 'string', 'conversion 1 should return type string')
|
||||
assert(conv2 instanceof BigNumber, 'conversion 2 should be a BigNumber')
|
||||
})
|
||||
it('Converts from dec to hex', function () {
|
||||
assert.equal(conversionUtil('1000000000000000000', { fromNumericBase: 'dec', toNumericBase: 'hex' }), 'de0b6b3a7640000')
|
||||
assert.equal(conversionUtil('1500000000000000000', { fromNumericBase: 'dec', toNumericBase: 'hex' }), '14d1120d7b160000')
|
||||
})
|
||||
it('Converts hex formatted numbers to dec', function () {
|
||||
assert.equal(conversionUtil('0xde0b6b3a7640000', { fromNumericBase: 'hex', toNumericBase: 'dec' }), 1000000000000000000)
|
||||
assert.equal(conversionUtil('0x14d1120d7b160000', { fromNumericBase: 'hex', toNumericBase: 'dec' }), 1500000000000000000)
|
||||
})
|
||||
it('Converts WEI to ETH', function () {
|
||||
assert.equal(conversionUtil('0xde0b6b3a7640000', { fromNumericBase: 'hex', toNumericBase: 'dec', fromDenomination: 'WEI', toDenomination: 'ETH' }), 1)
|
||||
assert.equal(conversionUtil('0x14d1120d7b160000', { fromNumericBase: 'hex', toNumericBase: 'dec', fromDenomination: 'WEI', toDenomination: 'ETH' }), 1.5)
|
||||
})
|
||||
it('Converts ETH to WEI', function () {
|
||||
assert.equal(conversionUtil('1', { fromNumericBase: 'dec', fromDenomination: 'ETH', toDenomination: 'WEI' }), 1000000000000000000)
|
||||
assert.equal(conversionUtil('1.5', { fromNumericBase: 'dec', fromDenomination: 'ETH', toDenomination: 'WEI' }), 1500000000000000000)
|
||||
})
|
||||
it('Converts ETH to GWEI', function () {
|
||||
assert.equal(conversionUtil('1', { fromNumericBase: 'dec', fromDenomination: 'ETH', toDenomination: 'GWEI' }), 1000000000)
|
||||
assert.equal(conversionUtil('1.5', { fromNumericBase: 'dec', fromDenomination: 'ETH', toDenomination: 'GWEI' }), 1500000000)
|
||||
})
|
||||
it('Converts ETH to USD', function () {
|
||||
assert.equal(conversionUtil('1', { fromNumericBase: 'dec', toNumericBase: 'dec', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2 }), 468.58)
|
||||
assert.equal(conversionUtil('1.5', { fromNumericBase: 'dec', toNumericBase: 'dec', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2 }), 702.87)
|
||||
})
|
||||
it('Converts USD to ETH', function () {
|
||||
assert.equal(conversionUtil('468.58', { fromNumericBase: 'dec', toNumericBase: 'dec', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2, invertConversionRate: true }), 1)
|
||||
assert.equal(conversionUtil('702.87', { fromNumericBase: 'dec', toNumericBase: 'dec', toCurrency: 'usd', conversionRate: 468.58, numberOfDecimals: 2, invertConversionRate: true }), 1.5)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -6,7 +6,6 @@ import { compose } from 'redux'
|
||||
import * as actions from '../../store/actions'
|
||||
import txHelper from '../../../lib/tx-helper'
|
||||
import log from 'loglevel'
|
||||
import R from 'ramda'
|
||||
import SignatureRequest from '../../components/app/signature-request'
|
||||
import SignatureRequestOriginal from '../../components/app/signature-request-original'
|
||||
import Loading from '../../components/ui/loading-screen'
|
||||
@ -106,7 +105,7 @@ class ConfirmTxScreen extends Component {
|
||||
log.info(`rendering a combined ${unconfTxList.length} unconf msgs & txs`)
|
||||
|
||||
return transactionId
|
||||
? R.find(({ id }) => id + '' === transactionId)(unconfTxList)
|
||||
? unconfTxList.find(({ id }) => id + '' === transactionId)
|
||||
: unconfTxList[index]
|
||||
}
|
||||
|
||||
@ -196,7 +195,7 @@ class ConfirmTxScreen extends Component {
|
||||
let prevTx
|
||||
|
||||
if (transactionId) {
|
||||
prevTx = R.find(({ id }) => id + '' === transactionId)(currentNetworkTxList)
|
||||
prevTx = currentNetworkTxList.find(({ id }) => id + '' === transactionId)
|
||||
} else {
|
||||
const { index: prevIndex, unapprovedTxs: prevUnapprovedTxs } = prevProps
|
||||
const prevUnconfTxList = txHelper(prevUnapprovedTxs, {}, {}, {}, network)
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { pipe, partialRight } from 'ramda'
|
||||
import {
|
||||
conversionUtil,
|
||||
multiplyCurrencies,
|
||||
@ -128,21 +127,17 @@ export function basicPriceEstimateToETHTotal (estimate, gasLimit, numberOfDecima
|
||||
}
|
||||
|
||||
export function getRenderableEthFee (estimate, gasLimit, numberOfDecimals = 9) {
|
||||
return pipe(
|
||||
(x) => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
|
||||
partialRight(basicPriceEstimateToETHTotal, [gasLimit, numberOfDecimals]),
|
||||
formatETHFee
|
||||
)(estimate, gasLimit)
|
||||
const value = conversionUtil(estimate, { fromNumericBase: 'dec', toNumericBase: 'hex' })
|
||||
const fee = basicPriceEstimateToETHTotal(value, gasLimit, numberOfDecimals)
|
||||
return formatETHFee(fee)
|
||||
}
|
||||
|
||||
|
||||
export function getRenderableConvertedCurrencyFee (estimate, gasLimit, convertedCurrency, conversionRate) {
|
||||
return pipe(
|
||||
(x) => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
|
||||
partialRight(basicPriceEstimateToETHTotal, [gasLimit]),
|
||||
partialRight(ethTotalToConvertedCurrency, [convertedCurrency, conversionRate]),
|
||||
partialRight(formatCurrency, [convertedCurrency])
|
||||
)(estimate, gasLimit, convertedCurrency, conversionRate)
|
||||
const value = conversionUtil(estimate, { fromNumericBase: 'dec', toNumericBase: 'hex' })
|
||||
const fee = basicPriceEstimateToETHTotal(value, gasLimit)
|
||||
const feeInCurrency = ethTotalToConvertedCurrency(fee, convertedCurrency, conversionRate)
|
||||
return formatCurrency(feeInCurrency, convertedCurrency)
|
||||
}
|
||||
|
||||
export function getTimeEstimateInSeconds (blockWaitEstimate) {
|
||||
@ -179,10 +174,7 @@ export function formatTimeEstimate (totalSeconds, greaterThanMax, lessThanMin) {
|
||||
}
|
||||
|
||||
export function getRenderableTimeEstimate (blockWaitEstimate) {
|
||||
return pipe(
|
||||
getTimeEstimateInSeconds,
|
||||
formatTimeEstimate
|
||||
)(blockWaitEstimate)
|
||||
return formatTimeEstimate(getTimeEstimateInSeconds(blockWaitEstimate))
|
||||
}
|
||||
|
||||
export function priceEstimateToWei (priceEstimate) {
|
||||
@ -196,11 +188,8 @@ export function priceEstimateToWei (priceEstimate) {
|
||||
}
|
||||
|
||||
export function getGasPriceInHexWei (price) {
|
||||
return pipe(
|
||||
(x) => conversionUtil(x, { fromNumericBase: 'dec', toNumericBase: 'hex' }),
|
||||
priceEstimateToWei,
|
||||
addHexPrefix
|
||||
)(price)
|
||||
const value = conversionUtil(price, { fromNumericBase: 'dec', toNumericBase: 'hex' })
|
||||
return addHexPrefix(priceEstimateToWei(value))
|
||||
}
|
||||
|
||||
export function getRenderableBasicEstimateData (state, gasLimit) {
|
||||
|
@ -22244,11 +22244,6 @@ ramda@^0.21.0:
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.21.0.tgz#a001abedb3ff61077d4ff1d577d44de77e8d0a35"
|
||||
integrity sha1-oAGr7bP/YQd9T/HVd9RN536NCjU=
|
||||
|
||||
ramda@^0.24.1:
|
||||
version "0.24.1"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.24.1.tgz#c3b7755197f35b8dc3502228262c4c91ddb6b857"
|
||||
integrity sha1-w7d1UZfzW43DUCIoJixMkd22uFc=
|
||||
|
||||
ramda@^0.26.1:
|
||||
version "0.26.1"
|
||||
resolved "https://registry.yarnpkg.com/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
|
||||
|
Loading…
Reference in New Issue
Block a user