diff --git a/CHANGELOG.md b/CHANGELOG.md index f3bd50a770..023b7cb476 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,35 @@ # Changelog for formsflow.ai -Mark items as `Added`, `Changed`, `Fixed`, `Removed` +Mark items as `Added`, `Changed`, `Fixed`, `Removed`, `Untested Features`, `Upcoming Features` -## Future Release -* Render the form workflow association in readonly and give option to edit -* Show task navigation in workflow diagram -* Support to access formsflow.ai solution in mobile +## 3.1.0 - 2020-12-17 +`Modified` +* Formio upgraded to latest version-2.0.0.rc34 (Component : forms-flow-forms) +* In application & task dashboard, the process diagram navigation is highlighted on the diagram (Component : forms-flow-web) +* Made cosmetic changes to menu icons (Component: forms-flow-web) +* Update on swagger documentation (Component: forms-flow-api) +* For the designer's edit scenario, by default the workflow selection & association is rendered as read-only with an option to toggle and edit(Component: forms-flow-web) -## 3.0.0 - 2020-10-10 +`Untested Features` +* Support to associate an unique form at every manual task in workflow process (Component: forms-flow-bpm) + +`Fixed` +* Support to access forms-flow-ai solution in mobile(Component: forms-flow-web) +* Forms flow Edit/submission Routing Fix for User with Multiple Role (Component: forms-flow-web) + +`Upcoming Features` +* Refactoring python api to use module *flask-resk-jsonapi* (Component: forms-flow-api) +* Enhanced sorting, searching and pagination (Component: forms-flow-web) + +`Known Issues` +* Custom component (Text Area with analytics) not retaining the value after submission +* Cosmetic changes to show success message after loading is completed + +## 3.0.1 - 2020-10-08 +`Modified` +* In application dashboard, the "Application Status" column search component has been enhanced to show all possible values in dropdown (Component : forms-flow-web) +* In application dashboard, the button label has been modified to show as "Acknowledge" for status "Awaiting Acknowledgement" (Component : forms-flow-web) + +## 3.0.0 - 2020-10-07 `Added` * Logo & UI Styling * Introduced Applications menu diff --git a/README.md b/README.md index 3738d3c5cb..d97652bace 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -


+



**formsflow.ai** is an open source solution framework developed and maintained by [AOT Technologies](https://www.aot-technologies.com/). The framework combines selected open source Forms, Workflow, Analytics, and Security products with custom-built integration code to provide a seamless solution that provides a viable alternative to expensive, enterprise software products. diff --git a/deployment/README.md b/deployment/README.md index d2d968bdd1..46ed555a3e 100644 --- a/deployment/README.md +++ b/deployment/README.md @@ -45,6 +45,11 @@ Docker #### Full Deployment Follow the instructions on [docker installation guide](./docker) +Openshift +------------------ +#### Full Deployment + Follow the instructions on [openshift installation guide](./openshift) + ## Verifying the Installation status * The following applications will be started and can be accessed in your browser. * http://localhost:7000 - Redash analytics diff --git a/deployment/docker/docker-compose-linux.yml b/deployment/docker/docker-compose-linux.yml index b8e663b5ed..922cb1f671 100644 --- a/deployment/docker/docker-compose-linux.yml +++ b/deployment/docker/docker-compose-linux.yml @@ -54,6 +54,7 @@ services: MONGO_INITDB_ROOT_PASSWORD: ${FORMIO_MONGO_PASSWORD} MONGO_INITDB_DATABASE: ${FORMIO_MONGO_DATABASE} volumes: + - ./../../forms-flow-forms/mongo_entrypoint/001_user.js:/docker-entrypoint-initdb.d/001_user.js:ro - ./mongodb/data/db/:/data/db/ - ./mongodb/data/log/:/var/log/mongodb/ - ./mongodb/mongod.conf:/etc/mongod.conf @@ -76,9 +77,10 @@ services: - ./:/app:rw environment: DEBUG: formio:* - NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/formio?authSource=admin\"}" + NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/${FORMIO_MONGO_DATABASE}?authSource=admin\"}" ROOT_EMAIL: ${FORMIO_ROOT_EMAIL} ROOT_PASSWORD: ${FORMIO_ROOT_PASSWORD} + FORMIO_DOMAIN: ${FORMIO_DEFAULT_PROJECT_URL} stdin_open: true # -i tty: true # -t networks: diff --git a/deployment/docker/docker-compose-windows.yml b/deployment/docker/docker-compose-windows.yml index 4806ea8725..534662901c 100644 --- a/deployment/docker/docker-compose-windows.yml +++ b/deployment/docker/docker-compose-windows.yml @@ -54,6 +54,7 @@ services: MONGO_INITDB_ROOT_PASSWORD: ${FORMIO_MONGO_PASSWORD} MONGO_INITDB_DATABASE: ${FORMIO_MONGO_DATABASE} volumes: + - ./../../forms-flow-forms/mongo_entrypoint/001_user.js:/docker-entrypoint-initdb.d/001_user.js:ro - mdb-data:/data/db/ - ./mongodb/data/log/:/var/log/mongodb/ - ./mongodb/mongod.conf:/etc/mongod.conf @@ -76,9 +77,10 @@ services: - ./:/app:rw environment: DEBUG: formio:* - NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/formio?authSource=admin\"}" + NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/${FORMIO_MONGO_DATABASE}?authSource=admin\"}" ROOT_EMAIL: ${FORMIO_ROOT_EMAIL} ROOT_PASSWORD: ${FORMIO_ROOT_PASSWORD} + FORMIO_DOMAIN: ${FORMIO_DEFAULT_PROJECT_URL} stdin_open: true # -i tty: true # -t networks: diff --git a/forms-flow-api/README.md b/forms-flow-api/README.md index 2158f3cb53..2ec862ffa7 100644 --- a/forms-flow-api/README.md +++ b/forms-flow-api/README.md @@ -1,11 +1,11 @@ -# formsflow.ai Rest API +# formsflow.ai API ![Python](https://img.shields.io/badge/python-3.8-blue) ![Flask](https://img.shields.io/badge/Flask-1.1.1-blue) ![postgres](https://img.shields.io/badge/postgres-latest-blue) **formsflow.ai** has built this adaptive tier for correlating form management, BPM and analytics together. The goal of the REST API is to provide access to all relevant interfaces of -the system. It's build using Python 🐍. +the system. It's build using Python :snake: . ## Table of Content * [Prerequisites](#prerequisites) @@ -99,6 +99,8 @@ Content-Type : application/json Authorization: Bearer {access token} ``` + * Checkout out the API documentation which can be accessed at **/swagger** endpoint. + ## Steps for enabling Sentiment Analysis component One of the unique features of the formsflow.ai framework is Sentiment Analysis. It can diff --git a/forms-flow-api/migrations/env.py b/forms-flow-api/migrations/env.py index 23663ff2f5..79b8174be8 100644 --- a/forms-flow-api/migrations/env.py +++ b/forms-flow-api/migrations/env.py @@ -1,8 +1,12 @@ from __future__ import with_statement -from alembic import context -from sqlalchemy import engine_from_config, pool -from logging.config import fileConfig + import logging +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context # this is the Alembic Config object, which provides # access to the values within the .ini file in use. @@ -18,8 +22,9 @@ # from myapp import mymodel # target_metadata = mymodel.Base.metadata from flask import current_app -config.set_main_option('sqlalchemy.url', - current_app.config.get('SQLALCHEMY_DATABASE_URI')) +config.set_main_option( + 'sqlalchemy.url', current_app.config.get( + 'SQLALCHEMY_DATABASE_URI').replace('%', '%%')) target_metadata = current_app.extensions['migrate'].db.metadata # other values from the config, defined by the needs of env.py, @@ -41,7 +46,9 @@ def run_migrations_offline(): """ url = config.get_main_option("sqlalchemy.url") - context.configure(url=url) + context.configure( + url=url, target_metadata=target_metadata, literal_binds=True + ) with context.begin_transaction(): context.run_migrations() @@ -65,21 +72,23 @@ def process_revision_directives(context, revision, directives): directives[:] = [] logger.info('No changes in schema detected.') - engine = engine_from_config(config.get_section(config.config_ini_section), - prefix='sqlalchemy.', - poolclass=pool.NullPool) + connectable = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.', + poolclass=pool.NullPool, + ) - connection = engine.connect() - context.configure(connection=connection, - target_metadata=target_metadata, - process_revision_directives=process_revision_directives, - **current_app.extensions['migrate'].configure_args) + with connectable.connect() as connection: + context.configure( + connection=connection, + target_metadata=target_metadata, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args + ) - try: with context.begin_transaction(): context.run_migrations() - finally: - connection.close() + if context.is_offline_mode(): run_migrations_offline() diff --git a/forms-flow-api/src/api/models/form_process_mapper.py b/forms-flow-api/src/api/models/form_process_mapper.py index 7c7d1797f6..3a8a579f3c 100644 --- a/forms-flow-api/src/api/models/form_process_mapper.py +++ b/forms-flow-api/src/api/models/form_process_mapper.py @@ -17,28 +17,30 @@ class FormProcessMapper(AuditDateTimeMixin, AuditUserMixin, BaseModel, db.Model) form_id = db.Column(db.String(50), nullable=False) form_name = db.Column(db.String(100), nullable=False) form_revision_number = db.Column(db.String(10), nullable=False) - process_key = db.Column(db.String(50), nullable=True) - process_name = db.Column(db.String(100), nullable=True) + process_key = db.Column(db.String(50), nullable=False) + process_name = db.Column(db.String(100), nullable=False) status = db.Column(db.String(10), nullable=False) comments = db.Column(db.String(300), nullable=True) tenant_id = db.Column(db.Integer, nullable=True) - application = db.relationship('Application', backref='form_process_mapper', lazy=True) + application = db.relationship( + "Application", backref="form_process_mapper", lazy=True + ) @classmethod def create_from_dict(cls, mapper_info: dict) -> FormProcessMapper: """Create new mapper between form and process.""" if mapper_info: mapper = FormProcessMapper() - mapper.form_id = mapper_info['form_id'] - mapper.form_name = mapper_info['form_name'] - mapper.form_revision_number = mapper_info['form_revision_number'] - mapper.process_key = mapper_info['process_key'] - mapper.process_name = mapper_info['process_name'] - mapper.status = mapper_info['status'] - mapper.comments = mapper_info['comments'] - mapper.created_by = mapper_info['created_by'] - mapper.tenant_id = mapper_info.get('tenant_id') + mapper.form_id = mapper_info["form_id"] + mapper.form_name = mapper_info["form_name"] + mapper.form_revision_number = mapper_info["form_revision_number"] + mapper.process_key = mapper_info.get("process_key") + mapper.process_name = mapper_info.get("process_name") + mapper.status = mapper_info["status"] + mapper.comments = mapper_info.get("comments") + mapper.created_by = mapper_info["created_by"] + mapper.tenant_id = mapper_info.get("tenant_id") mapper.save() return mapper return None @@ -46,10 +48,17 @@ def create_from_dict(cls, mapper_info: dict) -> FormProcessMapper: def update(self, mapper_info: dict): """Update form process mapper.""" self.update_from_dict( - ['form_id', 'form_name', 'form_revision_number', - 'process_key', 'process_name', 'comments', - 'modified_by'], - mapper_info) + [ + "form_id", + "form_name", + "form_revision_number", + "process_key", + "process_name", + "comments", + "modified_by", + ], + mapper_info, + ) self.commit() def mark_inactive(self): @@ -60,41 +69,52 @@ def mark_inactive(self): @classmethod def find_all(cls, page_number, limit): """Fetch all active form process mapper.""" - return cls.query.filter(FormProcessMapper.status == FormProcessMapperStatus.Active.value) \ - .paginate(page_number, limit, False).items # pylint: disable=no-member + return ( + cls.query.filter( + FormProcessMapper.status == FormProcessMapperStatus.Active.value + ) + .paginate(page_number, limit, False) + .items + ) # pylint: disable=no-member @classmethod def find_by_id(cls, form_process_mapper_id) -> FormProcessMapper: """Find active form process mapper that matches the provided id.""" return cls.query.filter( - and_(FormProcessMapper.id == form_process_mapper_id, - FormProcessMapper.status == FormProcessMapperStatus.Active.value)).first() # pylint: disable=no-member + and_( + FormProcessMapper.id == form_process_mapper_id, + FormProcessMapper.status == FormProcessMapperStatus.Active.value, + ) + ).first() # pylint: disable=no-member @classmethod def find_by_form_id(cls, form_id) -> FormProcessMapper: """Find active form process mapper that matches the provided form_id.""" return cls.query.filter( - and_(FormProcessMapper.form_id == form_id, - FormProcessMapper.status == FormProcessMapperStatus.Active.value)).first() # pylint: disable=no-member + and_( + FormProcessMapper.form_id == form_id, + FormProcessMapper.status == FormProcessMapperStatus.Active.value, + ) + ).first() # pylint: disable=no-member @classmethod def find_by_application_id(cls, application_id: int): """Fetch form process mapper details with application id.""" - where_condition = '' + where_condition = "" where_condition += f""" app.id = {str(application_id)} """ - result_proxy = db.session.execute(f"""select + result_proxy = db.session.execute( + f"""select mapper.id,mapper.process_key,mapper.process_name from application app, form_process_mapper mapper where app.form_process_mapper_id=mapper.id and {where_condition} - """) + """ + ) result = [] for row in result_proxy: info = dict(row) result.append(info) - return result[0] - - + return result[0] diff --git a/forms-flow-api/src/api/resources/__init__.py b/forms-flow-api/src/api/resources/__init__.py index 871a9a109c..84b0f62021 100644 --- a/forms-flow-api/src/api/resources/__init__.py +++ b/forms-flow-api/src/api/resources/__init__.py @@ -36,13 +36,12 @@ def specs_url(self): } API = CustomApi( - title='FORMIO API', + title='formsflow.ai API', version='1.0', - description='The API for FORMIO', + description='The API for formsflow.ai. Checkout: formsflow.ai to know more', security=['apikey'], authorizations=AUTHORIZATIONS, doc='/swagger/') - @API.errorhandler(BusinessException) def handle_business_exception(error: BusinessException): """Handle Business exception.""" diff --git a/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/listeners/task/FormConnectorListener.java b/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/listeners/task/FormConnectorListener.java new file mode 100644 index 0000000000..e15c2c6ebb --- /dev/null +++ b/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/listeners/task/FormConnectorListener.java @@ -0,0 +1,120 @@ +package org.camunda.bpm.extension.hooks.listeners.task; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.oauth2.sdk.util.CollectionUtils; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.camunda.bpm.engine.delegate.DelegateTask; +import org.camunda.bpm.engine.delegate.TaskListener; +import org.camunda.bpm.extension.commons.connector.HTTPServiceInvoker; +import org.camunda.bpm.extension.hooks.services.FormSubmissionService; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties; +import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + + +import java.util.List; + +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * This class associates form for workflow user task. + * + * @author sumathi.thirumani@aot-technologies.com + */ +@Component +public class FormConnectorListener implements TaskListener { + + private static final Logger LOGGER = Logger.getLogger(FormConnectorListener.class.getName()); + + @Autowired + private FormSubmissionService formSubmissionService; + + @Autowired + private HTTPServiceInvoker httpServiceInvoker; + + @Override + public void notify(DelegateTask delegateTask) { + LOGGER.info("Inside form connector listener-7"); + String submissionId = createSubmission(getFormUrl(delegateTask),getModifiedFormUrl(delegateTask)); + if(StringUtils.isNotBlank(submissionId)) { + delegateTask.getExecution().setVariable("formUrl", StringUtils.substringBeforeLast(getModifiedFormUrl(delegateTask),"/") + "/" + submissionId); + } + } + + + private String createSubmission(String sourceFormUrl, String targetFormUrl) { + String submission = formSubmissionService.readSubmission(sourceFormUrl); + if(submission.isEmpty()) { + throw new RuntimeException("Unable to retrieve submission"); + } + return formSubmissionService.createSubmission(targetFormUrl, createFormSubmissionData(submission)); + } + + + private String getFormId(DelegateTask delegateTask) { + + CamundaProperties camundaProperties = delegateTask.getExecution() + .getBpmnModelElementInstance() + .getExtensionElements() + .getElementsQuery() + .filterByType(CamundaProperties.class).singleResult(); + + List userTaskExtensionProperties = camundaProperties.getCamundaProperties() + .stream() + .filter(camundaProperty -> + camundaProperty.getCamundaName() + .equals(getFormIdProperty())) + .collect(Collectors.toList()); + + + if(CollectionUtils.isNotEmpty(userTaskExtensionProperties)) { + return userTaskExtensionProperties.get(0).getCamundaValue(); + } + + return null; + } + + private ObjectMapper getObjectMapper(){ + return new ObjectMapper(); + } + + private String createFormSubmissionData(String submission) { + try { + JsonNode dataNode = getObjectMapper().readTree(submission); + return getObjectMapper().writeValueAsString(new FormSubmission(dataNode.get("data"))); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + return null; + } + + private String getModifiedFormUrl(DelegateTask delegateTask) { + String formUrl = getFormUrl(delegateTask); + return StringUtils.replace(formUrl, StringUtils.substringBetween(formUrl, "form/", "/submission"), getFormId(delegateTask)); + + } + + private String getFormUrl(DelegateTask delegateTask) { + return String.valueOf(delegateTask.getExecution().getVariables().get("formUrl")); + } + + private String getFormIdProperty() { + return "formid"; + } +} + +@Scope("prototype") +@Data +@NoArgsConstructor +@AllArgsConstructor +class FormSubmission{ + private JsonNode data; +} diff --git a/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/services/FormSubmissionService.java b/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/services/FormSubmissionService.java index 4bb7956e15..5b5dc24530 100644 --- a/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/services/FormSubmissionService.java +++ b/forms-flow-bpm/src/main/java/org/camunda/bpm/extension/hooks/services/FormSubmissionService.java @@ -55,6 +55,22 @@ public String createRevision(String formUrl) { } return null; } + + public String createSubmission(String formUrl, String submission) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + ResponseEntity response = httpServiceInvoker.execute(getSubmissionUrl(formUrl), HttpMethod.POST, submission); + if(response.getStatusCode().value() == HttpStatus.CREATED.value()) { + JsonNode jsonNode = objectMapper.readTree(response.getBody()); + String submissionId = jsonNode.get("_id").asText(); + return submissionId; + } + } catch (JsonProcessingException e) { + LOGGER.log(Level.SEVERE,"Exception occurred in creating submission", e); + } + return null; + } + private String getSubmissionUrl(String formUrl){ return StringUtils.substringBeforeLast(formUrl,"/"); } diff --git a/forms-flow-forms/.circleci/config.yml b/forms-flow-forms/.circleci/config.yml index cf32c7ea9b..c110a3a9b3 100644 --- a/forms-flow-forms/.circleci/config.yml +++ b/forms-flow-forms/.circleci/config.yml @@ -2,7 +2,7 @@ version: 2 jobs: build: docker: - - image: circleci/node:8.11.3 + - image: circleci/node:12 - image: mongo:3.4.16 steps: - checkout diff --git a/forms-flow-forms/.gitignore b/forms-flow-forms/.gitignore index 736de89e01..fdaaaaa72b 100644 --- a/forms-flow-forms/.gitignore +++ b/forms-flow-forms/.gitignore @@ -4,4 +4,4 @@ node_modules client* app* npm-debug.log -.env +.env \ No newline at end of file diff --git a/forms-flow-forms/Changelog.md b/forms-flow-forms/Changelog.md index 30f0b43f9e..28323ea236 100644 --- a/forms-flow-forms/Changelog.md +++ b/forms-flow-forms/Changelog.md @@ -4,6 +4,392 @@ All notable changes to this project will be documented in this file The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/) +## 2.0.0-rc.34 +### Fixed + - Issues with ResetPassword action not setting form object correctly within email parameters. + +## 2.0.0-rc.33 +### Fixed + - Tests regarding new validations. + +## 2.0.0-rc.32 +### Added + - FJS-1380: Adds field actions for Select and Radio which allow onlyAvailableItems validation + +### Fixed + - FJS-1297: fix submission of nested wizards + +### Changed + - Upgrade mongoose@5.10.15, debug@4.3.1, adm-zip@0.5.0 + - Upgrade formiojs@4.12.2-rc.3 + +## 2.0.0-rc.31 +### Fixed + - FJS-1443: add exceptions for the DynamicWizard component + - Fixes an issue where for validating a submission with a form revision always was getting the latest version of the form. + +### Changed + - Upgrade formiojs@4.12.1 + - Upgrade mongoose@5.10.14, nodemailer@6.4.16, moment-timezone@0.5.32 + +## 2.0.0-rc.30 +### Fixed + - Fix references updating + +### Changed + - Upgrade mongodb@3.6.3, mongoose@5.10.13, nodemailer@6.4.15, eslint@7.13.0 + +## 2.0.0-rc.29 +### Fixed + - FJS-1336, FJS-1337, FJS-1422: Adds configFormio hook call to the index.js + +### Upgrade + - mongoose@5.10.12 + - formiojs@4.12.1-rc.25 + +## 2.0.0-rc.28 +### Fixed + - 112 split roles bug that fixes server tests + +### Changed + - Upgrade formiojs@4.12.1-rc.24 + +## 2.0.0-rc.27 +### Changed + - Upgrade formiojs@4.12.1-rc.23 + - Added hook to configure Formio instance + +## 2.0.0-rc.26 +### Fixed + - Failing tests for enterprise. + +## 2.0.0-rc.25 +### Fixed + - FOR-2805: Wizard Conditional pages not saving data + - 112 split permissions for field based resource access + +### Changed + - Upgrade formiojs@4.12.1-rc.19 + +## 2.0.0-rc.24 +### Fixed + - - Errors being thrown about calling save() in parallel + +## 2.0.0-rc.23 +### Fixed + - FOR-2741: Fixes memory leaks that come from Validator. + - Fixes an issue when we get a server error while submitting a form with conditional page with subform inside. + +### Changed + - Upgrade mongoose@5.10.9, nodemailer@6.4.14, nodemon@2.0.6, eslint@7.11.0, mocha@8.2.0 + +## 2.0.0-rc.22 +### Fixed + - Problem where btoa not being defined would cause validator to crash. + +## 2.0.0-rc.21 +### Fixed + - Issue where server calculations were not getting performed correctly." + +## 2.0.0-rc.20 +### Fixed + - FOR-2771: Fixed issue where calculated values get overridden on the server. + +## 2.0.0-rc.19 +### Changed + - Upgrade formiojs@4.12.1-rc.4 + +## 2.0.0-rc.18 +### Fixed + - FJS-1240: fixed an issue where address data (if address is inside dataGrid) is missed when loading as CSV. + +## 2.0.0-rc.17 +### Fixed + - Fixed issues where data would be ignored due to bug with renderer validation. + +### Changed + - Upgrade formiojs@4.12.1-rc.2 + - Upgrade config@3.3.2, mongoose@5.10.7, mssql@6.2.3, eslint@7.10.0 + +## 2.0.0-rc.16 +### Fixed + - Revert "Fixed an issue where submission of reference Nested Form is not updated when was modified through the parent…" + +## 2.0.0-rc.15 +### Changed + - Upgrade mongodb@3.6.2, mongoose@5.10.6, mssql@6.2.2, node-fetch@2.6.1, resourcejs@2.3.2, debug@4.2.0, formiojs@4.12.0, moment@2.29.0, eslint@7.9.0 + +### Fixed + - Fix some updates resetting password due to isomorphic validator adding back a default value. + - Fix queries so indexes exist for cosmos. + +## 2.0.0-rc.14 +### Fixed + - Adding ability to execute field actions on dryrun. Fixes validate endpoint for DataSource component. + +## 2.0.0-rc.13 +### Fixed + - FOR-2719: Fixes an issue where new actions and removed actions weren't deployed on a stage. + - Ensure we also check hostname when checking the NO_PROXY environment variable. + +### Changed + - FOR-2722: Updated a test for Webhook actions. + - Upgrade mongodb@3.6.1 + +## 2.0.0-rc.12 +### Fixed + - Ensure the mongoSA variables are always set correctly. + - Fixing an issue with the form revisions not getting set correctly when loading with full=true. + +### Changed + - Upgrade chance@1.1.7, formiojs@4.11.3, mongoose@5.10.2, mocha@8.1.3, eslint@7.8.0 + +## 2.0.0-rc.11 +### Removed + - Method override for security reasons. + +## 2.0.0-rc.10 +### Fixed + - FJS-1129: fixes an issue where the Custom Error Message is not used for the Unique validation error + - FOR-2728: modified CSVExporter preprocessor to convert roadio component data to string + +### Added + - A way to include the mongoCA certificate as a file path. + +### Changed + - Changing configuration (with reverse compatibility) of the mongoSA variable to the more correct mongoCA name. + +## 2.0.0-rc.8 +### Changed + - Merged changes from 1.x + +## 2.0.0-rc.8 +### Changed + - Group permissions so that it can handle more complex group assignments. + - Upgrade mongoose@5.9.25, eslint@7.5.0 + +## 2.0.0-rc.7 +### Fixed + - FOR-2708: Remove resource from action if it was not found on import. + - FJS-1049: Fixed CSV export of components with minimized schema. + - Fixed setting of formRevision property on import when revisions are enabled. + +## 2.0.0-rc.5-6 +### Changed + - Updated logging functionality. + +## 2.0.0-rc.1-4 +### Changed + - Merge changes from 1.x + +## 2.0.0-beta.10 +### Changed + - Update formio.js to 4.1.0-rc.13 + +## 2.0.0-beta.9 +### Changed + - retagging + +## 2.0.0-beta.8 +### Added + - Additional options to fetch wrapper. + +## 2.0.0-beta.7 +### Added + - New hooks to extend the authentication system. + +### Changed + - Update formio.js to 4.1.0-rc.6 + +## 2.0.0-beta.6 +### Added + - Add tree validation to server. + - Mongo SSL Certificate options. + +### Changed + - Replace request library with node-fetch. + - Updated formio.js to 4.10.0-rc.4 to fix isomorphic validation. + +## 2.0.0-beta.5 +### Changed + - Upgrade dependencies. + - FJS 864: Fixed login action resources limitation + - Set email for User and Admin ressource required & unique + - Fixed server crash on invalid x-query + +## 2.0.0-beta.4 +### Added + - Hooks for the alias. + - Hooks for the formResponse. + +### Changed + - Upgrade mongodb@3.5.5, mongoose@5.9.4, nodemailer@6.4.5, mssql@6.2.0 + - Upgrade formiojs@4.9.0-rc.10 + - Ensure that field actions are triggered on dryrun. + +## 2.0.0-beta.3 +#### Changed + - Upgrade formiojs@4.9.0-rc.6 + +## 2.0.0-beta.2 +#### Changed + - Upgrading dependencies. + +## 2.0.0-beta.1 +### Breaking Changes + - Isomorphic validations. May cause error interface and codes to change slightly. + +### Changed + - Upgrade mongodb@3.5.4, async@3.2.0 + - Upgrade ResourceJS@2.0.0 + +## 1.90.7 +### Fixed + - Problem where the req.params would get removed before sending off emails. + +## 1.90.6 +### Fixed + - Upgrade formiojs@4.11.2-rc.4 so it will remove errors about Element not defined. + +## 1.90.5 +### Added + - Add TLS connection for mongoose connection as well. + +## 1.90.4 +### Changed + - Upgrade dependencies. + +### Fixed + - FOR-2708: Remove resource from action if it was not found on import. + - FJS-1049: Fixed CSV export of components with minimized schema. + +## 1.90.3 +### Changed + - Fixed setting of formRevision property on import when revisions are enabled. + +## 1.90.2 +### Changed + - Cherry pick email fix for large emails. + +## 1.90.1 +### Changed + - Resource.js library to 2.3.1 to revert change in aggregation. + +## 1.90.0 +### Fixed + - A bad revert. + +## 1.89.0 +### Changed + - Revert "Added a middleware for loading a full form schema for use component settings. + +## 1.88.0 +### Fixed + - FOR-2707: Fixes an issue where the PATCH request was being failed if a form has a nested form as reference. + - Server crashes when a bad query is passed to ResourceJS + - Added a middleware for loading a full form schema for use component settings. + +## 1.87.0 +### Fixed + - Export of form controllers. + +## 1.86.0 +### Fixed + - FJS-704: Address Refactor Issues + - Refactored for the verbose health endpoint + - Fixed Form Controller export. + +### Added + - Support Extra form fields exporting + +### Changed + - Upgraded formio-workers@1.14.8, mongodb@3.5.9, mongoose@5.9.19, nodemailer@6.4.10, formiojs@4.10.2, fs-extra@9.0.1, resourcejs@2.2.0, mocha@8.0.1 + +## 1.85.0 +### Changed + - FJS-953: Fixed getting error when exporting scv with time inside dataGrid + - PDF 14 - Allow PDF Submission endpoint to be retrieved by 'Form Alias' + +## 1.84.0 +### Fixed + - Fixed callback invocation after alterFormSave series. + +## 1.83.0 +### Changed + - Update chance@1.1.6, formio-workers@1.14.7, mongodb@3.5.8, mongoose@5.9.16, nodemailer@6.4.8, moment@2.26.0, eslint@7.1.0, mocha@7.2.0 + +### Fixed + - FOR-2665: Ensure calculate value eval context + +## 1.82.0 +### Added + - Template import/export improvements. + +## 1.81.0 +### Added + - Fix (Tree): added validation schema. + +## 1.80.0 +### Changed + - FJS-917: Add options to use SSL Certs with Mongo connection. + +## 1.79.0 +### Added + - More options for mapping Save Submission action to a Resource. + +## 1.78.0 +### Changed + - Reverted action logs to save correctly. + +### Fixed + - Problem where malformed data could throw errors. + +## 1.77.0 +### Fixed + - Issue with email renderings not working with workers upgrade. + - EditGrid issues when exported in CSV format. + +## 1.76.0 +### Fixed + - FJS 864: Fixed login action resources limitation + - Fixed server crash on invalid x-query + +## 1.75.0 +### Fixed + - Issues where loading subforms could lose references. + +### Changed + - Upgrade formiojs@4.9.19 + +## 1.74.0 +### Changed + - Upgrade formiojs@4.9.18, mongodb@3.5.6, mongoose@5.9.9, nodemon@2.0.3, html-entities@1.3.1, semver@7.3.2 + +### Added + - Debug messages to the loadSubForms method. + - Validator for tagpad component. + +## 1.73.0 +### Fixed + - Validations for checkboxes configured as radio inputs. + +### Changed + - Upgrade config@3.3.1, formiojs@4.9.13, mongoose@5.9.7, nodemailer@6.4.6, mocha@7.1.1 + +## 1.72.0 +### Changed + - Upgrade mongoose@5.9.5 + +### Fixed + - Fixed CSVExporter: Add default format for datetime + +## 1.71.0 +### Changed + - Upgrade csv@5.3.2, mongodb@3.5.5, mongoose@5.9.4, nodemailer@6.4.5, async@3.2.0, config@3.3.0, mssql@6.2.0, mocha@7.1.0 + +### Fixed + - Fixing dryrun for field actions so they execute, and adding more hooks for formResponse and alias. + ## 1.70.0 ### Added - Tokens to the calculate value evaluate contexts. diff --git a/forms-flow-forms/Dockerfile b/forms-flow-forms/Dockerfile index d2a5993fdb..9aa7496080 100644 --- a/forms-flow-forms/Dockerfile +++ b/forms-flow-forms/Dockerfile @@ -13,7 +13,6 @@ WORKDIR /forms-flow-forms/app # (note: using pinned versions to ensure immutable build environment) RUN apk update && \ apk upgrade && \ - apk add python=2.7.18-r0 && \ apk add make=4.2.1-r2 && \ apk add g++=8.3.0-r0 @@ -49,4 +48,4 @@ RUN set -x \ # This will initialize the application based on # some questions to the user (login email, password, etc.) -ENTRYPOINT [ "node", "main.js" ] +ENTRYPOINT [ "node", "main" ] diff --git a/forms-flow-forms/LICENSE.txt b/forms-flow-forms/LICENSE.txt index d1fc9d1555..14a2a66ee5 100644 --- a/forms-flow-forms/LICENSE.txt +++ b/forms-flow-forms/LICENSE.txt @@ -1,26 +1,45 @@ -BSD License -Copyright (c) 2015, Form.io LLC +OSL-3.0 +https://opensource.org/licenses/OSL-3.0 +Copyright (c) 2020, Form.io LLC All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - * Neither the name of Form.io nor the - names of its contributors may be used to endorse or promote products - derived from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL Form.io LLC BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: + +Licensed under the Open Software License version 3.0 + +1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: + a) to reproduce the Original Work in copies, either alone or as part of a collective work; + b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; + c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; + d) to perform the Original Work publicly; and + e) to display the Original Work publicly. + +2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. + +3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. + +4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. + +5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). + +6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. + +7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. + +8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. + +9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). + +10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. + +11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. + +12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. + +13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. + +14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. + +16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. diff --git a/forms-flow-forms/README.md b/forms-flow-forms/README.md index 981d5e98d3..dba031c7dd 100644 --- a/forms-flow-forms/README.md +++ b/forms-flow-forms/README.md @@ -1,11 +1,13 @@ # Form Management Platform -![Formio](https://img.shields.io/badge/formio-1.70.0-blue) + +![Formio](https://img.shields.io/badge/formio-2.0.0--rc.34-blue) **formsflow.ai** leverages form.io to build "serverless" data management applications using a simple drag-and-drop form builder interface. To know more about form.io, go to https://github.com/formio/formio. ## Table of Content + * [Prerequisites](#prerequisites) * [Solution Setup](#solution-setup) * [Step 1 : Keycloak Setup](#keycloak-setup) @@ -29,10 +31,10 @@ Not applicable. ### Installation - * Make sure you have a Docker machine up and running. - * Make sure your current working directory is "forms-flow-forms". - * Rename the file **sample.env** to **.env**. - * Modify the configuration values as needed. Details below, +* Make sure you have a Docker machine up and running. +* Make sure your current working directory is "forms-flow-forms". +* Rename the file **sample.env** to **.env**. +* Modify the configuration values as needed. Details below, |Variable name | Meaning | Possible values | Default value | |--- | --- | --- | --- @@ -47,6 +49,7 @@ Not applicable. * The value of ROOT user account details (especially if this instance is not just for testing purposes) ### Running the application + * For Linux, * Run `docker-compose -f docker-compose-linux.yml build` to build. * Run `docker-compose -f docker-compose-linux.yml up -d` to start. @@ -130,3 +133,9 @@ recommend you to take a look at [Custom Component Docs](https://formio.github.io to understand how Form.io renderer allows for the creation of Custom components. You can also take a look at [formio.contrib](https://github.com/formio/contrib) to look for examples and even contribute the custom components you create. + +## LICENSE + +We have build formsflow.ai form management platform leveraging [formio](https://github.com/formio/formio). +We use the OSL-v3 license similar to formio to ensure appropriate attribution is +provided to form.io. Please read the [license](./LICENSE.txt) for more information. diff --git a/forms-flow-forms/config/default.json b/forms-flow-forms/config/default.json index 74d2d97565..2ad3e2d0fa 100644 --- a/forms-flow-forms/config/default.json +++ b/forms-flow-forms/config/default.json @@ -1,53 +1,15 @@ { "port": 3001, "appPort": 8080, - "host": "localhost", + "host": "localhost:3001", "protocol": "http", "allowedOrigins": ["*"], "domain": "http://localhost:3001", "basePath": "", "mongo": "mongodb://localhost:27017/formioapp", "mongoConfig": "", - "mongoSA": "", - "mongoSecret": "aot-admin", - "roles": { - "administrator": { - "title": "Administrator", - "description": "A role for Administrative Users.", - "admin": true, - "default": false - }, - "authenticated": { - "title": "Authenticated", - "description": "A role for Authenticated Users.", - "admin": false, - "default": false - }, - "anonymous": { - "title": "Anonymous", - "description": "A role for Anonymous Users.", - "admin": false, - "default": true - }, - "staffDesigner": { - "title": "Staff Designer", - "description": "A role for Staff Designer who would be designing the forms.", - "admin": true, - "default": false - }, - "staffReviewer": { - "title": "Staff Reviewer", - "description": "A role for form submission reviewer.", - "admin": false, - "default": false - }, - "client": { - "title": "Client", - "description": "A role for Clients to submit forms.", - "admin": false, - "default": true - } - }, + "mongoCA": "", + "mongoSecret": "--- change me now ---", "reservedForms": [ "submission", "exists", diff --git a/forms-flow-forms/docker-compose-linux.yml b/forms-flow-forms/docker-compose-linux.yml index ee1933eb18..ece898b51c 100644 --- a/forms-flow-forms/docker-compose-linux.yml +++ b/forms-flow-forms/docker-compose-linux.yml @@ -26,6 +26,7 @@ services: MONGO_INITDB_ROOT_PASSWORD: ${FORMIO_MONGO_PASSWORD} MONGO_INITDB_DATABASE: ${FORMIO_MONGO_DATABASE} volumes: + - ./mongo_entrypoint/001_user.js:/docker-entrypoint-initdb.d/001_user.js:ro - ./mongodb/data/db/:/data/db/ - ./mongodb/data/log/:/var/log/mongodb/ - ./mongodb/mongod.conf:/etc/mongod.conf @@ -48,9 +49,10 @@ services: - ./:/app:rw environment: DEBUG: formio:* - NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/formio?authSource=admin\"}" + NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/${FORMIO_MONGO_DATABASE}?authSource=admin\"}" ROOT_EMAIL: ${FORMIO_ROOT_EMAIL} ROOT_PASSWORD: ${FORMIO_ROOT_PASSWORD} + FORMIO_DOMAIN: ${FORMIO_DEFAULT_PROJECT_URL} stdin_open: true # -i tty: true # -t networks: diff --git a/forms-flow-forms/docker-compose-windows.yml b/forms-flow-forms/docker-compose-windows.yml index 383d868461..28bab6fe64 100644 --- a/forms-flow-forms/docker-compose-windows.yml +++ b/forms-flow-forms/docker-compose-windows.yml @@ -26,6 +26,7 @@ services: MONGO_INITDB_ROOT_PASSWORD: ${FORMIO_MONGO_PASSWORD} MONGO_INITDB_DATABASE: ${FORMIO_MONGO_DATABASE} volumes: + - ./mongo_entrypoint/001_user.js:/docker-entrypoint-initdb.d/001_user.js:ro - mdb-data:/data/db/ - ./mongodb/data/log/:/var/log/mongodb/ - ./mongodb/mongod.conf:/etc/mongod.conf @@ -48,9 +49,10 @@ services: - ./:/app:rw environment: DEBUG: formio:* - NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/formio?authSource=admin\"}" + NODE_CONFIG: "{\"mongo\":\"mongodb://${FORMIO_MONGO_USERNAME}:${FORMIO_MONGO_PASSWORD}@forms-flow-forms-db:27017/${FORMIO_MONGO_DATABASE}?authMechanism=SCRAM-SHA-1&authSource=admin\"}" ROOT_EMAIL: ${FORMIO_ROOT_EMAIL} ROOT_PASSWORD: ${FORMIO_ROOT_PASSWORD} + FORMIO_DOMAIN: ${FORMIO_DEFAULT_PROJECT_URL} stdin_open: true # -i tty: true # -t networks: diff --git a/forms-flow-forms/docker-compose.mongoreplica.backup b/forms-flow-forms/docker-compose.mongoreplica.backup deleted file mode 100644 index c35bfd0412..0000000000 --- a/forms-flow-forms/docker-compose.mongoreplica.backup +++ /dev/null @@ -1,57 +0,0 @@ -# note: the application initialization will download/unpack/configure files -# in the local directory... -# -# ##### Usage: -# *0. Create a broken symlink: `ln -sf "/.npm-packages/node_modules/" node_modules` -# 1. Start the database: `docker-compose up -d mongo` -# 2. Start the application: `docker-compose run formio` -# [3]. Stop the database: `docker-compose down` (add --volumes to clear data) -# [4]. Remove lingering docker images: `docker-compose down -v --rmi all` -# -# *TODO: Step 0 is for the bcrypt binary compiled on alpine, which is required... -# but this step feels like an anti-pattern and a better approach should be found - -version: '3.7' -services: - mongo: - image: mongo:4.1 - restart: always - volumes: - - mdb-data:/data/db - env_file: - - .env - environment: - MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME} - MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD} - MONGO_INITDB_DATABASE: ${MONGO_INITDB_DATABASE} - MONGO_REPLICA_SET_NAME: ${MONGO_REPLICA_SET_NAME} - ports: - - "27017:27017" - healthcheck: - test: test $$(echo "rs.initiate().ok || rs.status().ok" | mongo -u $${MONGO_INITDB_ROOT_USERNAME} -p $${MONGO_INITDB_ROOT_PASSWORD} --quiet) -eq 1 - interval: 10s - start_period: 30s - command: ["--replSet", "${MONGO_REPLICA_SET_NAME}", "--bind_ip_all"] - - formio: - build: ./ - # The app will restart until Mongo is listening - restart: always - links: - - mongo - ports: - - "3001:3001" - # The application wants to download things to the local directory - # TODO: really wish I could mount this as read-only - volumes: - - ./:/app:rw - environment: - DEBUG: formio:* - NODE_CONFIG: '{"mongo": "mongodb://mongo:27017/formio"}' - ROOT_EMAIL: admin@example.com - ROOT_PASSWORD: CHANGEME - stdin_open: true # -i - tty: true # -t - -volumes: - mdb-data: diff --git a/forms-flow-forms/index.js b/forms-flow-forms/index.js index e4fed95403..31f098db4d 100644 --- a/forms-flow-forms/index.js +++ b/forms-flow-forms/index.js @@ -7,13 +7,16 @@ const router = express.Router(); const mongoose = require('mongoose'); mongoose.Promise = global.Promise; const bodyParser = require('body-parser'); -const methodOverride = require('method-override'); const _ = require('lodash'); const events = require('events'); const Q = require('q'); const nunjucks = require('nunjucks'); const util = require('./src/util/util'); const log = require('debug')('formio:log'); + +const originalGetToken = util.Formio.getToken; +const originalEvalContext = util.Formio.Components.components.component.prototype.evalContext; + // Keep track of the formio interface. router.formio = {}; @@ -48,6 +51,16 @@ module.exports = function(config) { } }; + router.formio.audit = (event, req, ...info) => { + if (config.audit) { + const result = router.formio.hook.alter('audit', info, event, req); + + if (result) { + console.log(...result); + } + } + }; + /** * Initialize the formio server. */ @@ -63,6 +76,8 @@ module.exports = function(config) { // Get the hook system. router.formio.hook = require('./src/util/hook')(router.formio); + router.formio.hook.alter('configFormio', {Formio: util.Formio}); + // Get the encryption system. router.formio.encrypt = require('./src/util/encrypt'); @@ -82,6 +97,22 @@ module.exports = function(config) { // Add the database connection to the router. router.formio.db = db; + // Ensure we do not have memory leaks in core renderer + router.use((req, res, next) => { + util.Formio.forms = {}; + util.Formio.cache = {}; + util.Formio.Components.components.component.Validator.config = { + db: null, + token: null, + form: null, + submission: null + }; + util.Formio.getToken = originalGetToken; + util.Formio.Components.components.component.prototype.evalContext = originalEvalContext; + + next(); + }); + // Establish our url alias middleware. if (!router.formio.hook.invoke('init', 'alias', router.formio)) { router.use(router.formio.middleware.alias); @@ -100,10 +131,9 @@ module.exports = function(config) { router.use(bodyParser.json({ limit: '16mb' })); - router.use(methodOverride('X-HTTP-Method-Override')); // Error handler for malformed JSON - router.use(function(err, req, res, next) { + router.use((err, req, res, next) => { if (err instanceof SyntaxError) { res.status(400).send(err.message); } @@ -113,7 +143,7 @@ module.exports = function(config) { // CORS Support const corsRoute = cors(router.formio.hook.alter('cors')); - router.use(function(req, res, next) { + router.use((req, res, next) => { if (req.url === '/') { return next(); } @@ -159,7 +189,7 @@ module.exports = function(config) { } let mongoUrl = config.mongo; - const mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {}; + let mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {}; if (!mongoConfig.hasOwnProperty('connectTimeoutMS')) { mongoConfig.connectTimeoutMS = 300000; } @@ -179,14 +209,21 @@ module.exports = function(config) { mongoUrl = config.mongo.join(','); mongoConfig.mongos = true; } - if (config.mongoSA) { + if (config.mongoSA || config.mongoCA) { mongoConfig.sslValidate = true; - mongoConfig.sslCA = config.mongoSA; + mongoConfig.sslCA = config.mongoSA || config.mongoCA; } mongoConfig.useUnifiedTopology = true; mongoConfig.useCreateIndex = true; + if (config.mongoSSL) { + mongoConfig = { + ...mongoConfig, + ...config.mongoSSL, + }; + } + // Connect to MongoDB. mongoose.connect(mongoUrl, mongoConfig); mongoose.set('useFindAndModify', false); @@ -210,7 +247,8 @@ module.exports = function(config) { router.formio.schemas = { PermissionSchema: require('./src/models/PermissionSchema')(router.formio), - AccessSchema: require('./src/models/AccessSchema')(router.formio) + AccessSchema: require('./src/models/AccessSchema')(router.formio), + FieldMatchAccessPermissionSchema: require('./src/models/FieldMatchAccessPermissionSchema')(router.formio), }; // Get the models for our project. diff --git a/forms-flow-forms/install.js b/forms-flow-forms/install.js index 8840569a96..aff79ea634 100644 --- a/forms-flow-forms/install.js +++ b/forms-flow-forms/install.js @@ -40,7 +40,6 @@ module.exports = function(formio, items, done) { return done(); } - const request = require('request'); const ProgressBar = require('progress'); util.log(`Downloading ${dir}${'...'.green}`); @@ -49,11 +48,11 @@ module.exports = function(formio, items, done) { let tries = 0; let bar = null; (function downloadProject() { - request.get(url) - .on('response', function(res) { + util.fetch(url) + .then(function(res) { if ( - !res.headers.hasOwnProperty('content-disposition') || - !parseInt(res.headers['content-length'], 10) + !res.headers.has('content-disposition') || + !parseInt(res.headers.get('content-length'), 10) ) { if (tries++ > 3) { return done('Unable to download project. Please try again.'); @@ -68,21 +67,21 @@ module.exports = function(formio, items, done) { complete: '=', incomplete: ' ', width: 50, - total: parseInt(res.headers['content-length'], 10) + total: parseInt(res.headers.get('content-length'), 10) }); - res.pipe(fs.createWriteStream(zipFile, { + res.body.pipe(fs.createWriteStream(zipFile, { flags: 'w' })); - res.on('data', function(chunk) { + res.body.on('data', function(chunk) { if (bar) { bar.tick(chunk.length); } }); - res.on('error', function(err) { + res.body.on('error', function(err) { downloadError = err; }); - res.on('end', function() { + res.body.on('end', function() { setTimeout(function() { done(downloadError); }, 100); @@ -145,7 +144,7 @@ module.exports = function(formio, items, done) { // Change the project configuration. const config = fs.readFileSync(path.join(directoryPath, 'config.template.js')); const newConfig = nunjucks.renderString(config.toString(), { - domain: formio.config.domain ? formio.config.domain : 'https://form.io' + domain: process.env.FORMIO_DOMAIN?process.env.FORMIO_DOMAIN : (formio.config.domain ? formio.config.domain : 'https://form.io') }); fs.writeFileSync(path.join(directoryPath, 'config.js'), newConfig); done(); diff --git a/forms-flow-forms/mongo_entrypoint/001_user.js b/forms-flow-forms/mongo_entrypoint/001_user.js new file mode 100644 index 0000000000..f45e0fcea6 --- /dev/null +++ b/forms-flow-forms/mongo_entrypoint/001_user.js @@ -0,0 +1,16 @@ +require('dotenv').config(); +const user = process.env("FORMIO_MONGO_USERNAME"); +const password = process.env("FORMIO_MONGO_PASSWORD"); + +db.createUser( + { + user: user, + pwd: password, + roles:[ + { + role: "readWrite", + db: "admin" + } + ] + } +); \ No newline at end of file diff --git a/forms-flow-forms/package-lock.json b/forms-flow-forms/package-lock.json index bcac1daebf..badaf9f567 100644 --- a/forms-flow-forms/package-lock.json +++ b/forms-flow-forms/package-lock.json @@ -1,6 +1,6 @@ { "name": "formio", - "version": "1.70.0", + "version": "2.0.0-rc.34", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -10,9 +10,9 @@ "integrity": "sha512-l7z0DPCi2Hp88w12JhDTtx5d0Y3+vhfE7JKJb9O7sEz71Cwp053N8piTtTnnk/tUor9oZHgEKi/p3tQQmLPjvA==" }, "@azure/ms-rest-js": { - "version": "1.8.14", - "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.8.14.tgz", - "integrity": "sha512-IrCPN22c8RbKWA06ZXuFwwEb15cSnr0zZ6J8Fspp9ns1SSNTERf7hv+gWvTIis1FlwHy42Mfk8hVu0/r3a0AWA==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-1.9.0.tgz", + "integrity": "sha512-cB4Z2Mg7eBmet1rfbf0QSO1XbhfknRW7B+mX3IHJq0KGHaGJvCPoVTgdsJdCkazEMK1jtANFNEDDzSQacxyzbA==", "requires": { "@types/tunnel": "0.0.0", "axios": "^0.19.0", @@ -24,24 +24,10 @@ "xml2js": "^0.4.19" }, "dependencies": { - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" - }, - "xml2js": { - "version": "0.4.23", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", - "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", - "requires": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - } - }, - "xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -56,60 +42,141 @@ } }, "@babel/code-frame": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", - "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", "dev": true, "requires": { - "@babel/highlight": "^7.0.0" + "@babel/highlight": "^7.10.4" } }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, "@babel/highlight": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", - "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", "dev": true, "requires": { + "@babel/helper-validator-identifier": "^7.10.4", "chalk": "^2.0.0", - "esutils": "^2.0.2", "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } } }, "@babel/runtime": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.5.5.tgz", - "integrity": "sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@eslint/eslintrc": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.1.tgz", + "integrity": "sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA==", + "dev": true, "requires": { - "regenerator-runtime": "^0.13.2" + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + } } }, "@formio/bootstrap3": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@formio/bootstrap3/-/bootstrap3-2.2.0.tgz", - "integrity": "sha512-dyb2crBEjaF1pE6ruTIVFqBAlygP1MI6FGuP05VjIj+yz4VS1KEGkNo9ii89HFKqJ0/BW/YVcMPA7/FzCh2MvQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@formio/bootstrap3/-/bootstrap3-2.6.8.tgz", + "integrity": "sha512-gkDXJS5bvl4nn9tepFUctXedWd27JOfRID6zaEKVXiiAGBxcvvdcF2bHxslFFJbZTxHAHV2QgB2249R5R2UIyg==", "requires": { "resize-observer-polyfill": "^1.5.1" } }, "@formio/semantic": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@formio/semantic/-/semantic-2.1.0.tgz", - "integrity": "sha512-UL+JATzQim1MSkKsdqdNurq85GRyBS4kmlA7xysN458TvsHCbYbTY9g81UiQlxDicICkDKBg5ie9PxxF6q4YfA==" + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@formio/semantic/-/semantic-2.4.5.tgz", + "integrity": "sha512-azkpbc5HjjLFlXWEQxFfyVM/jpb3+4tqZX/MylePE4QYNZ9Oo4GoYSAhIyyP/u/JmcPgchldtP2jsbmP67cG6w==" + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true }, "@sphinxxxx/color-conversion": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz", "integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw==" }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@types/node": { - "version": "12.12.27", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.27.tgz", - "integrity": "sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A==" + "version": "12.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.19.7.tgz", + "integrity": "sha512-zvjOU1g4CpPilbTDUATnZCUb/6lARMRAqzT7ILwl1P3YvU2leEcZ2+fw9+Jrw/paXB1CgQyXTrN4hWDtqT9O2A==" }, "@types/readable-stream": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.5.tgz", - "integrity": "sha512-Mq2eLkGYamlcolW603FY2ROBvcl90jPF+3jLkjpBV6qS+2aVeJqlgRG0TVAa1oWbmPdb5yOWlOPVvQle76nUNw==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.9.tgz", + "integrity": "sha512-sqsgQqFT7HmQz/V5jH1O0fvQQnXAJO46Gg9LRO/JPfjmVmGUlcx831TZZO3Y3HtWhIkzf3kTsNT0Z0kzIhIvZw==", "requires": { "@types/node": "*", "safe-buffer": "*" @@ -123,6 +190,12 @@ "@types/node": "*" } }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, "JSONStream": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", @@ -137,12 +210,26 @@ "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" }, + "abab": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", + "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", + "optional": true + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "requires": { + "event-target-shim": "^5.0.0" + } + }, "accepts": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", @@ -150,33 +237,27 @@ "requires": { "mime-types": "~2.1.24", "negotiator": "0.6.2" - }, - "dependencies": { - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - } } }, "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-2.7.0.tgz", + "integrity": "sha1-q259nYhqrKiwhbwzEreaGYQz8Oc=", + "optional": true + }, + "acorn-globals": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-1.0.9.tgz", + "integrity": "sha1-VbtemGkVB7dFedBRNBMhfDgMVM8=", + "optional": true, + "requires": { + "acorn": "^2.1.0" + } }, "acorn-jsx": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz", - "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", + "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", "dev": true }, "adal-node": { @@ -196,9 +277,14 @@ }, "dependencies": { "@types/node": { - "version": "8.10.59", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.59.tgz", - "integrity": "sha512-8RkBivJrDCyPpBXhVZcjh7cQxVBSmRk9QM7hOketZzp6Tg79c0N8kkpAIito9bnJ3HCVCHVYz+KHTEbfQNfeVQ==" + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, @@ -208,22 +294,22 @@ "integrity": "sha1-R6++GiqSYhkdtoOOT9HTm0CCF0Y=" }, "adm-zip": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.14.tgz", - "integrity": "sha512-/9aQCnQHF+0IiCl0qhXoK7qs//SwYE7zX8lsr/DNk1BRAHYxeLZPL4pguwK29gUEqasYQjqPtEpDRSWEkdHn9g==" + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.0.tgz", + "integrity": "sha512-X6W8Pb1LFdDOQGMsO6OLGmRmyTzIxg5zjXitKjf2fQbqva4iAjcxWS4c01DH+HJ19n3qoQ9TstIRCxJmPOtDow==" }, "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "requires": { - "es6-promisify": "^5.0.0" + "debug": "4" } }, "ajv": { - "version": "6.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.11.0.tgz", - "integrity": "sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -232,91 +318,44 @@ }, "dependencies": { "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" } } }, "ansi-align": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", - "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", "dev": true, "requires": { - "string-width": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "string-width": "^3.0.0" } }, "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", "dev": true }, - "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "optional": true, + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, "argparse": { @@ -336,35 +375,16 @@ } } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "optional": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "optional": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "optional": true + "array-filter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-1.0.0.tgz", + "integrity": "sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=" }, "array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "optional": true - }, "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -383,16 +403,25 @@ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, - "assign-symbols": { + "assertion-error": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "optional": true + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.0.tgz", + "integrity": "sha1-x/hUOP3UZrx8oWq5DIFRN5el0js=" }, "ast-types": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.2.tgz", - "integrity": "sha512-uWMHxJxtfj/1oZClOxDEV1sQ1HCDkA4MG8Gr69KKeBjEVH0R84WlejZ0y2DcwyBlpAEMltmVYkVgqfLFb2oyiA==" + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz", + "integrity": "sha512-O0yuUDnZeQDL+ncNGlJ78BiO4jnYI3bvMsD5prT0/nsgijG/LpNBIr63gTjVTNsiGkgQhiyCShTgxt8oXOrklA==", + "requires": { + "tslib": "^2.0.1" + }, + "dependencies": { + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + } + } }, "astral-regex": { "version": "1.0.0", @@ -401,41 +430,42 @@ "dev": true }, "async": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/async/-/async-3.1.1.tgz", - "integrity": "sha512-X5Dj8hK1pJNC2Wzo2Rcp9FBVdJMGRR/S7V+lH46s8GVFhtbo5O4Le5GECCF/8PISVdkUA6mMPvgz7qTTD1rf1g==" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "optional": true + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "atoa": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atoa/-/atoa-1.0.0.tgz", "integrity": "sha1-DMDpGkgOc4+SPrwQNnZHF3mzSkk=" }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "optional": true - }, "autocompleter": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-6.0.2.tgz", - "integrity": "sha512-euXlT7i61H1Jd43kEA9Sq2M5pX8jl0Pxy5yEwxp79Jz00DejveE58ca/hF9jGS6l9FZIeA2yTKVK/gDKHNLDOA==" + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-6.0.5.tgz", + "integrity": "sha512-3SMOcS+2VT/RYjS1Fd7tO0IK7PCGKu/Z5QGMhNuEeXdgpT0FmyQyQj+r3fEF/w1qUNejVclbnBF6+jWExVjb1w==" + }, + "available-typed-arrays": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz", + "integrity": "sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==", + "requires": { + "array-filter": "^1.0.0" + } }, "aws-serverless-express": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/aws-serverless-express/-/aws-serverless-express-3.3.6.tgz", - "integrity": "sha512-VTn8YQpPpMAEdMeGjyaSygy7Rc0057C9MUjeZION0NBqmwTyphpu9Tc5DCHRNF4qNFQ9x1xcOte6OXKzJvvDhw==", + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/aws-serverless-express/-/aws-serverless-express-3.3.8.tgz", + "integrity": "sha512-2TQdF5EhxnAtGeEi+wSi2M3xCfpiemuImnpU7HKih3onH0izJ/G2tkM+gwcGHZEsW/gLWFl/JjQAYGa3fILfvw==", "requires": { "binary-case": "^1.0.0", "type-is": "^1.6.16" @@ -447,9 +477,9 @@ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz", - "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==" + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { "version": "0.19.2", @@ -459,66 +489,121 @@ "follow-redirects": "1.5.10" } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + } + } }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "optional": true, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "optional": true, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "core-js": { + "version": "2.6.12", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz", + "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==" + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "ms": "2.0.0" } } } }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -543,15 +628,14 @@ "integrity": "sha512-9Kq8m6NZTAgy05Ryuh7U3Qc4/ujLQU1AZ5vMw4cr3igTdi5itZC6kCNrRr2X8NzPiDn2oUIFTfa71DKMnue/Zg==" }, "binary-extensions": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", - "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", - "optional": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", + "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==" }, "bl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.1.tgz", + "integrity": "sha512-6Pesp1w0DEX1N550i/uGV/TqucVL4AM/pgThFSN/Qq9si1/DF9aIHs1BxD8V/QU0HoeHO6cQRTAuYnLPKq1e4g==", "requires": { "readable-stream": "^2.3.5", "safe-buffer": "^5.1.1" @@ -579,11 +663,6 @@ "type-is": "~1.6.17" }, "dependencies": { - "bytes": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", - "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" - }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -591,122 +670,115 @@ "requires": { "ms": "2.0.0" } + } + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^5.3.1", + "chalk": "^3.0.0", + "cli-boxes": "^2.2.0", + "string-width": "^4.1.0", + "term-size": "^2.1.0", + "type-fest": "^0.8.1", + "widest-line": "^3.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" + "color-convert": "^2.0.1" } }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" } }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "raw-body": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", - "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { - "bytes": "3.1.0", - "http-errors": "1.7.2", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" + "color-name": "~1.1.4" } }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } - } - } - }, - "boxen": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", - "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", - "dev": true, - "requires": { - "ansi-align": "^2.0.0", - "camelcase": "^4.0.0", - "chalk": "^2.0.1", - "cli-boxes": "^1.0.0", - "string-width": "^2.0.0", - "term-size": "^1.2.0", - "widest-line": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" } }, "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" } } } @@ -721,32 +793,11 @@ } }, "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "optional": true, + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "fill-range": "^7.0.1" } }, "browser-cookies": { @@ -761,9 +812,9 @@ "dev": true }, "bson": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz", - "integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg==" + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.5.tgz", + "integrity": "sha512-kDuEzldR21lHciPQAIulLs1LZlCXdLziXI6Mb/TDkwXhb//UORJNPXgcRs2CuO4H0DcMkpfT3/ySsP3unoZjBg==" }, "buffer-equal-constant-time": { "version": "1.0.1", @@ -781,21 +832,45 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "optional": true, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + } + } + }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" } }, "callsites": { @@ -805,14 +880,9 @@ "dev": true }, "camelcase": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", - "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=" - }, - "capture-stack-trace": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz", - "integrity": "sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true }, "caseless": { @@ -820,157 +890,129 @@ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, + "chai": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-2.3.0.tgz", + "integrity": "sha1-ii9qNHSNqAEJD9cyh7Kqc5pOkJo=", + "requires": { + "assertion-error": "1.0.0", + "deep-eql": "0.1.3" + } + }, "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" } }, "chance": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.4.tgz", - "integrity": "sha512-pXPDSu3knKlb6H7ahQfpq//J9mSOxYK8SMtp8MV/nRJh8aLRDIl0ipLH8At8+nVogVwtvPZzyIzY/EbcY/cLuQ==" + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/chance/-/chance-1.1.7.tgz", + "integrity": "sha512-bua/2cZEfzS6qPm0vi3JEvGNbriDLcMj9lKxCQOjUcCJRcyjA7umP0zZm6bKWWlBN04vA0L99QGH/CZQawr0eg==" }, - "chardet": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", - "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", - "dev": true + "cheerio": { + "version": "1.0.0-rc.2", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.2.tgz", + "integrity": "sha1-S59TqBsn5NXawxwP/Qz6A8xoMNs=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash": "^4.15.0", + "parse5": "^3.0.1" + } }, "choices.js": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-7.0.0.tgz", - "integrity": "sha512-qK9NEX4/riACBGle27BOWf6dZf+VqitbALB96LERBslqa05Tsn91bicACmLhw2Qym227mQIKN17hcIPwsRZ0mQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-9.0.1.tgz", + "integrity": "sha512-JgpeDY0Tmg7tqY6jaW/druSklJSt7W68tXFJIw0GSGWmO37SDAL8o60eICNGbzIODjj02VNNtf5h6TgoHDtCsA==", "requires": { - "classnames": "^2.2.6", - "deepmerge": "^2.2.1", - "fuse.js": "3.4.2", - "redux": "^3.3.1" + "deepmerge": "^4.2.0", + "fuse.js": "^3.4.5", + "redux": "^4.0.4" } }, "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "optional": true, + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.3.tgz", + "integrity": "sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ==", "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "optional": true - } + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.1.2", + "glob-parent": "~5.1.0", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.5.0" } }, "ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", "dev": true }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "optional": true, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "optional": true, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "ansi-regex": "^4.1.0" } } } }, - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" - }, - "cli-boxes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz", - "integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM=", - "dev": true - }, - "cli-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", - "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", - "dev": true, - "requires": { - "restore-cursor": "^3.1.0" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, "clone": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "optional": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -980,6 +1022,11 @@ "color-name": "1.1.3" } }, + "color-logger": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/color-logger/-/color-logger-0.0.6.tgz", + "integrity": "sha1-5WJF7ymCJlcRDHy3WpzXhstp7Rs=" + }, "color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", @@ -992,22 +1039,28 @@ "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" }, "combined-stream": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", - "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "requires": { "delayed-stream": "~1.0.0" } }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" }, - "composable-middleware": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/composable-middleware/-/composable-middleware-0.3.0.tgz", - "integrity": "sha1-JYyUYunQ6eMhM/cmDuJRWdDbvgk=" + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==" + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true }, "concat-map": { "version": "0.0.1", @@ -1027,25 +1080,25 @@ } }, "config": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/config/-/config-3.2.6.tgz", - "integrity": "sha512-oDHjuBD4w4qrG/Rb0ghDeELz6u/NR0Jb0hP6tEEKWTC6e/mD+rXmPTdPPQMAjtgyo+p7ILb8MvTX/erbDuGzqA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/config/-/config-3.3.3.tgz", + "integrity": "sha512-T3RmZQEAji5KYqUQpziWtyGJFli6Khz7h0rpxDwYNjSkr5ynyTWwO7WpfjHzTXclNCDfSWQRcwMb+NwxJesCKw==", "requires": { - "json5": "^1.0.1" + "json5": "^2.1.1" } }, "configstore": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.2.tgz", - "integrity": "sha512-vtv5HtGjcYUgFrXc6Kx747B83MRRVS5R1VTEQoXvuP+kMI+if6uywV0nDGoiydJRy4yk7h9od5Og0kxx4zUXmw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, "requires": { - "dot-prop": "^4.1.0", + "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", - "make-dir": "^1.0.0", - "unique-string": "^1.0.0", - "write-file-atomic": "^2.0.0", - "xdg-basedir": "^3.0.0" + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" } }, "consolidate": { @@ -1094,16 +1147,10 @@ "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==", "dev": true }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "optional": true - }, "core-js": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.2.1.tgz", - "integrity": "sha512-Qa5XSVefSVPRxy2XfUC13WbvqkxhkwB3ve+pgCQveNgYzbM/UxZeu1dcOX/xr4UmfUd+muuvsaxilQzCyUurMw==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.0.tgz", + "integrity": "sha512-W2VYNB0nwQQE7tKS7HzXd7r2y/y2SVJl4ga6oH/dnaLFzM0o2lB2P3zCkWj5Wc/zyMYjtgd5Hmhk0ObkQFZOIA==" }, "core-util-is": { "version": "1.0.2", @@ -1119,80 +1166,92 @@ "vary": "^1" } }, - "create-error-class": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz", - "integrity": "sha1-Br56vvlHo/FKMP1hBnHUAbyot7Y=", - "dev": true, - "requires": { - "capture-stack-trace": "^1.0.0" - } - }, "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" } }, "crossvent": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", - "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz", + "integrity": "sha1-rSCHjkkh6b5z2daXb4suzQ9xoLE=", "requires": { - "custom-event": "1.0.0" + "custom-event": "^1.0.0" } }, "crypto-random-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", - "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "optional": true + }, + "cssstyle": { + "version": "0.2.37", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.2.37.tgz", + "integrity": "sha1-VBCXI0yyUTyDzu06zdwn/yeYfVQ=", + "optional": true, + "requires": { + "cssom": "0.3.x" + } + }, "csv": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.1.tgz", - "integrity": "sha512-UBO4x5EYpihikfjHUQ7dCTIgC+e9TrWWZbCcoMr935tcAZfXT1MZKHLD+aYSHs1jwW2G1uljpFfJ4XxYwQ6t5w==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/csv/-/csv-5.3.2.tgz", + "integrity": "sha512-odDyucr9OgJTdGM2wrMbJXbOkJx3nnUX3Pt8SFOwlAMOpsUQlz1dywvLMXJWX/4Ib0rjfOsaawuuwfI5ucqBGQ==", "requires": { "csv-generate": "^3.2.4", - "csv-parse": "^4.8.2", - "csv-stringify": "^5.3.4", + "csv-parse": "^4.8.8", + "csv-stringify": "^5.3.6", "stream-transform": "^2.0.1" } }, "csv-generate": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.2.4.tgz", - "integrity": "sha512-qNM9eqlxd53TWJeGtY1IQPj90b563Zx49eZs8e0uMyEvPgvNVmX1uZDtdzAcflB3PniuH9creAzcFOdyJ9YGvA==" + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-3.3.0.tgz", + "integrity": "sha512-EXSru4QwEWKwM7wwsJbhrZC+mHEJrhQFoXlohHs80CAU8Qhlv9gaw1sjzNiC3Hr3oUx5skDmEiAlz+tnKWV0RA==" }, "csv-parse": { - "version": "4.8.2", - "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.8.2.tgz", - "integrity": "sha512-WfYwyJepTbjS5jWAWpVskOJ8Z10231HaFw6qJhSjGrpfMPf3yuoRohlasYsP/6/3YgTQcvZpTvoUo37eaei9Fw==" + "version": "4.14.1", + "resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-4.14.1.tgz", + "integrity": "sha512-4wmcO7QbWtDAncGFaBwlWFPhEN4Akr64IbM4zvDwEOFekI8blLc04Nw7XjQjtSNy+3AUAgBgtUa9nWo5Cq89Xg==" }, "csv-stringify": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.3.4.tgz", - "integrity": "sha512-w3sjZh/b5xvN1NeWPbMBnvW+Q4D+cCoAk/2J0C/DqJKV3dHqseQGzP/BsdpqbIBl5UTFQxHgHkSUu5aiMFT62g==" + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-5.5.3.tgz", + "integrity": "sha512-JKG8vIHpWPzdilp2SAmvjmAiIhD+XGKGdhZBGi8QIECgJAsFr7k5CmJIW2QkSxBBsctvmojM25s+UINzQ5NLTg==" }, "custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" }, "custom-event-polyfill": { "version": "1.0.7", @@ -1223,35 +1282,63 @@ "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", "requires": { - "ms": "^2.1.1" + "ms": "2.1.2" }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "decamelize": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "optional": true + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + } }, "deep-equal": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", - "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.4.tgz", + "integrity": "sha512-BUfaXrVoCfgkOQY/b09QdO9L3XNoF2XH0A3aY9IQwQL/ZjLOe8FQgCNVl1wiolhsFo8kFdO9zdPViCPbmaJA5w==", + "requires": { + "es-abstract": "^1.18.0-next.1", + "es-get-iterator": "^1.1.0", + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.2", + "is-regex": "^1.1.1", + "isarray": "^2.0.5", + "object-is": "^1.1.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "regexp.prototype.flags": "^1.3.0", + "side-channel": "^1.0.3", + "which-boxed-primitive": "^1.0.1", + "which-collection": "^1.0.1", + "which-typed-array": "^1.1.2" + } }, "deep-extend": { "version": "0.6.0", @@ -1265,60 +1352,24 @@ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "deepmerge": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz", - "integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==" + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, "requires": { "object-keys": "^1.0.12" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "degenerator": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-1.0.4.tgz", @@ -1357,15 +1408,23 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "requires": { + "repeating": "^2.0.0" + } + }, "dialog-polyfill": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.0.tgz", - "integrity": "sha512-fOj68T8KB6UIsDFmK7zYbmORJMLYkRmtsLM1W6wbCVUWu4Hdcud5bqbvuueTxO84JXtK9HcpCHV9vNwlWUdCIw==" + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.4.tgz", + "integrity": "sha512-LqQvIky9+qIFD7MCESOXdMZc4ObCdEbzbuvnPJVCiRXKQMzMjpQKK50UI+2sj0bJBPKn6ugPpMRpuLrgYc4RjA==" }, "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", "dev": true }, "doctrine": { @@ -1377,24 +1436,55 @@ "esutils": "^2.0.2" } }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, "dompurify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.1.tgz", - "integrity": "sha512-57cdUdCG300XfdO+BTYfgBtcP0G6LuwnQ8BZQ2t9wFDIa+DoQT9iaxKox5aJJYovAA3B4wNIeNlqBfnS/OWvRQ==" + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.11.tgz", + "integrity": "sha512-qVoGPjIW9IqxRij7klDQQ2j6nSe4UNWANBhZNLnsS7ScTtLb+3YdxkRY8brNTpkUiTtcXsCJO+jS0UCDfenLuA==" + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } }, "dot-prop": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz", - "integrity": "sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, "requires": { - "is-obj": "^1.0.0" + "is-obj": "^2.0.0" } }, "dotenv": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", - "integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==" + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" }, "downloadjs": { "version": "1.4.7", @@ -1402,12 +1492,12 @@ "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, "dragula": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.2.tgz", - "integrity": "sha1-SjXJ05gf+sGpScKcpyhQWOhzk84=", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.3.tgz", + "integrity": "sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==", "requires": { "contra": "1.9.4", - "crossvent": "1.5.4" + "crossvent": "1.5.5" } }, "duplexer3": { @@ -1439,9 +1529,9 @@ "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", "dev": true }, "encodeurl": { @@ -1449,30 +1539,67 @@ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-get-iterator": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", + "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.1", + "has-symbols": "^1.0.1", + "is-arguments": "^1.0.4", + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-string": "^1.0.5", + "isarray": "^2.0.5" } }, "es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, "requires": { "is-callable": "^1.1.4", "is-date-object": "^1.0.1", @@ -1492,6 +1619,12 @@ "es6-promise": "^4.0.3" } }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -1500,13 +1633,12 @@ "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz", + "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==", "requires": { "esprima": "^4.0.1", "estraverse": "^4.2.0", @@ -1519,32 +1651,86 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" + } + } + }, + "esdoc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esdoc/-/esdoc-1.1.0.tgz", + "integrity": "sha512-vsUcp52XJkOWg9m1vDYplGZN2iDzvmjDL5M/Mp8qkoDG3p2s0yIQCIjKR5wfPBaM3eV14a6zhQNYiNTCVzPnxA==", + "requires": { + "babel-generator": "6.26.1", + "babel-traverse": "6.26.0", + "babylon": "6.18.0", + "cheerio": "1.0.0-rc.2", + "color-logger": "0.0.6", + "escape-html": "1.0.3", + "fs-extra": "5.0.0", + "ice-cap": "0.0.4", + "marked": "0.3.19", + "minimist": "1.2.0", + "taffydb": "2.7.3" + }, + "dependencies": { + "fs-extra": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-5.0.0.tgz", + "integrity": "sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "optional": true + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" } } }, + "esdoc-coverage-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/esdoc-coverage-plugin/-/esdoc-coverage-plugin-1.1.0.tgz", + "integrity": "sha1-OGmGnNf4eJH5cmJXh2laKZrs5Fw=" + }, + "esdoc-type-inference-plugin": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/esdoc-type-inference-plugin/-/esdoc-type-inference-plugin-1.0.2.tgz", + "integrity": "sha512-tMIcEHNe1uhUGA7lT1UTWc9hs2dzthnTgmqXpmeUhurk7fL2tinvoH+IVvG/sLROzwOGZQS9zW/F9KWnpMzLIQ==" + }, "eslint": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-6.8.0.tgz", - "integrity": "sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.14.0.tgz", + "integrity": "sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA==", "dev": true, "requires": { "@babel/code-frame": "^7.0.0", + "@eslint/eslintrc": "^0.2.1", "ajv": "^6.10.0", - "chalk": "^2.1.0", - "cross-spawn": "^6.0.5", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^1.4.3", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.1.2", - "esquery": "^1.0.1", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.0", + "esquery": "^1.2.0", "esutils": "^2.0.2", "file-entry-cache": "^5.0.1", "functional-red-black-tree": "^1.0.1", @@ -1553,91 +1739,134 @@ "ignore": "^4.0.6", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", - "inquirer": "^7.0.0", "is-glob": "^4.0.0", "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.3.0", - "lodash": "^4.17.14", + "levn": "^0.4.1", + "lodash": "^4.17.19", "minimatch": "^3.0.4", - "mkdirp": "^0.5.1", "natural-compare": "^1.4.0", - "optionator": "^0.8.3", + "optionator": "^0.9.1", "progress": "^2.0.0", - "regexpp": "^2.0.1", - "semver": "^6.1.2", - "strip-ansi": "^5.2.0", - "strip-json-comments": "^3.0.1", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", "table": "^5.2.3", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "color-convert": "^2.0.1" } }, - "ansi-regex": { + "chalk": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", "dev": true, "requires": { - "is-glob": "^4.0.1" - }, - "dependencies": { - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - } + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" } }, "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", "dev": true, "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" } }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "ansi-regex": "^5.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" } } } @@ -1648,39 +1877,61 @@ "integrity": "sha512-8V3cbflYi53i9QyWpl1MffkrSM/uz3tRQ86N+5K4WAa7qGtUeo9hDB5+ZrsfxT0nfnwqVf/1d5CgzUKH2EuKqw==" }, "eslint-scope": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.0.0.tgz", - "integrity": "sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, "requires": { - "esrecurse": "^4.1.0", + "esrecurse": "^4.3.0", "estraverse": "^4.1.1" } }, "eslint-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", - "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", "dev": true }, "espree": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.1.2.tgz", - "integrity": "sha512-2iUPuuPP+yW1PZaMSDM9eyVf8D5P0Hi8h83YtZ5bPc/zHYjII5khoixIUTMO794NOY8F/ThF1Bo8ncZILarUTA==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.0.tgz", + "integrity": "sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw==", "dev": true, "requires": { - "acorn": "^7.1.0", - "acorn-jsx": "^5.1.0", - "eslint-visitor-keys": "^1.1.0" + "acorn": "^7.4.0", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } } }, "esprima": { @@ -1689,32 +1940,48 @@ "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" }, "esquery": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", - "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", "dev": true, "requires": { - "estraverse": "^4.0.0" + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "requires": { - "estraverse": "^4.1.0" + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } } }, "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" }, "etag": { "version": "1.8.1", @@ -1740,87 +2007,20 @@ } } }, - "eventemitter2": { + "event-target-shim": { "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-5.0.1.tgz", - "integrity": "sha1-YZegldX7a1folC9v1+qtY6CclFI=" - }, - "eventemitter3": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", - "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "dev": true, - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - }, - "dependencies": { - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "dev": true, - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - } - } + "eventemitter2": { + "version": "6.4.3", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", + "integrity": "sha512-t0A2msp6BzOf+QAcI6z9XMktLj52OjGQg+8SJH6v5+3uxNpWYRR3wQmfA+6xtMU9kOC59qk9licus5dYcrYkMQ==" }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } + "eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "express": { "version": "4.17.1", @@ -1866,43 +2066,6 @@ "requires": { "ms": "2.0.0" } - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - }, - "qs": { - "version": "6.7.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", - "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" - }, - "type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "requires": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - } } } }, @@ -1911,162 +2074,56 @@ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, - "external-editor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", - "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", - "dev": true, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + }, + "fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==" + }, + "fast-json-patch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", + "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", "requires": { - "chardet": "^0.7.0", - "iconv-lite": "^0.4.24", - "tmp": "^0.0.33" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-patch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-2.2.1.tgz", - "integrity": "sha512-4j5uBaTnsYAV5ebkidvxiLUYOwjQ+JSFljeqfTxCrH9bDmlCQaOJFS84oDJ2rAXZq2yskmk3ORfoP9DCwqFNig==", - "requires": { - "fast-deep-equal": "^2.0.1" + "fast-deep-equal": "^2.0.1" } }, "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, - "fetch-ponyfill": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-6.1.0.tgz", - "integrity": "sha512-bLc7JCjpJZZUXVxbgwUhd72Q19MAokrCXOg/Akq+wl0uFLFLklFdBkZo5OpQ3kLI0oGqrKdx6t5Zo3QwXBRmbQ==", - "requires": { - "node-fetch": "~2.6.0" - } + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==", + "dev": true }, - "figures": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-3.1.0.tgz", - "integrity": "sha512-ravh8VRXqHuMvZt/d8GblBeqDMkdJMBdv/2KntFH+ra5MXkO7nxNKpzQ3n6QD/2da1kH0aWmNISdvhM7gl2gVg==", - "dev": true, + "fetch-ponyfill": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-7.0.0.tgz", + "integrity": "sha512-ynRMX7w11EcG3sJSYTT2N6EUl0QsqWbyW3Xw5+DuLeA2uo/yhssb7JqnjamGQaHBUo/AOUsmTz6zeUX3cPvZsQ==", "requires": { - "escape-string-regexp": "^1.0.5" + "node-fetch": "~2.6.1" } }, "file-entry-cache": { @@ -2084,26 +2141,11 @@ "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "optional": true, + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "to-regex-range": "^5.0.1" } }, "finalhandler": { @@ -2127,39 +2169,24 @@ "requires": { "ms": "2.0.0" } - }, - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" } } }, "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "requires": { - "locate-path": "^3.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true }, "flat-cache": { "version": "2.0.1", @@ -2183,15 +2210,10 @@ } } }, - "flatpickr": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.2.tgz", - "integrity": "sha512-bRfBPyxohUQWgCTg6jPALUO1t/x+sRJ/S/RVti/NzYvHqGRpCAesKSWKLzOuLmpu+vGHfXBld4SXvOCLFgYb+g==" - }, "flatted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz", - "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", "dev": true }, "follow-redirects": { @@ -2212,11 +2234,10 @@ } } }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "optional": true + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=" }, "forever-agent": { "version": "0.6.1", @@ -2224,9 +2245,9 @@ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -2234,205 +2255,136 @@ } }, "formidable": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.1.tgz", - "integrity": "sha512-Fs9VRguL0gqGHkXS5GQiMCr1VhZBxz0JnJs4JmMp/2jL18Fmbzvv7vOFRU+U8TBkHEE/CX1qDXzJplVULgsLeg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.2.2.tgz", + "integrity": "sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==", "dev": true }, "formio-workers": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/formio-workers/-/formio-workers-1.14.0.tgz", - "integrity": "sha512-4dMnoI+VfsrBounXfLd4Y6lHTkd3yjCDz9yKCZWLhhbUujN26iQmwcfgpOyDqtS/OdkzxvBi7ZKsAARttRf5LA==", + "version": "1.14.8", + "resolved": "https://registry.npmjs.org/formio-workers/-/formio-workers-1.14.8.tgz", + "integrity": "sha512-qbyaW5rG4UnL1SlGhPScF/PT5c5R5aYG+mJaeZh7vf1D0WY15jWE/7Fn22ey0xCTPAgAlfNDqCSVm6cEcTDjcg==", "requires": { - "aws-serverless-express": "^3.3.6", + "aws-serverless-express": "^3.3.8", "body-parser": "^1.19.0", - "core-js": "^3.2.1", - "dotenv": "^8.1.0", + "core-js": "^3.6.5", + "dotenv": "^8.2.0", "express": "^4.17.1", - "formiojs": "^4.1.0", + "formiojs": "^4.9.26", "method-override": "^3.0.0", - "moment": "^2.24.0", - "nunjucks": "^3.2.0", + "moment": "^2.26.0", + "nunjucks": "^3.2.1", "nunjucks-date-filter": "^0.1.1", "threads": "^0.12.1" }, "dependencies": { + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, "formiojs": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.1.0.tgz", - "integrity": "sha512-YiEbkkdhrlzNZWy5Tx4+9SG7cPNI50qOB1I/iE+FSQMYR2tFMLhlWQIaiQUr3e2gY0J6/DoOdH/yJ3rQC9co7Q==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.12.1.tgz", + "integrity": "sha512-OZpPT+SxKP+C5TOEGL9Me+EEcJXgcTzbfZVrZLUtvXDLhYIhmx83UTPZCPuEYMs6gCrNbwuNkEUgcMZXsvVmJQ==", "requires": { + "@formio/bootstrap3": "^2.6.8", + "@formio/semantic": "^2.4.5", + "autocompleter": "^6.0.5", "browser-cookies": "^1.2.0", - "choices.js": "^7.0.0", - "core-js": "^3.2.1", - "dialog-polyfill": "^0.5.0", - "dompurify": "^2.0.0", + "choices.js": "^9.0.1", + "compare-versions": "^3.6.0", + "core-js": "3.5.0", + "custom-event-polyfill": "^1.0.7", + "dialog-polyfill": "^0.5.2", + "dompurify": "2.0.11", "downloadjs": "^1.4.7", - "dragula": "^3.7.2", - "eventemitter2": "^5.0.1", - "fast-deep-equal": "^2.0.1", + "dragula": "^3.7.3", + "eventemitter2": "^6.4.3", + "fast-deep-equal": "^3.1.3", "fast-json-patch": "^2.2.1", - "fetch-ponyfill": "^6.1.0", - "flatpickr": "^4.6.2", - "i18next": "^17.0.16", - "idb": "^4.0.4", - "json-logic-js": "^1.2.2", + "fetch-ponyfill": "^7.0.0", + "i18next": "^19.8.3", + "idb": "^5.0.7", + "ismobilejs": "^1.1.1", + "json-logic-js": "^2.0.0", "jstimezonedetect": "^1.0.7", - "lodash": "^4.17.15", - "moment": "^2.24.0", - "moment-timezone": "^0.5.26", + "jwt-decode": "^3.1.1", + "lodash": "^4.17.20", + "moment": "^2.29.1", + "moment-timezone": "^0.5.31", "native-promise-only": "^0.8.1", + "quill": "^2.0.0-dev.3", "resize-observer-polyfill": "^1.5.1", "signature_pad": "^2.3.2", "string-hash": "^1.1.3", "text-mask-addons": "^3.8.0", - "tooltip.js": "^1.3.2", - "two.js": "^0.7.0-beta.3", - "uuid": "^3.3.3", - "vanilla-picker": "^2.10.0", + "tooltip.js": "^1.3.3", + "uuid": "^8.3.1", + "vanilla-picker": "^2.10.1", "vanilla-text-mask": "^5.1.1" + }, + "dependencies": { + "core-js": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz", + "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw==" + } } - }, - "i18next": { - "version": "17.0.16", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-17.0.16.tgz", - "integrity": "sha512-PtPiycw8H/45AAy2nuS3Ehov1X9k5V/gTJ89Uh8VAA3dx8EbsWwyP3c25fd4PWlLUey3YbRLTNPbre/dPho8Og==", - "requires": { - "@babel/runtime": "^7.3.1" - } - }, - "jstimezonedetect": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/jstimezonedetect/-/jstimezonedetect-1.0.7.tgz", - "integrity": "sha512-ARADHortktl9IZ1tr4GHwGPIAzgz3mLNCbR/YjWtRtc/O0o634O3NeFlpLjv95EvuDA5dc8z6yfgbS8nUc4zcQ==" - }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" } } }, "formiojs": { - "version": "4.9.0-rc.2", - "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.9.0-rc.2.tgz", - "integrity": "sha512-kOtU1rGQdE5CS5pj+M2xGPDXHEgkRmN4iRcS71dzPnLzIqwFRIthd4sZsGOewxdpz1hwiQxE3krsX9HPJAo5Mw==", + "version": "4.12.2-rc.5", + "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.12.2-rc.5.tgz", + "integrity": "sha512-NWHkOgHNjbYfyF0ABCTOyYlsBear39NWVjYsvOW81SiQCsaHvbg8gvIX3wKODd3kLXUjonsRn8Vzispm6KElSw==", "requires": { - "@formio/bootstrap3": "^2.2.0", - "@formio/semantic": "^2.1.0", - "autocompleter": "^6.0.2", + "@formio/bootstrap3": "^2.6.8", + "@formio/semantic": "^2.4.5", + "autocompleter": "^6.0.5", "browser-cookies": "^1.2.0", "choices.js": "^9.0.1", - "core-js": "^3.6.4", + "compare-versions": "^3.6.0", + "core-js": "3.5.0", "custom-event-polyfill": "^1.0.7", - "dialog-polyfill": "^0.5.0", - "dompurify": "^2.0.8", + "dialog-polyfill": "^0.5.4", + "dompurify": "2.0.11", "downloadjs": "^1.4.7", - "dragula": "^3.7.2", - "eventemitter2": "^6.0.0", - "fast-deep-equal": "^3.1.1", + "dragula": "^3.7.3", + "eventemitter2": "^6.4.3", + "fast-deep-equal": "^3.1.3", "fast-json-patch": "^2.2.1", - "fetch-ponyfill": "^6.1.0", - "flatpickr": "^4.6.3", - "i18next": "^19.3.1", - "idb": "^5.0.1", - "ismobilejs": "^1.0.3", - "json-logic-js": "^1.2.2", + "fetch-ponyfill": "^7.0.0", + "i18next": "^19.8.4", + "idb": "^5.0.7", + "ismobilejs": "^1.1.1", + "json-logic-js": "^2.0.0", "jstimezonedetect": "^1.0.7", - "jwt-decode": "^2.2.0", - "lodash": "^4.17.15", - "moment": "^2.24.0", - "moment-timezone": "^0.5.28", + "jwt-decode": "^3.1.2", + "lodash": "^4.17.20", + "moment": "^2.29.1", + "moment-timezone": "^0.5.32", "native-promise-only": "^0.8.1", + "quill": "^2.0.0-dev.3", "resize-observer-polyfill": "^1.5.1", "signature_pad": "^2.3.2", "string-hash": "^1.1.3", "text-mask-addons": "^3.8.0", "tooltip.js": "^1.3.3", - "uuid": "^3.4.0", - "vanilla-picker": "^2.10.1", + "uuid": "^8.3.1", + "vanilla-picker": "^2.11.0", "vanilla-text-mask": "^5.1.1" }, "dependencies": { - "choices.js": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/choices.js/-/choices.js-9.0.1.tgz", - "integrity": "sha512-JgpeDY0Tmg7tqY6jaW/druSklJSt7W68tXFJIw0GSGWmO37SDAL8o60eICNGbzIODjj02VNNtf5h6TgoHDtCsA==", - "requires": { - "deepmerge": "^4.2.0", - "fuse.js": "^3.4.5", - "redux": "^4.0.4" - } - }, "core-js": { - "version": "3.6.4", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.4.tgz", - "integrity": "sha512-4paDGScNgZP2IXXilaffL9X7968RuvwlkK3xWtZRVqgd8SYNiVKRJvkFd1aqqEuPfN7E68ZHEp9hDj6lHj4Hyw==" - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" - }, - "dompurify": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.0.8.tgz", - "integrity": "sha512-vIOSyOXkMx81ghEalh4MLBtDHMx1bhKlaqHDMqM2yeitJ996SLOk5mGdDpI9ifJAgokred8Rmu219fX4OltqXw==" - }, - "eventemitter2": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.0.0.tgz", - "integrity": "sha512-ZuNWHD7S7IoikyEmx35vPU8H1W0L+oi644+4mSTg7nwXvBQpIwQL7DPjYUF0VMB0jPkNMo3MqD07E7MYrkFmjQ==" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz", + "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw==" }, "fast-deep-equal": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz", - "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==" - }, - "flatpickr": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.3.tgz", - "integrity": "sha512-007VucCkqNOMMb9ggRLNuJowwaJcyOh4sKAFcdGfahfGc7JQbf94zSzjdBq/wVyHWUEs5o3+idhFZ0wbZMRmVQ==" - }, - "fuse.js": { - "version": "3.4.6", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.6.tgz", - "integrity": "sha512-H6aJY4UpLFwxj1+5nAvufom5b2BT2v45P1MkPvdGIK8fWjQx/7o6tTT1+ALV0yawQvbmvCF0ufl2et8eJ7v7Cg==" - }, - "idb": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/idb/-/idb-5.0.1.tgz", - "integrity": "sha512-jdFnxTvz3JZ5AQP4nAb0x7bnisFNO5KqafsarDn/aehIn84UcVY3z96mq6WiqNe8z7OpZWZ0xG1ORbkwuBp5Mw==" - }, - "redux": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", - "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", - "requires": { - "loose-envify": "^1.4.0", - "symbol-observable": "^1.2.0" - } - }, - "tooltip.js": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tooltip.js/-/tooltip.js-1.3.3.tgz", - "integrity": "sha512-XWWuy/dBdF/F/YpRE955yqBZ4VdLfiTAUdOqoU+wJm6phJlMpEzl/iYHZ+qJswbeT9VG822bNfsETF9wzmoy5A==", - "requires": { - "popper.js": "^1.0.2" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "vanilla-picker": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.10.1.tgz", - "integrity": "sha512-Bo4HOKkSorcQoRB08HwDMb8X2jt3SsZw7gzFlbzXbhnaxdUVJBm3LOUudr7M1SCVwPCo8d3nq8ajiAg8lAoqPg==", - "requires": { - "@sphinxxxx/color-conversion": "^2.2.2" - } + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" } } }, @@ -2441,35 +2393,20 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "optional": true, - "requires": { - "map-cache": "^0.2.2" - } - }, "fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" }, "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", + "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", "requires": { + "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.0.tgz", - "integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==" - } + "jsonfile": "^6.0.1", + "universalify": "^1.0.0" } }, "fs.realpath": { @@ -2478,485 +2415,10 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "fsevents": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", - "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", - "optional": true, - "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.6.0", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "bundled": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "optional": true - }, - "minipass": { - "version": "2.3.5", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.2.1", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "optional": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.2.4", - "bundled": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.3", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.5", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.2.0", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "bundled": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.6.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.8", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.3.4", - "minizlib": "^1.1.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "yallist": { - "version": "3.0.3", - "bundled": true, - "optional": true - } - } + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "optional": true }, "ftp": { "version": "0.3.10", @@ -2993,8 +2455,7 @@ "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, "functional-red-black-tree": { "version": "1.0.1", @@ -3003,9 +2464,9 @@ "dev": true }, "fuse.js": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.4.2.tgz", - "integrity": "sha512-WVbrm+cAxPtyMqdtL7cYhR7aZJPhtOfjNClPya8GKMVukKDYs7pEnPINeRVX1C9WmWgU8MdYGYbUPAP2AJXdoQ==" + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-3.6.1.tgz", + "integrity": "sha512-hT9yh/tiinkmirKrlv4KWOjztdoZo1mx9Qh4KvWqC7isoXwdUY3PNWUxceF4/qO9R6riA2C29jdTOeQOIROjgw==" }, "get-caller-file": { "version": "2.0.5", @@ -3013,11 +2474,24 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=", - "dev": true + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } }, "get-uri": { "version": "2.0.4", @@ -3042,12 +2516,6 @@ } } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "optional": true - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -3057,9 +2525,9 @@ } }, "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3070,67 +2538,50 @@ } }, "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "optional": true, + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } + "is-glob": "^4.0.1" } }, "global-dirs": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", - "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", + "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", "dev": true, "requires": { - "ini": "^1.3.4" + "ini": "^1.3.5" } }, "globals": { - "version": "12.3.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", - "integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" }, "got": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", - "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, "requires": { - "create-error-class": "^3.0.0", + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", "duplexer3": "^0.1.4", - "get-stream": "^3.0.0", - "is-redirect": "^1.0.0", - "is-retry-allowed": "^1.0.0", - "is-stream": "^1.0.0", - "lowercase-keys": "^1.0.0", - "safe-buffer": "^5.0.1", - "timed-out": "^4.0.0", - "unzip-response": "^2.0.1", - "url-parse-lax": "^1.0.0" + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" } }, "graceful-fs": { - "version": "4.1.15", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", - "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==" + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" }, "growl": { "version": "1.10.5", @@ -3144,11 +2595,11 @@ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", "requires": { - "ajv": "^6.5.5", + "ajv": "^6.12.3", "har-schema": "^2.0.0" } }, @@ -3156,11 +2607,18 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, "requires": { "function-bind": "^1.1.1" } }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -3170,40 +2628,13 @@ "has-symbols": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "optional": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "optional": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true }, "he": { "version": "1.2.0", @@ -3212,32 +2643,56 @@ "dev": true }, "hoek": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.2.tgz", - "integrity": "sha512-6qhh/wahGYZHFSFw12tBbJw5fsAhhwrrG/y3Cs0YMTv2WzMnL0oLPnQJjv1QJvEfylRSOFuP+xCu+tdx0tD16Q==" + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-6.1.3.tgz", + "integrity": "sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==" }, "html-entities": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz", - "integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8=" + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz", + "integrity": "sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA==" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true }, "http-errors": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", - "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", "requires": { "depd": "~1.1.2", - "inherits": "2.0.4", + "inherits": "2.0.3", "setprototypeof": "1.1.1", "statuses": ">= 1.5.0 < 2", "toidentifier": "1.0.0" - }, - "dependencies": { - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - } } }, "http-proxy-agent": { @@ -3249,6 +2704,14 @@ "debug": "3.1.0" }, "dependencies": { + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -3270,27 +2733,12 @@ } }, "https-proxy-agent": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", - "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } + "agent-base": "6", + "debug": "4" } }, "i": { @@ -3299,25 +2747,102 @@ "integrity": "sha1-2WyScyB28HJxG2sQ/X1PZa2O4j0=" }, "i18next": { - "version": "19.3.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.3.1.tgz", - "integrity": "sha512-fSd67ADNfVtffECirge69OBChESzdJgXMVa8cmnR96mHn+rB2lTmNE5bqwPz32aufAKOV1LHwOTAepG+P9QbjA==", + "version": "19.8.4", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.8.4.tgz", + "integrity": "sha512-FfVPNWv+felJObeZ6DSXZkj9QM1Ivvh7NcFCgA8XPtJWHz0iXVa9BUy+QY8EPrCLE+vWgDfV/sc96BgXVo6HAA==", + "requires": { + "@babel/runtime": "^7.12.0" + } + }, + "ice-cap": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/ice-cap/-/ice-cap-0.0.4.tgz", + "integrity": "sha1-im0xq0ysjUtW3k+pRt8zUlYbbhg=", "requires": { - "@babel/runtime": "^7.3.1" + "cheerio": "0.20.0", + "color-logger": "0.0.3" + }, + "dependencies": { + "cheerio": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.20.0.tgz", + "integrity": "sha1-XHEPK6uVZTJyhCugHG6mGzVF7DU=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "~3.8.1", + "jsdom": "^7.0.2", + "lodash": "^4.1.0" + } + }, + "color-logger": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/color-logger/-/color-logger-0.0.3.tgz", + "integrity": "sha1-2bIt0dlz4Waxi/MT+fSBu6TfIBg=" + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "requires": { + "domelementtype": "1" + } + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=" + } + } + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } } }, "iconv-lite": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.1.tgz", - "integrity": "sha512-ONHr16SQvKZNSqjQT9gy5z24Jw+uqfO02/ngBSBoqChZ+W8qXX7GPRa1RoUnzGADw8K63R1BXUMzarCVQBpY8Q==", + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "requires": { "safer-buffer": ">= 2.1.2 < 3" } }, "idb": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/idb/-/idb-4.0.4.tgz", - "integrity": "sha512-ZYsaBSNub2yAnjvmRKudQlMIPqZQIefAOwNIPeXC+RLIeXYFc0UNQqONKNuQeBNf8oBOV5L75yJ9zFISjHVj4g==" + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/idb/-/idb-5.0.7.tgz", + "integrity": "sha512-tXkkEtzOEolCKNLpxEvE5ctPqUhgTEi+wPWVWIWavl/Z0/NjSJx0o/79z4/etJWpEpVjhbQNZ7fvmp/UFv/Yog==" }, "ignore": { "version": "4.0.6", @@ -3332,9 +2857,9 @@ "dev": true }, "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz", + "integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==", "dev": true, "requires": { "parent-module": "^1.0.0", @@ -3386,339 +2911,231 @@ "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true }, - "inquirer": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.1.tgz", - "integrity": "sha512-V1FFQ3TIO15det8PijPLFR9M9baSlnRs9nL7zWu1MNVA2T9YVl9ZbrHJhYs7e9X8jeMZ3lr2JH/rdHFgNCBdYw==", - "dev": true, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", "requires": { - "ansi-escapes": "^4.2.1", - "chalk": "^2.4.2", - "cli-cursor": "^3.1.0", - "cli-width": "^2.0.0", - "external-editor": "^3.0.3", - "figures": "^3.0.0", - "lodash": "^4.17.15", - "mute-stream": "0.0.8", - "run-async": "^2.2.0", - "rxjs": "^6.5.3", - "string-width": "^4.1.0", - "strip-ansi": "^5.1.0", - "through": "^2.3.6" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "mute-stream": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", - "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - } - } - } + "loose-envify": "^1.0.0" } }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "ipaddr.js": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", - "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", + "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==" }, "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "optional": true, + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "requires": { - "binary-extensions": "^1.0.0" + "binary-extensions": "^2.0.0" } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "optional": true + "is-boolean-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", + "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==" }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" }, "is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, "requires": { - "ci-info": "^1.5.0" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "ci-info": "^2.0.0" } }, "is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "optional": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "optional": true + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true }, "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", "requires": { "is-extglob": "^2.1.1" } }, "is-installed-globally": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.1.0.tgz", - "integrity": "sha1-Df2Y9akRFxbdU13aZJL2e/PSWoA=", + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", + "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", "dev": true, "requires": { - "global-dirs": "^0.1.0", - "is-path-inside": "^1.0.0" + "global-dirs": "^2.0.1", + "is-path-inside": "^3.0.1" } }, + "is-map": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.1.tgz", + "integrity": "sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==" + }, + "is-negative-zero": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz", + "integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=" + }, "is-npm": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", - "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", "dev": true }, "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", + "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==" }, "is-obj": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", - "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true }, "is-path-inside": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.1.tgz", - "integrity": "sha1-jvW33lBDej/cprToZe96pVy0gDY=", - "dev": true, - "requires": { - "path-is-inside": "^1.0.1" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "optional": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-promise": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", - "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", + "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", "dev": true }, - "is-redirect": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", - "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, - "is-retry-allowed": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", - "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==", - "dev": true - }, + "is-set": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz", + "integrity": "sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==" + }, "is-symbol": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, "requires": { "has-symbols": "^1.0.1" } }, + "is-typed-array": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", + "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "requires": { + "available-typed-arrays": "^1.0.0", + "es-abstract": "^1.17.4", + "foreach": "^2.0.5", + "has-symbols": "^1.0.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "optional": true + "is-weakmap": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", + "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==" + }, + "is-weakset": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.1.tgz", + "integrity": "sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==" + }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true }, "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==" }, "isemail": { "version": "3.2.0", @@ -3735,15 +3152,9 @@ "dev": true }, "ismobilejs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.0.3.tgz", - "integrity": "sha512-6rTcdWK7PHIWPYlCPdTbU9eE9yzdnIQSpiH+8Ln5OqugpEszQK5KHlsjZrDae26fEhki9rPvQmsjI1q4CLuKIA==" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "optional": true + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==" }, "isstream": { "version": "0.1.2", @@ -3766,9 +3177,9 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", "dev": true, "requires": { "argparse": "^1.0.7", @@ -3784,19 +3195,61 @@ } }, "jsbi": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.1.tgz", - "integrity": "sha512-+HQESPaV0mRiH614z4JPVPAftcRC2p53x92lySPzUzFwJbJTMpzHz8OYUkcXPN3fOcHUe0NdVcHnCtX/1+eCrA==" + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", + "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==" }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, + "jsdom": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-7.2.2.tgz", + "integrity": "sha1-QLQCdwwr2iNGkJa+6Rq2deOx/G4=", + "optional": true, + "requires": { + "abab": "^1.0.0", + "acorn": "^2.4.0", + "acorn-globals": "^1.0.4", + "cssom": ">= 0.3.0 < 0.4.0", + "cssstyle": ">= 0.2.29 < 0.3.0", + "escodegen": "^1.6.1", + "nwmatcher": ">= 1.3.7 < 2.0.0", + "parse5": "^1.5.1", + "request": "^2.55.0", + "sax": "^1.1.4", + "symbol-tree": ">= 3.1.0 < 4.0.0", + "tough-cookie": "^2.2.0", + "webidl-conversions": "^2.0.0", + "whatwg-url-compat": "~0.6.5", + "xml-name-validator": ">= 2.0.1 < 3.0.0" + }, + "dependencies": { + "parse5": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-1.5.1.tgz", + "integrity": "sha1-m387DeMr543CQBsXVzzK8Pb1nZQ=", + "optional": true + } + } + }, + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, "json-logic-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-1.2.2.tgz", - "integrity": "sha1-5cOCqm3yXfSF7erTOYaTlsUzr+g=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.0.tgz", + "integrity": "sha512-cQBDOXgFtFladCg99wnQ7YfN+nv1+Sznj4K6bp3CTgDJNJKgEXJE2VCXzVBjEU2e1UagDHSek52IQk5Ha38n7Q==" }, "json-schema": { "version": "0.2.3", @@ -3820,19 +3273,27 @@ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", "requires": { - "minimist": "^1.2.0" + "minimist": "^1.2.5" } }, "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", "requires": { - "graceful-fs": "^4.1.6" + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } } }, "jsonparse": { @@ -3858,14 +3319,14 @@ }, "dependencies": { "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, @@ -3905,36 +3366,31 @@ } }, "jwt-decode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", - "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, "kareem": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "optional": true - }, - "latest-version": { + "keyv": { "version": "3.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", - "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, "requires": { - "package-json": "^4.0.0" + "json-buffer": "3.0.0" } }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, "requires": { - "invert-kv": "^1.0.0" + "package-json": "^6.3.0" } }, "levn": { @@ -3947,24 +3403,18 @@ } }, "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" + "p-locate": "^5.0.0" } }, "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" - }, - "lodash-es": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.15.tgz", - "integrity": "sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==" + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, "lodash._basecallback": { "version": "3.3.1", @@ -4116,12 +3566,63 @@ } }, "log-symbols": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", - "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, "requires": { - "chalk": "^2.0.1" + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "loose-envify": { @@ -4139,12 +3640,10 @@ "dev": true }, "lru-cache": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.4.tgz", - "integrity": "sha512-EPstzZ23znHUVLKj+lcXO1KvZkrlw+ZirdwvOmnAnA/1PB4ggyXJ77LRkCqkff+ShQ+cqoxCxLQOh4cKITO5iA==", - "dev": true, + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "requires": { - "pseudomap": "^1.0.2", "yallist": "^3.0.2" } }, @@ -4175,12 +3674,20 @@ } }, "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, "requires": { - "pify": "^3.0.0" + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } } }, "mandrill-api": { @@ -4188,20 +3695,10 @@ "resolved": "https://registry.npmjs.org/mandrill-api/-/mandrill-api-1.0.45.tgz", "integrity": "sha1-Fjk5z0hr0YJ3sPO69BLD5l2Epy0=" }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "optional": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "optional": true, - "requires": { - "object-visit": "^1.0.0" - } + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" }, "media-typer": { "version": "0.3.0", @@ -4250,49 +3747,28 @@ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, "mime": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", - "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" }, "mime-db": { - "version": "1.37.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" }, "mime-types": { - "version": "2.1.21", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", - "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", "requires": { - "mime-db": "~1.37.0" + "mime-db": "1.44.0" } }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true }, "minimatch": { @@ -4304,333 +3780,114 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "optional": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, "mixme": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.3.3.tgz", - "integrity": "sha512-TuRDkNZGvkeDPEbhdJSnfVxREuCPFfvmRfdlWVSw29v0XfP+YWnXmBaPD7yugPIDamONvp69DA/vOLOKsToHXA==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/mixme/-/mixme-0.4.0.tgz", + "integrity": "sha512-B4Sm1CDC5+ov5AYxSkyeT5HLtiDgNOLKwFlq34wr8E2O3zRdTvQiLzo599Jt9cir6VJrSenOlgvdooVYCQJIYw==" }, "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - } + "minimist": "^1.2.5" } }, "mocha": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.0.1.tgz", - "integrity": "sha512-9eWmWTdHLXh72rGrdZjNbG3aa1/3NRPpul1z0D979QpEnFdCG0Q5tv834N+94QEN2cysfV72YocQ3fn87s70fg==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.2.1.tgz", + "integrity": "sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w==", "dev": true, "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "2.2.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.0", - "yargs-parser": "13.1.1", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", - "dev": true, - "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.4.3", + "debug": "4.2.0", + "diff": "4.0.2", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.1.6", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.14.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.2", + "nanoid": "3.1.12", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "7.2.0", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.0.2", + "yargs": "13.3.2", + "yargs-parser": "13.1.2", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz", + "integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==", "dev": true, "requires": { - "is-number": "^7.0.0" + "ms": "2.1.2" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" - } + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true }, - "y18n": { + "has-flag": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" + "has-flag": "^4.0.0" } } } }, "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "moment-timezone": { - "version": "0.5.28", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.28.tgz", - "integrity": "sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==", + "version": "0.5.32", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", + "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", "requires": { "moment": ">= 2.9.0" } }, "mongodb": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.3.tgz", - "integrity": "sha512-II7P7A3XUdPiXRgcN96qIoRa1oesM6qLNZkzfPluNZjVkgQk3jnQwOT6/uDk4USRDTTLjNFw2vwfmbRGTA7msg==", + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.3.tgz", + "integrity": "sha512-rOZuR0QkodZiM+UbQE5kDsJykBqWi0CL4Ec2i1nrGrUI3KO11r6Fbxskqmq3JK2NH7aW4dcccBuUujAP0ERl5w==", "requires": { - "bl": "^2.2.0", - "bson": "^1.1.1", + "bl": "^2.2.1", + "bson": "^1.1.4", "denque": "^1.4.1", "require_optional": "^1.0.1", "safe-buffer": "^5.1.2", @@ -4638,19 +3895,19 @@ } }, "mongoose": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.2.tgz", - "integrity": "sha512-Sa1qfqBvUfAgsrXpZjbBoIx8PEDUJSKF5Ous8gnBFI7TPiueSgJjg6GRA7A0teU8AB/vd0h8rl1rD5RQNfWhIw==", + "version": "5.10.16", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.10.16.tgz", + "integrity": "sha512-rgfK1lvAQdCZ0buPju7Wny3suls5A1GjYRsv+jrQBVA0N/OhtGKHjr5RXJs0rxQhodwNVfc7O8g4bwDqW4R0sQ==", "requires": { - "bson": "~1.1.1", + "bson": "^1.1.4", "kareem": "2.3.1", - "mongodb": "3.5.3", + "mongodb": "3.6.3", "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.6.0", + "mpath": "0.7.0", "mquery": "3.2.2", "ms": "2.1.2", "regexp-clone": "1.0.0", - "safe-buffer": "5.1.2", + "safe-buffer": "5.2.1", "sift": "7.0.1", "sliced": "1.0.1" }, @@ -4659,6 +3916,11 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" } } }, @@ -4668,9 +3930,9 @@ "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" }, "mpath": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.6.0.tgz", - "integrity": "sha512-i75qh79MJ5Xo/sbhxrDrPSEG0H/mr1kcZXJ8dH6URU5jD/knFxCVqVC/gVSW7GIXL/9hHWlT9haLbCXWOll3qw==" + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", + "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" }, "mquery": { "version": "3.2.2", @@ -4700,9 +3962,9 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, "mssql": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/mssql/-/mssql-6.1.0.tgz", - "integrity": "sha512-tlzqjnWkV2Ra5TuPh/Snmp1CF/+yDJOPjQ2yoVzdvl0vQePPFITSLQCBLRydNbg8sR6HXQFEIQL9mPaCA3oO0Q==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/mssql/-/mssql-6.2.3.tgz", + "integrity": "sha512-4TW/fA9UgzmVTNgjl65r6ISr6aL5QHnlptEt1A3jIpdzkNbFPIkRbUNz90324HIdE+5pKc3VqikOImcTrhd4og==", "requires": { "debug": "^4", "tarn": "^1.1.5", @@ -4710,9 +3972,9 @@ } }, "mute-stream": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", - "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=" + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" }, "mysql": { "version": "2.18.1", @@ -4723,48 +3985,13 @@ "readable-stream": "2.3.7", "safe-buffer": "5.1.2", "sqlstring": "2.3.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - } } }, - "nan": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", - "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } + "nanoid": { + "version": "3.1.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.12.tgz", + "integrity": "sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==", + "dev": true }, "native-duplexpair": { "version": "1.0.0", @@ -4797,34 +4024,10 @@ "resolved": "https://registry.npmjs.org/netmask/-/netmask-1.0.6.tgz", "integrity": "sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU=" }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-paginate-anything": { "version": "1.0.0", @@ -4832,14 +4035,14 @@ "integrity": "sha1-WR/jCw1r8J/7yrl9ETt2z6mdHZs=" }, "nodemailer": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.3.tgz", - "integrity": "sha512-zmx4MTzYWQo2abFexvFYVURaiBGLekoA/yjP2Ctigd82hYtC4n38pcphQBi805t7AB2sY6DwmxnP/tb77f3KHA==" + "version": "6.4.16", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.4.16.tgz", + "integrity": "sha512-68K0LgZ6hmZ7PVmwL78gzNdjpj5viqBdFqKrTtr9bZbJYj6BRj5W6WGkxXrEnUl3Co3CBXi3CZBUlpV/foGnOQ==" }, "nodemailer-mailgun-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nodemailer-mailgun-transport/-/nodemailer-mailgun-transport-2.0.0.tgz", - "integrity": "sha512-TPGi2anyS0w/4jv7TJNS3wX5DbNQ2+j+ssnwY4IYxL4QaYXAXewcw6YUtBgnOsEvQVJvmxQLDuW4f4JaMPgfaA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/nodemailer-mailgun-transport/-/nodemailer-mailgun-transport-2.0.2.tgz", + "integrity": "sha512-4j0G4yUrYrJ2o/1Ow3d5HeW/ed9anWQ1+He7HuL2+0XcSyLLjNkft30zodFTyTWlFaiLiaIJ9Ssnjmz+qrtIZg==", "requires": { "consolidate": "^0.15.1", "mailgun-js": "^0.22.0" @@ -4864,9 +4067,9 @@ } }, "nodemon": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz", - "integrity": "sha512-GWhYPMfde2+M0FsHnggIHXTqPDHXia32HRhh6H0d75Mt9FKUoCBvumNHr7LdrpPBTKxsWmIEOjoN+P4IU6Hcaw==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.6.tgz", + "integrity": "sha512-4I3YDSKXg6ltYpcnZeHompqac4E6JeAMpGm8tJnB9Y3T0ehasLa4139dJOcCrB93HHrUMsCrKtoAlXTqT5n4AQ==", "dev": true, "requires": { "chokidar": "^3.2.2", @@ -4877,143 +4080,38 @@ "semver": "^5.7.1", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^2.5.0" + "undefsafe": "^2.0.3", + "update-notifier": "^4.1.0" }, "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz", - "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.3.0" - } - }, "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, "requires": { "ms": "^2.1.1" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz", - "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==", - "dev": true, - "optional": true - }, - "glob-parent": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz", - "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "readdirp": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz", - "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.7" - } - }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", "dev": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "requires": { - "is-number": "^7.0.0" + "has-flag": "^3.0.0" } } } @@ -5028,37 +4126,33 @@ } }, "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", "requires": { - "path-key": "^2.0.0" + "boolbase": "~1.0.0" } }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nunjucks": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.0.tgz", - "integrity": "sha512-YS/qEQ6N7qCnUdm6EoYRBfJUdWNT0PpKbbRnogV2XyXbBm2STIP1O6yrdZHgwMVK7fIYUx7i8+yatEixnXSB1w==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.2.tgz", + "integrity": "sha512-KUi85OoF2NMygwODAy28Lh9qHmq5hO3rBlbkYoC8v377h4l8Pt5qFjILl0LWpMbOrZ18CzfVVUvIHUIrtED3sA==", "requires": { "a-sync-waterfall": "^1.0.0", "asap": "^2.0.3", - "chokidar": "^2.0.0", - "yargs": "^3.32.0" + "chokidar": "^3.3.0", + "commander": "^5.1.0" } }, "nunjucks-date-filter": { @@ -5069,6 +4163,12 @@ "moment": "^2.9.0" } }, + "nwmatcher": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/nwmatcher/-/nwmatcher-1.4.4.tgz", + "integrity": "sha512-3iuY4N5dhgMpCUrOVnuAdGrgxVqV2cJpM+XNccjR2DKOB1RUP0aA+wGXEiNziG/UKboFyGBIoKOaNlJxx8bciQ==", + "optional": true + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", @@ -5079,87 +4179,34 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "optional": true, + "object-inspect": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==" + }, + "object-is": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.3.tgz", + "integrity": "sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg==", "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "define-properties": "^1.1.3", + "es-abstract": "^1.18.0-next.1" } }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, "object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "optional": true, - "requires": { - "isobject": "^3.0.0" - } + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" }, "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", "requires": { + "call-bind": "^1.0.0", "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "optional": true, - "requires": { - "isobject": "^3.0.1" + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" } }, "on-finished": { @@ -5178,15 +4225,6 @@ "wrappy": "1" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, "optionator": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", @@ -5200,48 +4238,34 @@ "word-wrap": "~1.2.3" } }, - "os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=", - "requires": { - "lcid": "^1.0.0" - } - }, "os-shim": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/os-shim/-/os-shim-0.1.3.tgz", "integrity": "sha1-a2LDeRz3kJ6jXtRuF2WLtBfLORc=", "dev": true }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true }, "p-limit": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz", - "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "requires": { - "p-try": "^2.0.0" + "yocto-queue": "^0.1.0" } }, "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "requires": { - "p-limit": "^2.0.0" + "p-limit": "^3.0.2" } }, "p-try": { @@ -5263,6 +4287,40 @@ "pac-resolver": "^3.0.0", "raw-body": "^2.2.0", "socks-proxy-agent": "^4.0.1" + }, + "dependencies": { + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", + "requires": { + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, "pac-resolver": { @@ -5278,25 +4336,30 @@ } }, "package-json": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", - "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, "requires": { - "got": "^6.7.1", - "registry-auth-token": "^3.0.1", - "registry-url": "^3.0.3", - "semver": "^5.1.0" + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } }, + "parchment": { + "version": "2.0.0-dev.2", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-2.0.0-dev.2.tgz", + "integrity": "sha512-4fgRny4pPISoML08Zp7poi52Dff3E2G1ORTi2D/acJ/RiROdDAMDB6VcQNfBcmehrX5Wixp6dxh6JjLyE5yUNQ==" + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5306,27 +4369,23 @@ "callsites": "^3.0.0" } }, - "parseurl": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", - "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "optional": true + "parse5": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz", + "integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==", + "requires": { + "@types/node": "*" + } }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "optional": true + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true }, "path-is-absolute": { @@ -5334,16 +4393,10 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, "path-proxy": { @@ -5372,16 +4425,9 @@ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "picomatch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz", - "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==", - "dev": true - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==" }, "pkginfo": { "version": "0.4.1", @@ -5389,15 +4435,9 @@ "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" }, "popper.js": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.15.0.tgz", - "integrity": "sha512-w010cY1oCUmI+9KwwlWki+r5jxKfTFDVoadl7MSrIujHU5MJ5OR6HTDj6Xo8aoR/QsA56x8jKjA59qGH4ELtrA==" - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "optional": true + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" }, "pre-commit": { "version": "1.2.2", @@ -5421,6 +4461,31 @@ "which": "^1.2.9" } }, + "lru-cache": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", + "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", + "dev": true, + "requires": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, "which": { "version": "1.2.14", "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", @@ -5429,6 +4494,12 @@ "requires": { "isexe": "^2.0.0" } + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true } } }, @@ -5438,15 +4509,15 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, "progress": { "version": "2.0.3", @@ -5475,12 +4546,12 @@ } }, "proxy-addr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", - "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", "requires": { "forwarded": "~0.1.2", - "ipaddr.js": "1.9.0" + "ipaddr.js": "1.9.1" } }, "proxy-agent": { @@ -5498,20 +4569,44 @@ "socks-proxy-agent": "^4.0.1" }, "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "agent-base": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", + "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", + "requires": { + "es6-promisify": "^5.0.0" + } + }, + "https-proxy-agent": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-3.0.1.tgz", + "integrity": "sha512-+ML2Rbh6DAuee7d07tYGEKOEi2voWPUGan+ExdPbPW6Z3svq+JCqr0v8WmKPOkz1vOVykPCBSuobe7G8GJUtVg==", "requires": { - "yallist": "^3.0.2" + "agent-base": "^4.3.0", + "debug": "^3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" } } }, "proxy-from-env": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", - "integrity": "sha1-M8UDmPcOp+uW0h97gXYwpVeRx+4=" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "pseudomap": { "version": "1.0.2", @@ -5520,30 +4615,96 @@ "dev": true }, "psl": { - "version": "1.1.29", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", - "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "pstree.remy": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.7.tgz", - "integrity": "sha512-xsMgrUwRpuGskEzBFkH8NmTimbZ5PcPup0LA8JJkHIm2IMUbQcpo3yeLNWVrufEYjh8YwtSVh0xz6UeWc5Oh5A==", + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" }, "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "quill": { + "version": "2.0.0-dev.4", + "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.0-dev.4.tgz", + "integrity": "sha512-9WmMVCEIhf3lDdhzl+i+GBDeDl0BFi65waC4Im8Y4HudEJ9kEEb1lciAz9A8pcDmLMjiMbvz84lNt/U5OBS8Vg==", + "requires": { + "clone": "^2.1.2", + "deep-equal": "^2.0.2", + "eventemitter3": "^4.0.0", + "extend": "^3.0.2", + "parchment": "2.0.0-dev.2", + "quill-delta": "4.2.1" + } + }, + "quill-delta": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-4.2.1.tgz", + "integrity": "sha512-Y2nksOj6Q+4hizre8n0dml76vLNGK4/y86EoI1d7rv6EL1bx7DPDYRmqQMPu1UqFQO/uQuVHQ3fOmm4ZSzWrfA==", + "requires": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.2.0" + }, + "dependencies": { + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + } + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } }, "range-parser": { "version": "1.2.1", @@ -5551,24 +4712,14 @@ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" }, "raw-body": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.1.tgz", - "integrity": "sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", "requires": { "bytes": "3.1.0", - "http-errors": "1.7.3", + "http-errors": "1.7.2", "iconv-lite": "0.4.24", "unpipe": "1.0.0" - }, - "dependencies": { - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - } } }, "rc": { @@ -5600,9 +4751,9 @@ } }, "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "requires": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -5611,92 +4762,102 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" + }, + "dependencies": { + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + } } }, "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "optional": true, + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "picomatch": "^2.2.1" } }, "redux": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/redux/-/redux-3.7.2.tgz", - "integrity": "sha512-pNqnf9q1hI5HHZRBkj3bAngGZW/JMCmexDlOxw4XagXY2o1327nHH54LoTjiPJ0gizoqPDRqWyX/00g0hD6w+A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz", + "integrity": "sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==", "requires": { - "lodash": "^4.2.1", - "lodash-es": "^4.2.1", - "loose-envify": "^1.1.0", - "symbol-observable": "^1.0.3" + "loose-envify": "^1.4.0", + "symbol-observable": "^1.2.0" } }, "regenerator-runtime": { - "version": "0.13.3", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz", - "integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==" - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "optional": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "regexp-clone": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "regexpp": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", - "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", "dev": true }, "registry-auth-token": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-3.4.0.tgz", - "integrity": "sha512-4LM6Fw8eBQdwMYcES4yTnn2TqIasbXuwDx3um+QRs7S55aMKCBKBxvPXl2RiUjHwuJLTyYfxSpmfSAjQpcuP+A==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", "dev": true, "requires": { - "rc": "^1.1.6", - "safe-buffer": "^5.0.1" + "rc": "^1.2.8" } }, "registry-url": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", - "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", "dev": true, "requires": { - "rc": "^1.0.1" + "rc": "^1.2.8" } }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "optional": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "optional": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "optional": true + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } }, "request": { "version": "2.88.2", @@ -5725,35 +4886,28 @@ "uuid": "^3.3.2" }, "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" } } }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "requires": { - "lodash": "^4.17.15" - } - }, - "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "requires": { - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -5792,24 +4946,33 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "optional": true - }, "resourcejs": { - "version": "1.39.0", - "resolved": "https://registry.npmjs.org/resourcejs/-/resourcejs-1.39.0.tgz", - "integrity": "sha512-aj3YZYr9BAmivj+7o79FrOYlzV9bdIOs6lfyawsLTG7hn5Wnf2xeDl6wgmFJIUP3CihHsrmBTKlV9ILGIPcWjQ==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/resourcejs/-/resourcejs-2.3.2.tgz", + "integrity": "sha512-H7XNH8lydXDpevXMdzm/YnBRsEKNrdm/oDENI0ca4H6F37lk1APRyjfVaFq1CcGxS7Xdv6+i68TvK1xpDaybAg==", "requires": { - "composable-middleware": "^0.3.0", "debug": "^4.1.1", - "fast-json-patch": "^2.2.1", - "lodash": "^4.17.15", - "moment": "^2.24.0", - "mongodb": "^3.5.3", - "node-paginate-anything": "^1.0.0" + "fast-json-patch": "^3.0.0-1", + "moment": "^2.28.0", + "mongodb": "^3.6.2", + "node-paginate-anything": "^1.0.0", + "range-parser": "^1.2.1" + }, + "dependencies": { + "fast-json-patch": { + "version": "3.0.0-1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.0.0-1.tgz", + "integrity": "sha512-6pdFb07cknxvPzCeLsFHStEy+MysPJPgZQ9LbQ/2O67unQF93SNqfdSqnPPl71YMHX+AD8gbl7iuoGFzHEdDuw==" + } + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" } }, "restler": { @@ -5832,54 +4995,34 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/qs/-/qs-1.2.0.tgz", "integrity": "sha1-7Qeb4oaCFH5v2aNMwrDB4OxkU+4=" + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" + }, + "xml2js": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.0.tgz", + "integrity": "sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o=", + "requires": { + "sax": "0.5.x", + "xmlbuilder": ">=0.4.2" + } } } }, - "restore-cursor": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", - "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", - "dev": true, - "requires": { - "onetime": "^5.1.0", - "signal-exit": "^3.0.2" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "optional": true - }, "revalidator": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/revalidator/-/revalidator-0.1.8.tgz", "integrity": "sha1-/s5hv6DBtSoga9axgZgYS91SOjs=" }, "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "requires": { - "glob": "^7.0.5" - } - }, - "run-async": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", - "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", - "dev": true, - "requires": { - "is-promise": "^2.1.0" - } - }, - "rxjs": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.4.tgz", - "integrity": "sha512-naMQXcgEo3csAEGvw/NydRA0fuS2nDZJiw1YUWFKU7aPPAPGZEsD4Iimit96qwCieH6y614MCLYwdkrWx7z/7Q==", - "dev": true, + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", "requires": { - "tslib": "^1.9.0" + "glob": "^7.1.3" } }, "safe-buffer": { @@ -5887,15 +5030,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "optional": true, - "requires": { - "ret": "~0.1.10" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -5911,28 +5045,28 @@ } }, "sax": { - "version": "0.5.8", - "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", - "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "semver": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.3.tgz", - "integrity": "sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA==" + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, "semver-diff": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", - "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dev": true, "requires": { - "semver": "^5.0.3" + "semver": "^6.3.0" }, "dependencies": { "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", "dev": true } } @@ -5972,32 +5106,10 @@ } } }, - "http-errors": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", - "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", - "requires": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.1", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.0" - } - }, - "mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" - }, "ms": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" - }, - "setprototypeof": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", - "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" } } }, @@ -6019,6 +5131,15 @@ } } }, + "serialize-javascript": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", + "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, "serve-static": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", @@ -6028,13 +5149,6 @@ "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.17.1" - }, - "dependencies": { - "parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" - } } }, "set-blocking": { @@ -6043,58 +5157,44 @@ "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "setprototypeof": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" }, "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "shebang-regex": "^1.0.0" + "shebang-regex": "^3.0.0" } }, "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, + "side-channel": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.3.tgz", + "integrity": "sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==", + "requires": { + "es-abstract": "^1.18.0-next.0", + "object-inspect": "^1.8.0" + } + }, "sift": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" }, "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, "signature_pad": { @@ -6106,150 +5206,43 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - } - } - }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, - "smart-buffer": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", - "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" - }, - "smtpapi": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/smtpapi/-/smtpapi-1.3.1.tgz", - "integrity": "sha1-GHQp607SffSjz/0j8OAkXN/mh9Q=" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "optional": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "optional": true, + "dev": true, "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" }, "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "optional": true, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "color-convert": "^1.9.0" } } } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "optional": true, + "sliced": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", + "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" + }, + "smart-buffer": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.1.0.tgz", + "integrity": "sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw==" + }, + "smtpapi": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/smtpapi/-/smtpapi-1.4.4.tgz", + "integrity": "sha512-sVixyAfytmpSFonYGN7cggpLaYvtsBEkPN/ilWb/WFcL/hu93P7OaWthPabsEpMZsq3UK4w93V3ywz2gHS2ksw==", "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "chai": "^2.3.0", + "esdoc": "^1.0.3", + "esdoc-coverage-plugin": "^1.1.0", + "esdoc-type-inference-plugin": "^1.0.1" } }, "socks": { @@ -6281,28 +5274,9 @@ } }, "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "optional": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "optional": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "optional": true }, "sparse-bitfield": { @@ -6324,15 +5298,6 @@ "os-shim": "^0.1.2" } }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "optional": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -6364,43 +5329,17 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "optional": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, "stream-transform": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.1.tgz", - "integrity": "sha512-GiTcO/rRvZP2R8WPwxmxCFP+Of1yIATuFAmYkvSLDfcD93X2WHiPwdgIqeFT2CvL1gyAsjQvu1nB6RDNQ5b2jw==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-2.0.3.tgz", + "integrity": "sha512-HgNXs2rNG9pKC2gK4owtMPMKfto81S/TWZw0Ybsx6Wp23klnN0YctnMXYe0PmzzhB/zIBEB+0Urf+PMorE4d/A==", "requires": { - "mixme": "^0.3.1" + "mixme": "^0.4.0" } }, "string-hash": { @@ -6409,33 +5348,49 @@ "integrity": "sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=" }, "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, + "string.prototype.trimend": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", + "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, + "string.prototype.trimstart": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", + "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, "string_decoder": { @@ -6454,77 +5409,114 @@ "ansi-regex": "^2.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, "strip-json-comments": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.0.1.tgz", - "integrity": "sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true }, "superagent": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-3.8.3.tgz", - "integrity": "sha512-GLQtLMCoEIK4eDv6OGtkOoSMt3D+oq0y3dsxMuYuDvaNUvuT8eFBuLmfR0iYYzHC1e8hpzC6ZsxbuP6DIalMFA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-6.1.0.tgz", + "integrity": "sha512-OUDHEssirmplo3F+1HWKUrUjvnQuA+nZI6i/JJBdXb5eq9IyEQwPyPpqND+SSsxf6TygpBEkUjISVRN4/VOpeg==", "dev": true, "requires": { - "component-emitter": "^1.2.0", - "cookiejar": "^2.1.0", - "debug": "^3.1.0", - "extend": "^3.0.0", - "form-data": "^2.3.1", - "formidable": "^1.2.0", - "methods": "^1.1.1", - "mime": "^1.4.1", - "qs": "^6.5.1", - "readable-stream": "^2.3.5" + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.2", + "debug": "^4.1.1", + "fast-safe-stringify": "^2.0.7", + "form-data": "^3.0.0", + "formidable": "^1.2.2", + "methods": "^1.1.2", + "mime": "^2.4.6", + "qs": "^6.9.4", + "readable-stream": "^3.6.0", + "semver": "^7.3.2" }, "dependencies": { - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", + "form-data": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", + "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", "dev": true, "requires": { - "ms": "^2.1.1" + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" } }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "mime": { + "version": "2.4.6", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz", + "integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==", + "dev": true + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==", "dev": true + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } } } }, - "supertest": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-4.0.2.tgz", - "integrity": "sha512-1BAbvrOZsGA3YTCWqbmh14L0YEq0EGICX/nBnfkfVJn7SrxQV1I3pMYjSzG9y/7ZU2V9dWqyqk2POwxlb09duQ==", + "superagent-retry": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/superagent-retry/-/superagent-retry-0.6.0.tgz", + "integrity": "sha1-5Js1ypbA47HQ4/SWBRNt8OCgKLc=" + }, + "superagent-use": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/superagent-use/-/superagent-use-0.1.0.tgz", + "integrity": "sha1-0o27v1c7qYAym0ZFiXYIvJlQHP8=", "dev": true, "requires": { - "methods": "^1.1.2", - "superagent": "^3.8.3" + "extend": "^3.0.0", + "methods": "~1.1.2" } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "supertest": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-5.0.0.tgz", + "integrity": "sha512-2JAWpPrUOZF4hHH5ZTCN2xjKXvJS3AEwPNXl0HUseHsfcXFvMy9kcsufIHCNAmQ5hlGCvgeAqaR5PBEouN3hlQ==", "dev": true, "requires": { - "has-flag": "^3.0.0" + "methods": "1.1.2", + "superagent": "6.1.0" } }, + "supertest-capture-error": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supertest-capture-error/-/supertest-capture-error-1.0.0.tgz", + "integrity": "sha512-/W7u/L8JbGkdP+BD+pq5FDnCoOCsW1+cYEtkDzmJUafgsTr5tJ4YpVCoQQUR3njYLkGA8HfhHFr0W/S6CP+PYg==", + "dev": true + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, "symbol-observable": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "optional": true + }, "table": { "version": "5.4.6", "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", @@ -6535,60 +5527,13 @@ "lodash": "^4.17.14", "slice-ansi": "^2.1.0", "string-width": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "dev": true, - "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } } }, + "taffydb": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.7.3.tgz", + "integrity": "sha1-KtNxaWKUmPylvIQkMJbTzeDsOjQ=" + }, "tarn": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/tarn/-/tarn-1.1.5.tgz", @@ -6613,9 +5558,9 @@ }, "dependencies": { "bl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.0.tgz", - "integrity": "sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bl/-/bl-3.0.1.tgz", + "integrity": "sha512-jrCW5ZhfQ/Vt07WX1Ngs+yn9BDqPL/gw28S7s9H6QK/gupnizNzJAss5akW20ISgOrbLTlXOOCTJeNUQqruAWQ==", "requires": { "readable-stream": "^3.0.1" } @@ -6625,6 +5570,14 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" }, + "iconv-lite": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz", + "integrity": "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -6638,13 +5591,10 @@ } }, "term-size": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz", - "integrity": "sha1-RYuDiH8oj8Vtb/+/rSYuJmOO+mk=", - "dev": true, - "requires": { - "execa": "^0.7.0" - } + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true }, "text-mask-addons": { "version": "3.8.0", @@ -6664,6 +5614,13 @@ "requires": { "eventemitter3": "^2.0.2", "native-promise-only": "^0.8.1" + }, + "dependencies": { + "eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo=" + } } }, "through": { @@ -6681,61 +5638,23 @@ "resolved": "https://registry.npmjs.org/ticky/-/ticky-1.0.1.tgz", "integrity": "sha1-t8+nHnaPHJAAxJe5FRswlHxQ5G0=" }, - "timed-out": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", - "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=", - "dev": true - }, - "tmp": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", - "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", - "dev": true, - "requires": { - "os-tmpdir": "~1.0.2" - } - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "optional": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true }, "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "optional": true, + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "is-number": "^7.0.0" } }, "toidentifier": { @@ -6744,9 +5663,9 @@ "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, "tooltip.js": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/tooltip.js/-/tooltip.js-1.3.2.tgz", - "integrity": "sha512-DeDr9JxYx/lSvQ53ZCRFLxXrmrSyU3fLz6k+ITUTw69AIYtpWij/NmOJQscJ7BwY5lcEwWJWSfqqQWVvTMYZiw==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tooltip.js/-/tooltip.js-1.3.3.tgz", + "integrity": "sha512-XWWuy/dBdF/F/YpRE955yqBZ4VdLfiTAUdOqoU+wJm6phJlMpEzl/iYHZ+qJswbeT9VG822bNfsETF9wzmoy5A==", "requires": { "popper.js": "^1.0.2" } @@ -6769,25 +5688,29 @@ } }, "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } + "psl": "^1.1.28", + "punycode": "^2.1.1" } }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=", + "optional": true + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, "tsscmp": { "version": "1.0.6", @@ -6812,11 +5735,6 @@ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, - "two.js": { - "version": "0.7.0-beta.3", - "resolved": "https://registry.npmjs.org/two.js/-/two.js-0.7.0-beta.3.tgz", - "integrity": "sha512-oPEmRGCo5c296Wn40+yTxYydwPTf1iwJY+EPdLoka5TxUkLkAUo10CkQxRZJ7VGmbShBxQRXQxS5i5/otwqGZg==" - }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", @@ -6825,6 +5743,11 @@ "prelude-ls": "~1.1.2" } }, + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + }, "type-fest": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", @@ -6838,21 +5761,6 @@ "requires": { "media-typer": "0.3.0", "mime-types": "~2.1.24" - }, - "dependencies": { - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - } } }, "typedarray": { @@ -6861,10 +5769,19 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "dev": true, + "requires": { + "is-typedarray": "^1.0.0" + } + }, "undefsafe": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", - "integrity": "sha1-Il9rngM3Zj4Njnz9aG/Cg2zKznY=", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", + "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", "dev": true, "requires": { "debug": "^2.2.0" @@ -6878,144 +5795,122 @@ "requires": { "ms": "2.0.0" } - } - } - }, - "underscore": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz", - "integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + } } }, + "underscore": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.0.tgz", + "integrity": "sha512-21rQzss/XPMjolTiIezSu3JAjgagXKROtNrYFEOWK109qY1Uv2tVjPTZ1ci2HgvQDA16gHYSthQIJfB+XId/rQ==" + }, "unique-string": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", - "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, "requires": { - "crypto-random-string": "^1.0.0" + "crypto-random-string": "^2.0.0" } }, "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" }, "unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "optional": true, + "update-notifier": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.3.tgz", + "integrity": "sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A==", + "dev": true, "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.3.1", + "is-npm": "^4.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.0.0", + "pupa": "^2.0.1", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" }, "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "optional": true, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "optional": true, - "requires": { - "isarray": "1.0.0" - } - } + "color-convert": "^2.0.1" } }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "optional": true + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } } } }, - "unzip-response": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", - "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", - "dev": true - }, - "upath": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", - "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==", - "optional": true - }, - "update-notifier": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-2.5.0.tgz", - "integrity": "sha512-gwMdhgJHGuj/+wHJJs9e6PcCszpxR1b236igrOkUofGhqJuG+amlIKwApH1IW1WWl7ovZxsX49lMBWLxSdm5Dw==", - "dev": true, - "requires": { - "boxen": "^1.2.1", - "chalk": "^2.0.1", - "configstore": "^3.0.0", - "import-lazy": "^2.1.0", - "is-ci": "^1.0.10", - "is-installed-globally": "^0.1.0", - "is-npm": "^1.0.0", - "latest-version": "^3.0.0", - "semver-diff": "^2.0.0", - "xdg-basedir": "^3.0.0" - } - }, "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", "requires": { "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "optional": true - }, "url-parse-lax": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", - "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", "dev": true, "requires": { - "prepend-http": "^1.0.1" + "prepend-http": "^2.0.0" } }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "optional": true - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7038,6 +5933,11 @@ "version": "0.9.2", "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=" + }, + "deep-equal": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.2.2.tgz", + "integrity": "sha1-hLdFiW80xoTpjyzg5Cq69Du6AX0=" } } }, @@ -7047,20 +5947,20 @@ "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg==" }, "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", + "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", "dev": true }, "vanilla-picker": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.10.0.tgz", - "integrity": "sha512-s1K+oa/JrT5blJFbLLw1O+PMncJM1qCF8DVXKNPaTVxXgTteSCjSr4rvf8TpcqEcQQ+S4RV/nvPBZanrlJE82w==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.11.0.tgz", + "integrity": "sha512-MsTAyROQRN3yoUdToiQtfnW752wC9DpnXbOhAMGJgBi+TMDZ0IUfbmIVB5u3P1ltBDlqy8TjY59a4lOioSlgZw==", "requires": { "@sphinxxxx/color-conversion": "^2.2.2" } @@ -7085,21 +5985,92 @@ "extsprintf": "^1.2.0" } }, + "webidl-conversions": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-2.0.1.tgz", + "integrity": "sha1-O/glj30xjHRDw28uFpQCoaZwNQY=", + "optional": true + }, + "whatwg-url-compat": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/whatwg-url-compat/-/whatwg-url-compat-0.6.5.tgz", + "integrity": "sha1-AImBEa9om7CXVBzVpFymyHmERb8=", + "optional": true, + "requires": { + "tr46": "~0.0.1" + } + }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "requires": { "isexe": "^2.0.0" } }, + "which-boxed-primitive": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz", + "integrity": "sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==", + "requires": { + "is-bigint": "^1.0.0", + "is-boolean-object": "^1.0.0", + "is-number-object": "^1.0.3", + "is-string": "^1.0.4", + "is-symbol": "^1.0.2" + } + }, + "which-collection": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", + "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", + "requires": { + "is-map": "^2.0.1", + "is-set": "^2.0.1", + "is-weakmap": "^2.0.1", + "is-weakset": "^2.0.1" + } + }, "which-module": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, + "which-typed-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", + "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "requires": { + "available-typed-arrays": "^1.0.2", + "es-abstract": "^1.17.5", + "foreach": "^2.0.5", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.1", + "is-typed-array": "^1.1.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.7", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz", + "integrity": "sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + } + } + }, "wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", @@ -7107,15 +6078,6 @@ "dev": true, "requires": { "string-width": "^1.0.2 || 2" - } - }, - "widest-line": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz", - "integrity": "sha512-Ba5m9/Fa4Xt9eb2ELXt77JxVDV8w7qQrH0zS/TWSJdLyAwQjWoOzpzj5lwVftDz6n/EOu3tNACS84v509qwnJA==", - "dev": true, - "requires": { - "string-width": "^2.1.1" }, "dependencies": { "ansi-regex": { @@ -7124,12 +6086,6 @@ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", "dev": true }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -7151,10 +6107,54 @@ } } }, - "window-size": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz", - "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY=" + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + } + } }, "winston": { "version": "2.1.1", @@ -7197,13 +6197,47 @@ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==" }, + "workerpool": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.0.2.tgz", + "integrity": "sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q==", + "dev": true + }, "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } } }, "wrappy": { @@ -7221,40 +6255,47 @@ } }, "write-file-atomic": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz", - "integrity": "sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", + "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, "requires": { - "graceful-fs": "^4.1.11", "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.2" + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" } }, "xdg-basedir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", - "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true }, + "xml-name-validator": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", + "integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", + "optional": true + }, "xml2js": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.0.tgz", - "integrity": "sha1-Ek/EEUtBKcgQgA7LKshs8lRiy5o=", + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", "requires": { - "sax": "0.5.x", - "xmlbuilder": ">=0.4.2" + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" } }, "xmlbuilder": { - "version": "10.1.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-10.1.1.tgz", - "integrity": "sha512-OyzrcFLL/nb6fMGHbiRDuPup9ljBycsdCypwuyg5AAHvyWzGfChJpCXMG88AGTIMFhGZ9RccFN1e6lhg3hkwKg==" + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" }, "xmldom": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.2.1.tgz", - "integrity": "sha512-kXXiYvmblIgEemGeB75y97FyaZavx6SQhGppLw5TKWAD2Wd0KAly0g23eVLh17YcpxZpnFym1Qk/eaRjy1APPg==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.4.0.tgz", + "integrity": "sha512-2E93k08T30Ugs+34HBSTQLVtpi6mCddaY8uO+pMNk1pqSjV5vElzn4mmh6KLxN3hki8rNcHSYzILoh3TEWORvA==" }, "xpath.js": { "version": "1.1.0", @@ -7267,14 +6308,15 @@ "integrity": "sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM=" }, "y18n": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz", - "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=" + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true }, "yallist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", - "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" }, "yaml": { "version": "0.2.3", @@ -7282,133 +6324,109 @@ "integrity": "sha1-tUUOkudu82td0k42YAkeuu7z5cc=" }, "yargs": { - "version": "3.32.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz", - "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=", - "requires": { - "camelcase": "^2.0.1", - "cliui": "^3.0.3", - "decamelize": "^1.1.1", - "os-locale": "^1.4.0", - "string-width": "^1.0.1", - "window-size": "^0.1.4", - "y18n": "^3.2.0" - } - }, - "yargs-parser": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", - "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "dependencies": { - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - } - } - }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", "dev": true, "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.2" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "locate-path": "^3.0.0" } }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", "dev": true, "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" } }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "p-try": "^2.0.0" } }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "p-limit": "^2.0.0" } }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + } + } + }, + "yargs-parser": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", + "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "dependencies": { + "camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", "dev": true }, - "yargs": { - "version": "13.3.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", - "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", - "dev": true, - "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.1" - } + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true } } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true } } } diff --git a/forms-flow-forms/package.json b/forms-flow-forms/package.json index 515c152281..476428231c 100644 --- a/forms-flow-forms/package.json +++ b/forms-flow-forms/package.json @@ -1,8 +1,8 @@ { "name": "formio", - "version": "1.70.0", + "version": "2.0.0-rc.34", "description": "The formio server application.", - "license": "SEE LICENSE IN LICENSE.txt", + "license": "OSL-3.0", "main": "index.js", "scripts": { "test": "env TEST_SUITE=1 mocha test/test.js -b -t 60000 --exit", @@ -18,58 +18,62 @@ "templateVersion": "2.0.0", "dependencies": { "JSONStream": "^1.3.5", - "adm-zip": "^0.4.14", - "async": "^3.1.1", + "abort-controller": "^3.0.0", + "adm-zip": "^0.5.0", + "async": "^3.2.0", "bcryptjs": "^2.4.3", "body-parser": "^1.19.0", - "chance": "^1.1.4", + "chance": "^1.1.7", "clone": "^2.1.2", "colors": "^1.4.0", - "config": "^3.2.6", + "config": "^3.3.2", "cors": "^2.8.5", - "csv": "^5.3.1", - "debug": "^4.1.1", + "csv": "^5.3.2", + "debug": "^4.3.1", "delete-property": "0.0.4", + "dotenv": "^8.2.0", "eslint-config-formio": "^1.1.2", "event-chain": "^0.0.1", "express": "^4.17.1", "fast-json-patch": "^2.2.1", - "formio-workers": "^1.14.0", - "formiojs": "^4.9.0-rc.2", - "fs-extra": "^8.1.0", - "html-entities": "^1.2.1", + "formio-workers": "^1.14.8", + "formiojs": "^4.12.2-rc.3", + "fs-extra": "^9.0.1", + "html-entities": "^1.3.1", + "https-proxy-agent": "^5.0.0", "joi": "^14.3.1", "jsonwebtoken": "^8.5.1", - "lodash": "^4.17.19", + "lodash": "^4.17.20", "memory-cache": "^0.2.0", - "method-override": "^3.0.0", - "moment": "^2.24.0", - "moment-timezone": "^0.5.28", - "mongodb": "^3.5.3", - "mongoose": "^5.9.2", - "mssql": "^6.1.0", + "moment": "^2.29.1", + "moment-timezone": "^0.5.32", + "mongodb": "^3.6.3", + "mongoose": "^5.10.15", + "mssql": "^6.2.3", "mysql": "^2.18.1", - "nodemailer": "^6.4.3", - "nodemailer-mailgun-transport": "^2.0.0", + "node-fetch": "^2.6.1", + "nodemailer": "^6.4.16", + "nodemailer-mailgun-transport": "^2.0.1", "nodemailer-mandrill-transport": "^1.2.0", "nodemailer-sendgrid-transport": "^0.2.0", "progress": "^2.0.3", "prompt": "^1.0.0", "q": "^1.5.0", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "resourcejs": "^1.39.0", + "resourcejs": "^2.3.2", "restler": "^3.4.0", - "semver": "^7.1.3", + "semver": "^7.3.2", + "superagent-retry": "^0.6.0", "through": "^2.3.8", "vanilla-text-mask": "^5.1.1" }, "devDependencies": { - "eslint": "^6.8.0", - "mocha": "^7.0.1", - "nodemon": "^2.0.2", + "eslint": "^7.13.0", + "mocha": "^8.2.1", + "nodemon": "^2.0.6", "pre-commit": "^1.2.2", - "supertest": "^4.0.2" + "superagent-use": "^0.1.0", + "supertest": "^5.0.0", + "supertest-capture-error": "^1.0.0" }, "directories": { "test": "test" diff --git a/forms-flow-forms/sample.env b/forms-flow-forms/sample.env index 2141637f78..887220b3da 100644 --- a/forms-flow-forms/sample.env +++ b/forms-flow-forms/sample.env @@ -7,3 +7,5 @@ FORMIO_MONGO_DATABASE=formio FORMIO_ROOT_EMAIL=admin@example.com FORMIO_ROOT_PASSWORD=CHANGEME +#URL of forms-flow-forms +FORMIO_DEFAULT_PROJECT_URL=http://localhost:3001 \ No newline at end of file diff --git a/forms-flow-forms/sample.json b/forms-flow-forms/sample.json index 748bb1f466..1ebd99d873 100644 --- a/forms-flow-forms/sample.json +++ b/forms-flow-forms/sample.json @@ -1,910 +1,910 @@ { - "template": - { - "title": "Export", - "version": "2.0.0", - "description": "", - "name": "export", - "roles": { - "administrator": { - "title": "Administrator", - "description": "A role for Administrative Users.", - "admin": true, - "default": false - }, - "authenticated": { - "title": "Authenticated", - "description": "A role for Authenticated Users.", - "admin": false, - "default": false - }, - "anonymous": { - "title": "Anonymous", - "description": "A role for Anonymous Users.", - "admin": false, - "default": true - }, - "staffReviewer": { - "title": "Staff Reviewer", - "description": "A person who can view all form submissions.", - "admin": false, - "default": false - }, - "client": { - "title": "Client", - "description": "A person who can do form submission.", - "admin": false, - "default": false - } + "template": + { + "title": "Export", + "version": "2.0.0", + "description": "", + "name": "export", + "roles": { + "administrator": { + "title": "Administrator", + "description": "A role for Administrative Users.", + "admin": true, + "default": false + }, + "authenticated": { + "title": "Authenticated", + "description": "A role for Authenticated Users.", + "admin": false, + "default": false + }, + "anonymous": { + "title": "Anonymous", + "description": "A role for Anonymous Users.", + "admin": false, + "default": true + }, + "staffReviewer": { + "title": "Staff Reviewer", + "description": "A person who can view all form submissions.", + "admin": false, + "default": false + }, + "client": { + "title": "Client", + "description": "A person who can do form submission.", + "admin": false, + "default": false + } + }, + "forms": { + "userLogin": { + "title": "User Login", + "type": "form", + "name": "userLogin", + "path": "user/login", + "tags": [], + "components": [ + { + "type": "email", + "persistent": true, + "unique": false, + "protected": false, + "defaultValue": "", + "suffix": "", + "prefix": "", + "placeholder": "Enter your email address", + "key": "email", + "lockKey": true, + "label": "Email", + "inputType": "email", + "tableView": true, + "input": true + }, + { + "type": "password", + "persistent": true, + "protected": true, + "suffix": "", + "prefix": "", + "placeholder": "Enter your password.", + "key": "password", + "lockKey": true, + "label": "Password", + "inputType": "password", + "tableView": false, + "input": true + }, + { + "type": "button", + "theme": "primary", + "disableOnInvalid": true, + "action": "submit", + "block": false, + "rightIcon": "", + "leftIcon": "", + "size": "md", + "key": "submit", + "tableView": false, + "label": "Submit", + "input": true + } + ], + "access": [ + { + "roles": [ + "anonymous" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [ + "anonymous" + ], + "type": "create_own" + } + ] + }, + "userRegister": { + "title": "User Register", + "type": "form", + "name": "userRegister", + "path": "user/register", + "tags": [], + "components": [ + { + "type": "email", + "persistent": true, + "unique": false, + "protected": false, + "defaultValue": "", + "suffix": "", + "prefix": "", + "placeholder": "Enter your email address", + "key": "email", + "lockKey": true, + "label": "Email", + "inputType": "email", + "tableView": true, + "input": true + }, + { + "type": "password", + "persistent": true, + "protected": true, + "suffix": "", + "prefix": "", + "placeholder": "Enter your password.", + "key": "password", + "lockKey": true, + "label": "Password", + "inputType": "password", + "tableView": false, + "input": true + }, + { + "theme": "primary", + "disableOnInvalid": true, + "action": "submit", + "block": false, + "rightIcon": "", + "leftIcon": "", + "size": "md", + "key": "submit", + "label": "Submit", + "input": true, + "type": "button" + } + ], + "access": [ + { + "roles": [ + "anonymous" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [ + "anonymous" + ], + "type": "create_own" + } + ] + } + }, + "actions": { + "userSave": { + "title": "Save Submission", + "name": "save", + "form": "user", + "settings": {}, + "priority": 11, + "method": [ + "create", + "update" + ], + "handler": [ + "before" + ] + }, + "adminSave": { + "title": "Save Submission", + "name": "save", + "form": "admin", + "settings": {}, + "priority": 11, + "method": [ + "create", + "update" + ], + "handler": [ + "before" + ] + }, + "userLogin": { + "title": "Login", + "name": "login", + "form": "userLogin", + "condition": {}, + "settings": { + "resources": [ + "user", + "admin" + ], + "username": "email", + "password": "password" }, - "forms": { - "userLogin": { - "title": "User Login", - "type": "form", - "name": "userLogin", - "path": "user/login", - "tags": [], - "components": [ - { - "type": "email", - "persistent": true, - "unique": false, - "protected": false, - "defaultValue": "", - "suffix": "", - "prefix": "", - "placeholder": "Enter your email address", - "key": "email", - "lockKey": true, - "label": "Email", - "inputType": "email", - "tableView": true, - "input": true - }, - { - "type": "password", - "persistent": true, - "protected": true, - "suffix": "", - "prefix": "", - "placeholder": "Enter your password.", - "key": "password", - "lockKey": true, - "label": "Password", - "inputType": "password", - "tableView": false, - "input": true - }, - { - "type": "button", - "theme": "primary", - "disableOnInvalid": true, - "action": "submit", - "block": false, - "rightIcon": "", - "leftIcon": "", - "size": "md", - "key": "submit", - "tableView": false, - "label": "Submit", - "input": true - } - ], - "access": [ - { - "roles": [ - "anonymous" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [ - "anonymous" - ], - "type": "create_own" - } - ] - }, - "userRegister": { - "title": "User Register", - "type": "form", - "name": "userRegister", - "path": "user/register", - "tags": [], - "components": [ - { - "type": "email", - "persistent": true, - "unique": false, - "protected": false, - "defaultValue": "", - "suffix": "", - "prefix": "", - "placeholder": "Enter your email address", - "key": "email", - "lockKey": true, - "label": "Email", - "inputType": "email", - "tableView": true, - "input": true - }, - { - "type": "password", - "persistent": true, - "protected": true, - "suffix": "", - "prefix": "", - "placeholder": "Enter your password.", - "key": "password", - "lockKey": true, - "label": "Password", - "inputType": "password", - "tableView": false, - "input": true - }, - { - "theme": "primary", - "disableOnInvalid": true, - "action": "submit", - "block": false, - "rightIcon": "", - "leftIcon": "", - "size": "md", - "key": "submit", - "label": "Submit", - "input": true, - "type": "button" - } - ], - "access": [ - { - "roles": [ - "anonymous" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [ - "anonymous" - ], - "type": "create_own" - } - ] - } + "priority": 2, + "method": [ + "create" + ], + "handler": [ + "before" + ] + }, + "userRegisterSave": { + "title": "Save Submission", + "name": "save", + "form": "userRegister", + "settings": { + "resource": "user", + "fields": { + "email": "email", + "password": "password" + } }, - "actions": { - "userSave": { - "title": "Save Submission", - "name": "save", - "form": "user", - "settings": {}, - "priority": 11, - "method": [ - "create", - "update" - ], - "handler": [ - "before" - ] - }, - "adminSave": { - "title": "Save Submission", - "name": "save", - "form": "admin", - "settings": {}, - "priority": 11, - "method": [ - "create", - "update" - ], - "handler": [ - "before" - ] - }, - "userLogin": { - "title": "Login", - "name": "login", - "form": "userLogin", - "condition": {}, - "settings": { - "resources": [ - "user", - "admin" - ], - "username": "email", - "password": "password" - }, - "priority": 2, - "method": [ - "create" - ], - "handler": [ - "before" - ] - }, - "userRegisterSave": { - "title": "Save Submission", - "name": "save", - "form": "userRegister", - "settings": { - "resource": "user", - "fields": { - "email": "email", - "password": "password" - } - }, - "priority": 10, - "method": [ - "create" - ], - "handler": [ - "before" - ] - }, - "userRegisterLogin": { - "title": "Login", - "name": "login", - "form": "userRegister", - "settings": { - "resources": [ - "user" - ], - "username": "email", - "password": "password" - }, - "priority": 2, - "method": [ - "create" - ], - "handler": [ - "before" - ] - }, - "authenticatedRole": { - "title": "Role Assignment", - "name": "role", - "form": "user", - "settings": { - "role": "authenticated", - "type": "add", - "association": "new" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] - }, - "adminRole": { - "title": "Role Assignment", - "name": "role", - "form": "admin", - "settings": { - "role": "administrator", - "type": "add", - "association": "new" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] - }, - "user:role": { - "title": "Assign Employee Role", - "name": "role", - "form": "user", - "settings": { - "association": "new", - "role": "staffReviewer", - "type": "add" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] - }, - "userRegister:role": { - "title": "Role Assignment", - "name": "role", - "form": "userRegister", - "condition": {}, - "settings": { - "association": "existing", - "type": "add", - "role": "staffReviewer" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] - }, + "priority": 10, + "method": [ + "create" + ], + "handler": [ + "before" + ] + }, + "userRegisterLogin": { + "title": "Login", + "name": "login", + "form": "userRegister", + "settings": { + "resources": [ + "user" + ], + "username": "email", + "password": "password" + }, + "priority": 2, + "method": [ + "create" + ], + "handler": [ + "before" + ] + }, + "authenticatedRole": { + "title": "Role Assignment", + "name": "role", + "form": "user", + "settings": { + "role": "authenticated", + "type": "add", + "association": "new" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + }, + "adminRole": { + "title": "Role Assignment", + "name": "role", + "form": "admin", + "settings": { + "role": "administrator", + "type": "add", + "association": "new" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + }, + "user:role": { + "title": "Assign Employee Role", + "name": "role", + "form": "user", + "settings": { + "association": "new", + "role": "staffReviewer", + "type": "add" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + }, + "userRegister:role": { + "title": "Role Assignment", + "name": "role", + "form": "userRegister", + "condition": {}, + "settings": { + "association": "existing", + "type": "add", + "role": "staffReviewer" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + }, - "staffReviewer:save": { - "title": "Save Submission", - "name": "save", - "form": "staffReviewer", - "priority": 10, - "method": [ - "create", - "update" - ], - "handler": [ - "before" - ] + "staffReviewer:save": { + "title": "Save Submission", + "name": "save", + "form": "staffReviewer", + "priority": 10, + "method": [ + "create", + "update" + ], + "handler": [ + "before" + ] + }, + "staffReviewer:role": { + "title": "Role Assignment", + "name": "role", + "form": "staffReviewer", + "settings": { + "association": "new", + "type": "add", + "role": "staffReviewer" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + }, + + "client:save": { + "title": "Save Submission", + "name": "save", + "form": "client", + "priority": 10, + "method": [ + "create", + "update" + ], + "handler": [ + "before" + ] + }, + "client:role": { + "title": "Role Assignment", + "name": "role", + "form": "client", + "settings": { + "association": "new", + "type": "add", + "role": "client" + }, + "priority": 1, + "method": [ + "create" + ], + "handler": [ + "after" + ] + } + }, + "resources": { + "user": { + "title": "User", + "type": "resource", + "name": "user", + "path": "user", + "display": "form", + "tags": [], + "components": [ + { + "type": "email", + "persistent": true, + "unique": false, + "protected": false, + "defaultValue": "", + "suffix": "", + "prefix": "", + "placeholder": "Enter your email address", + "key": "email", + "label": "Email", + "inputType": "email", + "tableView": true, + "input": true + }, + { + "type": "password", + "persistent": true, + "protected": true, + "suffix": "", + "prefix": "", + "placeholder": "Enter your password.", + "key": "password", + "label": "Password", + "inputType": "password", + "tableView": false, + "input": true + }, + { + "type": "button", + "theme": "primary", + "disableOnInvalid": true, + "action": "submit", + "block": false, + "rightIcon": "", + "leftIcon": "", + "size": "md", + "key": "submit", + "tableView": false, + "label": "Submit", + "input": true + } + ], + "access": [ + { + "roles": [ + "anonymous", + "authenticated", + "administrator" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [ + "administrator" + ], + "type": "create_all" + }, + { + "roles": [ + "administrator" + ], + "type": "read_all" + }, + { + "roles": [ + "administrator" + ], + "type": "update_all" + }, + { + "roles": [ + "administrator" + ], + "type": "delete_all" + }, + { + "roles": [], + "type": "create_own" + }, + { + "roles": [], + "type": "read_own" + }, + { + "roles": [], + "type": "update_own" + }, + { + "roles": [], + "type": "delete_own" + } + ] + }, + "admin": { + "title": "Admin", + "type": "resource", + "name": "admin", + "path": "admin", + "tags": [], + "components": [ + { + "type": "email", + "persistent": true, + "unique": false, + "protected": false, + "defaultValue": "", + "suffix": "", + "prefix": "", + "placeholder": "Enter your email address", + "key": "email", + "label": "Email", + "inputType": "email", + "tableView": true, + "input": true + }, + { + "type": "password", + "persistent": true, + "protected": true, + "suffix": "", + "prefix": "", + "placeholder": "Enter your password.", + "key": "password", + "label": "Password", + "inputType": "password", + "tableView": false, + "input": true + }, + { + "type": "button", + "theme": "primary", + "disableOnInvalid": true, + "action": "submit", + "block": false, + "rightIcon": "", + "leftIcon": "", + "size": "md", + "key": "submit", + "tableView": false, + "label": "Submit", + "input": true + } + ], + "access": [ + { + "roles": [ + "anonymous", + "authenticated", + "administrator" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [ + "administrator" + ], + "type": "create_all" + }, + { + "roles": [ + "administrator" + ], + "type": "read_all" + }, + { + "roles": [ + "administrator" + ], + "type": "update_all" + }, + { + "roles": [ + "administrator" + ], + "type": "delete_all" + }, + { + "roles": [], + "type": "create_own" + }, + { + "roles": [], + "type": "read_own" + }, + { + "roles": [], + "type": "update_own" + }, + { + "roles": [], + "type": "delete_own" + } + ] + }, + "staffReviewer": { + "title": "Staff Reviewer", + "type": "resource", + "name": "staffReviewer", + "path": "staffreviewer", + "display": "form", + "tags": [ + "common" + ], + "components": [ + { + "autofocus": false, + "input": true, + "tableView": true, + "inputType": "email", + "label": "Email", + "key": "email", + "placeholder": "Enter your email address", + "prefix": "", + "suffix": "", + "defaultValue": "", + "protected": false, + "unique": false, + "persistent": true, + "hidden": false, + "clearOnHide": true, + "kickbox": { + "enabled": false }, - "staffReviewer:role": { - "title": "Role Assignment", - "name": "role", - "form": "staffReviewer", - "settings": { - "association": "new", - "type": "add", - "role": "staffReviewer" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] + "type": "email", + "lockKey": true, + "source": "5e78bdd7a054f922bc82d065", + "labelPosition": "top", + "inputFormat": "plain", + "tags": [], + "conditional": { + "show": "", + "when": null, + "eq": "" }, - - "client:save": { - "title": "Save Submission", - "name": "save", - "form": "client", - "priority": 10, - "method": [ - "create", - "update" - ], - "handler": [ - "before" - ] + "properties": {} + }, + { + "autofocus": false, + "input": true, + "tableView": false, + "inputType": "password", + "label": "Password", + "key": "password", + "placeholder": "Enter your password.", + "prefix": "", + "suffix": "", + "protected": true, + "persistent": true, + "hidden": false, + "clearOnHide": true, + "type": "password", + "lockKey": true, + "source": "5e78bdd7a054f922bc82d065", + "labelPosition": "top", + "tags": [], + "conditional": { + "show": "", + "when": null, + "eq": "" }, - "client:role": { - "title": "Role Assignment", - "name": "role", - "form": "client", - "settings": { - "association": "new", - "type": "add", - "role": "client" - }, - "priority": 1, - "method": [ - "create" - ], - "handler": [ - "after" - ] - } - }, - "resources": { - "user": { - "title": "User", - "type": "resource", - "name": "user", - "path": "user", - "display": "form", - "tags": [], - "components": [ - { - "type": "email", - "persistent": true, - "unique": false, - "protected": false, - "defaultValue": "", - "suffix": "", - "prefix": "", - "placeholder": "Enter your email address", - "key": "email", - "label": "Email", - "inputType": "email", - "tableView": true, - "input": true - }, - { - "type": "password", - "persistent": true, - "protected": true, - "suffix": "", - "prefix": "", - "placeholder": "Enter your password.", - "key": "password", - "label": "Password", - "inputType": "password", - "tableView": false, - "input": true - }, - { - "type": "button", - "theme": "primary", - "disableOnInvalid": true, - "action": "submit", - "block": false, - "rightIcon": "", - "leftIcon": "", - "size": "md", - "key": "submit", - "tableView": false, - "label": "Submit", - "input": true - } - ], - "access": [ - { - "roles": [ - "anonymous", - "authenticated", - "administrator" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [ - "administrator" - ], - "type": "create_all" - }, - { - "roles": [ - "administrator" - ], - "type": "read_all" - }, - { - "roles": [ - "administrator" - ], - "type": "update_all" - }, - { - "roles": [ - "administrator" - ], - "type": "delete_all" - }, - { - "roles": [], - "type": "create_own" - }, - { - "roles": [], - "type": "read_own" - }, - { - "roles": [], - "type": "update_own" - }, - { - "roles": [], - "type": "delete_own" - } - ] + "properties": {}, + "tabindex": "admin@example.com" + }, + { + "autofocus": false, + "input": true, + "label": "Submit", + "tableView": false, + "key": "submit", + "size": "md", + "leftIcon": "", + "rightIcon": "", + "block": false, + "action": "submit", + "disableOnInvalid": false, + "theme": "primary", + "type": "button" + } + ], + "access": [ + { + "roles": [ + "administrator", + "authenticated", + "anonymous", + "client", + "staffReviewer" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [], + "type": "create_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "read_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "update_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "delete_all" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "create_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "read_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "update_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "delete_own" + } + ] + }, + + "client": { + "title": "Client", + "type": "resource", + "name": "client", + "path": "client", + "display": "form", + "tags": [ + "common" + ], + "components": [ + { + "autofocus": false, + "input": true, + "tableView": true, + "inputType": "email", + "label": "Email", + "key": "email", + "placeholder": "Enter your email address", + "prefix": "", + "suffix": "", + "defaultValue": "", + "protected": false, + "unique": false, + "persistent": true, + "hidden": false, + "clearOnHide": true, + "kickbox": { + "enabled": false }, - "admin": { - "title": "Admin", - "type": "resource", - "name": "admin", - "path": "admin", - "tags": [], - "components": [ - { - "type": "email", - "persistent": true, - "unique": false, - "protected": false, - "defaultValue": "", - "suffix": "", - "prefix": "", - "placeholder": "Enter your email address", - "key": "email", - "label": "Email", - "inputType": "email", - "tableView": true, - "input": true - }, - { - "type": "password", - "persistent": true, - "protected": true, - "suffix": "", - "prefix": "", - "placeholder": "Enter your password.", - "key": "password", - "label": "Password", - "inputType": "password", - "tableView": false, - "input": true - }, - { - "type": "button", - "theme": "primary", - "disableOnInvalid": true, - "action": "submit", - "block": false, - "rightIcon": "", - "leftIcon": "", - "size": "md", - "key": "submit", - "tableView": false, - "label": "Submit", - "input": true - } - ], - "access": [ - { - "roles": [ - "anonymous", - "authenticated", - "administrator" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [ - "administrator" - ], - "type": "create_all" - }, - { - "roles": [ - "administrator" - ], - "type": "read_all" - }, - { - "roles": [ - "administrator" - ], - "type": "update_all" - }, - { - "roles": [ - "administrator" - ], - "type": "delete_all" - }, - { - "roles": [], - "type": "create_own" - }, - { - "roles": [], - "type": "read_own" - }, - { - "roles": [], - "type": "update_own" - }, - { - "roles": [], - "type": "delete_own" - } - ] + "type": "email", + "lockKey": true, + "source": "5e78bdd7a054f91afa82d066", + "labelPosition": "top", + "inputFormat": "plain", + "tags": [], + "conditional": { + "show": "", + "when": null, + "eq": "" }, - "staffReviewer": { - "title": "Staff Reviewer", - "type": "resource", - "name": "staffReviewer", - "path": "staffreviewer", - "display": "form", - "tags": [ - "common" - ], - "components": [ - { - "autofocus": false, - "input": true, - "tableView": true, - "inputType": "email", - "label": "Email", - "key": "email", - "placeholder": "Enter your email address", - "prefix": "", - "suffix": "", - "defaultValue": "", - "protected": false, - "unique": false, - "persistent": true, - "hidden": false, - "clearOnHide": true, - "kickbox": { - "enabled": false - }, - "type": "email", - "lockKey": true, - "source": "5e78bdd7a054f922bc82d065", - "labelPosition": "top", - "inputFormat": "plain", - "tags": [], - "conditional": { - "show": "", - "when": null, - "eq": "" - }, - "properties": {} - }, - { - "autofocus": false, - "input": true, - "tableView": false, - "inputType": "password", - "label": "Password", - "key": "password", - "placeholder": "Enter your password.", - "prefix": "", - "suffix": "", - "protected": true, - "persistent": true, - "hidden": false, - "clearOnHide": true, - "type": "password", - "lockKey": true, - "source": "5e78bdd7a054f922bc82d065", - "labelPosition": "top", - "tags": [], - "conditional": { - "show": "", - "when": null, - "eq": "" - }, - "properties": {}, - "tabindex": "admin@example.com" - }, - { - "autofocus": false, - "input": true, - "label": "Submit", - "tableView": false, - "key": "submit", - "size": "md", - "leftIcon": "", - "rightIcon": "", - "block": false, - "action": "submit", - "disableOnInvalid": false, - "theme": "primary", - "type": "button" - } - ], - "access": [ - { - "roles": [ - "administrator", - "authenticated", - "anonymous", - "client", - "staffReviewer" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [], - "type": "create_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "read_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "update_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "delete_all" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "create_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "read_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "update_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "delete_own" - } - ] + "properties": {} + }, + { + "autofocus": false, + "input": true, + "tableView": false, + "inputType": "password", + "label": "Password", + "key": "password", + "placeholder": "Enter your password.", + "prefix": "", + "suffix": "", + "protected": true, + "persistent": true, + "hidden": false, + "clearOnHide": true, + "type": "password", + "lockKey": true, + "source": "5e78bdd7a054f91afa82d066", + "labelPosition": "top", + "tags": [], + "conditional": { + "show": "", + "when": null, + "eq": "" }, - - "client": { - "title": "Client", - "type": "resource", - "name": "client", - "path": "client", - "display": "form", - "tags": [ - "common" - ], - "components": [ - { - "autofocus": false, - "input": true, - "tableView": true, - "inputType": "email", - "label": "Email", - "key": "email", - "placeholder": "Enter your email address", - "prefix": "", - "suffix": "", - "defaultValue": "", - "protected": false, - "unique": false, - "persistent": true, - "hidden": false, - "clearOnHide": true, - "kickbox": { - "enabled": false - }, - "type": "email", - "lockKey": true, - "source": "5e78bdd7a054f91afa82d066", - "labelPosition": "top", - "inputFormat": "plain", - "tags": [], - "conditional": { - "show": "", - "when": null, - "eq": "" - }, - "properties": {} - }, - { - "autofocus": false, - "input": true, - "tableView": false, - "inputType": "password", - "label": "Password", - "key": "password", - "placeholder": "Enter your password.", - "prefix": "", - "suffix": "", - "protected": true, - "persistent": true, - "hidden": false, - "clearOnHide": true, - "type": "password", - "lockKey": true, - "source": "5e78bdd7a054f91afa82d066", - "labelPosition": "top", - "tags": [], - "conditional": { - "show": "", - "when": null, - "eq": "" - }, - "properties": {}, - "tabindex": "admin@example.com" - }, - { - "autofocus": false, - "input": true, - "label": "Submit", - "tableView": false, - "key": "submit", - "size": "md", - "leftIcon": "", - "rightIcon": "", - "block": false, - "action": "submit", - "disableOnInvalid": false, - "theme": "primary", - "type": "button" - } - ], - "access": [ - { - "roles": [ - "administrator", - "authenticated", - "anonymous", - "client", - "staffReviewer" - ], - "type": "read_all" - } - ], - "submissionAccess": [ - { - "roles": [], - "type": "create_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "read_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "update_all" - }, - { - "roles": [ - "staffReviewer" - ], - "type": "delete_all" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "create_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "read_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "update_own" - }, - { - "roles": [ - "staffReviewer", - "client" - ], - "type": "delete_own" - } - ] - } - } + "properties": {}, + "tabindex": "admin@example.com" + }, + { + "autofocus": false, + "input": true, + "label": "Submit", + "tableView": false, + "key": "submit", + "size": "md", + "leftIcon": "", + "rightIcon": "", + "block": false, + "action": "submit", + "disableOnInvalid": false, + "theme": "primary", + "type": "button" + } + ], + "access": [ + { + "roles": [ + "administrator", + "authenticated", + "anonymous", + "client", + "staffReviewer" + ], + "type": "read_all" + } + ], + "submissionAccess": [ + { + "roles": [], + "type": "create_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "read_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "update_all" + }, + { + "roles": [ + "staffReviewer" + ], + "type": "delete_all" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "create_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "read_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "update_own" + }, + { + "roles": [ + "staffReviewer", + "client" + ], + "type": "delete_own" + } + ] + } } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/forms-flow-forms/server.js b/forms-flow-forms/server.js index a8d9c6c8a2..41bae83ac8 100644 --- a/forms-flow-forms/server.js +++ b/forms-flow-forms/server.js @@ -83,7 +83,7 @@ module.exports = function(options) { application.use('/', express.static(path.join(__dirname, '/app/dist'))); config.appPort = config.appPort || 8080; application.listen(config.appPort); - const appHost = `http://{config.host}:${config.appPort}`; + const appHost = `http://localhost:${config.appPort}`; util.log(` > Serving application at ${appHost.green}`); } diff --git a/forms-flow-forms/src/actions/EmailAction.js b/forms-flow-forms/src/actions/EmailAction.js index d28f590eb5..f0d91ca104 100644 --- a/forms-flow-forms/src/actions/EmailAction.js +++ b/forms-flow-forms/src/actions/EmailAction.js @@ -1,5 +1,5 @@ 'use strict'; -const request = require('request'); +const fetch = require('../util/fetch'); const LOG_EVENT = 'Email Action'; @@ -178,12 +178,16 @@ module.exports = (router) => { return next(err); } + // Save off the req.params since they get deleted after the response and we need them later. + const reqParams = req.params; + // Dont block on sending emails. next(); // eslint-disable-line callback-return // Get the email parameters. emailer.getParams(req, res, form, req.body) .then((params) => { + req.params = reqParams; const query = { _id: params.owner, deleted: {$eq: null}, @@ -199,21 +203,20 @@ module.exports = (router) => { params.owner = owner; } - return new Promise((resolve, reject) => { - if (!this.settings.template) { - return resolve(this.settings.message); - } - - return request(this.settings.template, (error, response, body) => { - if (!error && response.statusCode === 200) { - // Save the content before overwriting the message. - params.content = this.settings.message; - return resolve(body); - } + if (!this.settings.template) { + return this.settings.message; + } - return resolve(this.settings.message); - }); - }); + return fetch(this.settings.template) + .then((response) => response.ok ? response.text() : null) + .then((body) => { + if (body) { + // Save the content before overwriting the message. + params.content = this.settings.message; + } + return body || this.settings.message; + }) + .catch(() => this.settings.message); }) .then((template) => { this.settings.message = template; diff --git a/forms-flow-forms/src/actions/LoginAction.js b/forms-flow-forms/src/actions/LoginAction.js index 2bcd591419..b050ca1db1 100644 --- a/forms-flow-forms/src/actions/LoginAction.js +++ b/forms-flow-forms/src/actions/LoginAction.js @@ -4,12 +4,13 @@ const _ = require('lodash'); const LOG_EVENT = 'Login Action'; -module.exports = function(router) { +module.exports = (router) => { const Action = router.formio.Action; const hook = require('../util/hook')(router.formio); const debug = require('debug')('formio:action:login'); const ecode = router.formio.util.errorCodes; const logOutput = router.formio.log || debug; + const audit = router.formio.audit || (() => {}); const log = (...args) => logOutput(LOG_EVENT, ...args); /** @@ -25,12 +26,12 @@ module.exports = function(router) { priority: 2, defaults: { handler: ['before'], - method: ['create'] + method: ['create'], }, access: { handler: false, - method: false - } + method: false, + }, }); } @@ -44,6 +45,7 @@ module.exports = function(router) { static settingsForm(req, res, next) { const basePath = hook.alter('path', '/form', req); const dataSrc = `${basePath}/${req.params.formId}/components`; + next(null, [ { type: 'select', @@ -52,13 +54,14 @@ module.exports = function(router) { key: 'resources', placeholder: 'Select the resources we should login against.', dataSrc: 'url', - data: {url: `${basePath}?type=resource`}, + data: {url: `${basePath}?type=resource&limit={{ limit }}`}, valueProperty: '_id', template: '{{ item.title }}', + limit: 4294967295, multiple: true, validate: { - required: true - } + required: true, + }, }, { type: 'select', @@ -72,8 +75,8 @@ module.exports = function(router) { valueProperty: 'key', multiple: false, validate: { - required: true - } + required: true, + }, }, { type: 'select', @@ -87,8 +90,8 @@ module.exports = function(router) { valueProperty: 'key', multiple: false, validate: { - required: true - } + required: true, + }, }, { type: 'textfield', @@ -96,7 +99,7 @@ module.exports = function(router) { input: true, label: 'Maximum Login Attempts', description: 'Use 0 for unlimited attempts', - defaultValue: '5' + defaultValue: '5', }, { type: 'textfield', @@ -105,7 +108,7 @@ module.exports = function(router) { label: 'Login Attempt Time Window', description: 'This is the window of time to count the login attempts.', defaultValue: '30', - suffix: 'seconds' + suffix: 'seconds', }, { type: 'textfield', @@ -114,8 +117,8 @@ module.exports = function(router) { label: 'Locked Account Wait Time', description: 'The amount of time a person needs to wait before they can try to login again.', defaultValue: '1800', - suffix: 'seconds' - } + suffix: 'seconds', + }, ]); } @@ -138,32 +141,28 @@ module.exports = function(router) { */ /* eslint-disable max-statements */ checkAttempts(error, req, user, next) { - if (!user || !user._id) { - return next.call(this, error); - } - - if (!this.settings.allowedAttempts) { - return next.call(this, error); + if (!user || !user._id || !this.settings.allowedAttempts) { + return next(error); } const allowedAttempts = parseInt(this.settings.allowedAttempts, 10); - if (!allowedAttempts) { - return next.call(this, error); + if (Number.isNaN(allowedAttempts) || allowedAttempts === 0) { + return next(error); } // Initialize the login metadata. - if (!user.metadata) { - user.metadata = {login: {}}; - } - if (!user.metadata.login) { - user.metadata.login = {}; + if (!_.has(user, 'metadata.login')) { + _.set(user, 'metadata.login', {}); } const now = (new Date()).getTime(); - const lastAttempt = parseInt(user.metadata.login.last, 10) || 0; + const { + login: loginMetadata, + } = user.metadata; + const lastAttempt = parseInt(loginMetadata.last, 10) || 0; // See if the login is locked. - if (user.metadata.login.locked) { + if (loginMetadata.locked) { // Get how long they must wait to be locked out. let lockWait = parseInt(this.settings.lockWait, 10) || 1800; @@ -173,13 +172,13 @@ module.exports = function(router) { // See if the time has expired. if ((lastAttempt + lockWait) < now) { // Reset the locked state and attempts totals. - user.metadata.login.attempts = 0; - user.metadata.login.locked = false; - user.metadata.login.last = now; + loginMetadata.attempts = 0; + loginMetadata.locked = false; + loginMetadata.last = now; } else { const howLong = (lastAttempt + lockWait) - now; - return next.call(this, `You must wait ${this.waitText(howLong / 1000)} before you can login.`); + return next(`You must wait ${this.waitText(howLong / 1000)} before you can login.`); } } else if (error) { @@ -191,29 +190,28 @@ module.exports = function(router) { // Determine the login attempts within a certain window. const withinWindow = lastAttempt ? ((lastAttempt + attemptWindow) > now) : false; - if (!withinWindow) { - user.metadata.login.attempts = 0; - user.metadata.login.last = now; - } - else { - let attempts = parseInt(user.metadata.login.attempts, 10) || 0; - attempts++; + if (withinWindow) { + const attempts = (parseInt(loginMetadata.attempts, 10) || 0) + 1; // If they exceeded the login attempts. if (attempts >= allowedAttempts) { const lockWait = parseInt(this.settings.lockWait, 10) || 1800; error = `Maximum Login attempts. Please wait ${this.waitText(lockWait)} before trying again.`; - user.metadata.login.locked = true; + loginMetadata.locked = true; } // Set the login attempts. - user.metadata.login.attempts = attempts; + loginMetadata.attempts = attempts; + } + else { + loginMetadata.attempts = 0; + loginMetadata.last = now; } } else { // If there was no error, then reset the attempts to zero. - user.metadata.login.attempts = 0; - user.metadata.login.last = now; + loginMetadata.attempts = 0; + loginMetadata.last = now; } // Update the user record @@ -221,13 +219,14 @@ module.exports = function(router) { submissionModel.updateOne( {_id: user._id}, {$set: {metadata: user.metadata}}, - function(err) { + (err) => { if (err) { log(req, ecode.auth.ELOGINCOUNT, err); - return next.call(this, ecode.auth.ELOGINCOUNT); + return next(ecode.auth.ELOGINCOUNT); } - next.call(this, error); - }.bind(this) + + next(error); + }, ); } /* eslint-enable max-statements */ @@ -253,24 +252,19 @@ module.exports = function(router) { } if (!this.settings) { + audit('EAUTH_AUTHCONFIG', req, _.get(req.submission.data, this.settings.username)); return res.status(400).send('Misconfigured Login Action.'); } - if (!req.submission || !req.submission.hasOwnProperty('data')) { - return res.status(401).send('User or password was incorrect.'); - } - - // They must provide a username. - if (!_.has(req.submission.data, this.settings.username)) { - return res.status(401).send('User or password was incorrect.'); - } - - // They must provide a password. - if (!_.has(req.submission.data, this.settings.password)) { + if ( + (!req.submission || !req.submission.hasOwnProperty('data')) + || !_.has(req.submission.data, this.settings.username) + || !_.has(req.submission.data, this.settings.password) + ) { + audit('EAUTH_PASSWORD', req, _.get(req.submission.data, this.settings.username)); return res.status(401).send('User or password was incorrect.'); } - // Perform an authentication. router.formio.auth.authenticate( req, this.settings.resources, @@ -278,15 +272,17 @@ module.exports = function(router) { this.settings.password, _.get(req.submission.data, this.settings.username), _.get(req.submission.data, this.settings.password), - function(err, response) { + (err, response) => { if (err && !response) { + audit('EAUTH_NOUSER', req, _.get(req.submission.data, this.settings.username)); log(req, ecode.auth.EAUTH, err); return res.status(401).send(err); } // Check the amount of attempts made by this user. - this.checkAttempts(err, req, response.user, function(error) { + this.checkAttempts(err, req, response.user, (error) => { if (error) { + audit('EAUTH_LOGINCOUNT', req, _.get(req.submission.data, this.settings.username)); log(req, ecode.auth.EAUTH, error); return res.status(401).send(error); } @@ -296,7 +292,7 @@ module.exports = function(router) { req.token = response.token.decoded; res.token = response.token.token; req['x-jwt-token'] = response.token.token; - router.formio.auth.currentUser(req, res, function(err) { + router.formio.auth.currentUser(req, res, (err) => { if (err) { log(req, ecode.auth.EAUTH, err); return res.status(401).send(err.message); @@ -304,8 +300,8 @@ module.exports = function(router) { next(); }); - }.bind(this)); - }.bind(this) + }); + }, ); } } diff --git a/forms-flow-forms/src/actions/ResetPassword.js b/forms-flow-forms/src/actions/ResetPassword.js index 341cfe985a..14755ab37f 100644 --- a/forms-flow-forms/src/actions/ResetPassword.js +++ b/forms-flow-forms/src/actions/ResetPassword.js @@ -6,7 +6,7 @@ const _ = require('lodash'); const LOG_EVENT = 'Reset Password Action'; -module.exports = function(router) { +module.exports = (router) => { const Action = router.formio.Action; const hook = require('../util/hook')(router.formio); const emailer = require('../util/email')(router.formio); @@ -31,14 +31,14 @@ module.exports = function(router) { }, access: { handler: false, - method: false - } + method: false, + }, }); } static settingsForm(req, res, next) { // Get the available email transports. - emailer.availableTransports(req, function(err, availableTransports) { + emailer.availableTransports(req, (err, availableTransports) => { if (err) { log(req, ecode.emailer.ENOTRANSP, err); return next(err); @@ -61,8 +61,8 @@ module.exports = function(router) { template: '{{ item.title }}', multiple: true, validate: { - required: true - } + required: true, + }, }, { type: 'select', @@ -76,8 +76,8 @@ module.exports = function(router) { valueProperty: 'key', multiple: false, validate: { - required: true - } + required: true, + }, }, { type: 'select', @@ -91,8 +91,8 @@ module.exports = function(router) { valueProperty: 'key', multiple: false, validate: { - required: true - } + required: true, + }, }, { label: 'Reset Link URL', @@ -104,8 +104,8 @@ module.exports = function(router) { type: 'textfield', multiple: false, validate: { - required: true - } + required: true, + }, }, { label: 'Reset Password Button Label', @@ -115,7 +115,7 @@ module.exports = function(router) { input: true, placeholder: '', type: 'textfield', - multiple: false + multiple: false, }, { type: 'select', @@ -127,13 +127,13 @@ module.exports = function(router) { defaultValue: 'default', dataSrc: 'json', data: { - json: JSON.stringify(availableTransports) + json: JSON.stringify(availableTransports), }, valueProperty: 'transport', multiple: false, validate: { - required: true - } + required: true, + }, }, { label: 'From:', @@ -142,10 +142,8 @@ module.exports = function(router) { defaultValue: 'no-reply@form.io', input: true, placeholder: 'Send the email from the following address', - prefix: '', - suffix: '', type: 'textfield', - multiple: false + multiple: false, }, { label: 'Subject', @@ -155,7 +153,7 @@ module.exports = function(router) { input: true, placeholder: '', type: 'textfield', - multiple: false + multiple: false, }, { label: 'Message', @@ -168,8 +166,8 @@ module.exports = function(router) { suffix: '', prefix: '', placeholder: '', - input: true - } + input: true, + }, ]); }); } @@ -192,7 +190,7 @@ module.exports = function(router) { // Create the query. const query = { - deleted: {$eq: null} + deleted: {$eq: null}, }; query[usernamekey] = {$regex: new RegExp(`^${util.escapeRegExp(token.username)}$`), $options: 'i'}; @@ -200,15 +198,15 @@ module.exports = function(router) { // Perform a mongo query to find the submission. const submissionModel = req.submissionModel || router.formio.resources.submission.model; - submissionModel.findOne(hook.alter('submissionQuery', query, req), function(err, submission) { + submissionModel.findOne(hook.alter('submissionQuery', query, req), (err, submission) => { if (err || !submission) { log(req, ecode.submission.ENOSUB, err); - return next.call(this, ecode.submission.ENOSUB); + return next(ecode.submission.ENOSUB); } // Submission found. - next.call(this, null, submission); - }.bind(this)); + next(null, submission); + }); } /** @@ -221,45 +219,46 @@ module.exports = function(router) { */ updatePassword(req, token, password, next) { // Get the submission. - this.getSubmission(req, token, function(err, submission) { + this.getSubmission(req, token, (err, submission) => { // Make sure we found the user. if (err || !submission) { log(req, ecode.user.ENOUSER, err); - return next.call(this, ecode.user.ENOUSER); + return next(ecode.user.ENOUSER); } // Get the name of the password field. if (!this.settings.password) { log(req, ecode.auth.EPASSFIELD, new Error(ecode.auth.EPASSFIELD)); - return next.call(this, ecode.auth.EPASSFIELD); + return next(ecode.auth.EPASSFIELD); } // Manually encrypt and update the password. - router.formio.encrypt(password, function(err, hash) { + router.formio.encrypt(password, (err, hash) => { if (err) { log(req, ecode.auth.EPASSRESET, err); - return next.call(this, ecode.auth.EPASSRESET); + return next(ecode.auth.EPASSRESET); } - const setValue = {}; - setValue[`data.${this.settings.password}`] = hash; + const setValue = { + [`data.${this.settings.password}`]: hash, + }; // Update the password. const submissionModel = req.submissionModel || router.formio.resources.submission.model; submissionModel.updateOne( {_id: submission._id}, {$set: setValue}, - function(err, newSub) { + (err, newSub) => { if (err) { log(req, ecode.auth.EPASSRESET, err); - return next.call(this, ecode.auth.EPASSRESET); + return next(ecode.auth.EPASSRESET); } // The submission was saved! - next.call(this, null, submission); - }.bind(this) + next(null, submission); + }, ); - }.bind(this)); + }); }); } @@ -268,7 +267,7 @@ module.exports = function(router) { */ initialize(method, req, res, next) { // See if we have a reset password token. - const hasResetToken = !!(req.tempToken && (req.tempToken.type === 'resetpass')); + const hasResetToken = Boolean(req.tempToken && (req.tempToken.type === 'resetpass')); if (!hasResetToken && (method === 'create')) { // Figure out the username data. const username = _.get(req.body.data, this.settings.username); @@ -281,44 +280,59 @@ module.exports = function(router) { // Create a token. const token = { - username: username, + username, form: req.formId, resources: this.settings.resources, temp: true, - type: 'resetpass' + type: 'resetpass', }; - // Look up the user. - this.getSubmission(req, token, function(err, submission) { - if (err || !submission) { - log(req, ecode.user.ENOUSER, err); - return res.status(400).send(ecode.user.ENOUSER); + // Load the form for this request. + router.formio.cache.loadCurrentForm(req, (err, form) => { + if (err) { + log(req, ecode.cache.EFORMLOAD, err); + return next(err); } - // Generate a temporary token for resetting their password. - const resetToken = jwt.sign(token, router.formio.config.jwt.secret, { - expiresIn: 5 * 60 - }); + // Look up the user. + this.getSubmission(req, token, (err, submission) => { + if (err || !submission) { + log(req, ecode.user.ENOUSER, err); + return res.status(400).send(ecode.user.ENOUSER); + } - // Create the reset link and add it to the email parameters. - const params = { - resetlink: `${this.settings.url}?x-jwt-token=${resetToken}` - }; + // Generate a temporary token for resetting their password. + const resetToken = jwt.sign(token, router.formio.config.jwt.secret, { + expiresIn: 5 * 60, + }); - // Now send them an email. - emailer.send(req, res, { - transport: this.settings.transport, - from: this.settings.from, - emails: username, - subject: this.settings.subject, - message: this.settings.message - }, _.assign(params, req.body), function(err) { - if (err) { - log(req, ecode.emailer.ESENDMAIL, err); - } - // Let them know an email is on its way. - res.status(200).json({ - message: 'Password reset email was sent.' + // Create the reset link and add it to the email parameters. + const params = { + resetlink: `${this.settings.url}?x-jwt-token=${resetToken}`, + }; + + const { + transport, + from, + subject, + message, + } = this.settings; + + // Now send them an email. + emailer.send(req, res, { + transport, + from, + emails: username, + subject, + message, + }, _.assign(params, req.body, {form}), (err) => { + if (err) { + log(req, ecode.emailer.ESENDMAIL, err); + } + // Let them know an email is on its way. + res.status(200).json({ + message: 'Password reset email was sent.', + }); }); }); }); @@ -349,7 +363,7 @@ module.exports = function(router) { */ resolve(handler, method, req, res, next) { // See if we have a reset password token. - const hasResetToken = !!(req.tempToken && (req.tempToken.type === 'resetpass')); + const hasResetToken = Boolean(req.tempToken && (req.tempToken.type === 'resetpass')); // Only show the reset password username field on form get. if ( @@ -361,7 +375,7 @@ module.exports = function(router) { res.resource.item._id ) { // Modify the form based on if there is a reset token or not. - util.eachComponent(res.resource.item.components, function(component) { + util.eachComponent(res.resource.item.components, (component) => { if ( !hasResetToken && (component.type === 'button') && @@ -378,7 +392,7 @@ module.exports = function(router) { component.validate.required = false; } } - }.bind(this)); + }); return next(); } @@ -401,7 +415,7 @@ module.exports = function(router) { const password = _.get(req.submission.data, this.settings.password); if (!password) { debug(ecode.auth.ENOPASSP); - return next.call(this, ecode.auth.ENOPASSP); + return next(ecode.auth.ENOPASSP); } // Update the password. @@ -411,7 +425,7 @@ module.exports = function(router) { return res.status(400).send('Unable to update the password. Please try again.'); } res.status(200).send({ - message: 'Password was successfully updated.' + message: 'Password was successfully updated.', }); }); } @@ -423,7 +437,7 @@ module.exports = function(router) { ResetPasswordAction.access = { handler: false, - method: false + method: false, }; // Return the ResetPasswordAction. diff --git a/forms-flow-forms/src/actions/SaveSubmission.js b/forms-flow-forms/src/actions/SaveSubmission.js index 1c383db056..7221e0b161 100644 --- a/forms-flow-forms/src/actions/SaveSubmission.js +++ b/forms-flow-forms/src/actions/SaveSubmission.js @@ -2,6 +2,7 @@ const _ = require('lodash'); const async = require('async'); +const vm = require('vm'); const util = require('../util/util'); const LOG_EVENT = 'Save Submission Action'; @@ -203,10 +204,26 @@ module.exports = function(router) { // Iterate over all the available fields. _.each(this.settings.fields, function(field, key) { - if (req.body.data.hasOwnProperty(field)) { - submission.data[key] = req.body.data[field]; + if (field === 'data') { + _.set(submission.data, key, req.body.data); + } + else if (_.has(req.body.data, field)) { + _.set(submission.data, key, _.get(req.body.data, field)); } }); + + if (this.settings.transform) { + const script = new vm.Script(this.settings.transform); + const sandbox = { + submission: (res.resource && res.resource.item) ? res.resource.item : req.body, + data: submission.data, + }; + script.runInContext(vm.createContext(sandbox), { + timeout: 500 + }); + submission.data = sandbox.data; + } + return submission; }.bind(this); diff --git a/forms-flow-forms/src/actions/WebhookAction.js b/forms-flow-forms/src/actions/WebhookAction.js index b15a268c0d..106f97eb98 100644 --- a/forms-flow-forms/src/actions/WebhookAction.js +++ b/forms-flow-forms/src/actions/WebhookAction.js @@ -204,6 +204,7 @@ module.exports = function(router) { case 'post': rest.postJson(url, payload, options).on('success', handleSuccess).on('fail', handleError); break; + case 'patch': case 'put': rest.putJson(url, payload, options).on('success', handleSuccess).on('fail', handleError); break; diff --git a/forms-flow-forms/src/actions/actions.js b/forms-flow-forms/src/actions/actions.js index f74802f00e..6511fdef0f 100644 --- a/forms-flow-forms/src/actions/actions.js +++ b/forms-flow-forms/src/actions/actions.js @@ -33,12 +33,12 @@ module.exports = (router) => { */ actions: hook.alter('actions', { email: require('./EmailAction')(router), - webhook: require('./WebhookAction')(router), - sql: require('./SQLAction')(router), - role: require('./RoleAction')(router), + login: require('./LoginAction')(router), resetpass: require('./ResetPassword')(router), + role: require('./RoleAction')(router), save: require('./SaveSubmission')(router), - login: require('./LoginAction')(router) + sql: require('./SQLAction')(router), + webhook: require('./WebhookAction')(router), }), /** @@ -179,7 +179,6 @@ module.exports = (router) => { async.eachSeries(actions, (action, cb) => { this.shouldExecute(action, req).then(execute => { - debug.action(`execute (${execute}):`, action); if (!execute) { return cb(); } @@ -204,25 +203,24 @@ module.exports = (router) => { } ] }, req), (err, actionItem) => { + // Mongoose has issues if you call "save" too frequently on the same item. We need to wait till the previous + // save is complete before calling again. + if (!req.actionItemPromise) { + req.actionItemPromise = Promise.resolve(); + } const setActionItemMessage = (message, data = {}, state = null) => { - actionItem.messages.push({ - datetime: new Date(), - info: message, - data - }); - - if (state) { - actionItem.state = state; - } - - // Update the action item. - router.formio.mongoose.models.actionItem.updateOne({ - _id: actionItem._id - }, { - $set: { - state: actionItem.state, - messages: actionItem.messages + req.actionItemPromise = req.actionItemPromise.then(() => { + actionItem.messages.push({ + datetime: new Date(), + info: message, + data + }); + + if (state) { + actionItem.state = state; } + + return actionItem.save(); }); }; diff --git a/forms-flow-forms/src/actions/fields/datetime.js b/forms-flow-forms/src/actions/fields/datetime.js index aab0390972..e889142de7 100644 --- a/forms-flow-forms/src/actions/fields/datetime.js +++ b/forms-flow-forms/src/actions/fields/datetime.js @@ -1,7 +1,7 @@ 'use strict'; const _ = require('lodash'); -module.exports = formio => async (component, data, handler, action, {validation}) => { +module.exports = (formio) => async (component, data, handler, action, {validation}) => { // Only perform before validation has occurred. if (validation && ['put', 'post', 'patch'].includes(action)) { let value = _.get(data, component.key); diff --git a/forms-flow-forms/src/actions/fields/email.js b/forms-flow-forms/src/actions/fields/email.js index a83b85c530..a393c2eaa8 100644 --- a/forms-flow-forms/src/actions/fields/email.js +++ b/forms-flow-forms/src/actions/fields/email.js @@ -1,7 +1,8 @@ 'use strict'; -module.exports = formio => { +module.exports = (formio) => { const hook = require('../../util/hook')(formio); + return async (component, data, handler, action, {validation, path, req, res}) => { // Only perform before validation has occurred. if (validation) { diff --git a/forms-flow-forms/src/actions/fields/form.js b/forms-flow-forms/src/actions/fields/form.js index 216adf3652..83cd7d70c3 100644 --- a/forms-flow-forms/src/actions/fields/form.js +++ b/forms-flow-forms/src/actions/fields/form.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const util = require('../../util/util'); -module.exports = router => { +module.exports = (router) => { /** * Perform hierarchial submissions of sub-forms. */ @@ -14,7 +14,9 @@ module.exports = router => { } // Get the submission object. - const subSubmission = _.get(data, component.key, {}); + let subSubmission = _.get(data, component.key, {}); + const id = subSubmission._id ? subSubmission._id.toString() : null; + subSubmission = _.cloneDeep(_.get(req, `resources.${id}`, subSubmission)); // if there isn't a sub-submission or the sub-submission has an _id, don't submit. // Should be submitted from the frontend. @@ -35,7 +37,7 @@ module.exports = router => { } let url = '/form/:formId/submission'; - if (req.method === 'PUT') { + if (['PUT', 'PATCH'].includes(req.method)) { url += '/:submissionId'; } const childRes = router.formio.util.createSubResponse((err) => { @@ -45,6 +47,10 @@ module.exports = router => { _.each(err.details, (details) => { if (details.path) { details.path = `${path}.data.${details.path}`; + details.path = details.path.replace(/[[\]]/g, '.') + .replace(/\.\./g, '.') + .split('.') + .map(part => _.defaultTo(_.toNumber(part), part)); } }); } @@ -70,13 +76,26 @@ module.exports = router => { // Make the child request. const method = (req.method === 'POST') ? 'post' : 'put'; + + if (req.method === 'PATCH') { + childReq.subPatch = true; + } + router.resourcejs[url][method](childReq, childRes, function(err) { if (err) { return next(err); } - if (childRes.resource && childRes.resource.item) { - _.set(data, component.key, childRes.resource.item); + if (!req.query.dryrun) { + if (childRes.resource && childRes.resource.item && childRes.resource.item._id) { + // Set resources to return full submission on response + if (!req.resources) { + req.resources = {}; + } + req.resources[childRes.resource.item._id.toString()] = childRes.resource.item; + // Set child submission to { _id } to save only the reference + _.set(data, component.key, {_id: childRes.resource.item._id}); + } } next(); }); diff --git a/forms-flow-forms/src/actions/fields/index.js b/forms-flow-forms/src/actions/fields/index.js index a232879bda..4e062c0c80 100644 --- a/forms-flow-forms/src/actions/fields/index.js +++ b/forms-flow-forms/src/actions/fields/index.js @@ -6,4 +6,6 @@ module.exports = (router) => ({ form: require('./form')(router), email: require('./email')(router.formio), datetime: require('./datetime')(router.formio), + select: require('./select')(router.formio), + radio: require('./radio')(router.formio), }); diff --git a/forms-flow-forms/src/actions/fields/password.js b/forms-flow-forms/src/actions/fields/password.js index 5b4f1ad740..89470654de 100644 --- a/forms-flow-forms/src/actions/fields/password.js +++ b/forms-flow-forms/src/actions/fields/password.js @@ -3,8 +3,10 @@ const _ = require('lodash'); module.exports = (formio) => { - const encryptField =(component, data, next) => { - formio.encrypt(_.get(data, component.key), function encryptResults(err, hash) { + const hook = require('../../util/hook')(formio); + + const encryptField = (component, data, next) => { + formio.encrypt(_.get(data, component.key), (err, hash) => { if (err) { return next(err); } @@ -14,7 +16,7 @@ module.exports = (formio) => { }); }; - return async (component, data, handler, action, {validation, path, req}) => { + return async (component, data, handler, action, {validation, path, req, res}) => { switch (handler) { case 'beforeGet': req.modelQuery.select(`-data.${path}${component.key}`); @@ -28,11 +30,18 @@ module.exports = (formio) => { if (err) { return reject(err); } - return resolve(); + // Since the password was changed, invalidate all user tokens. + hook.alter('invalidateTokens', req, res, (err) => { + if (err) { + return reject(err); + } + + if (!req.skipTokensInvalidation) { + _.set(req.body, 'metadata.jwtIssuedAfter', req.tokenIssued || (Date.now() / 1000)); + } + resolve(); + }); }); - // Since the password was changed, invalidate all user tokens. - req.body.metadata = req.body.metadata || {}; - req.body.metadata.jwtIssuedAfter = req.tokenIssued || (Date.now() / 1000); } else { // If there is no password provided. diff --git a/forms-flow-forms/src/actions/fields/radio.js b/forms-flow-forms/src/actions/fields/radio.js new file mode 100644 index 0000000000..f5ef499cb0 --- /dev/null +++ b/forms-flow-forms/src/actions/fields/radio.js @@ -0,0 +1,18 @@ +'use strict'; +const _ = require('lodash'); + +module.exports = (formio) => async (component, data, handler, action, {validation}) => { + // Only perform before validation has occurred. + if (!validation && ['put', 'post', 'patch'].includes(action)) { + const value = _.get(data, component.key); + + if (value) { + if (component.validate) { + component.validate.onlyAvailableItems = true; + } + else { + component.validate = {onlyAvailableItems: true}; + } + } + } +}; diff --git a/forms-flow-forms/src/actions/fields/select.js b/forms-flow-forms/src/actions/fields/select.js new file mode 100644 index 0000000000..6495583f44 --- /dev/null +++ b/forms-flow-forms/src/actions/fields/select.js @@ -0,0 +1,19 @@ +'use strict'; +const _ = require('lodash'); + +module.exports = (formio) => async (component, data, handler, action, {validation}) => { + // Only perform before validation has occurred. + if (!validation && ['put', 'post', 'patch'].includes(action)) { + const value = _.get(data, component.key); + const {dataSrc} = component; + + if (value && (dataSrc === 'json' || dataSrc === 'values' || dataSrc === 'custom' || !dataSrc)) { + if (component.validate) { + component.validate.onlyAvailableItems = true; + } + else { + component.validate = {onlyAvailableItems: true}; + } + } + } +}; diff --git a/forms-flow-forms/src/actions/fields/signature.js b/forms-flow-forms/src/actions/fields/signature.js index a6407755af..5201bcab31 100644 --- a/forms-flow-forms/src/actions/fields/signature.js +++ b/forms-flow-forms/src/actions/fields/signature.js @@ -2,52 +2,50 @@ const _ = require('lodash'); -module.exports = (formio) => { - return async (component, data, handler, action, {validation, path, req, res}) => { - let value = _.get(data, component.key); - switch (handler) { - case 'beforePost': - if (!req.body.data) { - return; - } +module.exports = (formio) => async (component, data, handler, action, {validation, path, req, res}) => { + let value = _.get(data, component.key); + switch (handler) { + case 'beforePost': + if (!req.body.data) { + return; + } - // Coerse the value into an empty string. - if (!value && value !== '') { - _.set(data, component.key, ''); - } - break; - case 'beforePut': - if (!req.body.data) { - return; - } + // Coerse the value into an empty string. + if (!value && value !== '') { + _.set(data, component.key, ''); + } + break; + case 'beforePut': + if (!req.body.data) { + return; + } - // Ensure that signatures are not ever wiped out with a PUT request - // of data that came from the index request (where the signature is not populated). + // Ensure that signatures are not ever wiped out with a PUT request + // of data that came from the index request (where the signature is not populated). - // Coerse the value into an empty string. - if (!value && (value !== '')) { - value = ''; - _.set(data, component.key, ''); - } + // Coerse the value into an empty string. + if (!value && (value !== '')) { + value = ''; + _.set(data, component.key, ''); + } - if ( - (typeof value !== 'string') || - ((value !== '') && (value.substr(0, 5) !== 'data:')) - ) { - return new Promise((resolve, reject) => { - formio.cache.loadCurrentSubmission(req, (err, submission) => { - if (err) { - return reject(err); - } - if (!submission) { - return reject(new Error('No submission found.')); - } + if ( + (typeof value !== 'string') || + ((value !== '') && (value.substr(0, 5) !== 'data:')) + ) { + return new Promise((resolve, reject) => { + formio.cache.loadCurrentSubmission(req, (err, submission) => { + if (err) { + return reject(err); + } + if (!submission) { + return reject(new Error('No submission found.')); + } - _.set(data, component.key, _.get(submission.data, path)); - return resolve(); - }); + _.set(data, component.key, _.get(submission.data, path)); + return resolve(); }); - } - } - }; + }); + } + } }; diff --git a/forms-flow-forms/src/actions/properties/reference.js b/forms-flow-forms/src/actions/properties/reference.js index f640f5ebcb..5158f4cd33 100644 --- a/forms-flow-forms/src/actions/properties/reference.js +++ b/forms-flow-forms/src/actions/properties/reference.js @@ -3,7 +3,7 @@ const _ = require('lodash'); const util = require('../../util/util'); const async = require('async'); -module.exports = router => { +module.exports = (router) => { const hiddenFields = ['deleted', '__v', 'machineName']; // Get a subrequest and sub response for a nested request. diff --git a/forms-flow-forms/src/actions/properties/setDefaultProperties.js b/forms-flow-forms/src/actions/properties/setDefaultProperties.js new file mode 100644 index 0000000000..2097e13371 --- /dev/null +++ b/forms-flow-forms/src/actions/properties/setDefaultProperties.js @@ -0,0 +1,9 @@ +'use strict'; + +module.exports = { + reference: (component) => { + if (component.type === 'form') { + component.reference = true; + } + }, +}; diff --git a/forms-flow-forms/src/authentication/index.js b/forms-flow-forms/src/authentication/index.js index 5467e12719..43b101d843 100644 --- a/forms-flow-forms/src/authentication/index.js +++ b/forms-flow-forms/src/authentication/index.js @@ -13,11 +13,15 @@ const jwt = require('jsonwebtoken'); const util = require('../util/util'); const _ = require('lodash'); const debug = { - authenticate: require('debug')('formio:authentication:authenticate') + authenticate: require('debug')('formio:authentication:authenticate'), }; -module.exports = function(router) { +module.exports = (router) => { + const audit = router.formio.audit || (() => {}); const hook = require('../util/hook')(router.formio); + const { + jwt: jwtConfig, + } = router.formio.config; /** * Generate our JWT with the given payload, and pass it to the given callback function. @@ -28,14 +32,19 @@ module.exports = function(router) { * @return {String|Boolean} * The JWT from the given payload, or false if the jwt payload is still valid. */ - const getToken = function(tokenInfo) { + const getToken = (tokenInfo) => { // Clone to make sure we don't change original. const payload = Object.assign({}, tokenInfo); delete payload.iat; delete payload.exp; - return jwt.sign(payload, router.formio.config.jwt.secret, { - expiresIn: router.formio.config.jwt.expireTime * 60 + const { + expireTime, + secret, + } = jwtConfig; + + return jwt.sign(payload, secret, { + expiresIn: expireTime * 60, }); }; @@ -45,48 +54,47 @@ module.exports = function(router) { * @param decoded * @return {boolean} */ - const isTokenAllowed = function(req, decoded) { + const isTokenAllowed = (req, decoded) => { if (!decoded.allow) { return true; } - let isAllowed = false; const allowed = decoded.allow.split(','); - const urlParts = req.url.split('?'); - _.each(allowed, function(allow) { - const parts = allow.split(':'); - if (parts.length < 2) { - return; + + // If no project is provided, then provide it from the token. + if (decoded.isAdmin && !decoded.project && allowed.length) { + /* eslint-disable no-useless-escape */ + const ids = allowed[0].match(/\/project\/([^/]+)/); + /* eslint-enable no-useless-escape */ + if (ids && ids[1]) { + decoded.project = {_id: ids[1]}; + } + } + + const [urlPath] = req.url.split('?'); + return allowed.some((allow) => { + const [allowedMethod, allowedPath] = allow.split(':'); + if (!allowedMethod || !allowedPath) { + return false; } // Make sure the methods match. - if (req.method.toUpperCase() !== parts[0].toUpperCase()) { - return; + if (req.method.toUpperCase() !== allowedMethod.toUpperCase()) { + return false; } try { - const regex = new RegExp(parts[1]); - if (regex.test(req.baseUrl + urlParts[0])) { - isAllowed = true; - return false; + const regex = new RegExp(allowedPath); + if (regex.test(`${req.baseUrl}${urlPath}`)) { + return true; } } catch (err) { debug.authenticate('Bad token allow string.'); } - }); - - // If no project is provided, then provide it from the token. - if (decoded.isAdmin && allowed.length) { - /* eslint-disable no-useless-escape */ - const ids = allowed[0].match(/\/project\/([^\/]+)/); - /* eslint-enable no-useless-escape */ - if (ids && !decoded.project && ids[1]) { - decoded.project = {_id: ids[1]}; - } - } - return isAllowed; + return false; + }); }; /** @@ -97,14 +105,14 @@ module.exports = function(router) { * @param expire * @param cb */ - const getTempToken = function(req, res, allow, expire, admin, cb) { - let tempToken = {}; + const getTempToken = (req, res, allow, expire, admin, cb) => { + const tempToken = {}; if (!admin) { if (!req.token) { return cb('No authentication token provided.'); } - tempToken = _.cloneDeep(req.token); + _.merge(tempToken, req.token); // Check to see if this path is allowed. if (!isTokenAllowed(req, tempToken)) { @@ -118,9 +126,8 @@ module.exports = function(router) { } // Check the expiration. - const now = Math.round((new Date()).getTime() / 1000); - expire = expire || 3600; - expire = parseInt(expire, 10); + const now = Math.trunc((new Date()) / 1000); + expire = parseInt(expire || 3600, 10); const timeLeft = (parseInt(tempToken.exp, 10) - now); // Ensure they are not trying to create an extended expiration. @@ -140,14 +147,15 @@ module.exports = function(router) { delete tempToken.exp; // Sign the token. - jwt.sign(tempToken, router.formio.config.jwt.secret, { - expiresIn: expire + jwt.sign(tempToken, jwtConfig.secret, { + expiresIn: expire, }, (err, token) => { if (err) { return cb(err); } + const tokenResponse = { - token + token, }; // Allow other libraries to hook into the response. @@ -160,7 +168,7 @@ module.exports = function(router) { }); }; - const tempToken = function(req, res, next) { + const tempToken = (req, res) => { if (!req.headers) { return res.status(400).send('No headers provided.'); } @@ -174,12 +182,9 @@ module.exports = function(router) { } const allow = req.headers['x-allow']; - let expire = req.headers['x-expire']; - expire = expire || 3600; - expire = parseInt(expire, 10); + const expire = parseInt(req.headers['x-expire'] || 3600, 10); - // get a temporary token. - getTempToken(req, res, allow, expire, adminKey, function(err, tempToken) { + getTempToken(req, res, allow, expire, adminKey, (err, tempToken) => { if (err) { return res.status(400).send(err); } @@ -203,12 +208,14 @@ module.exports = function(router) { * @param next {Function} * The callback function to call after authentication. */ - const authenticate = function(req, forms, userField, passField, username, password, next) { + const authenticate = (req, forms, userField, passField, username, password, next) => { // Make sure they have provided a username and password. if (!username) { + audit('EAUTH_EMPTYUN', req); return next('Missing username'); } if (!password) { + audit('EAUTH_EMPTYPW', req, username); return next('Missing password'); } @@ -238,33 +245,53 @@ module.exports = function(router) { return next('User or password was incorrect'); } - if (!_.get(user.data, passField)) { + const hash = _.get(user.data, passField); + if (!hash) { + audit('EAUTH_BLANKPW', { + ...req, + userId: user._id, + }, username); return next('Your account does not have a password. You must reset your password to login.'); } // Compare the provided password. - bcrypt.compare(password, _.get(user.data, passField), function(err, value) { + bcrypt.compare(password, hash, (err, value) => { if (err) { + audit('EAUTH_BCRYPT', { + ...req, + userId: user._id + }, username, err); return next(err); } + if (!value) { - return next('User or password was incorrect', {user: user}); + audit('EAUTH_PASSWORD', req, user._id, username); + return next('User or password was incorrect', {user}); } // Load the form associated with this user record. router.formio.resources.form.model.findOne({ _id: user.form, - deleted: {$eq: null} + deleted: {$eq: null}, }).lean().exec((err, form) => { if (err) { + audit('EAUTH_USERFORM', { + ...req, + userId: user._id, + }, user.form, err); return next(err); } + if (!form) { + audit('EAUTH_USERFORM', { + ...req, + userId: user._id, + }, user.form, {message: 'User form not found'}); return next('User form not found.'); } // Allow anyone to hook and modify the user. - hook.alter('user', user, function hookUserCallback(err, _user) { + hook.alter('user', user, (err, _user) => { if (err) { // Attempt to fail safely and not update the user reference. debug.authenticate(err); @@ -275,23 +302,31 @@ module.exports = function(router) { user = _user; } - // Allow anyone to hook and modify the token. - const token = hook.alter('token', { - user: { - _id: user._id - }, - form: { - _id: form._id - } - }, form); - - // Continue with the token data. - next(null, { - user: user, - token: { - token: getToken(token), - decoded: token + hook.alter('login', user, req, (err) => { + if (err) { + return next(err); } + + // Allow anyone to hook and modify the token. + const token = hook.alter('token', { + user: { + _id: user._id, + }, + form: { + _id: form._id, + }, + }, form, req); + + hook.alter('tokenDecode', token, req, (err, decoded) => { + // Continue with the token data. + next(err, { + user, + token: { + token: getToken(token), + decoded, + }, + }); + }); }); }); }); @@ -306,7 +341,7 @@ module.exports = function(router) { * @param res * @param next */ - const currentUser = function(req, res, next) { + const currentUser = (req, res, next) => { if (!res.token || !req.token) { return res.sendStatus(401); } @@ -318,27 +353,27 @@ module.exports = function(router) { } // Duplicate the current request get the users information. - const url = '/form/:formId/submission/:submissionId'; const childReq = util.createSubRequest(req); - childReq.permissionsChecked = true; if (!childReq) { return res.status(400).send('Too many recursive requests.'); } + childReq.permissionsChecked = true; childReq.method = 'GET'; childReq.skipResource = false; // If this request is not directly accessing /current, allow the response to be sent. - if (req.url.split('?').shift() !== '/current') { + if (req.url.split('?')[0] !== '/current') { childReq.noResponse = true; } // Update the parameters to use from the decoded token. childReq.params = hook.alter('submissionRequestTokenQuery', { formId: req.token.form._id, - submissionId: req.token.user._id + submissionId: req.token.user._id, }, req.token); // Execute the resourcejs methods associated with the user submissions. + const url = '/form/:formId/submission/:submissionId'; router.resourcejs[url].get.call(this, childReq, res, next); }; @@ -346,15 +381,15 @@ module.exports = function(router) { * Return the public methods. */ return { - getToken: getToken, - isTokenAllowed: isTokenAllowed, - getTempToken: getTempToken, - authenticate: authenticate, - currentUser: currentUser, - tempToken: tempToken, + getToken, + isTokenAllowed, + getTempToken, + authenticate, + currentUser, + tempToken, logout(req, res) { res.setHeader('x-jwt-token', ''); res.sendStatus(200); - } + }, }; }; diff --git a/forms-flow-forms/src/cache/cache.js b/forms-flow-forms/src/cache/cache.js index c4e7b9aa26..78b389d834 100644 --- a/forms-flow-forms/src/cache/cache.js +++ b/forms-flow-forms/src/cache/cache.js @@ -9,6 +9,7 @@ const debug = { loadFormByAlias: require('debug')('formio:cache:loadFormByAlias'), loadSubmission: require('debug')('formio:cache:loadSubmission'), loadSubmissions: require('debug')('formio:cache:loadSubmissions'), + loadSubForms: require('debug')('formio:cache:loadSubForms'), error: require('debug')('formio:error') }; @@ -138,34 +139,44 @@ module.exports = function(router) { loadFormRevisions(req, revs, cb) { if (!revs || !revs.length || !router.formio.resources.formrevision) { + debug.loadSubForms(`Form revisions not used.`); return cb(); } + const formRevs = {}; async.each(revs, (rev, next) => { + const formRevision = parseInt(rev.revision || rev.formRevision); + debug.loadSubForms(`Loading form ${util.idToBson(rev.form)} revision ${formRevision}`); router.formio.resources.formrevision.model.findOne( hook.alter('formQuery', { _rid: util.idToBson(rev.form), - _vid: rev.formRevision, + _vid: formRevision, deleted: {$eq: null} }, req) ).lean().exec((err, result) => { if (err) { + debug.loadSubForms(err); return next(err); } if (!result) { + debug.loadSubForms( + `Cannot find form revision for form ${rev.form} revision ${formRevision}`, + ); return next(); } - rev.components = result.components; + debug.loadSubForms(`Loaded revision for form ${rev.form} revision ${formRevision}`); + formRevs[rev.form.toString()] = result; next(); }); }, (err) => { if (err) { + debug.loadSubForms(err); debug.loadFormRevisions(err); return cb(err); } - cb(); + cb(null, formRevs); }); }, @@ -174,7 +185,7 @@ module.exports = function(router) { if (req.params.formId) { formId = req.params.formId; } - else if (req.body.data.formId) { + else if (req.body.data && req.body.data.formId) { formId = req.body.data.formId; } else if (req.query.formId) { @@ -184,6 +195,7 @@ module.exports = function(router) { return ''; } req.formId = formId; + req.params.formId = formId; return formId; }, @@ -394,9 +406,10 @@ module.exports = function(router) { * @param depth * @returns {*} */ - loadSubForms(form, req, next, depth, forms) { + loadAllForms(form, req, next, depth, forms) { depth = depth || 0; forms = forms || {}; + debug.loadSubForms(`Loading subforms for ${form._id}`); // Only allow 5 deep. if (depth >= 5) { @@ -404,20 +417,17 @@ module.exports = function(router) { } // Get all of the form components. - const comps = {}; const formIds = []; const formRevs = []; util.eachComponent(form.components, function(component) { if ((component.type === 'form') && component.form) { const formId = component.form.toString(); - if (!comps[formId]) { - comps[formId] = []; - formIds.push(formId); - if (component.formRevision) { - formRevs.push(component); - } + formIds.push(formId); + debug.loadSubForms(`Found subform ${formId}`); + // TO-DO: Figure out why there are two revisions here?... + if (component.revision || component.formRevision) { + formRevs.push(component); } - comps[formId].push(component); } }, true); @@ -427,34 +437,56 @@ module.exports = function(router) { } // Load all subforms in this form. + debug.loadSubForms(`Loading subforms ${formIds.join(', ')}`); this.loadForms(req, formIds, (err, result) => { if (err) { return next(); } // Load all form revisions. - this.loadFormRevisions(req, formRevs, () => { + this.loadFormRevisions(req, formRevs, (err, revs) => { + if (err) { + return next(); + } + // Iterate through all subforms. + revs = revs || {}; async.each(result, (subForm, done) => { const formId = subForm._id.toString(); - if (!comps[formId]) { - return done(); - } - comps[formId].forEach((comp) => { - if (!comp.components || !comp.components.length) { - comp.components = subForm.components; - } - }); if (forms[formId]) { + debug.loadSubForms(`Subforms already loaded for ${formId}.`); return done(); } - forms[formId] = true; - this.loadSubForms(subForm, req, done, depth + 1, forms); + forms[formId] = revs[formId] ? revs[formId] : subForm; + this.loadAllForms(subForm, req, done, depth + 1, forms); }, next); }); }); }, + setFormComponents(components, forms) { + util.eachComponent(components, (component) => { + if ((component.type === 'form') && component.form) { + const formId = component.form.toString(); + if (forms[formId]) { + component.components = forms[formId].components; + this.setFormComponents(component.components, forms); + } + } + }, true); + }, + + loadSubForms(form, req, next) { + const forms = {}; + this.loadAllForms(form, req, (err) => { + if (err) { + return next(err); + } + this.setFormComponents(form.components, forms); + next(null, form); + }, 0, forms); + }, + /** * Loads sub submissions from a nested subform hierarchy. * diff --git a/forms-flow-forms/src/db/index.js b/forms-flow-forms/src/db/index.js index e9766b9e42..9c1ebcd2fe 100644 --- a/forms-flow-forms/src/db/index.js +++ b/forms-flow-forms/src/db/index.js @@ -3,7 +3,7 @@ const async = require('async'); const MongoClient = require('mongodb').MongoClient; const semver = require('semver'); -const request = require('request'); +const fetch = require('../util/fetch'); const _ = require('lodash'); const fs = require('fs'); const debug = { @@ -93,39 +93,116 @@ module.exports = function(formio) { } ); }; - /** * Fetch the SA certificate. * * @param next * @return {*} */ - const getSA = function(next) { - if (!config.mongoSA) { + const getCA = function(next) { + // Handle reverse compatability. + const CA = config.mongoSA || config.mongoCA; + if (!CA) { return next(); } // If they provide the SA url, then fetch it from there. - if (config.mongoSA.indexOf('http') === 0) { - debug.db(`Fetching SA Certificate ${config.mongoSA}`); - request.get(config.mongoSA, (err, response, body) => { - if (err || !body) { - debug.db(`Unable to fetch SA Certificate: ${err}`); - unlock(function() { - throw new Error(`Unable to fetch the SA Certificate: ${config.mongoSA}.`); + if (CA.indexOf('http') === 0) { + debug.db(`Fetching MongoDB Public Key ${CA}`); + fetch(CA) + .then((response) => { + if (response.ok) { + return response.text(); + } + throw new Error(response.statusText); + }) + .then((body) => { + if (body) { + debug.db('Fetched MongoDB Public Key'); + config.mongoSA = config.mongoCA = body; + return next(); + } + throw new Error('Empty Body'); + }) + .catch((error) => { + debug.db(`Unable to fetch MongoDB Public Key: ${error}`); + unlock(() => { + throw new Error(`Unable to fetch the SA Certificate: ${CA}.`); }); - } - - debug.db('Fetched SA Certificate'); - config.mongoSA = body; - return next(); - }); + config.mongoSA = config.mongoCA = ''; + return next(); + }); + } + else if (CA.indexOf('/') === 0) { + // This is a file path, load it directly. + try { + config.mongoSA = config.mongoCA = fs.readFileSync(CA); + } + catch (err) { + debug.db(`Unable to read MongoDB Public Key: ${err}`); + unlock(() => { + throw new Error(`Unable to read the MongoDB Public Key: ${CA}. ${err}`); + }); + config.mongoSA = config.mongoCA = ''; + } + return next(); } else { return next(); } }; + /** + * Fetch the SSL certificates from local dir. + * + * @param next + * @return {*} + */ + const getSSL = function(next) { + if (!config.mongoSSL) { + return next(); + } + + console.log('Loading Mongo SSL Certificates'); + + const certs = { + sslValidate: !!config.mongoSSLValidate, + ssl: true, + }; + + if (config.mongoSSLPassword) { + certs.sslPass = config.mongoSSLPassword; + } + + const files = { + sslCA: 'ca.pem', + sslCert: 'cert.pem', + sslCRL: 'crl.pem', + sslKey: 'key.pem', + }; + + // Load each file into its setting. + Object.keys(files).forEach((key) => { + const file = files[key]; + if (fs.existsSync(path.join(config.mongoSSL, file))) { + console.log(' > Reading', path.join(config.mongoSSL, file)); + if (key === 'sslCA') { + certs[key] = [fs.readFileSync(path.join(config.mongoSSL, file))]; + } + else { + certs[key] = fs.readFileSync(path.join(config.mongoSSL, file)); + } + } + else { + console.log(' > Could not find', path.join(config.mongoSSL, file), 'skipping'); + } + }); + console.log(''); + + config.mongoSSL = certs; + return next(); + }; + /** * Initialize the Mongo Connections for queries. * @@ -147,7 +224,7 @@ module.exports = function(formio) { : config.mongo[0]; debug.db(`Opening new connection to ${dbUrl}`); - const mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {}; + let mongoConfig = config.mongoConfig ? JSON.parse(config.mongoConfig) : {}; if (!mongoConfig.hasOwnProperty('connectTimeoutMS')) { mongoConfig.connectTimeoutMS = 300000; } @@ -157,9 +234,15 @@ module.exports = function(formio) { if (!mongoConfig.hasOwnProperty('useNewUrlParser')) { mongoConfig.useNewUrlParser = true; } - if (config.mongoSA) { + if (config.mongoSA || config.mongoCA) { mongoConfig.sslValidate = true; - mongoConfig.sslCA = config.mongoSA; + mongoConfig.sslCA = config.mongoSA || config.mongoCA; + } + if (config.mongoSSL) { + mongoConfig = { + ...mongoConfig, + ...config.mongoSSL, + }; } mongoConfig.useUnifiedTopology = true; @@ -262,10 +345,17 @@ module.exports = function(formio) { const sanityCheck = function sanityCheck(req, res, next) { // Determine if a response is expected by the request path. const response = (req.path === '/health'); + const {verboseHealth} = req; // Skip functionality if testing. if (process.env.TEST_SUITE) { debug.db('Skipping for TEST_SUITE'); + + if (response && verboseHealth) { + res.status(200); + return next(); + } + return response ? res.sendStatus(200) : next(); @@ -280,6 +370,16 @@ module.exports = function(formio) { * @returns {*} */ const handleResponse = function(err) { + if (response && verboseHealth) { + if (err) { + res.status(400); + } + else { + res.status(200); + } + return next(); + } + if (err) { return res.status(400).send(err); } @@ -621,7 +721,8 @@ module.exports = function(formio) { } async.series([ - getSA, + getCA, + getSSL, connection, checkSetup, checkEncryption, diff --git a/forms-flow-forms/src/export/export.js b/forms-flow-forms/src/export/export.js index 320cc04e2e..c428d5e284 100644 --- a/forms-flow-forms/src/export/export.js +++ b/forms-flow-forms/src/export/export.js @@ -199,7 +199,12 @@ module.exports = (router) => { }) ); }), {end: false}); - + // If the DB cursor throws an error, send the error. + cursor.on('error', (error) => { + debug(error); + router.formio.util.log(error); + res.status(400).send(error); + }); // When the DB cursor ends, allow the output stream a tick to perform the last write, // then manually end it by pushing a null item to the output stream's queue cursor.on('end',() => Promise.all(promises).then(() => process.nextTick(() => stream.queue(null)))); diff --git a/forms-flow-forms/src/export/exporters/CSVExporter.js b/forms-flow-forms/src/export/exporters/CSVExporter.js index a20cdfe44b..4a088496cf 100644 --- a/forms-flow-forms/src/export/exporters/CSVExporter.js +++ b/forms-flow-forms/src/export/exporters/CSVExporter.js @@ -8,6 +8,7 @@ const _ = require('lodash'); const Entities = require('html-entities').AllHtmlEntities; const moment = require('moment-timezone'); const {conformToMask} = require('vanilla-text-mask'); +const Formio = require('formiojs/formio.form'); const interpolate = (string, data) => string.replace(/{{\s*(\S*)\s*}}/g, (match, path) => { const value = _.get(data, path); @@ -28,6 +29,7 @@ const labelRegexp = /(?:(\.data\.)(?!\.data\.))/g; * @constructor */ class CSVExporter extends Exporter { + /* eslint-disable max-statements */ constructor(form, req, res) { super(form, req, res); this.timezone = _.get(form, 'settings.components.datetime.timezone', ''); @@ -43,29 +45,54 @@ class CSVExporter extends Exporter { const formattedView = req.query.view === 'formatted'; this.formattedView = formattedView; - const ignore = ['password', 'button', 'container', 'datagrid']; + const ignore = ['password', 'button', 'container', 'datagrid', 'editgrid', 'dynamicWizard']; try { - util.eachComponent(form.components, (component, path) => { - if (!component.input || !component.key || ignore.includes(component.type)) { + util.eachComponent(form.components, (comp, path) => { + if (!comp.input || !comp.key || ignore.includes(comp.type)) { return; } + const {component} = Formio.Components.create(comp); const items = []; + let noRecurse = false; // If a component has multiple parts, pick what we want. if (component.type === 'address') { items.push({ - subpath: 'formatted_address', - rename: (label) => `${label}.formatted` + rename: (label) => `${label}.formatted`, + preprocessor: (value) => { + if (_.isArray(value)) { + return value; + } + + const address = (value && value.address) || value || {}; + return address.formatted_address || ''; + }, }); items.push({ - subpath: 'geometry.location.lat', - rename: (label) => `${label}.lat` + rename: (label) => `${label}.lat`, + preprocessor: (value) => { + if (_.isArray(value)) { + return value; + } + + const address = (value && value.address) || value || {}; + return _.get(address, 'geometry.location.lat', ''); + }, }); items.push({ - subpath: 'geometry.location.lng', - rename: (label) => `${label}.lng` + rename: (label) => `${label}.lng`, + preprocessor: (value) => { + if (_.isArray(value)) { + return value; + } + + const address = (value && value.address) || value || {}; + return _.get(address, 'geometry.location.lng', ''); + }, }); + + noRecurse = true; } else if (component.type === 'selectboxes') { _.each(component.values, (option) => { @@ -83,7 +110,7 @@ class CSVExporter extends Exporter { return ''; } - const componentValue = component.values.find((v) => v.value === value) || ''; + const componentValue = component.values.find((v) => v.value === value.toString()) || ''; return componentValue && formattedView ? componentValue.label : componentValue.value; @@ -252,7 +279,8 @@ class CSVExporter extends Exporter { if (value) { const dateMoment = moment(value).tz(this.timezone || 'Etc/UTC'); - this.dateFormat = util.FormioUtils.convertFormatToMoment(component.format); + const format = component.format || 'yyyy-MM-dd hh:mm a'; + this.dateFormat = util.FormioUtils.convertFormatToMoment(format); const result = dateMoment.format(`${this.dateFormat} z`); return result; } @@ -279,6 +307,11 @@ class CSVExporter extends Exporter { if (!value) { return ''; } + + if (_.isObject(value)) { + return value; + } + return conformToMask(value, mask).conformedValue; } }); @@ -314,12 +347,15 @@ class CSVExporter extends Exporter { this.fields.push(finalItem); }); + + return noRecurse; }, true); } catch (err) { res.status(400).send(err.message || err); } } + /* eslint-enable max-statements */ /** * Start the CSV export by creating the headers. diff --git a/forms-flow-forms/src/middleware/addSubmissionResourceAccess.js b/forms-flow-forms/src/middleware/addSubmissionResourceAccess.js index d885748635..d0a87292db 100644 --- a/forms-flow-forms/src/middleware/addSubmissionResourceAccess.js +++ b/forms-flow-forms/src/middleware/addSubmissionResourceAccess.js @@ -6,24 +6,31 @@ const util = require('../util/util'); /** * Go through each field and if Submission Resource Access is defined on it, add it to the submissionAccess array. */ -module.exports = function(router) { - const grabIds = function(input) { +module.exports = (router) => { + const grabIds = (input, roles = []) => { if (!input) { return []; } - if (!(input instanceof Array)) { + if (!Array.isArray(input)) { input = [input]; } - const final = []; - input.forEach(function(element) { - if (element && element._id) { - final.push(element._id); + if (!Array.isArray(roles)) { + roles = [roles]; + } + + roles = roles.filter(_.identity); + + return input.flatMap((element) => { + if (!element || !element._id) { + return []; } - }); - return final; + return roles.length + ? roles.map((role) => (`${element._id}:${role}`)) + : element._id; + }); }; return function addSubmissionResourceAccess(req, res, next) { @@ -40,30 +47,42 @@ module.exports = function(router) { /* eslint-disable max-depth */ util.FormioUtils.eachComponent(form.components, (component, path) => { - if (component && component.key && component.defaultPermission) { + if (component && component.key && (component.submissionAccess || component.defaultPermission)) { + if (!component.submissionAccess) { + component.submissionAccess = [ + { + type: component.defaultPermission, + roles: [], + }, + ]; + } + let value = _.get(req.body.data, path); if (value) { - if (!(value instanceof Array)) { + if (!Array.isArray(value)) { value = [value]; } - const ids = grabIds(value); - if (ids.length) { - const perm = _.find(req.body.access, { - type: component.defaultPermission - }); - if (perm) { - if (!perm.resources) { - perm.resources = []; - } - perm.resources = perm.resources.concat(ids); - } - else { - req.body.access.push({ - type: component.defaultPermission, - resources: ids + + component.submissionAccess.map((access) => { + const ids = grabIds(value, access.roles); + if (ids.length) { + const perm = _.find(req.body.access, { + type: access.type, }); + if (perm) { + if (!perm.resources) { + perm.resources = []; + } + perm.resources = perm.resources.concat(ids); + } + else { + req.body.access.push({ + type: access.type, + resources: ids, + }); + } } - } + }); } } }); diff --git a/forms-flow-forms/src/middleware/alias.js b/forms-flow-forms/src/middleware/alias.js index 58cf61621a..aed657ecdf 100644 --- a/forms-flow-forms/src/middleware/alias.js +++ b/forms-flow-forms/src/middleware/alias.js @@ -39,7 +39,8 @@ module.exports = function(router) { const baseUrl = aliasHandler.baseUrl ? aliasHandler.baseUrl(req) : ''; // Get the alias from the request. - const alias = url.parse(req.url).pathname.substr(baseUrl.length).replace(formsRegEx, '').substr(1); + let alias = url.parse(req.url).pathname.substr(baseUrl.length).replace(formsRegEx, '').substr(1); + alias = router.formio.hook.alter('alias', alias, req, res); // If this is normal request, then pass this middleware. /* eslint-disable no-useless-escape */ diff --git a/forms-flow-forms/src/middleware/condenseSubmissionPermissionTypes.js b/forms-flow-forms/src/middleware/condenseSubmissionPermissionTypes.js index c1123ee0c7..d9fc6e6347 100644 --- a/forms-flow-forms/src/middleware/condenseSubmissionPermissionTypes.js +++ b/forms-flow-forms/src/middleware/condenseSubmissionPermissionTypes.js @@ -27,7 +27,7 @@ module.exports = function(router) { _.each(req.body.access, function(permission) { if ( _.has(permission, 'type') - && _.includes(['read', 'create', 'write', 'admin'], permission.type) + && _.includes(['read', 'create', 'update', 'delete', 'write', 'admin'], permission.type) && _.has(permission, 'resources') && (_.get(permission, 'resources') instanceof Array) ) { diff --git a/forms-flow-forms/src/middleware/formLoader.js b/forms-flow-forms/src/middleware/formLoader.js index 967013a13c..4451ce8759 100644 --- a/forms-flow-forms/src/middleware/formLoader.js +++ b/forms-flow-forms/src/middleware/formLoader.js @@ -12,10 +12,17 @@ const _ = require('lodash'); module.exports = function(router) { const hook = require('../util/hook')(router.formio); return function formLoader(req, res, next) { + if ( + req.method !== 'GET' || + Array.isArray(res.resource.item) || + (_.get(req, '__rMethod', 'get') !== 'get') + ) { + return next(); + } + let shouldLoadSubForms = true; // Only process on GET request, and if they provide full query. if ( - req.method !== 'GET' || !req.query.full || !res.resource || !res.resource.item @@ -23,20 +30,8 @@ module.exports = function(router) { shouldLoadSubForms = false; } - //populate reCAPTCHA siteKey from Project Settings if reCAPTCHA is enabled for this form - if (_.get(res.resource.item, 'settings.recaptcha.isEnabled')) { - hook.settings(req, (err, settings) => { - _.set(res.resource.item, 'settings.recaptcha.siteKey', _.get(settings, 'recaptcha.siteKey')); - // Load all subforms recursively. - if (shouldLoadSubForms) { - router.formio.cache.loadSubForms(res.resource.item, req, next); - } - else { - return next(); - } - }); - } - else { + // Allow modules to hook into the form loader middleware. + hook.alter('formResponse', res.resource.item, req, () => { // Load all subforms recursively. if (shouldLoadSubForms) { router.formio.cache.loadSubForms(res.resource.item, req, next); @@ -44,6 +39,6 @@ module.exports = function(router) { else { return next(); } - } + }); }; }; diff --git a/forms-flow-forms/src/middleware/middleware.js b/forms-flow-forms/src/middleware/middleware.js index 5e4c6d93ab..d6563ed881 100644 --- a/forms-flow-forms/src/middleware/middleware.js +++ b/forms-flow-forms/src/middleware/middleware.js @@ -25,6 +25,7 @@ module.exports = function(router) { formHandler: require('./formHandler')(router), formActionHandler: require('./formActionHandler')(router), ownerFilter: require('./ownerFilter')(router), + storageAccessHandler: require('./storageAccessHandler')(router), permissionHandler: require('./permissionHandler')(router), loadPreviousSubmission: require('./loadPreviousSubmission')(router), setFilterQueryTypes: require('./setFilterQueryTypes')(router), @@ -32,8 +33,10 @@ module.exports = function(router) { submissionApplyPatch: require('./submissionApplyPatch')(router), submissionHandler: require('./submissionHandler')(router), submissionResourceAccessFilter: require('./submissionResourceAccessFilter')(router), + submissionFieldMatchAccessFilter: require('./submissionFieldMatchAccessFilter')(router), tokenHandler: require('./tokenHandler')(router), restrictRequestTypes: require('./restrictRequestTypes')(router), - filterIndex: require('./filterIndex')(router) + filterIndex: require('./filterIndex')(router), + mongodbConnectionState: require('./mongodbConnectionState')(router), }; }; diff --git a/forms-flow-forms/src/middleware/mongodbConnectionState.js b/forms-flow-forms/src/middleware/mongodbConnectionState.js new file mode 100644 index 0000000000..8b18496e38 --- /dev/null +++ b/forms-flow-forms/src/middleware/mongodbConnectionState.js @@ -0,0 +1,26 @@ +'use strict'; + +/** + * The mongodbConnectionState middleware. + * + * This middleware is used for checking mongodb connection state. + * + * @param router + * @returns {Function} + */ +module.exports = (router) => (formio) => function(req, res, next) { + const states = { + 0: 'disconnected', + 1: 'connected', + 2: 'connecting', + 3: 'disconnecting', + }; + + if (!formio.mongoose || !formio.mongoose.connection) { + req.mongodbConnectionState = 'connection doesn\'t exist'; + return next(); + } + const mongodbState = formio.mongoose.connection.readyState; + req.mongodbConnectionState = states[mongodbState] || 'unresolved'; + return next(); +}; diff --git a/forms-flow-forms/src/middleware/params.js b/forms-flow-forms/src/middleware/params.js index fc95055a7b..ce76f0905f 100644 --- a/forms-flow-forms/src/middleware/params.js +++ b/forms-flow-forms/src/middleware/params.js @@ -1,11 +1,13 @@ 'use strict'; -const util = require('../util/util'); const _ = require('lodash'); -module.exports = function(router) { +const util = require('../util/util'); + +module.exports = (router) => { const hook = require('../util/hook')(router.formio); - return function paramsHandler(req, res, next) { + + return (req, res, next) => { // Split the request url into its corresponding parameters. const params = _.assign(util.getUrlParams(req.url), util.getUrlParams(req.baseUrl)); diff --git a/forms-flow-forms/src/middleware/permissionHandler.js b/forms-flow-forms/src/middleware/permissionHandler.js index eaccba29a3..a94f78084d 100644 --- a/forms-flow-forms/src/middleware/permissionHandler.js +++ b/forms-flow-forms/src/middleware/permissionHandler.js @@ -52,18 +52,39 @@ module.exports = function(router) { if (permission.type === 'read') { _.each(permission.resources, function(id) { access.submission.read_all.push(id); + + // Flag this request as not having admin access through submission resource access. + req.submissionResourceAccessAdminBlock = req.submissionResourceAccessAdminBlock || []; + req.submissionResourceAccessAdminBlock.push(util.idToString(id)); }); } else if (permission.type === 'create') { _.each(permission.resources, function(id) { access.submission.create_all.push(id); - access.submission.read_all.push(id); + + // Flag this request as not having admin access through submission resource access. + req.submissionResourceAccessAdminBlock = req.submissionResourceAccessAdminBlock || []; + req.submissionResourceAccessAdminBlock.push(util.idToString(id)); + }); + } + else if (permission.type === 'update') { + _.each(permission.resources, function(id) { + access.submission.update_all.push(id); + + // Flag this request as not having admin access through submission resource access. + req.submissionResourceAccessAdminBlock = req.submissionResourceAccessAdminBlock || []; + req.submissionResourceAccessAdminBlock.push(util.idToString(id)); + }); + } + else if (permission.type === 'delete') { + _.each(permission.resources, function(id) { + access.submission.delete_all.push(id); }); } else if (permission.type === 'write') { _.each(permission.resources, function(id) { - access.submission.create_all.push(id); access.submission.read_all.push(id); + access.submission.create_all.push(id); access.submission.update_all.push(id); // Flag this request as not having admin access through submission resource access. @@ -73,8 +94,8 @@ module.exports = function(router) { } else if (permission.type === 'admin') { _.each(permission.resources, function(id) { - access.submission.create_all.push(id); access.submission.read_all.push(id); + access.submission.create_all.push(id); access.submission.update_all.push(id); access.submission.delete_all.push(id); }); @@ -84,9 +105,23 @@ module.exports = function(router) { }, function(err) { // Force all the permissions to be unique, even if an error occurred. access.submission.read_all = _(access.submission.read_all).uniq().value(); + access.submission.create_all = _(access.submission.create_all).uniq().value(); access.submission.update_all = _(access.submission.update_all).uniq().value(); access.submission.delete_all = _(access.submission.delete_all).uniq().value(); + const adminAccess = _.chain(access.submission.delete_all) + .intersection(access.submission.update_all) + .intersection(access.submission.create_all) + .intersection(access.submission.read_all) + .uniq() + .value(); + + if (_.intersection(req.submissionResourceAccessAdminBlock, adminAccess).length) { + req.submissionResourceAccessAdminBlock = _.filter(req.submissionResourceAccessAdminBlock, function(el) { + adminAccess.includes(el); + }); + } + if (err) { return next(err); } @@ -96,6 +131,76 @@ module.exports = function(router) { /* eslint-enable camelcase */ }; + const getSubmissionFieldMatchAccess = function(req, submission, access) { + if (!submission || !access) { + return; + } + // If the form has no Field Match Access permissions or it is an index request + if (!req.submissionFieldMatchAccess || req.submissionFieldMatchAccessFilter === true) { + return; + } + + const userRoles = access.roles; + + // Allowed actions for each permission level + const permissions = { + read: ['read_all'], + create: ['create_all'], + update: ['update_all'], + delete: ['delete_all'], + write: ['read_all', 'create_all', 'update_all'], + admin: ['read_all', 'create_all', 'update_all', 'delete_all'] + }; + + const isConditionMet = (value, formFieldValue, operator) => { + switch (operator) { + case '$eq': + return value === formFieldValue; + case '$gte': + return (formFieldValue >= value); + case '$lte': + return (formFieldValue <= value); + case '$gt': + return (formFieldValue > value); + case '$lt': + return (formFieldValue < value); + case '$in': + return Array.isArray(value) ? _.find(value, formFieldValue) : false; + } + }; + + const grantAccess = (permissionLevel, roles) => { + if (permissions[permissionLevel]) { + permissions[permissionLevel].forEach((right) => { + if (access.submission[right]) { + access.submission[right].push(...roles); + access.submission[right] = _(access.submission[right]).uniq().value(); + } + }); + } + }; + + // Iterate through each permission level + Object.entries(req.submissionFieldMatchAccess).forEach(([permissionLevel, conditions]) => { + // Iterate through each condition within a permission level + conditions.forEach((condition) => { + // Get intersection of roles within condition and the user's roles + const rolesIntersection = _.intersectionWith(condition.roles, userRoles, (role, userRole) => { + return role.toString() === userRole.toString(); + }).map((role) => role.toString()); + + // If the user has a role specified in condition + if (rolesIntersection.length) { + const {formFieldPath, operator, value, valueType} = condition; + const formFieldValue = _.get(submission, formFieldPath); + if (isConditionMet(util.castValue(valueType, value), formFieldValue, operator)) { + grantAccess(permissionLevel, rolesIntersection); + } + } + }); + }); + }; + /** * Attempts to add Self Access Permissions if present in the form access. * @@ -132,6 +237,47 @@ module.exports = function(router) { } }; + const getAccessBasedOnMethod = function(req, item, access, roles) { + util.eachComponent(item.components, (component, path) => { + if (component && component.key && (component.submissionAccess || component.defaultPermission)) { + if (!component.submissionAccess) { + component.submissionAccess = [ + { + type: component.defaultPermission, + roles: [], + }, + ]; + } + + let selectValue = _.get(req.body.data, path); + if (selectValue) { + if (!Array.isArray(selectValue)) { + selectValue = [selectValue]; + } + + const createAccess = component.submissionAccess + .filter(({type}) => (roles.includes(type))) + .map((access) => ({ + ...access, + roles: _.compact(access.roles || []), + })); + + selectValue.filter((value) => (value && value._id)).forEach(({_id}) => { + createAccess.forEach(({roles}) => { + /* eslint-disable camelcase */ + access.submission.create_all = (access.submission.create_all || []).concat( + roles.length + ? roles.map((role) => (`${_id}:${role}`)) + : _id, + ); + /* eslint-enable camelcase */ + }); + }); + } + } + }); + }; + // Add this access handlers for all to use. router.formio.access = { @@ -220,65 +366,19 @@ module.exports = function(router) { // check for group permissions, only if creating submission (POST request) if (req.method === 'POST') { - util.eachComponent(item.components, (component, path) => { - if (component && component.key && component.defaultPermission) { - let selectValue = _.get(req.body.data, path); - if (selectValue) { - if (!(selectValue instanceof Array)) { - selectValue = [selectValue]; - } - /* eslint-disable camelcase */ - selectValue.forEach(value => { - if (value && value._id) { - if (['create', 'write', 'admin'].indexOf(component.defaultPermission) > -1) { - access.submission.create_all = access.submission.create_all || []; - access.submission.create_all.push(value._id); - } - } - }); - } - } - }); + getAccessBasedOnMethod(req, item, access, ['create', 'write', 'admin']); } - // Return the updated access list. - return callback(null); - }); - }, - - // Get the permissions for a Submission with the given ObjectId. - function getSubmissionAccess(callback) { - access.submission = access.submission || {}; - - // Skip submission access if no subId was given. - if (!req.subId) { - return callback(null); - } - - // Get the submission by request id and query its access. - router.formio.cache.loadSubmission(req, req.formId, req.subId, function(err, submission) { - if (err) { - return callback(400); - } - - // No submission exists. - if (!submission) { - return callback(404); + if (req.method === 'DELETE') { + getAccessBasedOnMethod(req, item, access, ['delete', 'admin']); } - // Add the submission owners UserId to the access list. - access.submission.owner = submission.owner - ? submission.owner.toString() - : null; - - // Add self access if previously defined. - if (req.selfAccess && req.selfAccess === true) { - // Add the submission id to the access entity. - access.submission._id = util.idToString(submission._id); + if (req.method === 'PUT' || req.method === 'PATCH') { + getAccessBasedOnMethod(req, item, access, ['update', 'write', 'admin']); } - // Load Submission Resource Access. - getSubmissionResourceAccess(req, submission, access, callback); + // Return the updated access list. + return callback(null); }); }, @@ -292,7 +392,6 @@ module.exports = function(router) { return callback(400); } - // Get a list of all valid roles this user can have. const validRoles = (roles && roles.length) ? roles.map((role) => { const roleId = role._id.toString(); if (role.default) { @@ -323,10 +422,82 @@ module.exports = function(router) { // Add the EVERYONE role. access.roles.push(EVERYONE); + req.accessRoles = access.roles; callback(); }); }, + // Load the form and set Field Match Access conditions to the request + function setSubmissionFieldMatchAccessToRequest(callback) { + if (!req.formId) { + return callback(null); + } + router.formio.cache.loadForm(req, null, req.formId, function(err, item) { + if (err) { + return callback(err); + } + if (!item) { + return callback(`No Form found with formId: ${req.formId}`); + } + + if (item.fieldMatchAccess) { + req.submissionFieldMatchAccess = item.fieldMatchAccess; + } + return callback(null); + }); + }, + + // Mark the index request to be proccessed by SubmissionFieldMatchAccessFilter + function flagIndexRequestAsSubmissionFieldMatchAccess(callback) { + const isIndexRequest = (req) => req.method.toUpperCase() === 'GET' && req.formId && !req.subId; + if (!isIndexRequest(req)) { + return callback(null); + } + + if (req.submissionFieldMatchAccess) { + req.submissionFieldMatchAccessFilter = true; + } + return callback(null); + }, + + // Get the permissions for a Submission with the given ObjectId. + function getSubmissionAccess(callback) { + access.submission = access.submission || {}; + + // Skip submission access if no subId was given. + if (!req.subId) { + // Still need to check if the user allowed to create submissions withing Field Match Access + getSubmissionFieldMatchAccess(req, req.body, access); + return callback(null); + } + // Get the submission by request id and query its access. + router.formio.cache.loadSubmission(req, req.formId, req.subId, function(err, submission) { + if (err) { + return callback(400); + } + + // No submission exists. + if (!submission) { + return callback(404); + } + + // Add the submission owners UserId to the access list. + access.submission.owner = submission.owner + ? submission.owner.toString() + : null; + + // Add self access if previously defined. + if (req.selfAccess && req.selfAccess === true) { + // Add the submission id to the access entity. + access.submission._id = util.idToString(submission._id); + } + // Load Submission Field Match Access. + getSubmissionFieldMatchAccess(req, submission, access); + // Load Submission Resource Access. + getSubmissionResourceAccess(req, submission, access, callback); + }); + }, + // Determine if this is a possible index request against submissions. function flagRequestAsSubmissionResourceAccess(callback) { if (req.method.toUpperCase() !== 'GET') { @@ -343,6 +514,12 @@ module.exports = function(router) { return callback(); } + const findSubmissionAccessByType = function(submissionAccess, type) { + return _.chain(submissionAccess) + .filter(access => access.type === type) + .value(); + }; + // Load the form, and get its roles/permissions data. router.formio.cache.loadForm(req, null, req.formId, function(err, item) { if (err) { @@ -352,9 +529,53 @@ module.exports = function(router) { return callback(`No Form found with formId: ${req.formId}`); } - // See if any of our components have "defaultPermission" established. + // See if any of our components have "submissionAccess" or "defaultPermission" established. util.eachComponent(item.components, (component) => { - if (component.defaultPermission) { + if (component.submissionAccess || component.defaultPermission) { + if (!component.submissionAccess) { + component.submissionAccess = [ + { + type: component.defaultPermission, + roles: [], + }, + ]; + } + + //We need to know if there is read submission access + const [readAccess] = findSubmissionAccessByType(component.submissionAccess, 'read'); + const [writeAccess] = findSubmissionAccessByType(component.submissionAccess, 'write'); + const [adminAccess] = findSubmissionAccessByType(component.submissionAccess, 'admin'); + + if ((readAccess && readAccess.roles && !readAccess.roles.length) || + (writeAccess && writeAccess.roles && !writeAccess.roles.length) || + (adminAccess && adminAccess.roles && !adminAccess.roles.length)) { + req.skipOwnerFilter = true; + req.submissionResourceAccessFilter = true; + return true; + } + + const readBlockingRoles = _.chain(component.submissionAccess) + .filter(access => access && access.type !== 'read') + .map(el => el.roles) + .flattenDeep() + .value(); + + if (writeAccess && writeAccess.roles && writeAccess.roles.length) { + _.pullAll(readBlockingRoles, writeAccess.roles); + } + + if (adminAccess && adminAccess.roles && adminAccess.roles.length) { + _.pullAll(readBlockingRoles, adminAccess.roles); + } + + userRoles.forEach(function(roleEntity) { + const role = roleEntity.split(':')[1]; + + if (role && readBlockingRoles.includes(role)) { + req.readBlockingRoles = roleEntity; + } + }); + // Since the access is now determined by the submission resource access, we // can skip the owner filter. req.skipOwnerFilter = true; @@ -484,13 +705,16 @@ module.exports = function(router) { // Skip the owner filter if they have all access. req.skipOwnerFilter = true; - // Do not include the submission resource access filter if they have "all" access. + // Do not include the submission resource access and field match access filters if they have "all" access. req.submissionResourceAccessFilter = false; + req.submissionFieldMatchAccessFilter = false; _hasAccess = true; } - // If resource access applies, then allow for that to be in the query. - if (_.has(req, 'submissionResourceAccessFilter') && req.submissionResourceAccessFilter) { + // If resource access or field match access applies, then allow for that to be in the query. + if (_.has(req, 'submissionResourceAccessFilter') && req.submissionResourceAccessFilter || + _.has(req, 'submissionFieldMatchAccessFilter') && req.submissionFieldMatchAccessFilter) { + req.skipOwnerFilter = true; _hasAccess = true; } @@ -548,7 +772,7 @@ module.exports = function(router) { return next(); } - // Determine if we are trying to access and entity of the form or submission. + // Determine if we are trying to access an entity of the form or submission. router.formio.access.getAccess(req, res, function(err, access) { if (err) { if (_.isNumber(err)) { @@ -594,6 +818,7 @@ module.exports = function(router) { // Attempt a final access check against submission index requests using the submission resource access. // If this passes, it is up to the submissionResourceAccessFilter middleware to handle permissions. + // TODO: ask Travis if these lines are redundant if (_.has(req, 'submissionResourceAccessFilter') && req.submissionResourceAccessFilter) { req.skipOwnerFilter = true; return next(); diff --git a/forms-flow-forms/src/middleware/recaptcha.js b/forms-flow-forms/src/middleware/recaptcha.js index 44f8043161..cb5c49be28 100644 --- a/forms-flow-forms/src/middleware/recaptcha.js +++ b/forms-flow-forms/src/middleware/recaptcha.js @@ -1,34 +1,34 @@ 'use strict'; -const util = require('../util/util'); +const querystring = require('querystring'); +const fetch = require('../util/fetch'); + module.exports = function(router) { const hook = require('../util/hook')(router.formio); - router.get('/recaptcha', - function(req, res) { - hook.settings(req, (err, settings) => { - if (!settings.recaptcha || !settings.recaptcha.secretKey) { - return res.status(400).send('reCAPTCHA settings not set.'); - } - if (!req.query.recaptchaToken) { - return res.status(400).send('reCAPTCHA token is not specified'); + + router.get('/recaptcha', function(req, res) { + hook.settings(req, (err, settings) => { + if (!settings.recaptcha || !settings.recaptcha.secretKey) { + return res.status(400).send('reCAPTCHA settings not set.'); + } + if (!req.query.recaptchaToken) { + return res.status(400).send('reCAPTCHA token is not specified'); + } + + const url = 'https://www.google.com/recaptcha/api/siteverify'; + const query = querystring.stringify({ + secret: settings.recaptcha.secretKey, + response: req.query.recaptchaToken, + }); + + fetch(`${url}?${query}`, {method: 'POST'}) + .then((res) => (res.ok ? res.json() : null)) + .then((body) => { + if (!body) { + throw new Error('No response from Google'); } - util.request({ - method: 'POST', - json: true, - url: 'https://www.google.com/recaptcha/api/siteverify', - form: { - secret: settings.recaptcha.secretKey, - response: req.query.recaptchaToken - } - }).spread((response, body) => { - if (!body) { - throw 'No response from Google'; - } - else { - res.send(body); - } - }); + res.send(body); }); - } - ); + }); + }); }; diff --git a/forms-flow-forms/src/middleware/storageAccessHandler.js b/forms-flow-forms/src/middleware/storageAccessHandler.js new file mode 100644 index 0000000000..84aedebcdc --- /dev/null +++ b/forms-flow-forms/src/middleware/storageAccessHandler.js @@ -0,0 +1,43 @@ +'use strict'; + +const _ = require('lodash'); + +module.exports = function(router) { + return function storageAccessHandler(req, res, next) { + if (!req.originalUrl.split('/').includes('storage')) { + return next(); + } + + router.formio.cache.loadForm(req, null, req.formId, function(err, item) { + if (err) { + return next(err); + } + if (!item) { + return next(`No Form found with formId: ${req.formId}`); + } + + if (req.body.groupId && req.user.roles.includes(req.body.groupId)) { + req.permissionsChecked = true; + } + + if (req.body.groupPermissions) { + const createRoles = _.chain(req.body.groupPermissions) + .filter(permission => permission.type && ['admin', 'write', 'create'].includes(permission.type)) + .map(permission => permission.roles) + .flattenDeep() + .value(); + + req.user.roles.forEach(function(roleEntity) { + const [groupId, role] = roleEntity.split(':'); + + if (role && groupId && + createRoles.includes(role) && req.body.groupId === groupId) { + req.permissionsChecked = true; + } + }); + } + + return next(); + }); + }; +}; diff --git a/forms-flow-forms/src/middleware/submissionApplyPatch.js b/forms-flow-forms/src/middleware/submissionApplyPatch.js index a626dcf044..221e9e068c 100644 --- a/forms-flow-forms/src/middleware/submissionApplyPatch.js +++ b/forms-flow-forms/src/middleware/submissionApplyPatch.js @@ -32,7 +32,15 @@ module.exports = router => (req, res, next) => { return res.sendStatus(404); } - const patch = req.body; + let patch = req.body; + + if (!_.isArray(patch) && req.subPatch && !_.isEmpty(req.body)) { + patch = jsonPatch.compare(submission.data, req.body.data) + .map((operation) => { + operation.path = `/data${operation.path}`; + return operation; + }); + } try { req.body = jsonPatch.applyPatch(submission, patch, true).newDocument; diff --git a/forms-flow-forms/src/middleware/submissionFieldMatchAccessFilter.js b/forms-flow-forms/src/middleware/submissionFieldMatchAccessFilter.js new file mode 100644 index 0000000000..5b88945a7d --- /dev/null +++ b/forms-flow-forms/src/middleware/submissionFieldMatchAccessFilter.js @@ -0,0 +1,72 @@ +'use strict'; + +const _ = require('lodash'); +const debug = require('debug')('formio:middleware:submissionResourceAccessFilter'); + +module.exports = function(router) { + return function submissionResourceAccessFilter(req, res, next) { + const util = router.formio.util; + + // Skip this filter, if request is from an administrator. + if (req.isAdmin) { + debug('Skipping, request is from an administrator.'); + return next(); + } + + // Skip this filter, if not flagged in the permission handler. + if (!_.has(req, 'submissionFieldMatchAccessFilter') || req.submissionFieldMatchAccessFilter === false) { + return next(); + } + + // Should never get here WITHOUT a form id present or WITH a submission id present. + if (!req.formId || req.subId) { + return res.sendStatus(400); + } + + const userId = _.get(req, 'user._id'); + const userRoles = _.get(req, 'accessRoles', []); + if (!userRoles.length) { + return res.sendStatus(401); + } + // Perform our search. + let query = null; + const hasRolesIntersection = (condition) => !!_.intersectionWith(condition.roles, userRoles, + (role, userRole) => role.toString() === userRole.toString()).length; + + // Map permissions to array of Mongo conditions + const fieldsToCheck = Object.entries(req.submissionFieldMatchAccess).flatMap(([, conditions]) => { + return conditions.map((condition) => { + if (hasRolesIntersection(condition)) { + const {formFieldPath, operator, value, valueType} = condition; + + if (value) { + return {[formFieldPath]: {[operator]: util.castValue(valueType, value)}}; + } + } + }); + }).filter((condition) => !!condition); + + if (userId) { + fieldsToCheck.push({owner: util.idToBson(userId)}); + } + + query = fieldsToCheck.length !== 0 ? { + form: util.idToBson(req.formId), + deleted: {$eq: null}, + $or: [...fieldsToCheck] + } : null; + + // If there no conditions which may give an access to the current user, return the Unauthorized response + if (!query) { + return res.sendStatus(401); + } + + req.modelQuery = req.modelQuery || req.model || this.model; + req.modelQuery = req.modelQuery.find(query); + + req.countQuery = req.countQuery || req.model || this.model; + req.countQuery = req.countQuery.find(query); + + next(); + }; +}; diff --git a/forms-flow-forms/src/middleware/submissionHandler.js b/forms-flow-forms/src/middleware/submissionHandler.js index d3da89c685..dd1776f36b 100644 --- a/forms-flow-forms/src/middleware/submissionHandler.js +++ b/forms-flow-forms/src/middleware/submissionHandler.js @@ -3,7 +3,9 @@ const _ = require('lodash'); const async = require('async'); const util = require('../util/util'); +const LegacyValidator = require('../resources/LegacyValidator'); const Validator = require('../resources/Validator'); +const setDefaultProperties = require('../actions/properties/setDefaultProperties'); module.exports = (router, resourceName, resourceId) => { const hook = require('../util/hook')(router.formio); @@ -98,6 +100,11 @@ module.exports = (router, resourceName, resourceId) => { req.body.data = {}; } + // Ensure that the _fvid is a number. + if (req.body.hasOwnProperty('_fvid') && !_.isNaN(parseInt(req.body._fvid))) { + req.body._fvid = parseInt(req.body._fvid); + } + // Ensure they cannot reset the submission id. if (req.params.submissionId) { req.body._id = req.params.submissionId; @@ -163,31 +170,54 @@ module.exports = (router, resourceName, resourceId) => { } // Assign submission data to the request body. + const formId = _.get(req, 'body.data.form'); + if (!req.mainForm) { + const hasSubforms = Object.values(_.get(req, 'body.data', {})).some(value => _.isObject(value) && value.form); + req.mainForm = hasSubforms && _.get(req, 'body.form'); + } + let isSubform = formId && formId.toString() !== req.currentForm._id.toString(); + isSubform = !isSubform && req.mainForm ? req.mainForm.toString() !== req.currentForm._id.toString() : isSubform; req.submission = req.submission || {data: {}}; - if (!_.isEmpty(req.submission.data)) { + if (!_.isEmpty(req.submission.data) && !isSubform) { req.body.data = _.assign(req.body.data, req.submission.data); } // Clone the submission to the real value of the request body. req.submission = _.cloneDeep(req.body); - hook.alter('validateSubmissionForm', req.currentForm, req.body, (form) => { + // Next we need to validate the input. + hook.alter('validateSubmissionForm', req.currentForm, req.body, async form => { // eslint-disable-line max-statements + // Allow use of the legacy validator + const useLegacyValidator = ( + process.env.LEGACY_VALIDATOR || + req.headers['legacy-validator'] || + req.query.legacy_validator + ); + // Get the submission model. const submissionModel = req.submissionModel || router.formio.resources.submission.model; // Next we need to validate the input. const token = util.getRequestValue(req, 'x-jwt-token'); - Validator.setHook(hook); - const validator = new Validator(req.currentForm, submissionModel, token, req.token); + const _Validator = useLegacyValidator ? LegacyValidator : Validator; + + if (useLegacyValidator) { + _Validator.setHook(hook); + } + + const validator = new _Validator(req.currentForm, submissionModel, token, req.token, hook); // Validate the request. - validator.validate(req.body, (err, submission) => { + validator.validate(req.body, (err, data, visibleComponents) => { if (err) { return res.status(400).json(err); } - res.submission = {data: submission}; + res.submission = {data: data}; + if (!_.isEqual(visibleComponents, req.currentForm.components)) { + req.currentFormComponents = visibleComponents; + } done(); }); }); @@ -219,76 +249,6 @@ module.exports = (router, resourceName, resourceId) => { }; } - // This should probably be moved to utils. - function eachValue(components, data, fn, context, path = '') { - const promises = []; - if (components) { - components.forEach((component) => { - if (component.hasOwnProperty('components') && Array.isArray(component.components)) { - // If tree type is an array of objects like datagrid and editgrid. - if (['datagrid', 'editgrid'].includes(component.type) || component.arrayTree) { - _.get(data, component.key, []).forEach((row, index) => { - promises.push(eachValue( - component.components, - row, - fn, - context, - `${path ? `${path}.` : ''}${component.key}[${index}]`, - )); - }); - } - else if (['form'].includes(component.type)) { - promises.push(eachValue( - component.components, - _.get(data, `${component.key}.data`, {}), - fn, - context, - `${path ? `${path}.` : ''}${component.key}.data`, - )); - } - else if ( - ['container'].includes(component.type) || - ( - component.tree && - !['panel', 'table', 'well', 'columns', 'fieldset', 'tabs', 'form'].includes(component.type) - ) - ) { - promises.push(eachValue( - component.components, - _.get(data, component.key), - fn, - context, - `${path ? `${path}.` : ''}${component.key}`, - )); - } - else { - promises.push(eachValue(component.components, data, fn, context, path)); - } - } - else if (component.hasOwnProperty('columns') && Array.isArray(component.columns)) { - // Handle column like layout components. - component.columns.forEach((column) => { - promises.push(eachValue(column.components, data, fn, context, path)); - }); - } - else if (component.hasOwnProperty('rows') && Array.isArray(component.rows)) { - // Handle table like layout components. - component.rows.forEach((row) => { - if (Array.isArray(row)) { - row.forEach((column) => { - promises.push(eachValue(column.components, data, fn, context, path)); - }); - } - }); - } - // Call the callback for each component. - promises.push(fn({...context, data, component, path})); - }); - } - - return Promise.all(promises); - } - /** * Execute the field handlers. * @@ -299,18 +259,20 @@ module.exports = (router, resourceName, resourceId) => { * @param done */ function executeFieldHandlers(validation, req, res, done) { - // If they wish to disable actions, then just skip. - if (req.query.hasOwnProperty('dryrun') && req.query.dryrun) { - return done(); - } - const promises = []; - - eachValue(req.currentForm.components, req.body.data, (context) => { - const {component, data, handler, action, path} = context; - + const resourceData = _.get(res, 'resource.item.data', {}); + const submissionData = req.body.data || resourceData; + + util.eachValue((req.currentFormComponents || req.currentForm.components), submissionData, ({ + component, + data, + handler, + action, + path, + }) => { // Remove not persistent data - if (data && + if ( + data && component.hasOwnProperty('persistent') && !component.persistent ) { @@ -319,32 +281,45 @@ module.exports = (router, resourceName, resourceId) => { const fieldActions = hook.alter('fieldActions', fActions); const propertyActions = hook.alter('propertyActions', pActions); - const componentPath = `${path}${path ? '.' : ''}${component.key}`; + const componentPath = util.valuePath(path, component.key); // Execute the property handlers after validation has occurred. + const handlerArgs = [ + component, + data, + handler, + action, + { + validation, + path: componentPath, + req, + res, + }, + ]; + if (validation) { Object.keys(propertyActions).forEach((property) => { + // Set the default value of property if only minified schema of component is loaded + if (!component.hasOwnProperty(property) && setDefaultProperties.hasOwnProperty(property)) { + setDefaultProperties[property](component); + } if (component.hasOwnProperty(property) && component[property]) { - promises.push(propertyActions[property](component, data, handler, action, { - validation, - path: componentPath, - req, - res, - })); + promises.push(propertyActions[property](...handlerArgs)); } }); } // Execute the field handler. if (fieldActions.hasOwnProperty(component.type)) { - promises.push(fieldActions[component.type](component, data, handler, action, { - validation, - path: componentPath, - req, - res, - })); + promises.push(fieldActions[component.type](...handlerArgs)); } - }, {validation, handler: req.handlerName, action: req.method.toLowerCase(), req, res}); + }, { + validation, + handler: req.handlerName, + action: req.method.toLowerCase(), + req, + res, + }); Promise.all(promises) .then(() => done()) @@ -426,7 +401,10 @@ module.exports = (router, resourceName, resourceId) => { async.apply(executeFieldHandlers, true, req, res), async.apply(alterSubmission, req, res), async.apply(ensureResponse, req, res) - ], next); + ], (...args) => { + delete req.currentFormComponents; + next(...args); + }); }; }); diff --git a/forms-flow-forms/src/middleware/submissionResourceAccessFilter.js b/forms-flow-forms/src/middleware/submissionResourceAccessFilter.js index 7eca69359b..cd3794c722 100644 --- a/forms-flow-forms/src/middleware/submissionResourceAccessFilter.js +++ b/forms-flow-forms/src/middleware/submissionResourceAccessFilter.js @@ -37,7 +37,15 @@ module.exports = function(router) { } const userId = _.get(req, 'user._id'); - const search = userRoles.map(util.idToBson.bind(util)); + const search = userRoles.filter(role => { + if (req.readBlockingRoles && + req.readBlockingRoles.length && + req.readBlockingRoles.includes(role)) { + return false; + } + + return true; + }).map(util.idToBson.bind(util)); search.push(util.idToBson(EVERYONE)); if (userId) { search.push(util.idToBson(userId)); @@ -65,8 +73,12 @@ module.exports = function(router) { deleted: {$eq: null}, $or: [ { - 'access.type': {$in: ['read', 'write', 'admin']}, - 'access.resources': {$in: newSearch} + access: { + $elemMatch: { + type: {$in: ['read', 'create', 'update', 'delete', 'write', 'admin']}, + resources: {$in: newSearch}, + }, + }, }, { owner: util.idToBson(userId) @@ -78,8 +90,12 @@ module.exports = function(router) { query = { form: util.idToBson(req.formId), deleted: {$eq: null}, - 'access.type': {$in: ['read', 'create', 'write', 'admin']}, - 'access.resources': {$in: newSearch} + access: { + $elemMatch: { + type: {$in: ['read', 'create', 'update', 'delete', 'write', 'admin']}, + resources: {$in: newSearch}, + }, + }, }; } diff --git a/forms-flow-forms/src/middleware/tokenHandler.js b/forms-flow-forms/src/middleware/tokenHandler.js index 6413980a4b..89a1d88dfe 100644 --- a/forms-flow-forms/src/middleware/tokenHandler.js +++ b/forms-flow-forms/src/middleware/tokenHandler.js @@ -1,10 +1,11 @@ 'use strict'; const jwt = require('jsonwebtoken'); +const _ = require('lodash'); const util = require('../util/util'); const debug = { error: require('debug')('formio:error'), - handler: require('debug')('formio:middleware:tokenHandler') + handler: require('debug')('formio:middleware:tokenHandler'), }; /** @@ -18,10 +19,13 @@ const debug = { * @returns {Function} * The middleware for an Express endpoint. */ -module.exports = function(router) { +module.exports = (router) => { // Load the form.io hooks. const hook = require('../util/hook')(router.formio); const formioCache = require('../cache/cache')(router); + const { + jwt: jwtConfig, + } = router.formio.config; /** * Util function to update the jwt in the response. @@ -54,7 +58,7 @@ module.exports = function(router) { * @param next */ const userHandler = (req, res, decoded, token, user, next) => { - hook.alter('user', user, function(err, user) { + hook.alter('user', user, (err, user) => { if (err) { return next(); } @@ -66,8 +70,8 @@ module.exports = function(router) { req.token = decoded; // Refresh the token that is sent back to the user when appropriate. - req.tokenIssued = parseInt(Date.now() / 1000); - generateToken(token, decoded, res); + req.tokenIssued = Math.trunc(Date.now() / 1000); + generateToken(token, decoded, res, req); router.formio.log('Token', req, 'Using normal token'); if (req.user) { router.formio.log('User', req, req.user._id); @@ -76,7 +80,7 @@ module.exports = function(router) { }); }; - return function tokenHandler(req, res, next) { + return (req, res, next) => { /* eslint-disable max-statements */ // If someone else provided then skip. if (req.user && req.token && res.token) { @@ -84,22 +88,25 @@ module.exports = function(router) { } const token = util.getRequestValue(req, 'x-jwt-token'); - const noToken = function() { + const noToken = () => { router.formio.log('Token', req, 'No token found'); // Try the request with no tokens. delete req.headers['x-jwt-token']; req.user = null; req.token = null; res.token = null; + router.formio.audit('AUTH_ANONYMOUS', req); return next(); }; - if (req.headers['x-token']) { - const keys = process.env.API_KEYS - ? process.env.API_KEYS.split(',').map(key => key.trim()).filter(key => !!key) + const apiKey = req.headers['x-token']; + if (apiKey) { + const apiKeys = process.env.API_KEYS + ? process.env.API_KEYS.split(',').map((key) => key.trim()).filter(Boolean) : []; - if (keys.includes(req.headers['x-token'])) { + if (apiKeys.includes(apiKey)) { + router.formio.audit('AUTH_APIKEY', req); req.isAdmin = true; req.permissionsChecked = true; req.user = null; @@ -114,9 +121,10 @@ module.exports = function(router) { } // Decode/refresh the token and store for later middleware. - jwt.verify(token, router.formio.config.jwt.secret, function(err, decoded) { + jwt.verify(token, jwtConfig.secret, (err, decoded) => { if (err || !decoded) { debug.handler(err || `Token could not decoded: ${token}`); + router.formio.audit('EAUTH_TOKENBAD', req, err); router.formio.log('Token', req, 'Token could not be decoded'); // If the token has expired, send a 440 error (Login Timeout) @@ -125,6 +133,7 @@ module.exports = function(router) { return res.status(400).send('Bad Token'); } else if (err && (err.name === 'TokenExpiredError')) { + router.formio.audit('EAUTH_TOKENEXPIRED', req, err); router.formio.log('Token', req, 'Token Expired'); return res.status(440).send('Token Expired'); } @@ -133,78 +142,90 @@ module.exports = function(router) { } } - // Check to see if this token is allowed to access this path. - if (!router.formio.auth.isTokenAllowed(req, decoded)) { - return noToken(); - } - - // If this is a temporary token, then decode it and set it in the request. - if (decoded.temp) { - router.formio.log('Token', req, 'Using temp token'); - debug.handler('Temp token'); - req.tempToken = decoded; - req.user = null; - req.token = null; - res.token = null; - return next(); - } - - // Allow external tokens. - if (!hook.alter('external', decoded, req)) { - decoded.user.project = decoded.project._id; - return userHandler(req, res, decoded, token, decoded.user, next); - } - - // See if this is a remote token. - if (decoded.project && decoded.permission) { - req.userProject = decoded.project; - req.remotePermission = decoded.permission; - return userHandler(req, res, decoded, token, decoded.user, next); - } - - if (decoded.isAdmin) { - router.formio.log('Token', req, 'User is admin'); - if (req.user) { - router.formio.log('User', req, req.user._id); + hook.alter('tokenDecode', decoded, req, (err, decoded) => { + // Check to see if this token is allowed to access this path. + if (!router.formio.auth.isTokenAllowed(req, decoded)) { + return noToken(); } - req.permissionsChecked = true; - req.isAdmin = true; - req.token = decoded; - return next(); - } - if (!decoded.form || !decoded.form._id) { - return noToken(); - } - if (!decoded.user || !decoded.user._id) { - return noToken(); - } - - // Load the user submission. - const cache = router.formio.cache || formioCache; - cache.loadSubmission(req, decoded.form._id, decoded.user._id, function(err, user) { - if (err) { - // Couldn't load the use, try to fail safely. - user = decoded.user; - } - else if (!user) { + // If this is a temporary token, then decode it and set it in the request. + if (decoded.temp) { + router.formio.log('Token', req, 'Using temp token'); + debug.handler('Temp token'); + req.tempToken = decoded; req.user = null; req.token = null; res.token = null; return next(); } - // Allow anyone to alter the user. - debug.handler(user); + // Allow external tokens. + if (!hook.alter('external', decoded, req)) { + decoded.user.project = decoded.project._id; + return userHandler(req, res, decoded, token, decoded.user, next); + } + + // See if this is a remote token. + if (decoded.project && decoded.permission) { + req.userProject = decoded.project; + req.remotePermission = decoded.permission; + return userHandler(req, res, decoded, token, decoded.user, next); + } + + if (decoded.isAdmin) { + router.formio.log('Token', req, 'User is admin'); + if (req.user) { + router.formio.log('User', req, req.user._id); + } + req.permissionsChecked = true; + req.isAdmin = true; + req.token = decoded; + return next(); + } + + const formId = _.get(decoded, 'form._id'); + const userId = _.get(decoded, 'user._id'); - // Check if the user has reset the password since the token was issued. - if (user.metadata && user.metadata.jwtIssuedAfter && decoded.iat < user.metadata.jwtIssuedAfter) { - router.formio.log('Token', req, 'Token No Longer Valid'); - return res.status(440).send('Token No Longer Valid'); + if (!formId || !userId) { + return noToken(); } - // Call the user handler. - userHandler(req, res, decoded, token, user, next); + // Load the user submission. + const cache = router.formio.cache || formioCache; + cache.loadSubmission(req, formId, userId, (err, user) => { + if (err) { + // Couldn't load the user, try to fail safely. + user = decoded.user; + } + else if (!user) { + req.user = null; + req.token = null; + res.token = null; + return next(); + } + + debug.handler(user); + + hook.alter('validateToken', req, decoded, user, (err) => { + if (err) { + return res.status(440).send(err); + } + + // Check if the user has reset the password since the token was issued. + if ( + !req.skipTokensValidation + && user.metadata + && user.metadata.jwtIssuedAfter + && decoded.iat < user.metadata.jwtIssuedAfter + ) { + router.formio.log('Token', req, 'Token No Longer Valid'); + return res.status(440).send('Token No Longer Valid'); + } + + // Call the user handler. + userHandler(req, res, decoded, token, user, next); + }); + }); }); }); }; diff --git a/forms-flow-forms/src/models/AccessSchema.js b/forms-flow-forms/src/models/AccessSchema.js index dbbbc0f07b..7c25caea50 100644 --- a/forms-flow-forms/src/models/AccessSchema.js +++ b/forms-flow-forms/src/models/AccessSchema.js @@ -5,6 +5,8 @@ module.exports = function(formio) { const available = [ 'read', 'create', + 'update', + 'delete', 'write', 'admin' ]; @@ -17,8 +19,17 @@ module.exports = function(formio) { required: 'A permission type is required to associate an available permission with a Resource.' }, resources: { - type: [formio.mongoose.Schema.Types.ObjectId], - ref: 'form' + type: [formio.mongoose.Schema.Types.Mixed], + ref: 'form', + set(resources) { + // Attempt to convert to objectId. + return resources.map(formio.util.ObjectId); + }, + get(resources) { + return Array.isArray(resources) + ? resources.map((resource) => resource.toString()) + : resources; + } } }, {_id: false}); }; diff --git a/forms-flow-forms/src/models/Action.js b/forms-flow-forms/src/models/Action.js index 83382509aa..7b9e47741b 100644 --- a/forms-flow-forms/src/models/Action.js +++ b/forms-flow-forms/src/models/Action.js @@ -1,5 +1,6 @@ 'use strict'; +const _last = require('lodash/last'); module.exports = function(formio) { const hook = require('../util/hook')(formio); @@ -53,6 +54,7 @@ module.exports = function(formio) { Action.schema = new formio.mongoose.Schema({ title: { type: String, + index: true, required: true }, name: { @@ -74,6 +76,7 @@ module.exports = function(formio) { priority: { type: Number, require: true, + index: true, default: 0 }, settings: { @@ -108,7 +111,9 @@ module.exports = function(formio) { return; } - hook.alter('actionMachineName', `${form.name}:${document.name}`, document, done); + const formMachineName = _last(form.machineName.split(':')); + + hook.alter('actionMachineName', `${formMachineName || form.name}:${document.name}`, document, done); }); }; diff --git a/forms-flow-forms/src/models/BaseModel.js b/forms-flow-forms/src/models/BaseModel.js index c9a28aa78e..b55368d5b1 100644 --- a/forms-flow-forms/src/models/BaseModel.js +++ b/forms-flow-forms/src/models/BaseModel.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function(model) { +module.exports = (model) => { const timestamps = require('../plugins/timestamps'); // Add timestamps to the schema. diff --git a/forms-flow-forms/src/models/FieldMatchAccessPermissionSchema.js b/forms-flow-forms/src/models/FieldMatchAccessPermissionSchema.js new file mode 100644 index 0000000000..44f34f5409 --- /dev/null +++ b/forms-flow-forms/src/models/FieldMatchAccessPermissionSchema.js @@ -0,0 +1,51 @@ +'use strict'; + +module.exports = function(formio) { + const hook = require('../util/hook')(formio); + const typeError = 'Value does not match a selected type'; + + // Defines the permissions schema for field match access form's permissions. + return new formio.mongoose.Schema(hook.alter('fieldMatchAccessPermissionSchema', { + formFieldPath: { + type: String, + required: true + }, + value: { + type: String, + required: true + }, + operator: { + type: String, + enum: ['$eq', '$lt', '$gt', '$lte', '$gte', '$in'], + default: '$eq' + }, + valueType: { + type: String, + enum: ['string', 'number', 'boolean', '[string]', '[number]'], + required: true, + default: 'string', + validate: [ + { + validator: function(type) { + switch (type) { + case 'number': + return isFinite(Number(this.value)); + case 'boolean': + return (this.value === 'true' || this.value === 'false'); + case '[number]': + return this.value.replace(/(^,)|(,$)/g, '') + .split(',') + .map(val => Number(val)) + .every(val => isFinite(val)); + } + }, + message: typeError + } + ] + }, + roles: { + type: [formio.mongoose.Schema.Types.ObjectId], + ref: 'role', + } + }), {_id: false}); +}; diff --git a/forms-flow-forms/src/models/Form.js b/forms-flow-forms/src/models/Form.js index 400745af05..2cc6abfdc1 100644 --- a/forms-flow-forms/src/models/Form.js +++ b/forms-flow-forms/src/models/Form.js @@ -147,6 +147,42 @@ module.exports = (formio) => { }, access: [formio.schemas.PermissionSchema], submissionAccess: [formio.schemas.PermissionSchema], + fieldMatchAccess: { + type: { + read: [formio.schemas.FieldMatchAccessPermissionSchema], + write: [formio.schemas.FieldMatchAccessPermissionSchema], + ceate: [formio.schemas.FieldMatchAccessPermissionSchema], + admin: [formio.schemas.FieldMatchAccessPermissionSchema] + }, + validate: [ + { + validator: function(accessLevels) { + const roles = {}; + Object.entries(accessLevels).forEach(([accessLevel, permissions]) => { + permissions.forEach((permission) => { + permission.roles.forEach((role) => { + if (!roles[role]) { + roles[role] = {}; + } + roles[role][accessLevel] = true; + }); + }); + }); + let errMsg = ''; + Object.entries(roles).forEach(([role, accessLevels]) => { + if (Object.keys(accessLevels).length > 1) { + const levelsWithTheSameRole = Object.keys(accessLevels).join(', '); + errMsg += `The ${role} role has an access on multiple levels: ${levelsWithTheSameRole} /n`; + } + }); + if (errMsg) { + throw new Error(errMsg); + } + return true; + } + } + ] + }, owner: { type: formio.mongoose.Schema.Types.Mixed, ref: 'submission', diff --git a/forms-flow-forms/src/models/Role.js b/forms-flow-forms/src/models/Role.js index bedbc86c79..15de422fe1 100644 --- a/forms-flow-forms/src/models/Role.js +++ b/forms-flow-forms/src/models/Role.js @@ -14,6 +14,7 @@ module.exports = function(formio) { title: { type: String, required: true, + index: true, validate: [ { message: 'Role title must be unique.', diff --git a/forms-flow-forms/src/models/Submission.js b/forms-flow-forms/src/models/Submission.js index dc2c2b7c56..6db992a659 100644 --- a/forms-flow-forms/src/models/Submission.js +++ b/forms-flow-forms/src/models/Submission.js @@ -45,9 +45,18 @@ module.exports = function(formio) { // The roles associated with this submission, if any. // Useful for complex custom resources. roles: { - type: [formio.mongoose.Schema.Types.ObjectId], + type: [formio.mongoose.Schema.Types.Mixed], ref: 'role', - index: true + index: true, + set(roles) { + // Attempt to convert to objectId. + return roles.map(formio.util.ObjectId); + }, + get(roles) { + return Array.isArray(roles) + ? roles.map((role) => role.toString()) + : roles; + } }, // The access associated with this submission. diff --git a/forms-flow-forms/src/models/Token.js b/forms-flow-forms/src/models/Token.js index d33bf99297..f3c4511b97 100644 --- a/forms-flow-forms/src/models/Token.js +++ b/forms-flow-forms/src/models/Token.js @@ -1,6 +1,6 @@ 'use strict'; -module.exports = function(formio) { +module.exports = (formio) => { // Include the hook system. const hook = require('../util/hook')(formio); @@ -17,13 +17,13 @@ module.exports = function(formio) { required: true, default: () => chance.string({ pool: 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', - length: 30 + length: 30, }), validate: [ { message: 'Token key must be unique.', async validator(key) { - const search = hook.alter('tokenSearch', {key: key}, this, key); + const search = hook.alter('tokenSearch', {key}, this, key); // Ignore the id of the token, if this is an update. if (this._id) { @@ -38,23 +38,23 @@ module.exports = function(formio) { catch (err) { return false; } - } - } - ] + }, + }, + ], }, value: { type: String, - required: true + required: true, }, expireAt: { - type: Date - } + type: Date, + }, })); TokenSchema.index({expireAt: 1}, {expireAfterSeconds: 0}); const model = require('./BaseModel')({ - schema: TokenSchema + schema: TokenSchema, }); // Return the model diff --git a/forms-flow-forms/src/models/models.js b/forms-flow-forms/src/models/models.js index 599721bacc..6609a4d1a6 100644 --- a/forms-flow-forms/src/models/models.js +++ b/forms-flow-forms/src/models/models.js @@ -2,7 +2,7 @@ const _ = require('lodash'); -module.exports = function(router) { +module.exports = (router) => { // Implement our hook system. const hook = require('../util/hook')(router.formio); @@ -11,16 +11,16 @@ module.exports = function(router) { action: require('./Action')(router.formio), actionItem: require('./ActionItem')(router.formio), form: require('./Form')(router.formio), + role: require('./Role')(router.formio), schema: require('./Schema')(router.formio), submission: require('./Submission')(router.formio), - role: require('./Role')(router.formio), - token: require('./Token')(router.formio) + token: require('./Token')(router.formio), }); const defs = { schemas: {}, models: {}, - specs: {} + specs: {}, }; _.each(models, (model, name) => { diff --git a/forms-flow-forms/src/plugins/plugins.js b/forms-flow-forms/src/plugins/plugins.js index bfcc9232c0..f96111c208 100644 --- a/forms-flow-forms/src/plugins/plugins.js +++ b/forms-flow-forms/src/plugins/plugins.js @@ -2,5 +2,5 @@ module.exports = { machineName: require('./machineName'), - timestamps: require('./timestamps') + timestamps: require('./timestamps'), }; diff --git a/forms-flow-forms/src/plugins/timestamps.js b/forms-flow-forms/src/plugins/timestamps.js index 0a18a2baf5..83b3ebae39 100644 --- a/forms-flow-forms/src/plugins/timestamps.js +++ b/forms-flow-forms/src/plugins/timestamps.js @@ -1,23 +1,25 @@ 'use strict'; -module.exports = function(schema, options) { +module.exports = (schema, options = {}) => { // Add the created and modified params. schema.add({ created: { type: Date, + index: true, description: 'The date this resource was created.', - 'default': Date.now, - __readonly: true + default: Date.now, + __readonly: true, }, modified: { type: Date, + index: true, description: 'The date this resource was modified.', - __readonly: true - } + __readonly: true, + }, }); // If we wish to make these an index. - if (options && options.index) { + if (options.index) { schema.path('created').index(options.index); schema.path('modified').index(options.index); } diff --git a/forms-flow-forms/src/resources/LegacyValidator.js b/forms-flow-forms/src/resources/LegacyValidator.js new file mode 100644 index 0000000000..4bde77a0e5 --- /dev/null +++ b/forms-flow-forms/src/resources/LegacyValidator.js @@ -0,0 +1,1111 @@ +'use strict'; + +const vm = require('vm'); +const Joi = require('joi'); +const _ = require('lodash'); +const moment = require('moment'); +const cache = require('memory-cache'); +const querystring = require('querystring'); +const util = require('../util/util'); + +const debug = { + validator: require('debug')('formio:validator'), + error: require('debug')('formio:error') +}; + +let hook = null; + +const getErrorMessage = (component, message) => { + return _.get(component, 'validate.customMessage', message); +}; + +/* + * Returns true or false based on visibility. + * + * @param {Object} component + * The form component to check. + * @param {Object} row + * The local data to check. + * @param {Object} data + * The full submission data. + */ +const checkConditional = (form, component, row, data, recurse = false) => { + let isVisible = true; + + if (!component || !component.hasOwnProperty('key')) { + return isVisible; + } + + // Custom conditional logic. Need special case so the eval is isolated in a sandbox + if (component.customConditional) { + try { + // Create the sandbox. + const sandbox = vm.createContext(hook.alter('evalContext', { + data, + row, + component, + moment, + _, + form + }, form)); + + // Execute the script. + const script = new vm.Script(component.customConditional); + script.runInContext(sandbox, { + timeout: 250 + }); + + if (util.isBoolean(sandbox.show)) { + isVisible = util.boolean(sandbox.show); + } + } + catch (e) { + debug.validator('Custom Conditional Error: '); + debug.validator(e); + debug.error(e); + } + } + else { + try { + isVisible = util.checkCondition(component, row, data); + } + catch (err) { + debug.error(err); + } + } + + // If visible and recurse, continue down tree to check parents. + if (isVisible && recurse && component.parent.type !== 'form') { + return !component.parent || checkConditional(form, component.parent, row, data, true); + } + else { + return isVisible; + } +}; + +const getRules = (type) => [ + { + name: 'custom', + params: { + component: Joi.any(), + data: Joi.any(), + form: Joi.any(), + }, + validate(params, value, state, options) { + const {component, data, form} = params; + + let row = state.parent; + let valid = true; + + if (!_.isArray(row)) { + row = [row]; + } + + // If a component has multiple rows of data, e.g. Datagrids, validate each row of data on the backend. + for (let b = 0; b < row.length; b++) { + const _row = row[b]; + + // Try a new sandboxed validation. + try { + // Replace with variable substitutions. + const replace = /({{\s{0,}(.*[^\s]){1}\s{0,}}})/g; + component.validate.custom = component.validate.custom.replace(replace, (match, $1, $2) => _.get(data, $2)); + + // Create the sandbox. + const sandbox = vm.createContext(hook.alter('evalContext', { + input: _.isObject(_row) ? util.getValue({data: _row}, component.key) : _row, + data, + row: _row, + scope: {data}, + component: component, + valid, + form, + _, + moment, + }, form)); + + // Execute the script. + const script = new vm.Script(component.validate.custom); + script.runInContext(sandbox, { + timeout: 100 + }); + valid = sandbox.valid; + } + catch (err) { + // Say this isn't valid based on bad code executed... + valid = err.toString(); + } + + // If there is an error, then set the error object and break from iterations. + if (valid !== true) { + return this.createError(`${type}.custom`, {message: valid}, state, options); + } + } + + return value; // Everything is OK + } + }, + { + name: 'json', + params: { + component: Joi.any(), + data: Joi.any() + }, + validate(params, value, state, options) { + const component = params.component; + const data = params.data; + let row = state.parent; + let valid = true; + + if (!_.isArray(row)) { + row = [row]; + } + + // If a component has multiple rows of data, e.g. Datagrids, validate each row of data on the backend. + for (let b = 0; b < row.length; b++) { + const _row = row[b]; + + try { + valid = util.jsonLogic.apply(component.validate.json, { + data, + row: _row + }); + } + catch (err) { + valid = err.message; + } + + // If there is an error, then set the error object and break from iterations. + if (valid !== true) { + return this.createError(`${type}.json`, {message: valid}, state, options); + } + } + + return value; // Everything is OK + } + }, + { + name: 'hidden', + params: { + component: Joi.any(), + data: Joi.any(), + form: Joi.any(), + }, + validate(params, value, state, options) { + // If we get here than the field has thrown an error. + // If we are hidden, sanitize the data and return true to override the error. + // If not hidden, return an error so the original error remains on the field. + + const {component, data, form} = params; + const row = state.parent; + const isVisible = checkConditional(form, component, row, data, true); + + if (isVisible) { + return value; + } + + return this.createError(`${type}.hidden`, {message: 'hidden with value'}, state, options); + } + }, + { + name: 'maxWords', + params: { + maxWords: Joi.any() + }, + validate(params, value, state, options) { + if (value.trim().split(/\s+/).length <= parseInt(params.maxWords, 10)) { + return value; + } + + return this.createError(`${type}.maxWords`, {message: 'exceeded maximum words.'}, state, options); + } + }, + { + name: 'minWords', + params: { + minWords: Joi.any() + }, + validate(params, value, state, options) { + if (value.trim().split(/\s+/).length >= parseInt(params.minWords, 10)) { + return value; + } + + return this.createError(`${type}.minWords`, {message: 'does not have enough words.'}, state, options); + } + }, + { + name: 'select', + params: { + component: Joi.any(), + submission: Joi.any(), + token: Joi.any(), + async: Joi.any(), + requests: Joi.any() + }, + validate(params, value, state, options) { + // Empty values are fine. + if (!value) { + return value; + } + + const component = params.component; + const submission = params.submission; + const token = params.token; + const async = params.async; + const requests = params.requests; + + // Initialize the request options. + let url = _.get(component, 'validate.select'); + const method = 'GET'; + const query = {}; + const headers = {}; + + // If the url is a boolean value. + if (util.isBoolean(url)) { + url = util.boolean(url); + if (!url) { + return value; + } + + if (component.dataSrc !== 'url') { + return value; + } + + if (!component.data.url || !component.searchField) { + return value; + } + + // Get the validation url. + url = component.data.url; + + // Add the search field. + query[component.searchField] = value; + + // Add the filters. + if (component.filter) { + url += (!url.includes('?') ? '?' : '&') + component.filter; + } + + // If they only wish to return certain fields. + if (component.selectFields) { + query.select = component.selectFields; + } + } + + if (!url) { + return value; + } + + url += (!url.includes('?') ? '?' : '&') + querystring.stringify(query); + + // Make sure to interpolate. + url = util.FormioUtils.interpolate(url, { + data: submission.data + }); + + // Set custom headers. + if (component.data && component.data.headers) { + _.each(component.data.headers, (header) => { + if (header.key) { + headers[header.key] = header.value; + } + }); + } + + // Set form.io authentication. + if (component.authenticate && token) { + headers['x-jwt-token'] = token; + } + + async.push(new Promise((resolve, reject) => { + const cacheKey = `${method}:${url}`; + const cacheTime = (process.env.VALIDATOR_CACHE_TIME || (3 * 60)) * 60 * 1000; + + // Check if this request was cached + const result = cache.get(cacheKey); + if (result !== null) { + debug.validator(cacheKey, 'hit!'); + // Null means no cache hit but is also used as a success callback which we are faking with true here. + if (result === true) { + return resolve(null); + } + else { + return resolve(result); + } + } + debug.validator(cacheKey, 'miss'); + + // Us an existing promise or create a new one. + requests[cacheKey] = requests[cacheKey] || util.fetch(url, {method, headers}) + .then((res) => (res.ok ? res.json() : null)); + + requests[cacheKey] + .then(body => { + if (!body || !body.length) { + const error = { + message: `"${value}" for "${component.label || component.key}" is not a valid selection.`, + path: state.path, + type: 'any.select' + }; + cache.put(cacheKey, error, cacheTime); + return resolve(error); + } + + cache.put(cacheKey, true, cacheTime); + return resolve(null); + }) + .catch(result => { + const error = { + message: `Select validation error: ${result.error}`, + path: state.path, + type: 'any.select' + }; + cache.put(cacheKey, error, cacheTime); + return resolve(error); + }); + })); + + return value; + } + }, + { + name: 'distinct', + params: { + component: Joi.any(), + submission: Joi.any(), + model: Joi.any(), + async: Joi.any() + }, + validate(params, value, state, options) { + const component = params.component; + const submission = params.submission; + const model = params.model; + const async = params.async; + + const path = `data.${state.path.join('.')}`; + + // Allow empty. + if (!value) { + return value; + } + if (_.isEmpty(value)) { + return value; + } + + // Get the query. + const query = {form: util.idToBson(submission.form)}; + if (_.isString(value)) { + query[path] = {$regex: new RegExp(`^${util.escapeRegExp(value)}$`), $options: 'i'}; + } + // FOR-213 - Pluck the unique location id + else if ( + !_.isString(value) && + value.hasOwnProperty('address_components') && + value.hasOwnProperty('place_id') + ) { + query[`${path}.place_id`] = {$regex: new RegExp(`^${util.escapeRegExp(value.place_id)}$`), $options: 'i'}; + } + // Compare the contents of arrays vs the order. + else if (_.isArray(value)) { + query[path] = {$all: value}; + } + else if (_.isObject(value)) { + query[path] = {$eq: value}; + } + + // Only search for non-deleted items. + if (!query.hasOwnProperty('deleted')) { + query['deleted'] = {$eq: null}; + } + + async.push(new Promise((resolve, reject) => { + // Try to find an existing value within the form. + model.findOne(query, (err, result) => { + if (err) { + return resolve({ + message: getErrorMessage(component, err), + path: state.path, + type: 'any.unique' + }); + } + else if (result && submission._id && (result._id.toString() === submission._id)) { + // This matches the current submission which is allowed. + return resolve(null); + } + else if (result) { + return resolve({ + message: getErrorMessage(component, `"${component.label}" must be unique.`), + path: state.path, + type: 'any.unique' + }); + } + return resolve(null); + }); + })); + + return value; // Everything is OK + } + } +]; + +const JoiX = Joi.extend([ + { + name: 'any', + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('any') + }, + { + name: 'string', + base: Joi.string(), + language: { + custom: '{{message}}', + maxWords: '{{message}}', + minWords: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('string') + }, + { + name: 'array', + base: Joi.array(), + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('array') + }, + { + name: 'object', + base: Joi.object(), + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('object') + }, + { + name: 'number', + base: Joi.number(), + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('number') + }, + { + name: 'boolean', + base: Joi.boolean(), + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('boolean') + }, + { + name: 'date', + base: Joi.date(), + language: { + custom: '{{message}}', + json: '{{message}}', + hidden: '{{message}}', + select: '{{message}}', + distinct: '{{message}}' + }, + rules: getRules('date') + } +]); + +/** + * @TODO: Add description. + * + * @param form + * @param model + * @constructor + */ +class Validator { + constructor(form, model, token, decodedToken) { + this.model = model; + this.async = []; + this.requests = {}; + this.form = form; + this.token = token; + this.decodedToken = decodedToken; + } + + /** + * Returns a validator per component. + * + * @param {Object} schema + * The validation schema to modify. + * @param {Object} component + * The form component. + * @param {Object} componentData + * The submission data corresponding to this component. + */ + buildSchema(schema, components, componentData, submission) { + if (!Array.isArray(components)) { + return schema; + } + // Add a validator for each component in the form, with its componentData. + /* eslint-disable max-statements */ + components.forEach((component) => { + let fieldValidator = null; + let componentKey = component.key; + + this.applyLogic(component, componentData, submission.data); + this.calculateValue(component, componentData, submission.data, submission); + + // The value is persistent if it doesn't say otherwise or explicitly says so. + const isPersistent = !component.hasOwnProperty('persistent') || component.persistent; + + let objectSchema; + const stringValidators = { + minLength: 'min', + maxLength: 'max', + minWords: 'minWords', + maxWords: 'maxWords' + }; + /* eslint-disable max-depth, valid-typeof */ + switch (component.type) { + case 'form': { + // Ensure each sub submission at least has an empty object or it won't validate. + _.update(componentData, `${component.key}.data`, value => value ? value : {}); + + const subSubmission = _.get(componentData, component.key, {}); + + // If this has already been submitted, then it has been validated. + if (!subSubmission._id && component.components) { + const formSchema = this.buildSchema( + {}, + component.components, + subSubmission, + subSubmission + ); + fieldValidator = JoiX.object().unknown(true).keys({ + data: JoiX.object().keys(formSchema) + }); + } + else { + fieldValidator = JoiX.object(); + } + break; + } + case 'dynamicWizard': + case 'editgrid': + case 'datagrid': + component.multiple = false; + objectSchema = this.buildSchema( + {}, + component.components, + _.get(componentData, component.key, componentData), + submission + ); + + fieldValidator = JoiX.array().items(JoiX.object().keys(objectSchema)).options({stripUnknown: false}); + break; + case 'tagpad': + objectSchema = this.buildSchema( + {}, + component.components, + _.get(componentData, component.key, componentData).map(dot => dot.data), + submission + ); + fieldValidator = JoiX.array().items(JoiX.object({ + coordinate: JoiX.object({ + x: JoiX.number(), + y: JoiX.number(), + width: JoiX.number(), + height: JoiX.number() + }), + data: JoiX.object().keys(objectSchema) + })).options({stripUnknown: false}); + break; + case 'tree': + objectSchema = this.buildSchema( + {}, + component.components, + _.get(componentData, component.key, componentData).data, + submission + ); + fieldValidator = JoiX.object({ + data: JoiX.object().keys(objectSchema), + children: JoiX.array().items(JoiX.lazy(() => fieldValidator)) + }).options({stripUnknown: false}); + break; + case 'container': + objectSchema = this.buildSchema( + {}, + component.components, + _.get(componentData, component.key, componentData), + submission + ); + + fieldValidator = JoiX.object().keys(objectSchema); + break; + case 'address': + objectSchema = this.buildSchema( + {}, + component.components, + componentData, + submission + ); + + fieldValidator = fieldValidator || JoiX.any(); + break; + case 'fieldset': + case 'panel': + case 'well': + this.buildSchema(schema, component.components, componentData, submission); + break; + case 'table': + if (!Array.isArray(component.rows)) { + break; + } + component.rows.forEach((row) => { + if (!Array.isArray(row)) { + return; + } + row.forEach((column) => { + this.buildSchema(schema, column.components, componentData, submission); + }); + }); + break; + case 'columns': + if (!Array.isArray(component.columns)) { + break; + } + component.columns.forEach((column) => { + this.buildSchema(schema, column.components, componentData, submission); + }); + break; + case 'textfield': + case 'textarea': + case 'phonenumber': + if (component.as === 'json') { + fieldValidator = JoiX.object(); + } + else { + fieldValidator = JoiX.string().allow(''); + for (const name in stringValidators) { + const funcName = stringValidators[name]; + if ( + component.validate && + component.validate.hasOwnProperty(name) && + _.isNumber(component.validate[name]) && + component.validate[name] >= 0 + ) { + fieldValidator = fieldValidator[funcName](component.validate[name]); + } + } + } + break; + case 'select': + if (component.validate && component.validate.select) { + fieldValidator = JoiX.any().select(component, submission, this.token, this.async, this.requests); + } + fieldValidator = fieldValidator || JoiX.any(); + break; + case 'email': + fieldValidator = JoiX.string().email().allow(''); + break; + case 'number': + fieldValidator = JoiX.number().empty(null); + if (component.validate) { + // If the step is provided... we can infer float vs. integer. + if (component.validate.step && (component.validate.step !== 'any')) { + const parts = component.validate.step.split('.'); + if (parts.length === 1) { + fieldValidator = fieldValidator.integer(); + } + else { + fieldValidator = fieldValidator.precision(parts[1].length); + } + } + + _.each(['min', 'max', 'greater', 'less'], (check) => { + if (component.validate.hasOwnProperty(check) && _.isNumber(component.validate[check])) { + fieldValidator = fieldValidator[check](component.validate[check]); + } + }); + } + break; + case 'signature': + fieldValidator = JoiX.string().allow(''); + break; + case 'checkbox': + if (component.name && !_.find(components, ['key', component.name])) { + componentKey = component.name; + } + fieldValidator = fieldValidator || JoiX.any(); + break; + default: + // Allow custom components to have subcomponents as well (like layout components). + if (component.components && Array.isArray(component.components)) { + if (component.tree) { + objectSchema = this.buildSchema( + {}, + component.components, + _.get(componentData, component.key, componentData), + submission + ); + fieldValidator = JoiX.object().keys(objectSchema); + } + else { + this.buildSchema( + schema, + component.components, + componentData, + submission + ); + } + } + fieldValidator = fieldValidator || JoiX.any(); + break; + } + /* eslint-enable max-depth, valid-typeof */ + + if (componentKey && (componentKey.indexOf('.') === -1) && component.validate) { + // Add required validator. + if (component.validate.required) { + fieldValidator = fieldValidator.required().empty().disallow('', null); + } + + // Add regex validator + if (component.validate.pattern) { + try { + const regex = new RegExp(component.validate.pattern); + fieldValidator = fieldValidator.regex(regex); + } + catch (err) { + debug.error(err); + } + } + + // Add the custom validations. + if (component.validate && component.validate.custom) { + fieldValidator = fieldValidator.custom(component, submission.data, this.form); + } + + // Add the json logic validations. + if (component.validate && component.validate.json) { + fieldValidator = fieldValidator.json(component, submission.data); + } + } + + // If the value must be unique. + if (component.unique) { + fieldValidator = fieldValidator.distinct(component, submission, this.model, this.async); + } + + //if multiple masks input, then data is object with 'value' field, and validation should be applied to that field + if (component.allowMultipleMasks) { + fieldValidator = JoiX.object().keys({ + value: fieldValidator, + maskName: JoiX.string() + }); + //additionally apply required rule to the field itself + if (component.validate && component.validate.required) { + fieldValidator = fieldValidator.required(); + } + } + + // Make sure to change this to an array if multiple is checked or component type is file. + if (component.multiple || component.type === 'file') { + // Allow(null) was added since some text fields have empty strings converted to null when multiple which then + // throws an error on re-validation. Allowing null fixes the issue. + fieldValidator = JoiX.array().sparse().items(fieldValidator.allow(null)).options({stripUnknown: false}); + if (component.validate) { + // If a multi-value is required, make sure there is at least one. + if (component.validate.required && !component.validate.minItems) { + fieldValidator = fieldValidator.min(1).required(); + } + else if (component.validate.minItems) { + fieldValidator = fieldValidator.min(component.validate.minItems).required(); + } + if (component.validate.maxItems) { + fieldValidator = fieldValidator.max(component.validate.maxItems); + } + } + } + + // Only run validations for persistent fields. + if (componentKey && fieldValidator && isPersistent) { + schema[componentKey] = fieldValidator.hidden(component, submission.data, this.form); + } + }); + /* eslint-enable max-statements */ + + return schema; + } + + static setHook(_hook) { + hook = _hook; + } + + evalContext(context) { + context.form = this.form; + return hook.alter('evalContext', context, this.form); + } + + applyLogic(component, row, data) { + if (!Array.isArray(component.logic)) { + return; + } + + component.logic.forEach((logic) => { + if (!Array.isArray(logic.actions)) { + return; + } + + const result = util.FormioUtils.checkTrigger(component, logic.trigger, row, data); + if (result) { + logic.actions.forEach((action) => { + switch (action.type) { + case 'property': + util.FormioUtils.setActionProperty(component, action, result, row, data); + break; + case 'value': + try { + // Create the sandbox. + const sandbox = vm.createContext(this.evalContext({ + value: _.get(row, component.key), + data, + row, + component, + token: this.decodedToken, + result, + })); + + // Execute the script. + const script = new vm.Script(action.value); + script.runInContext(sandbox, { + timeout: 250, + }); + + _.set(row, component.key, sandbox.value); + } + catch (e) { + debug.validator('Custom Logic Error: '); + debug.validator(e); + debug.error(e); + } + break; + case 'mergeComponentSchema': { + const sandbox = vm.createContext(this.evalContext({ + value: _.get(row, component.key), + data, + row, + component, + result, + })); + + const script = new vm.Script(action.schemaDefinition); + script.runInContext(sandbox, { + timeout: 250, + }); + + _.assign(component, sandbox.schema); + + break; + } + } + }); + } + }); + } + + calculateValue(component, row, data, submission) { + if (component.calculateServer && component.calculateValue) { + if (_.isString(component.calculateValue)) { + try { + const sandbox = vm.createContext(this.evalContext({ + value: _.get(row, component.key), + form: this.form, + submission, + data, + row, + component, + util, + utils: util, + moment, + _, + token: this.decodedToken, + })); + + // Execute the script. + const script = new vm.Script(component.calculateValue); + script.runInContext(sandbox, { + timeout: 250 + }); + + _.set(row, component.key, sandbox.value); + } + catch (e) { + debug.error(e); + } + } + else { + try { + _.set(row, component.key, util.jsonLogic(component.calculateValue, { + form: this.form, + submission, + data, + row, + component, + util, + utils: util, + moment, + _, + token: this.decodedToken, + })); + } + catch (e) { + debug.error(e); + } + } + } + } + + /** + * Validate a submission for a form. + * + * @param {Object} submission + * The data submission object. + * @param next + * The callback function to pass the results. + */ + /* eslint-disable max-statements */ + validate(submission, next) { + debug.validator('Starting validation'); + + // Skip validation if no data is provided. + if (!submission.data) { + debug.validator('No data skipping validation'); + debug.validator(submission); + return next(); + } + + // Build the JoiX validation schema. + let schema = { + // Start off with the _id key. + _id: JoiX.string().meta({primaryKey: true}) + }; + + // Create the validator schema. + schema = JoiX.object().keys(this.buildSchema(schema, this.form.components, submission.data, submission)); + + // Iterate the list of components one time to build the path map. + const components = {}; + util.eachComponent(this.form.components, (component, path) => { + if (component.hasOwnProperty('key')) { + components[path] = component; + } + }, true, '', true); + + JoiX.validate(submission.data, schema, {stripUnknown: true, abortEarly: false}, (validateErr, value) => { + // Wait for all async validators to complete and add any errors. + Promise.all(this.async).then(errors => { + errors = errors.filter(item => item); + // Add in any asyncronous errors. + if (errors.length) { + if (!validateErr) { + validateErr = new Error('Validation failed'); + validateErr.name = 'ValidationError'; + validateErr.details = errors; + } + else { + validateErr.details = validateErr.details.concat(errors); + } + } + if (validateErr) { + // Remove any conditionally hidden validations. Joi will still throw the errors but we don't want them since the + // fields are hidden. + validateErr.details = validateErr.details.filter((detail) => { + let result = { + hidden: false + }; + if (detail.type.includes('.hidden')) { + const component = components[detail.path.filter(isNaN).join('.')]; + + const clearOnHide = util.isBoolean(_.get(component, 'clearOnHide')) ? + util.boolean(_.get(component, 'clearOnHide')) : true; + + if (clearOnHide) { + _.unset(value, detail.path); + } + + result.hidden = true; + } + else { + // Walk up the path tree to determine if the component is hidden. + result = detail.path.reduce((result, key) => { + result.path.push(key); + + const component = components[result.path.filter(isNaN).join('.')]; + + // Form "data" keys don't have components. + if (component) { + result.hidden = result.hidden || + !checkConditional(this.form, component, + _.get(value, result.path.slice(0, result.path.length - 1), value), result.submission, true); + + const clearOnHide = util.isBoolean(_.get(component, 'clearOnHide')) ? + util.boolean(_.get(component, 'clearOnHide')) : true; + + if (clearOnHide && result.hidden) { + _.unset(value, result.path); + } + } + else { + // Since this is a subform, change the submission object going to the conditionals. + result.submission = _.get(value, result.path); + } + + return result; + }, {path: [], hidden: false, submission: value}); + } + + return !result.hidden; + }); + + // Only throw error if there are still errors. + if (validateErr.details.length) { + validateErr._validated = value; + + return next(validateErr); + } + else { + validateErr._object = value; + } + } + + submission.data = value; + next(null, value); + }); + }); + } + /* eslint-enable max-statements */ +} + +module.exports = Validator; diff --git a/forms-flow-forms/src/resources/SubmissionResource.js b/forms-flow-forms/src/resources/SubmissionResource.js index 8fe0576f3c..d99394f9e9 100644 --- a/forms-flow-forms/src/resources/SubmissionResource.js +++ b/forms-flow-forms/src/resources/SubmissionResource.js @@ -70,6 +70,7 @@ module.exports = (router) => { }), router.formio.middleware.ownerFilter, router.formio.middleware.submissionResourceAccessFilter, + router.formio.middleware.submissionFieldMatchAccessFilter, handlers.beforeIndex, ]; handlers.afterIndex = [ diff --git a/forms-flow-forms/src/resources/Validator.js b/forms-flow-forms/src/resources/Validator.js index ddc58205ca..6646771134 100644 --- a/forms-flow-forms/src/resources/Validator.js +++ b/forms-flow-forms/src/resources/Validator.js @@ -1,939 +1,42 @@ 'use strict'; - -const vm = require('vm'); -const Joi = require('joi'); const _ = require('lodash'); -const util = require('../util/util'); -const request = require('request-promise-native'); -const moment = require('moment'); -const cache = require('memory-cache'); - +const {Formio} = require('../util/util'); const debug = { validator: require('debug')('formio:validator'), error: require('debug')('formio:error') }; -let hook = null; - -/* - * Returns true or false based on visibility. - * - * @param {Object} component - * The form component to check. - * @param {Object} row - * The local data to check. - * @param {Object} data - * The full submission data. - */ -const checkConditional = (form, component, row, data, recurse = false) => { - let isVisible = true; - - if (!component || !component.hasOwnProperty('key')) { - return isVisible; - } - - // Custom conditional logic. Need special case so the eval is isolated in a sandbox - if (component.customConditional) { - try { - // Create the sandbox. - const sandbox = vm.createContext(hook.alter('evalContext', { - data, - row, - component, - moment, - _, - form, - }, form)); - - // Execute the script. - const script = new vm.Script(component.customConditional); - script.runInContext(sandbox, { - timeout: 250 - }); - - if (util.isBoolean(sandbox.show)) { - isVisible = util.boolean(sandbox.show); - } - } - catch (e) { - debug.validator('Custom Conditional Error: '); - debug.validator(e); - debug.error(e); - } - } - else { - try { - isVisible = util.checkCondition(component, row, data); - } - catch (err) { - debug.error(err); - } - } - - // If visible and recurse, continue down tree to check parents. - if (isVisible && recurse && component.parent.type !== 'form') { - return !component.parent || checkConditional(form, component.parent, row, data, true); - } - else { - return isVisible; - } -}; - -const getRules = (type) => [ - { - name: 'custom', - params: { - component: Joi.any(), - data: Joi.any(), - form: Joi.any(), - }, - validate(params, value, state, options) { - const {component, data, form} = params; - - let row = state.parent; - let valid = true; - - if (!_.isArray(row)) { - row = [row]; - } - - // If a component has multiple rows of data, e.g. Datagrids, validate each row of data on the backend. - for (let b = 0; b < row.length; b++) { - const _row = row[b]; - - // Try a new sandboxed validation. - try { - // Replace with variable substitutions. - const replace = /({{\s{0,}(.*[^\s]){1}\s{0,}}})/g; - component.validate.custom = component.validate.custom.replace(replace, (match, $1, $2) => _.get(data, $2)); - - // Create the sandbox. - const sandbox = vm.createContext(hook.alter('evalContext', { - input: _.isObject(_row) ? util.getValue({data: _row}, component.key) : _row, - data, - row: _row, - scope: {data}, - component: component, - valid, - form, - _, - moment, - }, form)); - - // Execute the script. - const script = new vm.Script(component.validate.custom); - script.runInContext(sandbox, { - timeout: 100 - }); - valid = sandbox.valid; - } - catch (err) { - // Say this isn't valid based on bad code executed... - valid = err.toString(); - } - - // If there is an error, then set the error object and break from iterations. - if (valid !== true) { - return this.createError(`${type}.custom`, {message: valid}, state, options); - } - } - - return value; // Everything is OK - } - }, - { - name: 'json', - params: { - component: Joi.any(), - data: Joi.any() - }, - validate(params, value, state, options) { - const component = params.component; - const data = params.data; - let row = state.parent; - let valid = true; - - if (!_.isArray(row)) { - row = [row]; - } - - // If a component has multiple rows of data, e.g. Datagrids, validate each row of data on the backend. - for (let b = 0; b < row.length; b++) { - const _row = row[b]; - - try { - valid = util.jsonLogic.apply(component.validate.json, { - data, - row: _row - }); - } - catch (err) { - valid = err.message; - } - - // If there is an error, then set the error object and break from iterations. - if (valid !== true) { - return this.createError(`${type}.json`, {message: valid}, state, options); - } - } - - return value; // Everything is OK - } - }, - { - name: 'hidden', - params: { - component: Joi.any(), - data: Joi.any(), - form: Joi.any(), - }, - validate(params, value, state, options) { - // If we get here than the field has thrown an error. - // If we are hidden, sanitize the data and return true to override the error. - // If not hidden, return an error so the original error remains on the field. - - const {component, data, form} = params; - const row = state.parent; - - const isVisible = checkConditional(form, component, row, data, true); - - if (isVisible) { - return value; - } - - return this.createError(`${type}.hidden`, {message: 'hidden with value'}, state, options); - } - }, - { - name: 'maxWords', - params: { - maxWords: Joi.any() - }, - validate(params, value, state, options) { - if (value.trim().split(/\s+/).length <= parseInt(params.maxWords, 10)) { - return value; - } - - return this.createError(`${type}.maxWords`, {message: 'exceeded maximum words.'}, state, options); - } - }, - { - name: 'minWords', - params: { - minWords: Joi.any() - }, - validate(params, value, state, options) { - if (value.trim().split(/\s+/).length >= parseInt(params.minWords, 10)) { - return value; - } - - return this.createError(`${type}.minWords`, {message: 'does not have enough words.'}, state, options); - } - }, - { - name: 'select', - params: { - component: Joi.any(), - submission: Joi.any(), - token: Joi.any(), - async: Joi.any(), - requests: Joi.any() - }, - validate(params, value, state, options) { - // Empty values are fine. - if (!value) { - return value; - } - - const component = params.component; - const submission = params.submission; - const token = params.token; - const async = params.async; - const requests = params.requests; - - // Initialize the request options. - const requestOptions = { - url: _.get(component, 'validate.select'), - method: 'GET', - qs: {}, - json: true, - headers: {} - }; - - // If the url is a boolean value. - if (util.isBoolean(requestOptions.url)) { - requestOptions.url = util.boolean(requestOptions.url); - if (!requestOptions.url) { - return value; - } - - if (component.dataSrc !== 'url') { - return value; - } - - if (!component.data.url || !component.searchField) { - return value; - } - - // Get the validation url. - requestOptions.url = component.data.url; - - // Add the search field. - requestOptions.qs[component.searchField] = value; - - // Add the filters. - if (component.filter) { - requestOptions.url += (!requestOptions.url.includes('?') ? '?' : '&') + component.filter; - } - - // If they only wish to return certain fields. - if (component.selectFields) { - requestOptions.qs.select = component.selectFields; - } - } - - if (!requestOptions.url) { - return value; - } - - // Make sure to interpolate. - requestOptions.url = util.FormioUtils.interpolate(requestOptions.url, { - data: submission.data - }); - - // Set custom headers. - if (component.data && component.data.headers) { - _.each(component.data.headers, (header) => { - if (header.key) { - requestOptions.headers[header.key] = header.value; - } - }); - } - - // Set form.io authentication. - if (component.authenticate && token) { - requestOptions.headers['x-jwt-token'] = token; - } - - async.push(new Promise((resolve, reject) => { - /* eslint-disable prefer-template */ - const cacheKey = `${requestOptions.method}:${requestOptions.url}?` + - Object.keys(requestOptions.qs).map(key => key + '=' + requestOptions.qs[key]).join('&'); - /* eslint-enable prefer-template */ - const cacheTime = (process.env.VALIDATOR_CACHE_TIME || (3 * 60)) * 60 * 1000; - - // Check if this request was cached - const result = cache.get(cacheKey); - if (result !== null) { - debug.validator(cacheKey, 'hit!'); - // Null means no cache hit but is also used as a success callback which we are faking with true here. - if (result === true) { - return resolve(null); - } - else { - return resolve(result); - } - } - debug.validator(cacheKey, 'miss'); - - // Us an existing promise or create a new one. - requests[cacheKey] = requests[cacheKey] || request(requestOptions); - - requests[cacheKey] - .then(body => { - if (!body || !body.length) { - const error = { - message: `"${value}" for "${component.label || component.key}" is not a valid selection.`, - path: state.path, - type: 'any.select' - }; - cache.put(cacheKey, error, cacheTime); - return resolve(error); - } - - cache.put(cacheKey, true, cacheTime); - return resolve(null); - }) - .catch(result => { - const error = { - message: `Select validation error: ${result.error}`, - path: state.path, - type: 'any.select' - }; - cache.put(cacheKey, error, cacheTime); - return resolve(error); - }); - })); - - return value; - } - }, - { - name: 'distinct', - params: { - component: Joi.any(), - submission: Joi.any(), - model: Joi.any(), - async: Joi.any() - }, - validate(params, value, state, options) { - const component = params.component; - const submission = params.submission; - const model = params.model; - const async = params.async; - - const path = `data.${state.path.join('.')}`; - - // Allow empty. - if (!value) { - return value; - } - if (_.isEmpty(value)) { - return value; - } - - // Get the query. - const query = {form: util.idToBson(submission.form)}; - if (_.isString(value)) { - query[path] = {$regex: new RegExp(`^${util.escapeRegExp(value)}$`), $options: 'i'}; - } - // FOR-213 - Pluck the unique location id - else if ( - !_.isString(value) && - value.hasOwnProperty('address_components') && - value.hasOwnProperty('place_id') - ) { - query[`${path}.place_id`] = {$regex: new RegExp(`^${util.escapeRegExp(value.place_id)}$`), $options: 'i'}; - } - // Compare the contents of arrays vs the order. - else if (_.isArray(value)) { - query[path] = {$all: value}; - } - else if (_.isObject(value)) { - query[path] = {$eq: value}; - } - - // Only search for non-deleted items. - if (!query.hasOwnProperty('deleted')) { - query['deleted'] = {$eq: null}; - } - - async.push(new Promise((resolve, reject) => { - // Try to find an existing value within the form. - model.findOne(query, (err, result) => { - if (err) { - return resolve({ - message: err, - path: state.path, - type: 'any.unique' - }); - } - else if (result && submission._id && (result._id.toString() === submission._id)) { - // This matches the current submission which is allowed. - return resolve(null); - } - else if (result) { - return resolve({ - message: `"${component.label}" must be unique.`, - path: state.path, - type: 'any.unique' - }); - } - return resolve(null); - }); - })); - - return value; // Everything is OK - } - } -]; - -const JoiX = Joi.extend([ - { - name: 'any', - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('any') - }, - { - name: 'string', - base: Joi.string(), - language: { - custom: '{{message}}', - maxWords: '{{message}}', - minWords: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('string') - }, - { - name: 'array', - base: Joi.array(), - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('array') - }, - { - name: 'object', - base: Joi.object(), - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('object') - }, - { - name: 'number', - base: Joi.number(), - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('number') - }, - { - name: 'boolean', - base: Joi.boolean(), - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('boolean') - }, - { - name: 'date', - base: Joi.date(), - language: { - custom: '{{message}}', - json: '{{message}}', - hidden: '{{message}}', - select: '{{message}}', - distinct: '{{message}}' - }, - rules: getRules('date') - } -]); - /** - * @TODO: Add description. + * @TODO: Isomorphic validation system. * * @param form * @param model * @constructor */ class Validator { - constructor(form, model, token, decodedToken) { + constructor(form, model, token, decodedToken, hook) { this.model = model; - this.async = []; - this.requests = {}; this.form = form; this.token = token; - this.decodedToken = decodedToken; - } + this.hook = hook; + + const self = this; + const evalContext = Formio.Components.components.component.prototype.evalContext; + Formio.Components.components.component.prototype.evalContext = function(additional) { + return evalContext.call(this, self.evalContext(additional)); + }; - static setHook(_hook) { - hook = _hook; + // Change Formio.getToken to return the server decoded token. + const getToken = Formio.getToken; + Formio.getToken = (options) => { + return options.decode ? decodedToken : getToken(options); + }; } evalContext(context) { + context = context || {}; context.form = this.form; - return hook.alter('evalContext', context, this.form); - } - - /** - * Returns a validator per component. - * - * @param {Object} schema - * The validation schema to modify. - * @param {Object} component - * The form component. - * @param {Object} componentData - * The submission data corresponding to this component. - */ - buildSchema(schema, components, componentData, submission) { - if (!Array.isArray(components)) { - return schema; - } - // Add a validator for each component in the form, with its componentData. - /* eslint-disable max-statements */ - components.forEach((component) => { - let fieldValidator = null; - - this.applyLogic(component, componentData, submission.data); - this.calculateValue(component, componentData, submission.data); - - // The value is persistent if it doesn't say otherwise or explicitly says so. - const isPersistent = !component.hasOwnProperty('persistent') || component.persistent; - - let objectSchema; - const stringValidators = { - minLength: 'min', - maxLength: 'max', - minWords: 'minWords', - maxWords: 'maxWords' - }; - /* eslint-disable max-depth, valid-typeof */ - switch (component.type) { - case 'form': { - // Ensure each sub submission at least has an empty object or it won't validate. - _.update(componentData, `${component.key}.data`, value => value ? value : {}); - - const subSubmission = _.get(componentData, component.key, {}); - - // If this has already been submitted, then it has been validated. - if (!subSubmission._id && component.components) { - const formSchema = this.buildSchema( - {}, - component.components, - subSubmission, - subSubmission - ); - fieldValidator = JoiX.object().unknown(true).keys({ - data: JoiX.object().keys(formSchema) - }); - } - else { - fieldValidator = JoiX.object(); - } - break; - } - case 'editgrid': - case 'datagrid': - component.multiple = false; - objectSchema = this.buildSchema( - {}, - component.components, - _.get(componentData, component.key, componentData), - submission - ); - - fieldValidator = JoiX.array().items(JoiX.object().keys(objectSchema)).options({stripUnknown: false}); - break; - case 'container': - objectSchema = this.buildSchema( - {}, - component.components, - _.get(componentData, component.key, componentData), - submission - ); - - fieldValidator = JoiX.object().keys(objectSchema); - break; - case 'fieldset': - case 'panel': - case 'well': - this.buildSchema(schema, component.components, componentData, submission); - break; - case 'table': - if (!Array.isArray(component.rows)) { - break; - } - component.rows.forEach((row) => { - if (!Array.isArray(row)) { - return; - } - row.forEach((column) => { - this.buildSchema(schema, column.components, componentData, submission); - }); - }); - break; - case 'columns': - if (!Array.isArray(component.columns)) { - break; - } - component.columns.forEach((column) => { - this.buildSchema(schema, column.components, componentData, submission); - }); - break; - case 'textfield': - case 'textarea': - case 'phonenumber': - if (component.as === 'json') { - fieldValidator = JoiX.object(); - } - else { - fieldValidator = JoiX.string().allow(''); - for (const name in stringValidators) { - const funcName = stringValidators[name]; - if ( - component.validate && - component.validate.hasOwnProperty(name) && - _.isNumber(component.validate[name]) && - component.validate[name] >= 0 - ) { - fieldValidator = fieldValidator[funcName](component.validate[name]); - } - } - } - break; - case 'select': - if (component.validate && component.validate.select) { - fieldValidator = JoiX.any().select(component, submission, this.token, this.async, this.requests); - } - fieldValidator = fieldValidator || JoiX.any(); - break; - case 'email': - fieldValidator = JoiX.string().email().allow(''); - break; - case 'number': - fieldValidator = JoiX.number().empty(null); - if (component.validate) { - // If the step is provided... we can infer float vs. integer. - if (component.validate.step && (component.validate.step !== 'any')) { - const parts = component.validate.step.split('.'); - if (parts.length === 1) { - fieldValidator = fieldValidator.integer(); - } - else { - fieldValidator = fieldValidator.precision(parts[1].length); - } - } - - _.each(['min', 'max', 'greater', 'less'], (check) => { - if (component.validate.hasOwnProperty(check) && _.isNumber(component.validate[check])) { - fieldValidator = fieldValidator[check](component.validate[check]); - } - }); - } - break; - case 'signature': - fieldValidator = JoiX.string().allow(''); - break; - case 'checkbox': - if (component.name && !_.find(components, ['key', component.name])) { - schema[component.name] = JoiX.any(); - } - fieldValidator = fieldValidator || JoiX.any(); - break; - default: - // Allow custom components to have subcomponents as well (like layout components). - if (component.components && Array.isArray(component.components)) { - if (component.tree) { - objectSchema = this.buildSchema( - {}, - component.components, - _.get(componentData, component.key, componentData), - submission - ); - fieldValidator = JoiX.object().keys(objectSchema); - } - else { - this.buildSchema( - schema, - component.components, - componentData, - submission - ); - } - } - fieldValidator = fieldValidator || JoiX.any(); - break; - } - /* eslint-enable max-depth, valid-typeof */ - - if (component.key && (component.key.indexOf('.') === -1) && component.validate) { - // Add required validator. - if (component.validate.required) { - fieldValidator = fieldValidator.required().empty().disallow('', null); - } - - // Add regex validator - if (component.validate.pattern) { - try { - const regex = new RegExp(component.validate.pattern); - fieldValidator = fieldValidator.regex(regex); - } - catch (err) { - debug.error(err); - } - } - - // Add the custom validations. - if (component.validate && component.validate.custom) { - fieldValidator = fieldValidator.custom(component, submission.data, this.form); - } - - // Add the json logic validations. - if (component.validate && component.validate.json) { - fieldValidator = fieldValidator.json(component, submission.data); - } - } - - // If the value must be unique. - if (component.unique) { - fieldValidator = fieldValidator.distinct(component, submission, this.model, this.async); - } - - //if multiple masks input, then data is object with 'value' field, and validation should be applied to that field - if (component.allowMultipleMasks) { - fieldValidator = JoiX.object().keys({ - value: fieldValidator, - maskName: JoiX.string() - }); - //additionally apply required rule to the field itself - if (component.validate && component.validate.required) { - fieldValidator = fieldValidator.required(); - } - } - - // Make sure to change this to an array if multiple is checked or component type is file. - if (component.multiple || component.type === 'file') { - // Allow(null) was added since some text fields have empty strings converted to null when multiple which then - // throws an error on re-validation. Allowing null fixes the issue. - fieldValidator = JoiX.array().sparse().items(fieldValidator.allow(null)).options({stripUnknown: false}); - if (component.validate) { - // If a multi-value is required, make sure there is at least one. - if (component.validate.required && !component.validate.minItems) { - fieldValidator = fieldValidator.min(1).required(); - } - else if (component.validate.minItems) { - fieldValidator = fieldValidator.min(component.validate.minItems).required(); - } - if (component.validate.maxItems) { - fieldValidator = fieldValidator.max(component.validate.maxItems); - } - } - } - - // Only run validations for persistent fields. - if (component.key && fieldValidator && isPersistent) { - schema[component.key] = fieldValidator.hidden(component, submission.data, this.form); - } - }); - /* eslint-enable max-statements */ - - return schema; - } - - applyLogic(component, row, data) { - if (!Array.isArray(component.logic)) { - return; - } - - component.logic.forEach((logic) => { - if (!Array.isArray(logic.actions)) { - return; - } - - const result = util.FormioUtils.checkTrigger(component, logic.trigger, row, data); - if (result) { - logic.actions.forEach((action) => { - switch (action.type) { - case 'property': - util.FormioUtils.setActionProperty(component, action, result, row, data); - break; - case 'value': - try { - // Create the sandbox. - const sandbox = vm.createContext(this.evalContext({ - value: _.get(row, component.key), - data, - row, - component, - token: this.decodedToken, - result, - })); - - // Execute the script. - const script = new vm.Script(action.value); - script.runInContext(sandbox, { - timeout: 250, - }); - - _.set(row, component.key, sandbox.value); - } - catch (e) { - debug.validator('Custom Logic Error: '); - debug.validator(e); - debug.error(e); - } - break; - case 'mergeComponentSchema': { - const sandbox = vm.createContext(this.evalContext({ - value: _.get(row, component.key), - data, - row, - component, - result, - })); - - const script = new vm.Script(action.schemaDefinition); - script.runInContext(sandbox, { - timeout: 250, - }); - - _.assign(component, sandbox.schema); - - break; - } - } - }); - } - }); - } - - calculateValue(component, row, data) { - if (component.calculateServer && component.calculateValue) { - if (_.isString(component.calculateValue)) { - try { - const sandbox = vm.createContext(this.evalContext({ - value: _.get(row, component.key), - data, - row, - component, - util, - moment, - token: this.decodedToken, - })); - - // Execute the script. - const script = new vm.Script(component.calculateValue); - script.runInContext(sandbox, { - timeout: 250 - }); - - _.set(row, component.key, sandbox.value); - } - catch (e) { - // Need to log error for calculated value. - } - } - else { - try { - _.set(row, component.key, util.jsonLogic(component.calculateValue, { - data, - row, - _, - token: this.decodedToken, - })); - } - catch (e) { - // Need to log error for calculated value. - } - } - } + return this.hook.alter('evalContext', context, this.form); } /** @@ -955,106 +58,93 @@ class Validator { return next(); } - // Build the JoiX validation schema. - let schema = { - // Start off with the _id key. - _id: JoiX.string().meta({primaryKey: true}) - }; - - // Create the validator schema. - schema = JoiX.object().keys(this.buildSchema(schema, this.form.components, submission.data, submission)); - - // Iterate the list of components one time to build the path map. - const components = {}; - util.eachComponent(this.form.components, (component, path) => { - if (component.hasOwnProperty('key')) { - components[path] = component; - } - }, true, '', true); - - JoiX.validate(submission.data, schema, {stripUnknown: true, abortEarly: false}, (validateErr, value) => { - // Wait for all async validators to complete and add any errors. - Promise.all(this.async).then(errors => { - errors = errors.filter(item => item); - // Add in any asyncronous errors. - if (errors.length) { - if (!validateErr) { - validateErr = new Error('Validation failed'); - validateErr.name = 'ValidationError'; - validateErr.details = errors; - } - else { - validateErr.details = validateErr.details.concat(errors); + const unsets = []; + const conditionallyInvisibleComponents = []; + const emptyData = _.isEmpty(submission.data); + let unsetsEnabled = false; + + // Create the form, then check validity. + Formio.createForm(this.form, { + server: true, + hooks: { + setDataValue: function(value, key, data) { + if (!unsetsEnabled) { + return value; + } + // Check if this component is not persistent. + if (this.component.hasOwnProperty('persistent') && + (!this.component.persistent || this.component.persistent === 'client-only') + ) { + unsets.push({key, data}); + } + // Check if this component is conditionally hidden and does not set clearOnHide to false. + else if ( + (!this.component.hasOwnProperty('clearOnHide') || this.component.clearOnHide) && + (!this.conditionallyVisible() || !this.parentVisible) + ) { + conditionallyInvisibleComponents.push({component: this, key, data}); + } + else if ( + this.component.type === 'password' && value === this.defaultValue + ) { + unsets.push({key, data}); } + return value; } - if (validateErr) { - // Remove any conditionally hidden validations. Joi will still throw the errors but we don't want them since the - // fields are hidden. - validateErr.details = validateErr.details.filter((detail) => { - let result = { - hidden: false - }; - if (detail.type.includes('.hidden')) { - const component = components[detail.path.filter(isNaN).join('.')]; - - const clearOnHide = util.isBoolean(_.get(component, 'clearOnHide')) ? - util.boolean(_.get(component, 'clearOnHide')) : true; - - if (clearOnHide) { - _.unset(value, detail.path); - } - - result.hidden = true; - } - else { - // Walk up the path tree to determine if the component is hidden. - result = detail.path.reduce((result, key) => { - result.path.push(key); - - const component = components[result.path.filter(isNaN).join('.')]; - - // Form "data" keys don't have components. - if (component) { - result.hidden = result.hidden || - !checkConditional(this.form, component, - _.get(value, result.path.slice(0, result.path.length - 1), value), result.submission, true); + } + }).then((form) => { + // Set the validation config. + form.validator.config = { + db: this.model, + token: this.token, + form: this.form, + submission: submission + }; - const clearOnHide = util.isBoolean(_.get(component, 'clearOnHide')) ? - util.boolean(_.get(component, 'clearOnHide')) : true; + // Set the submission data + form.data = submission.data; - if (clearOnHide && result.hidden) { - _.unset(value, result.path); - } - } - else { - // Since this is a subform, change the submission object going to the conditionals. - result.submission = _.get(value, result.path); - } + // Perform calculations and conditions. + form.calculateValue(); + form.checkConditions(); - return result; - }, {path: [], hidden: false, submission: value}); - } + // Reset the data + form.data = {}; - return !result.hidden; - }); + // Set the value to the submission. + unsetsEnabled = true; + form.setValue(submission, { + sanitize: true + }); - // Only throw error if there are still errors. - if (validateErr.details.length) { - validateErr._validated = value; + // Check the visibility of conditionally visible components after unconditionally visible + _.forEach(conditionallyInvisibleComponents, ({component, key, data}) => { + if (!component.conditionallyVisible() || !component.parentVisible) { + unsets.push({key, data}); + } + }); - return next(validateErr); - } - else { - validateErr._object = value; - } + // Check the validity of the form. + form.checkAsyncValidity(null, true).then((valid) => { + if (valid) { + // Clear the non-persistent fields. + unsets.forEach((unset) => _.unset(unset.data, unset.key)); + submission.data = emptyData ? {} : form.data; + const visibleComponents = (form.getComponents() || []).map(comp => comp.component); + return next(null, submission.data, visibleComponents); } - submission.data = value; - next(null, value); - }); - }); + const details = []; + form.errors.forEach((error) => error.messages.forEach((message) => details.push(message))); + + // Return the validation errors. + return next({ + name: 'ValidationError', + details: details + }); + }).catch(next); + }).catch(next); } - /* eslint-enable max-statements */ } module.exports = Validator; diff --git a/forms-flow-forms/src/templates/default.json b/forms-flow-forms/src/templates/default.json index 6f628f1f83..6fd80fbf8a 100644 --- a/forms-flow-forms/src/templates/default.json +++ b/forms-flow-forms/src/templates/default.json @@ -78,7 +78,8 @@ { "type": "email", "persistent": true, - "unique": false, + "unique": true, + "required": true, "protected": false, "defaultValue": "", "suffix": "", @@ -173,7 +174,8 @@ { "type": "email", "persistent": true, - "unique": false, + "unique": true, + "required": true, "protected": false, "defaultValue": "", "suffix": "", diff --git a/forms-flow-forms/src/templates/export.js b/forms-flow-forms/src/templates/export.js index a9645d8ce2..0a1f25bc16 100644 --- a/forms-flow-forms/src/templates/export.js +++ b/forms-flow-forms/src/templates/export.js @@ -114,6 +114,11 @@ module.exports = (router) => { // Export forms. const exportForms = function(_export, _map, options, next) { + let includeFormFields = []; + if (options && options.includeFormFields) { + includeFormFields = options.includeFormFields; + } + formio.resources.form.model .find(hook.alter('formQuery', {deleted: {$eq: null}}, options)) .lean(true) @@ -141,6 +146,8 @@ module.exports = (router) => { 'access', 'submissionAccess', 'properties', + 'controller', + ...includeFormFields, ); _map.forms[form._id.toString()] = machineName; }); @@ -236,6 +243,10 @@ module.exports = (router) => { if (router.get) { router.get('/export', (req, res, next) => { const options = hook.alter('exportOptions', {}, req, res); + if (options) { + options.includeFormFields = (req.query.include && req.query.include.split(',').filter((field) => !!field)); + } + exportTemplate(options, (err, data) => { if (err) { return next(err.message || err); diff --git a/forms-flow-forms/src/templates/import.js b/forms-flow-forms/src/templates/import.js index 90d4509a3e..40f9892170 100644 --- a/forms-flow-forms/src/templates/import.js +++ b/forms-flow-forms/src/templates/import.js @@ -137,6 +137,11 @@ module.exports = (router) => { let changes = false; + const getFormRevision = (_vid) => { + const formRevision = (parseInt(_vid, 10) + 1) || 1; + return formRevision.toString(); + }; + const formName = entity.form; // Attempt to add a form. if (template.forms && template.forms[entity.form] && template.forms[entity.form]._id) { @@ -144,6 +149,10 @@ module.exports = (router) => { if (template.forms[formName].hasOwnProperty('_vid') && template.forms[formName]._vid) { entity.formRevision = template.forms[formName]._vid.toString(); } + else if (template.forms[formName].revisions) { + // Make sure revision is set if revisions are enabled on the form + entity.formRevision = getFormRevision(template.forms[formName]._vid); + } changes = true; } @@ -153,6 +162,9 @@ module.exports = (router) => { if (template.resources[formName].hasOwnProperty('_vid') && template.resources[formName]._vid) { entity.formRevision = template.resources[formName]._vid.toString(); } + else if (template.resources[formName].revisions) { + entity.formRevision = getFormRevision(template.resources[formName]._vid); + } changes = true; } @@ -400,6 +412,12 @@ module.exports = (router) => { ] }; return hook.alter(`importFormQuery`, query, document, template); + }, + deleteAllActions(form, done) { + const prun = require('../util/delete')(router); + prun.action(null, form).then(() => { + done(); + }); } }, action: { @@ -412,7 +430,11 @@ module.exports = (router) => { return false; }, transform: (template, action) => { - resourceMachineNameToId(template, action.settings); + const isResourceChanged = resourceMachineNameToId(template, action.settings); + if (!isResourceChanged && action.settings && action.settings.resource) { + action.settings.resource = ''; + } + roleMachineNameToId(template, action.settings); // If no changes were made, the form was invalid and we can't insert the action. @@ -519,12 +541,22 @@ module.exports = (router) => { items[machineName] = result.toObject(); debug.save(machineName); debug.save(items[machineName]); - next(); + if (entity.hasOwnProperty('deleteAllActions')) { + return entity.deleteAllActions(updatedDoc._id, next); + } + return next(); }); }; + const setVid = (document, _vid) => { + if (document && document.hasOwnProperty('_vid')) { + document._vid = _vid; + } + }; + if (!doc) { debug.install(`Existing not found (${document.machineName})`); + setVid(document, 0); /* eslint-disable new-cap */ return saveDoc(new model(document)); /* eslint-enable new-cap */ @@ -532,6 +564,7 @@ module.exports = (router) => { else if (!createOnly) { debug.install(`Existing found`); doc = _.assign(doc, document); + setVid(doc, 0); debug.install(doc); return saveDoc(doc); } @@ -581,6 +614,12 @@ module.exports = (router) => { }); }; + const alterFormSave = (forms, alter) => { + return Object.values(forms || {}).map((form) => { + return async.apply((done) => alter(form, done)); + }); + }; + /** * Import the formio template. * @@ -619,7 +658,23 @@ module.exports = (router) => { cleanUp([ {entity: entities.resource, forms: template.resources}, {entity: entities.form, forms: template.forms}, - ], template, done); + ], template, (err, data) => { + if (err) { + return done(err); + } + + if (!alter.formSave) { + return done(null, data); + } + + return async.series( + [ + ...alterFormSave(data.forms, alter.formSave), + ...alterFormSave(data.resources, alter.formSave), + ], + () => done(null, data), + ); + }); }); }; diff --git a/forms-flow-forms/src/util/email.js b/forms-flow-forms/src/util/email.js index 18c63986cd..1f1b2dd3ea 100644 --- a/forms-flow-forms/src/util/email.js +++ b/forms-flow-forms/src/util/email.js @@ -170,8 +170,7 @@ module.exports = (formio) => { // Get the parameters for the email. params.form = form; // Allow hooks to alter params. - params = hook.alter('actionContext', params, req, ); - return resolve(params); + Promise.resolve(hook.alter('actionContext', params, req)).then(params => resolve(params), reject); }) .catch(reject); }); @@ -466,10 +465,10 @@ module.exports = (formio) => { let emails = []; debug.send(`message.sendEach: ${message.sendEach}`); - debug.send(`email: ${JSON.stringify(email)}`); + // debug.send(`email: ${JSON.stringify(email)}`); if (message.sendEach === true) { const addresses = _.uniq(email.to.split(',').map(_.trim)); - debug.send(`addresses: ${JSON.stringify(addresses)}`); + // debug.send(`addresses: ${JSON.stringify(addresses)}`); // Make a copy of the email for each recipient. emails = addresses.map((address) => Object.assign({}, email, {to: address})); } @@ -477,7 +476,7 @@ module.exports = (formio) => { emails = [email]; } - debug.send(`emails: ${JSON.stringify(emails)}`); + // debug.send(`emails: ${JSON.stringify(emails)}`); const chunks = _.chunk(emails, EMAIL_CHUNK_SIZE); return chunks.reduce((result, chunk) => { @@ -508,7 +507,7 @@ module.exports = (formio) => { return transporter.sendMail(email, (err, info) => { if (err) { - return reject(err); + debug.error(err); } return resolve(info); diff --git a/forms-flow-forms/src/util/error-codes.js b/forms-flow-forms/src/util/error-codes.js index 4d5bc116e4..6fe80ffad4 100644 --- a/forms-flow-forms/src/util/error-codes.js +++ b/forms-flow-forms/src/util/error-codes.js @@ -37,7 +37,7 @@ module.exports = { }, user: { ENOUSER: 'User not found.', - ENONAMEP: 'No user neme provided.', + ENONAMEP: 'No user name provided.', }, role: { EROLESLOAD: 'Cannot load the Roles.', @@ -49,7 +49,7 @@ module.exports = { EREQRECUR: 'Too many recursive requests.', }, resource: { - ENOIDP: 'Resource id is misssing.', + ENOIDP: 'Resource id is missing.', ENOHANDLER: 'Resource handler not found.', }, }; diff --git a/forms-flow-forms/src/util/fetch.js b/forms-flow-forms/src/util/fetch.js new file mode 100644 index 0000000000..40171efc9d --- /dev/null +++ b/forms-flow-forms/src/util/fetch.js @@ -0,0 +1,133 @@ +'use strict'; + +const {Agent} = require('https'); +const {parse} = require('url'); +const fetch = require('node-fetch'); +const HttpsProxyAgent = require('https-proxy-agent'); +const AbortController = require('abort-controller'); +const _ = require('lodash'); + +/** + * Parses a value to a UrlWithStringQuery object + * + * @param {any} value The value to be parsed + * @returns {UrlWithStringQuery} The parsed url + */ +function parseUrl(value) { + try { + if (value && value.href) { + return parse(value.href); + } + return parse(value); + } + catch (err) { + return parse(''); + } +} + +/** + * The server HTTP proxy configuration. + * + * @type {UrlWithStringQuery} + */ +const httpProxy = parseUrl(process.env.http_proxy || process.env.HTTP_PROXY); + +/** + * The server HTTPS proxy configuration. + * + * @type {UrlWithStringQuery} + */ +const httpsProxy = parseUrl(process.env.https_proxy || process.env.HTTPS_PROXY); + +/** + * The server no proxy domain list. + * + * @type {string[]} + */ +const noProxyDomains = (process.env.no_proxy || process.env.NO_PROXY || '') + .split(',') + .map((domain) => domain.trim()) + .filter((domain) => !!domain); + +/** + * Checks wether a url should be excluded from proxying or not depending on the server configuration. + * + * @param {any} url The url to check. + * @returns {boolean} Wether the url should be excluded from proxying or not. + */ +function noProxy(url) { + const {protocol, hostname, host} = parseUrl(url); + // If invalid url, just return true + if (!protocol || !host || !hostname) { + return true; + } + if (protocol === 'http:' && httpProxy.host === null) { + return true; + } + if (protocol === 'https:' && httpsProxy.host === null) { + return true; + } + return noProxyDomains.some( + (domain) => (host.endsWith(domain) || hostname.endsWith(domain)) + ); +} + +module.exports = (url, options = {}) => { + // Parse the request input information + const input = parseUrl(url); + + // Check that the target url is a valid URL + if (!input.host) { + return Promise.reject(new TypeError('Only absolute URLs are supported')); + } + + // Convert timeout into abort controller; + let keepAlive = () => {}; + if (options.timeout) { + const ctrl = new AbortController(); + keepAlive = _.debounce(() => { + ctrl.abort(); + }, options.timeout); + options.signal = ctrl.signal; + delete options.timeout; + // Start the timer + keepAlive(); + } + + let qs = ''; + if (options.qs) { + qs = Object.keys(options.qs).reduce((str, q) => `${str}${(str ? '&' : '?')}${q}=${options.qs[q]}`, ''); + } + + // Shallow clone the request init options + const init = Object.assign({}, options); + + // Check if using HTTPS + const isHTTPS = input.protocol === 'https:'; + + // Check if system is forcing invalid tls rejection (should be rejected by default) + const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED !== '0'; + + // Check if we need to use http(s) proxy or not + if (!noProxy(input)) { + // Set up the http(s) proxy agent + init.agent = new HttpsProxyAgent({ + ...(isHTTPS ? httpsProxy : httpProxy), + rejectUnauthorized, + }); + } + else if (isHTTPS) { + // Set up the https agent if no proxy and https + options.agent = new Agent({ + rejectUnauthorized, + }); + } + + return fetch(`${url}${qs}`, init) + .then((response) => { + if (response.ok) { + response.body.on('data', keepAlive); + } + return response; + }); +}; diff --git a/forms-flow-forms/src/util/util.js b/forms-flow-forms/src/util/util.js index 92e9fe2343..986a9c5b76 100644 --- a/forms-flow-forms/src/util/util.js +++ b/forms-flow-forms/src/util/util.js @@ -4,11 +4,10 @@ const mongoose = require('mongoose'); const ObjectID = require('mongodb').ObjectID; const _ = require('lodash'); const nodeUrl = require('url'); -const Q = require('q'); -const FormioUtils = require('formiojs/utils').default; const deleteProp = require('delete-property').default; const workerUtils = require('formio-workers/util'); const errorCodes = require('./error-codes.js'); +const fetch = require('./fetch'); const vm = require('vm'); const debug = { idToBson: require('debug')('formio:util:idToBson'), @@ -16,8 +15,37 @@ const debug = { removeProtectedFields: require('debug')('formio:util:removeProtectedFields') }; -FormioUtils.Evaluator.noeval = true; -FormioUtils.Evaluator.evaluator = function(func, args) { +// Define a few global noop placeholder shims and import the component classes +global.Text = class {}; +global.HTMLElement = class {}; +global.HTMLCanvasElement = class {}; +global.navigator = {userAgent: ''}; +global.document = { + createElement: () => ({}), + cookie: '', + getElementsByTagName: () => [], + documentElement: { + style: [], + firstElementChild: {appendChild: () => {}} + } +}; +global.window = {addEventListener: () => {}, Event: {}, navigator: global.navigator}; +global.btoa = (str) => { + return (str instanceof Buffer) ? + str.toString('base64') : + Buffer.from(str.toString(), 'binary').toString('base64'); +}; +global.self = global; +const Formio = require('formiojs/formio.form.js'); +global.Formio = Formio.Formio; + +// Remove onChange events from all renderer displays. +_.each(Formio.Displays.displays, (display) => { + display.prototype.onChange = _.noop; +}); + +Formio.Utils.Evaluator.noeval = true; +Formio.Utils.Evaluator.evaluator = function(func, args) { return function() { const params = _.keys(args); const sandbox = vm.createContext({ @@ -38,7 +66,8 @@ FormioUtils.Evaluator.evaluator = function(func, args) { }; const Utils = { - FormioUtils: FormioUtils, + Formio: Formio.Formio, + FormioUtils: Formio.Utils, deleteProp: deleteProp, /** @@ -162,7 +191,7 @@ const Utils = { */ createSubRequest(req) { // Determine how many child requests have been made. - let childRequests = req.childRequests || 0; + const childRequests = req.childRequests || 0; // Break recursive child requests. if (childRequests > 5) { @@ -183,7 +212,7 @@ const Utils = { childReq.user = req.user; childReq.modelQuery = null; childReq.countQuery = null; - childReq.childRequests = ++childRequests; + childReq.childRequests = childRequests + 1; childReq.permissionsChecked = false; // Delete the actions cache. @@ -214,7 +243,7 @@ const Utils = { * Whether or not to include layout components. * @param {String} path */ - eachComponent: FormioUtils.eachComponent.bind(FormioUtils), + eachComponent: Formio.Utils.eachComponent.bind(Formio.Utils), /** * Get a component by its key @@ -227,7 +256,7 @@ const Utils = { * @returns {Object} * The component that matches the given key, or undefined if not found. */ - getComponent: FormioUtils.getComponent.bind(FormioUtils), + getComponent: Formio.Utils.getComponent.bind(Formio.Utils), /** * Define if component should be considered input component @@ -238,7 +267,7 @@ const Utils = { * @returns {Boolean} * If component is input or not */ - isInputComponent: FormioUtils.isInputComponent.bind(FormioUtils), + isInputComponent: Formio.Utils.isInputComponent.bind(Formio.Utils), /** * Flatten the form components for data manipulation. @@ -251,7 +280,7 @@ const Utils = { * @returns {Object} * The flattened components map. */ - flattenComponents: FormioUtils.flattenComponents.bind(FormioUtils), + flattenComponents: Formio.Utils.flattenComponents.bind(Formio.Utils), /** * Get the value for a component key, in the given submission. @@ -261,7 +290,7 @@ const Utils = { * @param {String} key * A for components API key to search for. */ - getValue: FormioUtils.getValue.bind(FormioUtils), + getValue: Formio.Utils.getValue.bind(Formio.Utils), /** * Determine if a component is a layout component or not. @@ -272,7 +301,7 @@ const Utils = { * @returns {Boolean} * Whether or not the component is a layout component. */ - isLayoutComponent: FormioUtils.isLayoutComponent.bind(FormioUtils), + isLayoutComponent: Formio.Utils.isLayoutComponent.bind(Formio.Utils), /** * Apply JSON logic functionality. @@ -281,7 +310,7 @@ const Utils = { * @param row * @param data */ - jsonLogic: FormioUtils.jsonLogic, + jsonLogic: Formio.Utils.jsonLogic, /** * Check if the condition for a component is true or not. @@ -290,7 +319,7 @@ const Utils = { * @param row * @param data */ - checkCondition: FormioUtils.checkCondition.bind(FormioUtils), + checkCondition: Formio.Utils.checkCondition.bind(Formio.Utils), /** * Return the objectId. @@ -310,7 +339,11 @@ const Utils = { } }, - /** + flattenComponentsForRender: workerUtils.flattenComponentsForRender.bind(workerUtils), + renderFormSubmission: workerUtils.renderFormSubmission.bind(workerUtils), + renderComponentValue: workerUtils.renderComponentValue.bind(workerUtils), + +/** * Search the request headers for the given key. * * @param req @@ -329,10 +362,6 @@ const Utils = { return false; }, - flattenComponentsForRender: workerUtils.flattenComponentsForRender.bind(workerUtils), - renderFormSubmission: workerUtils.renderFormSubmission.bind(workerUtils), - renderComponentValue: workerUtils.renderComponentValue.bind(workerUtils), - /** * Search the request query for the given key. * @@ -468,12 +497,14 @@ const Utils = { }, /** - * A promisified version of request. Use this if you need - * to be able to mock requests for tests, as it's much easier - * to mock this than the individual required 'request' modules - * in each file. + * A node-fetch shim adding support for http(s) proxy and allowing + * invalid tls certificates (to be used with self signed certificates). + * + * @param {any} url The request url string or url like object. + * @param {any} options The request options object. + * @returns {Promise} The promise with the node-fetch response object. */ - request: Q.denodeify(require('request')), + fetch, /** * Utility function to ensure the given id is always a BSON object. @@ -492,7 +523,6 @@ const Utils = { } catch (e) { debug.idToBson(`Unknown _id given: ${_id}, typeof: ${typeof _id}`); - _id = false; } return _id; @@ -661,10 +691,136 @@ const Utils = { }); }, + castValue(valueType, value) { + switch (valueType) { + case 'string': + return value.toString(); + case 'number': + return Number(value); + case 'boolean': + return value === 'true'; + case '[number]': + return value.replace(/(^,)|(,$)/g, '') + .split(',') + .map(val => Number(val)); + case '[string]': + return value.replace(/(^,)|(,$)/g, '') + .split(',') + .map(val => val.toString()); + } + }, + /** * Application error codes. */ errorCodes, + + valuePath(prefix, key) { + return `${prefix ? `${prefix}.` : ''}${key}`; + }, + + layoutComponents: [ + 'panel', + 'table', + 'well', + 'columns', + 'fieldset', + 'tabs', + ], + + eachValue( + components, + data, + fn, + context, + path = '', + ) { + components.forEach((component) => { + if (Array.isArray(component.components)) { + // If tree type is an array of objects like datagrid and editgrid. + if (['datagrid', 'editgrid', 'dynamicWizard'].includes(component.type) || component.arrayTree) { + (_.get(data, component.key) || []).forEach((row, index) => { + this.eachValue( + component.components, + row, + fn, + context, + this.valuePath(path, `${component.key}[${index}]`), + ); + }); + } + else if (['form'].includes(component.type)) { + this.eachValue( + component.components, + _.get(data, `${component.key}.data`, {}), + fn, + context, + this.valuePath(path, `${component.key}.data`), + ); + } + else if ( + ['container'].includes(component.type) || + ( + component.tree && + !this.layoutComponents.includes(component.type) + ) + ) { + this.eachValue( + component.components, + _.get(data, component.key), + fn, + context, + this.valuePath(path, component.key), + ); + } + else { + this.eachValue( + component.components, + data, + fn, + context, + path, + ); + } + } + else if (Array.isArray(component.columns)) { + // Handle column like layout components. + component.columns.forEach((column) => { + this.eachValue( + column.components, + data, + fn, + context, + path, + ); + }); + } + else if (Array.isArray(component.rows)) { + // Handle table like layout components. + component.rows.forEach((row) => { + if (Array.isArray(row)) { + row.forEach((column) => { + this.eachValue( + column.components, + data, + fn, + context, + path, + ); + }); + } + }); + } + + // Call the callback for each component. + fn({ + ...context, + data, + component, + path, + }); + }); + }, }; module.exports = Utils; diff --git a/forms-flow-forms/test/actions.js b/forms-flow-forms/test/actions.js index 209d75cb87..d15edff13a 100644 --- a/forms-flow-forms/test/actions.js +++ b/forms-flow-forms/test/actions.js @@ -1,19 +1,19 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); -var assert = require('assert'); -var _ = require('lodash'); -var chance = new (require('chance'))(); -var http = require('http'); -var url = require('url'); -var docker = process.env.DOCKER; - -module.exports = function(app, template, hook) { - var Helper = require('./helper')(app); - describe('Actions', function() { +const request = require('./formio-supertest'); +const assert = require('assert'); +const _ = require('lodash'); +const chance = new (require('chance'))(); +const http = require('http'); +const url = require('url'); +const docker = process.env.DOCKER; + +module.exports = (app, template, hook) => { + const Helper = require('./helper')(app); + describe('Actions', () => { // Store the temp form for this test suite. - var tempForm = { + let tempForm = { title: 'Temp Form', name: 'tempForm', path: 'temp', @@ -28,7 +28,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -39,27 +39,27 @@ module.exports = function(app, template, hook) { label: 'foo', inputMask: '', inputType: 'text', - input: true - } - ] + input: true, + }, + ], }; // Store the temp action for this test suite. - var tempAction = {}; - describe('Bootstrap', function() { - it('Create a Form for Action tests', function(done) { + let tempAction = {}; + describe('Bootstrap', () => { + it('Create a Form for Action tests', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(tempForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -71,9 +71,9 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 3); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, tempForm.components); tempForm = response; @@ -86,8 +86,8 @@ module.exports = function(app, template, hook) { settings: { resources: [tempForm._id.toString()], username: 'username', - password: 'password' - } + password: 'password', + }, }; // Store the JWT for future API calls. @@ -98,20 +98,20 @@ module.exports = function(app, template, hook) { }); }); - describe('Permissions - Project Level - Project Owner', function() { - it('A Project Owner should be able to Create an Action', function(done) { + describe('Permissions - Project Level - Project Owner', () => { + it('A Project Owner should be able to Create an Action', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .post(hook.alter('url', `/form/${tempForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, tempAction.title); assert.equal(response.name, tempAction.name); @@ -129,18 +129,18 @@ module.exports = function(app, template, hook) { }); }); - it('A Project Owner should be able to Read an Action', function(done) { + it('A Project Owner should be able to Read an Action', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempAction); // Store the JWT for future API calls. @@ -150,22 +150,22 @@ module.exports = function(app, template, hook) { }); }); - it('A Project Owner should be able to Update an Action', function(done) { - var updatedAction = _.clone(tempAction); + it('A Project Owner should be able to Update an Action', (done) => { + const updatedAction = _.clone(tempAction); updatedAction.title = 'Updated'; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send({title: updatedAction.title}) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, updatedAction); tempAction = response; @@ -177,9 +177,9 @@ module.exports = function(app, template, hook) { }); }); - it('A Project Owner should not be able to Patch an Action', function(done) { + it('A Project Owner should not be able to Patch an Action', (done) => { request(app) - .patch(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .patch(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send([{op: 'replace', path: 'title', value: 'Patched'}]) // .expect('Content-Type', /json/) @@ -187,20 +187,20 @@ module.exports = function(app, template, hook) { .end(done); }); - it('A Project Owner should be able to Read the Index of Actions', function(done) { + it('A Project Owner should be able to Read the Index of Actions', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .get(hook.alter('url', `/form/${tempForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.equal(response.length, 2); - _.each(response, function(action) { + _.each(response, (action) => { if (action.name === 'login') { assert.deepEqual(action, tempAction); } @@ -217,13 +217,13 @@ module.exports = function(app, template, hook) { }); }); - it('Cant access an Action without a valid Action Id', function(done) { + it('Cant access an Action without a valid Action Id', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action/2342342344234', template)) + .get(hook.alter('url', `/form/${tempForm._id}/action/2342342344234`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(400) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -236,10 +236,10 @@ module.exports = function(app, template, hook) { }); }); - describe('Permissions - Project Level - Authenticated User', function() { - it('A user should not be able to Create an Action for a User-Created Project Form', function(done) { + describe('Permissions - Project Level - Authenticated User', () => { + it('A user should not be able to Create an Action for a User-Created Project Form', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .post(hook.alter('url', `/form/${tempForm._id}/action`, template)) .set('x-jwt-token', template.users.user1.token) .send(tempAction) .expect('Content-Type', /text\/plain/) @@ -247,18 +247,18 @@ module.exports = function(app, template, hook) { .end(done); }); - it('A user should not be able to Read an Action for a User-Created Project Form', function(done) { + it('A user should not be able to Read an Action for a User-Created Project Form', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('A user should not be able to Update an Action for a User-Created Project Form', function(done) { + it('A user should not be able to Update an Action for a User-Created Project Form', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send({foo: 'bar'}) .expect('Content-Type', /text\/plain/) @@ -266,18 +266,18 @@ module.exports = function(app, template, hook) { .end(done); }); - it('A user should not be able to Read the Index of Actions for a User-Created Project Form', function(done) { + it('A user should not be able to Read the Index of Actions for a User-Created Project Form', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .get(hook.alter('url', `/form/${tempForm._id}/action`, template)) .set('x-jwt-token', template.users.user1.token) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('A user should not be able to Delete an Action for a User-Created Project Form', function(done) { + it('A user should not be able to Delete an Action for a User-Created Project Form', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect('Content-Type', /text\/plain/) .expect(401) @@ -285,60 +285,60 @@ module.exports = function(app, template, hook) { }); }); - describe('Permissions - Project Level - Anonymous User', function() { - it('An Anonymous user should not be able to Create an Action for a User-Created Project Form', function(done) { + describe('Permissions - Project Level - Anonymous User', () => { + it('An Anonymous user should not be able to Create an Action for a User-Created Project Form', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .post(hook.alter('url', `/form/${tempForm._id}/action`, template)) .send(tempAction) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('An Anonymous user should not be able to Read an Action for a User-Created Project Form', function(done) { + it('An Anonymous user should not be able to Read an Action for a User-Created Project Form', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('An Anonymous user should not be able to Update an Action for a User-Created Project Form', function(done) { + it('An Anonymous user should not be able to Update an Action for a User-Created Project Form', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .send({foo: 'bar'}) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('An Anonymous user should not be able to Read the Index of Actions for a User-Created Project Form', function(done) { + it('An Anonymous user should not be able to Read the Index of Actions for a User-Created Project Form', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/action', template)) + .get(hook.alter('url', `/form/${tempForm._id}/action`, template)) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); - it('An Anonymous user should not be able to Delete an Action for a User-Created Project Form', function(done) { + it('An Anonymous user should not be able to Delete an Action for a User-Created Project Form', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .expect('Content-Type', /text\/plain/) .expect(401) .end(done); }); }); - describe('Action MachineNames', function() { - var _action; - var name = chance.word(); - var helper; + describe('Action MachineNames', () => { + let _action; + const name = chance.word(); + let helper; - before(function() { + before(() => { helper = new Helper(template.users.admin, template); }); - it('Actions expose their machineNames through the api', function(done) { + it('Actions expose their machineNames through the api', (done) => { helper .form({name: name}) .action({ @@ -350,49 +350,51 @@ module.exports = function(app, template, hook) { settings: { url: 'example.com', username: '', - password: '' - } + password: '', + }, }) - .execute(function(err, result) { + .execute((err, result) => { if (err) { return done(err); } - var action = result.getAction('Webhook'); + const action = result.getAction('Webhook'); assert(action.hasOwnProperty('machineName')); _action = action; + done(); }); }); - it('A user can modify their action machineNames', function(done) { - var newMachineName = chance.word(); + it('A user can modify their action machineNames', (done) => { + const newMachineName = chance.word(); helper .action(name, { _id: _action._id, - machineName: newMachineName + machineName: newMachineName, }) - .execute(function(err, result) { + .execute((err, result) => { if (err) { return done(err); } - var action = result.getAction('Webhook'); + const action = result.getAction('Webhook'); assert(action.hasOwnProperty('machineName')); assert.equal(action.machineName, newMachineName); + done(); }); }); }); - describe('Webhook Functionality tests', function() { + describe('Webhook Functionality tests', () => { if (docker) { return; } // The temp form with the add RoleAction for existing submissions. - var webhookForm = { + let webhookForm = { title: 'Webhook Form', name: 'webhookform', path: 'webhookform', @@ -407,7 +409,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -418,7 +420,7 @@ module.exports = function(app, template, hook) { label: 'First Name', inputMask: '', inputType: 'text', - input: true + input: true, }, { type: 'textfield', @@ -427,7 +429,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -438,7 +440,7 @@ module.exports = function(app, template, hook) { label: 'Last Name', inputMask: '', inputType: 'text', - input: true + input: true, }, { type: 'email', @@ -453,7 +455,7 @@ module.exports = function(app, template, hook) { label: 'Email', inputType: 'email', tableView: true, - input: true + input: true, }, { type: 'password', @@ -468,47 +470,48 @@ module.exports = function(app, template, hook) { label: 'Password', inputType: 'password', tableView: true, - input: true - } - ] + input: true, + }, + ], }; - var port = 4002; - var webhookSubmission = null; - var webhookHandler = function(body) {}; + let port = 4002; + let webhookSubmission = null; + let webhookHandler = () => {}; // Create a new server. - var newServer = function(ready) { - var server = http.createServer(function(request) { - var body = []; - request.on('data', function(chunk) { + const newServer = (ready) => { + const server = http.createServer((request) => { + let body = []; + request.on('data', (chunk) => { body.push(chunk); - }).on('end', function() { + }).on('end', () => { body = Buffer.concat(body).toString(); webhookHandler(body ? JSON.parse(body) : body, url.parse(request.url, true)); }); }); server.port = port++; - server.url = 'http://localhost:'+ server.port; - server.listen(server.port, function(err) { - hook.alter('webhookServer', server, app, template, function(err, server) { + server.url = `http://localhost:${server.port}`; + server.listen(server.port, () => { + hook.alter('webhookServer', server, app, template, (err, server) => { ready(err, server); }); }); }; - it('Should create the form and action for the webhook tests', function(done) { - newServer(function(err, server) { + it('Should create the form and action for the webhook tests', (done) => { + newServer((err, server) => { if (err) { return done(err); } + request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(webhookForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -516,7 +519,7 @@ module.exports = function(app, template, hook) { webhookForm = res.body; template.users.admin.token = res.headers['x-jwt-token']; request(app) - .post(hook.alter('url', '/form/' + webhookForm._id + '/action', template)) + .post(hook.alter('url', `/form/${webhookForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ title: 'Webhook', @@ -528,24 +531,26 @@ module.exports = function(app, template, hook) { settings: { url: server.url, username: '', - password: '' - } + password: '', + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } + template.users.admin.token = res.headers['x-jwt-token']; + done(); }); }); }); }); - it('Should send a webhook with create data.', function(done) { - webhookHandler = function(body) { + it('Should send a webhook with create data.', (done) => { + webhookHandler = (body) => { body = hook.alter('webhookBody', body); assert.equal(body.params.formId, webhookForm._id.toString()); @@ -555,22 +560,24 @@ module.exports = function(app, template, hook) { assert.equal(body.request.data.lastName, 'Person'); assert(body.request.data.password !== '123testing', 'Passwords must not be visible via webhooks.'); assert.deepEqual(_.pick(body.submission, _.keys(webhookSubmission)), webhookSubmission); + done(); }; + request(app) - .post(hook.alter('url', '/form/' + webhookForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${webhookForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { firstName: 'Test', lastName: 'Person', email: 'test@example.com', - password: '123testing' - } + password: '123testing', + }, }) .expect(201) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -579,13 +586,13 @@ module.exports = function(app, template, hook) { }); }); - it('Should be able to get the data from the webhook action.', function(done) { + it('Should be able to get the data from the webhook action.', (done) => { request(app) - .get(hook.alter('url', '/form/' + webhookForm._id + '/submission/' + webhookSubmission._id, template)) + .get(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -594,12 +601,13 @@ module.exports = function(app, template, hook) { assert.equal(res.body.data.email, 'test@example.com'); assert.equal(res.body.data.firstName, 'Test'); assert.equal(res.body.data.lastName, 'Person'); + done(); }); }); - it('Should send a webhook with update data.', function(done) { - webhookHandler = function(body) { + it('Should send a webhook with update data.', (done) => { + webhookHandler = (body) => { body = hook.alter('webhookBody', body); assert.equal(body.params.formId, webhookForm._id.toString()); @@ -607,21 +615,22 @@ module.exports = function(app, template, hook) { assert.equal(body.request.data.firstName, 'Test2'); assert.equal(body.request.data.lastName, 'Person3'); assert.deepEqual(_.pick(body.submission, _.keys(webhookSubmission)), webhookSubmission); + done(); }; request(app) - .put(hook.alter('url', '/form/' + webhookForm._id + '/submission/' + webhookSubmission._id, template)) + .put(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { firstName: 'Test2', lastName: 'Person3', - email: 'test@example.com' - } + email: 'test@example.com', + }, }) .expect(200) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -630,21 +639,22 @@ module.exports = function(app, template, hook) { }); }); - it('Should send a webhook with deleted data.', function(done) { - webhookHandler = function(body, url) { + it('Should send a webhook with deleted data.', (done) => { + webhookHandler = (body, url) => { body = hook.alter('webhookBody', body); - assert.equal(body, ''); assert.equal(url.query.formId, webhookForm._id); assert.equal(url.query.submissionId, webhookSubmission._id); + done(); }; + request(app) - .delete(hook.alter('url', '/form/' + webhookForm._id + '/submission/' + webhookSubmission._id, template)) + .delete(hook.alter('url', `/form/${webhookForm._id}/submission/${webhookSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err) => { if (err) { return done(err); } @@ -652,13 +662,13 @@ module.exports = function(app, template, hook) { }); }); - describe('EmailAction Functionality tests', function() { + describe('EmailAction Functionality tests', () => { if (docker) { return; } // The temp form with the add RoleAction for existing submissions. - var emailForm = { + const emailForm = { title: 'Email Form', name: 'emailform', path: 'emailform', @@ -668,83 +678,46 @@ module.exports = function(app, template, hook) { components: [ { type: 'textfield', - validate: { - custom: '', - pattern: '', - maxLength: '', - minLength: '', - required: false - }, - defaultValue: '', - multiple: false, - suffix: '', - prefix: '', - placeholder: 'foo', key: 'firstName', label: 'First Name', - inputMask: '', - inputType: 'text', - input: true + input: true, }, { type: 'textfield', - validate: { - custom: '', - pattern: '', - maxLength: '', - minLength: '', - required: false - }, - defaultValue: '', - multiple: false, - suffix: '', - prefix: '', - placeholder: 'foo', key: 'lastName', label: 'Last Name', - inputMask: '', - inputType: 'text', - input: true + input: true, }, { type: 'email', - persistent: true, - unique: false, - protected: false, - defaultValue: '', - suffix: '', - prefix: '', - placeholder: 'Enter your email address', key: 'email', label: 'Email', - inputType: 'email', - tableView: true, - input: true - } - ] + input: true, + }, + ], }; // The temp role add action for existing submissions. - var emailAction = { + const emailAction = { title: 'Email', name: 'email', handler: ['after'], method: ['create'], priority: 1, - settings: {} + settings: {}, }; - var numTests = 0; - var newEmailTest = function(settings, done) { + let numTests = 0; + const newEmailTest = (settings, done) => { numTests++; settings.transport = 'test'; - var testForm = _.assign(_.cloneDeep(emailForm), { + let testForm = _.assign(_.cloneDeep(emailForm), { title: (emailForm.title + numTests), name: (emailForm.name + numTests), - path: (emailForm.path + numTests) + path: (emailForm.path + numTests), }); - var testAction = _.assign(_.cloneDeep(emailAction), { - settings: settings + let testAction = _.assign(_.cloneDeep(emailAction), { + settings, }); // Create the form. @@ -754,7 +727,7 @@ module.exports = function(app, template, hook) { .send(testForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } @@ -765,58 +738,60 @@ module.exports = function(app, template, hook) { // Add the action to the form. request(app) - .post(hook.alter('url', '/form/' + testForm._id + '/action', template)) + .post(hook.alter('url', `/form/${testForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(testAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } testAction = res.body; template.users.admin.token = res.headers['x-jwt-token']; + done(null, testForm, testAction); }); }); }; - it('Should send an email with messages.', function(done) { + it('Should send an email with messages.', (done) => { newEmailTest({ from: 'travis@form.io', emails: '{{ data.email }}', sendEach: false, subject: 'Hello there {{ data.firstName }} {{ data.lastName }}', - message: 'Howdy, {{ id }}' - }, function(err, testForm, testAction) { + message: 'Howdy, {{ id }}', + }, (err, testForm) => { if (err) { return done(err); } // Check for an email. - let event = template.hooks.getEmitter(); + const event = template.hooks.getEmitter(); event.once('newMail', (email) => { assert.equal(email.from, 'travis@form.io'); assert.equal(email.to, 'test@example.com'); - assert.equal(email.html.indexOf('Howdy, '), 0); + assert(email.html.startsWith('Howdy, ')); assert.equal(email.subject, 'Hello there Test Person'); + done(); }); request(app) - .post(hook.alter('url', '/form/' + testForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${testForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { firstName: 'Test', lastName: 'Person', - email: 'test@example.com' - } + email: 'test@example.com', + }, }) .expect(201) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err) => { if (err) { return done(err); } @@ -824,40 +799,41 @@ module.exports = function(app, template, hook) { }); }); - it('Should send an email with multiple recipients.', function(done) { + it('Should send an email with multiple recipients.', (done) => { newEmailTest({ from: '{{ data.email }}', emails: '{{ data.email }}, gary@form.io', subject: 'Hello there {{ data.firstName }} {{ data.lastName }}', - message: 'Howdy, {{ id }}' - }, function(err, testForm, testAction) { + message: 'Howdy, {{ id }}', + }, (err, testForm) => { if (err) { return done(err); } // Check for an email. - let event = template.hooks.getEmitter(); + const event = template.hooks.getEmitter(); event.once('newMail', (email) => { assert.equal(email.from, 'joe@example.com'); assert.equal(email.to, 'joe@example.com, gary@form.io'); - assert.equal(email.html.indexOf('Howdy, '), 0); + assert(email.html.startsWith('Howdy, ')); assert.equal(email.subject, 'Hello there Joe Smith'); + done(); }); request(app) - .post(hook.alter('url', '/form/' + testForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${testForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { firstName: 'Joe', lastName: 'Smith', - email: 'joe@example.com' - } + email: 'joe@example.com', + }, }) .expect(201) .expect('Content-Type', /json/) - .end(function (err, res) { + .end((err) => { if (err) { return done(err); } @@ -865,84 +841,131 @@ module.exports = function(app, template, hook) { }); }); - it('Should send an email with multiple separate messages.', function(done) { + it('Should send an email with multiple separate messages.', (done) => { newEmailTest({ from: 'travis@form.io', emails: '{{ data.email }}, gary@form.io', sendEach: true, subject: 'Hello there {{ data.firstName }} {{ data.lastName }}', - message: 'Howdy, {{ id }}' - }, function(err, testForm, testAction) { + message: 'Howdy, {{ id }}', + }, (err, testForm) => { if (err) { return done(err); } // Check for an email. - let event = template.hooks.getEmitter(); - let email1 = new Promise((resolve, reject) => { - event.once('newMail', (email) => { - assert.equal(email.from, 'travis@form.io'); - assert.equal(email.to, 'gary@form.io'); - assert.equal(email.html.indexOf('Howdy, '), 0); - assert.equal(email.subject, 'Hello there Test Person'); - resolve(); - }); + const event = template.hooks.getEmitter(); + const emailTos = {'gary@form.io': true, 'test@example.com': true}; + event.on('newMail', (email) => { + assert.equal(email.from, 'travis@form.io'); + assert(emailTos[email.to]); + delete emailTos[email.to]; + assert.equal(email.html.indexOf('Howdy, '), 0); + assert.equal(email.subject, 'Hello there Test Person'); + if (Object.keys(emailTos).length === 0) { + event.removeAllListeners('newMail'); + done(); + } }); - let email2 = new Promise((resolve, reject) => { - event.once('newMail', (email) => { - assert.equal(email.from, 'travis@form.io'); - assert.equal(email.to, 'test@example.com'); - assert.equal(email.html.indexOf('Howdy, '), 0); - assert.equal(email.subject, 'Hello there Test Person'); - resolve(); + request(app) + .post(hook.alter('url', `/form/${testForm._id}/submission`, template)) + .set('x-jwt-token', template.users.admin.token) + .send({ + data: { + firstName: 'Test', + lastName: 'Person', + email: 'test@example.com', + }, + }) + .expect(201) + .expect('Content-Type', /json/) + .end((err) => { + if (err) { + done(err); + } }); + }); + }); + + it('Should send a giant email to large amount of people.', (done) => { + const amountOfEmails = 10000; + const addresses = _.range(amountOfEmails).map((index) => `test${index}@example.com`).join(','); + const message = chance.paragraph({ sentences: 1000 }); + let receivedEmails = 0; + + newEmailTest({ + from: 'travis@form.io', + emails: addresses, + sendEach: true, + subject: 'Hello there {{ data.firstName }} {{ data.lastName }}', + message, + }, (err, testForm) => { + if (err) { + return done(err); + } + + // Check for an email. + const event = template.hooks.getEmitter(); + event.on('newMail', (email) => { + + assert.equal(email.from, 'travis@form.io'); + // assert.equal(email.to, `test${receivedEmails}@example.com`); + assert.equal(email.html, message); + assert.equal(email.subject, 'Hello there Test Person'); + + receivedEmails += 1; + + if (receivedEmails === amountOfEmails) { + event.removeAllListeners('newMail'); + done(); + } }); - Promise.all([email1, email2]) - .then(done) - .catch(done); request(app) - .post(hook.alter('url', '/form/' + testForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${testForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { firstName: 'Test', lastName: 'Person', - email: 'test@example.com' - } + }, }) .expect(201) .expect('Content-Type', /json/) - .end(done); + .end((err) => { + if (err) { + done(err); + } + }); }); }); }); - describe('RoleAction Functionality tests', function() { + describe('RoleAction Functionality tests', () => { // The temp form with the add RoleAction for existing submissions. - var addForm = { + let addForm = { title: 'Add Form', name: 'addform', path: 'addform', type: 'form', submissionAccess: [], - components: [] + components: [], }; // The temp form with the remove RoleAction for existing submissions. - var removeForm = { + let removeForm = { title: 'Remove Form', name: 'removeform', path: 'removeform', type: 'form', access: [], submissionAccess: [], - components: [] + components: [], }; // The temp form with the add RoleAction for new submissions. - var submissionForm = { + let submissionForm = { title: 'Submission Form', name: 'submissionform', path: 'submissionform', @@ -957,7 +980,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -968,13 +991,13 @@ module.exports = function(app, template, hook) { label: 'foo', inputMask: '', inputType: 'text', - input: true - } - ] + input: true, + }, + ], }; // The temp role add action for existing submissions. - var addAction = { + let addAction = { title: 'Add Role', name: 'role', handler: ['before'], @@ -982,12 +1005,12 @@ module.exports = function(app, template, hook) { priority: 1, settings: { association: 'existing', - type: 'add' - } + type: 'add', + }, }; // The temp role remove action for existing submissions. - var removeAction = { + let removeAction = { title: 'Remove Role', name: 'role', handler: ['before'], @@ -995,12 +1018,12 @@ module.exports = function(app, template, hook) { priority: 1, settings: { association: 'existing', - type: 'remove' - } + type: 'remove', + }, }; // The temp role add action for new submissions. - var submissionAction = { + let submissionAction = { title: 'Add Role', name: 'role', handler: ['after'], @@ -1009,33 +1032,33 @@ module.exports = function(app, template, hook) { settings: { association: 'new', type: 'add', - role: null - } + role: null, + }, }; // The temp submission. - var submission = {}; + let submission = {}; // The dummy role for this test suite. - var dummyRole = { + let dummyRole = { title: 'dummyRole', - description: 'A dummy role.' + description: 'A dummy role.', }; - describe('Bootstrap', function() { - it('Create the dummy role', function(done) { + describe('Bootstrap', () => { + it('Create the dummy role', (done) => { request(app) .post(hook.alter('url', '/role', template)) .set('x-jwt-token', template.users.admin.token) .send(dummyRole) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'Each role in the response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'Each role in the response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'Each role in the response should contain a `created` timestamp.'); @@ -1052,36 +1075,36 @@ module.exports = function(app, template, hook) { }); }); - describe('Role dependent', function() { + describe('Role dependent', () => { // Attach the dummy role to the submission action before starting its tests. - before(function() { + before(() => { submissionForm.access = [ - {type: 'read_all', roles: [template.roles.anonymous._id.toString()]} + {type: 'read_all', roles: [template.roles.anonymous._id.toString()]}, ]; submissionForm.submissionAccess = [ {type: 'create_own', roles: [template.roles.anonymous._id.toString()]}, {type: 'read_own', roles: [dummyRole._id]}, {type: 'update_own', roles: [dummyRole._id]}, - {type: 'delete_own', roles: [dummyRole._id]} + {type: 'delete_own', roles: [dummyRole._id]}, ]; submissionAction.settings.role = dummyRole._id; }); // Create the dummy forms and attach each respective action. - it('Create the addForm Form', function(done) { + it('Create the addForm Form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(addForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -1093,7 +1116,7 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 4); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); assert.equal(response.submissionAccess.length, 0); assert.deepEqual(response.components, addForm.components); addForm = response; @@ -1105,19 +1128,19 @@ module.exports = function(app, template, hook) { }); }); - it('Attach the addAction (RoleAction) to its Form', function(done) { + it('Attach the addAction (RoleAction) to its Form', (done) => { request(app) - .post(hook.alter('url', '/form/' + addForm._id + '/action', template)) + .post(hook.alter('url', `/form/${addForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(addAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, addAction.title); assert.equal(response.name, addAction.name); @@ -1135,19 +1158,19 @@ module.exports = function(app, template, hook) { }); }); - it('Create the removeForm Form', function(done) { + it('Create the removeForm Form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(removeForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -1159,10 +1182,10 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 4); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(dummyRole._id), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); + assert(response.access[0].roles.includes(dummyRole._id)); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, removeForm.components); removeForm = response; @@ -1174,19 +1197,19 @@ module.exports = function(app, template, hook) { }); }); - it('Attach the removeAction (RoleAction) to its Form', function(done) { + it('Attach the removeAction (RoleAction) to its Form', (done) => { request(app) - .post(hook.alter('url', '/form/' + removeForm._id + '/action', template)) + .post(hook.alter('url', `/form/${removeForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(removeAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, removeAction.title); assert.equal(response.name, removeAction.name); @@ -1204,19 +1227,19 @@ module.exports = function(app, template, hook) { }); }); - it('Create the submissionForm Form', function(done) { + it('Create the submissionForm Form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(submissionForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -1225,7 +1248,7 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); assert.equal(response.submissionAccess.length, 4); assert.equal(response.title, submissionForm.title); assert.equal(response.name, submissionForm.name); @@ -1241,19 +1264,19 @@ module.exports = function(app, template, hook) { }); }); - it('Attach the submissionAction (RoleAction) to its Form', function(done) { + it('Attach the submissionAction (RoleAction) to its Form', (done) => { request(app) - .post(hook.alter('url', '/form/' + submissionForm._id + '/action', template)) + .post(hook.alter('url', `/form/${submissionForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(submissionAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, submissionAction.title); assert.equal(response.name, submissionAction.name); @@ -1273,21 +1296,21 @@ module.exports = function(app, template, hook) { }); }); - describe('RoleAction Functionality tests for Existing Submissions', function() { - it('The user should not have the dummy Role assigned', function(done) { + describe('RoleAction Functionality tests for Existing Submissions', () => { + it('The user should not have the dummy Role assigned', (done) => { request(app) - .get(hook.alter('url', '/form/' + template.resources.admin._id + '/submission/' + template.users.admin._id, template)) + .get(hook.alter('url', `/form/${template.resources.admin._id}/submission/${template.users.admin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } // Confirm the response does not contain the dummy role. - var response = res.body; - assert.equal(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(!response.roles.includes(dummyRole._id)); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -1296,20 +1319,20 @@ module.exports = function(app, template, hook) { }); }); - it('A submission to the addForm Form should archive the role addition and update the Submission with the dummy Role added', function(done) { + it('A submission to the addForm Form should archive the role addition and update the Submission with the dummy Role added', (done) => { request(app) - .post(hook.alter('url', '/form/' + addForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${addForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { role: dummyRole._id, - submission: template.users.admin._id - } + submission: template.users.admin._id, + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -1318,17 +1341,17 @@ module.exports = function(app, template, hook) { // Confirm that the user was updated to include the new role. request(app) - .get(hook.alter('url', '/form/' + template.resources.admin._id + '/submission/' + template.users.admin._id, template)) + .get(hook.alter('url', `/form/${template.resources.admin._id}/submission/${template.users.admin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.notEqual(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(response.roles.includes(dummyRole._id)); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -1338,20 +1361,20 @@ module.exports = function(app, template, hook) { }); }); - it('A submission to the removeForm Form should archive the role removal and update the Submission with the dummy Role removed', function(done) { + it('A submission to the removeForm Form should archive the role removal and update the Submission with the dummy Role removed', (done) => { request(app) - .post(hook.alter('url', '/form/' + removeForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${removeForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { role: dummyRole._id, - submission: template.users.admin._id - } + submission: template.users.admin._id, + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -1360,17 +1383,17 @@ module.exports = function(app, template, hook) { // Confirm that the user was updated to not include the dummy role. request(app) - .get(hook.alter('url', '/form/' + template.resources.admin._id + '/submission/' + template.users.admin._id, template)) + .get(hook.alter('url', `/form/${template.resources.admin._id}/submission/${template.users.admin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.equal(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(!response.roles.includes(dummyRole._id)); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -1380,20 +1403,20 @@ module.exports = function(app, template, hook) { }); }); - it('A submission to the addForm Form using the Form alias should return the updated Submission with the dummy Role added', function(done) { + it('A submission to the addForm Form using the Form alias should return the updated Submission with the dummy Role added', (done) => { request(app) - .post(hook.alter('url', '/' + addForm.path, template)) + .post(hook.alter('url', `/${addForm.path}`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { role: dummyRole._id, - submission: template.users.admin._id - } + submission: template.users.admin._id, + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -1402,17 +1425,17 @@ module.exports = function(app, template, hook) { // Confirm that the user was updated to include the new role. request(app) - .get(hook.alter('url', '/form/' + template.resources.admin._id + '/submission/' + template.users.admin._id, template)) + .get(hook.alter('url', `/form/${template.resources.admin._id}/submission/${template.users.admin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.notEqual(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(response.roles.includes(dummyRole._id)); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -1422,20 +1445,20 @@ module.exports = function(app, template, hook) { }); }); - it('A submission to the removeForm Form using the Form alias should return the updated Submission with the dummy Role removed', function(done) { + it('A submission to the removeForm Form using the Form alias should return the updated Submission with the dummy Role removed', (done) => { request(app) - .post(hook.alter('url', '/' + removeForm.path, template)) + .post(hook.alter('url', `/${removeForm.path}`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { role: dummyRole._id, - submission: template.users.admin._id - } + submission: template.users.admin._id, + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -1444,17 +1467,17 @@ module.exports = function(app, template, hook) { // Confirm that the user was updated to not include the dummy role. request(app) - .get(hook.alter('url', '/form/' + template.resources.admin._id + '/submission/' + template.users.admin._id, template)) + .get(hook.alter('url', `/form/${template.resources.admin._id}/submission/${template.users.admin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.equal(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(!response.roles.includes(dummyRole._id)); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -1464,19 +1487,19 @@ module.exports = function(app, template, hook) { }); }); - it('The user should not be able to assign a role that they do not have access to (including invalid roles)', function(done) { + it('The user should not be able to assign a role that they do not have access to (including invalid roles)', (done) => { request(app) - .post(hook.alter('url', '/' + addForm.path, template)) + .post(hook.alter('url', `/${addForm.path}`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { role: template.users.user1._id, // invalid roleId, but a valid MongoDB ObjectId. - submission: template.users.user1._id - } + submission: template.users.user1._id, + }, }) .expect(400) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -1488,24 +1511,24 @@ module.exports = function(app, template, hook) { }); }); - describe('RoleAction Functionality tests for New Submissions', function() { - it('A new Submission to the submissionForm should create a new Submission and contain the dummyRole Role', function(done) { + describe('RoleAction Functionality tests for New Submissions', () => { + it('A new Submission to the submissionForm should create a new Submission and contain the dummyRole Role', (done) => { request(app) - .post(hook.alter('url', '/form/' + submissionForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${submissionForm._id}/submission`, template)) .send({ data: { - foo: 'bar' - } + foo: 'bar', + }, }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; - assert.notEqual(response.roles.indexOf(dummyRole._id), -1); + const response = res.body; + assert(response.roles.includes(dummyRole._id)); submission = response; done(); @@ -1513,18 +1536,18 @@ module.exports = function(app, template, hook) { }); }); - describe('RoleAction Normalization', function() { - it('Remove the temp submission', function(done) { + describe('RoleAction Normalization', () => { + it('Remove the temp submission', (done) => { request(app) - .delete(hook.alter('url', '/form/' + submissionForm._id + '/submission/' + submission._id, template)) + .delete(hook.alter('url', `/form/${submissionForm._id}/submission/${submission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); submission = response; @@ -1535,17 +1558,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the dummy role', function(done) { + it('Remove the dummy role', (done) => { request(app) - .delete(hook.alter('url', '/role/' + dummyRole._id, template)) + .delete(hook.alter('url', `/role/${dummyRole._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); dummyRole = response; @@ -1556,17 +1579,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the submissionAction', function(done) { + it('Remove the submissionAction', (done) => { request(app) - .delete(hook.alter('url', '/form/' + submissionForm._id + '/action/' + submissionAction._id, template)) + .delete(hook.alter('url', `/form/${submissionForm._id}/action/${submissionAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); submissionAction = response; @@ -1577,17 +1600,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the removeAction', function(done) { + it('Remove the removeAction', (done) => { request(app) - .delete(hook.alter('url', '/form/' + removeForm._id + '/action/' + removeAction._id, template)) + .delete(hook.alter('url', `/form/${removeForm._id}/action/${removeAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); removeAction = response; @@ -1598,17 +1621,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the addAction', function(done) { + it('Remove the addAction', (done) => { request(app) - .delete(hook.alter('url', '/form/' + addForm._id + '/action/' + addAction._id, template)) + .delete(hook.alter('url', `/form/${addForm._id}/action/${addAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); addAction = response; @@ -1619,17 +1642,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the submissionForm', function(done) { + it('Remove the submissionForm', (done) => { request(app) - .delete(hook.alter('url', '/form/' + submissionForm._id, template)) + .delete(hook.alter('url', `/form/${submissionForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); submissionForm = response; @@ -1640,17 +1663,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the removeForm', function(done) { + it('Remove the removeForm', (done) => { request(app) - .delete(hook.alter('url', '/form/' + removeForm._id, template)) + .delete(hook.alter('url', `/form/${removeForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); removeForm = response; @@ -1661,17 +1684,17 @@ module.exports = function(app, template, hook) { }); }); - it('Remove the addForm', function(done) { + it('Remove the addForm', (done) => { request(app) - .delete(hook.alter('url', '/form/' + addForm._id, template)) + .delete(hook.alter('url', `/form/${addForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); addForm = response; @@ -1684,8 +1707,8 @@ module.exports = function(app, template, hook) { }); }); - describe('AuthAction Functionality tests', function() { - var dummyResource = { + describe('AuthAction Functionality tests', () => { + let dummyResource = { title: 'dummy', name: 'dummy', path: 'dummy', @@ -1700,7 +1723,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -1711,7 +1734,7 @@ module.exports = function(app, template, hook) { label: 'username', inputMask: '', inputType: 'text', - input: true + input: true, }, { type: 'password', @@ -1721,12 +1744,12 @@ module.exports = function(app, template, hook) { key: 'password', label: 'password', inputType: 'password', - input: true - } - ] + input: true, + }, + ], }; - var authForm = { + let authForm = { title: 'Auth Form', name: 'authform', path: 'authform', @@ -1742,7 +1765,7 @@ module.exports = function(app, template, hook) { pattern: '', maxLength: '', minLength: '', - required: false + required: false, }, defaultValue: '', multiple: false, @@ -1753,7 +1776,7 @@ module.exports = function(app, template, hook) { label: 'username', inputMask: '', inputType: 'text', - input: true + input: true, }, { type: 'password', @@ -1763,25 +1786,25 @@ module.exports = function(app, template, hook) { key: 'password', label: 'password', inputType: 'password', - input: true - } - ] + input: true, + }, + ], }; - describe('Bootstrap', function() { - it('Create the dummy resource form', function(done) { + describe('Bootstrap', () => { + it('Create the dummy resource form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(dummyResource) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -1793,9 +1816,9 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 3); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, dummyResource.components); dummyResource = response; @@ -1807,8 +1830,8 @@ module.exports = function(app, template, hook) { }); }); - it('Create the dummy role assignment action', function(done) { - var roleAction = { + it('Create the dummy role assignment action', (done) => { + let roleAction = { title: 'Role Assignment', name: 'role', priority: 1, @@ -1817,22 +1840,22 @@ module.exports = function(app, template, hook) { settings: { association: 'new', type: 'add', - role: template.users.admin._id.toString() - } + role: template.users.admin._id.toString(), + }, }; request(app) - .post(hook.alter('url', '/form/' + dummyResource._id + '/action', template)) + .post(hook.alter('url', `/form/${dummyResource._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(roleAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, roleAction.title); assert.equal(response.name, roleAction.name); @@ -1850,19 +1873,19 @@ module.exports = function(app, template, hook) { }); }); - it('Create the dummy auth form', function(done) { + it('Create the dummy auth form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(authForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -1874,9 +1897,9 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 3); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); assert.deepEqual(response.submissionAccess, []); assert.deepEqual(response.components, authForm.components); authForm = response; @@ -1888,8 +1911,8 @@ module.exports = function(app, template, hook) { }); }); - it('Create the dummy save submission action', function(done) { - var authAction = { + it('Create the dummy save submission action', (done) => { + let authAction = { title: 'Save Submission', name: 'save', handler: ['before'], @@ -1899,23 +1922,23 @@ module.exports = function(app, template, hook) { resource: dummyResource._id.toString(), fields: { username: 'username', - password: 'password' - } - } + password: 'password', + }, + }, }; request(app) - .post(hook.alter('url', '/form/' + authForm._id + '/action', template)) + .post(hook.alter('url', `/form/${authForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(authAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, authAction.title); assert.equal(response.name, authAction.name); @@ -1932,8 +1955,8 @@ module.exports = function(app, template, hook) { done(); }); }); - it('Create the dummy auth login action', function(done) { - var authLoginAction = { + it('Create the dummy auth login action', (done) => { + let authLoginAction = { title: 'Login', name: 'login', handler: ['before'], @@ -1945,22 +1968,22 @@ module.exports = function(app, template, hook) { password: 'password', allowedAttempts: 5, attemptWindow: 10, - lockWait: 10 - } - } + lockWait: 10, + }, + }; request(app) - .post(hook.alter('url', '/form/' + authForm._id + '/action', template)) + .post(hook.alter('url', `/form/${authForm._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send(authLoginAction) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, authLoginAction.title); assert.equal(response.name, authLoginAction.name); @@ -1979,20 +2002,20 @@ module.exports = function(app, template, hook) { }); }); - describe('AuthAction Functionality tests for New Submissions', function() { - it('A AuthAction should not be able to assign a role that is not accessible (including invalid roles)', function(done) { + describe('AuthAction Functionality tests for New Submissions', () => { + it('A AuthAction should not be able to assign a role that is not accessible (including invalid roles)', (done) => { request(app) - .post(hook.alter('url', '/form/' + authForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${authForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: { 'username': chance.word({length: 10}), - 'password': chance.word({length: 10}) - } + 'password': chance.word({length: 10}), + }, }) .expect(400) - .end(function(err, res) { - if(err) { + .end((err, res) => { + if (err) { return done(err); } @@ -2004,18 +2027,18 @@ module.exports = function(app, template, hook) { }); }); - describe('AuthAction Normalization', function() { - it('Should delete the dummy resource', function(done) { + describe('AuthAction Normalization', () => { + it('Should delete the dummy resource', (done) => { request(app) - .delete(hook.alter('url', '/form/' + dummyResource._id, template)) + .delete(hook.alter('url', `/form/${dummyResource._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); dummyResource = response; @@ -2026,17 +2049,17 @@ module.exports = function(app, template, hook) { }); }); - it('Should delete the authForm', function(done) { + it('Should delete the authForm', (done) => { request(app) - .delete(hook.alter('url', '/form/' + authForm._id, template)) + .delete(hook.alter('url', `/form/${authForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); authForm = response; @@ -2046,21 +2069,21 @@ module.exports = function(app, template, hook) { done(); }); }); - }) + }); }); - describe('Action Normalization', function() { - it('A Project Owner should be able to Delete an Action', function(done) { + describe('Action Normalization', () => { + it('A Project Owner should be able to Delete an Action', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/action/' + tempAction._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/action/${tempAction._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -2071,34 +2094,36 @@ module.exports = function(app, template, hook) { }); if (!docker) - it('A deleted Action should remain in the database', function(done) { - var formio = hook.alter('formio', app.formio); + it('A deleted Action should remain in the database', (done) => { + const formio = hook.alter('formio', app.formio); formio.actions.model.findOne({_id: tempAction._id}) - .exec(function(err, action) { + .exec((err, action) => { if (err) { return done(err); } + if (!action) { return done('No Action found, expected 1.'); } action = action.toObject(); assert.notEqual(action.deleted, null); + done(); }); }); - it('Delete the Form used for Action tests', function(done) { + it('Delete the Form used for Action tests', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -2109,23 +2134,24 @@ module.exports = function(app, template, hook) { }); if (!docker) - it('A deleted Form should not have active actions in the database', function(done) { - var formio = hook.alter('formio', app.formio); + it('A deleted Form should not have active actions in the database', (done) => { + const formio = hook.alter('formio', app.formio); formio.actions.model.find({form: tempForm._id, deleted: {$eq: null}}) - .exec(function(err, action) { + .exec((err, action) => { if (err) { return done(err); } + if (action && action.length !== 0) { - return done('Active actions found w/ form: ' + tempForm._id + ', expected 0.'); + return done(`Active actions found w/ form: ${tempForm._id}, expected 0.`); } done(); }); }); - var actionLogin = null; - it('A Project Owner should be able to Create an Authentication Action (Login Form)', function(done) { + let actionLogin = null; + it('A Project Owner should be able to Create an Authentication Action (Login Form)', (done) => { actionLogin = { title: 'Login', name: 'login', @@ -2138,22 +2164,22 @@ module.exports = function(app, template, hook) { password: 'password', allowedAttempts: 5, attemptWindow: 10, - lockWait: 10 - } + lockWait: 10, + }, }; request(app) - .post(hook.alter('url', '/form/' + template.forms.userLogin._id + '/action', template)) + .post(hook.alter('url', `/form/${template.forms.userLogin._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: actionLogin }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, actionLogin.title); assert.equal(response.name, actionLogin.name); @@ -2171,16 +2197,16 @@ module.exports = function(app, template, hook) { }); }); - it('Delete the login action', function(done) { + it('Delete the login action', (done) => { request(app) - .delete(hook.alter('url', '/form/' + template.forms.userLogin._id + '/action/' + actionLogin._id, template)) + .delete(hook.alter('url', `/form/${template.forms.userLogin._id}/action/${actionLogin._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(done); }); - var actionRegister = null; - it('A Project Owner should be able to Create an Authentication Action (Registration Form)', function(done) { + let actionRegister = null; + it('A Project Owner should be able to Create an Authentication Action (Registration Form)', (done) => { actionRegister = { title: 'Login', name: 'login', @@ -2193,22 +2219,22 @@ module.exports = function(app, template, hook) { password: 'password', allowedAttempts: 5, attemptWindow: 10, - lockWait: 10 - } + lockWait: 10, + }, }; request(app) - .post(hook.alter('url', '/form/' + template.forms.userRegister._id + '/action', template)) + .post(hook.alter('url', `/form/${template.forms.userRegister._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: actionRegister }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, actionRegister.title); assert.equal(response.name, actionRegister.name); @@ -2226,16 +2252,16 @@ module.exports = function(app, template, hook) { }); }); - it('Delete the register action', function(done) { + it('Delete the register action', (done) => { request(app) - .delete(hook.alter('url', '/form/' + template.forms.userRegister._id + '/action/' + actionRegister._id, template)) + .delete(hook.alter('url', `/form/${template.forms.userRegister._id}/action/${actionRegister._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(done); }); - var actionRole = null; - it('A Project Owner should be able to Create a Role Assignment Action (Registration Form)', function(done) { + let actionRole = null; + it('A Project Owner should be able to Create a Role Assignment Action (Registration Form)', (done) => { actionRole = { title: 'Role Assignment', name: 'role', @@ -2245,22 +2271,22 @@ module.exports = function(app, template, hook) { settings: { association: 'new', type: 'add', - role: template.roles.authenticated._id.toString() - } + role: template.roles.authenticated._id.toString(), + }, }; request(app) - .post(hook.alter('url', '/form/' + template.forms.userRegister._id + '/action', template)) + .post(hook.alter('url', `/form/${template.forms.userRegister._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ data: actionRole }) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert.equal(response.title, actionRole.title); assert.equal(response.name, actionRole.name); @@ -2278,19 +2304,19 @@ module.exports = function(app, template, hook) { }); }); - it('Delete the role action', function(done) { + it('Delete the role action', (done) => { request(app) - .delete(hook.alter('url', '/form/' + template.forms.userRegister._id + '/action/' + actionRole._id, template)) + .delete(hook.alter('url', `/form/${template.forms.userRegister._id}/action/${actionRole._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .end(done); }); }); - describe('Conditional Actions', function() { - var helper = null; - it('Create the forms', function(done) { - var owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; + describe('Conditional Actions', () => { + let helper = null; + it('Create the forms', (done) => { + const owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; helper = new Helper(owner); helper .project() @@ -2308,24 +2334,28 @@ module.exports = function(app, template, hook) { label: 'Email', inputType: 'email', tableView: true, - input: true + input: true, }, { - type: 'selectboxes', + type: 'select', label: 'Roles', key: 'roles', input: true, - values: [ - { - label: 'Administrator', - value: 'administrator' - }, - { - label: 'Authenticated', - value: 'authenticated' - } - ] - } + multiple: true, + dataSrc: 'values', + data: { + values: [ + { + label: 'Administrator', + value: 'administrator', + }, + { + label: 'Authenticated', + value: 'authenticated', + }, + ], + }, + }, ]) .action({ title: 'Role Assignment', @@ -2336,13 +2366,13 @@ module.exports = function(app, template, hook) { condition: { field: 'email', eq: 'equals', - value: 'admin@example.com' + value: 'admin@example.com', }, settings: { association: 'new', type: 'add', - role: 'administrator' - } + role: 'administrator', + }, }) .action({ title: 'Role Assignment', @@ -2353,13 +2383,13 @@ module.exports = function(app, template, hook) { condition: { field: 'email', eq: 'equals', - value: 'auth@example.com' + value: 'auth@example.com', }, settings: { association: 'new', type: 'add', - role: 'authenticated' - } + role: 'authenticated', + }, }) .action({ title: 'Role Assignment', @@ -2368,13 +2398,13 @@ module.exports = function(app, template, hook) { handler: ['after'], method: ['create'], condition: { - custom: 'execute = (data.roles.indexOf("administrator") !== -1)' + custom: 'execute = (data.roles.indexOf("administrator") !== -1)', }, settings: { association: 'new', type: 'add', - role: 'administrator' - } + role: 'administrator', + }, }) .action({ title: 'Role Assignment', @@ -2383,125 +2413,147 @@ module.exports = function(app, template, hook) { handler: ['after'], method: ['create'], condition: { - custom: 'execute = (data.roles.indexOf("authenticated") !== -1)' + custom: 'execute = (data.roles.indexOf("authenticated") !== -1)', }, settings: { association: 'new', type: 'add', - role: 'authenticated' - } + role: 'authenticated', + }, }) .execute(done); }); - it('Should conditionally execute the add role action.', function(done) { + it('Should conditionally execute the add role action.', (done) => { helper .submission({ email: 'test@example.com', - roles: ['administrator'] + roles: ['administrator'], }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) !== -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) === -1); + const submission = helper.getLastSubmission(); + assert(submission.roles.includes(helper.template.roles.administrator._id)); + assert(!submission.roles.includes(helper.template.roles.authenticated._id)); + done(); - }) + }); }); - it('Should conditionally execute the add role action.', function(done) { + it('Should conditionally execute the add role action.', (done) => { helper .submission({ email: 'test@example.com', - roles: ['authenticated'] + roles: ['authenticated'], }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) !== -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(submission.roles.includes(helper.template.roles.authenticated._id)); + done(); - }) + }); }); - it('Should conditionally execute the add role action.', function(done) { + it('Should conditionally execute the add role action.', (done) => { helper .submission({ - email: 'admin@example.com' + email: 'admin@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) !== -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) === -1); + const submission = helper.getLastSubmission(); + assert(submission.roles.includes(helper.template.roles.administrator._id)); + assert(!submission.roles.includes(helper.template.roles.authenticated._id)); + done(); - }) + }); }); - it('Should conditionally execute the add role action.', function(done) { + it('Should conditionally execute the add role action.', (done) => { helper .submission({ - email: 'auth@example.com' + email: 'auth@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) !== -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(submission.roles.includes(helper.template.roles.authenticated._id)); + done(); - }) + }); }); - it('Should execute ALL role actions.', function(done) { + it('Should execute ALL role actions.', (done) => { helper .submission({ email: 'auth@example.com', - roles: ['administrator'] + roles: ['administrator'], }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) !== -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) !== -1); + const submission = helper.getLastSubmission(); + assert(submission.roles.includes(helper.template.roles.administrator._id)); + assert(submission.roles.includes(helper.template.roles.authenticated._id)); + done(); - }) + }); }); - it('Should NOT execute any role actions.', function(done) { + it('Should NOT execute any role actions.', (done) => { helper - .submission({ + .submission(helper.contextName, { email: 'test@example.com', - roles: ['test'] - }) - .execute(function(err) { + roles: ['test'], + }, helper.owner, [ + /json/, + 400 + ]) + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) === -1); + const invalidRolesValueError = { + message: 'Roles is an invalid value.', + level: 'error', + path: ['roles'], + context: { + validator: 'onlyAvailableItems', + setting: true, + key: 'roles', + label: 'Roles', + value: 'test' + } + }; + + const responseBody = helper.lastResponse.body; + assert.strictEqual(responseBody.name, 'ValidationError', 'Should return validation error'); + assert.deepStrictEqual(responseBody.details[0], invalidRolesValueError); + done(); }); }); - it('Executes a does not equal action when not equal', function(done) { - var owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; + it('Executes a does not equal action when not equal', (done) => { + const owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; helper = new Helper(owner); helper .project() @@ -2519,7 +2571,7 @@ module.exports = function(app, template, hook) { label: 'Email', inputType: 'email', tableView: true, - input: true + input: true, }, { type: 'selectboxes', @@ -2529,14 +2581,14 @@ module.exports = function(app, template, hook) { values: [ { label: 'Administrator', - value: 'administrator' + value: 'administrator', }, { label: 'Authenticated', - value: 'authenticated' - } - ] - } + value: 'authenticated', + }, + ], + }, ]) .action({ title: 'Role Assignment', @@ -2547,48 +2599,50 @@ module.exports = function(app, template, hook) { condition: { field: 'email', eq: 'notEqual', - value: 'none@example.com' + value: 'none@example.com', }, settings: { association: 'new', type: 'add', - role: 'authenticated' - } + role: 'authenticated', + }, }) .submission({ - email: 'test@example.com' + email: 'test@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) !== -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(submission.roles.includes(helper.template.roles.authenticated._id)); + done(); }); }); - it('Does not execute a does not equal action when equal', function(done) { + it('Does not execute a does not equal action when equal', (done) => { helper .submission({ - email: 'none@example.com' + email: 'none@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) === -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(!submission.roles.includes(helper.template.roles.authenticated._id)); + done(); }); }); - it('Executes a equal action when equal', function(done) { - var owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; + it('Executes a equal action when equal', (done) => { + const owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; helper = new Helper(owner); helper .project() @@ -2606,7 +2660,7 @@ module.exports = function(app, template, hook) { label: 'Email', inputType: 'email', tableView: true, - input: true + input: true, }, { type: 'selectboxes', @@ -2616,14 +2670,14 @@ module.exports = function(app, template, hook) { values: [ { label: 'Administrator', - value: 'administrator' + value: 'administrator', }, { label: 'Authenticated', - value: 'authenticated' - } - ] - } + value: 'authenticated', + }, + ], + }, ]) .action({ title: 'Role Assignment', @@ -2634,46 +2688,47 @@ module.exports = function(app, template, hook) { condition: { field: 'email', eq: 'equals', - value: 'test@example.com' + value: 'test@example.com', }, settings: { association: 'new', type: 'add', - role: 'authenticated' - } + role: 'authenticated', + }, }) .submission({ - email: 'test@example.com' + email: 'test@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) !== -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(submission.roles.includes(helper.template.roles.authenticated._id)); + done(); }); }); - it('Does not execute a equal action when not equal', function(done) { + it('Does not execute a equal action when not equal', (done) => { helper .submission({ - email: 'none@example.com' + email: 'none@example.com', }) - .execute(function(err) { + .execute((err) => { if (err) { return done(err); } - var submission = helper.getLastSubmission(); - assert(submission.roles.indexOf(helper.template.roles.administrator._id) === -1); - assert(submission.roles.indexOf(helper.template.roles.authenticated._id) === -1); + const submission = helper.getLastSubmission(); + assert(!submission.roles.includes(helper.template.roles.administrator._id)); + assert(!submission.roles.includes(helper.template.roles.authenticated._id)); + done(); }); }); - }); }); }; diff --git a/forms-flow-forms/test/auth.js b/forms-flow-forms/test/auth.js index de82be4d43..cd8711e010 100644 --- a/forms-flow-forms/test/auth.js +++ b/forms-flow-forms/test/auth.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var _ = require('lodash'); var async = require('async'); diff --git a/forms-flow-forms/test/export/CSVExporter/CSVExporter.js b/forms-flow-forms/test/export/CSVExporter/CSVExporter.js new file mode 100644 index 0000000000..10218ef8ee --- /dev/null +++ b/forms-flow-forms/test/export/CSVExporter/CSVExporter.js @@ -0,0 +1,36 @@ +module.exports = function(app, template, hook) { + const docker = process.env.DOCKER; + const assert = require('assert'); + const Helper = require('../../helper')(app); + let helper = null; + const test = require('../../fixtures/forms/datetime-format.js'); + + describe('CSVExporter', () => { + it('Sets up a default project', (done) => { + let owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; + helper = new Helper(owner); + helper.project().user('user', 'user1').execute(done); + }); + + it (`Export works in case when format is not set`, (done) => { + let owner = (app.hasProjects || docker) ? template.formio.owner : template.users.admin; + helper = new Helper(owner); + helper + .project() + .form('test', test.components) + .submission({data: {dateTime: '2020-03-10T09:00:00.000Z'}}) + .execute((err) => { + if (err) { + return done(err); + } + helper.getExport(helper.template.forms.test,'csv', (error, result) => { + if (error) { + done(error); + } + assert(!!result.text.split('\n')[1].split(',')[3], 'Date was not set'); + done(); + }); + }); + }) + }) +}; \ No newline at end of file diff --git a/forms-flow-forms/test/fixtures/forms/complex.js b/forms-flow-forms/test/fixtures/forms/complex.js index 4a84231ba4..8103efd221 100644 --- a/forms-flow-forms/test/fixtures/forms/complex.js +++ b/forms-flow-forms/test/fixtures/forms/complex.js @@ -1,3 +1,5 @@ +'use strict'; + module.exports = { components: [ { @@ -74,7 +76,6 @@ module.exports = { "legend": "", "components": [ { - "searchField": "data.apparatusId", "conditional": { "eq": "", "when": null, @@ -93,7 +94,6 @@ module.exports = { "filter": "", "refreshOn": "", "defaultValue": "", - "valueProperty": "data.apparatusId", "dataSrc": "values", "data": { "project": "57ae043a78c4691a181d23f0", @@ -158,7 +158,6 @@ module.exports = { "project": "57ae043a78c4691a181d23f0" }, "dataSrc": "values", - "valueProperty": "data.shortName", "defaultValue": "", "refreshOn": "", "filter": "", @@ -177,7 +176,6 @@ module.exports = { "when": null, "eq": "" }, - "searchField": "data.shortName" }, { "tags": [], @@ -208,7 +206,6 @@ module.exports = { "project": "57ae043a78c4691a181d23f0" }, "dataSrc": "values", - "valueProperty": "data.fullName", "defaultValue": "", "refreshOn": "", "filter": "sort=data.fullName", @@ -227,7 +224,6 @@ module.exports = { "when": null, "eq": "" }, - "searchField": "data.fullName" }, { "tabindex": "2", @@ -330,4 +326,4 @@ module.exports = { ] }] } -}; \ No newline at end of file +}; diff --git a/forms-flow-forms/test/fixtures/forms/customValidation.js b/forms-flow-forms/test/fixtures/forms/customValidation.js index 085ecf0f36..3ccc33688d 100644 --- a/forms-flow-forms/test/fixtures/forms/customValidation.js +++ b/forms-flow-forms/test/fixtures/forms/customValidation.js @@ -45,7 +45,7 @@ module.exports = { }, "validate": { "customPrivate": false, - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "pattern": "", "maxLength": "", "minLength": "", @@ -226,7 +226,7 @@ module.exports = { "tags": [], "type": "number", "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "multiple": "", "integer": "", "step": "any", @@ -371,7 +371,7 @@ module.exports = { "input": true }, { "validate": { - "custom": "valid = {{ trigger }}.toString() === 'true'" + "custom": "valid = {{ data.trigger }}.toString() === 'true'" }, "input": true, "tableView": false, @@ -517,7 +517,7 @@ module.exports = { "tags": [], "type": "textarea", "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "pattern": "", "maxLength": "", "minLength": "", @@ -669,7 +669,7 @@ module.exports = { }, "type": "selectboxes", "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "required": false }, "persistent": true, @@ -838,7 +838,7 @@ module.exports = { "persistent": true, "validate": { "required": false, - "custom": "valid = {{ trigger }}.toString() == 'true'" + "custom": "valid = {{ data.trigger }}.toString() == 'true'" }, "type": "select", "tags": [], @@ -993,7 +993,7 @@ module.exports = { "type": "radio", "validate": { "customPrivate": false, - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "required": false }, "persistent": true, @@ -1132,7 +1132,7 @@ module.exports = { }, { "lockKey": true, "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'" + "custom": "valid = {{ data.trigger }}.toString() == 'true'" }, "conditional": { "eq": "", @@ -1289,7 +1289,7 @@ module.exports = { "tags": [], "type": "datetime", "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "required": false }, "persistent": true, @@ -1473,7 +1473,7 @@ module.exports = { "tags": [], "type": "day", "validate": { - "custom": "valid = {{ trigger }}.toString() == 'true'" + "custom": "valid = {{ data.trigger }}.toString() == 'true'" }, "persistent": true, "protected": false, @@ -1647,7 +1647,7 @@ module.exports = { "validate": { "required": false, "multiple": "", - "custom": "valid = {{ trigger }}.toString() == 'true'" + "custom": "valid = {{ data.trigger }}.toString() == 'true'" }, "conditional": { "show": "", @@ -1789,7 +1789,7 @@ module.exports = { "type": "survey", "validate": { "customPrivate": false, - "custom": "valid = {{ trigger }}.toString() == 'true'", + "custom": "valid = {{ data.trigger }}.toString() == 'true'", "required": false }, "persistent": true, @@ -2181,7 +2181,7 @@ module.exports = { }, "validate": { "customPrivate": false, - "custom": "valid = '{{ trigger }}' == 'true'", + "custom": "valid = '{{ data.trigger }}' == 'true'", "pattern": "", "maxLength": "", "minLength": "", diff --git a/forms-flow-forms/test/fixtures/forms/datetime-format.js b/forms-flow-forms/test/fixtures/forms/datetime-format.js new file mode 100644 index 0000000000..228879c97d --- /dev/null +++ b/forms-flow-forms/test/fixtures/forms/datetime-format.js @@ -0,0 +1,130 @@ +module.exports = { + components: [ + { + "conditional": { + "eq": "", + "when": null, + "show": "" + }, + "tags": [], + "type": "panel", + "components": [ + { + "label": "Date / Time", + "tableView": false, + "datePicker": { + "disableWeekends": false, + "disableWeekdays": false, + "showWeeks": true, + "startingDay": 0, + "initDate": "", + "minMode": "day", + "maxMode": "year", + "yearRows": 4, + "yearColumns": 5, + "minDate": null, + "maxDate": null + }, + "calculateServer": false, + "key": "dateTime", + "type": "datetime", + "input": true, + "suffix": "", + "widget": { + "type": "calendar", + "displayInTimezone": "viewer", + "language": "en", + "useLocaleSettings": false, + "allowInput": true, + "mode": "single", + "enableTime": true, + "noCalendar": false, + "format": "yyyy-MM-dd hh:mm a", + "hourIncrement": 1, + "minuteIncrement": 1, + "time_24hr": false, + "minDate": null, + "disableWeekends": false, + "disableWeekdays": false, + "maxDate": null + }, + "placeholder": "", + "prefix": "", + "customClass": "", + "multiple": false, + "defaultValue": "", + "protected": false, + "unique": false, + "persistent": true, + "hidden": false, + "clearOnHide": true, + "refreshOn": "", + "redrawOn": "", + "modalEdit": false, + "modalEdit": false, + "labelPosition": "top", + "description": "", + "errorLabel": "", + "tooltip": "", + "hideLabel": false, + "tabindex": "", + "disabled": false, + "autofocus": false, + "dbIndex": false, + "customDefaultValue": "", + "calculateValue": "", + "attributes": {}, + "validateOn": "change", + "validate": { + "required": false, + "custom": "", + "customPrivate": false, + "strictDateValidation": false, + "multiple": false, + "unique": false + }, + "conditional": {"show": null, "when": null, "eq": ""}, + "overlay": {"style": "", "left": "", "top": "", "width": "", "height": ""}, + "allowCalculateOverride": false, + "encrypted": false, + "showCharCount": false, + "showWordCount": false, + "properties": {}, + "allowMultipleMasks": false, + "useLocaleSettings": false, + "allowInput": true, + "enableDate": true, + "enableTime": true, + "defaultDate": "", + "displayInTimezone": "viewer", + "timezone": "", + "datepickerMode": "day", + "timePicker": { + "hourStep": 1, + "minuteStep": 1, + "showMeridian": true, + "readonlyInput": false, + "mousewheel": true, + "arrowkeys": true + }, + "customOptions": {}, + "id": "e9x9po8c" + } + ] + }, + { + "input": true, + "label": "Submit", + "tableView": false, + "key": "submit", + "size": "md", + "leftIcon": "", + "rightIcon": "", + "block": false, + "action": "submit", + "disableOnInvalid": false, + "theme": "primary", + "type": "button" + } + ] +}; diff --git a/forms-flow-forms/test/fixtures/forms/for213.js b/forms-flow-forms/test/fixtures/forms/for213.js index 914e2a0a9f..80e7ab2465 100644 --- a/forms-flow-forms/test/fixtures/forms/for213.js +++ b/forms-flow-forms/test/fixtures/forms/for213.js @@ -69,42 +69,46 @@ module.exports = { "type": "button" }], submission: { + "test": "", "for213": { - "address_components": [{ - "long_name": "Estonia", - "short_name": "EE", - "types": ["country", "political"] - }], - "formatted_address": "Estonia", - "geometry": { - "bounds": { - "northeast": { - "lat": 59.7315, - "lng": 28.2101389 + "mode": "autocomplete", + "address": { + "address_components": [{ + "long_name": "Estonia", + "short_name": "EE", + "types": ["country", "political"] + }], + "formatted_address": "Estonia", + "geometry": { + "bounds": { + "northeast": { + "lat": 59.7315, + "lng": 28.2101389 + }, + "southwest": { + "lat": 57.50931600000001, + "lng": 21.6540999 + } }, - "southwest": { - "lat": 57.50931600000001, - "lng": 21.6540999 - } - }, - "location": { - "lat": 58.595272, - "lng": 25.013607 - }, - "location_type": "APPROXIMATE", - "viewport": { - "northeast": { - "lat": 59.7001516, - "lng": 28.2089248 + "location": { + "lat": 58.595272, + "lng": 25.013607 }, - "southwest": { - "lat": 57.5093539, - "lng": 21.7643721 + "location_type": "APPROXIMATE", + "viewport": { + "northeast": { + "lat": 59.7001516, + "lng": 28.2089248 + }, + "southwest": { + "lat": 57.5093539, + "lng": 21.7643721 + } } - } - }, - "place_id": "ChIJ_UuggpyUkkYRwyW0T7qf6kA", - "types": ["country", "political"] + }, + "place_id": "ChIJ_UuggpyUkkYRwyW0T7qf6kA", + "types": ["country", "political"] + } } } }; diff --git a/forms-flow-forms/test/fixtures/forms/multicomponents.js b/forms-flow-forms/test/fixtures/forms/multicomponents.js index a2cb88a9a6..915f773a70 100644 --- a/forms-flow-forms/test/fixtures/forms/multicomponents.js +++ b/forms-flow-forms/test/fixtures/forms/multicomponents.js @@ -222,7 +222,7 @@ module.exports = { "inputMask": "", "inputType": "text", "tableView": true, - "mutiple": true, + "multiple": true, "input": true }, { @@ -274,110 +274,115 @@ module.exports = { textArea3: ['This is the contents', 'more Contents'], select3: ['one', 'three'], email3: ['none@example.com', 'test@example.com'], - phoneNumber3: ['0303030304', '2094739403'], + phoneNumber3: ['(030) 303-0304', '(209) 473-9403'], address3: [{ - "address_components": [ - { - "long_name": "123", - "short_name": "123", - "types": [ - "street_number" - ] - }, - { - "long_name": "Fake Drive", - "short_name": "Fake Dr", - "types": [ - "route" - ] - }, - { - "long_name": "Luray", - "short_name": "Luray", - "types": [ - "locality", - "political" - ] - }, - { - "long_name": "1, West Luray", - "short_name": "1, West Luray", - "types": [ - "administrative_area_level_3", - "political" - ] - }, - { - "long_name": "Page County", - "short_name": "Page County", - "types": [ - "administrative_area_level_2", - "political" - ] - }, - { - "long_name": "Virginia", - "short_name": "VA", - "types": [ - "administrative_area_level_1", - "political" - ] - }, - { - "long_name": "United States", - "short_name": "US", - "types": [ - "country", - "political" - ] - }, + "mode": "autocomplete", + "address": [ { - "long_name": "22835", - "short_name": "22835", - "types": [ - "postal_code" - ] - }, - { - "long_name": "2722", - "short_name": "2722", - "types": [ - "postal_code_suffix" - ] - } - ], - "formatted_address": "123 Fake Dr, Luray, VA 22835, USA", - "geometry": { - "bounds": { - "northeast": { - "lat": 38.7062041, + "address_components": [ + { + "long_name": "123", + "short_name": "123", + "types": [ + "street_number" + ] + }, + { + "long_name": "Fake Drive", + "short_name": "Fake Dr", + "types": [ + "route" + ] + }, + { + "long_name": "Luray", + "short_name": "Luray", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "1, West Luray", + "short_name": "1, West Luray", + "types": [ + "administrative_area_level_3", + "political" + ] + }, + { + "long_name": "Page County", + "short_name": "Page County", + "types": [ + "administrative_area_level_2", + "political" + ] + }, + { + "long_name": "Virginia", + "short_name": "VA", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "United States", + "short_name": "US", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "22835", + "short_name": "22835", + "types": [ + "postal_code" + ] + }, + { + "long_name": "2722", + "short_name": "2722", + "types": [ + "postal_code_suffix" + ] + } + ], + "formatted_address": "123 Fake Dr, Luray, VA 22835, USA", + "geometry": { + "bounds": { + "northeast": { + "lat": 38.7062041, + "lng": -78.5065 + }, + "southwest": { + "lat": 38.70619, + "lng": -78.5065048 + } + }, + "location": { + "lat": 38.7062041, "lng": -78.5065 + }, + "location_type": "RANGE_INTERPOLATED", + "viewport": { + "northeast": { + "lat": 38.7075460302915, + "lng": -78.50515341970849 + }, + "southwest": { + "lat": 38.7048480697085, + "lng": -78.50785138029151 + } + } }, - "southwest": { - "lat": 38.70619, - "lng": -78.5065048 - } - }, - "location": { - "lat": 38.7062041, - "lng": -78.5065 - }, - "location_type": "RANGE_INTERPOLATED", - "viewport": { - "northeast": { - "lat": 38.7075460302915, - "lng": -78.50515341970849 - }, - "southwest": { - "lat": 38.7048480697085, - "lng": -78.50785138029151 - } + "partial_match": true, + "place_id": "EiExMjMgRmFrZSBEciwgTHVyYXksIFZBIDIyODM1LCBVU0E", + "types": [ + "street_address" + ] } - }, - "partial_match": true, - "place_id": "EiExMjMgRmFrZSBEciwgTHVyYXksIFZBIDIyODM1LCBVU0E", - "types": [ - "street_address" ] }], currency3: ['3,000', '500', '1,000,000'], @@ -393,4 +398,4 @@ module.exports = { } ] } -} \ No newline at end of file +} diff --git a/forms-flow-forms/test/fixtures/forms/singlecomponents1.js b/forms-flow-forms/test/fixtures/forms/singlecomponents1.js index ab840d127f..e839069c8f 100644 --- a/forms-flow-forms/test/fixtures/forms/singlecomponents1.js +++ b/forms-flow-forms/test/fixtures/forms/singlecomponents1.js @@ -486,111 +486,114 @@ module.exports = { select1: 'one', radio1: 'two', email1: 'none@example.com', - phoneNumber1: '0303030304', + phoneNumber1: '(030) 303-0304', address1: { - "address_components": [ - { - "long_name": "123", - "short_name": "123", - "types": [ - "street_number" - ] - }, - { - "long_name": "Fake Drive", - "short_name": "Fake Dr", - "types": [ - "route" - ] - }, - { - "long_name": "Luray", - "short_name": "Luray", - "types": [ - "locality", - "political" - ] - }, - { - "long_name": "1, West Luray", - "short_name": "1, West Luray", - "types": [ - "administrative_area_level_3", - "political" - ] - }, - { - "long_name": "Page County", - "short_name": "Page County", - "types": [ - "administrative_area_level_2", - "political" - ] - }, - { - "long_name": "Virginia", - "short_name": "VA", - "types": [ - "administrative_area_level_1", - "political" - ] - }, - { - "long_name": "United States", - "short_name": "US", - "types": [ - "country", - "political" - ] - }, - { - "long_name": "22835", - "short_name": "22835", - "types": [ - "postal_code" - ] - }, - { - "long_name": "2722", - "short_name": "2722", - "types": [ - "postal_code_suffix" - ] - } - ], + "mode": "autocomplete", + "address": { + "address_components": [ + { + "long_name": "123", + "short_name": "123", + "types": [ + "street_number" + ] + }, + { + "long_name": "Fake Drive", + "short_name": "Fake Dr", + "types": [ + "route" + ] + }, + { + "long_name": "Luray", + "short_name": "Luray", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "1, West Luray", + "short_name": "1, West Luray", + "types": [ + "administrative_area_level_3", + "political" + ] + }, + { + "long_name": "Page County", + "short_name": "Page County", + "types": [ + "administrative_area_level_2", + "political" + ] + }, + { + "long_name": "Virginia", + "short_name": "VA", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "United States", + "short_name": "US", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "22835", + "short_name": "22835", + "types": [ + "postal_code" + ] + }, + { + "long_name": "2722", + "short_name": "2722", + "types": [ + "postal_code_suffix" + ] + } + ], "formatted_address": "123 Fake Dr, Luray, VA 22835, USA", "geometry": { - "bounds": { - "northeast": { - "lat": 38.7062041, + "bounds": { + "northeast": { + "lat": 38.7062041, "lng": -78.5065 - }, - "southwest": { - "lat": 38.70619, + }, + "southwest": { + "lat": 38.70619, "lng": -78.5065048 - } - }, - "location": { - "lat": 38.7062041, + } + }, + "location": { + "lat": 38.7062041, "lng": -78.5065 - }, - "location_type": "RANGE_INTERPOLATED", + }, + "location_type": "RANGE_INTERPOLATED", "viewport": { - "northeast": { - "lat": 38.7075460302915, + "northeast": { + "lat": 38.7075460302915, "lng": -78.50515341970849 - }, - "southwest": { - "lat": 38.7048480697085, + }, + "southwest": { + "lat": 38.7048480697085, "lng": -78.50785138029151 + } } - } - }, - "partial_match": true, + }, + "partial_match": true, "place_id": "EiExMjMgRmFrZSBEciwgTHVyYXksIFZBIDIyODM1LCBVU0E", "types": [ - "street_address" - ] + "street_address" + ] + } }, dateTime1: '2016-08-22T21:46:10.225Z', currency1: '1,000', diff --git a/forms-flow-forms/test/fixtures/forms/singlecomponents2.js b/forms-flow-forms/test/fixtures/forms/singlecomponents2.js index e6f2d9c678..682b16afde 100644 --- a/forms-flow-forms/test/fixtures/forms/singlecomponents2.js +++ b/forms-flow-forms/test/fixtures/forms/singlecomponents2.js @@ -486,111 +486,114 @@ module.exports = { select2: 'three', radio2: 'one', email2: 'none2@example.com', - phoneNumber2: '3947573039', + phoneNumber2: '(394) 757-3039', address2: { - "address_components": [ - { - "long_name": "123", - "short_name": "123", - "types": [ - "street_number" - ] - }, - { - "long_name": "Fake Drive", - "short_name": "Fake Dr", - "types": [ - "route" - ] - }, - { - "long_name": "Luray", - "short_name": "Luray", - "types": [ - "locality", - "political" - ] - }, - { - "long_name": "1, West Luray", - "short_name": "1, West Luray", - "types": [ - "administrative_area_level_3", - "political" - ] - }, - { - "long_name": "Page County", - "short_name": "Page County", - "types": [ - "administrative_area_level_2", - "political" - ] - }, - { - "long_name": "Virginia", - "short_name": "VA", - "types": [ - "administrative_area_level_1", - "political" - ] - }, - { - "long_name": "United States", - "short_name": "US", - "types": [ - "country", - "political" - ] - }, - { - "long_name": "22835", - "short_name": "22835", - "types": [ - "postal_code" - ] - }, - { - "long_name": "2722", - "short_name": "2722", - "types": [ - "postal_code_suffix" - ] - } - ], + "mode": "autocomplete", + "address": { + "address_components": [ + { + "long_name": "123", + "short_name": "123", + "types": [ + "street_number" + ] + }, + { + "long_name": "Fake Drive", + "short_name": "Fake Dr", + "types": [ + "route" + ] + }, + { + "long_name": "Luray", + "short_name": "Luray", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "1, West Luray", + "short_name": "1, West Luray", + "types": [ + "administrative_area_level_3", + "political" + ] + }, + { + "long_name": "Page County", + "short_name": "Page County", + "types": [ + "administrative_area_level_2", + "political" + ] + }, + { + "long_name": "Virginia", + "short_name": "VA", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "United States", + "short_name": "US", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "22835", + "short_name": "22835", + "types": [ + "postal_code" + ] + }, + { + "long_name": "2722", + "short_name": "2722", + "types": [ + "postal_code_suffix" + ] + } + ], "formatted_address": "123 Fake Dr, Luray, VA 22835, USA", "geometry": { - "bounds": { - "northeast": { - "lat": 38.7062041, + "bounds": { + "northeast": { + "lat": 38.7062041, "lng": -78.5065 - }, - "southwest": { - "lat": 38.70619, + }, + "southwest": { + "lat": 38.70619, "lng": -78.5065048 - } - }, - "location": { - "lat": 38.7062041, + } + }, + "location": { + "lat": 38.7062041, "lng": -78.5065 - }, - "location_type": "RANGE_INTERPOLATED", + }, + "location_type": "RANGE_INTERPOLATED", "viewport": { - "northeast": { - "lat": 38.7075460302915, + "northeast": { + "lat": 38.7075460302915, "lng": -78.50515341970849 - }, - "southwest": { - "lat": 38.7048480697085, + }, + "southwest": { + "lat": 38.7048480697085, "lng": -78.50785138029151 + } } - } - }, - "partial_match": true, + }, + "partial_match": true, "place_id": "EiExMjMgRmFrZSBEciwgTHVyYXksIFZBIDIyODM1LCBVU0E", "types": [ - "street_address" - ] + "street_address" + ] + } }, dateTime2: '2016-08-23T21:48:10.225Z', currency2: '2,000', @@ -606,4 +609,4 @@ module.exports = { two: "alpha" } } -} \ No newline at end of file +} diff --git a/forms-flow-forms/test/fixtures/forms/singlecomponents3.js b/forms-flow-forms/test/fixtures/forms/singlecomponents3.js index fe3627da64..70d01cbb74 100644 --- a/forms-flow-forms/test/fixtures/forms/singlecomponents3.js +++ b/forms-flow-forms/test/fixtures/forms/singlecomponents3.js @@ -486,111 +486,114 @@ module.exports = { select2: 'three', radio2: 'one', email2: 'none2@example.com', - phoneNumber2: '3947573039', + phoneNumber2: '(394) 757-3039', address2: { - "address_components": [ - { - "long_name": "123", - "short_name": "123", - "types": [ - "street_number" - ] - }, - { - "long_name": "Fake Drive", - "short_name": "Fake Dr", - "types": [ - "route" - ] - }, - { - "long_name": "Luray", - "short_name": "Luray", - "types": [ - "locality", - "political" - ] - }, - { - "long_name": "1, West Luray", - "short_name": "1, West Luray", - "types": [ - "administrative_area_level_3", - "political" - ] - }, - { - "long_name": "Page County", - "short_name": "Page County", - "types": [ - "administrative_area_level_2", - "political" - ] - }, - { - "long_name": "Virginia", - "short_name": "VA", - "types": [ - "administrative_area_level_1", - "political" - ] - }, - { - "long_name": "United States", - "short_name": "US", - "types": [ - "country", - "political" - ] - }, - { - "long_name": "22835", - "short_name": "22835", - "types": [ - "postal_code" - ] - }, - { - "long_name": "2722", - "short_name": "2722", - "types": [ - "postal_code_suffix" - ] - } - ], + "mode": "autocomplete", + "address": { + "address_components": [ + { + "long_name": "123", + "short_name": "123", + "types": [ + "street_number" + ] + }, + { + "long_name": "Fake Drive", + "short_name": "Fake Dr", + "types": [ + "route" + ] + }, + { + "long_name": "Luray", + "short_name": "Luray", + "types": [ + "locality", + "political" + ] + }, + { + "long_name": "1, West Luray", + "short_name": "1, West Luray", + "types": [ + "administrative_area_level_3", + "political" + ] + }, + { + "long_name": "Page County", + "short_name": "Page County", + "types": [ + "administrative_area_level_2", + "political" + ] + }, + { + "long_name": "Virginia", + "short_name": "VA", + "types": [ + "administrative_area_level_1", + "political" + ] + }, + { + "long_name": "United States", + "short_name": "US", + "types": [ + "country", + "political" + ] + }, + { + "long_name": "22835", + "short_name": "22835", + "types": [ + "postal_code" + ] + }, + { + "long_name": "2722", + "short_name": "2722", + "types": [ + "postal_code_suffix" + ] + } + ], "formatted_address": "123 Fake Dr, Luray, VA 22835, USA", "geometry": { - "bounds": { - "northeast": { - "lat": 38.7062041, + "bounds": { + "northeast": { + "lat": 38.7062041, "lng": -78.5065 - }, - "southwest": { - "lat": 38.70619, + }, + "southwest": { + "lat": 38.70619, "lng": -78.5065048 - } - }, - "location": { - "lat": 38.7062041, + } + }, + "location": { + "lat": 38.7062041, "lng": -78.5065 - }, - "location_type": "RANGE_INTERPOLATED", + }, + "location_type": "RANGE_INTERPOLATED", "viewport": { - "northeast": { - "lat": 38.7075460302915, + "northeast": { + "lat": 38.7075460302915, "lng": -78.50515341970849 - }, - "southwest": { - "lat": 38.7048480697085, + }, + "southwest": { + "lat": 38.7048480697085, "lng": -78.50785138029151 + } } - } - }, - "partial_match": true, + }, + "partial_match": true, "place_id": "EiExMjMgRmFrZSBEciwgTHVyYXksIFZBIDIyODM1LCBVU0E", "types": [ - "street_address" - ] + "street_address" + ] + } }, dateTime2: '2016-08-23T21:48:10.225Z', currency2: '2,000', diff --git a/forms-flow-forms/test/fixtures/templates/missingResourceAction.json b/forms-flow-forms/test/fixtures/templates/missingResourceAction.json new file mode 100644 index 0000000000..04aeba69da --- /dev/null +++ b/forms-flow-forms/test/fixtures/templates/missingResourceAction.json @@ -0,0 +1,66 @@ +{ + "title": "missingResourceAction", + "name": "missingResourceAction", + "version": "2.0.0", + "description": "missingResourceAction tests", + "roles": { + "administrator": { + "title": "Administrator", + "description": "A role for Administrative Users.", + "admin": true, + "default": false + }, + "authenticated": { + "title": "Authenticated", + "description": "A role for Authenticated Users.", + "admin": false, + "default": false + }, + "anonymous": { + "title": "Anonymous", + "description": "A role for Anonymous Users.", + "admin": false, + "default": true + } + }, + "resources": { + "b": { + "title": "Resource", + "type": "resource", + "name": "b", + "path": "b", + "tags": [], + "submissionAccess": [], + "access": [], + "components": [] + } + }, + "forms": { + "a": { + "title": "Form", + "type": "form", + "name": "a", + "path": "a", + "tags": [], + "submissionAccess": [], + "access": [], + "components": [] + } + }, + "actions": { + "a:save": { + "title": "Save Submission", + "name": "save", + "handler": ["before"], + "method": ["create", "update"], + "form": "a", + "priority": 11, + "settings": { + "resource": "c", + "fields": { + "name": "test" + } + } + } + } +} \ No newline at end of file diff --git a/forms-flow-forms/test/form.js b/forms-flow-forms/test/form.js index d4d70d37e9..601b23e9a0 100644 --- a/forms-flow-forms/test/form.js +++ b/forms-flow-forms/test/form.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var _ = require('lodash'); var chance = new (require('chance'))(); @@ -1818,7 +1818,7 @@ module.exports = function(app, template, hook) { }); }); - it('Negative Min Length validation wont crash the server on submission', function(done) { + it("Negative Min Length validation won't crash the server on submission", function(done) { var submission = { data: { foo: 'bar' @@ -1901,7 +1901,7 @@ module.exports = function(app, template, hook) { }); }); - it('Negative Max Length validation wont crash the server on submission', function(done) { + it("Negative Max Length validation will correctly reject submission", function(done) { var submission = { data: { foo: 'bar' @@ -1913,14 +1913,16 @@ module.exports = function(app, template, hook) { .set('x-jwt-token', template.users.admin.token) .send(submission) .expect('Content-Type', /json/) - .expect(201) + .expect(400) .end(function(err, res) { if (err) { return done(err); } var response = res.body; - assert.deepEqual(response.data, submission.data); + assert.deepEqual(response.name, 'ValidationError'); + assert.equal(response.details.length, 1); + assert.equal(response.details[0].message, 'foo must have no more than -1 characters.'); done(); }); }); @@ -2031,7 +2033,7 @@ module.exports = function(app, template, hook) { var response = res.body; assert.deepEqual(response.name, 'ValidationError'); assert.equal(response.details.length, 1); - assert.equal(response.details[0].message, '"test" must be larger than or equal to 0'); + assert.equal(response.details[0].message, 'test cannot be less than 0.'); done(); }); }); @@ -2250,7 +2252,7 @@ module.exports = function(app, template, hook) { var response = res.body; assert.equal(_.get(response, 'name'), 'ValidationError'); assert.equal(response.details.length, 1); - assert.equal(_.get(response, 'details[0].message'), '"number" must be larger than or equal to 0'); + assert.equal(_.get(response, 'details[0].message'), 'number cannot be less than 0.'); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -2824,7 +2826,7 @@ module.exports = function(app, template, hook) { }); }); - it('A duplicate submission can not be made', function(done) { + it('A duplicate submission cannot be made', function(done) { var submission = { data: { email: email.toString().toUpperCase() @@ -2868,7 +2870,7 @@ module.exports = function(app, template, hook) { it('Unique field data regex is not triggered by similar submissions (before)', function(done) { var submission = { data: { - email: email + '1' + email: email + 'a' } }; @@ -2892,7 +2894,7 @@ module.exports = function(app, template, hook) { it('Unique field data regex is not triggered by similar submissions (after)', function(done) { var submission = { data: { - email: '1' + email + email: 'a' + email } }; @@ -3062,7 +3064,7 @@ module.exports = function(app, template, hook) { }); }); - it('A duplicate submission can not be made', function(done) { + it('A duplicate submission cannot be made', function(done) { var submission = { data: { container1: { @@ -3363,7 +3365,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3389,7 +3391,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3415,7 +3417,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'number.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3441,7 +3443,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'number.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3467,7 +3469,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3494,7 +3496,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3521,7 +3523,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3547,7 +3549,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3573,7 +3575,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3599,7 +3601,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3625,7 +3627,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3651,7 +3653,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3677,7 +3679,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3703,7 +3705,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3729,7 +3731,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3755,7 +3757,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3781,7 +3783,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3807,7 +3809,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3833,7 +3835,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3859,7 +3861,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3885,7 +3887,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3911,7 +3913,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3937,7 +3939,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3963,7 +3965,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'any.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -3989,9 +3991,9 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 2); assert.equal(err.details[0].path, 'foo'); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); assert.equal(err.details[1].path, 'bar'); - assert.equal(err.details[1].type, 'string.custom'); + assert.equal(err.details[1].context.validator, 'custom'); return done(); }); }); @@ -4016,7 +4018,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.deepEqual(err.details[0].path, ['mydg', 0, 'foo']); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); }); @@ -4042,7 +4044,7 @@ module.exports = function(app, template, hook) { assert(err.details instanceof Array); assert.equal(err.details.length, 1); assert.deepEqual(err.details[0].path, ['foo']); - assert.equal(err.details[0].type, 'string.custom'); + assert.equal(err.details[0].context.validator, 'custom'); return done(); }); @@ -4166,7 +4168,6 @@ module.exports = function(app, template, hook) { if (err) { return done(err); } - // console.log(JSON.stringify(res.body, null, 4)); console.log('process.env.FILTER_ACCESS', process.env.FILTER_ACCESS); @@ -4184,14 +4185,11 @@ module.exports = function(app, template, hook) { console.log('projectFilter', projectFilter); if (filter && projectFilter) { - assert.equal(Object.keys(res.body.roles).length, 0); assert.equal(Object.keys(res.body.forms).length, 0); } else { - assert.notEqual(Object.keys(res.body.roles).length, 0); assert.notEqual(Object.keys(res.body.forms).length, 0); } - done(); }); }); @@ -4439,7 +4437,7 @@ module.exports = function(app, template, hook) { var references = []; it('Should create a new submission in that form.', (done) => { - async.eachOf(resources, (resource, index, next) => { + async.eachOfSeries(resources, (resource, index, next) => { request(app) .post(hook.alter('url', '/form/' + referenceForm._id + '/submission', template)) .set('x-jwt-token', template.users.admin.token) diff --git a/forms-flow-forms/test/formio-supertest.js b/forms-flow-forms/test/formio-supertest.js new file mode 100644 index 0000000000..89ce5c30db --- /dev/null +++ b/forms-flow-forms/test/formio-supertest.js @@ -0,0 +1,28 @@ +const supertest = require('supertest'); +const use = require('superagent-use'); +const captureError = require('supertest-capture-error'); + +const request = app => { + const _request = use(supertest(app)); + + // Enable automatic retries in cases of network flakiness + _request.use(test => { + test.retry(10); + }); + + // Cleaner messages + _request.use(captureError((error, test) => { + try { + error.message += '\n\nResponse: ' + JSON.stringify(JSON.parse(test.res.text), null, 4) + } + catch (err) { + console.warn(err); + } + + error.stack = ''; + })); + + return _request; +}; + +module.exports = request; diff --git a/forms-flow-forms/test/helper.js b/forms-flow-forms/test/helper.js index 11948743f0..bbbadf2257 100644 --- a/forms-flow-forms/test/helper.js +++ b/forms-flow-forms/test/helper.js @@ -1,6 +1,7 @@ +/* eslint-disable max-statements */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var chance = new (require('chance'))(); var assert = require('assert'); var _ = require('lodash'); @@ -33,13 +34,31 @@ module.exports = function(app) { permsConfig.push({ type: name, roles: _.map(roles, (roleName) => { - return this.template.roles[roleName]._id.toString() + return this.template.roles[roleName]._id.toString(); }) }); }); return permsConfig; }; + Helper.prototype.getExport = function(form, format, done) { + let url = ''; + if (this.template.project && this.template.project._id) { + url += `/project/${this.template.project._id}`; + } + url += `/form/${form._id}/export?format=${format || 'csv'}`; + + request(app) + .get(url) + .set('x-jwt-token', this.owner.token) + .expect('Content-Type', format === 'json' ? /json/ : 'text/csv') + .expect(200) + .end((err, res) => { + this.owner.token = res.headers['x-jwt-token']; + done(err, res); + }); + }; + Helper.prototype.getTemplate = function() { return this.template; }; @@ -52,7 +71,7 @@ module.exports = function(app) { Helper.prototype.getForms = function(done) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } url += '/form?limit=9999'; request(app) @@ -85,9 +104,9 @@ module.exports = function(app) { Helper.prototype.getActions = function(form, done) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/action'; + url += `/form/${ this.template.forms[form]._id }/action`; request(app) .get(url) .set('x-jwt-token', this.owner.token) @@ -98,7 +117,7 @@ module.exports = function(app) { return done(err, res); } if (!res.body) { - return done('No response', res) + return done('No response', res); } this.lastResponse = res; @@ -160,9 +179,9 @@ module.exports = function(app) { if (_action) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/action/' + _action._id; + url += `/form/${ this.template.forms[form]._id }/action/${ _action._id}`; request(app) .delete(url) @@ -173,7 +192,7 @@ module.exports = function(app) { return done(err, res); } if (!res.body) { - return done('No response', res) + return done('No response', res); } this.lastResponse = res; @@ -191,7 +210,7 @@ module.exports = function(app) { Helper.prototype.getRoles = function(done) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } url += '/role?limit=9999'; @@ -258,7 +277,7 @@ module.exports = function(app) { } request(app) - .get('/project/' + this.template.project._id) + .get(`/project/${ this.template.project._id}`) .set('x-jwt-token', this.owner.token) .expect('Content-Type', /json/) .expect(200) @@ -318,9 +337,9 @@ module.exports = function(app) { Helper.prototype.updateForm = function(form, done) { let url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + form._id; + url += `/form/${ form._id}`; request(app).put(url) .send(_.omit(form, 'modified')) @@ -335,7 +354,7 @@ module.exports = function(app) { this.template.forms[form.name] = res.body; done(null, res.body); }); - } + }; Helper.prototype.upsertForm = function(form, done) { this.contextName = form.name; @@ -363,7 +382,7 @@ module.exports = function(app) { var status = 201; var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } url += '/form'; var data = { @@ -378,7 +397,7 @@ module.exports = function(app) { if (this.template.forms.hasOwnProperty(form.name)) { method = 'put'; status = 200; - url += '/' + this.template.forms[form.name]._id; + url += `/${ this.template.forms[form.name]._id}`; data = { components: form.components }; @@ -454,7 +473,7 @@ module.exports = function(app) { if (this.template.forms.hasOwnProperty(_id)) { _id = this.template.forms[_id]._id.toString(); } - let url = '/form/' + _id; + let url = `/form/${ _id}`; if (this.hook) { url = this.hook.alter('url', url, this.template); } @@ -518,9 +537,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/action/' + _action._id; + url += `/form/${ this.template.forms[form]._id }/action/${ _action._id}`; request(app) .put(url) @@ -533,7 +552,7 @@ module.exports = function(app) { return done(err, res); } if (!res.body) { - return done('No response', res) + return done('No response', res); } this.lastResponse = res; @@ -591,9 +610,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/action'; + url += `/form/${ this.template.forms[form]._id }/action`; request(app) .post(url) @@ -648,9 +667,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + submission.form + '/submission/' + submission._id; + url += `/form/${ submission.form }/submission/${ submission._id}`; let currentRequest = request(app).put(url).send(submission); if (user) { @@ -720,9 +739,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/submission'; + url += `/form/${ this.template.forms[form]._id }/submission`; // Allow passing in the submission as well if (!data.hasOwnProperty('data')) { @@ -815,12 +834,12 @@ module.exports = function(app) { var url = ''; var subIndex = true; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + this.template.forms[form]._id + '/submission'; + url += `/form/${ this.template.forms[form]._id }/submission`; if (typeof id === 'string') { subIndex = false; - url += '/' + id; + url += `/${ id}`; } let currentRequest = request(app).get(url).send(); @@ -891,9 +910,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/form/' + submission.form + '/submission/' + submission._id; + url += `/form/${ submission.form }/submission/${ submission._id}`; let currentRequest = request(app).delete(url).send(submission); if (user) { @@ -937,7 +956,7 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } url += '/role'; @@ -947,7 +966,7 @@ module.exports = function(app) { _role = this.getRole(role.title || role); } - url += '/' + _role._id; + url += `/${ _role._id}`; request(app) .put(url) .send(update) @@ -1003,9 +1022,9 @@ module.exports = function(app) { var url = ''; if (this.template.project && this.template.project._id) { - url += '/project/' + this.template.project._id; + url += `/project/${ this.template.project._id}`; } - url += '/role/' + _role._id; + url += `/role/${ _role._id}`; request(app) .delete(url) @@ -1056,7 +1075,7 @@ module.exports = function(app) { this.template.users[user].data.password = data.password; // Now authenticate as this user to get JWT token. - this.createSubmission(form + 'Login', {data: { + this.createSubmission(`${form }Login`, {data: { email: this.template.users[user].data.email, password: this.template.users[user].data.password }}, null, [/application\/json/, 200], (err) => { diff --git a/forms-flow-forms/test/nested.js b/forms-flow-forms/test/nested.js index ce855c6d69..073cbab8a3 100644 --- a/forms-flow-forms/test/nested.js +++ b/forms-flow-forms/test/nested.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var docker = process.env.DOCKER; diff --git a/forms-flow-forms/test/resource.js b/forms-flow-forms/test/resource.js index d288be845a..ea240d33a6 100644 --- a/forms-flow-forms/test/resource.js +++ b/forms-flow-forms/test/resource.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var _ = require('lodash'); diff --git a/forms-flow-forms/test/roles.js b/forms-flow-forms/test/roles.js index 08c5e0bb0d..6c1df7fac8 100644 --- a/forms-flow-forms/test/roles.js +++ b/forms-flow-forms/test/roles.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var _ = require('lodash'); var async = require('async'); diff --git a/forms-flow-forms/test/submission-access.js b/forms-flow-forms/test/submission-access.js index a1ecce6416..f655a5a2b3 100644 --- a/forms-flow-forms/test/submission-access.js +++ b/forms-flow-forms/test/submission-access.js @@ -1,7 +1,7 @@ /* eslint-env mocha */ 'use strict'; -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var async = require('async'); var _ = require('lodash'); @@ -6445,78 +6445,39 @@ module.exports = function(app, template, hook) { }); }); - describe('Submission Resource Access Initial', function() { + describe('Submission Resource Access Initial', () => { let subAccessForm = { title: 'Submission Access', name: 'subaccess', path: 'subaccess', - components: [{ - "defaultPermission": "bad", - "conditional": { - "eq": "", - "when": null, - "show": "" - }, - "tags": [], - "type": "select", - "validate": { - "required": false - }, - "clearOnHide": true, - "hidden": false, - "persistent": true, - "unique": false, - "protected": false, - "multiple": false, - "template": "{{ item.label }}", - "authenticate": false, - "filter": "", - "refreshOn": "", - "defaultValue": "", - "valueProperty": "", - "dataSrc": "url", - "data": { - "project": "", - "custom": "", - "resource": "", - "url": "http://myfake.com/nothing", - "json": "", - "values": [] + components: [ + { + defaultPermission: 'bad', + type: 'select', + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', + }, + key: 'permField', + label: 'Perm Field', + input: true, }, - "placeholder": "", - "key": "permField", - "label": "Perm Field", - "tableView": true, - "input": true - }, { - "type": "button", - "theme": "primary", - "disableOnInvalid": false, - "action": "submit", - "block": false, - "rightIcon": "", - "leftIcon": "", - "size": "md", - "key": "submit", - "tableView": false, - "label": "Submit", - "input": true - }] + ], }; - it('Create the form with submission access component', function(done) { + it('Create the form with submission access component', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(subAccessForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response.components, subAccessForm.components); subAccessForm = response; @@ -6528,29 +6489,29 @@ module.exports = function(app, template, hook) { }); }); - it('Should not allow bad permissions.', done => { + it('Should not allow bad permissions.', (done) => { let submission = { data: { permField: { _id: '5919b9ddf6cc4b15bd98e0d4', other: 'data', - title: 'None' - } - } + title: 'None', + }, + }, }; + request(app) - .post(hook.alter('url', '/form/' + subAccessForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${subAccessForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(submission) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - + const response = res.body; submission = response; assert.deepEqual(submission.access, []); @@ -6562,22 +6523,22 @@ module.exports = function(app, template, hook) { }); }); - it('Changes permission to read', done => { + it('Changes permission to read', (done) => { subAccessForm.components[0].defaultPermission = 'read'; request(app) - .put(hook.alter('url', '/form/' + subAccessForm._id, template)) + .put(hook.alter('url', `/form/${subAccessForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(subAccessForm) .expect('Content-Type', /json/) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - //assert.deepEqual(response.components, subAccessForm.components); + const response = res.body; + assert.deepEqual(response.components, subAccessForm.components); subAccessForm = response; @@ -6588,28 +6549,28 @@ module.exports = function(app, template, hook) { }); }); - it('Should handle objects without ids.', done => { + it('Should handle objects without ids.', (done) => { let submission = { data: { permField: { other: 'data', - title: 'None' - } - } + title: 'None', + }, + }, }; + request(app) - .post(hook.alter('url', '/form/' + subAccessForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${subAccessForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(submission) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - + const response = res.body; submission = response; assert.deepEqual(submission.access, []); @@ -6622,9 +6583,9 @@ module.exports = function(app, template, hook) { }); }); - describe('Submission Resource Access', function() { + describe('Submission Resource Access - Legacy Mode', () => { // Create the temp form for testing. - var tempForm = { + let tempForm = { title: 'Submission resource access check', name: 'resourceaccess', path: 'resourceaccess', @@ -6633,178 +6594,116 @@ module.exports = function(app, template, hook) { submissionAccess: [], components: [ { - "defaultPermission": "read", - "conditional": { - "eq": "", - "when": null, - "show": "" - }, - "tags": [], - "type": "select", - "validate": { - "required": false + defaultPermission: 'read', + type: 'select', + multiple: true, + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', }, - "clearOnHide": true, - "hidden": false, - "persistent": true, - "unique": false, - "protected": false, - "multiple": true, - "template": "{{ item.label }}", - "authenticate": false, - "filter": "", - "refreshOn": "", - "defaultValue": "", - "valueProperty": "", - "dataSrc": "url", - "data": { - "project": "", - "custom": "", - "resource": "", - "url": "http://myfake.com/nothing", - "json": "", - "values": [] - }, - "placeholder": "", - "key": "readPerm", - "label": "Read Field", - "tableView": true, - "input": true + key: 'readPerm', + label: 'Read Field', + input: true, }, { - "defaultPermission": "write", - "conditional": { - "eq": "", - "when": null, - "show": "" - }, - "tags": [], - "type": "select", - "validate": { - "required": false - }, - "clearOnHide": true, - "hidden": false, - "persistent": true, - "unique": false, - "protected": false, - "multiple": true, - "template": "{{ item.label }}", - "authenticate": false, - "filter": "", - "refreshOn": "", - "defaultValue": "", - "valueProperty": "", - "dataSrc": "url", - "data": { - "project": "", - "custom": "", - "resource": "", - "url": "http://myfake.com/nothing", - "json": "", - "values": [] + submissionAccess: [ + { + type: 'read', + roles: [], + }, + { + type: 'create', + roles: [], + }, + { + type: 'update', + roles: [], + }, + ], + type: 'select', + multiple: true, + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', }, - "placeholder": "", - "key": "writePerm", - "label": "Write Field", - "tableView": true, - "input": true + key: 'writePerm', + label: 'Write Field', + input: true, }, { - "defaultPermission": "admin", - "conditional": { - "eq": "", - "when": null, - "show": "" - }, - "tags": [], - "type": "select", - "validate": { - "required": false - }, - "clearOnHide": true, - "hidden": false, - "persistent": true, - "unique": false, - "protected": false, - "multiple": true, - "template": "{{ item.label }}", - "authenticate": false, - "filter": "", - "refreshOn": "", - "defaultValue": "", - "valueProperty": "", - "dataSrc": "url", - "data": { - "project": "", - "custom": "", - "resource": "", - "url": "http://myfake.com/nothing", - "json": "", - "values": [] + submissionAccess: [ + { + type: 'read', + roles: [], + }, + { + type: 'create', + roles: [], + }, + { + type: 'update', + roles: [], + }, + { + type: 'delete', + roles: [], + }, + ], + type: 'select', + multiple: true, + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', }, - "placeholder": "", - "key": "adminPerm", - "label": "Admin Field", - "tableView": true, - "input": true + key: 'adminPerm', + label: 'Admin Field', + input: true, }, { type: 'textfield', - validate: { - custom: '', - pattern: '', - maxLength: '', - minLength: '', - required: false - }, - defaultValue: '', - multiple: false, - suffix: '', - prefix: '', - placeholder: 'value', key: 'value', label: 'value', - inputMask: '', - inputType: 'text', - input: true - } - ] + input: true, + }, + ], }; // Store the temp submission for this test suite. - var submission = { + let submission = { data: { - value: 'foo' - } + value: 'foo', + }, }; - var tempSubmission = {}; - var tempSubmissions = []; + let tempSubmission = {}; let managerRole = null; let managerResource = null; let managerRegister = null; let manager = null; - describe('Bootstrap', function() { + + describe('Bootstrap', () => { it('Create a manager role', (done) => { request(app) .post(hook.alter('url', '/role', template)) .set('x-jwt-token', template.users.admin.token) .send({ - "title": "Manager", - "description": "A role for Manager Users.", - "admin": false, - "default": false + title: 'Manager', + description: 'A role for Manager Users.', + admin: false, + default: false, }) .expect(201) .end((err, res) => { if (err) { return done(err); } + managerRole = res.body; + done(); }); }); - it('Create a manager resource', function(done) { + + it('Create a manager resource', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) @@ -6813,98 +6712,71 @@ module.exports = function(app, template, hook) { name: 'manager', path: 'manager', type: 'resource', - tags: [], - "submissionAccess": [], - "access": [], components: [ { type: 'email', - persistent: true, - unique: false, - protected: false, - defaultValue: '', - suffix: '', - prefix: '', - placeholder: 'Enter your email address', key: 'email', label: 'Email', - inputType: 'email', - tableView: true, - input: true + input: true, }, { type: 'password', - persistent: true, - protected: true, - suffix: '', - prefix: '', - placeholder: 'Enter your password.', key: 'password', label: 'Password', - inputType: 'password', - tableView: false, - input: true - }, - { - theme: 'primary', - disableOnInvalid: true, - action: 'submit', - block: false, - rightIcon: '', - leftIcon: '', - size: 'md', - key: 'submit', - label: 'Submit', input: true, - type: 'button' - } - ] + }, + ], }) .expect(201) .end((err, res) => { if (err) { return done(err); } + managerResource = res.body; + done(); }); }); - it('Create a manager role assignment action', function(done) { + + it('Create a manager role assignment action', (done) => { request(app) - .post(hook.alter('url', '/form/' + managerResource._id.toString() + '/action', template)) + .post(hook.alter('url', `/form/${managerResource._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ - "title": "Role Assignment", - "name": "role", - "priority": 1, - "handler": ["after"], - "method": ["create"], - "form": managerResource._id.toString(), - "settings": { - "association": "new", - "type": "add", - "role": managerRole._id.toString() - } + title: 'Role Assignment', + name: 'role', + priority: 1, + handler: ['after'], + method: ['create'], + form: managerResource._id, + settings: { + association: 'new', + type: 'add', + role: managerRole._id, + }, }) .expect(201) .end(done); }); - it('Create a manager role save action', function(done) { + + it('Create a manager role save action', (done) => { request(app) - .post(hook.alter('url', '/form/' + managerResource._id.toString() + '/action', template)) + .post(hook.alter('url', `/form/${managerResource._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ - "title": "Save Submission", - "name": "save", - "form": managerResource._id.toString(), - "handler": ["before"], - "method": ["create", "update"], - "priority": 10 + title: 'Save Submission', + name: 'save', + form: managerResource._id, + handler: ['before'], + method: ['create', 'update'], + priority: 10, }) .expect(201) .end(done); }); - it('Create a manager register form', function(done) { + + it('Create a manager register form', (done) => { request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) @@ -6913,62 +6785,32 @@ module.exports = function(app, template, hook) { name: 'managerRegister', path: 'manager/register', type: 'form', - tags: [], - "submissionAccess": [ + submissionAccess: [ { - "type": "create_own", - "roles": [template.roles.anonymous._id.toString()] - } + type: 'create_own', + roles: [template.roles.anonymous._id.toString()], + }, ], - "access": [ + access: [ { - "type": "read_all", - "roles": [template.roles.anonymous._id.toString()] - } + type: 'read_all', + roles: [template.roles.anonymous._id.toString()], + }, ], components: [ { type: 'email', - persistent: true, - unique: false, - protected: false, - defaultValue: '', - suffix: '', - prefix: '', - placeholder: 'Enter your email address', key: 'email', label: 'Email', - inputType: 'email', - tableView: true, - input: true + input: true, }, { type: 'password', - persistent: true, - protected: true, - suffix: '', - prefix: '', - placeholder: 'Enter your password.', key: 'password', label: 'Password', - inputType: 'password', - tableView: false, - input: true - }, - { - theme: 'primary', - disableOnInvalid: true, - action: 'submit', - block: false, - rightIcon: '', - leftIcon: '', - size: 'md', - key: 'submit', - label: 'Submit', input: true, - type: 'button' - } - ] + }, + ], }) .expect(201) .end((err, res) => { @@ -6977,92 +6819,100 @@ module.exports = function(app, template, hook) { } managerRegister = res.body; + done(); }); }); - it('Create a manager register save action', function(done) { + + it('Create a manager register save action', (done) => { request(app) - .post(hook.alter('url', '/form/' + managerRegister._id.toString() + '/action', template)) + .post(hook.alter('url', `/form/${managerRegister._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ name: 'save', title: 'Save Submission', - form: managerRegister._id.toString(), + form: managerRegister._id, priority: 11, method: ['create', 'update'], handler: ['before'], settings: { - resource: managerResource._id.toString(), + resource: managerResource._id, fields: { email: 'email', - password: 'password' - } - } + password: 'password', + }, + }, }) .expect(201) .end(done); }); - it('Create a manager login action', function(done) { + + it('Create a manager login action', (done) => { request(app) - .post(hook.alter('url', '/form/' + managerRegister._id.toString() + '/action', template)) + .post(hook.alter('url', `/form/${managerRegister._id}/action`, template)) .set('x-jwt-token', template.users.admin.token) .send({ name: 'login', title: 'Login', - form: managerRegister._id.toString(), + form: managerRegister._id, priority: 2, method: ['create'], handler: ['before'], settings: { - resources: [managerResource._id.toString()], + resources: [managerResource._id], username: 'email', password: 'password', allowedAttempts: 5, attemptWindow: 10, - lockWait: 10 - } + lockWait: 10, + }, }) .expect(201) .end(done); }); + it('Register a new manager', (done) => { request(app) .post(hook.alter('url', '/manager/register/submission', template)) .send({ data: { email: 'manager@example.com', - password: 'test123' - } + password: 'test123', + }, }) .expect(201) .end((err, res) => { if (err) { return done(err); } + manager = res.body; manager.token = res.headers['x-jwt-token']; + done(); }); }); - it('Create the form', function(done) { + + it('Create the form', (done) => { tempForm.submissionAccess = [ { type: 'read_all', - roles: [managerRole._id.toString()] - } + roles: [managerRole._id], + }, ]; + request(app) .post(hook.alter('url', '/form', template)) .set('x-jwt-token', template.users.admin.token) .send(tempForm) .expect('Content-Type', /json/) .expect(201) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -7074,9 +6924,10 @@ module.exports = function(app, template, hook) { assert.equal(response.access.length, 1); assert.equal(response.access[0].type, 'read_all'); assert.equal(response.access[0].roles.length, 4); - assert.notEqual(response.access[0].roles.indexOf(template.roles.anonymous._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.authenticated._id.toString()), -1); - assert.notEqual(response.access[0].roles.indexOf(template.roles.administrator._id.toString()), -1); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); + assert(response.access[0].roles.includes(managerRole._id.toString())); assert.deepEqual(response.submissionAccess, tempForm.submissionAccess); assert.deepEqual(response.components, tempForm.components); tempForm = response; @@ -7089,20 +6940,20 @@ module.exports = function(app, template, hook) { }); }); - describe('Admin Users', function() { - it('An Admin can create a submission', function(done) { + describe('Admin Users', () => { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -7119,7 +6970,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -7128,18 +6978,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read a submission, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can read a submission, without explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7149,23 +6999,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read the index of submissions, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can read the index of submissions, without explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7179,23 +7029,22 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submissions resource access, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can update a submissions resource access, without explicit resource access (read)', (done) => { tempSubmission.data.readPerm = [template.users.admin]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.cloneDeep(tempSubmission); + const response = res.body; + const expected = _.cloneDeep(tempSubmission); expected.access = [{type: 'read', resources: [template.users.admin._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); @@ -7208,18 +7057,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read a submission, with explicit resource access (read)', function(done) { + it('An Admin, the owner, can read a submission, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7229,23 +7078,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read the index of submissions, with explicit resource access (read)', function(done) { + it('An Admin, the owner, can read the index of submissions, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7259,20 +7108,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can update a submission, without explicit resource access (read)', (done) => { tempSubmission.data.value = '1231888123q'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7284,20 +7134,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission owner, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can update a submission owner, without explicit resource access (read)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7307,17 +7158,17 @@ module.exports = function(app, template, hook) { tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7330,18 +7181,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can delete a submission, without explicit resource access (read)', function(done) { + it('An Admin, the owner, can delete a submission, without explicit resource access (read)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -7351,19 +7202,19 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin can create a submission', function(done) { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -7380,7 +7231,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -7389,24 +7239,38 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submissions resource access, without explicit resource access (write)', function(done) { + it('An Admin, the owner, can update a submissions resource access, without explicit resource access (write)', (done) => { tempSubmission.data.writePerm = [template.users.admin]; - delete tempSubmission.data.readPerm; - delete tempSubmission.data.adminPerm; + tempSubmission.data.readPerm = []; + tempSubmission.data.adminPerm = []; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'write', resources: [template.users.admin._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.admin._id] + }, + { + type: 'create', + resources: [template.users.admin._id] + }, + { + type: 'update', + resources: [template.users.admin._id] + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -7418,18 +7282,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read a submission, with explicit resource access (write)', function(done) { + it('An Admin, the owner, can read a submission, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7439,23 +7303,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read the index of submissions, with explicit resource access (write)', function(done) { + it('An Admin, the owner, can read the index of submissions, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7469,23 +7333,23 @@ module.exports = function(app, template, hook) { }); }); - it('A Manager can read the index of submissions, with explicit resource access', function(done) { + it('A Manager can read the index of submissions, with explicit resource access', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', manager.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7495,20 +7359,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission, with explicit resource access (write)', function(done) { + it('An Admin, the owner, can update a submission, with explicit resource access (write)', (done) => { tempSubmission.data.value = '1231888123q'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7520,20 +7385,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission owner, without explicit resource access (write)', function(done) { + it('An Admin, the owner, can update a submission owner, without explicit resource access (write)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7543,17 +7409,17 @@ module.exports = function(app, template, hook) { tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7566,18 +7432,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can delete a submission, without explicit resource access (write)', function(done) { + it('An Admin, the owner, can delete a submission, without explicit resource access (write)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -7587,19 +7453,19 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin can create a submission', function(done) { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -7616,7 +7482,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -7625,25 +7490,40 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submissions resource access, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can update a submissions resource access, with explicit resource access (admin)', (done) => { tempSubmission.data.adminPerm = [template.users.admin]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.readPerm; - var update = {access: [{type: 'admin', resources: [template.users.admin._id]}]}; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'admin', resources: [template.users.admin._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.admin._id], + }, + { + type: 'create', + resources: [template.users.admin._id], + }, + { + type: 'update', + resources: [template.users.admin._id], + }, + { + type: 'delete', + resources: [template.users.admin._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -7655,18 +7535,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read a submission, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can read a submission, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7676,23 +7556,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can read the index of submissions, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can read the index of submissions, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7706,20 +7586,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can update a submission, with explicit resource access (admin)', (done) => { tempSubmission.data.value = 'qqwee1231'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7731,20 +7612,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can update a submission owner, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can update a submission owner, with explicit resource access (admin)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7754,17 +7636,17 @@ module.exports = function(app, template, hook) { tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7777,18 +7659,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, the owner, can delete a submission, with explicit resource access (admin)', function(done) { + it('An Admin, the owner, can delete a submission, with explicit resource access (admin)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -7799,19 +7681,19 @@ module.exports = function(app, template, hook) { }); // Repeat the tests with another admin, not the owner. - it('An Admin can create a submission', function(done) { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -7828,7 +7710,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -7837,18 +7718,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read a submission, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can read a submission, without explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7858,23 +7739,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read the index of submissions, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can read the index of submissions, without explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7888,23 +7769,22 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (read)', (done) => { tempSubmission.data.readPerm = [template.users.admin2]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); + const response = res.body; + const expected = _.clone(tempSubmission); expected.access = [{type: 'read', resources: [template.users.admin2._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -7916,18 +7796,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read a submission, with explicit resource access (read)', function(done) { + it('An Admin, not the owner, can read a submission, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -7937,23 +7817,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read the index of submissions, with explicit resource access (read)', function(done) { + it('An Admin, not the owner, can read the index of submissions, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -7967,20 +7847,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can update a submission, without explicit resource access (read)', (done) => { tempSubmission.data.value = '1231888123q'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -7992,20 +7873,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission owner, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can update a submission owner, without explicit resource access (read)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8013,19 +7895,19 @@ module.exports = function(app, template, hook) { // Store the JWT for future API calls. template.users.admin2.token = res.headers['x-jwt-token']; - tempSubmission.owner = template.users.admin2._id; + tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8038,23 +7920,22 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (read)', (done) => { tempSubmission.data.readPerm = [template.users.user2]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); + const response = res.body; + const expected = _.clone(tempSubmission); expected.access = [{type: 'read', resources: [template.users.user2._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -8066,18 +7947,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can delete a submission, without explicit resource access (read)', function(done) { + it('An Admin, not the owner, can delete a submission, without explicit resource access (read)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -8087,19 +7968,19 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin can create a submission', function(done) { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -8116,7 +7997,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -8125,24 +8005,36 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (write)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (write)', (done) => { tempSubmission.data.writePerm = [template.users.admin2]; - delete tempSubmission.data.readPerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'write', resources: [template.users.admin2._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.admin2._id], + }, + { + type: 'create', + resources: [template.users.admin2._id], + }, + { + type: 'update', + resources: [template.users.admin2._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -8153,18 +8045,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read a submission, with explicit resource access (write)', function(done) { + it('An Admin, not the owner, can read a submission, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -8174,23 +8066,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read the index of submissions, with explicit resource access (write)', function(done) { + it('An Admin, not the owner, can read the index of submissions, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -8204,20 +8096,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission, with explicit resource access (write)', function(done) { + it('An Admin, not the owner, can update a submission, with explicit resource access (write)', (done) => { tempSubmission.data.value = '1231888123q'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8229,20 +8122,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission owner, without explicit resource access (write)', function(done) { + it('An Admin, not the owner, can update a submission owner, without explicit resource access (write)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8250,19 +8144,19 @@ module.exports = function(app, template, hook) { // Store the JWT for future API calls. template.users.admin2.token = res.headers['x-jwt-token']; - tempSubmission.owner = template.users.admin2._id; + tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8275,24 +8169,36 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (write)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, without explicit resource access (write)', (done) => { tempSubmission.data.writePerm = [template.users.user2]; - delete tempSubmission.data.readPerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'write', resources: [template.users.user2._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.user2._id], + }, + { + type: 'create', + resources: [template.users.user2._id], + }, + { + type: 'update', + resources: [template.users.user2._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -8303,18 +8209,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can delete a submission, without explicit resource access (write)', function(done) { + it('An Admin, not the owner, can delete a submission, without explicit resource access (write)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -8324,19 +8230,19 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin can create a submission', function(done) { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -8353,7 +8259,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -8362,24 +8267,40 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, with explicit resource access (admin)', (done) => { tempSubmission.data.adminPerm = [template.users.admin2]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.readPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'admin', resources: [template.users.admin2._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.admin2._id], + }, + { + type: 'create', + resources: [template.users.admin2._id], + }, + { + type: 'update', + resources: [template.users.admin2._id], + }, + { + type: 'delete', + resources: [template.users.admin2._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -8390,18 +8311,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read a submission, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can read a submission, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, tempSubmission); // Store the JWT for future API calls. @@ -8411,23 +8332,23 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can read the index of submissions, with explicit resource access (read)', function(done) { + it('An Admin, not the owner, can read the index of submissions, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -8441,20 +8362,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can update a submission, with explicit resource access (admin)', (done) => { tempSubmission.data.value = '1231888123233q'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8466,20 +8388,21 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submission owner, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can update a submission owner, with explicit resource access (admin)', (done) => { tempSubmission.owner = ''; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8487,19 +8410,19 @@ module.exports = function(app, template, hook) { // Store the JWT for future API calls. template.users.admin2.token = res.headers['x-jwt-token']; - tempSubmission.owner = template.users.admin2._id; + tempSubmission.owner = template.users.admin._id; request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); tempSubmission = response; @@ -8512,24 +8435,40 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can update a submissions resource access, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can update a submissions resource access, with explicit resource access (admin)', (done) => { tempSubmission.data.adminPerm = [template.users.user2]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.readPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'admin', resources: [template.users.user2._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.user2._id], + }, + { + type: 'create', + resources: [template.users.user2._id], + }, + { + type: 'update', + resources: [template.users.user2._id], + }, + { + type: 'delete', + resources: [template.users.user2._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -8540,18 +8479,18 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin, not the owner, can delete a submission, with explicit resource access (admin)', function(done) { + it('An Admin, not the owner, can delete a submission, with explicit resource access (admin)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin2.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -8562,20 +8501,20 @@ module.exports = function(app, template, hook) { }); }); - describe('Authenticated User', function() { - it('An Admin can create a submission', function(done) { + describe('Authenticated User', () => { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -8592,7 +8531,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -8601,18 +8539,19 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not read a submission, without explicit resource access', function(done) { + it('A user can not read a submission, without explicit resource access', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8621,17 +8560,17 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not read the index of submissions, without explicit resource access (read)', function(done) { + it('A user can not read the index of submissions, without explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.equal(response.length, 0); // Store the JWT for future API calls. @@ -8641,19 +8580,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submission, without explicit resource access', function(done) { + it('A user can not update a submission, without explicit resource access', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send({data: {value: 'baz'}}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8662,19 +8602,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submissions owner, without explicit resource access (read)', function(done) { + it('A user can not update a submissions owner, without explicit resource access (read)', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send({owner: template.users.user1._id}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8683,19 +8624,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submissions resource access, without explicit resource access', function(done) { + it('A user can not update a submissions resource access, without explicit resource access', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) - .send({access: [{type: 'admin', resources: [template.users.user1._id]}]}) + .send({access: [{type: 'delete', resources: [template.users.user1._id]}]}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8704,18 +8646,19 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not delete a submission, without explicit resource access', function(done) { + it('A user can not delete a submission, without explicit resource access', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8724,22 +8667,21 @@ module.exports = function(app, template, hook) { }); }); - it('Give the user read access to the submission', function(done) { + it('Give the user read access to the submission', (done) => { tempSubmission.data.readPerm = [template.users.user1]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); + const response = res.body; + const expected = _.clone(tempSubmission); expected.access = [{type: 'read', resources: [template.users.user1._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); @@ -8753,17 +8695,17 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read a submission, with explicit resource access (read)', function(done) { + it('A user can read a submission, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); // Store the JWT for future API calls. @@ -8773,23 +8715,23 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read the index of submissions, with explicit resource access (read)', function(done) { + it('A user can read the index of submissions, with explicit resource access (read)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -8803,19 +8745,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submission, without explicit resource access (read)', function(done) { + it('A user can not update a submission, without explicit resource access (read)', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send({data: {value: 'baz'}}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8824,19 +8767,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submissions owner, without explicit resource access (read)', function(done) { + it('A user can not update a submissions owner, without explicit resource access (read)', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send({owner: template.users.user1._id}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8845,19 +8789,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submissions resource access, without explicit resource access (read)', function(done) { + it('A user can not update a submissions resource access, without explicit resource access (read)', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) - .send({access: [{type: 'admin', resources: [template.users.user1._id]}]}) + .send({access: [{type: 'delete', resources: [template.users.user1._id]}]}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8866,18 +8811,19 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not delete a submission, without explicit resource access (read)', function(done) { + it('A user can not delete a submission, without explicit resource access (read)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -8886,23 +8832,37 @@ module.exports = function(app, template, hook) { }); }); - it('Give the user write access to the submission', function(done) { + it('Give the user write access to the submission', (done) => { + tempSubmission.data.readPerm = []; tempSubmission.data.writePerm = [template.users.user1]; - delete tempSubmission.data.readPerm; - delete tempSubmission.data.adminPerm; + tempSubmission.data.adminPerm = []; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'write', resources: [template.users.user1._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.user1._id], + }, + { + type: 'create', + resources: [template.users.user1._id], + }, + { + type: 'update', + resources: [template.users.user1._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); @@ -8915,17 +8875,17 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read a submission, with explicit resource access (write)', function(done) { + it('A user can read a submission, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); // Store the JWT for future API calls. @@ -8935,23 +8895,23 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read the index of submissions, with explicit resource access (write)', function(done) { + it('A user can read the index of submissions, with explicit resource access (write)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -8965,19 +8925,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can update a submission, with explicit resource access (write)', function(done) { + it('A user can update a submission, with explicit resource access (write)', (done) => { tempSubmission.data.value = 'baz'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); @@ -8990,19 +8951,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not update a submissions owner, without explicit resource access (write)', function(done) { + it('A user can not update a submissions owner, without explicit resource access (write)', (done) => { tempSubmission.owner = template.users.user1._id + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, ['modified', 'owner']), _.omit(tempSubmission, ['modified', 'owner'])); assert.notEqual(response.owner, tempSubmission.owner); @@ -9015,18 +8977,19 @@ module.exports = function(app, template, hook) { }); }); - it('A user can not delete a submission, without explicit resource access (write)', function(done) { + it('A user can not delete a submission, without explicit resource access (write)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); // Store the JWT for future API calls. template.users.user1.token = res.headers['x-jwt-token']; @@ -9035,23 +8998,41 @@ module.exports = function(app, template, hook) { }); }); - it('Give the user admin access to the submission', function(done) { + it('Give the user admin access to the submission', (done) => { + tempSubmission.data.writePerm = []; + tempSubmission.data.readPerm = []; tempSubmission.data.adminPerm = [template.users.user1]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.readPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = [{type: 'admin', resources: [template.users.user1._id]}]; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.user1._id] + }, + { + type: 'create', + resources: [template.users.user1._id] + }, + { + type: 'update', + resources: [template.users.user1._id] + }, + { + type: 'delete', + resources: [template.users.user1._id] + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); @@ -9064,17 +9045,17 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read a submission, with explicit resource access (admin)', function(done) { + it('A user can read a submission, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); // Store the JWT for future API calls. @@ -9084,23 +9065,23 @@ module.exports = function(app, template, hook) { }); }); - it('A user can read the index of submissions, with explicit resource access (admin)', function(done) { + it('A user can read the index of submissions, with explicit resource access (admin)', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert(response instanceof Array); + const response = res.body; + assert(Array.isArray(response)); - var found = false; - _.forEach(response, function(result) { - if (_.has(result, '_id') && (_.get(result, '_id') === tempSubmission._id)) { + let found = false; + response.forEach((result) => { + if (result._id === tempSubmission._id) { found = true; assert.deepEqual(_.omit(result, 'modified'), _.omit(tempSubmission, 'modified')); } @@ -9114,19 +9095,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can update a submission, with explicit resource access (admin)', function(done) { + it('A user can update a submission, with explicit resource access (admin)', (done) => { tempSubmission.data.value = 'bqqqweqaz'; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); @@ -9139,19 +9121,20 @@ module.exports = function(app, template, hook) { }); }); - it('A user can update a submissions owner, with explicit resource access (admin)', function(done) { + it('A user can update a submissions owner, with explicit resource access (admin)', (done) => { tempSubmission.owner = template.users.user1._id; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .send(tempSubmission) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(_.omit(response, 'modified'), _.omit(tempSubmission, 'modified')); @@ -9164,17 +9147,17 @@ module.exports = function(app, template, hook) { }); }); - it('A user can delete a submission, with explicit resource access (admin)', function(done) { + it('A user can delete a submission, with explicit resource access (admin)', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.user1.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -9185,20 +9168,20 @@ module.exports = function(app, template, hook) { }); }); - describe('Anonymous User', function() { - it('An Admin can create a submission', function(done) { + describe('Anonymous User', () => { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -9215,7 +9198,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -9224,121 +9206,126 @@ module.exports = function(app, template, hook) { }); }); - it('A anonymous user can not read a submission, without explicit resource access', function(done) { + it('A anonymous user can not read a submission, without explicit resource access', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); done(); }); }); - it('A anonymous user can not read the index of submissions, without explicit resource access', function(done) { + it('A anonymous user can not read the index of submissions, without explicit resource access', (done) => { request(app) - .get(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .get(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .expect(401) .expect('Content-Type', /text/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.text; + const response = res.text; assert.deepEqual(response, 'Unauthorized'); done(); }); }); - it('A anonymous user can not update a submission, without explicit resource access', function(done) { + it('A anonymous user can not update a submission, without explicit resource access', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .send({data: {foo: 'anything'}}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); done(); }); }); - it('A anonymous user can not update a submissions owner, with explicit resource access', function(done) { + it('A anonymous user can not update a submissions owner, with explicit resource access', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .send({owner: template.users.user2._id}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); done(); }); }); - it('A anonymous user can not update a submissions resource access, without explicit resource access', function(done) { + it('A anonymous user can not update a submissions resource access, without explicit resource access', (done) => { request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .send({access: [{type: 'admin', resources: [template.users.user2._id]}]}) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); done(); }); }); - it('A anonymous user can not delete a submission, without explicit resource access', function(done) { + it('A anonymous user can not delete a submission, without explicit resource access', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .expect(401) - .end(function(err, res) { + .expect('Content-Type', /text/) + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - assert.deepEqual(response, {}); + const response = res.text; + assert.deepEqual(response, 'Unauthorized'); done(); }); }); }); - describe('Multiple permissions', function() { - it('An Admin can create a submission', function(done) { + describe('Multiple permissions', () => { + it('An Admin can create a submission', (done) => { request(app) - .post(hook.alter('url', '/form/' + tempForm._id + '/submission', template)) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) .set('x-jwt-token', template.users.admin.token) .send(_.clone(submission)) .expect(201) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); @@ -9355,7 +9342,6 @@ module.exports = function(app, template, hook) { // Update the submission data. tempSubmission = response; - tempSubmissions.push(response); // Store the JWT for future API calls. template.users.admin.token = res.headers['x-jwt-token']; @@ -9364,23 +9350,22 @@ module.exports = function(app, template, hook) { }); }); - it('An Admin can update the submissions resource access', function(done) { + it('An Admin can update the submissions resource access', (done) => { tempSubmission.data.readPerm = [template.users.admin]; - delete tempSubmission.data.writePerm; - delete tempSubmission.data.adminPerm; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); + const response = res.body; + const expected = _.clone(tempSubmission); expected.access = [{type: 'read', resources: [template.users.admin._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); @@ -9393,25 +9378,25 @@ module.exports = function(app, template, hook) { }); }); - it('An update to the submissions resource access, will be condensed (single)', function(done) { + it('An update to the submissions resource access, will be condensed (single)', (done) => { tempSubmission.data.readPerm = [template.users.admin, template.users.admin2]; tempSubmission.data.writePerm = [null]; tempSubmission.data.adminPerm = [null]; - var condensed = {access: [{type: 'read', resources: [template.users.admin._id, template.users.admin2._id]}]}; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = condensed.access; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [{type: 'read', resources: [template.users.admin._id, template.users.admin2._id]}]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -9423,38 +9408,51 @@ module.exports = function(app, template, hook) { }); }); - it('An update to the submissions resource access, will be condensed (multi)', function(done) { + it('An update to the submissions resource access, will be condensed (multi)', (done) => { tempSubmission.data.readPerm = [ template.users.admin, - template.users.admin2 + template.users.admin2, ]; tempSubmission.data.writePerm = [ template.users.admin, - template.users.admin2 + template.users.admin2, ]; tempSubmission.data.adminPerm = [ template.users.admin, - template.users.admin2 + template.users.admin2, ]; - var condensed = {access: [ - {type: 'read', resources: [template.users.admin._id, template.users.admin2._id]}, - {type: 'write', resources: [template.users.admin._id, template.users.admin2._id]}, - {type: 'admin', resources: [template.users.admin._id, template.users.admin2._id]} - ]}; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = condensed.access; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = [ + { + type: 'read', + resources: [template.users.admin._id, template.users.admin2._id], + }, + { + type: 'create', + resources: [template.users.admin._id, template.users.admin2._id], + }, + { + type: 'update', + resources: [template.users.admin._id, template.users.admin2._id], + }, + { + type: 'delete', + resources: [template.users.admin._id, template.users.admin2._id], + }, + ]; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -9467,31 +9465,31 @@ module.exports = function(app, template, hook) { }); // FA-892 - it('An update to resource access, with null access, will not be saved (single)', function(done) { + it('An update to resource access, with null access, will not be saved (single)', (done) => { tempSubmission.data.readPerm = [ - null + null, ]; tempSubmission.data.writePerm = [ - null + null, ]; tempSubmission.data.adminPerm = [ - null + null, ]; - var filtered = {access: []}; + request(app) - .put(hook.alter('url', '/form/' + tempForm._id + '/submission/' + tempSubmission._id, template)) + .put(hook.alter('url', `/form/${tempForm._id}/submission/${tempSubmission._id}`, template)) .set('x-jwt-token', template.users.admin.token) .send(tempSubmission) .expect(200) .expect('Content-Type', /json/) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; - var expected = _.clone(tempSubmission); - expected.access = filtered.access; + const response = res.body; + const expected = _.clone(tempSubmission); + expected.access = []; assert.deepEqual(_.omit(response, 'modified'), _.omit(expected, 'modified')); tempSubmission = response; @@ -9504,18 +9502,18 @@ module.exports = function(app, template, hook) { }); }); - describe('Form Normalization', function() { + describe('Form Normalization', () => { it('Delete the manager resource', (done) => { request(app) - .delete(hook.alter('url', '/form/' + managerResource._id, template)) + .delete(hook.alter('url', `/form/${managerResource._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -9524,17 +9522,18 @@ module.exports = function(app, template, hook) { done(); }); }); + it('Delete the manager register form', (done) => { request(app) - .delete(hook.alter('url', '/form/' + managerRegister._id, template)) + .delete(hook.alter('url', `/form/${managerRegister._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -9543,17 +9542,18 @@ module.exports = function(app, template, hook) { done(); }); }); + it('Delete the manager role', (done) => { request(app) - .delete(hook.alter('url', '/role/' + managerRole._id, template)) + .delete(hook.alter('url', `/role/${managerRole._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -9562,17 +9562,209 @@ module.exports = function(app, template, hook) { done(); }); }); - it('Delete the form', function(done) { + + it('Delete the form', (done) => { request(app) - .delete(hook.alter('url', '/form/' + tempForm._id, template)) + .delete(hook.alter('url', `/form/${tempForm._id}`, template)) .set('x-jwt-token', template.users.admin.token) .expect(200) - .end(function(err, res) { + .end((err, res) => { if (err) { return done(err); } - var response = res.body; + const response = res.body; + assert.deepEqual(response, {}); + + // Store the JWT for future API calls. + template.users.admin.token = res.headers['x-jwt-token']; + + done(); + }); + }); + }); + }); + + describe('Submission Resource Access', () => { + // Create the temp form for testing. + let tempForm = { + title: 'Submission resource access check', + name: 'resourceaccess', + path: 'resourceaccess', + type: 'form', + access: [], + submissionAccess: [], + components: [ + { + submissionAccess: [ + { + type: 'read', + roles: [], + }, + { + type: 'create', + roles: ['role1', 'role2'], + }, + { + type: 'update', + roles: ['role1', 'role2'], + }, + { + type: 'delete', + roles: [], + }, + ], + type: 'select', + multiple: true, + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', + }, + key: 'perm1', + label: 'Perm Field 1', + input: true, + }, + { + submissionAccess: [ + { + type: 'read', + roles: ['role'], + }, + ], + type: 'select', + multiple: true, + dataSrc: 'url', + data: { + url: 'http://myfake.com/nothing', + }, + key: 'perm2', + label: 'Perm Field 2', + input: true, + }, + { + type: 'textfield', + key: 'value', + label: 'value', + input: true, + }, + ], + }; + + describe('Bootstrap', () => { + it('Create the form', (done) => { + request(app) + .post(hook.alter('url', '/form', template)) + .set('x-jwt-token', template.users.admin.token) + .send(tempForm) + .expect('Content-Type', /json/) + .expect(201) + .end((err, res) => { + if (err) { + return done(err); + } + + const response = res.body; + assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); + assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); + assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); + assert(response.hasOwnProperty('access'), 'The response should contain an the `access`.'); + assert.equal(response.title, tempForm.title); + assert.equal(response.name, tempForm.name); + assert.equal(response.path, tempForm.path); + assert.equal(response.type, 'form'); + assert.equal(response.access.length, 1); + assert.equal(response.access[0].type, 'read_all'); + assert.equal(response.access[0].roles.length, 3); + assert(response.access[0].roles.includes(template.roles.anonymous._id.toString())); + assert(response.access[0].roles.includes(template.roles.authenticated._id.toString())); + assert(response.access[0].roles.includes(template.roles.administrator._id.toString())); + assert.deepEqual(response.submissionAccess, tempForm.submissionAccess); + assert.deepEqual(response.components, tempForm.components); + tempForm = response; + + // Store the JWT for future API calls. + template.users.admin.token = res.headers['x-jwt-token']; + + done(); + }); + }); + }); + + describe('Access Setup', () => { + it('Submission should have appropriate access', (done) => { + const submission = { + data: { + perm1: [template.users.admin], + perm2: [template.users.admin2], + value: 'test', + }, + }; + + request(app) + .post(hook.alter('url', `/form/${tempForm._id}/submission`, template)) + .set('x-jwt-token', template.users.admin.token) + .send(_.clone(submission)) + .expect(201) + .expect('Content-Type', /json/) + .end((err, res) => { + if (err) { + return done(err); + } + + const response = res.body; + assert(response.hasOwnProperty('_id'), 'The response should contain an `_id`.'); + assert(response.hasOwnProperty('modified'), 'The response should contain a `modified` timestamp.'); + assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); + assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); + assert(response.data.hasOwnProperty('value'), 'The submission `data` should contain the `value`.'); + assert.equal(response.data.value, submission.data.value); + assert(response.hasOwnProperty('form'), 'The response should contain the `form` id.'); + assert.equal(response.form, tempForm._id); + assert(response.hasOwnProperty('roles'), 'The response should contain the resource `roles`.'); + assert.deepEqual(response.roles, []); + assert(response.hasOwnProperty('owner'), 'The response should contain the resource `owner`.'); + assert.deepEqual(response.owner, template.users.admin._id); + assert(res.headers.hasOwnProperty('x-jwt-token'), 'The response should contain a `x-jwt-token` header.'); + assert(response.hasOwnProperty('access'), 'The response should contain the resource `access`.'); + assert.deepEqual(response.access, [ + { + type: 'read', + resources: [template.users.admin._id, `${template.users.admin2._id}:role`], + }, + { + type: 'create', + resources: [`${template.users.admin._id}:role1`, `${template.users.admin._id}:role2`], + }, + { + type: 'update', + resources: [`${template.users.admin._id}:role1`, `${template.users.admin._id}:role2`], + }, + { + type: 'delete', + resources: [template.users.admin._id], + }, + ]); + + // Store the JWT for future API calls. + template.users.admin.token = res.headers['x-jwt-token']; + + done(); + }); + }); + }); + + describe('Form Normalization', () => { + it('Delete the form', (done) => { + request(app) + .delete(hook.alter('url', `/form/${tempForm._id}`, template)) + .set('x-jwt-token', template.users.admin.token) + .expect(200) + .end((err, res) => { + if (err) { + return done(err); + } + + const response = res.body; assert.deepEqual(response, {}); // Store the JWT for future API calls. @@ -9707,7 +9899,24 @@ module.exports = function(app, template, hook) { key: 'manager', resource: 'manager', template: '{{ item.data.email }}', - defaultPermission: 'admin' + submissionAccess: [ + { + type: 'read', + roles: [] + }, + { + type: 'create', + roles: [] + }, + { + type: 'update', + roles: [] + }, + { + type: 'delete', + roles: [] + } + ] } ]) .user('user', 'clientuser') diff --git a/forms-flow-forms/test/submission.js b/forms-flow-forms/test/submission.js index 2f0b537bfb..38ef4ae5c0 100644 --- a/forms-flow-forms/test/submission.js +++ b/forms-flow-forms/test/submission.js @@ -1,5 +1,7 @@ +'use strict'; + /* eslint-env mocha */ -var request = require('supertest'); +const request = require('./formio-supertest'); var assert = require('assert'); var Chance = require('chance'); var chance = new Chance(); @@ -137,9 +139,9 @@ module.exports = function(app, template, hook) { // It should fail validation. assert.equal(updated.name, 'ValidationError'); assert.equal(updated.details.length, 1); - assert.equal(updated.details[0].message, '"signature2" is not allowed to be empty'); + assert.equal(updated.details[0].message, 'Signature is required'); assert.equal(updated.details[0].path, 'signature2'); - assert.equal(updated.details[0].type, 'any.empty'); + assert.equal(updated.details[0].context.validator, 'required'); done(); }); }); @@ -159,9 +161,9 @@ module.exports = function(app, template, hook) { var submission = helper.getLastSubmission(); assert.equal(submission.name, 'ValidationError'); assert.equal(submission.details.length, 1); - assert.equal(submission.details[0].message, '"signature2" is not allowed to be empty'); + assert.equal(submission.details[0].message, 'Signature is required'); assert.equal(submission.details[0].path, 'signature2'); - assert.equal(submission.details[0].type, 'any.empty'); + assert.equal(submission.details[0].context.validator, 'required'); done(); }); }); @@ -181,9 +183,9 @@ module.exports = function(app, template, hook) { var submission = helper.getLastSubmission(); assert.equal(submission.name, 'ValidationError'); assert.equal(submission.details.length, 1); - assert.equal(submission.details[0].message, '"signature2" is not allowed to be empty'); + assert.equal(submission.details[0].message, 'Signature is required'); assert.equal(submission.details[0].path, 'signature2'); - assert.equal(submission.details[0].type, 'any.empty'); + assert.equal(submission.details[0].context.validator, 'required'); done(); }); }); @@ -1554,17 +1556,19 @@ module.exports = function(app, template, hook) { var result = {textField: 'My Value'}; var submission = helper.getLastSubmission(); - assert(submission.isJoi); assert.equal(submission.name, 'ValidationError'); assert.deepEqual(submission.details, [ { context: { key: 'requiredField', - label: 'requiredField' + setting: true, + validator: 'required', + value: '', + label: 'Required Field' }, - message: '"requiredField" is required', - path: ['requiredField'], - type: 'any.required' + message: 'Required Field is required', + level: 'error', + path: ['requiredField'] } ]); done(); @@ -1920,17 +1924,19 @@ module.exports = function(app, template, hook) { var result = {textField: 'My Value'}; var submission = helper.getLastSubmission(); - assert(submission.isJoi); assert.equal(submission.name, 'ValidationError'); assert.deepEqual(submission.details, [ { context: { key: 'requiredField', - label: 'requiredField' + label: 'Required Field', + setting: true, + validator: 'required', + value: '' }, - message: '"requiredField" is required', - path: ['requiredField'], - type: 'any.required' + message: 'Required Field is required', + level: 'error', + path: ['requiredField'] } ]); done(); @@ -2129,7 +2135,6 @@ module.exports = function(app, template, hook) { return done(err); } - var result = {textField: 'My Value'}; var submission = helper.getLastSubmission(); assert.deepEqual(values, submission.data); done(); @@ -2475,6 +2480,60 @@ module.exports = function(app, template, hook) { describe('Verify multiple values are multiple', function() { it('Forces multi value fields to be an array', function(done) { + var components = [ + { + "input": true, + "tableView": true, + "inputType": "text", + "inputMask": "", + "label": "Text Field", + "key": "textField", + "placeholder": "", + "prefix": "", + "suffix": "", + "multiple": true, + "defaultValue": "", + "protected": false, + "unique": false, + "persistent": true, + "validate": { + "required": false, + "minLength": "", + "maxLength": "", + "pattern": "", + "custom": "", + "customPrivate": false + }, + "conditional": { + "show": null, + "when": null, + "eq": "" + }, + "type": "textfield" + } + ]; + var values = { + textField: 'My Value' + }; + + helper + .form('test', components) + .submission(values) + .expect(201) + .execute(function(err) { + if (err) { + return done(err); + } + + var submission = helper.getLastSubmission(); + assert.deepEqual(submission.data, { + textField: ['My Value'] + }); + done(); + }); + }); + + it('Should remove protected fields from the response.', function(done) { var components = [ { "input": true, @@ -2514,26 +2573,14 @@ module.exports = function(app, template, hook) { helper .form('test', components) .submission(values) - .expect(400) + .expect(201) .execute(function(err) { if (err) { return done(err); } var submission = helper.getLastSubmission(); - assert(submission.isJoi); - assert.equal(submission.name, 'ValidationError'); - assert.deepEqual(submission.details, [ - { - context: { - key: 'textField', - label: 'textField' - }, - message: '"textField" must be an array', - path: ['textField'], - type: 'array.base' - } - ]); + assert.deepEqual(submission.data, {}); done(); }); }); @@ -2585,18 +2632,19 @@ module.exports = function(app, template, hook) { } var submission = helper.getLastSubmission(); - assert(submission.isJoi); assert.equal(submission.name, 'ValidationError'); assert.deepEqual(submission.details, [ { context: { key: 'textField', - label: 'textField', + label: 'Text Field', + setting: false, + validator: 'multiple', value: ['Never', 'gonna', 'give', 'you', 'up'] }, - message: '"textField" must be a string', + message: 'Text Field must not be an array', path: ['textField'], - type: 'string.base' + level: 'error' } ]); done(); @@ -2655,7 +2703,7 @@ module.exports = function(app, template, hook) { assert.equal(helper.lastResponse.statusCode, 400); assert.equal(helper.lastResponse.body.name, 'ValidationError'); assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"Text Field" must be unique.'); + assert.equal(helper.lastResponse.body.details[0].message, 'Text Field must be unique'); assert.deepEqual(helper.lastResponse.body.details[0].path, ['textField']); done(); }); @@ -2711,7 +2759,7 @@ module.exports = function(app, template, hook) { assert.equal(helper.lastResponse.statusCode, 400); assert.equal(helper.lastResponse.body.name, 'ValidationError'); assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"textField" is required'); + assert.equal(helper.lastResponse.body.details[0].message, 'Text Field is required'); assert.deepEqual(helper.lastResponse.body.details[0].path, ['textField']); done(); }); @@ -2782,8 +2830,8 @@ module.exports = function(app, template, hook) { assert.equal(helper.lastResponse.statusCode, 400); assert.equal(helper.lastResponse.body.name, 'ValidationError'); assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"Text Field" must be unique.'); - assert.deepEqual(helper.lastResponse.body.details[0].path, ['textField', 0]); + assert.equal(helper.lastResponse.body.details[0].message, 'Text Field must be unique'); + assert.deepEqual(helper.lastResponse.body.details[0].path, ['textField']); done(); }); }); @@ -2842,7 +2890,7 @@ module.exports = function(app, template, hook) { var submission = helper.getLastSubmission(); assert.deepEqual(submission.name, 'ValidationError'); var error = submission.details.pop(); - assert.equal(error.message, '"req" is required'); + assert.equal(error.message, 'req is required'); done(); }); }); @@ -2909,7 +2957,7 @@ module.exports = function(app, template, hook) { assert.equal(helper.lastResponse.statusCode, 400); assert.equal(helper.lastResponse.body.name, 'ValidationError'); assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"address" must be unique.'); + assert.equal(helper.lastResponse.body.details[0].message, 'address must be unique'); assert.deepEqual(helper.lastResponse.body.details[0].path, ['for213']); done(); }); @@ -2980,8 +3028,8 @@ module.exports = function(app, template, hook) { var submission = helper.getLastSubmission(); assert.equal(helper.lastResponse.status, 400); assert.equal(submission.name, 'ValidationError'); - assert.equal(submission.details[0].type, 'string.maxWords'); - assert.equal(submission.details[0].message, '"test" exceeded maximum words.'); + assert.equal(submission.details[0].context.validator, 'maxWords'); + assert.equal(submission.details[0].message, 'test must have no more than 30 words.'); done(); }); }); @@ -3021,8 +3069,8 @@ module.exports = function(app, template, hook) { var submission = helper.getLastSubmission(); assert.equal(helper.lastResponse.status, 400); assert.equal(submission.name, 'ValidationError'); - assert.equal(submission.details[0].type, 'string.minWords'); - assert.equal(submission.details[0].message, '"test" does not have enough words.'); + assert.equal(submission.details[0].context.validator, 'minWords'); + assert.equal(submission.details[0].message, 'test must have at least 5 words.'); done(); }); }); @@ -3211,7 +3259,7 @@ module.exports = function(app, template, hook) { assert.equal(helper.lastResponse.statusCode, 400); assert.equal(helper.lastResponse.body.name, 'ValidationError'); assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"Foo" for "Select a fruit" is not a valid selection.'); + assert.equal(helper.lastResponse.body.details[0].message, 'Select a fruit contains an invalid selection'); assert.deepEqual(helper.lastResponse.body.details[0].path, ['fruit']); done(); }); @@ -3418,17 +3466,19 @@ module.exports = function(app, template, hook) { } var submission = helper.getLastSubmission(); - assert(submission.isJoi); assert.equal(submission.name, 'ValidationError'); assert.deepEqual(submission.details, [ { context: { key: 'changeme', - label: 'changeme' + label: 'Two', + setting: true, + validator: 'required', + value: '' }, - message: '"changeme" is required', - path: ['changeme'], - type: 'any.required' + level: 'error', + message: 'Two is required', + path: ['changeme'] } ]); done(); @@ -3627,17 +3677,19 @@ module.exports = function(app, template, hook) { if (err) { return done(err); } - assert(res.body.isJoi); assert.equal(res.body.name, 'ValidationError'); assert.deepEqual(res.body.details, [ { context: { key: 'test', - label: 'test' + label: 'Test', + setting: true, + validator: 'required', + value: '' }, - message: '"test" is required', - path: ['test'], - type: 'any.required' + level: 'error', + message: 'Test is required', + path: ['test'] } ]); done(); @@ -3889,7 +3941,7 @@ module.exports = function(app, template, hook) { } assert.equal(helper.lastResponse.body.details.length, 1); - assert.equal(helper.lastResponse.body.details[0].message, '"a" is required'); + assert.equal(helper.lastResponse.body.details[0].message, 'A is required'); assert.deepEqual(helper.lastResponse.body.details[0].path, [ 'childA', 'data', @@ -4000,40 +4052,40 @@ module.exports = function(app, template, hook) { }); }); - if (app.hasProjects || docker) - it('Should allow a an update to the submission where all sub-submissions are also updated.', (done) => { - const existing = _.cloneDeep(helper.lastSubmission); - existing.state = 'submitted'; - existing.data.childA.data.a = 'Seven'; - existing.data.childB.data.c = 'Eight'; - existing.data.childC.data.e = 'Nine'; - helper.updateSubmission(existing, (err) => { - if (err) { - return done(err); - } - - const submission = helper.lastSubmission; - assert.equal(submission.state, 'submitted'); - assert(submission.data.childA.hasOwnProperty('_id'), 'The childA form was not submitted'); - assert(submission.data.childB.hasOwnProperty('_id'), 'The childB form was not submitted'); - assert(submission.data.childC.hasOwnProperty('_id'), 'The childC form was not submitted'); - assert.equal(submission.data.childA.state, 'submitted'); - assert.equal(submission.data.childB.state, 'submitted'); - assert.equal(submission.data.childC.state, 'submitted'); - assert.deepEqual(submission.data.childA.data, { - a: 'Seven', - b: 'Two' - }); - assert.deepEqual(submission.data.childB.data, { - c: 'Eight', - d: 'Four' - }); - assert.deepEqual(submission.data.childC.data, { - e: 'Nine', - f: 'Six' - }); - done(); - }); - }); + // if (app.hasProjects || docker) + // it('Should allow an update to the submission where all sub-submissions are also updated.', (done) => { + // const existing = _.cloneDeep(helper.lastSubmission); + // existing.state = 'submitted'; + // existing.data.childA.data.a = 'Seven'; + // existing.data.childB.data.c = 'Eight'; + // existing.data.childC.data.e = 'Nine'; + // helper.updateSubmission(existing, (err) => { + // if (err) { + // return done(err); + // } + // + // const submission = helper.lastSubmission; + // assert.equal(submission.state, 'submitted'); + // assert(submission.data.childA.hasOwnProperty('_id'), 'The childA form was not submitted'); + // assert(submission.data.childB.hasOwnProperty('_id'), 'The childB form was not submitted'); + // assert(submission.data.childC.hasOwnProperty('_id'), 'The childC form was not submitted'); + // assert.equal(submission.data.childA.state, 'submitted'); + // assert.equal(submission.data.childB.state, 'submitted'); + // assert.equal(submission.data.childC.state, 'submitted'); + // assert.deepEqual(submission.data.childA.data, { + // a: 'Seven', + // b: 'Two' + // }); + // assert.deepEqual(submission.data.childB.data, { + // c: 'Eight', + // d: 'Four' + // }); + // assert.deepEqual(submission.data.childC.data, { + // e: 'Nine', + // f: 'Six' + // }); + // done(); + // }); + // }); }); }; diff --git a/forms-flow-forms/test/templates.js b/forms-flow-forms/test/templates.js index d079247a47..a1e32af40c 100644 --- a/forms-flow-forms/test/templates.js +++ b/forms-flow-forms/test/templates.js @@ -128,7 +128,7 @@ module.exports = (app, template, hook) => { assert.equal(form.hasOwnProperty('machineName'), true); let machineName = form.machineName; - let tempForm = _.omit(form, ['_id', '__v', 'created', 'deleted', 'modified', 'machineName', 'owner', '_vid', 'revisions', 'controller']); + let tempForm = _.omit(form, ['_id', '__v', 'created', 'deleted', 'modified', 'machineName', 'owner', '_vid', 'revisions']); tempForm.access = tempForm.access.map(access => { access.roles = access.roles.map(role => { @@ -1813,6 +1813,124 @@ module.exports = (app, template, hook) => { }); }); + describe('missingResourceAction', function() { + let testTemplate = require('./fixtures/templates/missingResourceAction.json'); + let _template = _.cloneDeep(testTemplate); + + const checkMissingResourceActions = (actionsObject) => { + Object.values(actionsObject).forEach((action) => { + assert(action.settings && !action.settings.resource, 'Save to resource should be clear'); + }); + }; + + describe('Import', function() { + let project = {title: 'Export', name: 'export'}; + + it('Should be able to bootstrap the template', function(done) { + importer.import.template(_template, alters, (err) => { + if (err) { + return done(err); + } + + done(); + }); + }); + + it('All the roles should be imported', function(done) { + checkTemplateRoles(project, testTemplate.roles, done); + }); + + it('All the forms should be imported', function(done) { + hook.alter('templateImportComponent', testTemplate.forms); + checkTemplateFormsAndResources(project, 'form', testTemplate.forms, done); + }); + + it('All the resources should be imported', function(done) { + hook.alter('templateImportComponent', testTemplate.resources); + checkTemplateFormsAndResources(project, 'resource', testTemplate.resources, done); + }); + + it('Save submission to resource should be empty', function(done) { + formio.actions.model.find({deleted: {$eq: null}}).then((actions) => { + checkMissingResourceActions(actions); + done(); + }); + }); + }); + + describe('Export', function() { + let project = {}; + let exportData = {}; + + it('Should be able to export project data', function(done) { + importer.export(_template, (err, data) => { + if (err) { + return done(err); + } + + exportData = data; + return done(); + }); + }); + + it('An export should contain the export title', function() { + assert.equal( + hook.alter('exportTitle', 'Export', exportData), + 'Export' + ); + }); + + it('An export should contain the current export version', function() { + assert.equal( + exportData.version, + '2.0.0' + ); + }); + + it('An export should contain the description', function() { + assert.equal( + hook.alter('exportDescription', '', exportData), + '' + ); + }); + + it('An export should contain the export name', function() { + assert.equal( + hook.alter('exportName', 'export', exportData), + 'export' + ); + }); + + it('An export should contain the export plan', function() { + assert.equal( + hook.alter('exportPlan', 'community', exportData), + 'community' + ); + }); + + it('The template should export all forms', function(done) { + checkTemplateFormsAndResources(project, 'form', exportData.forms, done); + }); + + it('The template should export all resources', function(done) { + checkTemplateFormsAndResources(project, 'resource', exportData.resources, done); + }); + + it('The template should export all actions without mapped resource', () => { + console.log(exportData.actions); + checkMissingResourceActions(exportData.actions); + }); + }); + + before(function(done) { + template.clearData(done); + }); + + after(function(done) { + template.clearData(done); + }); + }); + describe('unknownRoleResources Template', function() { let testTemplate = require('./fixtures/templates/unknownRoleResources.json'); let _template = _.cloneDeep(testTemplate); diff --git a/forms-flow-forms/test/test.js b/forms-flow-forms/test/test.js index 7aa1c123d3..8f12766741 100644 --- a/forms-flow-forms/test/test.js +++ b/forms-flow-forms/test/test.js @@ -60,7 +60,6 @@ describe('Initialization', function() { after(function() { require('./templates')(app, template, hook); require('./bootstrap')(app, template, hook); - require('./unit')(app, template, hook); require('./auth')(app, template, hook); require('./roles')(app, template, hook); require('./form')(app, template, hook); @@ -69,5 +68,7 @@ describe('Initialization', function() { require('./actions')(app, template, hook); require('./submission-access')(app, template, hook); require('./submission')(app, template, hook); + require('./export/CSVExporter/CSVExporter')(app, template, hook); + require('./unit')(app, template, hook); }); }); diff --git a/forms-flow-forms/test/unit.js b/forms-flow-forms/test/unit.js index c82793a395..ca85f9133f 100644 --- a/forms-flow-forms/test/unit.js +++ b/forms-flow-forms/test/unit.js @@ -2,9 +2,10 @@ 'use strict'; let assert = require('assert'); +var chance = new (require('chance'))(); let fs = require('fs'); let docker = process.env.DOCKER; -var request = require('supertest'); +const request = require('./formio-supertest'); module.exports = function(app, template, hook) { let Thread = require('formio-workers/Thread'); @@ -126,12 +127,14 @@ module.exports = function(app, template, hook) { let request2 = new Promise((resolve, reject) => { setTimeout(() => { started.push('request2'); + const email = chance.email(); + const password = chance.word({length: 10}); request(app) .post(hook.alter('url', '/form/' + template.forms.adminRegister._id + '/submission', template)) .send({ data: { - 'email': template.users.admin.data.email, - 'password': template.users.admin.data.password + 'email': email, + 'password': password, } }) .expect(200) @@ -148,7 +151,7 @@ module.exports = function(app, template, hook) { assert(response.hasOwnProperty('created'), 'The response should contain a `created` timestamp.'); assert(response.hasOwnProperty('data'), 'The response should contain a submission `data` object.'); assert(response.data.hasOwnProperty('email'), 'The submission `data` should contain the `email`.'); - assert.equal(response.data.email, template.users.admin.data.email); + assert.equal(response.data.email, email); assert(!response.data.hasOwnProperty('password'), 'The submission `data` should not contain the `password`.'); assert(response.hasOwnProperty('form'), 'The response should contain the resource `form`.'); assert.equal(response.form, template.resources.admin._id); @@ -159,14 +162,6 @@ module.exports = function(app, template, hook) { assert.equal(response.roles.length, 1); assert.equal(response.roles[0].toString(), template.roles.administrator._id.toString()); - // Update our testProject.owners data. - let tempPassword = template.users.admin.data.password; - template.users.admin = response; - template.users.admin.data.password = tempPassword; - - // Store the JWT for future API calls. - template.users.admin.token = res.headers['x-jwt-token']; - resolve(); }); }, 5000); diff --git a/forms-flow-web/Dockerfile b/forms-flow-web/Dockerfile index ed399d11e2..bd86313458 100644 --- a/forms-flow-web/Dockerfile +++ b/forms-flow-web/Dockerfile @@ -11,6 +11,7 @@ RUN apk update && apk upgrade && \ apk add --no-cache bash git openssh # install and cache app dependencies +COPY package-lock.json /forms-flow-web/app/package-lock.json COPY package.json /forms-flow-web/app/package.json COPY forms-flow-util/package.json /forms-flow-web/app/forms-flow-util/package.json RUN npm install --silent @@ -22,4 +23,4 @@ COPY . /forms-flow-web/app/ # && chmod -R 777 /forms-flow-web/app/ # start app -CMD ["npm", "start", "--prefix", "/forms-flow-web/app"] \ No newline at end of file +CMD ["npm", "start", "--prefix", "/forms-flow-web/app"] diff --git a/forms-flow-web/README.md b/forms-flow-web/README.md index 888fad32f5..c364c4cc55 100644 --- a/forms-flow-web/README.md +++ b/forms-flow-web/README.md @@ -1,7 +1,8 @@ # formsflow.ai Web Application + ![React](https://img.shields.io/badge/React-16.12.0-blue) -**formsflow.ai** delivers progressive web application with React version `16.12` and `create-react-app`. Also currently uses [form.io](https://github.com/formio/formio) version `1.70.0`. +**formsflow.ai** delivers progressive web application with React version `16.12` and `create-react-app`. Also currently uses [form.io](https://github.com/formio/formio) version `2.0.0--rc.34`. A React library for rendering out forms based on the form.io platform. diff --git a/forms-flow-web/package-lock.json b/forms-flow-web/package-lock.json index dcb2e1933c..e84c63d4b4 100644 --- a/forms-flow-web/package-lock.json +++ b/forms-flow-web/package-lock.json @@ -1426,9 +1426,9 @@ "integrity": "sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==" }, "@formio/bootstrap3": { - "version": "2.6.6", - "resolved": "https://registry.npmjs.org/@formio/bootstrap3/-/bootstrap3-2.6.6.tgz", - "integrity": "sha512-4ltAz1qt9EKjLm4tvpoMHc/0bi/fb8BBxtguTQtFNy95SOriitORzS7C23NUklvQdFjMHWEAOZsXNRBmF5GSbQ==", + "version": "2.6.8", + "resolved": "https://registry.npmjs.org/@formio/bootstrap3/-/bootstrap3-2.6.8.tgz", + "integrity": "sha512-gkDXJS5bvl4nn9tepFUctXedWd27JOfRID6zaEKVXiiAGBxcvvdcF2bHxslFFJbZTxHAHV2QgB2249R5R2UIyg==", "requires": { "resize-observer-polyfill": "^1.5.1" } @@ -2027,6 +2027,18 @@ "react-transition-group": "^4.4.0" } }, + "@material-ui/lab": { + "version": "4.0.0-alpha.56", + "resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.56.tgz", + "integrity": "sha512-xPlkK+z/6y/24ka4gVJgwPfoCF4RCh8dXb1BNE7MtF9bXEBLN/lBxNTK8VAa0qm3V2oinA6xtUIdcRh0aeRtVw==", + "requires": { + "@babel/runtime": "^7.4.4", + "@material-ui/utils": "^4.10.2", + "clsx": "^1.0.4", + "prop-types": "^15.7.2", + "react-is": "^16.8.0" + } + }, "@material-ui/styles": { "version": "4.10.0", "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", @@ -3246,7 +3258,8 @@ "asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=", + "dev": true }, "asn1": { "version": "0.2.4", @@ -3296,6 +3309,58 @@ "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", "dev": true }, + "ast-transform": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/ast-transform/-/ast-transform-0.0.0.tgz", + "integrity": "sha1-dJRAWIh9goPhidlUYAlHvJj+AGI=", + "requires": { + "escodegen": "~1.2.0", + "esprima": "~1.0.4", + "through": "~2.3.4" + }, + "dependencies": { + "escodegen": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.2.0.tgz", + "integrity": "sha1-Cd55Z3kcyVi3+Jot220jRRrzJ+E=", + "requires": { + "esprima": "~1.0.4", + "estraverse": "~1.5.0", + "esutils": "~1.0.0", + "source-map": "~0.1.30" + } + }, + "esprima": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", + "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" + }, + "estraverse": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.5.1.tgz", + "integrity": "sha1-hno+jlip+EYYr7bC3bzZFrfLr3E=" + }, + "esutils": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.0.0.tgz", + "integrity": "sha1-gVHTWOIMisx/t0XnRywAJf5JZXA=" + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "optional": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, + "ast-types": { + "version": "0.7.8", + "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.7.8.tgz", + "integrity": "sha1-kC0uDWDQcb3NRtwRXhgJ7RHBOKk=" + }, "ast-types-flow": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", @@ -3351,9 +3416,9 @@ "dev": true }, "autocompleter": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-6.0.3.tgz", - "integrity": "sha512-CACd1r+AzgXOrppaYuOUWtJrM84EdUNSwRVXM0cuP6UQ09kLv7QumV7PD25kxRuZ7X4kVeTX9gkloH4NkNglvg==" + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/autocompleter/-/autocompleter-6.0.5.tgz", + "integrity": "sha512-3SMOcS+2VT/RYjS1Fd7tO0IK7PCGKu/Z5QGMhNuEeXdgpT0FmyQyQj+r3fEF/w1qUNejVclbnBF6+jWExVjb1w==" }, "autoprefixer": { "version": "9.8.6", @@ -4112,7 +4177,6 @@ "version": "1.11.3", "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, "requires": { "resolve": "1.1.7" }, @@ -4120,8 +4184,7 @@ "resolve": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=" } } }, @@ -4162,6 +4225,16 @@ "safe-buffer": "^5.1.2" } }, + "browserify-optional": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-optional/-/browserify-optional-1.0.1.tgz", + "integrity": "sha1-HhNyLP3g2F8SFnbCpyztUzoBiGk=", + "requires": { + "ast-transform": "0.0.0", + "ast-types": "^0.7.0", + "browser-resolve": "^1.8.1" + } + }, "browserify-rsa": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", @@ -4354,6 +4427,15 @@ "unset-value": "^1.0.0" } }, + "call-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", + "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.0" + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -5019,9 +5101,9 @@ "dev": true }, "core-js": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", - "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz", + "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw==" }, "core-js-compat": { "version": "3.6.5", @@ -5139,15 +5221,23 @@ } }, "create-react-class": { - "version": "15.6.3", - "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.3.tgz", - "integrity": "sha512-M+/3Q6E6DLO6Yx3OwrWjwHBnvfXXYA7W+dFjt/ZDBemHO1DDZhsalX/NUtnTYclN6GfnBDRh4qRHjcDHmlJBJg==", + "version": "15.7.0", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz", + "integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==", "requires": { - "fbjs": "^0.8.9", "loose-envify": "^1.3.1", "object-assign": "^4.1.1" } }, + "create-react-context": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/create-react-context/-/create-react-context-0.3.0.tgz", + "integrity": "sha512-dNldIoSuNSvlTJ7slIKC/ZFGKexBMBrrcc+TTe1NdmROnaASuLPvqpwj9v4XS4uXZ8+YPu0sNmShX2rXI5LNsw==", + "requires": { + "gud": "^1.0.0", + "warning": "^4.0.3" + } + }, "cross-spawn": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz", @@ -5158,11 +5248,11 @@ } }, "crossvent": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.4.tgz", - "integrity": "sha1-2ixPj0DJR4JRe/K+7BBEFIGUq5I=", + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/crossvent/-/crossvent-1.5.5.tgz", + "integrity": "sha1-rSCHjkkh6b5z2daXb4suzQ9xoLE=", "requires": { - "custom-event": "1.0.0" + "custom-event": "^1.0.0" } }, "crypto-browserify": { @@ -5532,9 +5622,9 @@ } }, "custom-event": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.0.tgz", - "integrity": "sha1-LkYovhncSyFLXAJjDFlx6BFhgGI=" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz", + "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=" }, "custom-event-polyfill": { "version": "1.0.7", @@ -5713,24 +5803,47 @@ "integrity": "sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=" }, "deep-equal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.3.tgz", - "integrity": "sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.0.5.tgz", + "integrity": "sha512-nPiRgmbAtm1a3JsnLCf6/SLfXcjyN5v8L1TXzdCmHrXJ4hx+gW/w1YCcn7z8gJtSiDArZCgYtbao3QqLm/N1Sw==", "requires": { - "es-abstract": "^1.17.5", - "es-get-iterator": "^1.1.0", + "call-bind": "^1.0.0", + "es-get-iterator": "^1.1.1", + "get-intrinsic": "^1.0.1", "is-arguments": "^1.0.4", "is-date-object": "^1.0.2", - "is-regex": "^1.0.5", + "is-regex": "^1.1.1", "isarray": "^2.0.5", - "object-is": "^1.1.2", + "object-is": "^1.1.4", "object-keys": "^1.1.1", - "object.assign": "^4.1.0", + "object.assign": "^4.1.2", "regexp.prototype.flags": "^1.3.0", - "side-channel": "^1.0.2", + "side-channel": "^1.0.3", "which-boxed-primitive": "^1.0.1", "which-collection": "^1.0.1", "which-typed-array": "^1.1.2" + }, + "dependencies": { + "object-is": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.4.tgz", + "integrity": "sha512-1ZvAZ4wlF7IyPVOcE1Omikt7UpaFlOQq0HlSti+ZvDH3UiD2brwGMwDbyV43jao2bKJ+4+WdPJHSd7kgzKYVqg==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" + } + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "deep-is": { @@ -5954,9 +6067,9 @@ } }, "dialog-polyfill": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.3.tgz", - "integrity": "sha512-DVYazl3klQtPjs4dOzDTOt4uQ8cRDfu8EBhoY81ISmH0fbrMvYuaethcQ9UevmF9kAaLnBJ0O5eDUnZ7QalArA==" + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dialog-polyfill/-/dialog-polyfill-0.5.4.tgz", + "integrity": "sha512-LqQvIky9+qIFD7MCESOXdMZc4ObCdEbzbuvnPJVCiRXKQMzMjpQKK50UI+2sj0bJBPKn6ugPpMRpuLrgYc4RjA==" }, "didi": { "version": "4.0.0", @@ -6187,12 +6300,12 @@ "integrity": "sha1-9p+W+UDg0FU9rCkROYZaPNAQHjw=" }, "dragula": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.2.tgz", - "integrity": "sha1-SjXJ05gf+sGpScKcpyhQWOhzk84=", + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/dragula/-/dragula-3.7.3.tgz", + "integrity": "sha512-/rRg4zRhcpf81TyDhaHLtXt6sEywdfpv1cRUMeFFy7DuypH2U0WUL0GTdyAQvXegviT4PJK4KuMmOaIDpICseQ==", "requires": { "contra": "1.9.4", - "crossvent": "1.5.4" + "crossvent": "1.5.5" } }, "duplexer": { @@ -6291,24 +6404,6 @@ "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=", "dev": true }, - "encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "requires": { - "iconv-lite": "^0.6.2" - }, - "dependencies": { - "iconv-lite": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", - "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } - } - }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -6383,11 +6478,12 @@ } }, "es-get-iterator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.0.tgz", - "integrity": "sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.1.tgz", + "integrity": "sha512-qorBw8Y7B15DVLaJWy6WdEV/ZkieBcu6QCq/xzWzGOKJqgG1j754vXRfZ3NY7HSShneqU43mPB4OkQBTkvHhFw==", "requires": { - "es-abstract": "^1.17.4", + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.1", "has-symbols": "^1.0.1", "is-arguments": "^1.0.4", "is-map": "^2.0.1", @@ -7058,6 +7154,11 @@ "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=", "dev": true }, + "eve": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eve/-/eve-0.5.4.tgz", + "integrity": "sha1-Z9CAuXJSkdfjieNMJoYN2X8d66o=" + }, "eventemitter2": { "version": "6.4.3", "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.3.tgz", @@ -7606,33 +7707,12 @@ "bser": "2.1.1" } }, - "fbjs": { - "version": "0.8.17", - "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.17.tgz", - "integrity": "sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=", - "requires": { - "core-js": "^1.0.0", - "isomorphic-fetch": "^2.1.1", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^0.7.18" - } - }, "fetch-ponyfill": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-6.1.1.tgz", - "integrity": "sha512-rWLgTr5A44/XhvCQPYj0X9Tc+cjUaHofSM4lcwjc9MavD5lkjIhJ+h8JQlavPlTIgDpwhuRozaIykBvX9ItaSA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-7.0.0.tgz", + "integrity": "sha512-ynRMX7w11EcG3sJSYTT2N6EUl0QsqWbyW3Xw5+DuLeA2uo/yhssb7JqnjamGQaHBUo/AOUsmTz6zeUX3cPvZsQ==", "requires": { - "node-fetch": "~2.6.0" - }, - "dependencies": { - "node-fetch": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", - "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" - } + "node-fetch": "~2.6.1" } }, "figgy-pudding": { @@ -7760,11 +7840,6 @@ } } }, - "flatpickr": { - "version": "4.6.6", - "resolved": "https://registry.npmjs.org/flatpickr/-/flatpickr-4.6.6.tgz", - "integrity": "sha512-EZ48CJMttMg3maMhJoX+GvTuuEhX/RbA1YeuI19attP3pwBdbYy6+yqAEVm0o0hSBFYBiLbVxscLW6gJXq6H3A==" - }, "flatted": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", @@ -7992,36 +8067,35 @@ } }, "formiojs": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.11.3.tgz", - "integrity": "sha512-PByP/XtorxPEL7e4RTfTjJP/OdBDJob9TAeQ3u1s9NG3Rr7iU7njpTpJ4kOpW1OXrnFpUIufDxGokgu6IVoY3Q==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/formiojs/-/formiojs-4.12.2.tgz", + "integrity": "sha512-cvKWcExFjqo8BjIWqs3vOyMzq3cso3kxS90wl3rtjC70ALATcTYMBUoGIUkLmBbDj+Byc1++67m3gZago6sZdg==", "requires": { - "@formio/bootstrap3": "^2.6.4", - "@formio/semantic": "^2.4.4", - "autocompleter": "^6.0.3", + "@formio/bootstrap3": "^2.6.8", + "@formio/semantic": "^2.4.5", + "autocompleter": "^6.0.5", "browser-cookies": "^1.2.0", "choices.js": "^9.0.1", "compare-versions": "^3.6.0", "core-js": "3.5.0", "custom-event-polyfill": "^1.0.7", - "dialog-polyfill": "^0.5.1", + "dialog-polyfill": "^0.5.4", "dompurify": "2.0.11", "downloadjs": "^1.4.7", - "dragula": "^3.7.2", + "dragula": "^3.7.3", "eventemitter2": "^6.4.3", "fast-deep-equal": "^3.1.3", "fast-json-patch": "^2.2.1", - "fetch-ponyfill": "^6.1.0", - "flatpickr": "^4.6.6", - "i18next": "^19.6.2", - "idb": "^5.0.4", + "fetch-ponyfill": "^7.0.0", + "i18next": "^19.8.4", + "idb": "^5.0.7", "ismobilejs": "^1.1.1", - "json-logic-js": "^1.2.2", + "json-logic-js": "^2.0.0", "jstimezonedetect": "^1.0.7", - "jwt-decode": "^2.2.0", - "lodash": "^4.17.19", - "moment": "^2.27.0", - "moment-timezone": "^0.5.31", + "jwt-decode": "^3.1.2", + "lodash": "^4.17.20", + "moment": "^2.29.1", + "moment-timezone": "^0.5.32", "native-promise-only": "^0.8.1", "quill": "^2.0.0-dev.3", "resize-observer-polyfill": "^1.5.1", @@ -8029,20 +8103,20 @@ "string-hash": "^1.1.3", "text-mask-addons": "^3.8.0", "tooltip.js": "^1.3.3", - "uuid": "^8.2.0", - "vanilla-picker": "^2.10.1", + "uuid": "^8.3.1", + "vanilla-picker": "^2.11.0", "vanilla-text-mask": "^5.1.1" }, "dependencies": { - "core-js": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.5.0.tgz", - "integrity": "sha512-Ifh3kj78gzQ7NAoJXeTu+XwzDld0QRIwjBLRqAMhuLhP3d2Av5wmgE9ycfnvK6NAEjTkQ1sDPeoEZAWO3Hx1Uw==" + "moment": { + "version": "2.29.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz", + "integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==" }, "uuid": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.0.tgz", - "integrity": "sha512-fX6Z5o4m6XsXBdli9g7DtWgAx+osMsRRZFKma1mIUsLCz6vRvv+pz5VNbyu9UEDzpMWulZfvpgb/cmDXVulYFQ==" + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" } } }, @@ -8182,6 +8256,16 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" }, + "get-intrinsic": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz", + "integrity": "sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==", + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1" + } + }, "get-own-enumerable-property-symbols": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", @@ -8342,6 +8426,11 @@ "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", "dev": true }, + "gud": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gud/-/gud-1.0.0.tgz", + "integrity": "sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw==" + }, "gzip-size": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz", @@ -8769,11 +8858,21 @@ "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" }, "i18next": { - "version": "19.7.0", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.7.0.tgz", - "integrity": "sha512-sxZhj6u7HbEYOMx81oGwq5MiXISRBVg2wRY3n6YIbe+HtU8ydzlGzv6ErHdrRKYxATBFssVXYbc3lNZoyB4vfA==", + "version": "19.8.4", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-19.8.4.tgz", + "integrity": "sha512-FfVPNWv+felJObeZ6DSXZkj9QM1Ivvh7NcFCgA8XPtJWHz0iXVa9BUy+QY8EPrCLE+vWgDfV/sc96BgXVo6HAA==", "requires": { - "@babel/runtime": "^7.10.1" + "@babel/runtime": "^7.12.0" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } } }, "iconv-lite": { @@ -8794,9 +8893,9 @@ } }, "idb": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/idb/-/idb-5.0.5.tgz", - "integrity": "sha512-HbU+RcJ0tCg3QQgqmyFRy6k0ApTb2FUfrEiBg5zl5VpFg0GYG6c/0vk4GNNqgq1C88t/YA8PKCJY0wbm8mAcnA==" + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/idb/-/idb-5.0.8.tgz", + "integrity": "sha512-K9xInRkVbT3ZsYimD2KVj6B4E93IBvOjEQTryu99WuuN7G+7x3SzA79+yubbX0QRN9V64Gi+L+ulG5QYTVydOg==" }, "identity-obj-proxy": { "version": "3.0.0", @@ -9121,9 +9220,9 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" }, "is-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.0.tgz", - "integrity": "sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==" + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.1.tgz", + "integrity": "sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg==" }, "is-binary-path": { "version": "2.1.0", @@ -9135,9 +9234,12 @@ } }, "is-boolean-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.0.1.tgz", - "integrity": "sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", + "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", + "requires": { + "call-bind": "^1.0.0" + } }, "is-buffer": { "version": "1.1.6", @@ -9360,7 +9462,8 @@ "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true }, "is-string": { "version": "1.0.5", @@ -9385,14 +9488,52 @@ } }, "is-typed-array": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.3.tgz", - "integrity": "sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.4.tgz", + "integrity": "sha512-ILaRgn4zaSrVNXNGtON6iFNotXW3hAPF3+0fB1usg2jFlWqo5fEDdmJkz0zBfoi7Dgskr8Khi2xZ8cXqZEfXNA==", "requires": { - "available-typed-arrays": "^1.0.0", - "es-abstract": "^1.17.4", + "available-typed-arrays": "^1.0.2", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", "foreach": "^2.0.5", "has-symbols": "^1.0.1" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "is-typedarray": { @@ -9448,15 +9589,6 @@ "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", "dev": true }, - "isomorphic-fetch": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", - "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", - "requires": { - "node-fetch": "^1.0.1", - "whatwg-fetch": ">=0.10.0" - } - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -11154,9 +11286,9 @@ "dev": true }, "json-logic-js": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-1.2.2.tgz", - "integrity": "sha1-5cOCqm3yXfSF7erTOYaTlsUzr+g=" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-logic-js/-/json-logic-js-2.0.0.tgz", + "integrity": "sha512-cQBDOXgFtFladCg99wnQ7YfN+nv1+Sznj4K6bp3CTgDJNJKgEXJE2VCXzVBjEU2e1UagDHSek52IQk5Ha38n7Q==" }, "json-parse-better-errors": { "version": "1.0.2", @@ -11420,9 +11552,9 @@ } }, "jwt-decode": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz", - "integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk=" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", + "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, "keycloak-js": { "version": "9.0.3", @@ -12161,9 +12293,9 @@ "integrity": "sha512-al0MUK7cpIcglMv3YF13qSgdAIqxHTO7brRtaz3DlSULbqfazqkc5kEjNrLDOM7fsjshoFIihnU8snrP7zUvhQ==" }, "moment-timezone": { - "version": "0.5.31", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", - "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", + "version": "0.5.32", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.32.tgz", + "integrity": "sha512-Z8QNyuQHQAmWucp8Knmgei8YNo28aLjJq6Ma+jy1ZSpSk5nyfRT8xgUbSQvD2+2UajISfenndwvFuH3NGS+nvA==", "requires": { "moment": ">= 2.9.0" } @@ -12287,19 +12419,9 @@ } }, "node-fetch": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", - "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", - "requires": { - "encoding": "^0.1.11", - "is-stream": "^1.0.1" - } - }, - "node-forge": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz", - "integrity": "sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ==", - "dev": true + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" }, "node-gyp": { "version": "3.8.0", @@ -14332,14 +14454,6 @@ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true }, - "promise": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", - "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", - "requires": { - "asap": "~2.0.3" - } - }, "promise-inflight": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", @@ -14596,9 +14710,9 @@ } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -14704,6 +14818,18 @@ } } }, + "react-burger-menu": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-burger-menu/-/react-burger-menu-2.7.1.tgz", + "integrity": "sha512-WJiypBIsX6WNF31CabHhPqvsYygXxdWLPhK+ue84MNuWWL/x5c6M/1UfiIHpX5ZuuQ4MghzzpmYKiY6PEdsr8Q==", + "requires": { + "browserify-optional": "^1.0.0", + "classnames": "^2.2.6", + "eve": "~0.5.1", + "prop-types": "^15.7.2", + "snapsvg-cjs": "0.0.6" + } + }, "react-calendar": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-3.1.0.tgz", @@ -15023,10 +15149,19 @@ } } }, + "react-device-emulator": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/react-device-emulator/-/react-device-emulator-1.0.4.tgz", + "integrity": "sha512-KBzQTFUpH8JI5bdakXIukIFnD/3y4iqvt93gUjUiysobVOCELk9Bq66XnbJcfK8zDoRhavGD8PyNp/HnHfUQqQ==", + "requires": { + "prop-types": "^15.7.2", + "react-frame-component": "^4.1.1" + } + }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", + "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", @@ -15083,6 +15218,11 @@ } } }, + "react-frame-component": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/react-frame-component/-/react-frame-component-4.1.3.tgz", + "integrity": "sha512-4PurhctiqnmC1F5prPZ+LdsalH7pZ3SFA5xoc0HBe8mSHctdLLt4Cr2WXfXOoajHBYq/yiipp9zOgx+vy8GiEA==" + }, "react-input-autosize": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/react-input-autosize/-/react-input-autosize-2.2.2.tgz", @@ -15160,6 +15300,40 @@ "warning": "^4.0.3" } }, + "react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-nmqYTx7QVjCm3WUZLeuOomna138R1luC4EqkW3hxJUrAe+3eNz3oFCLYdnPwILfn0mX1Ew2c3wctrjlUMYYUww==", + "requires": { + "@babel/runtime": "^7.1.2", + "create-react-context": "^0.3.0", + "deep-equal": "^1.1.1", + "popper.js": "^1.14.4", + "prop-types": "^15.6.1", + "typed-styles": "^0.0.7", + "warning": "^4.0.2" + }, + "dependencies": { + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + } + }, + "popper.js": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", + "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==" + } + } + }, "react-redux": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz", @@ -15356,6 +15530,39 @@ "prop-types": "^15.6.2" } }, + "reactstrap": { + "version": "8.6.0", + "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-8.6.0.tgz", + "integrity": "sha512-03/UMbLPR6MhVStVUfCLuKh8xh4JOtNVkRxDB9/uHixN+cEQPOpSYa0K69YyK1/2YdZBs2qS6y0cQkK8NQKBHA==", + "requires": { + "@babel/runtime": "^7.2.0", + "classnames": "^2.2.3", + "prop-types": "^15.5.8", + "react-popper": "^1.3.6", + "react-transition-group": "^2.3.1" + }, + "dependencies": { + "dom-helpers": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz", + "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==", + "requires": { + "@babel/runtime": "^7.1.2" + } + }, + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, "read-pkg": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", @@ -16287,12 +16494,20 @@ "dev": true }, "selfsigned": { - "version": "1.10.7", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.7.tgz", - "integrity": "sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA==", + "version": "1.10.8", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-1.10.8.tgz", + "integrity": "sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w==", "dev": true, "requires": { - "node-forge": "0.9.0" + "node-forge": "^0.10.0" + }, + "dependencies": { + "node-forge": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.10.0.tgz", + "integrity": "sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA==", + "dev": true + } } }, "semver": { @@ -16454,7 +16669,8 @@ "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true }, "setprototypeof": { "version": "1.1.1", @@ -16747,6 +16963,22 @@ "kind-of": "^3.2.0" } }, + "snapsvg": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/snapsvg/-/snapsvg-0.5.1.tgz", + "integrity": "sha1-DK9Sx5GJopB0b8RGzF6GP2vd3+M=", + "requires": { + "eve": "~0.5.1" + } + }, + "snapsvg-cjs": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/snapsvg-cjs/-/snapsvg-cjs-0.0.6.tgz", + "integrity": "sha1-Oy9WryVz09Nkw+1b+IhXRfTS3eE=", + "requires": { + "snapsvg": "0.5.1" + } + }, "sockjs": { "version": "0.3.20", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.20.tgz", @@ -17658,8 +17890,7 @@ "through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, "through2": { "version": "2.0.5", @@ -17885,6 +18116,11 @@ "mime-types": "~2.1.24" } }, + "typed-styles": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/typed-styles/-/typed-styles-0.0.7.tgz", + "integrity": "sha512-pzP0PWoZUhsECYjABgCGQlRGL1n7tOHsgwYv3oIiEpJwGhFTuty/YNeduxQYzXXa3Ge5BdT6sHYIQYpl4uJ+5Q==" + }, "typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", @@ -17896,11 +18132,6 @@ "resolved": "https://registry.npmjs.org/typeface-nunito-sans/-/typeface-nunito-sans-0.0.72.tgz", "integrity": "sha512-b58R4movZchZJNe5pWvqMVoJzKRs7GP09iIri4WWQOs9+G+yjoTSIpOUMU2xtLmxuyXUo1F8XhnOHY06kSDhuQ==" }, - "ua-parser-js": { - "version": "0.7.21", - "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.21.tgz", - "integrity": "sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ==" - }, "uncontrollable": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.1.1.tgz", @@ -18193,9 +18424,9 @@ "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" }, "vanilla-picker": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.10.1.tgz", - "integrity": "sha512-Bo4HOKkSorcQoRB08HwDMb8X2jt3SsZw7gzFlbzXbhnaxdUVJBm3LOUudr7M1SCVwPCo8d3nq8ajiAg8lAoqPg==", + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/vanilla-picker/-/vanilla-picker-2.11.0.tgz", + "integrity": "sha512-MsTAyROQRN3yoUdToiQtfnW752wC9DpnXbOhAMGJgBi+TMDZ0IUfbmIVB5u3P1ltBDlqy8TjY59a4lOioSlgZw==", "requires": { "@sphinxxxx/color-conversion": "^2.2.2" } @@ -19145,7 +19376,8 @@ "whatwg-fetch": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.4.1.tgz", - "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==" + "integrity": "sha512-sofZVzE1wKwO+EYPbWfiwzaKovWiZXf4coEzjGP9b2GBVgQRLQUZ2QcuPpQExGDAW5GItpEm6Tl4OU5mywnAoQ==", + "dev": true }, "whatwg-mimetype": { "version": "2.3.0", @@ -19199,16 +19431,54 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, "which-typed-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.2.tgz", - "integrity": "sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.4.tgz", + "integrity": "sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA==", "requires": { "available-typed-arrays": "^1.0.2", - "es-abstract": "^1.17.5", + "call-bind": "^1.0.0", + "es-abstract": "^1.18.0-next.1", "foreach": "^2.0.5", "function-bind": "^1.1.1", "has-symbols": "^1.0.1", "is-typed-array": "^1.1.3" + }, + "dependencies": { + "es-abstract": { + "version": "1.18.0-next.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", + "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.2", + "is-negative-zero": "^2.0.0", + "is-regex": "^1.1.1", + "object-inspect": "^1.8.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.1", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "is-callable": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", + "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==" + }, + "object.assign": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", + "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", + "requires": { + "call-bind": "^1.0.0", + "define-properties": "^1.1.3", + "has-symbols": "^1.0.1", + "object-keys": "^1.1.1" + } + } } }, "wide-align": { diff --git a/forms-flow-web/package.json b/forms-flow-web/package.json index b8b5292630..6ae36fd63f 100644 --- a/forms-flow-web/package.json +++ b/forms-flow-web/package.json @@ -18,30 +18,33 @@ "dependencies": { "@bcgov/bootstrap-theme": "https://github.com/bcgov/bootstrap-theme/releases/download/v1.1.1/bcgov-bootstrap-theme-1.1.1.tgz", "@material-ui/core": "^4.11.0", + "@material-ui/lab": "^4.0.0-alpha.56", "@wojtekmaj/react-daterange-picker": "^3.0.0", "aot-formio-export": "file:forms-flow-util", "axios": "^0.19.0", "bootstrap": "^4.4.1", "bpmn-js": "^7.3.0", "connected-react-router": "^6.8.0", - "create-react-class": "^15.6.3", + "create-react-class": "^15.7.0", "font-awesome": "^4.7.0", - "formiojs": "^4.9.12", + "formiojs": "^4.12.2", "http-proxy-middleware": "^1.0.3", "jsonwebtoken": "^8.5.1", "keycloak-js": "^9.0.0", "lodash": "^4.17.19", "node-sass": "^4.13.1", "querystring": "^0.2.0", - "react": "^16.12.0", + "react": "^16.14.0", "react-bootstrap": "^1.0.0-beta.17", "react-bootstrap-table-next": "^4.0.1", "react-bootstrap-table2-filter": "^1.3.2", "react-bootstrap-table2-overlay": "^2.0.0", "react-bootstrap-table2-paginator": "^2.1.2", "react-bootstrap-table2-toolkit": "^2.1.2", + "react-burger-menu": "^2.7.1", "react-date-range": "^1.0.3", - "react-dom": "^16.12.0", + "react-device-emulator": "^1.0.4", + "react-dom": "^16.14.0", "react-dropdown-select": "^4.4.2", "react-formio": "^4.3.0", "react-loading-overlay": "^1.0.1", @@ -49,6 +52,7 @@ "react-redux": "^7.1.3", "react-router-dom": "^5.1.2", "react-select": "^3.1.0", + "reactstrap": "^8.6.0", "recharts": "^1.8.5", "redux": "^4.0.4", "redux-logger": "^3.0.6", diff --git a/forms-flow-web/public/index.html b/forms-flow-web/public/index.html index 99279e45bb..c38158e980 100644 --- a/forms-flow-web/public/index.html +++ b/forms-flow-web/public/index.html @@ -2,7 +2,7 @@ - + diff --git a/forms-flow-web/src/actions/actionConstants.js b/forms-flow-web/src/actions/actionConstants.js index 8d6ad039d7..45f3e5807b 100644 --- a/forms-flow-web/src/actions/actionConstants.js +++ b/forms-flow-web/src/actions/actionConstants.js @@ -11,6 +11,7 @@ const ACTION_CONSTANTS = { FORM_SUBMISSION_DELETE: "FORM_SUBMISSION_DELETE", FORM_SUBMISSION_ERROR: "FORM_SUBMISSION_ERROR", IS_FORM_SUBMISSION_LOADING:"IS_FORM_SUBMISSION_LOADING", + IS_FORM_WORKFLOW_SAVED:"IS_FORM_WORKFLOW_SAVED", //TASKS LIST_TASKS: "LIST_TASKS", TASKS_COUNT: "TASKS_COUNT", @@ -31,6 +32,7 @@ const ACTION_CONSTANTS = { IS_PROCESS_STATUS_LOADING: "IS_PROCESS_STATUS_LOADING", PROCESS_STATUS_LIST: "PROCESS_STATUS_LIST", IS_PROCESS_STATUS_LOAD_ERROR: "IS_PROCESS_STATUS_LOAD_ERROR", + IS_PROCESS_ACTIVITY_LOAD_ERROR: "IS_PROCESS_ACTIVITY_LOAD_ERROR", // Application history LIST_APPLICATION_HISTORY: "LIST_APPLICATION_HISTORY", APPLICATION_HISTORY_DETAIL: "APPLICATION_HISTORY_DETAIL", @@ -46,7 +48,12 @@ const ACTION_CONSTANTS = { IS_APPLICATION_DETAIL_LOADING:"IS_APPLICATION_DETAIL_LOADING", IS_APPLICATION_UPDATING: "IS_APPLICATION_UPDATING", APPLICATION_PROCESS: "APPLICATION_PROCESS", - SET_APPLICATION_LIST_COUNT:"SET_APPLICATION_LIST_COUNT" + SET_APPLICATION_LIST_COUNT:"SET_APPLICATION_LIST_COUNT", + PROCESS_ACTIVITIES: "PROCESS_ACTIVITIES", + PROCESS_DIAGRAM_XML: "PROCESS_DIAGRAM_XML", + IS_PROCESS_DIAGRAM_LOADING: "IS_PROCESS_DIAGRAM_LOADING", + //Menu + TOGGLE_MENU:"TOGGLE_MENU" }; export default ACTION_CONSTANTS; diff --git a/forms-flow-web/src/actions/formActions.js b/forms-flow-web/src/actions/formActions.js index 198aa18687..b45731ecfa 100644 --- a/forms-flow-web/src/actions/formActions.js +++ b/forms-flow-web/src/actions/formActions.js @@ -27,3 +27,10 @@ export const setFormDeleteStatus = (data) => dispatch =>{ }) } +export const setFormWorkflowSaved = (data) => dispatch =>{ + dispatch({ + type:ACTION_CONSTANTS.IS_FORM_WORKFLOW_SAVED, + payload:data + }) +} + diff --git a/forms-flow-web/src/actions/menuActions.js b/forms-flow-web/src/actions/menuActions.js new file mode 100644 index 0000000000..0a6a982677 --- /dev/null +++ b/forms-flow-web/src/actions/menuActions.js @@ -0,0 +1,8 @@ +import ACTION_CONSTANTS from "./actionConstants"; + +export const toggleMenu = (data) => dispatch =>{ + dispatch({ + type:ACTION_CONSTANTS.TOGGLE_MENU, + payload:data + }) +} diff --git a/forms-flow-web/src/actions/processActions.js b/forms-flow-web/src/actions/processActions.js index e5f51af3e0..76ad459a43 100644 --- a/forms-flow-web/src/actions/processActions.js +++ b/forms-flow-web/src/actions/processActions.js @@ -21,6 +21,14 @@ export const setProcessLoadError = (data) => (dispatch) => { }); }; + +export const setProcessActivityLoadError = (data) => (dispatch) => { + dispatch({ + type: ACTION_CONSTANTS.IS_PROCESS_ACTIVITY_LOAD_ERROR, + payload: data, + }); +}; + export const setAllProcessList = (data) => (dispatch) => { dispatch({ type: ACTION_CONSTANTS.PROCESS_LIST, @@ -48,3 +56,25 @@ export const setFormProcessesData = (data) => (dispatch) => { payload: data, }); }; + +export const setProcessActivityData = (data) => (dispatch) => { + dispatch({ + type: ACTION_CONSTANTS.PROCESS_ACTIVITIES, + payload: data, + }); +}; + +export const setProcessDiagramXML = (data) => (dispatch) => { + dispatch({ + type: ACTION_CONSTANTS.PROCESS_DIAGRAM_XML, + payload: data, + }); +}; + +export const setProcessDiagramLoading = (data) => (dispatch) => { + dispatch({ + type: ACTION_CONSTANTS.IS_PROCESS_DIAGRAM_LOADING, + payload: data, + }); +}; + diff --git a/forms-flow-web/src/apiManager/endpoints/index.js b/forms-flow-web/src/apiManager/endpoints/index.js index 983420e235..96ceca1791 100644 --- a/forms-flow-web/src/apiManager/endpoints/index.js +++ b/forms-flow-web/src/apiManager/endpoints/index.js @@ -14,6 +14,7 @@ const API = { GET_ALL_APPLICATIONS: `${WEB_BASE_URL}/application`, GET_PROCESS_MAPPER_FOR_APPLICATION: `${WEB_BASE_URL}/application//process`, PROCESSES: `${WEB_BASE_URL}/process`, + PROCESS_ACTIVITIES: `${WEB_BASE_URL}/process/process-instance//activity-instances`, FORM: `${WEB_BASE_URL}/form`, FORM_PROCESSES: `${WEB_BASE_URL}/form/formid`, APPLICATION_EVENT_UPDATE:`${WEB_BASE_URL}/process/event`, diff --git a/forms-flow-web/src/apiManager/services/applicationAuditServices.js b/forms-flow-web/src/apiManager/services/applicationAuditServices.js index a008118800..a3b3a4bfd4 100644 --- a/forms-flow-web/src/apiManager/services/applicationAuditServices.js +++ b/forms-flow-web/src/apiManager/services/applicationAuditServices.js @@ -3,14 +3,13 @@ import API from "../endpoints"; import { setApplicationHistoryList, serviceActionError, - setLoader, - // setApplicationHistoryDetail, + setUpdateHistoryLoader } from "../../actions/taskActions"; import UserService from "../../services/UserService"; import { replaceUrl } from "../../helper/helper"; -export const fetchApplicatinAuditHistoryList = (applicationId, ...rest) => { +export const fetchApplicationAuditHistoryList = (applicationId, ...rest) => { const done = rest.length ? rest[0] : () => {}; return (dispatch) => { const apiUrlAppHistory = replaceUrl( @@ -27,16 +26,16 @@ export const fetchApplicatinAuditHistoryList = (applicationId, ...rest) => { return { ...app}; }); dispatch(setApplicationHistoryList(data)); - //dispatch(setLoader(false)); + dispatch(setUpdateHistoryLoader(false)) done(null, res.data); } else { dispatch(serviceActionError(res)); - dispatch(setLoader(false)); + dispatch(setUpdateHistoryLoader(false)); } }) .catch((error) => { dispatch(serviceActionError(error)); - dispatch(setLoader(false)); + dispatch(setUpdateHistoryLoader(false)) done(error); }); }; diff --git a/forms-flow-web/src/apiManager/services/formServices.js b/forms-flow-web/src/apiManager/services/formServices.js index 7ea4b0ea96..c4d27ff672 100644 --- a/forms-flow-web/src/apiManager/services/formServices.js +++ b/forms-flow-web/src/apiManager/services/formServices.js @@ -2,27 +2,28 @@ import { httpPOSTRequest, httpPUTRequest } from "../httpRequestHandler"; import API from "../endpoints"; import { serviceActionError } from "../../actions/taskActions"; -export const saveFormProcessMapper = (data, update = false, ...rest) => { - const done = rest.length ? rest[0] : () => {}; - return (dispatch) => { - let request; +// export const saveFormProcessMapper = (data, update = false, ...rest) => { +// const done = rest.length ? rest[0] : () => {}; +// return (dispatch) => { +// let request; - if (update) { - request = httpPUTRequest(`${API.FORM}/${data.id}`, data); - } else { - request = httpPOSTRequest(`${API.FORM}`, data); - } - request - .then((res) => { - // if (res.status === 200) { - //TODO REMOVE - done(null, res.data); - // } - }) - .catch((error) => { - console.log("Error", error); - dispatch(serviceActionError(error)); - done(error); - }); - }; -}; +// if (update) { +// request = httpPUTRequest(`${API.FORM}/${data.id}`, data); +// } else { +// request = httpPOSTRequest(`${API.FORM}`, data); +// } +// request +// .then((res) => { +// // if (res.status === 200) { +// //TODO REMOVE +// done(null, res.data); +// dispatch(setFormProcessesData(res.data)); +// // } +// }) +// .catch((error) => { +// console.log("Error", error); +// dispatch(serviceActionError(error)); +// done(error); +// }); +// }; +// }; diff --git a/forms-flow-web/src/apiManager/services/processServices.js b/forms-flow-web/src/apiManager/services/processServices.js index 0f1d203f19..800a9ba3c3 100644 --- a/forms-flow-web/src/apiManager/services/processServices.js +++ b/forms-flow-web/src/apiManager/services/processServices.js @@ -1,12 +1,16 @@ -import { httpGETRequest } from "../httpRequestHandler"; +import { httpGETRequest,httpPOSTRequest, httpPUTRequest } from "../httpRequestHandler"; import API from "../endpoints"; import { setProcessStatusLoading, setProcessList, + setProcessActivityLoadError, setProcessLoadError, setAllProcessList, setFormProcessesData, setFormProcessLoadError, + setProcessActivityData, + setProcessDiagramXML, + setProcessDiagramLoading, } from "../../actions/processActions"; import { replaceUrl } from "../../helper/helper"; import UserService from "../../services/UserService"; @@ -27,6 +31,7 @@ export const getProcessStatusList = (processId, taskId) => { .then((res) => { if (res.data) { dispatch(setProcessStatusLoading(false)); + dispatch(setProcessLoadError(false)); dispatch(setProcessList(res.data.status)); } else { dispatch(setProcessStatusLoading(false)); @@ -93,3 +98,94 @@ export const getFormProcesses = (formId, ...rest) => { }); }; }; + +export const saveFormProcessMapper = (data, update = false, ...rest) => { + const done = rest.length ? rest[0] : () => {}; + return (dispatch) => { + let request; + + if (update) { + request = httpPUTRequest(`${API.FORM}/${data.id}`, data); + } else { + request = httpPOSTRequest(`${API.FORM}`, data); + } + request + .then((res) => { + // if (res.status === 200) { + //TODO REMOVE + done(null, res.data); + //dispatch(setFormProcessesData(res.data)); + dispatch(setFormProcessesData([])); + + // } + }) + .catch((error) => { + console.log("Error", error); + dispatch(setFormProcessLoadError(true)); + done(error); + }); + }; +}; + +/** + * + * @param {...any} rest + */ +export const getProcessActivities = (process_instance_id, ...rest) => { + const done = rest.length ? rest[0] : () => {}; + const apiUrlProcessActivities= replaceUrl( + API.PROCESS_ACTIVITIES, + "", + process_instance_id + ); + return (dispatch) => { + httpGETRequest( + apiUrlProcessActivities, + {}, + UserService.getToken(), + true + ) + .then((res) => { + if (res.data) { + dispatch(setProcessActivityData(res.data.childActivityInstances)); + dispatch(setProcessActivityLoadError(false)); + } else { + dispatch(setProcessActivityLoadError(true)); + } + done(null,res.data); + }) + .catch((error) => { + done(error); + dispatch(setProcessActivityLoadError(true)); + }); + }; +}; + +export const fetchDiagram = (process_key, ...rest) => { + console.log('inside fetchDiagram >>',process_key); + const url =API.PROCESSES+'/'+process_key+'/xml'; + const done = rest.length ? rest[0] : () => {}; + console.log('inside fetchDiagram URL>>',url); + return (dispatch) => { + httpGETRequest( + url, + {}, + UserService.getToken(), + true + ) + .then((res) => { + if (res.data) { + dispatch(setProcessDiagramXML(res.data.bpmn20Xml)); + // console.log('res.data.bpmn20Xml>>',res.data.bpmn20Xml); + } else { + //TODO + } + dispatch(setProcessDiagramLoading(false)); + done(null,res.data); + }) + .catch((error) => { + done(error); + dispatch(setProcessDiagramLoading(false)); + }); + }; +}; diff --git a/forms-flow-web/src/assets/styles/layouts.scss b/forms-flow-web/src/assets/styles/layouts.scss index 6740e8928c..3e9a594319 100644 --- a/forms-flow-web/src/assets/styles/layouts.scss +++ b/forms-flow-web/src/assets/styles/layouts.scss @@ -4,6 +4,30 @@ .content { padding-bottom: 2.5rem; } + +.main-container{ + min-height: 95vh; + padding-top: 75px; + margin-left:250px !important; +} + + +.react-bootstrap-table table { + table-layout: auto !important; + position:relative !important; +} +.react-bootstrap-table{ + display: block; + width: 100%; + overflow-x: auto; + -webkit-overflow-scrolling: touch; +} +@media (max-width: 768px) { + .main-container{ + margin-left:0 !important; + } +} + #app { background: $white; } diff --git a/forms-flow-web/src/components/App.jsx b/forms-flow-web/src/components/App.jsx index 29be3f48ae..0b672928dc 100644 --- a/forms-flow-web/src/components/App.jsx +++ b/forms-flow-web/src/components/App.jsx @@ -1,47 +1,23 @@ -import React, { Component } from "react"; +import React from "react"; import PropTypes from "prop-types"; import { Provider } from "react-redux"; -import { Route, Switch } from "react-router-dom"; import { ConnectedRouter } from "connected-react-router"; - -// import PublicRoute from "./PublicRoute"; -import PrivateRoute from "./PrivateRoute"; -import SideBar from "../containers/SideBar"; - -import NavBar from "../containers/NavBar"; -import Footer from "../components/Footer"; -// import TopNav from "../containers/TopNav"; import "../assets/styles/layouts.scss"; +import BaseRouting from "./BaseRouting"; require("typeface-nunito-sans"); -class App extends Component { - render() { - const { store, history } = this.props; +const App = (props) => { + const { store, history } = props; return (
- - {/* */} -
- - -
- - {/* */} - - - - -
-
-
+
); - } -} +}; App.propTypes = { history: PropTypes.any.isRequired, diff --git a/forms-flow-web/src/components/Application/History.js b/forms-flow-web/src/components/Application/ApplicationHistory.js similarity index 72% rename from forms-flow-web/src/components/Application/History.js rename to forms-flow-web/src/components/Application/ApplicationHistory.js index 269092f5bf..58a4670014 100644 --- a/forms-flow-web/src/components/Application/History.js +++ b/forms-flow-web/src/components/Application/ApplicationHistory.js @@ -1,6 +1,5 @@ import React, {useEffect} from "react"; import {useDispatch, useSelector} from "react-redux"; -import {useParams} from "react-router-dom"; import BootstrapTable from "react-bootstrap-table-next"; import filterFactory from "react-bootstrap-table2-filter"; @@ -10,7 +9,7 @@ import LoadingOverlay from "react-loading-overlay"; import "react-bootstrap-table-next/dist/react-bootstrap-table2.min.css"; import { - fetchApplicatinAuditHistoryList + fetchApplicationAuditHistoryList } from "../../apiManager/services/applicationAuditServices"; import { columns_history, @@ -22,24 +21,21 @@ import Nodata from "./nodata"; import {setUpdateHistoryLoader} from "../../actions/taskActions"; -const HistoryList = () => { +const HistoryList = (props) => { const dispatch = useDispatch(); - const {applicationId} = useParams(); - const isTaskLoading = useSelector(state => state.tasks.isTaskLoading); const isHistoryListLoading = useSelector(state => state.tasks.isHistoryListLoading); const appHistory = useSelector(state => state.tasks.appHistory); + const applicationId = props.applicationId; + + useEffect(()=>{ + dispatch(setUpdateHistoryLoader(true)); + },[dispatch]); useEffect(() => { - if (isHistoryListLoading || isTaskLoading) { - dispatch( - fetchApplicatinAuditHistoryList(applicationId, (err, res) => { - if (!err) { - dispatch(setUpdateHistoryLoader(false)); - } - }) - ) + if(applicationId && isHistoryListLoading) { + dispatch(fetchApplicationAuditHistoryList(applicationId)); } - }, [applicationId, isHistoryListLoading, isTaskLoading, dispatch]); + }, [applicationId, isHistoryListLoading, dispatch]); if (isHistoryListLoading) { return ; @@ -48,16 +44,14 @@ const HistoryList = () => { const getNoDataIndicationContent = () => { return (
- -
- +
); }; + + + return ( appHistory.length > 0 ? ( { {(props) => (
- task

- Application History + {/* */} + +  Application History


diff --git a/forms-flow-web/src/components/Application/List.js b/forms-flow-web/src/components/Application/List.js index e18f3e9561..aac8aef39c 100644 --- a/forms-flow-web/src/components/Application/List.js +++ b/forms-flow-web/src/components/Application/List.js @@ -85,9 +85,9 @@ const ApplicationList = () => { {(props) => (
- application

- Applications +