diff --git a/package.json b/package.json index 3a0ec121b..fc79c8d38 100644 --- a/package.json +++ b/package.json @@ -121,7 +121,8 @@ "@types/color-convert": "^1.9.0", "@types/iframe-resizer": "^3.5.7", "@types/js-yaml": "^3.12.2", - "@types/ramda": "^0.26.33", + "@types/nearley": "^2.11.1", + "@types/ramda": "^0.26.43", "@types/raven-for-redux": "^1.1.1", "@types/react": "^16.9.11", "@types/react-addons-css-transition-group": "^15.0.5", diff --git a/source/engine/error.ts b/source/engine/error.ts index 14f7327a6..3a5a9d006 100644 --- a/source/engine/error.ts +++ b/source/engine/error.ts @@ -2,7 +2,7 @@ import { coerceArray } from '../utils' export function syntaxError( rules: string[] | string, message: string, - originalError: Error + originalError?: Error ) { throw new Error( `\n[ Erreur syntaxique ] diff --git a/source/engine/evaluateRule.js b/source/engine/evaluateRule.ts similarity index 70% rename from source/engine/evaluateRule.js rename to source/engine/evaluateRule.ts index 4a9d13306..c1b4b3d29 100644 --- a/source/engine/evaluateRule.js +++ b/source/engine/evaluateRule.ts @@ -1,5 +1,6 @@ import { bonus, evaluateNode, mergeMissing } from 'Engine/evaluation' import { map, mergeAll, pick, pipe } from 'ramda' +import { Rule } from 'Types/rule' import { typeWarning } from './error' import { convertNodeToUnit } from './nodeUnits' import { anyNull, undefOrTruthy, val } from './traverse-common-functions' @@ -9,12 +10,14 @@ export const evaluateApplicability = ( cache, situationGate, parsedRules, - node + node: Rule ) => { let evaluatedAttributes = pipe( - pick(['non applicable si', 'applicable si', 'rendu non applicable']), + pick(['non applicable si', 'applicable si', 'rendu non applicable']) as ( + x: any + ) => any, map(value => evaluateNode(cache, situationGate, parsedRules, value)) - )(node), + )(node) as any, { 'non applicable si': notApplicable, 'applicable si': applicable, @@ -51,39 +54,43 @@ export const evaluateApplicability = ( export default (cache, situationGate, parsedRules, node) => { cache._meta.contextRule.push(node.dottedName) - let applicabilityEvaluation = evaluateApplicability( - cache, - situationGate, - parsedRules, - node - ), - { - missingVariables: condMissing, - nodeValue: isApplicable - } = applicabilityEvaluation, - evaluateFormula = () => - node.formule - ? evaluateNode(cache, situationGate, parsedRules, node.formule) - : {}, - // evaluate the formula lazily, only if the applicability is known and true - evaluatedFormula = isApplicable - ? evaluateFormula() - : isApplicable === false - ? { - ...node.formule, - missingVariables: {}, - nodeValue: 0 - } - : { - ...node.formule, - missingVariables: {}, - nodeValue: null - }, - { missingVariables: formulaMissingVariables, nodeValue } = evaluatedFormula, - missingVariables = mergeMissing( - bonus(condMissing, !!Object.keys(condMissing).length), - formulaMissingVariables - ) + const applicabilityEvaluation = evaluateApplicability( + cache, + situationGate, + parsedRules, + node + ) + const { + missingVariables: condMissing, + nodeValue: isApplicable + } = applicabilityEvaluation + + const evaluateFormula = () => + node.formule + ? evaluateNode(cache, situationGate, parsedRules, node.formule) + : {} + // evaluate the formula lazily, only if the applicability is known and true + const evaluatedFormula = isApplicable + ? evaluateFormula() + : isApplicable === false + ? { + ...node.formule, + missingVariables: {}, + nodeValue: 0 + } + : { + ...node.formule, + missingVariables: {}, + nodeValue: null + } + let { + missingVariables: formulaMissingVariables, + nodeValue + } = evaluatedFormula + const missingVariables = mergeMissing( + bonus(condMissing, !!Object.keys(condMissing).length), + formulaMissingVariables + ) const unit = node.unit || (node.defaultUnit && diff --git a/source/engine/explication-demo-CDD.md b/source/engine/explication-demo-CDD.md deleted file mode 100644 index 9c08f6369..000000000 --- a/source/engine/explication-demo-CDD.md +++ /dev/null @@ -1,22 +0,0 @@ -L'objectif dans un premier temps est de faire une démonstration d'interface de saisie pour calculer les obligations du CDD. Le CDD est un contrat d'exception, ce qui le rend assez complexe. - -Les Variables du système social comportent des entrées particulières (des cléfs de l'objet) que l'on nommera "mécanismes". Elles diffèrent des entrées simples (telle la description d'une variable) car elles sont susceptibles de comporter des variables, et la description de calculs. - -Voici une liste des mécanismes qui sont à traverser pour notamment : - - d'extraire les variables utilisées et donc que l'utilisateur devra saisir - - d'attribuer une note à ces variables pour déterminer la prochaine saisie (ou "question") - - de court-circuiter les branches rendues inutiles par la situation courante saisie par l'utilisateur, ce qui influence la note. - -Dans un premier temps, l'idée est de créer un formulaire à questions unitaires optimisées pour que la saisie de données inutiles soit évitée. - -- Il faut résoudre le calcul des variables. - - ça introduit l'assiette 2300€ - - ça nous donne son intervalle en % vu que l'assiette est constante, et donc un avancement du formulaire - - ça nous donne des résultats !!!!!! :)) - -- Il faut donner des explications à TOUTES les variables. Aucune n'est triviale ! - -- Il faut ajouter la notion d'entité. -- Ce qui permettra de regrouper les questions par thème. Exemple, demander "Quel type de CDD" et ordonner les réponses possibles par influence (e.g. si c'est un CDD d'usage ) - ------------------------------------------ diff --git a/source/engine/index.js b/source/engine/index.js deleted file mode 100644 index fe09942bb..000000000 --- a/source/engine/index.js +++ /dev/null @@ -1,69 +0,0 @@ -// This file exports the functions of the public computing library -import { safeLoad } from 'js-yaml' -import { collectDefaults, enrichRule, rulesFr } from './rules' -import { analyseMany, parseAll } from './traverse' - -// The public evaluation function takes a nested object of input values -let inputToStateSelector = rules => input => dottedName => - ({ - ...collectDefaults(rules), - ...input - }[dottedName]) - -let enrichRules = input => { - const rules = typeof input === 'string' ? safeLoad(input) : input - const rulesList = Array.isArray(rules) - ? rules - : Object.entries(rules).map(([dottedName, rule]) => ({ - dottedName, - ...rule - })) - return rulesList.map(enrichRule) -} - -class Engine { - situation = {} - parsedRules - constructor(rules = rulesFr) { - this.parsedRules = parseAll(rules) - this.defaultValues = collectDefaults(rules) - } - evaluate(targets, { defaultUnits, situation, withDefaultValues = true }) { - this.evaluation = analyseMany( - this.parsedRules, - targets, - defaultUnits - )( - dottedName => - situation[dottedName] || - (withDefaultValues && this.defaultValues[dottedName]) - ) - return this.evaluation.targets.map(({ nodeValue }) => nodeValue) - } - getLastEvaluationExplanations() { - return this.evaluation - } -} - -export default { - evaluate: (targetInput, input, config, defaultUnits = []) => { - let rules = config - ? [ - ...(config.base ? enrichRules(config.base) : rulesFr), - ...(config.extra ? enrichRules(config.extra) : []) - ] - : rulesFr - - let evaluation = analyseMany( - parseAll(rules), - Array.isArray(targetInput) ? targetInput : [targetInput], - defaultUnits - )(inputToStateSelector(rules)(input)) - if (config?.debug) return evaluation - - let values = evaluation.targets.map(t => t.nodeValue) - - return Array.isArray(targetInput) ? values : values[0] - }, - Engine -} diff --git a/source/engine/index.ts b/source/engine/index.ts new file mode 100644 index 000000000..b5fae34d0 --- /dev/null +++ b/source/engine/index.ts @@ -0,0 +1,86 @@ +import { safeLoad } from 'js-yaml' +import { Simulation } from 'Reducers/rootReducer' +import { DottedName, Rule } from 'Types/rule' +import { evaluateNode } from './evaluation' +import { collectDefaults, enrichRule, rulesFr } from './rules' +import { parseAll } from './traverse' +import { parseUnit } from './units' + +const emptyCache = { + _meta: { contextRule: [], defaultUnits: [] } +} + +type EngineConfig = { + rules?: string | Array | object + extra?: string | Array | object +} + +let enrichRules = input => { + const rules = typeof input === 'string' ? safeLoad(input) : input + const rulesList = Array.isArray(rules) + ? rules + : Object.entries(rules).map(([dottedName, rule]) => ({ + dottedName, + ...(rule as any) + })) + return rulesList.map(enrichRule) +} + +export default class Engine { + parsedRules: Record + defaultValues: Simulation['situation'] + situation: Simulation['situation'] = {} + cache = { ...emptyCache } + + constructor(config: EngineConfig = {}) { + const rules = config + ? [ + ...(config.rules ? enrichRules(config.rules) : rulesFr), + ...(config.extra ? enrichRules(config.extra) : []) + ] + : rulesFr + this.parsedRules = parseAll(rules) as any + this.defaultValues = collectDefaults(rules) + } + + private resetCache() { + this.cache = { ...emptyCache } + } + + setSituation(situation: Simulation['situation'] = {}) { + this.situation = situation + this.resetCache() + } + + setDefaultUnits(defaultUnits = []) { + this.cache._meta.defaultUnits = defaultUnits.map(unit => + parseUnit(unit) + ) as any + } + + evaluate(expression: string | Array) { + const results = (Array.isArray(expression) ? expression : [expression]).map( + expr => + this.cache[expr] || + (this.parsedRules[expr] + ? evaluateNode( + this.cache, + this.situationGate, + this.parsedRules, + this.parsedRules[expr] + // TODO: To support expressions (with operations, unit conversion, + // etc.) it should be enough to replace the above line with : + // parse(this.parsedRules, { dottedName: '' }, this.parsedRules)(expr) + // But currently there are small side effects (null values converted + // to 0), so we need to modify a little bit the engine before enabling + // publicode expressions in the UI. + ) + : null) + ) + + return Array.isArray(expression) ? results : results[0] + } + + situationGate = (dottedName: string) => + this.situation[dottedName] || this.defaultValues[dottedName] +} diff --git a/source/engine/parse.js b/source/engine/parse.tsx similarity index 98% rename from source/engine/parse.js rename to source/engine/parse.tsx index 968a1c267..76386546c 100644 --- a/source/engine/parse.js +++ b/source/engine/parse.tsx @@ -25,7 +25,7 @@ import { subtract } from 'ramda' import React from 'react' -import { syntaxError } from './error.ts' +import { syntaxError } from './error' import grammar from './grammar.ne' import { mecanismAllOf, @@ -69,7 +69,7 @@ Utilisez leur contrepartie française : 'oui' / 'non'` const compiledGrammar = Grammar.fromCompiled(grammar) -const parseExpression = (rule, rawNode) => { +export const parseExpression = (rule, rawNode) => { /* Strings correspond to infix expressions. * Indeed, a subset of expressions like simple arithmetic operations `3 + (quantity * 2)` or like `salary [month]` are more explicit that their prefixed counterparts. * This function makes them prefixed operations. */ diff --git a/source/engine/parseRule.tsx b/source/engine/parseRule.tsx index 9db4967b5..67dbcbd90 100644 --- a/source/engine/parseRule.tsx +++ b/source/engine/parseRule.tsx @@ -1,13 +1,13 @@ import { ShowValuesConsumer } from 'Components/rule/ShowValuesContext' import RuleLink from 'Components/RuleLink' -import evaluate from 'Engine/evaluateRule' -import { parse } from 'Engine/parse' import { evolve, map } from 'ramda' import React from 'react' import { Trans } from 'react-i18next' import { coerceArray } from '../utils' +import evaluate from './evaluateRule' import { evaluateNode, makeJsx, mergeAllMissing } from './evaluation' import { Node } from './mecanismViews/common' +import { parse } from './parse' import { disambiguateRuleReference, findParentDependencies } from './rules' export default (rules, rule, parsedRules) => { diff --git a/source/engine/react.tsx b/source/engine/react.tsx new file mode 100644 index 000000000..ef456f308 --- /dev/null +++ b/source/engine/react.tsx @@ -0,0 +1,37 @@ +import Value from 'Components/Value' +import React, { createContext, useContext, useMemo } from 'react' +import Engine from '.' + +const EngineContext = createContext(new Engine()) + +type InputProps = { + rules?: any + situation?: any + children: React.ReactNode +} + +export function Provider({ rules, situation, children }: InputProps) { + const engine = useMemo(() => new Engine({ rules }), [rules]) + if (!Object.is(situation, engine.situation)) { + engine.setSituation(situation) + } + return ( + {children} + ) +} + +export function useEvaluation(expression: string) { + const engine = useContext(EngineContext) + return engine.evaluate(expression) +} + +export function Evaluation({ expression }) { + const value = useEvaluation(expression) + return +} + +export default { + Provider, + useEvaluation, + Evaluation +} diff --git a/source/sites/publi.codes/Studio.tsx b/source/sites/publi.codes/Studio.tsx index f42445698..9dcd1bef1 100644 --- a/source/sites/publi.codes/Studio.tsx +++ b/source/sites/publi.codes/Studio.tsx @@ -1,11 +1,9 @@ import baremeIr from '!!raw-loader!./exemples/bareme-ir.yaml' import douche from '!!raw-loader!./exemples/douche.yaml' import { ControlledEditor } from '@monaco-editor/react' -import { formatValue } from 'Engine/format' -import Engine from 'Engine/index' -import { buildFlatRules } from 'Engine/rules' +import Engine from 'Engine/react' import { safeLoad } from 'js-yaml' -import React, { useRef, useState } from 'react' +import React, { useState } from 'react' import emoji from 'react-easy-emoji' import { useLocation } from 'react-router' import styled from 'styled-components' @@ -63,75 +61,69 @@ export function Studio() { ) const [targets, setTargets] = useState([]) const [currentTarget, setCurrentTarget] = useState('') - const [analysis, setAnalysis] = useState() - const engine = useRef(null) + const [rules, setRules] = useState(editorValue) try { setTargets(Object.keys(safeLoad(editorValue) ?? {})) } catch {} - function updateResult() { - engine.current = new Engine.Engine(buildFlatRules(safeLoad(editorValue))) - engine.current.evaluate( - [targets.includes(currentTarget) ? currentTarget : targets[0]], - { defaultUnits: [], situation: {} } - ) - setAnalysis(engine.current.getLastEvaluationExplanations()?.targets?.[0]) - } - return ( - -
- + +
+ setEditorValue(newValue ?? '')} + options={{ minimap: { enabled: false } }} + /> +
+
setEditorValue(newValue ?? '')} - options={{ minimap: { enabled: false } }} - /> -
-
-
- - -
- -
- -
-
+ + +
+
+ + + +
+
+ ) } @@ -153,18 +145,15 @@ const Layout = styled.div` } ` -export const Results = ({ analysis }) => { +export const Results = ({ rule }) => { + const analysis = Engine.useEvaluation(rule) return analysis ? (

Résultats

{analysis.isApplicable === false ? ( - <>❌ Cette règle n'est pas applicable + <>{emoji('❌')} Cette règle n'est pas applicable ) : ( - formatValue({ - language: 'fr', - value: analysis.nodeValue, - unit: analysis.unit - }) + )}
) : null diff --git a/source/types/import-nearley.ts b/source/types/import-nearley.ts new file mode 100644 index 000000000..0bdaa74fa --- /dev/null +++ b/source/types/import-nearley.ts @@ -0,0 +1,4 @@ +declare module '*.ne' { + const content: any + export default content +} diff --git a/source/types/rule.ts b/source/types/rule.ts index b4d2f3776..4bdb2eff3 100644 --- a/source/types/rule.ts +++ b/source/types/rule.ts @@ -10,6 +10,7 @@ export type Rule = { summary?: string title?: string defaultValue: any + parentDependencies: Array icons: string formule: any } diff --git a/test/library.test.js b/test/library.test.js index ccf79e75c..1b480c1df 100644 --- a/test/library.test.js +++ b/test/library.test.js @@ -1,32 +1,33 @@ import { expect } from 'chai' -import Lib from '../source/engine/index' +import Engine from '../source/engine/index' import co2 from './rules/co2.yaml' import sasuRules from './rules/sasu.yaml' describe('library', function() { it('should evaluate one target with no input data', function() { let target = 'contrat salarié . rémunération . net' - let value = Lib.evaluate(target, { + let engine = new Engine() + engine.setSituation({ 'contrat salarié . rémunération . brut de base': 2300 }) - expect(value).to.be.within(1798, 1800) + expect(engine.evaluate(target).nodeValue).to.be.within(1798, 1800) }) it('should let the user replace the default rules', function() { let rules = ` -- nom: yo +yo: formule: 200 -- nom: ya +ya: formule: yo + 1 -- nom: yi +yi: formule: yo + 2 ` + let engine = new Engine({ rules }) - let values = Lib.evaluate(['ya', 'yi'], {}, { base: rules }) - - expect(values[0]).to.equal(201) - expect(values[1]).to.equal(202) + expect(engine.evaluate('ya').nodeValue).to.equal(201) + expect(engine.evaluate('yi').nodeValue).to.equal(202) }) + it('should let the user add rules to the default ones', function() { let rules = ` yo: @@ -34,60 +35,54 @@ yo: ya: formule: contrat salarié . rémunération . net + yo ` - - let value = Lib.evaluate( - 'ya', - { - 'contrat salarié . rémunération . brut de base': 2300 - }, - { extra: rules } - ) - - expect(value).to.be.closeTo(1799, 1) + let engine = new Engine({ extra: rules }) + engine.setSituation({ + 'contrat salarié . rémunération . brut de base': 2300 + }) + expect(engine.evaluate('ya').nodeValue).to.be.closeTo(1799, 1) }) + it('should let the user extend the rules constellation in a serious manner', function() { let CA = 550 * 16 - let salaireTotal = Lib.evaluate( - 'salaire total', - { - 'chiffre affaires': CA - }, - { extra: sasuRules } - ) + let engine = new Engine({ extra: sasuRules }) + engine.setSituation({ + 'chiffre affaires': CA + }) + let salaireTotal = engine.evaluate('salaire total').nodeValue - let salaireNetAprèsImpôt = Lib.evaluate( + engine.setSituation({ + 'contrat salarié . prix du travail': salaireTotal + }) + let salaireNetAprèsImpôt = engine.evaluate( + 'contrat salarié . rémunération . net après impôt' + ).nodeValue + + engine.setSituation({ + 'contrat salarié . rémunération . net après impôt': salaireNetAprèsImpôt, + 'chiffre affaires': CA + }) + let [revenuDisponible, dividendes] = engine.evaluate([ 'contrat salarié . rémunération . net après impôt', - { - 'contrat salarié . prix du travail': salaireTotal - } - ) - let [revenuDisponible, dividendes] = Lib.evaluate( - ['contrat salarié . rémunération . net après impôt', 'dividendes . net'], - { - 'contrat salarié . rémunération . net après impôt': salaireNetAprèsImpôt, - 'chiffre affaires': CA - }, - { extra: sasuRules } - ) + 'dividendes . net' + ]) - expect(revenuDisponible).to.be.closeTo(2324, 1) - expect(dividendes).to.be.closeTo(2507, 1) + expect(revenuDisponible.nodeValue).to.be.closeTo(2324, 1) + expect(dividendes.nodeValue).to.be.closeTo(2507, 1) }).timeout(5000) it('should let the user define a simplified revenue tax system', function() { - let règles = ` -- nom: revenu imposable + let rules = ` +revenu imposable: question: Quel est votre revenu imposable ? unité: € -- nom: revenu abattu +revenu abattu: formule: allègement: assiette: revenu imposable abattement: 10% - -- nom: impôt sur le revenu +impôt sur le revenu: formule: barème: assiette: revenu abattu @@ -102,8 +97,7 @@ ya: plafond: 153783 - taux: 45% - -- nom: impôt sur le revenu à payer +impôt sur le revenu à payer: formule: allègement: assiette: impôt sur le revenu @@ -112,28 +106,22 @@ ya: plafond: 1177 ` - let target = 'impôt sur le revenu à payer' - - let value = Lib.evaluate( - target, - { 'revenu imposable': '48000' }, - { base: règles } - ) - expect(value).to.equal(7253.26) + let engine = new Engine({ rules }) + engine.setSituation({ + 'revenu imposable': '48000' + }) + let value = engine.evaluate('impôt sur le revenu à payer') + expect(value.nodeValue).to.equal(7253.26) }) - it('should let let user define a rule base on a completely different subject', function() { - let targets = 'impact' - let value = Lib.evaluate( - targets, - { - 'nombre de douches': 30, - 'chauffage . type': 'gaz', - 'durée de la douche': 10 - }, - { base: co2, debug: false } - ) - //console.log(JSON.stringify(value.targets[0], null, 4)) - expect(value).to.be.within(20, 21) + it('should let the user define a rule base on a completely different subject', function() { + let engine = new Engine({ rules: co2 }) + engine.setSituation({ + 'nombre de douches': 30, + 'chauffage . type': 'gaz', + 'durée de la douche': 10 + }) + let value = engine.evaluate('douche . impact') + expect(value.nodeValue).to.be.within(20, 21) }) }) diff --git a/test/regressions/simulations.jest.js b/test/regressions/simulations.jest.js index bad6fbebd..9c54d99a7 100644 --- a/test/regressions/simulations.jest.js +++ b/test/regressions/simulations.jest.js @@ -11,7 +11,7 @@ import autoentrepreneurConfig from '../../source/components/simulationConfigs/au import independantConfig from '../../source/components/simulationConfigs/indépendant.yaml' import remunerationDirigeantConfig from '../../source/components/simulationConfigs/rémunération-dirigeant.yaml' import employeeConfig from '../../source/components/simulationConfigs/salarié.yaml' -import Lib from '../../source/engine/index' +import Engine from '../../source/engine' import artisteAuteurSituations from './simulations-artiste-auteur.yaml' import autoEntrepreneurSituations from './simulations-auto-entrepreneur.yaml' import independentSituations from './simulations-indépendant.yaml' @@ -19,7 +19,7 @@ import remunerationDirigeantSituations from './simulations-rémunération-dirige import employeeSituations from './simulations-salarié.yaml' const roundResult = arr => arr.map(x => Math.round(x)) -const engine = new Lib.Engine() +const engine = new Engine() const runSimulations = ( situations, targets, @@ -29,10 +29,9 @@ const runSimulations = ( ) => Object.entries(situations).map(([name, situations]) => situations.forEach(situation => { - const res = engine.evaluate(targets, { - situation: { ...baseSituation, ...situation }, - defaultUnits - }) + engine.setSituation({ ...baseSituation, ...situation }) + engine.setDefaultUnits(defaultUnits) + const res = engine.evaluate(targets).map(node => node.nodeValue) // Stringify is not required, but allows the result to be displayed in a single // line in the snapshot, which considerably reduce the number of lines of this snapshot // and improve its readability. diff --git a/tsconfig.json b/tsconfig.json index f44cf3a3d..bc12ea112 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,6 +28,7 @@ "Components": ["components"], "Components/*": ["components/*"], "Ui/*": ["components/ui/*"], + "Engine": ["engine"], "Engine/*": ["engine/*"], "Images/*": ["images/*"], "Reducers/*": ["reducers/*"], diff --git a/yarn.lock b/yarn.lock index ce704d32e..3e27bb1ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1200,6 +1200,11 @@ resolved "https://registry.yarnpkg.com/@types/mime-types/-/mime-types-2.1.0.tgz#9ca52cda363f699c69466c2a6ccdaad913ea7a73" integrity sha1-nKUs2jY/aZxpRmwqbM2q2RPqenM= +"@types/nearley@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@types/nearley/-/nearley-2.11.1.tgz#6ac3f57c00ca28071a1774ec72d2e45750f21420" + integrity sha512-oaAg5gn74VFpPYs6Ou2pjDao3WJxnlnH29q9rLOxSGb0PTw2QtBQcTAN9xs1OAHrtI9En5kIXKM96stf7//c9w== + "@types/node@*": version "13.1.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.1.tgz#6d11a8c2d58405b3db9388ab740106cbfa64c3c9" @@ -1215,12 +1220,12 @@ resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== -"@types/ramda@^0.26.33": - version "0.26.39" - resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.26.39.tgz#bb392cdb4d431cf6884b3ef11bef635c5d20274f" - integrity sha512-3bu32X02VpjJhsYPUWkdOQGoBXjb/UveZgGg4IYMm+SPAXio96BOYrRhVELfM4AoP00sxoi/f2tqrXdwtR4jjg== +"@types/ramda@^0.26.43": + version "0.26.43" + resolved "https://registry.yarnpkg.com/@types/ramda/-/ramda-0.26.43.tgz#62e235ea17133b8629bc891a26851cf0ea2b4204" + integrity sha512-VK2EaHR/fpeMNPDboGSPAmH+a6HN1pflWqRt67Jii2n8vGpYt6vxIBc1ZdoYrA/jQXRaGGpJKRiSPcHALRD3/A== dependencies: - ts-toolbelt "^4.12.0" + ts-toolbelt "^6.3.3" "@types/raven-for-redux@^1.1.1": version "1.1.1" @@ -11030,10 +11035,10 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.4.tgz#3b52b1f13924f460c3fbfd0df69b587dbcbc762e" integrity sha512-tdzBRDGWcI1OpPVmChbdSKhvSVurznZ8X36AYURAcl+0o2ldlCY2XPzyXNNxwJwwyIU+rIglTCG4kxtNKBQH7Q== -ts-toolbelt@^4.12.0: - version "4.14.6" - resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-4.14.6.tgz#9a232f62276caeee4fa9e81e0c4bffa047de0765" - integrity sha512-SONcnRd93+LuYGfn/CZg5A5qhCODohZslAVZKHHu5bnwUxoXLqd2k2VIdwRUXYfKnY+UCeNbI2pTPz+Dno6Mpg== +ts-toolbelt@^6.3.3: + version "6.3.5" + resolved "https://registry.yarnpkg.com/ts-toolbelt/-/ts-toolbelt-6.3.5.tgz#5cb4e0454ab954faa9b6e4d5bce366fdb262e364" + integrity sha512-Xvh/gvBBCRU1qGeholaN8kgiwBH4neyun6VIDDsJf/jNwz4PXyR8ZY/5qdpB1DuMBrWMG2oTT1oWcOzGPOnluQ== tslib@^1.9.0: version "1.10.0"