-
- What else do you know about the Series?
-
+ {!isUnifiedForm && heading}
+ {!hideItemSelect &&
All fields are mandatory — select the option from dropdown
-
+ }
-
+
Ordering Type
@@ -195,6 +203,7 @@ function SeriesSection({
onChange={onOrderTypeChange}
/>
+ {!isUnifiedForm &&
Series Type
@@ -215,11 +224,13 @@ function SeriesSection({
value={seriesTypeOption}
onChange={onSeriesTypeChange}
/>
-
+ }
): Promise {
}
});
}
+function transformFormData(data:Record):Record {
+ const newData = {};
+ const nextId = 0;
+ // add new series
+ _.forEach(data.Series, (series, sid) => {
+ // sync local series section with global series section
+ series.seriesSection = data.seriesSection;
+ // might be possible for series items to not have target id
+ _.forEach(series.seriesSection.seriesItems, (item) => {
+ _.set(item, 'targetEntity.bbid', series.id);
+ });
+ series.seriesSection.seriesItems = filterObject(series.seriesSection.seriesItems, (rel) => !rel.attributeSetId);
+ // if new items have been added to series, then add series to the post data
+ if (_.size(series.seriesSection.seriesItems) > 0) {
+ series.__isNew__ = false;
+ series.submissionSection = {
+ note: 'added more series items'
+ };
+ newData[sid] = series;
+ }
+ });
+ // add new works
+ const authorWorkRelationshipTypeId = 8;
+ _.forEach(data.Works, (work, wid) => {
+ // if authors have been added to the work, then add work to the post data
+ if (!work.checked) { return; }
+ let relationshipCount = 0;
+ // hashset in order to avoid duplicate relationships
+ const authorBBIDSet = new Set();
+ if (work.relationshipSet) {
+ _.forEach(work.relationshipSet.relationships, (rel) => {
+ if (rel.typeId === authorWorkRelationshipTypeId) {
+ authorBBIDSet.add(rel.sourceBbid);
+ }
+ });
+ }
+ let flag = false;
+ _.forEach(data.authorCreditEditor, (authorCredit) => {
+ if (authorBBIDSet.has(authorCredit.author.bbid)) { return; }
+ const relationship = {
+ attributeSetId: null,
+ attributes: [],
+ isAdded: true,
+ relationshipType: {
+ id: authorWorkRelationshipTypeId
+ },
+ rowId: `a${relationshipCount}`,
+ sourceEntity: {
+ bbid: authorCredit.author.id
+ },
+ targetEntity: {
+ bbid: work.id
+ }
+ };
+ _.set(work, ['relationshipSection', 'relationships', `a${relationshipCount}`], relationship);
+ relationshipCount++;
+ flag = true;
+ });
+ if (flag) {
+ work.submissionSection = {
+ note: 'added authors from parent edition'
+ };
+ work.__isNew__ = false;
+ newData[wid] = work;
+ }
+ });
+ // add edition at last
+ if (data.ISBN.type) {
+ data.identifierEditor.m0 = data.ISBN;
+ }
+ data.relationshipSection.relationships = _.mapValues(data.Works, (work, key) => {
+ const relationship = {
+ attributeSetId: null,
+ attributes: [],
+ isAdded: true,
+ relationshipType: {
+ id: 10
+ },
+ rowID: key,
+ sourceEntity: {
+ },
+ targetEntity: {
+ bbid: work.id
+ }
+ };
+ return relationship;
+ });
+ newData[`e${nextId}`] = {...data, type: 'Edition'};
+ return newData;
+}
+
+function postUFSubmission(url: string, data: Map): Promise {
+ // transform data
+ const jsonData = data.toJS();
+ const postData = transformFormData(jsonData);
+ return request.post(url).send(postData)
+ .then((response) => {
+ if (!response.body) {
+ window.location.replace('/login');
+ }
+ const editionEntity = response.body.find((entity) => entity.type === 'Edition');
+ const redirectUrl = `/edition/${editionEntity.bbid}`;
+ if (response.body.alert) {
+ const alertParam = `?alert=${response.body.alert}`;
+ window.location.href = `${redirectUrl}${alertParam}`;
+ }
+ else {
+ window.location.href = redirectUrl;
+ }
+ });
+}
type SubmitResult = (arg1: (Action) => unknown, arg2: () => Map) => unknown;
export function submit(
- submissionUrl: string
+ submissionUrl: string,
+ isUnifiedForm = false
): SubmitResult {
return (dispatch, getState) => {
const rootState = getState();
dispatch(setSubmitted(true));
+ if (isUnifiedForm) {
+ return postUFSubmission(submissionUrl, rootState)
+ .catch(
+ (error: {message: string}) => {
+ const message =
+ _.get(error, ['response', 'body', 'error'], null) ||
+ error.message;
+ dispatch(setSubmitted(false));
+ return dispatch(setSubmitError(message));
+ }
+ );
+ }
return postSubmission(submissionUrl, rootState)
.catch(
(error: {message: string}) => {
@@ -138,3 +264,40 @@ export function submit(
);
};
}
+
+/**
+ *
+ * @param {string} submissionUrl - The URL to post the submission to
+ * @param {string} entityType - The type of entity being submitted
+ * @param {Function} callback - A function that adds the entity to the store
+ * @param {Object} initialState - The initial state of the entity being submitted, this include some fields which are required by the server
+ * @returns {function} - A thunk that posts the submission to the server
+ */
+export function submitSingleEntity(submissionUrl:string, entityType:EntityTypeString, callback:(newEntity)=>void, initialState = {}):SubmitResult {
+ return async (dispatch, getState) => {
+ const rootState = getState();
+ dispatch(setSubmitted(true));
+ const JSONState = rootState.toJS();
+ const entity = {...JSONState, type: entityType};
+ const postData = {
+ 0: entity
+ };
+ try {
+ const response = await request.post(submissionUrl).send(postData);
+ const mainEntity = response.body[0];
+ const entityObject = {...initialState,
+ __isNew__: true,
+ id: mainEntity.bbid,
+ text: mainEntity.name,
+ ...mainEntity};
+ return dispatch(callback(entityObject)) && dispatch(setSubmitted(false));
+ }
+ catch (error) {
+ const message =
+ _.get(error, ['response', 'body', 'error'], null) ||
+ error.message;
+ dispatch(setSubmitted(false));
+ return dispatch(setSubmitError(message));
+ }
+ };
+}
diff --git a/src/client/entity-editor/submission-section/submission-section.js b/src/client/entity-editor/submission-section/submission-section.js
index 087ab44de6..e7ed66234c 100644
--- a/src/client/entity-editor/submission-section/submission-section.js
+++ b/src/client/entity-editor/submission-section/submission-section.js
@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-import {Alert, Button, Col, Form, OverlayTrigger, Row, Tooltip} from 'react-bootstrap';
+import {Alert, Button, Col, Form, OverlayTrigger, Row, Spinner, Tooltip} from 'react-bootstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import PropTypes from 'prop-types';
import React from 'react';
@@ -99,7 +99,15 @@ function SubmissionSection({
type="submit"
variant="success"
>
- Submit
+ {submitted &&
+ }
+ {submitted ? ' Submit' : 'Submit'}
@@ -117,11 +125,11 @@ SubmissionSection.propTypes = {
submitted: PropTypes.bool.isRequired
};
-function mapStateToProps(rootState, {validate, identifierTypes, isMerge}) {
+function mapStateToProps(rootState, {validate, identifierTypes, isMerge, formValid = false}) {
const state = rootState.get('submissionSection');
return {
errorText: state.get('submitError'),
- formValid: validate(rootState, identifierTypes, isMerge),
+ formValid: formValid || (validate && validate(rootState, identifierTypes, isMerge)),
note: state.get('note'),
submitted: state.get('submitted')
};
diff --git a/src/client/entity-editor/validators/common.ts b/src/client/entity-editor/validators/common.ts
index 85644116f4..b16cd84c46 100644
--- a/src/client/entity-editor/validators/common.ts
+++ b/src/client/entity-editor/validators/common.ts
@@ -25,9 +25,9 @@ import {
validateUUID
} from './base';
+import {AuthorCredit} from '../author-credit-editor/actions';
import {Iterable} from 'immutable';
import _ from 'lodash';
-import {AuthorCredit} from '../author-credit-editor/actions';
export function validateMultiple(
diff --git a/src/client/entity-editor/validators/edition.ts b/src/client/entity-editor/validators/edition.ts
index d889c7ad34..7e88e4ecd6 100644
--- a/src/client/entity-editor/validators/edition.ts
+++ b/src/client/entity-editor/validators/edition.ts
@@ -84,7 +84,8 @@ export function validateEditionSectionPublisher(value: any): boolean {
for (const pubId in publishers) {
if (Object.prototype.hasOwnProperty.call(publishers, pubId)) {
const publisher = publishers[pubId];
- if (!validateUUID(get(publisher, 'id', null), true)) {
+ const isValid = validateUUID(get(publisher, 'id', null), true);
+ if (!isValid) {
return false;
}
}
diff --git a/src/client/entity-editor/work-section/work-section.tsx b/src/client/entity-editor/work-section/work-section.tsx
index b156abe208..7818e62f3c 100644
--- a/src/client/entity-editor/work-section/work-section.tsx
+++ b/src/client/entity-editor/work-section/work-section.tsx
@@ -55,6 +55,7 @@ type DisplayLanguageOption = {
};
type OwnProps = {
+ isUnifiedForm?: boolean,
languageOptions: Array
,
workTypes: Array
};
@@ -89,6 +90,7 @@ function WorkSection({
languageValues,
typeValue,
workTypes,
+ isUnifiedForm,
onLanguagesChange,
onTypeChange
}: Props) {
@@ -103,24 +105,25 @@ function WorkSection({
value: type.id
}));
const typeOption = workTypesForDisplay.filter((el) => el.value === typeValue);
-
const tooltip = (
Literary form or structure of the work
);
-
+ const heading = What else do you know about the Work? ;
+ const lgCol = {offset: 3, span: 6};
+ if (isUnifiedForm) {
+ lgCol.offset = 0;
+ }
return (
-
- What else do you know about the Work?
-
+ {!isUnifiedForm && heading}
All fields optional — leave something blank if you don’t
know it
-
+
Type
@@ -142,7 +145,7 @@ function WorkSection({
-
+
>;
function mapStateToProps(rootState: RootState): StateProps {
const state: Map = rootState.get('workSection');
diff --git a/src/client/helpers/entity.tsx b/src/client/helpers/entity.tsx
index c5ebdb135d..62de5deb6d 100644
--- a/src/client/helpers/entity.tsx
+++ b/src/client/helpers/entity.tsx
@@ -23,7 +23,7 @@ import * as React from 'react';
import {FontAwesomeIconProps as FAProps, FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {get as _get, isNil as _isNil, kebabCase as _kebabCase, upperFirst} from 'lodash';
import {
- faBook, faGlobe, faGripVertical, faLayerGroup, faPenNib, faUniversity, faUser, faUserCircle, faWindowRestore
+ faBook, faGlobe, faGripVertical, faLayerGroup, faMagicWandSparkles, faPenNib, faUniversity, faUser, faUserCircle, faWindowRestore
} from '@fortawesome/free-solid-svg-icons';
import {format, isValid, parseISO} from 'date-fns';
import {dateObjectToISOString} from './utils';
@@ -256,6 +256,7 @@ export function getEntityUrl(entity) {
export const ENTITY_TYPE_ICONS = {
Area: faGlobe,
Author: faUser,
+ Book: faMagicWandSparkles,
Collection: faGripVertical,
Edition: faBook,
EditionGroup: faWindowRestore,
diff --git a/src/client/helpers/utils.tsx b/src/client/helpers/utils.tsx
index 1ffa923955..28f3f2ef08 100644
--- a/src/client/helpers/utils.tsx
+++ b/src/client/helpers/utils.tsx
@@ -219,3 +219,12 @@ export function getEntityKey(entityType:string) {
};
return keys[entityType];
}
+
+export function countWords(text: string) : number {
+ // Credit goes to iamwhitebox https://stackoverflow.com/a/39125279/14911205
+ const words = text.match(/\w+/g);
+ if (words === null) {
+ return 0;
+ }
+ return words.length;
+}
diff --git a/src/client/stylesheets/external-services.scss b/src/client/stylesheets/external-services.scss
new file mode 100644
index 0000000000..61f8a24cff
--- /dev/null
+++ b/src/client/stylesheets/external-services.scss
@@ -0,0 +1,74 @@
+.external-service-option {
+ input[type="radio"] {
+ display: none;
+ &:not(:disabled) ~ label {
+ cursor: pointer;
+ }
+ &:disabled ~ label {
+ color: hsla(150, 5%, 75%, 1);
+ border-color: hsla(150, 5%, 75%, 1);
+ cursor: not-allowed;
+ }
+ }
+ label {
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+
+ background: white;
+ border: 2px solid #5aa854;
+ border-radius: 5px;
+ padding: 0.5rem;
+
+ text-align: center;
+ font-weight: normal;
+ position: relative;
+
+ > .title {
+ flex: 1;
+ flex-basis: 120px;
+ font-size: 1.2em;
+ }
+ > .details {
+ flex: 2;
+ text-align: justify;
+ flex-basis: 200px;
+ }
+ }
+ input[type="radio"]:checked + label {
+ background: #5aa854;
+ color: white;
+
+ > .details {
+ font-weight: bold;
+ }
+ &::after {
+ position: absolute;
+ left: 0;
+ top: 50%;
+ border-radius: 50%;
+ transform: translate(-50%, -50%);
+
+ content: "✔";
+ color: #5aa854;
+ font-size: 24px;
+ text-align: center;
+
+ width: 40px;
+ height: 40px;
+ line-height: 40px;
+
+ border: 2px solid #5aa854;
+ background: white;
+ box-shadow: 3px 1px 5px -2px #053b47;
+ }
+ }
+ &.disable {
+ label {
+ border-color: #a0a0a0;
+ }
+ input[type="radio"]:checked + label {
+ background: #a0a0a0;
+ }
+ }
+}
diff --git a/src/client/stylesheets/lobes b/src/client/stylesheets/lobes
deleted file mode 160000
index ad7d57abdc..0000000000
--- a/src/client/stylesheets/lobes
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit ad7d57abdc59fdb335e788922a41678a0de1b710
diff --git a/src/client/stylesheets/style.scss b/src/client/stylesheets/style.scss
index 0e0e8b1015..20313bfc91 100644
--- a/src/client/stylesheets/style.scss
+++ b/src/client/stylesheets/style.scss
@@ -28,6 +28,7 @@ $disabled-color: lighten($disabled-bg,15%);
@import "lobes4/scss/bootstrap.scss";
@import "react-datepicker/dist/react-datepicker.css";
+@import "./external-services.scss";
.navbar.BookBrainz {
background: rgba(255, 255, 255, 1);
@@ -296,7 +297,7 @@ a.contact-text:visited {
border: 1px solid hsl(0, 0%, 70%) !important;
box-shadow: none !important;
&:hover{
- border: 1px solid hsl(0, 0%, 70%) !important;
+ border: 1px solid hsl(0, 0%, 70%) !important;
}
}
@@ -718,6 +719,61 @@ ul {
.input-group > .input-group-append > .btn {
padding: 6px 12px;
}
+
+/* Unified form */
+.uf-main{
+ padding: 0.6rem;
+}
+.uf-tab{
+ border: 1px #dfdfdf solid;
+ h4{
+ padding: 0.8rem;
+ margin-bottom: 0;
+ border-bottom: 0.125rem #ed8d60 solid;
+ background-color: #F7F7F7;
+
+ }
+ .tab-content{
+ padding: 1rem;
+ }
+}
+.uf-tab-header{
+ padding: 0.6rem 0 0 0.6rem;
+ margin-bottom: 1rem;
+ background-color: #F4E9E3;
+ a{
+ color: #754E37;
+ }
+ .nav-link.nav-item.active{
+ color: $orange;
+ border-color: $orange;
+ border-bottom: none;
+ }
+}
+.uf-navbtn-row {
+ margin: 0;
+ padding-top: 1rem;
+ padding-bottom: 1rem;
+ button{
+ width: max-content;
+ }
+ .col{
+ align-items: stretch;
+ flex-grow: 0;
+ margin-right: 0.3rem;
+ padding: 0;
+ }
+ .col:nth-child(1)
+ {
+ margin-right: 3rem;
+ }
+}
+.uf-dialog{
+ max-width: 700px;
+}
+.uf-dialog.modal-dialog{
+ margin: 1.75rem 2rem;
+}
.ac-select {
flex-grow: 2;
}
@@ -726,4 +782,73 @@ ul {
}
.series-editor-select {
margin-top: 1.7em;
+}
+
+.cb-data-tip {
+ width: 300px;
+}
+
+.duplicate-result-scroll-window{
+ max-height: 17em;
+ overflow-y: scroll;
+ &:after {
+ display: block;
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ content: " ";
+ height: 20px;
+ background: -moz-linear-gradient(top,rgba(255,255,255,0) 0,#fff 100%);
+ background: -webkit-gradient(linear,left top,left bottom,color-stop(0,rgba(255,255,255,0)),color-stop(100%,#fff));
+ background: -webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fff 100%);
+ background: -o-linear-gradient(top,rgba(255,255,255,0) 0,#fff 100%);
+ background: -ms-linear-gradient(top,rgba(255,255,255,0) 0,#fff 100%);
+ background: linear-gradient(top,rgba(255,255,255,0) 0,#fff 100%);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#ffffff', GradientType=0);
+ }
+}
+.uf-dialog .accordion > .card{
+ overflow:visible;
+}
+
+// Adding icon to accordion header
+.accordion > .icon-card{
+ display: flex;
+ overflow: visible;
+ flex-direction: column-reverse;
+}
+
+.accordion > .card > .card-header{
+ border: none;
+ cursor: pointer;
+ display: flex;
+ justify-content: space-between;
+}
+div[class='card-header'] .accordion-arrow {
+ transition: all 0.3s linear;
+ transform: rotate(0deg);
+}
+div[class~='show'] + div[class='card-header'] .accordion-arrow,
+div[class~=collapsing]+div[class=card-header] .accordion-arrow {
+ transform: rotate(90deg);
+}
+.work-item{
+ border-bottom: 1px solid #EAE7E5;
+ margin-bottom: 0.5rem;
+}
+
+.uf-modal-body .accordion > .card{
+ margin-bottom: 10px;
+}
+.review-section{
+ display: flex;
+}
+
+.series-item-dialog{
+ max-width: 600px;
+}
+
+.review-card{
+ margin: 10px;
+ max-width: 200px;
}
\ No newline at end of file
diff --git a/src/client/unified-form/action.ts b/src/client/unified-form/action.ts
new file mode 100644
index 0000000000..ec136a6903
--- /dev/null
+++ b/src/client/unified-form/action.ts
@@ -0,0 +1,64 @@
+import {Action} from './interface/type';
+
+
+export const DUMP_EDITION = 'DUMP_EDITION';
+export const LOAD_EDITION = 'LOAD_EDITION';
+export const OPEN_ENTITY_MODAL = 'OPEN_ENTITY_MODAL';
+export const CLOSE_ENTITY_MODAL = 'CLOSE_ENTITY_MODAL';
+
+const nextEditionId = 0;
+
+/**
+ * Produces an action indicating that current edition state should be saved in `Editions`.
+ *
+ * @param {string} type - type of new entity which caused dump
+ * @returns {Action} The resulting DUMP_EDITION action.
+ */
+export function dumpEdition(type?:string):Action {
+ return {
+ payload: {
+ id: `e${nextEditionId}`,
+ type,
+ value: null
+ },
+ type: DUMP_EDITION
+ };
+}
+
+/**
+ * Produces an action indicating that particular edition state having that id
+ * should be loaded from `Editions`.
+ *
+ * @param {string} editionId - id of edition to load
+ * @returns {Action} The resulting LOAD_EDITION action.
+ */
+export function loadEdition(editionId = 'e0'):Action {
+ return {
+ payload: {
+ id: editionId
+ },
+ type: LOAD_EDITION
+ };
+}
+
+/**
+ * Set entity modal state to open
+ *
+ * @returns {Action} The resulting OPEN_ENTITY_MODAL action.
+ */
+export function openEntityModal():Action {
+ return {
+ type: OPEN_ENTITY_MODAL
+ };
+}
+
+/**
+ * Set entity modal state to close
+ *
+ * @returns {Action} The resulting CLOSE_ENTITY_MODAL action.
+ */
+export function closeEntityModal():Action {
+ return {
+ type: CLOSE_ENTITY_MODAL
+ };
+}
diff --git a/src/client/unified-form/common/create-entity-modal.tsx b/src/client/unified-form/common/create-entity-modal.tsx
new file mode 100644
index 0000000000..0f7cbe1426
--- /dev/null
+++ b/src/client/unified-form/common/create-entity-modal.tsx
@@ -0,0 +1,33 @@
+import * as Bootstrap from 'react-bootstrap';
+import {getEntitySection, getValidator} from '../../entity-editor/helpers';
+import {CreateEntityModalProps} from '../interface/type';
+import EntityModalBody from './entity-modal-body';
+import React from 'react';
+import {filterIdentifierTypesByEntityType} from '../../../common/helpers/utils';
+import {upperFirst} from 'lodash';
+
+
+const {Modal} = Bootstrap;
+export default function CreateEntityModal({show, handleClose, handleSubmit, type, ...rest}:CreateEntityModalProps) {
+ const heading = `Add ${upperFirst(type)}`;
+ const EntitySection = getEntitySection(type);
+ const validate = getValidator(type);
+ const {allIdentifierTypes} = rest;
+ const entityIdentifierTypes = filterIdentifierTypesByEntityType(allIdentifierTypes, upperFirst(type));
+ return (
+
+
+ {heading}
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/client/unified-form/common/entity-modal-body.tsx b/src/client/unified-form/common/entity-modal-body.tsx
new file mode 100644
index 0000000000..dd43e31f13
--- /dev/null
+++ b/src/client/unified-form/common/entity-modal-body.tsx
@@ -0,0 +1,60 @@
+import {EntityModalBodyProps, EntityModalDispatchProps} from '../interface/type';
+import AliasModalBody from '../../entity-editor/alias-editor/alias-modal-body';
+// import {validateAliases, validateIdentifiers, validateNameSection} from '../../entity-editor/validators/common';
+import AnnotationSection from '../../entity-editor/annotation-section/annotation-section';
+import IdentifierModalBody from '../../entity-editor/identifier-editor/identifier-modal-body';
+import NameSection from '../../entity-editor/name-section/name-section';
+import React from 'react';
+import RelationshipSection from '../../entity-editor/relationship-editor/relationship-section';
+import SingleAccordion from './single-accordion';
+import SubmissionSection from '../../entity-editor/submission-section/submission-section';
+import {connect} from 'react-redux';
+import {omit} from 'lodash';
+import {removeEmptyAliases} from '../../entity-editor/alias-editor/actions';
+import {removeEmptyIdentifiers} from '../../entity-editor/identifier-editor/actions';
+
+
+function EntityModalBody({onModalSubmit, children, validate, onAliasClose, onIdentifierClose, ...rest}
+ :EntityModalBodyProps) {
+ const genericProps:any = omit(rest, ['allIdentifierTypes']);
+ return (
+
+ );
+}
+EntityModalBody.defaultProps = {
+ children: null
+};
+function mapDispatchToProps(dispatch) {
+ return {
+ onAliasClose: () => dispatch(removeEmptyAliases()),
+ onIdentifierClose: () => dispatch(removeEmptyIdentifiers())
+
+ };
+}
+export default connect(null, mapDispatchToProps)(EntityModalBody);
diff --git a/src/client/unified-form/common/freezed-objects.ts b/src/client/unified-form/common/freezed-objects.ts
new file mode 100644
index 0000000000..8bdc3e733e
--- /dev/null
+++ b/src/client/unified-form/common/freezed-objects.ts
@@ -0,0 +1,4 @@
+// Singleton Pattern: this will ensure that expensive calculations is done only once
+export const freezeObjects = {
+ filterOptions: null
+};
diff --git a/src/client/unified-form/common/search-entity-create-select.tsx b/src/client/unified-form/common/search-entity-create-select.tsx
new file mode 100644
index 0000000000..5a97e3e829
--- /dev/null
+++ b/src/client/unified-form/common/search-entity-create-select.tsx
@@ -0,0 +1,100 @@
+import {SearchEntityCreateDispatchProps, SearchEntityCreateProps} from '../interface/type';
+import {addAuthor, addPublisher} from '../cover-tab/action';
+import {addSeries, addWork} from '../content-tab/action';
+import {checkIfNameExists, searchName, updateNameField, updateSortNameField} from '../../entity-editor/name-section/actions';
+import {closeEntityModal, dumpEdition, loadEdition, openEntityModal} from '../action';
+import AsyncCreatable from 'react-select/async-creatable';
+import BaseEntitySearch from '../../entity-editor/common/entity-search-field-option';
+import CreateEntityModal from './create-entity-modal';
+import React from 'react';
+import {addEditionGroup} from '../detail-tab/action';
+import {connect} from 'react-redux';
+import makeImmutable from '../../entity-editor/common/make-immutable';
+import {submitSingleEntity} from '../../entity-editor/submission-section/actions';
+
+
+const ImmutableCreatableAsync = makeImmutable(AsyncCreatable);
+const addEntityAction = {
+ author: addAuthor,
+ editionGroup: addEditionGroup,
+ publisher: addPublisher,
+ series: addSeries,
+ work: addWork
+};
+function SearchEntityCreate(props:SearchEntityCreateProps) {
+ const {type, nextId, onModalOpen, onModalClose, onSubmitEntity, rowId, onOpenCallback, ...rest} = props;
+ const createLabel = React.useCallback((input) => `Create ${type} "${input}"`, [type]);
+ const [showModal, setShowModal] = React.useState(false);
+ const getNewOptionData = React.useCallback((_, label) => ({
+ __isNew__: true,
+ id: nextId,
+ text: label,
+ type
+ }), [type, nextId]);
+ const openModalHandler = React.useCallback((name) => {
+ if (typeof onOpenCallback === 'function') {
+ onOpenCallback();
+ }
+ setShowModal(true);
+ onModalOpen(name);
+ }, [onModalOpen]);
+ const closeModalHandler = React.useCallback(() => {
+ setShowModal(false);
+ onModalClose();
+ }, [onModalClose]);
+ const submitModalHandler = React.useCallback((ev: React.FormEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+ setShowModal(false);
+ onSubmitEntity(rowId);
+ onModalClose();
+ }, [onSubmitEntity, onModalClose, rowId]);
+ // always show `create new entity` Option when user types in the search field
+ const isValidNewOption = React.useCallback((input) => input.length > 0, []);
+ return (
+ <>
+
+
+ >);
+}
+SearchEntityCreate.defaultProps = {
+ bbid: null,
+ empty: true,
+ error: false,
+ filters: [],
+ languageOptions: [],
+ rowId: null,
+ tooltipText: null
+};
+
+function mapDispatchToProps(dispatch, {type, submissionUrl, onAddCallback}):SearchEntityCreateDispatchProps {
+ return {
+ onModalClose: () => dispatch(loadEdition()) && dispatch(closeEntityModal()),
+ onModalOpen: (name) => {
+ dispatch(dumpEdition(type));
+ dispatch(updateNameField(name));
+ dispatch(updateSortNameField(name));
+ dispatch(checkIfNameExists(name, null, type, null));
+ dispatch(searchName(name, null, type));
+ dispatch(openEntityModal());
+ },
+ onSubmitEntity: (arg) => dispatch(submitSingleEntity(submissionUrl, type,
+ (val) => {
+ if (typeof onAddCallback === 'function') {
+ onAddCallback(val);
+ }
+ return addEntityAction[type](val, arg);
+ }))
+ };
+}
+
+export default connect(null, mapDispatchToProps)(SearchEntityCreate);
+
diff --git a/src/client/unified-form/common/single-accordion.tsx b/src/client/unified-form/common/single-accordion.tsx
new file mode 100644
index 0000000000..0a70e63e5e
--- /dev/null
+++ b/src/client/unified-form/common/single-accordion.tsx
@@ -0,0 +1,36 @@
+import {Accordion, Card} from 'react-bootstrap';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import React from 'react';
+import {SingleAccordionProps} from '../interface/type';
+import ValidationLabel from '../../entity-editor/common/validation-label';
+import {faChevronRight} from '@fortawesome/free-solid-svg-icons';
+
+
+export default function SingleAccordion({children, defaultActive, heading, onToggle, isEmpty, isValid}:SingleAccordionProps) {
+ const inputLabel = (
+
+ {heading}
+
+ );
+ return (
+
+
+
+
+ {children}
+
+
+ {inputLabel}
+
+
+
+
+ );
+}
+
+SingleAccordion.defaultProps = {
+ defaultActive: false,
+ isEmpty: true,
+ isValid: true,
+ onToggle: null
+};
diff --git a/src/client/unified-form/content-tab/action.ts b/src/client/unified-form/content-tab/action.ts
new file mode 100644
index 0000000000..1a37fbe7cb
--- /dev/null
+++ b/src/client/unified-form/content-tab/action.ts
@@ -0,0 +1,107 @@
+import {Action} from '../interface/type';
+
+// Work Actions
+export const ADD_WORK = 'ADD_WORK';
+export const UPDATE_WORKS = 'UPDATE_WORKS';
+export const REMOVE_WORK = 'REMOVE_WORK';
+export const UPDATE_WORK = 'UPDATE_WORK';
+export const TOGGLE_COPY_AUTHOR_CREDITS = 'TOGGLE_COPY_AUTHOR_CREDITS';
+export const DUPLICATE_WORK = 'DUPLICATE_WORK';
+// Series Actions
+export const ADD_SERIES = 'ADD_SERIES';
+export const REMOVE_SERIES = 'REMOVE_SERIES';
+
+let nextWorkId = 0;
+const nextSeriesId = 0;
+
+/**
+ * Produces an action indicating that new Work should be added in `Works`.
+ *
+ * @param {Object} value - New work state.
+ * @returns {Action} The resulting ADD_WORK action.
+ */
+export function addWork(value = null):Action {
+ return {
+ payload: {id: `w${nextWorkId++}`, value: {checked: true, ...value}},
+ type: ADD_WORK
+ };
+}
+
+/**
+ * Produces an action indicating that a given Work should be removed from `Works`.
+ *
+ * @param {string} id - id of the work to be removed
+ * @returns {Action} The resulting REMOVE_WORK action.
+ */
+export function removeWork(id:string):Action {
+ return {
+ payload: id,
+ type: REMOVE_WORK
+ };
+}
+
+/**
+ * Produces an action indicating that `Works` State should be updated with the new `Works`.
+ *
+ * @param {string} id - id of work to be updated
+ * @param {Object} value - updated work state.
+ * @returns {Action} The resulting UPDATE_WORK action.
+ */
+export function updateWork(id:string, value):Action {
+ return {
+ payload: {id, value},
+ type: UPDATE_WORK
+ };
+}
+
+/**
+ * Produces an action indicating that a Work's checkbox should be toggled in `Works`.
+ *
+ * @param {string} id - id of the work to be toggle
+ * @returns {Action} The resulting TOGGLE_COPY_AUTHOR_CREDITS action.
+ */
+export function toggleCheck(id:string):Action {
+ return {
+ payload: id,
+ type: TOGGLE_COPY_AUTHOR_CREDITS
+ };
+}
+
+/**
+ * Produces an action indicating that a Work need to be copied.
+ *
+ * @param {string} id - id of the work to be copied
+ * @returns {Action} The resulting DUPLICATE_WORK action.
+ */
+export function duplicateWork(id:string):Action {
+ return {
+ payload: id,
+ type: DUPLICATE_WORK
+ };
+}
+
+/**
+ * Produces an action indicating that new Series should be added in `Series`.
+ *
+ * @param {object} value - New series entity state.
+ * @returns {Action} The resulting ADD_SERIES action.
+ */
+export function addSeries(value = null):Action {
+ return {
+ payload: {id: `s${nextSeriesId}`, value},
+ type: ADD_SERIES
+ };
+}
+
+/**
+ * Produces an action indicating that a given Series should be removed from `Series`.
+ *
+ * @param {string} id - id of the series to be removed
+ * @returns {Action} The resulting REMOVE_SERIES action.
+ */
+export function removeSeries(id = `s${nextSeriesId}`):Action {
+ return {
+ payload: id,
+ type: REMOVE_SERIES
+ };
+}
diff --git a/src/client/unified-form/content-tab/content-tab.tsx b/src/client/unified-form/content-tab/content-tab.tsx
new file mode 100644
index 0000000000..40657f841b
--- /dev/null
+++ b/src/client/unified-form/content-tab/content-tab.tsx
@@ -0,0 +1,273 @@
+import * as Bootstrap from 'react-bootstrap/';
+import {ContentTabDispatchProps, ContentTabProps, ContentTabStateProps, State} from '../interface/type';
+import {addBulkSeriesItems, addSeriesItem, removeAllSeriesItems, updateOrderType, updateSeriesType} from '../../entity-editor/series-section/actions';
+import {addSeries, addWork, duplicateWork, removeSeries} from './action';
+import {closeEntityModal, dumpEdition, loadEdition, openEntityModal} from '../action';
+import {forEach, get, map, size, toLower} from 'lodash';
+import CreateEntityModal from '../common/create-entity-modal';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import React from 'react';
+import SearchEntityCreate from '../common/search-entity-create-select';
+import SeriesSection from '../../entity-editor/series-section/series-section';
+import WorkRow from './work-row';
+import {connect} from 'react-redux';
+import {convertMapToObject} from '../../helpers/utils';
+import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
+import {submitSingleEntity} from '../../entity-editor/submission-section/actions';
+
+
+const {Row, Col, FormCheck, OverlayTrigger, FormLabel, Tooltip} = Bootstrap;
+let workSeriesItemId = 0;
+const seriesWorkTypeId = 71;
+function getRelEntity(entity) {
+ return {
+ bbid: get(entity, 'bbid'),
+ defaultAlias: {
+ name: get(entity, 'name')
+ },
+ disambiguation: get(entity, 'disambiguation'),
+ type: get(entity, 'type')
+ };
+}
+function generateRel(workEntity, seriesEntity, attributeSetId?, isAdded = false, isRemoved = false) {
+ return {
+ attributeSetId,
+ attributes: [{attributeType: 1, value: {textValue: null}}, {attributeType: 2, value: {textValue: null}}],
+ isAdded,
+ isRemoved,
+ relationshipType: {
+ id: seriesWorkTypeId,
+ linkPhrase: 'is part of',
+ reverseLinkPhrase: 'contains',
+ sourceEntityType: 'Work',
+ targetEntityType: 'Series'
+ },
+ rowID: `ws${workSeriesItemId++}`,
+ sourceEntity: workEntity,
+ targetEntity: seriesEntity
+ };
+}
+export function ContentTab({works, onChange, onModalClose, onModalOpen, onSeriesChange, series,
+ onAddSeriesItem, onSubmitWork, resetSeries, bulkAddSeriesItems, ...rest}:ContentTabProps) {
+ const [isChecked, setIsChecked] = React.useState(true);
+ const [copyToSeries, setCopyToSeries] = React.useState(false);
+ const toggleCheck = React.useCallback(() => {
+ setIsChecked(!isChecked);
+ }, [isChecked]);
+ function addAllWorks(seriesEntity = series) {
+ const baseEntity = getRelEntity(seriesEntity);
+ const relationships = {};
+ forEach(works, (work) => {
+ const rel = generateRel(getRelEntity(work), baseEntity, null, true);
+ relationships[rel.rowID] = rel;
+ });
+ bulkAddSeriesItems(relationships);
+ }
+ const addWorkItem = React.useCallback((workEntity) => {
+ const baseEntity = getRelEntity(series);
+ const otherEntity = getRelEntity(workEntity);
+ const relationship = generateRel(otherEntity, baseEntity, null, true);
+ onAddSeriesItem(relationship);
+ }, [series, onAddSeriesItem]);
+ const addNewSeriesCallback = React.useCallback((seriesEntity) => {
+ resetSeries(true);
+ addAllWorks(seriesEntity);
+ }, [series, addAllWorks, works]);
+ const addNewWorkCallback = React.useCallback((workEntity) => {
+ if (copyToSeries) {
+ addWorkItem(workEntity);
+ }
+ }, [series, addWorkItem, copyToSeries]);
+ const toggleCopyToSeries = React.useCallback(() => {
+ if (copyToSeries) {
+ resetSeries();
+ }
+ else {
+ addAllWorks();
+ }
+ setCopyToSeries(!copyToSeries);
+ }, [copyToSeries, series, works]);
+ const seriesChangeHandler = React.useCallback((value) => {
+ onSeriesChange(value);
+ addAllWorks(value);
+ }, [onSeriesChange, addAllWorks]);
+ const [showModal, setShowModal] = React.useState(false);
+ const openModalHandler = React.useCallback((id) => {
+ setShowModal(true);
+ onModalOpen(id);
+ }, []);
+ const closeModalHandler = React.useCallback(() => {
+ setShowModal(false);
+ onModalClose();
+ }, []);
+ const submitModalHandler = React.useCallback((ev: React.FormEvent) => {
+ ev.preventDefault();
+ ev.stopPropagation();
+ setShowModal(false);
+ onSubmitWork();
+ onModalClose();
+ }, []);
+ const onChangeHandler = React.useCallback((work:any) => {
+ work.checked = isChecked;
+ if (copyToSeries) {
+ addWorkItem(work);
+ }
+ onChange(work);
+ }, [isChecked, onChange, copyToSeries, series]);
+ const checkToolTip = (
+ This will set the book's Author Credits from the ‘Cover‘ tab as this Work's
+ Author
+ );
+ const seriesSectionProps = {
+ ...rest,
+ entity: {
+ bbid: series?.id
+ },
+ entityType: 'series',
+ hideItemSelect: true
+ };
+ const checkLabel = (
+ <>
+
+ Copy Authors from Author Credit
+
+
+
+
+ >);
+ const seriesWorkLabel = (
+
+ <>
+
+ Add Works to Series
+ This will automatically add each new selected work to series items (if present)}
+ >
+
+
+
+ >
+
+ );
+ const filterSeries = React.useCallback((item) => toLower(item.entityType) === 'work', []);
+ const filters = [filterSeries];
+ return (
+ <>
+
+
Works
+ {map(works, (_, rowId) => )}
+
+
+
+
+
+
+
+
+
+
+
Series
+
You can add all the Works above to an existing or new series if they are part of the
+ same a set or sequence of related Works.
+ Check the checkbox below to add the Works to a Series
+
+
+ {copyToSeries &&
+
+
+
+
+
}
+ {copyToSeries &&
}
+
+ >
+ );
+}
+
+function mapStateToProps(rootState:State):ContentTabStateProps {
+ const worksObj = convertMapToObject(rootState.get('Works'));
+ const seriesObj = convertMapToObject(rootState.getIn(['Series', 's0'], null));
+ return {
+ series: seriesObj,
+ works: worksObj
+ };
+}
+function mapDispatchToProps(dispatch, {submissionUrl}):ContentTabDispatchProps {
+ const type = 'Work';
+ return {
+ bulkAddSeriesItems: (data) => dispatch(addBulkSeriesItems(data)),
+ onAddSeriesItem: (data) => dispatch(addSeriesItem(data, data.rowID)),
+ onChange: (value:any) => dispatch(addWork(value)),
+ onModalClose: () => dispatch(loadEdition()) && dispatch(closeEntityModal()),
+ onModalOpen: (id) => {
+ dispatch(dumpEdition(type));
+ dispatch(duplicateWork(id));
+ dispatch(openEntityModal());
+ },
+ onSeriesChange: (value:any) => {
+ dispatch(addSeries(value));
+ dispatch(removeAllSeriesItems());
+ if (value?.orderingTypeId) {
+ dispatch(updateOrderType(value.orderingTypeId));
+ dispatch(updateSeriesType(value.seriesEntityType));
+ }
+ // add all existing work rels to series items
+ if (value?.relationshipSet) {
+ const relationships = value.relationshipSet.relationships.reduce((obj, rel) => {
+ if (rel.type.id === seriesWorkTypeId) {
+ const workRel = generateRel(getRelEntity(rel.source), getRelEntity(rel.target), rel.attributeSetId);
+ obj[workRel.rowID] = workRel;
+ }
+ return obj;
+ }, {});
+ if (size(relationships) > 0) {
+ dispatch(addBulkSeriesItems(relationships));
+ }
+ }
+ },
+ onSubmitWork: () => dispatch(submitSingleEntity(submissionUrl, 'Work', addWork)),
+ resetSeries: (itemsOnly = false) => dispatch(removeAllSeriesItems()) && !itemsOnly && dispatch(removeSeries())
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ContentTab);
diff --git a/src/client/unified-form/content-tab/reducer.ts b/src/client/unified-form/content-tab/reducer.ts
new file mode 100644
index 0000000000..d08a2d89b2
--- /dev/null
+++ b/src/client/unified-form/content-tab/reducer.ts
@@ -0,0 +1,34 @@
+import {ADD_SERIES, ADD_WORK, REMOVE_SERIES, REMOVE_WORK, TOGGLE_COPY_AUTHOR_CREDITS, UPDATE_WORK, UPDATE_WORKS} from './action';
+import {Action, State} from '../interface/type';
+import Immutable from 'immutable';
+
+
+const initialState = Immutable.Map({});
+
+export function worksReducer(state = initialState, {type, payload}:Action):State {
+ switch (type) {
+ case ADD_WORK:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case UPDATE_WORKS:
+ return Immutable.fromJS(payload);
+ case REMOVE_WORK:
+ return state.delete(payload);
+ case UPDATE_WORK:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case TOGGLE_COPY_AUTHOR_CREDITS:
+ return state.setIn([payload, 'checked'], !state.getIn([payload, 'checked']));
+ default:
+ return state;
+ }
+}
+
+export function seriesReducer(state = initialState, {type, payload}:Action):State {
+ switch (type) {
+ case ADD_SERIES:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case REMOVE_SERIES:
+ return state.delete(payload);
+ default:
+ return state;
+ }
+}
diff --git a/src/client/unified-form/content-tab/work-row.tsx b/src/client/unified-form/content-tab/work-row.tsx
new file mode 100644
index 0000000000..859ea06707
--- /dev/null
+++ b/src/client/unified-form/content-tab/work-row.tsx
@@ -0,0 +1,86 @@
+import * as Bootstrap from 'react-bootstrap/';
+import {WorkRowDispatchProps, WorkRowProps, WorkRowStateProps} from '../interface/type';
+import {removeWork, toggleCheck, updateWork} from './action';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import React from 'react';
+import SearchEntityCreate from '../common/search-entity-create-select';
+import {connect} from 'react-redux';
+import {convertMapToObject} from '../../helpers/utils';
+import {faInfoCircle} from '@fortawesome/free-solid-svg-icons';
+
+
+const {Row, Col, Button, FormCheck, ButtonGroup, Tooltip, OverlayTrigger, FormLabel} = Bootstrap;
+
+function WorkRow({onChange, work, onRemove, onToggle, onCopyHandler, rowId, ...rest}:WorkRowProps) {
+ const isChecked = work?.checked;
+ const handleCopy = React.useCallback(() => onCopyHandler(rowId), [onCopyHandler, work]);
+ const onChangeHandler = React.useCallback((value:any) => {
+ value.checked = isChecked;
+ onChange(value);
+ }, [isChecked, onChange]);
+ const checkToolTip = (
+ This will set the book's Author Credits from the ‘Cover‘ tab as this Work's
+ Author
+ );
+ const checkLabel = (
+ <>
+
+ Copy Authors from Author Credit
+
+
+
+
+ >);
+ return (
+
+
+
+
+
+
+
+ Duplicate
+ Remove
+
+
+
+
+
+ );
+}
+
+function mapStateToProps(state, {rowId}) {
+ return {
+ work: convertMapToObject(state.getIn(['Works', rowId]))
+ };
+}
+
+
+function mapDispatchToProps(dispatch, {rowId}) {
+ return {
+ onChange: (value:any) => dispatch(updateWork(rowId, value)),
+ onRemove: () => dispatch(removeWork(rowId)),
+ onToggle: () => dispatch(toggleCheck(rowId))
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(WorkRow);
diff --git a/src/client/unified-form/controller.js b/src/client/unified-form/controller.js
new file mode 100644
index 0000000000..a94d8f9061
--- /dev/null
+++ b/src/client/unified-form/controller.js
@@ -0,0 +1,65 @@
+import * as helpers from './helpers';
+import {applyMiddleware, compose, createStore} from 'redux';
+import {
+ extractChildProps,
+ extractLayoutProps
+} from '../helpers/props';
+import {AppContainer} from 'react-hot-loader';
+import Immutable from 'immutable';
+import Layout from '../containers/layout';
+import {Provider} from 'react-redux';
+import React from 'react';
+import ReactDOM from 'react-dom';
+import ReduxThunk from 'redux-thunk';
+import UnifiedForm from './unified-form';
+import createDebounce from 'redux-debounce';
+import {validateForm as validateEditionForm} from '../entity-editor/validators/edition';
+
+
+const {
+ createRootReducer, shouldDevToolsBeInjected
+} = helpers;
+
+const KEYSTROKE_DEBOUNCE_TIME = 250;
+
+const propsTarget = document.getElementById('props');
+const props = propsTarget ? JSON.parse(propsTarget.innerHTML) : {};
+const {initialState, ...rest} = props;
+
+
+const rootReducer = createRootReducer();
+const debouncer = createDebounce({keystroke: KEYSTROKE_DEBOUNCE_TIME});
+const composeEnhancers = shouldDevToolsBeInjected() ?
+ window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ : compose;
+
+function getEntityEditor() {
+ return ;
+}
+
+const store = createStore(
+ rootReducer,
+ Immutable.fromJS(initialState),
+ composeEnhancers(applyMiddleware(debouncer, ReduxThunk))
+);
+
+const markup = (
+
+
+
+ {getEntityEditor()}
+
+
+
+);
+
+ReactDOM.hydrate(markup, document.getElementById('target'));
+
+/*
+ * As we are not exporting a component,
+ * we cannot use the react-hot-loader module wrapper,
+ * but instead directly use webpack Hot Module Replacement API
+ */
+
+if (module.hot) {
+ module.hot.accept();
+}
diff --git a/src/client/unified-form/cover-tab/action.ts b/src/client/unified-form/cover-tab/action.ts
new file mode 100644
index 0000000000..12e9eadb1e
--- /dev/null
+++ b/src/client/unified-form/cover-tab/action.ts
@@ -0,0 +1,121 @@
+import {Action} from '../interface/type';
+
+
+export const UPDATE_ISBN_VALUE = 'UPDATE_ISBN_VALUE';
+export const ADD_AUTHOR = 'ADD_AUTHOR';
+export const UPDATE_ISBN_TYPE = 'UPDATE_ISBN_TYPE';
+export const ADD_PUBLISHER = 'ADD_PUBLISHER';
+export const CLEAR_PUBLISHER = 'CLEAR_PUBLISHER';
+export const CLEAR_PUBLISHERS = 'CLEAR_PUBLISHERS';
+export const CLEAR_AUTHOR = 'CLEAR_AUTHOR';
+export const AUTO_ISBN = 'AUTO_ISBN';
+
+let nextPublisherId = 0;
+let nextAuthorId = 0;
+
+/**
+ * Produces an action indicating that new Publisher should be added in `Publishers`.
+ *
+ * @param {Object} value - New publisher state.
+ * @returns {Action} The resulting ADD_PUBLISHER action.
+ */
+export function addPublisher(value = null):Action {
+ return {
+ payload: {id: `p${nextPublisherId++}`, value},
+ type: ADD_PUBLISHER
+ };
+}
+
+/**
+ * Produces an action indicating that newly created
+ * Publisher should be removed from `Publishers`.
+ *
+ * @param {string} pid - Publisher id to be removed.
+ * @returns {Action} The resulting CLEAR_PUBLISHER action.
+ */
+export function clearPublisher(pid:string):Action {
+ return {
+ payload: pid,
+ type: CLEAR_PUBLISHER
+ };
+}
+
+/**
+ * Produces an action indicating that all Publishers should be removed from `Publishers`.
+ *
+ * @returns {Action} The resulting CLEAR_PUBLISHERS action.
+ */
+export function clearPublishers():Action {
+ return {
+ type: CLEAR_PUBLISHERS
+ };
+}
+
+/**
+ * Produces an action indicating that newly created
+ * Author should be removed from `Authors`.
+ *
+ * @param {string}aid - Author id to be removed.
+ * @returns {Action} The resulting CLEAR_AUTHOR action.
+ */
+export function clearAuthor(aid:string):Action {
+ return {
+ payload: aid,
+ type: CLEAR_AUTHOR
+ };
+}
+
+/**
+ * Produces an action indicating that new Author should be added in `Authors`
+ * as well as in AC of Edition.
+ *
+ * @param {object} value - New author credit state.
+ * @param {string} rowId - Row id of author credit editor.
+ * @returns {Action} The resulting ADD_AUTHOR action.
+ */
+export function addAuthor(value = null, rowId:string):Action {
+ return {
+ payload: {id: `a${nextAuthorId++}`, rowId, value},
+ type: ADD_AUTHOR
+ };
+}
+
+/**
+ * Produces an action indicating that `ISBN` value should be updated.
+ *
+ * @param {string} newValue - New value of ISBN Field.
+ * @returns {Action} The resulting UPDATE_ISBN_VALUE action.
+ */
+export function debouncedUpdateISBNValue(newValue: string): Action {
+ return {
+ meta: {debounce: 'keystroke'},
+ payload: newValue,
+ type: UPDATE_ISBN_VALUE
+ };
+}
+
+/**
+ * Produces an action indicating that `ISBN` type should be updated.
+ *
+ * @param {number} typeId - Type of corresponding ISBN value.
+ * @returns {Action} The resulting UPDATE_ISBN_TYPE action.
+ */
+export function updateISBNType(typeId:number) {
+ return {
+ payload: typeId,
+ type: UPDATE_ISBN_TYPE
+ };
+}
+
+/**
+ * Produces an action indicating that `autoISBN` value should be updated.
+ *
+ * @param {boolean} value - New value for autoISBN.
+ * @returns {Action} The resulting AUTO_ISBN action.
+ */
+export function updateAutoISBN(value:boolean):Action {
+ return {
+ payload: value,
+ type: AUTO_ISBN
+ };
+}
diff --git a/src/client/unified-form/cover-tab/cover-tab.tsx b/src/client/unified-form/cover-tab/cover-tab.tsx
new file mode 100644
index 0000000000..e3d7907938
--- /dev/null
+++ b/src/client/unified-form/cover-tab/cover-tab.tsx
@@ -0,0 +1,77 @@
+import {Col, Row} from 'react-bootstrap';
+import {CoverDispatchProps, CoverProps, CoverStateProps, EntitySelect, State} from '../interface/type';
+import {clearPublisher, clearPublishers} from './action';
+import AuthorCreditSection from '../../entity-editor/author-credit-editor/author-credit-section';
+import ButtonBar from '../../entity-editor/button-bar/button-bar';
+import ISBNField from './isbn-field';
+import IdentifierEditor from '../../entity-editor/identifier-editor/identifier-editor';
+import NameSection from '../../entity-editor/name-section/name-section';
+import React from 'react';
+import SearchEntityCreate from '../common/search-entity-create-select';
+import {connect} from 'react-redux';
+import {convertMapToObject} from '../../helpers/utils';
+import {updatePublisher} from '../../entity-editor/edition-section/actions';
+
+
+export function CoverTab(props:CoverProps) {
+ const {publisherValue: publishers, onPublisherChange, identifierEditorVisible,
+ onClearPublisher, handleClearPublishers, modalIsOpen, ...rest} = props;
+ const publisherValue:EntitySelect[] = Object.values(convertMapToObject(publishers ?? {}));
+ const onChangeHandler = React.useCallback((value:EntitySelect[], action) => {
+ if (['remove-value', 'pop-value'].includes(action.action)) {
+ if (action.removedValue.__isNew__) {
+ onClearPublisher(action.removedValue.id);
+ }
+ }
+ if (action.action === 'clear') {
+ handleClearPublishers();
+ }
+ onPublisherChange(value);
+ }, []);
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+function mapStateToProps(rootState:State):CoverStateProps {
+ return {
+ identifierEditorVisible: rootState.getIn(['buttonBar', 'identifierEditorVisible']),
+ modalIsOpen: rootState.get('entityModalIsOpen', false),
+ publisherValue: rootState.getIn(['editionSection', 'publisher'], {})
+ };
+}
+
+function mapDispatchToProps(dispatch):CoverDispatchProps {
+ return {
+ handleClearPublishers: () => dispatch(clearPublishers()),
+ onClearPublisher: (arg) => dispatch(clearPublisher(arg)),
+ onPublisherChange: (value) => dispatch(updatePublisher(Object.fromEntries(value.map((pub, index) => [index, pub]))))
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(CoverTab);
diff --git a/src/client/unified-form/cover-tab/isbn-field.tsx b/src/client/unified-form/cover-tab/isbn-field.tsx
new file mode 100644
index 0000000000..cfe4d4dfb3
--- /dev/null
+++ b/src/client/unified-form/cover-tab/isbn-field.tsx
@@ -0,0 +1,89 @@
+import {ISBNDispatchProps, ISBNProps, ISBNStateProps, RInputEvent, State} from '../interface/type';
+import {debouncedUpdateISBNValue, updateAutoISBN, updateISBNType} from './action';
+import {isbn10To13, isbn13To10} from '../../../common/helpers/utils';
+import {FormCheck} from 'react-bootstrap';
+import NameField from '../../entity-editor/common/name-field';
+import React from 'react';
+import {addOtherISBN} from '../../entity-editor/identifier-editor/actions';
+import {connect} from 'react-redux';
+
+
+export function ISBNField(props:ISBNProps) {
+ const {value, type, onChange, autoISBN, onAutoISBNChange} = props;
+ const onChangeHandler = React.useCallback((event:RInputEvent) => onChange(event.target.value, autoISBN), [onChange, autoISBN]);
+ const onAutoISBNChangeHandler = React.useCallback(
+ (event:RInputEvent) => onAutoISBNChange(event.target.checked) && onChange(value, event.target.checked), [onAutoISBNChange, onChange, value]
+ );
+ let checkboxLabel = 'Automatically add ISBN';
+ if (type) {
+ checkboxLabel += type === 10 ? `13 (${isbn10To13(value)})` : `10 (${isbn13To10(value)})`;
+ }
+ else {
+ checkboxLabel += '?';
+ }
+ return (
+
+
+
+
);
+}
+
+function mapStateToProps(rootState:State):ISBNStateProps {
+ return {
+ autoISBN: rootState.get('autoISBN'),
+ type: rootState.getIn(['ISBN', 'type']),
+ value: rootState.getIn(['ISBN', 'value'])
+ };
+}
+
+function mapDispatchToProps(dispatch):ISBNDispatchProps {
+ function onChange(value:string, autoISBN = false) {
+ const isbn10rgx = new
+ RegExp('^(?:ISBN(?:-10)?:?●)?(?=[0-9X]{10}$|(?=(?:[0-9]+[-●]){3})[-●0-9X]{13}$)[0-9]{1,5}[-●]?[0-9]+[-●]?[0-9]+[-●]?[0-9X]$');
+ const isbn13rgx = new
+ RegExp('^(?:ISBN(?:-13)?:?●)?(?=[0-9]{13}$|(?=(?:[0-9]+[-●]){4})[-●0-9]{17}$)97[89][-●]?[0-9]{1,5}[-●]?[0-9]+[-●]?[0-9]+[-●]?[0-9]$');
+ const otherISBN = {type: null, value: ''};
+ // typeID for ISBN10: 10, ISBN13: 9
+ let type = null;
+ if (isbn10rgx.test(value)) {
+ type = 10;
+ if (autoISBN) {
+ otherISBN.type = 9;
+ otherISBN.value = isbn10To13(value);
+ }
+ }
+ else if (isbn13rgx.test(value)) {
+ type = 9;
+ if (autoISBN) {
+ otherISBN.type = 10;
+ otherISBN.value = isbn13To10(value);
+ }
+ }
+ dispatch(updateISBNType(type));
+ if (autoISBN && otherISBN.type) {
+ dispatch(addOtherISBN(otherISBN.type, otherISBN.value));
+ }
+ dispatch(debouncedUpdateISBNValue(value));
+ }
+ return {
+ onAutoISBNChange: (checked:boolean) => dispatch(updateAutoISBN(checked)),
+ onChange
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ISBNField);
diff --git a/src/client/unified-form/cover-tab/reducer.ts b/src/client/unified-form/cover-tab/reducer.ts
new file mode 100644
index 0000000000..6ea7835a0f
--- /dev/null
+++ b/src/client/unified-form/cover-tab/reducer.ts
@@ -0,0 +1,51 @@
+import {ADD_AUTHOR, ADD_PUBLISHER, AUTO_ISBN, CLEAR_AUTHOR, CLEAR_PUBLISHER, CLEAR_PUBLISHERS, UPDATE_ISBN_TYPE, UPDATE_ISBN_VALUE} from './action';
+import Immutable from 'immutable';
+
+
+export function ISBNReducer(state = Immutable.Map({
+ type: null,
+ value: ''
+}), action) {
+ const {payload, type} = action;
+ switch (type) {
+ case UPDATE_ISBN_TYPE:
+ return state.set('type', payload);
+ case UPDATE_ISBN_VALUE:
+ return state.set('value', payload);
+ default:
+ return state;
+ }
+}
+
+export function publishersReducer(state = Immutable.Map({}), {type, payload}) {
+ switch (type) {
+ case ADD_PUBLISHER:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case CLEAR_PUBLISHER:
+ return state.delete(payload);
+ case CLEAR_PUBLISHERS:
+ return Immutable.Map({});
+ default:
+ return state;
+ }
+}
+
+export function authorsReducer(state = Immutable.Map({}), {type, payload}) {
+ switch (type) {
+ case ADD_AUTHOR:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case CLEAR_AUTHOR:
+ return state.delete(payload);
+ default:
+ return state;
+ }
+}
+
+export function autoISBNReducer(state = false, {type, payload}) {
+ switch (type) {
+ case AUTO_ISBN:
+ return payload;
+ default:
+ return state;
+ }
+}
diff --git a/src/client/unified-form/detail-tab/action.ts b/src/client/unified-form/detail-tab/action.ts
new file mode 100644
index 0000000000..b66a0f4073
--- /dev/null
+++ b/src/client/unified-form/detail-tab/action.ts
@@ -0,0 +1,30 @@
+import {Action} from '../interface/type';
+
+
+export const ADD_EDITION_GROUP = 'ADD_EDITION_GROUP';
+export const CLEAR_EDITION_GROUPS = 'CLEAR_EDITION_GROUPS';
+const nextEditionGroupId = 0;
+
+/**
+ * Produces an action indicating that new edition group should be added in `EditionGroups`.
+ *
+ * @param {Object} value - New edition group state.
+ * @returns {Action} The resulting ADD_EDITION_GROUP action.
+ */
+export function addEditionGroup(value = null):Action {
+ return {
+ payload: {id: `eg${nextEditionGroupId}`, value},
+ type: ADD_EDITION_GROUP
+ };
+}
+
+/**
+ * Produces an action indicating that all edition groups should be removed from `EditionGroups`.
+ *
+ * @returns {Action} The resulting CLEAR_EDITION_GROUPS action.
+ */
+export function clearEditionGroups():Action {
+ return {
+ type: CLEAR_EDITION_GROUPS
+ };
+}
diff --git a/src/client/unified-form/detail-tab/detail-tab.tsx b/src/client/unified-form/detail-tab/detail-tab.tsx
new file mode 100644
index 0000000000..5ad552beaf
--- /dev/null
+++ b/src/client/unified-form/detail-tab/detail-tab.tsx
@@ -0,0 +1,14 @@
+import AnnotationSection from '../../entity-editor/annotation-section/annotation-section';
+import EditionSection from '../../entity-editor/edition-section/edition-section';
+import React from 'react';
+
+
+export function DetailTab(props) {
+ return (
+ <>
+
+
+ >);
+}
+
+export default DetailTab;
diff --git a/src/client/unified-form/detail-tab/reducer.ts b/src/client/unified-form/detail-tab/reducer.ts
new file mode 100644
index 0000000000..37f9eafdca
--- /dev/null
+++ b/src/client/unified-form/detail-tab/reducer.ts
@@ -0,0 +1,14 @@
+import {ADD_EDITION_GROUP, CLEAR_EDITION_GROUPS} from './action';
+import Immutable from 'immutable';
+
+
+export default function editionGroupsReducer(state = Immutable.Map({}), {type, payload}) {
+ switch (type) {
+ case ADD_EDITION_GROUP:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case CLEAR_EDITION_GROUPS:
+ return Immutable.Map({});
+ default:
+ return state;
+ }
+}
diff --git a/src/client/unified-form/helpers.ts b/src/client/unified-form/helpers.ts
new file mode 100644
index 0000000000..fc38fee763
--- /dev/null
+++ b/src/client/unified-form/helpers.ts
@@ -0,0 +1,280 @@
+import {ADD_AUTHOR, ADD_PUBLISHER} from './cover-tab/action';
+import {Action, State} from './interface/type';
+import {CLOSE_ENTITY_MODAL, DUMP_EDITION, LOAD_EDITION, OPEN_ENTITY_MODAL} from './action';
+import {ISBNReducer, authorsReducer, autoISBNReducer, publishersReducer} from './cover-tab/reducer';
+import {seriesReducer, worksReducer} from './content-tab/reducer';
+import {ADD_EDITION_GROUP} from './detail-tab/action';
+import {DUPLICATE_WORK} from './content-tab/action';
+import Immutable from 'immutable';
+import aliasEditorReducer from '../entity-editor/alias-editor/reducer';
+import annotationSectionReducer from '../entity-editor/annotation-section/reducer';
+import authorCreditEditorReducer from '../entity-editor/author-credit-editor/reducer';
+import authorSectionReducer from '../entity-editor/author-section/reducer';
+import buttonBarReducer from '../entity-editor/button-bar/reducer';
+import {camelCase} from 'lodash';
+import {combineReducers} from 'redux-immutable';
+import {convertMapToObject} from '../helpers/utils';
+import editionGroupSectionReducer from '../entity-editor/edition-group-section/reducer';
+import editionGroupsReducer from './detail-tab/reducer';
+import editionSectionReducer from '../entity-editor/edition-section/reducer';
+import identifierEditorReducer from '../entity-editor/identifier-editor/reducer';
+import nameSectionReducer from '../entity-editor/name-section/reducer';
+import publisherSectionReducer from '../entity-editor/publisher-section/reducer';
+import relationshipSectionReducer from '../entity-editor/relationship-editor/reducer';
+import seriesSectionReducer from '../entity-editor/series-section/reducer';
+import submissionSectionReducer from '../entity-editor/submission-section/reducer';
+import workSectionReducer from '../entity-editor/work-section/reducer';
+
+
+type ReduxWindow = typeof window & {__REDUX_DEVTOOLS_EXTENSION_COMPOSE__: any};
+export function shouldDevToolsBeInjected(): boolean {
+ return Boolean(
+ typeof window === 'object' &&
+ (window as ReduxWindow).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
+ );
+}
+
+function newEditionReducer(state = Immutable.Map({}), action) {
+ const {type, payload} = action;
+ switch (type) {
+ case DUMP_EDITION:
+ return state.set(payload.id, Immutable.fromJS(payload.value));
+ case LOAD_EDITION:
+ return Immutable.Map({});
+ default:
+ return state;
+ }
+}
+function entityModalIsOpenReducer(state = false, action) {
+ const {type} = action;
+ switch (type) {
+ case OPEN_ENTITY_MODAL:
+ return true;
+ case CLOSE_ENTITY_MODAL:
+ return false;
+ default:
+ return state;
+ }
+}
+const initialACState = Immutable.fromJS(
+ {
+ n0: {
+ author: null,
+ automaticJoinPhrase: true,
+ joinPhrase: '',
+ name: ''
+ }
+ }
+);
+const initialState = Immutable.Map({
+ aliasEditor: Immutable.Map({}),
+ annotationSection: Immutable.Map({content: ''}),
+ buttonBar: Immutable.Map({
+ aliasEditorVisible: false,
+ identifierEditorVisible: false
+ }),
+ editionSection: Immutable.Map({
+ authorCreditEditorVisible: false,
+ editionGroupVisible: true,
+ format: null,
+ languages: Immutable.List([]),
+ matchingNameEditionGroups: [],
+ physicalEnable: true,
+ publisher: Immutable.Map({}),
+ releaseDate: '',
+ status: null
+ }),
+ identifierEditor: Immutable.OrderedMap(),
+ nameSection: Immutable.Map({
+ disambiguation: '',
+ exactMatches: [],
+ language: null,
+ name: '',
+ searchResults: [],
+ sortName: ''
+ }),
+ relationshipSection: Immutable.Map({
+ canEdit: true,
+ lastRelationships: null,
+ relationshipEditorProps: null,
+ relationshipEditorVisible: false,
+ relationships: Immutable.OrderedMap()
+ }),
+ submissionSection: Immutable.Map({
+ note: '',
+ submitError: '',
+ submitted: false
+ }),
+ workSection: Immutable.Map({
+ languages: Immutable.List([]),
+ type: null
+ })
+});
+
+// this reducer act as a intermediary between the entity editor reducers and the UF state
+function crossSliceReducer(state:State, action:Action) {
+ const {type} = action;
+ let intermediateState = state;
+ const activeEntityState = {
+ aliasEditor: state.get('aliasEditor'),
+ annotationSection: state.get('annotationSection'),
+ buttonBar: state.get('buttonBar'),
+ identifierEditor: state.get('identifierEditor'),
+ nameSection: state.get('nameSection'),
+ relationshipSection: state.get('relationshipSection'),
+ submissionSection: state.get('submissionSection')
+ };
+ const newEntity = action.payload && action.payload.value;
+ // Designed for single edition entity, will need to be modified to support multiple editions
+ switch (type) {
+ case ADD_AUTHOR:
+ // add new author for AC in edition
+ intermediateState = intermediateState.setIn(['authorCreditEditor', action.payload.rowId, 'author'], Immutable.Map({
+ __isNew__: true,
+ id: newEntity.id,
+ rowId: action.payload.rowId,
+ text: newEntity.text,
+ type: 'Author'
+ }));
+ intermediateState = intermediateState.setIn(
+ ['authorCreditEditor', action.payload.rowId, 'name'], newEntity.text
+ );
+ break;
+ case DUMP_EDITION:
+ // dump/save current edition state to `Editions`
+ action.payload.value = {
+ ...activeEntityState,
+ authorCreditEditor: state.get('authorCreditEditor'),
+ editionSection: state.get('editionSection')
+ };
+ // reset the state after saving
+ intermediateState = intermediateState.merge(initialState);
+ // keep the AC modal open after resetting state
+ intermediateState = intermediateState.setIn(['editionSection', 'authorCreditEditorVisible'],
+ state.getIn(['editionSection', 'authorCreditEditorVisible']));
+ if (action.payload.type && camelCase(action.payload.type) === 'editionGroup') {
+ // only reset AC state if required
+ intermediateState = intermediateState.set('authorCreditEditor', initialACState);
+ }
+ // default alias language of new entity will be same as of edition
+ intermediateState = intermediateState.setIn(['nameSection', 'language'], activeEntityState.nameSection.get('language', null));
+ break;
+ case ADD_EDITION_GROUP:
+ // add new edition group for edition
+ intermediateState = intermediateState.setIn(['editionSection', 'editionGroup'], newEntity);
+ break;
+ case ADD_PUBLISHER: {
+ // add new publisher for edition
+ // set new publisher in edition state as well
+ intermediateState = intermediateState.setIn(
+ ['editionSection', 'publisher', newEntity.id]
+ , Immutable.Map(newEntity)
+ );
+ break;
+ }
+ case DUPLICATE_WORK:
+ // copy work state from `Works`
+ {
+ const fromWork:State = intermediateState.getIn(['Works', action.payload]);
+ const defaultAlias = fromWork.get('defaultAlias');
+ const relationships = fromWork.getIn(['relationshipSet', 'relationships'], null);
+ const identifiers = fromWork.getIn(['identifierSet', 'identifiers'], null);
+ const other:any = {};
+ if (identifiers) {
+ const identifierEditor = identifiers.map((identifier) => Immutable.Map({type: identifier.get('typeId'),
+ value: identifier.get('value')}));
+ other.identifierEditor = identifierEditor;
+ }
+ if (relationships) {
+ const rels = relationships.map((rel, key) => {
+ const relationship = convertMapToObject(rel);
+ relationship.rowId = `t-${key}`;
+ relationship.sourceEntity = {
+ bbid: relationship.source.bbid,
+ defaultAlias: {
+ name: relationship.source.name
+ },
+ type: relationship.source.type
+ };
+ relationship.targetEntity = {
+ bbid: relationship.target.bbid,
+ defaultAlias: {
+ name: relationship.target.name
+ },
+ type: relationship.target.type
+ };
+ relationship.relationshipType = relationship.type;
+ return Immutable.fromJS(relationship);
+ });
+ other.relationshipSection = {
+ canEdit: true,
+ lastRelationships: null,
+ relationshipEditorProps: null,
+ relationshipEditorVisible: false,
+ relationships: rels
+ };
+ }
+ const changedAttributes = Immutable.fromJS({
+ nameSection: {
+ language: defaultAlias.get('languageId'),
+ name: defaultAlias.get('name'),
+ sortName: defaultAlias.get('sortName')
+ },
+ workSection: {
+ type: fromWork.get('typeId')
+ },
+ ...other
+ });
+ intermediateState = intermediateState.merge(changedAttributes);
+ // get rid of properities that are not needed
+ intermediateState = ['text', '__isNew__', 'type', 'id'].reduce((istate, key) => istate.delete(key), intermediateState);
+ break;
+ }
+ case LOAD_EDITION:
+ {
+ // load old edition state from `Editions`
+ intermediateState = intermediateState.merge(intermediateState.getIn(['Editions', action.payload.id]));
+ // check whether the new edition group has been added
+ const newEditionGroup = intermediateState.getIn(['EditionGroups', 'eg0'], null);
+ if (newEditionGroup) {
+ intermediateState = intermediateState.setIn(['editionSection', 'editionGroup'], newEditionGroup);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return intermediateState;
+}
+
+export function createRootReducer() {
+ return (state: Immutable.Map, action) => {
+ // first pass the state to our cross slice reducer to handle UF specific actions.
+ const intermediateState = crossSliceReducer(state, action);
+ return combineReducers({
+ Authors: authorsReducer,
+ EditionGroups: editionGroupsReducer,
+ Editions: newEditionReducer,
+ ISBN: ISBNReducer,
+ Publishers: publishersReducer,
+ Series: seriesReducer,
+ Works: worksReducer,
+ aliasEditor: aliasEditorReducer,
+ annotationSection: annotationSectionReducer,
+ authorCreditEditor: authorCreditEditorReducer,
+ authorSection: authorSectionReducer,
+ autoISBN: autoISBNReducer,
+ buttonBar: buttonBarReducer,
+ editionGroupSection: editionGroupSectionReducer,
+ editionSection: editionSectionReducer,
+ entityModalIsOpen: entityModalIsOpenReducer,
+ identifierEditor: identifierEditorReducer,
+ nameSection: nameSectionReducer,
+ publisherSection: publisherSectionReducer,
+ relationshipSection: relationshipSectionReducer,
+ seriesSection: seriesSectionReducer,
+ submissionSection: submissionSectionReducer,
+ workSection: workSectionReducer
+ })(intermediateState, action);
+ };
+}
diff --git a/src/client/unified-form/interface/type.ts b/src/client/unified-form/interface/type.ts
new file mode 100644
index 0000000000..539200b22c
--- /dev/null
+++ b/src/client/unified-form/interface/type.ts
@@ -0,0 +1,215 @@
+import Immutable from 'immutable';
+
+
+export type RInputEvent = React.ChangeEvent;
+
+export type Action = {
+ type: string,
+ payload?: any,
+ meta?: {
+ debounce?: string
+ }
+};
+
+export type Entity = {
+ __isNew__: boolean,
+ text:string,
+ type:string,
+ id:string
+};
+export type State = Immutable.Map;
+
+export type IdentifierType = {
+ id:number,
+ entityType:string
+};
+type EditionFormat = {
+ label: string,
+ id: number
+};
+
+type EditionStatus = {
+ label: string,
+ id: number
+};
+type LanguageOption = {
+ frequency: number,
+ name: string,
+ id: number
+};
+
+export type SingleAccordionProps = {
+ children: React.ReactNode,
+ defaultActive?: boolean,
+ onToggle?: () => void,
+ isEmpty?: boolean,
+ isValid?: boolean,
+ heading: string
+};
+
+export type UnifiedFormDispatchProps = {
+ onSubmit: (event:React.FormEvent) =>unknown
+};
+export type UnifiedFormStateProps = {
+ contentTabEmpty: boolean,
+ detailTabValid: boolean,
+ detailTabEmpty: boolean,
+ coverTabValid: boolean,
+ coverTabEmpty: boolean,
+ formValid:boolean
+};
+export type UnifiedFormOwnProps = {
+ allIdentifierTypes?:IdentifierType[],
+ languageOptions?:LanguageOption[],
+ validator?:(state:Immutable.Map, ...args) => boolean,
+};
+export type UnifiedFormProps = UnifiedFormOwnProps & UnifiedFormDispatchProps & UnifiedFormStateProps;
+
+export type CoverOwnProps = {
+ languageOptions?: LanguageOption[],
+ editionFormats?:EditionFormat[],
+ identifierTypes:IdentifierType[]
+ editionStatuses?: EditionStatus[]
+};
+export type CoverStateProps = {
+ publisherValue:any[],
+ modalIsOpen:boolean,
+ identifierEditorVisible:boolean
+};
+export type CoverDispatchProps = {
+ onPublisherChange: (arg:any)=>unknown,
+ onClearPublisher: (arg:string)=>unknown,
+ handleClearPublishers: ()=>unknown
+};
+export type CoverProps = CoverOwnProps & CoverStateProps & CoverDispatchProps;
+
+export type ISBNStateProps = {
+ autoISBN: boolean,
+ type:number,
+ value:string
+};
+export type ISBNDispatchProps = {
+ onChange: (...arg)=>unknown,
+ onAutoISBNChange: (arg:boolean)=>unknown,
+};
+export type ISBNProps = ISBNStateProps & ISBNDispatchProps;
+
+export type EntitySelect = {
+ text:string,
+ id:string
+};
+export type ContentTabStateProps = {
+ works:any | any[],
+ series:any | any[]
+};
+export type ContentTabDispatchProps = {
+ onChange:(value:EntitySelect)=>unknown,
+ onAddSeriesItem:(data:any)=>unknown,
+ onSeriesChange:(value:EntitySelect)=>unknown,
+ resetSeries:(itemsOnly?:boolean)=>void,
+ onSubmitWork:()=>unknown,
+ bulkAddSeriesItems:(data)=>unknown,
+ onModalOpen:(arg)=>unknown,
+ onModalClose:()=>unknown,
+};
+export type ContentTabProps = ContentTabStateProps & ContentTabDispatchProps;
+
+export type NavButtonsProps = {
+ onNext:()=>unknown,
+ onBack:()=>unknown,
+ disableBack:boolean,
+ disableNext:boolean
+};
+export type SearchEntityCreateDispatchProps = {
+ onModalOpen:(arg:string)=>unknown,
+ onModalClose:()=>unknown,
+ onSubmitEntity:(arg:string)=>unknown
+};
+
+export type SearchEntityCreateOwnProps = {
+ bbid?:string,
+ onAddCallback?:(...arg)=>unknown,
+ onOpenCallback?:(...arg)=>unknown,
+ empty?:boolean,
+ isClearable?:boolean,
+ isMulti?:boolean,
+ nextId?:string|number,
+ error?:boolean,
+ filters?:Array,
+ label?:string,
+ tooltipText?:string,
+ languageOptions?:Array,
+ value?:Array | EntitySelect
+ type:string,
+ rowId?:string,
+ onChange:(arg, ...optional)=>unknown
+
+};
+export type SearchEntityCreateProps = SearchEntityCreateDispatchProps & SearchEntityCreateOwnProps;
+
+export type EntityModalStateProps = {
+ isNameSectionValid:boolean,
+ isNameSectionEmpty:boolean,
+ isAliasEditorValid:boolean,
+ isIdentifierEditorValid:boolean,
+ isEntitySectionValid:boolean,
+ isAliasEditorEmpty:boolean,
+ isIdentifierEditorEmpty:boolean
+};
+
+export type EntityModalBodyOwnProps = {
+ onModalSubmit:(e)=>unknown,
+ entityType:string,
+ validate:(arg)=>unknown,
+ identifierTypes:IdentifierType[],
+ children?: React.ReactElement
+};
+export type EntityModalDispatchProps = {
+ onAliasClose: () => unknown,
+ onIdentifierClose: () => unknown
+};
+
+export type EntityModalBodyProps = EntityModalDispatchProps & EntityModalBodyOwnProps & EntityModalStateProps;
+
+export type CreateEntityModalOwnProps = {
+ handleClose:() => unknown,
+ allIdentifierTypes?:Array,
+ handleSubmit:(e)=> unknown,
+ type:string,
+ show:boolean
+};
+export type CreateEntityModalProps = CreateEntityModalOwnProps;
+
+export type SummarySectionStateProps = {
+ Authors: Array;
+ EditionGroups: Array;
+ Editions: Array;
+ Publishers: Array;
+ Series: Array;
+ Works: Array;
+};
+export type SummarySectionOwnProps = {
+ languageOptions: any[]
+};
+export type SummarySectionProps = SummarySectionOwnProps & SummarySectionStateProps;
+
+export type WorkRowStateProps = {
+ work: any;
+};
+export type WorkRowDispatchProps = {
+ onChange: (value:any) => void;
+ onRemove: () => void;
+ onToggle: () => void;
+};
+
+export type WorkRowOwnProps = {
+ onCopyHandler:(arg)=>unknown,
+ rowId: string;
+};
+
+export type WorkRowProps = WorkRowStateProps & WorkRowDispatchProps & WorkRowOwnProps;
+
+export type SingleEntityCardProps = {
+ entity:any,
+ languageOptions:any[]
+};
diff --git a/src/client/unified-form/navbutton.tsx b/src/client/unified-form/navbutton.tsx
new file mode 100644
index 0000000000..531ff2f683
--- /dev/null
+++ b/src/client/unified-form/navbutton.tsx
@@ -0,0 +1,24 @@
+import * as Bootstrap from 'react-bootstrap';
+import {faAngleLeft, faAngleRight} from '@fortawesome/free-solid-svg-icons';
+import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
+import {NavButtonsProps} from './interface/type';
+import React from 'react';
+
+
+const {Row, Col, Button} = Bootstrap;
+
+export default function NavButtons({onNext, onBack, disableBack, disableNext}:NavButtonsProps) {
+ return (
+
+
+ Cancel
+
+
+ Back
+
+
+ Next
+
+
+ );
+}
diff --git a/src/client/unified-form/submit-tab/single-entity-card.tsx b/src/client/unified-form/submit-tab/single-entity-card.tsx
new file mode 100644
index 0000000000..88c8290101
--- /dev/null
+++ b/src/client/unified-form/submit-tab/single-entity-card.tsx
@@ -0,0 +1,113 @@
+import {Card} from 'react-bootstrap';
+import React from 'react';
+import {SingleEntityCardProps} from '../interface/type';
+import _ from 'lodash';
+import {dateObjectToISOString} from '../../helpers/utils';
+
+/* eslint-disable sort-keys */
+const BASE_ENTITY = {
+ Name: 'name',
+ Type: 'type',
+ Language: 'defaultAlias.languageId',
+ 'Sort-Name': 'sortName',
+ Disambiguation: 'disambiguation',
+ Annotation: 'annotation.content',
+ 'Edit-Note': 'submissionSection.note'
+
+};
+const ENTITY_FIELDS = {
+ edition: {
+ Name: 'nameSection.name',
+ Language: 'nameSection.language',
+ 'Sort-Name': 'nameSection.sortName',
+ Disambiguation: 'nameSection.disambiguation',
+ Annotation: 'annotationSection.content',
+ format: 'editionSection.format',
+ 'Release-date': 'editionSection.releaseDate',
+ status: 'editionSection.status',
+ 'Edition-Languages': 'editionSection.languages',
+ pages: 'editionSection.pages',
+ width: 'editionSection.width',
+ height: 'editionSection.height',
+ weight: 'editionSection.weight',
+ depth: 'editionSection.depth'
+ },
+ editionGroup: {
+ ...BASE_ENTITY,
+ 'EditionGroup-Type': 'typeId'
+ },
+ author: {
+ ...BASE_ENTITY,
+ Gender: 'genderId',
+ 'Author-Type': 'typeId',
+ 'Begin-Date': 'beginDate',
+ 'Begin-Area': 'beginArea.text',
+ 'Dead?': 'ended',
+ 'End-Date': 'endDate',
+ 'End-Area': 'endArea.text'
+ },
+ publisher: {
+ ...BASE_ENTITY,
+ 'Publihser-Type': 'typeId',
+ 'Begin-Date': 'beginDate',
+ 'Dissolved?': 'ended',
+ 'End-Date': 'endDate'
+
+ },
+ series: {
+ ...BASE_ENTITY,
+ orderType: 'orderingTypeId',
+ 'Series-Items': 'seriesSection.seriesItems',
+ 'series-Type': 'seriesEntityType'
+ },
+ work: {
+ ...BASE_ENTITY,
+ 'Work-Type': 'typeId',
+ 'Work-Languages': 'languages'
+ }
+};
+export default function SingleEntityCard({entity, languageOptions}:SingleEntityCardProps) {
+ const id2LanguageMap = React.useMemo(() => Object.fromEntries(_.map(languageOptions, (option) => [option.id, option.name])), []);
+ // display formatted entity attributes in modal
+ function renderField(path, key) {
+ let fieldVal = _.get(entity, path, '');
+ if (!fieldVal || (fieldVal.length === 0) || key === 'Name') {
+ return;
+ }
+ if (key === 'Language') {
+ fieldVal = id2LanguageMap[fieldVal];
+ }
+ // correctly format multiple languages
+ if (path.includes('languages')) {
+ fieldVal = _.reduce(fieldVal, (acc, next) => `${acc}${acc.length !== 0 ? ',' : ''} ${next.label}`, '');
+ }
+ // correctly format series items
+ if (path.includes('seriesItems')) {
+ fieldVal = _.reduce(
+ fieldVal,
+ (acc, nextVal) => `${acc.length > 0 ? `${acc}, ` : acc}${_.get(nextVal, ['sourceEntity', 'defaultAlias', 'name'], '')}`, ''
+ );
+ }
+ // correctly format date attribute
+ if (path.includes('Date')) {
+ if (typeof fieldVal !== 'string') {
+ if (!fieldVal.day && !fieldVal.month && !fieldVal.year) {
+ return;
+ }
+ fieldVal = dateObjectToISOString(fieldVal);
+ }
+ }
+ // make sure attribute is stringified
+ // eslint-disable-next-line consistent-return
+ return {key} : {typeof fieldVal === 'string' ? fieldVal : JSON.stringify(fieldVal)} ;
+ }
+ const entityFields = ENTITY_FIELDS[_.camelCase(entity.type)] ?? {};
+ return (
+
+ {_.get(entity, entityFields.Name, '')}
+
+ {_.map(entityFields, renderField)}
+
+
+ );
+}
diff --git a/src/client/unified-form/submit-tab/summary.tsx b/src/client/unified-form/submit-tab/summary.tsx
new file mode 100644
index 0000000000..5a87824f89
--- /dev/null
+++ b/src/client/unified-form/submit-tab/summary.tsx
@@ -0,0 +1,88 @@
+import {Badge, ListGroup} from 'react-bootstrap';
+import {Entity, State, SummarySectionProps, SummarySectionStateProps} from '../interface/type';
+import Immutable from 'immutable';
+import React from 'react';
+import SingleEntityCard from './single-entity-card';
+import _ from 'lodash';
+import {connect} from 'react-redux';
+import {convertMapToObject} from '../../helpers/utils';
+
+
+function SummarySection({
+ Publishers,
+ Works,
+ Authors,
+ Series,
+ EditionGroups,
+ languageOptions,
+ Editions
+}: SummarySectionProps) {
+ const createdEntities = {
+ Authors,
+ EditionGroups,
+ Editions,
+ Publishers,
+ Series,
+ Works
+ };
+ function renderEntityGroup(entities: Array) {
+ const newEntities = entities.filter((entity) => entity.__isNew__);
+ if (newEntities.length === 0) {
+ return null;
+ }
+ return (
+
+ {newEntities.map((entity) => ( ))
+ }
+
+ );
+ }
+ return (
+
+
New Entities
+
Below you can see a preview of the entities you are about to create
+ or have created in the process of filling the form. Please verify the information and
+ make any adjustment if necessary before submitting the form
+
+
+ {_.map(createdEntities, renderEntityGroup)}
+
+
+ );
+}
+function getEntitiesArray(state: Immutable.Map): Array {
+ return Object.values(convertMapToObject(state));
+}
+function mapStateToProps(state:State) {
+ let EditionGroups:any[] = getEntitiesArray(state.get('EditionGroups'));
+ const Editions:Entity[] = [{
+ __isNew__: true,
+ id: 'e0',
+ text: state.getIn(['nameSection', 'name']),
+ type: 'Edition',
+ ...convertMapToObject(state)
+ }];
+ if (EditionGroups.length === 0) {
+ EditionGroups = [{...Editions[0]}];
+ EditionGroups[0].name = EditionGroups[0].text;
+ EditionGroups[0].language = EditionGroups[0].nameSection.language;
+ EditionGroups[0].sortName = EditionGroups[0].nameSection.sortName;
+
+ EditionGroups[0].type = 'EditionGroup';
+ EditionGroups[0].id = 'eg0';
+ }
+ // copy global series section to selected series.
+ const Series = getEntitiesArray(state.get('Series').map((series) => series.set('seriesSection', state.getIn(['seriesSection']))));
+ return {
+ Authors: getEntitiesArray(state.get('Authors')),
+ EditionGroups,
+ Editions,
+ Publishers: getEntitiesArray(state.get('Publishers')),
+ Series,
+ Works: getEntitiesArray(state.get('Works'))
+ };
+}
+export default connect(mapStateToProps)(SummarySection);
diff --git a/src/client/unified-form/unified-form.tsx b/src/client/unified-form/unified-form.tsx
new file mode 100644
index 0000000000..5a6af770d1
--- /dev/null
+++ b/src/client/unified-form/unified-form.tsx
@@ -0,0 +1,114 @@
+import * as Boostrap from 'react-bootstrap';
+import {IdentifierType, State, UnifiedFormDispatchProps, UnifiedFormOwnProps, UnifiedFormProps, UnifiedFormStateProps} from './interface/type';
+import {isCoverTabEmpty, validateCoverTab} from './validators/cover-tab';
+import {isDetailTabEmpty, validateDetailTab} from './validators/detail-tab';
+import ContentTab from './content-tab/content-tab';
+import CoverTab from './cover-tab/cover-tab';
+import DetailTab from './detail-tab/detail-tab';
+import NavButtons from './navbutton';
+import React from 'react';
+import SubmitSection from '../entity-editor/submission-section/submission-section';
+import SummarySection from './submit-tab/summary';
+import ValidationLabel from '../entity-editor/common/validation-label';
+import {connect} from 'react-redux';
+import {convertMapToObject} from '../helpers/utils';
+import createFilterOptions from 'react-select-fast-filter-options';
+import {filterIdentifierTypesByEntityType} from '../../common/helpers/utils';
+import {freezeObjects} from './common/freezed-objects';
+import {getUfValidator} from './validators/base';
+import {omit} from 'lodash';
+import {submit} from '../entity-editor/submission-section/actions';
+
+
+const {Tabs, Tab} = Boostrap;
+
+
+export function UnifiedForm(props:UnifiedFormProps) {
+ const {allIdentifierTypes, validator, onSubmit, formValid,
+ languageOptions, contentTabEmpty, coverTabValid, coverTabEmpty, detailTabValid, detailTabEmpty} = props;
+ const rest = omit(props, ['contentTabEmpty', 'coverTabValid', 'coverTabEmpty', 'detailTabValid', 'formValid', 'detailTabEmpty']);
+ React.useMemo(() => {
+ // without this check, it would cause undefined behaviour
+ if (!freezeObjects.filterOptions) {
+ const options = languageOptions.map((language) => ({
+ frequency: language.frequency,
+ label: language.name,
+ value: language.id
+ }));
+ freezeObjects.filterOptions = createFilterOptions({options});
+ Object.freeze(freezeObjects);
+ }
+ }, []);
+ const [tabKey, setTabKey] = React.useState('cover');
+ const editionIdentifierTypes = React.useMemo(() => filterIdentifierTypesByEntityType(allIdentifierTypes, 'Edition'), []);
+ const editionValidator = validator && getUfValidator(validator);
+ const tabKeys = ['cover', 'detail', 'content', 'submit'];
+ const onNextHandler = React.useCallback(() => {
+ const index = tabKeys.indexOf(tabKey);
+ if (index >= 0 && index < tabKeys.length - 1) {
+ setTabKey(tabKeys[index + 1]);
+ }
+ }, [tabKey]);
+ const onBackHandler = React.useCallback(() => {
+ const index = tabKeys.indexOf(tabKey);
+ if (index > 0 && index < tabKeys.length) {
+ setTabKey(tabKeys[index - 1]);
+ }
+ }, [tabKey]);
+ const tabIndex = tabKeys.indexOf(tabKey);
+ const disableNext = tabIndex === tabKeys.length - 1 || ((tabIndex === tabKeys.length - 2) && !formValid);
+ const disableBack = tabIndex === 0;
+ return (
+
+ );
+}
+
+function mapStateToProps(state:State, {allIdentifierTypes}:UnifiedFormOwnProps) {
+ const jsonState = convertMapToObject(state);
+ const editionIdentifierTypes = filterIdentifierTypesByEntityType(allIdentifierTypes, 'Edition');
+ const coverTabEmpty = isCoverTabEmpty(jsonState);
+ const coverTabValid = !coverTabEmpty && validateCoverTab(jsonState, editionIdentifierTypes);
+ const detailTabValid = validateDetailTab(jsonState);
+ return {
+ contentTabEmpty: state.get('Works').size === 0,
+ coverTabEmpty,
+ coverTabValid,
+ detailTabEmpty: isDetailTabEmpty(jsonState),
+ detailTabValid,
+ formValid: coverTabValid && detailTabValid
+ };
+}
+function mapDispatchToProps(dispatch, {submissionUrl}) {
+ return {
+ onSubmit: (event:React.FormEvent) => {
+ event.preventDefault();
+ dispatch(submit(submissionUrl, true));
+ }
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(UnifiedForm);
diff --git a/src/client/unified-form/validators/base.ts b/src/client/unified-form/validators/base.ts
new file mode 100644
index 0000000000..cde516be77
--- /dev/null
+++ b/src/client/unified-form/validators/base.ts
@@ -0,0 +1,18 @@
+import {State} from '../interface/type';
+import {convertMapToObject} from '../../helpers/utils';
+import {get} from 'lodash';
+import {validateISBN} from './cover-tab';
+
+/**
+ * Validate the unified form state
+ *
+ * @param {Function} validator - validator function
+ * @returns {Function} - uf validator function
+ */
+
+export function getUfValidator(validator:any): {(state: State, ...args: any[]): boolean} {
+ return (state, identifierTypes, ...args) => {
+ const jsonState = convertMapToObject(state);
+ return validateISBN(get(jsonState, 'ISBN')) && validator(jsonState, identifierTypes, ...args);
+ };
+}
diff --git a/src/client/unified-form/validators/cover-tab.ts b/src/client/unified-form/validators/cover-tab.ts
new file mode 100644
index 0000000000..6f636ae67e
--- /dev/null
+++ b/src/client/unified-form/validators/cover-tab.ts
@@ -0,0 +1,52 @@
+import {get, size} from 'lodash';
+import {validateAuthorCreditSection, validateIdentifiers, validateNameSection} from '../../entity-editor/validators/common';
+
+/**
+ * Validates the ISBN Field.
+ *
+ * @param {object} isbn - ISBN state object containing `type` and `value`
+ * @returns {boolean} - true if valid, false if invalid
+ */
+export function validateISBN(isbn) {
+ // since type will already be defined for valid ISBNs
+ return !(
+ Boolean(isbn) &&
+ !get(isbn, 'type', null) &&
+ get(isbn, 'value', '').length > 0
+ );
+}
+
+/**
+ * Validates the Cover Tab state.
+ *
+ * @param {object} data - the form state object
+ * @param {Array} identifierTypes - the list of identifier types
+ * @returns {boolean} - true if form state valid, false if invalid
+ */
+export function validateCoverTab(data:any, identifierTypes:any[]) {
+ return validateNameSection(get(data, 'nameSection')) &&
+ validateIdentifiers(get(data, 'identifierEditor', {}), identifierTypes) &&
+ validateAuthorCreditSection(get(data, 'authorCreditEditor')) &&
+ validateISBN(get(data, 'ISBN'));
+}
+
+/**
+ * Check whether Cover Tab is modified or not.
+ *
+ * @param {object} data - the form state object
+ * @returns {boolean} - true if cover tab state empty
+ */
+export function isCoverTabEmpty(data:any) {
+ const nameSection = get(data, 'nameSection', {});
+ const authorCreditEditor = get(data, 'authorCreditEditor', {});
+ const ISBN = get(data, 'ISBN', {});
+ const identifierEditor = get(data, 'identifierEditor', {});
+ return nameSection.name?.length === 0 &&
+ nameSection.sortName?.length === 0 &&
+ !nameSection.language &&
+ nameSection.disambiguation?.length === 0 &&
+ size(authorCreditEditor) === 1 &&
+ !authorCreditEditor.n0?.author &&
+ size(identifierEditor) === 0 &&
+ !ISBN.type;
+}
diff --git a/src/client/unified-form/validators/detail-tab.ts b/src/client/unified-form/validators/detail-tab.ts
new file mode 100644
index 0000000000..076328da4b
--- /dev/null
+++ b/src/client/unified-form/validators/detail-tab.ts
@@ -0,0 +1,37 @@
+import {get} from 'lodash';
+import {validateEditionSection} from '../../entity-editor/validators/edition';
+
+
+export const initialEditionSection = {
+ authorCreditEditorVisible: false,
+ format: null,
+ languages: [],
+ matchingNameEditionGroups: [],
+ physicalEnable: true,
+ publisher: {},
+ releaseDate: '',
+ status: null
+};
+const stringifiedInitialState = JSON.stringify(initialEditionSection);
+
+/**
+ * Validates the Detail Tab state.
+ *
+ * @param {object} data - the form state object
+ * @returns {boolean} - true if detail tab state is valid
+ */
+export function validateDetailTab(data: any): boolean {
+ return validateEditionSection(get(data, 'editionSection'), true);
+}
+
+/**
+ * Check whether Detail Tab is modified or not.
+ *
+ * @param {object} data - the form state object
+ * @returns {boolean} - true if detail tab state is empty
+ */
+export function isDetailTabEmpty(data:any): boolean {
+ const editionSection = get(data, 'editionSection', {});
+ const annotationContent = get(data, ['annotationSection', 'content'], '');
+ return JSON.stringify(editionSection) === stringifiedInitialState && annotationContent.length === 0;
+}
diff --git a/src/common/helpers/search.js b/src/common/helpers/search.js
index 72640671f0..58fc0847a8 100644
--- a/src/common/helpers/search.js
+++ b/src/common/helpers/search.js
@@ -101,9 +101,17 @@ async function _fetchEntityModelsForESResults(orm, results) {
// Regular entity
const model = commonUtils.getEntityModelByType(orm, entityStub.type);
const entity = await model.forge({bbid: entityStub.bbid})
- .fetch({require: false, withRelated: ['defaultAlias.language', 'disambiguation', 'aliasSet.aliases', 'identifierSet.identifiers']});
-
- return entity?.toJSON();
+ .fetch({require: false, withRelated: ['defaultAlias.language', 'disambiguation', 'aliasSet.aliases', 'identifierSet.identifiers',
+ 'relationshipSet.relationships.source', 'relationshipSet.relationships.target', 'relationshipSet.relationships.type', 'annotation']});
+ const entityJSON = entity?.toJSON();
+ if (entityJSON && entityJSON.relationshipSet) {
+ entityJSON.relationshipSet.relationships = await Promise.all(entityJSON.relationshipSet.relationships.map(async (rel) => {
+ rel.source = await commonUtils.getEntityAlias(orm, rel.source.bbid, rel.source.type);
+ rel.target = await commonUtils.getEntityAlias(orm, rel.target.bbid, rel.target.type);
+ return rel;
+ }));
+ }
+ return entityJSON;
})).catch(err => log.error(err));
return processedResults;
}
@@ -121,7 +129,7 @@ async function _searchForEntities(orm, dslQuery) {
return {results: [], total: 0};
}
-async function _bulkIndexEntities(entities) {
+export async function _bulkIndexEntities(entities) {
if (!entities.length) {
return;
}
diff --git a/src/common/helpers/utils.ts b/src/common/helpers/utils.ts
index 6ae59978a8..c5299d8fda 100644
--- a/src/common/helpers/utils.ts
+++ b/src/common/helpers/utils.ts
@@ -1,6 +1,7 @@
-import {Relationship, RelationshipForDisplay} from '../../client/entity-editor/relationship-editor/types';
+import {EntityType, Relationship, RelationshipForDisplay} from '../../client/entity-editor/relationship-editor/types';
-import {isString, kebabCase, toString} from 'lodash';
+import {isString, kebabCase, toString, upperFirst} from 'lodash';
+import {IdentifierType} from '../../client/unified-form/interface/type';
/**
* Regular expression for valid BookBrainz UUIDs (bbid)
@@ -269,19 +270,29 @@ export function isbn13To10(isbn13:string):string | null {
return digits.join('');
}
+export function filterIdentifierTypesByEntityType(
+ identifierTypes: Array<{id: number, entityType: string}>,
+ entityType: string
+): Array {
+ return identifierTypes.filter(
+ (type) => type.entityType === entityType
+ );
+}
/**
*
* @param {Object} orm - orm
* @param {string} bbid - bookbrainz id
+ * @param {Array} otherRelations - entity specific relations to fetch
* @returns {Promise} - Promise resolves to entity data if exist else null
*/
-export async function getEntityByBBID(orm, bbid:string):Promise | null> {
+export async function getEntityByBBID(orm, bbid:string, otherRelations:Array = []):Promise | null> {
if (!isValidBBID(bbid)) {
return null;
}
const {Entity} = orm;
- const entity = await new Entity({bbid}).fetch({require: false});
+ const redirectBbid = await orm.func.entity.recursivelyGetRedirectBBID(orm, bbid, null);
+ const entity = await new Entity({bbid: redirectBbid}).fetch({require: false});
if (!entity) {
return null;
}
@@ -290,9 +301,25 @@ export async function getEntityByBBID(orm, bbid:string):Promise {
+ if (!isValidBBID(bbid)) {
+ return null;
+ }
+ const entityData = await orm.func.entity.getEntity(orm, upperFirst(type), bbid, []);
+ return entityData;
+}
+
+export function filterObject(obj, filter) {
+ return Object.keys(obj)
+ .filter((key) => filter(obj[key]))
+ .reduce((res, key) => Object.assign(res, {[key]: obj[key]}), {});
+}
diff --git a/src/server/helpers/achievement.js b/src/server/helpers/achievement.js
index c8723d45e1..9c6bd1fb9f 100644
--- a/src/server/helpers/achievement.js
+++ b/src/server/helpers/achievement.js
@@ -23,6 +23,7 @@
import * as error from '../../common/helpers/error';
+import {differenceInCalendarDays, parseISO} from 'date-fns';
import {flattenDeep, isNil} from 'lodash';
import log from 'log';
@@ -386,14 +387,14 @@ async function processSprinter(orm, editorId) {
}
/**
- * Gets number of distinct days the editor created revisions within specified
+ * Gets number of consecutive days from today the editor created revisions upto a specified
* time limit
* @param {object} orm - the BookBrainz ORM, initialized during app setup
* @param {int} editorId - Editor to query on
* @param {int} days - Number of days before today to collect edits from
- * @returns {int} - Number of days edits were performed on
+ * @returns {int} - Number of consecutive days edits were performed on
*/
-export async function getEditsInDays(orm, editorId, days) {
+export async function getConsecutiveDaysWithEdits(orm, editorId, days) {
const {bookshelf} = orm;
const rawSql =
`SELECT DISTINCT created_at::date from bookbrainz.revision \
@@ -401,11 +402,22 @@ export async function getEditsInDays(orm, editorId, days) {
and created_at > (SELECT CURRENT_DATE - INTERVAL '${days} days');`;
const out = await bookshelf.knex.raw(rawSql);
- return out.rowCount;
+ const dateList = out.rows.map(row => parseISO(row.created_at));
+ let countDays = 0;
+ let lastDate = new Date();
+ // count number of consecutive days starting from today
+ for (let i = dateList.length - 1; i >= 0; i--) {
+ if (differenceInCalendarDays(lastDate, dateList[i]) > 1) {
+ break;
+ }
+ countDays++;
+ lastDate = dateList[i];
+ }
+ return countDays;
}
async function processFunRunner(orm, editorId) {
- const rowCount = await getEditsInDays(orm, editorId, 6);
+ const rowCount = await getConsecutiveDaysWithEdits(orm, editorId, 6);
const tiers = [
{
name: 'Fun Runner',
@@ -417,7 +429,7 @@ async function processFunRunner(orm, editorId) {
}
async function processMarathoner(orm, editorId) {
- const rowCount = await getEditsInDays(orm, editorId, 29);
+ const rowCount = await getConsecutiveDaysWithEdits(orm, editorId, 29);
const tiers = [{
name: 'Marathoner',
threshold: 30,
diff --git a/src/server/helpers/critiquebrainz.ts b/src/server/helpers/critiquebrainz.ts
new file mode 100644
index 0000000000..221e8ce7da
--- /dev/null
+++ b/src/server/helpers/critiquebrainz.ts
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2022 Ansh Goyal
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* eslint-disable camelcase */
+
+import config from '../../common/helpers/config';
+import log from 'log';
+import request from 'superagent';
+
+
+const OAUTH_AUTHORIZE_URL = 'https://critiquebrainz.org/oauth/authorize';
+const OAUTH_TOKEN_URL = 'https://critiquebrainz.org/ws/1/oauth/token';
+const REVIEW_URL = 'https://critiquebrainz.org/ws/1/review/';
+const critiquebrainzScopes = ['review'];
+const cbConfig = config.critiquebrainz;
+
+const mapEntityType = {
+ Author: 'bb_author',
+ EditionGroup: 'bb_edition_group',
+ Series: 'bb_series',
+ Work: 'bb_literary_work'
+};
+
+export async function addNewUser(
+ editorId: number,
+ token: Record,
+ orm: Record
+): Promise {
+ const expires = Math.floor(new Date().getTime() / 1000.0) + token.tokenExpires;
+
+ try {
+ const newUser = await orm.func.externalServiceOauth.saveOauthToken(
+ editorId,
+ 'critiquebrainz',
+ token.accessToken,
+ token.refreshToken,
+ expires,
+ critiquebrainzScopes,
+ orm
+ );
+ return newUser;
+ }
+ catch (error) {
+ log.error(error);
+ return null;
+ }
+}
+
+
+export function getAuthURL(): string {
+ const authURL = new URL(OAUTH_AUTHORIZE_URL);
+ authURL.searchParams.set('client_id', cbConfig.clientID);
+ authURL.searchParams.set('redirect_uri', cbConfig.callbackURL);
+ authURL.searchParams.set('response_type', 'code');
+ authURL.searchParams.set('scope', critiquebrainzScopes.join(','));
+ return authURL.href;
+}
+
+
+export async function fetchAccessToken(
+ code: string,
+ editorId: number,
+ orm: Record
+) : Promise {
+ try {
+ const data = await request.post(OAUTH_TOKEN_URL)
+ .type('form')
+ .send({
+ client_id: cbConfig.clientID,
+ client_secret: cbConfig.clientSecret,
+ code,
+ grant_type: 'authorization_code',
+ redirect_uri: cbConfig.callbackURL
+ });
+
+ const token = await data.body;
+ const expires = Math.floor(new Date().getTime() / 1000.0) + token.expires_in;
+ const newUser = await orm.func.externalServiceOauth.saveOauthToken(
+ editorId,
+ 'critiquebrainz',
+ token.access_token,
+ token.refresh_token,
+ expires,
+ critiquebrainzScopes,
+ orm
+ );
+ return newUser;
+ }
+ catch (error) {
+ log.error(error.response?.error);
+ return null;
+ }
+}
+
+
+export async function refreshAccessToken(
+ editorId: number,
+ refreshToken: string,
+ orm: Record
+): Promise {
+ try {
+ const data = await request.post(OAUTH_TOKEN_URL)
+ .type('form')
+ .send({
+ client_id: cbConfig.clientID,
+ client_secret: cbConfig.clientSecret,
+ grant_type: 'refresh_token',
+ redirect_uri: cbConfig.callbackURL,
+ refresh_token: refreshToken
+ });
+ const token = await data.body;
+ const expires = Math.floor(new Date().getTime() / 1000.0) + token.expires_in;
+ const updatedToken = await orm.func.externalServiceOauth.updateOauthToken(
+ editorId,
+ 'critiquebrainz',
+ token.access_token,
+ token.refresh_token,
+ expires,
+ orm
+ );
+ return updatedToken;
+ }
+ catch (error) {
+ log.error(error.response?.error);
+ return null;
+ }
+}
+
+
+export async function getReviewsFromCB(bbid: string,
+ entityType: string): Promise {
+ const cbEntityType = mapEntityType[entityType];
+ if (!cbEntityType) {
+ return {reviews: [], successfullyFetched: true};
+ }
+ try {
+ const res = await request
+ .get(REVIEW_URL)
+ .query({
+ entity_id: bbid,
+ entity_type: cbEntityType,
+ limit: 3,
+ offset: 0
+ });
+ return {reviews: res.body, successfullyFetched: true};
+ }
+ catch (err) {
+ log.error(err.response?.error);
+ return {reviews: [], successfullyFetched: false};
+ }
+}
+
+export async function submitReviewToCB(
+ accessToken: string,
+ review: Record
+): Promise {
+ const cbEntityType = mapEntityType[review.entityType];
+
+ if (!cbEntityType) {
+ return {
+ message: 'Entity type not supported',
+ reviewID: null,
+ successfullySubmitted: false
+ };
+ }
+
+ try {
+ const res = await request
+ .post(REVIEW_URL)
+ .set('Content-Type', 'application/json')
+ .auth(accessToken, {type: 'bearer'})
+ .send({
+ entity_id: review.entityBBID,
+ entity_type: cbEntityType,
+ language: review.language,
+ license_choice: 'CC BY-SA 3.0',
+ rating: review.rating,
+ text: review.textContent
+ });
+ return {
+ error: null,
+ message: res.body.message,
+ reviewID: res.body.id,
+ successfullySubmitted: true
+ };
+ }
+
+ catch (error) {
+ return {
+ error: error.response?.body?.error,
+ message: error.response?.body?.description,
+ reviewID: null,
+ successfullySubmitted: false
+ };
+ }
+}
diff --git a/src/server/helpers/entityRouteUtils.tsx b/src/server/helpers/entityRouteUtils.tsx
index c8cb5f233a..a05f97be03 100644
--- a/src/server/helpers/entityRouteUtils.tsx
+++ b/src/server/helpers/entityRouteUtils.tsx
@@ -19,25 +19,30 @@
import * as Immutable from 'immutable';
import * as React from 'react';
+import * as UnifiedFormHelpers from '../../client/unified-form/helpers';
import * as entityEditorHelpers from '../../client/entity-editor/helpers';
import * as entityRoutes from '../routes/entity/entity';
import * as error from '../../common/helpers/error';
import * as propHelpers from '../../client/helpers/props';
+import * as unifiedRoutes from '../routes/entity/process-unified-form';
import * as utils from './utils';
import type {Request as $Request, Response as $Response} from 'express';
+import {filterIdentifierTypesByEntityType, isValidBBID} from '../../common/helpers/utils';
import EntityEditor from '../../client/entity-editor/entity-editor';
import EntityMerge from '../../client/entity-editor/entity-merge';
import Layout from '../../client/containers/layout';
import {Provider} from 'react-redux';
import ReactDOMServer from 'react-dom/server';
+import UnifiedForm from '../../client/unified-form/unified-form';
import _ from 'lodash';
import {createStore} from 'redux';
import {generateProps} from './props';
+const {createRootReducer: ufCreateRootReducer} = UnifiedFormHelpers;
const {createRootReducer, getEntitySection, getEntitySectionMerge, getValidator} = entityEditorHelpers;
-
+const validEntityTypes = ['author', 'edition', 'editionGroup', 'publisher', 'series', 'work'];
type EntityAction = 'create' | 'edit';
type PassportRequest = $Request & {user: any, session: any};
@@ -69,7 +74,7 @@ export function generateEntityProps(
const getFilteredIdentifierTypes = isEdit ?
_.partialRight(utils.filterIdentifierTypesByEntity, entity) :
- _.partialRight(utils.filterIdentifierTypesByEntityType, entityName);
+ _.partialRight(filterIdentifierTypesByEntityType, entityName);
const filteredIdentifierTypes = getFilteredIdentifierTypes(
res.locals.identifierTypes
);
@@ -296,6 +301,7 @@ export function addInitialRelationship(props, relationshipTypeId, relationshipIn
const initialRelationship = {
attributes: [],
+ isAdded: true,
label: relationship.linkPhrase,
relationshipType: relationship,
rowID: rowId,
@@ -315,3 +321,107 @@ export function addInitialRelationship(props, relationshipTypeId, relationshipIn
return props;
}
+
+/**
+ * Return markup for the unified form.
+ * This also modifies the props value with a new initialState!
+ * @param {object} props - react props
+ * @returns {object} - Updated props and HTML string with markup
+ */
+
+export function unifiedFormMarkup(props) {
+ const {initialState, ...rest} = props;
+ const rootReducer = ufCreateRootReducer();
+ const store:any = createStore(rootReducer, Immutable.fromJS(initialState));
+ const markup = ReactDOMServer.renderToString(
+
+
+
+
+
+ );
+ return {
+ markup,
+ props: Object.assign({}, props, {initialState: store.getState()})
+ };
+}
+
+/**
+ * Returns a props object with reasonable defaults for unified form.
+ * @param {request} req - request object
+ * @param {response} res - response object
+ * @param {object} additionalProps - additional props
+ * @param {initialStateCallback} initialStateCallback - callback
+ * to get the initial state
+ * @returns {object} - props
+ */
+export function generateUnifiedProps(
+ req: PassportRequest, res: $Response,
+ additionalProps: any,
+ initialStateCallback: () => any = () => new Object()
+): any {
+ const submissionUrl = '/create/handler';
+ const props = Object.assign({
+ allIdentifierTypes: res.locals.identifierTypes,
+ entityType: 'edition',
+ initialState: initialStateCallback(),
+ isUnifiedForm: true,
+ languageOptions: res.locals.languages,
+ requiresJS: true,
+ submissionUrl
+ }, additionalProps);
+
+ return generateProps(req, res, props);
+}
+
+/**
+ * Validate Unified form
+ * @param {object} body - request body
+ * @returns {boolean}
+ */
+
+function validateUnifiedForm(body:Record):boolean {
+ for (const entityKey in body) {
+ if (Object.prototype.hasOwnProperty.call(body, entityKey)) {
+ const entityForm = body[entityKey];
+ const entityType = _.camelCase(entityForm.type);
+ const isNew = _.get(entityForm, '__isNew__', true);
+ const bbid = _.get(entityForm, 'id', null);
+ // for existing entity, it must have id attribute set to its bbid
+ if (!isNew && (!bbid || !isValidBBID(bbid))) {
+ return false;
+ }
+ if (!entityType || !validEntityTypes.includes(entityType)) {
+ return false;
+ }
+ const validator = getValidator(entityType);
+ if (isNew && !validator(entityForm)) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Middleware for handling unified form submission
+ * @param {object} req - Request object
+ * @param {object} res - Response object
+ */
+
+export async function createEntitiesHandler(
+ req:$Request,
+ res:$Response
+) {
+ const {orm} = req.app.locals;
+ // generate the state for current entity
+ req.body = await unifiedRoutes.preprocessForm(req.body, orm);
+ // validating the uf state
+ if (!validateUnifiedForm(req.body)) {
+ const err = new error.FormSubmissionError();
+ return error.sendErrorAsJSON(res, err);
+ }
+ // transforming uf state into separate entity state
+ req.body = unifiedRoutes.transformForm(req.body);
+ return unifiedRoutes.handleCreateMultipleEntities(req as PassportRequest, res);
+}
diff --git a/src/server/helpers/middleware.ts b/src/server/helpers/middleware.ts
index 0c6e90f786..688439caf3 100644
--- a/src/server/helpers/middleware.ts
+++ b/src/server/helpers/middleware.ts
@@ -2,6 +2,7 @@
* Copyright (C) 2015 Ben Ockmore
* 2015-2016 Sean Burke
* 2021 Akash Gupta
+ * 2022 Ansh Goyal
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
@@ -20,11 +21,11 @@
import * as commonUtils from '../../common/helpers/utils';
import * as error from '../../common/helpers/error';
import * as utils from '../helpers/utils';
-
import type {Response as $Response, NextFunction, Request} from 'express';
import _ from 'lodash';
import {getRelationshipTargetBBIDByTypeId} from '../../client/helpers/entity';
+import {getReviewsFromCB} from './critiquebrainz';
interface $Request extends Request {
@@ -121,6 +122,44 @@ export async function loadWorkTableAuthors(req: $Request, res: $Response, next:
return next();
}
+/**
+ * Add the relationships on entity object.
+ *
+ * @param {Object} entity - The entity to load the relationships for.
+ * @param {Object} relationshipSet - The RelationshipSet model.
+ * @param {Object} orm - The ORM instance.
+ * @returns
+ */
+
+export function addRelationships(entity, relationshipSet, orm) {
+ async function getEntityWithAlias(relEntity) {
+ const redirectBbid = await orm.func.entity.recursivelyGetRedirectBBID(orm, relEntity.bbid, null);
+ const model = commonUtils.getEntityModelByType(orm, relEntity.type);
+
+ return model.forge({bbid: redirectBbid})
+ .fetch({require: false, withRelated: ['defaultAlias'].concat(utils.getAdditionalRelations(relEntity.type))});
+ }
+ entity.relationships = relationshipSet ?
+ relationshipSet.related('relationships').toJSON() : [];
+
+ utils.attachAttributes(entity.relationships);
+
+
+ /**
+ * Source and target are generic Entity objects, so until we have
+ * a good way of polymorphically fetching the right specific entity,
+ * we need to fetch default alias in a somewhat sketchier way.
+ */
+ return Promise.all(entity.relationships.map((relationship) =>
+ Promise.all([getEntityWithAlias(relationship.source), getEntityWithAlias(relationship.target)])
+ .then(([source, target]) => {
+ relationship.source = source.toJSON();
+ relationship.target = target.toJSON();
+
+ return relationship;
+ })));
+}
+
export function loadEntityRelationships(req: $Request, res: $Response, next: NextFunction) {
const {orm}: any = req.app.locals;
const {RelationshipSet} = orm;
@@ -146,34 +185,7 @@ export function loadEntityRelationships(req: $Request, res: $Response, next: Nex
]
})
)
- .then((relationshipSet) => {
- entity.relationships = relationshipSet ?
- relationshipSet.related('relationships').toJSON() : [];
-
- utils.attachAttributes(entity.relationships);
-
- async function getEntityWithAlias(relEntity) {
- const redirectBbid = await orm.func.entity.recursivelyGetRedirectBBID(orm, relEntity.bbid, null);
- const model = commonUtils.getEntityModelByType(orm, relEntity.type);
-
- return model.forge({bbid: redirectBbid})
- .fetch({require: false, withRelated: ['defaultAlias'].concat(utils.getAdditionalRelations(relEntity.type))});
- }
-
- /**
- * Source and target are generic Entity objects, so until we have
- * a good way of polymorphically fetching the right specific entity,
- * we need to fetch default alias in a somewhat sketchier way.
- */
- return Promise.all(entity.relationships.map((relationship) =>
- Promise.all([getEntityWithAlias(relationship.source), getEntityWithAlias(relationship.target)])
- .then(([source, target]) => {
- relationship.source = source.toJSON();
- relationship.target = target.toJSON();
-
- return relationship;
- })));
- })
+ .then((relationshipSet) => addRelationships(entity, relationshipSet, orm))
.then(() => {
next();
return null;
@@ -226,6 +238,8 @@ export function makeEntityLoader(modelName: string, additionalRels: Array collection.public === true ||
parseInt(collection.ownerId, 10) === parseInt(req.user?.id, 10));
}
+ const reviews = await getReviewsFromCB(bbid, entity.type);
+ entity.reviews = reviews;
res.locals.entity = entity;
return next();
}
diff --git a/src/server/helpers/utils.ts b/src/server/helpers/utils.ts
index 22d1f4efab..c9781363af 100644
--- a/src/server/helpers/utils.ts
+++ b/src/server/helpers/utils.ts
@@ -20,8 +20,8 @@
*/
import * as search from '../../common/helpers/search';
+import {filterIdentifierTypesByEntityType, unflatten} from '../../common/helpers/utils';
import _ from 'lodash';
-import {unflatten} from '../../common/helpers/utils';
export function getDateBeforeDays(days) {
@@ -30,15 +30,6 @@ export function getDateBeforeDays(days) {
return date;
}
-export function filterIdentifierTypesByEntityType(
- identifierTypes: Array<{id: number, entityType: string}>,
- entityType: string
-): Array> {
- return identifierTypes.filter(
- (type) => type.entityType === entityType
- );
-}
-
export function filterIdentifierTypesByEntity(
identifierTypes: any[],
entity: any
diff --git a/src/server/routes.js b/src/server/routes.js
index 56a29a35e3..2a9a015808 100644
--- a/src/server/routes.js
+++ b/src/server/routes.js
@@ -24,26 +24,32 @@ import collectionsRouter from './routes/collections';
import editionGroupRouter from './routes/entity/edition-group';
import editionRouter from './routes/entity/edition';
import editorRouter from './routes/editor';
+import externalServiceRouter from './routes/externalService';
import indexRouter from './routes/index';
import mergeRouter from './routes/merge';
import publisherRouter from './routes/entity/publisher';
import registerRouter from './routes/register';
+import reviewsRouter from './routes/reviews';
import revisionRouter from './routes/revision';
import revisionsRouter from './routes/revisions';
import searchRouter from './routes/search';
import seriesRouter from './routes/entity/series';
import statisticsRouter from './routes/statistics';
+import unifiedFormRouter from './routes/unifiedform';
import workRouter from './routes/entity/work';
function initRootRoutes(app) {
app.use('/', indexRouter);
app.use('/', authRouter);
+ app.use('/', unifiedFormRouter);
+ app.use('/', reviewsRouter);
app.use('/search', searchRouter);
app.use('/register', registerRouter);
app.use('/revisions', revisionsRouter);
app.use('/collections', collectionsRouter);
app.use('/statistics', statisticsRouter);
+ app.use('/external-service', externalServiceRouter);
}
function initEditionGroupRoutes(app) {
diff --git a/src/server/routes/editor.js b/src/server/routes/editor.js
index 14ff3a753a..3eb7d8d194 100644
--- a/src/server/routes/editor.js
+++ b/src/server/routes/editor.js
@@ -25,7 +25,7 @@ import * as propHelpers from '../../client/helpers/props';
import * as search from '../../common/helpers/search';
import {eachMonthOfInterval, format, isAfter, isValid} from 'date-fns';
import {escapeProps, generateProps} from '../helpers/props';
-import {getEditsInDays, getEntityVisits, getTypeCreation} from '../helpers/achievement';
+import {getConsecutiveDaysWithEdits, getEntityVisits, getTypeCreation} from '../helpers/achievement';
import AchievementsTab from '../../client/components/pages/parts/editor-achievements';
import CollectionsPage from '../../client/components/pages/collections';
import EditorContainer from '../../client/containers/editor';
@@ -448,11 +448,11 @@ async function getProgress(achievementId, editorId, orm) {
}
// Fun Runner
if (achievementId === 11) {
- return getEditsInDays(orm, editorId, 6);
+ return getConsecutiveDaysWithEdits(orm, editorId, 6);
}
// Marathoner
if (achievementId === 12) {
- return getEditsInDays(orm, editorId, 29);
+ return getConsecutiveDaysWithEdits(orm, editorId, 29);
}
// Explorer achivements
const explorerVisits = [24, 25, 26];
diff --git a/src/server/routes/entity/author.js b/src/server/routes/entity/author.js
index aac6c8905a..6cd1bd9258 100644
--- a/src/server/routes/entity/author.js
+++ b/src/server/routes/entity/author.js
@@ -46,7 +46,7 @@ const additionalAuthorProps = [
];
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -236,7 +236,7 @@ router.get('/:bbid/revisions/revisions', (req, res, next) => {
});
-function authorToFormState(author) {
+export function authorToFormState(author) {
/** The front-end expects a language id rather than the language object. */
const aliases = author.aliasSet ?
author.aliasSet.aliases.map(({languageId, ...rest}) => ({
diff --git a/src/server/routes/entity/edition-group.js b/src/server/routes/entity/edition-group.js
index cdd0a908e0..6d6d2aa183 100644
--- a/src/server/routes/entity/edition-group.js
+++ b/src/server/routes/entity/edition-group.js
@@ -40,7 +40,7 @@ import target from '../../templates/target';
*********** Helpers ************
*******************************/
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -226,7 +226,7 @@ router.get('/:bbid/revisions/revisions', (req, res, next) => {
});
-function editionGroupToFormState(editionGroup) {
+export function editionGroupToFormState(editionGroup) {
/** The front-end expects a language id rather than the language object. */
const aliases = editionGroup.aliasSet ?
editionGroup.aliasSet.aliases.map(({languageId, ...rest}) => ({
diff --git a/src/server/routes/entity/edition.ts b/src/server/routes/entity/edition.ts
index a29e43c2aa..abcbd28d32 100644
--- a/src/server/routes/entity/edition.ts
+++ b/src/server/routes/entity/edition.ts
@@ -52,7 +52,7 @@ type PassportRequest = express.Request & {
user: any,
session: any
};
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -101,7 +101,7 @@ function transformNewForm(data) {
pages: data.editionSection.pages &&
parseInt(data.editionSection.pages, 10),
publishers: data.editionSection.publisher &&
- Object.values(data.editionSection.publisher).map((pub) => pub.id),
+ Object.values(data.editionSection.publisher).map((pub:any) => pub.id),
relationships,
releaseEvents,
statusId: data.editionSection.status &&
@@ -251,7 +251,7 @@ router.post(
'/create', entityRoutes.displayPreview, auth.isAuthenticatedForHandler, middleware.loadIdentifierTypes,
middleware.loadEditionStatuses, middleware.loadEditionFormats,
middleware.loadLanguages, middleware.loadRelationshipTypes,
- async (req, res, next) => {
+ async (req:PassportRequest, res, next) => {
// parsing submitted data to correct format
const entity = await utils.parseInitialState(req, 'edition');
if (entity.editionSection) {
@@ -330,7 +330,7 @@ function _setEditionTitle(res) {
);
}
-router.get('/:bbid', middleware.loadEntityRelationships, middleware.loadWorkTableAuthors, (req, res) => {
+router.get('/:bbid', middleware.loadEntityRelationships, middleware.loadWorkTableAuthors, (req:PassportRequest, res) => {
_setEditionTitle(res);
entityRoutes.displayEntity(req, res);
});
@@ -348,7 +348,7 @@ router.get('/:bbid/revisions/revisions', (req:PassportRequest, res, next) => {
});
-router.get('/:bbid/delete', auth.isAuthenticated, (req, res, next) => {
+router.get('/:bbid/delete', auth.isAuthenticated, (req:PassportRequest, res, next) => {
if (!res.locals.entity.dataId) {
return next(new ConflictError('This entity has already been deleted'));
}
@@ -368,7 +368,7 @@ router.post(
);
-function editionToFormState(edition) {
+export function editionToFormState(edition) {
/** The front-end expects a language id rather than the language object. */
const aliases = edition.aliasSet ?
edition.aliasSet.aliases.map(({languageId, ...rest}) => ({
@@ -402,7 +402,7 @@ function editionToFormState(edition) {
})
) : [];
- const authorCreditEditor = {};
+ const authorCreditEditor:any = {};
for (const credit of credits) {
authorCreditEditor[credit.position] = credit;
}
diff --git a/src/server/routes/entity/entity.tsx b/src/server/routes/entity/entity.tsx
index e6abc0f0c1..31ee3d31c3 100644
--- a/src/server/routes/entity/entity.tsx
+++ b/src/server/routes/entity/entity.tsx
@@ -52,6 +52,7 @@ import _ from 'lodash';
import {getEntityLabel} from '../../../client/helpers/entity';
import {getOrderedRevisionsForEntityPage} from '../../helpers/revisions';
import log from 'log';
+import {processAchievement} from './process-unified-form';
import target from '../../templates/target';
@@ -299,7 +300,7 @@ async function setParentRevisions(transacting, newRevision, parentRevisionIDs) {
}
-async function saveEntitiesAndFinishRevision(
+export async function saveEntitiesAndFinishRevision(
orm, transacting, isNew: boolean, newRevision: any, mainEntity: any,
updatedEntities: any[], editorID: number, note: string
) {
@@ -378,9 +379,14 @@ export async function deleteRelationships(orm: any, transacting: Transaction, ma
// Fetch other entity relationships to remove relation with the deleted entity
let otherEntityRelationships = otherEntityRelationshipSet.related('relationships').toJSON();
- // Filter out entites related to deleted entity
- otherEntityRelationships = otherEntityRelationships.filter(({sourceBbid, targetBbid}) =>
- mainBBID !== sourceBbid && mainBBID !== targetBbid);
+ // Mark entites related to deleted entity as removed
+ otherEntityRelationships = otherEntityRelationships.map((rel) => {
+ if (mainBBID !== rel.sourceBbid && mainBBID !== rel.targetBbid) {
+ return rel;
+ }
+ _.set(rel, 'isRemoved', true);
+ return rel;
+ });
const newRelationshipSet = await orm.func.relationship.updateRelationshipSets(
orm, transacting, otherEntityRelationshipSet, otherEntityRelationships
@@ -399,7 +405,7 @@ export async function deleteRelationships(orm: any, transacting: Transaction, ma
return otherEntities;
}
-function fetchOrCreateMainEntity(
+export function fetchOrCreateMainEntity(
orm, transacting, isNew, bbid, entityType
) {
const model = commonUtils.getEntityModelByType(orm, entityType);
@@ -510,7 +516,7 @@ export async function processMergeOperation(orm, transacting, session, mainEntit
const relationshipsToRemove = refreshedrelationshipSet
.related('relationships').toJSON()
.filter(({sourceBbid, targetBbid}) => _.includes(entitiesToMergeBBIDs, sourceBbid) ||
- _.includes(entitiesToMergeBBIDs, targetBbid))
+ _.includes(entitiesToMergeBBIDs, targetBbid))
.map(({id}) => id);
if (relationshipsToRemove.length) {
await refreshedrelationshipSet
@@ -558,16 +564,19 @@ export async function processMergeOperation(orm, transacting, session, mainEntit
}
const otherEntityRelationships = otherEntityRelationshipSet.related('relationships').toJSON();
- // Remove relationships with entity being merged
- const cleanedUpRelationships = otherEntityRelationships
- .filter(({sourceBbid, targetBbid}) =>
- entityBBID !== sourceBbid &&
- entityBBID !== targetBbid);
+ let relsHaveChanged = false;
+ // Mark relationships with entity being merged as removed
+ otherEntityRelationships.forEach((rel) => {
+ if (entityBBID === rel.sourceBbid || entityBBID === rel.targetBbid) {
+ _.set(rel, 'isRemoved', true);
+ relsHaveChanged = true;
+ }
+ });
// If there's a difference, apply the new relationships array without rels to merged entity
- if (cleanedUpRelationships.length !== otherEntityRelationships.length) {
+ if (relsHaveChanged) {
const updatedRelationshipSets = await orm.func.relationship.updateRelationshipSets(
- orm, transacting, otherEntityRelationshipSet, cleanedUpRelationships
+ orm, transacting, otherEntityRelationshipSet, otherEntityRelationships
);
// Make sure the entity is later updated with its new relationshipSet id
allEntitiesReturnArray = _.unionBy(allEntitiesReturnArray, [otherEntity], 'id');
@@ -874,9 +883,13 @@ async function getNextIdentifierSet(orm, transacting, currentEntity, body) {
orm, transacting, oldIdentifierSet, body.identifiers || []
);
}
-async function getNextRelationshipAttributeSets(orm, transacting, body) {
+export async function getNextRelationshipAttributeSets(orm, transacting, body) {
const {RelationshipAttributeSet} = orm;
const relationships = await Promise.all(body.relationships.map(async (relationship) => {
+ if (!relationship.isAdded) {
+ relationship.attributeSetId = _.get(relationship, ['attributeSetId'], null);
+ return relationship;
+ }
const id = relationship.attributeSetId;
const oldRelationshipAttributeSet = await (
id &&
@@ -896,7 +909,7 @@ async function getNextRelationshipAttributeSets(orm, transacting, body) {
return relationships;
}
-async function getNextRelationshipSets(
+export async function getNextRelationshipSets(
orm, transacting, currentEntity, body
) {
const {RelationshipSet} = orm;
@@ -946,7 +959,7 @@ async function getNextDisambiguation(orm, transacting, currentEntity, body) {
);
}
-async function getChangedProps(
+export async function getChangedProps(
orm, transacting, isNew, currentEntity, body, entityType,
newRevision, derivedProps
) {
@@ -1001,7 +1014,7 @@ async function getChangedProps(
}
-function fetchEntitiesForRelationships(
+export function fetchEntitiesForRelationships(
orm, transacting, currentEntity, relationshipSets
) {
const bbidsToFetch = _.without(
@@ -1021,7 +1034,7 @@ function fetchEntitiesForRelationships(
* @description Edition Groups will be created automatically by the ORM if no EditionGroup BBID is set on a new Edition.
* This method fetches and indexes (search) those potential new EditionGroups that may have been created automatically.
*/
-async function indexAutoCreatedEditionGroup(orm, newEdition, transacting) {
+export async function indexAutoCreatedEditionGroup(orm, newEdition, transacting) {
const {EditionGroup} = orm;
const bbid = newEdition.get('editionGroupBbid');
try {
@@ -1047,20 +1060,10 @@ function sanitizeBody(body:any) {
return body;
}
-export function handleCreateOrEditEntity(
- req: PassportRequest,
- res: $Response,
- entityType: EntityTypeString,
- derivedProps: Record,
- isMergeOperation: boolean
-) {
- const {orm}: {orm?: any} = req.app.locals;
- const {Entity, Revision, bookshelf} = orm;
- const editorJSON = req.user;
-
- let {body}: {body: any} = req;
- const {locals: resLocals}: {locals: any} = res;
-
+export async function processSingleEntity(formBody, JSONEntity, reqSession,
+ entityType, orm:any, editorJSON, derivedProps, isMergeOperation, transacting):Promise {
+ const {Entity, Revision} = orm;
+ let body = sanitizeBody(formBody);
let currentEntity: {
aliasSet: {id: number} | null | undefined,
annotation: {id: number} | null | undefined,
@@ -1068,124 +1071,138 @@ export function handleCreateOrEditEntity(
disambiguation: {id: number} | null | undefined,
identifierSet: {id: number} | null | undefined,
type: EntityTypeString
- } | null | undefined = resLocals.entity;
+ } | null | undefined = JSONEntity;
- const entityEditPromise = bookshelf.transaction(async (transacting) => {
- try {
- // Determine if a new entity is being created
- const isNew = !currentEntity;
- // sanitize namesection inputs
- body = sanitizeBody(body);
- if (isNew) {
- const newEntity = await new Entity({type: entityType})
- .save(null, {transacting});
- const newEntityBBID = newEntity.get('bbid');
- body.relationships = _.map(
- body.relationships,
- ({sourceBbid, targetBbid, ...others}) => ({
- sourceBbid: sourceBbid || newEntityBBID,
- targetBbid: targetBbid || newEntityBBID,
- ...others
- })
- );
+ try {
+ // Determine if a new entity is being created
+ const isNew = !currentEntity;
+ // sanitize namesection inputs
+ body = sanitizeBody(body);
+ if (isNew) {
+ const newEntity = await new Entity({type: entityType})
+ .save(null, {transacting});
+ const newEntityBBID = newEntity.get('bbid');
+ body.relationships = _.map(
+ body.relationships,
+ ({sourceBbid, targetBbid, ...others}) => ({
+ sourceBbid: sourceBbid || newEntityBBID,
+ targetBbid: targetBbid || newEntityBBID,
+ ...others
+ })
+ );
- currentEntity = newEntity.toJSON();
- }
+ currentEntity = newEntity.toJSON();
+ }
- // Then, edit the entity
- const newRevision = await new Revision({
- authorId: editorJSON.id,
- isMerge: isMergeOperation
- }).save(null, {transacting});
+ // Then, edit the entity
+ const newRevision = await new Revision({
+ authorId: editorJSON.id,
+ isMerge: isMergeOperation
+ }).save(null, {transacting});
- const relationshipSets = await getNextRelationshipSets(
- orm, transacting, currentEntity, body
- );
+ const relationshipSets = await getNextRelationshipSets(
+ orm, transacting, currentEntity, body
+ );
- const changedProps = await getChangedProps(
- orm, transacting, isNew, currentEntity, body, entityType,
- newRevision, derivedProps
- );
+ const changedProps = await getChangedProps(
+ orm, transacting, isNew, currentEntity, body, entityType,
+ newRevision, derivedProps
+ );
- // If there are no differences, bail
- if (_.isEmpty(changedProps) && _.isEmpty(relationshipSets) && !isMergeOperation) {
- throw new error.FormSubmissionError('No Updated Field');
- }
+ // If there are no differences, bail
+ if (_.isEmpty(changedProps) && _.isEmpty(relationshipSets) && !isMergeOperation) {
+ throw new error.FormSubmissionError('No Updated Field');
+ }
- // Fetch or create main entity
- const mainEntity = await fetchOrCreateMainEntity(
- orm, transacting, isNew, currentEntity.bbid, entityType
- );
+ // Fetch or create main entity
+ const mainEntity = await fetchOrCreateMainEntity(
+ orm, transacting, isNew, currentEntity.bbid, entityType
+ );
- // Fetch all entities that definitely exist
- const otherEntities = await fetchEntitiesForRelationships(
- orm, transacting, currentEntity, relationshipSets
- );
- otherEntities.forEach(entity => { entity.shouldInsert = false; });
- mainEntity.shouldInsert = isNew;
+ // Fetch all entities that definitely exist
+ const otherEntities = await fetchEntitiesForRelationships(
+ orm, transacting, currentEntity, relationshipSets
+ );
+ otherEntities.forEach(entity => { entity.shouldInsert = false; });
+ mainEntity.shouldInsert = isNew;
- _.forOwn(changedProps, (value, key) => mainEntity.set(key, value));
+ _.forOwn(changedProps, (value, key) => mainEntity.set(key, value));
- // Don't try to modify 'deleted' entities (those with no dataId)
- let allEntities = [...otherEntities, mainEntity]
- .filter(entity => entity.get('dataId') !== null);
+ // Don't try to modify 'deleted' entities (those with no dataId)
+ let allEntities = [...otherEntities, mainEntity]
+ .filter(entity => entity.get('dataId') !== null);
- if (isMergeOperation) {
- allEntities = await processMergeOperation(orm, transacting, req.session,
- mainEntity, allEntities, relationshipSets);
- }
+ if (isMergeOperation) {
+ allEntities = await processMergeOperation(orm, transacting, reqSession,
+ mainEntity, allEntities, relationshipSets);
+ }
- _.forEach(allEntities, (entityModel) => {
- const bbid: string = entityModel.get('bbid');
- if (_.has(relationshipSets, bbid)) {
- entityModel.set(
- 'relationshipSetId',
- // Set to relationshipSet id or null if empty set
- relationshipSets[bbid] && relationshipSets[bbid].get('id')
- );
- }
- });
+ _.forEach(allEntities, (entityModel) => {
+ const bbid: string = entityModel.get('bbid');
+ if (_.has(relationshipSets, bbid)) {
+ entityModel.set(
+ 'relationshipSetId',
+ // Set to relationshipSet id or null if empty set
+ relationshipSets[bbid] && relationshipSets[bbid].get('id')
+ );
+ }
+ });
- const savedMainEntity = await saveEntitiesAndFinishRevision(
- orm, transacting, isNew, newRevision, mainEntity, allEntities,
- editorJSON.id, body.note
- );
+ const savedMainEntity = await saveEntitiesAndFinishRevision(
+ orm, transacting, isNew, newRevision, mainEntity, allEntities,
+ editorJSON.id, body.note
+ );
- /* We need to load the aliases for search reindexing and refresh it*/
- await savedMainEntity.load('aliasSet.aliases', {transacting});
+ /* We need to load the aliases for search reindexing and refresh it*/
+ await savedMainEntity.load(['aliasSet.aliases', 'defaultAlias.language', 'relationshipSet.relationships.source',
+ 'relationshipSet.relationships.target', 'relationshipSet.relationships.type', 'annotation'], {transacting});
- /* New entities will lack some attributes like 'type' required for search indexing */
- if (isNew) {
- await savedMainEntity.refresh({transacting});
+ /* New entities will lack some attributes like 'type' required for search indexing */
+ if (isNew) {
+ await savedMainEntity.refresh({transacting});
- /* fetch and reindex EditionGroups that may have been created automatically by the ORM and not indexed */
- if (savedMainEntity.get('type') === 'Edition') {
- await indexAutoCreatedEditionGroup(orm, savedMainEntity, transacting);
- }
+ /* fetch and reindex EditionGroups that may have been created automatically by the ORM and not indexed */
+ if (savedMainEntity.get('type') === 'Edition') {
+ await indexAutoCreatedEditionGroup(orm, savedMainEntity, transacting);
}
-
-
- return savedMainEntity.toJSON();
}
- catch (err) {
- log.error(err);
- throw err;
+
+ const entityJSON = savedMainEntity.toJSON();
+ if (entityJSON && entityJSON.relationshipSet) {
+ entityJSON.relationshipSet.relationships = await Promise.all(entityJSON.relationshipSet.relationships.map(async (rel) => {
+ try {
+ rel.source = await commonUtils.getEntityAlias(orm, rel.source.bbid, rel.source.type);
+ rel.target = await commonUtils.getEntityAlias(orm, rel.target.bbid, rel.target.type);
+ }
+ catch (err) {
+ log.error(err);
+ }
+ return rel;
+ }));
}
- });
+ return entityJSON;
+ }
+ catch (err) {
+ log.error(err);
+ throw err;
+ }
+}
+export function handleCreateOrEditEntity(
+ req: PassportRequest,
+ res: $Response,
+ entityType: EntityTypeString,
+ derivedProps: Record,
+ isMergeOperation: boolean
+) {
+ const {orm}: {orm?: any} = req.app.locals;
+ const editorJSON = req.user;
+ const {bookshelf} = orm;
+ const entityEditPromise = bookshelf.transaction((transacting) =>
+ processSingleEntity(req.body, res.locals.entity, req.session, entityType, orm, editorJSON, derivedProps, isMergeOperation, transacting));
const achievementPromise = entityEditPromise.then(
- (entityJSON) => achievement.processEdit(
- orm, editorJSON.id, entityJSON.revisionId
- )
- .then((unlock) => {
- if (unlock.alert) {
- entityJSON.alert = unlock.alert;
- }
- return entityJSON;
- })
- .catch(err => { throw err; })
+ (entityJSON) => processAchievement(orm, editorJSON.id, entityJSON)
);
-
return handler.sendPromiseResult(
res,
achievementPromise,
@@ -1254,10 +1271,12 @@ export function constructIdentifiers(
export function constructRelationships(parentSection, childAttributeName = 'relationships') {
return _.map(
parentSection[childAttributeName],
- ({attributeSetId, rowID, relationshipType, sourceEntity, targetEntity, attributes}) => ({
+ ({attributeSetId, rowID, relationshipType, sourceEntity, targetEntity, attributes, isRemoved, isAdded}) => ({
attributeSetId,
attributes,
id: rowID,
+ isAdded,
+ isRemoved,
sourceBbid: _.get(sourceEntity, 'bbid'),
targetBbid: _.get(targetEntity, 'bbid'),
typeId: relationshipType.id
@@ -1379,3 +1398,4 @@ export function displayPreview(req:PassportRequest, res:$Response, next) {
title: 'Preview'
}));
}
+
diff --git a/src/server/routes/entity/process-unified-form.ts b/src/server/routes/entity/process-unified-form.ts
new file mode 100644
index 0000000000..8890cb7664
--- /dev/null
+++ b/src/server/routes/entity/process-unified-form.ts
@@ -0,0 +1,215 @@
+/* eslint-disable no-await-in-loop */
+import * as achievement from '../../helpers/achievement';
+import type {Request as $Request, Response as $Response} from 'express';
+import {FormSubmissionError, sendErrorAsJSON} from '../../../common/helpers/error';
+import {authorToFormState, transformNewForm as authorTransform} from './author';
+import {editionGroupToFormState, transformNewForm as editionGroupTransform} from './edition-group';
+import {editionToFormState, transformNewForm as editionTransform} from './edition';
+import {publisherToFormState, transformNewForm as publisherTransform} from './publisher';
+import {seriesToFormState, transformNewForm as seriesTransform} from './series';
+
+import {workToFormState, transformNewForm as workTransform} from './work';
+import type {
+ EntityTypeString
+} from 'bookbrainz-data/lib/func/types';
+import _ from 'lodash';
+import {_bulkIndexEntities} from '../../../common/helpers/search';
+import log from 'log';
+import {processSingleEntity} from './entity';
+
+
+type PassportRequest = $Request & {user: any, session: any};
+const transformFunctions = {
+ author: authorTransform,
+ edition: editionTransform,
+ editionGroup: editionGroupTransform,
+ publisher: publisherTransform,
+ series: seriesTransform,
+ work: workTransform
+};
+const additionalEntityProps = {
+ author: [
+ 'typeId', 'genderId', 'beginAreaId', 'beginDate', 'endDate', 'ended',
+ 'endAreaId'
+ ],
+ edition: [
+ 'editionGroupBbid', 'width', 'height', 'depth', 'weight', 'pages',
+ 'formatId', 'statusId'
+ ],
+ editionGroup: 'typeid',
+ publisher: ['typeId', 'areaId', 'beginDate', 'endDate', 'ended'],
+ series: ['entityType', 'orderingTypeId'],
+ work: 'typeId'
+
+};
+
+const entityToFormStateMap = {
+ author: authorToFormState,
+ edition: editionToFormState,
+ editionGroup: editionGroupToFormState,
+ publisher: publisherToFormState,
+ series: seriesToFormState,
+ work: workToFormState
+};
+
+const baseRelations = [
+ 'aliasSet.aliases.language',
+ 'annotation.lastRevision',
+ 'defaultAlias',
+ 'disambiguation',
+ 'identifierSet.identifiers.type',
+ 'relationshipSet.relationships.type',
+ 'revision.revision',
+ 'collections.owner'
+];
+
+const additionalEntityAttributes = {
+ author: ['authorType', 'gender', 'beginArea', 'endArea'],
+ edition: [
+ 'authorCredit.names.author.defaultAlias',
+ 'editionGroup.defaultAlias',
+ 'languageSet.languages',
+ 'editionFormat',
+ 'editionStatus',
+ 'releaseEventSet.releaseEvents',
+ 'publisherSet.publishers.defaultAlias'
+ ],
+ editionGroup: [
+ 'authorCredit.names.author.defaultAlias',
+ 'editionGroupType',
+ 'editions.defaultAlias',
+ 'editions.disambiguation',
+ 'editions.releaseEventSet.releaseEvents',
+ 'editions.identifierSet.identifiers.type',
+ 'editions.editionFormat'
+ ],
+ publisher: ['publisherType', 'area'],
+ series: [
+ 'defaultAlias',
+ 'disambiguation',
+ 'seriesOrderingType',
+ 'identifierSet.identifiers.type'
+ ],
+ work: ['workType', 'languageSet.languages']
+};
+
+export async function processAchievement(orm, editorId, entityJSON) {
+ const {revisionId} = entityJSON;
+ try {
+ const achievementUnlock = await achievement.processEdit(orm, editorId, revisionId);
+ if (achievementUnlock.alert) {
+ entityJSON.alert = achievementUnlock.alert;
+ }
+ return entityJSON;
+ }
+ catch (err) {
+ return Promise.reject(new Error(err));
+ }
+}
+
+function getEntityRelations(type:EntityTypeString) {
+ return [...baseRelations, ...additionalEntityAttributes[_.camelCase(type)]];
+}
+
+export function transformForm(body:Record):Record {
+ return _.mapValues(body, (currentForm) => {
+ const transformedForm = transformFunctions[_.camelCase(currentForm.type)](currentForm);
+ const __isNew__ = _.get(currentForm, '__isNew__', true);
+ const {id, type} = currentForm;
+ return {__isNew__, id, type, ...transformedForm};
+ });
+}
+
+export async function preprocessForm(body:Record, orm):Promise> {
+ async function processForm(currentForm) {
+ const {id, type} = currentForm;
+ const isNew = _.get(currentForm, '__isNew__', true);
+ // if new entity, no need to process further
+ if (!isNew && id) {
+ const entityType = _.upperFirst(_.camelCase(type));
+ // fetch the entity with all related attributes from the database
+ const currentEntity = await orm.func.entity.getEntity(orm, entityType, id, getEntityRelations(entityType as EntityTypeString));
+ currentEntity.relationships = [];
+ if (!currentEntity.annotation) {
+ _.set(currentEntity, ['annotation', 'content'], '');
+ }
+ // convert this state to normal entity-editor state
+ const oldFormState = entityToFormStateMap[_.camelCase(type)](currentEntity);
+ // deep merge the old state with new one
+ return _.merge(oldFormState, currentForm);
+ }
+ return currentForm;
+ }
+ const allEntities = await Promise.all(_.values(body).map(processForm));
+ return Object.fromEntries(Object.keys(body).map((key, index) => [key, allEntities[index]]));
+}
+
+
+export async function handleCreateMultipleEntities(
+ req: PassportRequest,
+ res: $Response
+) {
+ const {orm}: {orm?: any} = req.app.locals;
+ const editorJSON = req.user;
+ let currentEntity: {
+ __isNew__: boolean | undefined,
+ aliasSet: {id: number} | null | undefined,
+ annotation: {id: number} | null | undefined,
+ bbid: string,
+ disambiguation: {id: number} | null | undefined,
+ identifierSet: {id: number} | null | undefined,
+ type: EntityTypeString
+ } | null | undefined;
+
+
+ const {body}: {body: Record} = req;
+ async function processEntity(entityKey:string) {
+ const entityForm = body[entityKey];
+ const entityType = _.upperFirst(entityForm.type);
+ const deriveProps = _.pick(entityForm, additionalEntityProps[_.camelCase(entityType)]);
+ const isNew = _.get(entityForm, '__isNew__', true);
+ currentEntity = null;
+ if (!isNew) {
+ currentEntity = await orm.func.entity.getEntity(orm, entityType, entityForm.id, getEntityRelations(entityType as EntityTypeString));
+ if (!currentEntity) {
+ throw new FormSubmissionError('Entity with given id not found');
+ }
+ }
+ const {bookshelf} = orm;
+ const entityEditPromise = bookshelf.transaction((transacting) =>
+ processSingleEntity(entityForm, currentEntity, null, entityType, orm, editorJSON, deriveProps, false, transacting));
+ return entityEditPromise;
+ }
+ // first, process all entities
+ const processedEntities = [];
+ const processedAchievements = [];
+ try {
+ for (const entityKey in body) {
+ if (Object.prototype.hasOwnProperty.call(body, entityKey)) {
+ processedEntities.push(await processEntity(entityKey));
+ }
+ }
+ }
+ catch (err) {
+ log.error(err);
+ return sendErrorAsJSON(res, err);
+ }
+ // log achievements error if any
+ try {
+ for (const entity of processedEntities) {
+ const achievementPromise = processAchievement(orm, editorJSON.id, entity);
+ processedAchievements.push(await achievementPromise);
+ }
+ }
+ catch (err) {
+ log.error(err);
+ }
+ // log indexing error if any
+ try {
+ _bulkIndexEntities(processedAchievements);
+ }
+ catch (err) {
+ log.error(err);
+ }
+ return res.send(processedEntities);
+}
diff --git a/src/server/routes/entity/publisher.js b/src/server/routes/entity/publisher.js
index d688b375c8..4552937e27 100644
--- a/src/server/routes/entity/publisher.js
+++ b/src/server/routes/entity/publisher.js
@@ -46,7 +46,7 @@ const additionalPublisherProps = [
'typeId', 'areaId', 'beginDate', 'endDate', 'ended'
];
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -246,7 +246,7 @@ router.get('/:bbid/revisions/revisions', (req, res, next) => {
});
-function publisherToFormState(publisher) {
+export function publisherToFormState(publisher) {
/** The front-end expects a language id rather than the language object. */
const aliases = publisher.aliasSet ?
publisher.aliasSet.aliases.map(({languageId, ...rest}) => ({
diff --git a/src/server/routes/entity/series.js b/src/server/routes/entity/series.js
index c892e7f1ed..7d53bfb484 100644
--- a/src/server/routes/entity/series.js
+++ b/src/server/routes/entity/series.js
@@ -43,7 +43,7 @@ const additionalSeriesProps = [
'entityType', 'orderingTypeId'
];
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -233,7 +233,7 @@ router.get('/:bbid/revisions/revisions', (req, res, next) => {
entityRoutes.updateDisplayedRevisions(req, res, next, SeriesRevision);
});
-function seriesToFormState(series) {
+export function seriesToFormState(series) {
const aliases = series.aliasSet ?
series.aliasSet.aliases.map(({languageId, ...rest}) => ({
...rest,
diff --git a/src/server/routes/entity/work.js b/src/server/routes/entity/work.js
index 925c447922..076cbc1a07 100644
--- a/src/server/routes/entity/work.js
+++ b/src/server/routes/entity/work.js
@@ -43,7 +43,7 @@ import target from '../../templates/target';
*********** Helpers ************
*******************************/
-function transformNewForm(data) {
+export function transformNewForm(data) {
const aliases = entityRoutes.constructAliases(
data.aliasEditor, data.nameSection
);
@@ -258,8 +258,7 @@ router.get('/:bbid/revisions/revisions', (req, res, next) => {
entityRoutes.updateDisplayedRevisions(req, res, next, WorkRevision);
});
-
-function workToFormState(work) {
+export function workToFormState(work) {
/** The front-end expects a language id rather than the language object. */
const aliases = work.aliasSet ?
work.aliasSet.aliases.map(({languageId, ...rest}) => ({
diff --git a/src/server/routes/externalService.js b/src/server/routes/externalService.js
new file mode 100644
index 0000000000..0c18b6da61
--- /dev/null
+++ b/src/server/routes/externalService.js
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2022 Ansh Goyal
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+import * as auth from '../helpers/auth';
+import * as cbHelper from '../helpers/critiquebrainz';
+import * as propHelpers from '../../client/helpers/props';
+import {AuthenticationFailedError, BadRequestError} from '../../common/helpers/error';
+import {escapeProps, generateProps} from '../helpers/props';
+import ExternalServices from '../../client/components/pages/externalService';
+import Layout from '../../client/containers/layout';
+import React from 'react';
+import ReactDOMServer from 'react-dom/server';
+import express from 'express';
+import target from '../templates/target';
+import url from 'url';
+
+
+const marginTime = 5 * 60 * 1000;
+const router = express.Router();
+
+
+router.get('/', async (req, res) => {
+ if (!req.user) {
+ return res.redirect('/register');
+ }
+ const {alertType, alertDetails} = req.query;
+ const {orm} = req.app.locals;
+ const editorId = req.user.id;
+
+ const cbUser = await orm.func.externalServiceOauth.getOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+
+ let cbPermission = 'disable';
+ if (cbUser) {
+ cbPermission = 'review';
+ }
+
+ const props = generateProps(req, res, {
+ alertDetails,
+ alertType,
+ cbPermission
+ });
+
+ const markup = ReactDOMServer.renderToString(
+
+
+
+ );
+
+ return res.send(target({
+ markup,
+ props: escapeProps(props),
+ script: '/js/externalService.js',
+ title: 'External Services'
+ }));
+});
+
+
+router.get('/critiquebrainz/callback', auth.isAuthenticated, async (req, res, next) => {
+ const {orm} = req.app.locals;
+ const {code} = req.query;
+ const editorId = req.user.id;
+ if (!code) {
+ return next(new BadRequestError('Response type must be code'));
+ }
+ const token = await cbHelper.fetchAccessToken(code, editorId, orm);
+
+ let alertType = 'success';
+ let alertDetails = 'You have successfully linked your account with CritiqueBrainz.';
+ if (!token) {
+ alertType = 'danger';
+ alertDetails = 'Failed to connect to CritiqueBrainz';
+ }
+ return res.redirect(
+ url.format({
+ pathname: '/external-service',
+ query: {
+ alertDetails,
+ alertType
+ }
+ })
+ );
+});
+
+
+router.post('/critiquebrainz/refresh', auth.isAuthenticated, async (req, res, next) => {
+ const editorId = req.user.id;
+ const {orm} = req.app.locals;
+ let token = await orm.func.externalServiceOauth.getOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+
+ if (!token) {
+ return next(new AuthenticationFailedError('User has not authenticated to CritiqueBrainz'));
+ }
+ const tokenExpired = new Date(token.token_expires).getTime() <= new Date(new Date().getTime() + marginTime).getTime();
+ if (tokenExpired) {
+ try {
+ token = await cbHelper.refreshAccessToken(editorId, token.refresh_token, orm);
+ }
+ catch (error) {
+ return res.json({error: error.message});
+ }
+ }
+ return res.json({accessToken: token?.access_token});
+});
+
+
+router.post('/critiquebrainz/connect', auth.isAuthenticated, async (req, res) => {
+ const editorId = req.user.id;
+ const {orm} = req.app.locals;
+
+ // First we check if the user has already connected to CritiqueBrainz
+ // If so, we first delete the existing tokens and then try to connect again.
+
+ const token = await orm.func.externalServiceOauth.getOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+ if (token?.length) {
+ await orm.func.externalServiceOauth.deleteOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+ }
+ const redirectUrl = cbHelper.getAuthURL();
+
+ return res.send(redirectUrl);
+});
+
+
+router.post('/critiquebrainz/disconnect', auth.isAuthenticated, async (req, res) => {
+ const editorId = req.user.id;
+ const {orm} = req.app.locals;
+
+ let alertType = 'success';
+ let alertDetails = 'You have successfully disconnected your account with CritiqueBrainz.';
+ try {
+ await orm.func.externalServiceOauth.deleteOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+ }
+ catch {
+ alertType = 'danger';
+ alertDetails = 'Failed to disconnect from CritiqueBrainz';
+ }
+
+ res.send({alertDetails, alertType});
+});
+
+export default router;
diff --git a/src/server/routes/merge.ts b/src/server/routes/merge.ts
index fa854e2b05..aa894b8c87 100644
--- a/src/server/routes/merge.ts
+++ b/src/server/routes/merge.ts
@@ -122,9 +122,11 @@ function entitiesToFormState(entities: any[]) {
}, []);
const otherEntitiesBBIDs = otherEntities.map(entity => entity.bbid);
relationships.forEach((relationship) => {
+ const isAffectedByMerge = otherEntitiesBBIDs.includes(relationship.sourceBbid) || otherEntitiesBBIDs.includes(relationship.targetBbid);
const formattedRelationship = {
attributeSetId: relationship.attributeSetId,
attributes: relationship.attributeSet ? relationship.attributeSet.relationshipAttributes : [],
+ isAdded: isAffectedByMerge,
relationshipType: relationship.type,
rendered: relationship.rendered,
rowID: `n${relationship.id}`,
diff --git a/src/server/routes/reviews.js b/src/server/routes/reviews.js
new file mode 100644
index 0000000000..616d72af8a
--- /dev/null
+++ b/src/server/routes/reviews.js
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 Ansh Goyal
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+
+import * as auth from '../helpers/auth';
+import * as cbHelper from '../helpers/critiquebrainz';
+import express from 'express';
+
+
+const router = express.Router();
+
+router.get('/:entityType/:bbid/reviews', async (req, res) => {
+ const {entityType, bbid} = req.params;
+ const reviews = await cbHelper.getReviewsFromCB(bbid, entityType);
+ res.json(reviews);
+});
+
+router.post('/:entityType/:bbid/reviews', auth.isAuthenticated, async (req, res) => {
+ const editorId = req.user.id;
+ const {orm} = req.app.locals;
+
+ const {accessToken} = req.body;
+ const {review} = req.body;
+
+ let response = await cbHelper.submitReviewToCB(
+ accessToken,
+ review
+ );
+
+ let newAccessToken = '';
+ // If the token has expired, we try to refresh it and then submit the review again.
+ if (response?.error === 'invalid_token') {
+ try {
+ const token = await orm.func.externalServiceOauth.getOauthToken(
+ editorId,
+ 'critiquebrainz',
+ orm
+ );
+
+ newAccessToken = await cbHelper.refreshAccessToken(
+ editorId,
+ token.refresh_token,
+ orm
+ );
+ }
+ catch (error) {
+ return res.json({error: error.message});
+ }
+
+ response = await cbHelper.submitReviewToCB(
+ newAccessToken.access_token,
+ review
+ );
+ }
+
+ return res.json(response);
+});
+
+export default router;
diff --git a/src/server/routes/search.js b/src/server/routes/search.js
index d646364e8e..7644f98758 100644
--- a/src/server/routes/search.js
+++ b/src/server/routes/search.js
@@ -117,8 +117,9 @@ router.get('/autocomplete', (req, res) => {
const {orm} = req.app.locals;
const query = req.query.q;
const type = req.query.type || 'allEntities';
+ const size = req.query.size || 42;
- const searchPromise = search.autocomplete(orm, query, type);
+ const searchPromise = search.autocomplete(orm, query, type, size);
handler.sendPromiseResult(res, searchPromise);
});
diff --git a/src/server/routes/unifiedform.ts b/src/server/routes/unifiedform.ts
new file mode 100644
index 0000000000..d50e195a64
--- /dev/null
+++ b/src/server/routes/unifiedform.ts
@@ -0,0 +1,32 @@
+import * as middleware from '../helpers/middleware';
+import {createEntitiesHandler, generateUnifiedProps, unifiedFormMarkup} from '../helpers/entityRouteUtils';
+import {isAuthenticated, isAuthenticatedForHandler} from '../helpers/auth';
+import {escapeProps} from '../helpers/props';
+import express from 'express';
+import target from '../templates/target';
+
+
+type PassportRequest = express.Request & {user: any, session: any};
+
+const router = express.Router();
+router.get('/create', isAuthenticated, middleware.loadIdentifierTypes,
+ middleware.loadEditionStatuses, middleware.loadEditionFormats, middleware.loadEditionGroupTypes, middleware.loadSeriesOrderingTypes,
+ middleware.loadLanguages, middleware.loadWorkTypes, middleware.loadGenders, middleware.loadPublisherTypes, middleware.loadAuthorTypes,
+ middleware.loadRelationshipTypes, (req:PassportRequest, res:express.Response) => {
+ const props = generateUnifiedProps(req, res, {
+ genderOptions: res.locals.genders
+ });
+ const formMarkup = unifiedFormMarkup(props);
+ const {markup, props: updatedProps} = formMarkup;
+ return res.send(target({
+ markup,
+ page: 'Unified form',
+ props: escapeProps(updatedProps),
+ script: '/js/unified-form.js',
+ title: 'Unified form'
+ }));
+ });
+
+router.post('/create/handler', isAuthenticatedForHandler, createEntitiesHandler);
+
+export default router;
diff --git a/static/images/critiquebrainz-logo.svg b/static/images/critiquebrainz-logo.svg
new file mode 100644
index 0000000000..44f1697a7b
--- /dev/null
+++ b/static/images/critiquebrainz-logo.svg
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/src/client/entity-editor/common/__snapshots__/name-field.jsx.snap b/test/src/client/entity-editor/common/__snapshots__/name-field.jsx.snap
new file mode 100644
index 0000000000..96fa9c594a
--- /dev/null
+++ b/test/src/client/entity-editor/common/__snapshots__/name-field.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`NameField should render correctly 1`] = `""`;
diff --git a/test/src/client/entity-editor/common/__snapshots__/new-date-field.jsx.snap b/test/src/client/entity-editor/common/__snapshots__/new-date-field.jsx.snap
new file mode 100644
index 0000000000..e6e8dfd2f9
--- /dev/null
+++ b/test/src/client/entity-editor/common/__snapshots__/new-date-field.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DateField should render correctly 1`] = `" Are you sure? You entered a date in the future! "`;
diff --git a/test/src/client/entity-editor/common/__snapshots__/validation-label.jsx.snap b/test/src/client/entity-editor/common/__snapshots__/validation-label.jsx.snap
new file mode 100644
index 0000000000..18d17cac6d
--- /dev/null
+++ b/test/src/client/entity-editor/common/__snapshots__/validation-label.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ValidationLabel should render correctly 1`] = `" "`;
diff --git a/test/src/client/entity-editor/common/name-field.jsx b/test/src/client/entity-editor/common/name-field.jsx
new file mode 100644
index 0000000000..ee918c8606
--- /dev/null
+++ b/test/src/client/entity-editor/common/name-field.jsx
@@ -0,0 +1,40 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import NameField from '../../../../../src/client/entity-editor/common/name-field';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+describe('NameField', () => {
+ let onChangeSpy;
+ let wrapper;
+ const label = 'SomeLabel';
+ const tooltipText = 'Some tooltip text';
+ before(() => {
+ onChangeSpy = spy();
+ wrapper = mount();
+ });
+ it('should render correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should call onChange when input changes', () => {
+ const input = wrapper.find('input');
+ input.simulate('change', {target: {value: 'new value'}});
+ expect(onChangeSpy.calledOnce).to.be.true;
+ });
+ it('should have correct label', () => {
+ expect(wrapper.find('span').first().text()).include(label);
+ });
+ it('should have correct tooltip text', async () => {
+ wrapper.find('OverlayTrigger').simulate('mouseover');
+ await new Promise((foo) => setTimeout(foo, 50));
+ wrapper.update();
+ const tooltip = wrapper.find('Tooltip');
+ expect(tooltip.text()).to.equal(tooltipText);
+ });
+});
diff --git a/test/src/client/entity-editor/common/new-date-field.jsx b/test/src/client/entity-editor/common/new-date-field.jsx
new file mode 100644
index 0000000000..00ccde9907
--- /dev/null
+++ b/test/src/client/entity-editor/common/new-date-field.jsx
@@ -0,0 +1,44 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import DateField from '../../../../../src/client/entity-editor/common/new-date-field';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+describe('DateField', () => {
+ let wrapper;
+ it('should render correctly', () => {
+ wrapper = mount( );
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should display given date correcly', () => {
+ const isoDate = '+000012-12-14';
+ const dummyLabel = 'SomeLabel';
+ wrapper = mount();
+ expect(wrapper.find('label span').text()).to.equal(dummyLabel);
+ expect(wrapper.find('FormControl.year-field').props().value).to.equal('0012');
+ const obj = wrapper.find('FormControl.other-date-field');
+ expect(obj.at(0).props().value).to.equal('12');
+ expect(obj.at(1).props().value).to.equal('14');
+ });
+ it('should call onChange callback with correct arguments', () => {
+ const onChangeSpy = spy();
+ wrapper = mount();
+ const yearInput = wrapper.find('.year-field input');
+ yearInput.simulate('change', {target: {value: '2022'}});
+ expect(onChangeSpy.calledOnce).to.be.true;
+ expect(onChangeSpy.args[0][0]).to.equal('+002022');
+ const otherInput = wrapper.find('.other-date-field input');
+ otherInput.at(0).simulate('change', {target: {value: '12'}});
+ expect(onChangeSpy.calledTwice).to.be.true;
+ expect(onChangeSpy.args[1][0]).to.equal('+002022-12');
+ otherInput.at(1).simulate('change', {target: {value: '14'}});
+ expect(onChangeSpy.calledThrice).to.be.true;
+ expect(onChangeSpy.args[2][0]).to.equal('+002022-12-14');
+ });
+});
diff --git a/test/src/client/entity-editor/common/validation-label.jsx b/test/src/client/entity-editor/common/validation-label.jsx
new file mode 100644
index 0000000000..0288fd61ae
--- /dev/null
+++ b/test/src/client/entity-editor/common/validation-label.jsx
@@ -0,0 +1,40 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import ValidationLabel from '../../../../../src/client/entity-editor/common/validation-label';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+
+
+use(jestSnapshotPlugin());
+
+describe('ValidationLabel', () => {
+ it('should render correctly', () => {
+ const wrapper = mount( );
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should show correct error messege on error', () => {
+ const errorMessage = 'error message';
+ const wrapper = mount();
+ expect(wrapper.find('span').last().text()).include(errorMessage);
+ });
+ it('should show correct warning messege on warning', () => {
+ const warningMessage = 'warning message';
+ const wrapper = mount();
+ expect(wrapper.find('span').last().text()).include(warningMessage);
+ });
+ it('should show correct label', () => {
+ const label = 'label';
+ const wrapper = mount({label} );
+ expect(wrapper.find('span').first().text()).include(label);
+ });
+ it('should show icon on entity editor', () => {
+ const wrapper = mount();
+ expect(wrapper.find('FontAwesomeIcon').length).to.equal(1);
+ });
+ it('should not show icon on unified form editor', () => {
+ const wrapper = mount();
+ expect(wrapper.find('FontAwesomeIcon').length).to.equal(0);
+ });
+});
diff --git a/test/src/client/unified-form/common/__snapshots__/search-entity-create-select.jsx.snap b/test/src/client/unified-form/common/__snapshots__/search-entity-create-select.jsx.snap
new file mode 100644
index 0000000000..86872d8903
--- /dev/null
+++ b/test/src/client/unified-form/common/__snapshots__/search-entity-create-select.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SearchEntityCreaateSelect renders correctly 1`] = `""`;
diff --git a/test/src/client/unified-form/common/__snapshots__/single-accordion.jsx.snap b/test/src/client/unified-form/common/__snapshots__/single-accordion.jsx.snap
new file mode 100644
index 0000000000..5d16fa8e06
--- /dev/null
+++ b/test/src/client/unified-form/common/__snapshots__/single-accordion.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SingleAccordion renders correctly 1`] = `""`;
diff --git a/test/src/client/unified-form/common/search-entity-create-select.jsx b/test/src/client/unified-form/common/search-entity-create-select.jsx
new file mode 100644
index 0000000000..0ba3c1224e
--- /dev/null
+++ b/test/src/client/unified-form/common/search-entity-create-select.jsx
@@ -0,0 +1,38 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import {Provider} from 'react-redux';
+import SearchEntityCreate from '../../../../../src/client/unified-form/common/search-entity-create-select';
+import configureStore from 'redux-mock-store';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {stub} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+const props = {
+ allIdentifierTypes: [],
+ nextId: 0,
+ type: 'Work'
+};
+
+const mockStore = configureStore();
+describe('SearchEntityCreaateSelect', () => {
+ let wrapper;
+ let store;
+ const formatCreateLabelSpy = stub();
+ before(() => {
+ store = mockStore();
+ wrapper = mount(
+
+
+
+ );
+ });
+ it('renders correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+});
+
diff --git a/test/src/client/unified-form/common/single-accordion.jsx b/test/src/client/unified-form/common/single-accordion.jsx
new file mode 100644
index 0000000000..65e2a8a091
--- /dev/null
+++ b/test/src/client/unified-form/common/single-accordion.jsx
@@ -0,0 +1,33 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import SingleAccordion from '../../../../../src/client/unified-form/common/single-accordion';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+
+
+use(jestSnapshotPlugin());
+
+
+describe('SingleAccordion', () => {
+ let wrapper;
+ const heading = 'BIGHEADING';
+ const content = 'HELLOWORLD';
+ before(() => {
+ wrapper = mount(
+
+ {content}
+
+ );
+ });
+ it('renders correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should have given heading', () => {
+ expect(wrapper.find('ValidationLabel').text()).to.equal(heading);
+ });
+ it('should render the given children', () => {
+ expect(wrapper.find('h1').text()).to.equal(content);
+ });
+});
diff --git a/test/src/client/unified-form/content-tab/__snapshots__/content-tab.jsx.snap b/test/src/client/unified-form/content-tab/__snapshots__/content-tab.jsx.snap
new file mode 100644
index 0000000000..d94dcbadc5
--- /dev/null
+++ b/test/src/client/unified-form/content-tab/__snapshots__/content-tab.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ContentTab should render correctly 1`] = `"Works Copy authors from AC for this work
"`;
diff --git a/test/src/client/unified-form/content-tab/__snapshots__/work-row.jsx.snap b/test/src/client/unified-form/content-tab/__snapshots__/work-row.jsx.snap
new file mode 100644
index 0000000000..84f2a74b91
--- /dev/null
+++ b/test/src/client/unified-form/content-tab/__snapshots__/work-row.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`WorkRow should render correctly 1`] = `""`;
diff --git a/test/src/client/unified-form/content-tab/content-tab.jsx b/test/src/client/unified-form/content-tab/content-tab.jsx
new file mode 100644
index 0000000000..78bb710137
--- /dev/null
+++ b/test/src/client/unified-form/content-tab/content-tab.jsx
@@ -0,0 +1,71 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import {BASE_PROPS} from '../helpers';
+import ContentTab from '../../../../../src/client/unified-form/content-tab/content-tab';
+import Immutable from 'immutable';
+import {Provider} from 'react-redux';
+import configureStore from 'redux-mock-store';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+const initialState = Immutable.fromJS({
+ Works: {
+ 0: {},
+ 1: {}
+ }
+});
+const props = {
+ ...BASE_PROPS
+};
+
+describe('ContentTab', () => {
+ let store;
+ let wrapper;
+ let dispatchSpy;
+ const newWork = {
+ name: 'Dummy'
+ };
+ beforeEach(() => {
+ store = configureStore()(initialState);
+ dispatchSpy = spy();
+ store.dispatch = dispatchSpy;
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ it('should render correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should have correct number of work rows by default', () => {
+ const rows = wrapper.find('WorkRow');
+ expect(rows.length).to.equal(2);
+ });
+
+ it('should trigger add work action', () => {
+ const select = wrapper.find('Select').at(2);
+ select.props().onChange(newWork, {});
+ expect(dispatchSpy.callCount).equal(1);
+ const {payload} = dispatchSpy.args[0][0];
+ expect(payload.value).equal(newWork);
+ });
+
+ it('should correctly set check property', () => {
+ const select = wrapper.find('Select').at(2);
+ const checkbox = wrapper.find('input.form-check-input').at(2);
+ checkbox.simulate('change');
+ select.props().onChange(newWork, {});
+ expect(dispatchSpy.callCount).equal(1);
+ const {payload} = dispatchSpy.args[0][0];
+ expect(payload?.value?.checked).equal(true);
+ });
+});
diff --git a/test/src/client/unified-form/content-tab/work-row.jsx b/test/src/client/unified-form/content-tab/work-row.jsx
new file mode 100644
index 0000000000..ac16057015
--- /dev/null
+++ b/test/src/client/unified-form/content-tab/work-row.jsx
@@ -0,0 +1,74 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import {BASE_PROPS} from '../helpers';
+import Immutable from 'immutable';
+import {Provider} from 'react-redux';
+import WorkRow from '../../../../../src/client/unified-form/content-tab/work-row';
+import configureStore from 'redux-mock-store';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+const initialState = Immutable.fromJS({
+ Works: {
+ n0: {
+ id: 0
+ }
+ }
+});
+const props = {
+ ...BASE_PROPS
+};
+
+describe('WorkRow', () => {
+ let store;
+ let wrapper;
+ let dispatchSpy;
+ beforeEach(() => {
+ store = configureStore()(initialState);
+ dispatchSpy = spy();
+ store.dispatch = dispatchSpy;
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ it('should render correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+
+ it('should trigger delete work action', () => {
+ const deleteButton = wrapper.find('button.btn-danger').at(0);
+ deleteButton.simulate('click');
+ const payload = dispatchSpy.args[0][0];
+ expect(dispatchSpy.callCount).equal(1);
+ expect(payload.type).equal('REMOVE_WORK');
+ expect(payload.payload).equal('n0');
+ });
+
+ it('should update the work value', () => {
+ const select = wrapper.find('Select').at(0);
+ select.props().onChange({name: 'Dummy'}, {});
+ const payload = dispatchSpy.args[0][0];
+ expect(dispatchSpy.callCount).equal(1);
+ expect(payload.type).equal('UPDATE_WORK');
+ expect(payload.payload.id).equal('n0');
+ expect(payload?.payload?.value?.name).equal('Dummy');
+ });
+
+ it('should update checkbox on toggle', () => {
+ const checkbox = wrapper.find('input.form-check-input').at(0);
+ checkbox.simulate('change');
+ const payload = dispatchSpy.args[0][0];
+ expect(dispatchSpy.callCount).equal(1);
+ expect(payload.type).equal('TOGGLE_COPY_AUTHOR_CREDITS');
+ expect(payload.payload).equal('n0');
+ });
+});
diff --git a/test/src/client/unified-form/cover-tab/__snapshots__/isbn-field.jsx.snap b/test/src/client/unified-form/cover-tab/__snapshots__/isbn-field.jsx.snap
new file mode 100644
index 0000000000..f1fa5880ea
--- /dev/null
+++ b/test/src/client/unified-form/cover-tab/__snapshots__/isbn-field.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ISBNField should render with given state from Redux store 1`] = `""`;
diff --git a/test/src/client/unified-form/cover-tab/isbn-field.jsx b/test/src/client/unified-form/cover-tab/isbn-field.jsx
new file mode 100644
index 0000000000..7476a2a6f0
--- /dev/null
+++ b/test/src/client/unified-form/cover-tab/isbn-field.jsx
@@ -0,0 +1,53 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import ISBNField from '../../../../../src/client/unified-form/cover-tab/isbn-field';
+import Immutable from 'immutable';
+import {Provider} from 'react-redux';
+import configureStore from 'redux-mock-store';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+const initialState = Immutable.fromJS(
+ {
+ ISBN: {
+ type: 1,
+ value: 'nbsi'
+ }
+ }
+);
+const mockStore = configureStore();
+describe('ISBNField', () => {
+ let store;
+ let wrapper;
+ const dispatchSpy = spy();
+ before(() => {
+ store = mockStore(initialState);
+ store.dispatch = dispatchSpy;
+ wrapper = mount(
+
+
+
+ );
+ });
+ it('should render with given state from Redux store', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should have correct default input value', () => {
+ const inputEl = wrapper.find('input');
+ expect(inputEl.props().defaultValue).to.equal(initialState.getIn(['ISBN', 'value']));
+ });
+ it('should correctly dispatch change input action', () => {
+ const inputEl = wrapper.find('input');
+ const newValue = '9783161484100';
+ inputEl.simulate('change', {target: {value: newValue}});
+ expect(dispatchSpy.callCount).equal(2);
+ expect(dispatchSpy.args[0][0].payload).equal(9);
+ expect(dispatchSpy.args[1][0].payload).equal(newValue);
+ });
+});
diff --git a/test/src/client/unified-form/helpers.ts b/test/src/client/unified-form/helpers.ts
new file mode 100644
index 0000000000..9d1f1804d3
--- /dev/null
+++ b/test/src/client/unified-form/helpers.ts
@@ -0,0 +1,22 @@
+export const BASE_PROPS = {
+ allIdentifierTypes: [],
+ entityType: 'Author'
+};
+export const emptyCoverTabState = {
+ ISBN: {
+ type: null,
+ value: ''
+ },
+ authorCreditEditor: {
+ n0: {
+ author: null
+ }
+ },
+ identifierEditor: {},
+ nameSection: {
+ disambiguation: '',
+ language: null,
+ name: '',
+ sortName: ''
+ }
+};
diff --git a/test/src/client/unified-form/submit-tab/__snapshots__/summary.jsx.snap b/test/src/client/unified-form/submit-tab/__snapshots__/summary.jsx.snap
new file mode 100644
index 0000000000..0d41f689fc
--- /dev/null
+++ b/test/src/client/unified-form/submit-tab/__snapshots__/summary.jsx.snap
@@ -0,0 +1,3 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SummarySection should render correctly 1`] = `""`;
diff --git a/test/src/client/unified-form/submit-tab/summary.jsx b/test/src/client/unified-form/submit-tab/summary.jsx
new file mode 100644
index 0000000000..ae5c26c4e8
--- /dev/null
+++ b/test/src/client/unified-form/submit-tab/summary.jsx
@@ -0,0 +1,53 @@
+// eslint-disable-next-line import/no-unassigned-import
+import '../../../../../enzyme.config';
+import * as React from 'react';
+import {expect, use} from 'chai';
+import {BASE_PROPS} from '../helpers';
+import Immutable from 'immutable';
+import {Provider} from 'react-redux';
+import SummarySection from '../../../../../src/client/unified-form/submit-tab/summary';
+import configureStore from 'redux-mock-store';
+import {jestSnapshotPlugin} from 'mocha-chai-jest-snapshot';
+import {mount} from 'enzyme';
+import {spy} from 'sinon';
+
+
+use(jestSnapshotPlugin());
+
+const initialState = Immutable.fromJS({
+ Authors: {},
+ EditionGroups: {},
+ Publishers: {},
+ Works: {
+ n0: {
+ __isNew__: true,
+ text: 'Dummy'
+ }
+ }
+});
+
+describe('SummarySection', () => {
+ let store;
+ let wrapper;
+ let dispatchSpy;
+ beforeEach(() => {
+ store = configureStore()(initialState);
+ dispatchSpy = spy();
+ store.dispatch = dispatchSpy;
+ wrapper = mount(
+
+
+
+ );
+ });
+
+ it('should render correctly', () => {
+ expect(wrapper.html()).toMatchSnapshot();
+ });
+ it('should show proper summary', () => {
+ const summaries = wrapper.find('li');
+ expect(summaries.length).to.equal(3);
+ const worksPreview = summaries.last().find('span.entities-preview');
+ expect(worksPreview.text()).to.equal('Dummy');
+ });
+});
diff --git a/test/src/client/unified-form/validators/test-cover-tab.js b/test/src/client/unified-form/validators/test-cover-tab.js
new file mode 100644
index 0000000000..309b1636df
--- /dev/null
+++ b/test/src/client/unified-form/validators/test-cover-tab.js
@@ -0,0 +1,42 @@
+import {isCoverTabEmpty, validateISBN} from '../../../../../src/client/unified-form/validators/cover-tab';
+import {emptyCoverTabState} from '../helpers';
+import {expect} from 'chai';
+
+
+function describeISBNState() {
+ it('should be false for invalid isbn type', () => {
+ const isbn = {
+ type: null,
+ value: 'someisbn'
+ };
+ const isValid = validateISBN(isbn);
+ expect(isValid).to.be.not.true;
+ });
+ it('should be true for valid isbn type', () => {
+ const isbn = {
+ type: 1,
+ value: 'someisbn'
+ };
+ const isValid = validateISBN(isbn);
+ expect(isValid).to.be.true;
+ });
+}
+function describeCoverTabValidators() {
+ it('should be false for modified cover tab state', () => {
+ const coverTabState = {...emptyCoverTabState, ISBN: {type: 1, value: 'someisbn'}};
+ const isEmpty = isCoverTabEmpty(coverTabState);
+ expect(isEmpty).to.be.not.true;
+ });
+ it('should be true for unmodified cover tab state', () => {
+ const coverTabState = {...emptyCoverTabState};
+ const isEmpty = isCoverTabEmpty(coverTabState);
+ expect(isEmpty).to.be.true;
+ });
+}
+
+function tests() {
+ describe('validateISBNState', describeISBNState);
+ describe('validateCoverTabState', describeCoverTabValidators);
+}
+
+describe('CoverTabValidators', tests);
diff --git a/test/src/client/unified-form/validators/test-detail-tab.js b/test/src/client/unified-form/validators/test-detail-tab.js
new file mode 100644
index 0000000000..819bd584e8
--- /dev/null
+++ b/test/src/client/unified-form/validators/test-detail-tab.js
@@ -0,0 +1,24 @@
+import {initialEditionSection, isDetailTabEmpty} from '../../../../../src/client/unified-form/validators/detail-tab';
+import {expect} from 'chai';
+
+
+const emptyDetailTabState = {
+ annotationSection: {
+ content: ''
+ },
+ editionSection: initialEditionSection
+};
+
+describe('DetailTabValidators', () => {
+ it('should be false for modified detail-tab state', () => {
+ const detailTabState = {...emptyDetailTabState, annotationSection: {
+ content: 'some annotation'
+ }};
+ const isEmpty = isDetailTabEmpty(detailTabState);
+ expect(isEmpty).to.be.not.true;
+ });
+ it('should be true for unmodified detail-tab state', () => {
+ const isEmpty = isDetailTabEmpty(emptyDetailTabState);
+ expect(isEmpty).to.be.true;
+ });
+});
diff --git a/test/src/server/routes/relationship.js b/test/src/server/routes/relationship.js
new file mode 100644
index 0000000000..aff2d867bf
--- /dev/null
+++ b/test/src/server/routes/relationship.js
@@ -0,0 +1,116 @@
+import {createEdition, createEditor, createWork, getRandomUUID, truncateEntities} from '../../../test-helpers/create-entities';
+import app from '../../../../src/server/app';
+import chai from 'chai';
+import chaiHttp from 'chai-http';
+import {get} from 'lodash';
+import {getEntityByBBID} from '../../../../src/common/helpers/utils';
+import orm from '../../../bookbrainz-data';
+import {workToFormState} from '../../../../src/server/routes/entity/work';
+
+
+const {RelationshipType} = orm;
+chai.use(chaiHttp);
+const {expect} = chai;
+
+describe('Relationship', () => {
+ const wBBID = getRandomUUID();
+ const eBBID = getRandomUUID();
+ const relationshipTypeData = {
+ description: 'test descryption',
+ id: 1,
+ label: 'test label',
+ linkPhrase: 'test phrase',
+ reverseLinkPhrase: 'test reverse link phrase',
+ sourceEntityType: 'Edition',
+ targetEntityType: 'Work'
+ };
+ let work;
+ let agent;
+ const commonRels = {
+ relationshipType: {
+ id: 1
+ },
+ sourceEntity: {
+ bbid: eBBID,
+ type: 'Edition'
+ },
+ targetEntity: {
+ bbid: wBBID,
+ type: 'Work'
+ }
+ };
+ before(async () => {
+ await truncateEntities();
+ await createEditor(123456);
+ await new RelationshipType(relationshipTypeData).save(null, {method: 'insert'});
+ createEdition(eBBID);
+ work = (await (await createWork(wBBID, {relationshipSetId: null})).load('aliasSet.aliases')).toJSON();
+ agent = await chai.request.agent(app);
+ await agent.get('/cb');
+ });
+ after((done) => {
+ // Clear DB tables then close superagent server
+ truncateEntities().then(() => {
+ agent.close(done);
+ });
+ });
+ it('should be able to add relationship on an entity', async () => {
+ work.relationships = [];
+ work.annotation = {content: ''};
+ const entityState = workToFormState(work);
+ entityState.relationshipSection.relationships = {
+ 0: {
+ isAdded: true,
+ ...commonRels
+ }
+ };
+ entityState.submissionSection = {note: ''};
+ const res = await agent.post(`/work/${wBBID}/edit/handler`).send(entityState);
+ expect(res).to.have.status(200);
+ const fetchedworkEntity = await getEntityByBBID(orm, wBBID);
+ expect(Boolean(fetchedworkEntity)).to.be.true;
+ const relationships = get(fetchedworkEntity, ['relationshipSet', 'relationships'], []);
+ expect(relationships.length).to.be.equal(1);
+ expect(get(relationships[0], 'targetBbid')).to.be.equal(wBBID);
+ expect(get(relationships[0], 'sourceBbid')).to.be.equal(eBBID);
+ });
+ it('should be able to remove relationship of an entity', async () => {
+ work.relationships = [];
+ work.annotation = {content: ''};
+ const entityState = workToFormState(work);
+ entityState.relationshipSection.relationships = {
+ 0: {
+ isRemoved: true,
+ ...commonRels
+ }
+ };
+ entityState.submissionSection = {note: ''};
+ const res = await agent.post(`/work/${wBBID}/edit/handler`).send(entityState);
+ expect(res).to.have.status(200);
+ const {body} = res;
+ expect(body?.relationshipSetId).null;
+ });
+ it('should be able to add new relationships without having previous relationships', async () => {
+ work.relationships = [];
+ work.annotation = {content: ''};
+ const entityState = workToFormState(work);
+ entityState.relationshipSection.relationships = {
+ 0: {
+ isAdded: true,
+ ...commonRels
+ }
+ };
+ entityState.submissionSection = {note: ''};
+ // mimicing the behavior when more than one users edit the relationships on the same entity
+ await agent.post(`/work/${wBBID}/edit/handler`).send(entityState);
+ const res = await agent.post(`/work/${wBBID}/edit/handler`).send(entityState);
+ expect(res).to.have.status(200);
+ // should now have two relationships in total
+ const fetchedworkEntity = await getEntityByBBID(orm, wBBID);
+ expect(Boolean(fetchedworkEntity)).to.be.true;
+ const relationships = get(fetchedworkEntity, ['relationshipSet', 'relationships'], []);
+ expect(relationships.length).to.be.equal(2);
+ expect(get(relationships[1], 'targetBbid')).to.be.equal(wBBID);
+ expect(get(relationships[1], 'sourceBbid')).to.be.equal(eBBID);
+ });
+});
diff --git a/test/src/server/routes/unifiedform.js b/test/src/server/routes/unifiedform.js
new file mode 100644
index 0000000000..48fdf5f442
--- /dev/null
+++ b/test/src/server/routes/unifiedform.js
@@ -0,0 +1,311 @@
+import {authorWorkRelationshipTypeData, baseState, createAuthor, createEditionGroup,
+ createEditor, createPublisher, createWork, getRandomUUID, languageAttribs, truncateEntities} from '../../../test-helpers/create-entities';
+import {every, forOwn, get, map} from 'lodash';
+import app from '../../../../src/server/app';
+import chai from 'chai';
+import chaiHttp from 'chai-http';
+import {getEntityByBBID} from '../../../../src/common/helpers/utils';
+import orm from '../../../bookbrainz-data';
+
+
+const {Language, RelationshipType} = orm;
+const relationshipTypeData = {
+ description: 'test descryption',
+ label: 'test label',
+ linkPhrase: 'test phrase',
+ reverseLinkPhrase: 'test reverse link phrase',
+ sourceEntityType: 'Edition',
+ targetEntityType: 'Work'
+};
+chai.use(chaiHttp);
+const {expect} = chai;
+
+function areKeysEqual(fromObj, toObj) {
+ return every(map(fromObj, (value, key) => toObj[key] === value));
+}
+function testDefaultAlias(entity, languageId) {
+ const expectedDefaultAlias = {
+ languageId,
+ name: baseState.nameSection.name,
+ primary: true,
+ sortName: baseState.nameSection.sortName
+ };
+ const {defaultAlias: actualDefaultAlias} = entity;
+ return areKeysEqual(expectedDefaultAlias, actualDefaultAlias);
+}
+
+describe('Unified form routes', () => {
+ let agent;
+ let newLanguage;
+ let newRelationshipType;
+ let authorWorkRelationshipType;
+ const wBBID = getRandomUUID();
+ const pBBID = getRandomUUID();
+ const egBBID = getRandomUUID();
+ const aBBID = getRandomUUID();
+ before(async () => {
+ try {
+ await createEditor(123456);
+ await createWork(wBBID);
+ await createPublisher(pBBID);
+ await createEditionGroup(egBBID);
+ await createAuthor(aBBID);
+ newRelationshipType = await new RelationshipType(relationshipTypeData)
+ .save(null, {method: 'insert'});
+ newLanguage = await new Language({...languageAttribs})
+ .save(null, {method: 'insert'});
+ authorWorkRelationshipType = await new RelationshipType(authorWorkRelationshipTypeData)
+ .save(null, {method: 'insert'});
+ }
+ catch (error) {
+ // console.log(error);
+ }
+ // Log in; use agent to use logged in session
+ agent = await chai.request.agent(app);
+ await agent.get('/cb');
+ });
+
+ after(truncateEntities);
+ it('should not throw error while creating single entity', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {},
+ type: 'Edition'
+ }};
+ postData.b0.nameSection.language = newLanguage.id;
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(1);
+ const editionEntity = createdEntities[0];
+ const fetchedEditionEntity = await getEntityByBBID(orm, editionEntity.bbid);
+ expect(Boolean(fetchedEditionEntity)).to.be.true;
+ expect(testDefaultAlias(fetchedEditionEntity, newLanguage.id)).to.be.true;
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error while editing single entity', async () => {
+ const postData = {b0: {
+ __isNew__: false,
+ id: wBBID,
+ nameSection: {},
+ submissionSection: {
+ note: 'note',
+ submitError: '',
+ submitted: false
+ },
+ type: 'Work'
+ }};
+ const newName = 'changedName';
+ postData.b0.nameSection.name = newName;
+ const res = await agent.post('/create/handler').send(postData);
+ const editEntities = res.body;
+ expect(editEntities.length).equal(1);
+ const workEntity = editEntities[0];
+ const fetchedworkEntity = await getEntityByBBID(orm, workEntity.bbid);
+ expect(Boolean(fetchedworkEntity)).to.be.true;
+ expect(fetchedworkEntity.defaultAlias.name).to.be.equal(newName);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error while adding relationship to single entity', async () => {
+ // we need to pass extra id and __isNew__ attributes
+ const postData = {b0: {
+ __isNew__: false,
+ id: wBBID,
+ relationshipSection: {
+ relationships: {
+ a0: {
+ attributeSetId: null,
+ attributes: [],
+ isAdded: true,
+ relationshipType: {
+ id: authorWorkRelationshipType.id
+ },
+ rowId: 'a0',
+ sourceEntity: {
+ bbid: aBBID
+ },
+ targetEntity: {
+ bbid: wBBID
+ }
+ }
+ }
+ },
+ submissionSection: {
+ note: 'note',
+ submitError: '',
+ submitted: false
+ },
+ type: 'Work'
+ }};
+ const res = await agent.post('/create/handler').send(postData);
+ const editEntities = res.body;
+ expect(editEntities.length).equal(1);
+ const workEntity = editEntities[0];
+ const fetchedworkEntity = await getEntityByBBID(orm, workEntity.bbid);
+ expect(Boolean(fetchedworkEntity)).to.be.true;
+ const relationships = get(fetchedworkEntity, ['relationshipSet', 'relationships'], []);
+ // one relationship added on creation
+ expect(relationships.length).to.be.equal(2);
+ expect(get(relationships[1], 'targetBbid')).to.be.equal(wBBID);
+ expect(get(relationships[1], 'sourceBbid')).to.be.equal(aBBID);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error while creating multiple entities', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {},
+ type: 'Edition'
+ },
+ b1: {
+ ...baseState,
+ type: 'Work',
+ workSection: {
+ languages: [],
+ type: null
+ }
+ }};
+ forOwn(postData, (value) => {
+ value.nameSection.language = newLanguage.id;
+ });
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(2);
+ const conditions = await map(createdEntities, async (entity) => {
+ const fetchedEntity = await getEntityByBBID(orm, entity.bbid);
+ return !fetchedEntity ? false : testDefaultAlias(fetchedEntity, newLanguage.id);
+ });
+ expect(every(conditions)).to.be.true;
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error when linking existing works to edition', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {},
+ relationshipSection: {
+ relationships: {
+ n0: {
+ attributeSetId: null,
+ attributes: [],
+ isAdded: true,
+ relationshipType: {
+ id: newRelationshipType.id
+ },
+ rowID: 'n0',
+ sourceEntity: {
+ },
+ targetEntity: {
+ bbid: wBBID
+ }
+ }
+ }
+ },
+ type: 'Edition'
+ }};
+ forOwn(postData, (value) => {
+ value.nameSection.language = newLanguage.id;
+ });
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(1);
+ const editionEntity = createdEntities.find((entity) => entity.type === 'Edition');
+ const fetchedEditionEntity = await getEntityByBBID(orm, editionEntity.bbid);
+ expect(Boolean(fetchedEditionEntity)).to.be.true;
+ const relationship = fetchedEditionEntity.relationshipSet.relationships[0];
+ expect(relationship.sourceBbid).equal(fetchedEditionEntity.bbid);
+ expect(relationship.targetBbid).equal(wBBID);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error while linking existing publisher to edition', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {
+ publisher: {
+ 0: {
+ id: pBBID
+ }
+ }
+ },
+ type: 'Edition'
+ }};
+ postData.b0.nameSection.language = newLanguage.id;
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(1);
+ const editionEntity = createdEntities.find((entity) => entity.type === 'Edition');
+ const fetchedEditionEntity = await getEntityByBBID(orm, editionEntity.bbid, ['publisherSet.publishers']);
+ expect(Boolean(fetchedEditionEntity)).to.be.true;
+ const publisherId = fetchedEditionEntity.publisherSet.publishers[0].bbid;
+ expect(publisherId).equal(pBBID);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+
+ it('should not throw error while linking existing edition-group to edition', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {
+ editionGroup: {
+ id: egBBID
+ }
+ },
+ type: 'Edition'
+ }};
+ postData.b0.nameSection.language = newLanguage.id;
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(1);
+ const editionEntity = createdEntities.find((entity) => entity.type === 'Edition');
+ const fetchedEditionEntity = await getEntityByBBID(orm, editionEntity.bbid, ['editionGroup']);
+ expect(Boolean(fetchedEditionEntity)).to.be.true;
+ const editionGroupBbid = fetchedEditionEntity.editionGroup.bbid;
+ expect(editionGroupBbid).equal(egBBID);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should not throw error while linking existing author to edition using AC', async () => {
+ const postData = {b0: {
+ ...baseState,
+ authorCreditEditor: {
+ n0: {
+ author: {
+ id: aBBID
+ },
+ joinPhrase: '',
+ name: 'author1'
+ }
+ },
+ editionSection: {},
+ type: 'Edition'
+ }};
+ postData.b0.nameSection.language = newLanguage.id;
+ const res = await agent.post('/create/handler').send(postData);
+ const createdEntities = res.body;
+ expect(createdEntities.length).equal(1);
+ const editionEntity = createdEntities.find((entity) => entity.type === 'Edition');
+ const fetchedEditionEntity = await getEntityByBBID(orm, editionEntity.bbid, ['authorCredit']);
+ expect(Boolean(fetchedEditionEntity)).to.be.true;
+ expect(fetchedEditionEntity.authorCredit.authorCount).equal(1);
+ expect(res).to.be.ok;
+ expect(res).to.have.status(200);
+ });
+
+ it('should throw bad request error while posting invalid form', async () => {
+ const postData = {b0: {
+ ...baseState,
+ editionSection: {}
+ }};
+ const res = await agent.post('/create/handler').send(postData);
+ expect(res).to.have.status(400);
+ });
+});
diff --git a/test/test-fun-runner.js b/test/test-fun-runner.js
index b2de621811..e0701b2b10 100644
--- a/test/test-fun-runner.js
+++ b/test/test-fun-runner.js
@@ -29,7 +29,7 @@ const funRunnerDays = 6;
function rewireEditsInDaysTwo(threshold) {
return common.rewire(Achievement, {
- getEditsInDays: (editorId, days) => {
+ getConsecutiveDaysWithEdits: (editorId, days) => {
let editPromise;
if (days === funRunnerDays) {
editPromise = Promise.resolve(threshold);
@@ -44,7 +44,7 @@ function rewireEditsInDaysTwo(threshold) {
function rewireEditsInDaysThree(threshold) {
return common.rewire(Achievement, {
- getEditsInDays: (_orm, editorId, days) => {
+ getConsecutiveDaysWithEdits: (_orm, editorId, days) => {
let editPromise;
if (days === funRunnerDays) {
editPromise = Promise.resolve(threshold);
diff --git a/test/test-helpers/create-entities.js b/test/test-helpers/create-entities.js
index c7709e10cc..a62e1881aa 100644
--- a/test/test-helpers/create-entities.js
+++ b/test/test-helpers/create-entities.js
@@ -46,6 +46,38 @@ export const seedInitialState = {
submissionSection: 'note'
};
+export const baseState = {
+ aliasEditor: {},
+ annotationSection: {
+ content: ''
+ },
+ authorCreditEditor: {},
+ identifierEditor: {},
+ nameSection: {
+ disambiguation: '',
+ language: 42,
+ name: 'Entity name',
+ sortName: 'Entity sort name'
+ },
+ relationshipSection: {
+ relationships: {}
+ },
+ submissionSection: {
+ note: 'first entity',
+ submitError: '',
+ submitted: false
+ }
+};
+
+export const authorWorkRelationshipTypeData = {
+ description: 'test descryption',
+ label: 'test label',
+ linkPhrase: 'test phrase',
+ reverseLinkPhrase: 'test reverse link phrase',
+ sourceEntityType: 'Author',
+ targetEntityType: 'Work'
+};
+
export const editorTypeAttribs = {
label: 'test_type'
};
@@ -59,7 +91,7 @@ export const editorAttribs = {
typeId: 1
};
-const languageAttribs = {
+export const languageAttribs = {
frequency: 1,
isoCode1: 'en',
isoCode2b: 'eng',
diff --git a/test/test-marathoner.js b/test/test-marathoner.js
index c06b1baab4..13e6e64533 100644
--- a/test/test-marathoner.js
+++ b/test/test-marathoner.js
@@ -29,7 +29,7 @@ const marathonerThreshold = 30;
function rewireEditsInDaysTwo(threshold) {
return common.rewire(Achievement, {
- getEditsInDays: (editorId, days) => {
+ getConsecutiveDaysWithEdits: (editorId, days) => {
let editPromise;
if (days === marathonerDays) {
editPromise = Promise.resolve(threshold);
@@ -44,7 +44,7 @@ function rewireEditsInDaysTwo(threshold) {
function rewireEditsInDaysThree(threshold) {
return common.rewire(Achievement, {
- getEditsInDays: (_orm, editorId, days) => {
+ getConsecutiveDaysWithEdits: (_orm, editorId, days) => {
let editPromise;
if (days === marathonerDays) {
editPromise = Promise.resolve(threshold);
diff --git a/webpack.client.js b/webpack.client.js
index 4596dcd838..8e24cef9b8 100644
--- a/webpack.client.js
+++ b/webpack.client.js
@@ -25,6 +25,7 @@ const clientConfig = {
deletion: ['./controllers/deletion.js'],
preview: ['./controllers/preview.js'],
error: ['./controllers/error.js'],
+ externalService: ['./controllers/externalService.js'],
index: ['./controllers/index.js'],
registrationDetails: ['./controllers/registrationDetails.js'],
revision: ['./controllers/revision.js'],
@@ -36,6 +37,7 @@ const clientConfig = {
'editor/editor': ['./controllers/editor/editor.js'],
'entity/entity': ['./controllers/entity/entity.js'],
'entity-editor': ['./entity-editor/controller.js'],
+ 'unified-form':['./unified-form/controller.js'],
'entity-merge': ['./entity-editor/entity-merge.tsx'],
style: './stylesheets/style.scss'
},
diff --git a/yarn.lock b/yarn.lock
index ba5b1aa2fb..ef64aeca3c 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,6 +2,14 @@
# yarn lockfile v1
+"@ampproject/remapping@^2.1.0":
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d"
+ integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.1.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
"@apidevtools/json-schema-ref-parser@^9.0.6":
version "9.0.9"
resolved "https://registry.yarnpkg.com/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.9.tgz#d720f9256e3609621280584f2b47ae165359268b"
@@ -65,6 +73,13 @@
dependencies:
"@babel/highlight" "^7.16.0"
+"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a"
+ integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==
+ dependencies:
+ "@babel/highlight" "^7.18.6"
+
"@babel/code-frame@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789"
@@ -77,6 +92,11 @@
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.17.7.tgz#078d8b833fbbcc95286613be8c716cef2b519fa2"
integrity sha512-p8pdE6j0a29TNGebNm7NzYZWB3xVZJBZ7XGs42uAKzQo8VQ3F0By/cQCtUEABwIqw5zo6WA4NbmxsfzADzMKnQ==
+"@babel/compat-data@^7.18.8":
+ version "7.18.8"
+ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d"
+ integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==
+
"@babel/core@^7.10.1":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.0.tgz#c4ff44046f5fe310525cc9eb4ef5147f0c5374d4"
@@ -98,6 +118,27 @@
semver "^6.3.0"
source-map "^0.5.0"
+"@babel/core@^7.11.6", "@babel/core@^7.12.3":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59"
+ integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==
+ dependencies:
+ "@ampproject/remapping" "^2.1.0"
+ "@babel/code-frame" "^7.18.6"
+ "@babel/generator" "^7.18.9"
+ "@babel/helper-compilation-targets" "^7.18.9"
+ "@babel/helper-module-transforms" "^7.18.9"
+ "@babel/helpers" "^7.18.9"
+ "@babel/parser" "^7.18.9"
+ "@babel/template" "^7.18.6"
+ "@babel/traverse" "^7.18.9"
+ "@babel/types" "^7.18.9"
+ convert-source-map "^1.7.0"
+ debug "^4.1.0"
+ gensync "^1.0.0-beta.2"
+ json5 "^2.2.1"
+ semver "^6.3.0"
+
"@babel/generator@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.0.tgz#d40f3d1d5075e62d3500bccb67f3daa8a95265b2"
@@ -116,6 +157,15 @@
jsesc "^2.5.1"
source-map "^0.5.0"
+"@babel/generator@^7.18.9", "@babel/generator@^7.7.2":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5"
+ integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug==
+ dependencies:
+ "@babel/types" "^7.18.9"
+ "@jridgewell/gen-mapping" "^0.3.2"
+ jsesc "^2.5.1"
+
"@babel/helper-annotate-as-pure@^7.16.0", "@babel/helper-annotate-as-pure@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.16.7.tgz#bb2339a7534a9c128e3102024c60760a3a7f3862"
@@ -141,6 +191,16 @@
browserslist "^4.17.5"
semver "^6.3.0"
+"@babel/helper-compilation-targets@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf"
+ integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==
+ dependencies:
+ "@babel/compat-data" "^7.18.8"
+ "@babel/helper-validator-option" "^7.18.6"
+ browserslist "^4.20.2"
+ semver "^6.3.0"
+
"@babel/helper-create-class-features-plugin@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.16.0.tgz#090d4d166b342a03a9fec37ef4fd5aeb9c7c6a4b"
@@ -196,6 +256,11 @@
dependencies:
"@babel/types" "^7.16.7"
+"@babel/helper-environment-visitor@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be"
+ integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==
+
"@babel/helper-explode-assignable-expression@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.16.0.tgz#753017337a15f46f9c09f674cff10cee9b9d7778"
@@ -212,6 +277,14 @@
"@babel/template" "^7.16.7"
"@babel/types" "^7.16.7"
+"@babel/helper-function-name@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0"
+ integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==
+ dependencies:
+ "@babel/template" "^7.18.6"
+ "@babel/types" "^7.18.9"
+
"@babel/helper-get-function-arity@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.16.7.tgz#ea08ac753117a669f1508ba06ebcc49156387419"
@@ -233,6 +306,13 @@
dependencies:
"@babel/types" "^7.16.7"
+"@babel/helper-hoist-variables@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678"
+ integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==
+ dependencies:
+ "@babel/types" "^7.18.6"
+
"@babel/helper-member-expression-to-functions@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.16.0.tgz#29287040efd197c77636ef75188e81da8bccd5a4"
@@ -254,6 +334,13 @@
dependencies:
"@babel/types" "^7.16.7"
+"@babel/helper-module-imports@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e"
+ integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==
+ dependencies:
+ "@babel/types" "^7.18.6"
+
"@babel/helper-module-transforms@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.16.0.tgz#1c82a8dd4cb34577502ebd2909699b194c3e9bb5"
@@ -268,6 +355,20 @@
"@babel/traverse" "^7.16.0"
"@babel/types" "^7.16.0"
+"@babel/helper-module-transforms@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712"
+ integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==
+ dependencies:
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-module-imports" "^7.18.6"
+ "@babel/helper-simple-access" "^7.18.6"
+ "@babel/helper-split-export-declaration" "^7.18.6"
+ "@babel/helper-validator-identifier" "^7.18.6"
+ "@babel/template" "^7.18.6"
+ "@babel/traverse" "^7.18.9"
+ "@babel/types" "^7.18.9"
+
"@babel/helper-optimise-call-expression@^7.16.0", "@babel/helper-optimise-call-expression@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.16.7.tgz#a34e3560605abbd31a18546bd2aad3e6d9a174f2"
@@ -280,6 +381,11 @@
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
+"@babel/helper-plugin-utils@^7.18.6":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f"
+ integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==
+
"@babel/helper-remap-async-to-generator@^7.16.0", "@babel/helper-remap-async-to-generator@^7.16.4":
version "7.16.4"
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.4.tgz#5d7902f61349ff6b963e07f06a389ce139fbfe6e"
@@ -307,6 +413,13 @@
dependencies:
"@babel/types" "^7.16.0"
+"@babel/helper-simple-access@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea"
+ integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==
+ dependencies:
+ "@babel/types" "^7.18.6"
+
"@babel/helper-skip-transparent-expression-wrappers@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.16.0.tgz#0ee3388070147c3ae051e487eca3ebb0e2e8bb09"
@@ -321,6 +434,13 @@
dependencies:
"@babel/types" "^7.16.7"
+"@babel/helper-split-export-declaration@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075"
+ integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==
+ dependencies:
+ "@babel/types" "^7.18.6"
+
"@babel/helper-validator-identifier@^7.15.7":
version "7.15.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz#220df993bfe904a4a6b02ab4f3385a5ebf6e2389"
@@ -331,11 +451,21 @@
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
+"@babel/helper-validator-identifier@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076"
+ integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==
+
"@babel/helper-validator-option@^7.14.5", "@babel/helper-validator-option@^7.16.7":
version "7.16.7"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
+"@babel/helper-validator-option@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8"
+ integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==
+
"@babel/helper-wrap-function@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.0.tgz#b3cf318afce774dfe75b86767cd6d68f3482e57c"
@@ -355,6 +485,15 @@
"@babel/traverse" "^7.16.3"
"@babel/types" "^7.16.0"
+"@babel/helpers@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9"
+ integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==
+ dependencies:
+ "@babel/template" "^7.18.6"
+ "@babel/traverse" "^7.18.9"
+ "@babel/types" "^7.18.9"
+
"@babel/highlight@^7.10.4", "@babel/highlight@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.0.tgz#6ceb32b2ca4b8f5f361fb7fd821e3fddf4a1725a"
@@ -373,6 +512,15 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/highlight@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf"
+ integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.18.6"
+ chalk "^2.0.0"
+ js-tokens "^4.0.0"
+
"@babel/node@^7.14.2":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.16.0.tgz#855e783ba4cbca88dbdebf4b01c2d95844c4afdf"
@@ -385,6 +533,11 @@
regenerator-runtime "^0.13.4"
v8flags "^3.1.1"
+"@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539"
+ integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg==
+
"@babel/parser@^7.16.0", "@babel/parser@^7.16.3":
version "7.16.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e"
@@ -546,7 +699,14 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-class-properties@^7.12.13":
+"@babel/plugin-syntax-bigint@^7.8.3":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea"
+ integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.8.0"
+
+"@babel/plugin-syntax-class-properties@^7.12.13", "@babel/plugin-syntax-class-properties@^7.8.3":
version "7.12.13"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10"
integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==
@@ -574,6 +734,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.3"
+"@babel/plugin-syntax-import-meta@^7.8.3":
+ version "7.10.4"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51"
+ integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.10.4"
+
"@babel/plugin-syntax-json-strings@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a"
@@ -588,7 +755,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.16.7"
-"@babel/plugin-syntax-logical-assignment-operators@^7.10.4":
+"@babel/plugin-syntax-logical-assignment-operators@^7.10.4", "@babel/plugin-syntax-logical-assignment-operators@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699"
integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==
@@ -602,7 +769,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.8.0"
-"@babel/plugin-syntax-numeric-separator@^7.10.4":
+"@babel/plugin-syntax-numeric-separator@^7.10.4", "@babel/plugin-syntax-numeric-separator@^7.8.3":
version "7.10.4"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97"
integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==
@@ -637,7 +804,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
-"@babel/plugin-syntax-top-level-await@^7.14.5":
+"@babel/plugin-syntax-top-level-await@^7.14.5", "@babel/plugin-syntax-top-level-await@^7.8.3":
version "7.14.5"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c"
integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==
@@ -651,6 +818,13 @@
dependencies:
"@babel/helper-plugin-utils" "^7.14.5"
+"@babel/plugin-syntax-typescript@^7.7.2":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285"
+ integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.18.6"
+
"@babel/plugin-transform-arrow-functions@^7.16.0":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.16.0.tgz#951706f8b449c834ed07bd474c0924c944b95a8e"
@@ -1109,6 +1283,15 @@
"@babel/parser" "^7.16.7"
"@babel/types" "^7.16.7"
+"@babel/template@^7.18.6":
+ version "7.18.6"
+ resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31"
+ integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw==
+ dependencies:
+ "@babel/code-frame" "^7.18.6"
+ "@babel/parser" "^7.18.6"
+ "@babel/types" "^7.18.6"
+
"@babel/traverse@^7.13.0", "@babel/traverse@^7.16.0", "@babel/traverse@^7.16.3":
version "7.16.3"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.3.tgz#f63e8a938cc1b780f66d9ed3c54f532ca2d14787"
@@ -1140,6 +1323,22 @@
debug "^4.1.0"
globals "^11.1.0"
+"@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98"
+ integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg==
+ dependencies:
+ "@babel/code-frame" "^7.18.6"
+ "@babel/generator" "^7.18.9"
+ "@babel/helper-environment-visitor" "^7.18.9"
+ "@babel/helper-function-name" "^7.18.9"
+ "@babel/helper-hoist-variables" "^7.18.6"
+ "@babel/helper-split-export-declaration" "^7.18.6"
+ "@babel/parser" "^7.18.9"
+ "@babel/types" "^7.18.9"
+ debug "^4.1.0"
+ globals "^11.1.0"
+
"@babel/types@^7.0.0-beta.49", "@babel/types@^7.16.0", "@babel/types@^7.4.4":
version "7.16.0"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.0.tgz#db3b313804f96aadd0b776c4823e127ad67289ba"
@@ -1156,6 +1355,21 @@
"@babel/helper-validator-identifier" "^7.16.7"
to-fast-properties "^2.0.0"
+
+"@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3":
+ version "7.18.9"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f"
+ integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.18.6"
+ to-fast-properties "^2.0.0"
+
+"@cospired/i18n-iso-languages@^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/@cospired/i18n-iso-languages/-/i18n-iso-languages-4.0.0.tgz#fbd54bd046e28295a2ab8f3806c77b0d92852b29"
+ integrity sha512-8dKE8TJIhb6/JpwpshTV96Q/fT8kAohnAD2KnOuKkVemhDcWDjurPmzj8NnA25YW4gcKfvJYq/iDbXyB0gZ2jg==
+
+
"@discoveryjs/json-ext@^0.5.0":
version "0.5.6"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f"
@@ -1277,6 +1491,11 @@
resolved "https://registry.yarnpkg.com/@faker-js/faker/-/faker-7.2.0.tgz#c5fe4c34f3a3664ac64fe1a21bac2004ea5faa22"
integrity sha512-dzjQ0LFT+bPLWg0yyV3MpxaLJp/+VW4a0SnjNSWJ4YpJ928LXDOZAN+kB2/JPPisI3Ra0w2BxbD4M9J7o0jcpw==
+"@fortawesome/fontawesome-common-types@6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.1.1.tgz#7dc996042d21fc1ae850e3173b5c67b0549f9105"
+ integrity sha512-wVn5WJPirFTnzN6tR95abCx+ocH+3IFLXAgyavnf9hUmN0CfWoDjPT/BAWsUVwSlYYVBeCLJxaqi7ZGe4uSjBA==
+
"@fortawesome/fontawesome-common-types@^0.2.36":
version "0.2.36"
resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz#b44e52db3b6b20523e0c57ef8c42d315532cb903"
@@ -1289,19 +1508,19 @@
dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.36"
-"@fortawesome/free-brands-svg-icons@^5.14.0":
- version "5.15.4"
- resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-5.15.4.tgz#ec8a44dd383bcdd58aa7d1c96f38251e6fec9733"
- integrity sha512-f1witbwycL9cTENJegcmcZRYyawAFbm8+c6IirLmwbbpqz46wyjbQYLuxOc7weXFXfB7QR8/Vd2u5R3q6JYD9g==
+"@fortawesome/free-brands-svg-icons@^6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.1.1.tgz#3580961d4f42bd51dc171842402f23a18a5480b1"
+ integrity sha512-mFbI/czjBZ+paUtw5NPr2IXjun5KAC8eFqh1hnxowjA4mMZxWz4GCIksq6j9ZSa6Uxj9JhjjDVEd77p2LN2Blg==
dependencies:
- "@fortawesome/fontawesome-common-types" "^0.2.36"
+ "@fortawesome/fontawesome-common-types" "6.1.1"
-"@fortawesome/free-solid-svg-icons@^5.14.0":
- version "5.15.4"
- resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz#2a68f3fc3ddda12e52645654142b9e4e8fbb6cc5"
- integrity sha512-JLmQfz6tdtwxoihXLg6lT78BorrFyCf59SAwBM6qV/0zXyVeDygJVb3fk+j5Qat+Yvcxp1buLTY5iDh1ZSAQ8w==
+"@fortawesome/free-solid-svg-icons@^6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.1.1.tgz#3369e673f8fe8be2fba30b1ec274d47490a830a6"
+ integrity sha512-0/5exxavOhI/D4Ovm2r3vxNojGZioPwmFrKg0ZUH69Q68uFhFPs6+dhAToh6VEQBntxPRYPuT5Cg1tpNa9JUPg==
dependencies:
- "@fortawesome/fontawesome-common-types" "^0.2.36"
+ "@fortawesome/fontawesome-common-types" "6.1.1"
"@fortawesome/react-fontawesome@^0.1.11":
version "0.1.16"
@@ -1324,15 +1543,138 @@
resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+"@istanbuljs/load-nyc-config@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
+ integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==
+ dependencies:
+ camelcase "^5.3.1"
+ find-up "^4.1.0"
+ get-package-type "^0.1.0"
+ js-yaml "^3.13.1"
+ resolve-from "^5.0.0"
+
+"@istanbuljs/schema@^0.1.2":
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98"
+ integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==
+
+"@jest/console@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/console/-/console-28.1.3.tgz#2030606ec03a18c31803b8a36382762e447655df"
+ integrity sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==
+ dependencies:
+ "@jest/types" "^28.1.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
+ slash "^3.0.0"
+
+"@jest/expect-utils@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-28.1.3.tgz#58561ce5db7cd253a7edddbc051fb39dda50f525"
+ integrity sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==
+ dependencies:
+ jest-get-type "^28.0.2"
+
+"@jest/schemas@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905"
+ integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==
+ dependencies:
+ "@sinclair/typebox" "^0.24.1"
+
+"@jest/test-result@^28.1.1":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-28.1.3.tgz#5eae945fd9f4b8fcfce74d239e6f725b6bf076c5"
+ integrity sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==
+ dependencies:
+ "@jest/console" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ collect-v8-coverage "^1.0.0"
+
+"@jest/transform@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-28.1.3.tgz#59d8098e50ab07950e0f2fc0fc7ec462371281b0"
+ integrity sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@jest/types" "^28.1.3"
+ "@jridgewell/trace-mapping" "^0.3.13"
+ babel-plugin-istanbul "^6.1.1"
+ chalk "^4.0.0"
+ convert-source-map "^1.4.0"
+ fast-json-stable-stringify "^2.0.0"
+ graceful-fs "^4.2.9"
+ jest-haste-map "^28.1.3"
+ jest-regex-util "^28.0.2"
+ jest-util "^28.1.3"
+ micromatch "^4.0.4"
+ pirates "^4.0.4"
+ slash "^3.0.0"
+ write-file-atomic "^4.0.1"
+
+"@jest/types@^28.1.3":
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-28.1.3.tgz#b05de80996ff12512bc5ceb1d208285a7d11748b"
+ integrity sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==
+ dependencies:
+ "@jest/schemas" "^28.1.3"
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^17.0.8"
+ chalk "^4.0.0"
+
+"@jridgewell/gen-mapping@^0.1.0":
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996"
+ integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.0"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+
+"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9"
+ integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==
+ dependencies:
+ "@jridgewell/set-array" "^1.0.1"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
"@jridgewell/resolve-uri@^3.0.3":
- version "3.0.5"
- resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.0.5.tgz#68eb521368db76d040a6315cdb24bf2483037b9c"
- integrity sha512-VPeQ7+wH0itvQxnG+lIzWgkysKIr3L9sslimFW55rHMdGu/qCQ5z5h9zq4gI8uBtqkpHhsF4Z/OwExufUCThew==
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78"
+ integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==
+
+"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72"
+ integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+
+"@jridgewell/source-map@^0.3.2":
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.2.tgz#f45351aaed4527a298512ec72f81040c998580fb"
+ integrity sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
"@jridgewell/sourcemap-codec@^1.4.10":
- version "1.4.11"
- resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.11.tgz#771a1d8d744eeb71b6adb35808e1a6c7b9b8c8ec"
- integrity sha512-Fg32GrJo61m+VqYSdRSjRXMjQ06j8YIYfcTqndLYVAaHmroZHLJZCydsWBOTDqXS2v+mjxohBWEMfg97GXmYQg==
+ version "1.4.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24"
+ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==
+
+"@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.9":
+ version "0.3.14"
+ resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed"
+ integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==
+ dependencies:
+ "@jridgewell/resolve-uri" "^3.0.3"
+ "@jridgewell/sourcemap-codec" "^1.4.10"
"@jridgewell/trace-mapping@^0.3.4":
version "0.3.4"
@@ -1395,11 +1737,44 @@
dependencies:
dequal "^2.0.2"
+"@sinclair/typebox@^0.24.1":
+ version "0.24.20"
+ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.20.tgz#11a657875de6008622d53f56e063a6347c51a6dd"
+ integrity sha512-kVaO5aEFZb33nPMTZBxiPEkY+slxiPtqC7QX8f9B3eGOMBvEfuMfxp9DSTTCsRJPumPKjrge4yagyssO4q6qzQ==
+
"@sindresorhus/is@^0.14.0":
version "0.14.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea"
integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==
+"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.3":
+ version "1.8.3"
+ resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d"
+ integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==
+ dependencies:
+ type-detect "4.0.8"
+
+"@sinonjs/fake-timers@>=5", "@sinonjs/fake-timers@^9.1.2":
+ version "9.1.2"
+ resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c"
+ integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==
+ dependencies:
+ "@sinonjs/commons" "^1.7.0"
+
+"@sinonjs/samsam@^6.1.1":
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-6.1.1.tgz#627f7f4cbdb56e6419fa2c1a3e4751ce4f6a00b1"
+ integrity sha512-cZ7rKJTLiE7u7Wi/v9Hc2fs3Ucc3jrWeMgPHbbTCeVAB2S0wOBbYlkJVeNSL04i7fdhT8wIbDq1zhC/PXTD2SA==
+ dependencies:
+ "@sinonjs/commons" "^1.6.0"
+ lodash.get "^4.4.2"
+ type-detect "^4.0.8"
+
+"@sinonjs/text-encoding@^0.7.1":
+ version "0.7.1"
+ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5"
+ integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==
+
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@@ -1412,6 +1787,18 @@
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==
+"@tootallnate/once@2":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
+ integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
+
+"@types/babel__traverse@^7.0.6":
+ version "7.17.1"
+ resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314"
+ integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA==
+ dependencies:
+ "@babel/types" "^7.3.0"
+
"@types/body-parser@*":
version "1.19.2"
resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
@@ -1500,6 +1887,13 @@
"@types/minimatch" "*"
"@types/node" "*"
+"@types/graceful-fs@^4.1.3":
+ version "4.1.5"
+ resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15"
+ integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==
+ dependencies:
+ "@types/node" "*"
+
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
@@ -1513,6 +1907,25 @@
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be"
integrity sha512-DxX1V9P8zdJPYQat1gHyY0xj3efl8gnMVjiM9iCY6y27lj+PoQWkgjt8jDqmovPqULkKVpKRg8J36iQiA+EtEg==
+"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44"
+ integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==
+
+"@types/istanbul-lib-report@*":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686"
+ integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==
+ dependencies:
+ "@types/istanbul-lib-coverage" "*"
+
+"@types/istanbul-reports@^3.0.0":
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff"
+ integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==
+ dependencies:
+ "@types/istanbul-lib-report" "*"
+
"@types/json-schema@*", "@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.7", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9":
version "7.0.9"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.9.tgz#97edc9037ea0c38585320b28964dde3b39e4660d"
@@ -1548,6 +1961,11 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
+"@types/prettier@^2.1.5":
+ version "2.6.3"
+ resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.3.tgz#68ada76827b0010d0db071f739314fa429943d0a"
+ integrity sha512-ymZk3LEC/fsut+/Q5qejp6R9O1rMxz3XaRHDV6kX8MrGAhOSPqVARbDi+EZvInBpw+BnCX3TD240byVkOfQsHg==
+
"@types/prop-types@*", "@types/prop-types@^15.7.3":
version "15.7.4"
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
@@ -1628,6 +2046,11 @@
"@types/mime" "^1"
"@types/node" "*"
+"@types/stack-utils@^2.0.0":
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c"
+ integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==
+
"@types/superagent@^3.8.3":
version "3.8.7"
resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-3.8.7.tgz#1f1ed44634d5459b3a672eb7235a8e7cfd97704c"
@@ -1646,6 +2069,18 @@
resolved "https://registry.yarnpkg.com/@types/warning/-/warning-3.0.0.tgz#0d2501268ad8f9962b740d387c4654f5f8e23e52"
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
+"@types/yargs-parser@*":
+ version "21.0.0"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b"
+ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==
+
+"@types/yargs@^17.0.8":
+ version "17.0.10"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a"
+ integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA==
+ dependencies:
+ "@types/yargs-parser" "*"
+
"@typescript-eslint/eslint-plugin@^4.28.3":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz#c24dc7c8069c7706bc40d99f6fa87edcb2005276"
@@ -1842,22 +2277,22 @@
"@webassemblyjs/ast" "1.11.1"
"@xtuc/long" "4.2.2"
-"@webpack-cli/configtest@^1.1.0":
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.1.0.tgz#8342bef0badfb7dfd3b576f2574ab80c725be043"
- integrity sha512-ttOkEkoalEHa7RaFYpM0ErK1xc4twg3Am9hfHhL7MVqlHebnkYd2wuI/ZqTDj0cVzZho6PdinY0phFZV3O0Mzg==
+"@webpack-cli/configtest@^1.2.0":
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-1.2.0.tgz#7b20ce1c12533912c3b217ea68262365fa29a6f5"
+ integrity sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==
-"@webpack-cli/info@^1.4.0":
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.4.0.tgz#b9179c3227ab09cbbb149aa733475fcf99430223"
- integrity sha512-F6b+Man0rwE4n0409FyAJHStYA5OIZERxmnUfLVwv0mc0V1wLad3V7jqRlMkgKBeAq07jUvglacNaa6g9lOpuw==
+"@webpack-cli/info@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-1.5.0.tgz#6c78c13c5874852d6e2dd17f08a41f3fe4c261b1"
+ integrity sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==
dependencies:
envinfo "^7.7.3"
-"@webpack-cli/serve@^1.6.0":
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.6.0.tgz#2c275aa05c895eccebbfc34cfb223c6e8bd591a2"
- integrity sha512-ZkVeqEmRpBV2GHvjjUZqEai2PpUbuq8Bqd//vEYsp63J8WyexI8ppCqVS3Zs0QADf6aWuPdU+0XsPI647PVlQA==
+"@webpack-cli/serve@^1.7.0":
+ version "1.7.0"
+ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-1.7.0.tgz#e1993689ac42d2b16e9194376cfb6753f6254db1"
+ integrity sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
@@ -1874,6 +2309,11 @@ abab@^2.0.3, abab@^2.0.5:
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a"
integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q==
+abab@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
+ integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
+
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@@ -1920,10 +2360,10 @@ acorn@^7.1.1, acorn@^7.4.0:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
-acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1:
- version "8.6.0"
- resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895"
- integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw==
+acorn@^8.0.4, acorn@^8.2.4, acorn@^8.4.1, acorn@^8.5.0, acorn@^8.7.1:
+ version "8.7.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
+ integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
adjust-sourcemap-loader@^4.0.0:
version "4.0.0"
@@ -1940,6 +2380,21 @@ agent-base@6:
dependencies:
debug "4"
+airbnb-prop-types@^2.16.0:
+ version "2.16.0"
+ resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2"
+ integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg==
+ dependencies:
+ array.prototype.find "^2.1.1"
+ function.prototype.name "^1.1.2"
+ is-regex "^1.1.0"
+ object-is "^1.1.2"
+ object.assign "^4.1.0"
+ object.entries "^1.1.2"
+ prop-types "^15.7.2"
+ prop-types-exact "^1.2.0"
+ react-is "^16.13.1"
+
ajv-formats@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520"
@@ -2027,7 +2482,12 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
dependencies:
color-convert "^2.0.1"
-anymatch@~3.1.2:
+ansi-styles@^5.0.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b"
+ integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==
+
+anymatch@^3.0.3, anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -2115,6 +2575,37 @@ array-unique@^0.3.2:
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
+array.prototype.filter@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/array.prototype.filter/-/array.prototype.filter-1.0.1.tgz#20688792acdb97a09488eaaee9eebbf3966aae21"
+ integrity sha512-Dk3Ty7N42Odk7PjU/Ci3zT4pLj20YvuVnneG/58ICM6bt4Ij5kZaJTVQ9TSaWaIECX2sFyz4KItkVZqHNnciqw==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.0"
+ es-array-method-boxes-properly "^1.0.0"
+ is-string "^1.0.7"
+
+array.prototype.find@^2.1.1:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.2.0.tgz#153b8a28ad8965cd86d3117b07e6596af6f2880d"
+ integrity sha512-sn40qmUiLYAcRb/1HsIQjTTZ1kCy8II8VtZJpMn2Aoen9twULhbWXisfh3HimGqMlHGUul0/TfKCnXg42LuPpQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.4"
+ es-shim-unscopables "^1.0.0"
+
+array.prototype.flat@^1.2.3:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b"
+ integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.2"
+ es-shim-unscopables "^1.0.0"
+
array.prototype.flat@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13"
@@ -2190,6 +2681,17 @@ babel-plugin-dynamic-import-node@^2.3.3:
dependencies:
object.assign "^4.1.0"
+babel-plugin-istanbul@^6.1.1:
+ version "6.1.1"
+ resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73"
+ integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.0.0"
+ "@istanbuljs/load-nyc-config" "^1.0.0"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-instrument "^5.0.4"
+ test-exclude "^6.0.0"
+
babel-plugin-lodash@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/babel-plugin-lodash/-/babel-plugin-lodash-3.3.4.tgz#4f6844358a1340baed182adbeffa8df9967bc196"
@@ -2247,6 +2749,24 @@ babel-plugin-transform-require-ignore@^0.1.1:
resolved "https://registry.yarnpkg.com/babel-plugin-transform-require-ignore/-/babel-plugin-transform-require-ignore-0.1.1.tgz#1abb0f803cc29646dd0057f538fdb4a98d6cb8b2"
integrity sha512-A7EmlVd3ZYWJI3eg3dklAXn1yfq8Y5omHMQgAUo0FwGLYRuL5daLR6+LtCVHi4f9+fStr7HuPkW9rmKtcmY67w==
+babel-preset-current-node-syntax@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b"
+ integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==
+ dependencies:
+ "@babel/plugin-syntax-async-generators" "^7.8.4"
+ "@babel/plugin-syntax-bigint" "^7.8.3"
+ "@babel/plugin-syntax-class-properties" "^7.8.3"
+ "@babel/plugin-syntax-import-meta" "^7.8.3"
+ "@babel/plugin-syntax-json-strings" "^7.8.3"
+ "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3"
+ "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3"
+ "@babel/plugin-syntax-numeric-separator" "^7.8.3"
+ "@babel/plugin-syntax-object-rest-spread" "^7.8.3"
+ "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
+ "@babel/plugin-syntax-optional-chaining" "^7.8.3"
+ "@babel/plugin-syntax-top-level-await" "^7.8.3"
+
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@@ -2308,10 +2828,10 @@ body-parser@1.19.1:
raw-body "2.4.2"
type-is "~1.6.18"
-bookbrainz-data@2.14.1:
- version "2.14.1"
- resolved "https://registry.yarnpkg.com/bookbrainz-data/-/bookbrainz-data-2.14.1.tgz#77eba018f2af1894bef2ea0cfb4ddc4b417f3a12"
- integrity sha512-VrGrRLEWEvKtFfR4/MCUBWjq7XEvrUQQ0dBj2Z90aSZrQy00HEJ5/WhO6xoOeYpnvPk6TRMUc8XZiBhjvNgKyw==
+bookbrainz-data@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bookbrainz-data/-/bookbrainz-data-3.0.0.tgz#87e345243132daed3d0d1bc68b376fe3fa9f6d29"
+ integrity sha512-L5jijPCmAZBmXVaCDIEin2oM8q2BfdP7L7UzJxlpcgPh2oT0Fq9OWgXwqDCiD5OZ1Fxq9k8bDuvCmz/VZ1hRWg==
dependencies:
bookshelf "^1.2.0"
bookshelf-virtuals-plugin "^0.1.1"
@@ -2339,6 +2859,11 @@ bookshelf@^1.2.0:
inflection "^1.12.0"
lodash "^4.17.15"
+boolbase@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
bootstrap@^4.6.1:
version "4.6.1"
resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.6.1.tgz#bc25380c2c14192374e8dec07cf01b2742d222a2"
@@ -2421,6 +2946,23 @@ browserslist@^4.19.1:
node-releases "^2.0.2"
picocolors "^1.0.0"
+browserslist@^4.20.2:
+ version "4.21.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf"
+ integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA==
+ dependencies:
+ caniuse-lite "^1.0.30001366"
+ electron-to-chromium "^1.4.188"
+ node-releases "^2.0.6"
+ update-browserslist-db "^1.0.4"
+
+bser@2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05"
+ integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==
+ dependencies:
+ node-int64 "^0.4.0"
+
buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
@@ -2487,6 +3029,11 @@ callsites@^3.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+camelcase@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
+ integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
+
camelcase@^6.0.0, camelcase@^6.2.0:
version "6.2.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.1.tgz#250fd350cfd555d0d2160b1d51510eaf8326e86e"
@@ -2502,6 +3049,11 @@ caniuse-lite@^1.0.30001313:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001313.tgz#a380b079db91621e1b7120895874e2fd62ed2e2f"
integrity sha512-rI1UN0koZUiKINjysQDuRi2VeSCce3bYJNmDcj3PIKREiAmjakugBul1QSkg/fPrlULYl6oWfGg3PbgOSY9X4Q==
+caniuse-lite@^1.0.30001366:
+ version "1.0.30001367"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001367.tgz#2b97fe472e8fa29c78c5970615d7cd2ee414108a"
+ integrity sha512-XDgbeOHfifWV3GEES2B8rtsrADx4Jf+juKX2SICJcaUhjYBO3bR96kvEIHa15VU6ohtOhBZuPGGYGbXMRn0NCw==
+
chai-arrays@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/chai-arrays/-/chai-arrays-2.2.0.tgz#571479cbc5eca81605ed4eed1e8a2a28552d2a25"
@@ -2554,7 +3106,7 @@ chalk@^2.0.0, chalk@^2.1.0:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^4.0.0, chalk@^4.1.0:
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -2600,6 +3152,31 @@ check-error@^1.0.2:
resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82"
integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=
+cheerio-select@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4"
+ integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==
+ dependencies:
+ boolbase "^1.0.0"
+ css-select "^5.1.0"
+ css-what "^6.1.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+
+cheerio@^1.0.0-rc.3:
+ version "1.0.0-rc.12"
+ resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683"
+ integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==
+ dependencies:
+ cheerio-select "^2.1.0"
+ dom-serializer "^2.0.0"
+ domhandler "^5.0.3"
+ domutils "^3.0.1"
+ htmlparser2 "^8.0.1"
+ parse5 "^7.0.0"
+ parse5-htmlparser2-tree-adapter "^7.0.0"
+
chokidar@3.5.2, chokidar@^3.4.0, chokidar@^3.5.2:
version "3.5.2"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75"
@@ -2640,6 +3217,11 @@ ci-info@^2.0.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+ci-info@^3.2.0:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128"
+ integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==
+
class-utils@^0.3.5:
version "0.3.6"
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
@@ -2730,6 +3312,11 @@ clone@^1.0.2:
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+collect-v8-coverage@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59"
+ integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==
+
collection-visit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0"
@@ -2784,7 +3371,7 @@ commander@6.2.0:
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.0.tgz#b990bfb8ac030aedc6d11bc04d1488ffef56db75"
integrity sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==
-commander@^2.20.0, commander@^2.7.1:
+commander@^2.19.0, commander@^2.20.0, commander@^2.7.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
@@ -2876,7 +3463,7 @@ content-type@~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.5.0, convert-source-map@^1.7.0:
+convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369"
integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==
@@ -2991,6 +3578,22 @@ css-loader@^6.7.1:
postcss-value-parser "^4.2.0"
semver "^7.3.5"
+css-select@^5.1.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6"
+ integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^6.1.0"
+ domhandler "^5.0.2"
+ domutils "^3.0.1"
+ nth-check "^2.0.1"
+
+css-what@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+ integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
@@ -3001,6 +3604,11 @@ cssom@^0.4.4:
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.4.4.tgz#5a66cf93d2d0b661d80bf6a44fb65f5c2e4e0a10"
integrity sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==
+cssom@^0.5.0:
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
+ integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
+
cssom@~0.3.6:
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
@@ -3035,6 +3643,15 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
+data-urls@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
+ integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
+ dependencies:
+ abab "^2.0.6"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+
date-fns@^2.15.0, date-fns@^2.24.0:
version "2.28.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
@@ -3080,7 +3697,7 @@ decamelize@^4.0.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
-decimal.js@^10.2.1:
+decimal.js@^10.2.1, decimal.js@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
@@ -3145,6 +3762,14 @@ define-properties@^1.1.3:
dependencies:
object-keys "^1.0.12"
+define-properties@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1"
+ integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==
+ dependencies:
+ has-property-descriptors "^1.0.0"
+ object-keys "^1.1.1"
+
define-property@^0.2.5:
version "0.2.5"
resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116"
@@ -3223,11 +3848,21 @@ dezalgo@1.0.3:
asap "^2.0.0"
wrappy "1"
+diff-sequences@^28.1.1:
+ version "28.1.1"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6"
+ integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==
+
diff@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+diff@^5.0.0:
+ version "5.1.0"
+ resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
+ integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
+
dir-glob@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
@@ -3235,6 +3870,11 @@ dir-glob@^3.0.1:
dependencies:
path-type "^4.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"
+ integrity sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==
+
doctrine@3.0.0, doctrine@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
@@ -3257,11 +3897,25 @@ dom-helpers@^5.0.1, dom-helpers@^5.2.0, dom-helpers@^5.2.1:
"@babel/runtime" "^7.8.7"
csstype "^3.0.2"
+dom-serializer@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53"
+ integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ entities "^4.2.0"
+
dom-walk@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
+domelementtype@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
domexception@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304"
@@ -3269,11 +3923,34 @@ domexception@^2.0.1:
dependencies:
webidl-conversions "^5.0.0"
+domexception@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
+ integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
+ dependencies:
+ webidl-conversions "^7.0.0"
+
+domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3:
+ version "5.0.3"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31"
+ integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==
+ dependencies:
+ domelementtype "^2.3.0"
+
dompurify@^2.2.7:
version "2.3.3"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c"
integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg==
+domutils@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c"
+ integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==
+ dependencies:
+ dom-serializer "^2.0.0"
+ domelementtype "^2.3.0"
+ domhandler "^5.0.1"
+
dot-prop@^5.2.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@@ -3309,6 +3986,11 @@ electron-to-chromium@^1.3.896:
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.7.tgz#58e0b4ec9096ee1422e4d9e83ceeb05b0cf076eb"
integrity sha512-UPy2MsQw1OdcbxR7fvwWZH/rXcv+V26+uvQVHx0fGa1kqRfydtfOw+NMGAvZJ63hyaH4aEBxbhSEtqbpliSNWA==
+electron-to-chromium@^1.4.188:
+ version "1.4.195"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.195.tgz#139b2d95a42a3f17df217589723a1deac71d1473"
+ integrity sha512-vefjEh0sk871xNmR5whJf9TEngX+KTKS3hOHpjoMpauKkwlGwtMz1H8IaIjAT/GNnX0TbGwAdmVoXCAzXf+PPg==
+
electron-to-chromium@^1.4.76:
version "1.4.76"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.76.tgz#a0494baedaf51094b1c172999919becd9975a934"
@@ -3356,11 +4038,80 @@ enquirer@^2.3.5:
dependencies:
ansi-colors "^4.1.1"
+entities@^4.2.0, entities@^4.3.0:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4"
+ integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==
+
envinfo@^7.7.3:
version "7.8.1"
resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.8.1.tgz#06377e3e5f4d379fea7ac592d5ad8927e0c4d475"
integrity sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==
+enzyme-adapter-react-16@^1.15.6:
+ version "1.15.6"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz#fd677a658d62661ac5afd7f7f541f141f8085901"
+ integrity sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g==
+ dependencies:
+ enzyme-adapter-utils "^1.14.0"
+ enzyme-shallow-equal "^1.0.4"
+ has "^1.0.3"
+ object.assign "^4.1.2"
+ object.values "^1.1.2"
+ prop-types "^15.7.2"
+ react-is "^16.13.1"
+ react-test-renderer "^16.0.0-0"
+ semver "^5.7.0"
+
+enzyme-adapter-utils@^1.14.0:
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz#afbb0485e8033aa50c744efb5f5711e64fbf1ad0"
+ integrity sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg==
+ dependencies:
+ airbnb-prop-types "^2.16.0"
+ function.prototype.name "^1.1.3"
+ has "^1.0.3"
+ object.assign "^4.1.2"
+ object.fromentries "^2.0.3"
+ prop-types "^15.7.2"
+ semver "^5.7.1"
+
+enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e"
+ integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q==
+ dependencies:
+ has "^1.0.3"
+ object-is "^1.1.2"
+
+enzyme@^3.11.0:
+ version "3.11.0"
+ resolved "https://registry.yarnpkg.com/enzyme/-/enzyme-3.11.0.tgz#71d680c580fe9349f6f5ac6c775bc3e6b7a79c28"
+ integrity sha512-Dw8/Gs4vRjxY6/6i9wU0V+utmQO9kvh9XLnz3LIudviOnVYDEe2ec+0k+NQoMamn1VrjKgCUOWj5jG/5M5M0Qw==
+ dependencies:
+ array.prototype.flat "^1.2.3"
+ cheerio "^1.0.0-rc.3"
+ enzyme-shallow-equal "^1.0.1"
+ function.prototype.name "^1.1.2"
+ has "^1.0.3"
+ html-element-map "^1.2.0"
+ is-boolean-object "^1.0.1"
+ is-callable "^1.1.5"
+ is-number-object "^1.0.4"
+ is-regex "^1.0.5"
+ is-string "^1.0.5"
+ is-subset "^0.1.1"
+ lodash.escape "^4.0.1"
+ lodash.isequal "^4.5.0"
+ object-inspect "^1.7.0"
+ object-is "^1.0.2"
+ object.assign "^4.1.0"
+ object.entries "^1.1.1"
+ object.values "^1.1.1"
+ raf "^3.4.1"
+ rst-selector-parser "^2.2.3"
+ string.prototype.trim "^1.2.1"
+
error-ex@^1.3.1:
version "1.3.2"
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
@@ -3394,11 +4145,52 @@ es-abstract@^1.19.0, es-abstract@^1.19.1:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
+es-abstract@^1.19.2, es-abstract@^1.19.4, es-abstract@^1.19.5:
+ version "1.20.1"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814"
+ integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA==
+ dependencies:
+ call-bind "^1.0.2"
+ es-to-primitive "^1.2.1"
+ function-bind "^1.1.1"
+ function.prototype.name "^1.1.5"
+ get-intrinsic "^1.1.1"
+ get-symbol-description "^1.0.0"
+ has "^1.0.3"
+ has-property-descriptors "^1.0.0"
+ has-symbols "^1.0.3"
+ internal-slot "^1.0.3"
+ is-callable "^1.2.4"
+ is-negative-zero "^2.0.2"
+ is-regex "^1.1.4"
+ is-shared-array-buffer "^1.0.2"
+ is-string "^1.0.7"
+ is-weakref "^1.0.2"
+ object-inspect "^1.12.0"
+ object-keys "^1.1.1"
+ object.assign "^4.1.2"
+ regexp.prototype.flags "^1.4.3"
+ string.prototype.trimend "^1.0.5"
+ string.prototype.trimstart "^1.0.5"
+ unbox-primitive "^1.0.2"
+
+es-array-method-boxes-properly@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
+ integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
+
es-module-lexer@^0.9.0:
version "0.9.3"
resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz#6f13db00cc38417137daf74366f535c8eb438f19"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
+es-shim-unscopables@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
+ integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
+ dependencies:
+ has "^1.0.3"
+
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -3469,6 +4261,11 @@ escape-string-regexp@^1.0.5:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+escape-string-regexp@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
+ integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+
escodegen@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
@@ -3784,21 +4581,6 @@ events@^3.2.0:
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
-execa@^5.0.0:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
- integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
- dependencies:
- cross-spawn "^7.0.3"
- get-stream "^6.0.0"
- human-signals "^2.1.0"
- is-stream "^2.0.0"
- merge-stream "^2.0.0"
- npm-run-path "^4.0.1"
- onetime "^5.1.2"
- signal-exit "^3.0.3"
- strip-final-newline "^2.0.0"
-
expand-brackets@^2.1.4:
version "2.1.4"
resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622"
@@ -3819,6 +4601,17 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
+expect@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec"
+ integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==
+ dependencies:
+ "@jest/expect-utils" "^28.1.3"
+ jest-get-type "^28.0.2"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
+
express-session@^1.17.1:
version "1.17.2"
resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.17.2.tgz#397020374f9bf7997f891b85ea338767b30d0efd"
@@ -3974,6 +4767,13 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
+fb-watchman@^2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85"
+ integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==
+ dependencies:
+ bser "2.1.1"
+
figures@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@@ -4051,6 +4851,11 @@ find-cache-dir@^3.3.1:
make-dir "^3.0.2"
pkg-dir "^4.1.0"
+find-package-json@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/find-package-json/-/find-package-json-1.2.0.tgz#4057d1b943f82d8445fe52dc9cf456f6b8b58083"
+ integrity sha512-+SOGcLGYDJHtyqHd87ysBhmaeQ95oWspDKnMXBrnQ9Eq4OkLNqejgoaD8xVWu6GPa0B6roa6KinCMEMcVeqONw==
+
find-root@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/find-root/-/find-root-1.1.0.tgz#abcfc8ba76f708c42a97b3d685b7e9450bfb9ce4"
@@ -4078,7 +4883,7 @@ find-up@^3.0.0:
dependencies:
locate-path "^3.0.0"
-find-up@^4.0.0:
+find-up@^4.0.0, find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==
@@ -4245,7 +5050,7 @@ fs.realpath@^1.0.0:
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
-fsevents@~2.3.2:
+fsevents@^2.3.2, fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
@@ -4255,11 +5060,26 @@ function-bind@^1.1.1:
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+function.prototype.name@^1.1.2, function.prototype.name@^1.1.3, function.prototype.name@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621"
+ integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.0"
+ functions-have-names "^1.2.2"
+
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+functions-have-names@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
+ integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
+
gensync@^1.0.0-beta.2:
version "1.0.0-beta.2"
resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
@@ -4284,6 +5104,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
has "^1.0.3"
has-symbols "^1.0.1"
+get-package-type@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a"
+ integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==
+
get-stream@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@@ -4298,11 +5123,6 @@ get-stream@^5.1.0:
dependencies:
pump "^3.0.0"
-get-stream@^6.0.0:
- version "6.0.1"
- resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
- integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
-
get-symbol-description@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
@@ -4369,6 +5189,18 @@ glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.3:
once "^1.3.0"
path-is-absolute "^1.0.0"
+glob@^7.1.4:
+ version "7.2.3"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+ integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.1.1"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
global-dirs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
@@ -4485,6 +5317,11 @@ has-bigints@^1.0.1:
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
+has-bigints@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
+ integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==
+
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -4495,11 +5332,23 @@ has-flag@^4.0.0:
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+has-property-descriptors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
+ integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
+ dependencies:
+ get-intrinsic "^1.1.1"
+
has-symbols@^1.0.1, has-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423"
integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==
+has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
has-tostringtag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
@@ -4574,6 +5423,14 @@ homedir-polyfill@^1.0.1:
dependencies:
parse-passwd "^1.0.0"
+html-element-map@^1.2.0:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/html-element-map/-/html-element-map-1.3.1.tgz#44b2cbcfa7be7aa4ff59779e47e51012e1c73c08"
+ integrity sha512-6XMlxrAFX4UEEGxctfFnmrFaaZFNf9i5fNuV5wZ3WWQ4FVaNP1aX1LkX9j2mfEx1NpjeE/rL3nmgEn23GdFmrg==
+ dependencies:
+ array.prototype.filter "^1.0.0"
+ call-bind "^1.0.2"
+
html-encoding-sniffer@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3"
@@ -4581,11 +5438,28 @@ html-encoding-sniffer@^2.0.1:
dependencies:
whatwg-encoding "^1.0.5"
+html-encoding-sniffer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
+ integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
+ dependencies:
+ whatwg-encoding "^2.0.0"
+
html-entities@^2.1.0:
version "2.3.2"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.2.tgz#760b404685cb1d794e4f4b744332e3b00dcfe488"
integrity sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==
+htmlparser2@^8.0.1:
+ version "8.0.1"
+ resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010"
+ integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==
+ dependencies:
+ domelementtype "^2.3.0"
+ domhandler "^5.0.2"
+ domutils "^3.0.1"
+ entities "^4.3.0"
+
http-cache-semantics@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390"
@@ -4611,6 +5485,15 @@ http-proxy-agent@^4.0.1:
agent-base "6"
debug "4"
+http-proxy-agent@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
+ integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
+ dependencies:
+ "@tootallnate/once" "2"
+ agent-base "6"
+ debug "4"
+
http-status@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/http-status/-/http-status-1.5.0.tgz#2edfb02068d236ba60fd1481ad89219aa96e1677"
@@ -4624,10 +5507,13 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
-human-signals@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
- integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
+https-proxy-agent@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
+ integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
+ dependencies:
+ agent-base "6"
+ debug "4"
iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
@@ -4636,6 +5522,13 @@ iconv-lite@0.4.24, iconv-lite@^0.4.24:
dependencies:
safer-buffer ">= 2.1.2 < 3"
+iconv-lite@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
+ integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3.0.0"
+
icss-utils@^5.0.0, icss-utils@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
@@ -4824,7 +5717,7 @@ is-binary-path@~2.1.0:
dependencies:
binary-extensions "^2.0.0"
-is-boolean-object@^1.1.0:
+is-boolean-object@^1.0.1, is-boolean-object@^1.1.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
@@ -4837,7 +5730,7 @@ is-buffer@^1.1.5:
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
-is-callable@^1.1.4, is-callable@^1.2.4:
+is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
@@ -4956,6 +5849,11 @@ is-negative-zero@^2.0.1:
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24"
integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==
+is-negative-zero@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
+ integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
+
is-npm@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-5.0.0.tgz#43e8d65cc56e1b67f8d47262cf667099193f45a8"
@@ -5031,7 +5929,7 @@ is-promise@^2.2.2:
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
-is-regex@^1.1.4:
+is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
@@ -5051,10 +5949,12 @@ is-shared-array-buffer@^1.0.1:
resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.1.tgz#97b0c85fbdacb59c9c446fe653b82cf2b5b7cfe6"
integrity sha512-IU0NmyknYZN0rChcKhRO1X8LYz5Isj/Fsqh8NJOSf+N/hCOTwy29F32Ik7a+QszE63IdvmwdTPDd6cZ5pg4cwA==
-is-stream@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
- integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
+is-shared-array-buffer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
+ integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
+ dependencies:
+ call-bind "^1.0.2"
is-string@^1.0.5, is-string@^1.0.7:
version "1.0.7"
@@ -5063,6 +5963,11 @@ is-string@^1.0.5, is-string@^1.0.7:
dependencies:
has-tostringtag "^1.0.0"
+is-subset@^0.1.1:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6"
+ integrity sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw==
+
is-symbol@^1.0.2, is-symbol@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
@@ -5094,6 +5999,13 @@ is-weakref@^1.0.1:
dependencies:
call-bind "^1.0.0"
+is-weakref@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+ integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
+ dependencies:
+ call-bind "^1.0.2"
+
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"
@@ -5104,6 +6016,11 @@ is-yarn-global@^0.3.0:
resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232"
integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==
+isarray@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
+ integrity sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==
+
isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
@@ -5135,6 +6052,127 @@ isomorphic-dompurify@^0.13.0:
dompurify "^2.2.7"
jsdom "^16.5.2"
+istanbul-lib-coverage@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3"
+ integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==
+
+istanbul-lib-instrument@^5.0.4:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f"
+ integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==
+ dependencies:
+ "@babel/core" "^7.12.3"
+ "@babel/parser" "^7.14.7"
+ "@istanbuljs/schema" "^0.1.2"
+ istanbul-lib-coverage "^3.2.0"
+ semver "^6.3.0"
+
+jest-diff@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f"
+ integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==
+ dependencies:
+ chalk "^4.0.0"
+ diff-sequences "^28.1.1"
+ jest-get-type "^28.0.2"
+ pretty-format "^28.1.3"
+
+jest-get-type@^28.0.2:
+ version "28.0.2"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203"
+ integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==
+
+jest-haste-map@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-28.1.3.tgz#abd5451129a38d9841049644f34b034308944e2b"
+ integrity sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==
+ dependencies:
+ "@jest/types" "^28.1.3"
+ "@types/graceful-fs" "^4.1.3"
+ "@types/node" "*"
+ anymatch "^3.0.3"
+ fb-watchman "^2.0.0"
+ graceful-fs "^4.2.9"
+ jest-regex-util "^28.0.2"
+ jest-util "^28.1.3"
+ jest-worker "^28.1.3"
+ micromatch "^4.0.4"
+ walker "^1.0.8"
+ optionalDependencies:
+ fsevents "^2.3.2"
+
+jest-matcher-utils@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e"
+ integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==
+ dependencies:
+ chalk "^4.0.0"
+ jest-diff "^28.1.3"
+ jest-get-type "^28.0.2"
+ pretty-format "^28.1.3"
+
+jest-message-util@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-28.1.3.tgz#232def7f2e333f1eecc90649b5b94b0055e7c43d"
+ integrity sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==
+ dependencies:
+ "@babel/code-frame" "^7.12.13"
+ "@jest/types" "^28.1.3"
+ "@types/stack-utils" "^2.0.0"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.9"
+ micromatch "^4.0.4"
+ pretty-format "^28.1.3"
+ slash "^3.0.0"
+ stack-utils "^2.0.3"
+
+jest-regex-util@^28.0.2:
+ version "28.0.2"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-28.0.2.tgz#afdc377a3b25fb6e80825adcf76c854e5bf47ead"
+ integrity sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==
+
+jest-snapshot@^28.1.1:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-28.1.3.tgz#17467b3ab8ddb81e2f605db05583d69388fc0668"
+ integrity sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==
+ dependencies:
+ "@babel/core" "^7.11.6"
+ "@babel/generator" "^7.7.2"
+ "@babel/plugin-syntax-typescript" "^7.7.2"
+ "@babel/traverse" "^7.7.2"
+ "@babel/types" "^7.3.3"
+ "@jest/expect-utils" "^28.1.3"
+ "@jest/transform" "^28.1.3"
+ "@jest/types" "^28.1.3"
+ "@types/babel__traverse" "^7.0.6"
+ "@types/prettier" "^2.1.5"
+ babel-preset-current-node-syntax "^1.0.0"
+ chalk "^4.0.0"
+ expect "^28.1.3"
+ graceful-fs "^4.2.9"
+ jest-diff "^28.1.3"
+ jest-get-type "^28.0.2"
+ jest-haste-map "^28.1.3"
+ jest-matcher-utils "^28.1.3"
+ jest-message-util "^28.1.3"
+ jest-util "^28.1.3"
+ natural-compare "^1.4.0"
+ pretty-format "^28.1.3"
+ semver "^7.3.5"
+
+jest-util@^28.1.1, jest-util@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-28.1.3.tgz#f4f932aa0074f0679943220ff9cbba7e497028b0"
+ integrity sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==
+ dependencies:
+ "@jest/types" "^28.1.3"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ ci-info "^3.2.0"
+ graceful-fs "^4.2.9"
+ picomatch "^2.2.3"
+
jest-worker@^27.0.6, jest-worker@^27.3.1:
version "27.4.2"
resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.4.2.tgz#0fb123d50955af1a450267787f340a1bf7e12bc4"
@@ -5144,6 +6182,15 @@ jest-worker@^27.0.6, jest-worker@^27.3.1:
merge-stream "^2.0.0"
supports-color "^8.0.0"
+jest-worker@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-28.1.3.tgz#7e3c4ce3fa23d1bb6accb169e7f396f98ed4bb98"
+ integrity sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==
+ dependencies:
+ "@types/node" "*"
+ merge-stream "^2.0.0"
+ supports-color "^8.0.0"
+
js-search@^1.3.1:
version "1.4.3"
resolved "https://registry.yarnpkg.com/js-search/-/js-search-1.4.3.tgz#23a86d7e064ca53a473930edc48615b6b1c1954a"
@@ -5154,14 +6201,6 @@ js-search@^1.3.1:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
-js-yaml@3.14.0:
- version "3.14.0"
- resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
- integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
- dependencies:
- argparse "^1.0.7"
- esprima "^4.0.0"
-
js-yaml@4.1.0, js-yaml@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
@@ -5177,6 +6216,44 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
+jsdom-global@3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9"
+ integrity sha512-t1KMcBkz/pT5JrvcJbpUR2u/w1kO9jXctaaGJ0vZDzwFnIvGWw9IDSRciT83kIs8Bnw4qpOl8bQK08V01YgMPg==
+
+jsdom@20.0.0:
+ version "20.0.0"
+ resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.0.tgz#882825ac9cc5e5bbee704ba16143e1fa78361ebf"
+ integrity sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA==
+ dependencies:
+ abab "^2.0.6"
+ acorn "^8.7.1"
+ acorn-globals "^6.0.0"
+ cssom "^0.5.0"
+ cssstyle "^2.3.0"
+ data-urls "^3.0.2"
+ decimal.js "^10.3.1"
+ domexception "^4.0.0"
+ escodegen "^2.0.0"
+ form-data "^4.0.0"
+ html-encoding-sniffer "^3.0.0"
+ http-proxy-agent "^5.0.0"
+ https-proxy-agent "^5.0.1"
+ is-potential-custom-element-name "^1.0.1"
+ nwsapi "^2.2.0"
+ parse5 "^7.0.0"
+ saxes "^6.0.0"
+ symbol-tree "^3.2.4"
+ tough-cookie "^4.0.0"
+ w3c-hr-time "^1.0.2"
+ w3c-xmlserializer "^3.0.0"
+ webidl-conversions "^7.0.0"
+ whatwg-encoding "^2.0.0"
+ whatwg-mimetype "^3.0.0"
+ whatwg-url "^11.0.0"
+ ws "^8.8.0"
+ xml-name-validator "^4.0.0"
+
jsdom@^16.5.2:
version "16.7.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710"
@@ -5269,6 +6346,11 @@ json5@^2.1.2:
dependencies:
minimist "^1.2.5"
+json5@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c"
+ integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==
+
"jsx-ast-utils@^2.4.1 || ^3.0.0":
version "3.2.1"
resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.1.tgz#720b97bfe7d901b927d87c3773637ae8ea48781b"
@@ -5277,6 +6359,11 @@ json5@^2.1.2:
array-includes "^3.1.3"
object.assign "^4.1.2"
+just-extend@^4.0.2:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
+ integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==
+
keyv@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9"
@@ -5451,6 +6538,16 @@ lodash.defaults@^4.0.1:
resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
+lodash.escape@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/lodash.escape/-/lodash.escape-4.0.1.tgz#c9044690c21e04294beaa517712fded1fa88de98"
+ integrity sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==
+
+lodash.flattendeep@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
+ integrity sha512-uHaJFihxmJcEX3kT4I23ABqKKalJ/zDrDg0lsFtc1h+3uw49SIJ5beyhx5ExVRti3AvKoOJngIj7xz3oylPdWQ==
+
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@@ -5480,6 +6577,11 @@ lodash.isplainobject@^3.2.0:
lodash.isarguments "^3.0.0"
lodash.keysin "^3.0.0"
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==
+
lodash.keysin@^3.0.0:
version "3.0.8"
resolved "https://registry.yarnpkg.com/lodash.keysin/-/lodash.keysin-3.0.8.tgz#22c4493ebbedb1427962a54b445b2c8a767fb47f"
@@ -5498,6 +6600,11 @@ lodash.merge@^4.6.2:
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+lodash.mergewith@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
+ integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==
+
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@@ -5603,6 +6710,13 @@ make-iterator@^1.0.0:
dependencies:
kind-of "^6.0.2"
+makeerror@1.0.12:
+ version "1.0.12"
+ resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a"
+ integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==
+ dependencies:
+ tmpl "1.0.5"
+
map-cache@^0.2.0, map-cache@^0.2.2:
version "0.2.2"
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
@@ -5751,6 +6865,13 @@ minimatch@3.0.4, minimatch@^3.0.4:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^3.1.1:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
minimist@^1.2.0, minimist@^1.2.5:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
@@ -5771,6 +6892,19 @@ mkdirp@^0.5.1:
dependencies:
minimist "^1.2.5"
+mocha-chai-jest-snapshot@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/mocha-chai-jest-snapshot/-/mocha-chai-jest-snapshot-1.1.4.tgz#0c8e15530968074b08d1b7fbf28c2ed8b92f4380"
+ integrity sha512-ybwtS10P8BXDJQn9B3QyQA8Lxr/CcYxtuyWKk1PxD9vJorH8VL3edB7re4GcG9dRAdDPE/B0BsfwmCo6W43O7w==
+ dependencies:
+ "@jest/test-result" "^28.1.1"
+ chalk "^4.1.2"
+ find-package-json "^1.2.0"
+ jest-snapshot "^28.1.1"
+ jest-util "^28.1.1"
+ slash "^3.0.0"
+ yargs "^17.5.1"
+
mocha@^9.1.3:
version "9.1.3"
resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb"
@@ -5802,9 +6936,14 @@ mocha@^9.1.3:
yargs-unparser "2.0.0"
moment@^2.10.2, moment@^2.29.1:
- version "2.29.2"
- resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.2.tgz#00910c60b20843bcba52d37d58c628b47b1f20e4"
- integrity sha512-UgzG4rvxYpN15jgCmVJwac49h9ly9NurikMWGPdVxm8GZD6XjkKPxDTjQQ43gtGgnV3X0cAyWDdP2Wexoquifg==
+ version "2.29.4"
+ resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108"
+ integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==
+
+moo@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.1.tgz#7aae7f384b9b09f620b6abf6f74ebbcd1b65dbc4"
+ integrity sha512-I1mnb5xn4fO80BH9BLcF0yLypy2UKl+Cb01Fu0hJRkJjlCRtxZMWkTdAtDd5ZqCOxtCkhmRwyI57vWT+1iZ67w==
morgan@^1.10.0:
version "1.10.0"
@@ -5879,6 +7018,16 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+nearley@^2.7.10:
+ version "2.20.1"
+ resolved "https://registry.yarnpkg.com/nearley/-/nearley-2.20.1.tgz#246cd33eff0d012faf197ff6774d7ac78acdd474"
+ integrity sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==
+ dependencies:
+ commander "^2.19.0"
+ moo "^0.5.0"
+ railroad-diagrams "^1.0.0"
+ randexp "0.4.6"
+
negotiator@0.6.2:
version "0.6.2"
resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
@@ -5904,6 +7053,17 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
+nise@^5.1.1:
+ version "5.1.1"
+ resolved "https://registry.yarnpkg.com/nise/-/nise-5.1.1.tgz#ac4237e0d785ecfcb83e20f389185975da5c31f3"
+ integrity sha512-yr5kW2THW1AkxVmCnKEh4nbYkJdB3I7LUkiUgOvEkOp414mc2UMaHMA7pjq1nYowhdoJZGwEKGaQVbxfpWj10A==
+ dependencies:
+ "@sinonjs/commons" "^1.8.3"
+ "@sinonjs/fake-timers" ">=5"
+ "@sinonjs/text-encoding" "^0.7.1"
+ just-extend "^4.0.2"
+ path-to-regexp "^1.7.0"
+
node-environment-flags@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088"
@@ -5912,6 +7072,11 @@ node-environment-flags@^1.0.5:
object.getownpropertydescriptors "^2.0.3"
semver "^5.7.0"
+node-int64@^0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
+ integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==
+
node-releases@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
@@ -5922,6 +7087,11 @@ node-releases@^2.0.2:
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01"
integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==
+node-releases@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503"
+ integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==
+
nodemailer@^6.5.0:
version "6.7.2"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.7.2.tgz#44b2ad5f7ed71b7067f7a21c4fedabaec62b85e0"
@@ -5960,12 +7130,12 @@ normalize-url@^4.1.0:
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a"
integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==
-npm-run-path@^4.0.1:
- version "4.0.1"
- resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
- integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
+nth-check@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+ integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
dependencies:
- path-key "^3.0.0"
+ boolbase "^1.0.0"
nwsapi@^2.2.0:
version "2.2.0"
@@ -5996,6 +7166,19 @@ object-inspect@^1.11.0, object-inspect@^1.9.0:
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1"
integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg==
+object-inspect@^1.12.0, object-inspect@^1.7.0:
+ version "1.12.2"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea"
+ integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==
+
+object-is@^1.0.2, object-is@^1.1.2:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac"
+ integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+
object-keys@^1.0.12, object-keys@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
@@ -6028,7 +7211,7 @@ object.defaults@^1.1.0:
for-own "^1.0.0"
isobject "^3.0.0"
-object.entries@^1.1.5:
+object.entries@^1.1.1, object.entries@^1.1.2, object.entries@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861"
integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g==
@@ -6037,7 +7220,7 @@ object.entries@^1.1.5:
define-properties "^1.1.3"
es-abstract "^1.19.1"
-object.fromentries@^2.0.5:
+object.fromentries@^2.0.3, object.fromentries@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251"
integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw==
@@ -6078,7 +7261,7 @@ object.pick@^1.2.0, object.pick@^1.3.0:
dependencies:
isobject "^3.0.1"
-object.values@^1.1.5:
+object.values@^1.1.1, object.values@^1.1.2, object.values@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==
@@ -6106,7 +7289,7 @@ once@1.4.0, once@^1.3.0, once@^1.3.1, once@^1.4.0:
dependencies:
wrappy "1"
-onetime@^5.1.0, onetime@^5.1.2:
+onetime@^5.1.0:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
@@ -6267,11 +7450,26 @@ parse-passwd@^1.0.0:
resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6"
integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=
+parse5-htmlparser2-tree-adapter@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1"
+ integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==
+ dependencies:
+ domhandler "^5.0.2"
+ parse5 "^7.0.0"
+
parse5@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
+parse5@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a"
+ integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==
+ dependencies:
+ entities "^4.3.0"
+
parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
@@ -6339,7 +7537,7 @@ path-key@^2.0.1:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-key@^3.0.0, path-key@^3.1.0:
+path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
@@ -6366,6 +7564,13 @@ path-to-regexp@0.1.7:
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+path-to-regexp@^1.7.0:
+ version "1.8.0"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a"
+ integrity sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==
+ dependencies:
+ isarray "0.0.1"
+
path-type@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
@@ -6474,7 +7679,7 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
-pirates@^4.0.5:
+pirates@^4.0.4, pirates@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
@@ -6597,6 +7802,16 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
+pretty-format@^28.1.3:
+ version "28.1.3"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5"
+ integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==
+ dependencies:
+ "@jest/schemas" "^28.1.3"
+ ansi-regex "^5.0.1"
+ ansi-styles "^5.0.0"
+ react-is "^18.0.0"
+
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
@@ -6612,6 +7827,15 @@ progress@^2.0.0:
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
+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-extra@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.1.1.tgz#58c3b74cbfbb95d304625975aa2f0848329a010b"
@@ -6694,13 +7918,26 @@ queue-microtask@^1.2.2:
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-raf@^3.3.0:
+raf@^3.3.0, raf@^3.4.1:
version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
dependencies:
performance-now "^2.1.0"
+railroad-diagrams@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
+ integrity sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==
+
+randexp@0.4.6:
+ version "0.4.6"
+ resolved "https://registry.yarnpkg.com/randexp/-/randexp-0.4.6.tgz#e986ad5e5e31dae13ddd6f7b3019aa7c87f60ca3"
+ integrity sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==
+ dependencies:
+ discontinuous-range "1.0.0"
+ ret "~0.1.10"
+
random-bytes@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b"
@@ -6817,7 +8054,7 @@ react-input-autosize@^3.0.0:
dependencies:
prop-types "^15.5.8"
-react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0:
+react-is@^16.13.1, react-is@^16.3.2, react-is@^16.7.0, react-is@^16.8.6:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@@ -6827,6 +8064,11 @@ react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
+react-is@^18.0.0:
+ version "18.2.0"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
+ integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
+
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"
@@ -6891,6 +8133,11 @@ react-select@^4.3.1:
react-input-autosize "^3.0.0"
react-transition-group "^4.3.0"
+react-simple-star-rating@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/react-simple-star-rating/-/react-simple-star-rating-4.0.5.tgz#030e576015c9fca881677c9eeb55218f42278cd8"
+ integrity sha512-995YpXtLNNLim/K59lhRqFnvpRXJHsiJAnYAu2iHEjfCn4u8hP9Eam53hi+ubc6stU25FzvBPyXKzjhu7wl+hA==
+
react-sortable-hoc@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
@@ -6908,6 +8155,25 @@ react-sticky@^6.0.1:
prop-types "^15.5.8"
raf "^3.3.0"
+
+react-test-renderer@^16.0.0-0:
+ version "16.14.0"
+ resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae"
+ integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg==
+ dependencies:
+ object-assign "^4.1.1"
+ prop-types "^15.6.2"
+ react-is "^16.8.6"
+ scheduler "^0.19.1"
+
+react-tooltip@^4.2.21:
+ version "4.2.21"
+ resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-4.2.21.tgz#840123ed86cf33d50ddde8ec8813b2960bfded7f"
+ integrity sha512-zSLprMymBDowknr0KVDiJ05IjZn9mQhhg4PRsqln0OZtURAJ1snt1xi5daZfagsh6vfsziZrc9pErPTDY1ACig==
+ dependencies:
+ prop-types "^15.7.2"
+ uuid "^7.0.3"
+
react-transition-group@^4.3.0, react-transition-group@^4.4.1:
version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@@ -7011,6 +8277,13 @@ redux-immutable@^4.0.0:
resolved "https://registry.yarnpkg.com/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3"
integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM=
+redux-mock-store@^1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/redux-mock-store/-/redux-mock-store-1.5.4.tgz#90d02495fd918ddbaa96b83aef626287c9ab5872"
+ integrity sha512-xmcA0O/tjCLXhh9Fuiq6pMrJCwFRaouA8436zcikdIpYWWCjU76CRk+i2bHx8EeiSiMGnB85/lZdU3wIJVXHTA==
+ dependencies:
+ lodash.isplainobject "^4.0.6"
+
redux-thunk@^2.2.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714"
@@ -7033,6 +8306,11 @@ redux@^4.0.0:
dependencies:
"@babel/runtime" "^7.9.2"
+reflect.ownkeys@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460"
+ integrity sha512-qOLsBKHCpSOFKK1NUOCGC5VyeufB6lEsFe92AL2bhIJsacZS1qdoOZSbPk3MYKuT2cFlRDnulKXuuElIrMjGUg==
+
regenerate-unicode-properties@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-9.0.0.tgz#54d09c7115e1f53dc2314a974b32c1c344efe326"
@@ -7078,6 +8356,15 @@ regexp.prototype.flags@^1.3.1:
call-bind "^1.0.2"
define-properties "^1.1.3"
+regexp.prototype.flags@^1.4.3:
+ version "1.4.3"
+ resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac"
+ integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ functions-have-names "^1.2.2"
+
regexpp@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
@@ -7270,6 +8557,14 @@ rimraf@^3.0.2:
dependencies:
glob "^7.1.3"
+rst-selector-parser@^2.2.3:
+ version "2.2.3"
+ resolved "https://registry.yarnpkg.com/rst-selector-parser/-/rst-selector-parser-2.2.3.tgz#81b230ea2fcc6066c89e3472de794285d9b03d91"
+ integrity sha512-nDG1rZeP6oFTLN6yNDV/uiAvs1+FS/KlrEwh7+y7dpuApDBy6bI2HTBcc0/V8lv9OTqfyD34eF7au2pm8aBbhA==
+ dependencies:
+ lodash.flattendeep "^4.4.0"
+ nearley "^2.7.10"
+
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
@@ -7311,7 +8606,7 @@ safe-regex@^1.1.0:
dependencies:
ret "~0.1.10"
-"safer-buffer@>= 2.1.2 < 3":
+"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
@@ -7340,6 +8635,13 @@ saxes@^5.0.1:
dependencies:
xmlchars "^2.2.0"
+saxes@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5"
+ integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==
+ dependencies:
+ xmlchars "^2.2.0"
+
scheduler@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196"
@@ -7517,11 +8819,28 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
-signal-exit@^3.0.2, signal-exit@^3.0.3:
+signal-exit@^3.0.2:
version "3.0.6"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af"
integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==
+signal-exit@^3.0.7:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+sinon@^14.0.0:
+ version "14.0.0"
+ resolved "https://registry.yarnpkg.com/sinon/-/sinon-14.0.0.tgz#203731c116d3a2d58dc4e3cbe1f443ba9382a031"
+ integrity sha512-ugA6BFmE+WrJdh0owRZHToLd32Uw3Lxq6E6LtNRU+xTVBefx632h03Q7apXWRsRdZAJ41LB8aUfn2+O4jsDNMw==
+ dependencies:
+ "@sinonjs/commons" "^1.8.3"
+ "@sinonjs/fake-timers" "^9.1.2"
+ "@sinonjs/samsam" "^6.1.1"
+ diff "^5.0.0"
+ nise "^5.1.1"
+ supports-color "^7.2.0"
+
sirv@^1.0.7:
version "1.0.19"
resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49"
@@ -7628,7 +8947,7 @@ source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
-source-map@^0.7.3, source-map@~0.7.2:
+source-map@^0.7.3:
version "0.7.3"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
@@ -7659,6 +8978,13 @@ sprintf-kit@^2.0.1:
dependencies:
es5-ext "^0.10.53"
+stack-utils@^2.0.3:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5"
+ integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==
+ dependencies:
+ escape-string-regexp "^2.0.0"
+
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -7704,6 +9030,15 @@ string.prototype.matchall@^4.0.6:
regexp.prototype.flags "^1.3.1"
side-channel "^1.0.4"
+string.prototype.trim@^1.2.1:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.6.tgz#824960787db37a9e24711802ed0c1d1c0254f83e"
+ integrity sha512-8lMR2m+U0VJTPp6JjvJTtGyc4FIGq9CdRt7O9p6T0e6K4vjU+OP+SQJpbe/SBmRcCUIvNUnjsbmY6lnMp8MhsQ==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.4"
+ es-abstract "^1.19.5"
+
string.prototype.trimend@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
@@ -7712,6 +9047,15 @@ string.prototype.trimend@^1.0.4:
call-bind "^1.0.2"
define-properties "^1.1.3"
+string.prototype.trimend@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0"
+ integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.4"
+ es-abstract "^1.19.5"
+
string.prototype.trimstart@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
@@ -7720,6 +9064,15 @@ string.prototype.trimstart@^1.0.4:
call-bind "^1.0.2"
define-properties "^1.1.3"
+string.prototype.trimstart@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef"
+ integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.4"
+ es-abstract "^1.19.5"
+
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -7753,11 +9106,6 @@ strip-bom@^3.0.0:
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
-strip-final-newline@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
- integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-
strip-json-comments@3.1.1, strip-json-comments@^3.0.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
@@ -7827,7 +9175,7 @@ supports-color@^6.1.0:
dependencies:
has-flag "^3.0.0"
-supports-color@^7.1.0:
+supports-color@^7.1.0, supports-color@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
@@ -7839,16 +9187,17 @@ supports-preserve-symlinks-flag@^1.0.0:
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
-swagger-jsdoc@^4.0.0:
- version "4.3.2"
- resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-4.3.2.tgz#34d4e1b1f707a89f0adbe79e246148474bb2cb0a"
- integrity sha512-GK+J0LftvEurROVi70bMiIrd/A7pJD2AiI8faMkznsuyokGEu8WCdFsuZhmcE0XQt8hP/UTTkHEZpe3pS1eUjw==
+swagger-jsdoc@^6.2.5:
+ version "6.2.5"
+ resolved "https://registry.yarnpkg.com/swagger-jsdoc/-/swagger-jsdoc-6.2.5.tgz#65bffa142276436b2b131255f59a6b55384a0e8e"
+ integrity sha512-l+cdsKS2y+QDhrH1TJSUiE0y9XKuf5xaGSatjf0hR/wjTlMpO8WfubBK9d/nASdbHPMtj9iJZLBH2ogBEhL7Sw==
dependencies:
commander "6.2.0"
doctrine "3.0.0"
glob "7.1.6"
- js-yaml "3.14.0"
+ lodash.mergewith "^4.6.2"
swagger-parser "10.0.2"
+ yaml "2.0.0-1"
swagger-parser@10.0.2:
version "10.0.2"
@@ -7922,14 +9271,24 @@ terser-webpack-plugin@^5.1.3:
terser "^5.7.2"
terser@^5.7.2:
- version "5.10.0"
- resolved "https://registry.yarnpkg.com/terser/-/terser-5.10.0.tgz#b86390809c0389105eb0a0b62397563096ddafcc"
- integrity sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==
+ version "5.14.2"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.14.2.tgz#9ac9f22b06994d736174f4091aa368db896f1c10"
+ integrity sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==
dependencies:
+ "@jridgewell/source-map" "^0.3.2"
+ acorn "^8.5.0"
commander "^2.20.0"
- source-map "~0.7.2"
source-map-support "~0.5.20"
+test-exclude@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e"
+ integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==
+ dependencies:
+ "@istanbuljs/schema" "^0.1.2"
+ glob "^7.1.4"
+ minimatch "^3.0.4"
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
@@ -7960,6 +9319,11 @@ tmp@^0.0.33:
dependencies:
os-tmpdir "~1.0.2"
+tmpl@1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
+ integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==
+
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -8035,6 +9399,13 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
+tr46@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
+ integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
+ dependencies:
+ punycode "^2.1.1"
+
tsconfig-paths@^3.11.0:
version "3.12.0"
resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz#19769aca6ee8f6a1a341e38c8fa45dd9fb18899b"
@@ -8071,7 +9442,7 @@ type-check@~0.3.2:
dependencies:
prelude-ls "~1.1.2"
-type-detect@^4.0.0, type-detect@^4.0.5:
+type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, 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==
@@ -8143,6 +9514,16 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
+unbox-primitive@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e"
+ integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==
+ dependencies:
+ call-bind "^1.0.2"
+ has-bigints "^1.0.2"
+ has-symbols "^1.0.3"
+ which-boxed-primitive "^1.0.2"
+
unc-path-regex@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa"
@@ -8228,6 +9609,14 @@ unset-value@^1.0.0:
has-value "^0.3.1"
isobject "^3.0.0"
+update-browserslist-db@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38"
+ integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==
+ dependencies:
+ escalade "^3.1.1"
+ picocolors "^1.0.0"
+
update-notifier@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-5.1.0.tgz#4ab0d7c7f36a231dd7316cf7729313f0214d9ad9"
@@ -8282,6 +9671,11 @@ utils-merge@1.0.1, utils-merge@1.x.x:
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+uuid@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
+ integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
+
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
@@ -8323,6 +9717,20 @@ w3c-xmlserializer@^2.0.0:
dependencies:
xml-name-validator "^3.0.0"
+w3c-xmlserializer@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
+ integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
+ dependencies:
+ xml-name-validator "^4.0.0"
+
+walker@^1.0.8:
+ version "1.0.8"
+ resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
+ integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==
+ dependencies:
+ makeerror "1.0.12"
+
warning@^4.0.0, warning@^4.0.2, warning@^4.0.3:
version "4.0.3"
resolved "https://registry.yarnpkg.com/warning/-/warning-4.0.3.tgz#16e9e077eb8a86d6af7d64aa1e05fd85b4678ca3"
@@ -8348,6 +9756,11 @@ webidl-conversions@^6.1.0:
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514"
integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
+webidl-conversions@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
+ integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
+
webpack-bundle-analyzer@^4.3.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.5.0.tgz#1b0eea2947e73528754a6f9af3e91b2b6e0f79d5"
@@ -8363,18 +9776,18 @@ webpack-bundle-analyzer@^4.3.0:
sirv "^1.0.7"
ws "^7.3.1"
-webpack-cli@^4.3.0:
- version "4.9.1"
- resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.9.1.tgz#b64be825e2d1b130f285c314caa3b1ba9a4632b3"
- integrity sha512-JYRFVuyFpzDxMDB+v/nanUdQYcZtqFPGzmlW4s+UkPMFhSpfRNmf1z4AwYcHJVdvEFAM7FFCQdNTpsBYhDLusQ==
+webpack-cli@^4.10.0:
+ version "4.10.0"
+ resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-4.10.0.tgz#37c1d69c8d85214c5a65e589378f53aec64dab31"
+ integrity sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==
dependencies:
"@discoveryjs/json-ext" "^0.5.0"
- "@webpack-cli/configtest" "^1.1.0"
- "@webpack-cli/info" "^1.4.0"
- "@webpack-cli/serve" "^1.6.0"
+ "@webpack-cli/configtest" "^1.2.0"
+ "@webpack-cli/info" "^1.5.0"
+ "@webpack-cli/serve" "^1.7.0"
colorette "^2.0.14"
commander "^7.0.0"
- execa "^5.0.0"
+ cross-spawn "^7.0.3"
fastest-levenshtein "^1.0.12"
import-local "^3.0.2"
interpret "^2.2.0"
@@ -8452,11 +9865,31 @@ whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
+whatwg-encoding@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
+ integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
+ dependencies:
+ iconv-lite "0.6.3"
+
whatwg-mimetype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
+whatwg-mimetype@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
+ integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
+
+whatwg-url@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
+ integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
+ dependencies:
+ tr46 "^3.0.0"
+ webidl-conversions "^7.0.0"
+
whatwg-url@^8.0.0, whatwg-url@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
@@ -8537,6 +9970,14 @@ write-file-atomic@^3.0.0:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
+write-file-atomic@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f"
+ integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==
+ dependencies:
+ imurmurhash "^0.1.4"
+ signal-exit "^3.0.7"
+
write@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3"
@@ -8549,6 +9990,11 @@ ws@^7.3.1, ws@^7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
+ws@^8.8.0:
+ version "8.8.1"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
+ integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
+
xdg-basedir@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13"
@@ -8559,6 +10005,11 @@ xml-name-validator@^3.0.0:
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==
+xml-name-validator@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
+ integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
+
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
@@ -8579,6 +10030,11 @@ yallist@^4.0.0:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
+yaml@2.0.0-1:
+ version "2.0.0-1"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.0.0-1.tgz#8c3029b3ee2028306d5bcf396980623115ff8d18"
+ integrity sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==
+
yaml@^1.7.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
@@ -8594,6 +10050,11 @@ yargs-parser@^20.2.2:
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
+yargs-parser@^21.0.0:
+ version "21.0.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35"
+ integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==
+
yargs-unparser@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
@@ -8617,6 +10078,19 @@ yargs@16.2.0:
y18n "^5.0.5"
yargs-parser "^20.2.2"
+yargs@^17.5.1:
+ version "17.5.1"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
+ integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.0.0"
+
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"