diff --git a/.eslintignore b/.eslintignore index adfb1bc..f360378 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,2 @@ **/*.snap +**/*.md diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 75a9bd1..0593f41 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,6 +1,18 @@ # Changelog All notable changes to this project will be documented in this file. +## [Unreleased] +### Breaking changes +- `withValidation` has been removed, use the new hook `useValiation` instead +- `withField` has been deprecated, use the new hook `useField` instead +- removed some utils that were not intended for public use + +### New features +- Added `useValidation` hook as replacement for `withValidation` +- Added `useField` hook as replacement for `withField` +- Added `useFormEventListener` hook for easy access to form events +- Added `reset` to `FormContext` + ## [2.2.0] # 2019-02-19 ### New features - Added `useFormContext` hook for easy access to the form context diff --git a/docs/introduction.md b/docs/introduction.md new file mode 100644 index 0000000..88108a5 --- /dev/null +++ b/docs/introduction.md @@ -0,0 +1,55 @@ +react-ocean-forms is a flexible and lightweight framework for rendering and validating forms with React. + +[![npm](https://img.shields.io/npm/v/react-ocean-forms.svg)](https://www.npmjs.com/package/react-ocean-forms) +[![GitHub license](https://img.shields.io/github/license/environment-agency-austria/react-ocean-forms.svg)](https://github.com/environment-agency-austria/react-ocean-forms/blob/master/LICENSE) +[![travis](https://travis-ci.com/environment-agency-austria/react-ocean-forms.svg?branch=master)](https://travis-ci.com/environment-agency-austria/react-ocean-forms) [![Greenkeeper badge](https://badges.greenkeeper.io/environment-agency-austria/react-ocean-forms.svg)](https://greenkeeper.io/) +[![Coverage Status](https://coveralls.io/repos/github/environment-agency-austria/react-ocean-forms/badge.svg?branch=master)](https://coveralls.io/github/environment-agency-austria/react-ocean-forms?branch=master) + +## Installation +with npm: + +```bash +npm install --save-dev react-ocean-forms +``` +or with yarn: + +```bash +yarn add --dev react-ocean-forms +``` + +### Optional packages +The react-ocean-forms package is the core package. While you can use it on it's own, we +highly recommend adding the [react-ocean-forms-bootstrap](https://github.com/environment-agency-austria/react-ocean-forms-bootstrap) +package as well. It offers you easy bootstrap (reactstrap) integration. + +If you're using react-intl in your project, add the +[react-ocean-forms-react-intl](https://github.com/environment-agency-austria/react-ocean-forms-react-intl) +package for react-intl support. + +## Getting started +Assuming that you already have an up and running React app, otherwise please follow the +[create-react-app](https://github.com/facebook/create-react-app#creating-an-app) guideline. + +After adding the react-ocean-forms package(s) to your project, you can simply import the components you need. + +```jsx static +import React from 'react'; +import { Form, Input } from 'react-ocean-forms'; + +function handleSubmit(values) { + console.log('form submitted with', values); +} + +function GettingStarted() { + return ( +
+ + +
+ ); +} + +export default GettingStarted; +``` + +For further documentation head to the components section. diff --git a/package.json b/package.json index 9819fbe..67e24bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-ocean-forms", - "version": "2.2.0", + "version": "3.0.0-alpha.1", "description": "Forms components for react based on the context api.", "main": "build/index.js", "module": "build/index.es.js", @@ -13,7 +13,9 @@ "test:coverage": "jest --coverage", "start": "rollup -c -w", "build": "rollup -c", - "lint": "eslint \"src/**\"" + "lint": "eslint \"src/**\"", + "start:docs": "styleguidist server", + "build:docs": "styleguidist build" }, "peerDependencies": { "react": "^16.8.0" @@ -23,31 +25,36 @@ "@types/enzyme": "^3.9.1", "@types/enzyme-adapter-react-16": "^1.0.5", "@types/jest": "^24.0.11", - "@types/react": "^16.8.10", - "@typescript-eslint/eslint-plugin": "^1.6.0", - "@typescript-eslint/parser": "^1.6.0", + "@types/react": "^16.8.14", + "@typescript-eslint/eslint-plugin": "^1.7.0", + "@typescript-eslint/parser": "^1.7.0", "coveralls": "^3.0.3", "enzyme": "^3.9.0", - "enzyme-adapter-react-16": "^1.11.2", + "enzyme-adapter-react-16": "^1.12.1", "enzyme-to-json": "^3.3.5", "eslint": "^5.16.0", "eslint-config-oceanjs": "^1.1.0", "eslint-plugin-react": "^7.12.4", "eslint-plugin-react-hooks": "^1.6.0", - "jest": "^24.5.0", - "react": "^16.8.6", - "react-dom": "^16.8.6", - "rollup": "^1.7.4", - "rollup-plugin-commonjs": "^9.2.2", + "jest": "^24.7.1", + "react": "^16.9.0-alpha.0", + "react-docgen-typescript": "^1.12.4", + "react-dom": "^16.9.0-alpha.0", + "react-hooks-testing-library": "^0.5.0", + "react-styleguidist": "^9.0.8", + "react-test-renderer": "^16.9.0-alpha.0", + "rollup": "^1.10.1", + "rollup-plugin-commonjs": "^9.3.4", "rollup-plugin-filesize": "^6.0.1", - "rollup-plugin-node-resolve": "^4.0.1", - "rollup-plugin-typescript2": "^0.20.1", - "ts-jest": "^24.0.1", - "ts-loader": "^5.3.3", - "typescript": "^3.4.1" + "rollup-plugin-node-resolve": "^4.2.3", + "rollup-plugin-typescript2": "^0.21.0", + "ts-jest": "^24.0.2", + "ts-loader": "^5.4.3", + "typescript": "^3.4.5", + "webpack": "^4.30.0" }, "resolutions": { - "@types/react": "16.8.10", + "@types/react": "16.8.14", "@types/enzyme": "^3.9.1" }, "files": [ diff --git a/src/components/FieldError/FieldError.test.tsx b/src/components/FieldError/FieldError.test.tsx index fbd588e..446c7ef 100644 --- a/src/components/FieldError/FieldError.test.tsx +++ b/src/components/FieldError/FieldError.test.tsx @@ -2,15 +2,18 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; +import { useFormContext } from '../../hooks'; import { FieldError } from './FieldError'; import { IFieldErrorProps } from './FieldError.types'; +jest.mock('../../hooks'); + describe('', () => { const mockStringFormatter = jest.fn().mockReturnValue('string'); + (useFormContext as jest.Mock).mockReturnValue({ stringFormatter: mockStringFormatter }); const setup = (props?: Partial): ShallowWrapper => shallow(( ', () => { errorParams, ); }); + + it('should render multiple errors', () => { + const wrapper = setup({ + invalid: true, + error: [ + { + message_id: errorId, + params: errorParams, + }, + { + message_id: 'foo2', + params: { }, + } + ], + }); + + expect(wrapper).toMatchSnapshot(); + }); }); diff --git a/src/components/FieldError/FieldError.tsx b/src/components/FieldError/FieldError.tsx index 7a364e1..135536f 100644 --- a/src/components/FieldError/FieldError.tsx +++ b/src/components/FieldError/FieldError.tsx @@ -7,35 +7,36 @@ import React from 'react'; -import { toArray } from '../../utils'; +import { useFormContext } from '../../hooks'; import { IFieldErrorProps } from './FieldError.types'; /** * Component for displaying bootstrap * form feedbacks if there are any errors */ -export const FieldError: React.SFC = (props: IFieldErrorProps): JSX.Element | null => { +export const FieldError: React.FC = (props) => { const { id, invalid, error, - stringFormatter, } = props; + const { stringFormatter } = useFormContext(); + // If the field isn't invalid do nothing if (invalid !== true || error === null) { return null; } // Error could be either an string or an array of strings - const errorArray = toArray(error); + const errorArray = Array.isArray(error) ? error : [error]; return ( - + <> {errorArray.map((item) => { const errorString = stringFormatter(item.message_id, item.params); return {errorString}; })} - + ); }; FieldError.displayName = 'FieldError'; diff --git a/src/components/FieldError/FieldError.types.ts b/src/components/FieldError/FieldError.types.ts index 1e8edbe..dab7ada 100644 --- a/src/components/FieldError/FieldError.types.ts +++ b/src/components/FieldError/FieldError.types.ts @@ -4,7 +4,6 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { TSTringFormatter } from '../../utils'; import { TFieldErrors } from '../../validators'; /** @@ -23,8 +22,4 @@ export interface IFieldErrorProps { * Field errors */ error: TFieldErrors; - /** - * stringFormatter method - */ - stringFormatter: TSTringFormatter; } diff --git a/src/components/FieldError/__snapshots__/FieldError.test.tsx.snap b/src/components/FieldError/__snapshots__/FieldError.test.tsx.snap index c6c620c..6204556 100644 --- a/src/components/FieldError/__snapshots__/FieldError.test.tsx.snap +++ b/src/components/FieldError/__snapshots__/FieldError.test.tsx.snap @@ -11,3 +11,18 @@ exports[` should render an error 1`] = ` `; + +exports[` should render multiple errors 1`] = ` + + + string + + + string + + +`; diff --git a/src/components/FieldGroup/FieldGroup.md b/src/components/FieldGroup/FieldGroup.md new file mode 100644 index 0000000..f21ca5c --- /dev/null +++ b/src/components/FieldGroup/FieldGroup.md @@ -0,0 +1,46 @@ +### Examples +#### Simple field group +Showcase of a simple field group + +```jsx +import { Form, FieldGroup, Input } from 'react-ocean-forms'; + +function Example() { + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + // Notice how a field group creates a sub object + const defaultValues = { + myGroup: { + myInput1: 'input 1', + myInput2: 'input 2', + } + }; + + return ( +
+ { + return ( + + + + + ); + }} + /> + + + ); +} + + +``` diff --git a/src/components/FieldGroup/FieldGroup.test.tsx b/src/components/FieldGroup/FieldGroup.test.tsx index 7c0d14b..9b06375 100644 --- a/src/components/FieldGroup/FieldGroup.test.tsx +++ b/src/components/FieldGroup/FieldGroup.test.tsx @@ -2,30 +2,28 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { createMockFormContext, createMockValidation } from '../../test-utils/enzymeFormContext'; -import { IFieldState, IFormContext } from '../FormContext'; -import { IValidationProp } from '../withValidation'; -import { BaseFieldGroup } from './FieldGroup'; +import { createMockFormContext, createMockValidationResult } from '../../test-utils/enzymeFormContext'; +import { IFormContext } from '../FormContext'; + +import { FieldGroup } from './FieldGroup'; import { IFieldGroupProps } from './FieldGroup.types'; +import { useFieldGroup } from './hooks/useFieldGroup'; +import { IFieldGroupRenderParams } from './hooks/useFieldGroup.types'; + +jest.mock('./hooks/useFieldGroup'); describe('', () => { const mockName = 'unitGroup'; const mockLabel = 'Unit group'; - const mockValue = { - field1: 'value1', - field2: 'value2', - }; interface ISetupArgs { props?: Partial; contextOverrides?: Partial; - validationOverrides?: Partial; + validationOverrides?: Partial; } interface ISetupResult { formContext: IFormContext; - fieldState: IFieldState; - validation: IValidationProp; wrapper: ShallowWrapper; renderMock: jest.Mock; groupContext: IFormContext; @@ -36,31 +34,29 @@ describe('', () => { contextOverrides, validationOverrides, }: ISetupArgs = {}): ISetupResult => { - let fieldState = null; - const registerCallback = (name: string, state: IFieldState): void => { fieldState = state; }; const renderMock = jest.fn().mockReturnValue(null); - const formContext = { - ...createMockFormContext(registerCallback), - getValues: jest.fn().mockReturnValue({ - foo: 'bar', - hue: 'hue', - [mockName]: mockValue, - }), + const formContext: IFormContext = { + ...createMockFormContext(), + fieldPrefix: mockName, ...contextOverrides, }; - const validation = { - ...createMockValidation(), + const { validationState } = createMockValidationResult(); + const validation: IFieldGroupRenderParams = { + fullName: mockName, + ...validationState, ...validationOverrides, }; + (useFieldGroup as jest.Mock).mockReturnValue({ + groupFormContext: formContext, + renderParams: validation, + }); + const wrapper = shallow(( - @@ -70,9 +66,6 @@ describe('', () => { return { formContext, - //@ts-ignore Field state is always initialized through the registerCallback - fieldState, - validation, wrapper, renderMock, groupContext, @@ -86,85 +79,6 @@ describe('', () => { }); }); - describe('Form registration', () => { - let formContext: IFormContext; - let validation: IValidationProp; - let wrapper: ShallowWrapper; - - beforeAll(() => { - ({ formContext, validation, wrapper } = setup()); - }); - - it('should register itself in the form context', () => { - expect(formContext.registerField).toHaveBeenCalledWith( - mockName, - { - label: mockLabel, - validate: expect.any(Function), - reset: expect.any(Function), - getValue: expect.any(Function), - updateValidation: validation.update, - - isGroup: true, - }, - ); - }); - - it('should unregister itself on unmount', () => { - wrapper.unmount(); - expect(formContext.unregisterField).toHaveBeenCalledWith(mockName); - }); - }); - - describe('Invalid form context', () => { - const mockErrorString = `Could not find a form context for field group "${mockName}". ` - + 'Fields can only be used inside a Form tag.'; - - it('should throw an error if there is no form context', () => { - expect(() => setup({ props: { context: undefined } })).toThrowError(mockErrorString); - }); - - it('should throw an error if the form context is invalid', () => { - // @ts-ignore The whole point of this test is to check the behaviour with an invalid type - expect(() => setup({ props: { context: { foo: 'bar' } } })).toThrowError(mockErrorString); - }); - }); - - describe('Form context callbacks', () => { - describe('Context.getValue', () => { - it('should always return an empty object', () => { - // Currently the form checks if the "Field" is a group, - // ignores its values and fetches them from all the child - // Fields of the group. - const { fieldState } = setup(); - expect(fieldState.getValue()).toEqual({}); - }); - }); - - describe('Context.validate', () => { - it('should correctly pass the validate method', () => { - const mockValidateArgs = { checkAsync: false }; - const { fieldState, validation } = setup(); - - void fieldState.validate(mockValidateArgs); - - expect(validation.validate).toHaveBeenLastCalledWith( - mockValue, - mockValidateArgs, - ); - }); - }); - - describe('Context.reset', () => { - it('should reset its validation state', () => { - const { fieldState, validation } = setup(); - - fieldState.reset(); - expect(validation.reset).toHaveBeenCalled(); - }); - }); - }); - describe('Context overrides', () => { it('should create a valid form context', () => { const { formContext, groupContext } = setup(); @@ -172,7 +86,8 @@ describe('', () => { fieldPrefix: mockName, registerField: formContext.registerField, unregisterField: formContext.unregisterField, - notifyFieldEvent: expect.any(Function), + notifyFieldEvent: formContext.notifyFieldEvent, + plaintext: formContext.plaintext, registerListener: formContext.registerListener, unregisterListener: formContext.unregisterListener, getFieldState: formContext.getFieldState, @@ -182,214 +97,10 @@ describe('', () => { asyncValidateOnChange: formContext.asyncValidateOnChange, asyncValidationWait: formContext.asyncValidationWait, defaultValues: formContext.defaultValues, - values: formContext.values, - }); - }); - - describe('Context.fieldPrefix behaviour', () => { - it('should override the fieldPrefix with the FieldGroup fullName', () => { - const { groupContext } = setup(); - expect(groupContext.fieldPrefix).toBe(mockName); - }); - }); - - describe('Context.notifyFieldEvent behaviour', () => { - const mockSenderLocal = 'field2'; - const mockSender = `${mockName}.${mockSenderLocal}`; - - const triggerNotification = (groupContext: IFormContext, eventName: string, eventArgs?: unknown): void => { - const notifyCallback = groupContext.notifyFieldEvent; - notifyCallback(mockSender, eventName, eventArgs); - }; - - const checkEventPassing = (eventName: string, eventArgs?: unknown): void => { - it('should pass the event to the parent form context', () => { - const { groupContext, formContext } = setup(); - triggerNotification(groupContext, eventName, eventArgs); - expect(formContext.notifyFieldEvent).toHaveBeenCalledWith( - mockSender, - eventName, - eventArgs, - ); - }); - }; - - const cases: [string, unknown][] = [ - ['test', { foo: 'bar' }], - ['random', null], - ]; - describe.each(cases)('Event "%s"', (eventName: string, eventArgs: unknown) => { - checkEventPassing(eventName, eventArgs); - }); - - describe('Event "change"', () => { - const mockChangedFieldValue = 'mock-value'; - const eventName = 'change'; - - const assertValidateCalled = ({ validate }: IValidationProp, checkAsync: boolean): void => { - expect(validate).toHaveBeenCalledWith( - { - ...mockValue, - [mockSenderLocal]: mockChangedFieldValue, - }, - { checkAsync }, - ); - }; - - checkEventPassing(eventName, { foo: 'bar' }); - - it('should call the validate function correctly', () => { - const { groupContext, validation } = setup(); - triggerNotification(groupContext, eventName, mockChangedFieldValue); - assertValidateCalled(validation, false); - }); - - it('should respect the Form.asyncValidateOnChange configuration', () => { - const mockCheckAsync = true; - const { groupContext, validation } = setup({ - contextOverrides: { - asyncValidateOnChange: mockCheckAsync, - }, - }); - triggerNotification(groupContext, eventName, mockChangedFieldValue); - assertValidateCalled(validation, mockCheckAsync); - }); - - it('should respect the FieldGroup.asyncValidateOnChange configuration', () => { - const mockCheckAsync = true; - const { groupContext, validation } = setup({ - props: { - asyncValidateOnChange: mockCheckAsync, - }, - }); - triggerNotification(groupContext, eventName, mockChangedFieldValue); - assertValidateCalled(validation, mockCheckAsync); - }); - - it('should use an empty object as the current state if the form does not provide one', () => { - const { groupContext, validation } = setup({ - contextOverrides: { - getValues: jest.fn().mockReturnValue({ - foo: 'bar', - }), - }, - }); - - triggerNotification(groupContext, eventName, mockChangedFieldValue); - expect(validation.validate).toHaveBeenCalledWith( - { - [mockSenderLocal]: mockChangedFieldValue, - }, - { checkAsync: false }, - ); - }); - }); - - describe('Event "blur"', () => { - const eventName = 'blur'; - checkEventPassing(eventName, { foo: 'bar' }); - - const cases = [ - ['Form.asyncValidateOnChange', 'props'], - ['FieldGroup.asyncValidateOnChange', 'contextOverrides'], - ]; - - describe.each(cases)('should respect the %s configuration', (name, config) => { - it('should not trigger a validation if validateOnChange is true', () => { - const mockCheckAsync = true; - const { groupContext, validation } = setup({ - [config]: { - asyncValidateOnChange: mockCheckAsync, - }, - }); - triggerNotification(groupContext, eventName); - expect(validation.validate).not.toHaveBeenCalled(); - }); - - it('should trigger a validation if validateOnChange is true', () => { - const mockCheckAsync = false; - const { groupContext, validation } = setup({ - [config]: { - asyncValidateOnChange: mockCheckAsync, - }, - }); - triggerNotification(groupContext, eventName); - expect(validation.validate).toHaveBeenCalledWith(mockValue); - }); - }); + stringFormatter: formContext.stringFormatter, + submit: formContext.submit, }); }); - - const propCases = [ - ['defaultValues'], - ['values'], - ]; - - describe.each(propCases)('Context.%s behaviour', (prop) => { - const formStates: [string, unknown][] = [ - ['null', null], - ['undefined', undefined], - ['empty', {}], - ['existing', { - mockField: '42', - [mockName]: { default: 'values' }, - }], - ]; - - it.each(formStates)(`should correctly override %s Context.${prop}`, (stateName, formState) => { - const mockGroupValue = { foo: 'bar' }; - - const { groupContext } = setup({ - props: { - [prop]: mockGroupValue, - }, - contextOverrides: { - [prop]: formState, - }, - }); - - // @ts-ignore any is OK here - expect(groupContext[prop]).toEqual({ - ...formState, - ...{ - [mockName]: mockGroupValue, - }, - }); - }); - }); - - const overrideCases = [ - ['plaintext'], - ['disabled'], - ]; - - describe.each(overrideCases)('Context.%s behaviour', (prop) => { - const cases: [boolean, undefined | boolean, boolean][] = [ - [false, undefined, false], - [true, undefined, true], - [false, false, false], - [false, false, true], - [true, true, true], - [true, true, false], - ]; - - it.each(cases)( - `${prop} should be %s if Field.${prop} is %s and FormContext.${prop} is %s`, - (expectedValue: boolean, propValue: boolean | undefined, contextValue: boolean) => { - const { groupContext } = setup({ - props: { - [prop]: propValue, - }, - contextOverrides: { - [prop]: contextValue, - }, - }); - - // @ts-ignore any is OK here - expect(groupContext[prop]).toEqual(expectedValue); - }, - ); - }); }); describe('render prop', () => { @@ -427,19 +138,4 @@ describe('', () => { }); }); }); - - describe('Edge cases', () => { - it('FieldGroup.getGroupValue should return undefined if context.getValues() doesn\'t have values for the group', () => { - const mockGetValues = jest.fn().mockReturnValue({}); - const { wrapper } = setup({ - contextOverrides: { - getValues: mockGetValues, - }, - }); - - // @ts-ignore getGroupValue is private, maybe there is a better solution to test this? - expect((wrapper.instance() as BaseFieldGroup).getGroupValue()).toBeUndefined(); - expect(mockGetValues).toHaveBeenCalled(); - }); - }); }); diff --git a/src/components/FieldGroup/FieldGroup.tsx b/src/components/FieldGroup/FieldGroup.tsx index 98876bf..358fe22 100644 --- a/src/components/FieldGroup/FieldGroup.tsx +++ b/src/components/FieldGroup/FieldGroup.tsx @@ -7,251 +7,20 @@ import React from 'react'; -import { FormContext, IFieldValues, IFormContext } from '../FormContext'; -import { TBasicFieldValue } from '../withField'; -import { IValidationArgs, IValidationState, withValidation } from '../withValidation'; +import { FormContext } from '../FormContext'; import { IFieldGroupProps } from './FieldGroup.types'; - -interface IFieldGroupState { - fieldPrefix: string; - notifyFieldEvent(name: string, event: string, args?: unknown): void; -} +import { useFieldGroup } from './hooks/useFieldGroup'; /** - * Wrapper for groups of input fields - * managed by the form component + * Defines a collection of fields. It combines its values to a + * sub-object and provides the possibility to attach validators to the group. */ -export class BaseFieldGroup extends React.Component, IFieldGroupState> { - public static displayName: string = 'FieldGroup'; - - constructor(props: IFieldGroupProps) { - super(props); - - const { - fullName, - label, - context, - validation: { - update: updateValidation, - }, - } = props; - - this.checkFormContext(); - - // Our state will overwrite the parts of the formContext - this.state = { - fieldPrefix: fullName, - notifyFieldEvent: this.notifyFieldEvent, - }; - - // Register the group in the formContext, so the group - // validation can be called on form submit. - context.registerField( - fullName, - { - label, - - updateValidation, - validate: this.validate, - reset: this.reset, - getValue: (): TBasicFieldValue => ({}), - - isGroup: true, - }, - ); - } - - /** - * Unregisters the field from the form - */ - public componentWillUnmount(): void { - const { context, fullName } = this.props; - context.unregisterField(fullName); - } - - /** - * Helper function to get the correct value - * of the group (including all values of the nested fields) - */ - private getGroupValue(): TFieldValues | undefined { - const { context, fullName } = this.props; - const formValues = context.getValues(); - - const formValue = formValues[fullName]; - if (formValue === '' || formValue === undefined) { - return undefined; - } - - return formValue as TFieldValues; - } - - /** - * Returns the correct asyncValidateOnChange setting, - * where the field setting takes priorty over the - * form setting - */ - private getAsyncValidateOnChangeSetting(): boolean { - const { - asyncValidateOnChange: propChange, - context: { asyncValidateOnChange: contextChange }, - } = this.props; - - return propChange === undefined ? contextChange : propChange; - } - - /** - * Generates the form context for the FieldGroup children - * Overrides fieldPrefix and notifyFieldEvent from the - * parent context, and overrides defaultValues and values - * for the group items if needed - */ - private getSubContext(): IFormContext { - const { context, disabled, plaintext } = this.props; - - return { - ...context, - ...this.state, - disabled: disabled === undefined ? context.disabled : disabled, - plaintext: plaintext === undefined ? context.plaintext : plaintext, - defaultValues: this.overrideContextValues('defaultValues'), - values: this.overrideContextValues('values'), - }; - } - - /** - * Checks if the FieldGroup is inside a valid form context - * and throws an user friendly error if not - */ - private checkFormContext(): void { - const { context, fullName } = this.props; - if (context === undefined || typeof context.registerField !== 'function') { - throw new Error( - `Could not find a form context for field group "${fullName}". ` - + 'Fields can only be used inside a Form tag.', - ); - } - } - - /** - * Checks if the FieldGroup has a prop with the given name - * and overrides the according value in the parent form context. - * @param name Property name - */ - private overrideContextValues(name: 'defaultValues' | 'values'): T { - let contextValue: Partial | undefined; - let propValue: Partial | undefined; - - const { fullName } = this.props; - - if (name === 'defaultValues') { - contextValue = this.props.context.defaultValues; - propValue = this.props.defaultValues; - } else if (name === 'values') { - contextValue = this.props.context.values; - propValue = this.props.values; - } - - if (propValue === undefined) { - return contextValue as T; - } - - const returnValue = { - ...contextValue, - ...{ - [fullName]: propValue, - }, - }; - - return returnValue as T; - } - - /** - * Resets the validation state - */ - private reset = (): void => { - const { validation: { reset } } = this.props; - reset(); - } - - /** - * Listens to child field events, triggers validation if - * needed and passes them to the higher context - * @param name Field name - * @param event Event name - * @param args Event args - */ - private notifyFieldEvent = (name: string, event: string, args?: unknown): void => { - const { fullName, context, validation: { validate } } = this.props; - context.notifyFieldEvent(name, event, args); - - if (event !== 'change' && event !== 'blur') { return; } - - const asyncValidateOnChange = this.getAsyncValidateOnChangeSetting(); - if (event === 'change') { - const localName = name.substring(fullName.length + 1); - - const currentGroupValue = this.getGroupValue(); - const intermediateGroupValue = { - ...(currentGroupValue === undefined ? { } : currentGroupValue), - ...{ - // Override the value of the event sender, because - // the Field didn't update its state yet, making the - // Form.getValues() returning an old Field value. - [localName]: args, - }, - }; - - void validate( - intermediateGroupValue as TFieldValues, - { checkAsync: asyncValidateOnChange }, - ); - } else if (!asyncValidateOnChange) { - void validate(this.getGroupValue()); - } - } - - /** - * Triggers the validation of the group - * @param args Options for the validate call - */ - private validate = async (args?: Partial): Promise => { - const { validation: { validate } } = this.props; - - // Overwrite the value of the group state with - // the parsed one. - const value = this.getGroupValue(); - - return validate(value, args); - } - - public render(): JSX.Element { - const { - fullName, - validation: { - isValidating, - isRequired, - valid, - error, - }, - render: renderProp, - } = this.props; - - const groupState = { - fullName, - isValidating, - isRequired, - valid, - error, - }; - - const subContext = this.getSubContext(); - - return ( - - {renderProp(groupState)} - - ); - } -} - -export const FieldGroup = withValidation(BaseFieldGroup); +export const FieldGroup: React.FC = ({ render, ...props }) => { + const { groupFormContext, renderParams } = useFieldGroup(props); + + return ( + + {render(renderParams)} + + ); +}; diff --git a/src/components/FieldGroup/FieldGroup.types.ts b/src/components/FieldGroup/FieldGroup.types.ts index 58a8781..f44b067 100644 --- a/src/components/FieldGroup/FieldGroup.types.ts +++ b/src/components/FieldGroup/FieldGroup.types.ts @@ -4,56 +4,17 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { IFieldValues } from '../FormContext'; -import { IValidationProps, IValidationState } from '../withValidation'; - -/** - * Meta information about the group - * for the render prop - */ -export interface IFieldGroupRenderParams extends IValidationState { - /** - * Full name of the group - */ - fullName: string; -} +import React from 'react'; +import { IUseFieldGroupArgs, IFieldGroupRenderParams } from './hooks/useFieldGroup.types'; /** * Props for the field group component */ -export interface IFieldGroupProps extends IValidationProps { - /** - * Field name - */ - name: string; - /** - * Label of the group - */ - label: string; - /** - * True, if the async validators should be triggered - * on a change event - */ - asyncValidateOnChange?: boolean; - /** - * Optional default values - */ - defaultValues?: TFieldValues; - /** - * Optional values - */ - values?: TFieldValues; - /** - * Disables this field group and all its fields. - */ - disabled?: boolean; - /** - * Puts the field group and all its fields in plaintext mode. - */ - plaintext?: boolean; +export interface IFieldGroupProps extends IUseFieldGroupArgs { /** - * Render prop - * @param params Meta information about the group + * Gets called to render its children (see render prop pattern). + * @param params Contains the group state consisting of fullName, + * isValidating,valid, error which can be used to display those informations. */ - render(params: IFieldGroupRenderParams): JSX.Element; + render(params: IFieldGroupRenderParams): React.ReactNode; } diff --git a/src/components/FieldGroup/__snapshots__/FieldGroup.test.tsx.snap b/src/components/FieldGroup/__snapshots__/FieldGroup.test.tsx.snap index 9bc176d..715768d 100644 --- a/src/components/FieldGroup/__snapshots__/FieldGroup.test.tsx.snap +++ b/src/components/FieldGroup/__snapshots__/FieldGroup.test.tsx.snap @@ -12,35 +12,15 @@ exports[` Render should render without crashing 1`] = ` "fieldPrefix": "unitGroup", "getFieldState": [MockFunction], "getValues": [MockFunction], - "notifyFieldEvent": [Function], + "notifyFieldEvent": [MockFunction], "plaintext": false, - "registerField": [MockFunction] { - "calls": Array [ - Array [ - "unitGroup", - Object { - "getValue": [Function], - "isGroup": true, - "label": "Unit group", - "reset": [Function], - "updateValidation": [MockFunction], - "validate": [Function], - }, - ], - ], - "results": Array [ - Object { - "type": "return", - "value": undefined, - }, - ], - }, + "registerField": [MockFunction], "registerListener": [MockFunction], + "reset": [MockFunction], "stringFormatter": [MockFunction], "submit": [MockFunction], "unregisterField": [MockFunction], "unregisterListener": [MockFunction], - "values": undefined, } } /> diff --git a/src/components/FieldGroup/hooks/useFieldGroup.test.ts b/src/components/FieldGroup/hooks/useFieldGroup.test.ts new file mode 100644 index 0000000..987cbb1 --- /dev/null +++ b/src/components/FieldGroup/hooks/useFieldGroup.test.ts @@ -0,0 +1,415 @@ +import { renderHook } from 'react-hooks-testing-library'; + +import { createMockFormContext, createMockValidationResult } from '../../../test-utils/enzymeFormContext'; +import { useFormContext, useValidation, IUseValidationResult } from '../../../hooks'; +import { useFullName, useFieldRegistration, IFieldState } from '../../../hooks/internal'; +import { IFormContext } from '../../FormContext'; + +import { useFieldGroup } from './useFieldGroup'; +import { IUseFieldGroupArgs, IUseFieldGroupResult } from './useFieldGroup.types'; + +jest.mock('../../../hooks'); +jest.mock('../../../hooks/internal'); + +describe('useFieldGroup', () => { + const mockName = 'unitGroup'; + const mockLabel = 'Unit group'; + const mockValue = { + field1: 'value1', + field2: 'value2', + }; + + interface ISetupArgs { + props?: Partial; + contextOverrides?: Partial; + validationOverrides?: Partial; + } + + interface ISetupResult { + formContext: IFormContext; + validation: IUseValidationResult; + + fieldState: IFieldState; + + result: { current: IUseFieldGroupResult }; + unmount(): boolean; + } + + const setup = ({ + props = { }, + contextOverrides, + validationOverrides, + }: ISetupArgs = {}): ISetupResult => { + let fieldState = null; + + (useFullName as jest.Mock).mockImplementation((name: string) => name) + const formContext = { + ...createMockFormContext(), + values: undefined, + getValues: jest.fn().mockReturnValue({ + foo: 'bar', + hue: 'hue', + [mockName]: mockValue, + }), + ...contextOverrides, + }; + (useFormContext as jest.Mock).mockReturnValue(formContext); + + const validation = { + ...createMockValidationResult(), + ...validationOverrides, + }; + (useValidation as jest.Mock).mockReturnValue(validation); + + (useFieldRegistration as jest.Mock).mockImplementation((fullName, state) => { + fieldState = state; + }); + + const fieldGroupProps = { + name: mockName, + label: mockLabel, + ...props, + }; + + const { result, unmount } = renderHook(() => useFieldGroup(fieldGroupProps)); + + return { + formContext, + validation, + + // @ts-ignore + fieldState, + + result, + unmount, + }; + }; + + describe('Form registration', () => { + it('should register itself using useFieldRegistration', () => { + const { fieldState } = setup(); + expect((useFieldRegistration as jest.Mock)).toHaveBeenCalledWith( + mockName, + fieldState, + ); + }); + }); + + describe('Form context callbacks', () => { + describe('Context.getValue', () => { + it('should always return an empty object', () => { + // Currently the form checks if the "Field" is a group, + // ignores its values and fetches them from all the child + // Fields of the group. + const { fieldState } = setup(); + expect(fieldState.getValue()).toEqual({}); + }); + }); + + describe('Context.validate', () => { + it('should correctly pass the validate method', () => { + const mockValidateArgs = { checkAsync: false }; + const { fieldState, validation } = setup(); + + void fieldState.validate(mockValidateArgs); + + expect(validation.validate).toHaveBeenLastCalledWith( + mockValue, + mockValidateArgs, + ); + }); + }); + + describe('Context.reset', () => { + it('should reset its validation state', () => { + const { fieldState, validation } = setup(); + + fieldState.reset(); + expect(validation.resetValidation).toHaveBeenCalled(); + }); + }); + }); + + describe('Context overrides', () => { + it('should create a valid form context', () => { + const { formContext, result } = setup(); + expect(result.current.groupFormContext).toMatchObject({ + fieldPrefix: mockName, + registerField: formContext.registerField, + unregisterField: formContext.unregisterField, + notifyFieldEvent: expect.any(Function), + registerListener: formContext.registerListener, + unregisterListener: formContext.unregisterListener, + getFieldState: formContext.getFieldState, + getValues: formContext.getValues, + busy: formContext.busy, + disabled: formContext.disabled, + asyncValidateOnChange: formContext.asyncValidateOnChange, + asyncValidationWait: formContext.asyncValidationWait, + defaultValues: formContext.defaultValues, + values: formContext.values, + plaintext: formContext.plaintext, + stringFormatter: formContext.stringFormatter, + submit: formContext.submit, + }); + }); + + describe('Context.fieldPrefix behaviour', () => { + it('should override the fieldPrefix with the FieldGroup fullName', () => { + const { result } = setup(); + expect(result.current.groupFormContext.fieldPrefix).toBe(mockName); + }); + }); + + describe('Context.notifyFieldEvent behaviour', () => { + const mockSenderLocal = 'field2'; + const mockSender = `${mockName}.${mockSenderLocal}`; + + const triggerNotification = (groupContext: IFormContext, eventName: string, eventArgs?: unknown): void => { + const notifyCallback = groupContext.notifyFieldEvent; + notifyCallback(mockSender, eventName, eventArgs); + }; + + const checkEventPassing = (eventName: string, eventArgs?: unknown): void => { + it('should pass the event to the parent form context', () => { + const { result, formContext } = setup(); + triggerNotification(result.current.groupFormContext, eventName, eventArgs); + expect(formContext.notifyFieldEvent).toHaveBeenCalledWith( + mockSender, + eventName, + eventArgs, + ); + }); + }; + + const cases: [string, unknown][] = [ + ['test', { foo: 'bar' }], + ['random', null], + ]; + describe.each(cases)('Event "%s"', (eventName: string, eventArgs: unknown) => { + checkEventPassing(eventName, eventArgs); + }); + + describe('Event "change"', () => { + const mockChangedFieldValue = 'mock-value'; + const eventName = 'change'; + + const assertValidateCalled = ({ validate }: IUseValidationResult, checkAsync: boolean): void => { + expect(validate).toHaveBeenCalledWith( + { + ...mockValue, + [mockSenderLocal]: mockChangedFieldValue, + }, + { checkAsync }, + ); + }; + + checkEventPassing(eventName, { foo: 'bar' }); + + it('should call the validate function correctly', () => { + const { result, validation } = setup(); + triggerNotification(result.current.groupFormContext, eventName, mockChangedFieldValue); + assertValidateCalled(validation, false); + }); + + it('should respect the Form.asyncValidateOnChange configuration', () => { + const mockCheckAsync = true; + const { result, validation } = setup({ + contextOverrides: { + asyncValidateOnChange: mockCheckAsync, + }, + }); + triggerNotification(result.current.groupFormContext, eventName, mockChangedFieldValue); + assertValidateCalled(validation, mockCheckAsync); + }); + + it('should respect the FieldGroup.asyncValidateOnChange configuration', () => { + const mockCheckAsync = true; + const { result, validation } = setup({ + props: { + asyncValidateOnChange: mockCheckAsync, + }, + }); + triggerNotification(result.current.groupFormContext, eventName, mockChangedFieldValue); + assertValidateCalled(validation, mockCheckAsync); + }); + + it('should use an empty object as the current state if the form does not provide one', () => { + const { result, validation } = setup({ + contextOverrides: { + getValues: jest.fn().mockReturnValue({ + foo: 'bar', + }), + }, + }); + + triggerNotification(result.current.groupFormContext, eventName, mockChangedFieldValue); + expect(validation.validate).toHaveBeenCalledWith( + { + [mockSenderLocal]: mockChangedFieldValue, + }, + { checkAsync: false }, + ); + }); + }); + + describe('Event "blur"', () => { + const eventName = 'blur'; + checkEventPassing(eventName, { foo: 'bar' }); + + const cases = [ + ['Form.asyncValidateOnChange', 'props'], + ['FieldGroup.asyncValidateOnChange', 'contextOverrides'], + ]; + + describe.each(cases)('should respect the %s configuration', (name, config) => { + it('should not trigger a validation if validateOnChange is true', () => { + const mockCheckAsync = true; + const { result, validation } = setup({ + [config]: { + asyncValidateOnChange: mockCheckAsync, + }, + }); + triggerNotification(result.current.groupFormContext, eventName); + expect(validation.validate).not.toHaveBeenCalled(); + }); + + it('should trigger a validation if validateOnChange is true', () => { + const mockCheckAsync = false; + const { result, validation } = setup({ + [config]: { + asyncValidateOnChange: mockCheckAsync, + }, + }); + triggerNotification(result.current.groupFormContext, eventName); + expect(validation.validate).toHaveBeenCalledWith(mockValue); + }); + }); + }); + }); + + const propCases = [ + ['defaultValues'], + ['values'], + ]; + + describe.each(propCases)('Context.%s behaviour', (prop) => { + const formStates: [string, unknown][] = [ + ['null', null], + ['undefined', undefined], + ['empty', {}], + ['existing', { + mockField: '42', + [mockName]: { default: 'values' }, + }], + ]; + + it.each(formStates)(`should correctly override %s Context.${prop}`, (stateName, formState) => { + const mockGroupValue = { foo: 'bar' }; + + const { result } = setup({ + props: { + [prop]: mockGroupValue, + }, + contextOverrides: { + [prop]: formState, + }, + }); + + // @ts-ignore any is OK here + expect(result.current.groupFormContext[prop]).toEqual({ + ...formState, + ...{ + [mockName]: mockGroupValue, + }, + }); + }); + }); + + const overrideCases = [ + ['plaintext'], + ['disabled'], + ]; + + describe.each(overrideCases)('Context.%s behaviour', (prop) => { + const cases: [boolean, undefined | boolean, boolean][] = [ + [false, undefined, false], + [true, undefined, true], + [false, false, false], + [false, false, true], + [true, true, true], + [true, true, false], + ]; + + it.each(cases)( + `${prop} should be %s if Field.${prop} is %s and FormContext.${prop} is %s`, + (expectedValue: boolean, propValue: boolean | undefined, contextValue: boolean) => { + const { result } = setup({ + props: { + [prop]: propValue, + }, + contextOverrides: { + [prop]: contextValue, + }, + }); + + // @ts-ignore any is OK here + expect(result.current.groupFormContext[prop]).toEqual(expectedValue); + }, + ); + }); + }); + + describe('render params', () => { + it('should create the correct parameters', () => { + const { result } = setup(); + expect(result.current.renderParams).toMatchObject({ + fullName: mockName, + isValidating: false, + isRequired: false, + valid: true, + error: null, + }); + }); + + it('should correctly pass the validation state', () => { + const mockIsValidating = true; + const mockIsRequired = true; + const mockIsValid = false; + const mockError = { message_id: 'bar', params: {} }; + + const { result } = setup({ + validationOverrides: { + validationState: { + isValidating: mockIsValidating, + isRequired: mockIsRequired, + valid: mockIsValid, + error: mockError, + } + }, + }); + expect(result.current.renderParams).toMatchObject({ + fullName: mockName, + isValidating: mockIsValidating, + isRequired: mockIsRequired, + valid: mockIsValid, + error: mockError, + }); + }); + }); + + // describe('Edge cases', () => { + // it('FieldGroup.getGroupValue should return undefined if context.getValues() doesn\'t have values for the group', () => { + // const mockGetValues = jest.fn().mockReturnValue({}); + // const { wrapper } = setup({ + // contextOverrides: { + // getValues: mockGetValues, + // }, + // }); + + // // @ts-ignore getGroupValue is private, maybe there is a better solution to test this? + // expect((wrapper.instance() as BaseFieldGroup).getGroupValue()).toBeUndefined(); + // expect(mockGetValues).toHaveBeenCalled(); + // }); + // }); +}); diff --git a/src/components/FieldGroup/hooks/useFieldGroup.ts b/src/components/FieldGroup/hooks/useFieldGroup.ts new file mode 100644 index 0000000..f0a8ae3 --- /dev/null +++ b/src/components/FieldGroup/hooks/useFieldGroup.ts @@ -0,0 +1,118 @@ +import { useCallback, useMemo } from 'react'; + +import { useFormContext, useValidation, IValidationArgs, IBasicValidationState } from '../../../hooks'; +import { useFullName, useFieldRegistration } from '../../../hooks/internal'; +import { IFieldValues, IFormContext } from '../../FormContext'; + +import { getGroupValue } from './useFieldGroup.utils'; +import { IUseFieldGroupArgs, IUseFieldGroupResult } from './useFieldGroup.types'; + +export function useFieldGroup(props: IUseFieldGroupArgs): IUseFieldGroupResult { + const formContext = useFormContext(); + + const { + name, + label, + defaultValues, + values, + disabled = formContext.disabled, + plaintext = formContext.plaintext, + asyncValidateOnChange = formContext.asyncValidateOnChange, + } = props; + + const fullName = useFullName(name); + const { validationState, validate, resetValidation, updateValidationState } = useValidation(props); + + /** + * Triggers the validation of the group + */ + const validateGroup = useCallback( + (args?: Partial): Promise => { + const groupValue = getGroupValue(formContext, fullName); + return validate(groupValue, args); + }, + [formContext, fullName, validate], + ); + + // Register the group in the formContext, so the group + // validation can be called on form submit. + const registerFieldState = useMemo(() => ({ + label, + isGroup: true, + updateValidation: updateValidationState, + validate: validateGroup, + reset: resetValidation, + getValue: () => ({}), + }), [label, resetValidation, updateValidationState, validateGroup]); + useFieldRegistration( + fullName, + registerFieldState, + ); + + /** + * Listens to child field events, triggers validation if + * needed and passes them to the higher context + * @param name Field name + * @param event Event name + * @param args Event args + */ + const notifyFieldEvent = useCallback( + (name: string, event: string, args?: unknown): void => { + formContext.notifyFieldEvent(name, event, args); + + if (event !== 'change' && event !== 'blur') { return; } + + if (event === 'change') { + const localName = name.substring(fullName.length + 1); + + const currentGroupValue = getGroupValue(formContext, fullName); + const intermediateGroupValue = { + ...(currentGroupValue === undefined ? { } : currentGroupValue), + ...{ + // Override the value of the event sender, because + // the Field didn't update its state yet, making the + // Form.getValues() returning an old Field value. + [localName]: args, + }, + }; + + void validate( + intermediateGroupValue as IFieldValues, + { checkAsync: asyncValidateOnChange }, + ); + } else if (!asyncValidateOnChange) { + void validate(getGroupValue(formContext, fullName)); + } + }, + [asyncValidateOnChange, formContext, fullName, validate], + ); + + const subContext: IFormContext = useMemo(() => ({ + ...formContext, + fieldPrefix: fullName, + notifyFieldEvent, + disabled, + plaintext, + defaultValues: defaultValues === undefined ? formContext.defaultValues : { ...formContext.defaultValues, ... { [fullName]: defaultValues }}, + values: values === undefined ? formContext.values : { ...formContext.values, ... { [fullName]: values }}, + }), [defaultValues, disabled, formContext, fullName, notifyFieldEvent, plaintext, values]); + + const groupState = useMemo( + () => ({ + fullName, + isValidating: validationState.isValidating, + isRequired: validationState.isRequired, + valid: validationState.valid, + error: validationState.error, + }), + [ + fullName, + validationState, + ], + ); + + return { + groupFormContext: subContext, + renderParams: groupState, + }; +} diff --git a/src/components/FieldGroup/hooks/useFieldGroup.types.ts b/src/components/FieldGroup/hooks/useFieldGroup.types.ts new file mode 100644 index 0000000..22acc37 --- /dev/null +++ b/src/components/FieldGroup/hooks/useFieldGroup.types.ts @@ -0,0 +1,67 @@ +import { IUseValidationArgs, IValidationState } from '../../../hooks'; +import { IFieldValues, IFormContext } from '../../FormContext'; + +/** + * Props for the field group component + */ +export interface IUseFieldGroupArgs extends IUseValidationArgs { + /** + * Message id of the label that will be displayed along the input. + * If you don't want to use any i18n features you can pass a raw message instead. + */ + label: string; + /** + * If set to true the form will trigger asynchronous validation on Fields whenever + * they change (e.g. on key press). Default behaviour is that the fields will only + * async validate when they loose focus. + * @default Form.asyncValidateOnChange + */ + asyncValidateOnChange?: boolean; + /** + * Overwrites the Form default values for the child fields of this field group. + * Those values will be put into the according fields when the form initializes. + */ + defaultValues?: IFieldValues; + /** + * Overwrites the Form values for the child fields of this field group. Changing + * this property will update all Field values, overwriting their default values + * but also any value the user put in. + */ + values?: IFieldValues; + /** + * Overwrites the disabled state for this field group. + * @default Form.disabled + */ + disabled?: boolean; + /** + * Overwrites the plaintext state for this field group. + * @default Form.plaintext + */ + plaintext?: boolean; +} + +/** + * Meta information about the group + * for the render prop + */ +export interface IFieldGroupRenderParams extends IValidationState { + /** + * Full name of the group + */ + fullName: string; +} + +/** + * Result of the useFieldGroup hook + */ +export interface IUseFieldGroupResult { + /** + * Overriden form context for the group + */ + groupFormContext: IFormContext; + /** + * Params with meta information to be passed + * to the render method of FieldGroup + */ + renderParams: IFieldGroupRenderParams; +} diff --git a/src/components/FieldGroup/hooks/useFieldGroup.utils.ts b/src/components/FieldGroup/hooks/useFieldGroup.utils.ts new file mode 100644 index 0000000..639e23e --- /dev/null +++ b/src/components/FieldGroup/hooks/useFieldGroup.utils.ts @@ -0,0 +1,18 @@ +import { IFormContext, IFieldValues } from '../../FormContext'; + +/** + * Helper function to get the correct value + * of the group (including all values of the nested fields) + * @param formContext Form context + * @param fullName Full name of the field group + */ +export function getGroupValue(formContext: IFormContext, fullName: string): IFieldValues | undefined { + const formValues = formContext.getValues(); + + const formValue = formValues[fullName]; + if (formValue === '' || formValue === undefined) { + return undefined; + } + + return formValue as IFieldValues; +} diff --git a/src/components/FieldGroup/index.ts b/src/components/FieldGroup/index.ts index 82c117a..4e2eb0b 100644 --- a/src/components/FieldGroup/index.ts +++ b/src/components/FieldGroup/index.ts @@ -1,2 +1,3 @@ export * from './FieldGroup'; export * from './FieldGroup.types'; +export { IFieldGroupRenderParams} from './hooks/useFieldGroup.types'; diff --git a/src/components/FieldLine/FieldLine.tsx b/src/components/FieldLine/FieldLine.tsx index ddf3902..7c8f95d 100644 --- a/src/components/FieldLine/FieldLine.tsx +++ b/src/components/FieldLine/FieldLine.tsx @@ -15,7 +15,7 @@ import { IFieldLineProps } from './FieldLine.types'; * Create a required * * @param validators Validator array */ -function createRequiredMarker(isRequired: boolean): JSX.Element | null { +function createRequiredMarker(isRequired: boolean): React.ReactNode { if (isRequired) { return *; } @@ -27,7 +27,7 @@ function createRequiredMarker(isRequired: boolean): JSX.Element | null { * Component for displaying bootstrap * form groups with any children */ -export const FieldLine: React.SFC = (props: IFieldLineProps): JSX.Element => { +export const FieldLine: React.FC = (props) => { const { field, meta, @@ -49,7 +49,6 @@ export const FieldLine: React.SFC = (props: IFieldLineProps): J id={`${field.id}_errors`} invalid={!meta.valid} error={meta.error} - stringFormatter={meta.stringFormatter} /> diff --git a/src/components/FieldLine/FieldLine.types.ts b/src/components/FieldLine/FieldLine.types.ts index 1cab1bf..ca49390 100644 --- a/src/components/FieldLine/FieldLine.types.ts +++ b/src/components/FieldLine/FieldLine.types.ts @@ -4,6 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ +import React from 'react'; import { IFieldComponentProps } from '../withField'; /** diff --git a/src/components/FieldLine/__snapshots__/FieldLine.test.tsx.snap b/src/components/FieldLine/__snapshots__/FieldLine.test.tsx.snap index 4c2608d..19b22d8 100644 --- a/src/components/FieldLine/__snapshots__/FieldLine.test.tsx.snap +++ b/src/components/FieldLine/__snapshots__/FieldLine.test.tsx.snap @@ -22,7 +22,6 @@ exports[` should display invalid correctly 1`] = ` error={null} id="unitInput_errors" invalid={true} - stringFormatter={[MockFunction]} /> @@ -50,7 +49,6 @@ exports[` should render without crashing 1`] = ` error={null} id="unitInput_errors" invalid={false} - stringFormatter={[MockFunction]} /> @@ -83,7 +81,6 @@ exports[` should show a required marker 1`] = ` error={null} id="unitInput_errors" invalid={true} - stringFormatter={[MockFunction]} /> diff --git a/src/components/Form/Form.md b/src/components/Form/Form.md new file mode 100644 index 0000000..dd997dc --- /dev/null +++ b/src/components/Form/Form.md @@ -0,0 +1,231 @@ +### Examples + +#### Simple form +Showcase of a simple form + +```jsx +import { Form, Input } from 'react-ocean-forms'; + +function Example() { + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + // Reset callback, can be useful in some cases for cleanup + const handleReset = () => { + console.log('onReset'); + }; + + return ( +
+ + + +
+ ); +} + + +``` + +#### Default values +Provide the form with default values that should be displayed on load. Note how the default value won't update the Field, if the user changed the input. However, on form reset the default value will be used again. + +```jsx +import React, { useState } from 'react'; +import { Form, Input } from 'react-ocean-forms'; + +function Example() { + const [ defaultValues, setDefaultValues ] = useState({ myInput: 'default value' }); + + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + const randomizeDefaultValues = () => { + setDefaultValues({ + myInput: `default ${(Math.floor(Math.random() * 100))}`, + }); + } + + return ( +
+ + +

+ Current default value: {defaultValues.myInput} +

+ + + + +
+ ); +} + + +``` + +#### Values +Override the values of the form fields. Changing those values will override the Field value, even if the user changed it. + +```jsx +import React, { useState } from 'react'; +import { Form, Input } from 'react-ocean-forms'; + +function Example() { + const [ values, setValues ] = useState({ myInput: 'demo value' }); + + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + const randomizeValues = () => { + setValues({ + myInput: `demo ${(Math.floor(Math.random() * 100))}`, + }); + } + + return ( +
+ + + + + +
+ ); +} + + +``` + +#### Disabled form +Setting the disabled prop will disable all form fields. + +```jsx +import { Form, Input } from 'react-ocean-forms'; + +function Example() { + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + return ( +
+ + + +
+ ); +} + + +``` + +#### Plaintext form +Setting the plaintext prop will show all form fields in a text-only mode. + +```jsx +import { Form, Input } from 'react-ocean-forms'; + +function Example({ logMessage }) { + const defaultValues = { + myInput: 'default value 1', + myInput2: 'default value 2', + }; + + return ( +
+ + +
+ ); +} + + +``` + +#### Form-wide validation +Use a form-wide validation function before submit. Notice that the onSubmit callback is not invoked if you type 'bad' into the input. + +```jsx +import { Form, Input, ValidationSummary } from 'react-ocean-forms'; + +function Example() { + // Submit callback, here you'd make your api calls + const handleSubmit = (values) => { + console.log('onSubmit, values: ' + JSON.stringify(values)); + }; + + // Reset callback, can be useful in some cases for cleanup + const handleReset = () => { + console.log('onReset'); + }; + + // Form wide validation function + const handleValidate = (values) => { + if (values.demoInput === 'bad') { + return { + demoInput: 'Invalid input!', + }; + } + + return null; + }; + + return ( +
+ + + + + + + + ); +} + + +``` diff --git a/src/components/Form/Form.test.tsx b/src/components/Form/Form.test.tsx index 82d9aca..a10bad5 100644 --- a/src/components/Form/Form.test.tsx +++ b/src/components/Form/Form.test.tsx @@ -1,14 +1,18 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { mockEvent } from '../../test-utils/enzymeEventUtils'; -import { IFieldState, IFieldValues, IFormContext, TFormEventListener } from '../FormContext'; +import { IFormContext } from '../FormContext'; import { Form } from './Form'; import { IFormProps } from './Form.types'; +import { createMockFormContext } from '../../test-utils/enzymeFormContext'; +import { useForm } from './hooks/useForm'; + +jest.mock('./hooks/useForm'); describe('
', () => { interface ISetupArgs { props?: Partial; + formContext?: Partial; } interface ISetupResult { @@ -17,9 +21,14 @@ describe('', () => { form: ShallowWrapper; } - const setup = ({ - props, - }: Partial = {}): ISetupResult => { + const setup = ({ props, formContext }: Partial = {}): ISetupResult => { + const overriddenContext = { + ...createMockFormContext(), + ...formContext, + }; + + (useForm as jest.Mock).mockReturnValue(overriddenContext); + const wrapper = shallow(( ', () => { )); - const formContext = wrapper.first().prop('value') as IFormContext; const form = wrapper.find('form'); return { wrapper, - formContext, + formContext: overriddenContext, form, }; }; - const createMockFieldState = (label: string, isGroup?: boolean): IFieldState => ({ - label, - isGroup, - validate: jest.fn().mockResolvedValue({ - isValidating: false, - valid: true, - error: null, - }), - updateValidation: jest.fn(), - reset: jest.fn(), - getValue: jest.fn().mockReturnValue(label), - }); - - interface IMockField { - name: string; - state: IFieldState; - } - - const createMockField = (name: string, label: string, isGroup?: boolean): IMockField => ({ - name, - state: createMockFieldState(label, isGroup), - }); - - const registerUnitField = (fields: IMockField[], formContext: IFormContext): void => { - fields.forEach(field => { - formContext.registerField(field.name, field.state); - }); - }; - - interface IMockListener { - id: string; - state: TFormEventListener; - } - - const createMockListener = (id?: string): IMockListener => ({ - id: id === undefined ? 'listener' : id, - state: jest.fn(), - }); - - const createMockListeners = (count: number): IMockListener[] => { - const result = []; - for (let i = 0; i < count; i += 1) { - result.push(createMockListener(`listener${i}`)); - } - - return result; - }; - it('should render without error', () => { const { wrapper } = setup(); expect(wrapper).toMatchSnapshot(); }); - it('should create a valid form context', () => { - const { formContext } = setup(); - expect(formContext).toMatchObject({ - fieldPrefix: null, - registerField: expect.any(Function), - unregisterField: expect.any(Function), - notifyFieldEvent: expect.any(Function), - registerListener: expect.any(Function), - unregisterListener: expect.any(Function), - getFieldState: expect.any(Function), - getValues: expect.any(Function), - busy: false, - disabled: false, - asyncValidateOnChange: false, - asyncValidationWait: 400, - defaultValues: {}, - values: undefined, - }); + it('should pass the props to useForm', () => { + const props: Partial = { disabled: true, busy: true, plaintext: false, asyncValidateOnChange: false }; + setup({ props }); + + expect(useForm).toHaveBeenCalledWith(props); }); describe('css classes', () => { @@ -124,553 +71,62 @@ describe('
', () => { describe('plaintext', () => { it('should have the plaintext css class if in plaintext mode', () => { - const { wrapper } = setup({ props: { plaintext: true }}); + const { wrapper } = setup({ formContext: { plaintext: true }}); hasClass(wrapper, 'plaintext'); }); it('should still add the additional classNames', () => { const mockClass = 'mock-class'; - const { wrapper } = setup({ props: { plaintext: true, className: mockClass }}); + const { wrapper } = setup({ props: { className: mockClass }, formContext: { plaintext: true }}); hasClass(wrapper, 'plaintext'); hasClass(wrapper, mockClass); }); }); }); - describe('configuration', () => { - const cases: any[] = [ - ['disabled', true, 'disabled'], - ['formatString', jest.fn(), 'stringFormatter'], - ['asyncValidateOnChange', true, 'asyncValidateOnChange'], - ['asyncValidationWait', 800, 'asyncValidationWait'], - ['defaultValues', { foo: 'bar' }, 'defaultValues'], - ['values', { foo: 'bar' }, 'values'], - ['plaintext', true, 'plaintext'], - ]; - - test.each(cases)('case %s', (prop, value, contextProp) => { - const { formContext } = setup({ - props: { [prop]: value }, - }); - expect(formContext).toMatchObject({ [contextProp]: value }); - }); - }); - - describe('invalid field registration', () => { - const mf = (): void => {}; - const cases: any[] = [ - ['no parameters', undefined, undefined], - ['invalid field name', '', undefined], - ['no state', 'foo', undefined], - ['empty state', 'foo', {}], - ['1 of 5 props', 'foo', { label: 'hey' }], - ['2 of 5 props', 'foo', { label: 'hey', validate: mf }], - ['3 of 5 props', 'foo', { label: 'hey', validate: mf, updateValidation: mf }], - ['4 of 5 props', 'foo', { - label: 'hey', - validate: mf, - updateValidation: mf, - reset: mf, - }], - ]; - - test.each(cases)('case %s', (testName: string, fieldName: string, fieldState: IFieldState) => { - const { formContext } = setup(); - expect(() => { - formContext.registerField(fieldName, fieldState); - }).toThrowErrorMatchingSnapshot(); - }); - }); - - describe('field states and values', () => { - const createCases = (): [string, IMockField, boolean?][] => { - return [ - ['field', createMockField('unitField', 'Unit field')], - ['group', createMockField('unitGroup', 'Unit group', true)], - ['sub field', createMockField('unitGroup.subField', 'Sub field', true)], - ]; - }; - - describe('formContext.registerField - field registration', () => { - const cases = createCases(); - - test.each(cases)('should register a new %s without crashing', (testName, field: IMockField) => { - const { formContext } = setup(); - expect(() => { - registerUnitField([field], formContext); - }).not.toThrowError(); - }); - }); - - describe('formContext.unregisterField - field cleanup', () => { - const cases = createCases(); - - test.each(cases)('should unregister %s without crashing', (testName, field: IMockField) => { - const { formContext } = setup(); - formContext.registerField(field.name, field.state); - formContext.unregisterField(field.name); - }); - }); - - describe('formContext.getFieldState - field states', () => { - const cases = createCases(); - - test.each(cases)('should return the correct field state of a %s', (testName, field: IMockField) => { - const { formContext } = setup(); - registerUnitField([field], formContext); - expect(formContext.getFieldState(field.name)).toBe(field.state); - }); - - it('should throw an error when trying to access an non-existing field state', () => { - const { formContext } = setup(); - const mockFieldName = 'mock-test'; - - expect( - () => formContext.getFieldState(mockFieldName), - ).toThrowError(`[Form] getFieldState: Could not find state of field '${mockFieldName}'`); - }); - }); - - describe('formContext.getValues - form values', () => { - const { formContext } = setup(); - - const unitField = createMockField('unitField', 'Unit field'); - const unitGroup = createMockField('unitGroup', 'Unit group', true); - const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); - const unitSubField2 = createMockField(`${unitGroup.name}.subField2`, 'Sub field 2'); - - const mockFields = [unitField, unitGroup, unitSubField, unitSubField2]; - registerUnitField(mockFields, formContext); - - let formValues: IFieldValues; - it('should return the values without crashing', () => { - expect(() => { - formValues = formContext.getValues(); - }).not.toThrowError(); - }); - - it('should call unitField.getValue', () => { - expect(unitField.state.getValue).toHaveBeenCalled(); - }); - - it('should call subField.getValue', () => { - expect(unitSubField.state.getValue).toHaveBeenCalled(); - }); - - it('should not call group.getValue', () => { - // The value of fieldGroups is computed by its children - expect(unitGroup.state.getValue).not.toHaveBeenCalled(); - }); - - it('should return the correct form values', () => { - const subFieldLocalName = unitSubField.name.substring(unitGroup.name.length + 1); - const subFieldLocalName2 = unitSubField2.name.substring(unitGroup.name.length + 1); - - const expectedFormValues = { - [unitField.name]: unitField.state.label, - [unitGroup.name]: { - [subFieldLocalName]: unitSubField.state.label, - [subFieldLocalName2]: unitSubField2.state.label, - }, + describe('html form event handling', () => { + describe('onSubmit', () => { + it('should call event.preventDefault', () => { + const { form } = setup(); + const mockSubmitEvent = { + preventDefault: jest.fn(), }; - expect(formValues).toMatchObject(expectedFormValues); - }); - }); - }); - - describe('listener / notify system', () => { - interface ISetupListenerResult extends ISetupResult { - unitField: IMockField; - mockListeners: IMockListener[]; - } - - const setupListener = (count: number): ISetupListenerResult => { - const setupResult = setup(); - - const unitField = createMockField('unitField', 'Unit field'); - setupResult.formContext.registerField(unitField.name, unitField.state); - - const mockListeners = createMockListeners(count); - mockListeners.forEach(item => { - setupResult.formContext.registerListener(item.id, item.state); - }); - - return { - ...setupResult, - mockListeners, - unitField, - }; - }; - - it('should register new listeners without crashing', () => { - const mockListeners = createMockListeners(3); - const { formContext } = setup(); - mockListeners.forEach((item) => { - expect(() => { - formContext.registerListener(item.id, item.state); - }).not.toThrowError(); - }); - }); - - it('should unregister new listeners without crashing', () => { - const mockListeners = createMockListeners(3); - const { formContext } = setup(); - mockListeners.forEach((item) => { - formContext.registerListener(item.id, item.state); - expect(() => { - formContext.unregisterListener(item.id); - }).not.toThrowError(); - }); - }); - - it('should pass the validation notification to all listeners', () => { - const eventName = 'validation'; - const eventArgs = { foo: 'bar' }; - - const { formContext, unitField, mockListeners } = setupListener(3); - - formContext.notifyFieldEvent(unitField.name, eventName, eventArgs); - mockListeners.forEach(item => expect(item.state).toHaveBeenLastCalledWith( - unitField.name, - eventName, - { - label: unitField.state.label, - ...eventArgs, - }, - )); - }); - - it('should call the listeners', () => { - const eventName = 'change'; - const eventArgs = 'myNewValue'; - - const { formContext, unitField, mockListeners } = setupListener(3); - - formContext.notifyFieldEvent(unitField.name, eventName, eventArgs); - mockListeners.forEach(item => expect(item.state).toHaveBeenLastCalledWith( - unitField.name, - eventName, - eventArgs, - )); - }); - }); - - describe('onSubmit handling', () => { - // @ts-ignore Form submit is private - const simulateSubmitEvent = async (wrapper: ShallowWrapper): Promise => (wrapper.instance() as Form).submit(); - - interface ISetupSubmitArgs extends ISetupArgs { - addListeners: boolean; - customField: IMockField; - } - - interface ISetupSubmitResult extends ISetupResult { - expectedFormValues: IFieldValues; - mockFields: IMockField[]; - mockListeners?: IMockListener[]; - } - - const unitFieldName = 'unitField'; - - const setupSubmit = async ({ - props, - customField, - addListeners = false, - }: Partial = {}): Promise => { - const result = setup({ props }); - - const unitField = createMockField(unitFieldName, 'Unit field'); - const unitGroup = createMockField('unitGroup', 'Unit group', true); - const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); - - const mockFields = [unitField, unitGroup, unitSubField]; - if (customField) { - mockFields.push(customField); - } - - registerUnitField(mockFields, result.formContext); - - let mockListeners; - - if (addListeners) { - mockListeners = createMockListeners(3); - mockListeners.forEach(item => { - result.formContext.registerListener(item.id, item.state); - }); - } - - await simulateSubmitEvent(result.wrapper); - - const subFieldLocalName = unitSubField.name.substring(unitGroup.name.length + 1); - const expectedFormValues = { - [unitField.name]: unitField.state.label, - [unitGroup.name]: { - [subFieldLocalName]: unitSubField.state.label, - }, - }; - - return { - ...result, - expectedFormValues, - mockFields, - mockListeners, - }; - }; - - describe('all valid', () => { - it('should call all the validation functions', async () => { - const { mockFields } = await setupSubmit(); - - mockFields.forEach(item => expect(item.state.validate).toHaveBeenLastCalledWith({ - checkAsync: true, - immediateAsync: true, - })); - }); - - it('should call the onValidate prop', async () => { - const onValidateHandler = jest.fn().mockReturnValue(null); - const { expectedFormValues } = await setupSubmit({ props: { onValidate: onValidateHandler }}); - - expect(onValidateHandler).toHaveBeenCalledWith(expectedFormValues); - }); - - it('should call the onSubmit prop', async () => { - const onSubmitHandler = jest.fn(); - const { expectedFormValues } = await setupSubmit({ props: { onSubmit: onSubmitHandler }}); - - expect(onSubmitHandler).toHaveBeenCalledWith(expectedFormValues, undefined); - }); - - describe('onValidate handler that returns valid field states', () => { - it('should call the onSubmit prop', async () => { - const onValidateHandler = jest.fn().mockReturnValue({ - unitField: undefined, - }); - const onSubmitHandler = jest.fn(); - const { expectedFormValues } = await setupSubmit({ props: { onValidate: onValidateHandler, onSubmit: onSubmitHandler }}); - - expect(onSubmitHandler).toHaveBeenCalledWith(expectedFormValues, undefined); - }); - }); - }); - - describe('invalid through form validator', () => { - const createInvalidValidator = (): jest.Mock => jest.fn().mockReturnValue({ - [unitFieldName]: 'error', - }); - - it('should update the validation state of the field', async () => { - const { mockFields } = await setupSubmit({ props: { onValidate: createInvalidValidator() }}); - - mockFields.forEach(item => { - if (item.name !== unitFieldName) { return; } - expect(item.state.updateValidation).toHaveBeenCalledWith({ - valid: false, - error: { - message_id: 'error', - params: { }, - }, - }); - }); - }); - - it('should trigger a submit-invalid event', async () => { - const { mockListeners } = await setupSubmit({ props: { onValidate: createInvalidValidator() }, addListeners: true}); - mockListeners!.forEach(item => expect(item.state).toHaveBeenLastCalledWith( - '_form', - 'submit-invalid', - undefined, - )); - }); - - it('should not call the onSubmit prop', async () => { - const onSubmitHandler = jest.fn(); - await setupSubmit({ props: { - onValidate: createInvalidValidator(), - onSubmit: onSubmitHandler, - }}); - expect(onSubmitHandler).not.toHaveBeenCalled(); - }); - }); - - describe('invalid through field validators', () => { - const createInvalidField = (): IMockField => { - const field = createMockField('invalidField', 'Invalid field'); - field.state.validate = jest.fn().mockResolvedValue({ - isValidating: false, - valid: false, - error: 'foobar', - }); - - return field; - }; - - it('should trigger a submit-invalid event', async () => { - const { mockListeners } = await setupSubmit({ customField: createInvalidField(), addListeners: true}); - - mockListeners!.forEach(item => expect(item.state).toHaveBeenLastCalledWith( - '_form', - 'submit-invalid', - undefined, - )); - }); - - it('should not call the onSubmit prop', async () => { - const onSubmitHandler = jest.fn(); - await setupSubmit({ - props: { onSubmit: onSubmitHandler }, - customField: createInvalidField(), - }); - expect(onSubmitHandler).not.toHaveBeenCalled(); - }); - }); - - describe('context busy state', () => { - const testBusyState = (wrapper: ShallowWrapper, formContext: IFormContext, expected: boolean, done: jest.DoneCallback): void => { - void simulateSubmitEvent(wrapper); - - process.nextTick(() => { - wrapper.update(); - expect(formContext.busy).toBe(expected); - done(); - }); - }; - - it('should not be busy if the onSubmit callback returns immediately', (done) => { - const onSubmitHandler = jest.fn(); - const { wrapper, formContext } = setup({ props: { onSubmit: onSubmitHandler }}); - testBusyState(wrapper, formContext, false, done); - }); - - it('should not be busy if there is no onSubmit callback', (done) => { - const { wrapper, formContext } = setup(); - testBusyState(wrapper, formContext, false, done); - }); - - describe('busy prop override set to true', () => { - it('should be always busy', () => { - const { formContext } = setup({ props: { busy: true }}); - expect(formContext.busy).toBeTruthy(); - }); - - it('should be busy if the onSubmit callback returns immediately', (done) => { - const onSubmitHandler = jest.fn(); - const { wrapper, formContext } = setup({ props: { onSubmit: onSubmitHandler, busy: true }}); - testBusyState(wrapper, formContext, true, done); - }); - - it('should be busy if there is no onSubmit callback', (done) => { - const { wrapper, formContext } = setup({ props: { busy: true }}); - testBusyState(wrapper, formContext, true, done); - }); + form.simulate('submit', mockSubmitEvent); + expect(mockSubmitEvent.preventDefault).toHaveBeenCalled(); }); - describe('async onSubmit callback', () => { - const createSlowOnSubmit = (): () => Promise => { - return async (): Promise => new Promise( - (resolve: Function): NodeJS.Timer => setTimeout( - (): void => { resolve(); }, - 1000, - ), - ); + it('should call formContext.submit', () => { + const { form, formContext } = setup(); + const mockSubmitEvent = { + preventDefault: jest.fn(), }; - beforeAll(() => { - jest.useFakeTimers(); - }); - - afterAll(() => { - jest.useRealTimers(); - }); - - it('should be busy after invoking onSubmit', async (done) => { - const { wrapper } = setup({ props: { onSubmit: createSlowOnSubmit() }}); - await simulateSubmitEvent(wrapper); - - process.nextTick(() => { - wrapper.update(); - const formContext: IFormContext = wrapper.first().prop('value'); - expect(formContext.busy).toBe(true); - done(); - }); - }); - - it('should not be busy after onSubmit finished', async (done) => { - const { wrapper } = setup({ props: { onSubmit: createSlowOnSubmit() }}); - await simulateSubmitEvent(wrapper); - - jest.runAllTimers(); - - process.nextTick(() => { - wrapper.update(); - const formContext: IFormContext = wrapper.first().prop('value'); - expect(formContext.busy).toBe(false); - done(); - }); - }); - - it('should be busy after onSubmit finished if the busy prop is set to true', async (done) => { - const { wrapper } = setup({ props: { onSubmit: createSlowOnSubmit(), busy: true }}); - await simulateSubmitEvent(wrapper); - - jest.runAllTimers(); - - process.nextTick(() => { - wrapper.update(); - const formContext: IFormContext = wrapper.first().prop('value'); - expect(formContext.busy).toBe(true); - done(); - }); - }); - }); - }); - - describe('reset on successful submit', () => { - it('should call the onReset prop on submit when resetOnSubmit is true', async () => { - const onResetHandler = jest.fn(); - const { wrapper } = setup({ props: { onReset: onResetHandler, resetOnSubmit: true }}); - await simulateSubmitEvent(wrapper); - - expect(onResetHandler).toHaveBeenCalled(); + form.simulate('submit', mockSubmitEvent); + expect(formContext.submit).toHaveBeenCalled(); }); }); - describe('html form submit event handling', () => { + describe('onReset', () => { it('should call event.preventDefault', () => { const { form } = setup(); const mockSubmitEvent = { preventDefault: jest.fn(), }; - form.simulate('submit', mockSubmitEvent); + form.simulate('reset', mockSubmitEvent); expect(mockSubmitEvent.preventDefault).toHaveBeenCalled(); }); - }); - }); - - describe('onReset handling', () => { - const simulateResetEvent = (form: ShallowWrapper): ShallowWrapper => form.simulate('reset', mockEvent()); - - it('should reset all fields', () => { - const { form, formContext } = setup(); - - const unitField = createMockField('unitField', 'Unit field'); - const unitGroup = createMockField('unitGroup', 'Unit group', true); - const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); - const mockFields = [unitField, unitGroup, unitSubField]; - registerUnitField(mockFields, formContext); - - simulateResetEvent(form); - mockFields.forEach(item => expect(item.state.reset).toHaveBeenCalled()); - }); - it('should call the onReset prop', () => { - const onResetHandler = jest.fn(); - const { form } = setup({ props: { onReset: onResetHandler }}); - simulateResetEvent(form); + it('should call formContext.reset', () => { + const { form, formContext } = setup(); + const mockSubmitEvent = { + preventDefault: jest.fn(), + }; - expect(onResetHandler).toHaveBeenCalled(); + form.simulate('reset', mockSubmitEvent); + expect(formContext.reset).toHaveBeenCalled(); + }); }); }); }); diff --git a/src/components/Form/Form.tsx b/src/components/Form/Form.tsx index 6724edd..50e4c3e 100644 --- a/src/components/Form/Form.tsx +++ b/src/components/Form/Form.tsx @@ -6,381 +6,36 @@ */ import React from 'react'; -import { getDeepValue, parseValidationError } from '../../utils'; -import { stringFormatter as defaultStringFormatter } from '../../utils/stringFormatter'; -import { FormContext, IBaseFormContext, IFieldState, IFieldValues, IFormContext, TFormEventListener } from '../FormContext'; -import { IValidationState } from '../withValidation'; +import { FormContext, IFieldValues } from '../FormContext'; import { IFormProps } from './Form.types'; - -interface IFormState { - context: IBaseFormContext; -} +import { useForm } from './hooks/useForm'; /** - * Wrapper for managed forms + * The form is the main component. It glues together all the Form logic through the context api. + * All form specific components must be wrapped by a form. */ -export class Form - extends React.Component, IFormState> { - public static displayName: string = 'Form'; - - public static defaultProps = { - defaultValues: {}, - asyncValidationWait: 400, - asyncValidateOnChange: false, - formatString: defaultStringFormatter, - disabled: false, - plaintext: false, - }; - - private readonly fields: Map = new Map(); - private readonly eventListeners: Map = new Map(); - - constructor(props: IFormProps) { - super(props); - - this.state = { - context: { - fieldPrefix: null, - - registerField: this.registerField, - unregisterField: this.unregisterField, - notifyFieldEvent: this.notifyFieldEvent, - - registerListener: (name: string, callback: TFormEventListener): void => { this.eventListeners.set(name, callback); }, - unregisterListener: (name: string): void => { this.eventListeners.delete(name); }, - - getFieldState: this.getFieldState, - getValues: this.getValues, - - submit: this.submit, - - busy: false, - }, - }; - } - - /** - * Returns the current state of the given field - * @param name Field name - * @returns Current field state or default field state - */ - private getFieldState = (name: string): IFieldState => { - const fieldState = this.fields.get(name); - if (fieldState === undefined) { - throw new Error(`[Form] getFieldState: Could not find state of field '${name}'`); - } - - return fieldState; - } - - /** - * Generates and returns an object that contains - * all values from all the fields. - * @returns Current values in form of { name: value, name2: value2, ... } - */ - private getValues = (): TFieldValues => { - const values: IFieldValues = {}; +export const Form = ({ children, className, ...rest }: +React.PropsWithChildren>): React.ReactElement | null => { + const formContext = useForm(rest); - this.fields.forEach((state, name) => { - if (state.isGroup === true) { return; } + let formClass = className === undefined ? '' : className; + if (formContext.plaintext) { formClass = `${formClass} plaintext`; } - const nameParts = name.split('.'); - let valueRef = values; - - nameParts.forEach((key, index) => { - if (nameParts.length === 1 || index === nameParts.length - 1) { - valueRef[key] = state.getValue(); - } else { - if (valueRef[key] === undefined) { valueRef[key] = {}; } - valueRef = valueRef[key] as IFieldValues; - } - }); - }); - - return (values as unknown) as TFieldValues; - } - - /** - * Gets called when a field triggers an event - * @param name Field name - * @param event Event name - * @param args Event args - */ - private notifyFieldEvent = (name: string, event: string, args?: unknown): void => { - if (event === 'validation') { - const { label } = this.getFieldState(name); - this.notifyListeners(name, event, { ...args, label }); - } else { - this.notifyListeners(name, event, args); - } - } - - /** - * Notifies the event listeners about an event - * @param name Field name - * @param event Event name - * @param args Event args - */ - private notifyListeners(name: string, event: string, args?: unknown): void { - this.eventListeners.forEach((callback) => { - callback(name, event, args); - }); - } - - /** - * Handles the submit event of the form - prevents - * the default and runs the submit logic - * @param event Event object - */ - private handleSubmit = (event: React.FormEvent): void => { + const handleSubmit = (event: React.FormEvent): void => { event.preventDefault(); - void this.submit(); - } - - /** - * Submits the form - triggers any validations if needed - * and raises the onFormSubmit prop callback if the - * form is currently valid. - * @param submitArgs Arguments that will be passed - * to the onSubmit callback - */ - private submit = async (submitArgs?: TSubmitArgs): Promise => { - this.updateBusyState(true); - - // Iterate through all fields and validate them - // if needed. - const validations: Promise[] = []; - this.fields.forEach(field => { - validations.push(field.validate({ - checkAsync: true, - immediateAsync: true, - })); - }); - - const validationStates = await Promise.all(validations); - - // Check if all fields are valid - const allValid = validationStates.every(state => state.valid === true); - if (allValid === false) { - this.notifyFieldEvent('_form', 'submit-invalid'); - this.updateBusyState(false); - - return; - } - - // Call the form wide validation - const formValid = this.triggerFormValidation(); - if (formValid === false) { - this.notifyFieldEvent('_form', 'submit-invalid'); - this.updateBusyState(false); - - return; - } - - // Await the result from callOnSubmit - const callOnSubmitResult = this.callOnSubmit(submitArgs); - const { resetOnSubmit } = this.props; - - // Make sure the state is cleaned up before - const cleanup = (resetForm: boolean | undefined): void => { - this.updateBusyState(false); - if (resetForm) { this.reset(); } - }; - if (callOnSubmitResult instanceof Promise) { - void callOnSubmitResult.then( - () => { cleanup(resetOnSubmit); }, - ); - } else { - cleanup(resetOnSubmit); - } - } - - /** - * Updates the busy state in the form context - * @param busy Busy state - */ - private updateBusyState(busy: boolean): void { - this.setState(prevState => ({ - context: { - ...prevState.context, - busy, - }, - })); - } - - /** - * Triggers the form wide validation callback if given - * @returns Validation state of the form - */ - private triggerFormValidation(): boolean { - const { onValidate } = this.props; - if (onValidate === undefined) { return true; } - - const values = this.getValues(); - const result = onValidate(values); - - // If the callback returned null then the form is valid - if (result === null) { return true; } - - // Otherwise parse the result object and update the - // field states. - let allFieldsValid = true; - this.fields.forEach((state, name) => { - const fieldError = parseValidationError(name, getDeepValue(name, result)); - const isValid = fieldError === null || typeof fieldError !== 'object'; - - if (isValid) { return; } - - state.updateValidation({ - valid: false, - error: fieldError, - }); - allFieldsValid = false; - }); - - return allFieldsValid; - } - - /** - * Gathers all the data and calls the onSubmit callback - * if provided. - * @param submitArgs Arguments that will be passed - * to the onSubmit callback - */ - private callOnSubmit(submitArgs?: TSubmitArgs): void | Promise { - const { onSubmit } = this.props; - if (onSubmit === undefined) { return; } - - const values = this.getValues(); - - return onSubmit(values, submitArgs); - } + formContext.submit(); + }; - /** - * Handles the reset event of the form - prevents - * the default and sets the state of all fields back - * to the default state. - * @param event Event object - */ - private handleReset = (event: React.FormEvent): void => { + const handleReset = (event: React.FormEvent): void => { event.preventDefault(); - this.reset(); - } - - /** - * Sets the state of all fields back - * to the default state. - */ - private reset = (): void => { - this.fields.forEach((state) => { - state.reset(); - }); - - const { onReset } = this.props; - if (onReset !== undefined) { - onReset(); - } - } - - /** - * Registers a new field to the form. - * @param name Field name - * @param fieldState Field state - */ - private registerField = (name: string, fieldState: IFieldState): void => { - if (typeof name !== 'string' || name.length === 0) { - throw new Error('[Form] registerField: name is required'); - } - - if (typeof fieldState !== 'object') { - throw new Error('[Form] registerField: field state is required'); - } - - if ( - typeof fieldState.label !== 'string' - || typeof fieldState.validate !== 'function' - || typeof fieldState.updateValidation !== 'function' - || typeof fieldState.reset !== 'function' - || typeof fieldState.getValue !== 'function' - ) { - throw new Error('[Form] registerField: invalid field state given'); - } - - this.fields.set(name, fieldState); - } - - /** - * Unregisters a field from the form. - * @param name Field name - */ - private unregisterField = (name: string): void => { - const { label } = this.getFieldState(name); - this.notifyListeners(name, 'validation', { - label, - valid: true, - }); - this.fields.delete(name); - } - - /** - * Combines the local form context with - * the values from the props to form the - * full form context passed to the form - * components. - */ - private prepareFormContext(): IFormContext { - const { context } = this.state; - const { - defaultValues, - values, - asyncValidateOnChange, - asyncValidationWait, - formatString: stringFormatter, - disabled, - plaintext, - busy: busyProp, - } = this.props; - - // Override the busy state with the busy prop if it is set to true - const busy = busyProp === true ? busyProp : context.busy; - - return { - ...context, - defaultValues, - values, - asyncValidationWait, - stringFormatter, - disabled, - plaintext, - busy, - asyncValidateOnChange: asyncValidateOnChange, - }; - } - - /** - * Renders the form and wraps all its children - * in a FormContext provider and a html form. - */ - public render(): JSX.Element { - const { - children, - className, - plaintext, - } = this.props; - - const context = this.prepareFormContext(); - - let formClass = className === undefined ? '' : className; - if (plaintext) { formClass = `${formClass} plaintext`; } - - const TypedFormContext = (FormContext as unknown) as React.Context>; + formContext.reset(); + }; - return ( - - - {children} - - - ); - } -} + return ( + +
+ {children} +
+
+ ); +}; diff --git a/src/components/Form/Form.types.ts b/src/components/Form/Form.types.ts index dd51174..ce1ed93 100644 --- a/src/components/Form/Form.types.ts +++ b/src/components/Form/Form.types.ts @@ -14,9 +14,10 @@ import { IFieldValues } from '../FormContext'; export interface IFormProps { /** * Contains the default values of the form. Those values will be - * put into the according fields when the form initializes. + * put into the according fields when the form initializes. Those + * values will be put into the according fields when the form initializes. */ - defaultValues: Partial; + defaultValues?: Partial; /** * Contains the values of the form. Changing this property will * update all Field values, overwriting their default values but also @@ -28,23 +29,26 @@ export interface IFormProps * Fields whenever they change (e.g. on key press). Default behaviour * is that the fields will only async validate when they loose focus. * Can be overriden per field. + * @default false */ - asyncValidateOnChange: boolean; + asyncValidateOnChange?: boolean; /** * Configures the wait time before the async validators will be called. * Per default the form will call the async validators only 400ms after * the last value change. Can be overriden per field. + * @default 400 */ - asyncValidationWait: number; + asyncValidationWait?: number; /** * If set every text output will be put through this function. * Per default an internal implementation is used. */ - formatString: TSTringFormatter; + formatString?: TSTringFormatter; /** * If set to true the form will disable all Fields and FormButtons. + * @default false */ - disabled: boolean; + disabled?: boolean; /** * Sets the className of the html form. */ @@ -52,22 +56,28 @@ export interface IFormProps /** * If set to true, all the fields will display only text instead of an * input element. This is useful to re-use Fields in a check page. + * @default false */ - plaintext: boolean; + plaintext?: boolean; /** * If set to true, all fields will be reset on an successful form submit + * @default false */ resetOnSubmit?: boolean; /** * If set to true, the form will be forced into a busy state and thus disabling * any form buttons. + * @default false */ busy?: true; /** * Triggered when the form has been validated successfully and is ready to be submitted. + * If the passed function is an async function / returns a promise, then the form context + * will stay in a busy state until the function resolves. * @param values Contains the form values. The name of the fields are * used as property names for the values object. FieldGroups result in a nested object. + *
* @param submitArgs By default undefined. Can be set by FormButton or * any other manual way of calling the submit method of the form context. */ @@ -78,6 +88,7 @@ export interface IFormProps * inside those properties. * @param values Contains the form values. The name of the fields are used as property * names for the values object. FieldGroups result in a nested object. + * @returns: should return null if the values are valid, otherwise an error object. */ onValidate?(values: TFieldValues): TFormValidationResult; /** diff --git a/src/components/Form/__snapshots__/Form.test.tsx.snap b/src/components/Form/__snapshots__/Form.test.tsx.snap index 271a1ca..d056a21 100644 --- a/src/components/Form/__snapshots__/Form.test.tsx.snap +++ b/src/components/Form/__snapshots__/Form.test.tsx.snap @@ -1,21 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`
invalid field registration case 1 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; - -exports[` invalid field registration case 2 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; - -exports[` invalid field registration case 3 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; - -exports[` invalid field registration case 4 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; - -exports[` invalid field registration case empty state 1`] = `"[Form] registerField: invalid field state given"`; - -exports[` invalid field registration case invalid field name 1`] = `"[Form] registerField: name is required"`; - -exports[` invalid field registration case no parameters 1`] = `"[Form] registerField: name is required"`; - -exports[` invalid field registration case no state 1`] = `"[Form] registerField: field state is required"`; - exports[` should render without error 1`] = ` should render without error 1`] = ` "defaultValues": Object {}, "disabled": false, "fieldPrefix": null, - "getFieldState": [Function], - "getValues": [Function], - "notifyFieldEvent": [Function], + "getFieldState": [MockFunction], + "getValues": [MockFunction], + "notifyFieldEvent": [MockFunction], "plaintext": false, - "registerField": [Function], - "registerListener": [Function], - "stringFormatter": [Function], - "submit": [Function], - "unregisterField": [Function], - "unregisterListener": [Function], - "values": undefined, + "registerField": [MockFunction], + "registerListener": [MockFunction], + "reset": [MockFunction], + "stringFormatter": [MockFunction], + "submit": [MockFunction], + "unregisterField": [MockFunction], + "unregisterListener": [MockFunction], } } > diff --git a/src/components/Form/hooks/useForm.test.ts b/src/components/Form/hooks/useForm.test.ts new file mode 100644 index 0000000..9f5cc62 --- /dev/null +++ b/src/components/Form/hooks/useForm.test.ts @@ -0,0 +1,452 @@ +import { renderHook, act } from 'react-hooks-testing-library'; + +import { IFormProps } from '../Form.types'; +import { IFormContext, IFieldValues } from '../../FormContext'; + +import { useForm } from './useForm'; +import { useFieldEvents, IUseFieldEventsResult, IUseFieldStatesResult, useFieldStates, IFieldState } from '../../../hooks/internal'; + +jest.mock('../../../hooks/internal'); + +describe('useForm', () => { + interface ISetupArgs { + props?: IFormProps; + fieldStatesOverride?: Partial; + } + + interface IMockResult { + current: IFormContext; + } + + interface ISetupResult { + result: IMockResult; + fieldEventResult: IUseFieldEventsResult; + fieldStatesResult: IUseFieldStatesResult; + } + + const setup = ({ props = { }, fieldStatesOverride }: Partial = {}): ISetupResult => { + const fieldEventResult: IUseFieldEventsResult = { + notifyListeners: jest.fn(), + registerListener: jest.fn(), + unregisterListener: jest.fn(), + }; + (useFieldEvents as jest.Mock).mockReturnValue(fieldEventResult); + + const fieldStatesResult: IUseFieldStatesResult = { + registerField: jest.fn(), + unregisterField: jest.fn(), + getFieldState: jest.fn(), + forEachFieldState: jest.fn(), + ...fieldStatesOverride, + }; + (useFieldStates as jest.Mock).mockReturnValue(fieldStatesResult); + + return { + fieldEventResult, + fieldStatesResult, + ...renderHook(() => useForm(props)), + }; + }; + + const createMockFieldState = (label: string, isGroup?: boolean): IFieldState => ({ + label, + isGroup, + validate: jest.fn().mockResolvedValue({ + isValidating: false, + valid: true, + error: null, + }), + updateValidation: jest.fn(), + reset: jest.fn(), + getValue: jest.fn().mockReturnValue(label), + }); + + interface IMockField { + name: string; + state: IFieldState; + } + + const createMockField = (name: string, label: string, isGroup?: boolean): IMockField => ({ + name, + state: createMockFieldState(label, isGroup), + }); + + it('should create a valid form context', () => { + const { result } = setup(); + expect(result.current).toMatchObject({ + fieldPrefix: null, + registerField: expect.any(Function), + unregisterField: expect.any(Function), + notifyFieldEvent: expect.any(Function), + registerListener: expect.any(Function), + unregisterListener: expect.any(Function), + getFieldState: expect.any(Function), + getValues: expect.any(Function), + busy: false, + disabled: false, + asyncValidateOnChange: false, + asyncValidationWait: 400, + defaultValues: {}, + values: undefined, + }); + }); + + describe('configuration', () => { + const cases: any[] = [ + ['disabled', true, 'disabled'], + ['formatString', jest.fn(), 'stringFormatter'], + ['asyncValidateOnChange', true, 'asyncValidateOnChange'], + ['asyncValidationWait', 800, 'asyncValidationWait'], + ['defaultValues', { foo: 'bar' }, 'defaultValues'], + ['values', { foo: 'bar' }, 'values'], + ['plaintext', true, 'plaintext'], + ]; + + test.each(cases)('case %s', (prop, value, contextProp) => { + const { result } = setup({ + props: { [prop]: value }, + }); + expect(result.current).toMatchObject({ [contextProp]: value }); + }); + }); + + describe('formContext.getValues - form values', () => { + const unitField = createMockField('unitField', 'Unit field'); + const unitGroup = createMockField('unitGroup', 'Unit group', true); + const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); + const unitSubField2 = createMockField(`${unitGroup.name}.subField2`, 'Sub field 2'); + + const mockFields = new Map([ + [ unitField.name, unitField.state ], + [ unitGroup.name, unitGroup.state ], + [ unitSubField.name, unitSubField.state ], + [ unitSubField2.name, unitSubField2.state ], + ]); + + const forEachFieldState = jest.fn().mockImplementation((cb) => { + mockFields.forEach(cb); + }); + const { result } = setup({ fieldStatesOverride: { forEachFieldState }}); + + let formValues: IFieldValues; + it('should return the values without crashing', () => { + expect(() => { + formValues = result.current.getValues(); + }).not.toThrowError(); + }); + + it('should call unitField.getValue', () => { + expect(unitField.state.getValue).toHaveBeenCalled(); + }); + + it('should call subField.getValue', () => { + expect(unitSubField.state.getValue).toHaveBeenCalled(); + }); + + it('should not call group.getValue', () => { + // The value of fieldGroups is computed by its children + expect(unitGroup.state.getValue).not.toHaveBeenCalled(); + }); + + it('should return the correct form values', () => { + const subFieldLocalName = unitSubField.name.substring(unitGroup.name.length + 1); + const subFieldLocalName2 = unitSubField2.name.substring(unitGroup.name.length + 1); + + const expectedFormValues = { + [unitField.name]: unitField.state.label, + [unitGroup.name]: { + [subFieldLocalName]: unitSubField.state.label, + [subFieldLocalName2]: unitSubField2.state.label, + }, + }; + + expect(formValues).toMatchObject(expectedFormValues); + }); + }); + + describe('onSubmit handling', () => { + const simulateSubmitEvent = async (context: IFormContext): Promise => { + return act(async () => { + await context.submit(); + }); + }; + + interface ISetupSubmitArgs extends ISetupArgs { + customField: IMockField; + } + + interface ISetupSubmitResult extends ISetupResult { + expectedFormValues: IFieldValues; + mockFields: Map; + } + + const unitFieldName = 'unitField'; + + const setupSubmit = async ({ props, customField, }: Partial = {}): Promise => { + const unitField = createMockField(unitFieldName, 'Unit field'); + const unitGroup = createMockField('unitGroup', 'Unit group', true); + const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); + + const mockFields = new Map([ + [ unitField.name, unitField.state ], + [ unitGroup.name, unitGroup.state ], + [ unitSubField.name, unitSubField.state ], + ]); + + if (customField) { + mockFields.set(customField.name, customField.state); + } + + const forEachFieldState = jest.fn().mockImplementation((cb) => { + mockFields.forEach(cb); + }); + + const result = setup({ props, fieldStatesOverride: { forEachFieldState } }); + + await simulateSubmitEvent(result.result.current); + + const subFieldLocalName = unitSubField.name.substring(unitGroup.name.length + 1); + const expectedFormValues = { + [unitField.name]: unitField.state.label, + [unitGroup.name]: { + [subFieldLocalName]: unitSubField.state.label, + }, + }; + + return { + ...result, + expectedFormValues, + mockFields, + }; + }; + + describe('all valid', () => { + it('should call all the validation functions', async () => { + const { mockFields } = await setupSubmit(); + + mockFields.forEach(item => expect(item.validate).toHaveBeenLastCalledWith({ + checkAsync: true, + immediateAsync: true, + })); + }); + + it('should call the onValidate prop', async () => { + const onValidateHandler = jest.fn().mockReturnValue(null); + const { expectedFormValues } = await setupSubmit({ props: { onValidate: onValidateHandler }}); + + expect(onValidateHandler).toHaveBeenCalledWith(expectedFormValues); + }); + + it('should call the onSubmit prop', async () => { + const onSubmitHandler = jest.fn(); + const { expectedFormValues } = await setupSubmit({ props: { onSubmit: onSubmitHandler }}); + + expect(onSubmitHandler).toHaveBeenCalledWith(expectedFormValues, undefined); + }); + + describe('onValidate handler that returns valid field states', () => { + it('should call the onSubmit prop', async () => { + const onValidateHandler = jest.fn().mockReturnValue({ + unitField: undefined, + }); + const onSubmitHandler = jest.fn(); + const { expectedFormValues } = await setupSubmit({ props: { onValidate: onValidateHandler, onSubmit: onSubmitHandler }}); + + expect(onSubmitHandler).toHaveBeenCalledWith(expectedFormValues, undefined); + }); + }); + }); + + describe('invalid through form validator', () => { + const createInvalidValidator = (): jest.Mock => jest.fn().mockReturnValue({ + [unitFieldName]: 'error', + }); + + it('should update the validation state of the field', async () => { + const { mockFields } = await setupSubmit({ props: { onValidate: createInvalidValidator() }}); + + mockFields.forEach((item, name) => { + if (name !== unitFieldName) { return; } + expect(item.updateValidation).toHaveBeenCalledWith({ + valid: false, + error: { + message_id: 'error', + params: { }, + }, + }); + }); + }); + + it('should trigger a submit-invalid event', async () => { + const { fieldEventResult } = await setupSubmit({ props: { onValidate: createInvalidValidator() } }); + expect(fieldEventResult.notifyListeners).toHaveBeenCalledWith('_form', 'submit-invalid'); + }); + + it('should not call the onSubmit prop', async () => { + const onSubmitHandler = jest.fn(); + await setupSubmit({ props: { + onValidate: createInvalidValidator(), + onSubmit: onSubmitHandler, + }}); + expect(onSubmitHandler).not.toHaveBeenCalled(); + }); + }); + + describe('invalid through field validators', () => { + const createInvalidField = (): IMockField => { + const field = createMockField('invalidField', 'Invalid field'); + field.state.validate = jest.fn().mockResolvedValue({ + isValidating: false, + valid: false, + error: 'foobar', + }); + + return field; + }; + + it('should trigger a submit-invalid event', async () => { + const { fieldEventResult } = await setupSubmit({ customField: createInvalidField() }); + expect(fieldEventResult.notifyListeners).toHaveBeenCalledWith('_form', 'submit-invalid'); + }); + + it('should not call the onSubmit prop', async () => { + const onSubmitHandler = jest.fn(); + await setupSubmit({ + props: { onSubmit: onSubmitHandler }, + customField: createInvalidField(), + }); + expect(onSubmitHandler).not.toHaveBeenCalled(); + }); + }); + + describe('context busy state', () => { + const testBusyState = async (result: IMockResult, expected: boolean): Promise => { + await simulateSubmitEvent(result.current); + expect(result.current.busy).toBe(expected); + }; + + it('should not be busy if the onSubmit callback returns immediately', async () => { + const onSubmitHandler = jest.fn(); + const { result } = setup({ props: { onSubmit: onSubmitHandler }}); + await testBusyState(result, false); + }); + + it('should not be busy if there is no onSubmit callback', async () => { + const { result } = setup(); + await testBusyState(result, false); + }); + + describe('busy prop override set to true', () => { + it('should be always busy', () => { + const { result } = setup({ props: { busy: true }}); + expect(result.current.busy).toBeTruthy(); + }); + + it('should be busy if the onSubmit callback returns immediately', async () => { + const onSubmitHandler = jest.fn(); + const { result } = setup({ props: { onSubmit: onSubmitHandler, busy: true }}); + await testBusyState(result, true); + }); + + it('should be busy if there is no onSubmit callback', async () => { + const { result } = setup({ props: { busy: true }}); + await testBusyState(result, true); + }); + }); + + describe('async onSubmit callback', () => { + const createSlowOnSubmit = (): () => Promise => { + return async (): Promise => new Promise( + (resolve: Function) => setTimeout( + (): void => { resolve(); }, + 1000, + ), + ); + }; + + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + + it('should be busy after invoking onSubmit', async () => { + const { result } = setup({ props: { onSubmit: createSlowOnSubmit() }}); + await simulateSubmitEvent(result.current); + + expect(result.current.busy).toBe(true); + }); + + it('should not be busy after onSubmit finished', async () => { + const { result } = setup({ props: { onSubmit: createSlowOnSubmit() }}); + await simulateSubmitEvent(result.current); + + await act(async () => { + jest.runAllTimers(); + }); + + expect(result.current.busy).toBe(false); + }); + + it('should be busy after onSubmit finished if the busy prop is set to true', async () => { + const { result } = setup({ props: { onSubmit: createSlowOnSubmit(), busy: true }}); + await simulateSubmitEvent(result.current); + + await act(async () => { + jest.runAllTimers(); + }); + + expect(result.current.busy).toBe(true); + }); + }); + }); + + describe('reset on successful submit', () => { + it('should call the onReset prop on submit when resetOnSubmit is true', async () => { + const onResetHandler = jest.fn(); + const { result } = setup({ props: { onReset: onResetHandler, resetOnSubmit: true }}); + await simulateSubmitEvent(result.current); + + expect(onResetHandler).toHaveBeenCalled(); + }); + }); + }); + + describe('onReset handling', () => { + const simulateResetEvent = (formContext: IFormContext): void => { + act(() => { + formContext.reset(); + }); + }; + + it('should reset all fields', () => { + const unitField = createMockField('unitField', 'Unit field'); + const unitGroup = createMockField('unitGroup', 'Unit group', true); + const unitSubField = createMockField(`${unitGroup.name}.subField`, 'Sub field'); + const mockFields = new Map([ + [ unitField.name, unitField.state ], + [ unitGroup.name, unitGroup.state ], + [ unitSubField.name, unitSubField.state ], + ]); + + const forEachFieldState = jest.fn().mockImplementation((cb) => { + mockFields.forEach(cb); + }); + + const { result } = setup({ fieldStatesOverride: { forEachFieldState }}); + + simulateResetEvent(result.current); + mockFields.forEach(item => expect(item.reset).toHaveBeenCalled()); + }); + + it('should call the onReset prop', () => { + const onResetHandler = jest.fn(); + const { result } = setup({ props: { onReset: onResetHandler }}); + simulateResetEvent(result.current); + + expect(onResetHandler).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/components/Form/hooks/useForm.ts b/src/components/Form/hooks/useForm.ts new file mode 100644 index 0000000..46c7d00 --- /dev/null +++ b/src/components/Form/hooks/useForm.ts @@ -0,0 +1,200 @@ +import { useMemo, useState, useCallback } from 'react'; + +import { stringFormatter as defaultStringFormatter, noopFunction, parseValidationError, getDeepValue } from '../../../utils'; +import { IBasicValidationState } from '../../../hooks'; +import { IFormContext, IFieldValues } from '../../FormContext'; +import { IFormProps } from '../Form.types'; +import { useFieldEvents, useFieldStates } from '../../../hooks/internal'; + +export function useForm(props: IFormProps): IFormContext { + const [ busyState, setBusyState ] = useState(false); + + const { + defaultValues = { }, + values, + asyncValidationWait = 400, + asyncValidateOnChange = false, + formatString = defaultStringFormatter, + disabled = false, + plaintext = false, + busy = busyState, + resetOnSubmit, + onReset = noopFunction, + onSubmit = noopFunction, + onValidate = noopFunction, + } = props; + + const { registerListener, unregisterListener, notifyListeners } = useFieldEvents(); + const { getFieldState, registerField, unregisterField, forEachFieldState } = useFieldStates(); + + /** + * Generates and returns an object that contains + * all values from all the fields. + * @returns Current values in form of { name: value, name2: value2, ... } + */ + const getValues = useCallback((): IFieldValues => { + const values: IFieldValues = {}; + + forEachFieldState((state, name) => { + if (state.isGroup === true) { return; } + + const nameParts = name.split('.'); + let valueRef = values; + + nameParts.forEach((key, index) => { + if (nameParts.length === 1 || index === nameParts.length - 1) { + valueRef[key] = state.getValue(); + } else { + if (valueRef[key] === undefined) { valueRef[key] = {}; } + valueRef = valueRef[key] as IFieldValues; + } + }); + }); + + return values; + }, [forEachFieldState]); + + /** + * Sets the state of all fields back + * to the default state. + */ + const reset = useCallback((): void => { + forEachFieldState((state) => { + state.reset(); + }); + + onReset(); + }, [forEachFieldState, onReset]); + + /** + * Triggers the form wide validation callback if given + * @returns Validation state of the form + */ + const triggerFormValidation = useCallback((): boolean => { + const values = getValues(); + const result = onValidate(values); + + // If the callback returned null then the form is valid + if (result === null || result === undefined) { return true; } + + // Otherwise parse the result object and update the + // field states. + let allFieldsValid = true; + forEachFieldState((state, name) => { + const fieldError = parseValidationError(getDeepValue(name, result)); + const isValid = fieldError === null || typeof fieldError !== 'object'; + + if (isValid) { return; } + + state.updateValidation({ + valid: false, + error: fieldError, + }); + allFieldsValid = false; + }); + + return allFieldsValid; + }, [forEachFieldState, getValues, onValidate]); + + /** + * Submits the form - triggers any validations if needed + * and raises the onFormSubmit prop callback if the + * form is currently valid. + * @param submitArgs Arguments that will be passed + * to the onSubmit callback + */ + const submit = useCallback(async (submitArgs?: unknown): Promise => { + setBusyState(true); + + // Iterate through all fields and validate them + // if needed. + const validations: Promise[] = []; + forEachFieldState(field => { + validations.push(field.validate({ + checkAsync: true, + immediateAsync: true, + })); + }); + const validationStates = await Promise.all(validations); + + const notifyInvalid = (): void => { + notifyListeners('_form', 'submit-invalid'); + setBusyState(false); + } + + // Check if all fields are valid + const allValid = validationStates.every(state => state.valid === true); + if (allValid === false) { + notifyInvalid(); + return; + } + + // Call the form wide validation + const formValid = triggerFormValidation(); + if (formValid === false) { + notifyInvalid(); + return; + } + + // Await the result from callOnSubmit + const callOnSubmitResult = onSubmit(getValues(), submitArgs); + + // Make sure the state is cleaned up before + const cleanup = (resetForm: boolean | undefined): void => { + setBusyState(false); + if (resetForm) { reset(); } + }; + if (callOnSubmitResult instanceof Promise) { + void callOnSubmitResult.then( + () => { cleanup(resetOnSubmit); }, + ); + } else { + cleanup(resetOnSubmit); + } + }, [forEachFieldState, getValues, notifyListeners, onSubmit, reset, resetOnSubmit, triggerFormValidation]); + + const formContext: IFormContext = useMemo(() => ({ + fieldPrefix: null, + defaultValues, + values, + asyncValidationWait, + stringFormatter: formatString, + disabled, + plaintext, + busy, + asyncValidateOnChange, + + registerField, + unregisterField, + + notifyFieldEvent: notifyListeners, + registerListener, + unregisterListener, + + getFieldState: getFieldState, + getValues: getValues, + + reset, + submit, + }), [ + asyncValidateOnChange, + asyncValidationWait, + busy, + defaultValues, + disabled, + formatString, + getFieldState, + getValues, + notifyListeners, + plaintext, + registerField, + registerListener, + reset, + submit, + unregisterField, + unregisterListener, + values + ]); + + return formContext; +} diff --git a/src/components/FormButton/FormButton.md b/src/components/FormButton/FormButton.md new file mode 100644 index 0000000..68b4163 --- /dev/null +++ b/src/components/FormButton/FormButton.md @@ -0,0 +1,89 @@ +### Examples +#### FormButton +Showcase of the form button + +```jsx +import { Form, Input, FormButton } from 'react-ocean-forms'; + +function asyncValidator(value) { + return new Promise(function(resolve) { + setTimeout(() => { + if (value === '') { + resolve('Invalid input'); + } else { + resolve(); + } + }, 1000); + }); +} + +function Example() { + return ( + + + + Submit + Reset + + ); +} + + +``` + +#### Submit args +Arguments to the form.onSubmit handler can be passed this way + +```jsx +import { Form, Input, FormButton } from 'react-ocean-forms'; + +function Example() { + const handleSubmit = (values, submitArgs) => { + console.log( + 'onSubmit, values: ' + JSON.stringify(values) + + ', submitArgs: ' + JSON.stringify(submitArgs) + ); + }; + + return ( +
+ + + Submit + Reset +
+ ); +} + + +``` + +#### Disabled form +FormButtons are disabled if the form is disabled too + +```jsx +import { Form, Input, FormButton } from 'react-ocean-forms'; + +function Example() { + return ( +
+ + + Submit + Reset +
+ ); +} + + +``` diff --git a/src/components/FormButton/FormButton.test.tsx b/src/components/FormButton/FormButton.test.tsx index 886445f..7f7350b 100644 --- a/src/components/FormButton/FormButton.test.tsx +++ b/src/components/FormButton/FormButton.test.tsx @@ -3,11 +3,12 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { mockEvent } from '../../test-utils/enzymeEventUtils'; import { createMockFormContext } from '../../test-utils/enzymeFormContext'; -import { IFormContext, useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; +import { IFormContext } from '../FormContext'; import { FormButton } from './FormButton'; import { IFormButtonProps } from './FormButton.types'; -jest.mock('../FormContext'); +jest.mock('../../hooks'); describe('', () => { interface ISetupArgs { diff --git a/src/components/FormButton/FormButton.tsx b/src/components/FormButton/FormButton.tsx index fd2c400..1dc6061 100644 --- a/src/components/FormButton/FormButton.tsx +++ b/src/components/FormButton/FormButton.tsx @@ -6,15 +6,15 @@ */ import React from 'react'; -import { useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; import { IFormButtonProps } from './FormButton.types'; /** - * Wrapper for a button that will - * automatically disable the button if the - * form is busy + * Defines a button that integrates into the form context. It will + * be disabled when the form is busy or disabled. You can also pass + * submitArgs to the onSubmit handler this way. */ -export const FormButton: React.FunctionComponent = (props: IFormButtonProps): JSX.Element => { +export const FormButton: React.FC = (props) => { const { disabled = false, type = 'submit', @@ -49,6 +49,5 @@ export const FormButton: React.FunctionComponent = (props: IFo onClick(event); }; - // @ts-ignore Waiting for https://github.com/Microsoft/TypeScript/issues/28768 to be fixed return ; }; diff --git a/src/components/FormButton/FormButton.types.ts b/src/components/FormButton/FormButton.types.ts index 7b86ae6..f19b49e 100644 --- a/src/components/FormButton/FormButton.types.ts +++ b/src/components/FormButton/FormButton.types.ts @@ -11,25 +11,28 @@ import React from 'react'; */ export interface IFormButtonProps { /** - * Button type + * Type of the button, either submit, reset or button. + * @default submit */ type?: string; /** - * Optional submit args, will be passed to the - * form submit function + * Optional object that will be passed as the second parameter + * to form.onSubmit if the button is of submit type. */ submitArgs?: unknown; /** - * Disabled state of the button + * True, if the button should be always disabled. If set to false + * it will be still disabled if the form context is busy or disabled. + * @default false */ disabled?: boolean; /** - * Component that should be rendered instead of the - * default html button + * Can be used to render any other component other than the standard html button. + * @default button */ component?: React.ReactType; /** - * OnClick callback + * OnClick callback. Will be triggered after the form.onSubmit call. * @param event event */ onClick?(event: React.MouseEvent): void; diff --git a/src/components/FormContext/FormContext.test.ts b/src/components/FormContext/FormContext.test.ts deleted file mode 100644 index b8cad27..0000000 --- a/src/components/FormContext/FormContext.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useContext } from 'react'; - -import { useFormContext } from './FormContext.hooks'; - -jest.mock('react'); - -describe('FormContext', () => { - describe('useFormContext', () => { - it('should return the correct form context', () => { - const fakeContext = { fake: true }; - (useContext).mockReturnValueOnce(fakeContext); - - expect(useFormContext()).toBe(fakeContext); - }); - - it('should throw an error if no form context could be found', () => { - (useContext).mockReturnValueOnce(undefined); - - expect(() => { useFormContext(); }).toThrowError( - '[useFormContext]: Could not find form context. This component must be used inside a
tag.', - ); - }); - }); -}); diff --git a/src/components/FormContext/FormContext.types.ts b/src/components/FormContext/FormContext.types.ts index 60cfb92..febb4ed 100644 --- a/src/components/FormContext/FormContext.types.ts +++ b/src/components/FormContext/FormContext.types.ts @@ -5,49 +5,15 @@ * LICENSE file in the root directory of this source tree. */ import { TSTringFormatter } from '../../utils/stringFormatter'; -import { TBasicFieldValue } from '../withField'; -import { IValidationArgs, IValidationState } from '../withValidation'; - -export type TFormEventListener = ((name: string, event: string, args?: unknown) => void); - -/** - * Interface describing field states - */ -export interface IFieldState { - /** - * Label of the field - */ - label: string; - /** - * True if the field is actually a FieldGroup - */ - isGroup?: boolean; - /** - * Triggers the validation of this field - * @param args Validation args - */ - validate(args?: Partial): Promise; - /** - * Returns the current value of the field - */ - getValue(): TBasicFieldValue; - /** - * Resets the field to its initial state - */ - reset(): void; - /** - * Updates the validation state of the field - * @param state New validation state - */ - updateValidation(state: Partial): void; -} +import { TFormEventListener, IFieldState } from '../../hooks/internal'; +import { TBasicFieldValue } from '../../hooks'; /** * Type describing a collection of field values */ -export type IFieldValues = { +export interface IFieldValues { [prop: string]: TBasicFieldValue | IFieldValues; -}; +} /** * Base interface for the form context @@ -111,6 +77,10 @@ export interface IBaseFormContext { * @param submitArgs Optional submit args */ submit(submitArgs?: unknown): Promise; + /** + * Resets the form + */ + reset(): void; } /** diff --git a/src/components/FormContext/index.ts b/src/components/FormContext/index.ts index 1e05216..80e2fc0 100644 --- a/src/components/FormContext/index.ts +++ b/src/components/FormContext/index.ts @@ -1,3 +1,2 @@ export * from './FormContext'; export * from './FormContext.types'; -export * from './FormContext.hooks'; diff --git a/src/components/FormText/FormText.md b/src/components/FormText/FormText.md new file mode 100644 index 0000000..932905e --- /dev/null +++ b/src/components/FormText/FormText.md @@ -0,0 +1,28 @@ +### Examples +#### FormText +Showcase of the form text + +```jsx +import { Form, FormText } from 'react-ocean-forms'; + +function Example() { + return ( + +

+ +

+ +

+ +

+ + ); +} + + +``` diff --git a/src/components/FormText/FormText.test.tsx b/src/components/FormText/FormText.test.tsx index a396cc6..e24953a 100644 --- a/src/components/FormText/FormText.test.tsx +++ b/src/components/FormText/FormText.test.tsx @@ -4,10 +4,11 @@ import { shallow, ShallowWrapper } from 'enzyme'; import { createMockFormContext } from '../../test-utils/enzymeFormContext'; import { IMessageValues } from '../../utils'; -import { IFormContext, useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; +import { IFormContext } from '../FormContext'; import { FormText } from './FormText'; -jest.mock('../FormContext'); +jest.mock('../../hooks'); describe('', () => { const mockContext: IFormContext = createMockFormContext(); diff --git a/src/components/FormText/FormText.tsx b/src/components/FormText/FormText.tsx index e52676c..0eb8698 100644 --- a/src/components/FormText/FormText.tsx +++ b/src/components/FormText/FormText.tsx @@ -6,21 +6,23 @@ */ import React from 'react'; -import { useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; import { IFormTextProps } from './FormText.types'; /** - * Wrapper component for passing strings to the - * context.stringFormatter method + * Wrapper for text output. It will use the Form.stringFormatter function to + * output the message passed through the props. Best practice for custom input + * component development is to pass every text output through the stringFormatter. + * This enables the user of the form to add the react-ocean-forms-react-intl package + * and get i18n support out of the box. */ -export const FormText: React.SFC = ({ text, values }: IFormTextProps): JSX.Element | null => { +export const FormText: React.FC = ({ text, values }) => { const context = useFormContext(); if (text === '' || text === null) { return null; } return ( {context.stringFormatter(text, values)} - + + + ); +} + + +``` diff --git a/src/components/Input/Input.test.tsx b/src/components/Input/Input.test.tsx index eade4e2..159eecc 100644 --- a/src/components/Input/Input.test.tsx +++ b/src/components/Input/Input.test.tsx @@ -2,10 +2,12 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { IFieldComponentFieldProps, IFieldComponentMeta } from '../withField'; -import { BaseInput } from './Input'; +import { IFieldComponentMeta, IFieldComponentFieldProps, useField } from '../../hooks'; +import { Input } from './Input'; import { IInputProps } from './Input.types'; +jest.mock('../../hooks'); + describe('', () => { interface ISetupArgs { props?: Partial; @@ -42,11 +44,15 @@ describe('', () => { ...fieldOverrides, }; + (useField as jest.Mock).mockReturnValue({ + fieldProps: field, + metaProps: meta, + }); + const wrapper = shallow(( - )); @@ -66,6 +72,11 @@ describe('', () => { expect(wrapper).toMatchSnapshot(); }); + it('should properly pass the type prop', () => { + const wrapper = setup({ props: { type: 'number' }}); + expect(wrapper).toMatchSnapshot(); + }); + describe('Unsupported value types', () => { it('should throw an error if the value is not a string', () => { expect(() => setup({ fieldOverrides: { value: 42 }})).toThrowError(); diff --git a/src/components/Input/Input.tsx b/src/components/Input/Input.tsx index 758ce24..2f643de 100644 --- a/src/components/Input/Input.tsx +++ b/src/components/Input/Input.tsx @@ -7,43 +7,34 @@ import React from 'react'; +import { useField } from '../../hooks'; import { FieldLine } from '../FieldLine'; -import { withField } from '../withField'; import { IInputProps } from './Input.types'; /** - * Component for displaying bootstrap - * form groups with an html input and - * oForm support + * Defines a form line containing a label and an input. Additionally it + * will render validation messages. If the user adds the required validator + * then it will mark the field as required as well. */ -export class BaseInput extends React.Component { - public static displayName: string = 'Input'; - - public static defaultProps = { - type: 'text', - }; - - public render(): JSX.Element { - const { - field, - type, - meta, - } = this.props; - - const fieldValue = field.value; - if (typeof fieldValue !== 'string' && fieldValue !== undefined) { - throw new Error( - 'Incompatible field value supplied for input component ' - + `${field.id}. Only values with type string or undefined are allowed.`, - ); - } - - return ( - - {meta.plaintext ? field.value : } - +export const Input: React.FC = (props) => { + const { + type = 'text', + ...rest + } = props; + + const { fieldProps, metaProps } = useField(rest); + + const fieldValue = fieldProps.value; + if (typeof fieldValue !== 'string' && fieldValue !== undefined) { + throw new Error( + 'Incompatible field value supplied for input component ' + + `${fieldProps.id}. Only values with type string or undefined are allowed.`, ); } -} -export const Input = withField(BaseInput); + return ( + + {metaProps.plaintext ? fieldProps.value : } + + ); +} diff --git a/src/components/Input/Input.types.ts b/src/components/Input/Input.types.ts index 5829516..a63126c 100644 --- a/src/components/Input/Input.types.ts +++ b/src/components/Input/Input.types.ts @@ -4,14 +4,15 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { IFieldComponentProps } from '../withField'; +import { IUseFieldProps } from '../../hooks'; /** * Props for the Input component */ -export interface IInputProps extends IFieldComponentProps { +export interface IInputProps extends IUseFieldProps { /** - * Input type (e.g. text, number, ...) + * HTML5 input type of the input element. + * @default 'text' */ - type: string; + type?: string; } diff --git a/src/components/Input/__snapshots__/Input.test.tsx.snap b/src/components/Input/__snapshots__/Input.test.tsx.snap index e4a3277..eaa7d48 100644 --- a/src/components/Input/__snapshots__/Input.test.tsx.snap +++ b/src/components/Input/__snapshots__/Input.test.tsx.snap @@ -25,11 +25,52 @@ Object { "valid": true, } } - type="text" + name="unitInput" />, } `; +exports[` should properly pass the type prop 1`] = ` +Object { + "wrapper": + + , +} +`; + exports[` should render without crashing 1`] = ` Object { "wrapper": + + + + + + + + + + ); +} + + +``` diff --git a/src/components/ValidationSummary/ValidationSummary.test.tsx b/src/components/ValidationSummary/ValidationSummary.test.tsx index 8bb3760..1036b96 100644 --- a/src/components/ValidationSummary/ValidationSummary.test.tsx +++ b/src/components/ValidationSummary/ValidationSummary.test.tsx @@ -2,47 +2,44 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { mockEvent } from '../../test-utils/enzymeEventUtils'; -import { createMockFormContext } from '../../test-utils/enzymeFormContext'; -import { IFormContext, TFormEventListener } from '../FormContext'; -import { BaseValidationSummary } from './ValidationSummary'; +import { ValidationSummary } from './ValidationSummary'; import { IValidationSummaryProps } from './ValidationSummary.types'; +import { IUseValidationSummaryResult, useValidationSummary, IInvalidField } from './hooks/useValidationSummary'; + +jest.mock('./hooks/useValidationSummary'); describe('', () => { + const summaryId = 'unitSummary'; + interface ISetupArgs { props?: Partial; + validationSummaryResult?: Partial; } interface ISetupResult { wrapper: ShallowWrapper; - formContext: IFormContext; - summaryId: string; - summaryMatcher: string; - listenerCallback: TFormEventListener; } const setup = ({ props, + validationSummaryResult, }: ISetupArgs = {}): ISetupResult => { - let listenerCallback: TFormEventListener = jest.fn(); - const registerCallback = (name: string, state: TFormEventListener): void => { listenerCallback = state; }; - const formContext = createMockFormContext(registerCallback); - const summaryId = 'unitSummary'; + const summaryResult: IUseValidationSummaryResult = { + headerRef: { current: null }, + errorList: [ ], + ...validationSummaryResult + }; + (useValidationSummary as jest.Mock).mockReturnValue(summaryResult); const wrapper = shallow(( - )); return { wrapper, - formContext, - summaryId, - listenerCallback, - summaryMatcher: `#${summaryId}`, }; }; @@ -51,60 +48,40 @@ describe('', () => { expect(wrapper).toMatchSnapshot(); }); - describe('form context registration', () => { - it('should register itself in the form context', () => { - const { formContext, summaryId } = setup(); + it('should call useValidationSummary correctly', () => { + setup(); + expect(useValidationSummary).toHaveBeenLastCalledWith(summaryId, false); - expect(formContext.registerListener).toHaveBeenCalledWith( - summaryId, - expect.any(Function), - ); - }); - - it('should unregister on unmount', () => { - const { wrapper, formContext, summaryId } = setup(); - - wrapper.unmount(); - expect(formContext.unregisterListener).toHaveBeenCalledWith(summaryId); - }); + setup({ props: { disableFocusOnSubmit: true, id: 'foobar' }}); + expect(useValidationSummary).toHaveBeenLastCalledWith('foobar', true); }); - const raiseError = (callback: TFormEventListener): { fieldId: string; fieldLabel: string } => { - const fieldId = 'unitField'; - const fieldLabel = 'Unit field'; - - callback( - fieldId, - 'validation', - { - label: fieldLabel, - valid: false, - error: { - message_id: 'test', - params: { }, - }, - }, - ); - - return { - fieldId, fieldLabel, - }; - }; + const fieldId = 'mock-error'; + const fieldLabel = 'mock-label'; + + function createErrorList(): [string, IInvalidField][] { + return [ + [ + fieldId, + { + id: fieldId, + name: fieldLabel, + error: Error, + linkCallback: jest.fn(), + } + ] + ]; + } describe('basic rendering', () => { it('should not render anything if the form is valid', () => { - const { wrapper, summaryMatcher } = setup(); - expect(wrapper.exists(summaryMatcher)).toBeFalsy(); + const { wrapper } = setup(); + expect(wrapper.children().exists()).toBeFalsy(); }); - it('should not render anything if the validation state of a field is valid', () => { - const { wrapper, summaryMatcher, listenerCallback } = setup(); - - listenerCallback('foo', 'validation', { valid: true }); - expect(wrapper.exists(summaryMatcher)).toBeFalsy(); - - listenerCallback('foo', 'validation', { valid: false, error: null }); - expect(wrapper.exists(summaryMatcher)).toBeFalsy(); + it('should render correctly if there is an error', () => { + const { wrapper } = setup({ props: { title: 'mock-title' }, validationSummaryResult: { errorList: createErrorList() }}); + expect(wrapper).toMatchSnapshot(); }); }); @@ -119,16 +96,11 @@ describe('', () => { it('should be called with the basic summary render if the form is invalid', () => { const mockRender = jest.fn(); - const { listenerCallback } = setup({ props: { render: mockRender }}); - - expect(mockRender).not.toHaveBeenCalled(); - - raiseError(listenerCallback); + setup({ props: { render: mockRender }, validationSummaryResult: { errorList: createErrorList() }}); // We are using the mock calls in order to extract the parameters // that have been used in the last call. The first parameter of the // first call will contain the react element that we need. - // PS: Shut up tslint const typedCalls = mockRender.mock.calls as [[React.ReactElement]]; const wrapper = shallow(typedCalls[0][0]); @@ -137,10 +109,8 @@ describe('', () => { it('should render the result of the render prop', () => { const mockRender = (): JSX.Element => (
mock renderer
); - const { listenerCallback, wrapper } = setup({ props: { render: mockRender }}); - - raiseError(listenerCallback); - expect(wrapper.exists('#mock-renderer')).toBeTruthy(); + const { wrapper } = setup({ props: { render: mockRender }, validationSummaryResult: { errorList: createErrorList() }}); + expect(wrapper).toMatchSnapshot(); }); }); @@ -154,103 +124,22 @@ describe('', () => { it('should be called with informations about the invalid field if the form is invalid', () => { const mockRender = jest.fn(); - const { listenerCallback } = setup({ props: { renderFieldError: mockRender }}); - - expect(mockRender).not.toHaveBeenCalled(); + setup({ props: { renderFieldError: mockRender }, validationSummaryResult: { errorList: createErrorList() }}); - const { fieldId, fieldLabel } = raiseError(listenerCallback); expect(mockRender).toHaveBeenCalledWith( fieldId, fieldLabel, - expect.any(Array), + expect.any(Object), expect.any(Function), ); }); it('should render the result of the render prop', () => { const mockRender = (): JSX.Element => (
mock renderer
); - const { listenerCallback, wrapper } = setup({ props: { renderFieldError: mockRender }}); + const { wrapper } = setup({ props: { renderFieldError: mockRender }, validationSummaryResult: { errorList: createErrorList() }}); - raiseError(listenerCallback); - expect(wrapper.exists('#mock-renderer')).toBeTruthy(); + expect(wrapper).toMatchSnapshot(); }); }); }); - - describe('input focus', () => { - const { wrapper, listenerCallback } = setup(); - const { fieldId } = raiseError(listenerCallback); - - const clickErrorLink = (): void => { - const errorLink = wrapper.find(`a[href="#${fieldId}"]`); - errorLink.simulate('click', mockEvent()); - }; - - it('should focus on the field when you click on the error message', () => { - const inputNode = document.createElement('input'); - inputNode.type = 'text'; - inputNode.id = fieldId; - - document.body.appendChild(inputNode); - const spy = jest.spyOn(inputNode, 'focus'); - - clickErrorLink(); - - expect(spy).toHaveBeenCalled(); - document.body.removeChild(inputNode); - }); - - it('should not crash if the input with the given id could not be found', () => { - expect(clickErrorLink).not.toThrowError(); - }); - }); - - describe('scrollIntoView', () => { - it('should scroll into view when called by the context', () => { - const { wrapper, listenerCallback } = setup(); - - const mockRef = { - current: { - scrollIntoView: jest.fn(), - }, - }; - // @ts-ignore headerRef is private but it's the simplest solution to test this - (wrapper.instance() as BaseValidationSummary).headerRef = mockRef; - - listenerCallback('_form', 'submit-invalid'); - expect(mockRef.current.scrollIntoView).toHaveBeenCalled(); - }); - - it('should not scroll into view when called by the context if disableFocusOnSubmit is set', () => { - const { wrapper, listenerCallback } = setup({ props: { disableFocusOnSubmit: true }}); - - const mockRef = { - current: { - scrollIntoView: jest.fn(), - }, - }; - - // @ts-ignore headerRef is private but it's the simplest solution to test this - (wrapper.instance() as BaseValidationSummary).headerRef = mockRef; - - listenerCallback('_form', 'submit-invalid'); - expect(mockRef.current.scrollIntoView).not.toHaveBeenCalled(); - }); - - it('should not crash if the header was not rendered', () => { - const { listenerCallback } = setup(); - expect(() => { - listenerCallback('_form', 'submit-invalid'); - }).not.toThrowError(); - }); - }); - - describe('other form events', () => { - it('should ignore other form events', () => { - const { listenerCallback } = setup(); - expect(() => { - listenerCallback('_form', 'random-event'); - }).not.toThrowError(); - }); - }); }); diff --git a/src/components/ValidationSummary/ValidationSummary.tsx b/src/components/ValidationSummary/ValidationSummary.tsx index 572d186..1d2f795 100644 --- a/src/components/ValidationSummary/ValidationSummary.tsx +++ b/src/components/ValidationSummary/ValidationSummary.tsx @@ -1,222 +1,69 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - import React from 'react'; -import { toArray } from '../../utils'; -import { IFieldErrorObject } from '../../validators'; -import { withForm } from '../withForm'; -import { IValidationState } from '../withValidation'; import { IValidationSummaryProps } from './ValidationSummary.types'; +import { useValidationSummary } from './hooks/useValidationSummary'; +import { FormText } from '../FormText'; -interface IValidationEventArgs extends IValidationState { - label: string; -} - -interface IInvalidField { - id: string; - name: string; - error: IFieldErrorObject | IFieldErrorObject[]; -} - -interface IFieldContainer { - [s: string]: IValidationEventArgs; -} - -interface IValidationSummaryState { - fields: IFieldContainer; +/** + * Renders a field error + * @param id Unique id + * @param fieldName Name of the field + * @param errors Field errors + * @param linkCallback Callback for the error click + */ +function defaultRenderFieldError(id: string, fieldName: string, errors: React.ReactNode, linkCallback: React.MouseEventHandler): React.ReactNode { + return ( +
  • + + {fieldName} + : + {' '} + {errors} + +
  • + ); } /** - * Component for displaying a summary of all - * validation errors of the form this component - * lives in. + * Displays a clickable list of errors from the current form. When an + * error is clicked, the corresponding input field is focused. */ -export class BaseValidationSummary extends React.Component { - public static displayName: string = 'ValidationSummary'; - - public static defaultProps = { - title: 'ojs_form_validationSummaryHeader', - disableFocusOnSubmit: false, - }; - - private headerRef: React.RefObject; - - constructor(props: IValidationSummaryProps) { - super(props); - - this.headerRef = React.createRef(); - - const { context: { registerListener }, id } = props; - // @ts-ignore TODO: Improve the typings of registerListener to allow custom listener methods - registerListener(id, this.notify); - - this.state = { - fields: {}, - }; - } - - /** - * Renders a field error - * @param id Unique id - * @param fieldName Name of the field - * @param errors Field errors - * @param linkCallback Callback for the error click - */ - private static renderError(id: string, fieldName: string, errors: JSX.Element[], linkCallback: React.MouseEventHandler): JSX.Element { - return ( -
  • - - {fieldName} - : - {' '} - {errors} - -
  • - ); - } - - /** - * Unregisters the field from the form - */ - public componentWillUnmount(): void { - const { context, id } = this.props; - context.unregisterListener(id); - } - - /** - * Gets called when a validation state changes - * @param name Field name - * @param event Event name - * @param state Field state - */ - private notify = (name: string, event: string, state?: IValidationEventArgs): void => { - if (event === 'validation') { - this.setState(oldState => ({ - fields: { - ...oldState.fields, - [name]: state as IValidationEventArgs, - }, - })); - } else if (event === 'submit-invalid') { - this.scrollIntoView(); - } +export const ValidationSummary: React.FC = (props) => { + const { + id, + title = 'ojs_form_validationSummaryHeader', + disableFocusOnSubmit = false, + renderFieldError = defaultRenderFieldError, + render, + } = props; + + const { headerRef, errorList } = useValidationSummary(id, disableFocusOnSubmit); + + const renderedErrors = errorList.map(([, value]) => renderFieldError( + value.id, + value.name, + value.error, + value.linkCallback, + )); + + // Don't render anything if there are no errors + if (renderedErrors.length === 0) { return null; } + + const summary = ( +
    +

    +
      + {renderedErrors} +
    +
    + ); + + // Use an optional render prop if the user + // wants to customize the look and feel of + // the validation summary + if (render !== undefined) { + return render(summary); } - private scrollIntoView = (): void => { - const { disableFocusOnSubmit } = this.props; - if (this.headerRef.current !== null && !disableFocusOnSubmit) { - this.headerRef.current.scrollIntoView({ block: 'start', behavior: 'smooth' }); - } - } - - /** - * Renders a list of all errors - * @param errors Error array - */ - private renderErrors(errors: IInvalidField[]): JSX.Element[] { - const { - context: { - stringFormatter, - }, - } = this.props; - - return errors.map((error: IInvalidField) => { - const errorArray = toArray(error.error); - const fieldErrors = errorArray.map((item) => { - const errorString = stringFormatter(item.message_id, item.params); - - return ( - {errorString} - ); - }); - - // Focuses the invalid field on click on the error message - const linkCallback = (event: React.MouseEvent): void => { - event.preventDefault(); - const input = document.getElementById(error.id); - if (input !== null) { - input.focus(); - } - }; - - const fieldName = stringFormatter(error.name); - - // Use an optional render prop if the user - // wants to customize the output of the error - const { renderFieldError } = this.props; - if (renderFieldError !== undefined) { - return renderFieldError( - error.id, - fieldName, - fieldErrors, - linkCallback, - ); - } - - return BaseValidationSummary.renderError( - error.id, - fieldName, - fieldErrors, - linkCallback, - ); - }); - } - - public render(): JSX.Element | null { - const { - id, - title, - context: { - stringFormatter, - }, - } = this.props; - - const { fields } = this.state; - - // Fetch the current errors out of the form context - const errors: IInvalidField[] = []; - - Object.keys(fields).forEach((name: string) => { - const state = fields[name]; - if (state.valid || state.error === null) { return; } - - errors.push({ - id: name, - name: state.label, - error: state.error, - }); - }); - - // Don't render anything if there are no errors - if (errors.length === 0) { return null; } - - const renderedErrors = this.renderErrors(errors); - const titleString = stringFormatter(title); - - const summary = ( -
    -

    {titleString}

    -
      - {renderedErrors} -
    -
    - ); - - // Use an optional render prop if the user - // wants to customize the look and feel of - // the validation summary - const { render: renderProp } = this.props; - if (renderProp !== undefined) { - return renderProp(summary); - } - - return summary; - } -} - -export const ValidationSummary = withForm(BaseValidationSummary); + return summary; +}; diff --git a/src/components/ValidationSummary/ValidationSummary.types.ts b/src/components/ValidationSummary/ValidationSummary.types.ts index 23930ac..2c1277a 100644 --- a/src/components/ValidationSummary/ValidationSummary.types.ts +++ b/src/components/ValidationSummary/ValidationSummary.types.ts @@ -4,36 +4,39 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -import { IFormContextProps } from '../withForm'; +import React from 'react'; /** * Properties for the ValidationSummary */ -export interface IValidationSummaryProps extends IFormContextProps { +export interface IValidationSummaryProps { /** - * (html) id for the validation summary container div + * Id of this input. Will be used as the unique identifier of the div. + * **Must be unique form wide!** */ id: string; /** - * Optional title text / message id + * Message that will be used as the title, wrapped in a h4. */ - title: string; + title?: string; /** - * If true, then the validation summary won't try to sroll - * itself into view if the user submits an invalid form + * If set to true the validation summary will stop automatically scrolling + * to itself when the user clicks on a submit button and the form is invalid. + * @default false */ - disableFocusOnSubmit: boolean; + disableFocusOnSubmit?: boolean; /** - * Render prop for drawing single field errors + * Optional function that can be used to customize the output of each error. * @param id Id of the failing field * @param fieldName Display name of the failing field - * @param errors Rendered error messages - * @param linkCallback Callback that will focus the failed field + * @param errors Object / Array of objects with the rendered error messages + * @param linkCallback Function that should be called on click, will focus on the input element. */ - renderFieldError?(id: string, fieldName: string, errors: React.ReactNode, linkCallback: React.MouseEventHandler): JSX.Element; + renderFieldError?(id: string, fieldName: string, errors: React.ReactNode, linkCallback: React.MouseEventHandler): React.ReactNode; /** - * Render prop for customizing the validation summary + * Optional function that can be used to customize the ValidationSummary rendering. + * Note: This function will only be called when there are errors to be displayed. * @param children Validation summary content (header + rendered field errors) */ - render?(children: JSX.Element): JSX.Element; + render?(children: React.ReactNode): React.ReactElement | null; } diff --git a/src/components/ValidationSummary/__snapshots__/ValidationSummary.test.tsx.snap b/src/components/ValidationSummary/__snapshots__/ValidationSummary.test.tsx.snap index e5a5919..ee08212 100644 --- a/src/components/ValidationSummary/__snapshots__/ValidationSummary.test.tsx.snap +++ b/src/components/ValidationSummary/__snapshots__/ValidationSummary.test.tsx.snap @@ -1,5 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[` basic rendering should render correctly if there is an error 1`] = ` + +`; + exports[` render props render prop should be called with the basic summary render if the form is invalid 1`] = `
    render props render prop should be called with th

    - Errors +

    `; +exports[` render props render prop should render the result of the render prop 1`] = ` +
    + mock renderer +
    +`; + +exports[` render props renderFieldError prop should render the result of the render prop 1`] = ` +
    +

    + +

    +
      +
      + mock renderer +
      +
    +
    +`; + exports[` should render without crashing 1`] = `""`; diff --git a/src/components/ValidationSummary/hooks/useValidationSummary.test.tsx b/src/components/ValidationSummary/hooks/useValidationSummary.test.tsx new file mode 100644 index 0000000..b2d5d7d --- /dev/null +++ b/src/components/ValidationSummary/hooks/useValidationSummary.test.tsx @@ -0,0 +1,217 @@ +import React from 'react'; +import { renderHook, act } from 'react-hooks-testing-library'; +import { shallow } from 'enzyme'; + +import { useFormContext, useFormEventListener } from '../../../hooks'; +import { TFormEventListener } from '../../../hooks/internal'; + +import { useValidationSummary, IUseValidationSummaryResult, IInvalidField } from './useValidationSummary'; +import { mockEvent } from '../../../test-utils/enzymeEventUtils'; + +jest.mock('../../../hooks'); + +describe('useValidationSummary', () => { + const mockId = 'test-vs'; + + interface ISetupArgs { + disableFocusOnSubmit?: boolean; + } + + interface ISetupResult { + handleEvent: TFormEventListener; + + unmount(): boolean; + rerender(): void; + waitForNextUpdate(): Promise; + result: { current: IUseValidationSummaryResult }; + } + + const setup = ({ + disableFocusOnSubmit = false, + }: Partial = {}): ISetupResult => { + (useFormContext as jest.Mock).mockReturnValue({ + stringFormatter: jest.fn().mockImplementation(str => str), + }); + + let handleEvent: TFormEventListener; + (useFormEventListener as jest.Mock).mockImplementation((name, callback) => { + handleEvent = callback; + }); + + const { result, unmount, rerender, waitForNextUpdate } = renderHook(() => useValidationSummary(mockId, disableFocusOnSubmit)); + + return { + // @ts-ignore + handleEvent, + + unmount, + rerender, + waitForNextUpdate, + result, + }; + }; + + it('should return an empty headerRef and empty errors on initial load', () => { + const { result } = setup(); + expect(result.current).toMatchObject({ + headerRef: expect.any(Object), + errorList: [], + }); + }); + + it('should register an event listener to the form context', () => { + (useFormEventListener as jest.Mock).mockClear(); + + setup(); + expect(useFormEventListener).toHaveBeenCalledWith(mockId, expect.any(Function)); + }); + + describe('Form submit handling (submit-invalid event)', () => { + it('should not crash if there is no header ref', () => { + const { handleEvent } = setup(); + + act(() => { + expect(() => handleEvent('_form', 'submit-invalid')).not.toThrowError(); + }); + }); + + it('should call header scrollIntoView by default', () => { + const scrollIntoViewMock = jest.fn(); + const { handleEvent, result } = setup(); + + act(() => { + result.current.headerRef.current = { scrollIntoView: scrollIntoViewMock } as any; + handleEvent('_form', 'submit-invalid'); + }); + + expect(scrollIntoViewMock).toHaveBeenCalledWith({ block: 'start', behavior: 'smooth' }); + }); + + it('should not call header scrollIntoView if disableFocusOnSubmit is set', () => { + const scrollIntoViewMock = jest.fn(); + const { handleEvent, result } = setup({ disableFocusOnSubmit: true }); + + act(() => { + result.current.headerRef.current = { scrollIntoView: scrollIntoViewMock } as any; + handleEvent('_form', 'submit-invalid'); + }); + + expect(scrollIntoViewMock).not.toHaveBeenCalled(); + }); + }); + + describe('Validation event handling', () => { + function triggerValidationEvent(handleEvent: TFormEventListener, fieldId: string, label: string, error: string | null, valid: boolean = false): void { + act(() => { + handleEvent(fieldId, 'validation', { valid, label, error }); + }); + } + + function checkErrorItem(item: [string, IInvalidField], fieldId: string, label: string): void { + expect(item).toHaveLength(2); + expect(item[0]).toEqual(fieldId); + expect(item[1]).toMatchObject({ + id: fieldId, + name: label, + error: expect.any(Object), + linkCallback: expect.any(Function), + }); + } + + it('should add the error to the errorList', () => { + const mockFieldId = 'mock-field'; + const mockFieldLabel = 'mock-label'; + const mockError = 'oh no'; + const { handleEvent, result } = setup(); + + triggerValidationEvent(handleEvent, mockFieldId, mockFieldLabel, mockError); + + expect(result.current.errorList).toHaveLength(1); + checkErrorItem(result.current.errorList[0], mockFieldId, mockFieldLabel); + }); + + it('should not add valid fields to the errorList', () => { + const { handleEvent, result } = setup(); + act(() => { + handleEvent('valid-field', 'validation', { valid: true, label: 'valid-field', error: null }); + }); + + expect(result.current.errorList).toHaveLength(0); + }); + + it('should handle multiple errors', () => { + const mockFields = [ + { fieldId: 'mock-field-1', label: 'mock-field-1', error: 'mock-error-1' }, + { fieldId: 'mock-field-2', label: 'mock-field-2', error: 'mock-error-2' } + ]; + const { handleEvent, result } = setup(); + + mockFields.forEach(field => triggerValidationEvent(handleEvent, field.fieldId, field.label, field.error)); + + expect(result.current.errorList).toHaveLength(2); + result.current.errorList.forEach((error, index) => checkErrorItem(error, mockFields[index].fieldId, mockFields[index].label)); + }); + + it('should remove fields that are valid again from the error list', () => { + const mockFields = [ + { fieldId: 'mock-field-1', label: 'mock-field-1', error: 'mock-error-1' }, + { fieldId: 'mock-field-2', label: 'mock-field-2', error: 'mock-error-2' } + ]; + const { handleEvent, result } = setup(); + + mockFields.forEach(field => triggerValidationEvent(handleEvent, field.fieldId, field.label, field.error)); + + expect(result.current.errorList).toHaveLength(2); + + triggerValidationEvent(handleEvent, mockFields[0].fieldId, mockFields[0].label, null, true); + + expect(result.current.errorList).toHaveLength(1); + }); + + describe('error details', () => { + const mockFieldId = 'mock-field'; + const mockFieldLabel = 'mock-label'; + const mockError = 'oh no'; + const { handleEvent, result } = setup(); + + triggerValidationEvent(handleEvent, mockFieldId, mockFieldLabel, mockError); + + it('should render a into the error property', () => { + const fieldError = result.current.errorList[0][1].error; + const wrapper = shallow(
    {fieldError}
    ); + + const renderedFieldError = wrapper.find('FieldError'); + expect(renderedFieldError.prop('id')).toEqual(mockFieldId); + expect(renderedFieldError.prop('invalid')).toEqual(true); + expect(renderedFieldError.prop('error')).toEqual(mockError); + }); + + describe('link callback', () => { + const linkCallback = result.current.errorList[0][1].linkCallback; + it('should not crash if no element with the given id could be found', () => { + expect(() => { + linkCallback(mockEvent() as any); + }).not.toThrowError(); + }); + + it('should prevent the default action of the event', () => { + const preventDefault = jest.fn(); + const event = mockEvent({ preventDefault }); + linkCallback(event as any); + expect(preventDefault).toHaveBeenCalled(); + }); + + it('the linkCallback should focus the field', () => { + const mockInput = { focus: jest.fn() }; + const mockedGetElementById = jest.spyOn(document, 'getElementById').mockReturnValue(mockInput as any); + + linkCallback(mockEvent() as any); + expect(mockedGetElementById).toHaveBeenCalledWith(mockFieldId); + expect(mockInput.focus).toHaveBeenCalled(); + + mockedGetElementById.mockRestore(); + }); + }); + }); + }); +}); diff --git a/src/components/ValidationSummary/hooks/useValidationSummary.tsx b/src/components/ValidationSummary/hooks/useValidationSummary.tsx new file mode 100644 index 0000000..c3dc90d --- /dev/null +++ b/src/components/ValidationSummary/hooks/useValidationSummary.tsx @@ -0,0 +1,77 @@ +import React, { useRef, useCallback, useState } from 'react'; + +import { useFormContext, IValidationState, useFormEventListener } from '../../../hooks'; +import { FieldError } from '../../FieldError'; + +interface IValidationEventArgs extends IValidationState { + label: string; +} + +export interface IInvalidField { + id: string; + name: string; + error: React.ReactNode; + linkCallback(event: React.MouseEvent): void; +} + +export interface IUseValidationSummaryResult { + headerRef: React.MutableRefObject; + errorList: [string, IInvalidField][]; +} + +export function useValidationSummary(id: string, disableFocusOnSubmit: boolean): IUseValidationSummaryResult { + const headerRef = useRef(null); + const [errorList, setErrorList] = useState<[string, IInvalidField][]>([]); + const { stringFormatter } = useFormContext(); + + const handleEvent = useCallback((name: string, event: string, args?: unknown): void => { + if (event === 'validation') { + const validationEventArgs = args as IValidationEventArgs; + + // If the field has been reported as valid delete it from our error map + if (validationEventArgs.valid || validationEventArgs.error === null) { + setErrorList((oldList) => oldList.filter(([oldName]) => name !== oldName)); + return; + } + + // Create the individual error renderers + const renderedErrors = ( + + ); + + const linkCallback = (event: React.MouseEvent): void => { + event.preventDefault(); + const input = document.getElementById(name); + if (input !== null) { + input.focus(); + } + }; + + // Create the meta information for the error map + setErrorList((oldList) => { + return [ + ...oldList.filter(([oldName]) => name !== oldName), + [ + name, + { + id: name, + name: stringFormatter(validationEventArgs.label), + error: renderedErrors, + linkCallback, + }, + ] + ]; + }); + } else if (event === 'submit-invalid' && headerRef.current !== null && !disableFocusOnSubmit) { + headerRef.current.scrollIntoView({ block: 'start', behavior: 'smooth' }); + } + }, [disableFocusOnSubmit, stringFormatter]); + + useFormEventListener(id, handleEvent); + + return { headerRef, errorList }; +} diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..2871a70 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +export * from './FieldError'; +export * from './FieldGroup'; +export * from './FieldLine'; +export * from './Form'; +export * from './FormButton'; +export * from './FormContext'; +export * from './FormText'; +export * from './Input'; +export * from './ValidationSummary'; +export * from './withField'; +export * from './withForm'; diff --git a/src/components/withField/Field/Field.tsx b/src/components/withField/Field/Field.tsx deleted file mode 100644 index 6021cb5..0000000 --- a/src/components/withField/Field/Field.tsx +++ /dev/null @@ -1,493 +0,0 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; - -import { getDeepValue } from '../../../utils'; -import { IFieldValues, IFormContext } from '../../FormContext'; -import { IValidationArgs, IValidationState, withValidation } from '../../withValidation'; -import { - IFieldChangedEvent, - IFieldComponentFieldProps, - IFieldComponentMeta, - IFieldProps, - IValueMeta, - TBasicFieldValue, -} from './Field.types'; - -interface IContextMeta extends IValueMeta { - defaultValue?: TBasicFieldValue; - externalValue?: TBasicFieldValue; -} - -interface ILocalOverridenMeta { - disabled?: boolean; - plaintext?: boolean; -} - -interface IFieldState { - touched: boolean; - dirty: boolean; - value: TBasicFieldValue; - contextMeta: IContextMeta; - propsMeta: ILocalOverridenMeta; -} - -interface IMetaChanges { - defaultValue: boolean; - externalValue: boolean; - meta: boolean; -} - -/** - * Wrapper for input fields managed by - * the form component - */ -export class BaseField extends React.Component { - public static displayName: string = 'Field'; - - constructor(props: IFieldProps) { - super(props); - - const { - fullName, - label, - context, - disabled, - plaintext, - validation: { - update: updateValidation, - }, - } = props; - - this.checkFormContext(); - - context.registerField( - fullName, - { - label, - - updateValidation, - validate: this.validate, - reset: this.reset, - getValue: this.getValue, - }, - ); - - this.state = { - touched: false, - dirty: false, - value: BaseField.callGetDisplayValue(props, ''), - contextMeta: { - disabled: false, - plaintext: false, - }, - propsMeta: { - disabled, - plaintext, - }, - }; - } - - /** - * Changes the field value based on changes in Form.defaultValues, - * Form.values, Field.defaultValue or Field.value. The Field props - * always override the Form props. Changes in defaultValue will only - * update the field value, if there is no values or value prop present - * and if the field hasn't been touched. - */ - public static getDerivedStateFromProps(nextProps: IFieldProps, prevState: IFieldState): IFieldState | null { - const { - disabled: overrideDisabled, - plaintext: overridePlaintext, - context: { - disabled: newDisabled, - plaintext: newPlaintext, - }, - } = nextProps; - - // Get the default value from Field.defaultValue or Form.defaultValues - const defaultValue = BaseField.getDefaultValue(nextProps); - // Get the "external" value from Field.value or Form.values - const externalValue = BaseField.getExternalValue(nextProps); - - // Get changes in default or external value and meta information (plaintext, disabled) - // Meta information must trigger a state change as well, because they are passed to - // Field.getDisplayValue and could result in a different return value of that function - const changes = BaseField.getPropChanges(nextProps, prevState, defaultValue, externalValue); - // Get the new value based on the information above, or undefined if we don't need any - // value changes. - const propValue = BaseField.getPropValue(defaultValue, externalValue, changes); - - // Update the Field state if needed, also remember the current prop values so we can - // detect changes in the next call of getDerivedStateFromProps. - if (propValue !== undefined) { - return ({ - value: BaseField.callGetDisplayValue(nextProps, propValue), - touched: false, - dirty: false, - contextMeta: { - defaultValue, - externalValue, - disabled: newDisabled, - plaintext: newPlaintext, - }, - propsMeta: { - disabled: overrideDisabled, - plaintext: overridePlaintext, - }, - }); - } - - return null; - } - - /** - * Correctly calls the getDisplayValue callback - * @param props Field props - * @param value Field value - */ - private static callGetDisplayValue(props: IFieldProps, value: TBasicFieldValue | undefined): TBasicFieldValue { - const { - context, - disabled, - plaintext, - getDisplayValue = (val: TBasicFieldValue): TBasicFieldValue => val, - } = props; - - return getDisplayValue( - value === undefined ? '' : value, - BaseField.getValueMeta(context, disabled, plaintext), - ); - } - - /** - * Calculates the new state value based on the parameters - it will return - * the externalValue if it exists and has changes, or the default value - * otherwise. If no state update is needed it will return undefined. Meta - * changes will trigger a state change as well. - * @param defaultValue Default value - * @param externalValue External value - * @param changes Changes object - */ - private static getPropValue( - defaultValue: TBasicFieldValue | undefined, externalValue: TBasicFieldValue | undefined, changes: IMetaChanges, - ): TBasicFieldValue | undefined { - const hasExternalValue = externalValue !== undefined; - - if (hasExternalValue && (changes.externalValue || changes.meta)) { - return externalValue; - } - - if (!hasExternalValue && (changes.defaultValue || changes.meta)) { - return defaultValue; - } - - return undefined; - } - - /** - * Detects changes in the defaultValue, the externalValue and in - * context.disabled or context.plaintext (merged as meta prop) - * @param props Field props - * @param state Field state - * @param defaultValue Default value - * @param externalValue External value - */ - private static getPropChanges( - props: IFieldProps, state: IFieldState, defaultValue: TBasicFieldValue | undefined, externalValue: TBasicFieldValue | undefined, - ): IMetaChanges { - const { - disabled: newOverrideDisabled, - plaintext: newOverridePlaintext, - context: { - disabled: newDisabled, - plaintext: newPlaintext, - }, - } = props; - - const { - contextMeta: { - defaultValue: oldDefaultValue, - externalValue: oldExternalValue, - disabled: oldDisabled, - plaintext: oldPlaintext, - }, - propsMeta: { - disabled: oldOverrideDisabled, - plaintext: oldOverridePlaintext, - }, - touched, - } = state; - - return { - defaultValue: !touched && defaultValue !== oldDefaultValue, - externalValue: externalValue !== oldExternalValue, - meta: newDisabled !== oldDisabled || newPlaintext !== oldPlaintext - || newOverrideDisabled !== oldOverrideDisabled || newOverridePlaintext !== oldOverridePlaintext, - }; - } - - /** - * Returns the local value if existing, otherwise tries to - * extract the correct context value based on the fullName. - * @param localValue Local value - * @param contextValue Context value - * @param fullName Field.fullName - */ - private static getLocalOverridenValue( - localValue: TBasicFieldValue | undefined, contextValue: Partial | undefined, fullName: string, - ): TBasicFieldValue | undefined { - return localValue === undefined ? getDeepValue(fullName, contextValue) : localValue; - } - - /** - * Returns either the Field.value or the correct value from - * Form.values - * @param props Field props - */ - private static getExternalValue(props: IFieldProps): TBasicFieldValue | undefined { - const { fullName, context: { values }, value } = props; - - return BaseField.getLocalOverridenValue(value, values, fullName); - } - - /** - * Returns either the Field.defaultValue or the correct default value - * from Form.defaultValues - * @param props Field props - */ - private static getDefaultValue(props: IFieldProps): TBasicFieldValue | undefined { - const { fullName, context: { defaultValues }, defaultValue } = props; - - return BaseField.getLocalOverridenValue(defaultValue, defaultValues, fullName); - } - - /** - * Returns a meta object for the value lifecycle hooks - * @param context Form context - * @param overrideDisabled Disabled prop from field - * @param overridePlaintext Plaintext prop from field - */ - private static getValueMeta(context: IFormContext, overrideDisabled?: boolean, overridePlaintext?: boolean): IValueMeta { - return { - disabled: overrideDisabled === undefined ? context.disabled : overrideDisabled, - plaintext: overridePlaintext === undefined ? context.plaintext : overridePlaintext, - }; - } - - /** - * Unregisters the field from the form - */ - public componentWillUnmount(): void { - const { context, fullName } = this.props; - context.unregisterField(fullName); - } - - /** - * Returns the current field value - */ - private getValue = (): TBasicFieldValue => { - const { value } = this.state; - - return this.getSubmitValue(value); - } - - private getSubmitValue = (value: TBasicFieldValue): TBasicFieldValue => { - const { - context, - disabled, - plaintext, - getSubmitValue = (val: TBasicFieldValue): TBasicFieldValue => val, - } = this.props; - - return getSubmitValue( - value, - BaseField.getValueMeta(context, disabled, plaintext), - ); - } - - /** - * Returns the correct asyncValidateOnChange setting, - * where the field setting takes priorty over the - * form setting - */ - private getAsyncValidateOnChangeSetting(): boolean { - const { - asyncValidateOnChange: propChange, - context: { asyncValidateOnChange: contextChange }, - } = this.props; - - return propChange === undefined ? contextChange : propChange; - } - - /** - * Checks if the Field is inside a valid form context - * and throws an user friendly error if not - */ - private checkFormContext(): void { - const { context, fullName } = this.props; - if (context === undefined || typeof context.registerField !== 'function') { - throw new Error( - `Could not find a form context for field "${fullName}". ` - + 'Fields can only be used inside a Form tag.', - ); - } - } - - /** - * Resets the field to its default state - */ - private reset = (): void => { - const { contextMeta: { externalValue, defaultValue } } = this.state; - const { validation, onChange } = this.props; - - const value = BaseField.callGetDisplayValue( - this.props, - externalValue === undefined ? defaultValue : externalValue, - ); - - this.setState({ - value, - touched: false, - dirty: false, - }); - - validation.reset(); - if (onChange !== undefined) { onChange(value); } - } - - /** - * Validates the field - * @param args Validation arguments - */ - private validate = async (args?: Partial): Promise => { - const { value } = this.state; - const { validation: { validate } } = this.props; - - return validate( - this.getSubmitValue(value), - args, - ); - } - - /** - * Handles the change event of an input field - - * triggers any validation if needed and updates - * the field state accordingly. - * @param event Event object - */ - private handleFieldChanged = (event: IFieldChangedEvent): void => { - const { value } = event.target; - const { - fullName, - context, - validation: { validate }, - onChange, - } = this.props; - - this.setState({ - value, - dirty: true, - touched: true, - }); - - const asyncValidateOnChange = this.getAsyncValidateOnChangeSetting(); - const submitValue = this.getSubmitValue(value); - - void validate( - submitValue, - { checkAsync: asyncValidateOnChange }, - ); - - context.notifyFieldEvent(fullName, 'change', submitValue); - if (onChange !== undefined) { onChange(submitValue); } - } - - /** - * Handles the blur event of an input field - - * triggers any validation if needed and updates - * the field state accordingly. - */ - private handleFieldBlurred = (): void => { - const { - fullName, - validation: { validate }, - context, - onBlur, - } = this.props; - const { value, dirty } = this.state; - - const asyncValidateOnChange = this.getAsyncValidateOnChangeSetting(); - const submitValue = this.getSubmitValue(value); - - if (dirty && !asyncValidateOnChange) { void validate(submitValue); } - context.notifyFieldEvent(fullName, 'blur'); - if (onBlur !== undefined) { onBlur(); } - } - - /** - * Creates the properties that can be directly - * mapped to the input component (e.g. html input) - */ - private createFieldProps(): IFieldComponentFieldProps { - const { disabled: disabledOverride, context: { disabled }, fullName } = this.props; - const { value } = this.state; - - return { - value, - disabled: disabledOverride === undefined ? disabled : disabledOverride, - id: fullName, - name: fullName, - onChange: this.handleFieldChanged, - onBlur: this.handleFieldBlurred, - }; - } - - /** - * Creates the meta properties with meta information - * used by the input component - */ - private createMetaProps(): IFieldComponentMeta { - const { - plaintext: plaintextOverride, - context: { - stringFormatter, - plaintext, - }, - validation: { - valid, - error, - isValidating, - isRequired, - }, - } = this.props; - - const { touched } = this.state; - - return { - valid, - error, - isValidating, - isRequired, - touched, - stringFormatter, - plaintext: plaintextOverride === undefined ? plaintext : plaintextOverride, - }; - } - - public render(): JSX.Element { - const { render } = this.props; - - return render( - this.createFieldProps(), - this.createMetaProps(), - ); - } -} - -export const Field = withValidation(BaseField); diff --git a/src/components/withField/Field/__snapshots__/Field.test.tsx.snap b/src/components/withField/Field/__snapshots__/Field.test.tsx.snap deleted file mode 100644 index 8a065c1..0000000 --- a/src/components/withField/Field/__snapshots__/Field.test.tsx.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[` Render should render without crashing 1`] = ` -
    -`; diff --git a/src/components/withField/Field/index.ts b/src/components/withField/Field/index.ts deleted file mode 100644 index 92bc4cd..0000000 --- a/src/components/withField/Field/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './Field'; -export * from './Field.types'; diff --git a/src/components/withField/__snapshots__/withField.test.tsx.snap b/src/components/withField/__snapshots__/withField.test.tsx.snap index 46df174..d6e5069 100644 --- a/src/components/withField/__snapshots__/withField.test.tsx.snap +++ b/src/components/withField/__snapshots__/withField.test.tsx.snap @@ -1,15 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`withField render prop should render without error 1`] = ` -
    -`; - exports[`withField should render without error 1`] = ` - `; diff --git a/src/components/withField/index.ts b/src/components/withField/index.ts index 5f08fda..e1857de 100644 --- a/src/components/withField/index.ts +++ b/src/components/withField/index.ts @@ -1,2 +1,2 @@ export * from './withField'; -export * from './Field/Field.types'; +export * from './withField.types'; diff --git a/src/components/withField/withField.test.tsx b/src/components/withField/withField.test.tsx index 5d8d3df..26b45d0 100644 --- a/src/components/withField/withField.test.tsx +++ b/src/components/withField/withField.test.tsx @@ -2,8 +2,11 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; -import { IFieldComponentFieldProps, IFieldComponentMeta, IFieldComponentProps } from './Field/Field.types'; +import { useField } from '../../hooks'; import { withField } from './withField'; +import { IFieldComponentProps } from './withField.types'; + +jest.mock('../../hooks'); describe('withField', () => { interface ISetupArgs { @@ -12,12 +15,31 @@ describe('withField', () => { interface ISetupResult { wrapper: ShallowWrapper; - renderProp(field: IFieldComponentFieldProps, meta: IFieldComponentMeta): JSX.Element; } const setup = ({ props, }: Partial = {}): ISetupResult => { + (useField as jest.Mock).mockReturnValue({ + fieldProps: { + disabled: false, + id: 'mock-item', + name: 'mock-item', + value: '', + onChange: jest.fn(), + onBlur: jest.fn(), + }, + metaProps: { + error: null, + isValidating: false, + isRequired: false, + plaintext: false, + stringFormatter: jest.fn(), + touched: false, + valid: true, + }, + }); + const TestComponent = (): JSX.Element => (
    ); const WrappedComponent = withField(TestComponent); @@ -29,11 +51,8 @@ describe('withField', () => { /> )); - const renderProp = wrapper.prop('render') as ((field: IFieldComponentFieldProps, meta: IFieldComponentMeta) => JSX.Element); - return { wrapper, - renderProp, }; }; @@ -41,32 +60,4 @@ describe('withField', () => { const { wrapper } = setup(); expect(wrapper).toMatchSnapshot(); }); - - describe('render prop', () => { - it('should render without error', () => { - const { renderProp } = setup(); - const wrapper = shallow( - renderProp( - { - disabled: false, - id: 'mock-item', - name: 'mock-item', - value: '', - onChange: jest.fn(), - onBlur: jest.fn(), - }, - { - error: null, - isValidating: false, - isRequired: false, - plaintext: false, - stringFormatter: jest.fn(), - touched: false, - valid: true, - }, - ), - ); - expect(wrapper).toMatchSnapshot(); - }); - }); }); diff --git a/src/components/withField/withField.tsx b/src/components/withField/withField.tsx index bc38dd3..b7b0c01 100644 --- a/src/components/withField/withField.tsx +++ b/src/components/withField/withField.tsx @@ -7,14 +7,17 @@ import React from 'react'; import { getDisplayName, PropsOf, Subtract } from '../../utils'; -import { IValidatedComponentProps } from '../withValidation'; -import { Field, IBaseFieldProps, IFieldComponentFieldProps, IFieldComponentMeta, IFieldComponentProps } from './Field'; +import { useField, IBaseFieldProps, IUseValidationArgs } from '../../hooks'; +import { IFieldComponentProps } from './withField.types'; + +export type IValidatedComponentProps = IUseValidationArgs; type WrappedValidatedComponentProps = - Subtract>, IFieldComponentProps> & IBaseFieldProps & IValidatedComponentProps; + Subtract>, IFieldComponentProps> & IBaseFieldProps & IUseValidationArgs; /** * Higher order component for validation + * @deprecated Deprecated in favour of `useField` hook */ export const withField = , TProps extends IFieldComponentProps = PropsOf> (component: TComp): React.ComponentType> => { @@ -23,20 +26,15 @@ export const withField = , TProps exte type IWrappedProps = WrappedValidatedComponentProps; - const validatedComponent: React.SFC = (props: IWrappedProps): JSX.Element => { - const renderComponent = (field: IFieldComponentFieldProps, meta: IFieldComponentMeta): JSX.Element => { - // @ts-ignore - return ; - }; + const ValidatedComponent: React.FC = (props) => { + const { fieldProps, metaProps } = useField(props); return ( - + // @ts-ignore + ); }; - validatedComponent.displayName = `withField(${getDisplayName(component)})`; + ValidatedComponent.displayName = `withField(${getDisplayName(component)})`; - return validatedComponent; + return ValidatedComponent; }; diff --git a/src/components/withField/withField.types.ts b/src/components/withField/withField.types.ts new file mode 100644 index 0000000..c84da72 --- /dev/null +++ b/src/components/withField/withField.types.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2018-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { IFieldComponentFieldProps, IFieldComponentMeta } from '../../hooks'; + +/** + * Props for the input component of a Field + */ +export interface IFieldComponentProps { + /** + * Props for the actual html input, designed + * to be passed as-is + */ + field: IFieldComponentFieldProps; + /** + * Meta informations about the field state + */ + meta: IFieldComponentMeta; + /** + * Label (string or message id) + */ + label: string; +} diff --git a/src/components/withForm/__snapshots__/withForm.test.tsx.snap b/src/components/withForm/__snapshots__/withForm.test.tsx.snap index ca52852..0d795d9 100644 --- a/src/components/withForm/__snapshots__/withForm.test.tsx.snap +++ b/src/components/withForm/__snapshots__/withForm.test.tsx.snap @@ -22,6 +22,7 @@ exports[`withForm should render without error 1`] = ` "plaintext": false, "registerField": [MockFunction], "registerListener": [MockFunction], + "reset": [MockFunction], "stringFormatter": [MockFunction], "submit": [MockFunction], "unregisterField": [MockFunction], diff --git a/src/components/withForm/withForm.test.tsx b/src/components/withForm/withForm.test.tsx index 4fd4966..49d348c 100644 --- a/src/components/withForm/withForm.test.tsx +++ b/src/components/withForm/withForm.test.tsx @@ -3,12 +3,12 @@ import React from 'react'; import { shallow, ShallowWrapper } from 'enzyme'; import { createMockFormContext } from '../../test-utils/enzymeFormContext'; -import { useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; import { withForm } from './withForm'; import { IFormContextProps } from './withForm.types'; -jest.mock('../FormContext'); +jest.mock('../../hooks'); describe('withForm', () => { const formContext = createMockFormContext(); diff --git a/src/components/withForm/withForm.tsx b/src/components/withForm/withForm.tsx index 2058588..6485d9e 100644 --- a/src/components/withForm/withForm.tsx +++ b/src/components/withForm/withForm.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { getDisplayName, PropsOf, Subtract } from '../../utils'; -import { useFormContext } from '../FormContext'; +import { useFormContext } from '../../hooks'; import { IFormContextProps } from './withForm.types'; /** @@ -35,7 +35,7 @@ React.ComponentType> => { * Component that injects the form context prop * to the wrapped component */ - const FormComponent: React.SFC> = (props: FormComponentProps): JSX.Element => { + const FormComponent: React.FC> = (props) => { const context = useFormContext(); // @ts-ignore diff --git a/src/components/withValidation/ValidationWrapper/ValidationWrapper.tsx b/src/components/withValidation/ValidationWrapper/ValidationWrapper.tsx deleted file mode 100644 index 5ce34be..0000000 --- a/src/components/withValidation/ValidationWrapper/ValidationWrapper.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import React from 'react'; - -import { parseValidationError } from '../../../utils'; -import { isDefaultValidator, isIFieldErrorObject } from '../../../validators'; -import { TBasicFieldValue } from '../../withField'; -import { withForm } from '../../withForm'; -import { IValidationArgs, IValidationState, IValidationWrapperProps } from '../withValidation.types'; - -/** - * Component that handles validation of the - * wrapped component. - */ -export class BaseValidationWrapper extends React.Component { - public static displayName: string = 'ValidationWrapper'; - - private unmounted: boolean = false; - - private asyncTimeout?: number; - - private get fullName(): string { - const { name, context } = this.props; - - return context.fieldPrefix !== null - ? context.fieldPrefix.concat('.', name) - : name; - } - - constructor(props: IValidationWrapperProps) { - super(props); - - this.state = this.createInitialValidationState(); - } - - /** - * Unregisters the field from the form - */ - public componentWillUnmount(): void { - this.clearValidationTimeout(); - this.unmounted = true; - } - - /** - * Returns the current async timeout - */ - public getAsyncTimeout(): number | undefined { - return this.asyncTimeout; - } - - /** - * Creates the initial / default validation - * state of a validated component - */ - private createInitialValidationState = (): IValidationState => { - return { - valid: true, - error: null, - isValidating: false, - isRequired: this.checkIsRequired(), - }; - } - - /** - * Returns true if this component contains - * a required field validator - */ - private checkIsRequired = (): boolean => { - const { validators } = this.props; - - return Array.isArray(validators) && validators.some(isDefaultValidator); - } - - /** - * Clears the validation timeout if currently - * running - */ - private clearValidationTimeout(): void { - if (this.asyncTimeout !== undefined) { - clearTimeout(this.asyncTimeout); - this.asyncTimeout = undefined; - } - } - - /** - * Resets the validation state to the default - */ - private reset = (): void => { - this.clearValidationTimeout(); - - this.updateAndNotify(this.createInitialValidationState()); - } - - /** - * Updates the validation state - * @param state New state - */ - private updateValidationState = (state: Partial): void => { - const oldState = this.state; - const newState = { - ...oldState, - ...state, - }; - - this.updateAndNotify(newState); - } - - /** - * Triggers the validation of a field. - * @param value Value of the field - * @param options Validation params @see checkAsync @see immediateAsync - * @param checkAsync True if the async validators should be triggered as well - * @param immediateAsync True if the async validators should fire immediately - */ - private validate = async ( - value: TBasicFieldValue, - { - checkAsync = true, - immediateAsync = false, - }: Partial = {}, - ): Promise => { - const { - validators, - asyncValidators, - context: formContext, - } = this.props; - - const validationState: IValidationState = { - valid: true, - error: null, - isValidating: false, - isRequired: this.checkIsRequired(), - }; - - // Clear the old timeout so we only run the - // async validators after the waiting period - // when the value didn't change in the meantime - this.clearValidationTimeout(); - - // No validators - nothing to do here - if (!Array.isArray(validators) && !Array.isArray(asyncValidators)) { - this.setState(validationState); - - return validationState; - } - - // Synchronous validators - if (Array.isArray(validators)) { - validationState.valid = validators.every((validator) => { - const result = validator(value, formContext); - const parsedResult = parseValidationError(this.fullName, result); - - if (isIFieldErrorObject(parsedResult)) { - validationState.error = parsedResult; - - return false; - } - - return true; - }); - } - - // Ignore async validation if sync validation is already false - if (validationState.valid === false) { - this.updateAndNotify(validationState); - - return validationState; - } - - // Only run async validation if needed - if (!checkAsync || !Array.isArray(asyncValidators)) { - this.updateAndNotify(validationState); - - return validationState; - } - - // Asynchronous validators - const performAsyncValidation = async (): Promise => { - const validatorFunctions = asyncValidators.map(async validator => validator( - value, - formContext, - )); - - const errors = await Promise.all(validatorFunctions); - const parsedErrors = errors.map(error => parseValidationError(this.fullName, error)); - - validationState.error = parsedErrors.filter(isIFieldErrorObject); - validationState.valid = validationState.error.length === 0; - - if (validationState.error.length === 0) { validationState.error = null; } - - validationState.isValidating = false; - this.asyncTimeout = undefined; - - this.updateAndNotify(validationState); - - return validationState; - }; - - validationState.isValidating = true; - - if (immediateAsync === true) { - this.updateAndNotify(validationState); - - return performAsyncValidation(); - } - - // Get the correct wait setting - const { asyncValidationWait: propAsyncValidationWait } = this.props; - const asyncValidationWait = propAsyncValidationWait === undefined - ? formContext.asyncValidationWait - : propAsyncValidationWait; - - this.asyncTimeout = window.setTimeout(performAsyncValidation, asyncValidationWait); - - this.updateAndNotify(validationState); - - return validationState; - } - - /** - * Updates the validation state and notifies the form - * @param newState New validation state - */ - private updateAndNotify(newState: IValidationState): void { - const { context } = this.props; - - // Don't do anything if the component has already been - // unmounted. This can happen when the validated Field - // is already removed while there are async validators - // running in the background. - if (this.unmounted) { return; } - - this.setState(newState); - context.notifyFieldEvent(this.fullName, 'validation', newState); - } - - public render(): JSX.Element { - const { - isValidating, - isRequired, - valid, - error, - } = this.state; - - const { context } = this.props; - - const validation = { - isValidating, - isRequired, - valid, - error, - - validate: this.validate, - reset: this.reset, - update: this.updateValidationState, - }; - - return this.props.render( - this.fullName, - validation, - context, - ); - } -} - -export const ValidationWrapper = withForm(BaseValidationWrapper); diff --git a/src/components/withValidation/ValidationWrapper/__snapshots__/ValidationWrapper.test.tsx.snap b/src/components/withValidation/ValidationWrapper/__snapshots__/ValidationWrapper.test.tsx.snap deleted file mode 100644 index 01e8916..0000000 --- a/src/components/withValidation/ValidationWrapper/__snapshots__/ValidationWrapper.test.tsx.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`withValidation should render without error 1`] = ` -
    -`; diff --git a/src/components/withValidation/ValidationWrapper/index.ts b/src/components/withValidation/ValidationWrapper/index.ts deleted file mode 100644 index 5f16cf2..0000000 --- a/src/components/withValidation/ValidationWrapper/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './ValidationWrapper'; diff --git a/src/components/withValidation/__snapshots__/withValidation.test.tsx.snap b/src/components/withValidation/__snapshots__/withValidation.test.tsx.snap deleted file mode 100644 index 9cd1a4b..0000000 --- a/src/components/withValidation/__snapshots__/withValidation.test.tsx.snap +++ /dev/null @@ -1,14 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`withValidation render prop should render without error 1`] = ` -
    -`; - -exports[`withValidation should render without error 1`] = ` - -`; diff --git a/src/components/withValidation/index.ts b/src/components/withValidation/index.ts deleted file mode 100644 index 4fcee7e..0000000 --- a/src/components/withValidation/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './withValidation'; -export * from './withValidation.types'; diff --git a/src/components/withValidation/withValidation.test.tsx b/src/components/withValidation/withValidation.test.tsx deleted file mode 100644 index 8f8d78a..0000000 --- a/src/components/withValidation/withValidation.test.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; - -import { shallow, ShallowWrapper } from 'enzyme'; - -import { createMockFormContext, createMockValidation } from '../../test-utils/enzymeFormContext'; -import { IFormContext } from '../FormContext'; -import { withValidation } from './withValidation'; -import { IValidationProp, IValidationProps } from './withValidation.types'; - -describe('withValidation', () => { - interface ISetupArgs { - props?: Partial; - } - - interface ISetupResult { - wrapper: ShallowWrapper; - renderProp(fullName: string, validation: IValidationProp, context: IFormContext): JSX.Element; - } - - const setup = ({ - props, - }: Partial = {}): ISetupResult => { - const TestComponent = (): JSX.Element => (
    ); - const WrappedComponent = withValidation(TestComponent); - - const wrapper = shallow(( - - )); - - const renderProp = wrapper.prop('render') as ((fullName: string, validation: IValidationProp, context: IFormContext) => JSX.Element); - - return { - wrapper, - renderProp, - }; - }; - - it('should render without error', () => { - const { wrapper } = setup(); - expect(wrapper).toMatchSnapshot(); - }); - - describe('render prop', () => { - it('should render without error', () => { - const { renderProp } = setup(); - const wrapper = shallow( - renderProp( - 'fullName', - createMockValidation(), - createMockFormContext(), - ), - ); - expect(wrapper).toMatchSnapshot(); - }); - }); -}); diff --git a/src/components/withValidation/withValidation.tsx b/src/components/withValidation/withValidation.tsx deleted file mode 100644 index ab0fbef..0000000 --- a/src/components/withValidation/withValidation.tsx +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import React from 'react'; - -import { getDisplayName, PropsOf, Subtract } from '../../utils'; -import { IFormContext } from '../FormContext'; -import { ValidationWrapper } from './ValidationWrapper'; -import { IValidatedComponentProps, IValidationProp, IValidationProps } from './withValidation.types'; - -type WrappedValidatedComponentProps = - Subtract>, IValidationProps> & IValidatedComponentProps; - -/** - * Higher order component for validation - */ -export const withValidation = , TProps extends IValidationProps = PropsOf> -(component: TComp): React.ComponentType> => { - - const CastedComponent = component as React.ComponentType; - - type IWrappedProps = WrappedValidatedComponentProps; - - const validatedComponent: React.SFC = (props: IWrappedProps): JSX.Element => { - const renderComponent = (fullName: string, validation: IValidationProp, context: IFormContext): JSX.Element => { - // @ts-ignore - return ; - }; - - return ( - - ); - }; - validatedComponent.displayName = `ValidatedComponent(${getDisplayName(component)})`; - - return validatedComponent; -}; diff --git a/src/components/withValidation/withValidation.types.ts b/src/components/withValidation/withValidation.types.ts deleted file mode 100644 index bb657c8..0000000 --- a/src/components/withValidation/withValidation.types.ts +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -import { TAsyncValidator, TFieldErrors, TValidator } from '../../validators'; -import { IFormContext } from '../FormContext'; -import { TBasicFieldValue } from '../withField'; -import { IFormContextProps } from '../withForm'; - -/** - * Properties of a component that is wrapped - * by withValidation - */ -export interface IValidatedComponentProps { - /** - * Field name - */ - name: string; - /** - * Synchronous validators - */ - validators?: TValidator[]; - /** - * Asynchronous validators - */ - asyncValidators?: TAsyncValidator[]; - /** - * Wait time in ms that should pass after - * the last user input before the async - * validators will be triggered - */ - asyncValidationWait?: number; -} - -/** - * Properties of a component that is wrapped - * by withValidation - */ -export interface IValidationWrapperProps extends IValidatedComponentProps, IFormContextProps { - /** - * Render prop - */ - render(fullName: string, validation: IValidationProp, context: IFormContext): JSX.Element; -} - -/** - * Internal state of the validated component - */ -export interface IValidationState { - /** - * True, if the field is currently validating - * (asynchronous validation running in background) - */ - isValidating: boolean; - /** - * True, if the field is a required field - * (has a required validator attached) - */ - isRequired: boolean; - /** - * True, if all validators report a valid state - */ - valid: boolean; - /** - * Contains any errors if available - */ - error: TFieldErrors; -} - -/** - * Interface with properties describing the current - * validation state and offering interfaces for - * various validation tasks - */ -export interface IValidationProp extends IValidationState { - /** - * Triggers the validation of the field - * @param value Field value - * @param args Validation args @see IValidationArgs - */ - validate(value: TFieldValue | undefined, args?: Partial): Promise; - /** - * Resets the validation state - */ - reset(): void; - /** - * Forces a new validation state on this Field - * @param state New validation state - */ - update(state: Partial): void; -} - -/** - * Base interface for consumers of withValidation props - */ -export interface IValidationProps extends IFormContextProps { - /** - * Full Name of the component - * (context.fieldPrefix + '.' + fieldName) - */ - fullName: string; - /** - * Validation properties, describes the current - * validation state of the component - */ - validation: IValidationProp; -} - -/** - * Arguments for the validate method - */ -export interface IValidationArgs { - /** - * True, if the async validators should - * be triggered as well, otherwise only - * the sync validators are triggered - * - * Default: true - */ - checkAsync: boolean; - /** - * True, if the async validators should - * be triggered without any delay - * - * Default: false - */ - immediateAsync: boolean; -} diff --git a/src/hooks/index.md b/src/hooks/index.md new file mode 100644 index 0000000..686dcf7 --- /dev/null +++ b/src/hooks/index.md @@ -0,0 +1 @@ +Hooks overview diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000..8c5a599 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,5 @@ +export * from './useFormContext'; +export * from './useValidation'; +export * from './useField'; +export * from './useFormEventListener'; +export { TFormEventListener, IFieldState } from './internal'; diff --git a/src/hooks/internal/__snapshots__/useFieldStates.test.ts.snap b/src/hooks/internal/__snapshots__/useFieldStates.test.ts.snap new file mode 100644 index 0000000..6f50021 --- /dev/null +++ b/src/hooks/internal/__snapshots__/useFieldStates.test.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`useFieldStates invalid field registration case 1 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; + +exports[`useFieldStates invalid field registration case 2 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; + +exports[`useFieldStates invalid field registration case 3 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; + +exports[`useFieldStates invalid field registration case 4 of 5 props 1`] = `"[Form] registerField: invalid field state given"`; + +exports[`useFieldStates invalid field registration case empty state 1`] = `"[Form] registerField: invalid field state given"`; + +exports[`useFieldStates invalid field registration case invalid field name 1`] = `"[Form] registerField: name is required"`; + +exports[`useFieldStates invalid field registration case no parameters 1`] = `"[Form] registerField: name is required"`; + +exports[`useFieldStates invalid field registration case no state 1`] = `"[Form] registerField: field state is required"`; diff --git a/src/hooks/internal/index.ts b/src/hooks/internal/index.ts new file mode 100644 index 0000000..648b714 --- /dev/null +++ b/src/hooks/internal/index.ts @@ -0,0 +1,6 @@ +export * from './useFieldRegistration'; +export * from './useFullName'; +export * from './useIsUnmounted'; +export * from './useTimeout'; +export * from './useFieldEvents'; +export * from './useFieldStates'; diff --git a/src/hooks/internal/useFieldEvents.test.ts b/src/hooks/internal/useFieldEvents.test.ts new file mode 100644 index 0000000..e66da57 --- /dev/null +++ b/src/hooks/internal/useFieldEvents.test.ts @@ -0,0 +1,72 @@ +import { renderHook } from 'react-hooks-testing-library'; + +import { useFieldEvents, TFormEventListener } from './useFieldEvents'; + +describe('useFieldEvents', () => { + interface IMockListener { + id: string; + state: TFormEventListener; + } + + const createMockListener = (id?: string): IMockListener => ({ + id: id === undefined ? 'listener' : id, + state: jest.fn(), + }); + + const createMockListeners = (count: number): IMockListener[] => { + const result = []; + for (let i = 0; i < count; i += 1) { + result.push(createMockListener(`listener${i}`)); + } + + return result; + }; + + it('should return the correct result', () => { + const { result } = renderHook(() => useFieldEvents()); + + expect(result.current).toMatchObject({ + registerListener: expect.any(Function), + unregisterListener: expect.any(Function), + notifyListeners: expect.any(Function), + }); + }); + + it('should register new listeners without crashing', () => { + const mockListeners = createMockListeners(3); + const { result } = renderHook(() => useFieldEvents()); + mockListeners.forEach((item) => { + expect(() => { + result.current.registerListener(item.id, item.state); + }).not.toThrowError(); + }); + }); + + it('should unregister new listeners without crashing', () => { + const mockListeners = createMockListeners(3); + const { result } = renderHook(() => useFieldEvents()); + mockListeners.forEach((item) => { + result.current.registerListener(item.id, item.state); + expect(() => { + result.current.unregisterListener(item.id); + }).not.toThrowError(); + }); + }); + + it('should call the listeners', () => { + const eventName = 'change'; + const eventArgs = 'myNewValue'; + const fieldName = 'mockFieldName'; + + const { result } = renderHook(() => useFieldEvents()); + const mockListeners = createMockListeners(3); + mockListeners.forEach(item => result.current.registerListener(item.id, item.state)); + + result.current.notifyListeners(fieldName, eventName, eventArgs); + mockListeners.forEach(item => expect(item.state).toHaveBeenLastCalledWith( + fieldName, + eventName, + eventArgs, + )); + }); +}); diff --git a/src/hooks/internal/useFieldEvents.ts b/src/hooks/internal/useFieldEvents.ts new file mode 100644 index 0000000..648a3a8 --- /dev/null +++ b/src/hooks/internal/useFieldEvents.ts @@ -0,0 +1,45 @@ +import { useRef, useCallback, useMemo } from 'react'; + +export type TFormEventListener = ((name: string, event: string, args?: unknown) => void); + +export interface IUseFieldEventsResult { + registerListener(name: string, callback: TFormEventListener): void; + unregisterListener(name: string): void; + notifyListeners(name: string, event: string, args?: unknown): void; +} + +export function useFieldEvents(): IUseFieldEventsResult { + const eventListeners = useRef(new Map()); + + /** + * Registers a new listener + */ + const registerListener = useCallback((name: string, callback: TFormEventListener): void => { + eventListeners.current.set(name, callback); + }, []); + + /** + * Unregisters a listener + */ + const unregisterListener = useCallback((name: string): void => { + eventListeners.current.delete(name); + }, []); + + /** + * Notifies the event listeners about an event + * @param name Field name + * @param event Event name + * @param args Event args + */ + const notifyListeners = useCallback((name: string, event: string, args?: unknown): void => { + eventListeners.current.forEach((callback) => { + callback(name, event, args); + }); + }, []); + + return useMemo(() => ({ + registerListener, + unregisterListener, + notifyListeners, + }), [notifyListeners, registerListener, unregisterListener]); +} diff --git a/src/hooks/internal/useFieldRegistration.test.ts b/src/hooks/internal/useFieldRegistration.test.ts new file mode 100644 index 0000000..8a620c4 --- /dev/null +++ b/src/hooks/internal/useFieldRegistration.test.ts @@ -0,0 +1,104 @@ +import { renderHook } from 'react-hooks-testing-library'; + +import { useFieldRegistration } from './useFieldRegistration'; +import { useFormContext } from '../useFormContext'; +import { IFieldState } from './useFieldStates'; + +jest.mock('../useFormContext'); + +function getMockFieldState(): IFieldState { + return { + label: 'mock-label', + isGroup: false, + updateValidation: jest.fn(), + validate: jest.fn(), + reset: jest.fn(), + getValue: jest.fn(), + }; +} + +interface ISetupResult { + registerField: jest.Mock; + unregisterField: jest.Mock; + fullName: string; + fieldState: IFieldState; + rerender(): void; + unmount(): boolean; +} + +function setup(): ISetupResult { + const registerField = jest.fn(); + const unregisterField = jest.fn(); + + const fieldState = getMockFieldState(); + + (useFormContext as jest.Mock).mockReturnValue({ + registerField, + unregisterField, + }); + + const fullName = 'mock-name'; + + const { rerender, unmount } = renderHook(() => useFieldRegistration( + fullName, + fieldState, + )); + + return { + registerField, + unregisterField, + fieldState, + fullName, + rerender, + unmount, + } +} + +describe('useFieldRegistration', () => { + it('should call formContext.registerField with the correct values', () => { + const { fieldState, fullName, registerField, unregisterField } = setup(); + + expect(registerField).toHaveBeenCalledTimes(1); + expect(registerField).toHaveBeenCalledWith(fullName, fieldState); + expect(unregisterField).not.toHaveBeenCalled(); + }); + + it('should call formContext.unregisterField on unmount', () => { + const { unregisterField, fullName, unmount } = setup(); + + unmount(); + + expect(unregisterField).toHaveBeenCalledTimes(1); + expect(unregisterField).toHaveBeenCalledWith(fullName); + }); + + // TODO: Wait for support with multiple params + // it('should re-register itself if a value changed', () => { + // const { fieldState, registerField, unregisterField, rerender, fullName} = setup(); + + // registerField.mockClear(); + // unregisterField.mockClear(); + + // const oldName = fullName; + // const newName = 'mock-rerender-name'; + // rerender(); + + // expect(unregisterField).toHaveBeenCalledTimes(1); + // expect(unregisterField).toHaveBeenCalledWith(oldName); + + // expect(registerField).toHaveBeenCalledTimes(1); + // expect(registerField).toHaveBeenCalledWith(fullName, fieldState); + // }); + + it('should not re-register itself if nothing has changed', () => { + const { registerField, unregisterField, rerender } = setup(); + + registerField.mockClear(); + unregisterField.mockClear(); + + rerender(); + + expect(unregisterField).not.toHaveBeenCalled(); + expect(registerField).not.toHaveBeenCalled(); + }); +}); diff --git a/src/hooks/internal/useFieldRegistration.ts b/src/hooks/internal/useFieldRegistration.ts new file mode 100644 index 0000000..201fec5 --- /dev/null +++ b/src/hooks/internal/useFieldRegistration.ts @@ -0,0 +1,33 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { useEffect } from 'react'; + +import { useFormContext } from '../useFormContext'; +import { IFieldState } from './useFieldStates'; + +/** + * Hook for registering fields to the form context. Will automatically + * unregister the field on unmount. + * @param fullName Full name of the field + * @param fieldState Field state to register @see IFieldState + */ +export function useFieldRegistration( + fullName: string, + fieldState: IFieldState, +): void { + const formContext = useFormContext(); + useEffect(() => { + formContext.registerField( + fullName, + fieldState, + ); + + return () => { + formContext.unregisterField(fullName); + }; + }, [formContext, fullName, fieldState]); +} diff --git a/src/hooks/internal/useFieldStates.test.ts b/src/hooks/internal/useFieldStates.test.ts new file mode 100644 index 0000000..9386f15 --- /dev/null +++ b/src/hooks/internal/useFieldStates.test.ts @@ -0,0 +1,144 @@ +import { renderHook } from 'react-hooks-testing-library'; + +import { useFieldStates, IFieldState, IUseFieldStatesResult } from './useFieldStates'; + +const createMockFieldState = (label: string, isGroup?: boolean): IFieldState => ({ + label, + isGroup, + validate: jest.fn().mockResolvedValue({ + isValidating: false, + valid: true, + error: null, + }), + updateValidation: jest.fn(), + reset: jest.fn(), + getValue: jest.fn().mockReturnValue(label), +}); + +interface IMockField { + name: string; + state: IFieldState; +} + +const createMockField = (name: string, label: string, isGroup?: boolean): IMockField => ({ + name, + state: createMockFieldState(label, isGroup), +}); + +describe('useFieldStates', () => { + interface ISetupResult { + result: { + current: IUseFieldStatesResult; + }; + } + + const setup = (): ISetupResult => renderHook(useFieldStates) + + it('should return the correct result', () => { + const { result } = setup(); + + expect(result.current).toMatchObject({ + getFieldState: expect.any(Function), + registerField: expect.any(Function), + unregisterField: expect.any(Function), + forEachFieldState: expect.any(Function), + }); + }); + + describe('invalid field registration', () => { + const mf = (): void => {}; + const cases: any[] = [ + ['no parameters', undefined, undefined], + ['invalid field name', '', undefined], + ['no state', 'foo', undefined], + ['empty state', 'foo', {}], + ['1 of 5 props', 'foo', { label: 'hey' }], + ['2 of 5 props', 'foo', { label: 'hey', validate: mf }], + ['3 of 5 props', 'foo', { label: 'hey', validate: mf, updateValidation: mf }], + ['4 of 5 props', 'foo', { + label: 'hey', + validate: mf, + updateValidation: mf, + reset: mf, + }], + ]; + + test.each(cases)('case %s', (testName: string, fieldName: string, fieldState: IFieldState) => { + const { result } = setup(); + expect(() => { + result.current.registerField(fieldName, fieldState); + }).toThrowErrorMatchingSnapshot(); + }); + }); + + describe('field registration and values', () => { + const createCases = (): [string, IMockField, boolean?][] => { + return [ + ['field', createMockField('unitField', 'Unit field')], + ['group', createMockField('unitGroup', 'Unit group', true)], + ['sub field', createMockField('unitGroup.subField', 'Sub field', true)], + ]; + }; + + describe('registerField - field registration', () => { + const cases = createCases(); + + test.each(cases)('should register a new %s without crashing', (testName, field: IMockField) => { + const { result } = setup(); + expect(() => { + result.current.registerField(field.name, field.state); + }).not.toThrowError(); + }); + }); + + describe('unregisterField - field cleanup', () => { + const cases = createCases(); + + test.each(cases)('should unregister %s without crashing', (testName, field: IMockField) => { + const { result } = setup(); + result.current.registerField(field.name, field.state); + result.current.unregisterField(field.name); + }); + }); + + describe('getFieldState - field states', () => { + const cases = createCases(); + + test.each(cases)('should return the correct field state of a %s', (testName, field: IMockField) => { + const { result } = setup(); + result.current.registerField(field.name, field.state); + expect(result.current.getFieldState(field.name)).toBe(field.state); + }); + + it('should throw an error when trying to access an non-existing field state', () => { + const { result } = setup(); + const mockFieldName = 'mock-test'; + + expect( + () => result.current.getFieldState(mockFieldName), + ).toThrowError(`[Form] getFieldState: Could not find state of field '${mockFieldName}'`); + }); + }); + + it('forEachFieldState - should correctly iterate through the field states', () => { + const { result } = setup(); + + const field = createMockField('unitField', 'Unit field'); + result.current.registerField(field.name, field.state); + + const group = createMockField('unitGroup', 'Unit group', true); + result.current.registerField(group.name, group.state); + + const subField = createMockField('unitGroup.subField', 'Sub field', true); + result.current.registerField(subField.name, subField.state); + + const forEachCallback = jest.fn(); + result.current.forEachFieldState(forEachCallback); + + expect(forEachCallback).toHaveBeenCalledTimes(3); + expect(forEachCallback).toHaveBeenNthCalledWith(1, field.state, field.name, expect.anything()); + expect(forEachCallback).toHaveBeenNthCalledWith(2, group.state, group.name, expect.anything()); + expect(forEachCallback).toHaveBeenNthCalledWith(3, subField.state, subField.name, expect.anything()); + }); + }); +}); diff --git a/src/hooks/internal/useFieldStates.ts b/src/hooks/internal/useFieldStates.ts new file mode 100644 index 0000000..2af1406 --- /dev/null +++ b/src/hooks/internal/useFieldStates.ts @@ -0,0 +1,107 @@ +import { useRef, useCallback } from 'react'; + +import { IValidationArgs, IBasicValidationState, TUpdateMethod } from '../useValidation'; +import { TBasicFieldValue } from '../useField'; + +/** + * Interface describing field states + */ +export interface IFieldState { + /** + * Label of the field + */ + label: string; + /** + * True if the field is actually a FieldGroup + */ + isGroup?: boolean; + /** + * Triggers the validation of this field + * @param args Validation args + */ + validate(args?: Partial): Promise; + /** + * Returns the current value of the field + */ + getValue(): TBasicFieldValue; + /** + * Resets the field to its initial state + */ + reset(): void; + /** + * Updates the validation state of the field + * @param state New validation state + */ + updateValidation: TUpdateMethod; +} + +export interface IUseFieldStatesResult { + getFieldState(name: string): IFieldState; + registerField(name: string, fieldState: IFieldState): void; + unregisterField(name: string): void; + forEachFieldState(callback: (value: IFieldState, key: string, map: Map) => void): void; +} + +export function useFieldStates(): IUseFieldStatesResult { + const fields = useRef(new Map()); + + /** + * Returns the current state of the given field + * @param name Field name + * @returns Current field state or default field state + */ + const getFieldState = useCallback((name: string): IFieldState => { + const fieldState = fields.current.get(name); + if (fieldState === undefined) { + throw new Error(`[Form] getFieldState: Could not find state of field '${name}'`); + } + + return fieldState; + }, []); + + /** + * Registers a new field to the form. + * @param name Field name + * @param fieldState Field state + */ + const registerField = useCallback((name: string, fieldState: IFieldState): void => { + if (typeof name !== 'string' || name.length === 0) { + throw new Error('[Form] registerField: name is required'); + } + + if (typeof fieldState !== 'object') { + throw new Error('[Form] registerField: field state is required'); + } + + if ( + typeof fieldState.label !== 'string' + || typeof fieldState.validate !== 'function' + || typeof fieldState.updateValidation !== 'function' + || typeof fieldState.reset !== 'function' + || typeof fieldState.getValue !== 'function' + ) { + throw new Error('[Form] registerField: invalid field state given'); + } + + fields.current.set(name, fieldState); + }, []); + + /** + * Unregisters a field from the form. + * @param name Field name + */ + const unregisterField = useCallback((name: string): void => { + fields.current.delete(name); + }, []); + + const forEachFieldState = useCallback((callback: (value: IFieldState, key: string, map: Map) => void) => { + return fields.current.forEach(callback); + }, []); + + return { + getFieldState, + registerField, + unregisterField, + forEachFieldState, + }; +} diff --git a/src/hooks/internal/useFullName.test.ts b/src/hooks/internal/useFullName.test.ts new file mode 100644 index 0000000..6185222 --- /dev/null +++ b/src/hooks/internal/useFullName.test.ts @@ -0,0 +1,30 @@ +import { renderHook } from 'react-hooks-testing-library'; +import { useFullName } from './useFullName'; +import { useFormContext } from '../useFormContext'; + +jest.mock('../useFormContext'); + +describe('useFullName', () => { + it('should return the raw name if formContext.fieldPrefix is null', () => { + (useFormContext as jest.Mock).mockReturnValueOnce({ + fieldPrefix: null, + }); + + const mockName = 'mock-name'; + const { result } = renderHook(() => useFullName(mockName)); + + expect(result.current).toBe(mockName); + }); + + it('should prefix the raw name with formContext.fieldPrefix if existing', () => { + const mockPrefix = 'mockPrefix'; + (useFormContext as jest.Mock).mockReturnValueOnce({ + fieldPrefix: mockPrefix, + }); + + const mockName = 'mock-name'; + const { result } = renderHook(() => useFullName(mockName)); + + expect(result.current).toBe(`${mockPrefix}.${mockName}`); + }); +}); diff --git a/src/hooks/internal/useFullName.ts b/src/hooks/internal/useFullName.ts new file mode 100644 index 0000000..e3b2794 --- /dev/null +++ b/src/hooks/internal/useFullName.ts @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +import { useFormContext } from '../useFormContext'; + +/** + * Returns the full name of the field, consisting of + * the current `FormContext.fieldPrefix` and the `name` of the field. + * @param name Name of the field + */ +export function useFullName(name: string): string { + const formContext = useFormContext(); + return formContext.fieldPrefix !== null ? formContext.fieldPrefix.concat('.', name) : name; +} diff --git a/src/hooks/internal/useIsUnmounted.test.ts b/src/hooks/internal/useIsUnmounted.test.ts new file mode 100644 index 0000000..6513e3b --- /dev/null +++ b/src/hooks/internal/useIsUnmounted.test.ts @@ -0,0 +1,20 @@ +import { renderHook } from 'react-hooks-testing-library'; +import { useIsUnmounted } from './useIsUnmounted'; + +describe('useIsUnmounted', () => { + const { result, rerender, unmount } = renderHook(() => useIsUnmounted()); + + it('should return false while the component is mounted', () => { + expect(result.current.current).toBe(false); + }); + + it('should return false after a rerender', () => { + rerender(); + expect(result.current.current).toBe(false); + }); + + it('should return true after unmount', () => { + unmount(); + expect(result.current.current).toBe(true); + }); +}); diff --git a/src/hooks/internal/useIsUnmounted.ts b/src/hooks/internal/useIsUnmounted.ts new file mode 100644 index 0000000..ec87c9f --- /dev/null +++ b/src/hooks/internal/useIsUnmounted.ts @@ -0,0 +1,26 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useRef, useEffect } from 'react'; + +/** + * Returns a ref object that is true if the + * current component has already been unmounted. + * @example + * const isUnmounted = useIsUnmounted(); + * if (isUnmounted.current === true) { + * // do stuff + * } + */ +export function useIsUnmounted(): React.MutableRefObject { + const isUnmounted = useRef(false); + useEffect(() => { + return () => { isUnmounted.current = true; } + }, []); + + return isUnmounted; +} diff --git a/src/hooks/internal/useTimeout.test.ts b/src/hooks/internal/useTimeout.test.ts new file mode 100644 index 0000000..230addd --- /dev/null +++ b/src/hooks/internal/useTimeout.test.ts @@ -0,0 +1,57 @@ +import { renderHook, act } from 'react-hooks-testing-library'; +import { useTimeout } from './useTimeout'; + +beforeAll(jest.useFakeTimers); + +describe('useTimeout', () => { + const { result, unmount } = renderHook(() => useTimeout()); + + it('should return an array with a setTimeout and a clearTimeout function', () => { + expect(result.current).toBeInstanceOf(Array); + expect(result.current.length).toBe(2); + expect(result.current[0]).toBeInstanceOf(Function); + expect(result.current[1]).toBeInstanceOf(Function); + }); + + it('should call the timeout handler after timeout milliseconds', () => { + const mockHandler = jest.fn(); + const mockTimeout = 1000; + + act(() => { result.current[0](mockHandler, mockTimeout); }) + + expect(mockHandler).not.toHaveBeenCalled(); + jest.runAllTimers(); + + expect(mockHandler).toHaveBeenCalledTimes(1); + }); + + it('should cancel the timeout with the clearTimeout function', () => { + const mockHandler = jest.fn(); + const mockTimeout = 1000; + + act(() => { result.current[0](mockHandler, mockTimeout); }) + + expect(mockHandler).not.toHaveBeenCalled(); + + act(() => { result.current[1](); }) + expect(mockHandler).not.toHaveBeenCalled(); + + jest.runAllTimers(); + expect(mockHandler).not.toHaveBeenCalled(); + }); + + it('should cancel the timeout on unmount', () => { + const mockHandler = jest.fn(); + const mockTimeout = 1000; + + act(() => { result.current[0](mockHandler, mockTimeout); }) + + expect(mockHandler).not.toHaveBeenCalled(); + + unmount(); + expect(mockHandler).not.toHaveBeenCalled(); + + jest.runAllTimers(); + expect(mockHandler).not.toHaveBeenCalled(); + }); +}); diff --git a/src/hooks/internal/useTimeout.ts b/src/hooks/internal/useTimeout.ts new file mode 100644 index 0000000..82f843e --- /dev/null +++ b/src/hooks/internal/useTimeout.ts @@ -0,0 +1,58 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useRef, useEffect, useCallback } from 'react'; + +/** + * Calls the `handle` after `timeout` milliseconds have passed + * @param handle Callback that should be called after the timout has been reached + * @param timeout Timeout in milliseconds + */ +export type TSetTimeout = ((handle: TimerHandler, timeout: number) => void); +/** + * Clears any timeout if existing + */ +export type TClearTimeout = (() => void); +/** + * Result of useTimeout. Contains an array with a setTimeout and a + * clearTimeout function + */ +export type TUseTimeoutResult = [ + TSetTimeout, + TClearTimeout, +]; + +/** + * Wrapper hook for `window.setTimeout` and `window.clearTimeout`. Will + * automatically clear the timeout on unmount. + */ +export function useTimeout(): TUseTimeoutResult { + const timeoutId = useRef(undefined); + + const internalClearTimeout = useCallback(() => { + if (timeoutId.current !== undefined) { + window.clearTimeout(timeoutId.current); + timeoutId.current = undefined; + } + }, []); + + const internalSetTimeout = useCallback( + (handle: TimerHandler, timeout: number) => { + timeoutId.current = window.setTimeout(handle, timeout); + }, + [], + ); + + useEffect(() => { + return () => { internalClearTimeout(); } + }, [ internalClearTimeout ]); + + return [ + internalSetTimeout, + internalClearTimeout, + ]; +} diff --git a/src/hooks/useField/index.ts b/src/hooks/useField/index.ts new file mode 100644 index 0000000..d1be912 --- /dev/null +++ b/src/hooks/useField/index.ts @@ -0,0 +1,2 @@ +export * from './useField'; +export * from './useField.types'; diff --git a/src/hooks/useField/useField.md b/src/hooks/useField/useField.md new file mode 100644 index 0000000..3a69bed --- /dev/null +++ b/src/hooks/useField/useField.md @@ -0,0 +1,3 @@ +Hook for writing custom fields. Will handle the internal state +of the field, the validation and all communication with the +form context. diff --git a/src/components/withField/Field/Field.test.tsx b/src/hooks/useField/useField.test.ts similarity index 65% rename from src/components/withField/Field/Field.test.tsx rename to src/hooks/useField/useField.test.ts index 8fc86e3..ca395a5 100644 --- a/src/components/withField/Field/Field.test.tsx +++ b/src/hooks/useField/useField.test.ts @@ -1,271 +1,218 @@ -import React from 'react'; +import { renderHook, act } from 'react-hooks-testing-library'; -import { shallow, ShallowWrapper } from 'enzyme'; +import { IFormContext } from '../../components'; +import { createMockFormContext, createMockValidationResult } from '../../test-utils/enzymeFormContext'; +import { useFormContext } from '../useFormContext'; +import { useValidation, IUseValidationResult } from '../useValidation'; +import { useFullName, useFieldRegistration, IFieldState } from '../internal'; -import { createMockFormContext, createMockValidation } from '../../../test-utils/enzymeFormContext'; -import { IFieldState, IFormContext } from '../../FormContext'; -import { IValidationProp } from '../../withValidation'; -import { BaseField } from './Field'; -import { IFieldComponentFieldProps, IFieldProps, TBasicFieldValue } from './Field.types'; +import { useField } from './useField'; +import { TBasicFieldValue, IFieldComponentFieldProps, IUseFieldProps, IUseFieldResult } from './useField.types'; -describe('', () => { +jest.mock('../useFormContext'); +jest.mock('../useValidation'); +jest.mock('../internal'); + +describe('useField', () => { const mockName = 'unitField'; const mockLabel = 'Unit field'; interface ISetupArgs { - props?: Partial; + props?: Partial; contextOverrides?: Partial; - renderCallback?(field: IFieldComponentFieldProps): void; } interface ISetupResult { formContext: IFormContext; + validation: IUseValidationResult; fieldState: IFieldState; - validation: IValidationProp; - fieldProps: IFieldComponentFieldProps; - wrapper: ShallowWrapper; + + usedFieldProps: IUseFieldProps; + + unmount(): boolean; + rerender(newProps?: IUseFieldProps): void; + waitForNextUpdate(): Promise; + result: { current: IUseFieldResult }; } const setup = ({ props, contextOverrides, - renderCallback, }: Partial = {}): ISetupResult => { - let fieldState: IFieldState; - let fieldProps: IFieldComponentFieldProps; - const registerCallback = (name: string, state: IFieldState): void => { fieldState = state; }; - - const formContext: IFormContext = { - ...createMockFormContext(registerCallback), + (useFullName as jest.Mock).mockImplementation((name: string) => name) + const formContext = { + ...createMockFormContext(), ...contextOverrides, }; - const validation = createMockValidation(); + (useFormContext as jest.Mock).mockReturnValue(formContext); - const defaultRenderCallback = (field: IFieldComponentFieldProps): JSX.Element => { - fieldProps = field; + const validation = createMockValidationResult(); + (useValidation as jest.Mock).mockReturnValue(validation); - if (renderCallback) { renderCallback(fieldProps); } + let fieldState = null; + (useFieldRegistration as jest.Mock).mockImplementation((fullName, state) => { + fieldState = state; + }); - return ( -
    - ); + const useFieldParams = { + name: mockName, + label: mockLabel, + ...props, }; - - const wrapper = shallow(( - - )); + const { result, unmount, rerender, waitForNextUpdate } = renderHook(useField, { initialProps: useFieldParams }); return { formContext, - //@ts-ignore Field state is always initialized through the registerCallback - fieldState, validation, - //@ts-ignore Field props should be always initialized through the render callback - fieldProps, - wrapper, + + // @ts-ignore + fieldState, + + usedFieldProps: useFieldParams, + + unmount, + rerender, + waitForNextUpdate, + result, }; }; const assertValue = (fieldProps: IFieldComponentFieldProps, value: TBasicFieldValue): unknown => expect(fieldProps.value).toBe(value); const simulateChange = (field: IFieldComponentFieldProps, value: TBasicFieldValue): void => { - field.onChange({ - target: { - value, - }, - }); - }; - - describe('Render', () => { - it('should render without crashing', () => { - const { wrapper } = setup(); - expect(wrapper).toMatchSnapshot(); - }); - - describe('render prop', () => { - it('should call the render prop', () => { - const renderCallback = jest.fn(); - setup({ props: { render: renderCallback }}); - expect(renderCallback).toHaveBeenCalled(); + act(() => { + field.onChange({ + target: { + value, + }, }); }); - }); + }; describe('Form registration', () => { - let formContext: IFormContext; - let validation: IValidationProp; - let wrapper: ShallowWrapper; - - beforeAll(() => { - ({ formContext, validation, wrapper } = setup()); - }); - it('should register itself in the form context', () => { - expect(formContext.registerField).toHaveBeenCalledWith( + const { fieldState } = setup(); + expect((useFieldRegistration as jest.Mock)).toHaveBeenCalledWith( mockName, - { - label: mockLabel, - validate: expect.any(Function), - reset: expect.any(Function), - getValue: expect.any(Function), - updateValidation: validation.update, - }, + fieldState, ); }); - - it('should unregister itself on unmount', () => { - wrapper.unmount(); - expect(formContext.unregisterField).toHaveBeenCalledWith(mockName); - }); - }); - - describe('Invalid form context', () => { - const mockErrorString = `Could not find a form context for field "${mockName}". ` - + 'Fields can only be used inside a Form tag.'; - - it('should throw an error if there is no form context', () => { - expect(() => setup({ props: { context: undefined } })).toThrowError(mockErrorString); - }); - - it('should throw an error if the form context is invalid', () => { - // @ts-ignore The whole point of this test is to check the behaviour with an invalid type - expect(() => setup({ props: { context: { foo: 'bar' } } })).toThrowError(mockErrorString); - }); }); describe('Default value handling', () => { - let wrapper: ShallowWrapper; - let fieldProps: IFieldComponentFieldProps; - - afterEach(() => wrapper.unmount()); - it('should have an empty string as the default value', () => { - ({ wrapper, fieldProps } = setup()); - assertValue(fieldProps, ''); + const { result } = setup(); + assertValue(result.current.fieldProps, ''); }); it('should use the default value from Form.defaultValues if existing', () => { const mockDefaultValue = 'mock-default-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ contextOverrides: { defaultValues: { [mockName]: mockDefaultValue }, }, - })); + }); - assertValue(fieldProps, mockDefaultValue); + assertValue(result.current.fieldProps, mockDefaultValue); }); it('should use the Field.defaultValue if existing', () => { const mockDefaultValue = 'mock-field-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { defaultValue: mockDefaultValue, }, - })); + }); - assertValue(fieldProps, mockDefaultValue); + assertValue(result.current.fieldProps, mockDefaultValue); }); it('should prefer the Field.defaultValue over the Form.defaultValues', () => { const mockFieldDefaultValue = 'mock-field-value'; const mockFormDefaultValue = 'mock-form-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { defaultValue: mockFieldDefaultValue, }, contextOverrides: { defaultValues: { [mockName]: mockFormDefaultValue }, }, - })); + }); - assertValue(fieldProps, mockFieldDefaultValue); + assertValue(result.current.fieldProps, mockFieldDefaultValue); }); it('should not use the defaultValue if the Field is touched', () => { - let formContext; - const mockDefaultValue = 'mock-field-value'; const mockChangeValue = 'mock-change-value'; - ({ wrapper, formContext, fieldProps } = setup({ - renderCallback: (newProps: IFieldComponentFieldProps): void => { fieldProps = newProps; }, - })); + const { formContext, result, rerender, usedFieldProps } = setup(); // Recreate a field value change - simulateChange(fieldProps, mockChangeValue); + simulateChange(result.current.fieldProps, mockChangeValue); // Set new defaultProps through Form.defaultValues formContext.defaultValues = { [mockName]: mockDefaultValue }; - wrapper.setProps({ context: formContext }); - assertValue(fieldProps, mockChangeValue); + rerender(); + assertValue(result.current.fieldProps, mockChangeValue); // Set new defaultProps through Field.defaultValue - wrapper.setProps({ defaultValue: mockDefaultValue }); - assertValue(fieldProps, mockChangeValue); + rerender({ + ...usedFieldProps, + defaultValue: mockDefaultValue, + }); + assertValue(result.current.fieldProps, mockChangeValue); }); }); describe('Prop value handling', () => { - let wrapper: ShallowWrapper; - let fieldProps: IFieldComponentFieldProps; - - afterEach(() => wrapper.unmount()); - it('should use the value from Form.values if existing', () => { const mockValue = 'mock-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ contextOverrides: { values: { [mockName]: mockValue }, }, - })); + }); - assertValue(fieldProps, mockValue); + assertValue(result.current.fieldProps, mockValue); }); it('should use the Field.value if existing', () => { const mockValue = 'mock-field-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { value: mockValue, }, - })); + }); - assertValue(fieldProps, mockValue); + assertValue(result.current.fieldProps, mockValue); }); it('should prefer the Field.value over the Form.values', () => { const mockFieldValue = 'mock-field-value'; const mockFormValue = 'mock-form-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { value: mockFieldValue, }, contextOverrides: { values: { [mockName]: mockFormValue }, }, - })); + }); - assertValue(fieldProps, mockFieldValue); + assertValue(result.current.fieldProps, mockFieldValue); }); it('Field.value should override the default values', () => { const mockValue = 'mock-field-value'; const mockDefaultValue = 'mock-default-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { value: mockValue, defaultValue: mockDefaultValue, @@ -273,16 +220,16 @@ describe('', () => { contextOverrides: { defaultValues: { [mockName]: mockDefaultValue }, }, - })); + }); - assertValue(fieldProps, mockValue); + assertValue(result.current.fieldProps, mockValue); }); it('Form.values should override the default values', () => { const mockValue = 'mock-field-value'; const mockDefaultValue = 'mock-default-value'; - ({ wrapper, fieldProps } = setup({ + const { result } = setup({ props: { defaultValue: mockDefaultValue, }, @@ -290,73 +237,65 @@ describe('', () => { values: { [mockName]: mockValue }, defaultValues: { [mockName]: mockDefaultValue }, }, - })); + }); - assertValue(fieldProps, mockValue); + assertValue(result.current.fieldProps, mockValue); }); it('should use the changed value even if the Field is touched', () => { - let formContext; - let mockValue = 'mock-field-value'; const mockChangeValue = 'mock-change-value'; + const { formContext, result, rerender, usedFieldProps } = setup(); + const updateValue = (): void => { // Recreate a field value change - simulateChange(fieldProps, mockChangeValue); - assertValue(fieldProps, mockChangeValue); + simulateChange(result.current.fieldProps, mockChangeValue); + assertValue(result.current.fieldProps, mockChangeValue); }; - ({ wrapper, formContext, fieldProps } = setup({ - renderCallback: (newProps: IFieldComponentFieldProps): void => { fieldProps = newProps; }, - })); - // Mock user input updateValue(); // Set new value through Form.values formContext.values = { [mockName]: mockValue }; - wrapper.setProps({ context: formContext }); - assertValue(fieldProps, mockValue); + rerender(); + + assertValue(result.current.fieldProps, mockValue); // Mock user input updateValue(); // Set new props through Field.value mockValue = 'mock-new-field-value'; - wrapper.setProps({ value: mockValue }); - assertValue(fieldProps, mockValue); + rerender({ + ...usedFieldProps, + value: mockValue, + }); + assertValue(result.current.fieldProps, mockValue); }); }); describe('onChange handling', () => { const mockValue = 'mock-change-value'; - let wrapper: ShallowWrapper; - let validation: IValidationProp; - let formContext: IFormContext; - let fieldProps: IFieldComponentFieldProps; - - const setupOnChange = (props?: Partial, contextOverrides?: Partial): void => { - ({ wrapper, validation, formContext, fieldProps } = setup({ + const setupOnChange = (props?: Partial, contextOverrides?: Partial): ISetupResult => { + const setupResult = setup({ props, contextOverrides, - renderCallback: (newProps: IFieldComponentFieldProps): void => { fieldProps = newProps; }, - })); - simulateChange(fieldProps, mockValue); - }; + }); + simulateChange(setupResult.result.current.fieldProps, mockValue); - afterEach(() => { - wrapper.unmount(); - }); + return setupResult; + }; it('should remember the changed value', () => { - setupOnChange(); - assertValue(fieldProps, mockValue); + const { result } = setupOnChange(); + assertValue(result.current.fieldProps, mockValue); }); it('should call the validate function', () => { - setupOnChange(); + const { validation } = setupOnChange(); expect(validation.validate).toHaveBeenCalledWith( mockValue, { checkAsync: false }, @@ -364,7 +303,7 @@ describe('', () => { }); it('should notify the form context', () => { - setupOnChange(); + const { formContext } = setupOnChange(); expect(formContext.notifyFieldEvent).toHaveBeenCalledWith( mockName, 'change', @@ -380,7 +319,7 @@ describe('', () => { it('should respect the Form.asyncValidateOnChange configuration', () => { const mockCheckAsync = true; - setupOnChange(undefined, { asyncValidateOnChange: mockCheckAsync }); + const { validation } = setupOnChange(undefined, { asyncValidateOnChange: mockCheckAsync }); expect(validation.validate).toHaveBeenCalledWith( mockValue, { checkAsync: mockCheckAsync }, @@ -389,7 +328,7 @@ describe('', () => { it('should respect the Field.asyncValidateOnChange configuration', () => { const mockCheckAsync = true; - setupOnChange({ asyncValidateOnChange: mockCheckAsync }); + const { validation } = setupOnChange({ asyncValidateOnChange: mockCheckAsync }); expect(validation.validate).toHaveBeenCalledWith( mockValue, @@ -459,56 +398,57 @@ describe('', () => { describe('onBlur handling', () => { const mockValue = 'mock-value'; - let wrapper: ShallowWrapper; - let validation: IValidationProp; - let formContext: IFormContext; - let fieldProps: IFieldComponentFieldProps; - - const setupLocal = (props?: Partial, contextOverrides?: Partial): void => { - ({ wrapper, validation, formContext, fieldProps } = setup({ + const setupLocal = (props?: Partial, contextOverrides?: Partial): ISetupResult => { + return setup({ props: { ...props, value: mockValue }, contextOverrides: contextOverrides, - })); - }; - - const setupOnBlur = (props?: Partial, contextOverrides?: Partial): void => { - setupLocal(props, contextOverrides); - fieldProps.onBlur(); + }); }; - afterEach(() => { - wrapper.unmount(); - }); + const setupOnBlur = (props?: Partial, contextOverrides?: Partial): ISetupResult => { + const setupResult = setupLocal(props, contextOverrides); - it('should call the Field.getSubmitValue function', () => { - const mockGetSubmitValue = jest.fn().mockImplementation((value: TBasicFieldValue): TBasicFieldValue => value); - setupOnBlur({ getSubmitValue: mockGetSubmitValue }); + act(() => { + setupResult.result.current.fieldProps.onBlur(); + }); - expect(mockGetSubmitValue).toHaveBeenCalledWith( - mockValue, - { disabled: false, plaintext: false }, - ); - }); + return setupResult; + }; - it('should call the validate function', () => { + it('should call the validate function with the value from Field.getSubmitValue', () => { + const mockGetSubmitValue = jest.fn().mockImplementation((value: TBasicFieldValue): TBasicFieldValue => value); const mockChangeValue = 'foo'; - setupLocal(); + const { result, validation } = setupLocal({ getSubmitValue: mockGetSubmitValue }); - simulateChange(fieldProps, mockChangeValue); + simulateChange(result.current.fieldProps, mockChangeValue); (validation.validate as jest.Mock).mockClear(); - fieldProps.onBlur(); + act(() => { + result.current.fieldProps.onBlur(); + }); expect(validation.validate).toHaveBeenCalledWith(mockChangeValue); + expect(mockGetSubmitValue).toHaveBeenCalledWith( + mockChangeValue, + { disabled: false, plaintext: false }, + ); }); it('should not call the validate function if the field is not dirty', () => { - setupOnBlur(); + const { validation } = setupOnBlur(); expect(validation.validate).not.toHaveBeenCalledWith(mockValue); }); + it('should not call the Field.getSubmitValue function if the field is not dirty', () => { + const mockGetSubmitValue = jest.fn().mockImplementation((value: TBasicFieldValue): TBasicFieldValue => value); + const { validation } = setupOnBlur({ getSubmitValue: mockGetSubmitValue }); + + expect(validation.validate).not.toHaveBeenCalledWith(mockValue); + expect(mockGetSubmitValue).not.toHaveBeenCalled(); + }); + it('should notify the form context', () => { - setupOnBlur(); + const { formContext } = setupOnBlur(); expect(formContext.notifyFieldEvent).toHaveBeenCalledWith( mockName, 'blur', @@ -540,8 +480,8 @@ describe('', () => { }; it('should call Field.getDisplayValue on first render', () => { - const { getDisplayValue, fieldProps } = setupWithDisplayName(); - assertValue(fieldProps, mockDisplayValue); + const { getDisplayValue, result } = setupWithDisplayName(); + assertValue(result.current.fieldProps, mockDisplayValue); expect(getDisplayValue).toHaveBeenCalledWith( '', { disabled: false, plaintext: false }, @@ -549,11 +489,11 @@ describe('', () => { }); it('should call getDisplayValue whenever the Context.disabled state changes', () => { - const { wrapper, formContext, getDisplayValue } = setupWithDisplayName(); + const { formContext, getDisplayValue, rerender } = setupWithDisplayName(); getDisplayValue.mockClear(); formContext.disabled = true; - wrapper.setProps({ context: formContext }); + rerender(); expect(getDisplayValue).toHaveBeenCalledWith( mockValue, @@ -562,11 +502,11 @@ describe('', () => { }); it('should call getDisplayValue whenever the Context.plaintext state changes', () => { - const { wrapper, formContext, getDisplayValue } = setupWithDisplayName(); + const { formContext, getDisplayValue, rerender } = setupWithDisplayName(); getDisplayValue.mockClear(); formContext.plaintext = true; - wrapper.setProps({ context: formContext }); + rerender(); expect(getDisplayValue).toHaveBeenCalledWith( mockValue, @@ -575,10 +515,13 @@ describe('', () => { }); it('should call getDisplayValue whenever the disabled prop changes', () => { - const { wrapper, getDisplayValue } = setupWithDisplayName(); + const { getDisplayValue, rerender, usedFieldProps } = setupWithDisplayName(); getDisplayValue.mockClear(); - wrapper.setProps({ disabled: true }); + rerender({ + ...usedFieldProps, + disabled: true, + }); expect(getDisplayValue).toHaveBeenCalledWith( mockValue, @@ -587,10 +530,13 @@ describe('', () => { }); it('should call getDisplayValue whenever the plaintext prop changes', () => { - const { wrapper, getDisplayValue } = setupWithDisplayName(); + const { getDisplayValue, rerender, usedFieldProps } = setupWithDisplayName(); getDisplayValue.mockClear(); - wrapper.setProps({ plaintext: true }); + rerender({ + ...usedFieldProps, + plaintext: true, + }); expect(getDisplayValue).toHaveBeenCalledWith( mockValue, @@ -653,7 +599,10 @@ describe('', () => { }, }); - void fieldState.validate(); + act(() => { + void fieldState.validate(); + }); + expect(mockGetSubmitValue).toHaveBeenCalledWith( mockValue, { disabled: false, plaintext: false }, @@ -665,8 +614,10 @@ describe('', () => { it('should reset its validation state', () => { const { fieldState, validation } = setup(); - fieldState.reset(); - expect(validation.reset).toHaveBeenCalled(); + act(() => { + fieldState.reset(); + }); + expect(validation.resetValidation).toHaveBeenCalled(); }); const cases = [ @@ -686,19 +637,21 @@ describe('', () => { : undefined; it('should correctly reset to its defaultValue', () => { - const { fieldState, fieldProps } = setup({ props, contextOverrides }); + const { fieldState, result } = setup({ props, contextOverrides }); - simulateChange(fieldProps, mockChangeValue); - fieldState.reset(); + simulateChange(result.current.fieldProps, mockChangeValue); + act(() => { + fieldState.reset(); + }); - assertValue(fieldProps, mockDefaultValue); + assertValue(result.current.fieldProps, mockDefaultValue); }); it('should call the Field.getDisplayValue function', () => { const mockDisplayValue = 'mock-display-value'; const mockGetDisplayValue = jest.fn().mockReturnValue(mockDisplayValue); - const { fieldState, fieldProps } = setup({ + const { fieldState, result } = setup({ props: { ...props, getDisplayValue: mockGetDisplayValue, @@ -706,10 +659,12 @@ describe('', () => { contextOverrides: contextOverrides, }); - simulateChange(fieldProps, mockChangeValue); - fieldState.reset(); + simulateChange(result.current.fieldProps, mockChangeValue); + act(() => { + fieldState.reset(); + }); - assertValue(fieldProps, mockDisplayValue); + assertValue(result.current.fieldProps, mockDisplayValue); expect(mockGetDisplayValue).toHaveBeenCalledWith( mockDefaultValue, { disabled: false, plaintext: false }, @@ -726,7 +681,9 @@ describe('', () => { contextOverrides: contextOverrides, }); - fieldState.reset(); + act(() => { + fieldState.reset(); + }); expect(mockOnChange).toHaveBeenCalledWith(mockDefaultValue); }); }); diff --git a/src/hooks/useField/useField.ts b/src/hooks/useField/useField.ts new file mode 100644 index 0000000..0aff4bd --- /dev/null +++ b/src/hooks/useField/useField.ts @@ -0,0 +1,226 @@ +import { useMemo, useState, useCallback, useEffect, useRef } from 'react'; + +import { getDeepValue, noopFunction } from '../../utils'; +import { useFormContext } from '../useFormContext'; +import { useValidation, IValidationArgs, IBasicValidationState } from '../useValidation'; +import { useFullName, useFieldRegistration } from '../internal'; + +import { IFieldComponentFieldProps, IFieldComponentMeta, IFieldChangedEvent, IUseFieldProps, IUseFieldResult, IUseFieldState, TBasicFieldValue, IValueMeta } from './useField.types'; +import { noopFieldValueFunction } from './useField.utils'; + +/** + * Hook for writing custom fields. Will handle the internal state + * of the field, the validation and all communication with the + * form context. + * @param props Field props @see IUseFieldProps + */ +export function useField(props: IUseFieldProps): IUseFieldResult { + const formContext = useFormContext(); + const fullName = useFullName(props.name); + + const { + label, + getSubmitValue = noopFieldValueFunction, + getDisplayValue = noopFieldValueFunction, + onChange = noopFunction, + onBlur = noopFunction, + asyncValidateOnChange = formContext.asyncValidateOnChange, + defaultValue = getDeepValue(fullName, formContext.defaultValues), + value = getDeepValue(fullName, formContext.values), + disabled = formContext.disabled, + plaintext = formContext.plaintext, + } = props; + + const [ fieldState, setFieldState ] = useState( + // Initialize the field state with an empty string provided to getDisplayValue + // Note: the correct defaultValue / value from props will be overriden in an + // effect later on + () => ({ touched: false, dirty: false, value: getDisplayValue('', { plaintext, disabled }) }) + ); + const { validationState, validate, resetValidation, updateValidationState } = useValidation(props); + + /** + * Contains the memoized value meta for passing to + * getSubmitValue and getDisplayValue + */ + const valueMeta: IValueMeta = useMemo(() => ({ + disabled, + plaintext, + }), [disabled, plaintext]); + + /** + * Returns the current submit value using the getSubmitValue + * callback and the correct value and value meta parameters + */ + const getFieldValue = useCallback((valueOverride?: TBasicFieldValue) => { + return getSubmitValue( + valueOverride !== undefined ? valueOverride : fieldState.value, + valueMeta, + ); + }, [getSubmitValue, fieldState.value, valueMeta]); + + /** + * Resets the field to its initial state. + */ + const resetField = useCallback( + () => { + const overridenValue = value === undefined ? defaultValue : value; + const displayValue = getDisplayValue( + overridenValue === undefined ? '' : overridenValue, + valueMeta, + ); + + setFieldState({ + value: displayValue, + touched: false, + dirty: false, + }); + resetValidation(); + onChange(displayValue); + }, + [value, defaultValue, getDisplayValue, valueMeta, resetValidation, onChange], + ); + + /** + * Calls the validation method with the current field value + */ + const validateField = useCallback( + async (args?: Partial): Promise => { + return validate( + getFieldValue(), + args, + ); + }, + [getFieldValue, validate], + ); + + /** + * Register / unregister the field in the form context + */ + const registerFieldState = useMemo(() => ({ + label, + isGroup: false, + updateValidation: updateValidationState, + validate: validateField, + reset: resetField, + getValue: getFieldValue, + }), [getFieldValue, label, resetField, updateValidationState, validateField]); + useFieldRegistration( + fullName, + registerFieldState, + ); + + /** + * Effect for overwriting the current field value with either + * defaultValue or value provided through the props / form context + */ + const oldPropValue = useRef(value); + useEffect(() => { + // If the Field.value / FormContext.value did not change and + // the field is dirty, do nothing + if (oldPropValue.current === value && fieldState.dirty) { + oldPropValue.current = value; + return; + } + + // Remember the prop value for later change checks + oldPropValue.current = value; + + // Check if we have either a defaultValue or a value + const overridenValue = value === undefined ? defaultValue : value; + if (overridenValue === undefined) return; + + // Update the field state with the overriden values + setFieldState({ + value: getDisplayValue(overridenValue, valueMeta), + touched: false, + dirty: false, + }); + }, [defaultValue, fieldState.dirty, getDisplayValue, value, valueMeta]); + + /** + * Handles the field change event - will run any validations, + * notify the form context about the change and update its + * internal state + */ + const handleFieldChanged = useCallback( + (event: IFieldChangedEvent) => { + const updatedValue = event.target.value; + setFieldState({ + value: updatedValue, + touched: true, + dirty: true, + }); + + const submitValue = getFieldValue(updatedValue); + + validate( + submitValue, + { checkAsync: asyncValidateOnChange }, + ); + + formContext.notifyFieldEvent(fullName, 'change', submitValue); + onChange(submitValue); + }, + [asyncValidateOnChange, formContext, fullName, getFieldValue, onChange, validate], + ); + + /** + * Handles the field blur event - will run validations if the + * field is dirty and the asyncValidateOnChange prop is false + * (otherwise the async validators would never be called). + * Notifies the form context and calls the onBlur callback + */ + const handleFieldBlur = useCallback( + () => { + if (fieldState.dirty && !asyncValidateOnChange) { + validate(getFieldValue()); + } + formContext.notifyFieldEvent(fullName, 'blur'); + onBlur(); + }, + [asyncValidateOnChange, fieldState.dirty, formContext, fullName, getFieldValue, onBlur, validate], + ); + + /** + * Memoize the field props which are designed to be used directly + * in an input like that: + * ```jsx + * + * ``` + * @see IFieldComponentFieldProps + */ + const fieldProps = useMemo( + (): IFieldComponentFieldProps => ({ + value: fieldState.value, + disabled, + id: fullName, + name: fullName, + onChange: handleFieldChanged, + onBlur: handleFieldBlur, + }), + [fieldState.value, disabled, fullName, handleFieldChanged, handleFieldBlur], + ); + + /** + * Memoize various meta informations + * @see IFieldComponentMeta for further details + */ + const metaProps = useMemo( + (): IFieldComponentMeta => ({ + valid: validationState.valid, + error: validationState.error, + isValidating: validationState.isValidating, + isRequired: validationState.isRequired, + touched: fieldState.touched, + stringFormatter: formContext.stringFormatter, + plaintext, + }), + [fieldState.touched, formContext.stringFormatter, plaintext, validationState.error, validationState.isRequired, validationState.isValidating, validationState.valid], + ); + + return { + fieldProps, + metaProps, + } +} diff --git a/src/components/withField/Field/Field.types.ts b/src/hooks/useField/useField.types.ts similarity index 55% rename from src/components/withField/Field/Field.types.ts rename to src/hooks/useField/useField.types.ts index 3e9de42..a007612 100644 --- a/src/components/withField/Field/Field.types.ts +++ b/src/hooks/useField/useField.types.ts @@ -1,20 +1,5 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ -import { TSTringFormatter } from '../../../utils/stringFormatter'; -import { IValidationProps, IValidationState } from '../../withValidation'; - -/** - * Type that defines which values a field could hold - */ -export type TBasicFieldValue = string | boolean | number | object; -/** - * Type definition for getDisplayValue and getSubmitValue callbacks - */ -export type TValueCallback = ((value: TBasicFieldValue, meta: IValueMeta) => TBasicFieldValue); +import { IValidationState, IUseValidationArgs } from '../useValidation'; +import { TSTringFormatter } from '../../utils'; /** * Information about the current meta state @@ -31,47 +16,73 @@ export interface IValueMeta { plaintext: boolean; } +/** + * Type that defines which values a field could hold + */ +export type TBasicFieldValue = string | boolean | number | object | null | undefined; +/** + * Type definition for getDisplayValue and getSubmitValue callbacks + */ +export type TValueCallback = ((value: TBasicFieldValue, meta: IValueMeta) => TBasicFieldValue); + /** * Basic props for the field component */ export interface IBaseFieldProps { /** - * Field name + * Name of this input. Will be used as the unique identifier of this value. + * **Must be unique inside its context (e.g. form wide or form group wide)!** */ name: string; /** - * Label (string or message id) + * Message id of the label that will be displayed along the input. If you + * don't want to use any i18n features you can pass a raw message instead. */ label: string; /** - * Optional default value + * Overwrites the Form default values for this field. This value will be + * used during form initialization. */ defaultValue?: TBasicFieldValue; /** - * Optional value + * Overwrites the Form value for this field. Changing this property will + * update the Field value, overwriting its default value but also any + * value the user put in. */ value?: TBasicFieldValue; /** - * True, if the async validators should be triggered - * on a change event + * If set to true the form will trigger asynchronous validation on this field whenever + * it changes (e.g. on key press). Default behaviour is that the fields will only async + * validate when they loose focus. + * @default Form.asyncValidateOnChange */ asyncValidateOnChange?: boolean; /** * Called, when the field is loading its value from the forms * default values. Must return the value to display. + * @param value Contains the current field value. + *
    + * @param meta Contains the properties disabled and plaintext, representing the current Form setup. + * @returns: the function should return the value that should be displayed. */ getDisplayValue?: TValueCallback; /** * Called, when the field is submitting its value to the form. * Must return the value to submit. + * @param value Contains the current field value. + *
    + * @param meta Contains the properties disabled and plaintext, representing the current Form setup. + * @returns: the function should return the value that should be submitted. */ getSubmitValue?: TValueCallback; /** - * Disables this field. + * Overwrites the disabled state for this field. + * @default Form.disabled */ disabled?: boolean; /** - * Puts the field in plaintext mode. + * Overwrites the plaintext state for this field. + * @default Form.plaintext */ plaintext?: boolean; /** @@ -85,18 +96,6 @@ export interface IBaseFieldProps { onChange?(value: TBasicFieldValue): void; } -/** - * Props for the Field component - */ -export interface IFieldProps extends IBaseFieldProps, IValidationProps { - /** - * Render prop for the input element - * @param field Props designed to be passed to the field as is @see IFieldComponentFieldProps - * @param meta Meta information about the field state - */ - render(field: IFieldComponentFieldProps, meta: IFieldComponentMeta): JSX.Element; -} - /** * Event for field value changes */ @@ -157,21 +156,15 @@ export interface IFieldComponentMeta extends IValidationState { plaintext: boolean; } -/** - * Props for the input component of a Field - */ -export interface IFieldComponentProps { - /** - * Props for the actual html input, designed - * to be passed as-is - */ - field: IFieldComponentFieldProps; - /** - * Meta informations about the field state - */ - meta: IFieldComponentMeta; - /** - * Label (string or message id) - */ - label: string; +export interface IUseFieldProps extends IBaseFieldProps, IUseValidationArgs { } + +export interface IUseFieldResult { + fieldProps: IFieldComponentFieldProps; + metaProps: IFieldComponentMeta; +} + +export interface IUseFieldState { + touched: boolean; + dirty: boolean; + value: TBasicFieldValue; } diff --git a/src/hooks/useField/useField.utils.ts b/src/hooks/useField/useField.utils.ts new file mode 100644 index 0000000..a59a582 --- /dev/null +++ b/src/hooks/useField/useField.utils.ts @@ -0,0 +1,9 @@ +import { TBasicFieldValue } from './useField.types'; + +/** + * Function that will directly return the passed value + * @param value Field value + */ +export function noopFieldValueFunction(value: TBasicFieldValue): TBasicFieldValue { + return value; +} diff --git a/src/hooks/useFormContext/index.ts b/src/hooks/useFormContext/index.ts new file mode 100644 index 0000000..0789566 --- /dev/null +++ b/src/hooks/useFormContext/index.ts @@ -0,0 +1 @@ +export * from './useFormContext'; diff --git a/src/hooks/useFormContext/useFormContext.md b/src/hooks/useFormContext/useFormContext.md new file mode 100644 index 0000000..b70c5c0 --- /dev/null +++ b/src/hooks/useFormContext/useFormContext.md @@ -0,0 +1,19 @@ +The `useFormContext` hook provides an easy way to get the current form context. +Please refer to the [FormContext API](bar) for further details. + +### Usage +```jsx static +import { useFormContext } from 'react-ocean-forms'; + +function FancyComponent() { + const formContext = useFormContext(); + + function handleClick() { + formContext.submit(); + } + + return ( + + ) +} +``` diff --git a/src/hooks/useFormContext/useFormContext.test.ts b/src/hooks/useFormContext/useFormContext.test.ts new file mode 100644 index 0000000..8eb62a8 --- /dev/null +++ b/src/hooks/useFormContext/useFormContext.test.ts @@ -0,0 +1,22 @@ +import { useContext } from 'react'; + +import { useFormContext } from './useFormContext'; + +jest.mock('react'); + +describe('useFormContext', () => { + it('should return the correct form context', () => { + const fakeContext = { fake: true }; + (useContext).mockReturnValueOnce(fakeContext); + + expect(useFormContext()).toBe(fakeContext); + }); + + it('should throw an error if no form context could be found', () => { + (useContext).mockReturnValueOnce(undefined); + + expect(() => { useFormContext(); }).toThrowError( + '[useFormContext]: Could not find form context. This component must be used inside a
    tag.', + ); + }); +}); diff --git a/src/components/FormContext/FormContext.hooks.ts b/src/hooks/useFormContext/useFormContext.ts similarity index 62% rename from src/components/FormContext/FormContext.hooks.ts rename to src/hooks/useFormContext/useFormContext.ts index fe4463f..a9d5abb 100644 --- a/src/components/FormContext/FormContext.hooks.ts +++ b/src/hooks/useFormContext/useFormContext.ts @@ -6,14 +6,17 @@ */ import { useContext } from 'react'; -import { FormContext } from './FormContext'; -import { IFieldValues, IFormContext } from './FormContext.types'; +import { IFieldValues, IFormContext, FormContext } from '../../components/FormContext'; -export const useFormContext = (): IFormContext => { +/** + * Returns the FormContext of the current closure. + * Throws an error if no form context could be found. + */ +export function useFormContext(): IFormContext { const context = useContext( | undefined>>FormContext); if (context === undefined) { throw new Error('[useFormContext]: Could not find form context. This component must be used inside a tag.'); } return context; -}; +} diff --git a/src/hooks/useFormEventListener/index.ts b/src/hooks/useFormEventListener/index.ts new file mode 100644 index 0000000..a1f7d3a --- /dev/null +++ b/src/hooks/useFormEventListener/index.ts @@ -0,0 +1 @@ +export * from './useFormEventListener'; diff --git a/src/hooks/useFormEventListener/useFormEventListener.md b/src/hooks/useFormEventListener/useFormEventListener.md new file mode 100644 index 0000000..bbaac90 --- /dev/null +++ b/src/hooks/useFormEventListener/useFormEventListener.md @@ -0,0 +1,25 @@ +The `useFormEventListener` hook registers the given callback to form events +and automatically unregisters it on cleanup. + +### Parameters +- **id**: unique event listener id +- **callback**: function to call on each event + +### Callback method +The callback method will be called with the parameters: +- **name**: Name of the event emitter +- **event**: Event name +- **args**: (Optional) event args + +### Usage +```jsx static +import { useFormEventListener } from 'react-ocean-forms'; + +function ListenerComponent() { + useFormEventListener('eventLogger', (name, event, args) => { + console.log(`Field ${name} triggered event ${event} with args ${args}`); + }); + + // ... +} +``` diff --git a/src/hooks/useFormEventListener/useFormEventListener.test.ts b/src/hooks/useFormEventListener/useFormEventListener.test.ts new file mode 100644 index 0000000..1332f1c --- /dev/null +++ b/src/hooks/useFormEventListener/useFormEventListener.test.ts @@ -0,0 +1,73 @@ +import { renderHook } from 'react-hooks-testing-library'; + +import { useFormContext } from '../useFormContext'; +import { useFormEventListener } from './useFormEventListener'; + +jest.mock('../useFormContext'); + +interface ISetupResult { + registerListener: jest.Mock; + unregisterListener: jest.Mock; + fullName: string; + mockListener: jest.Mock; + rerender(): void; + unmount(): boolean; +} + +function setup(): ISetupResult { + const registerListener = jest.fn(); + const unregisterListener = jest.fn(); + const mockListener = jest.fn(); + + (useFormContext as jest.Mock).mockReturnValue({ + registerListener, + unregisterListener, + }); + + const fullName = 'mock-name'; + + const { rerender, unmount } = renderHook(() => useFormEventListener( + fullName, + mockListener, + )); + + return { + registerListener, + unregisterListener, + mockListener, + fullName, + rerender, + unmount, + } +} + +describe('useFormEventListener', () => { + it('should call formContext.registerListener with the correct values', () => { + const { mockListener, fullName, registerListener, unregisterListener } = setup(); + + expect(registerListener).toHaveBeenCalledTimes(1); + expect(registerListener).toHaveBeenCalledWith(fullName, mockListener); + expect(unregisterListener).not.toHaveBeenCalled(); + }); + + it('should call formContext.unregisterField on unmount', () => { + const { unregisterListener, fullName, unmount } = setup(); + + unmount(); + + expect(unregisterListener).toHaveBeenCalledTimes(1); + expect(unregisterListener).toHaveBeenCalledWith(fullName); + }); + + it('should not re-register itself if nothing has changed', () => { + const { registerListener, unregisterListener, rerender } = setup(); + + registerListener.mockClear(); + unregisterListener.mockClear(); + + rerender(); + + expect(unregisterListener).not.toHaveBeenCalled(); + expect(registerListener).not.toHaveBeenCalled(); + }); +}); diff --git a/src/hooks/useFormEventListener/useFormEventListener.ts b/src/hooks/useFormEventListener/useFormEventListener.ts new file mode 100644 index 0000000..4a9de57 --- /dev/null +++ b/src/hooks/useFormEventListener/useFormEventListener.ts @@ -0,0 +1,24 @@ +import { useEffect } from 'react'; + +import { useFormContext } from '../useFormContext'; +import { TFormEventListener } from '../internal'; + +/** + * Hook for registering listeners for form events. Will + * automatically unregister the listener on unmount. + * @param id Unique listener id + * @param callback Callback @see TFormEventListener + */ +export function useFormEventListener( + id: string, + callback: TFormEventListener +): void { + const formContext = useFormContext(); + useEffect(() => { + formContext.registerListener(id, callback); + + return () => { + formContext.unregisterListener(id); + }; + }, [formContext, id, callback]); +} diff --git a/src/hooks/useValidation/index.ts b/src/hooks/useValidation/index.ts new file mode 100644 index 0000000..d8cea96 --- /dev/null +++ b/src/hooks/useValidation/index.ts @@ -0,0 +1,2 @@ +export * from './useValidation'; +export * from './useValidation.types'; diff --git a/src/hooks/useValidation/useValidation.md b/src/hooks/useValidation/useValidation.md new file mode 100644 index 0000000..79c7121 --- /dev/null +++ b/src/hooks/useValidation/useValidation.md @@ -0,0 +1 @@ +useValidation! diff --git a/src/components/withValidation/ValidationWrapper/ValidationWrapper.test.tsx b/src/hooks/useValidation/useValidation.test.ts similarity index 51% rename from src/components/withValidation/ValidationWrapper/ValidationWrapper.test.tsx rename to src/hooks/useValidation/useValidation.test.ts index 949a977..f865f20 100644 --- a/src/components/withValidation/ValidationWrapper/ValidationWrapper.test.tsx +++ b/src/hooks/useValidation/useValidation.test.ts @@ -1,76 +1,65 @@ -import React from 'react'; +import { renderHook, act } from 'react-hooks-testing-library'; -import { shallow, ShallowWrapper } from 'enzyme'; +import { IFormContext } from '../../components'; +import { createMockFormContext } from '../../test-utils/enzymeFormContext'; +import { TValidator, validators as defaultValidators } from '../../validators'; -import { createMockFormContext } from '../../../test-utils/enzymeFormContext'; -import { TValidator, validators as defaultValidators } from '../../../validators'; -import { IFormContext } from '../../FormContext'; -import { IValidationProp, IValidationState, IValidationWrapperProps } from '../withValidation.types'; -import { BaseValidationWrapper } from './ValidationWrapper'; +import { useFormContext } from '../useFormContext'; +import { useFullName } from '../internal/useFullName'; -describe('withValidation', () => { +import { useValidation } from './useValidation'; +import { IUseValidationArgs, IUseValidationResult } from './useValidation.types'; + +jest.mock('../useFormContext'); +jest.mock('../internal/useFullName'); + +describe('useValidation', () => { const fieldName = 'unitField'; interface ISetupArgs { - props?: Partial; + props?: Partial; contextOverrides?: Partial; } interface ISetupResult { formContext: IFormContext; - validation: IValidationProp; - wrapper: ShallowWrapper; - fullName: string; + + unmount(): boolean; + rerender(): void; + waitForNextUpdate(): Promise; + result: { current: IUseValidationResult }; } - const setup = ({ - props, - contextOverrides, - }: Partial = {}): ISetupResult => { + const setup = ({ props, contextOverrides }: Partial = {}): ISetupResult => { + (useFullName as jest.Mock).mockImplementation((name: string) => name); const formContext: IFormContext = { ...createMockFormContext(), ...contextOverrides, }; + (useFormContext as jest.Mock).mockReturnValue(formContext); - let fullName: string; - let validation: IValidationProp; - - const renderCallback = (cFullName: string, cValidation: IValidationProp): JSX.Element => { - fullName = cFullName; - validation = cValidation; - - return ( -
    - ); + const fullProps: IUseValidationArgs = { + name: fieldName, + label: fieldName, + ...props, }; - - const wrapper = shallow(( - - )); + const { result, unmount, rerender, waitForNextUpdate } = renderHook(() => useValidation(fullProps)); return { formContext, - // @ts-ignore - validation, - wrapper, - // @ts-ignore - fullName, + + unmount, + rerender, + result, + waitForNextUpdate, }; }; - it('should render without error', () => { - const { wrapper } = setup(); - expect(wrapper).toMatchSnapshot(); - }); - it('should return a valid state without validators', async () => { - const { validation } = setup(); - await expect(validation.validate('foo')).resolves.toMatchObject({ + const { result } = setup(); + + await act(async () => { result.current.validate('foo') }); + expect(result.current.validationState).toMatchObject({ error: null, isValidating: false, valid: true, @@ -78,32 +67,35 @@ describe('withValidation', () => { }); const mockValue = 'foobar'; - const checkNotifyCalled = (formContext: IFormContext, state: IValidationState): void => { + const checkNotifyCalled = (formContext: IFormContext, state: any): void => { expect(formContext.notifyFieldEvent).toHaveBeenLastCalledWith( fieldName, 'validation', - state, + { + ...state, + label: fieldName, + }, ); }; - const getAsyncTimeout = (wrapper: ShallowWrapper): number | undefined => (wrapper.instance() as BaseValidationWrapper).getAsyncTimeout(); - describe('sync validation', () => { it('should call the sync validators and return a validation state', async () => { const validator = jest.fn().mockReturnValue(undefined); - const { validation, formContext } = setup({ props: { + const { result, formContext } = setup({ props: { validators: [validator], }}); - const state = await validation.validate(mockValue); - expect(state).toMatchObject({ - isValidating: false, - valid: true, - error: null, - }); + await act(async () => { + const state = await result.current.validate(mockValue); + expect(state).toMatchObject({ + isValidating: false, + valid: true, + error: null, + }); - checkNotifyCalled(formContext, state); + checkNotifyCalled(formContext, state); + }); }); it('should stop at the first invalid validator', async () => { @@ -114,11 +106,13 @@ describe('withValidation', () => { jest.fn().mockReturnValue(undefined), ]; - const { validation } = setup({ props: { + const { result } = setup({ props: { validators, }}); - await validation.validate(mockValue); + await act(async () => { + await result.current.validate(mockValue); + }); expect(validators[0]).toHaveBeenCalledTimes(1); expect(validators[1]).toHaveBeenCalledTimes(1); @@ -129,12 +123,14 @@ describe('withValidation', () => { const validator = jest.fn().mockReturnValue('error'); const asyncValidator = jest.fn().mockResolvedValue(undefined); - const { validation } = setup({ props: { + const { result } = setup({ props: { validators: [validator], asyncValidators: [asyncValidator], }}); - await validation.validate(mockValue); + await act(async () => { + await result.current.validate(mockValue); + }); expect(asyncValidator).not.toHaveBeenCalled(); }); @@ -176,11 +172,11 @@ describe('withValidation', () => { ]; it.each(cases)('%s', (name: string, validators: undefined | TValidator[], expectedIsRequiredState: unknown) => { - const { validation } = setup({ props: { + const { result } = setup({ props: { validators, }}); - expect(validation.isRequired).toEqual(expectedIsRequiredState); + expect(result.current.validationState.isRequired).toEqual(expectedIsRequiredState); }); }); }); @@ -199,82 +195,88 @@ describe('withValidation', () => { it('should ignore async validators if checkAsync is false', async () => { const asyncValidator = jest.fn().mockResolvedValue(undefined); - const { validation, formContext } = setup({ props: { + const { result, formContext } = setup({ props: { asyncValidators: [asyncValidator], }}); - const state = await validation.validate(mockValue, { checkAsync: false }); - expect(state).toMatchObject({ - isValidating: false, - valid: true, - error: null, - }); - expect(asyncValidator).not.toHaveBeenCalled(); + await act(async () => { + const state = await result.current.validate(mockValue, { checkAsync: false }); + expect(state).toMatchObject({ + isValidating: false, + valid: true, + error: null, + }); + expect(asyncValidator).not.toHaveBeenCalled(); - checkNotifyCalled(formContext, state); + checkNotifyCalled(formContext, state); + }); }); it('should immediately run the validators if immediateAsync is true', async () => { const errorId = 'mockError'; const asyncValidator = jest.fn().mockResolvedValue(errorId); - const { validation, formContext } = setup({ props: { + const { result, formContext } = setup({ props: { asyncValidators: [asyncValidator], }}); - const state = await validation.validate(mockValue, { immediateAsync: true }); - expect(state).toMatchObject({ - isValidating: false, - valid: false, - error: [{ - message_id: errorId, - params: {}, - }], - }); - expect(asyncValidator).toHaveBeenCalledTimes(1); + await act(async () => { + const state = await result.current.validate(mockValue, { immediateAsync: true }); + expect(state).toMatchObject({ + isValidating: false, + valid: false, + error: [{ + message_id: errorId, + params: {}, + }], + }); + expect(asyncValidator).toHaveBeenCalledTimes(1); - checkNotifyCalled(formContext, state); + checkNotifyCalled(formContext, state); + }); }); - it('should wait for the default amount until triggering the async validators', async (done) => { + it('should wait for the default amount until triggering the async validators', async () => { const errorId = 'mockError'; const asyncValidator = jest.fn().mockResolvedValue(errorId); - const { wrapper, validation, formContext } = setup({ props: { + const { formContext, result } = setup({ props: { asyncValidators: [asyncValidator], }}); const spiedTimeout = jest.spyOn(window, 'setTimeout'); - const state = await validation.validate(mockValue); - expect(state).toMatchObject({ - isValidating: true, - valid: true, - error: null, - }); - expect(asyncValidator).not.toHaveBeenCalled(); - expect(getAsyncTimeout(wrapper)).toBeGreaterThan(0); - checkNotifyCalled(formContext, state); + await act(async () => { + const state = await result.current.validate(mockValue); - expect(spiedTimeout).toHaveBeenCalledWith( - expect.any(Function), - formContext.asyncValidationWait, - ); - - jest.runAllTimers(); - - process.nextTick(() => { expect(state).toMatchObject({ - isValidating: false, - valid: false, - error: [{ - message_id: errorId, - params: {}, - }], + isValidating: true, + valid: true, + error: null, }); - expect(asyncValidator).toHaveBeenCalledTimes(1); + expect(asyncValidator).not.toHaveBeenCalled(); checkNotifyCalled(formContext, state); - done(); + expect(formContext.notifyFieldEvent).toHaveBeenCalledTimes(1); + + expect(spiedTimeout).toHaveBeenCalledWith( + expect.any(Function), + formContext.asyncValidationWait, + ); + }); + + await act(async () => { + jest.runAllTimers(); + expect(asyncValidator).toHaveBeenCalledTimes(1); + }); + + expect(formContext.notifyFieldEvent).toHaveBeenCalledTimes(2); + expect(result.current.validationState).toMatchObject({ + isValidating: false, + valid: false, + error: [{ + message_id: errorId, + params: {}, + }], }); }); @@ -282,74 +284,83 @@ describe('withValidation', () => { const errorId = 'mockError'; const asyncValidator = jest.fn().mockResolvedValue(errorId); - const { validation } = setup({ props: { + const { result } = setup({ props: { asyncValidators: [asyncValidator], asyncValidationWait: 42, }}); - const spiedTimeout = jest.spyOn(window, 'setTimeout'); - await validation.validate(mockValue); + await act(async () => { + const spiedTimeout = jest.spyOn(window, 'setTimeout'); + await result.current.validate(mockValue); - expect(spiedTimeout).toHaveBeenCalledWith( - expect.any(Function), - 42, - ); + expect(spiedTimeout).toHaveBeenCalledWith( + expect.any(Function), + 42, + ); + }); }); it('should clear any existing timeout if validate is called again', async () => { const errorId = 'mockError'; const asyncValidator = jest.fn().mockResolvedValue(errorId); - const { wrapper, validation } = setup({ props: { + const { result } = setup({ props: { asyncValidators: [asyncValidator], }}); - expect(getAsyncTimeout(wrapper)).toBeUndefined(); - - const state1 = await validation.validate(mockValue); - const timeout1 = getAsyncTimeout(wrapper); - - const state2 = await validation.validate(mockValue); - const timeout2 = getAsyncTimeout(wrapper); - - expect(state1).toMatchObject({ - isValidating: true, + await act(async () => { + const state1 = await result.current.validate(mockValue); + expect(state1).toMatchObject({ + isValidating: true, + }); }); - expect(state2).toMatchObject({ - isValidating: true, + expect(asyncValidator).toHaveBeenCalledTimes(0); + + await act(async () => { + const state2 = await result.current.validate(mockValue); + expect(state2).toMatchObject({ + isValidating: true, + }); }); - expect(timeout1).not.toEqual(timeout2); - jest.runAllTimers(); + await act(async () => { + jest.runAllTimers(); + expect(asyncValidator).toHaveBeenCalledTimes(1); + }); }); it('should set validationState.error to null if it is an empty array after filtering invalid errors out', async () => { const asyncValidator = jest.fn().mockResolvedValue({ foo: 'bar' }); - const { validation } = setup({ props: { + const { result } = setup({ props: { asyncValidators: [asyncValidator], }}); - const state = await validation.validate(mockValue, { immediateAsync: true }); - expect(state).toMatchObject({ - isValidating: false, - valid: true, - error: null, + await act(async () => { + const state = await result.current.validate(mockValue, { immediateAsync: true }); + expect(state).toMatchObject({ + isValidating: false, + valid: true, + error: null, + }); }); }); }); describe('form context callbacks', () => { it('should update the validation state if called for', () => { - const { formContext, validation } = setup(); + const { formContext, result } = setup(); const mockError = { message_id: 'dummy', params: {}, }; - validation.update({ - valid: false, - error: mockError, + + act(() => { + result.current.updateValidationState({ + valid: false, + error: mockError, + }); }); checkNotifyCalled( formContext, @@ -357,80 +368,34 @@ describe('withValidation', () => { valid: false, error: mockError, isValidating: false, - isRequired: false, }, ); }); - it('should correctly reset the validation state', () => { - const { wrapper, formContext, validation } = setup(); - - validation.reset(); - checkNotifyCalled( - formContext, - { - valid: true, - error: null, - isValidating: false, - isRequired: false, - }, - ); - expect(getAsyncTimeout(wrapper)).toBeUndefined(); + it('should correctly reset the validation state', async () => { + const { formContext, result } = setup(); - // Edge case where we check if the timeout has - // been cleared if a validation is in progress - // when we call reset - void validation.validate(mockValue); - validation.reset(); + act(() => { + result.current.resetValidation(); + }); checkNotifyCalled( formContext, { valid: true, error: null, isValidating: false, - isRequired: false, }, ); - expect(getAsyncTimeout(wrapper)).toBeUndefined(); - }); - }); - - it('should clear any timeouts on unmount', () => { - const { wrapper, validation } = setup({ - props: { - asyncValidators: [ jest.fn().mockResolvedValue(undefined) ], - }, }); - - void validation.validate(mockValue); - expect(getAsyncTimeout(wrapper)).not.toBeUndefined(); - - const oldInstance = wrapper.instance() as BaseValidationWrapper; - wrapper.unmount(); - - const asyncTimeout = oldInstance.getAsyncTimeout(); - expect(asyncTimeout).toBe(undefined); - }); - - it('should adapt the fieldPrefix to the fullName', () => { - const { fullName } = setup({ - contextOverrides: { - fieldPrefix: 'unit', - }, - }); - - expect(fullName).toBe(`unit.${fieldName}`); }); it('should not update its state after unmounting', () => { - const { wrapper, formContext } = setup(); + const { result, formContext, unmount } = setup(); - const oldInstance = wrapper.instance() as BaseValidationWrapper; - wrapper.unmount(); + unmount(); expect(() => { - // @ts-ignore - oldInstance.updateAndNotify({ foo: 'bar' }); + result.current.updateValidationState({ isValidating: true }); }).not.toThrowError(); expect(formContext.notifyFieldEvent).not.toHaveBeenCalled(); }); diff --git a/src/hooks/useValidation/useValidation.ts b/src/hooks/useValidation/useValidation.ts new file mode 100644 index 0000000..7856a1a --- /dev/null +++ b/src/hooks/useValidation/useValidation.ts @@ -0,0 +1,135 @@ +/** + * Copyright (c) 2019-present, Umweltbundesamt GmbH + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useCallback, useState, useMemo } from 'react'; + +import { useFormContext } from '../useFormContext'; +import { TBasicFieldValue } from '../useField'; +import { useIsUnmounted, useTimeout, useFullName } from '../internal'; + +import { IBasicValidationState, IUseValidationResult, IValidationArgs, IUseValidationArgs } from './useValidation.types'; +import { createInitialValidationState, isRequired, runSyncValidators, runAsyncValidators } from './useValidation.utils'; + +export function useValidation(args: IUseValidationArgs): IUseValidationResult { + const formContext = useFormContext(); + const isUnmounted = useIsUnmounted(); + const [validationState, setValidationState] = useState(createInitialValidationState()); + const [setAsyncTimeout, clearAsyncTimeout] = useTimeout(); + const fullName = useFullName(args.name); + + const { + label, + validators, + asyncValidators, + asyncValidationWait = formContext.asyncValidationWait, + } = args; + + /** + * Updates the current validation state and raises + * a `validation` form event for the current field + */ + const updateAndNotify = useCallback( + (newState: Partial): void => { + if (isUnmounted.current) return; + + setValidationState((prevState) => { + const fullNewState = { + ...prevState, + ...newState, + }; + + formContext.notifyFieldEvent(fullName, 'validation', { ...fullNewState, label }); + return fullNewState; + }); + }, + [isUnmounted, formContext, fullName, label], + ); + + /** + * Resets the validation state to the initial value and + * clears any pending async validations + */ + const reset = useCallback( + () => { + clearAsyncTimeout(); + updateAndNotify(createInitialValidationState()); + }, + [clearAsyncTimeout, updateAndNotify], + ); + + /** + * Performs the sync and async validation logic + */ + const validate = useCallback( + async (value: TBasicFieldValue, { + checkAsync = true, + immediateAsync = false, + }: Partial = {} ): + Promise => { + let temporaryValidationState = createInitialValidationState(); + + // Clear the old timeout so we only run the + // async validators after the waiting period + // when the value didn't change in the meantime + clearAsyncTimeout(); + + // No validators - nothing to do here + if (!Array.isArray(validators) && !Array.isArray(asyncValidators)) { + setValidationState(temporaryValidationState); + return temporaryValidationState; + } + + // Update the local validation state with the results of sync validation + temporaryValidationState = { + ...temporaryValidationState, + ...runSyncValidators(validators, value, formContext), + }; + + // Ignore async validation if sync validation is already false + // or checkAsync is disabled or there are no async validators + if (temporaryValidationState.valid === false || !checkAsync || !Array.isArray(asyncValidators)) { + updateAndNotify(temporaryValidationState); + return temporaryValidationState; + } + + // Calls the async validators, updates and notifies the field / form + // about the validation results + const performAsyncValidation = async (): Promise => { + clearAsyncTimeout(); + + const asyncValidationResult = await runAsyncValidators(asyncValidators, value, formContext); + updateAndNotify(asyncValidationResult); + return asyncValidationResult; + }; + + // Execute async validators immediatly if configured + if (immediateAsync === true) { + return performAsyncValidation(); + } + + // Sets a timeout to run the async validation and notifies the form + // about the is validating status + setAsyncTimeout(performAsyncValidation, asyncValidationWait); + temporaryValidationState.isValidating = true; + updateAndNotify(temporaryValidationState); + return temporaryValidationState; + }, + [clearAsyncTimeout, validators, asyncValidators, setAsyncTimeout, asyncValidationWait, updateAndNotify, formContext], + ); + + const validationResult = useMemo(() => ({ + validationState: { + ...validationState, + isRequired: isRequired(validators), + }, + validate, + resetValidation: reset, + updateValidationState: updateAndNotify, + }), [reset, updateAndNotify, validate, validationState, validators]); + + return validationResult; +} diff --git a/src/hooks/useValidation/useValidation.types.ts b/src/hooks/useValidation/useValidation.types.ts new file mode 100644 index 0000000..8d8a188 --- /dev/null +++ b/src/hooks/useValidation/useValidation.types.ts @@ -0,0 +1,124 @@ +import { TFieldErrors, TValidator, TAsyncValidator } from '../../validators'; +import { TBasicFieldValue } from '../useField'; + +export interface IBasicValidationState { + /** + * True, if the field is currently validating + * (asynchronous validation running in background) + */ + isValidating: boolean; + /** + * True, if all validators report a valid state + */ + valid: boolean; + /** + * Contains any errors if available + */ + error: TFieldErrors; +} + +/** + * Arguments for the validate method + */ +export interface IValidationArgs { + /** + * True, if the async validators should + * be triggered as well, otherwise only + * the sync validators are triggered + * + * Default: true + */ + checkAsync: boolean; + /** + * True, if the async validators should + * be triggered without any delay + * + * Default: false + */ + immediateAsync: boolean; +} + +export interface IValidationState extends IBasicValidationState { + /** + * True, if the field is a required field + * (has a required validator attached) + */ + isRequired: boolean; +} + +/** + * Validation method + * @param value Value to validate + * @param args Optional validation args, @see IValidationArgs + */ +export type TValidateMethod = (value: TBasicFieldValue, args?: Partial) => Promise; +export type TResetMethod = () => void; +/** + * Update validation state method + * @param state Validation state overrides + */ +export type TUpdateMethod = (state: Partial) => void; + +/** + * Result of the useValidation hook + */ +export interface IUseValidationResult { + /** + * Current validation state + */ + validationState: IValidationState; + /** + * Triggers the validation + * @see TValidateMethod + */ + validate: TValidateMethod; + /** + * Resets the validation to default + */ + resetValidation: TResetMethod; + /** + * Overwrites the validation state + * @see TUpdateMethod + */ + updateValidationState: TUpdateMethod; +} + +/** + * Properties of a component that is wrapped + * by withValidation + */ +export interface IUseValidationArgs { + /** + * Name of this input. Will be used as the unique identifier of this value. + * **Must be unique inside its context (e.g. form wide or form group wide)!** + */ + name: string; + /** + * Message id of the label that will be displayed along the input. If you don't + * want to use any i18n features you can pass a raw message instead. + */ + label: string; + /** + * Contains an array of functions that will validate this input. Those functions are called whenever + * the value changes (on keypress, ...). They are called in order and whenever one fails the other ones + * are not called. The validator function must return either undefined or a string containing the message + * id of the validation error text. + */ + validators?: TValidator[]; + /** + * Contains an array of functions that must return a Promise. Those functions are called by default onBlur, + * however this behaviour can be changed by setting asyncValidateOnChange on the Form. The Form will call + * all async validators of a Field at the same time and will wait for the result of every one of them. If + * one of them returns a string the field will be marked as invalid. Per default the form will wait for 400ms + * before triggering any validation. This is put in place so the validation won't get triggered on every + * keystroke of the user. The async validators will be called 400ms after the last value change. + */ + asyncValidators?: TAsyncValidator[]; + /** + * Wait time in ms that should pass after + * the last user input before the async + * validators will be triggered + * @default Form.asyncValidationWait + */ + asyncValidationWait?: number; +} diff --git a/src/hooks/useValidation/useValidation.utils.ts b/src/hooks/useValidation/useValidation.utils.ts new file mode 100644 index 0000000..28b0b24 --- /dev/null +++ b/src/hooks/useValidation/useValidation.utils.ts @@ -0,0 +1,86 @@ +import { TValidator, isDefaultValidator, isIFieldErrorObject, TAsyncValidator } from '../../validators'; +import { IBasicValidationState } from './useValidation.types'; +import { TBasicFieldValue } from '../useField'; +import { IFormContext } from '../../components'; +import { parseValidationError } from '../../utils'; + +/** + * Checks if the given validators contain at least one default + * validator + * @param validators Sync validators provided through props + */ +export function isRequired(validators?: TValidator[]): boolean { + return Array.isArray(validators) && validators.some(isDefaultValidator); +} + +/** + * Creates the initial / default validation state of a + * validated component + * @param validators Sync validators provied through props + */ +export function createInitialValidationState(): IBasicValidationState { + return { + valid: true, + error: null, + isValidating: false, + } +} + +/** + * Executes the sync validators and returns updates + * to the validation state + * @param validators Array of validator functions + * @param value Value to be validated + * @param formContext Form context + */ +export function runSyncValidators( + validators: TValidator[] | undefined, + value: TBasicFieldValue, + formContext: IFormContext, +): Partial { + // No sync validators given - do nothing + if (!Array.isArray(validators)) return { }; + + for (let i = 0; i < validators.length; i++) { + const validator = validators[i]; + + const result = validator(value, formContext); + const parsedResult = parseValidationError(result); + if (isIFieldErrorObject(parsedResult)) { + return { + valid: false, + error: parsedResult, + }; + } + } + + return { }; +} + +/** + * Executes the async validators and returns the validation + * state based on the results + * @param validators Array of async validator functions + * @param value Value to be validated + * @param formContext Form context + */ +export async function runAsyncValidators( + validators: TAsyncValidator[], + value: TBasicFieldValue, + formContext: IFormContext, +): Promise { + const validationResults = await Promise.all(validators.map( + async validator => validator(value, formContext), + )) + const parsedErrors = validationResults.map(result => parseValidationError(result)).filter(isIFieldErrorObject); + + if (parsedErrors.length === 0) { + return createInitialValidationState(); + } + + return { + valid: false, + isValidating: false, + error: parsedErrors, + }; +} diff --git a/src/index.test.ts b/src/index.test.ts index b48738b..9493a15 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -5,7 +5,8 @@ describe('Index', () => { const components = [ 'withField', 'FieldError', 'FieldGroup', 'FieldLine', 'Form', 'FormButton', 'FormContext', 'FormText', - 'Input', 'ValidationSummary', 'withForm', 'withValidation', + 'Input', 'ValidationSummary', 'withForm', + 'useField', 'useFormContext', 'useValidation', ]; components.forEach((component) => { diff --git a/src/index.ts b/src/index.ts index d4bbf75..1062eea 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,19 +4,7 @@ * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ - -export * from './components/FieldError'; -export * from './components/FieldGroup'; -export * from './components/FieldLine'; -export * from './components/Form'; -export * from './components/FormButton'; -export * from './components/FormContext'; -export * from './components/FormText'; -export * from './components/Input'; -export * from './components/ValidationSummary'; -export * from './components/withField'; -export * from './components/withForm'; -export * from './components/withValidation'; - -export * from './utils'; +export * from './components'; +export { addCustomMessages, stringFormatter, parseValidationError, IMessageValues, PropsOf, Subtract } from './utils'; export * from './validators'; +export * from './hooks'; diff --git a/src/test-utils/enzymeFormContext.tsx b/src/test-utils/enzymeFormContext.tsx index 40d6cd4..4c3f15c 100644 --- a/src/test-utils/enzymeFormContext.tsx +++ b/src/test-utils/enzymeFormContext.tsx @@ -1,6 +1,7 @@ -import { IFieldState, IFormContext, TFormEventListener } from '../components/FormContext'; -import { IValidationProp } from '../components/withValidation'; +import { IFormContext } from '../components'; import { stringFormatter } from '../utils'; +import { IUseValidationResult } from '../hooks'; +import { IFieldState, TFormEventListener } from '../hooks/internal'; /** * Components inside the form module require access to the form context. @@ -35,6 +36,7 @@ export const createMockFormContext = (registerCallback?: Function): IFormContext stringFormatter: jest.fn().mockImplementation(stringFormatter), submit: jest.fn(), + reset: jest.fn(), busy: false, disabled: false, @@ -49,13 +51,15 @@ export const createMockFormContext = (registerCallback?: Function): IFormContext * Creates a validation object mocking the * withValidation hoc */ -export const createMockValidation = (): IValidationProp => ({ +export const createMockValidationResult = (): IUseValidationResult => ({ validate: jest.fn(), - reset: jest.fn(), - update: jest.fn(), + resetValidation: jest.fn(), + updateValidationState: jest.fn(), - isValidating: false, - isRequired: false, - valid: true, - error: null, + validationState: { + isValidating: false, + isRequired: false, + valid: true, + error: null, + }, }); diff --git a/src/utils/__tests__/parseValidationError.test.ts b/src/utils/__tests__/parseValidationError.test.ts index 9fd7a0b..e52460b 100644 --- a/src/utils/__tests__/parseValidationError.test.ts +++ b/src/utils/__tests__/parseValidationError.test.ts @@ -1,19 +1,17 @@ import { parseValidationError } from '../parseValidationError'; describe('parseValidationError', () => { - const fieldName = 'fieldname'; - it('should pass an error object right through', () => { const error = { message_id: 'foobar', params: {}, }; - expect(parseValidationError(fieldName, error)).toBe(error); + expect(parseValidationError(error)).toBe(error); }); it('should convert a string to a valid error object', () => { const error = 'error_id'; - expect(parseValidationError(fieldName, error)).toEqual({ + expect(parseValidationError(error)).toEqual({ message_id: error, params: {}, }); @@ -22,11 +20,11 @@ describe('parseValidationError', () => { it('should return null if an invalid error was provided', () => { const error = { foo: 'bar' }; // @ts-ignore - expect(parseValidationError(fieldName, error)).toBeNull(); + expect(parseValidationError(error)).toBeNull(); }); it('should return null if an invalid type was provided', () => { // @ts-ignore - expect(parseValidationError(fieldName, 42)).toBeNull(); + expect(parseValidationError(42)).toBeNull(); }); }); diff --git a/src/utils/__tests__/toArray.test.ts b/src/utils/__tests__/toArray.test.ts deleted file mode 100644 index 696bfc5..0000000 --- a/src/utils/__tests__/toArray.test.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { toArray } from '../toArray'; - -describe('toArray', () => { - it('should wrap anything into an array', () => { - expect(toArray('foo')).toEqual(['foo']); - }); - - it('should leave arrays alone', () => { - const mockArray = [1, 2, 3, 4, 5]; - expect(toArray(mockArray)).toBe(mockArray); - }); -}); diff --git a/src/utils/index.ts b/src/utils/index.ts index 63fb852..296b650 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -7,8 +7,8 @@ export * from './getDisplayName'; export * from './getDeepValue'; -export * from './toArray'; export * from './parseValidationError'; export * from './stringFormatter'; export * from './stringHasValue'; export * from './Types'; +export * from './noopFunction'; diff --git a/src/utils/noopFunction.ts b/src/utils/noopFunction.ts new file mode 100644 index 0000000..04c8eb9 --- /dev/null +++ b/src/utils/noopFunction.ts @@ -0,0 +1,4 @@ +/** + * Noop function that does nothing + */ +export function noopFunction(): void { } diff --git a/src/utils/parseValidationError.ts b/src/utils/parseValidationError.ts index b69c709..5129287 100644 --- a/src/utils/parseValidationError.ts +++ b/src/utils/parseValidationError.ts @@ -9,10 +9,9 @@ import { IFieldErrorObject, TFieldError } from '../validators'; /** * Parses the validation error and returns either * a validation object or undefined - * @param name field name * @param error error message */ -export const parseValidationError = (name: string, error: TFieldError): IFieldErrorObject | null => { +export const parseValidationError = (error: TFieldError): IFieldErrorObject | null => { if (typeof error === 'object') { if (error.message_id === undefined || error.params === undefined) { // Error object is invalid @@ -30,9 +29,5 @@ export const parseValidationError = (name: string, error: TFieldError): IFieldEr }; } - if (error !== undefined) { - console.error(`[Form] Validation result for the field ${name} was unexpected. Result: ${error}`); - } - return null; }; diff --git a/src/utils/toArray.ts b/src/utils/toArray.ts deleted file mode 100644 index 8a83dd9..0000000 --- a/src/utils/toArray.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2018-present, Umweltbundesamt GmbH - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * Returns an array with the parameter as its only element if - * the parameter is not an array. Otherwise it returns an array - */ -export const toArray = (param: T | T[]): T[] => { - if (!Array.isArray(param)) { - return [param]; - } - - return param; -}; diff --git a/src/validators/validators.test.ts b/src/validators/validators.test.ts index 091b5a1..2482c2a 100644 --- a/src/validators/validators.test.ts +++ b/src/validators/validators.test.ts @@ -1,4 +1,4 @@ -import { TBasicFieldValue } from '../components/withField'; +import { TBasicFieldValue } from '../hooks'; import { createMockFormContext } from '../test-utils/enzymeFormContext'; import { validators } from './validators'; import { FieldErrorMessageId } from './validators.types'; diff --git a/src/validators/validators.ts b/src/validators/validators.ts index b462320..8d7e231 100644 --- a/src/validators/validators.ts +++ b/src/validators/validators.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import { IFormContext } from '../components/FormContext'; -import { TBasicFieldValue } from '../components/withField'; +import { IFormContext } from '../components'; +import { TBasicFieldValue } from '../hooks'; import { FieldErrorMessageId, TAsyncValidator, TFieldError, TValidator } from './validators.types'; /** diff --git a/src/validators/validators.types.ts b/src/validators/validators.types.ts index c32b84e..b7313d7 100644 --- a/src/validators/validators.types.ts +++ b/src/validators/validators.types.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ -import { IFormContext } from '../components/FormContext'; -import { TBasicFieldValue } from '../components/withField'; +import { IFormContext } from '../components'; +import { TBasicFieldValue } from '../hooks'; import { IMessageValues } from '../utils/stringFormatter'; /** diff --git a/styleguide.config.js b/styleguide.config.js new file mode 100644 index 0000000..ae9ac61 --- /dev/null +++ b/styleguide.config.js @@ -0,0 +1,110 @@ +// eslint-disable-next-line @typescript-eslint/no-var-requires +const path = require('path'); + +module.exports = { + title: 'react-ocean-forms', + pagePerSection: true, + skipComponentsWithoutExample: true, + propsParser: require('react-docgen-typescript').withCustomConfig('./tsconfig.json', { skipPropsWithoutDoc: false }).parse, + moduleAliases: { + 'react-ocean-forms': 'C:\\source\\github\\react-ocean-forms\\src', + }, + getComponentPathLine(componentPath) { + const componentName = path.basename(componentPath, '.tsx') + return `import { ${componentName} } from 'react-ocean-forms';` + }, + sections: [ + { + name: 'Introduction', + content: 'docs/introduction.md' + }, + { + name: 'Changelog', + content: 'CHANGELOG.md' + }, + { + name: 'Components', + components: 'src/components/**/*.tsx', + sectionDepth: 1 + }, + { + name: 'Hooks', + sectionDepth: 1, + sections: [ + { + name: 'useField', + content: 'src/hooks/useField/useField.md' + }, + { + name: 'useFormContext', + content: 'src/hooks/useFormContext/useFormContext.md' + }, + { + name: 'useFormEventListener', + content: 'src/hooks/useFormEventListener/useFormEventListener.md' + }, + { + name: 'useValidation', + content: 'src/hooks/useValidation/useValidation.md' + }, + ], + } + ], + theme: { + fontFamily: { + base: '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;' + }, + }, + styles: { + StyleGuide: { + logo: { + 'background-color': '#343A40' + } + }, + Logo: { + logo: { + 'color': '#FFF' + } + }, + Heading: { + heading: { + 'font-weight': '300', + } + }, + TabButton: { + button: { + 'color': '#1673B1', + 'border-bottom': '1px #1673B1 solid', + 'padding-bottom': '0px', + } + }, + Playground: { + preview: { + 'font-family': '-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;', + } + } + }, + ribbon: { + url: 'https://github.com/environment-agency-austria/react-ocean-forms', + }, + webpackConfig: { + module: { + rules: [ + { + test: /\.tsx?$/, + loader: 'ts-loader', + options: { + compilerOptions: { + noEmit: false, + jsx: "react", + } + }, + exclude: /node_modules/, + }, + ] + }, + resolve: { + extensions: [".tsx", ".ts"] + }, + } +}; diff --git a/tsconfig.build.json b/tsconfig.build.json index 393e9d0..f82d3d9 100644 --- a/tsconfig.build.json +++ b/tsconfig.build.json @@ -13,6 +13,7 @@ "**/*.test.tsx", "**/*.test.js", "**/*.test.jsx", - "config/**" + "config/**", + "src/test-utils/**" ] } diff --git a/yarn.lock b/yarn.lock index 9dda22d..940a58e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -"@babel/code-frame@^7.0.0": +"@babel/code-frame@7.0.0", "@babel/code-frame@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA== @@ -95,6 +95,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.2.tgz#b4521a400cb5a871eab3890787b4bc1326d38d91" integrity sha512-9fJTDipQFvlfSVdD/JBtkiY0br9BtfvW2R8wo6CX/Ej2eMuV0gWPk1M67Mt3eggQvBqYW1FCEk8BN7WvGm/g5g== +"@babel/parser@^7.1.3": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.4.3.tgz#eb3ac80f64aa101c907d4ce5406360fe75b7895b" + integrity sha512-gxpEUhTS1sGA63EGQGuA+WESPR/6tz6ng7tSHFCmaTJK/cGK8y37cBTspX+U2xCAue2IQVvF6Z0oigmjwD8YGQ== + "@babel/plugin-syntax-object-rest-spread@^7.0.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e" @@ -102,6 +107,20 @@ dependencies: "@babel/helper-plugin-utils" "^7.0.0" +"@babel/runtime@^7.0.0": + version "7.4.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.3.tgz#79888e452034223ad9609187a0ad1fe0d2ad4bdc" + integrity sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA== + dependencies: + regenerator-runtime "^0.13.2" + +"@babel/runtime@^7.4.2": + version "7.4.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.4.2.tgz#f5ab6897320f16decd855eed70b705908a313fe8" + integrity sha512-7Bl2rALb7HpvXFL7TETNzKSAeBVCPHELzc0C//9FCxN8nsiueWSJBqaF+2oIJScyILStASR/Cx5WMkXGYTiJFA== + dependencies: + regenerator-runtime "^0.13.2" + "@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2": version "7.2.2" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907" @@ -143,42 +162,41 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@jest/console@^24.3.0": - version "24.3.0" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.3.0.tgz#7bd920d250988ba0bf1352c4493a48e1cb97671e" - integrity sha512-NaCty/OOei6rSDcbPdMiCbYCI0KGFGPgGO6B09lwWt5QTxnkuhKYET9El5u5z1GAcSxkQmSMtM63e24YabCWqA== +"@jest/console@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/console/-/console-24.7.1.tgz#32a9e42535a97aedfe037e725bd67e954b459545" + integrity sha512-iNhtIy2M8bXlAOULWVTUxmnelTLFneTNEkHCgPmgd+zNwy9zVddJ6oS5rZ9iwoscNdT5mMwUd0C51v/fSlzItg== dependencies: "@jest/source-map" "^24.3.0" - "@types/node" "*" chalk "^2.0.1" slash "^2.0.0" -"@jest/core@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.5.0.tgz#2cefc6a69e9ebcae1da8f7c75f8a257152ba1ec0" - integrity sha512-RDZArRzAs51YS7dXG1pbXbWGxK53rvUu8mCDYsgqqqQ6uSOaTjcVyBl2Jce0exT2rSLk38ca7az7t2f3b0/oYQ== +"@jest/core@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/core/-/core-24.7.1.tgz#6707f50db238d0c5988860680e2e414df0032024" + integrity sha512-ivlZ8HX/FOASfHcb5DJpSPFps8ydfUYzLZfgFFqjkLijYysnIEOieg72YRhO4ZUB32xu40hsSMmaw+IGYeKONA== dependencies: - "@jest/console" "^24.3.0" - "@jest/reporters" "^24.5.0" - "@jest/test-result" "^24.5.0" - "@jest/transform" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/console" "^24.7.1" + "@jest/reporters" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/transform" "^24.7.1" + "@jest/types" "^24.7.0" ansi-escapes "^3.0.0" chalk "^2.0.1" exit "^0.1.2" graceful-fs "^4.1.15" - jest-changed-files "^24.5.0" - jest-config "^24.5.0" - jest-haste-map "^24.5.0" - jest-message-util "^24.5.0" + jest-changed-files "^24.7.0" + jest-config "^24.7.1" + jest-haste-map "^24.7.1" + jest-message-util "^24.7.1" jest-regex-util "^24.3.0" - jest-resolve-dependencies "^24.5.0" - jest-runner "^24.5.0" - jest-runtime "^24.5.0" - jest-snapshot "^24.5.0" - jest-util "^24.5.0" - jest-validate "^24.5.0" - jest-watcher "^24.5.0" + jest-resolve-dependencies "^24.7.1" + jest-runner "^24.7.1" + jest-runtime "^24.7.1" + jest-snapshot "^24.7.1" + jest-util "^24.7.1" + jest-validate "^24.7.0" + jest-watcher "^24.7.1" micromatch "^3.1.10" p-each-series "^1.0.0" pirates "^4.0.1" @@ -186,36 +204,34 @@ rimraf "^2.5.4" strip-ansi "^5.0.0" -"@jest/environment@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.5.0.tgz#a2557f7808767abea3f9e4cc43a172122a63aca8" - integrity sha512-tzUHR9SHjMXwM8QmfHb/EJNbF0fjbH4ieefJBvtwO8YErLTrecc1ROj0uo2VnIT6SlpEGZnvdCK6VgKYBo8LsA== - dependencies: - "@jest/fake-timers" "^24.5.0" - "@jest/transform" "^24.5.0" - "@jest/types" "^24.5.0" - "@types/node" "*" - jest-mock "^24.5.0" - -"@jest/fake-timers@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.5.0.tgz#4a29678b91fd0876144a58f8d46e6c62de0266f0" - integrity sha512-i59KVt3QBz9d+4Qr4QxsKgsIg+NjfuCjSOWj3RQhjF5JNy+eVJDhANQ4WzulzNCHd72srMAykwtRn5NYDGVraw== - dependencies: - "@jest/types" "^24.5.0" - "@types/node" "*" - jest-message-util "^24.5.0" - jest-mock "^24.5.0" - -"@jest/reporters@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.5.0.tgz#9363a210d0daa74696886d9cb294eb8b3ad9b4d9" - integrity sha512-vfpceiaKtGgnuC3ss5czWOihKOUSyjJA4M4udm6nH8xgqsuQYcyDCi4nMMcBKsHXWgz9/V5G7iisnZGfOh1w6Q== - dependencies: - "@jest/environment" "^24.5.0" - "@jest/test-result" "^24.5.0" - "@jest/transform" "^24.5.0" - "@jest/types" "^24.5.0" +"@jest/environment@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-24.7.1.tgz#9b9196bc737561f67ac07817d4c5ece772e33135" + integrity sha512-wmcTTYc4/KqA+U5h1zQd5FXXynfa7VGP2NfF+c6QeGJ7c+2nStgh65RQWNX62SC716dTtqheTRrZl0j+54oGHw== + dependencies: + "@jest/fake-timers" "^24.7.1" + "@jest/transform" "^24.7.1" + "@jest/types" "^24.7.0" + jest-mock "^24.7.0" + +"@jest/fake-timers@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-24.7.1.tgz#56e5d09bdec09ee81050eaff2794b26c71d19db2" + integrity sha512-4vSQJDKfR2jScOe12L9282uiwuwQv9Lk7mgrCSZHA9evB9efB/qx8i0KJxsAKtp8fgJYBJdYY7ZU6u3F4/pyjA== + dependencies: + "@jest/types" "^24.7.0" + jest-message-util "^24.7.1" + jest-mock "^24.7.0" + +"@jest/reporters@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-24.7.1.tgz#38ac0b096cd691bbbe3051ddc25988d42e37773a" + integrity sha512-bO+WYNwHLNhrjB9EbPL4kX/mCCG4ZhhfWmO3m4FSpbgr7N83MFejayz30kKjgqr7smLyeaRFCBQMbXpUgnhAJw== + dependencies: + "@jest/environment" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/transform" "^24.7.1" + "@jest/types" "^24.7.0" chalk "^2.0.1" exit "^0.1.2" glob "^7.1.2" @@ -223,11 +239,11 @@ istanbul-lib-coverage "^2.0.2" istanbul-lib-instrument "^3.0.1" istanbul-lib-source-maps "^3.0.1" - jest-haste-map "^24.5.0" - jest-resolve "^24.5.0" - jest-runtime "^24.5.0" - jest-util "^24.5.0" - jest-worker "^24.4.0" + jest-haste-map "^24.7.1" + jest-resolve "^24.7.1" + jest-runtime "^24.7.1" + jest-util "^24.7.1" + jest-worker "^24.6.0" node-notifier "^5.2.1" slash "^2.0.0" source-map "^0.6.0" @@ -242,44 +258,67 @@ graceful-fs "^4.1.15" source-map "^0.6.0" -"@jest/test-result@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.5.0.tgz#ab66fb7741a04af3363443084e72ea84861a53f2" - integrity sha512-u66j2vBfa8Bli1+o3rCaVnVYa9O8CAFZeqiqLVhnarXtreSXG33YQ6vNYBogT7+nYiFNOohTU21BKiHlgmxD5A== +"@jest/test-result@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-24.7.1.tgz#19eacdb29a114300aed24db651e5d975f08b6bbe" + integrity sha512-3U7wITxstdEc2HMfBX7Yx3JZgiNBubwDqQMh+BXmZXHa3G13YWF3p6cK+5g0hGkN3iufg/vGPl3hLxQXD74Npg== + dependencies: + "@jest/console" "^24.7.1" + "@jest/types" "^24.7.0" + "@types/istanbul-lib-coverage" "^2.0.0" + +"@jest/test-sequencer@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-24.7.1.tgz#9c18e428e1ad945fa74f6233a9d35745ca0e63e0" + integrity sha512-84HQkCpVZI/G1zq53gHJvSmhUer4aMYp9tTaffW28Ih5OxfCg8hGr3nTSbL1OhVDRrFZwvF+/R9gY6JRkDUpUA== dependencies: - "@jest/console" "^24.3.0" - "@jest/types" "^24.5.0" - "@types/istanbul-lib-coverage" "^1.1.0" + "@jest/test-result" "^24.7.1" + jest-haste-map "^24.7.1" + jest-runner "^24.7.1" + jest-runtime "^24.7.1" -"@jest/transform@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.5.0.tgz#6709fc26db918e6af63a985f2cc3c464b4cf99d9" - integrity sha512-XSsDz1gdR/QMmB8UCKlweAReQsZrD/DK7FuDlNo/pE8EcKMrfi2kqLRk8h8Gy/PDzgqJj64jNEzOce9pR8oj1w== +"@jest/transform@^24.7.1": + version "24.7.1" + resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-24.7.1.tgz#872318f125bcfab2de11f53b465ab1aa780789c2" + integrity sha512-EsOUqP9ULuJ66IkZQhI5LufCHlTbi7hrcllRMUEV/tOgqBVQi93+9qEvkX0n8mYpVXQ8VjwmICeRgg58mrtIEw== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" babel-plugin-istanbul "^5.1.0" chalk "^2.0.1" convert-source-map "^1.4.0" fast-json-stable-stringify "^2.0.0" graceful-fs "^4.1.15" - jest-haste-map "^24.5.0" + jest-haste-map "^24.7.1" jest-regex-util "^24.3.0" - jest-util "^24.5.0" + jest-util "^24.7.1" micromatch "^3.1.10" realpath-native "^1.1.0" slash "^2.0.0" source-map "^0.6.1" write-file-atomic "2.4.1" -"@jest/types@^24.5.0": - version "24.5.0" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.5.0.tgz#feee214a4d0167b0ca447284e95a57aa10b3ee95" - integrity sha512-kN7RFzNMf2R8UDadPOl6ReyI+MT8xfqRuAnuVL+i4gwjv/zubdDK+EDeLHYwq1j0CSSR2W/MmgaRlMZJzXdmVA== +"@jest/types@^24.7.0": + version "24.7.0" + resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.7.0.tgz#c4ec8d1828cdf23234d9b4ee31f5482a3f04f48b" + integrity sha512-ipJUa2rFWiKoBqMKP63Myb6h9+iT3FHRTF2M8OR6irxWzItisa8i4dcSg14IbvmXUnBlHBlUQPYUHWyX3UPpYA== dependencies: - "@types/istanbul-lib-coverage" "^1.1.0" + "@types/istanbul-lib-coverage" "^2.0.0" "@types/yargs" "^12.0.9" +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.1.2": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" + integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== + "@types/babel__core@^7.1.0": version "7.1.0" resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.0.tgz#710f2487dda4dcfd010ca6abb2b4dc7394365c51" @@ -337,10 +376,10 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== -"@types/istanbul-lib-coverage@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.0.tgz#2cc2ca41051498382b43157c8227fea60363f94a" - integrity sha512-ohkhb9LehJy+PA40rDtGAji61NCgdtKLAlFoYp4cnuuQEswwdK3vz9SOIkkyc3wrk8dzjphQApNs56yyXLStaQ== +"@types/istanbul-lib-coverage@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.0.tgz#1eb8c033e98cf4e1a4cedcaf8bcafe8cb7591e85" + integrity sha512-eAtOAFZefEnfJiRFQBGw1eYqa5GTLCZ1y86N0XSI/D6EB+E8z6VPV/UL7Gi5UEclFqoQk+6NRqEDsfmDLXn8sg== "@types/jest-diff@*": version "20.0.1" @@ -358,64 +397,276 @@ version "10.11.7" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.11.7.tgz#0e75ca9357d646ca754016ca1d68a127ad7e7300" -"@types/node@^11.11.6": - version "11.12.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.12.2.tgz#d7f302e74b10e9801d52852137f652d9ee235da8" - integrity sha512-c82MtnqWB/CqqK7/zit74Ob8H1dBdV7bK+BcErwtXbe0+nUGkgzq5NTDmRW/pAv2lFtmeNmW95b0zK2hxpeklg== +"@types/node@^11.13.5": + version "11.13.7" + resolved "https://registry.yarnpkg.com/@types/node/-/node-11.13.7.tgz#85dbb71c510442d00c0631f99dae957ce44fd104" + integrity sha512-suFHr6hcA9mp8vFrZTgrmqW2ZU3mbWsryQtQlY/QvwTISCw7nw/j+bCQPPohqmskhmqa5wLNuMHTTsc+xf1MQg== "@types/prop-types@*": version "15.5.6" resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.5.6.tgz#9c03d3fed70a8d517c191b7734da2879b50ca26c" -"@types/react@*", "@types/react@16.8.10", "@types/react@^16.8.10": - version "16.8.10" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.10.tgz#1ccb6fde17f71a62ef055382ec68bdc379d4d8d9" - integrity sha512-7bUQeZKP4XZH/aB4i7k1i5yuwymDu/hnLMhD9NjVZvQQH7ZUgRN3d6iu8YXzx4sN/tNr0bj8jgguk8hhObzGvA== +"@types/react@*", "@types/react@16.8.14", "@types/react@^16.8.14": + version "16.8.14" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.8.14.tgz#b561bfabeb8f60d12e6d4766367e7a9ae927aa18" + integrity sha512-26tFVJ1omGmzIdFTFmnC5zhz1GTaqCjxgUxV4KzWvsybF42P7/j4RBn6UeO3KbHPXqKWZszMXMoI65xIWm954A== dependencies: "@types/prop-types" "*" csstype "^2.2.0" +"@types/resolve@0.0.8": + version "0.0.8" + resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-0.0.8.tgz#f26074d238e02659e323ce1a13d041eee280e194" + integrity sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== + dependencies: + "@types/node" "*" + "@types/stack-utils@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e" integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw== +"@types/unist@*", "@types/unist@^2.0.0": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.3.tgz#9c088679876f374eb5983f150d4787aa6fb32d7e" + integrity sha512-FvUupuM3rlRsRtCN+fDudtmytGO6iHJuuRKS1Ss0pG5z8oX0diNEw94UEL7hgDbpN94rgaK5R7sWm6RrSkZuAQ== + +"@types/vfile-message@*": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@types/vfile-message/-/vfile-message-1.0.1.tgz#e1e9895cc6b36c462d4244e64e6d0b6eaf65355a" + integrity sha512-mlGER3Aqmq7bqR1tTTIVHq8KSAFFRyGbrxuM8C/H82g6k7r2fS+IMEkIu3D7JHzG10NvPdR8DNx0jr0pwpp4dA== + dependencies: + "@types/node" "*" + "@types/unist" "*" + +"@types/vfile@^3.0.0": + version "3.0.2" + resolved "https://registry.yarnpkg.com/@types/vfile/-/vfile-3.0.2.tgz#19c18cd232df11ce6fa6ad80259bc86c366b09b9" + integrity sha512-b3nLFGaGkJ9rzOcuXRfHkZMdjsawuDD0ENL9fzTophtBg8FJHSGbH7daXkEpcwy3v7Xol3pAvsmlYyFhR4pqJw== + dependencies: + "@types/node" "*" + "@types/unist" "*" + "@types/vfile-message" "*" + "@types/yargs@^12.0.2", "@types/yargs@^12.0.9": version "12.0.10" resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.10.tgz#17a8ec65cd8e88f51b418ceb271af18d3137df67" integrity sha512-WsVzTPshvCSbHThUduGGxbmnwcpkgSctHGHTqzWyFg4lYAuV5qXlyFPOsP3OWqCINfmg/8VXP+zJaa4OxEsBQQ== -"@typescript-eslint/eslint-plugin@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.6.0.tgz#a5ff3128c692393fb16efa403ec7c8a5593dab0f" - integrity sha512-U224c29E2lo861TQZs6GSmyC0OYeRNg6bE9UVIiFBxN2MlA0nq2dCrgIVyyRbC05UOcrgf2Wk/CF2gGOPQKUSQ== +"@typescript-eslint/eslint-plugin@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.7.0.tgz#570e45dc84fb97852e363f1e00f47e604a0b8bcc" + integrity sha512-NUSz1aTlIzzTjFFVFyzrbo8oFjHg3K/M9MzYByqbMCxeFdErhLAcGITVfXzSz+Yvp5OOpMu3HkIttB0NyKl54Q== dependencies: - "@typescript-eslint/parser" "1.6.0" - "@typescript-eslint/typescript-estree" "1.6.0" + "@typescript-eslint/parser" "1.7.0" + "@typescript-eslint/typescript-estree" "1.7.0" + eslint-utils "^1.3.1" + regexpp "^2.0.1" requireindex "^1.2.0" tsutils "^3.7.0" -"@typescript-eslint/parser@1.6.0", "@typescript-eslint/parser@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.6.0.tgz#f01189c8b90848e3b8e45a6cdad27870529d1804" - integrity sha512-VB9xmSbfafI+/kI4gUK3PfrkGmrJQfh0N4EScT1gZXSZyUxpsBirPL99EWZg9MmPG0pzq/gMtgkk7/rAHj4aQw== +"@typescript-eslint/parser@1.7.0", "@typescript-eslint/parser@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-1.7.0.tgz#c3ea0d158349ceefbb6da95b5b09924b75357851" + integrity sha512-1QFKxs2V940372srm12ovSE683afqc1jB6zF/f8iKhgLz1yoSjYeGHipasao33VXKI+0a/ob9okeogGdKGvvlg== dependencies: - "@typescript-eslint/typescript-estree" "1.6.0" + "@typescript-eslint/typescript-estree" "1.7.0" eslint-scope "^4.0.0" eslint-visitor-keys "^1.0.0" -"@typescript-eslint/typescript-estree@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.6.0.tgz#6cf43a07fee08b8eb52e4513b428c8cdc9751ef0" - integrity sha512-A4CanUwfaG4oXobD5y7EXbsOHjCwn8tj1RDd820etpPAjH+Icjc2K9e/DQM1Hac5zH2BSy+u6bjvvF2wwREvYA== +"@typescript-eslint/typescript-estree@1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-1.7.0.tgz#59ec02f5371964da1cc679dba7b878a417bc8c60" + integrity sha512-K5uedUxVmlYrVkFbyV3htDipvLqTE3QMOUQEHYJaKtgzxj6r7c5Ca/DG1tGgFxX+fsbi9nDIrf4arq7Ib7H/Yw== dependencies: lodash.unescape "4.0.1" semver "5.5.0" +"@vxna/mini-html-webpack-template@^0.1.7": + version "0.1.7" + resolved "https://registry.yarnpkg.com/@vxna/mini-html-webpack-template/-/mini-html-webpack-template-0.1.7.tgz#2a8270e513ee14f395cc17c2ce22ced383c45d22" + integrity sha512-qV2VslV48ECPwyuG7c4O6JpYgjnvdm88YYkMncIXzakXXwVUxhcg6YipXxWK7U+pixHkWWSnDX/8DIOmWeh+PQ== + dependencies: + common-tags "^1.7.2" + +"@webassemblyjs/ast@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359" + integrity sha512-aJMfngIZ65+t71C3y2nBBg5FFG0Okt9m0XEgWZ7Ywgn1oMAT8cNwx00Uv1cQyHtidq0Xn94R4TAywO+LCQ+ZAQ== + dependencies: + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + +"@webassemblyjs/floating-point-hex-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.8.5.tgz#1ba926a2923613edce496fd5b02e8ce8a5f49721" + integrity sha512-9p+79WHru1oqBh9ewP9zW95E3XAo+90oth7S5Re3eQnECGq59ly1Ri5tsIipKGpiStHsUYmY3zMLqtk3gTcOtQ== + +"@webassemblyjs/helper-api-error@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.8.5.tgz#c49dad22f645227c5edb610bdb9697f1aab721f7" + integrity sha512-Za/tnzsvnqdaSPOUXHyKJ2XI7PDX64kWtURyGiJJZKVEdFOsdKUCPTNEVFZq3zJ2R0G5wc2PZ5gvdTRFgm81zA== + +"@webassemblyjs/helper-buffer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.8.5.tgz#fea93e429863dd5e4338555f42292385a653f204" + integrity sha512-Ri2R8nOS0U6G49Q86goFIPNgjyl6+oE1abW1pS84BuhP1Qcr5JqMwRFT3Ah3ADDDYGEgGs1iyb1DGX+kAi/c/Q== + +"@webassemblyjs/helper-code-frame@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.8.5.tgz#9a740ff48e3faa3022b1dff54423df9aa293c25e" + integrity sha512-VQAadSubZIhNpH46IR3yWO4kZZjMxN1opDrzePLdVKAZ+DFjkGD/rf4v1jap744uPVU6yjL/smZbRIIJTOUnKQ== + dependencies: + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/helper-fsm@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.8.5.tgz#ba0b7d3b3f7e4733da6059c9332275d860702452" + integrity sha512-kRuX/saORcg8se/ft6Q2UbRpZwP4y7YrWsLXPbbmtepKr22i8Z4O3V5QE9DbZK908dh5Xya4Un57SDIKwB9eow== + +"@webassemblyjs/helper-module-context@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.8.5.tgz#def4b9927b0101dc8cbbd8d1edb5b7b9c82eb245" + integrity sha512-/O1B236mN7UNEU4t9X7Pj38i4VoU8CcMHyy3l2cV/kIF4U5KoHXDVqcDuOs1ltkac90IM4vZdHc52t1x8Yfs3g== + dependencies: + "@webassemblyjs/ast" "1.8.5" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.8.5.tgz#537a750eddf5c1e932f3744206551c91c1b93e61" + integrity sha512-Cu4YMYG3Ddl72CbmpjU/wbP6SACcOPVbHN1dI4VJNJVgFwaKf1ppeFJrwydOG3NDHxVGuCfPlLZNyEdIYlQ6QQ== + +"@webassemblyjs/helper-wasm-section@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.8.5.tgz#74ca6a6bcbe19e50a3b6b462847e69503e6bfcbf" + integrity sha512-VV083zwR+VTrIWWtgIUpqfvVdK4ff38loRmrdDBgBT8ADXYsEZ5mPQ4Nde90N3UYatHdYoDIFb7oHzMncI02tA== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + +"@webassemblyjs/ieee754@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.8.5.tgz#712329dbef240f36bf57bd2f7b8fb9bf4154421e" + integrity sha512-aaCvQYrvKbY/n6wKHb/ylAJr27GglahUO89CcGXMItrOBqRarUMxWLJgxm9PJNuKULwN5n1csT9bYoMeZOGF3g== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.8.5.tgz#044edeb34ea679f3e04cd4fd9824d5e35767ae10" + integrity sha512-plYUuUwleLIziknvlP8VpTgO4kqNaH57Y3JnNa6DLpu/sGcP6hbVdfdX5aHAV716pQBKrfuU26BJK29qY37J7A== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.8.5.tgz#a8bf3b5d8ffe986c7c1e373ccbdc2a0915f0cedc" + integrity sha512-U7zgftmQriw37tfD934UNInokz6yTmn29inT2cAetAsaU9YeVCveWEwhKL1Mg4yS7q//NGdzy79nlXh3bT8Kjw== + +"@webassemblyjs/wasm-edit@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.8.5.tgz#962da12aa5acc1c131c81c4232991c82ce56e01a" + integrity sha512-A41EMy8MWw5yvqj7MQzkDjU29K7UJq1VrX2vWLzfpRHt3ISftOXqrtojn7nlPsZ9Ijhp5NwuODuycSvfAO/26Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/helper-wasm-section" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-opt" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + "@webassemblyjs/wast-printer" "1.8.5" + +"@webassemblyjs/wasm-gen@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.8.5.tgz#54840766c2c1002eb64ed1abe720aded714f98bc" + integrity sha512-BCZBT0LURC0CXDzj5FXSc2FPTsxwp3nWcqXQdOZE4U7h7i8FqtFK5Egia6f9raQLpEKT1VL7zr4r3+QX6zArWg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wasm-opt@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.8.5.tgz#b24d9f6ba50394af1349f510afa8ffcb8a63d264" + integrity sha512-HKo2mO/Uh9A6ojzu7cjslGaHaUU14LdLbGEKqTR7PBKwT6LdPtLLh9fPY33rmr5wcOMrsWDbbdCHq4hQUdd37Q== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-buffer" "1.8.5" + "@webassemblyjs/wasm-gen" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + +"@webassemblyjs/wasm-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.8.5.tgz#21576f0ec88b91427357b8536383668ef7c66b8d" + integrity sha512-pi0SYE9T6tfcMkthwcgCpL0cM9nRYr6/6fjgDtL6q/ZqKHdMWvxitRi5JcZ7RI4SNJJYnYNaWy5UUrHQy998lw== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-wasm-bytecode" "1.8.5" + "@webassemblyjs/ieee754" "1.8.5" + "@webassemblyjs/leb128" "1.8.5" + "@webassemblyjs/utf8" "1.8.5" + +"@webassemblyjs/wast-parser@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.8.5.tgz#e10eecd542d0e7bd394f6827c49f3df6d4eefb8c" + integrity sha512-daXC1FyKWHF1i11obK086QRlsMsY4+tIOKgBqI1lxAnkp9xe9YMcgOxm9kLe+ttjs5aWV2KKE1TWJCN57/Btsg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/floating-point-hex-parser" "1.8.5" + "@webassemblyjs/helper-api-error" "1.8.5" + "@webassemblyjs/helper-code-frame" "1.8.5" + "@webassemblyjs/helper-fsm" "1.8.5" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/wast-printer@1.8.5": + version "1.8.5" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.8.5.tgz#114bbc481fd10ca0e23b3560fa812748b0bae5bc" + integrity sha512-w0U0pD4EhlnvRyeJzBqaVSJAo9w/ce7/WPogeXLzGkO6hzhr4GnQIZ4W4uUt5b9ooAaXPtnXlj0gzsXEOUNYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/wast-parser" "1.8.5" + "@xtuc/long" "4.2.2" + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + abab@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.0.tgz#aba0ab4c5eee2d4c79d3487d85450fb2376ebb0f" +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +accepts@~1.3.4, accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + integrity sha1-63d99gEXI6OxTopywIBcjoZ0a9I= + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + +acorn-dynamic-import@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz#482210140582a36b83c3e342e1cfebcaa9240948" + integrity sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw== + acorn-globals@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-4.3.0.tgz#e3b6f8da3c1552a95ae627571f7dd6923bb54103" @@ -423,7 +674,7 @@ acorn-globals@^4.1.0: acorn "^6.0.1" acorn-walk "^6.0.1" -acorn-jsx@^5.0.0: +acorn-jsx@^5.0.0, acorn-jsx@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== @@ -440,11 +691,42 @@ acorn@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.0.2.tgz#6a459041c320ab17592c6317abbfdf4bbaa98ca4" -acorn@^6.0.7, acorn@^6.1.1: +acorn@^6.0.5, acorn@^6.0.7, acorn@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.1.1.tgz#7d25ae05bb8ad1f9b699108e1094ecd7884adc1f" integrity sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA== +address@1.0.3, address@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" + integrity sha512-z55ocwKBRLryBs394Sm3ushTtBeg6VAeuku7utSoSnsJKvKcnXFIyC6vh27n3rXyxSgkJBBCAvyOn7gSUcTYjg== + +airbnb-prop-types@^2.12.0: + version "2.13.2" + resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.13.2.tgz#43147a5062dd2a4a5600e748a47b64004cc5f7fc" + integrity sha512-2FN6DlHr6JCSxPPi25EnqGaXC4OC3/B3k1lCd6MMYrZ51/Gf/1qDfaR+JElzWa+Tl7cY2aYOlsYJGFeQyVHIeQ== + dependencies: + array.prototype.find "^2.0.4" + function.prototype.name "^1.1.0" + has "^1.0.3" + is-regex "^1.0.4" + object-is "^1.0.1" + object.assign "^4.1.0" + object.entries "^1.1.0" + prop-types "^15.7.2" + prop-types-exact "^1.2.0" + react-is "^16.8.6" + +ajv-errors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" + integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== + +ajv-keywords@^3.1.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.0.tgz#4b831e7b531415a7cc518cd404e73f6193c6349d" + integrity sha512-aUjdRFISbuFOl0EIZc+9e4FfZp0bDZgAdOOf30bJmw8VM9v84SHyVyxDfbWxpGYbdZD/9XoKxfHVNmxPkhwyGw== + ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -454,6 +736,16 @@ ajv@^5.3.0: fast-json-stable-stringify "^2.0.0" json-schema-traverse "^0.3.0" +ajv@^6.1.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.10.0.tgz#90d0d54439da587cd7e843bfb7045f50bd22bdf1" + integrity sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg== + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.9.1: version "6.9.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.9.2.tgz#4927adb83e7f48e5a32b45729744c71ec39c9c7b" @@ -471,6 +763,11 @@ ansi-align@^2.0.0: dependencies: string-width "^2.0.0" +ansi-colors@^3.0.0: + version "3.2.4" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" + integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== + ansi-escapes@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" @@ -480,6 +777,11 @@ ansi-escapes@^3.2.0: resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== +ansi-html@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" + integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -493,6 +795,11 @@ ansi-regex@^4.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.0.0.tgz#70de791edf021404c3fd615aa89118ae0432e5a9" integrity sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w== +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + ansi-styles@^3.2.0, ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -513,7 +820,7 @@ append-transform@^1.0.0: dependencies: default-require-extensions "^2.0.0" -aproba@^1.0.3: +aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -551,6 +858,21 @@ array-filter@^1.0.0: resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= + +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + +array-flatten@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" + integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== + array-includes@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" @@ -559,10 +881,40 @@ array-includes@^3.0.3: define-properties "^1.1.2" es-abstract "^1.7.0" +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + integrity sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI= + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + integrity sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys= + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= + array-unique@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" +array.prototype.find@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.0.4.tgz#556a5c5362c08648323ddaeb9de9d14bc1864c90" + integrity sha1-VWpcU2LAhkgyPdrrnenRS8GGTJA= + dependencies: + define-properties "^1.1.2" + es-abstract "^1.7.0" + array.prototype.flat@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.1.tgz#812db8f02cad24d3fab65dd67eabe3b8903494a4" @@ -575,6 +927,15 @@ arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + integrity sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw== + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" @@ -585,32 +946,69 @@ assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + dependencies: + util "0.10.3" + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" +ast-types@0.11.5: + version "0.11.5" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.5.tgz#9890825d660c03c28339f315e9fa0a360e31ec28" + integrity sha512-oJjo+5e7/vEc2FBK8gUalV0pba4L3VdBIs2EKhOLHLcOd2FgQIVQN9xb0eZ9IjEWyAL7vq6fGJxOvVvdCHNyMw== + +ast-types@0.11.7: + version "0.11.7" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.11.7.tgz#f318bf44e339db6a320be0009ded64ec1471f46c" + integrity sha512-2mP3TwtkY/aTv5X3ZsMpNAbOnyoC/aMJwJSoaELPkHId0nSQgFcnU4dRW3isxiz7+zBexk0ym3WNVjMiQBnJSw== + +ast-types@0.12.3, ast-types@^0.12.2: + version "0.12.3" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.12.3.tgz#2299c6201d34b2a749a2dd9f2de7ef5f0e84f423" + integrity sha512-wJUcAfrdW+IgDoMGNz5MmcvahKgB7BwIbLupdKVVHxHNYt+HVR2k35swdYNv9aZpF8nvlkjbnkp2rrNwxGckZA== + +ast-types@^0.7.2: + version "0.7.8" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.7.8.tgz#902d2e0d60d071bdcd46dc115e1809ed11c138a9" + integrity sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk= + astral-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" +async-each@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.2.tgz#8b8a7ca2a658f927e9f307d6d1a42f4199f0f735" + integrity sha512-6xrbvN0MOBKSJDdonmSSz2OwFSgxRaVtBDes26mj9KIGtDo+g9xosFRSC+i1gQh2oAN/tQ62AI/pGZGQjVOiRg== + async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@^2.5.0: - version "2.6.1" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" - integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== - dependencies: - lodash "^4.17.10" +async@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@^2.6.1: +async@^2.1.4, async@^2.6.1: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== dependencies: lodash "^4.17.11" +async@^2.5.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== + dependencies: + lodash "^4.17.10" + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" @@ -627,16 +1025,16 @@ aws4@^1.8.0: version "1.8.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f" -babel-jest@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.5.0.tgz#0ea042789810c2bec9065f7c8ab4dc18e1d28559" - integrity sha512-0fKCXyRwxFTJL0UXDJiT2xYxO9Lu2vBd9n+cC+eDjESzcVG3s2DRGAxbzJX21fceB1WYoBjAh8pQ83dKcl003g== +babel-jest@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.7.1.tgz#73902c9ff15a7dfbdc9994b0b17fcefd96042178" + integrity sha512-GPnLqfk8Mtt0i4OemjWkChi73A3ALs4w2/QbG64uAj8b5mmwzxc7jbJVRZt8NJkxi6FopVHog9S3xX6UJKb2qg== dependencies: - "@jest/transform" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/transform" "^24.7.1" + "@jest/types" "^24.7.0" "@types/babel__core" "^7.1.0" babel-plugin-istanbul "^5.1.0" - babel-preset-jest "^24.3.0" + babel-preset-jest "^24.6.0" chalk "^2.4.2" slash "^2.0.0" @@ -649,25 +1047,35 @@ babel-plugin-istanbul@^5.1.0: istanbul-lib-instrument "^3.0.0" test-exclude "^5.0.0" -babel-plugin-jest-hoist@^24.3.0: - version "24.3.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.3.0.tgz#f2e82952946f6e40bb0a75d266a3790d854c8b5b" - integrity sha512-nWh4N1mVH55Tzhx2isvUN5ebM5CDUvIpXPZYMRazQughie/EqGnbR+czzoQlhUmJG9pPJmYDRhvocotb2THl1w== +babel-plugin-jest-hoist@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.6.0.tgz#f7f7f7ad150ee96d7a5e8e2c5da8319579e78019" + integrity sha512-3pKNH6hMt9SbOv0F3WVmy5CWQ4uogS3k0GY5XLyQHJ9EGpAT9XWkFd2ZiXXtkwFHdAHa5j7w7kfxSP5lAIwu7w== dependencies: "@types/babel__traverse" "^7.0.6" -babel-preset-jest@^24.3.0: - version "24.3.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.3.0.tgz#db88497e18869f15b24d9c0e547d8e0ab950796d" - integrity sha512-VGTV2QYBa/Kn3WCOKdfS31j9qomaXSgJqi65B6o05/1GsJyj9LVhSljM9ro4S+IBGj/ENhNBuH9bpqzztKAQSw== +babel-preset-jest@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.6.0.tgz#66f06136eefce87797539c0d63f1769cc3915984" + integrity sha512-pdZqLEdmy1ZK5kyRUfvBb2IfTPb2BUvIJczlPspS8fWmBQslNNDBqVfh7BW5leOVJMDZKzjD8XEyABTk6gQ5yw== dependencies: "@babel/plugin-syntax-object-rest-spread" "^7.0.0" - babel-plugin-jest-hoist "^24.3.0" + babel-plugin-jest-hoist "^24.6.0" + +bail@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" + integrity sha512-1X8CnjFVQ+a+KW36uBNMTU5s8+v5FzeqrP7hTG5aTb4aPreSbZJlhwPon9VKMuEVgV++JM+SQrALY3kr7eswdg== balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + integrity sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw== + base@^0.11.1: version "0.11.2" resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" @@ -680,6 +1088,11 @@ base@^0.11.1: mixin-deep "^1.2.0" pascalcase "^0.1.1" +batch@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= + bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -690,6 +1103,16 @@ big.js@^3.1.3: version "3.2.0" resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" +big.js@^5.2.2: + version "5.2.2" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" + integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== + +binary-extensions@^1.0.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" + integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== + bl@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/bl/-/bl-1.2.2.tgz#a160911717103c07410cef63ef51b397c025af9c" @@ -698,6 +1121,44 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" +bluebird@^3.5.1, bluebird@^3.5.3: + version "3.5.4" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" + integrity sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw== + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + +body-parser@1.18.3: + version "1.18.3" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" + integrity sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ= + dependencies: + bytes "3.0.0" + content-type "~1.0.4" + debug "2.6.9" + depd "~1.1.2" + http-errors "~1.6.3" + iconv-lite "0.4.23" + on-finished "~2.3.0" + qs "6.5.2" + raw-body "2.3.3" + type-is "~1.6.16" + +bonjour@^3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" + integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= + dependencies: + array-flatten "^2.1.0" + deep-equal "^1.0.1" + dns-equal "^1.0.0" + dns-txt "^2.0.2" + multicast-dns "^6.0.1" + multicast-dns-service-types "^1.1.0" + boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -722,7 +1183,7 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^2.3.1: +braces@^2.3.1, braces@^2.3.2: version "2.3.2" resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" dependencies: @@ -737,6 +1198,11 @@ braces@^2.3.1: split-string "^3.0.2" to-regex "^3.0.1" +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= + brotli-size@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/brotli-size/-/brotli-size-0.0.3.tgz#1d3855b38f182591a6f69da1516131676e5f62f2" @@ -755,6 +1221,74 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + integrity sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ= + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + integrity sha1-qk62jl17ZYuqa/alfmMMvXqT0pg= + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== + dependencies: + pako "~1.0.5" + +browserslist@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.1.1.tgz#328eb4ff1215b12df6589e9ab82f8adaa4fc8cd6" + integrity sha512-VBorw+tgpOtZ1BYhrVSVTzTt/3+vSE3eFUh0N2GCFK1HffceOaf32YS/bs6WiFhjDAblAFrx85jMy3BG9fBK2Q== + dependencies: + caniuse-lite "^1.0.30000884" + electron-to-chromium "^1.3.62" + node-releases "^1.0.0-alpha.11" + bs-logger@0.x: version "0.2.5" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.5.tgz#1d82f0cf88864e1341cd9262237f8d0748a49b22" @@ -767,6 +1301,20 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +buble@0.19.7: + version "0.19.7" + resolved "https://registry.yarnpkg.com/buble/-/buble-0.19.7.tgz#1dfd080ab688101aad5388d3304bc82601a244fd" + integrity sha512-YLgWxX/l+NnfotydBlxqCMPR4FREE4ubuHphALz0FxQ7u2hp3BzxTKQ4nKpapOaRJfEm1gukC68KnT2OymRK0g== + dependencies: + acorn "^6.1.1" + acorn-dynamic-import "^4.0.0" + acorn-jsx "^5.0.1" + chalk "^2.4.2" + magic-string "^0.25.2" + minimist "^1.2.0" + os-homedir "^1.0.1" + regexpu-core "^4.5.4" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -789,14 +1337,82 @@ buffer-from@1.x, buffer-from@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" +buffer-indexof@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" + integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + integrity sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg= + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" -builtin-modules@^3.0.0: +builtin-modules@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.1.0.tgz#aad97c15131eb76b65b50ef208e7584cd76a7484" + integrity sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw== + +builtin-status-codes@^3.0.0: version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.0.0.tgz#1e587d44b006620d90286cc7a9238bbc6129cab1" - integrity sha512-hMIeU4K2ilbXV6Uv93ZZ0Avg/M91RaKXucQ+4me2Do1txxBDyDZWCBa5bJSLqoNTRpXTLwEzIk1KmloenDDjhg== + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= + +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + integrity sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA== + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cacache@^11.0.2: + version "11.3.2" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-11.3.2.tgz#2d81e308e3d258ca38125b676b98b2ac9ce69bfa" + integrity sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg== + dependencies: + bluebird "^3.5.3" + chownr "^1.1.1" + figgy-pudding "^3.5.1" + glob "^7.1.3" + graceful-fs "^4.1.15" + lru-cache "^5.1.1" + mississippi "^3.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^6.0.1" + unique-filename "^1.1.1" + y18n "^4.0.0" cache-base@^1.0.1: version "1.0.1" @@ -812,6 +1428,11 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + integrity sha1-JtII6onje1y95gJQoV8DHBak1ms= + callsites@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3" @@ -826,6 +1447,11 @@ camelcase@^5.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== +caniuse-lite@^1.0.30000884: + version "1.0.30000957" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000957.tgz#fb1026bf184d7d62c685205358c3b24b9e29f7b3" + integrity sha512-8wxNrjAzyiHcLXN/iunskqQnJquQQ6VX8JHfW5kLgAPRSiSuKZiNfmIkP5j7jgyXqAQBSoXyJxfnbCFS0ThSiQ== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -837,7 +1463,12 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1: +ccount@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff" + integrity sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw== + +chalk@2.4.1, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" dependencies: @@ -854,6 +1485,26 @@ chalk@^2.1.0, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +character-entities-html4@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" + integrity sha512-sIrXwyna2+5b0eB9W149izTPJk/KkJTg6mEzDGibwBUkyH1SbDa+nf515Ppdi3MaH35lW0JFJDWeq9Luzes1Iw== + +character-entities-legacy@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" + integrity sha512-9NB2VbXtXYWdXzqrvAHykE/f0QJxzaKIpZ5QzNZrrgQ7Iyxr2vnfS8fCBNVW9nUEZE0lo57nxKRqnzY/dKrwlA== + +character-entities@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" + integrity sha512-sMoHX6/nBiy3KKfC78dnEalnpn0Az0oSNvqUWYTtYrhRI5iUIYsROU48G+E+kMFQzqXaJ8kHJZ85n7y6/PHgwQ== + +character-reference-invalid@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" + integrity sha512-7I/xceXfKyUJmSAn/jw8ve/9DyOP7XxufNYLI9Px7CmsKgEUaZLUTax6nZxGQtaoiZCjpu6cHPj20xC/vqRReQ== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -870,15 +1521,49 @@ cheerio@^1.0.0-rc.2: lodash "^4.15.0" parse5 "^3.0.1" -chownr@^1.0.1: +chokidar@^2.0.2, chokidar@^2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.5.tgz#0ae8434d962281a5f56c72869e79cb6d9d86ad4d" + integrity sha512-i0TprVWp+Kj4WRPtInjexJ8Q+BqTE909VpH8xVhXrJkoc5QC8VO9TryGOqTr+2hljzc1sC62t22h5tZePodM/A== + dependencies: + anymatch "^2.0.0" + async-each "^1.0.1" + braces "^2.3.2" + glob-parent "^3.1.0" + inherits "^2.0.3" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + normalize-path "^3.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.2.1" + upath "^1.1.1" + optionalDependencies: + fsevents "^1.2.7" + +chownr@^1.0.1, chownr@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.1.tgz#54726b8b8fff4df053c42187e801fb4412df1494" +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + integrity sha512-xDbVgyfDTT2piup/h8dK/y4QZfJRSa73bw1WZ8b4XM1o7fsFubUVGYcE+1ANtOzJJELGpYoG2961z0Z6OAld9A== + dependencies: + tslib "^1.9.0" + ci-info@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -888,6 +1573,13 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-webpack-plugin@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/clean-webpack-plugin/-/clean-webpack-plugin-1.0.1.tgz#b16ee2f1386aea403010236e632447c7d3505f5a" + integrity sha512-gvwfMsqu3HBgTVvaBa1H3AZKO03CHpr5uP92SPIktP3827EovAitwW+1xoqXyTxCuXnLYpMHG5ytS4AoukHDWA== + dependencies: + rimraf "^2.6.1" + cli-boxes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-1.0.0.tgz#4fa917c3e59c94a004cd61f8ee509da651687143" @@ -900,11 +1592,30 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-spinners@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.1.0.tgz#22c34b4d51f573240885b201efda4e4ec9fff3c7" + integrity sha512-8B00fJOEh1HPrx4fo5eW16XmE1PcL1tGpGrxy63CXGP9nHdPBN63X75hA1zhvQuhVztJWLqV58Roj2qlNM7cAA== + cli-width@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= +clipboard-copy@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/clipboard-copy/-/clipboard-copy-3.0.0.tgz#01ab812f4b5828b763e029172fd23ff92641eefb" + integrity sha512-1wgoaIRbRGTuv82jTpUgUkUFUQDF8H9hDa8IlkQfdETDHGk0CuisNPvFWNnPT/s3drikxhN33tvv54190c9lYA== + +clipboard@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" + integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + cliui@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" @@ -913,6 +1624,16 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= + +clsx@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.0.4.tgz#0c0171f6d5cb2fe83848463c15fcc26b4df8c2ec" + integrity sha512-1mQ557MIZTrL/140j+JVdRM6e31/OA4vTYxXgqIIZlndyfjHpyawKZia1Im05Vp9BWmImkcNrNtFYQMyFcgJDg== + co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -921,6 +1642,11 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" +collapse-white-space@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" + integrity sha512-YfQ1tAUZm561vpYD+5eyWN8+UsceQbSrqqlc/6zDY2gtAE+uZLSdkkovhnGpmCThsvKBFakq4EdY/FF93E8XIw== + collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -947,6 +1673,11 @@ colors@^1.3.2: resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== +colors@~0.6.0-1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= + combined-stream@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" @@ -959,10 +1690,42 @@ combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^2.19.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.0.tgz#d58bb2b5c1ee8f87b0d340027e9e94e222c5a422" + integrity sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ== + +commander@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.1.0.tgz#d121bbae860d9992a3d517ba96f56588e47c6781" + integrity sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E= + commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" +common-dir@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/common-dir/-/common-dir-2.0.2.tgz#cfea16f48564a0ecbb088065fad029047f469dc1" + integrity sha512-AiVcdIuevkFWyqrcHhpOWJbaFBLm+OLPiaRy3QikrMypalgA4ehU1SugZO2rPawgdzkTAycXGs3F65PGPA2DWg== + dependencies: + common-sequence "^1.0.2" + +common-sequence@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/common-sequence/-/common-sequence-1.0.2.tgz#30e07f3f8f6f7f9b3dee854f20b2d39eee086de8" + integrity sha1-MOB/P49vf5s97oVPILLTnu4Ibeg= + +common-tags@^1.7.2: + version "1.8.0" + resolved "https://registry.yarnpkg.com/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937" + integrity sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw== + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= + compare-versions@^3.2.1: version "3.4.0" resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.4.0.tgz#e0747df5c9cb7f054d6d3dc3e1dbc444f9e92b26" @@ -972,13 +1735,70 @@ component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" +compressible@~2.0.16: + version "2.0.16" + resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.16.tgz#a49bf9858f3821b64ce1be0296afc7380466a77f" + integrity sha512-JQfEOdnI7dASwCuSPWIeVYwc/zMsu/+tRhoUvEfXz2gxOA2DNjmG5vhtFdBlhWPPGo+RdT9S3tgc/uH5qgDiiA== + dependencies: + mime-db ">= 1.38.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" +concat-stream@^1.5.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +connect-history-api-fallback@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" + integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + integrity sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA= + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= + +content-disposition@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" + integrity sha1-DPaLud318r55YcOoUXjLhdunjLQ= + +content-type@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" + integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== convert-source-map@^1.1.0, convert-source-map@^1.4.0: version "1.6.0" @@ -986,10 +1806,51 @@ convert-source-map@^1.1.0, convert-source-map@^1.4.0: dependencies: safe-buffer "~5.1.1" +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + +cookie@0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" + integrity sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s= + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + copy-descriptor@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" +copy-webpack-plugin@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.6.0.tgz#e7f40dd8a68477d405dd1b7a854aae324b158bae" + integrity sha512-Y+SQCF+0NoWQryez2zXn5J5knmr9z/9qSQt7fbL78u83rxmigOy8X5+BFn8CFSuX+nKT8gpYwJX68ekqtQt6ZA== + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + globby "^7.1.1" + is-glob "^4.0.0" + loader-utils "^1.1.0" + minimatch "^3.0.4" + p-limit "^1.0.0" + serialize-javascript "^1.4.0" + +core-js@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.1.tgz#1343182634298f7f38622f95e73f54e48ddf4738" + integrity sha512-sco40rF+2KlE0ROMvydjkrVMMG1vYilP2ALoRXcYR4obqbYIuV3Bg+51GEDW+HF8n7NRA+iaA4qD0nD9lo9mew== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1006,15 +1867,38 @@ coveralls@^3.0.3: minimist "^1.2.0" request "^2.86.0" -cross-spawn@^5.0.1: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + integrity sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw== dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@6.0.5, cross-spawn@^6.0.0, cross-spawn@^6.0.5: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -1025,6 +1909,36 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-initials@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/css-initials/-/css-initials-0.2.0.tgz#14c225bd8656255a6baee07231ef82fa55aacaa3" + integrity sha512-t80yjg0pi4VAIc5itIqLh6M+ZlA+cB+gUXEQkDR09+ExTDwLMGfJ8YviBsGW+DklIrb2k9fwB75Io8ooWXDxxw== + css-select@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" @@ -1052,6 +1966,11 @@ csstype@^2.2.0: version "2.5.7" resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.5.7.tgz#bf9235d5872141eccfb2d16d82993c6b149179ff" +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + integrity sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA= + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1066,13 +1985,25 @@ data-urls@^1.0.0: whatwg-mimetype "^2.1.0" whatwg-url "^7.0.0" -debug@^2.2.0, debug@^2.3.3: +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + integrity sha1-6vQ5/U1ISK105cx9vvIAZyueNFs= + +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.6: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" +debug@^3.2.5, debug@^3.2.6: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -1103,6 +2034,11 @@ deep-assign@^2.0.0: dependencies: is-obj "^1.0.0" +deep-equal@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" + integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= + deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -1111,6 +2047,14 @@ deep-is@~0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" +default-gateway@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" + integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== + dependencies: + execa "^1.0.0" + ip-regex "^2.1.0" + default-require-extensions@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7" @@ -1118,6 +2062,13 @@ default-require-extensions@^2.0.0: dependencies: strip-bom "^3.0.0" +defaults@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" + integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= + dependencies: + clone "^1.0.2" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -1143,15 +2094,50 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +del@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/del/-/del-4.1.0.tgz#049543b8290e1a9293e2bd150ab3a06f637322b8" + integrity sha512-C4kvKNlYrwXhKxz97BuohF8YoGgQ23Xm9lvoHmgT7JaPGprSEjk3+XFled74Yt/x0ZABUHg2D67covzAPUKx5Q== + dependencies: + globby "^6.1.0" + is-path-cwd "^2.0.0" + is-path-in-cwd "^2.0.0" + p-map "^2.0.0" + pify "^4.0.1" + rimraf "^2.6.3" + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -detect-libc@^1.0.3: +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + integrity sha1-wHTS4qpqipoH29YfmhXCzYPsjsw= + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +destroy@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" + integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + +detect-libc@^1.0.2, detect-libc@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" @@ -1159,16 +2145,65 @@ detect-newline@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" +detect-node@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + +detect-port-alt@1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275" + integrity sha512-5tQykt+LqfJFBEYaDITx7S7cR7mJ/zQmLXZ2qt5w04ainYZw6tBf9dBunMjVeVOdYVRUzUOE4HkY5J7+uttb5Q== + dependencies: + address "^1.0.1" + debug "^2.6.0" + diff-sequences@^24.3.0: version "24.3.0" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975" integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw== +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0: + version "2.2.2" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" + integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== + dependencies: + path-type "^3.0.0" + discontinuous-range@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/discontinuous-range/-/discontinuous-range-1.0.0.tgz#e38331f0844bba49b9a9cb71c771585aab1bc65a" -doctrine@^2.1.0: +dns-equal@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" + integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= + +dns-packet@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" + integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== + dependencies: + ip "^1.1.0" + safe-buffer "^5.0.1" + +dns-txt@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" + integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= + dependencies: + buffer-indexof "^1.0.0" + +doctrine@^2.0.0, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== @@ -1189,6 +2224,11 @@ dom-serializer@0, dom-serializer@~0.1.0: domelementtype "~1.1.1" entities "~1.1.1" +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== + domelementtype@1, domelementtype@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" @@ -1228,6 +2268,16 @@ duplexer@^0.1.1: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.7.1" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" + integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -1235,6 +2285,34 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + +electron-to-chromium@^1.3.62: + version "1.3.124" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.124.tgz#861fc0148748a11b3e5ccebdf8b795ff513fa11f" + integrity sha512-glecGr/kFdfeXUHOHAWvGcXrxNU+1wSO/t5B23tT1dtlvYB26GY8aHzZSWD7HqhqC800Lr+w/hQul6C5AF542w== + +elliptic@^6.0.0: + version "6.4.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.1.tgz#c2d0b7776911b86722c632c3c06c60f2f819939a" + integrity sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ== + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +"emoji-regex@>=6.0.0 <=6.1.1": + version "6.1.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.1.1.tgz#c6cd0ec1b0642e2a3c67a1137efc5e796da4f88e" + integrity sha1-xs0OwbBkLio8Z6ETfvxeeW2k+I4= + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -1244,13 +2322,18 @@ emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" dependencies: once "^1.4.0" -enhanced-resolve@^4.0.0: +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" dependencies: @@ -1262,24 +2345,25 @@ entities@^1.1.1, entities@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" -enzyme-adapter-react-16@^1.11.2: - version "1.11.2" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.11.2.tgz#8efeafb27e96873a5492fdef3f423693182eb9d4" - integrity sha512-2ruTTCPRb0lPuw/vKTXGVZVBZqh83MNDnakMhzxhpJcIbneEwNy2Cv0KvL97pl57/GOazJHflWNLjwWhex5AAA== +enzyme-adapter-react-16@^1.12.1: + version "1.12.1" + resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.12.1.tgz#6a2d74c80559d35ac0a91ca162fa45f4186290cf" + integrity sha512-GB61gvY97XvrA6qljExGY+lgI6BBwz+ASLaRKct9VQ3ozu0EraqcNn3CcrUckSGIqFGa1+CxO5gj5is5t3lwrw== dependencies: - enzyme-adapter-utils "^1.10.1" + enzyme-adapter-utils "^1.11.0" object.assign "^4.1.0" object.values "^1.1.0" prop-types "^15.7.2" - react-is "^16.8.4" + react-is "^16.8.6" react-test-renderer "^16.0.0-0" semver "^5.6.0" -enzyme-adapter-utils@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.10.1.tgz#58264efa19a7befdbf964fb7981a108a5452ac96" - integrity sha512-oasinhhLoBuZsIkTe8mx0HiudtfErUtG0Ooe1FOplu/t4c9rOmyG5gtrBASK6u4whHIRWvv0cbZMElzNTR21SA== +enzyme-adapter-utils@^1.11.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.11.0.tgz#6ffff782b1b57dd46c72a845a91fc4103956a117" + integrity sha512-0VZeoE9MNx+QjTfsjmO1Mo+lMfunucYB4wt5ficU85WB/LoetTJrbuujmHP3PJx6pSoaAuLA+Mq877x4LoxdNg== dependencies: + airbnb-prop-types "^2.12.0" function.prototype.name "^1.1.0" object.assign "^4.1.0" object.fromentries "^2.0.0" @@ -1320,7 +2404,7 @@ enzyme@^3.9.0: rst-selector-parser "^2.2.3" string.prototype.trim "^1.1.2" -errno@^0.1.3: +errno@^0.1.3, errno@~0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" dependencies: @@ -1363,10 +2447,37 @@ es-to-primitive@^1.1.1, es-to-primitive@^1.2.0: is-date-object "^1.0.1" is-symbol "^1.0.2" -escape-string-regexp@^1.0.5: +es6-object-assign@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c" + integrity sha1-wsNYJlYkfDnqEHyx5mUrb58kUjw= + +es6-promise@^4.2.6: + version "4.2.6" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.6.tgz#b685edd8258886365ea62b57d30de28fadcd974f" + integrity sha512-aRVgGdnmW2OiySVPUC9e6m+plolMAJKjZnQlCwNSuK5yQ0JN61DZSO1X1Ufd1foqWRAlig0rhduTCHe7sVtK5Q== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" +escodegen@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.1.tgz#c485ff8d6b4cdb89e27f4a856e91f118401ca510" + integrity sha512-JwiqFD9KdGVVpeuRa68yU3zZnBEOcPs0nKW7wZzXky8Z7tffdYUHbe11bPCV5jYlK6DVdKLWLm0f5I/QlL0Kmw== + dependencies: + esprima "^3.1.3" + estraverse "^4.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + escodegen@^1.9.1: version "1.11.0" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.11.0.tgz#b27a9389481d5bfd5bec76f7bb1eb3f8f4556589" @@ -1478,11 +2589,16 @@ espree@^5.0.1: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" +esprima@^2.1.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= + esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" -esprima@^4.0.0: +esprima@^4.0.0, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -1513,6 +2629,43 @@ esutils@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + +eventemitter3@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" + integrity sha512-ivIvhpq/Y0uSjcHDcOIccjmYjGLcP09MFGE7ysAwkAvkXfpZlC985pH2/ui64DKazbTW/4kN3yqozUxlXzI6cA== + +events@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88" + integrity sha512-Dc381HFWJzEOhQ+d8pkNon++bk9h6cdAoAj4iE6Q4y6xgTzySWXlKn05/TVNpjnfRqi/X0EpJEJohPjNI3zpVA== + +eventsource@0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" + integrity sha1-Cs7ehJ7X3RzMMsgRuxG5RNTykjI= + dependencies: + original ">=0.0.5" + +eventsource@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" + integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== + dependencies: + original "^1.0.0" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + exec-sh@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.2.tgz#6738de2eb7c8e671d0366aea0b0db8c6f7d7391b" @@ -1564,18 +2717,61 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expect@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-24.5.0.tgz#492fb0df8378d8474cc84b827776b069f46294ed" - integrity sha512-p2Gmc0CLxOgkyA93ySWmHFYHUPFIHG6XZ06l7WArWAsrqYVaVEkOU5NtT5i68KUyGKbkQgDCkiT65bWmdoL6Bw== +expand-tilde@^2.0.0, expand-tilde@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" + integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= + dependencies: + homedir-polyfill "^1.0.1" + +expect@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/expect/-/expect-24.7.1.tgz#d91defbab4e627470a152feaf35b3c31aa1c7c14" + integrity sha512-mGfvMTPduksV3xoI0xur56pQsg2vJjNf5+a+bXOjqCkiCBbmCayrBbHS/75y9K430cfqyocPr2ZjiNiRx4SRKw== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" ansi-styles "^3.2.0" jest-get-type "^24.3.0" - jest-matcher-utils "^24.5.0" - jest-message-util "^24.5.0" + jest-matcher-utils "^24.7.0" + jest-message-util "^24.7.1" jest-regex-util "^24.3.0" +express@^4.16.4: + version "4.16.4" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.4.tgz#fddef61926109e24c515ea97fd2f1bdbf62df12e" + integrity sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg== + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.3" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.4" + qs "6.5.2" + range-parser "~1.2.0" + safe-buffer "5.1.2" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -1589,11 +2785,11 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@~3.0.2: +extend@^3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" -external-editor@^3.0.3: +external-editor@^3.0.0, external-editor@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" integrity sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA== @@ -1632,6 +2828,18 @@ fast-deep-equal@^2.0.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" integrity sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk= +fast-glob@^2.0.2: + version "2.2.6" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.6.tgz#a5d5b697ec8deda468d85a74035290a025a95295" + integrity sha512-0BvMaZc1k9F+MeWWMe8pL6YltFzZYcJsYU7D4JyDA6PAczaXvxqQQ/z+mDF7/4Mw01DeUc+i3CTKajnkANkV4w== + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.1.2" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.3" + micromatch "^3.1.10" + fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -1640,12 +2848,31 @@ fast-levenshtein@~2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +faye-websocket@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" + integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= + dependencies: + websocket-driver ">=0.5.1" + +faye-websocket@~0.11.0, faye-websocket@~0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" + integrity sha1-8O/hjE9W5PQK/H4Gxxn9XuYYjzg= + dependencies: + websocket-driver ">=0.5.1" + fb-watchman@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.0.tgz#54e9abf7dfa2f26cd9b1636c588c1afc05de5d58" dependencies: bser "^2.0.0" +figgy-pudding@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" + integrity sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w== + figures@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" @@ -1668,7 +2895,7 @@ fileset@^2.0.3: glob "^7.0.3" minimatch "^3.0.3" -filesize@^3.6.1: +filesize@3.6.1, filesize@^3.6.1: version "3.6.1" resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317" integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg== @@ -1682,13 +2909,59 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" -find-up@^3.0.0: +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + integrity sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + integrity sha1-kojj6ePMN0hxfTnq3hfPcfww7m8= + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-cache-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" + integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== + dependencies: + commondir "^1.0.1" + make-dir "^2.0.0" + pkg-dir "^3.0.0" + +find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== dependencies: locate-path "^3.0.0" +find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= + dependencies: + locate-path "^2.0.0" + +findup@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/findup/-/findup-0.1.5.tgz#8ad929a3393bac627957a7e5de4623b06b0e2ceb" + integrity sha1-itkpozk7rGJ5V6fl3kYjsGsOLOs= + dependencies: + colors "~0.6.0-1" + commander "~2.1.0" + flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -1703,6 +2976,21 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.0.tgz#55122b6536ea496b4b44893ee2608141d10d9916" integrity sha512-R+H8IZclI8AAkSBRQJLVOsxwAoHd6WC40b4QTNWIjzAa6BXOBfQcM587MXDTVPeYaopFNWHUFLx7eNmHDSxMWg== +flush-write-stream@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" + integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== + dependencies: + inherits "^2.0.3" + readable-stream "^2.3.6" + +follow-redirects@^1.0.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" + integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== + dependencies: + debug "^3.2.6" + for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" @@ -1719,12 +3007,30 @@ form-data@~2.3.2: combined-stream "1.0.6" mime-types "^2.1.12" +forwarded@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" + integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= + fragment-cache@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" dependencies: map-cache "^0.2.2" +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" @@ -1739,14 +3045,44 @@ fs-extra@7.0.1: jsonfile "^4.0.0" universalify "^0.1.0" +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + integrity sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ== + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" +fsevents@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.7.tgz#4851b664a3783e52003b3c66eb0eee1074933aa4" + integrity sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw== + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" +function.name-polyfill@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/function.name-polyfill/-/function.name-polyfill-1.0.6.tgz#c54e37cae0a77dfcb49d47982815b0826b5c60d9" + integrity sha512-ejQivNFbBPTY5O/waFta6D5AzV8GJiM/fMDaT6LrsYax1cb4eipxuQqKNlugF2jlcXIjifsqvju3wsgV35TELg== + function.prototype.name@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.0.tgz#8bd763cc0af860a859cc5d49384d74b932cd2327" @@ -1777,6 +3113,11 @@ get-caller-file@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" +get-own-enumerable-property-symbols@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz#b877b49a5c16aefac3655f2ed2ea5b684df8d203" + integrity sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg== + get-stream@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" @@ -1803,6 +3144,26 @@ github-from-package@0.0.0: resolved "https://registry.yarnpkg.com/github-from-package/-/github-from-package-0.0.0.tgz#97fb5d96bfde8973313f20e8288ef9a167fa64ce" integrity sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= +github-slugger@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/github-slugger/-/github-slugger-1.2.1.tgz#47e904e70bf2dccd0014748142d31126cfd49508" + integrity sha512-SsZUjg/P03KPzQBt7OxJPasGw6NRO5uOgiZ5RGXVud5iSIZ0eNZeNp5rTwCxtavrRUa/A77j8mePVc5lEvk0KQ== + dependencies: + emoji-regex ">=6.0.0 <=6.1.1" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= + glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: version "7.1.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" @@ -1814,11 +3175,81 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +global-modules@1.0.0, global-modules@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" + integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== + dependencies: + global-prefix "^1.0.1" + is-windows "^1.0.1" + resolve-dir "^1.0.0" + +global-prefix@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" + integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= + dependencies: + expand-tilde "^2.0.2" + homedir-polyfill "^1.0.1" + ini "^1.3.4" + is-windows "^1.0.1" + which "^1.2.14" + globals@^11.1.0, globals@^11.7.0: version "11.11.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.11.0.tgz#dcf93757fa2de5486fbeed7118538adf789e9c2e" integrity sha512-WHq43gS+6ufNOEqlrDBxVEbb8ntfXrfAUU2ZOpCxrBdGKW3gyv8mCxAfIBD0DroPKGrJ2eSsXsLtY9MPntsyTw== +globby@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" + integrity sha512-oMrYrJERnKBLXNLVTqhm3vPEdJ/b2ZE28xN4YARiix1NOIOBPEpOUnm844K1iu/BkphCaf2WNFwMszv8Soi1pw== + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globby@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" + integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= + dependencies: + array-union "^1.0.1" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" + integrity sha1-+yzP+UAfhgCUXfral0QMypcrhoA= + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +glogg@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" + integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== + dependencies: + sparkles "^1.0.0" + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + graceful-fs@^4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -1836,7 +3267,7 @@ growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" -gzip-size@^5.0.0: +gzip-size@5.0.0, gzip-size@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.0.0.tgz#a55ecd99222f4c48fd8c01c625ce3b349d0a0e80" integrity sha512-5iI7omclyqrnWw4XbXAmGhPsABkSIDQonv2K0h61lybgofWa6iZyvrI3r2zsJH4P8Nb64fFVzlvfhs0g7BBxAA== @@ -1844,6 +3275,11 @@ gzip-size@^5.0.0: duplexer "^0.1.1" pify "^3.0.0" +handle-thing@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754" + integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ== + handlebars@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" @@ -1911,22 +3347,69 @@ has@^1.0.1, has@^1.0.3: dependencies: function-bind "^1.1.1" -hosted-git-info@^2.1.4: - version "2.7.1" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" - -html-element-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.0.tgz#19a41940225153ecdfead74f8509154ff1cdc18b" - integrity sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw== +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + integrity sha1-X8hoaEfs1zSZQDMZprCj8/auSRg= dependencies: - array-filter "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.0.1" -html-encoding-sniffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.7" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" + integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== dependencies: - whatwg-encoding "^1.0.1" + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +homedir-polyfill@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" + integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== + dependencies: + parse-passwd "^1.0.0" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-element-map@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.0.0.tgz#19a41940225153ecdfead74f8509154ff1cdc18b" + integrity sha512-/SP6aOiM5Ai9zALvCxDubIeez0LvG3qP7R9GcRDnJEP/HBmv0A8A9K0o8+HFudcFt46+i921ANjzKsjPjb7Enw== + dependencies: + array-filter "^1.0.0" + +html-encoding-sniffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" + dependencies: + whatwg-encoding "^1.0.1" + +html-entities@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" + integrity sha1-DfKTUfByEWNRXfueVUPl9u7VFi8= htmlparser2@^3.9.1: version "3.9.2" @@ -1939,6 +3422,45 @@ htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^2.0.2" +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= + +http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: + version "1.6.3" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.4.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.0.tgz#d65edbede84349d0dc30320815a15d39cc3cbbd8" + integrity sha512-cZdEF7r4gfRIq7ezX9J0T+kQmJNOub71dWbgAXVHDct80TKP4MCETtZQ31xyv38UwgzkWPYF/Xc0ge55dW9Z9w== + +http-proxy-middleware@^0.19.1: + version "0.19.1" + resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" + integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== + dependencies: + http-proxy "^1.17.0" + is-glob "^4.0.0" + lodash "^4.17.11" + micromatch "^3.1.10" + +http-proxy@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.17.0.tgz#7ad38494658f84605e2f6db4436df410f4e5be9a" + integrity sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g== + dependencies: + eventemitter3 "^3.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -1947,12 +3469,51 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -iconv-lite@0.4.24, iconv-lite@^0.4.24: +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= + +hyphenate-style-name@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48" + integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ== + +iconv-lite@0.4.23: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + integrity sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" dependencies: safer-buffer ">= 2.1.2 < 3" +ieee754@^1.1.4: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + integrity sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ== + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + ignore@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -1968,6 +3529,11 @@ iltorb@^2.0.5: prebuild-install "^5.2.1" which-pm-runs "^1.0.0" +immer@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/immer/-/immer-1.7.2.tgz#a51e9723c50b27e132f6566facbec1c85fc69547" + integrity sha512-4Urocwu9+XLDJw4Tc6ZCg7APVjjLInCFvO4TwGsAYV5zT6YYSor14dsZR0+0tHlDIN92cFUOq+i7fC00G5vTxA== + import-fresh@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.0.0.tgz#a3d897f420cab0e671236897f75bc14b4885c390" @@ -1988,6 +3554,11 @@ imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -1995,14 +3566,38 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@^2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" -ini@~1.3.0: +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + +ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" +inquirer@6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.0.tgz#51adcd776f661369dc1e894859c2560a224abdd8" + integrity sha512-QIEQG4YyQ2UYZGDC4srMZ7BjHOmNk1lR2JQj5UknBapklm6WHA+VVH7N+sUdX3A7NeCfGF8o4X1S3Ao7nAcIeg== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.0" + figures "^2.0.0" + lodash "^4.17.10" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.1.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + inquirer@^6.2.2: version "6.2.2" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.2.2.tgz#46941176f65c9eb20804627149b743a218f25406" @@ -2022,6 +3617,14 @@ inquirer@^6.2.2: strip-ansi "^5.0.0" through "^2.3.6" +internal-ip@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" + integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== + dependencies: + default-gateway "^4.2.0" + ipaddr.js "^1.9.0" + invariant@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" @@ -2033,6 +3636,26 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ip-regex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" + integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= + +ip@^1.1.0, ip@^1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" + integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= + +ipaddr.js@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.8.0.tgz#eaa33d6ddd7ace8f7f6fe0c9ca0440e706738b1e" + integrity sha1-6qM9bd16zo9/b+DJygRA5wZzix4= + +ipaddr.js@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" + integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== + is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -2045,10 +3668,35 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" +is-alphabetical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" + integrity sha512-V0xN4BYezDHcBSKb1QHUFMlR4as/XEuCZBzMJUU4n7+Cbt33SmUnSol+pnXFvLxSHNq2CemUXNdaXV6Flg7+xg== + +is-alphanumeric@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" + integrity sha1-Spzvcdr0wAHB2B1j0UDPU/1oifQ= + +is-alphanumerical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" + integrity sha512-pyfU/0kHdISIgslFfZN9nfY1Gk3MquQgUm1mJTjdkEPpkAKNWuBTSqFwewOpR7N351VkErCiyV71zX7mlQQqsg== + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= + dependencies: + binary-extensions "^1.0.0" + is-boolean-object@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93" @@ -2057,6 +3705,11 @@ is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" +is-buffer@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" + integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw== + is-builtin-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" @@ -2090,6 +3743,11 @@ is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" +is-decimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" + integrity sha512-TRzl7mOCchnhchN+f3ICUCzYvL9ul7R+TYOsZ8xia++knyZAJfv/uA1FvQXsAnYIl1T3B2X5E/J7Wb1QXiIBXg== + is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -2106,6 +3764,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -2116,6 +3779,11 @@ is-extendable@^1.0.1: dependencies: is-plain-object "^2.0.4" +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -2131,6 +3799,30 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.0.0.tgz#038c31b774709641bda678b1f06a4e3227c10b3e" integrity sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g== +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" + integrity sha512-but/G3sapV3MNyqiDBLrOi4x8uCIw0RY3o/Vb5GT0sMFHrVV7731wFSVy41T5FO1og7G0gXLJh0MkgPRouko/A== + +is-in-browser@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" + integrity sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU= + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -2146,11 +3838,35 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-obj@^1.0.0: +is-obj@^1.0.0, is-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8= +is-path-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.0.0.tgz#d4777a8e227a00096a31f030db3770f84b116c02" + integrity sha512-m5dHHzpOXEiv18JEORttBO64UgTEypx99vCxQLjbBvGhOJxnTNglYoFXxwo6AbsQb79sqqycQEHv2hWkHZAijA== + +is-path-in-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.0.0.tgz#68e452a6eec260500cec21e029c0a44cc0dcd2ea" + integrity sha512-6Vz5Gc9s/sDA3JBVu0FzWufm8xaBsqy1zn8Q6gmvGP6nSDMw78aS4poBNeatWjaRpTpxxLn1WOndAiOlk+qY8A== + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= + is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -2168,6 +3884,16 @@ is-regex@^1.0.4: dependencies: has "^1.0.1" +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= + +is-root@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019" + integrity sha512-F/pJIk8QD6OX5DNhRB7hWamLsUilmkDGho48KbgZ6xg/lmAZXHxzXQ91jzB3yRSw5kdQGGGc4yz8HYhTYIMWPg== + is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -2190,11 +3916,26 @@ is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" -is-windows@^1.0.2: +is-whitespace-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" + integrity sha512-SzM+T5GKUCtLhlHFKt2SDAX2RFzfS6joT91F2/WSi9LxgFdsnhfPK/UIA+JhRR2xuyLdrCys2PiFDrtn1fU5hQ== + +is-windows@^1.0.1, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" -isarray@1.0.0, isarray@~1.0.0: +is-word-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" + integrity sha512-T3FlsX8rCHAH8e7RE7PfOPZVFQlcV3XRF9eOOBQ1uf70OxO7CjjSOjeImMPCADBdYWcStAbVbYvJ1m2D3tb+EA== + +is-wsl@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" + integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -2287,65 +4028,71 @@ istanbul-reports@^2.1.1: dependencies: handlebars "^4.1.0" -jest-changed-files@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.5.0.tgz#4075269ee115d87194fd5822e642af22133cf705" - integrity sha512-Ikl29dosYnTsH9pYa1Tv9POkILBhN/TLZ37xbzgNsZ1D2+2n+8oEZS2yP1BrHn/T4Rs4Ggwwbp/x8CKOS5YJOg== +javascript-stringify@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" + integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= + +jest-changed-files@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.7.0.tgz#39d723a11b16ed7b373ac83adc76a69464b0c4fa" + integrity sha512-33BgewurnwSfJrW7T5/ZAXGE44o7swLslwh8aUckzq2e17/2Os1V0QU506ZNik3hjs8MgnEMKNkcud442NCDTw== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" execa "^1.0.0" throat "^4.0.0" -jest-cli@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.5.0.tgz#598139d3446d1942fb7dc93944b9ba766d756d4b" - integrity sha512-P+Jp0SLO4KWN0cGlNtC7JV0dW1eSFR7eRpoOucP2UM0sqlzp/bVHeo71Omonvigrj9AvCKy7NtQANtqJ7FXz8g== +jest-cli@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.7.1.tgz#6093a539073b6f4953145abeeb9709cd621044f1" + integrity sha512-32OBoSCVPzcTslGFl6yVCMzB2SqX3IrWwZCY5mZYkb0D2WsogmU3eV2o8z7+gRQa4o4sZPX/k7GU+II7CxM6WQ== dependencies: - "@jest/core" "^24.5.0" - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/core" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" chalk "^2.0.1" exit "^0.1.2" import-local "^2.0.0" is-ci "^2.0.0" - jest-config "^24.5.0" - jest-util "^24.5.0" - jest-validate "^24.5.0" + jest-config "^24.7.1" + jest-util "^24.7.1" + jest-validate "^24.7.0" prompts "^2.0.1" realpath-native "^1.1.0" yargs "^12.0.2" -jest-config@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.5.0.tgz#404d1bc6bb81aed6bd1890d07e2dca9fbba2e121" - integrity sha512-t2UTh0Z2uZhGBNVseF8wA2DS2SuBiLOL6qpLq18+OZGfFUxTM7BzUVKyHFN/vuN+s/aslY1COW95j1Rw81huOQ== +jest-config@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.7.1.tgz#6c1dd4db82a89710a3cf66bdba97827c9a1cf052" + integrity sha512-8FlJNLI+X+MU37j7j8RE4DnJkvAghXmBWdArVzypW6WxfGuxiL/CCkzBg0gHtXhD2rxla3IMOSUAHylSKYJ83g== dependencies: "@babel/core" "^7.1.0" - "@jest/types" "^24.5.0" - babel-jest "^24.5.0" + "@jest/test-sequencer" "^24.7.1" + "@jest/types" "^24.7.0" + babel-jest "^24.7.1" chalk "^2.0.1" glob "^7.1.1" - jest-environment-jsdom "^24.5.0" - jest-environment-node "^24.5.0" + jest-environment-jsdom "^24.7.1" + jest-environment-node "^24.7.1" jest-get-type "^24.3.0" - jest-jasmine2 "^24.5.0" + jest-jasmine2 "^24.7.1" jest-regex-util "^24.3.0" - jest-resolve "^24.5.0" - jest-util "^24.5.0" - jest-validate "^24.5.0" + jest-resolve "^24.7.1" + jest-util "^24.7.1" + jest-validate "^24.7.0" micromatch "^3.1.10" - pretty-format "^24.5.0" + pretty-format "^24.7.0" realpath-native "^1.1.0" -jest-diff@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.5.0.tgz#a2d8627964bb06a91893c0fbcb28ab228c257652" - integrity sha512-mCILZd9r7zqL9Uh6yNoXjwGQx0/J43OD2vvWVKwOEOLZliQOsojXwqboubAQ+Tszrb6DHGmNU7m4whGeB9YOqw== +jest-diff@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.7.0.tgz#5d862899be46249754806f66e5729c07fcb3580f" + integrity sha512-ULQZ5B1lWpH70O4xsANC4tf4Ko6RrpwhE3PtG6ERjMg1TiYTC2Wp4IntJVGro6a8HG9luYHhhmF4grF0Pltckg== dependencies: chalk "^2.0.1" diff-sequences "^24.3.0" jest-get-type "^24.3.0" - pretty-format "^24.5.0" + pretty-format "^24.7.0" jest-docblock@^24.3.0: version "24.3.0" @@ -2354,119 +4101,123 @@ jest-docblock@^24.3.0: dependencies: detect-newline "^2.1.0" -jest-each@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.5.0.tgz#da14d017a1b7d0f01fb458d338314cafe7f72318" - integrity sha512-6gy3Kh37PwIT5sNvNY2VchtIFOOBh8UCYnBlxXMb5sr5wpJUDPTUATX2Axq1Vfk+HWTMpsYPeVYp4TXx5uqUBw== +jest-each@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.7.1.tgz#fcc7dda4147c28430ad9fb6dc7211cd17ab54e74" + integrity sha512-4fsS8fEfLa3lfnI1Jw6NxjhyRTgfpuOVTeUZZFyVYqeTa4hPhr2YkToUhouuLTrL2eMGOfpbdMyRx0GQ/VooKA== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" chalk "^2.0.1" jest-get-type "^24.3.0" - jest-util "^24.5.0" - pretty-format "^24.5.0" - -jest-environment-jsdom@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.5.0.tgz#1c3143063e1374100f8c2723a8b6aad23b6db7eb" - integrity sha512-62Ih5HbdAWcsqBx2ktUnor/mABBo1U111AvZWcLKeWN/n/gc5ZvDBKe4Og44fQdHKiXClrNGC6G0mBo6wrPeGQ== - dependencies: - "@jest/environment" "^24.5.0" - "@jest/fake-timers" "^24.5.0" - "@jest/types" "^24.5.0" - jest-mock "^24.5.0" - jest-util "^24.5.0" + jest-util "^24.7.1" + pretty-format "^24.7.0" + +jest-environment-jsdom@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.7.1.tgz#a40e004b4458ebeb8a98082df135fd501b9fbbd6" + integrity sha512-Gnhb+RqE2JuQGb3kJsLF8vfqjt3PHKSstq4Xc8ic+ax7QKo4Z0RWGucU3YV+DwKR3T9SYc+3YCUQEJs8r7+Jxg== + dependencies: + "@jest/environment" "^24.7.1" + "@jest/fake-timers" "^24.7.1" + "@jest/types" "^24.7.0" + jest-mock "^24.7.0" + jest-util "^24.7.1" jsdom "^11.5.1" -jest-environment-node@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.5.0.tgz#763eebdf529f75b60aa600c6cf8cb09873caa6ab" - integrity sha512-du6FuyWr/GbKLsmAbzNF9mpr2Iu2zWSaq/BNHzX+vgOcts9f2ayXBweS7RAhr+6bLp6qRpMB6utAMF5Ygktxnw== +jest-environment-node@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.7.1.tgz#fa2c047a31522a48038d26ee4f7c8fd9c1ecfe12" + integrity sha512-GJJQt1p9/C6aj6yNZMvovZuxTUd+BEJprETdvTKSb4kHcw4mFj8777USQV0FJoJ4V3djpOwA5eWyPwfq//PFBA== dependencies: - "@jest/environment" "^24.5.0" - "@jest/fake-timers" "^24.5.0" - "@jest/types" "^24.5.0" - jest-mock "^24.5.0" - jest-util "^24.5.0" + "@jest/environment" "^24.7.1" + "@jest/fake-timers" "^24.7.1" + "@jest/types" "^24.7.0" + jest-mock "^24.7.0" + jest-util "^24.7.1" jest-get-type@^24.3.0: version "24.3.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.3.0.tgz#582cfd1a4f91b5cdad1d43d2932f816d543c65da" integrity sha512-HYF6pry72YUlVcvUx3sEpMRwXEWGEPlJ0bSPVnB3b3n++j4phUEoSPcS6GC0pPJ9rpyPSe4cb5muFo6D39cXow== -jest-haste-map@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.5.0.tgz#3f17d0c548b99c0c96ed2893f9c0ccecb2eb9066" - integrity sha512-mb4Yrcjw9vBgSvobDwH8QUovxApdimGcOkp+V1ucGGw4Uvr3VzZQBJhNm1UY3dXYm4XXyTW2G7IBEZ9pM2ggRQ== +jest-haste-map@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.7.1.tgz#772e215cd84080d4bbcb759cfb668ad649a21471" + integrity sha512-g0tWkzjpHD2qa03mTKhlydbmmYiA2KdcJe762SbfFo/7NIMgBWAA0XqQlApPwkWOF7Cxoi/gUqL0i6DIoLpMBw== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" + anymatch "^2.0.0" fb-watchman "^2.0.0" graceful-fs "^4.1.15" invariant "^2.2.4" jest-serializer "^24.4.0" - jest-util "^24.5.0" - jest-worker "^24.4.0" + jest-util "^24.7.1" + jest-worker "^24.6.0" micromatch "^3.1.10" sane "^4.0.3" + walker "^1.0.7" + optionalDependencies: + fsevents "^1.2.7" -jest-jasmine2@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.5.0.tgz#e6af4d7f73dc527d007cca5a5b177c0bcc29d111" - integrity sha512-sfVrxVcx1rNUbBeyIyhkqZ4q+seNKyAG6iM0S2TYBdQsXjoFDdqWFfsUxb6uXSsbimbXX/NMkJIwUZ1uT9+/Aw== +jest-jasmine2@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.7.1.tgz#01398686dabe46553716303993f3be62e5d9d818" + integrity sha512-Y/9AOJDV1XS44wNwCaThq4Pw3gBPiOv/s6NcbOAkVRRUEPu+36L2xoPsqQXsDrxoBerqeyslpn2TpCI8Zr6J2w== dependencies: "@babel/traverse" "^7.1.0" - "@jest/environment" "^24.5.0" - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/environment" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" chalk "^2.0.1" co "^4.6.0" - expect "^24.5.0" + expect "^24.7.1" is-generator-fn "^2.0.0" - jest-each "^24.5.0" - jest-matcher-utils "^24.5.0" - jest-message-util "^24.5.0" - jest-runtime "^24.5.0" - jest-snapshot "^24.5.0" - jest-util "^24.5.0" - pretty-format "^24.5.0" + jest-each "^24.7.1" + jest-matcher-utils "^24.7.0" + jest-message-util "^24.7.1" + jest-runtime "^24.7.1" + jest-snapshot "^24.7.1" + jest-util "^24.7.1" + pretty-format "^24.7.0" throat "^4.0.0" -jest-leak-detector@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.5.0.tgz#21ae2b3b0da252c1171cd494f75696d65fb6fa89" - integrity sha512-LZKBjGovFRx3cRBkqmIg+BZnxbrLqhQl09IziMk3oeh1OV81Hg30RUIx885mq8qBv1PA0comB9bjKcuyNO1bCQ== +jest-leak-detector@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.7.0.tgz#323ff93ed69be12e898f5b040952f08a94288ff9" + integrity sha512-zV0qHKZGXtmPVVzT99CVEcHE9XDf+8LwiE0Ob7jjezERiGVljmqKFWpV2IkG+rkFIEUHFEkMiICu7wnoPM/RoQ== dependencies: - pretty-format "^24.5.0" + pretty-format "^24.7.0" -jest-matcher-utils@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.5.0.tgz#5995549dcf09fa94406e89526e877b094dad8770" - integrity sha512-QM1nmLROjLj8GMGzg5VBra3I9hLpjMPtF1YqzQS3rvWn2ltGZLrGAO1KQ9zUCVi5aCvrkbS5Ndm2evIP9yZg1Q== +jest-matcher-utils@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.7.0.tgz#bbee1ff37bc8b2e4afcaabc91617c1526af4bcd4" + integrity sha512-158ieSgk3LNXeUhbVJYRXyTPSCqNgVXOp/GT7O94mYd3pk/8+odKTyR1JLtNOQSPzNi8NFYVONtvSWA/e1RDXg== dependencies: chalk "^2.0.1" - jest-diff "^24.5.0" + jest-diff "^24.7.0" jest-get-type "^24.3.0" - pretty-format "^24.5.0" + pretty-format "^24.7.0" -jest-message-util@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.5.0.tgz#181420a65a7ef2e8b5c2f8e14882c453c6d41d07" - integrity sha512-6ZYgdOojowCGiV0D8WdgctZEAe+EcFU+KrVds+0ZjvpZurUW2/oKJGltJ6FWY2joZwYXN5VL36GPV6pNVRqRnQ== +jest-message-util@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.7.1.tgz#f1dc3a6c195647096a99d0f1dadbc447ae547018" + integrity sha512-dk0gqVtyqezCHbcbk60CdIf+8UHgD+lmRHifeH3JRcnAqh4nEyPytSc9/L1+cQyxC+ceaeP696N4ATe7L+omcg== dependencies: "@babel/code-frame" "^7.0.0" - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" "@types/stack-utils" "^1.0.1" chalk "^2.0.1" micromatch "^3.1.10" slash "^2.0.0" stack-utils "^1.0.1" -jest-mock@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.5.0.tgz#976912c99a93f2a1c67497a9414aa4d9da4c7b76" - integrity sha512-ZnAtkWrKf48eERgAOiUxVoFavVBziO2pAi2MfZ1+bGXVkDfxWLxU0//oJBkgwbsv6OAmuLBz4XFFqvCFMqnGUw== +jest-mock@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.7.0.tgz#e49ce7262c12d7f5897b0d8af77f6db8e538023b" + integrity sha512-6taW4B4WUcEiT2V9BbOmwyGuwuAFT2G8yghF7nyNW1/2gq5+6aTqSPcS9lS6ArvEkX55vbPAS/Jarx5LSm4Fng== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" jest-pnp-resolver@^1.2.1: version "1.2.1" @@ -2478,75 +4229,75 @@ jest-regex-util@^24.3.0: resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.3.0.tgz#d5a65f60be1ae3e310d5214a0307581995227b36" integrity sha512-tXQR1NEOyGlfylyEjg1ImtScwMq8Oh3iJbGTjN7p0J23EuVX1MA8rwU69K4sLbCmwzgCUbVkm0FkSF9TdzOhtg== -jest-resolve-dependencies@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.5.0.tgz#1a0dae9cdd41349ca4a84148b3e78da2ba33fd4b" - integrity sha512-dRVM1D+gWrFfrq2vlL5P9P/i8kB4BOYqYf3S7xczZ+A6PC3SgXYSErX/ScW/469pWMboM1uAhgLF+39nXlirCQ== +jest-resolve-dependencies@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.7.1.tgz#cf93bbef26999488a96a2b2012f9fe7375aa378f" + integrity sha512-2Eyh5LJB2liNzfk4eo7bD1ZyBbqEJIyyrFtZG555cSWW9xVHxII2NuOkSl1yUYTAYCAmM2f2aIT5A7HzNmubyg== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" jest-regex-util "^24.3.0" - jest-snapshot "^24.5.0" + jest-snapshot "^24.7.1" -jest-resolve@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.5.0.tgz#8c16ba08f60a1616c3b1cd7afb24574f50a24d04" - integrity sha512-ZIfGqLX1Rg8xJpQqNjdoO8MuxHV1q/i2OO1hLXjgCWFWs5bsedS8UrOdgjUqqNae6DXA+pCyRmdcB7lQEEbXew== +jest-resolve@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.7.1.tgz#e4150198299298380a75a9fd55043fa3b9b17fde" + integrity sha512-Bgrc+/UUZpGJ4323sQyj85hV9d+ANyPNu6XfRDUcyFNX1QrZpSoM0kE4Mb2vZMAYTJZsBFzYe8X1UaOkOELSbw== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" browser-resolve "^1.11.3" chalk "^2.0.1" jest-pnp-resolver "^1.2.1" realpath-native "^1.1.0" -jest-runner@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.5.0.tgz#9be26ece4fd4ab3dfb528b887523144b7c5ffca8" - integrity sha512-oqsiS9TkIZV5dVkD+GmbNfWBRPIvxqmlTQ+AQUJUQ07n+4xTSDc40r+aKBynHw9/tLzafC00DIbJjB2cOZdvMA== +jest-runner@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.7.1.tgz#41c8a02a06aa23ea82d8bffd69d7fa98d32f85bf" + integrity sha512-aNFc9liWU/xt+G9pobdKZ4qTeG/wnJrJna3VqunziDNsWT3EBpmxXZRBMKCsNMyfy+A/XHiV+tsMLufdsNdgCw== dependencies: - "@jest/console" "^24.3.0" - "@jest/environment" "^24.5.0" - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/console" "^24.7.1" + "@jest/environment" "^24.7.1" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" chalk "^2.4.2" exit "^0.1.2" graceful-fs "^4.1.15" - jest-config "^24.5.0" + jest-config "^24.7.1" jest-docblock "^24.3.0" - jest-haste-map "^24.5.0" - jest-jasmine2 "^24.5.0" - jest-leak-detector "^24.5.0" - jest-message-util "^24.5.0" - jest-resolve "^24.5.0" - jest-runtime "^24.5.0" - jest-util "^24.5.0" - jest-worker "^24.4.0" + jest-haste-map "^24.7.1" + jest-jasmine2 "^24.7.1" + jest-leak-detector "^24.7.0" + jest-message-util "^24.7.1" + jest-resolve "^24.7.1" + jest-runtime "^24.7.1" + jest-util "^24.7.1" + jest-worker "^24.6.0" source-map-support "^0.5.6" throat "^4.0.0" -jest-runtime@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.5.0.tgz#3a76e0bfef4db3896d5116e9e518be47ba771aa2" - integrity sha512-GTFHzfLdwpaeoDPilNpBrorlPoNZuZrwKKzKJs09vWwHo+9TOsIIuszK8cWOuKC7ss07aN1922Ge8fsGdsqCuw== +jest-runtime@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.7.1.tgz#2ffd70b22dd03a5988c0ab9465c85cdf5d25c597" + integrity sha512-0VAbyBy7tll3R+82IPJpf6QZkokzXPIS71aDeqh+WzPRXRCNz6StQ45otFariPdJ4FmXpDiArdhZrzNAC3sj6A== dependencies: - "@jest/console" "^24.3.0" - "@jest/environment" "^24.5.0" + "@jest/console" "^24.7.1" + "@jest/environment" "^24.7.1" "@jest/source-map" "^24.3.0" - "@jest/transform" "^24.5.0" - "@jest/types" "^24.5.0" + "@jest/transform" "^24.7.1" + "@jest/types" "^24.7.0" "@types/yargs" "^12.0.2" chalk "^2.0.1" exit "^0.1.2" glob "^7.1.3" graceful-fs "^4.1.15" - jest-config "^24.5.0" - jest-haste-map "^24.5.0" - jest-message-util "^24.5.0" - jest-mock "^24.5.0" + jest-config "^24.7.1" + jest-haste-map "^24.7.1" + jest-message-util "^24.7.1" + jest-mock "^24.7.0" jest-regex-util "^24.3.0" - jest-resolve "^24.5.0" - jest-snapshot "^24.5.0" - jest-util "^24.5.0" - jest-validate "^24.5.0" + jest-resolve "^24.7.1" + jest-snapshot "^24.7.1" + jest-util "^24.7.1" + jest-validate "^24.7.0" realpath-native "^1.1.0" slash "^2.0.0" strip-bom "^3.0.0" @@ -2557,35 +4308,34 @@ jest-serializer@^24.4.0: resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.4.0.tgz#f70c5918c8ea9235ccb1276d232e459080588db3" integrity sha512-k//0DtglVstc1fv+GY/VHDIjrtNjdYvYjMlbLUed4kxrE92sIUewOi5Hj3vrpB8CXfkJntRPDRjCrCvUhBdL8Q== -jest-snapshot@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.5.0.tgz#e5d224468a759fd19e36f01217aac912f500f779" - integrity sha512-eBEeJb5ROk0NcpodmSKnCVgMOo+Qsu5z9EDl3tGffwPzK1yV37mjGWF2YeIz1NkntgTzP+fUL4s09a0+0dpVWA== +jest-snapshot@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.7.1.tgz#bd5a35f74aedff070975e9e9c90024f082099568" + integrity sha512-8Xk5O4p+JsZZn4RCNUS3pxA+ORKpEKepE+a5ejIKrId9CwrVN0NY+vkqEkXqlstA5NMBkNahXkR/4qEBy0t5yA== dependencies: "@babel/types" "^7.0.0" - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" chalk "^2.0.1" - expect "^24.5.0" - jest-diff "^24.5.0" - jest-matcher-utils "^24.5.0" - jest-message-util "^24.5.0" - jest-resolve "^24.5.0" + expect "^24.7.1" + jest-diff "^24.7.0" + jest-matcher-utils "^24.7.0" + jest-message-util "^24.7.1" + jest-resolve "^24.7.1" mkdirp "^0.5.1" natural-compare "^1.4.0" - pretty-format "^24.5.0" + pretty-format "^24.7.0" semver "^5.5.0" -jest-util@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.5.0.tgz#9d9cb06d9dcccc8e7cc76df91b1635025d7baa84" - integrity sha512-Xy8JsD0jvBz85K7VsTIQDuY44s+hYJyppAhcsHsOsGisVtdhar6fajf2UOf2mEVEgh15ZSdA0zkCuheN8cbr1Q== +jest-util@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.7.1.tgz#b4043df57b32a23be27c75a2763d8faf242038ff" + integrity sha512-/KilOue2n2rZ5AnEBYoxOXkeTu6vi7cjgQ8MXEkih0oeAXT6JkS3fr7/j8+engCjciOU1Nq5loMSKe0A1oeX0A== dependencies: - "@jest/console" "^24.3.0" - "@jest/fake-timers" "^24.5.0" + "@jest/console" "^24.7.1" + "@jest/fake-timers" "^24.7.1" "@jest/source-map" "^24.3.0" - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" - "@types/node" "*" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" callsites "^3.0.0" chalk "^2.0.1" graceful-fs "^4.1.15" @@ -2594,48 +4344,46 @@ jest-util@^24.5.0: slash "^2.0.0" source-map "^0.6.0" -jest-validate@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.5.0.tgz#62fd93d81214c070bb2d7a55f329a79d8057c7de" - integrity sha512-gg0dYszxjgK2o11unSIJhkOFZqNRQbWOAB2/LOUdsd2LfD9oXiMeuee8XsT0iRy5EvSccBgB4h/9HRbIo3MHgQ== +jest-validate@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.7.0.tgz#70007076f338528ee1b1c8a8258b1b0bb982508d" + integrity sha512-cgai/gts9B2chz1rqVdmLhzYxQbgQurh1PEQSvSgPZ8KGa1AqXsqC45W5wKEwzxKrWqypuQrQxnF4+G9VejJJA== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" camelcase "^5.0.0" chalk "^2.0.1" jest-get-type "^24.3.0" leven "^2.1.0" - pretty-format "^24.5.0" + pretty-format "^24.7.0" -jest-watcher@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.5.0.tgz#da7bd9cb5967e274889b42078c8f501ae1c47761" - integrity sha512-/hCpgR6bg0nKvD3nv4KasdTxuhwfViVMHUATJlnGCD0r1QrmIssimPbmc5KfAQblAVxkD8xrzuij9vfPUk1/rA== +jest-watcher@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.7.1.tgz#e161363d7f3f4e1ef3d389b7b3a0aad247b673f5" + integrity sha512-Wd6TepHLRHVKLNPacEsBwlp9raeBIO+01xrN24Dek4ggTS8HHnOzYSFnvp+6MtkkJ3KfMzy220KTi95e2rRkrw== dependencies: - "@jest/test-result" "^24.5.0" - "@jest/types" "^24.5.0" - "@types/node" "*" + "@jest/test-result" "^24.7.1" + "@jest/types" "^24.7.0" "@types/yargs" "^12.0.9" ansi-escapes "^3.0.0" chalk "^2.0.1" - jest-util "^24.5.0" + jest-util "^24.7.1" string-length "^2.0.0" -jest-worker@^24.4.0: - version "24.4.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.4.0.tgz#fbc452b0120bb5c2a70cdc88fa132b48eeb11dd0" - integrity sha512-BH9X/klG9vxwoO99ZBUbZFfV8qO0XNZ5SIiCyYK2zOuJBl6YJVAeNIQjcoOVNu4HGEHeYEKsUWws8kSlSbZ9YQ== +jest-worker@^24.6.0: + version "24.6.0" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.6.0.tgz#7f81ceae34b7cde0c9827a6980c35b7cdc0161b3" + integrity sha512-jDwgW5W9qGNvpI1tNnvajh0a5IE/PuGLFmHk6aR/BZFz8tSgGw17GsDPXAJ6p91IvYDjOw8GpFbvvZGAK+DPQQ== dependencies: - "@types/node" "*" merge-stream "^1.0.1" supports-color "^6.1.0" -jest@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-24.5.0.tgz#38f11ae2c2baa2f86c2bc4d8a91d2b51612cd19a" - integrity sha512-lxL+Fq5/RH7inxxmfS2aZLCf8MsS+YCUBfeiNO6BWz/MmjhDGaIEA/2bzEf9q4Q0X+mtFHiinHFvQ0u+RvW/qQ== +jest@^24.7.1: + version "24.7.1" + resolved "https://registry.yarnpkg.com/jest/-/jest-24.7.1.tgz#0d94331cf510c75893ee32f87d7321d5bf8f2501" + integrity sha512-AbvRar5r++izmqo5gdbAjTeA6uNRGoNRuj5vHB0OnDXo2DXWZJVuaObiGgtlvhKb+cWy2oYbQSfxv7Q7GjnAtA== dependencies: import-local "^2.0.0" - jest-cli "^24.5.0" + jest-cli "^24.7.1" "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: version "4.0.0" @@ -2704,7 +4452,12 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== -json-parse-better-errors@^1.0.1: +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== @@ -2731,6 +4484,11 @@ json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" +json3@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" + integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE= + json5@2.x, json5@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850" @@ -2743,6 +4501,13 @@ json5@^0.5.0: resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= +json5@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" + integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== + dependencies: + minimist "^1.2.0" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -2750,6 +4515,11 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -2759,6 +4529,53 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" +jss-camel-case@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-6.1.0.tgz#ccb1ff8d6c701c02a1fed6fb6fb6b7896e11ce44" + integrity sha512-HPF2Q7wmNW1t79mCqSeU2vdd/vFFGpkazwvfHMOhPlMgXrJDzdj9viA2SaHk9ZbD5pfL63a8ylp4++irYbbzMQ== + dependencies: + hyphenate-style-name "^1.0.2" + +jss-compose@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/jss-compose/-/jss-compose-5.0.0.tgz#ce01b2e4521d65c37ea42cf49116e5f7ab596484" + integrity sha512-YofRYuiA0+VbeOw0VjgkyO380sA4+TWDrW52nSluD9n+1FWOlDzNbgpZ/Sb3Y46+DcAbOS21W5jo6SAqUEiuwA== + dependencies: + warning "^3.0.0" + +jss-default-unit@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-8.0.2.tgz#cc1e889bae4c0b9419327b314ab1c8e2826890e6" + integrity sha512-WxNHrF/18CdoAGw2H0FqOEvJdREXVXLazn7PQYU7V6/BWkCV0GkmWsppNiExdw8dP4TU1ma1dT9zBNJ95feLmg== + +jss-global@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-3.0.0.tgz#e19e5c91ab2b96353c227e30aa2cbd938cdaafa2" + integrity sha512-wxYn7vL+TImyQYGAfdplg7yaxnPQ9RaXY/cIA8hawaVnmmWxDHzBK32u1y+RAvWboa3lW83ya3nVZ/C+jyjZ5Q== + +jss-isolate@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/jss-isolate/-/jss-isolate-5.1.0.tgz#8eff1294c3659f86535852f4aeb79370743d890e" + integrity sha512-8OVa/SObXRMaKvFeCqzpDOZY8So4fAcTH0K6LsITiYpEQNABICSx1NCmubnt/JbPQaqnnQsF5F47uG86uQ9ZRA== + dependencies: + css-initials "^0.2.0" + +jss-nested@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-6.0.1.tgz#ef992b79d6e8f63d939c4397b9d99b5cbbe824ca" + integrity sha512-rn964TralHOZxoyEgeq3hXY8hyuCElnvQoVrQwKHVmu55VRDd6IqExAx9be5HgK0yN/+hQdgAXQl/GUrBbbSTA== + dependencies: + warning "^3.0.0" + +jss@^9.8.7: + version "9.8.7" + resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.7.tgz#ed9763fc0f2f0260fc8260dac657af61e622ce05" + integrity sha512-awj3XRZYxbrmmrx9LUSj5pXSUfm12m8xzi/VKeqI1ZwWBtQ0kVPTs3vYs32t4rFw83CgFDukA8wKzOE9sMQnoQ== + dependencies: + is-in-browser "^1.1.3" + symbol-observable "^1.1.0" + warning "^3.0.0" + jsx-ast-utils@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" @@ -2766,6 +4583,11 @@ jsx-ast-utils@^2.0.1: dependencies: array-includes "^3.0.3" +killable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" + integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== + kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -2817,6 +4639,11 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +listify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/listify/-/listify-1.0.0.tgz#03ca7ba2d150d4267773f74e57558d1053d2bee3" + integrity sha1-A8p7otFQ1CZ3c/dOV1WNEFPSvuM= + load-json-file@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" @@ -2827,7 +4654,12 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -loader-utils@^1.0.2: +loader-runner@^2.3.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" + integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== + +loader-utils@1.1.0, loader-utils@^1.0.2: version "1.1.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" dependencies: @@ -2835,6 +4667,23 @@ loader-utils@^1.0.2: emojis-list "^2.0.0" json5 "^0.5.0" +loader-utils@^1.1.0, loader-utils@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" + integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== + dependencies: + big.js "^5.2.2" + emojis-list "^2.0.0" + json5 "^1.0.1" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -2864,7 +4713,7 @@ lodash.unescape@4.0.1: resolved "https://registry.yarnpkg.com/lodash.unescape/-/lodash.unescape-4.0.1.tgz#bf2249886ce514cda112fae9218cdc065211fc9c" integrity sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw= -lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4: +lodash@^4.13.1, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5: version "4.17.11" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d" @@ -2872,12 +4721,34 @@ log-driver@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.7.tgz#63b95021f0702fedfa2c9bb0a24e7797d71871d8" +log-symbols@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + integrity sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg== + dependencies: + chalk "^2.0.1" + +loglevel@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" + integrity sha1-4PyVEztu8nbNyIh82vJKpvFW+Po= + +longest-streak@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e" + integrity sha512-TmYTeEYxiAmSVdpbnQDXGtvYOIRsCMg89CVZzwzc2o7GFL1CjoiRPjH5ec0NFAVlAx3fVof9dX/t6KKRAo2OWA== + loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" dependencies: js-tokens "^3.0.0 || ^4.0.0" +lowercase-keys@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" + integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== + lru-cache@^4.0.1: version "4.1.3" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" @@ -2885,6 +4756,21 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" +lru-cache@^4.1.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + magic-string@^0.25.2: version "0.25.2" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.2.tgz#139c3a729515ec55e96e69e82a11fe890a293ad9" @@ -2892,13 +4778,21 @@ magic-string@^0.25.2: dependencies: sourcemap-codec "^1.4.4" -make-dir@^1.3.0: +make-dir@^1.0.0, make-dir@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" +make-dir@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + make-error@1.x: version "1.3.5" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8" @@ -2909,6 +4803,11 @@ makeerror@1.0.x: dependencies: tmpl "1.0.x" +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== + map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -2926,6 +4825,45 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-escapes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" + integrity sha512-lbRZ2mE3Q9RtLjxZBZ9+IMl68DKIXaVAhwvwn9pmjnPLS0h/6kyBMgNhqi1xFJ/2yv6cSyv0jbiZavZv93JkkA== + +markdown-table@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" + integrity sha512-NcWuJFHDA8V3wkDgR/j4+gZx+YQwstPgfQDV8ndUeWWzta3dnDTBxpVzqS9lkmJAuV5YX35lmyojl6HO5JXAgw== + +markdown-to-jsx@^6.9.3: + version "6.9.3" + resolved "https://registry.yarnpkg.com/markdown-to-jsx/-/markdown-to-jsx-6.9.3.tgz#31719e3c54517ba9805db81d53701b89f5d2ed7e" + integrity sha512-iXteiv317VZd1vk/PBH5MWMD4r0XWekoWCHRVVadBcnCtxavhtfV1UaEaQgq9KyckTv31L60ASh5ZVVrOh37Qg== + dependencies: + prop-types "^15.6.2" + unquote "^1.1.0" + +md5.js@^1.3.4: + version "1.3.5" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" + integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +mdast-util-compact@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz#c12ebe16fffc84573d3e19767726de226e95f649" + integrity sha512-d2WS98JSDVbpSsBfVvD9TaDMlqPRz7ohM/11G0rp5jOBb5q96RJ6YLszQ/09AAixyzh23FeIpCGqfaamEADtWg== + dependencies: + unist-util-visit "^1.1.0" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + mem@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/mem/-/mem-4.1.0.tgz#aeb9be2d21f47e78af29e4ac5978e8afa2ca5b8a" @@ -2935,20 +4873,35 @@ mem@^4.0.0: mimic-fn "^1.0.0" p-is-promise "^2.0.0" -memory-fs@^0.4.0: +memory-fs@^0.4.0, memory-fs@^0.4.1, memory-fs@~0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" dependencies: errno "^0.1.3" readable-stream "^2.0.1" +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + merge-stream@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" dependencies: readable-stream "^2.0.1" -micromatch@^3.1.10, micromatch@^3.1.4: +merge2@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.3.tgz#7ee99dbd69bb6481689253f018488a1b902b0ed5" + integrity sha512-gdUU1Fwj5ep4kplwcmftruWofEFt6lfpkkr3h860CXbAB9c3hGb55EOL2ali0Td5oebvW0E1+3Sr+Ur7XfKpRA== + +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -2967,16 +4920,51 @@ micromatch@^3.1.10, micromatch@^3.1.4: snapdragon "^0.8.1" to-regex "^3.0.2" +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +"mime-db@>= 1.38.0 < 2": + version "1.39.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.39.0.tgz#f95a20275742f7d2ad0429acfe40f4233543780e" + integrity sha512-DTsrw/iWVvwHH+9Otxccdyy0Tgiil6TWK/xhfARJZF/QFhwOgZgOIvA2/VIGpM8U7Q8z5nDmdDWC6tuVMJNibw== + mime-db@~1.36.0: version "1.36.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.36.0.tgz#5020478db3c7fe93aad7bbcc4dcf869c43363397" +mime-db@~1.38.0: + version "1.38.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.38.0.tgz#1a2aab16da9eb167b49c6e4df2d9c68d63d8e2ad" + integrity sha512-bqVioMFFzc2awcdJZIzR3HjZFX20QhilVS7hytkKrv7xFAn8bM1gzc/FOX2awLISvWe0PV8ptFKcon+wZ5qYkg== + mime-types@^2.1.12, mime-types@~2.1.19: version "2.1.20" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.20.tgz#930cb719d571e903738520f8470911548ca2cc19" dependencies: mime-db "~1.36.0" +mime-types@~2.1.17, mime-types@~2.1.18: + version "2.1.22" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.22.tgz#fe6b355a190926ab7698c9a0556a11199b2199bd" + integrity sha512-aGl6TZGnhm/li6F7yx82bJiBZwgiEa4Hf6CNr8YO+r5UHr53tSTYZb102zyU50DOWWKeOv0uQLRL0/9EiKWCog== + dependencies: + mime-db "~1.38.0" + +mime@1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" + integrity sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ== + +mime@^2.3.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.2.tgz#ce5229a5e99ffc313abac806b482c10e7ba6ac78" + integrity sha512-zJBfZDkwRu+j3Pdd2aHsR5GfH2jIWhmL1ZzBoc+X+3JEti2hbArWcyJ+1laC1D2/U/W1a/+Cegj0/OnEU2ybjg== + mimic-fn@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" @@ -2986,7 +4974,24 @@ mimic-response@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== -minimatch@^3.0.3, minimatch@^3.0.4: +mini-html-webpack-plugin@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/mini-html-webpack-plugin/-/mini-html-webpack-plugin-0.2.3.tgz#2dfbdc3f35f6ae03864a608808381f8137311ea0" + integrity sha512-wfkLf+CmyDg++K1S0QdAvUvS29DfVHe9SQ63syX8aX375mInzC5uwHxb/1+3exiiv84xnPrf6zsOnReRe15rjg== + dependencies: + webpack-sources "^1.1.0" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= + +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" dependencies: @@ -3004,6 +5009,53 @@ minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" +minipass@^2.2.1, minipass@^2.3.4: + version "2.3.5" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.5.tgz#cacebe492022497f656b0f0f51e2682a9ed2d848" + integrity sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA== + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.2.1.tgz#dd27ea6136243c7c880684e8672bb3a45fd9b614" + integrity sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA== + dependencies: + minipass "^2.2.1" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + integrity sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mississippi@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" + integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^3.0.0" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + mixin-deep@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" @@ -3011,7 +5063,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mkdirp@0.x, mkdirp@^0.5.1: +mkdirp@0.5.x, mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" dependencies: @@ -3021,6 +5073,23 @@ moo@^0.4.3: version "0.4.3" resolved "https://registry.yarnpkg.com/moo/-/moo-0.4.3.tgz#3f847a26f31cf625a956a87f2b10fbc013bfd10e" +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +mri@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/mri/-/mri-1.1.4.tgz#7cb1dd1b9b40905f1fac053abe25b6720f44744a" + integrity sha512-6y7IjGPm8AzlvoUrwAaw1tLnUBudaS3752vcd8JtrpGGQn+rXIe63LFVHm/YMwtqAuh+LJPCFdlLYPWM1nYn6w== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3029,11 +5098,29 @@ ms@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" +multicast-dns-service-types@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" + integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= + +multicast-dns@^6.0.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" + integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== + dependencies: + dns-packet "^1.3.1" + thunky "^1.0.2" + mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= +nan@^2.9.2: + version "2.13.2" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" + integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== + nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" @@ -3069,6 +5156,25 @@ nearley@^2.7.10: randexp "0.4.6" semver "^5.4.1" +needle@^2.2.1: + version "2.3.0" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.0.tgz#ce3fea21197267bacb310705a7bbe24f2a3a3492" + integrity sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg== + dependencies: + debug "^4.1.0" + iconv-lite "^0.4.4" + sax "^1.2.4" + +negotiator@0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" + integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= + +neo-async@^2.5.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.0.tgz#b9d15e4d71c6762908654b5183ed38b753340835" + integrity sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA== + nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -3081,10 +5187,51 @@ node-abi@^2.2.0: dependencies: semver "^5.4.1" +node-dir@^0.1.10: + version "0.1.17" + resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" + integrity sha1-X1Zl2TNRM1yqvvjxxVRRbPXx5OU= + dependencies: + minimatch "^3.0.2" + +node-forge@0.7.5: + version "0.7.5" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df" + integrity sha512-MmbQJ2MTESTjt3Gi/3yG1wGpIMhUfcIypUCGtTizFR9IiccFwxSpfp0vtIZlkFclEqERemxfnSdZEMR9VqqEFQ== + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" +node-libs-browser@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.0.tgz#c72f60d9d46de08a940dedbb25f3ffa2f9bbaa77" + integrity sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA== + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^3.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.11.0" + vm-browserify "0.0.4" + node-modules-regexp@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40" @@ -3099,6 +5246,29 @@ node-notifier@^5.2.1: shellwords "^0.1.1" which "^1.3.0" +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + integrity sha512-d1xFs+C/IPS8Id0qPTZ4bUT8wWryfR/OzzAFxweG+uLN85oPzyo2Iw6bVlLQ/JOdgNonXLCoRyqDzDWq4iw72A== + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.0.0-alpha.11: + version "1.1.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.14.tgz#f1f41c83cac82caebd6739e6313d56b3b09c9189" + integrity sha512-d58EpVZRhQE60kWiWUaaPlK9dyC4zg3ZoMcHcky2d4hDksyQj0rUozwInOl0C66mBsqo01Tuns8AvxnL5S7PKg== + dependencies: + semver "^5.3.0" + nomnom@~1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/nomnom/-/nomnom-1.6.2.tgz#84a66a260174408fc5b77a18f888eccc44fb6971" @@ -3111,6 +5281,14 @@ noop-logger@^0.1.1: resolved "https://registry.yarnpkg.com/noop-logger/-/noop-logger-0.1.1.tgz#94a2b1633c4f1317553007d8966fd0e841b6a4c2" integrity sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= + dependencies: + abbrev "1" + osenv "^0.1.4" + normalize-package-data@^2.3.2: version "2.4.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" @@ -3126,13 +5304,31 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" +normalize-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +npm-bundled@^1.0.1: + version "1.0.6" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.6.tgz#e7ba9aadcef962bb61248f91721cd932b3fe6bdd" + integrity sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g== + +npm-packlist@^1.1.6: + version "1.4.1" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.1.tgz#19064cdf988da80ea3cee45533879d90192bbfbc" + integrity sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw== + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" dependencies: path-key "^2.0.0" -npmlog@^4.0.1, npmlog@^4.1.2: +npmlog@^4.0.1, npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: @@ -3159,7 +5355,7 @@ oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" -object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -3207,6 +5403,16 @@ object.entries@^1.0.4: function-bind "^1.1.0" has "^1.0.1" +object.entries@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.0.tgz#2024fc6d6ba246aee38bdb0ffd5cfbcf371b7519" + integrity sha512-l+H6EQ8qzGRxbkHOd5I/aHRhHDKoQXQ8g0BYt4uSweQU1/J6dZUOyWh9a2Vky35YCKjzmgxOzta2hH6kf9HuXA== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.12.0" + function-bind "^1.1.1" + has "^1.0.3" + object.fromentries@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.0.tgz#49a543d92151f8277b3ac9600f1e930b189d30ab" @@ -3249,6 +5455,23 @@ object.values@^1.1.0: function-bind "^1.1.1" has "^1.0.3" +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + dependencies: + ee-first "1.1.1" + +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3262,6 +5485,20 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +opn@5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.4.0.tgz#cb545e7aab78562beb11aa3bfabc7042e1761035" + integrity sha512-YF9MNdVy/0qvJvDtunAOzFw9iasOQHpVthTCvGzxt61Il64AYSGdK+rYwld7NAfk9qJ7dt+hymBNSc9LNYS+Sw== + dependencies: + is-wsl "^1.1.0" + +opn@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" + integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== + dependencies: + is-wsl "^1.1.0" + optimist@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" @@ -3280,7 +5517,31 @@ optionator@^0.8.1, optionator@^0.8.2: type-check "~0.3.2" wordwrap "~1.0.0" -os-homedir@^1.0.1: +ora@^3.2.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/ora/-/ora-3.4.0.tgz#bf0752491059a3ef3ed4c85097531de9fdbcd318" + integrity sha512-eNwHudNbO1folBP3JsZ19v9azXWtQZjICdr3Q0TDPIaeBQ3mXLrh54wM+er0+hSp+dWKf+Z8KM58CYzEyIYxYg== + dependencies: + chalk "^2.4.2" + cli-cursor "^2.1.0" + cli-spinners "^2.0.0" + log-symbols "^2.2.0" + strip-ansi "^5.2.0" + wcwidth "^1.0.1" + +original@>=0.0.5, original@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" + integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== + dependencies: + url-parse "^1.4.3" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= + +os-homedir@^1.0.0, os-homedir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" @@ -3293,10 +5554,18 @@ os-locale@^3.0.0: lcid "^2.0.0" mem "^4.0.0" -os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + p-defer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c" @@ -3318,6 +5587,13 @@ p-is-promise@^2.0.0: resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.0.0.tgz#7554e3d572109a87e1f3f53f6a7d85d1b194f4c5" integrity sha512-pzQPhYMCAgLAKPWD2jC3Se9fEfrD9npNos0y150EeqZll7akhEgGhTW/slB6lHku8AvYGiJ+YJ5hfHKePPgFWg== +p-limit@^1.0.0, p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== + dependencies: + p-try "^1.0.0" + p-limit@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.1.0.tgz#1d5a0d20fb12707c758a655f6bbc4386b5930d68" @@ -3325,6 +5601,13 @@ p-limit@^2.0.0: dependencies: p-try "^2.0.0" +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= + dependencies: + p-limit "^1.1.0" + p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -3332,16 +5615,40 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" +p-map@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" + integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== + p-reduce@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa" integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo= +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= + p-try@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" integrity sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ== +pako@~1.0.5: + version "1.0.10" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.10.tgz#4328badb5086a426aa90f541977d4955da5c9732" + integrity sha512-0DTvPVU3ed8+HNXOu5Bs+o//Mbdj9VNQMUOe9oKCwh8l0GNwpTDMKCWbRjgtD291AWnkAgkqA/LOnQS8AmS1tw== + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + integrity sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY= + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + parent-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.0.tgz#df250bdc5391f4a085fb589dad761f5ad6b865b5" @@ -3349,6 +5656,30 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-asn1@^5.0.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.4.tgz#37f6628f823fbdeb2273b4d540434a22f3ef1fcc" + integrity sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw== + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + safe-buffer "^5.1.1" + +parse-entities@^1.0.2, parse-entities@^1.1.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.2.1.tgz#2c761ced065ba7dc68148580b5a225e4918cdd69" + integrity sha512-NBWYLQm1KSoDKk7GAHyioLTvCZ5QjdH/ASBBQTD3iLiAWJXS5bg1jEWI8nIJ+vgVvsceBVBcDGRWSo0KVQBvvg== + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + parse-json@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" @@ -3357,6 +5688,11 @@ parse-json@^4.0.0: error-ex "^1.3.1" json-parse-better-errors "^1.0.1" +parse-passwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" + integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -3367,10 +5703,25 @@ parse5@^3.0.1: dependencies: "@types/node" "*" +parseurl@~1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" + integrity sha1-/CidTtiZMRlGDBViUyYs3I3mW/M= + pascalcase@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + integrity sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo= + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= + path-exists@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" @@ -3379,7 +5730,7 @@ path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.2: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= @@ -3392,6 +5743,11 @@ path-parse@^1.0.5, path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -3399,14 +5755,47 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +pbkdf2@^3.0.3: + version "3.0.17" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" + integrity sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA== + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" +pify@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= + pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= + pirates@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87" @@ -3414,6 +5803,13 @@ pirates@^4.0.1: dependencies: node-modules-regexp "^1.0.0" +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s= + dependencies: + find-up "^2.1.0" + pkg-dir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" @@ -3421,10 +5817,26 @@ pkg-dir@^3.0.0: dependencies: find-up "^3.0.0" +pkg-up@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-up/-/pkg-up-2.0.0.tgz#c819ac728059a461cab1c3889a2be3c49a004d7f" + integrity sha1-yBmscoBZpGHKscOImivjxJoATX8= + dependencies: + find-up "^2.1.0" + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" +portfinder@^1.0.20: + version "1.0.20" + resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.20.tgz#bea68632e54b2e13ab7b0c4775e9b41bf270e44a" + integrity sha512-Yxe4mTyDzTd59PZJY4ojZR8F+E5e97iq2ZOHPz3HDgSvYC5siNad2tLooQ5y5QHyQhc3xVqvyk/eNA3wuoa7Sw== + dependencies: + async "^1.5.2" + debug "^2.2.0" + mkdirp "0.5.x" + posix-character-classes@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" @@ -3455,25 +5867,47 @@ prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" -pretty-format@^24.5.0: - version "24.5.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.5.0.tgz#cc69a0281a62cd7242633fc135d6930cd889822d" - integrity sha512-/3RuSghukCf8Riu5Ncve0iI+BzVkbRU5EeUoArKARZobREycuH5O4waxvaNIloEXdb0qwgmEAed5vTpX1HNROQ== +pretty-format@^24.7.0: + version "24.7.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.7.0.tgz#d23106bc2edcd776079c2daa5da02bcb12ed0c10" + integrity sha512-apen5cjf/U4dj7tHetpC7UEFCvtAgnNZnBDkfPv3fokzIqyOJckAG9OlAPC1BlFALnqT/lGB2tl9EJjlK6eCsA== dependencies: - "@jest/types" "^24.5.0" + "@jest/types" "^24.7.0" ansi-regex "^4.0.0" ansi-styles "^3.2.0" react-is "^16.8.4" +prismjs@^1.16.0: + version "1.16.0" + resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.16.0.tgz#406eb2c8aacb0f5f0f1167930cb83835d10a4308" + integrity sha512-OA4MKxjFZHSvZcisLGe14THYsug/nF6O1f0pAJc0KN0wTyAcLqmsbE+lTGKSpyh+9pEW57+k6pg2AfYR+coyHA== + optionalDependencies: + clipboard "^2.0.0" + +private@^0.1.8, private@~0.1.5: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== + process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= + progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= + prompts@^2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.3.tgz#c5ccb324010b2e8f74752aadceeb57134c1d2522" @@ -3482,6 +5916,15 @@ prompts@^2.0.1: kleur "^3.0.2" sisteransi "^1.0.0" +prop-types-exact@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" + integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== + dependencies: + has "^1.0.3" + object.assign "^4.1.0" + reflect.ownkeys "^0.2.0" + prop-types@^15.6.2: version "15.6.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" @@ -3498,6 +5941,14 @@ prop-types@^15.7.2: object-assign "^4.1.1" react-is "^16.8.1" +proxy-addr@~2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.4.tgz#ecfc733bf22ff8c6f407fa275327b9ab67e48b93" + integrity sha512-5erio2h9jp5CHGwcybmxmVqHmnCBZeewlfJ0pex+UW7Qny7OOZXTtH56TGNyBizkgiOwhJtMKrVzDTeKcySZwA== + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.8.0" + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -3510,6 +5961,18 @@ psl@^1.1.24: version "1.1.29" resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.29.tgz#60f580d360170bb722a797cc704411e6da850c67" +public-encrypt@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" + integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + safe-buffer "^5.1.2" + pump@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" @@ -3518,7 +5981,7 @@ pump@^1.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pump@^2.0.1: +pump@^2.0.0, pump@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" dependencies: @@ -3533,7 +5996,21 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -punycode@^1.4.1: +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= + +punycode@^1.2.4, punycode@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" @@ -3541,10 +6018,39 @@ punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" -qs@~6.5.2: +q-i@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/q-i/-/q-i-2.0.1.tgz#fec7e3f0e713f3467358bb5ac80bcc4c115187d6" + integrity sha512-tr7CzPNxkBDBuPzqi/HDUS4uBOppb91akNTeh56TYio8TiIeXp2Yp8ea9NmDu2DmGH35ZjJDq6C3E4SepVZ4bQ== + dependencies: + ansi-styles "^3.2.0" + is-plain-object "^2.0.4" + stringify-object "^3.2.0" + +qs@6.5.2, qs@~6.5.2: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" +qss@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/qss/-/qss-2.0.3.tgz#630b38b120931b52d04704f3abfb0f861604a9ec" + integrity sha512-j48ZBT5IZbSqJiSU8EX4XrN8nXiflHvmMvv2XpFc31gh7n6EpSs75bNr6+oj3FOLWyT8m09pTmqLNl34L7/uPQ== + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= + +querystringify@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e" + integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA== + raf@^3.4.0: version "3.4.0" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" @@ -3562,6 +6068,36 @@ randexp@0.4.6: discontinuous-range "1.0.0" ret "~0.1.10" +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +range-parser@^1.0.3, range-parser@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" + integrity sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4= + +raw-body@2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.3.tgz#1b324ece6b5706e153855bc1148c65bb7f6ea0c3" + integrity sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw== + dependencies: + bytes "3.0.0" + http-errors "1.6.3" + iconv-lite "0.4.23" + unpipe "1.0.0" + rc@^1.2.7: version "1.2.8" resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" @@ -3571,25 +6107,198 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f" - integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA== +react-dev-utils@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-6.1.1.tgz#a07e3e8923c4609d9f27e5af5207e3ca20724895" + integrity sha512-ThbJ86coVd6wV/QiTo8klDTvdAJ1WsFCGQN07+UkN+QN9CtCSsl/+YuDJToKGeG8X4j9HMGXNKbk2QhPAZr43w== + dependencies: + "@babel/code-frame" "7.0.0" + address "1.0.3" + browserslist "4.1.1" + chalk "2.4.1" + cross-spawn "6.0.5" + detect-port-alt "1.1.6" + escape-string-regexp "1.0.5" + filesize "3.6.1" + find-up "3.0.0" + global-modules "1.0.0" + globby "8.0.1" + gzip-size "5.0.0" + immer "1.7.2" + inquirer "6.2.0" + is-root "2.0.0" + loader-utils "1.1.0" + opn "5.4.0" + pkg-up "2.0.0" + react-error-overlay "^5.1.0" + recursive-readdir "2.2.2" + shell-quote "1.6.1" + sockjs-client "1.1.5" + strip-ansi "4.0.0" + text-table "0.2.0" + +react-docgen-annotation-resolver@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-docgen-annotation-resolver/-/react-docgen-annotation-resolver-1.1.0.tgz#0a0f8d5d4d7ba924866d479ec7e709ed6cf33807" + integrity sha512-wTUI7IqWkV+BNRmEh1eHkU+Ijwh0XcFUdbgktynWVqe++MgtovdlbfMehFAw5b49mv8NN2DK0NF/G8x+UdIyNw== + +react-docgen-displayname-handler@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/react-docgen-displayname-handler/-/react-docgen-displayname-handler-2.1.1.tgz#a7863a2cc0058ac3c72e6348fa5e40cc4278c5ae" + integrity sha512-Dmu+WnQt5TRDokaQ6uGvgcxrIh92c6uhuljsuueEphTcy/1nKh8/Ep6RUmpd77G1iN2rqg5+4ztGAT7LJjVdpg== + dependencies: + ast-types "0.11.5" + +react-docgen-typescript@^1.12.4: + version "1.12.4" + resolved "https://registry.yarnpkg.com/react-docgen-typescript/-/react-docgen-typescript-1.12.4.tgz#9ec8080e9ef41067ff17153bf42b3b2d49ae5503" + integrity sha512-OSmUfmdtcz4kLRWPiR8uUdE8ta+s5DV0uXOz1YsWaAUf3Ty64use7DYWK97zH8ZOlD4slq5zUfGc+UbfGLqfEQ== + +react-docgen@3.0.0-rc.2: + version "3.0.0-rc.2" + resolved "https://registry.yarnpkg.com/react-docgen/-/react-docgen-3.0.0-rc.2.tgz#5939c64699fd9959da6d97d890f7b648e542dbcc" + integrity sha512-tXbIvq7Hxdc92jW570rztqsz0adtWEM5FX8bShJYozT2Y6L/LeHvBMQcED6mSqJ72niiNMPV8fi3S37OHrGMEw== + dependencies: + "@babel/parser" "^7.1.3" + "@babel/runtime" "^7.0.0" + async "^2.1.4" + commander "^2.19.0" + doctrine "^2.0.0" + node-dir "^0.1.10" + recast "^0.16.0" + +react-dom@^16.9.0-alpha.0: + version "16.9.0-alpha.0" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.9.0-alpha.0.tgz#9dfaec18ac1a500fa72cab7b70f2ae29d0cd7716" + integrity sha512-BQ5gN42yIPuTnBvE6K9vSjNfDRpSNcYCs2sUx9XR5VaWKwlHTt3G6qIWK6zdXy8TYKb1+IxpsAI0RtbRdXQZ2A== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" + scheduler "^0.14.0-alpha.0" + +react-error-overlay@^5.1.0: + version "5.1.4" + resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-5.1.4.tgz#88dfb88857c18ceb3b9f95076f850d7121776991" + integrity sha512-fp+U98OMZcnduQ+NSEiQa4s/XMsbp+5KlydmkbESOw4P69iWZ68ZMFM5a2BuE0FgqPBKApJyRuYHR95jM8lAmg== + +react-group@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/react-group/-/react-group-3.0.0.tgz#cc02d67b1b08fe125a664c527aa69f6b9ecab66b" + integrity sha512-BKfGTyszMUA754VqRr6kG5Nzmht07Um0szV7iZkQMSzZ3QNzjSHGMwQ7BpRp3kmVqSg/YjNq/18J5y4OrNAstQ== + dependencies: + prop-types "^15.6.2" + +react-hooks-testing-library@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/react-hooks-testing-library/-/react-hooks-testing-library-0.5.0.tgz#571af3522f88ea4ac23c634fb4deff84873f2bc2" + integrity sha512-qX4SA28pcCCf1Q23Gtl1VKqQk26pSPTEsdLtfJanDqm4oacT5wadL+e2Xypk/H+AOXN5kdZrWmXkt+hAaiNHgg== + dependencies: + "@babel/runtime" "^7.4.2" + +react-icon-base@2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/react-icon-base/-/react-icon-base-2.1.0.tgz#a196e33fdf1e7aaa1fda3aefbb68bdad9e82a79d" + integrity sha1-oZbjP98eeqof2jrvu2i9rZ6Cp50= + +react-icons@^2.2.7: + version "2.2.7" + resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-2.2.7.tgz#d7860826b258557510dac10680abea5ca23cf650" + integrity sha512-0n4lcGqzJFcIQLoQytLdJCE0DKSA9dkwEZRYoGrIDJZFvIT6Hbajx5mv9geqhqFiNjUgtxg8kPyDfjlhymbGFg== + dependencies: + react-icon-base "2.1.0" react-is@^16.5.2: version "16.5.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.5.2.tgz#e2a7b7c3f5d48062eb769fcb123505eb928722e3" -react-is@^16.8.1, react-is@^16.8.4: +react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6: version "16.8.6" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16" integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA== +react-is@^16.9.0-alpha.0: + version "16.9.0-alpha.0" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.9.0-alpha.0.tgz#af943642da49460f9fe98630182b0a62db3e9e6a" + integrity sha512-psl0ePLTFliYfwcbwvimLgTNN156ZdeWB4zvP7dV/6lTAqWMHFfidg/mSZ2fFgE1LMNN8ZJOLl2DfZ8yg+3ETA== + +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + +react-simple-code-editor@^0.9.7: + version "0.9.10" + resolved "https://registry.yarnpkg.com/react-simple-code-editor/-/react-simple-code-editor-0.9.10.tgz#1ab5ea7215687ed918c6d0dae90736cf01890905" + integrity sha512-80LJwRQS7Wi9Ugh/e6FRHWdcg4oQOpMBDFxyDpORILffrHdV3EIQ1IeX5x7488r05iFgLbVDV4nQ1LRKjgCm0g== + +react-styleguidist@^9.0.8: + version "9.0.8" + resolved "https://registry.yarnpkg.com/react-styleguidist/-/react-styleguidist-9.0.8.tgz#615057eac7a2cc88d13087f2d8c37037fdbce9cb" + integrity sha512-N3JEspClgUCNkj05X14G2aGLyDE9Izv1X8BFPjQR+oVo2AoUGLE92vsA3XYD26kmAChwfBUEja7b5IZV+91DTQ== + dependencies: + "@vxna/mini-html-webpack-template" "^0.1.7" + acorn "^6.1.1" + acorn-jsx "^5.0.1" + ast-types "^0.12.2" + buble "0.19.7" + clean-webpack-plugin "^1.0.1" + clipboard-copy "^3.0.0" + clsx "^1.0.3" + common-dir "^2.0.2" + copy-webpack-plugin "^4.6.0" + core-js "^3.0.0" + doctrine "^3.0.0" + es6-object-assign "~1.1.0" + es6-promise "^4.2.6" + escodegen "^1.11.1" + findup "^0.1.5" + function.name-polyfill "^1.0.6" + github-slugger "^1.2.1" + glob "^7.1.3" + glogg "^1.0.2" + is-directory "^0.3.1" + javascript-stringify "^1.6.0" + jss "^9.8.7" + jss-camel-case "^6.1.0" + jss-compose "^5.0.0" + jss-default-unit "^8.0.2" + jss-global "^3.0.0" + jss-isolate "^5.1.0" + jss-nested "^6.0.1" + kleur "^3.0.2" + leven "^2.1.0" + listify "^1.0.0" + loader-utils "^1.2.3" + lodash "^4.17.11" + lowercase-keys "^1.0.1" + markdown-to-jsx "^6.9.3" + mini-html-webpack-plugin "^0.2.3" + mri "^1.1.4" + ora "^3.2.0" + prismjs "^1.16.0" + prop-types "^15.7.2" + q-i "^2.0.1" + qss "^2.0.3" + react-dev-utils "^6.1.1" + react-docgen "3.0.0-rc.2" + react-docgen-annotation-resolver "^1.0.0" + react-docgen-displayname-handler "^2.1.1" + react-group "^3.0.0" + react-icons "^2.2.7" + react-lifecycles-compat "^3.0.4" + react-simple-code-editor "^0.9.7" + recast "^0.17.4" + remark "^10.0.1" + rewrite-imports "1.2.0" + terser-webpack-plugin "^1.2.3" + to-ast "^1.0.0" + type-detect "^4.0.8" + unist-util-visit "^1.4.0" + walkes "^0.2.1" + webpack-dev-server "^3.2.1" + webpack-merge "^4.2.1" + react-test-renderer@^16.0.0-0: version "16.5.2" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.5.2.tgz#92e9d2c6f763b9821b2e0b22f994ee675068b5ae" @@ -3599,15 +6308,25 @@ react-test-renderer@^16.0.0-0: react-is "^16.5.2" schedule "^0.5.0" -react@^16.8.6: - version "16.8.6" - resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe" - integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw== +react-test-renderer@^16.9.0-alpha.0: + version "16.9.0-alpha.0" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.9.0-alpha.0.tgz#60f584601dc73543dadbe3bca815e1acc8adcaba" + integrity sha512-eDl0oVFo6PGY1wpYFs0ezBpZhOgVce5TSta9UPLanshTi4z8NhlM6IgO8KBdioQ5H5/pmyGxOVtpUxJOt19NAQ== + dependencies: + object-assign "^4.1.1" + prop-types "^15.6.2" + react-is "^16.9.0-alpha.0" + scheduler "^0.14.0-alpha.0" + +react@^16.9.0-alpha.0: + version "16.9.0-alpha.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.9.0-alpha.0.tgz#e350f3d8af36e3251079cbc90d304620e2f78ccb" + integrity sha512-y4bu7rJvtnPPsIwOj7sp5Y2SqlOb0jFupfkdjWxxn8ZeqzUARgpR9wJBUVwW1/QosVdOblmApjo/j6iiAXnebA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.13.6" + scheduler "^0.14.0-alpha.0" read-pkg-up@^4.0.0: version "4.0.0" @@ -3626,7 +6345,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.3.0, readable-stream@^2.3.5: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.6" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" dependencies: @@ -3638,6 +6357,24 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.3.0.tgz#cb8011aad002eb717bf040291feba8569c986fb9" + integrity sha512-EsI+s3k3XsW+fU8fQACLN59ky34AZ14LoeVZpYwmZvldCFo0r0gnelwF2TcMjLor/BTL5aDJVBMkss0dthToPw== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readdirp@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" + integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== + dependencies: + graceful-fs "^4.1.11" + micromatch "^3.1.10" + readable-stream "^2.0.2" + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -3645,6 +6382,55 @@ realpath-native@^1.1.0: dependencies: util.promisify "^1.0.0" +recast@^0.16.0: + version "0.16.2" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.16.2.tgz#3796ebad5fe49ed85473b479cd6df554ad725dc2" + integrity sha512-O/7qXi51DPjRVdbrpNzoBQH5dnAPQNbfoOFyRiUwreTMJfIHYOEBzwuH+c0+/BTSJ3CQyKs6ILSWXhESH6Op3A== + dependencies: + ast-types "0.11.7" + esprima "~4.0.0" + private "~0.1.5" + source-map "~0.6.1" + +recast@^0.17.4: + version "0.17.5" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.17.5.tgz#cbba5757867b34826dbea3fffcee6225ba2cee3d" + integrity sha512-K+DgfAMIyEjNKjaFSWgg9TTu7wFgU/4KTyw4E9vl6M5QPDuUYbyt49Yzb0EIDbZks+6lXk/UZ9eTuE4jlLyf2A== + dependencies: + ast-types "0.12.3" + esprima "~4.0.0" + private "^0.1.8" + source-map "~0.6.1" + +recursive-readdir@2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f" + integrity sha512-nRCcW9Sj7NuZwa2XvH9co8NPeXUBhZP7CRKJtU+cS6PW9FpCIFoI5ib0NT1ZrbNuPoRy0ylyCaUL8Gih4LSyFg== + dependencies: + minimatch "3.0.4" + +reflect.ownkeys@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" + integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= + +regenerate-unicode-properties@^8.0.2: + version "8.0.2" + resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz#7b38faa296252376d363558cfbda90c9ce709662" + integrity sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ== + dependencies: + regenerate "^1.4.0" + +regenerate@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== + +regenerator-runtime@^0.13.2: + version "0.13.2" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz#32e59c9a6fb9b1a4aff09b4930ca2d4477343447" + integrity sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA== + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -3657,6 +6443,80 @@ regexpp@^2.0.1: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f" integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw== +regexpu-core@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.5.4.tgz#080d9d02289aa87fe1667a4f5136bc98a6aebaae" + integrity sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ== + dependencies: + regenerate "^1.4.0" + regenerate-unicode-properties "^8.0.2" + regjsgen "^0.5.0" + regjsparser "^0.6.0" + unicode-match-property-ecmascript "^1.0.4" + unicode-match-property-value-ecmascript "^1.1.0" + +regjsgen@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.0.tgz#a7634dc08f89209c2049adda3525711fb97265dd" + integrity sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA== + +regjsparser@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.0.tgz#f1e6ae8b7da2bae96c99399b868cd6c933a2ba9c" + integrity sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ== + dependencies: + jsesc "~0.5.0" + +remark-parse@^6.0.0: + version "6.0.3" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-6.0.3.tgz#c99131052809da482108413f87b0ee7f52180a3a" + integrity sha512-QbDXWN4HfKTUC0hHa4teU463KclLAnwpn/FBn87j9cKYJWWawbiLgMfP2Q4XwhxxuuuOxHlw+pSN0OKuJwyVvg== + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-stringify@^6.0.0: + version "6.0.4" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-6.0.4.tgz#16ac229d4d1593249018663c7bddf28aafc4e088" + integrity sha512-eRWGdEPMVudijE/psbIDNcnJLRVx3xhfuEsTDGgH4GsFF91dVhw5nhmnBppafJ7+NWINW6C7ZwWbi30ImJzqWg== + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^1.1.0" + mdast-util-compact "^1.0.0" + parse-entities "^1.0.2" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^1.0.1" + unherit "^1.0.4" + xtend "^4.0.1" + +remark@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/remark/-/remark-10.0.1.tgz#3058076dc41781bf505d8978c291485fe47667df" + integrity sha512-E6lMuoLIy2TyiokHprMjcWNJ5UxfGQjaMSMhV+f4idM625UjjK4j798+gPs5mfjzDE6vL0oFKVeZM6gZVSVrzQ== + dependencies: + remark-parse "^6.0.0" + remark-stringify "^6.0.0" + unified "^7.0.0" + remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -3665,10 +6525,15 @@ repeat-element@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" -repeat-string@^1.6.1: +repeat-string@^1.5.4, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs= + request-promise-core@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.1.tgz#3eee00b2c5aa83239cfb04c5700da36f81cd08b6" @@ -3722,12 +6587,25 @@ requireindex@^1.2.0: resolved "https://registry.yarnpkg.com/requireindex/-/requireindex-1.2.0.tgz#3463cdb22ee151902635aa6c9535d4de9c2ef1ef" integrity sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= + resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" dependencies: resolve-from "^3.0.0" +resolve-dir@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" + integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= + dependencies: + expand-tilde "^2.0.0" + global-modules "^1.0.0" + resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -3770,7 +6648,12 @@ ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" -rimraf@2.6.3, rimraf@^2.6.2: +rewrite-imports@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/rewrite-imports/-/rewrite-imports-1.2.0.tgz#091b05aa0a358e54b6582205b6feeb73977bd8fb" + integrity sha512-oVoZ3QImciK+S7I89vPyxDgohkwhJyWLP5EKcMvF2NOhaFkmdKFiJMJzTM35VeNV1gQfGvv9Cve6sMq5E7unHg== + +rimraf@2.6.3, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -3783,15 +6666,23 @@ rimraf@^2.5.4: dependencies: glob "^7.0.5" -rollup-plugin-commonjs@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.2.2.tgz#4959f3ff0d9706c132e5247b47ab385f11d9aae6" - integrity sha512-FXBgY+IvZIV2AZVT/0CPMsP+b1dKkxE+F6SHI9wddqKDV9KCGDA2cV5e/VsJLwXKFgrtliqMr7Rq3QBfPiJ8Xg== +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +rollup-plugin-commonjs@^9.3.4: + version "9.3.4" + resolved "https://registry.yarnpkg.com/rollup-plugin-commonjs/-/rollup-plugin-commonjs-9.3.4.tgz#2b3dddbbbded83d45c36ff101cdd29e924fd23bc" + integrity sha512-DTZOvRoiVIHHLFBCL4pFxOaJt8pagxsVldEXBOn6wl3/V21wVaj17HFfyzTsQUuou3sZL3lEJZVWKPFblJfI6w== dependencies: estree-walker "^0.6.0" magic-string "^0.25.2" resolve "^1.10.0" - rollup-pluginutils "^2.5.0" + rollup-pluginutils "^2.6.0" rollup-plugin-filesize@^6.0.1: version "6.0.1" @@ -3806,19 +6697,20 @@ rollup-plugin-filesize@^6.0.1: gzip-size "^5.0.0" terser "^3.10.0" -rollup-plugin-node-resolve@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.0.1.tgz#f95765d174e5daeef9ea6268566141f53aa9d422" - integrity sha512-fSS7YDuCe0gYqKsr5OvxMloeZYUSgN43Ypi1WeRZzQcWtHgFayV5tUSPYpxuaioIIWaBXl6NrVk0T2/sKwueLg== +rollup-plugin-node-resolve@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-4.2.3.tgz#638a373a54287d19fcc088fdd1c6fd8a58e4d90a" + integrity sha512-r+WaesPzdGEynpLZLALFEDugA4ACa5zn7bc/+LVX4vAXQQ8IgDHv0xfsSvJ8tDXUtprfBtrDtRFg27ifKjcJTg== dependencies: - builtin-modules "^3.0.0" + "@types/resolve" "0.0.8" + builtin-modules "^3.1.0" is-module "^1.0.0" resolve "^1.10.0" -rollup-plugin-typescript2@^0.20.1: - version "0.20.1" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.20.1.tgz#fb1d411975cd875d24882ea66f5f4fd11d2f2240" - integrity sha512-uxA5JQNOfmJ9rsO0yJKTObb1t4nNYUexCg9zxhEKF+NzZwljYWdfgrA06UzA24cOk8fQjGEe7Q5+Vge2vFlnnw== +rollup-plugin-typescript2@^0.21.0: + version "0.21.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.21.0.tgz#cc61ed756ac6e68cb3c03f7ee78001346243ed54" + integrity sha512-fbUAc2bvWxRrg1GGMYCFVf6aSxq3zvWn0sY2ubzFW9shJNT95ztFbM6GHO4/2rDSCjier7IswQnbr1ySqoLNPw== dependencies: fs-extra "7.0.1" resolve "1.10.0" @@ -3833,21 +6725,21 @@ rollup-pluginutils@2.4.1: estree-walker "^0.6.0" micromatch "^3.1.10" -rollup-pluginutils@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.5.0.tgz#23be0f05ac3972ea7b08fc7870cb91fde5b23a09" - integrity sha512-9Muh1H+XB5f5ONmKMayUoTYR1EZwHbwJJ9oZLrKT5yuTf/RLIQ5mYIGsrERquVucJmjmaAW0Y7+6Qo1Ep+5w3Q== +rollup-pluginutils@^2.6.0: + version "2.6.0" + resolved "https://registry.yarnpkg.com/rollup-pluginutils/-/rollup-pluginutils-2.6.0.tgz#203706edd43dfafeaebc355d7351119402fc83ad" + integrity sha512-aGQwspEF8oPKvg37u3p7h0cYNwmJR1sCBMZGZ5b9qy8HGtETknqjzcxrDRrcAnJNXN18lBH4Q9vZYth/p4n8jQ== dependencies: estree-walker "^0.6.0" micromatch "^3.1.10" -rollup@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.7.4.tgz#dd9d1d4935d3db38f16e1caaef635d8d1b0831c4" - integrity sha512-nc86fETLHdozhRWlW/uNVIQ7ODuA1vU2/L8znAxP9TNMx1NA6GTth3llqoxxCle2kkyui+OfGzbKaQxD60NJjA== +rollup@^1.10.1: + version "1.10.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.10.1.tgz#aeb763bbe98f707dc6496708db88372fa66687e7" + integrity sha512-pW353tmBE7QP622ITkGxtqF0d5gSRCVPD9xqM+fcPjudeZfoXMFW2sCzsTe2TU/zU1xamIjiS9xuFCPVT9fESw== dependencies: "@types/estree" "0.0.39" - "@types/node" "^11.11.6" + "@types/node" "^11.13.5" acorn "^6.1.1" rst-selector-parser@^2.2.3: @@ -3869,14 +6761,21 @@ run-async@^2.2.0: dependencies: is-promise "^2.1.0" -rxjs@^6.4.0: +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= + dependencies: + aproba "^1.1.1" + +rxjs@^6.1.0, rxjs@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.4.0.tgz#f3bb0fe7bda7fb69deac0c16f17b50b0b8790504" integrity sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw== dependencies: tslib "^1.9.0" -safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.2, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -3915,14 +6814,40 @@ schedule@^0.5.0: dependencies: object-assign "^4.1.1" -scheduler@^0.13.6: - version "0.13.6" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889" - integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ== +scheduler@^0.14.0-alpha.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.14.0.tgz#b392c23c9c14bfa2933d4740ad5603cc0d59ea5b" + integrity sha512-9CgbS06Kki2f4R9FjLSITjZo5BZxPsryiRNyL3LpvrM9WxcVmhlqAOc9E+KQbeI2nqej4JIIbOsfdL51cNb4Iw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" +schema-utils@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" + integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== + dependencies: + ajv "^6.1.0" + ajv-errors "^1.0.0" + ajv-keywords "^3.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +selfsigned@^1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.4.tgz#cdd7eccfca4ed7635d47a08bf2d5d3074092e2cd" + integrity sha512-9AukTiDmHXGXWtWjembZ5NDmVvP2695EtpgbCsxCa68w3c88B+alqbmZ4O3hZ4VWGXeGWzEVdvqgAJD8DQPCDw== + dependencies: + node-forge "0.7.5" + "semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" @@ -3932,6 +6857,63 @@ semver@5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== +semver@^5.3.0: + version "5.7.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.0.tgz#790a7cf6fea5459bac96110b29b60412dc8ff96b" + integrity sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA== + +semver@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.0.0.tgz#05e359ee571e5ad7ed641a6eec1e547ba52dea65" + integrity sha512-0UewU+9rFapKFnlbirLi3byoOuhrSsli/z/ihNnvM24vgF+8sNBiI1LZPBSH9wJKUwaUbw+s3hToDLCXkrghrQ== + +send@0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1" + integrity sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw== + dependencies: + debug "2.6.9" + depd "~1.1.2" + destroy "~1.0.4" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "~1.6.2" + mime "1.4.1" + ms "2.0.0" + on-finished "~2.3.0" + range-parser "~1.2.0" + statuses "~1.4.0" + +serialize-javascript@^1.4.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.6.1.tgz#4d1f697ec49429a847ca6f442a2a755126c4d879" + integrity sha512-A5MOagrPFga4YaKQSWHryl7AXvbQkEqpw4NNYMTNYUNV51bA8ABHgYFpqKx+YFFrw59xMV1qGH1R4AgoNIVgCw== + +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.13.2: + version "1.13.2" + resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" + integrity sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.2" + send "0.16.2" + set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -3954,6 +6936,24 @@ set-value@^2.0.0: is-plain-object "^2.0.3" split-string "^3.0.1" +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= + +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -3964,6 +6964,16 @@ shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" +shell-quote@1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + integrity sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c= + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -3991,6 +7001,11 @@ sisteransi@^1.0.0: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c" integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ== +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= + slash@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" @@ -4032,6 +7047,43 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" +sockjs-client@1.1.5: + version "1.1.5" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.5.tgz#1bb7c0f7222c40f42adf14f4442cbd1269771a83" + integrity sha1-G7fA9yIsQPQq3xT0RCy9Eml3GoM= + dependencies: + debug "^2.6.6" + eventsource "0.1.6" + faye-websocket "~0.11.0" + inherits "^2.0.1" + json3 "^3.3.2" + url-parse "^1.1.8" + +sockjs-client@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.3.0.tgz#12fc9d6cb663da5739d3dc5fb6e8687da95cb177" + integrity sha512-R9jxEzhnnrdxLCNln0xg5uGHqMnkhPSTzUZH2eXcR03S/On9Yvoq2wyUZILRUhZCNVu2PmwWVoyuiPz8th8zbg== + dependencies: + debug "^3.2.5" + eventsource "^1.0.7" + faye-websocket "~0.11.1" + inherits "^2.0.3" + json3 "^3.3.2" + url-parse "^1.4.3" + +sockjs@0.3.19: + version "0.3.19" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" + integrity sha512-V48klKZl8T6MzatbLlzzRNhMepEys9Y4oGFpypBFFn1gLI/QQ9HtLLyWJNbPlwGLelOVOEijUbTTJeLLI59jLw== + dependencies: + faye-websocket "^0.10.0" + uuid "^3.0.1" + +source-list-map@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" + integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== + source-map-resolve@^0.5.0: version "0.5.2" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" @@ -4049,6 +7101,14 @@ source-map-support@^0.5.6, source-map-support@~0.5.6: buffer-from "^1.0.0" source-map "^0.6.0" +source-map-support@~0.5.10: + version "0.5.12" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.12.tgz#b4f3b10d51857a5af0138d3ce8003b201613d599" + integrity sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + source-map-url@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" @@ -4067,6 +7127,11 @@ sourcemap-codec@^1.4.4: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.4.tgz#c63ea927c029dd6bd9a2b7fa03b3fec02ad56e9f" integrity sha512-CYAPYdBu34781kLHkaW3m6b/uUSyMOC2R61gcYMWooeuaGtjof86ZA/8T+qVPPt7np1085CR9hmMGrySwEc8Xg== +sparkles@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" + integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== + spdx-correct@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.2.tgz#19bb409e91b47b1ad54159243f7312a858db3c2e" @@ -4089,6 +7154,29 @@ spdx-license-ids@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.1.tgz#e2a303236cac54b04031fa7a5a79c7e701df852f" +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.0.tgz#81f222b5a743a329aa12cea6a390e60e9b613c52" + integrity sha512-ot0oEGT/PGUpzf/6uk4AWLqkq+irlqHXkrdbk51oWONh3bxQmBuljxPNl66zlRRcIJStWq0QkLUCPOPjgjvU0Q== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -4113,10 +7201,29 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + integrity sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ== + dependencies: + safe-buffer "^5.1.1" + +ssri@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" + integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== + dependencies: + figgy-pudding "^3.5.1" + stack-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.1.tgz#d4f33ab54e8e38778b0ca5cfd3b3afb12db68620" +state-toggle@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" + integrity sha512-Qe8QntFrrpWTnHwvwj2FZTgv+PKIsp0B9VxLzLLbSpPXWOgRgc5LVj/aTiSfK1RqIeF9jeC1UeOH8Q8y60A7og== + static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -4124,10 +7231,52 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" +"statuses@>= 1.4.0 < 2": + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + +statuses@~1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" + integrity sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew== + stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" +stream-browserify@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" + integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= + string-length@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed" @@ -4167,24 +7316,50 @@ string.prototype.trim@^1.1.2: es-abstract "^1.5.0" function-bind "^1.0.2" +string_decoder@^1.0.0, string_decoder@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.2.0.tgz#fe86e738b19544afe70469243b2a1ee9240eae8d" + integrity sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w== + dependencies: + safe-buffer "~5.1.0" + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" dependencies: safe-buffer "~5.1.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" +stringify-entities@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" + integrity sha512-nrBAQClJAPN2p+uGCVJRPIPakKeKWZ9GtBCmormE7pWOSlHat7+x5A8gx85M7HM5Dt0BP3pP5RhVW77WdbJJ3A== dependencies: - ansi-regex "^2.0.0" + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-hexadecimal "^1.0.0" -strip-ansi@^4.0.0: +stringify-object@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/stringify-object/-/stringify-object-3.3.0.tgz#703065aefca19300d3ce88af4f5b3956d7556629" + integrity sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw== + dependencies: + get-own-enumerable-property-symbols "^3.0.0" + is-obj "^1.0.1" + is-regexp "^1.0.0" + +strip-ansi@4.0.0, strip-ansi@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" dependencies: ansi-regex "^3.0.0" +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + strip-ansi@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.0.0.tgz#f78f68b5d0866c20b2c9b8c61b5298508dc8756f" @@ -4192,6 +7367,13 @@ strip-ansi@^5.0.0: dependencies: ansi-regex "^4.0.0" +strip-ansi@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -4218,6 +7400,11 @@ supports-color@^6.0.0, supports-color@^6.1.0: dependencies: has-flag "^3.0.0" +symbol-observable@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" @@ -4236,6 +7423,11 @@ tapable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.0.tgz#0d076a172e3d9ba088fd2272b2668fb8d194b78c" +tapable@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" + integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + tar-fs@^1.13.0: version "1.16.3" resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" @@ -4259,6 +7451,19 @@ tar-stream@^1.1.2: to-buffer "^1.1.1" xtend "^4.0.0" +tar@^4: + version "4.4.8" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" + integrity sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ== + dependencies: + chownr "^1.1.1" + fs-minipass "^1.2.5" + minipass "^2.3.4" + minizlib "^1.1.1" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + term-size@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/term-size/-/term-size-1.2.0.tgz#458b83887f288fc56d6fffbfad262e26638efa69" @@ -4266,6 +7471,20 @@ term-size@^1.2.0: dependencies: execa "^0.7.0" +terser-webpack-plugin@^1.1.0, terser-webpack-plugin@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.2.3.tgz#3f98bc902fac3e5d0de730869f50668561262ec8" + integrity sha512-GOK7q85oAb/5kE12fMuLdn2btOS9OBZn4VsecpHDywoUC/jLhSAKOiYo0ezx7ss2EXPMzyEWFoE0s1WLE+4+oA== + dependencies: + cacache "^11.0.2" + find-cache-dir "^2.0.0" + schema-utils "^1.0.0" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + terser "^3.16.1" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + terser@^3.10.0: version "3.11.0" resolved "https://registry.yarnpkg.com/terser/-/terser-3.11.0.tgz#60782893e1f4d6788acc696351f40636d0e37af0" @@ -4275,6 +7494,15 @@ terser@^3.10.0: source-map "~0.6.1" source-map-support "~0.5.6" +terser@^3.16.1: + version "3.17.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-3.17.0.tgz#f88ffbeda0deb5637f9d24b0da66f4e15ab10cb2" + integrity sha512-/FQzzPJmCpjAH9Xvk2paiWrFq+5M6aVOf+2KRbwhByISDX/EujxsK+BAvrhb6H+2rtrLCHK9N01wO014vrIwVQ== + dependencies: + commander "^2.19.0" + source-map "~0.6.1" + source-map-support "~0.5.10" + test-exclude@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.1.0.tgz#6ba6b25179d2d38724824661323b73e03c0c1de1" @@ -4285,7 +7513,7 @@ test-exclude@^5.0.0: read-pkg-up "^4.0.0" require-main-filename "^1.0.1" -text-table@^0.2.0: +text-table@0.2.0, text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= @@ -4294,11 +7522,36 @@ throat@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/throat/-/throat-4.1.0.tgz#89037cbc92c56ab18926e6ba4cbb200e15672a6a" +through2@^2.0.0: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= +thunky@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.3.tgz#f5df732453407b09191dae73e2a8cc73f381a826" + integrity sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow== + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + integrity sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg== + dependencies: + setimmediate "^1.0.4" + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== + tmp@^0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" @@ -4310,6 +7563,19 @@ tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= + +to-ast@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/to-ast/-/to-ast-1.0.0.tgz#0c4a31c8c98edfde9aaf0192c794b4c8b11ee287" + integrity sha1-DEoxyMmO396arwGSx5S0yLEe4oc= + dependencies: + ast-types "^0.7.2" + esprima "^2.1.0" + to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" @@ -4359,10 +7625,25 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -ts-jest@^24.0.1: - version "24.0.1" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.1.tgz#77258061cc354c3fa8616b8ac03baa0f8580f854" - integrity sha512-mgNZmYPuGBNgYpUzchI7vdSr6zATQI0TrSyzREnXHuPCvlW8T1DQ/fdscgx4ivS5vAMUGUaoxGdWIVHC5I8imw== +trim-trailing-lines@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" + integrity sha512-bWLv9BbWbbd7mlqqs2oQYnLD/U/ZqeJeJwbO0FG2zA1aTq+HTvxfHNKFa/HGCVyJpDiioUYaBhfiT6rgk+l4mg== + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + integrity sha1-WFhUf2spB1fulczMZm+1AITEYN0= + +trough@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24" + integrity sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw== + +ts-jest@^24.0.2: + version "24.0.2" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.2.tgz#8dde6cece97c31c03e80e474c749753ffd27194d" + integrity sha512-h6ZCZiA1EQgjczxq+uGLXQlNgeg02WWJBbeT8j6nyIBRQdglqbvzDoHahTEIiS6Eor6x8mK6PfZ7brQ9Q6tzHw== dependencies: bs-logger "0.x" buffer-from "1.x" @@ -4374,10 +7655,10 @@ ts-jest@^24.0.1: semver "^5.5" yargs-parser "10.x" -ts-loader@^5.3.3: - version "5.3.3" - resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.3.3.tgz#8b4af042e773132d86b3c99ef0acf3b4d325f473" - integrity sha512-KwF1SplmOJepnoZ4eRIloH/zXL195F51skt7reEsS6jvDqzgc/YSbz9b8E07GxIUwLXdcD4ssrJu6v8CwaTafA== +ts-loader@^5.4.3: + version "5.4.3" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-5.4.3.tgz#837f7f397799a665fc1774303c9fd0930d1c5947" + integrity sha512-pHwZFkZioL7Yi2su0bhW2/djxZ+0iGat1cxlAif4Eg9j5znVYuWGtW0YYY/5w8W+IzLcAlD5KwJDrs5unUKIRA== dependencies: chalk "^2.3.0" enhanced-resolve "^4.0.0" @@ -4396,6 +7677,11 @@ tsutils@^3.7.0: dependencies: tslib "^1.8.1" +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -4412,10 +7698,28 @@ type-check@~0.3.2: dependencies: prelude-ls "~1.1.2" -typescript@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.1.tgz#b6691be11a881ffa9a05765a205cb7383f3b63c6" - integrity sha512-3NSMb2VzDQm8oBTLH6Nj55VVtUEpe/rgkIzMir0qVoLyjDZlnMBva0U6vDiV3IH+sl/Yu6oP5QwsAQtHPmDd2Q== +type-detect@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + +type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + integrity sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= + +typescript@^3.4.5: + version "3.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.4.5.tgz#2d2618d10bb566572b8d7aad5180d84257d70a99" + integrity sha512-YycBxUb49UUhdNMU5aJ7z5Ej2XGmaIBL0x34vZ82fn3hGvD+bgrMrVDpatgz2f7YxUMJxMkbWxJZeAvDxVe7Vw== uglify-js@^3.1.4: version "3.4.9" @@ -4428,6 +7732,51 @@ underscore@~1.4.4: version "1.4.4" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.4.4.tgz#61a6a32010622afa07963bf325203cf12239d604" +unherit@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" + integrity sha512-+XZuV691Cn4zHsK0vkKYwBEwB74T3IZIcxrgn2E4rKwTfFyI1zCh7X7grwh9Re08fdPlarIdyWgI8aVB3F5A5g== + dependencies: + inherits "^2.0.1" + xtend "^4.0.1" + +unicode-canonical-property-names-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" + integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== + +unicode-match-property-ecmascript@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" + integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== + dependencies: + unicode-canonical-property-names-ecmascript "^1.0.4" + unicode-property-aliases-ecmascript "^1.0.4" + +unicode-match-property-value-ecmascript@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz#5b4b426e08d13a80365e0d657ac7a6c1ec46a277" + integrity sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g== + +unicode-property-aliases-ecmascript@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz#a9cc6cc7ce63a0a3023fc99e341b94431d405a57" + integrity sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw== + +unified@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-7.1.0.tgz#5032f1c1ee3364bd09da12e27fdd4a7553c7be13" + integrity sha512-lbk82UOIGuCEsZhPj8rNAkXSDXd6p0QLzIuSsCdxrqnqU56St4eyOB+AlXsVgVeRmetPTYydIuvFfpDIed8mqw== + dependencies: + "@types/unist" "^2.0.0" + "@types/vfile" "^3.0.0" + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^3.0.0" + x-is-string "^0.1.0" + union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -4437,11 +7786,66 @@ union-value@^1.0.0: is-extendable "^0.1.1" set-value "^0.4.3" +unique-filename@^1.1.0, unique-filename@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" + integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.1.tgz#5e9edc6d1ce8fb264db18a507ef9bd8544451ca6" + integrity sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg== + dependencies: + imurmurhash "^0.1.4" + +unist-util-is@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" + integrity sha512-YkXBK/H9raAmG7KXck+UUpnKiNmUdB+aBGrknfQ4EreE1banuzrKABx3jP6Z5Z3fMSPMQQmeXBlKpCbMwBkxVw== + +unist-util-remove-position@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" + integrity sha512-XxoNOBvq1WXRKXxgnSYbtCF76TJrRoe5++pD4cCBsssSiWSnPEktyFrFLE8LTk3JW5mt9hB0Sk5zn4x/JeWY7Q== + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + integrity sha512-pNCVrk64LZv1kElr0N1wPiHEUoXNVFERp+mlTg/s9R5Lwg87f9bM/3sQB99w+N9D/qnM9ar3+AKDBwo/gm/iQQ== + +unist-util-visit-parents@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" + integrity sha512-6B0UTiMfdWql4cQ03gDTCSns+64Zkfo2OCbK31Ov0uMizEz+CJeAp0cgZVb5Fhmcd7Bct2iRNywejT0orpbqUA== + dependencies: + unist-util-is "^2.1.2" + +unist-util-visit@^1.1.0, unist-util-visit@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.0.tgz#1cb763647186dc26f5e1df5db6bd1e48b3cc2fb1" + integrity sha512-FiGu34ziNsZA3ZUteZxSFaczIjGmksfSgdKqBfOejrrfzyUy5b7YrlzT1Bcvi+djkYDituJDy2XB7tGTeBieKw== + dependencies: + unist-util-visit-parents "^2.0.0" + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + +unquote@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" + integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= + unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -4449,6 +7853,11 @@ unset-value@^1.0.0: has-value "^0.3.1" isobject "^3.0.0" +upath@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" + integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== + uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -4460,11 +7869,27 @@ urix@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" +url-parse@^1.1.8, url-parse@^1.4.3: + version "1.4.6" + resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.6.tgz#baf91d6e6783c8a795eb476892ffef2737fc0456" + integrity sha512-/B8AD9iQ01seoXmXf9z/MjLZQIdOoYl/+gvsQF6+mpnxaTfG9P7srYaiqaDMyKkR36XMXfhqSHss5MyFAO8lew== + dependencies: + querystringify "^2.0.0" + requires-port "^1.0.0" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= + dependencies: + punycode "1.3.2" + querystring "0.2.0" + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" -util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -4475,7 +7900,26 @@ util.promisify@^1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" -uuid@^3.3.2: +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + +util@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" + integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== + dependencies: + inherits "2.0.3" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + +uuid@^3.0.1, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" @@ -4486,6 +7930,11 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +vary@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + verror@1.10.0: version "1.10.0" resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" @@ -4494,22 +7943,198 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vfile-location@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.4.tgz#2a5e7297dd0d9e2da4381464d04acc6b834d3e55" + integrity sha512-KRL5uXQPoUKu+NGvQVL4XLORw45W62v4U4gxJ3vRlDfI9QsT4ZN1PNXn/zQpKUulqGDpYuT0XDfp5q9O87/y/w== + +vfile-message@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.1.1.tgz#5833ae078a1dfa2d96e9647886cd32993ab313e1" + integrity sha512-1WmsopSGhWt5laNir+633LszXvZ+Z/lxveBf6yhGsqnQIhlhzooZae7zV6YVM1Sdkw68dtAW3ow0pOdPANugvA== + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-3.0.1.tgz#47331d2abe3282424f4a4bb6acd20a44c4121803" + integrity sha512-y7Y3gH9BsUSdD4KzHsuMaCzRjglXN0W2EcMf0gpvu6+SbsGhMje7xDc8AEoeXy6mIwCKMI6BkjMsRjzQbhMEjQ== + dependencies: + is-buffer "^2.0.0" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + integrity sha1-XX6kW7755Kb/ZflUOOCofDV9WnM= + dependencies: + indexof "0.0.1" + w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" dependencies: browser-process-hrtime "^0.1.2" -walker@~1.0.5: +walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" dependencies: makeerror "1.0.x" +walkes@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/walkes/-/walkes-0.2.1.tgz#7eca144fe67ed32782fffe6e8e95fb4481864796" + integrity sha1-fsoUT+Z+0yeC//5ujpX7RIGGR5Y= + +warning@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" + integrity sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w= + dependencies: + loose-envify "^1.0.0" + +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + integrity sha512-i6dHe3EyLjMmDlU1/bGQpEw25XSjkJULPuAVKCbNRefQVq48yXKUpwg538F7AZTf9kyr57zj++pQFltUa5H7yA== + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= + dependencies: + defaults "^1.0.3" + webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" +webpack-dev-middleware@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.6.2.tgz#f37a27ad7c09cd7dc67cd97655413abaa1f55942" + integrity sha512-A47I5SX60IkHrMmZUlB0ZKSWi29TZTcPz7cha1Z75yYOsgWh/1AcPmQEbC8ZIbU3A1ytSv1PMU0PyPz2Lmz2jg== + dependencies: + memory-fs "^0.4.1" + mime "^2.3.1" + range-parser "^1.0.3" + webpack-log "^2.0.0" + +webpack-dev-server@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.3.1.tgz#7046e49ded5c1255a82c5d942bcdda552b72a62d" + integrity sha512-jY09LikOyGZrxVTXK0mgIq9y2IhCoJ05848dKZqX1gAGLU1YDqgpOT71+W53JH/wI4v6ky4hm+KvSyW14JEs5A== + dependencies: + ansi-html "0.0.7" + bonjour "^3.5.0" + chokidar "^2.1.5" + compression "^1.7.4" + connect-history-api-fallback "^1.6.0" + debug "^4.1.1" + del "^4.1.0" + express "^4.16.4" + html-entities "^1.2.1" + http-proxy-middleware "^0.19.1" + import-local "^2.0.0" + internal-ip "^4.2.0" + ip "^1.1.5" + killable "^1.0.1" + loglevel "^1.6.1" + opn "^5.5.0" + portfinder "^1.0.20" + schema-utils "^1.0.0" + selfsigned "^1.10.4" + semver "^6.0.0" + serve-index "^1.9.1" + sockjs "0.3.19" + sockjs-client "1.3.0" + spdy "^4.0.0" + strip-ansi "^3.0.1" + supports-color "^6.1.0" + url "^0.11.0" + webpack-dev-middleware "^3.6.2" + webpack-log "^2.0.0" + yargs "12.0.5" + +webpack-log@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" + integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== + dependencies: + ansi-colors "^3.0.0" + uuid "^3.3.2" + +webpack-merge@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.1.tgz#5e923cf802ea2ace4fd5af1d3247368a633489b4" + integrity sha512-4p8WQyS98bUJcCvFMbdGZyZmsKuWjWVnVHnAS3FFg0HDaRVrPbkivx2RYCre8UiemD67RsiFFLfn4JhLAin8Vw== + dependencies: + lodash "^4.17.5" + +webpack-sources@^1.1.0, webpack-sources@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.3.0.tgz#2a28dcb9f1f45fe960d8f1493252b5ee6530fa85" + integrity sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA== + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.30.0: + version "4.30.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.30.0.tgz#aca76ef75630a22c49fcc235b39b4c57591d33a9" + integrity sha512-4hgvO2YbAFUhyTdlR4FNyt2+YaYBYHavyzjCMbZzgglo02rlKi/pcsEzwCuCpsn1ryzIl1cq/u8ArIKu8JBYMg== + dependencies: + "@webassemblyjs/ast" "1.8.5" + "@webassemblyjs/helper-module-context" "1.8.5" + "@webassemblyjs/wasm-edit" "1.8.5" + "@webassemblyjs/wasm-parser" "1.8.5" + acorn "^6.0.5" + acorn-dynamic-import "^4.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^1.0.0" + tapable "^1.1.0" + terser-webpack-plugin "^1.1.0" + watchpack "^1.5.0" + webpack-sources "^1.3.0" + +websocket-driver@>=0.5.1: + version "0.7.0" + resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" + integrity sha1-DK+dLXVdk67gSdS90NP+LMoqJOs= + dependencies: + http-parser-js ">=0.4.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" + integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg== + whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3: version "1.0.5" resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0" @@ -4545,7 +8170,7 @@ which-pm-runs@^1.0.0: resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb" integrity sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= -which@^1.2.9, which@^1.3.0: +which@^1.2.14, which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" dependencies: @@ -4572,6 +8197,13 @@ wordwrap@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + integrity sha512-6w+3tHbM87WnSWnENBUvA2pxJPLhQUg5LKwUQHq3r+XPhIM+Gh2R5ycbwPCyuGbNg+lPgdcnQUhuC02kJCvffQ== + dependencies: + errno "~0.1.7" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -4605,15 +8237,20 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + integrity sha1-R0tQhlrzpJqcRlfwWs0UVFj3fYI= + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" -xtend@^4.0.0: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" -"y18n@^3.2.1 || ^4.0.0": +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== @@ -4622,6 +8259,11 @@ yallist@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" + integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== + yargs-parser@10.x: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -4636,7 +8278,7 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs@^12.0.2: +yargs@12.0.5, yargs@^12.0.2: version "12.0.5" resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13" integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==