diff --git a/package.json b/package.json index b7ae5af..39f21ef 100644 --- a/package.json +++ b/package.json @@ -69,8 +69,8 @@ "ng-annotate-webpack-plugin": "^0.3.0", "node-sass": "^4.13.0", "prop-types": "^15.6.2", - "react": "^16.13.1", - "react-dom": "^16.13.1", + "react": "16.12.0", + "react-dom": "16.12.0", "react-popper": "^2.2.3", "react-table-6": "^6.8.6", "react-test-renderer": "^16.7.0", @@ -83,7 +83,7 @@ "ts-jest": "^24.2.0", "ts-loader": "^4.4.1", "tslint": "5.20.1", - "typescript": "3.7.2", + "typescript": "^3.9.2", "webpack": "4.29.6", "webpack-cli": "3.2.3" }, diff --git a/src/datasource-zabbix/components/FunctionEditor.tsx b/src/datasource-zabbix/components/FunctionEditor.tsx index c6ae673..528fc05 100644 --- a/src/datasource-zabbix/components/FunctionEditor.tsx +++ b/src/datasource-zabbix/components/FunctionEditor.tsx @@ -39,7 +39,6 @@ class FunctionEditor extends React.PureComponent

{name}

{description}
- /> ); } diff --git a/src/datasource-zabbix/constants.ts b/src/datasource-zabbix/constants.ts index ad90cd2..41d3776 100644 --- a/src/datasource-zabbix/constants.ts +++ b/src/datasource-zabbix/constants.ts @@ -28,8 +28,10 @@ export const SHOW_OK_EVENTS = 1; // Acknowledge export const ZBX_ACK_ACTION_NONE = 0; +export const ZBX_ACK_ACTION_CLOSE = 1; export const ZBX_ACK_ACTION_ACK = 2; export const ZBX_ACK_ACTION_ADD_MESSAGE = 4; +export const ZBX_ACK_ACTION_CHANGE_SEVERITY = 8; export const TRIGGER_SEVERITY = [ {val: 0, text: 'Not classified'}, diff --git a/src/datasource-zabbix/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts b/src/datasource-zabbix/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts index 9744da4..657fdab 100644 --- a/src/datasource-zabbix/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts +++ b/src/datasource-zabbix/zabbix/connectors/zabbix_api/zabbixAPIConnector.ts @@ -133,14 +133,21 @@ export class ZabbixAPIConnector { // Zabbix API method wrappers // //////////////////////////////// - acknowledgeEvent(eventid, message) { - const action = semver.gte(this.version, '4.0.0') ? ZBX_ACK_ACTION_ACK + ZBX_ACK_ACTION_ADD_MESSAGE : ZBX_ACK_ACTION_NONE; - const params = { + acknowledgeEvent(eventid: string, message: string, action?: number, severity?: number) { + if (!action) { + action = semver.gte(this.version, '4.0.0') ? ZBX_ACK_ACTION_ADD_MESSAGE : ZBX_ACK_ACTION_NONE; + } + + const params: any = { eventids: eventid, message: message, action: action }; + if (severity) { + params.severity = severity; + } + return this.request('event.acknowledge', params); } diff --git a/src/panel-triggers/components/AckModal.tsx b/src/panel-triggers/components/AckModal.tsx new file mode 100644 index 0000000..f69d59c --- /dev/null +++ b/src/panel-triggers/components/AckModal.tsx @@ -0,0 +1,236 @@ +import React, { PureComponent } from 'react'; +import ReactDOM from 'react-dom'; +import classNames from 'classnames'; +import { ZBX_ACK_ACTION_ADD_MESSAGE, ZBX_ACK_ACTION_ACK, ZBX_ACK_ACTION_CHANGE_SEVERITY, ZBX_ACK_ACTION_CLOSE } from '../../datasource-zabbix/constants'; +import { Button, Input, VerticalGroup, Spinner } from '@grafana/ui'; +import { FAIcon } from './FAIcon'; + +import * as grafanaUi from '@grafana/ui'; +const Checkbox: any = grafanaUi.Forms?.Checkbox || (grafanaUi as any).Checkbox; +const RadioButtonGroup: any = grafanaUi.Forms?.RadioButtonGroup || (grafanaUi as any).RadioButtonGroup; + +const KEYBOARD_ENTER_KEY = 13; +const KEYBOARD_ESCAPE_KEY = 27; + +interface Props { + canAck?: boolean; + canClose?: boolean; + isOpen?: boolean; + withBackdrop?: boolean; + onSubmit: (data?: AckProblemData) => Promise | any; + onClose?: () => void; +} + +interface State { + value: string; + error: boolean; + errorMessage: string; + ackError: string; + acknowledge: boolean; + closeProblem: boolean; + changeSeverity: boolean; + selectedSeverity: number; + loading: boolean; +} + +export interface AckProblemData { + message: string; + closeProblem?: boolean; + action?: number; + severity?: number; +} + +const severityOptions = [ + {value: 0, label: 'Not classified'}, + {value: 1, label: 'Information'}, + {value: 2, label: 'Warning'}, + {value: 3, label: 'Average'}, + {value: 4, label: 'High'}, + {value: 5, label: 'Disaster'} +]; + +export class AckModal extends PureComponent { + modalContainer: HTMLElement; + + static defaultProps: Partial = { + withBackdrop: true, + }; + + constructor(props) { + super(props); + this.state = { + value: '', + error: false, + errorMessage: '', + ackError: '', + acknowledge: false, + closeProblem: false, + changeSeverity: false, + selectedSeverity: 0, + loading: false, + }; + + this.modalContainer = document.body; + } + + handleChange = (event: React.ChangeEvent) => { + this.setState({ value: event.target.value, error: false }); + } + + handleKeyUp = (event: React.KeyboardEvent) => { + if (event.which === KEYBOARD_ENTER_KEY || event.key === 'Enter') { + this.submit(); + } else if (event.which === KEYBOARD_ESCAPE_KEY || event.key === 'Escape') { + this.dismiss(); + } + } + + handleBackdropClick = () => { + this.dismiss(); + } + + onAcknowledgeToggle = () => { + this.setState({ acknowledge: !this.state.acknowledge, error: false }); + } + + onChangeSeverityToggle = () => { + this.setState({ changeSeverity: !this.state.changeSeverity, error: false }); + } + + onCloseProblemToggle = () => { + this.setState({ closeProblem: !this.state.closeProblem, error: false }); + } + + onChangeSelectedSeverity = v => { + this.setState({ selectedSeverity: v }); + }; + + dismiss = () => { + this.setState({ value: '', error: false, errorMessage: '', ackError: '', loading: false }); + this.props.onClose(); + } + + submit = () => { + const { acknowledge, changeSeverity, closeProblem } = this.state; + + const actionSelected = acknowledge || changeSeverity || closeProblem; + if (!this.state.value && !actionSelected) { + return this.setState({ + error: true, + errorMessage: 'Enter message text or select an action' + }); + } + + this.setState({ ackError: '', loading: true }); + + const ackData: AckProblemData = { + message: this.state.value, + }; + + let action = ZBX_ACK_ACTION_ADD_MESSAGE; + if (this.state.acknowledge) { + action += ZBX_ACK_ACTION_ACK; + } + if (this.state.changeSeverity) { + action += ZBX_ACK_ACTION_CHANGE_SEVERITY; + ackData.severity = this.state.selectedSeverity; + } + if (this.state.closeProblem) { + action += ZBX_ACK_ACTION_CLOSE; + } + ackData.action = action; + + this.props.onSubmit(ackData).then((response) => { + this.dismiss(); + }).catch(err => { + this.setState({ + ackError: err.message || err.data, + loading: false, + }); + }); + } + + render() { + const { canClose, canAck } = this.props; + if (!this.props.isOpen || !this.modalContainer) { + return null; + } + + const inputClass = classNames({ 'zbx-ack-error': this.state.error }); + + const modalNode = ( +
+
+
+

+ {this.state.loading ? : } + Acknowledge Problem +

+ + + + +
+
+
+ +
+ +
+ + + + {this.state.changeSeverity && + + } + {canClose && + + } + +
+ + {this.state.ackError && +
+ {this.state.ackError} +
+ } + +
+ + +
+
+
+
+ ); + + const modalNodeWithBackdrop = [ + modalNode, +
+ ]; + + const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode; + return ReactDOM.createPortal(modal, this.modalContainer); + } +} diff --git a/src/panel-triggers/components/AlertList/AlertCard.tsx b/src/panel-triggers/components/AlertList/AlertCard.tsx index 7d6dc5d..2bb7559 100644 --- a/src/panel-triggers/components/AlertList/AlertCard.tsx +++ b/src/panel-triggers/components/AlertList/AlertCard.tsx @@ -4,7 +4,7 @@ import _ from 'lodash'; import moment from 'moment'; import { isNewProblem, formatLastChange } from '../../utils'; import { ProblemsPanelOptions, TriggerSeverity } from '../../types'; -import { AckProblemData, Modal } from '.././Modal'; +import { AckProblemData, AckModal } from '../AckModal'; import EventTag from '../EventTag'; import Tooltip from '.././Tooltip/Tooltip'; import AlertAcknowledges from './AlertAcknowledges'; @@ -165,7 +165,7 @@ export default class AlertCard extends PureComponent - diff --git a/src/panel-triggers/components/AlertList/AlertList.tsx b/src/panel-triggers/components/AlertList/AlertList.tsx index 8862334..3d1fdde 100644 --- a/src/panel-triggers/components/AlertList/AlertList.tsx +++ b/src/panel-triggers/components/AlertList/AlertList.tsx @@ -1,7 +1,7 @@ import React, { PureComponent, CSSProperties } from 'react'; import classNames from 'classnames'; import { ProblemsPanelOptions, GFTimeRange } from '../../types'; -import { AckProblemData } from '.././Modal'; +import { AckProblemData } from '../AckModal'; import AlertCard from './AlertCard'; import { ProblemDTO, ZBXTag } from '../../../datasource-zabbix/types'; diff --git a/src/panel-triggers/components/Modal.tsx b/src/panel-triggers/components/Modal.tsx deleted file mode 100644 index 4cedb8d..0000000 --- a/src/panel-triggers/components/Modal.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import React, { PureComponent } from 'react'; -import ReactDOM from 'react-dom'; -import classNames from 'classnames'; - -const KEYBOARD_ENTER_KEY = 13; -const KEYBOARD_ESCAPE_KEY = 27; - -interface ModalProps { - isOpen?: boolean; - withBackdrop?: boolean; - onSubmit: (data?: AckProblemData) => Promise | any; - onClose?: () => void; -} - -interface ModalState { - value: string; - error: boolean; - message: string; -} - -export interface AckProblemData { - message: string; - closeProblem?: boolean; - action?: number; -} - -export class Modal extends PureComponent { - modalContainer: HTMLElement; - - constructor(props) { - super(props); - this.state = { - value: '', - error: false, - message: '', - }; - - this.modalContainer = document.body; - } - - handleChange = (event: React.ChangeEvent) => { - this.setState({ value: event.target.value, error: false }); - } - - handleKeyUp = (event: React.KeyboardEvent) => { - if (event.which === KEYBOARD_ENTER_KEY || event.key === 'Enter') { - this.submit(); - } else if (event.which === KEYBOARD_ESCAPE_KEY || event.key === 'Escape') { - this.dismiss(); - } - } - - handleBackdropClick = () => { - this.dismiss(); - } - - dismiss = () => { - this.setState({ value: '', error: false, message: '' }); - this.props.onClose(); - } - - submit = () => { - if (!this.state.value) { - return this.setState({ - error: true, - message: 'Enter message text' - }); - } - this.props.onSubmit({ - message: this.state.value - }).then(() => { - this.dismiss(); - }); - } - - render() { - if (!this.props.isOpen || !this.modalContainer) { - return null; - } - - const inputClass = classNames('gf-form-input', { 'zbx-ack-error': this.state.error }); - - const modalNode = ( -
-
-
-

- - Acknowledge Problem -

- - - - -
-
-
- -
- -
- - -
-
-
-
- ); - - const modalNodeWithBackdrop = [ - modalNode, -
- ]; - - const modal = this.props.withBackdrop ? modalNodeWithBackdrop : modalNode; - return ReactDOM.createPortal(modal, this.modalContainer); - } -} diff --git a/src/panel-triggers/components/Problems/ProblemDetails.tsx b/src/panel-triggers/components/Problems/ProblemDetails.tsx index 19004f6..fe98234 100644 --- a/src/panel-triggers/components/Problems/ProblemDetails.tsx +++ b/src/panel-triggers/components/Problems/ProblemDetails.tsx @@ -4,7 +4,7 @@ import * as utils from '../../../datasource-zabbix/utils'; import { MODE_ITEMID, MODE_METRICS } from '../../../datasource-zabbix/constants'; import { ProblemDTO, ZBXHost, ZBXGroup, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types'; import { ZBXItem, ZBXAcknowledge, GFTimeRange, RTRow } from '../../types'; -import { Modal, AckProblemData } from '../Modal'; +import { AckModal, AckProblemData } from '../AckModal'; import EventTag from '../EventTag'; import Tooltip from '../Tooltip/Tooltip'; import ProblemStatusBar from './ProblemStatusBar'; @@ -79,9 +79,6 @@ export default class ProblemDetails extends PureComponent { this.closeAckDialog(); - }).catch(err => { - console.log(err); - this.closeAckDialog(); }); } @@ -211,7 +208,8 @@ export default class ProblemDetails extends PureComponent} {problem.hosts && } - diff --git a/src/panel-triggers/components/Problems/Problems.tsx b/src/panel-triggers/components/Problems/Problems.tsx index 4cec0ca..862a5f0 100644 --- a/src/panel-triggers/components/Problems/Problems.tsx +++ b/src/panel-triggers/components/Problems/Problems.tsx @@ -7,7 +7,7 @@ import * as utils from '../../../datasource-zabbix/utils'; import { isNewProblem } from '../../utils'; import EventTag from '../EventTag'; import ProblemDetails from './ProblemDetails'; -import { AckProblemData } from '../Modal'; +import { AckProblemData } from '../AckModal'; import GFHeartIcon from '../GFHeartIcon'; import { ProblemsPanelOptions, GFTimeRange, RTCell, TriggerSeverity, RTResized } from '../../types'; import { ProblemDTO, ZBXEvent, ZBXTag, ZBXAlert } from '../../../datasource-zabbix/types'; diff --git a/src/panel-triggers/triggers_panel_ctrl.ts b/src/panel-triggers/triggers_panel_ctrl.ts index 2ca1010..075bfcb 100644 --- a/src/panel-triggers/triggers_panel_ctrl.ts +++ b/src/panel-triggers/triggers_panel_ctrl.ts @@ -329,7 +329,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl { this.render(); } - acknowledgeProblem(problem: ProblemDTO, message) { + acknowledgeProblem(problem: ProblemDTO, message, action, severity) { const eventid = problem.eventid; const grafana_user = this.contextSrv.user.name; const ack_message = grafana_user + ' (Grafana): ' + message; @@ -340,7 +340,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl { return Promise.reject({message: 'You have no permissions to acknowledge events.'}); } if (eventid) { - return datasource.zabbix.acknowledgeEvent(eventid, ack_message); + return datasource.zabbix.acknowledgeEvent(eventid, ack_message, action, severity); } else { return Promise.reject({message: 'Trigger has no events. Nothing to acknowledge.'}); } @@ -348,6 +348,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl { .then(this.refresh.bind(this)) .catch((err) => { this.setPanelError(err); + return Promise.reject(err); }); } @@ -401,8 +402,8 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl { onPageSizeChange: ctrl.handlePageSizeChange.bind(ctrl), onColumnResize: ctrl.handleColumnResize.bind(ctrl), onProblemAck: (trigger, data) => { - const message = data.message; - return ctrl.acknowledgeProblem(trigger, message); + const { message, action, severity } = data; + return ctrl.acknowledgeProblem(trigger, message, action, severity); }, onTagClick: (tag, datasource, ctrlKey, shiftKey) => { if (ctrlKey || shiftKey) { diff --git a/src/sass/_panel-problems.scss b/src/sass/_panel-problems.scss index 9d3030e..9176635 100644 --- a/src/sass/_panel-problems.scss +++ b/src/sass/_panel-problems.scss @@ -598,9 +598,19 @@ } .zbx-ack-modal { - .gf-form-input.zbx-ack-error { + .zbx-ack-error { border-color: $btn-danger-bg; - box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px $btn-danger-bg; + // box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px $btn-danger-bg; + outline-offset: 2px; + box-shadow: 0 0 0 2px #141619, 0 0 0px 4px $btn-danger-bg; + } + + .ack-request-error { + padding-top: 1.2rem; + } + + .ack-error-message { + color: $error-text-color; } .gf-form .gf-form-hint { @@ -610,7 +620,6 @@ &.ack-error-message { float: left; - color: $error-text-color; } } } diff --git a/tsconfig.json b/tsconfig.json index 541b295..ee6b64c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,6 +18,7 @@ "noImplicitUseStrict": false, "noImplicitAny": false, "noUnusedLocals": false, - "baseUrl": "./src" + "baseUrl": "./src", + "strictFunctionTypes": false } } diff --git a/yarn.lock b/yarn.lock index b34860c..2dbfbae 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,10 +33,10 @@ dependencies: "@antv/util" "~1.3.1" -"@antv/g2@3.5.13": - version "3.5.13" - resolved "https://registry.yarnpkg.com/@antv/g2/-/g2-3.5.13.tgz#2b0365406e89855e6af34cd374bcb307a0edad42" - integrity sha512-Ok1hA4GyXWrYEAfDo54um4XCz2QNgwe4i45xNsVKHx+HXY0lwzkLp+LLa0QoFVW3gcxrBCrsGww3rp6GafEJGg== +"@antv/g2@3.5.15": + version "3.5.15" + resolved "https://registry.yarnpkg.com/@antv/g2/-/g2-3.5.15.tgz#5951808f88210f4a45ca1acb38fb25a743b4a578" + integrity sha512-gWN28V/BRHrCe6O12WcJ7ji9UE8XETSQ146ur4zMu5I50ZO7kxc/3s038N0iyuJh3Em9nlrTjfhqjlysogrX7g== dependencies: "@antv/adjust" "~0.1.0" "@antv/attr" "~0.1.2" @@ -2244,11 +2244,11 @@ bindings@^1.5.0: file-uri-to-path "1.0.0" bizcharts@^3.5.5: - version "3.5.8" - resolved "https://registry.yarnpkg.com/bizcharts/-/bizcharts-3.5.8.tgz#50abcb4960891aada6ca35318af791dd68d85825" - integrity sha512-s/Nt66HLQXD8oyN8yE26inh5ZGkoIr1eFE+/2TBln6lpyATm51LrqCXJPOTgOSyEp3dSNVZ7rOFCKFMMVcdOwA== + version "3.5.9" + resolved "https://registry.yarnpkg.com/bizcharts/-/bizcharts-3.5.9.tgz#b4c56c8bc5e8567f65748aeb3916902c4e9c98c0" + integrity sha512-1GI1SWNHfU3xRYGh4b4Dn6gfHMaOZnl0EXewZGEL5V5/m97k2kBonedA0LvtdrOQZRAAM+sP1uwny/ttkNsnEQ== dependencies: - "@antv/g2" "3.5.13" + "@antv/g2" "3.5.15" "@babel/runtime" "^7.7.6" invariant "^2.2.2" lodash.debounce "^4.0.8" @@ -7823,9 +7823,9 @@ rc-slider@8.7.1: warning "^4.0.3" rc-time-picker@^3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.7.2.tgz#fabe5501adf1374d31a2d3b47f1ba89fc2dc2467" - integrity sha512-UVWO9HXGyZoM4I2THlJsEAFcZQz+tYwdcpoHXCEFZsRLz9L2+7vV4EMp9Wa3UrtzMFEt83qSAX/90dCJeKl9sg== + version "3.7.3" + resolved "https://registry.yarnpkg.com/rc-time-picker/-/rc-time-picker-3.7.3.tgz#65a8de904093250ae9c82b02a4905e0f995e23e2" + integrity sha512-Lv1Mvzp9fRXhXEnRLO4nW6GLNxUkfAZ3RsiIBsWjGjXXvMNjdr4BX/ayElHAFK0DoJqOhm7c5tjmIYpEOwcUXg== dependencies: classnames "2.x" moment "2.x" @@ -7868,12 +7868,11 @@ rc-util@^4.0.4, rc-util@^4.4.0, rc-util@^4.8.0: shallowequal "^0.2.2" rc-util@^4.11.2: - version "4.20.1" - resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.20.1.tgz#a5976eabfc3198ed9b8e79ffb8c53c231db36e77" - integrity sha512-EGlDg9KPN0POzmAR2hk9ZyFc3DmJIrXwlC8NoDxJguX2LTINnVqwadLIVauLfYgYISMiFYFrSHiFW+cqUhZ5dA== + version "4.20.5" + resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.20.5.tgz#f7c77569e971ae6a8ad56f899cadd22275398325" + integrity sha512-f67s4Dt1quBYhrVPq5QMKmK3eS2hN1NNIAyhaiG0HmvqiGYAXMQ7SP2AlGqv750vnzhJs38JklbkWT1/wjhFPg== dependencies: add-dom-event-listener "^1.1.0" - babel-runtime "6.x" prop-types "^15.5.10" react-is "^16.12.0" react-lifecycles-compat "^3.0.4" @@ -7920,16 +7919,6 @@ react-dom@16.12.0: prop-types "^15.6.2" scheduler "^0.18.0" -react-dom@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.13.1.tgz#c1bd37331a0486c078ee54c4740720993b2e0e7f" - integrity sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" - react-fast-compare@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.0.1.tgz#884d339ce1341aad22392e7a88664c71da48600e" @@ -8069,15 +8058,6 @@ react@16.12.0: object-assign "^4.1.1" prop-types "^15.6.2" -react@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e" - integrity sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.2" - reactcss@^1.2.0: version "1.2.3" resolved "https://registry.yarnpkg.com/reactcss/-/reactcss-1.2.3.tgz#c00013875e557b1cf0dfd9a368a1c3dab3b548dd" @@ -8608,14 +8588,6 @@ scheduler@^0.18.0: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== - dependencies: - loose-envify "^1.1.0" - object-assign "^4.1.1" - schema-utils@^0.4.5: version "0.4.7" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187" @@ -9684,10 +9656,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb" - integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ== +typescript@^3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.2.tgz#64e9c8e9be6ea583c54607677dd4680a1cf35db9" + integrity sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw== typical@^2.6.1: version "2.6.1"