diff --git a/.bitbucket/PULL_REQUEST_TEMPLATE.md b/.bitbucket/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8d1725439 --- /dev/null +++ b/.bitbucket/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +# PR details + +## What is the purpose of this pull request? (put an "X" next to an item) + +- [ ] Documentation update +- [ ] Bug fix +- [ ] New metadata support +- [ ] Enhanced metadata +- [ ] Add a CLI option +- [ ] Add something to the core +- [ ] Other, please explain: + +## What changes did you make? (Give an overview) + +... + +## Is there anything you'd like reviewers to focus on? + +... diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..2ff010f36 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 000000000..92e33c296 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,3 @@ +/docs/** +/node_modules/** +/retrieve/** diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 000000000..1478e324e --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,47 @@ +{ + "env": { + "es6": true, + "node": true, + "mocha": true + }, + "extends": ["eslint:recommended", "prettier", "ssjs"], + "plugins": ["mocha", "prettier"], + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module" + }, + "rules": { + "arrow-body-style": ["error", "as-needed"], + "curly": "error", + "mocha/no-exclusive-tests": "error", + "no-console": "off", + "require-jsdoc": [ + "warn", + { + "require": { + "FunctionDeclaration": true, + "MethodDefinition": true, + "ClassDeclaration": true, + "ArrowFunctionExpression": false, + "FunctionExpression": true + } + } + ], + "valid-jsdoc": "error", + "spaced-comment": ["warn", "always", { "block": { "exceptions": ["*"], "balanced": true } }] + }, + "overrides": [ + { + "files": ["*.js"], + "rules": { + "no-var": "error", + "prefer-const": "error", + "prettier/prettier": "warn" + } + } + ] +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..8310c41d5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto eol=lf + +# Declare files that will always have LF line endings on checkout. +*.ssjs text eol=lf diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8d1725439 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +# PR details + +## What is the purpose of this pull request? (put an "X" next to an item) + +- [ ] Documentation update +- [ ] Bug fix +- [ ] New metadata support +- [ ] Enhanced metadata +- [ ] Add a CLI option +- [ ] Add something to the core +- [ ] Other, please explain: + +## What changes did you make? (Give an overview) + +... + +## Is there anything you'd like reviewers to focus on? + +... diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..9a97cf1cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +businessUnits/ +deploy/ +docs/ +logs/ +node_modules/ +retrieve/ +roles/ +template/ +tmp/ +.mcdev-auth.json +.mcdevrc.json +.vscode/tasks.json diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 000000000..31354ec13 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 000000000..3f831e70f --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +npx --no lint-staged diff --git a/.issuetracker b/.issuetracker new file mode 100644 index 000000000..0e4f2f1ce --- /dev/null +++ b/.issuetracker @@ -0,0 +1,7 @@ +# Integration with Issue Tracker +# +# (note that '\' need to be escaped). + +[issuetracker "Jira Rule"] + regex = "CSCLSROZ-(\\d+)" + url = "https://alm.accenture.com/jira/browse/CSCLSROZ-$1" diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 000000000..0f3585ddb --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,16 @@ +{ + "default": true, + "line-length": false, + "no-duplicate-header": false, + "no-trailing-punctuation": false, + "no-inline-html": false, + "no-bare-urls": false, + "list-marker-space": { + "ul_single": 1, + "ol_single": 1, + "ul_multi": 1, + "ol_multi": 1 + }, + "ul-indent": { "indent": 2 }, + "ol-prefix": { "style": "ordered" } +} diff --git a/.markdownlint.md b/.markdownlint.md new file mode 100644 index 000000000..e5c757549 --- /dev/null +++ b/.markdownlint.md @@ -0,0 +1,4 @@ +# How to set up markdownlint rules + +- [list of rules](https://github.com/DavidAnson/markdownlint/blob/master/doc/Rules.md) +- [how to configure in vscode](https://github.com/DavidAnson/vscode-markdownlint#configure) diff --git a/.npmrc b/.npmrc new file mode 100644 index 000000000..a00908d4f --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-prefix='' diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..7aa27c83d --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +docs +node_modules +retrieve diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..fc1ff66b4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,26 @@ +{ + "tabWidth": 4, + "printWidth": 100, + "singleQuote": true, + "overrides": [ + { + "files": "*.json", + "options": { + "parser": "json" + } + }, + { + "files": "*.ssjs", + "options": { + "parser": "babel", + "trailingComma": "none" + } + }, + { + "files": "*.md", + "options": { + "tabWidth": 2 + } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..f8e9678ed --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,26 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + // collaboration + "gruntfuggly.todo-tree", + "aaron-bond.better-comments", + "ms-vsliveshare.vsliveshare", + + // Linters + "dbaeumer.vscode-eslint", + + // Formatting & colors + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "sergey-agadzhanov.ampscript", + "adpyke.vscode-sql-formatter", + + // Markdown / Readme.md + "yzhang.markdown-all-in-one", + "davidanson.vscode-markdownlint", + "joernberkefeld.markdown-preview-bitbucket-innersource" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..2561334f4 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,15 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceFolder}\\lib\\index.js", + "args": ["retrieve", "Dev"] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..91adcfe77 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,45 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true, + "source.fixAll.markdownlint": true + }, + "editor.formatOnSave": true, + "files.associations": { + "*.ssjs": "javascript" + }, + "files.eol": "\n", + "js/ts.implicitProjectConfig.checkJs": true, + "javascript.validate.enable": false, + "markdown.extension.italic.indicator": "_", + "markdown.extension.list.indentationSize": "adaptive", + "markdown.extension.tableFormatter.enabled": false, + "markdown.extension.toc.levels": "2..6", + "markdown.extension.toc.orderedList": false, + "sql-formatter.indent": " ", + "sql-formatter.uppercase": true, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "markdown-toc.orderedList": true, + "markdown-toc.insertAnchor": true, + "markdown-toc.anchorMode": "gitlab.com", + "outline.showProperties": false, + "outline.showVariables": false, + "breadcrumbs.showVariables": false, + "breadcrumbs.showProperties": false +} diff --git a/.vsls.json b/.vsls.json new file mode 100644 index 000000000..2e45db256 --- /dev/null +++ b/.vsls.json @@ -0,0 +1,6 @@ +{ + "$schema": "http://json.schemastore.org/vsls", + "gitignore": "none", + "excludeFiles": ["*.p12", "*.cer", "token", ".gitignore"], + "hideFiles": ["bin", "obj"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..f2f2a296b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +Accenture SFMC DevTools follows [semantic versioning](https://semver.org/). + +--- + +## 3.0.0 - 2021-03-26 + +Initial public release. + +--- + +## 2.0.0 - 2020-02-03 + +Initial Accenture-wide release. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..a246177d0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +

Contributing to Accenture Salesforce Marketing Cloud DevTools

+

First and foremost, thank you! We appreciate that you want to contribute to Accenture Salesforce Marketing Cloud DevTools, your time is valuable, and your contributions mean a lot to us.

+

Important!

+

By contributing to this project, you:

+ +

Getting started

+

What does "contributing" mean?

+

Creating an issue is the simplest form of contributing to a project. But there are many ways to contribute, including the following:

+ +

Issues

+

Please only create issues for bug reports or feature requests. Issues discussing any other topics may be closed by the project's maintainers without further explanation.

+

Do not create issues about bumping dependencies unless a bug has been identified and you can demonstrate that it effects this repo.

diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..8aa75104a --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Accenture + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 000000000..8d1725439 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,19 @@ +# PR details + +## What is the purpose of this pull request? (put an "X" next to an item) + +- [ ] Documentation update +- [ ] Bug fix +- [ ] New metadata support +- [ ] Enhanced metadata +- [ ] Add a CLI option +- [ ] Add something to the core +- [ ] Other, please explain: + +## What changes did you make? (Give an overview) + +... + +## Is there anything you'd like reviewers to focus on? + +... diff --git a/README.md b/README.md new file mode 100644 index 000000000..3a24ddd98 --- /dev/null +++ b/README.md @@ -0,0 +1,1364 @@ + + +# mcdev + +Accenture SFMC DevTools is a rapid deployment/rollout, backup and development tool for Salesforce Marketing Cloud. It allows you to retrieve and deploy configuration and code across Business Units and instances. + + + +- [1. Changelog](#1-changelog) +- [2. Getting Started](#2-getting-started) + - [2.1. Pre-requisites](#21-pre-requisites) + - [2.1.1. Install Node.js with npm](#211-install-nodejs-with-npm) + - [2.1.2. Install the Git Command Line](#212-install-the-git-command-line) + - [2.2. Install Accenture SFMC DevTools](#22-install-accenture-sfmc-devtools) + - [2.3. Initial project setup](#23-initial-project-setup) + - [2.4. Joining a project that was set up before](#24-joining-a-project-that-was-set-up-before) + - [2.5. Recommended additional installs](#25-recommended-additional-installs) +- [3. Updating Accenture SFMC DevTools](#3-updating-accenture-sfmc-devtools) +- [4. Troubleshoot Install/Update](#4-troubleshoot-installupdate) + - [4.1. Installing specific version](#41-installing-specific-version) + - [4.2. Using custom CLIs](#42-using-custom-clis) + - [4.3. Missing write access to...on MacOS](#43-missing-write-access-toon-macos) + - [4.4. ...running scripts is disabled on this system](#44-running-scripts-is-disabled-on-this-system) + - [4.5. Operation not permitted OR No such file or directory](#45-operation-not-permitted-or-no-such-file-or-directory) +- [5. Metadata Type Support](#5-metadata-type-support) +- [6. Command Overview](#6-command-overview) + - [6.1. Maintenance and setup commands](#61-maintenance-and-setup-commands) + - [6.1.1. init](#611-init) + - [6.1.2. upgrade](#612-upgrade) + - [6.1.3. reloadBUs](#613-reloadbus) + - [6.1.4. badKeys](#614-badkeys) + - [6.1.5. document](#615-document) + - [6.1.6. selectTypes](#616-selecttypes) + - [6.1.7. explainTypes](#617-explaintypes) + - [6.2. Operational commands](#62-operational-commands) + - [6.2.1. retrieve](#621-retrieve) + - [6.2.2. deploy](#622-deploy) + - [6.2.3. delete](#623-delete) + - [6.2.4. retrieveAsTemplate](#624-retrieveastemplate) + - [6.2.5. buildDefinition](#625-builddefinition) + - [6.2.6. buildDefinitionBulk](#626-builddefinitionbulk) + - [6.2.7. createDeltaPkg](#627-createdeltapkg) +- [7. Advanced Configuration](#7-advanced-configuration) + - [7.1. Config Options](#71-config-options) + - [7.2. Metadata specific settings](#72-metadata-specific-settings) + - [7.2.1. Retention Policy fields in Data Extensions](#721-retention-policy-fields-in-data-extensions) +- [8. Examples](#8-examples) + - [8.1. Retrieve and deploy Data Extension](#81-retrieve-and-deploy-data-extension) + - [8.2. Metadata Retrieving/Backup](#82-metadata-retrievingbackup) + - [8.3. Automation Deployment](#83-automation-deployment) + - [8.4. Market Configuration](#84-market-configuration) + - [8.5. Market List Configuration](#85-market-list-configuration) +- [9. Contribute](#9-contribute) + - [9.1. Install Guide for Developers](#91-install-guide-for-developers) + - [9.2. Local install](#92-local-install) + - [9.3. NPM Scripts](#93-npm-scripts) + - [9.4. Developer Documentation](#94-developer-documentation) + + + + + +## 1. Changelog + +Find info on the latest changes in our [Changelog](CHANGELOG.md). + + + +## 2. Getting Started + +Accenture SFMC DevTools can be installed as Node.JS package. The following guide will demonstrate how you can get started within 10 minutes or less. + + + + + +### 2.1. Pre-requisites + + + +#### 2.1.1. Install Node.js with npm + +1. To check if it is already installed, at the OS command prompt, type: `node --version` + - If this command reports Node version 14.16.x or later, you’re done—proceed to the next installation. If the reported version is earlier than 14.16.x, continue to step 2. + - If you get a “command not found” error message, continue to step 2. +2. In a web browser, go to [nodejs.org](https://nodejs.org) +3. Download and run the latest **LTS** installer for your operating system. +4. When the installer finishes, try step 1 again. If it fails, please restart your terminal. If it still does not work, reboot your computer and try the version check then. + + + +#### 2.1.2. Install the Git Command Line + +1. To check if git is already installed, at the OS command prompt, type: `git version` + - If this command reports a git version such as “git version 2.31.0” (or "git version 2.31.0.windows.1" on Windows), you’re done. Proceed to native Android or iOS environment setup. + - If you get a “command not found” error message, continue to step 2. +2. Go to [git-scm.com/downloads](https://git-scm.com/downloads). +3. Under Downloads, click the icon for your operating system. +4. Run the installer. +5. When the installer finishes, try step 1 again. If it fails, please restart your terminal. If it still does not work, reboot your computer and try the version check then. + + + + + +### 2.2. Install Accenture SFMC DevTools + +If you experience issues installing Accenture SFMC DevTools, please check out the [Troubleshoot Install/Update](#troubleshoot-installupdate) section in this manual. + +**How to:** + +1. Install Accenture SFMC DevTools by running `npm install -g mcdev` (prefix with `sudo` on MacOS) + - If you get an error, please see the below troubleshooting section. + +When completed you will see `+ mcdev@3.0.0` printed to your screen (or the current version of it respectively). + +> **_Side note for proud nerds_:** +> +> The `-g` part behind `npm install` triggers a so-called "global installation". This is a term coined by Package Managers, in this case Node's package manager (npm) and it means that Accenture SFMC DevTools is installed on your computer and usable across projects. The alternative is a "local installation" would install Accenture SFMC DevTools only for the current project folder. This local install is a technique used to ensure everyone in the team is using the same version of a tool. Nevertheless, we strongly recommend going with the global installation to be able to use the full feature-set of Accenture SFMC DevTools. +> +> You may choose to install mcdev globally or locally. Global install runs faster and allows you to initialize new projects by running `mcdev init` in any directory. If your project does require a local installation, please refer to the [Local Install Guide](#local-install). + + + + + +### 2.3. Initial project setup + +After the successful installation, you will now need to setup the connection to your Marketing Cloud instance. + +1. In your Marketing Cloud instance + 1. Ensure that you **selected** your **Parent/Global Business Unit**. + 2. Go to `Setup -> Apps -> Installed Packages`. + 3. Create a new installed package and name it "Accenture SFMC DevTools Deployment Tool" + > Note: On some older SFMC instances it will ask you if you want the enhanced version. Please enable this option. + 4. Click on the _Add Component_ button and select `API Integration` with `Server-To-Server` mode. + 5. Make sure you grant all available rights. + 6. Go to the access tab and grant it access to all Business Units that you want to use it for, but ensure that the Parent/Global Business Unit is among these. + - _Why?_ Shared Data Extensions, roles, users, Business Unit info and some other metadata is internally stored solely on the parent Business Unit and hence can only be retrieved and updated via that BU. + 7. Note down _Client Id_, _Client Secret_ and _Authentication Base URI_. +2. In your project folder + 1. Open a CLI in your project folder (e.g. `C:\repos\MyProject\` on Windows or `~/repos/MyProject/` on Mac) + 2. Run `mcdev init` to start the interactive setup wizard. + 1. If not found yet, default configuration files will be copied into your project folder, copied by initializing an npm project and a local Git repository. + 2. The wizard will ask you to name your credential. The name you choose here will be used by all team members because the config is shared via Git. + > Being specific here reduces the chance of deploying to the wrong server (instance) in stressful situations. We suggest you use something like `ClientName`, or `ClientName-ProjectName` if you have multiple projects with the same client. In case your project uses multiples instances you might like to use something like `Client-ProjectName-Sandbox` and `Client-ProjectName-PROD`. + 3. It will then continue to ask for the client ID, client secret and Authentication Base URI. + 4. The credentials will be automatically tested & your list of BUs downloaded until finally the central configuration file `.mcdevrc.json` gets created in your project folder. + 5. Last step is to download an initial backup and commit it into git. No worries - the wizard does that for you! + 3. If this is the first time you set up Accenture SFMC DevTools or you recently upgraded Accenture SFMC DevTools, please restart VS Code now! A pop-up will likely appear in the lower right corner prompting you to install recommended extensions. + 4. Done. +3. Sharing the project with your team + 1. Make sure you have a Git repo (Bitbucket, GitHub, GitLab) set up somewhere. Usually your client will have to do this for you. Sometimes, we host client repos in our own Innersource, which is instance of Bitbucket. + > While running `mcdev init`, the tool already made sure to set up a local Git repo for you. Now, you need to upload ("push") it to the online repo: + 2. Open the URL of your online repo and find the "CLONE" button. This will likely show you a normal URL, ending on ".git" + 3. Add this as your repository remote named "origin". If you use a GUI based tool, that should be fairly simple, otherwise execute `git remote add origin YOUR-URL` in your project folder. + 4. Now run `git push -u origin master` to actually start the upload. + + + +### 2.4. Joining a project that was set up before + +If Accenture SFMC DevTools was already used to set up the project by somebody in your team, including all of the steps in the above chapter [Initial project configuration](#initial-project-setup), then basically you are in luck. Things are much faster from here on: + +1. Make sure you went through the chapters [Pre-requisites](#pre-requisites) and [Install Accenture SFMC DevTools](#install-mcdev). Do skip [Initial project configuration](#initial-project-setup)! +2. Acquire the URL to your Git repo and Clone it to your computer. It should end on `.git`. Also ask your team lead for `Client ID`, `Client Secret` and the `Authentication Base URI`. You will need this later. + > We recommend you create a folder like `C:\repos\` and clone the repo for your current project into that. By default, the repo name will be suggested as a sub-folder name which you should keep in place. That way you will always have one folder in which all your projects can be found in with their respective sub-folders. +3. Open your main repo folder (e.g. `C:\repos\`) in the CLI of your choice (e.g. PowerShell on Windows) +4. now execute `git clone YOUR-REPO-URL`. This will create a sub-folder with the name of the repo and download everything for you into it (e.g. `C:\repos\YOUR-REPO\`) +5. Still in the command prompt, execute `cd YOUR-REPO`. This will switch your current folder (visible before the command prompt) to the new repo folder (`C:\repos\YOUR-REPO\`). +6. Assuming you installed Accenture SFMC DevTools globally (recommended!), now execute `mcdev init`. +7. At this point the system will recognize the previously set up project and ask you for `Client ID`, `Client Secret` and the `Authentication Base URI`. +8. Done. + + + +### 2.5. Recommended additional installs + +The following seeks to enhance your daily process. Our guide assumes that you are using [Visual Studio Code](https://code.visualstudio.com/download) to develop, backup and deploy your project. For smooth operations we highly recommend the following Marketing Cloud specific plugins for it. + +Nevertheless, Accenture SFMC DevTools will run without them and is not associated with the development of these publicly available apps & plugins. + +> **Note:** The following lists are automatically installed when you run `mcdev init` or `mcdev upgrade`. + +**Visual Studio Code extensions:** + +When you run `mcdev init` or `mcdev upgrade` we add/update the file `.vscode/extensions.json` in your project directory with a list of recommended VSCode extensions that will help your daily workflow with Salesforce Marketing Cloud. + +Restart VS Code after cloning a prepared repo or after you've run `init` / `upgrade` and VS Code will prompt you in the lower right corner with: + +![Recommended extension prompt](img/README.md/vscode-recommendations-1.png) + +Click on `Install All` to quickly get things ready or review the recommendations first via `Show Recommendations`. If you opt for the second option, be aware that there are 2 kinds of recommendations: + +![Recommended extension prompt](img/README.md/vscode-recommendations-2.png) + +The "Workspace Recommendations" were defined by Accenture SFMC DevTools. Clicking on the little cloud icon will install all at once. The "Other Recommendations" are auto-generated by VS Code and are not controlled by Accenture SFMC DevTools. You _may_ look through those as well, but they might also be completely irrelevant for you. + +**Node modules:** + +- [eslint](https://npmjs.com/package/eslint): code linting +- [eslint-config-prettier](http://npmjs.com/package/eslint-config-prettier): ensures that prettier and eslint dont have conflicting rules +- [eslint-config-ssjs](http://npmjs.com/package/eslint-config-ssjs): allows you to have accurate code linting in \*.SSJS files +- [eslint-plugin-jsdoc](http://npmjs.com/package/eslint-plugin-jsdoc): will help you write proper jsdoc comments in your SSJS code +- [eslint-plugin-prettier](http://npmjs.com/package/eslint-plugin-prettier): runs prettier and eslint together +- [prettier](https://prettier.io): opinionated code formatter +- [npm-check](http://npmjs.com/package/npm-check): makes it easier to keep your node modules up-to-date +- [sfmc-boilerplate](http://npmjs.com/package/sfmc-boilerplate): build tool for your more complex email, cloudpage and automation code. + +Please note that Visual Studio Code might warn you about using the local installation of ESLint with a pop up like the following. Please confirm this with `Allow` or, if you are certain about what you are doing, with `Allow Everywhere`. Inside of Accenture SFMC DevTools project folders this warning is normal, because we ask to install the VSCode extension and the node module for ESLint. + +![VSCode Eslint install warning](img/README.md/vscode-eslint-allow_everywhere.jpg) + + + + + +## 3. Updating Accenture SFMC DevTools + +If you have mcdev already installed you can update your installation in a simplified way: + +```bash +npm update -g mcdev +``` + + + + + +## 4. Troubleshoot Install/Update + + + +### 4.1. Installing specific version + +To work with our **developer-version** or to install a **specific older version** you can select any branch or tag from our git repository during install to do so: + +```bash +// most recent developer version (using the branch name) +npm install -g mcdev@develop + +// install specific version (using a tag) +npm install -g mcdev@3.0.0 +``` + +**Warning**: When you used the above method to install Accenture SFMC DevTools for a specific version or tag, trying to [update Accenture SFMC DevTools](#updating-mcdev) might not download the most recently published official version but instead stay on the version or branch you previously selected (in the above examples: develop, 3.0.0)! + +If you opted for `@develop` you will continue to get the latest developer udates. If, however, you opted for a version, you will have to use the install command again to overwrite whatever version you used before. + +> **Note**: The version is currently _not_ updated on the developer branch until a new release is published. Hence, you will not see a change if you run `mcdev --version`. + + + +### 4.2. Using custom CLIs + +Some users of Accenture SFMC DevTools prefer to use git bash or other CLIs instead of the operating system's default. Please note that some of the functionality of Accenture SFMC DevTools but also of other tools like the Node package manager (npm) do not necessarily function properly in these. + +If you encounter problems, we strongly recommend to first try it in the default CLI. + + + + + +### 4.3. Missing write access to...on MacOS + +Depending on your setup, the default global installs & updates might error out with "Missing write access to /usr/local/lib/node_modules". In this case prefix your command with `sudo`: + +```bash +sudo npm install -g mcdev +``` + +```bash +sudo npm update -g mcdev +``` + +![Mac sudo](img/README.md/troubleshoot-mac-sudo.png) + + + +### 4.4. ...running scripts is disabled on this system + +If you see the below error then your system's security settings are rather strict. + +![Execution Policy](img/README.md/troubleshoot-execution_policy.jpg) + +Steps to solve this: + +1. Start Windows PowerShell with the "Run as Administrator" option. +2. Input the following and then hit ENTER: `set-executionpolicy remotesigned` +3. This will likely show a lenghty message with a question to confirm the change (screenshot below). Please type `y` (="yes") and confirm with `Enter`. + +![Execution Policy](img/README.md/troubleshoot-execution_policy-2.png) + +Please note that this change is global and not just for your current folder. + + + +### 4.5. Operation not permitted OR No such file or directory + +If you encounter out of the 3 following errors you will have to completely remove Node.JS and install it again afterwards. + +**Error 1:** Cannot find module index.js + +![index.js not found](img/README.md/troubleshoot-nodejs-index.jpg) + +**Error 2:** Cannot find module postinstall.js + +![postinstall.js not found](img/README.md/troubleshoot-nodejs-postinstall.jpg) + +**Error 3:** Operation not permitted + +![operation not permitted](img/README.md/troubleshoot-nodejs-permission.jpg) + +**How to completely remove Node.js from Windows:** + +1. Run npm cache clean --force + +2. Uninstall it via the system's `Add or remove programs` (find it by searching in the start menu). + +3. Reboot your computer. + +4. Look for these folders and remove them (and their contents) if any still exist. Depending on the version you installed, UAC settings, and CPU architecture, these may or may not exist: + + - C:\Program Files (x86)\Nodejs + - C:\Program Files\Nodejs + - C:\Users\\{User}\AppData\Roaming\npm + - (or %appdata%\npm) + - C:\Users\\{User}\AppData\Roaming\npm-cache + - (or %appdata%\npm-cache) + - C:\Users\\{User}\.npmrc (and possibly check for that without the . prefix too) + - C:\Users\\{User}\npmrc + - C:\Users\\{User}\AppData\Local\Temp\npm-\* + +5. Check your %PATH% environment variable to ensure no references to Nodejs or npm exist. + +6. If it's still not uninstalled, type `where node` at the command prompt and you'll see where it resides -- delete that (and probably the parent directory) too. + +7. Reboot again! + +**Re-install Node.js and Accenture SFMC DevTools:** + +Now, please follow the guides above in the [Pre-requisites](#pre-requisites) section to Install Node.js again and afterwards try again to [install Accenture SFMC DevTools](#install-mcdev). + + + + + +## 5. Metadata Type Support + +The following metadata types are currently supported: + +| MetadataType | CLI Argument | Retrieve | Deploy | Template | Retrieved by Default | Description | +| ---------------------------------- | ------------------------- | -------- | ---------- | ---------- | -------------------- | ------------------------------------------------------------------------------------------------------------------ | +| API Discovery | `discovery` | Yes | - | - | - | Description of all API endpoints accessible via REST API; only relevant for developers of Accenture SFMC DevTools. | +| Asset | `asset` | Yes | Yes | Yes | Yes | Assets from Content Builder grouped into subtypes. | +| Automation | `automation` | Yes | Yes | Yes | Yes | Used via Automation Studio directly - or indirectly via Journey Builder & MC Connect. | +| Automation: Data Extract Activity | `dataExtract` | Yes | Yes | Yes | Yes | Creates zipped files in your FTP directory or convert XML into CSV. | +| Automation: File Transfer Activity | `fileTransfer` | Yes | Yes | Yes | Yes | Unzip, decrypt a file or move a file from secure location into FTP directory. | +| Automation: Filter Activity | `filter` | Beta | in backlog | in backlog | - | Part of how filtered Data Extensions are created. Depends on type "FilterDefinitions". | +| Automation: Import File Activity | `importFile` | Yes | Yes | Yes | Yes | Reads files in FTP directory for further processing. | +| Automation: SQL Query Activity | `query` | Yes | Yes | Yes | Yes | Select & transform data using SQL. | +| Automation: Script Activity | `script` | Yes | Yes | Yes | Yes | Execute more complex tasks via SSJS or AMPScript. | +| Campaign Tag | `campaign` | Yes | in backlog | in backlog | Yes | Way of tagging/categorizing emails, journeys and alike. | +| Content Area (Classic) | `contentArea` | Yes | - | - | - | **DEPRECATED**: Old way of saving Content Blocks; please migrate these to new Content Blocks (`Asset: ...`). | +| Data Designer Attribute Groups | `attributeGroup` | Beta | in backlog | in backlog | - | Groupings of Set Definitions (Data Extensions) in Data Designer. | +| Data Designer Set Definitions | `setDefinition` | Beta | in backlog | in backlog | - | Data Extensions linked to Attribute Groups in Data Designer. | +| Data Extension | `dataExtension` | Yes | Yes | Yes | Yes | Database table schemas. | +| Data Extension Template | `dataExtensionTemplate` | Yes | - | - | - | OOTB Database table schemas used for special cases like Transactional Journeys. | +| Data Extract Type | `dataExtractType` | Yes | - | - | - | Types of Data Extracts enabled for a specific business unit. This normally should not be stored. | +| E-Mail (Classic) | `email` | Yes | - | - | - | **DEPRECATED**: Old way of saving E-Mails; please migrate these to new E-Mail (`Asset: message`). | +| E-Mail Send Definition | `emailSendDefinition` | Yes | Yes | in backlog | Yes | Mainly used in Automations as "Send Email Activity". | +| Folder | `folder` | Yes | Yes | in backlog | - | Used to structure all kinds of other metadata. | +| FTPLocation | `ftpLocation` | Yes | - | - | Yes | A File Location which can be used for export or import of files to/from Marketing Cloud. | +| Journey | `interaction` | Yes | in backlog | in backlog | - | Journey from Builder (internally called "Interaction"). | +| Journey: Entry Event Definition | `eventDefinition` | Yes | Yes | in backlog | - | Used in Journeys (Interactions) to define Entry Events. | +| List | `list` | Yes | in backlog | - | Yes | Old way of storing data. Still used for central Email Subscriber DB. | +| Role | `role` | Yes | Yes | - | Yes | User Roles define groups that are used to grant users access to SFMC systems. | +| Triggered Send | `triggeredSendDefinition` | Yes | Yes | - | Yes | **DEPRECATED**: Sends emails via API or DataExtension Event. | + + + +## 6. Command Overview + +If you installed mcdev globally as described above you can run mcdev in any directory. See our [install Accenture SFMC DevTools](#install-mcdev) chapter for more details. + +_Example (global install):_ + +```bash +mcdev retrieve +``` + +If you installed Accenture SFMC DevTools as a local dependency (**not recommended**) to the current directory then you will have to add `npx` in front of each command. See our [Local install](#local-install) chapter for more details. + +_Example (local install):_ + +```bash +npx mcdev retrieve +``` + +The following description will assume a global installation for simplicity reasons. + +_Note:_ Parameters listed below in between square brackets = `[...]` are optional parameters. Required parameters are listed in between less-than / greater-than signs = `<...>`. + +_Note:_ Credentials and Business Unit names can always be selected interactively. Try inputing a questionmark = `?` in their place if more parameters follow, or omit them completely if no other parameters are required for a command. + + + +### 6.1. Maintenance and setup commands + + + + + +#### 6.1.1. init + +_Command:_ `mcdev init` + +_Alias:_ - + +Creates the basic configuration file `.mcdevrc.json` and `.mcdev-auth.json` in your project directory. You may add more credentials by re-running the same command again, e.g. to add produciton and sandbox credentials next to each other. + +In addition, it initializes an npm package for your, installs recommended npm dependencies and places our default IDE configuration files for ESLint, Prettier, Git and VSCode into your project directory. + +The initialization ends with the creation of your Git repository and a first backup of your SFMC instance. + +_Example - initialize project / add additional credentials:_ + +```bash +mcdev init +``` + +_Example - update credentials:_ + +```bash +mcdev init yourCredentialName +``` + +The interactive setup will ask you for a `Client ID` and `Client Secret` of an enhanced installed package (default since August 2019). It also asks for the `Authentication Base Uri`. Each installed package on a given SFMC instance shares the same tenant sub-domain and always shows you 3 domains (Auth, REST and SOAP). + +Example url: `https://mcg123abcysykllg-0321cbs8bbt64.auth.marketingcloudapis.com` + +> **Note to CLI experts:** +> +> You can run this command without the interactive wizard asking questions using the `--skipInteraction` (or short`--yes`/`--y`) flag. In this case, you need to provide a few values in the command: +> +> ```bash +> mcdev init --y.credentialsName "yourCustomCredentialName" --y.clientId "yourClientIdHere" --y.clientSecret "yourClientSecretHere" --y.tenant "yourTenantSubdomainHere" --y.gitRemoteUrl "https://my.git.server.com/myrepo.git" +> ``` +> +> To get the tenant subdomain, please take the Authentication Base Uri and extract the part after `https://` and before `.auth.marketingcloudapis.com`. In the above example this would therefore be `mcg123abcysykllg-0321cbs8bbt64`. + + + +#### 6.1.2. upgrade + +_Command:_ `mcdev upgrade` + +_Alias:_ `mcdev up` + +This upgrades older Accenture SFMC DevTools projects to the newest standard: Outdated Accenture SFMC DevTools configuration files are upgraded and the right npm dependencies are installed. It also copies in the right IDE configuration files. See [init](#init) for more details. + +_Example:_ + +```bash +mcdev upgrade +``` + + + +#### 6.1.3. reloadBUs + +_Command:_ `mcdev reloadBUs [credential]` + +_Alias:_ `mcdev rb` + +Use this to synchronize your local list of available Business Units for a given credential. This is useful if you've added, deleted or renamed Business Units on your SFMC instance and want to make sure that your local setup reflects that - or if you accidentally changed your config file and want to restore it. + +_Example:_ + +```bash +mcdev reloadBUs MyProject +``` + + + +#### 6.1.4. badKeys + +_Command:_ `mcdev badKeys [business unit]` + +_Alias:_ - + +Lists all metadata for which the External key is not in synch with the name to enable you to update them quickly. + +_Example:_ + +```bash +mcdev badKeys MyProject/DEV +``` + + + +#### 6.1.5. document + +_Command:_ `mcdev document ` + +_Alias:_ `mcdev doc` + +Creates human readable documentation for your metadata. This command is executed by default unless you changed your config manually to set `options.documentOnRetrieve : false`. Therefore, running it manually is typically not required. You can choose to generate **HTML** (`html`) or **Markdown** (`md`) docs via `options.documentType`. + +The default format is set to `md` as Markdown renders nicely in Git as well as in VSCode's Markdown preview and can be copied from there into Confluence and other applications without losing the formatting. + +Currently supported types: + +| Name | CLI Argument | +| -------------- | --------------- | +| Data Extension | `dataExtension` | +| Role | `role` | + +_Example:_ + +```bash +mcdev document role myServer +``` + + + +#### 6.1.6. selectTypes + +_Command:_ `mcdev selectTypes` + +_Alias:_ `mcdev st` + +Allows you to interactive select which metadata is retrieved when you run the `retrieve` command. Try out `explainTypes` first to understand what each type means. + +_Example:_ + +```bash +mcdev selectTypes +``` + +_Note:_ You may select non-standard types if you run `mcdev selectTypes --debug`. This may be needed in edge cases but is not recommended in most situations. + + + +#### 6.1.7. explainTypes + +_Command:_ `mcdev explainTypes` + +_Alias:_ `mcdev et` + +A helper command for `selectTypes` that prints out a table that defines what the various types actually are. + +Types marked as not-default should be ignored. These are either under development or merely meant to support contributing to Accenture SFMC DevTools. + +_Example:_ + +```bash +mcdev explainTypes +``` + + + +### 6.2. Operational commands + + + +#### 6.2.1. retrieve + +_Command:_ `mcdev retrieve [business unit] [metadata type]` + +_Alias:_ `mcdev r` + +Retrieves all metadata from the specified Business Unit. You can limit what types are retrieved by default using the `selectTypes` command or by changing the list in the config file directly. + +_Example:_ + +```bash +mcdev retrieve MyProject/DEV +``` + +You can ommit the Business Unit which will trigger and interactive mode based on your config: + +_Example:_ + +```bash +mcdev retrieve +``` + +If you already know the credentials name but want to only select the Business Unit interactively try this +_Example:_ + +```bash +mcdev retrieve MyProject +``` + +**retrieve specific type:** + +If you want to retrieve only a certain metadata type, let's say `script`, then pass this type in as a second parameter. The other types will remain untouched and in place, if you've previously retrieved them. + +_Example:_ + +```bash +mcdev retrieve MyProject/DEV script +``` + +**retrieve all BUs:** + +A special variant of this command allows you to retrieve all Business Units of a given credential at once. +_Example:_ + +```bash +mcdev retrieve MyProject/* +``` + +or even + +```bash +mcdev retrieve "*" +``` + +> Note: retrieve-all will fail in some CLIs if you do not wrap the asterix (\*) in quotes. This is due to the special meaning of \* as a parameter in these CLIs. + + + +#### 6.2.2. deploy + +_Command:_ `mcdev deploy [business unit] [metadata type]` + +_Alias:_ `mcdev d` + +Deploys metadata to the specified Business Unit. +_Example:_ + +```bash +mcdev deploy MyProject/DEV +``` + +Only metadata that you copied into the **deploy** directory will be deployed. Please keek in mind that the folder structure needs to be similar to what the retrieve command creates in the retrieve folder, including the credentials and Business Unit name. + +Similarly to `mcdev retrieve` you can also use the interactive mode to select credential and/or Business Unit. + +**deploy sepcific type:** + +If you want to deploy only a certain metadata type, let's say `dataExtension`, then pass this type in as a second parameter. If there are other types in the current BU's deploy folder, these will be ignored and hence _not_ uploaded. + +_Example:_ + +```bash +mcdev deploy MyProject/DEV dataExtension +``` + +**deploy all BUs:** + +A special variant of this command allows you to dpeloy all Business Units of a given credential at once. +_Example:_ + +```bash +mcdev deploy MyProject/* +``` + +or even + +```bash +mcdev deploy "*" +``` + +> Note: deploy-all will fail in some CLIs if you do not wrap the asterix (\*) in quotes. This is due to the special meaning of \* as a parameter in these CLIs. + + + +#### 6.2.3. delete + +_Command:_ `mcdev delete ` + +_Alias:_ `mcdev del` + +Deletes the given metadata from your server. This needs to be run with care as any **data** stored in the deleted _meta_-data **will be lost**. + +Currently supported types: + +| Name | CLI Argument | +| -------------- | --------------- | +| Data Extension | `dataExtension` | + +_Example:_ + +```bash +mcdev delete MyProject/_ParentBU_ dataExtension MyUserTable +``` + + + +#### 6.2.4. retrieveAsTemplate + +_Command:_ `mcdev retrieveAsTemplate ` + +_Alias:_ `mcdev rt` + +The `rt` command retrieves metadata from the server and uses your `market` configuration in `.mcdevrc.json` to replace strings with variables. The result is then stored in your `template/` folder. Please note that files stored here will keep their original name, despite this possibly containing market-specific suffixes or similar. Also note, that contrary to the deploy & retrieve folders, you will not see credential- or Business Unit-sub-folders here. + +This command is a prerequisite for the `buildDefintion` command. Alternatively though, you can copy-paste retrieved metadata from your `retrieve/` folder to your `template/` folder and update it manually - or even create it from scratch. + +> **Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market Configuration](#market-configuration) to understand how to use templating and prepare your market config. + +Currently supported types: Check out [Metadata Type Support](#metadata-type-support). + +_Example:_ + +```bash +mcdev rt MyProject/DEV dataExtension MyUserTable pilotMarketDEV1 +``` + +This will result in `MyUserTable.dataExtension-meta.json` being created in your `template/` directory: + +**retrieveAsTemplate for multiple sources:** + +You can also create multiple templates with multiple sources at once. Simply specify them in a comma-separated list and put that list in quotes: + +```bash +mcdev rt MyProject/DEV dataExtension "table1,table2,table3" pilotMarketDEV1 +``` + +This will result in the following files being created in your `template/` directory: + +- `table1.dataExtension-meta.json` +- `table2.dataExtension-meta.json` +- `table3.dataExtension-meta.json` + + + +#### 6.2.5. buildDefinition + +_Command:_ `mcdev buildDefinition ` + +_Alias:_ `mcdev bd` + +The `buildDefinition` command allows to prepare deployments to one or multiple targets based on templates and [Market Configuration](#market-configuration). +After you have created your templates via `retrieveAsTemplate` (or manually) in your `template/dataExtension/` folder run this command to create the final deployable files in your respective `retrieve//` folder. + +This allows you to double-check if you actually changed something by comparing the before and after using your favorite Git client. You then have to manually copy files you want to deploy into the respective `deploy/` folder. + +> **Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market Configuration](#market-configuration) to understand how to use templating and prepare your market config. + +Currently supported types: Check out [Metadata Type Support](#metadata-type-support). + +_Example:_ + +```bash +mcdev bd MyProject/QA dataExtension MyUserTable pilotMarketQA1 +``` + +This will result in the following files being created in your `retrieve/MyProject/QA/dataExtension/` directory: + +- `MyUserTable.dataExtension-meta.json` + +**buildDefinition for multiple sources:** + +You can also create defintions based on multiple templates at once. Simply specify them in a comma-separated list and put that list in quotes: + +```bash +mcdev bd MyProject/QA dataExtension "table1,table2,table3" pilotMarketDEV1 +``` + +This will result in the following files being created in your `retrieve/MyProject/QA/dataExtension/` directory: + +- `table1.dataExtension-meta.json` +- `table2.dataExtension-meta.json` +- `table3.dataExtension-meta.json` + + + +#### 6.2.6. buildDefinitionBulk + +_Command:_ `mcdev buildDefinitionBulk ` + +_Alias:_ `mcdev bdb` + +With `buildDefinitionBulk` you get a very powerful command that wraps around `buildDefinition`, which it executes internally. It allows you to create the definitions for multiple Business Units and multiple markets at once. + +Instead of passing in the name of credentials, Business Units and markets, you simply refer to a pre-defined market list in your `.mcdevrc.json`. This enables you to re-use the same configs over and over again, across multiple deployments and share it with your team. + +**Note**: Before using this command, you need to configure your markets first! Check out our guide on [Market List Configuration](#market-list-configuration) and [Market Configuration](#market-configuration) to understand how to use **bulk** templating and prepare your market config. + +_Example:_ + +```bash +mcdev bdb pilotMarketsQA dataExtension MyUserTable +``` + + + +#### 6.2.7. createDeltaPkg + +_Command:_ `mcdev createDeltaPkg [range] [filter]` + +_Alias:_ `mcdev cdp` + +This command is rather versitile and hence can be used in multiple ways. The most powerful option presents itself when you configure `options.deployment.sourceTargetMapping` to point to a source `marketList` (usually for DEV-BU with a DEV market) and a target `marketList` (e.g. to a QA BU-market combo and a Production BU-market combo). Given this is configured, it can create all deployable files using Accenture SFMC DevTools's templating engine on the fly for you. + +The **minimum configuration** you need to have in your config could look something like the following: + +> Note: the following example does not show all necessary parts of the config, just those that are needed for createDeltaPkg. +> +> The example tells createDeltaPkg that the source BU is called `MyProject/DEV` and that the there are two target for deployment, `MyProject/QA` and `MyProject/PROD`. Furthermore it associates the markets `dev`, `qa` and `prod` to these BUs to ensure templating is applied - however, the markets in this example are empty which means no actual string replacement will occur. This is normal for most basic cases in which you would expect 1:1 copies on DEV, QA and PROD. +> +> When MC Connect is used, one would expect to see the External Keys of the Synchronized DataExtensions to be mentioned in the markets for easy auto-replacement. + +```json +{ + "credentials": { + "MyProject": { + "businessUnits": { + "DEV": "1235", + "QA": "1236", + "PROD": "1237" + } + } + }, + "options": { + "deployment": { + "commitHistory": 10, + "sourceTargetMapping": { + "deployment-source": "deployment-target" + } + } + }, + "directories": { + "deltaPackage": "docs/deltaPackage/", + "deploy": "deploy/", + "retrieve": "retrieve/", + "template": "template/", + "templateBuilds": ["retrieve/", "deploy/"] + }, + "markets": { + "dev": {}, + "qa": {}, + "prod": {} + }, + "marketList": { + "deployment-source": { + "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", + "MyProject/DEV": "dev" + }, + "deployment-target": { + "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", + "MyProject/QA": "qa", + "MyProject/PROD": "prod" + } + } +} +``` + +> **Detailed Background infos:** `createDeltaPkg` internally first compares the 2 commits in Git to find the differences, then executes `retrieveAsTemplate` for all of these found differences, which creates files in your `template/` directory. Finally, it runs `buildDefinitionBulk` for everything, updating your `retrieve/` directory as well as, optionally, your `deploy/` directory, depending on your Accenture SFMC DevTools config's `directories.templateBuilds` value. The update to the first folder enables you to update you branch, while the second folder gives you the right files to immediately deploy to your BUs afterwards (not part of `createDeltaPkg). + +**Interactive commit selection:** + +This allows you to compare your latest commit (**not** what's still only unstaged/staged) with previous commits. This approach is especially useful if you are a in charge of the deployment and simply want to compare the latest commits to your master / release branch with the commit that was last deployed. + +> **Important**: Make sure you are on the branch corresponding to the environment you want to deploy to, e.g. the master branch. + +```bash +mcdev createDeltaPkg +``` + +The output will look something like this: + +![interactive commit selection](img/README.md/cdp-1.jpg) + +> _Note: The amount of displayed past commits depends on your Accenture SFMC DevTools configs settings in `options.deployment.commitHistory`. The default value is 10._ + +**Manual commit selection:** + +This is what you would do when you work on a feature branch and want to include the proposed changes for the target BUs already in the branch. + +```bash +# Option 1 - RECOMMENDED +# compare based on what you last committed +# you just committed all your changes and want to create the deployment package for master/Production; +# Recommendation: run this before creating a pull request to include the changes in your PR. + +mcdev createDeltaPkg master # resolves to master..HEAD +``` + +Alternatives: + +```bash +# the same example with another branch name: +mcdev createDeltaPkg "release/sprint-14" # resolves to release/sprint-14..HEAD + +# or even use a commit ID instead of a branch name +mcdev createDeltaPkg d21b4221 # resolves to d21b4221..HEAD + + +# Option 2 - full git range +# Treat this option with caution because createDeltaPkg always runs retrieveAsTemplate and hence downloads from your DEV BU, not from the git branch. +mcdev createDeltaPkg master..develop +``` + +**Manual commit selection without templating:** + +If you don't want to use templating, you may instead provide the optional `filter` parameter. This will limit + +```bash +mcdev createDeltaPkg d21b4221..HEAD 'MyProject/BU1' +``` + +Range and multiple filters (without templating): + +```bash +mcdev createDeltaPkg d21b4221..HEAD 'MyProject/BU1,MyProject/BU3' +``` + +> **Note to CLI experts:** +> +> If you provide a range you can run this command without the interactive wizard asking questions using the `--skipInteraction` (or short`--yes`/`--y`) flag. This will automatically empty your deploy folder before adding new files. Your command will look like this: +> +> ```bash +> mcdev createDeltaPkg [filter] --y +> ``` + + + +## 7. Advanced Configuration + +The tools confiuration can be changed in the file `.mcdevrc.json` located in the root of your project folder. + +It contains [Market Configuration](#market-configuration) (`markets: { ... }`), [Market List Configuration](#market-list-configuration) (`marketList: { ... }`) the list of usable Business Units per credentials, `directories`, as well as other `options`. + +You will also find the configuration for what metadata shall be retrieved here in `metaDataTypes.retrieve: [ ... ]`. + +You will also find a secondary file named `.mcdev-auth.json` containing your credentials. **Do not commit this to your repository!** You should only commit `.mcdevrc.json` as this file contains project wide settings that do not compromise security. + + + +### 7.1. Config Options + +The central config in `.mcdevrc.json` holds multiple adjustable settings: + +```json +{ + "options": { + "deployment": { + "commitHistory": 10, + "sourceTargetMapping": { + "deployment-source": "deployment-target" + }, + "targetBranchBuMapping": { + "release/*": "MySandbox/QA-DE", + "master": ["MyProduction/PROD-DE", "MyProduction/PROD-NL"] + } + }, + "documentType": "md", + "exclude": { + "role": { + "CustomerKey": ["excludedRoleKey","excludedOtherRoleKey"] + } + }, + "include": { + "asset": { + "r__folder_Path": ["Content Builder/only/assets/in/here"] + }, + }, + "serverTimeOffset": -6 + }, + "directories": { + "badKeys": "docs/badKeys/", + "businessUnits": "businessUnits/", + "dataExtension": "docs/dataExtension/", + "deltaPackage": "docs/deltaPackage/", + "deploy": "deploy/", + "retrieve": "retrieve/", + "roles": "docs/roles/", + "template": "template/", + "templateBuilds": ["retrieve/", "deploy/"] + }, + "metaDataTypes": { + "documentOnRetrieve": ["role", "dataExtension"], + "retrieve": [...] + } +} +``` + +| Setting | Default | Description | +| ---------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| options.deployment.commitHistory | 10 | Configures how many commits `createDeltaPkg` will display if no parameters are given | +| options.deployment.sourceTargetMapping | `{"deployment-source": "deployment-target"}` | Configuration of 1 or many source-target marketList combos for `mcdev createDeltaPkg` | +| options.deployment.targetBranchBuMapping | `{"release/*": "...","master": ["..."]}` | Can be used by CI/CD pipelines to know what BUs shall be deployed to upon a merge into one of the specified branches | +| options.documentType | 'md' | Defines the format for documentation ('md', 'html', 'both') | +| options.exclude.`type`.`field` | [] | Allows you to filter out metadata on retrieve based on their field values, e.g. CustomerKey (previously `options.filter`) | +| options.include.`type`.`field` | [] | Allows you to filter out metadata on retrieve based on their field values, e.g. CustomerKey | +| options.serverTimeOffset | -6 | Used to work around quirks in how SFMC handles timezones; For stack4: set to -7 (US Mountain time); others: -6 (US Central) | +| directories.badKeys | 'docs/badKeys/' | Where the documentation for bad key-name combos is stored on retrieve | +| directories.businessUnits | 'businessUnits/' | Directory to save BU base details in | +| directories.dataExtension | 'docs/dataExtension/' | Directory for `document dataExtension` output | +| directories.deltaPackage | 'docs/deltaPackage/' | Where `createDeltaPkg` stores its log | +| directories.deploy | 'deploy/' | Where `deploy` searches for files to deploy | +| directories.retrieve | 'retrieve/' | Where `retrieve` stores downloaded files | +| directories.roles | 'docs/roles/' | Directory for `document role` output | +| directories.template | 'template/' | Where `rt` stores downloaded templates & `bd` retrieves them from | +| directories.templateBuilds | ['retrieve/','deploy/'] | Where `bd` saves final deployment versions in. This can hold multiple directories, e.g. ['retrieve/','deploy/'] | +| metaDataTypes.documentOnRetrieve | ['role','dataExtension'] | automatically executes `document` for selected types | +| metaDataTypes.retrieve | _changes with each release_ | check [Metadata Type Support](#metadata-type-support) for current list | + + + +### 7.2. Metadata specific settings + + + +#### 7.2.1. Retention Policy fields in Data Extensions + +The way retention policy is saved is a bit misleading and hence we wanted to provide a bit of guidance if you ever need to do a deep dive here. + +| Field | Description | Values | +| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| **DataRetentionPeriod** | this field should print the value of the unit of measure but it unfortunately is off by one (e.g. showing "weeks" instead of "months"). Also, it seems to have no impact on what's stored.
We therefore excluded it from retrieve/deploy | - | +| **DataRetentionPeriodUnitOfMeasure** | represents drop down for "period after" selection | 6: years
5: months
4: weeks
2: days | +| **DataRetentionPeriodLength** | represents number field for "period after" selection | min: 1
max: 999 | +| **RowBasedRetention** | only true if "delete individual records" is selected, otherwise false | true / false | +| **ResetRetentionPeriodOnImport** | true if "Reset period on import" is checked. | This option is always false if "delete individual records" is selected | true / false | +| **DeleteAtEndOfRetentionPeriod** | true if "delete all records" is selected, otherwise false | true / false | +| **RetainUntil** | Normally, this should only be filled if a date, rather than a period was set.

This is empty for "delete individual records", but filled with a (calculated) date for the other 2 delete options even if "period after" was used.
Warning: trying to update a DE is denied when "period after" fields & this is provided | ""
or date in US format (m/d/Y H:m:s") "12/6/2021 12:00:00 AM") | + +To **disable retention completely**, ensure that you have the 3 booleans set to false, RetainUntil set to an empty string and no DataRetentionPeriod set (=those 2 attributes not present in file). + +To enable "delete All records and data extensions" you have to set RowBasedRetention:false and DeleteAtEndOfRetentionPeriod:false while at the same time providing a date in RetainUntil field or a DataRetentionPeriod via the 2 associated fields. + +It seems the 2 other modes were added on top later and hence "all records and data extension" is the default retention mode. + + + +## 8. Examples + + + +### 8.1. Retrieve and deploy Data Extension + +1. Retrieve metadata by running `mcdev retrieve ` (where the BU corresponds to a credential-Business Unit combo in the **.mcdevrc.json**) +2. Create a directory called `deploy/` in the root directory +3. Create a directory called `dataExtension/` in the `deploy/` directory +4. Copy a single dataExtension directory from the `retrieve///dataExtension/` directory into `deploy///dataExtension/` +5. Run `mcdev deploy ` to deploy everything in the **deploy** folder to the specified Business Unit + + + +### 8.2. Metadata Retrieving/Backup + +Metadata of a Business Unit can be retrieved by running the following command: + +`mcdev retrieve ` + +where `` needs to be replaced with `credentialName/businessUnit-Name` that is defined in **.mcdevrc.json**. + +Run this command for each of your defined Business Units and this will result in a **retrieve** directory with a sub-directory for each Business Unit. Each sub-directory contains the metadata from this Business Unit that is currently supported to **retrieve**. + +This folder structure can be commited into a git repository and used as backup. + + + +### 8.3. Automation Deployment + +Now we want to deploy an Automation with it's related metadata. Select a retrieved Automation and copy it into the deploy folder. (`deploy///automation/myAutomation.meta-automation.json`) + +Copy all related activity metadata of this automation into the deploy folder. (_Example:_ `deploy///query/myquery.meta-query.json` and `deploy///query/myquery.meta-query.sql`) + +To start the deployment run the following command: + +`mcdev deploy ` + + + + + +### 8.4. Market Configuration + +You will want to setup configs for variable parts that change inbetween Business Units. We advise starting this _after_ you've first run the `retrieveAsTemplate` command. This might sound counterintuitive but when you review what was copied into your template folder you will likely spot these variable parts the fastest and can then start setting up your market config. Please consider this an iterative process as you will likely run `rt` followed by another update of your config multiple times until you got it right. + +We advise clustering your logical approach into variable things on the **instance parent** (=`_ParentBU_` in Accenture SFMC DevTools), the **environment parent** (under which you cluster your child Business Units for DEV, QA and PROD respectively), and **child Business Units**. Ideally, the instance parent is only used to deploy _Shared Data Extensions_, the environent parent is used for integrations with external services and to separate incoming data via Automations into the respective _Shared Data Extensions_. The child Business Units are then reserved for everything that is run on a market-by-market basis. + +_Note:_ We do see it often that instance parent and environment parent are one and the same. This is depending on your client's setup, since Business Units are not for free and clients sometimes decide to save the extra money. Sometimes, you even end up with only one BU for DEV activities, no QA environment - and share the instance parent between DEV and production... This is not the recommnded approach for multiple reasons, including security, but it is the reality in some of our projects. + +Here a simple example with one DEV BU, 1 QA BU and 2 PROD BUs: + +```json +// example market config in your .mcdevrc.json +"markets": { + "DEV-NL": { + "mid": "12345", + "buName": "DEV - Child NL", + "sharedFolder": "/Shared Data Extensions/DEV/NL", + "suffix": "_DEV_NL", + "countryCodeIn": "'NL'" + }, + "QA-DE": { + "mid": "12346", + "buName": "QA - Child DE", + "sharedFolder": "/Shared Data Extensions/QA/DE", + "suffix": "_QA_DE", + "countryCodeIn": "'DE'" + }, + "PROD-DE": { + "mid": "12349", + "buName": "DE - Germany", + "sharedFolder": "/Shared Data Extensions/DE - Germany", + "suffix": "_DE", + "countryCodeIn": "'DE'" + }, + "PROD-NL": { + "mid": "12351", + "buName": "NL - Netherlands", + "sharedFolder": "/Shared Data Extensions/NL - Netherlands", + "suffix": "_NL", + "countryCodeIn": "'NL'" + } +} +``` + +Way more complex example with dedicated "Parent" BUs per enviroment (DEV, QA, PROD) and multiple country-specific BUs for QA and for PROD: + +```json +// example market config in your .mcdevrc.json +"markets": { + "DEV-Parent": { + "Account_Salesforce": "Account_Salesforce_1", + "suffix": "_DEV" + }, + "DEV-NL": { + "mid": "12345", + "buName": "DEV - Child NL", + "sharedFolder": "Shared Items/Shared Data Extensions/DEV/NL", + "suffix": "_DEV_NL", + "countryCodeIn": "'NL'" + }, + "QA-Parent": { + "Account_Salesforce": "Account_Salesforce_2", + "suffix": "_QA" + }, + "QA-DE": { + "mid": "12346", + "buName": "QA - Child DE", + "sharedFolder": "Shared Items/Shared Data Extensions/QA/DE", + "suffix": "_QA_DE", + "countryCodeIn": "'DE'" + }, + "QA-GULF": { + "mid": "12347", + "buName": "QA - Child GULF", + "sharedFolder": "Shared Items/Shared Data Extensions/QA/GULF", + "suffix": "_QA_GULF", + "countryCodeIn": "'AE', 'BH', 'IQ', 'KW', 'OM', 'QA', 'SA'" + }, + "PROD-Parent": { + "Account_Salesforce": "Account_Salesforce", + "suffix": "" + }, + "PROD-AT": { + "mid": "123458", + "buName": "AT - Austria", + "sharedFolder": "Shared Items/Shared Data Extensions/AT - Austria", + "suffix": "_AT", + "countryCodeIn": "'AT'" + }, + "PROD-DE": { + "mid": "12349", + "buName": "DE - Germany", + "sharedFolder": "Shared Items/Shared Data Extensions/DE - Germany", + "suffix": "_DE", + "countryCodeIn": "'DE'" + }, + "PROD-CH": { + "mid": "12352", + "buName": "CH - Switzerland", + "sharedFolder": "Shared Items/Shared Data Extensions/CH - Switzerland", + "suffix": "_CH", + "countryCodeIn": "'CH'" + }, + "PROD-GULF": { + "mid": "12350", + "buName": "GULF - Arab states of the Persian Gulf", + "sharedFolder": "Shared Items/Shared Data Extensions/GULF - Arab states of the Persian Gulf", + "suffix": "_IC_GULF", + "countryCodeIn": "'AE', 'BH', 'IQ', 'KW', 'OM', 'QA', 'SA'" + }, + "PROD-NL": { + "mid": "12351", + "buName": "NL - Netherlands", + "sharedFolder": "Shared Items/Shared Data Extensions/NL - Netherlands", + "suffix": "_NL", + "countryCodeIn": "'NL'" + } +} +``` + + + + + +### 8.5. Market List Configuration + +Market Lists are very powerful and you will quickly notice how much time they can safe you during your deployment preparation. +Let's first look at an example list config: + +```json +// this is a market-list-config based on the example market-config listed above! +"markets": {...}, +"marketList": { + "deployment-source": { + "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", + "MySandbox/DEV-NL": "DEV-NL" + }, + "deployment-target": { + "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", + "MySandbox/QA-DE": "QA-DE", + "MyProduction/PROD-DE": "PROD-DE", + "MyProduction/PROD-NL": "PROD-NL" + } +} +``` + +The above can be used together with `createDeltaPkg` command. + +Way more complex example: + +```json +// this is a market-list-config based on the example market-config listed above! +"markets": {...}, +"marketList": { + "Parent-shared": { + "description": "used to deploy shared data extensions", + "MySandbox/_ParentBU_": ["QA-DE", "QA-GULF"], + "MyProduction/_ParentBU_": ["PROD-AT", "PROD-CH", "PROD-DE", "PROD-GULF", "PROD-NL"] + }, + "Parent-medium": { + "description": "if you use the instance's parent BU also for imports", + "MySandbox/_ParentBU_": "QA-Parent", + "MyProduction/_ParentBU_": "PROD-Parent" + }, + "Parent-large": { + "description": "very large projects often decide against using the instance parent to get more order into the chaos and define their own 'parents' instead", + "MySandbox/QA-_Parent_": "QA-Parent", + "MyProduction/ProjectX-_Parent_": "PROD-Parent" + }, + "Parent-medium-multi": { + "description": "equal to Parent-shared" + }, + "Parent-large-multi": { + "description": "use to deploy market-adapted queries to your parent BUs", + "MySandbox/QA-_Parent_": ["QA-DE", "QA-GULF"], + "MyProduction/ProjectX-_Parent_": [ + "PROD-AT", + "PROD-CH", + "PROD-DE", + "PROD-GULF", + "PROD-NL" + ] + }, + "Children": { + "description": "use this to deploy to your market BUs", + "MySandbox/QA-DE": "QA-DE", + "MySandbox/QA-GULF": "QA-GULF", + "MyProduction/PROD-Child_AT": "PROD-AT", + "MyProduction/PROD-Child_CH": "PROD-CH", + "MyProduction/PROD-Child_DE": "PROD-DE", + "MyProduction/PROD-Child_GULF": "PROD-GULF", + "MyProduction/PROD-Child_NL": "PROD-NL" + } +} +``` + +First off, we don't see DEV in here. If you have more than one market in DEV then this might deviate but in general, you don't want to bulk-deploy to DEV as this is your single source of truth. + +Apart from that we can see 4 types of lists here: + +1. `Parent-shared` (_instance parent_): This would be used to deploy the Shared Data Extensions to the instance parent. The child-configs are listed in an array to ensure we end up with one file per child in our parent BU folder. +2. `Parent-medium`/`Parent-large` (medium:_instance parent_; large:_environment parent_): A 1:1 config that handles automations and the part of your solution that only runs on the parent. +3. `Parent-medium-multi`/`Parent-large-multi` (medium:_instance parent_; large:_environment parent_): Any scripts, queries, automations that are executed on the parent but require one per child (e.g. query to fill country-specific Shared Data Extensions) +4. `Children` (_child BUs_): everything that is needed on the market specific Business Units. + + + +## 9. Contribute + +If you want to enhance Accenture SFMC DevTools you are welcome to fork the repo and create a pull request. Please understand that we will have to conduct a code review before accepting your changes. + + + +### 9.1. Install Guide for Developers + +Instead of installing Accenture SFMC DevTools as a npm dependency from our git repo, we recommend cloning our repo and then linking it locally: + +Assuming you cloned Accenture SFMC DevTools into `C:\repos\mcdev\` (or `~/repos/MyProject/` on Mac) you can then go into your project directory, open a terminal there and run: + +_Local install:_ + +```bash +npm install --save-dev "C:\repos\mcdev" +``` + +or + +_Global install **(recommended)**:_ + +```bash +npm install -g "C:\repos\mcdev" +``` + +This should tell npm to create a symlink to your cloned local directoty, allowing you to see updates you make in your mcdev repo instantly. + +> Note: on MacOS you might need to prepend `sudo` to elevate your command. + +**Local vs. Global developer installation:** + +If you use Accenture SFMC DevTools in your team it is recommended to install your developer version **globally**, while the project's package.json should point to our Git repo in its devDependency section. Otherwise other team members would have trouble due to potentially different paths. + +If you do need to install it locally, make sure you don't commit your project's package.json with this change or you might break mcdev for other developers in your team that either didn't clone the Accenture SFMC DevTools repo or stored in a different directory. + +To test your new **global** developer setup, run `mcdev --version` in CLI which should return the current version (e.g. `3.0.0`). Then, go into your mcdev repo and update the version with the suffix `-dev`, e.g. to `3.0.0-dev` and then run `mcdev --version` again to verify that your change propagates instantly. + + + + + +### 9.2. Local install + +> **Warning:** local installation (leading to the use of [npx](https://github.com/npm/npx#readme)) causes issues when spaces are used in keys/names and is therefore not recommended. +> You will also make setting up projects much harder if you choose the local installation as you cannot use `mcdev init` to automatically setup your entire project. + +The following explains how you _could_ install it locally for certain edge cases: + +1. Create a new folder for your upcoming SFMC project. Let's assume you named it `MyProject/` + > _Note:_ It is best practice to create a separate project folder for each of your client projects to avoid accidentally overwriting the wrong BU. +2. Now, open a command line interface (CLI) for that folder. + - In Windows, you can easily do that by pressing SHIFT + Right click into that folder and then selecting the option "Open PowerShell window here". + - Alternatively, you could use any other CLI. We recommend opting for [Visual Studio Code](https://code.visualstudio.com/download)'s "Terminal" as you can benefit from this later. + - Your CLI prompt should look something like `PS C:\repos\MyProject>` on Windows or `~/repos/MyProject/` on Mac. +3. Initialize your new SFMC project by running `npm init`. + > _Note:_ npm is the "package manager" of Node.js and we use it to bundle Accenture SFMC DevTools together with other tools. + > + > If you are not familiar with node-projects or npm yet, we found this [blog post on nodesource.com](https://nodesource.com/blog/an-absolute-beginners-guide-to-using-npm/) to be helpful for beginners. + > In short, a package manager provides you with an easy name-based way of installing and updating tools you want to use as a team and ensuring that you are all using the same version. +4. Afterwards, install Accenture SFMC DevTools by running `npm install --save-dev mcdev` + - If you get an error, please see the below troubleshooting section. + +When completed you will see `+ mcdev@3.0.0` printed to your screen (or the current version of it respectively). + + + +### 9.3. NPM Scripts + +- `start`: Main entry point +- `mcdev`: alias for `start` +- `build`: Runs documentation and linting scripts +- `debug`: start debugging session +- `docs`: Generates jsdocs API documentation as markdown +- `lint`: Runs eslint with autofix and prettier +- `test`: Runs mocha tests - outdated +- `upgrade`: run npm-check to test for updated depdencies + + + +### 9.4. Developer Documentation + +- [Link to API Documentation](docs/dist/documentation.md) +- [Link to Considerations & Findings Documentation](docs/dist/considerations.md) diff --git a/boilerplate/config.json b/boilerplate/config.json new file mode 100644 index 000000000..a81e5e877 --- /dev/null +++ b/boilerplate/config.json @@ -0,0 +1,84 @@ +{ + "credentials": { + "default": { + "clientId": "--- update me ---", + "clientSecret": "--- update me ---", + "tenant": "--- update me ---", + "eid": "000000000", + "businessUnits": {} + } + }, + "options": { + "deployment": { + "commitHistory": 10, + "sourceTargetMapping": { + "deployment-source": "deployment-target" + }, + "targetBranchBuMapping": { + "release/*": "MySandbox/QA-DE", + "master": ["MyProduction/PROD-DE", "MyProduction/PROD-NL"] + } + }, + "documentType": "md", + "exclude": {}, + "include": {}, + "serverTimeOffset": -6 + }, + "directories": { + "badKeys": "docs/badKeys/", + "businessUnits": "businessUnits/", + "dataExtension": "docs/dataExtension/", + "deltaPackage": "docs/deltaPackage/", + "deploy": "deploy/", + "retrieve": "retrieve/", + "roles": "docs/roles/", + "template": "template/", + "templateBuilds": ["retrieve/", "deploy/"] + }, + "markets": { + "DEV-NL": { + "mid": "12345", + "buName": "DEV - Child NL", + "sharedFolder": "/Shared Data Extensions/DEV/NL", + "suffix": "_DEV_NL", + "countryCodeIn": "'NL'" + }, + "QA-DE": { + "mid": "12346", + "buName": "QA - Child DE", + "sharedFolder": "/Shared Data Extensions/QA/DE", + "suffix": "_QA_DE", + "countryCodeIn": "'DE'" + }, + "PROD-DE": { + "mid": "12349", + "buName": "DE - Germany", + "sharedFolder": "/Shared Data Extensions/DE - Germany", + "suffix": "_DE", + "countryCodeIn": "'DE'" + }, + "PROD-NL": { + "mid": "12351", + "buName": "NL - Netherlands", + "sharedFolder": "/Shared Data Extensions/NL - Netherlands", + "suffix": "_NL", + "countryCodeIn": "'NL'" + } + }, + "marketList": { + "deployment-source": { + "description": "Define one 1:1 BU-Market combo here to as source for automated creation of deployment packages; you can create more than one source market list", + "MySandbox/DEV-NL": "DEV-NL" + }, + "deployment-target": { + "description": "Define n BU-Market combo here to as target for automated creation of deployment packages; you can create more than one target market list and they can be as complex as you like", + "MySandbox/QA-DE": "QA-DE", + "MyProduction/PROD-DE": "PROD-DE", + "MyProduction/PROD-NL": "PROD-NL" + } + }, + "metaDataTypes": { + "documentOnRetrieve": ["role", "dataExtension"], + "retrieve": [] + } +} diff --git a/boilerplate/files/.editorconfig b/boilerplate/files/.editorconfig new file mode 100644 index 000000000..2ff010f36 --- /dev/null +++ b/boilerplate/files/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +end_of_line = lf +indent_style = space +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/boilerplate/files/.eslintignore b/boilerplate/files/.eslintignore new file mode 100644 index 000000000..8cf62d16b --- /dev/null +++ b/boilerplate/files/.eslintignore @@ -0,0 +1,5 @@ +/deploy/** +/docs/** +/logs/** +/node_modules/** +/template/** diff --git a/boilerplate/files/.eslintrc b/boilerplate/files/.eslintrc new file mode 100644 index 000000000..859cae9a3 --- /dev/null +++ b/boilerplate/files/.eslintrc @@ -0,0 +1,37 @@ +{ + "extends": ["eslint:recommended", "prettier", "ssjs"], + "plugins": ["jsdoc", "prettier"], + "rules": { + "padded-blocks": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "require-jsdoc": [ + "warn", + { + "require": { + "FunctionDeclaration": true, + "MethodDefinition": true, + "ClassDeclaration": true, + "ArrowFunctionExpression": false, + "FunctionExpression": true + } + } + ], + "valid-jsdoc": "warn", + "spaced-comment": ["warn", "always", { "block": { "exceptions": ["*"], "balanced": true } }] + }, + "overrides": [ + { + "files": ["*.js"], + "env": { "es6": true }, + "parserOptions": { + "ecmaVersion": 2018 + }, + "rules": { + "no-var": "error", + "prefer-const": "error", + "prettier/prettier": "warn" + } + } + ] +} diff --git a/boilerplate/files/.gitattributes b/boilerplate/files/.gitattributes new file mode 100644 index 000000000..8310c41d5 --- /dev/null +++ b/boilerplate/files/.gitattributes @@ -0,0 +1,5 @@ +# Set the default behavior, in case people don't have core.autocrlf set. +* text=auto eol=lf + +# Declare files that will always have LF line endings on checkout. +*.ssjs text eol=lf diff --git a/boilerplate/files/.prettierrc b/boilerplate/files/.prettierrc new file mode 100644 index 000000000..4aea7a51a --- /dev/null +++ b/boilerplate/files/.prettierrc @@ -0,0 +1,16 @@ +{ + "useTabs": false, + "tabWidth": 4, + "printWidth": 100, + "singleQuote": true, + "trailingComma": "none", + "overrides": [ + { + "files": "*.ssjs", + "options": { + "parser": "babel", + "trailingComma": "none" + } + } + ] +} diff --git a/boilerplate/files/.vscode/extensions.json b/boilerplate/files/.vscode/extensions.json new file mode 100644 index 000000000..9385501af --- /dev/null +++ b/boilerplate/files/.vscode/extensions.json @@ -0,0 +1,23 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations. + // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp + + // List of extensions which should be recommended for users of this workspace. + "recommendations": [ + // collaboration + "gruntfuggly.todo-tree", + "aaron-bond.better-comments", + + // Linters + "dbaeumer.vscode-eslint", + + // Formatting & colors + "editorconfig.editorconfig", + "esbenp.prettier-vscode", + "sergey-agadzhanov.ampscript", + "adpyke.vscode-sql-formatter", + + // Markdown / Readme.md + "joernberkefeld.markdown-preview-bitbucket-innersource" + ] +} diff --git a/boilerplate/files/.vscode/settings.json b/boilerplate/files/.vscode/settings.json new file mode 100644 index 000000000..e19668f73 --- /dev/null +++ b/boilerplate/files/.vscode/settings.json @@ -0,0 +1,31 @@ +{ + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "editor.formatOnSave": true, + "files.associations": { + "*.ssjs": "javascript" + }, + "files.eol": "\n", + "files.exclude": { + "node_modules": true + }, + "javascript.validate.enable": false, + "sql-formatter.indent": " ", + "sql-formatter.uppercase": true, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[jsonc]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[markdown]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } +} diff --git a/boilerplate/files/README.md b/boilerplate/files/README.md new file mode 100644 index 000000000..102259af3 --- /dev/null +++ b/boilerplate/files/README.md @@ -0,0 +1,9 @@ +# Salesforce Marketing Cloud - powered by Accenture Accenture SFMC DevTools + +This project relies on **Accenture SFMC DevTools**! + +- Get it, check out the official docs & get support here: https://go.accenture.com/mcdev + +--- + +© 2021 Accenture - [www.accenture.com](https://www.accenture.com/) diff --git a/boilerplate/gitignore-template b/boilerplate/gitignore-template new file mode 100644 index 000000000..ae5a16f4e --- /dev/null +++ b/boilerplate/gitignore-template @@ -0,0 +1,9 @@ +*.DS_Store +deploy/ +docs/badKeys/ +docs/deltaPackage/ +logs/ +node_modules/ +template/ +**/QueryStudioResults at* +.mcdev-auth.json diff --git a/boilerplate/npm-dependencies.json b/boilerplate/npm-dependencies.json new file mode 100644 index 000000000..1e40ed256 --- /dev/null +++ b/boilerplate/npm-dependencies.json @@ -0,0 +1,10 @@ +[ + "eslint", + "prettier", + "eslint-config-ssjs", + "eslint-config-prettier", + "eslint-plugin-prettier", + "eslint-plugin-jsdoc", + "npm-check", + "sfmc-boilerplate" +] diff --git a/img/README.md/cdp-1.jpg b/img/README.md/cdp-1.jpg new file mode 100644 index 000000000..67e49767c Binary files /dev/null and b/img/README.md/cdp-1.jpg differ diff --git a/img/README.md/troubleshoot-authentication.jpg b/img/README.md/troubleshoot-authentication.jpg new file mode 100644 index 000000000..768ca6901 Binary files /dev/null and b/img/README.md/troubleshoot-authentication.jpg differ diff --git a/img/README.md/troubleshoot-execution_policy-2.png b/img/README.md/troubleshoot-execution_policy-2.png new file mode 100644 index 000000000..d1ff8a469 Binary files /dev/null and b/img/README.md/troubleshoot-execution_policy-2.png differ diff --git a/img/README.md/troubleshoot-execution_policy.jpg b/img/README.md/troubleshoot-execution_policy.jpg new file mode 100644 index 000000000..155f1cc26 Binary files /dev/null and b/img/README.md/troubleshoot-execution_policy.jpg differ diff --git a/img/README.md/troubleshoot-mac-sudo.png b/img/README.md/troubleshoot-mac-sudo.png new file mode 100644 index 000000000..c3cfef47e Binary files /dev/null and b/img/README.md/troubleshoot-mac-sudo.png differ diff --git a/img/README.md/troubleshoot-nodejs-index.jpg b/img/README.md/troubleshoot-nodejs-index.jpg new file mode 100644 index 000000000..25610be87 Binary files /dev/null and b/img/README.md/troubleshoot-nodejs-index.jpg differ diff --git a/img/README.md/troubleshoot-nodejs-permission.jpg b/img/README.md/troubleshoot-nodejs-permission.jpg new file mode 100644 index 000000000..c9bcac914 Binary files /dev/null and b/img/README.md/troubleshoot-nodejs-permission.jpg differ diff --git a/img/README.md/troubleshoot-nodejs-postinstall.jpg b/img/README.md/troubleshoot-nodejs-postinstall.jpg new file mode 100644 index 000000000..307822ed4 Binary files /dev/null and b/img/README.md/troubleshoot-nodejs-postinstall.jpg differ diff --git a/img/README.md/troubleshoot-ssh.jpg b/img/README.md/troubleshoot-ssh.jpg new file mode 100644 index 000000000..857f8cf96 Binary files /dev/null and b/img/README.md/troubleshoot-ssh.jpg differ diff --git a/img/README.md/vscode-eslint-allow_everywhere.jpg b/img/README.md/vscode-eslint-allow_everywhere.jpg new file mode 100644 index 000000000..dc008e477 Binary files /dev/null and b/img/README.md/vscode-eslint-allow_everywhere.jpg differ diff --git a/img/README.md/vscode-recommendations-1.png b/img/README.md/vscode-recommendations-1.png new file mode 100644 index 000000000..bc0257482 Binary files /dev/null and b/img/README.md/vscode-recommendations-1.png differ diff --git a/img/README.md/vscode-recommendations-2.png b/img/README.md/vscode-recommendations-2.png new file mode 100644 index 000000000..4c5293c8e Binary files /dev/null and b/img/README.md/vscode-recommendations-2.png differ diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 000000000..6993eb8c1 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,3 @@ +{ + "include": ["lib/**/*.js"] +} diff --git a/lib/Builder.js b/lib/Builder.js new file mode 100644 index 000000000..c38208c2b --- /dev/null +++ b/lib/Builder.js @@ -0,0 +1,150 @@ +'use strict'; + +const Util = require('./util/util'); + +const MetadataTypeInfo = require('./MetadataTypeInfo'); +// @ts-ignore + +/** + * Builds metadata from a template using market specific customisation + */ +class Builder { + /** + * Creates a Builder, uses v2 auth if v2AuthOptions are passed. + * + * @param {Object} properties properties for auth + * @param {String} properties.clientId clientId for FuelSDK auth + * @param {String} properties.clientSecret clientSecret for FuelSDK auth + * @param {Object} properties.directories list of default directories + * @param {String} properties.directories.template where templates are saved + * @param {String} properties.directories.templateBuilds where template-based deployment definitions are saved + * @param {String} properties.tenant v2 Auth Tenant Information + * @param {String} properties.businessUnits ID of Business Unit to authenticate with + * @param {Object} buObject properties for auth + * @param {String} buObject.clientId clientId for FuelSDK auth + * @param {String} buObject.clientSecret clientSecret for FuelSDK auth + * @param {Object} buObject.credential clientId for FuelSDK auth + * @param {String} buObject.tenant v2 Auth Tenant Information + * @param {String} buObject.mid ID of Business Unit to authenticate with + * @param {String} buObject.businessUnit name of Business Unit to authenticate with + * @param {Util.ET_Client} client fuel client + */ + constructor(properties, buObject, client) { + this.client = client; + this.properties = properties; + this.templateDir = properties.directories.template; + + // allow multiple target directories + const templateBuildsArr = Array.isArray(properties.directories.templateBuilds) + ? properties.directories.templateBuilds + : [properties.directories.templateBuilds]; + + this.targetDir = templateBuildsArr.map( + (directoriesTemplateBuilds) => + directoriesTemplateBuilds + buObject.credential + '/' + buObject.businessUnit + ); + + this.metadata = {}; + } + + /** + * Builds a specific metadata file by name + * @param {String} metadataType metadata type to build + * @param {String} name name of metadata to build + * @param {Object} variables variables to be replaced in the metadata + * @returns {Promise} Promise + */ + async buildDefinition(metadataType, name, variables) { + let nameArr; + if (name.includes(',')) { + nameArr = name.split(',').map((item) => + // allow whitespace in comma-separated lists + item.trim() + ); + } else { + nameArr = [name.trim()]; + } + const type = metadataType; + try { + const result = await Promise.all( + nameArr.map((name) => { + // with npx and powershell spaces are not parsed correctly as part of a string + // we hence require users to put %20 in their stead and have to convert that back + name = name.split('%20').join(' '); + + MetadataTypeInfo[type].cache = null; + MetadataTypeInfo[type].client = this.client; + MetadataTypeInfo[type].properties = this.properties; + return MetadataTypeInfo[type].buildDefinition( + this.templateDir, + this.targetDir, + name, + variables + ); + }) + ); + if (result) { + this.metadata[result[0].type] = []; + result.forEach((element) => { + this.metadata[result[0].type].push(element.metadata); + }); + } + } catch (ex) { + Util.logger.error('mcdev.buildDefinition:' + ex.message); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + } + return this.metadata; + } + /** + * ensure provided MarketList exists and it's content including markets and BUs checks out + * @param {String} mlName name of marketList + * @param {Object} properties General configuration to be used in retrieve + * @param {Object} properties.markets list of template variable combos + * @param {Object} properties.marketList list of bu-market combos + * @param {Object} properties.credentials list of credentials and their BUs + * @returns {void} throws errors if problems were found + */ + static verifyMarketList(mlName, properties) { + if (!properties.marketList[mlName]) { + // ML does not exist + throw new Error(`Market List ${mlName} is not defined`); + } else { + // ML exists, check if it is properly set up + + // check if BUs in marketList are valid + let buCounter = 0; + for (const businessUnit in properties.marketList[mlName]) { + if (businessUnit !== 'description') { + buCounter++; + const [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + if ( + !properties.credentials[cred] || + !properties.credentials[cred].businessUnits[bu] + ) { + throw new Error(`'${businessUnit}' in Market ${mlName} is not defined.`); + } + // check if markets are valid + let marketArr = properties.marketList[mlName][businessUnit]; + if ('string' === typeof marketArr) { + marketArr = [marketArr]; + } + for (const market of marketArr) { + if (!properties.markets[market]) { + throw new Error(`Market '${market}' is not defined.`); + } else { + // * markets can be empty or include variables. Nothing we can test here + } + } + } + } + if (!buCounter) { + throw new Error(`No BUs defined in marketList ${mlName}`); + } + } + } +} + +module.exports = Builder; diff --git a/lib/Deployer.js b/lib/Deployer.js new file mode 100644 index 000000000..6de98d35f --- /dev/null +++ b/lib/Deployer.js @@ -0,0 +1,253 @@ +'use strict'; + +const MetadataTypeInfo = require('./MetadataTypeInfo'); +const path = require('path'); +const Util = require('./util/util'); +const File = require('./util/file'); + +/** + * Reads metadata from local directory and deploys it to specified target business unit. + * Source and target business units are also compared before the deployment to apply metadata specific patches. + */ +class Deployer { + /** + * Creates a Deployer, uses v2 auth if v2AuthOptions are passed. + * + * + * @param {Object} properties General configuration to be used in retrieve + * @param {Object} properties.directories Directories to be used when interacting with FS + * @param {Object} buObject properties for auth + * @param {String} buObject.clientId clientId for FuelSDK auth + * @param {String} buObject.clientSecret clientSecret for FuelSDK auth + * @param {Object} buObject.credential clientId for FuelSDK auth + * @param {String} buObject.tenant v2 Auth Tenant Information + * @param {String} buObject.mid ID of Business Unit to authenticate with + * @param {String} buObject.businessUnit name of Business Unit to authenticate with + * @param {Util.ET_Client} client fuel client + * @param {String} [type] limit deployment to given metadata type + */ + constructor(properties, buObject, client, type) { + this.buObject = buObject; + this.client = client; + this.properties = properties; + this.deployDir = File.normalizePath([ + properties.directories.deploy, + buObject.credential, + buObject.businessUnit, + ]); + this.retrieveDir = File.normalizePath([ + properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + ]); + // prep folders for auto-creation + MetadataTypeInfo.folder.cache = this.cache; + MetadataTypeInfo.folder.client = this.client; + MetadataTypeInfo.folder.properties = this.properties; + // Remove tmp folder of previous deploys + File.removeSync('tmp'); + if (File.existsSync(this.deployDir)) { + this.metadata = Deployer.readBUMetadata(this.deployDir, type); + } else { + this.metadata = null; + Util.logger.warn( + 'Deployer.constructor:: Please create a directory called deploy and include your metadata in it: ./' + + this.deployDir + ); + } + this.cache = {}; + } + /** + * Deploy all metadata that is located in the deployDir + * @returns {Promise} Promise + */ + async deploy() { + if (this.metadata === null || !Object.keys(this.metadata).length) { + Util.logger.warn('No metadata found in deploy folder for selected BU'); + return null; + } + await Deployer.createFolderDefinitions( + this.deployDir, + this.metadata, + Object.keys(this.metadata) + ); + + const deployOrder = Util.getMetadataHierachy(Object.keys(this.metadata)); + // build cache, including all metadata types which will be deployed (Avoids retrieve later) + for (const metadataType of deployOrder) { + const [type, subType] = metadataType.split('-'); + // add metadata & client to metadata process class instead of passing cache/mapping every time + MetadataTypeInfo[type].cache = this.cache; + MetadataTypeInfo[type].client = this.client; + MetadataTypeInfo[type].properties = this.properties; + Util.logger.info('Caching dependent Metadata: ' + metadataType); + const result = await MetadataTypeInfo[type].retrieveForCache(this.buObject, subType); + this.cache[type] = result.metadata; + } + // deploy metadata files, extending cache once deploys + for (const metadataType of deployOrder) { + let result; + // TODO rewrite to allow deploying only a specific sub-type + // const [type, subType] = metadataType.split('-'); + const type = metadataType.split('-')[0]; + if (this.metadata[type]) { + MetadataTypeInfo[type].cache = this.cache; + Util.logger.info('Deploying: ' + metadataType); + await Util.retryOnError(`Retrying ${metadataType}`, async () => { + result = await MetadataTypeInfo[type].deploy( + this.metadata[type], + this.deployDir, + this.retrieveDir, + this.buObject + ); + }); + this.cache[type] = Object.assign(this.cache[type], result); + this.deployCallback(result, type); + } + } + } + + /** + * Gets called for every deployed metadata entry + * @param {Object} result Deployment result + * @param {String} metadataType Name of metadata type + * @returns {void} + */ + deployCallback(result, metadataType) { + if (result) { + File.writeJSONToFile('logs/', 'deployResult_' + metadataType, result); + Util.logger.verbose( + 'Deployer.deployCallback:: Results written to: ' + + path.normalize(process.cwd() + '/logs/deployResult_' + metadataType + '.json') + ); + } + } + + /** + * Returns metadata of a business unit that is saved locally + * @param {String} deployDir root directory of metadata. + * @param {String} [type] limit deployment to given metadata type + * @param {boolean} [listBadKeys=false] do not print errors, used for badKeys() + * @returns {Object} Metadata of BU in local directory + */ + static readBUMetadata(deployDir, type, listBadKeys) { + const buMetadata = {}; + try { + if (File.existsSync(deployDir)) { + const metadataTypes = File.readdirSync(deployDir); + metadataTypes.forEach((metadataType) => { + if (MetadataTypeInfo[metadataType] && (!type || type === metadataType)) { + // check if folder name is a valid metadataType, then check if the user limited to a certain type in the command params + buMetadata[metadataType] = MetadataTypeInfo[metadataType].getJsonFromFS( + File.normalizePath([deployDir, metadataType]), + listBadKeys + ); + } + }); + + return buMetadata; + } else { + throw new Error(`Directory '${deployDir}' does not exist.`); + } + } catch (ex) { + throw new Error(ex.message); + } + } + + /** + * parses asset metadata to auto-create folders in target folder + * @param {String} deployDir root directory of metadata. + * @param {Object} metadata list of metadata + * @param {String} metadataTypeArr list of metadata types + * @returns {void} + */ + static async createFolderDefinitions(deployDir, metadata, metadataTypeArr) { + let i = 0; + const folderMetadata = {}; + metadataTypeArr.forEach((metadataType) => { + if (!MetadataTypeInfo[metadataType].definition.dependencies.includes('folder')) { + Util.logger.debug( + `_createFolderDefinitions(${metadataType}) - folder not a dependency` + ); + return; + } + if (!MetadataTypeInfo[metadataType].definition.folderType) { + Util.logger.debug(`_createFolderDefinitions(${metadataType}) - folderType not set`); + return; + } + if ( + !MetadataTypeInfo.folder.definition.deployFolderTypes.includes( + MetadataTypeInfo[metadataType].definition.folderType + ) + ) { + Util.logger.warn( + `_createFolderDefinitions(${metadataType}: ${MetadataTypeInfo[metadataType].definition.folderType}) - folderType not supported for deployment` + ); + return; + } + Util.logger.debug(`Creating relevant folders for ${metadataType} in deploy dir`); + + let allFolders = Object.keys(metadata[metadataType]).filter( + // filter out root folders (which would not have a slash in their path) + (key) => metadata[metadataType][key].r__folder_Path.includes('/') + ); + if (metadataType === 'dataExtension') { + allFolders = allFolders + .filter( + // filter out any shared / synchronized / salesforce folders + (key) => + metadata[metadataType][key].r__folder_ContentType === 'dataextension' + ) + .map((key) => metadata[metadataType][key].r__folder_Path); + } else { + allFolders = allFolders.map((key) => metadata[metadataType][key].r__folder_Path); + } + // deduplicate + const folderPathSet = new Set(allFolders); + [...folderPathSet].sort().forEach((item) => { + let aggregatedPath = ''; + const parts = item.split('/'); + parts.forEach((pathElement) => { + if (aggregatedPath) { + aggregatedPath += '/'; + } + aggregatedPath += pathElement; + folderPathSet.add(aggregatedPath); + }); + }); + const folderPathArrExtended = [...folderPathSet] + // strip root folders + .filter((folderName) => folderName.includes('/')) + .sort(); + + folderPathArrExtended.forEach((folder) => { + i++; + folderMetadata[`on-the-fly-${i}`] = { + Path: folder, + Name: folder.split('/').pop(), + Description: '', + ContentType: MetadataTypeInfo[metadataType].definition.folderType, + IsActive: 'true', + IsEditable: 'true', + AllowChildren: 'true', + }; + }); + }); + + if (i > 0) { + Util.logger.info(`Saving folders to deploy dir (${i}) - please wait`); + + // await results to allow us to re-read it right after + await MetadataTypeInfo.folder.saveResults(folderMetadata, deployDir, null); + Util.logger.info(`Saved: folders in deploy dir`); + + // reload from file system to ensure we use the same logic for building the temporary JSON + metadata.folder = MetadataTypeInfo.folder.getJsonFromFS( + File.normalizePath([deployDir, 'folder']) + ); + } + return folderMetadata; + } +} + +module.exports = Deployer; diff --git a/lib/MetadataTypeDefinitions.js b/lib/MetadataTypeDefinitions.js new file mode 100644 index 000000000..d6b931e75 --- /dev/null +++ b/lib/MetadataTypeDefinitions.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * Provides access to all metadataType classes + */ +const MetadataTypeDefinitions = { + asset: require('./metadataTypes/definitions/Asset.definition'), + attributeGroup: require('./metadataTypes/definitions/AttributeGroup.definition'), + automation: require('./metadataTypes/definitions/Automation.definition'), + campaign: require('./metadataTypes/definitions/Campaign.definition'), + contentArea: require('./metadataTypes/definitions/ContentArea.definition'), + dataExtension: require('./metadataTypes/definitions/DataExtension.definition'), + dataExtensionField: require('./metadataTypes/definitions/DataExtensionField.definition'), + dataExtensionTemplate: require('./metadataTypes/definitions/DataExtensionTemplate.definition'), + dataExtract: require('./metadataTypes/definitions/DataExtract.definition'), + dataExtractType: require('./metadataTypes/definitions/DataExtractType.definition'), + discovery: require('./metadataTypes/definitions/Discovery.definition'), + email: require('./metadataTypes/definitions/Email.definition'), + emailSendDefinition: require('./metadataTypes/definitions/EmailSendDefinition.definition'), + eventDefinition: require('./metadataTypes/definitions/EventDefinition.definition'), + fileTransfer: require('./metadataTypes/definitions/FileTransfer.definition'), + filter: require('./metadataTypes/definitions/Filter.definition'), + folder: require('./metadataTypes/definitions/Folder.definition'), + ftpLocation: require('./metadataTypes/definitions/FtpLocation.definition'), + importFile: require('./metadataTypes/definitions/ImportFile.definition'), + interaction: require('./metadataTypes/definitions/Interaction.definition'), + list: require('./metadataTypes/definitions/List.definition'), + query: require('./metadataTypes/definitions/Query.definition'), + role: require('./metadataTypes/definitions/Role.definition'), + script: require('./metadataTypes/definitions/Script.definition'), + setDefinition: require('./metadataTypes/definitions/SetDefinition.definition'), + triggeredSendDefinition: require('./metadataTypes/definitions/TriggeredSendDefinition.definition'), +}; + +module.exports = MetadataTypeDefinitions; diff --git a/lib/MetadataTypeInfo.js b/lib/MetadataTypeInfo.js new file mode 100644 index 000000000..594af4d4e --- /dev/null +++ b/lib/MetadataTypeInfo.js @@ -0,0 +1,35 @@ +'use strict'; + +/** + * Provides access to all metadataType classes + */ +const MetadataTypeInfo = { + asset: require('./metadataTypes/Asset'), + attributeGroup: require('./metadataTypes/AttributeGroup'), + automation: require('./metadataTypes/Automation'), + campaign: require('./metadataTypes/Campaign'), + contentArea: require('./metadataTypes/ContentArea'), + dataExtension: require('./metadataTypes/DataExtension'), + dataExtensionField: require('./metadataTypes/DataExtensionField'), + dataExtensionTemplate: require('./metadataTypes/DataExtensionTemplate'), + dataExtract: require('./metadataTypes/DataExtract'), + dataExtractType: require('./metadataTypes/DataExtractType'), + discovery: require('./metadataTypes/Discovery'), + email: require('./metadataTypes/Email'), + emailSendDefinition: require('./metadataTypes/EmailSendDefinition'), + eventDefinition: require('./metadataTypes/EventDefinition'), + fileTransfer: require('./metadataTypes/FileTransfer'), + filter: require('./metadataTypes/Filter'), + folder: require('./metadataTypes/Folder'), + ftpLocation: require('./metadataTypes/FtpLocation'), + importFile: require('./metadataTypes/ImportFile'), + interaction: require('./metadataTypes/Interaction'), + list: require('./metadataTypes/List'), + query: require('./metadataTypes/Query'), + role: require('./metadataTypes/Role'), + script: require('./metadataTypes/Script'), + setDefinition: require('./metadataTypes/SetDefinition'), + triggeredSendDefinition: require('./metadataTypes/TriggeredSendDefinition'), +}; + +module.exports = MetadataTypeInfo; diff --git a/lib/Retriever.js b/lib/Retriever.js new file mode 100644 index 000000000..91874a595 --- /dev/null +++ b/lib/Retriever.js @@ -0,0 +1,129 @@ +'use strict'; + +const MetadataTypeInfo = require('./MetadataTypeInfo'); +const Util = require('./util/util'); +const File = require('./util/file'); +/** + * Retrieves metadata from a business unit and saves it to the local filesystem. + */ +class Retriever { + /** + * Creates a Retriever, uses v2 auth if v2AuthOptions are passed. + * + * @param {Object} properties General configuration to be used in retrieve + * @param {Object} properties.directories Directories to be used when interacting with FS + * @param {Object} buObject properties for auth + * @param {String} buObject.clientId clientId for FuelSDK auth + * @param {String} buObject.clientSecret clientSecret for FuelSDK auth + * @param {Object} buObject.credential clientId for FuelSDK auth + * @param {String} buObject.tenant v2 Auth Tenant Information + * @param {String} [buObject.mid] ID of Business Unit to authenticate with + * @param {String} [buObject.businessUnit] name of Business Unit to authenticate with + * @param {Util.ET_Client} client fuel client + */ + constructor(properties, buObject, client) { + this.buObject = buObject; + this.client = client; + this.properties = properties; + this.retrieveDir = properties.directories.retrieve; + this.templateDir = properties.directories.template; + this.savePath = File.normalizePath([ + properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + ]); + + this.metadata = {}; + } + + /** + * Retrieve metadata of specified types into local file system and Retriever.metadata + * @param {String[]} metadataTypes String list of metadata types to retrieve + * @param {String} [name] name of Metadata to retrieve (in case of templating) + * @param {Object} [templateVariables] Object of values which can be replaced (in case of templating) + * @returns {Promise} Promise + */ + async retrieve(metadataTypes, name, templateVariables) { + for (const metadataType of Util.getMetadataHierachy(metadataTypes)) { + let result; + const [type, subType] = metadataType.split('-'); + // add metadata & client to metadata process class instead of passing cache/mapping every time + MetadataTypeInfo[type].cache = this.metadata; + MetadataTypeInfo[type].client = this.client; + MetadataTypeInfo[type].properties = this.properties; + + try { + if (!metadataTypes.includes(type)) { + Util.logger.info(`Caching dependent Metadata: ${metadataType}`); + await Util.retryOnError( + `Retrying to cache ${metadataType}`, + async () => { + result = await MetadataTypeInfo[type].retrieveForCache( + this.buObject, + subType + ); + if (type == 'list') { + Util.logger.debug('==list=='); + Util.logger.debug(JSON.stringify(result)); + } + }, + true + ); + } else if (templateVariables) { + Util.logger.info(`Retrieving as Template: ${metadataType}`); + let nameArr; + if (name.includes(',')) { + nameArr = name.split(',').map((item) => + // allow whitespace in comma-separated lists + item.trim() + ); + } else { + nameArr = [name.trim()]; + } + await Util.retryOnError(`Retrying ${metadataType}`, async () => { + result = await Promise.all( + nameArr.map((name) => { + // with npx and powershell spaces are not parsed correctly as part of a string + // we hence require users to put %20 in their stead and have to convert that back + name = name.split('%20').join(' '); + + return MetadataTypeInfo[type].retrieveAsTemplate( + this.templateDir, + name, + templateVariables, + subType + ); + }) + ); + }); + } else { + Util.logger.info('Retrieving: ' + metadataType); + await Util.retryOnError(`Retrying ${metadataType}`, async () => { + result = await MetadataTypeInfo[type].retrieve( + this.savePath, + null, + this.buObject, + subType + ); + }); + } + if (result) { + if (templateVariables && Array.isArray(result)) { + // so far we are only doing this for templates, hence the above if-check + this.metadata[type] = result.map((element) => element.metadata); + } else { + this.metadata[type] = result.metadata; + } + } + } catch (ex) { + Util.logger.error(`Retriever.retrieve:: Retrieving ${metadataType} failed`); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + } + } + } +} + +module.exports = Retriever; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 000000000..a493b0831 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,1133 @@ +#!/usr/bin/env node +'use strict'; + +const Util = require('./util/util'); +const File = require('./util/file'); +const Init = require('./util/init'); +const InitGit = require('./util/init.git'); +const Cli = require('./util/cli'); +const DevOps = require('./util/devops'); +const BuHelper = require('./util/businessUnit'); +const Builder = require('./Builder'); +const Deployer = require('./Deployer'); +const MetadataTypeInfo = require('./MetadataTypeInfo'); +const MetadataTypeDefinitions = require('./MetadataTypeDefinitions'); +const Retriever = require('./Retriever'); +const yargs = require('yargs'); +const inquirer = require('inquirer'); +let properties; + +// CLI framework +yargs + .scriptName('mcdev') + .usage('$0 [options]') + .command({ + command: 'retrieve [BU] [TYPE]', + aliases: ['r'], + desc: 'retrieves metadata of a business unit', + // @ts-ignore + builder: (yargs) => { + yargs + .positional('BU', { + type: 'string', + describe: + 'the business unit to retrieve from (in format "credential name/BU name")', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type that shall be exclusively downloaded', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + retrieve(argv.BU, argv.TYPE); + }, + }) + .command({ + command: 'deploy [BU] [TYPE]', + aliases: ['d'], + desc: 'deploys local metadata to a business unit', + builder: (yargs) => { + yargs + .positional('BU', { + type: 'string', + describe: + 'the business unit to deploy to (in format "credential name/BU name")', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type that shall be exclusively uploaded', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + deploy(argv.BU, argv.TYPE); + }, + }) + .command({ + command: 'init [credentialsName]', + desc: `creates '${Util.configFileName}' in your root or adds additional credentials to the existing one`, + builder: (yargs) => { + yargs.positional('credentialsName', { + type: 'string', + describe: 'name of your installed package', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + initProject(argv.credentialsName, argv.skipInteraction); + }, + }) + .command({ + command: 'reloadBUs [credentialsName]', + aliases: ['rb'], + desc: 'loads the list of available BUs from the server and saves it to your config', + builder: (yargs) => { + yargs.positional('credentialsName', { + type: 'string', + describe: 'name of your installed package', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + findBUs(argv.credentialsName); + }, + }) + .command({ + command: 'badKeys [BU]', + desc: 'lists metadata with random API names in specified Business Unit directory', + builder: (yargs) => { + yargs.positional('BU', { + type: 'string', + describe: 'the business unit to deploy to', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + badKeys(argv.BU); + }, + }) + .command({ + command: 'document [BU]', + aliases: ['doc'], + desc: 'Creates Markdown or HTML documentation for the selected type', + builder: (yargs) => { + yargs + .positional('TYPE', { + type: 'string', + describe: + 'metadata type to generate docs for; currently supported: dataExtension, role', + }) + .positional('BU', { + type: 'string', + describe: + 'the business unit to generate docs for (in format "credential name/BU name")', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + document(argv.BU, argv.TYPE); + }, + }) + .command({ + command: 'delete ', + aliases: ['del'], + desc: 'deletes metadata of selected type and external key', + builder: (yargs) => { + yargs + .positional('BU', { + type: 'string', + describe: + 'the business unit to delete from (in format "credential name/BU name")', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type to delete from; currently supported: dataExtension', + }) + .positional('EXTERNALKEY', { + type: 'string', + describe: 'the key to delete', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + deleteByKey(argv.BU, argv.TYPE, argv.EXTERNALKEY); + }, + }) + .command({ + command: 'retrieveAsTemplate ', + aliases: ['rt'], + desc: 'Retrieves a specific metadata file by name for templating', + builder: (yargs) => { + yargs + .positional('BU', { + type: 'string', + describe: + 'the business unit to deploy to (in format "credential name/BU name")', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type', + }) + .positional('NAME', { + type: 'string', + describe: 'name of the metadata component', + }) + .positional('MARKET', { + type: 'string', + describe: 'market used for reverse building template', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + retrieveAsTemplate(argv.BU, argv.TYPE, argv.NAME, argv.MARKET); + }, + }) + .command({ + command: 'buildDefinition ', + aliases: ['bd'], + desc: 'builds metadata definition based on template', + builder: (yargs) => { + yargs + .positional('BU', { + type: 'string', + describe: 'the business unit to deploy to', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type', + }) + .positional('NAME', { + type: 'string', + describe: 'name of the metadata component', + }) + .positional('MARKET', { + type: 'string', + describe: 'the business unit to deploy to', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + buildDefinition(argv.BU, argv.TYPE, argv.NAME, argv.MARKET); + }, + }) + .command({ + command: 'buildDefinitionBulk ', + aliases: ['bdb'], + desc: 'builds metadata definition based on template en bulk', + builder: (yargs) => { + yargs + .positional('LISTNAME', { + type: 'string', + describe: 'name of list of BU-market combos', + }) + .positional('TYPE', { + type: 'string', + describe: 'metadata type', + }) + .positional('NAME', { + type: 'string', + describe: 'name of the metadata component', + }); + }, + handler: (argv) => { + _setLoggingLevel(argv); + buildDefinitionBulk(argv.LISTNAME, argv.TYPE, argv.NAME); + }, + }) + .command({ + command: 'selectTypes', + aliases: ['st'], + desc: 'lets you choose what metadata types to retrieve', + handler: (argv) => { + _setLoggingLevel(argv); + selectTypes(); + }, + }) + .command({ + command: 'explainTypes', + aliases: ['et'], + desc: 'explains metadata types that can be retrieved', + handler: (argv) => { + _setLoggingLevel(argv); + explainTypes(); + }, + }) + .command({ + command: 'createDeltaPkg [range] [filter]', + aliases: ['cdp'], + desc: 'Copies commit-based file delta into deploy folder', + builder: (yargs) => { + yargs + .positional('range', { + type: 'string', + describe: 'Pull Request target branch or git commit range', + }) + .positional('filter', { + type: 'string', + describe: + 'Disable templating & instead filter by the specified file path (comma separated)', + }); + }, + handler: createDeltaPkg, + }) + .command({ + command: 'upgrade', + aliases: ['up'], + desc: 'Add NPM dependencies and IDE configuration files to your project', + handler: (argv) => { + _setLoggingLevel(argv); + upgrade(argv.skipInteraction); + }, + }) + .option('verbose', { + type: 'boolean', + description: 'Run with verbose CLI output', + }) + .option('debug', { + type: 'boolean', + description: 'Enable developer & edge-case features', + }) + .option('silent', { + type: 'boolean', + description: 'Only output errors to CLI', + }) + .option('skipInteraction', { + alias: ['yes', 'y'], + description: 'Interactive questions where possible and go with defaults instead', + }) + .demandCommand(1, 'Please enter a valid command') + .strict() + .recommendCommands() + .wrap(yargs.terminalWidth()) + .epilog('Copyright 2021. Accenture.') + .help().argv; + +/** + * handler for 'mcdev createDeltaPkg + * @param {Object} argv yargs parameters + * @param {String} [argv.range] git commit range +into deploy directory + * @param {String} [argv.filter] filter file paths that start with any + * @param {Boolean} [argv.skipInteraction] allows to skip interactive wizard + * @returns {void} + */ +async function createDeltaPkg(argv) { + _setLoggingLevel(argv); + properties = properties || File.loadConfigFile(); + if (!Util.checkProperties(properties)) { + return null; + } + // get source market and source BU from config + if (argv.filter) { + return DevOps.createDeltaPkg(properties, argv.range, true, argv.filter); + } else { + // If no custom filter was provided, use deployment marketLists & templating + + // check if sourceTargetMapping is valid + if ( + !properties.options.deployment.sourceTargetMapping || + !Object.keys(properties.options.deployment.sourceTargetMapping).length + ) { + Util.logger.error('Bad configuration of options.deployment.sourceTargetMapping'); + return; + } + const sourceMarketListArr = Object.keys(properties.options.deployment.sourceTargetMapping); + + for (const sourceML of sourceMarketListArr) { + // check if sourceTargetMapping has valid values + // #1 check source marketlist + try { + Builder.verifyMarketList(sourceML, properties); + // remove potentially existing "description"-entry + delete properties.marketList[sourceML].description; + + const sourceMarketBuArr = Object.keys(properties.marketList[sourceML]); + if (sourceMarketBuArr.length !== 1) { + throw new Error('Only 1 BU is allowed per source marketList'); + } + if ('string' !== typeof properties.marketList[sourceML][sourceMarketBuArr[0]]) { + throw new Error('Only 1 market per BU is allowed per source marketList'); + } + } catch (ex) { + Util.logger.error('Deployment Source: ' + ex.message); + return; + } + // #2 check corresponding target marketList + let targetML; + try { + targetML = properties.options.deployment.sourceTargetMapping[sourceML]; + if ('string' !== typeof targetML) { + throw new Error( + 'Please define one target marketList per source in deployment.sourceTargetMapping (No arrays allowed)' + ); + } + Builder.verifyMarketList(targetML, properties); + // remove potentially existing "description"-entry + delete properties.marketList[targetML].description; + } catch (ex) { + Util.logger.error('Deployment Target: ' + ex.message); + } + } + // all good let's loop a second time for actual execution + for (const sourceMlName of sourceMarketListArr) { + const targetMlName = properties.options.deployment.sourceTargetMapping[sourceMlName]; + const sourceBU = Object.keys(properties.marketList[sourceMlName])[0]; + const sourceMarket = Object.values(properties.marketList[sourceMlName])[0]; + + const delta = await DevOps.createDeltaPkg(properties, argv.range, false, sourceBU); + // If only chaing templating and buildDefinition if required + if (!delta || delta.length === 0) { + // info/error messages was printed by DevOps.createDeltaPkg() already + return; + } + Util.logger.info('============='); + + // Put files into maps. One map with BU -> type -> file (for retrieveAsTemplate) + // Other map only with type -> file (for buildDefinitionBulk) + const buTypeDelta = {}; + const typeDelta = {}; + delta + // Only template/build files that were added/updated/moved. no deletions + // ! doesn't work for folder, because their name parsing doesnt work at the moment + .filter((file) => file.gitAction !== 'delete' && file.name) + .forEach((file) => { + const buPath = `${file._credential}/${file._businessUnit}`; + if (!buTypeDelta[buPath]) { + buTypeDelta[buPath] = {}; + } + if (!buTypeDelta[buPath][file.type]) { + buTypeDelta[buPath][file.type] = []; + } + buTypeDelta[buPath][file.type].push(file.name); + + if (!typeDelta[file.type]) { + typeDelta[file.type] = []; + } + typeDelta[file.type].push(file.name); + }); + + // Run retrieve as template for each business unit for each type + Util.logger.info('Retrieve template from Git delta'); + // ! needs to be for (.. in ..) loop so that it gets executed in series + for (const bu in buTypeDelta) { + for (const type in buTypeDelta[bu]) { + Util.logger.info( + `⚡ mcdev rt ${bu} ${type} "${buTypeDelta[bu][type].join( + ',' + )}" ${sourceMarket}` + ); + await retrieveAsTemplate( + bu, + type, + buTypeDelta[bu][type].join(','), + sourceMarket + ); + } + } + + // Run build definitions bulk for each type + Util.logger.info(`- ✔️ Templates created`); + Util.logger.info('============='); + Util.logger.info('Build definitions from delta templates'); + if ( + properties.directories.templateBuilds == properties.directories.deploy || + (Array.isArray(properties.directories.templateBuilds) && + properties.directories.templateBuilds.includes(properties.directories.deploy)) + ) { + let responses; + if (!argv.skipInteraction) { + // deploy folder is in targets for definition creation + // recommend to purge their content first + const questions = [ + { + type: 'confirm', + name: 'isPurgeDeployFolder', + message: + 'Do you want to empty the deploy folder (ensures no files from previous deployments remain)?', + default: true, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + if (argv.skipInteraction || responses.isPurgeDeployFolders) { + // Clear output folder structure for selected sub-type + File.removeSync(File.normalizePath([properties.directories.deploy])); + } + } + const bdPromises = []; + for (const type in typeDelta) { + Util.logger.info( + `⚡ mcdev bdb ${targetMlName} ${type} "${typeDelta[type].join(',')}"` + ); + // omitting "await" to speed up creation + bdPromises.push(buildDefinitionBulk(targetMlName, type, typeDelta[type].join(','))); + } + await Promise.all(bdPromises); + Util.logger.info(`- ✔️ Deploy defintions created`); + if ( + properties.directories.templateBuilds == properties.directories.deploy || + (Array.isArray(properties.directories.templateBuilds) && + properties.directories.templateBuilds.includes(properties.directories.deploy)) + ) { + Util.logger.info(`You can now run deploy on the prepared BUs`); + } else { + Util.logger.info( + `Your templated defintions are now ready to be copied into the deploy folder. Hint: You can have this auto-copied if you adjust directories.templateBuilds in your config.` + ); + } + } + } +} + +/** + * configures what is displayed in the console + * @param {object} argv list of command line parameters given by user + * @param {Boolean} [argv.silent] only errors printed to CLI + * @param {Boolean} [argv.verbose] chatty user CLI output + * @param {Boolean} [argv.debug] enables developer output & features + * @returns {void} + */ +function _setLoggingLevel(argv) { + if (argv.silent) { + // only errors printed to CLI + Util.logger.level = 'error'; + Util.loggerTransports.console.level = 'error'; + } else if (argv.verbose) { + // chatty user cli logs + Util.logger.level = 'verbose'; + Util.loggerTransports.console.level = 'verbose'; + } else { + // default user cli logs + // TODO to be switched to "warn" when cli-process is integrated + Util.logger.level = 'info'; + Util.loggerTransports.console.level = 'info'; + } + if (argv.debug) { + // enables developer output & features. no change to actual logs + Util.logger.level = 'debug'; + } +} + +/** + * @returns {Promise} . + */ +async function selectTypes() { + properties = properties || File.loadConfigFile(); + if (!Util.checkProperties(properties)) { + return null; + } + await Cli.selectTypes(properties); +} +/** + * @returns {Promise} . + */ +function explainTypes() { + Cli.explainTypes(); +} +/** + * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @returns {Promise} . + */ +async function upgrade(skipInteraction) { + properties = properties || File.loadConfigFile(); + if (!properties) { + Util.logger.error('No config found. Please run mcdev init'); + return; + } + if ((await InitGit.initGitRepo(skipInteraction)).status === 'error') { + return; + } + + Init.upgradeProject(properties, false); +} + +/** + * Retrieve all metadata from the specified business unit into the local file system. + * @param {String} businessUnit references credentials from properties.json + * @param {String} [selectedType] limit retrieval to given metadata type + * @returns {Promise} - + */ +async function retrieve(businessUnit, selectedType) { + Util.logger.info('mcdev:: Retrieve'); + properties = properties || File.loadConfigFile(); + if (!Util.checkProperties(properties)) { + // return null here to avoid seeing 2 error messages for the same issue + return null; + } + const [type, subType] = selectedType ? selectedType.split('-') : []; + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } else if ( + type && + subType && + (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType)) + ) { + Util.logger.error(`:: '${selectedType}' is not a valid metadata type`); + return; + } + + if (businessUnit === '*') { + Util.logger.info('\n:: Retrieving all BUs for all credentials'); + let counter_credTotal = 0; + for (const cred in properties.credentials) { + Util.logger.info(`\n:: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await _retrieveBU(cred, bu, selectedType); + counter_credBu++; + Util.restartLogger(); + } + counter_credTotal += counter_credBu; + Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`); + } + Util.logger.info(`\n:: ${counter_credTotal} BUs in total\n`); + } else { + let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + // to allow all-BU via user selection we need to run this here already + if ( + properties.credentials && + (!properties.credentials[cred] || + (bu !== '*' && properties.credentials[cred].businessUnits[bu])) + ) { + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + } else { + return; + } + } + + if (bu === '*' && properties.credentials && properties.credentials[cred]) { + Util.logger.info(`\n:: Retrieving all BUs for ${cred}`); + let counter_credBu = 0; + for (const bu in properties.credentials[cred].businessUnits) { + await _retrieveBU(cred, bu, selectedType); + counter_credBu++; + Util.restartLogger(); + } + Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`); + } else { + await _retrieveBU(cred, bu, selectedType); + Util.logger.info(`:: Done\n`); + } + } +} +/** + * helper for retrieve() + * @param {String} cred name of Credential + * @param {String} bu name of BU + * @param {String} [selectedType] limit retrieval to given metadata type/subtype + * @returns {Promise} ensure that BUs are worked on sequentially + */ +async function _retrieveBU(cred, bu, selectedType) { + properties = properties || File.loadConfigFile(); + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + Util.logger.info(`\n:: Retrieving ${cred}/${bu}\n`); + let retrieveTypesArr; + const [type, subType] = selectedType ? selectedType.split('-') : []; + if ( + type && + subType && + MetadataTypeInfo[type] && + MetadataTypeDefinitions[type].subTypes.includes(subType) + ) { + // Clear output folder structure for selected sub-type + File.removeSync( + File.normalizePath([properties.directories.retrieve, cred, bu, type, subType]) + ); + retrieveTypesArr = [selectedType]; + } else if (type && MetadataTypeInfo[type]) { + // Clear output folder structure for selected type + File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu, type])); + retrieveTypesArr = [type]; + } else { + // Clear output folder structure + File.removeSync(File.normalizePath([properties.directories.retrieve, cred, bu])); + // assume no type was given and config settings are used instead: + // removes subtypes and removes duplicates + retrieveTypesArr = [ + ...new Set(properties.metaDataTypes.retrieve.map((type) => type.split('-')[0])), + ]; + } + let client; + try { + client = await Util.getETClient(buObject); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const retriever = new Retriever(properties, buObject, client); + + try { + // await is required or the calls end up conflicting + await retriever.retrieve(retrieveTypesArr, null, null); + if (properties.options.documentOnRetrieve) { + // todo: find the underlying async issue that makes this wait necessary + await new Promise((resolve) => { + setTimeout(() => resolve('done!'), 1000); + }); + await badKeys(`${cred}/${bu}`); + } + } catch (ex) { + Util.logger.error('mcdev.retrieve failed: ' + ex.message); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + } + } +} +/** + * helper for deploy() + * @param {String} cred name of Credential + * @param {String} bu name of BU + * @param {String} [type] limit deployment to given metadata type + * @returns {Promise} ensure that BUs are worked on sequentially + */ +async function _deployBU(cred, bu, type) { + const buPath = `${cred}/${bu}`; + Util.logger.info(`::Deploying ${buPath}`); + properties = properties || File.loadConfigFile(); + const buObject = await Cli.getCredentialObject(properties, buPath, null, true); + if (buObject !== null) { + let client; + try { + client = await Util.getETClient(buObject); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const deployer = new Deployer(properties, buObject, client, type); + try { + // await is required or the calls end up conflicting + await deployer.deploy(); + } catch (ex) { + Util.logger.error('mcdev.deploy failed: ' + ex.message); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + } + } +} + +/** + * Deploys all metadata located in the 'deploy' directory to the specified business unit + * @param {String} businessUnit references credentials from properties.json + * @param {String} [selectedType] limit deployment to given metadata type + * @returns {Promise} - + */ +async function deploy(businessUnit, selectedType) { + Util.logger.info('mcdev:: Deploy'); + properties = properties || File.loadConfigFile(); + + const [type, subType] = selectedType ? selectedType.split('-') : []; + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } else if ( + type && + subType && + (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType)) + ) { + Util.logger.error(`:: '${selectedType}' is not a valid metadata type`); + return; + } + let counter_credBu = 0; + if (businessUnit === '*') { + // all credentials and all BUs shall be deployed to + const deployFolders = await File.readDirectories(properties.directories.deploy, 2, false); + for (const buPath of deployFolders.filter((r) => r.includes('/'))) { + const [cred, bu] = buPath.split('/'); + await _deployBU(cred, bu, type); + counter_credBu++; + Util.logger.info(''); + Util.restartLogger(); + } + } else { + // anything but "*" passed in + let [cred, bu] = businessUnit ? businessUnit.split('/') : [null, null]; + + // to allow all-BU via user selection we need to run this here already + if ( + properties.credentials && + (!properties.credentials[cred] || + (bu !== '*' && properties.credentials[cred].businessUnits[bu])) + ) { + const buObject = await Cli.getCredentialObject( + properties, + cred !== null ? cred + '/' + bu : null, + null, + true + ); + if (buObject !== null) { + cred = buObject.credential; + bu = buObject.businessUnit; + } else { + return; + } + } + + if (bu === '*' && properties.credentials && properties.credentials[cred]) { + // valid credential given and -all- BUs targeted + Util.logger.info(`\n:: Deploying all BUs for ${cred}`); + let counter_credBu = 0; + // for (const bu in properties.credentials[cred].businessUnits) { + const deployFolders = await File.readDirectories( + File.normalizePath([properties.directories.deploy, cred]), + 1, + false + ); + for (const buPath of deployFolders) { + await _deployBU(cred, buPath, type); + counter_credBu++; + Util.logger.info(''); + Util.restartLogger(); + } + Util.logger.info(`\n:: ${counter_credBu} BUs for ${cred}\n`); + } else { + // either bad credential or specific BU or no BU given + await _deployBU(cred, bu, type); + counter_credBu++; + } + } + if (counter_credBu !== 0) { + Util.logger.info(`\n:: Deployed ${counter_credBu} BUs\n`); + } +} + +/** + * Creates template file for properties.json + * @param {string} [credentialsName] identifying name of the installed package / project + * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @returns {Promise} - + */ +async function initProject(credentialsName, skipInteraction) { + Util.logger.info('mcdev:: Setting up project'); + properties = properties || File.loadConfigFile(!!credentialsName); + await Init.initProject(properties, credentialsName, skipInteraction); +} + +/** + * Refreshes BU names and ID's from MC instance + * @param {string} credentialsName identifying name of the installed package / project + * @returns {Promise} - + */ +async function findBUs(credentialsName) { + Util.logger.info('mcdev:: Load BUs'); + properties = properties || File.loadConfigFile(); + const buObject = await Cli.getCredentialObject(properties, credentialsName, true); + if (buObject !== null) { + BuHelper.refreshBUProperties(properties, buObject.credential); + } +} + +/** + * Creates docs for supported metadata types in Markdown and/or HTML format + * + * @param {String} businessUnit references credentials from properties.json + * @param {String} type metadata type + * @returns {Promise} - + */ +async function document(businessUnit, type) { + Util.logger.info('mcdev:: Document'); + properties = properties || File.loadConfigFile(); + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } + try { + const parentBUOnlyTypes = ['role']; + const buObject = await Cli.getCredentialObject( + properties, + parentBUOnlyTypes.includes(type) ? businessUnit.split('/')[0] : businessUnit, + parentBUOnlyTypes.includes(type) ? Util.parentBuName : null + ); + if (buObject !== null) { + MetadataTypeInfo[type].properties = properties; + MetadataTypeInfo[type].document(buObject); + } + } catch (ex) { + Util.logger.error('mcdev.document ' + ex.message); + Util.logger.debug(ex.stack); + Util.logger.info('If the directoy does not exist, you may need to retrieve this BU first.'); + } +} + +/** + * Creates docs for supported metadata types in Markdown and/or HTML format + * + * @param {String} businessUnit references credentials from properties.json + * @param {String} type supported metadata type + * @param {String} customerKey Identifier of data extension + * @returns {Promise} - + */ +async function deleteByKey(businessUnit, type, customerKey) { + Util.logger.info('mcdev:: delete'); + properties = properties || File.loadConfigFile(); + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + if ('string' !== typeof type) { + Util.logger.error('mcdev.delete failed: Bad metadata type passed in'); + return; + } + try { + MetadataTypeInfo[type].properties = properties; + MetadataTypeInfo[type].deleteByKey(buObject, customerKey); + } catch (ex) { + Util.logger.error('mcdev.delete ' + ex.message); + } + } +} + +/** + * Converts metadata to legacy format. Output is saved in 'converted' directory + * @param {String} businessUnit references credentials from properties.json + * @returns {Promise} - + */ +async function badKeys(businessUnit) { + properties = properties || File.loadConfigFile(); + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + Util.logger.info('Gathering list of Name<>External Key mismatches (bad keys)'); + const retrieveDir = File.filterIllegalPathChars( + File.normalizePath([ + properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + ]) + ); + const docPath = File.filterIllegalPathChars( + properties.directories.badKeys + buObject.credential + ); + const filename = File.normalizePath([ + docPath, + File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys.md', + ]); + if (!File.existsSync(docPath)) { + File.mkdirpSync(docPath); + } else if (File.existsSync(filename)) { + File.removeSync(filename); + } + + const regex = RegExp('(\\w+-){4}\\w+'); + let metadata; + if (File.existsSync(retrieveDir)) { + metadata = Deployer.readBUMetadata(retrieveDir, null, true); + } else { + Util.logger.warn( + `Looks like ${retrieveDir} does not exist. If there was no metadata retrieved this is expected, in other cases re-run retrieve to attempt to fix this issue` + ); + return; + } + let output = '# List of Metadata with Name-Key mismatches\n'; + for (const metadataType in metadata) { + let listEntries = ''; + for (const entry in metadata[metadataType]) { + const metadataEntry = metadata[metadataType][entry]; + if (regex.test(entry)) { + if (metadataType === 'query' && metadataEntry.Status === 'Inactive') { + continue; + } + listEntries += + '- ' + + entry + + (metadataEntry.name || metadataEntry.Name + ? ' => ' + (metadataEntry.name || metadataEntry.Name) + : '') + + '\n'; + } + } + if (listEntries !== '') { + output += '\n## ' + metadataType + '\n\n' + listEntries; + } + } + await File.writeToFile( + docPath, + File.filterIllegalFilenames(buObject.businessUnit) + '.badKeys', + 'md', + output + ); + Util.logger.info('Bad keys documented in ' + filename); + } +} + +/** + * Retrieve a specific metadata file and templatise. + * @param {String} businessUnit references credentials from properties.json + * @param {String} selectedType supported metadata type + * @param {String} name name of the metadata + * @param {String} market market which should be used to revert template + * @returns {Promise} - + */ +async function retrieveAsTemplate(businessUnit, selectedType, name, market) { + Util.logger.info('mcdev:: Retrieve as Template'); + properties = properties || File.loadConfigFile(); + const [type, subType] = selectedType ? selectedType.split('-') : []; + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } else if ( + type && + subType && + (!MetadataTypeInfo[type] || !MetadataTypeDefinitions[type].subTypes.includes(subType)) + ) { + Util.logger.error(`:: '${selectedType}' is not a valid metadata type`); + return; + } + + let retrieveTypesArr; + if ( + type && + subType && + MetadataTypeInfo[type] && + MetadataTypeDefinitions[type].subTypes.includes(subType) + ) { + retrieveTypesArr = [selectedType]; + } else if (type && MetadataTypeInfo[type]) { + retrieveTypesArr = [type]; + } + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + let client; + try { + client = await Util.getETClient(buObject); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const retriever = new Retriever(properties, buObject, client); + if (_checkMarket(market)) { + return retriever.retrieve(retrieveTypesArr, name, properties.markets[market]); + } + } +} + +/** + * Build a specific metadata file based on a template. + * @param {String} businessUnit references credentials from properties.json + * @param {String} type supported metadata type + * @param {String} name name of the metadata + * @param {String} market market localizations + * @returns {Promise} - + */ +async function buildDefinition(businessUnit, type, name, market) { + Util.logger.info('mcdev:: Build Definition from Template'); + properties = properties || File.loadConfigFile(); + if (type.includes('-')) { + Util.logger.error( + `:: '${type}' is not a valid metadata type. Please don't include subtypes.` + ); + return; + } + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } + const buObject = await Cli.getCredentialObject(properties, businessUnit); + if (buObject !== null) { + const builder = new Builder(properties, buObject, null); + if (market === '*') { + for (const oneMarket in properties.markets) { + builder.buildDefinition(type, name, properties.markets[oneMarket]); + } + } else { + if (_checkMarket(market)) { + builder.buildDefinition(type, name, properties.markets[market]); + } + } + } +} +/** + * check if a market name exists in current mcdev config + * @param {String} market market localizations + * @returns {Boolean} found market or not + */ +function _checkMarket(market) { + properties = properties || File.loadConfigFile(); + if (properties.markets[market]) { + return true; + } else { + Util.logger.error(`Could not find the market '${market}' in your configuration file.`); + const marketArr = []; + for (const oneMarket in properties.markets) { + marketArr.push(oneMarket); + } + if (marketArr.length) { + Util.logger.info('Available markets are: ' + marketArr.join(', ')); + } + return false; + } +} + +/** + * Build a specific metadata file based on a template using a list of bu-market combos + * @param {String} listName name of list of BU-market combos + * @param {String} type supported metadata type + * @param {String} name name of the metadata + * @returns {Promise} - + */ +async function buildDefinitionBulk(listName, type, name) { + Util.logger.info('mcdev:: Build Definition from Template Bulk'); + properties = properties || File.loadConfigFile(); + if (!properties.marketList) { + Util.logger.error('Please define properties.marketList object in your config'); + return; + } + if (!properties.marketList[listName]) { + Util.logger.error(`Please define properties.marketList.${listName} in your config`); + return; + } + if (type && !MetadataTypeInfo[type]) { + Util.logger.error(`:: '${type}' is not a valid metadata type`); + return; + } + let i = 0; + for (const businessUnit in properties.marketList[listName]) { + if (businessUnit === 'description') { + // skip, it's just a metadata on this list and not a BU + continue; + } + i++; + const market = properties.marketList[listName][businessUnit]; + let marketList = []; + if ('string' === typeof market) { + marketList.push(market); + } else { + marketList = market; + } + marketList.forEach((market) => { + if (market && properties.markets[market]) { + Util.logger.info(`Executing for '${businessUnit}': '${market}'`); + buildDefinition(businessUnit, type, name, market); + } else { + Util.logger.error( + `Could not find '${market}' in properties.markets. Please check your properties.marketList.${listName} confguration.` + ); + } + }); + } + if (!i) { + Util.logger.error('Please define properties.marketList in your config'); + } +} diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js new file mode 100644 index 000000000..ec5231a24 --- /dev/null +++ b/lib/metadataTypes/Asset.js @@ -0,0 +1,946 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); +const bluebird = require('bluebird'); +const cliProgress = require('cli-progress'); +const Mustache = require('mustache'); + +/** + * FileTransfer MetadataType + * @augments MetadataType + */ +class Asset extends MetadataType { + /** + * Retrieves Metadata of Asset + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} _ - + * @param {void} __ - + * @param {String} [selectedSubType] optionally limit to a single subtype + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir, _, __, selectedSubType) { + const items = []; + const subTypes = selectedSubType ? [selectedSubType] : this._getSubTypes(); + await File.initPrettier(); + // loop through subtypes and return results of each subType for caching (saving is handled per subtype) + for (const subType of subTypes) { + // each subtype contains multiple different specific types (images contains jpg and png for example) + // we use await here to limit the risk of too many concurrent api requests at time + items.push( + ...(await this.requestSubType( + subType, + this.definition.extendedSubTypes[subType], + retrieveDir + )) + ); + } + const metadata = this.parseResponseBody({ items: items }); + if (retrieveDir) { + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(metadata).length})` + ); + } + return { metadata: metadata, type: this.definition.type }; + } + + /** + * Retrieves asset metadata for caching + * @param {void} _ - + * @param {String} [selectedSubType] optionally limit to a single subtype + * @returns {Promise} Promise + */ + static retrieveForCache(_, selectedSubType) { + return this.retrieve(null, null, null, selectedSubType); + } + + /** + * Retrieves asset metadata for caching + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} templateName name of the metadata file + * @param {Object} templateVariables variables to be replaced in the metadata + * @param {String} [selectedSubType] optionally limit to a single subtype + * @returns {Promise} Promise + */ + static async retrieveAsTemplate(templateDir, templateName, templateVariables, selectedSubType) { + const items = []; + const subTypes = selectedSubType ? [selectedSubType] : this._getSubTypes(); + await File.initPrettier(); + // loop through subtypes and return results of each subType for caching (saving is handled per subtype) + for (const subType of subTypes) { + // each subtype contains multiple different specific types (images contains jpg and png for example) + // we use await here to limit the risk of too many concurrent api requests at time + items.push( + ...(await this.requestSubType( + subType, + this.definition.extendedSubTypes[subType], + templateDir, + templateName, + templateVariables + )) + ); + } + const metadata = this.parseResponseBody({ items: items }); + Util.logger.info(`Downloaded: ${this.definition.type} (${Object.keys(metadata).length})`); + + return { metadata: metadata, type: this.definition.type }; + } + /** + * helper for retrieve + retrieveAsTemplate + * @private + * @returns {String[]} subtype array + */ + static _getSubTypes() { + if (this.properties.metaDataTypes.retrieve.includes('asset')) { + // if "asset" is found in config assume to download the default subtypes only + return this.definition.typeRetrieveByDefault; + } else { + return this.properties.metaDataTypes.retrieve + .filter((type) => type.startsWith('asset-')) + .map((type) => type.replace('asset-', '')); + } + } + + /** + * Creates a single asset + * @param {Object} metadata a single asset + * @returns {Promise} Promise + */ + static create(metadata) { + const uri = '/asset/v1/content/assets/'; + return super.createREST(metadata, uri); + } + + /** + * Updates a single asset + * @param {Object} metadata a single asset + * @returns {Promise} Promise + */ + static update(metadata) { + const uri = '/asset/v1/content/assets/' + metadata.id; + return super.updateREST(metadata, uri); + } + /** + * Retrieves Metadata of a specific asset type + * @param {String} subType group of similar assets to put in a folder (ie. images) + * @param {Array} subTypeArray list of all asset types within this subtype + * @param {String} [retrieveDir] target directory for saving assets + * @param {String} [templateName] name of the metadata file + * @param {Object} [templateVariables] variables to be replaced in the metadata + * @returns {Promise} Promise + */ + static async requestSubType( + subType, + subTypeArray, + retrieveDir, + templateName, + templateVariables + ) { + if (retrieveDir) { + Util.logger.info(`- Retrieving Subtype: ${subType}`); + } else { + Util.logger.info(`- Caching Subtype: ${subType}`); + } + const uri = 'asset/v1/content/assets/'; + const options = { + uri: uri + 'query', + headers: {}, + json: { + page: { + page: 1, + pageSize: 50, + }, + query: null, + fields: [], + }, + }; + if (templateName) { + options.json.query = { + leftOperand: { + property: 'assetType.name', + simpleOperator: 'in', + value: subTypeArray, + }, + logicalOperator: 'AND', + rightOperand: { + property: 'name', + simpleOperator: 'equal', + value: templateName, + }, + }; + } else { + options.json.query = { + property: 'assetType.name', + simpleOperator: 'in', + value: subTypeArray, + }; + options.json.sort = [{ property: 'id', direction: 'ASC' }]; + } + // for caching we do not need these fields + if (retrieveDir) { + options.json.fields = [ + 'fileProperties', + 'status', + 'category', + 'createdDate', + 'createdBy', + 'modifiedDate', + 'modifiedBy', + 'availableViews', + 'data', + 'tags', + ]; + } + let moreResults = false; + let lastPage = 0; + let items = []; + do { + options.json.page.page = lastPage + 1; + const response = await this.client.RestClient.post(options); + if (response && response.body && response.body.items && response.body.items.length) { + // sometimes the api will return a payload without items + // --> ensure we only add proper items-arrays here + items = items.concat(response.body.items); + } + // check if any more records + if (response.body.message && response.body.message.includes('all shards failed')) { + // When running certain filters, there is a limit of 10k on ElastiCache. + // Since we sort by ID, we can get the last ID then run new requests from there + options.json.query = { + leftOperand: { + property: 'assetType.name', + simpleOperator: 'in', + value: subTypeArray, + }, + logicalOperator: 'AND', + rightOperand: { + property: 'id', + simpleOperator: 'greaterThan', + value: items[items.length - 1].id, + }, + }; + lastPage = 0; + moreResults = true; + } else if (response.body.page * response.body.pageSize < response.body.count) { + moreResults = true; + lastPage = Number(response.body.page); + } else { + moreResults = false; + } + } while (moreResults); + // only when we save results do we need the complete metadata or files. caching can skip these + if (retrieveDir && items.length > 0) { + // we have to wait on execution or it potentially causes memory reference issues when changing between BUs + await this.requestAndSaveExtended(items, subType, retrieveDir, templateVariables); + } + return items; + } + /** + * Retrieves extended metadata (files or extended content) of asset + * @param {Array} items array of items to retrieve + * @param {String} subType group of similar assets to put in a folder (ie. images) + * @param {String} retrieveDir target directory for saving assets + * @param {Object} [templateVariables] variables to be replaced in the metadata + * @returns {Promise} Promise + */ + static async requestAndSaveExtended(items, subType, retrieveDir, templateVariables) { + const extendedBar = new cliProgress.SingleBar( + { format: 'Processing [{bar}] {percentage}% | {value}/{total}' }, + cliProgress.Presets.shades_classic + ); + + const completed = []; + // put in do loop to manage issues with connection timeout + do { + // use promise execution limiting to avoid rate limits on api, but speed up execution + // start the progress bar with a total value of 200 and start value of 0 + extendedBar.start(items.length - completed.length, 0); + try { + const promiseMap = await bluebird.map( + items, + async (item) => { + await Util.retryOnError( + `Retrying asset-${subType} ${item[this.definition.nameField]} (${ + item[this.definition.keyField] + })`, + async () => { + const metadata = {}; + // this is a file so extended is at another endpoint + if ( + item.fileProperties && + item.fileProperties.extension && + !completed.includes(item.id) + ) { + metadata[item.customerKey] = item; + if (templateVariables) { + // do this here already because otherwise the extended file could be saved with wrong filename + const warningMsg = + 'Ensure that Code that might be loading this via ContentBlockByKey is updated with the new key before deployment.'; + this.overrideKeyWithName(item, warningMsg); + } + // retrieving the extended file does not need to be awaited + await this._retrieveExtendedFile(item, subType, retrieveDir); + } + // this is a complex type which stores data in the asset itself + else if (!completed.includes(item.id)) { + const extendedItem = await this.client.RestClient.get({ + uri: 'asset/v1/content/assets/' + item.id, + }); + metadata[item.customerKey] = extendedItem.body; + } + completed.push(item.id); + await this.saveResults( + metadata, + retrieveDir, + 'asset-' + subType, + templateVariables + ); + // update the current value in your application.. + extendedBar.increment(); + }, + true + ); + }, + { concurrency: 5 } + ); + + // stop the progress bar + extendedBar.stop(); + return promiseMap; + } catch (ex) { + extendedBar.stop(); + // timeouts should be retried, others can be retried + if (ex.code !== 'ETIMEDOUT') { + throw ex; + } + } + } while (completed.length === items.length); + } + /** + * Some metadata types store their actual content as a separate file, e.g. images + * This method retrieves these and saves them alongside the metadata json + * @param {Object} metadata a single asset + * @param {String} subType group of similar assets to put in a folder (ie. images) + * @param {String} retrieveDir target directory for saving assets + * @returns {Promise} - + */ + static async _retrieveExtendedFile(metadata, subType, retrieveDir) { + const file = await this.client.RestClient.get({ + uri: 'asset/v1/content/assets/' + metadata.id + '/file', + }); + + // to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the filename + // however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) " + const fileext = metadata.fileProperties.extension.split(' ')[0]; + + File.writeToFile( + [retrieveDir, this.definition.type, subType], + metadata.customerKey, + fileext, + file.body, + 'base64' + ); + } + /** + * helper for this.preDeployTasks() + * Some metadata types store their actual content as a separate file, e.g. images + * This method reads these from the local FS stores them in the metadata object allowing to deploy it + * @param {Object} metadata a single asset + * @param {String} subType group of similar assets to put in a folder (ie. images) + * @param {String} deployDir directory of deploy files + * @returns {Promise} - + */ + static async _readExtendedFileFromFS(metadata, subType, deployDir) { + if (metadata.fileProperties && metadata.fileProperties.extension) { + // to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the filename + // however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) " + const fileext = metadata.fileProperties.extension.split(' ')[0]; + + metadata.file = await File.readFile( + [deployDir, this.definition.type, subType], + metadata.customerKey, + fileext, + 'base64' + ); + } + } + /** + * manages post retrieve steps + * @param {Object} metadata a single asset + * @param {String} [_] unused + * @param {Boolean} isTemplating signals that we are retrieving templates + * @returns {Object[]} metadata + */ + static postRetrieveTasks(metadata, _, isTemplating) { + // if retrieving template, replace the name with customer key if that wasn't already the case + if (isTemplating) { + const warningMsg = + 'Ensure that Code that might be loading this via ContentBlockByKey is updated with the new key before deployment.'; + this.overrideKeyWithName(metadata, warningMsg); + } + return this.parseMetadata(metadata); + } + + /** + * prepares an asset definition for deployment + * @param {Object} metadata a single asset + * @param {String} deployDir directory of deploy files + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata, deployDir) { + // additonalattributes fail where the value is "" so we need to remove them from deploy + if ( + metadata.data && + metadata.data.email && + metadata.data.email && + metadata.data.email.attributes && + metadata.data.email.attributes.length > 0 + ) { + metadata.data.email.attributes = metadata.data.email.attributes.filter( + (attr) => attr.value + ); + } + + // folder + metadata.category = { + id: Util.getFromCache(this.cache, 'folder', metadata.r__folder_Path, 'Path', 'ID'), + }; + delete metadata.r__folder_Path; + + // restore asset type id which is needed for deploy + metadata.assetType.id = this.definition.typeMapping[metadata.assetType.name]; + + // define asset's subtype + const subType = this.getSubtype(metadata); + + // #1 get text extracts back into the JSON + await this._mergeCode(metadata, deployDir, subType); + + // #2 get file from local disk and insert as base64 + await this._readExtendedFileFromFS(metadata, deployDir, subType); + + return metadata; + } + /** + * determines the subtype of the current asset + * @param {Object} metadata a single asset + * @returns {String} subtype + */ + static getSubtype(metadata) { + for (const sub in this.definition.extendedSubTypes) { + if (this.definition.extendedSubTypes[sub].includes(metadata.assetType.name)) { + return sub; + } + } + } + + /** + * helper for buildDefinition + * handles extracted code if any are found for complex types + * @param {String} templateDir Directory where metadata templates are stored + * @param {String} targetDir Directory where built definitions will be saved + * @param {Object} metadata main JSON file that was read from file system + * @param {Object} variables variables to be replaced in the metadata + * @param {String} templateName name of the template to be built + * @returns {Promise} Promise + */ + static async buildDefinitionForExtracts( + templateDir, + targetDir, + metadata, + variables, + templateName + ) { + // clone metadata to ensure the main file is not modified by what we do in here + metadata = JSON.parse(JSON.stringify(metadata)); + + // #1 text extracts + // define asset's subtype + const subType = this.getSubtype(metadata); + // get HTML from filesystem + const fileList = await this._mergeCode(metadata, templateDir, subType, templateName); + // replace template variables with their values + for (const extractedFile of fileList) { + try { + extractedFile.content = Mustache.render(extractedFile.content, variables); + } catch (ex) { + throw new Error( + `${this.definition.type}:: Error applying template variables on ${ + metadata[this.definition.keyField] + }: ${extractedFile.filename}.${extractedFile.fileext}.` + ); + } + } + + // #2 binary extracts + if (metadata.fileProperties && metadata.fileProperties.extension) { + // to handle uploaded files that bear the same name, SFMC engineers decided to add a number after the filename + // however, their solution was not following standards: fileName="header.png (4) " and then extension="png (4) " + const fileext = metadata.fileProperties.extension.split(' ')[0]; + + const filecontent = await File.readFile( + [templateDir, this.definition.type, subType], + metadata.customerKey, + fileext, + 'base64' + ); + fileList.push({ + pathArr: [this.definition.type, subType], + filename: metadata.customerKey, + fileext: fileext, + content: filecontent, + encoding: 'base64', + }); + } + + // write to file (#1 + #2) + const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir]; + for (const targetDir of targetDirArr) { + for (const extractedFile of fileList) { + File.writeToFile( + [targetDir, ...extractedFile.pathArr], + extractedFile.filename, + extractedFile.fileext, + extractedFile.content, + extractedFile.encoding || null + ); + } + } + } + + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single asset definition + * @returns {Object} parsed metadata definition + */ + static parseMetadata(metadata) { + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.category.id, + 'ID', + 'Path' + ); + delete metadata.category; + } catch (ex) { + // ! if we don't catch this error here we end up saving the actual asset but not its corresponding JSON + Util.logger.debug(ex.message); + Util.logger.warn( + `Could not find folder with ID ${metadata.category.id} for '${metadata.name}' (${metadata.customerKey})` + ); + } + // extract HTML for selected subtypes and convert payload for easier processing in MetadataType.saveResults() + metadata = this._extractCode(metadata); + return metadata; + } + /** + * helper for this.preDeployTasks() that loads extracted code content back into JSON + * @param {Object} metadata a single asset definition + * @param {String} deployDir directory of deploy files + * @param {String} subType asset-subtype name + * @param {String} [templateName] name of the template used to built defintion (prior applying templating) + * @returns {Promise} fileList for templating (disregarded during deployment) + */ + static async _mergeCode(metadata, deployDir, subType, templateName) { + const subtypeExtension = `.${this.definition.type}-${subType}-meta`; + const fileList = []; + let subDirArr; + let readDirArr; + + switch (metadata.assetType.name) { + case 'webpage': // asset + case 'templatebasedemail': // message + case 'htmlemail': // message + // this complex type always creates its own subdir per asset + subDirArr = [this.definition.type, subType]; + readDirArr = [ + deployDir, + ...subDirArr, + templateName ? templateName : metadata.customerKey, + ]; + + // metadata.views.html.content (mandatory) + if ( + File.existsSync( + File.normalizePath([...readDirArr, `index${subtypeExtension}.html`]) + ) + ) { + // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs + metadata.views.html.content = await File.readFile( + readDirArr, + 'index' + subtypeExtension, + 'html' + ); + + if (templateName) { + // to use this method in templating, store a copy of the info in fileList + fileList.push({ + pathArr: [...subDirArr, metadata.customerKey], + filename: 'index' + subtypeExtension, + fileext: 'html', + content: metadata.views.html.content, + }); + } + } + + // metadata.views.html.slots.<>.blocks.<>.content (optional) + if (metadata.views && metadata.views.html && metadata.views.html.slots) { + await this._mergeCode_slots( + metadata.views.html.slots, + readDirArr, + subtypeExtension, + subDirArr, + fileList, + metadata.customerKey, + templateName + ); + } + break; + case 'textonlyemail': // message + // metadata.views.text.content + subDirArr = [this.definition.type, subType]; + readDirArr = [deployDir, ...subDirArr]; + if ( + File.existsSync( + File.normalizePath([ + ...readDirArr, + `${ + templateName ? templateName : metadata.customerKey + }${subtypeExtension}.html`, + ]) + ) + ) { + // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs + metadata.views.text.content = await File.readFile( + readDirArr, + (templateName ? templateName : metadata.customerKey) + subtypeExtension, + 'html' + ); + + if (templateName) { + // to use this method in templating, store a copy of the info in fileList + fileList.push({ + pathArr: subDirArr, + filename: metadata.customerKey + subtypeExtension, + fileext: 'html', + content: metadata.views.text.content, + }); + } + } + break; + case 'freeformblock': // block + case 'htmlblock': // block + case 'textblock': // block + case 'smartcaptureblock': // other + case 'codesnippetblock': // other + // metadata.content + subDirArr = [this.definition.type, subType]; + readDirArr = [deployDir, ...subDirArr]; + if ( + File.existsSync( + File.normalizePath([ + ...readDirArr, + `${ + templateName ? templateName : metadata.customerKey + }${subtypeExtension}.html`, + ]) + ) + ) { + // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs + metadata.content = await File.readFile( + readDirArr, + (templateName ? templateName : metadata.customerKey) + subtypeExtension, + 'html' + ); + if (templateName) { + // to use this method in templating, store a copy of the info in fileList + fileList.push({ + pathArr: subDirArr, + filename: metadata.customerKey + subtypeExtension, + fileext: 'html', + content: metadata.content, + }); + } + } + break; + } + return fileList; + } + /** + * helper for this.preDeployTasks() that loads extracted code content back into JSON + * @param {Object} metadataSlots metadata.views.html.slots or deeper slots.<>.blocks.<>.slots + * @param {String[]} readDirArr directory of deploy files + * @param {String} subtypeExtension asset-subtype name ending on -meta + * @param {String[]} subDirArr directory of files w/o leading deploy dir + * @param {Object[]} fileList directory of files w/o leading deploy dir + * @param {String} customerKey external key of template (could have been changed if used during templating) + * @param {String} [templateName] name of the template used to built defintion (prior applying templating) + * @returns {Promise} - + */ + static async _mergeCode_slots( + metadataSlots, + readDirArr, + subtypeExtension, + subDirArr, + fileList, + customerKey, + templateName + ) { + for (const slot in metadataSlots) { + if (Object.prototype.hasOwnProperty.call(metadataSlots, slot)) { + const slotObj = metadataSlots[slot]; + // found slot + if (slotObj.blocks) { + for (const block in slotObj.blocks) { + if (Object.prototype.hasOwnProperty.call(slotObj.blocks, block)) { + const filename = `${slot}-${block}${subtypeExtension}`; + if ( + File.existsSync( + File.normalizePath([ + ...readDirArr, + 'blocks', + `${filename}.html`, + ]) + ) + ) { + // the main content can be empty (=not set up yet) hence check if we did extract sth or else readFile() will print error msgs + // if an extracted block was found, save it back into JSON + slotObj.blocks[block].content = await File.readFile( + [...readDirArr, 'blocks'], + filename, + 'html' + ); + if (templateName) { + // to use this method in templating, store a copy of the info in fileList + fileList.push({ + pathArr: [...subDirArr, customerKey, 'blocks'], + filename: filename, + fileext: 'html', + content: slotObj.blocks[block].content, + }); + } + } + if (slotObj.blocks[block].slots) { + // * recursion: each block can have slots of its own + await this._mergeCode_slots( + slotObj.blocks[block].slots, + readDirArr, + subtypeExtension, + subDirArr, + fileList, + customerKey, + templateName + ); + } + } + } + } + } + } + } + /** + * helper for this.parseMetadata() that finds code content in JSON and extracts it + * to allow saving that separately and formatted + * @param {Object} metadata a single asset definition + * @returns {Object} { json: metadata, codeArr: object[], subFolder: string[] } + */ + static _extractCode(metadata) { + const codeArr = []; + switch (metadata.assetType.name) { + case 'webpage': // asset + case 'templatebasedemail': // message + case 'htmlemail': // message + // metadata.views.html.content (mandatory) + if (metadata.views.html.content && metadata.views.html.content.length) { + codeArr.push({ + subFolder: null, + fileName: 'index', + fileExt: 'html', + content: metadata.views.html.content, + }); + delete metadata.views.html.content; + } + + // metadata.views.html.slots.<>.blocks.<>.content (optional) + if (metadata.views && metadata.views.html && metadata.views.html.slots) { + this._extractCode_slots(metadata.views.html.slots, codeArr); + } + + return { json: metadata, codeArr: codeArr, subFolder: [metadata.customerKey] }; + case 'textonlyemail': // message + // metadata.views.text.content + if (metadata.views.text.content && metadata.views.text.content.length) { + codeArr.push({ + subFolder: null, + fileName: metadata.customerKey, + fileExt: 'html', + content: metadata.views.text.content, + }); + delete metadata.views.text.content; + } + return { json: metadata, codeArr: codeArr, subFolder: null }; + case 'freeformblock': // block + case 'htmlblock': // block + case 'textblock': // block + case 'smartcaptureblock': // other + case 'codesnippetblock': // other + // metadata.content + if (metadata.content && metadata.content.length) { + codeArr.push({ + subFolder: null, + fileName: metadata.customerKey, + fileExt: 'html', + content: metadata.content, + }); + delete metadata.content; + } + return { json: metadata, codeArr: codeArr, subFolder: null }; + default: + return { json: metadata, codeArr: codeArr, subFolder: null }; + } + } + /** + * @param {Object} metadataSlots metadata.views.html.slots or deeper slots.<>.blocks.<>.slots + * @param {Object[]} codeArr to be extended array for extracted code + * @returns {void} + */ + static _extractCode_slots(metadataSlots, codeArr) { + for (const slot in metadataSlots) { + if (Object.prototype.hasOwnProperty.call(metadataSlots, slot)) { + const slotObj = metadataSlots[slot]; + // found slot + for (const block in slotObj.blocks) { + if (Object.prototype.hasOwnProperty.call(slotObj.blocks, block)) { + if (slotObj.blocks[block].content) { + // found content block + const code = slotObj.blocks[block].content; + codeArr.push({ + subFolder: ['blocks'], + fileName: `${slot}-${block}`, + fileExt: 'html', + content: code, + }); + delete slotObj.blocks[block].content; + } + if (slotObj.blocks[block].slots) { + // * recursion: each block can have slots of its own + this._extractCode_slots(slotObj.blocks[block].slots, codeArr); + } + } + } + } + } + } + /** + * Returns file contents mapped to their filename without '.json' ending + * @param {String} dir directory that contains '.json' files to be read + * @returns {Object} fileName => fileContent map + */ + static getJsonFromFS(dir) { + const fileName2FileContent = {}; + try { + for (const subtype of this.definition.subTypes) { + const currentdir = File.normalizePath([dir, subtype]); + if (File.existsSync(currentdir)) { + const files = File.readdirSync(currentdir, { withFileTypes: true }); + + files.forEach((dirent) => { + try { + let thisDir = currentdir; + let filename = dirent.name; + if (dirent.isDirectory()) { + // complex types with more than one extracted piece of code are saved in their + // own subfolder (with folder name = CustomerKey) + // this section aims to find that json in the subfolder + const subfolderFiles = File.readdirSync( + File.normalizePath([currentdir, dirent.name]) + ); + subfolderFiles.forEach((subFileName) => { + if (subFileName.endsWith('-meta.json')) { + filename = subFileName; + thisDir = File.normalizePath([currentdir, dirent.name]); + } + }); + } + if (filename.endsWith('-meta.json')) { + const fileContent = File.readJSONFile( + thisDir, + filename, + true, + false + ); + // subtype will change the metadata suffix length + const fileNameWithoutEnding = filename.slice( + 0, + -17 - subtype.length + ); + fileName2FileContent[fileNameWithoutEnding] = fileContent; + } + } catch (ex) { + // by catching this in the loop we gracefully handle the issue and move on to the next file + Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex); + } + }); + } + } + } catch (ex) { + // this will catch issues with readdirSync + Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex); + throw new Error(ex); + } + return fileName2FileContent; + } + /** + * check template directory for complex types that open subfolders for their subtypes + * @param {String} templateDir Directory where metadata templates are stored + * @param {String} templateName name of the metadata file + * @returns {string} subtype name + */ + static findSubType(templateDir, templateName) { + const typeDirArr = [this.definition.type]; + let subType; + for (const st of this.definition.subTypes) { + const fileNameFull = templateName + '.' + this.definition.type + `-${st}-meta.json`; + if ( + File.existsSync( + File.normalizePath([templateDir, ...typeDirArr, st, fileNameFull]) + ) || + File.existsSync( + File.normalizePath([templateDir, ...typeDirArr, st, templateName, fileNameFull]) + ) + ) { + subType = st; + break; + } + } + if (!subType) { + throw new Error( + `Could not find asset with name ${templateName} in ${File.normalizePath([ + templateDir, + ...typeDirArr, + ])}` + ); + } + return subType; + } + /** + * optional method used for some types to try a different folder structure + * @param {String} templateDir Directory where metadata templates are stored + * @param {String[]} typeDirArr current subdir for this type + * @param {String} templateName name of the metadata template + * @param {String} fileName name of the metadata template file w/o extension + * @returns {Object} metadata + */ + static async readSecondaryFolder(templateDir, typeDirArr, templateName, fileName) { + // handles subtypes that create 1 folder per asset -> currently causes the below File.ReadFile to error out + typeDirArr.push(templateName); + return await File.readFile([templateDir, ...typeDirArr], fileName, 'json'); + } +} + +// Assign definition to static attributes +Asset.definition = require('../MetadataTypeDefinitions').asset; +module.exports = Asset; diff --git a/lib/metadataTypes/AttributeGroup.js b/lib/metadataTypes/AttributeGroup.js new file mode 100644 index 000000000..e8154c955 --- /dev/null +++ b/lib/metadataTypes/AttributeGroup.js @@ -0,0 +1,33 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * AttributeGroup MetadataType + * @augments MetadataType + */ +class AttributeGroup extends MetadataType { + /** + * Retrieves Metadata of schema attribute groups for caching. + * @returns {Promise} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/hub/v1/contacts/schema/attributeGroups', null); + } + + /** + * Retrieves Metadata of schema attribute groups. + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/hub/v1/contacts/schema/attributeGroups', null); + } +} + +// Assign definition to static attributes +AttributeGroup.definition = require('../MetadataTypeDefinitions').attributeGroup; +AttributeGroup.cache = {}; +AttributeGroup.client = undefined; + +module.exports = AttributeGroup; diff --git a/lib/metadataTypes/Automation.js b/lib/metadataTypes/Automation.js new file mode 100644 index 000000000..a04f84ac0 --- /dev/null +++ b/lib/metadataTypes/Automation.js @@ -0,0 +1,783 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); +const Definitions = require('../MetadataTypeDefinitions'); + +/** + * @typedef {Object} AutomationActivity + * @property {string} name name (not key) of activity + * @property {string} [objectTypeId] Id of assoicated activity type; see this.definition.activityTypeMapping + * @property {string} [activityObjectId] Object Id of assoicated metadata item + * @property {number} displayOrder order within step; starts with 1 or higher number + * @property {string} r__type see this.definition.activityTypeMapping + * + * @typedef {Object} AutomationStep + * @property {string} name description + * @property {string} [annotation] equals AutomationStep.name + * @property {number} step step iterator + * @property {number} [stepNumber] step iterator, automatically set during deployment + * @property {AutomationActivity[]} activities - + * + * @typedef {Object} AutomationSchedule REST format + * @property {number} typeId ? + * @property {string} startDate example: '2021-05-07T09:00:00' + * @property {string} endDate example: '2021-05-07T09:00:00' + * @property {string} icalRecur example: 'FREQ=DAILY;UNTIL=20790606T160000;INTERVAL=1' + * @property {string} timezoneName example: 'W. Europe Standard Time'; see this.definition.timeZoneMapping + * @property {number} [timezoneId] see this.definition.timeZoneMapping + * + * @typedef {Object} AutomationScheduleSoap SOAP format + * @property {Object} Recurrence - + * @property {Object} Recurrence.$ {'xsi:type': keyStem + 'lyRecurrence'} + * @property {'ByYear'} [Recurrence.YearlyRecurrencePatternType] * currently not supported by tool * + * @property {'ByMonth'} [Recurrence.MonthlyRecurrencePatternType] * currently not supported by tool * + * @property {'ByWeek'} [Recurrence.WeeklyRecurrencePatternType] * currently not supported by tool * + * @property {'ByDay'} [Recurrence.DailyRecurrencePatternType] - + * @property {'Interval'} [Recurrence.MinutelyRecurrencePatternType] - + * @property {'Interval'} [Recurrence.HourlyRecurrencePatternType] - + * @property {number} [Recurrence.YearInterval] 1..n * currently not supported by tool * + * @property {number} [Recurrence.MonthInterval] 1..n * currently not supported by tool * + * @property {number} [Recurrence.WeekInterval] 1..n * currently not supported by tool * + * @property {number} [Recurrence.DayInterval] 1..n + * @property {number} [Recurrence.HourInterval] 1..n + * @property {number} [Recurrence.MinuteInterval] 1..n + * @property {number} _interval internal variable for CLI output only + * @property {Object} TimeZone - + * @property {number} TimeZone.ID AutomationSchedule.timezoneId + * @property {string} _timezoneString internal variable for CLI output only + * @property {string} StartDateTime AutomationSchedule.startDate + * @property {string} EndDateTime AutomationSchedule.endDate + * @property {string} _StartDateTime AutomationSchedule.startDate; internal variable for CLI output only + * @property {'EndOn'|'EndAfter'} RecurrenceRangeType set to 'EndOn' if AutomationSchedule.icalRecur contains 'UNTIL'; otherwise to 'EndAfter' + * @property {number} Occurrences only exists if RecurrenceRangeType=='EndAfter' + * + * @typedef {Object} AutomationItem + * @property {string} [id] Object Id + * @property {string} key key + * @property {string} name name + * @property {string} description - + * @property {'scheduled'|'triggered'} type Starting Source = Schedule / File Drop + * @property {'Scheduled'|'Running'} status - + * @property {AutomationSchedule} [schedule] only existing if type=scheduled + * @property {Object} [fileTrigger] only existing if type=triggered + * @property {string} fileTrigger.fileNamingPattern - + * @property {string} fileTrigger.fileNamePatternTypeId - + * @property {string} fileTrigger.folderLocationText - + * @property {string} fileTrigger.queueFiles - + * @property {Object} [startSource] - + * @property {AutomationSchedule} [startSource.schedule] rewritten to AutomationItem.schedule + * @property {Object} [startSource.fileDrop] rewritten to AutomationItem.fileTrigger + * @property {string} startSource.fileDrop.fileNamingPattern - + * @property {string} startSource.fileDrop.fileNamePatternTypeId - + * @property {string} startSource.fileDrop.folderLocation - + * @property {string} startSource.fileDrop.queueFiles - + * @property {number} startSource.typeId - + * @property {AutomationStep[]} steps - + * @property {string} r__folder_Path folder path + * @property {string} [categoryId] holds folder ID, replaced with r__folder_Path during retrieve + * + * @typedef {Object.} AutomationMap + */ + +/** + * Automation MetadataType + * @augments MetadataType + */ +class Automation extends MetadataType { + /** + * Retrieves Metadata of Automation + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise<{metadata:AutomationMap,type:string}>} Promise of metadata + */ + static async retrieve(retrieveDir) { + const results = await new Promise((resolve) => { + this.client.SoapClient.retrieve('Program', ['ObjectID'], (error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response.body.Results); + } + }); + }); + const details = ( + await Promise.all( + results.map((a) => + this.client.RestClient.get({ + uri: '/automation/v1/automations/' + a.ObjectID, + }) + ) + ) + ).map((b) => b.body); + const parsed = this.parseResponseBody({ items: details }); + + const savedMetadata = await this.saveResults(parsed, retrieveDir, null, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + return { metadata: parsed, type: this.definition.type }; + } + + /** + * Retrieves automation metadata for caching + * @returns {Promise<{metadata:AutomationMap,type:string}>} Promise of metadata + */ + static async retrieveForCache() { + const results = await new Promise((resolve) => { + this.client.SoapClient.retrieve( + 'Program', + ['ObjectID', 'CustomerKey', 'Name'], + (error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response.body.Results); + } + } + ); + }); + const resultsConverted = {}; + for (const m of results) { + resultsConverted[m.CustomerKey] = { + id: m.ObjectID, + key: m.CustomerKey, + name: m.Name, + }; + } + return { metadata: resultsConverted, type: this.definition.type }; + } + + /** + * Retrieve a specific Automation Definition by Name + * @param {string} templateDir Directory where retrieved metadata directory will be saved + * @param {string} name name of the metadata file + * @param {Util.TemplateMap} variables variables to be replaced in the metadata + * @returns {Promise<{metadata:AutomationMap,type:string}>} Promise of metadata + */ + static async retrieveAsTemplate(templateDir, name, variables) { + const results = await new Promise((resolve) => { + this.client.SoapClient.retrieve( + 'Program', + ['ObjectID', 'Name'], + { + filter: { + leftOperand: 'Name', + operator: 'equals', + rightOperand: name, + }, + }, + (error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response.body.Results); + } + } + ); + }); + if (results && results[0]) { + // eq-operator returns a similar, not exact match and hence might return more than 1 entry + const [metadata] = results.filter((item) => item.Name === name); + if (!metadata) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + return; + } + const details = ( + await this.client.RestClient.get({ + uri: '/automation/v1/automations/' + metadata.ObjectID, + }) + ).body; + let val = null; + // if parsing fails, we should just save what we get + try { + const parsedDetails = this.parseMetadata(details); + if (parsedDetails !== null) { + val = JSON.parse( + Util.replaceByObject(JSON.stringify(parsedDetails), variables) + ); + } + } catch (ex) { + val = JSON.parse(JSON.stringify(details)); + } + if (val === null) { + throw new Error( + `Automations '${name}' was skipped and hence cannot be used for templating.` + ); + } + // remove all fields not listed in Definition for templating + this.keepTemplateFields(val); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + val[this.definition.keyField] + '.' + this.definition.type + '-meta', + val + ); + Util.logger.info( + `Automation.retrieveAsTemplate:: Written Metadata to filesystem (${name})` + ); + return { metadata: val, type: this.definition.type }; + } else if (results) { + throw new Error(`No Automations Found with name '${name}'`); + } else { + throw new Error(JSON.stringify(results)); + } + } + /** + * manages post retrieve steps + * @param {AutomationItem} metadata a single automation + * @param {string} [_] unused + * @param {Boolean} [isTemplating] signals that we are retrieving templates + * @returns {AutomationItem} metadata + */ + static postRetrieveTasks(metadata, _, isTemplating) { + // if retrieving template, replace the name with customer key if that wasn't already the case + if (isTemplating) { + const warningMsg = null; + this.overrideKeyWithName(metadata, warningMsg); + } + return this.parseMetadata(metadata); + } + + /** + * Deploys automation - the saved file is the original one due to large differences required for deployment + * @param {AutomationMap} metadata metadata mapped by their keyField + * @param {string} targetBU name/shorthand of target businessUnit for mapping + * @param {string} retrieveDir directory where metadata after deploy should be saved + * @returns {Promise} Promise + */ + static async deploy(metadata, targetBU, retrieveDir) { + const orignalMetadata = JSON.parse(JSON.stringify(metadata)); + const upsertResults = await this.upsert(metadata, targetBU); + await this.postDeployTasks(upsertResults, orignalMetadata); + await this.saveResults(upsertResults, retrieveDir, null); + return upsertResults; + } + + /** + * Creates a single automation + * @param {AutomationItem} metadata single metadata entry + * @returns {Promise} Promise + */ + static create(metadata) { + const uri = '/automation/v1/automations/'; + return super.createREST(metadata, uri); + } + + /** + * Updates a single automation + * @param {AutomationItem} metadata single metadata entry + * @param {AutomationItem} metadataBefore metadata mapped by their keyField + * @returns {Promise} Promise + */ + static update(metadata, metadataBefore) { + metadata.id = metadataBefore.id; + const uri = '/automation/v1/automations/' + metadata.id; + return super.updateREST(metadata, uri); + } + + /** + * Gets executed before deploying metadata + * @param {AutomationItem} metadata metadata mapped by their keyField + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + if (this.validateDeployMetadata(metadata)) { + try { + metadata.categoryId = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + if (metadata.r__folder_Path !== 'my automations') { + Util.logger.warn( + `Automation '${ + metadata[this.definition.nameField] + }' is located in subfolder ${ + metadata.r__folder_Path + }. Please note that creating automation folders is not supported via API and hence you will have to create it manually in the GUI if you choose to deploy this automation.` + ); + } + delete metadata.r__folder_Path; + } catch (ex) { + throw new Error( + `Folder '${metadata.r__folder_Path}' was not found on the server. Please create this manually in the GUI. Automation-folders cannot be deployed automatically.` + ); + } + if (metadata.type === 'scheduled' && metadata.schedule && metadata.schedule.startDate) { + // Starting Source == 'Schedule' + + delete metadata.schedule.rangeTypeId; + delete metadata.schedule.pattern; + delete metadata.schedule.scheduledTime; + delete metadata.schedule.scheduledStatus; + if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) { + metadata.schedule.timezoneId = this.definition.timeZoneMapping[ + metadata.schedule.timezoneName + ]; + } else { + Util.logger.error( + `Could not find timezone ${metadata.schedule.timezoneName} in definition.timeZoneMapping` + ); + } + delete metadata.schedule.timezoneName; + // the upsert API needs this to be named scheduleTypeId; the retrieve API returns it as typeId + metadata.schedule.scheduleTypeId = metadata.schedule.typeId; + delete metadata.schedule.typeId; + + // prep startSource + metadata.startSource = { schedule: metadata.schedule, typeId: 1 }; + } else if (metadata.type === 'triggered' && metadata.fileTrigger) { + // Starting Source == 'File Drop' + + // prep startSource + metadata.startSource = { + fileDrop: { + filenamePattern: metadata.fileTrigger.fileNamingPattern, + filenamePatternTypeId: metadata.fileTrigger.fileNamePatternTypeId, + folderLocation: metadata.fileTrigger.folderLocationText, + queueFiles: metadata.fileTrigger.queueFiles, + }, + typeId: 2, + }; + delete metadata.fileTrigger; + } + delete metadata.schedule; + delete metadata.type; + let i = 0; + for (const step of metadata.steps) { + for (const activity of step.activities) { + if (activity.name && this.definition.dependencies.includes(activity.r__type)) { + // automations can have empty placeholder for activities with only their type defined + activity.activityObjectId = Util.getFromCache( + this.cache, + activity.r__type, + activity.name, + Definitions[activity.r__type].nameField, + Definitions[activity.r__type].idField + ); + } + activity.objectTypeId = this.definition.activityTypeMapping[activity.r__type]; + delete activity.r__type; + } + step.annotation = step.name; + step.stepNumber = i; + delete step.name; + delete step.step; + i++; + } + return metadata; + } else { + return null; + } + } + /** + * Validates the automation to be sure it can be deployed. + * Whitelisted Activites are deployed but require configuration + * @param {AutomationItem} metadata single automation record + * @returns {Boolean} result if automation can be deployed based on steps + */ + static validateDeployMetadata(metadata) { + let deployable = true; + if (metadata.steps) { + for (const step of metadata.steps) { + for (const activity of step.activities) { + // check if manual deploy required. if so then log warning + if (this.definition.manualDeployTypes.includes(activity.r__type)) { + Util.logger.warn( + `Automation '${ + metadata.name + }' requires additional manual configuration: '${ + activity.name + }' in step ${step.stepNumber || step.step}.${activity.displayOrder}` + ); + } + // cannot deploy because it is not supported + else if (!this.definition.dependencies.includes(activity.r__type)) { + Util.logger.error( + `Automation '${ + metadata.name + }' cannot be deployed as the following activity is not supported: '${ + activity.name + }' in step ${step.stepNumber || step.step}.${activity.displayOrder}` + ); + deployable = false; + } + } + } + } + return deployable; + } + + /** + * Gets executed after deployment of metadata type + * @param {AutomationMap} metadata metadata mapped by their keyField + * @param {AutomationMap} originalMetadata metadata to be updated (contains additioanl fields) + * @returns {Promise} - + */ + static async postDeployTasks(metadata, originalMetadata) { + for (const key in metadata) { + // need to put schedule on here if status is scheduled + + if (originalMetadata[key] && originalMetadata[key].type === 'scheduled') { + // Starting Source == 'Schedule': Try starting the automation + if (originalMetadata[key].status === 'Scheduled') { + let schedule = null; + try { + schedule = this._buildSchedule(originalMetadata[key].schedule); + } catch (ex) { + Util.logger.error( + `Could not create schedule for automation ${originalMetadata[key].name} to start it: ${ex.message}` + ); + } + if (schedule !== null) { + try { + await new Promise((resolve, reject) => { + this.client.SoapClient.schedule( + 'Automation', + schedule, + { + Interaction: { + ObjectID: metadata[key].id, + }, + }, + 'start', + null, + (error, response) => { + if ( + error || + (response.body.Results && + response.body.Results[0] && + response.body.Results[0].StatusCode && + response.body.Results[0].StatusCode === 'Error') + ) { + reject(error || response.body.Results[0].StatusMessage); + } else { + resolve(response.body.Results); + } + } + ); + }); + const intervalString = + (schedule._interval > 1 ? `${schedule._interval} ` : '') + + (schedule.RecurrenceType === 'Daily' + ? 'Day' + : schedule.RecurrenceType.slice(0, -2) + + (schedule._interval > 1 ? 's' : '')); + Util.logger.warn( + `Automation '${ + originalMetadata[key].name + }' deployed Active: runs every ${intervalString} starting ${ + schedule._StartDateTime.split('T').join(' ').split('.')[0] + } ${schedule._timezoneString}` + ); + } catch (ex) { + Util.logger.error( + `Could not start automation '${originalMetadata[key].name}': ${ex.message}` + ); + } + } + } else { + Util.logger.warn(`Automation '${originalMetadata[key].name}' deployed Paused`); + } + } + if (metadata[key].startSource) { + metadata[key].schedule = metadata[key].startSource.schedule; + + delete metadata[key].startSource; + } + if (metadata[key].schedule) { + metadata[key].schedule.typeId = metadata[key].schedule.scheduleTypeId; + delete metadata[key].schedule.scheduleTypeId; + } + } + } + + /** + * parses retrieved Metadata before saving + * @param {AutomationItem} metadata a single automation definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // automations are often skipped due to lack of support. + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.categoryId, + 'ID', + 'Path' + ); + delete metadata.categoryId; + if (metadata.r__folder_Path !== 'my automations') { + Util.logger.verbose( + `Automation '${metadata[this.definition.nameField]}' is located in subfolder ${ + metadata.r__folder_Path + }. Please note that creating automation folders is not supported via API and hence you will have to create it manually in the GUI if you choose to deploy this automation.` + ); + } + } catch (ex) { + // * don't exit on missing folder for automation + Util.logger.warn( + `${this.definition.typeName} '${metadata[this.definition.nameField]}': ${ + ex.message + }` + ); + } + try { + if (metadata.type === 'scheduled' && metadata.schedule && metadata.schedule.startDate) { + // Starting Source == 'Schedule' + + try { + if (this.definition.timeZoneMapping[metadata.schedule.timezoneName]) { + // if we found the id in our list, remove the redundant data + delete metadata.schedule.timezoneId; + } + } catch (ex) { + Util.logger.debug( + `Schedule name '${metadata.schedule.timezoneName}' not found in definition.timeZoneMapping` + ); + } + try { + // type 'Running' is temporary status only, overwrite with Scheduled for storage. + if (metadata.type === 'scheduled' && metadata.status === 'Running') { + metadata.status === 'Scheduled'; + } + } catch (ex) { + Util.metadataLogger( + 'error', + this.definition.type, + 'parseMetadata', + `${metadata.name} does not have a valid schedule setting. ` + ); + return null; + } + } else if (metadata.type === 'triggered' && metadata.fileTrigger) { + // Starting Source == 'File Drop' + // Do nothing for now + } + if (metadata.steps) { + for (const step of metadata.steps) { + for (const activity of step.activities) { + try { + // get metadata type of activity + activity.r__type = Util.inverseGet( + this.definition.activityTypeMapping, + activity.objectTypeId + ); + delete activity.objectTypeId; + // if no activityObjectId then either serialized activity + // (config in Automation ) or unconfigured so no further action to be taken + if ( + activity.activityObjectId === + '00000000-0000-0000-0000-000000000000' || + activity.activityObjectId == null || + !this.definition.dependencies.includes(activity.r__type) + ) { + // empty if block + } + // / if managed by cache we can update references to support deployment + else if ( + Definitions[activity.r__type] && + Definitions[activity.r__type]['idField'] && + this.cache[activity.r__type] + ) { + try { + activity.activityObjectId = Util.getFromCache( + this.cache, + activity.r__type, + activity.activityObjectId, + Definitions[activity.r__type].idField, + Definitions[activity.r__type].nameField + ); + } catch (e) { + // getFromCache throws error where the dependent metadata is not found + Util.logger.error( + `Missing ${activity.r__type} activity '${activity.name}'` + + ` in step ${step.stepNumber || step.step}.${ + activity.displayOrder + }` + + ` of Automation '${metadata.name}' (${e.message})` + ); + return null; + } + } else { + Util.logger.error( + `Missing ${activity.r__type} activity '${activity.name}'` + + ` in step ${step.stepNumber || step.step}.${ + activity.displayOrder + }` + + ` of Automation '${metadata.name}' (Not Found in Cache)` + ); + return null; + } + } catch (ex) { + Util.logger.warn( + `Excluding automation '${metadata.name}' from retrieve (ObjectType ${activity.objectTypeId} is unknown)` + ); + return null; + } + } + } + } + return JSON.parse(JSON.stringify(metadata)); + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata[this.definition.nameField]}': ${ + ex.message + }` + ); + return null; + } + } + + /** + * Builds a schedule object to be used for scheduling an automation + * based on combination of ical string and start/end dates. + * @param {AutomationSchedule} scheduleObject child of automation metadata used for scheduling + * @returns {AutomationScheduleSoap} Schedulable object for soap API (currently not rest supported) + */ + static _buildSchedule(scheduleObject) { + /** + * @type {AutomationScheduleSoap} + */ + const schedule = { Recurrence: {}, TimeZone: { IDSpecified: true } }; + // build recurrence + const recurHelper = {}; + // ical values are split by ; then have key values split by = + for (const obj of scheduleObject.icalRecur.split(';')) { + const a = obj.split('='); + recurHelper[a[0]] = a[1]; + } + // the ical schedule is all in caps but soap objects require Title Case. + const keyStem = recurHelper.FREQ.charAt(0) + recurHelper.FREQ.slice(1, -2).toLowerCase(); + + const patternType = recurHelper['BYMONTH'] + ? 'ByMonth' + : recurHelper['BYWEEK'] + ? 'ByWeek' + : recurHelper['BYDAY'] + ? 'ByDay' + : 'Interval'; + schedule.Recurrence[keyStem + 'lyRecurrencePatternType'] = patternType; + schedule.Recurrence['$'] = { + 'xsi:type': keyStem + 'lyRecurrence', + }; + schedule.RecurrenceType = keyStem + 'ly'; + if (keyStem === 'Dai') { + schedule.Recurrence['DayInterval'] = recurHelper.INTERVAL; + } else { + schedule.Recurrence[keyStem + 'Interval'] = recurHelper.INTERVAL; + } + schedule._interval = recurHelper.INTERVAL; // for CLI output only + + if (!['Minute', 'Hour', 'Dai'].includes(keyStem)) { + // todo: add support for weekly + // todo: add support for monthly + // todo: add support for yearly + throw new Error( + 'Scheduling automatically not supported for Weekly, Monthly and Yearly, please configure manually.' + ); + } + + if (this.definition.timeZoneMapping[scheduleObject.timezoneName]) { + scheduleObject.timezoneId = this.definition.timeZoneMapping[ + scheduleObject.timezoneName + ]; + } else { + Util.logger.error( + `Could not find timezone ${scheduleObject.timezoneName} in definition.timeZoneMapping` + ); + } + schedule.TimeZone.ID = scheduleObject.timezoneId; + schedule._timezoneString = this.definition.timeZoneDifference[scheduleObject.timezoneId]; + + // add tz to input date to ensure Date() creates a date object with the right tz + const inputStartDateString = scheduleObject.startDate + schedule._timezoneString; + + if (new Date(inputStartDateString) > new Date()) { + // if start date is in future take this + schedule.StartDateTime = scheduleObject.startDate; + schedule._StartDateTime = schedule.StartDateTime; // store copy for CLI output + } else { + // if start date is in past calculate new start date + const scheduledDate = new Date(inputStartDateString); + const futureDate = new Date(); + + if (keyStem === 'Dai') { + // keep time from template and start today if possible + if (scheduledDate.getHours() <= futureDate.getHours()) { + // hour on template has already passed today, start tomorrow + futureDate.setDate(futureDate.getDate() + 1); + } + futureDate.setHours(scheduledDate.getHours()); + futureDate.setMinutes(scheduledDate.getMinutes()); + } else if (keyStem === 'Hour') { + // keep minute and start next possible hour + if (scheduledDate.getMinutes() <= futureDate.getMinutes()) { + futureDate.setHours(futureDate.getHours() + 1); + } + futureDate.setMinutes(scheduledDate.getMinutes()); + } else if (keyStem === 'Minute') { + // schedule in next 15 minutes randomly to avoid that all automations run at exactly + // earliest start 1 minute from now + // the same time which would slow performance + futureDate.setMinutes(futureDate.getMinutes() + 1 + Math.ceil(Math.random() * 15)); + } + // return time as Dateobject + schedule.StartDateTime = futureDate; + const localTimezoneOffset = schedule.StartDateTime.getTimezoneOffset() / -60; + schedule._StartDateTime = this._calcTime(localTimezoneOffset, schedule.StartDateTime); // store copy for CLI output + } + + // The Create/Update API expects dates to be in US-Central time + // The retrieve API returns the date in whatever timezone one chose, hence we need to convert this upon upsert + schedule.StartDateTime = this._calcTime( + this.properties.options.serverTimeOffset, + schedule.StartDateTime, + schedule._timezoneString + ); + + if (recurHelper.UNTIL) { + schedule.RecurrenceRangeType = 'EndOn'; + schedule.EndDateTime = scheduleObject.endDate; + } else { + schedule.RecurrenceRangeType = 'EndAfter'; + schedule.Occurrences = recurHelper.COUNT; + } + + return schedule; + } + + /** + * used to convert dates to the system timezone required for startDate + * + * @param {number} offsetServer stack4: US Mountain time (UTC-7); other stacks: US Central (UTC-6) + * @param {string|Date} dateInput date in ISO format (2021-12-05T20:00:00.983) + * @param {string} [offsetInput] timzone difference (+02:00) + * @returns {string} date in server + */ + static _calcTime(offsetServer, dateInput, offsetInput) { + // get UTC time in msec + let utc; + if ('string' === typeof dateInput) { + utc = new Date(dateInput + offsetInput).getTime(); + } else { + utc = dateInput.getTime(); + } + + // create new Date object reflecting SFMC's servertime + const dateServer = new Date(utc + 3600000 * offsetServer); + + // return time as a string without trailing "Z" + return dateServer.toISOString().slice(0, -1); + } +} + +// Assign definition to static attributes +Automation.definition = Definitions.automation; +Automation.cache = {}; +Automation.client = undefined; + +module.exports = Automation; diff --git a/lib/metadataTypes/Campaign.js b/lib/metadataTypes/Campaign.js new file mode 100644 index 000000000..56998c142 --- /dev/null +++ b/lib/metadataTypes/Campaign.js @@ -0,0 +1,122 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * Campaign MetadataType + * @augments MetadataType + */ +class Campaign extends MetadataType { + /** + * Retrieves Metadata of campaigns. Afterwards, starts metadata retrieval for their campaign assets + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return new Promise((resolve) => { + const config = { + props: null, + options: {}, + }; + this.client.campaign(config).get(async (error, response) => { + if (error) { + throw new Error(error); + } + // Save metadata structure + const metadata = Campaign.parseResponseBody(response.body); + // Util.logger.info('Campaign.retrieve:: Parsed Response'); + // Write metadata to local filesystem + for (const metadataEntry in metadata) { + File.writeJSONToFile( + retrieveDir + '/campaign/' + metadataEntry + '/', + metadataEntry + '.' + this.definition.type + '-meta', + metadata[metadataEntry] + ); + } + const campaignAssetPromises = []; + for (const campaign in metadata) { + try { + campaignAssetPromises.push( + Campaign._retrieveCampaignAsset( + retrieveDir, + metadata[campaign].id, + metadata[campaign].name + ) + ); + } catch (ex) { + Util.logger.error(ex.message); + } + } + const values = await Promise.all(campaignAssetPromises); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(metadata).length})` + ); + resolve({ metadata: values, type: 'campaign' }); + }); + }); + } + + /** + * Retrieves campaign asset for a specific campaign + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {Number} id id of the parent campaign + * @param {String} name name of the parent campaign + * @returns {Promise} Promise + */ + static _retrieveCampaignAsset(retrieveDir, id, name) { + return new Promise((resolve, reject) => { + const config = { + props: { id: id }, + options: {}, + }; + this.client.campaignAsset(config).get((error, response) => { + if (error) { + reject(error); + } else { + const assets = Campaign._parseAssetResponseBody(response.body); + + for (const asset in assets) { + File.writeJSONToFile( + retrieveDir + '/campaign/' + name + '/assets/', + asset + '.campaignAsset-meta', + assets[asset] + ); + } + resolve(assets); + } + }); + }); + } + + /** + * Parses campaign asset response body and returns metadata entries mapped to their id + * @param {Object} body response body of metadata retrieval + * @returns {Object} keyField => metadata map + */ + static _parseAssetResponseBody(body) { + const metadataStructure = {}; + if (body.items) { + for (let i = 0; i < body.items.length; i++) { + const item = body.items[i]; + // if needed: Makes sure files can be saved on windows operating systems + // .replace(/[/\\?%*:|"<>]/g, '-'); + const key = item.id; + metadataStructure[key] = item; + } + } else { + Util.logger.debug( + `Campaign._parseAssetResponseBody:: Format of 'body' parameter wrong: `, + body + ); + } + return metadataStructure; + } +} +// Assign definition to static attributes +Campaign.definition = require('../MetadataTypeDefinitions').campaign; +Campaign.client = undefined; +Campaign.cache = {}; + +module.exports = Campaign; diff --git a/lib/metadataTypes/ContentArea.js b/lib/metadataTypes/ContentArea.js new file mode 100644 index 000000000..9f6773dcc --- /dev/null +++ b/lib/metadataTypes/ContentArea.js @@ -0,0 +1,72 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * ContentArea MetadataType + * @augments MetadataType + */ +class ContentArea extends MetadataType { + /** + * Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + Util.logger.warn( + 'Classic Content Areas are deprecated and will be discontinued by SFMC in the near future. Ensure that you migrate any existing Content Areas to Content Builder as soon as possible.' + ); + // !dont activate `await File.initPrettier('html');` as we only want to retrieve for migration and formatting might mess with the outcome + return super.retrieveSOAPgeneric(retrieveDir); + } + /** + * manages post retrieve steps + * @param {Object} metadata a single query + * @returns {Object[]} Array with one metadata object and one query string + */ + static postRetrieveTasks(metadata) { + return this.parseMetadata(metadata); + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single query activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'Path' + ); + } catch (ex) { + Util.logger.debug(`Classic Content Area '${metadata.CustomerKey}': ${ex.message}`); + // classic content blocks that reside in the main folder are + // saved with CategoryID=0, instead of to the actual ID of + // their parent root folder. + metadata.r__folder_Path = 'my contents'; + } + + // extract code + const codeArr = [ + { + subFolder: null, + fileName: metadata.CustomerKey, + fileExt: 'html', + content: metadata.Content, + }, + ]; + delete metadata.Content; + + return { json: metadata, codeArr: codeArr, subFolder: null }; + } +} + +// Assign definition to static attributes +ContentArea.definition = require('../MetadataTypeDefinitions').contentArea; + +module.exports = ContentArea; diff --git a/lib/metadataTypes/DataExtension.js b/lib/metadataTypes/DataExtension.js new file mode 100644 index 000000000..0c3ecb9b9 --- /dev/null +++ b/lib/metadataTypes/DataExtension.js @@ -0,0 +1,994 @@ +'use strict'; + +const jsonToTable = require('json-to-table'); +const MetadataType = require('./MetadataType'); +const DataExtensionField = require('./DataExtensionField'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * @typedef {Object} DataExtensionItem + * @property {string} CustomerKey key + * @property {string} Name name + * @property {string} Description - + * @property {'true'|'false'} IsSendable - + * @property {'true'|'false'} IsTestable - + * @property {Object} SendableDataExtensionField - + * @property {string} SendableDataExtensionField.Name - + * @property {Object} SendableSubscriberField - + * @property {string} SendableSubscriberField.Name - + * @property {DataExtensionField.DataExtensionFieldItem[]} Fields list of DE fields + * @property {'dataextension'|'salesforcedataextension'|'synchronizeddataextension'|'shared_dataextension'|'shared_salesforcedataextension'} r__folder_ContentType retrieved from associated folder + * @property {string} r__folder_Path folder path in which this DE is saved + * @property {string} [CategoryID] holds folder ID, replaced with r__folder_Path during retrieve + * @property {string} [r__dataExtensionTemplate_Name] name of optionally associated DE template + * @property {Object} [Template] - + * @property {string} [Template.CustomerKey] key of optionally associated DE teplate + * + * @typedef {Object.} DataExtensionMap + */ + +/** + * DataExtension MetadataType + * @augments MetadataType + */ +class DataExtension extends MetadataType { + /** + * Upserts dataExtensions after retrieving them from source and target to compare + * if create or update operation is needed. + * @param {DataExtensionMap} desToDeploy dataExtensions mapped by their customerKey + * @param {Object} _ - + * @param {Util.BuObject} buObject properties for auth + * @returns {Promise} Promise + */ + static async upsert(desToDeploy, _, buObject) { + Util.logger.info('- Retrieve target metadata for comparison with deploy metadata'); + const results = await this.retrieveForCache(buObject); + const targetMetadata = results.metadata; + Util.logger.info('- Retrieved target metadata'); + /** @type {Promise[]} */ + const deCreatePromises = []; + /** @type {Promise[]} */ + const deUpdatePromises = []; + for (const dataExtension in desToDeploy) { + if (desToDeploy[dataExtension].Name.startsWith('_')) { + Util.logger.warn( + '- Cannot Upsert Strongly Typed Data Extensions - skipping ' + + desToDeploy[dataExtension].Name + ); + continue; + } + if ( + buObject.eid !== buObject.mid && + desToDeploy[dataExtension].r__folder_Path.startsWith('shared_') + ) { + // this needs to be run before executing preDeployTasks() + Util.logger.warn( + `- Cannot Create/Update a Shared Data Extesion from the Child BU - skipping ${desToDeploy[dataExtension].Name}` + ); + continue; + } + try { + desToDeploy[dataExtension] = await this.preDeployTasks(desToDeploy[dataExtension]); + } catch (ex) { + // problem with retrieving folder for this DE found + // output error & remove from deploy list + Util.logger.error( + `- dataExtension ${desToDeploy[dataExtension].Name}: ${ex.message}` + ); + delete desToDeploy[dataExtension]; + // skip rest of handling for this DE + continue; + } + // Error if SendableSubscriberField.Name = '_SubscriberKey' even though it is retrieved like that + // Therefore map it to 'Subscriber Key'. Retrieving afterward still results in '_SubscriberKey' + if ( + desToDeploy[dataExtension].SendableSubscriberField && + desToDeploy[dataExtension].SendableSubscriberField.Name === '_SubscriberKey' + ) { + desToDeploy[dataExtension].SendableSubscriberField.Name = 'Subscriber Key'; + } + if (targetMetadata[dataExtension]) { + // Update dataExtension + Columns if they already exist; Create them if not + // Modify columns for update call + DataExtension.prepareDeployColumnsOnUpdate( + desToDeploy[dataExtension].Fields, + targetMetadata[dataExtension].Fields + ); + // data extension already exists in target and needs to be updated + deUpdatePromises.push(DataExtension.update(desToDeploy[dataExtension])); + } else { + // data extension does not exist in target and has to be created + deCreatePromises.push(DataExtension.create(desToDeploy[dataExtension])); + } + } + if (deUpdatePromises.length) { + Util.logger.info( + 'Please note that Data Retention Policies can only be set during creation, not during update.' + ); + } + + const createResults = (await Promise.allSettled(deCreatePromises)).filter( + this._filterUpsertResults + ); + const updateResults = (await Promise.allSettled(deUpdatePromises)).filter( + this._filterUpsertResults + ); + const successfulResults = [...createResults, ...updateResults]; + + Util.metadataLogger( + 'info', + this.definition.type, + 'upsert', + `${createResults.length} of ${deCreatePromises.length} created / ${updateResults.length} of ${deUpdatePromises.length} updated` + ); + if (successfulResults.length > 0) { + const metadataResults = successfulResults + .map((r) => r.value.body.Results[0].Object) + .map((r) => { + // if only one fields added will return object otherwise array + if (r.Fields && r.Fields.Field && Array.isArray(r.Fields.Field)) { + r.Fields = r.Fields.Field; + } else if (r.Fields && r.Fields.Field) { + r.Fields = [r.Fields.Field]; + } + return r; + }); + const formattedResults = super.parseResponseBody({ Results: metadataResults }); + return formattedResults; + } else { + return {}; + } + } + + /** + * helper for upsert() + * @param {Object} res - + * @returns {Boolean} true: keep, false: discard + */ + static _filterUpsertResults(res) { + if (res.status === 'rejected') { + // promise rejects, whole request failed + Util.logger.error('- error upserting dataExtension: ' + res.reason); + return false; + } else if (res.value.results) { + Util.logger.error( + '- error upserting dataExtension: ' + + (res.value.results[0].Object ? res.value.results[0].Object.Name : '') + + '. ' + + res.value.results[0].StatusMessage + ); + return false; + } else if (res.status === 'fulfilled' && res.value && res.value.faultstring) { + // can happen that the promise does not reject, but that it resolves an error + Util.logger.error('- error upserting dataExtension: ' + res.value.faultstring); + return false; + } else { + return true; + } + } + + /** + * Mofifies passed deployColumns for update by mapping ObjectID to their target column's values. + * Removes FieldType field if its the same in deploy and target column, because it results in an error even if its of the same type + * + * @param {DataExtensionField.DataExtensionFieldItem[]} deployColumns Columns of data extension that will be deployed + * @param {DataExtensionField.DataExtensionFieldItem[]} targetColumns Columns of data extension that currently exists in target + * @returns {void} + */ + static prepareDeployColumnsOnUpdate(deployColumns, targetColumns) { + // Map data extension column ObjectIDs to their target, because they are environment specific + for (const column in deployColumns) { + const deployColumn = deployColumns[column]; + // Check if column exists in target + if (targetColumns[column]) { + // Map ObjectID to value of target, because DataExtensionField updates are based on ObjectID + deployColumn.ObjectID = targetColumns[column].ObjectID; + // Remove FieldType if it is the same in target, because an error occurs if a FieldTypes gets passed on a field update (even if its the same type) + if (targetColumns[column].FieldType === deployColumn.FieldType) { + delete deployColumn.FieldType; + } else { + // Updating to a new FieldType will result in an error + Util.logger.warn( + 'DataExtension.prepareDeployColumnsOnUpdate:: Cannot update FieldType of field: ' + + deployColumn.CustomerKey + ); + } + } else { + // Field doesn't exist in target, therefore Remove ObjectID if present + delete deployColumn.ObjectID; + } + } + } + + /** + * Create a single dataExtension. Also creates their columns in 'dataExtension.columns' + * @param {DataExtensionItem} metadata single metadata entry + * @returns {Promise} Promise + */ + static create(metadata) { + return new Promise((resolve) => { + this.removeNotCreateableFields(metadata); + this._cleanupRetentionPolicyFields(metadata); + const dataExtensionFields = metadata.Fields; + delete metadata.Fields; + + const config = { + props: metadata, + columns: dataExtensionFields, + }; + this.client.dataExtension(config).post((error, response) => { + if (error) { + Util.logger.error('- error creating dataExtension: ' + metadata.CustomerKey); + resolve(error); + } else { + Util.logger.info('- created dataExtension: ' + metadata.CustomerKey); + resolve(response); + } + }); + }); + } + + /** + * SFMC saves a date in "RetainUntil" under certain circumstances even + * if that field duplicates whats in the period fields + * during deployment, that extra value is not accepted by the APIs which is why it needs to be removed + * @private + * @param {DataExtensionItem} metadata single metadata entry + * @returns {void} + */ + static _cleanupRetentionPolicyFields(metadata) { + if ( + metadata.DataRetentionPeriodLength && + metadata.DataRetentionPeriodUnitOfMeasure && + metadata.RetainUntil !== '' + ) { + metadata.RetainUntil = ''; + Util.logger.warn( + `RetainUntil date was reset automatically because RetentionPeriod info was found in: ${metadata.CustomerKey}` + ); + } + } + /** + * Updates a single dataExtension. Also updates their columns in 'dataExtension.columns' + * @param {DataExtensionItem} metadata single metadata entry + * @returns {Promise} Promise + */ + static async update(metadata) { + // retrieve exsiting fields + const fieldsObj = await this._retrieveFields(['Name'], { + filter: { + leftOperand: 'DataExtension.CustomerKey', + operator: 'equals', + rightOperand: metadata.CustomerKey, + }, + }); + const existingFieldNames = Object.keys(fieldsObj).map((key) => fieldsObj[key].Name); + return new Promise((resolve) => { + this.removeNotUpdateableFields(metadata); + // find fields to add + const newFields = metadata.Fields.filter((a) => !existingFieldNames.includes(a.Name)); + delete metadata.Fields; + + const config = { + props: metadata, + columns: newFields, + }; + this.client.dataExtension(config).patch((error, response) => { + if (error) { + Util.logger.error('- error updating dataExtension: ' + metadata.CustomerKey); + resolve(error); + } else { + Util.logger.info('- updated dataExtension: ' + metadata.CustomerKey); + resolve(response); + } + }); + }); + } + + /** + * Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionColumn metadata retrieval + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Util.BuObject} buObject properties for auth + * @returns {Promise<{metadata:DataExtensionMap,type:string}>} Promise of item map + */ + static async retrieve(retrieveDir, additionalFields, buObject) { + let metadata = await this._retrieveAll(additionalFields); + // in case of cache dont get fields + if (metadata && retrieveDir) { + // get fields from API + const fieldsObj = await this._retrieveFields(additionalFields); + const fieldKeys = Object.keys(fieldsObj); + // add fields to corresponding DE + fieldKeys.forEach((key) => { + const field = fieldsObj[key]; + metadata[field.DataExtension.CustomerKey].Fields.push(field); + }); + + // sort fields by Ordinal value (API returns field unsorted) + for (const metadataEntry in metadata) { + metadata[metadataEntry].Fields.sort(DataExtensionField.sortDeFields); + } + + // remove attributes that we do not want to retrieve + // * do this after sorting on the DE's field list + fieldKeys.forEach((key) => { + DataExtensionField.postRetrieveTasks(fieldsObj[key], true); + }); + } + if (!retrieveDir && buObject.eid !== buObject.mid) { + // for caching, we want to retrieve shared DEs as well from the instance parent BU + Util.logger.info('- Caching dependent Metadata: dataExtension (shared via _ParentBU_)'); + /** @type {Util.BuObject} */ + const buObjectParentBu = { + clientId: this.properties.credentials[buObject.credential].clientId, + clientSecret: this.properties.credentials[buObject.credential].clientSecret, + tenant: this.properties.credentials[buObject.credential].tenant, + eid: this.properties.credentials[buObject.credential].eid, + mid: this.properties.credentials[buObject.credential].businessUnits[ + Util.parentBuName + ], + businessUnit: Util.parentBuName, + credential: buObject.credential, + }; + const clientBackup = this.client; + try { + this.client = await Util.getETClient(buObjectParentBu); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const metadataParentBu = await this._retrieveAll(additionalFields); + + // get the types and clean out non-shared ones + const folderTypesFromParent = require('../MetadataTypeDefinitions').folder + .folderTypesFromParent; + for (const metadataEntry in metadataParentBu) { + try { + // get the data extension type from the folder + const folderContentType = Util.getFromCache( + this.cache, + 'folder', + metadataParentBu[metadataEntry].CategoryID, + 'ID', + 'ContentType' + ); + if (!folderTypesFromParent.includes(folderContentType)) { + delete metadataParentBu[metadataEntry]; + } + } catch (ex) { + delete metadataParentBu[metadataEntry]; + } + } + + // revert client to current default + this.client = clientBackup; + + // make sure to overwrite parent bu DEs with local ones + metadata = { ...metadataParentBu, ...metadata }; + } + + if (retrieveDir) { + const savedMetadata = await this.saveResults(metadata, retrieveDir, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { + await this.document(buObject, savedMetadata); + } + } + return { metadata: metadata, type: 'dataExtension' }; + } + /** + * manages post retrieve steps + * @param {DataExtensionItem} metadata a single dataExtension + * @param {string} [_] unused + * @param {boolean} [isTemplating] signals that we are retrieving templates + * @returns {DataExtensionItem} metadata + */ + static async postRetrieveTasks(metadata, _, isTemplating) { + if (!metadata.Fields || !metadata.Fields.length) { + // assume we were in deploy mode. retrieve fields. + const tempList = {}; + tempList[metadata[this.definition.keyField]] = metadata; + await this._retrieveFieldsForSingleDe(tempList, metadata[this.definition.keyField]); + } + // if retrieving template, replace the name with customer key if that wasn't already the case + if (isTemplating) { + const warningMsg = + 'Ensure that Queries that write into this DE are updated with the new key before deployment.'; + this.overrideKeyWithName(metadata, warningMsg); + } + return this._parseMetadata(JSON.parse(JSON.stringify(metadata))); + } + + /** + * Helper to retrieve Data Extension Fields + * @private + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Object} [options] options (e.g. continueRequest) + * @returns {Promise} Promise of items + */ + static async _retrieveFields(additionalFields, options) { + if (!options) { + // dont print this during updates or templating which retrieves fields DE-by-DE + Util.logger.info('- Caching dependent Metadata: dataExtensionField'); + } + DataExtensionField.cache = this.metadata; + DataExtensionField.client = this.client; + DataExtensionField.properties = this.properties; + + const response = await DataExtensionField.retrieveForCache(options, additionalFields); + return response.metadata; + } + /** + * helps retrieving fields during templating and deploy where we dont want the full list + * @private + * @param {DataExtensionMap} metadata list of DEs + * @param {string} customerKey external key of single DE + * @returns {Promise} updates are made directly to `metadata` + */ + static async _retrieveFieldsForSingleDe(metadata, customerKey) { + const fieldOptions = { + filter: { + leftOperand: 'DataExtension.CustomerKey', + operator: 'equals', + rightOperand: customerKey, + }, + }; + const fieldsObj = await this._retrieveFields(null, fieldOptions); + + DataExtensionField.cache = this.metadata; + DataExtensionField.client = this.client; + DataExtensionField.properties = this.properties; + const fieldArr = DataExtensionField.convertToSortedArray(fieldsObj); + + // remove attributes that we do not want to retrieve + // * do this after sorting on the DE's field list + fieldArr.forEach((field) => { + DataExtensionField.postRetrieveTasks(field, true); + }); + + metadata[customerKey].Fields = fieldArr; + } + + /** + * prepares a DataExtension for deployment + * @param {DataExtensionItem} metadata a single data Extension + * @returns {Promise} Promise of updated single DE + */ + static async preDeployTasks(metadata) { + // folder + metadata.CategoryID = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + delete metadata.r__folder_Path; + + // DataExtensionTemplate + if (metadata.r__dataExtensionTemplate_Name) { + // remove templated fields + for (const templateField of this.definition.templateFields[ + metadata.r__dataExtensionTemplate_Name + ]) { + for (let index = 0; index < metadata.Fields.length; index++) { + const element = metadata.Fields[index]; + if (element.Name === templateField) { + metadata.Fields.splice(index, 1); + Util.logger.debug(`Removed template field: ${templateField}`); + break; + } + } + } + + // get template's CustomerKey + try { + metadata.Template = { + CustomerKey: Util.getFromCache( + this.cache, + 'dataExtensionTemplate', + metadata.r__dataExtensionTemplate_Name, + 'Name', + 'CustomerKey' + ), + }; + delete metadata.r__dataExtensionTemplate_Name; + } catch (ex) { + Util.logger.debug(ex.message); + // It is assumed that non-supported types would not have been converted to r__dataExtensionTemplate_Name upon retrieve. + // Deploying to same BU therefore still works! + // A workaround for cross-BU deploy exists but it's likely not beneficial to explain to users: + // Create a DE based on the not-supported template on the target BU, retrieve it, copy the Template.CustomerKey into the to-be-deployed DE (or use mcdev-templating), done + throw new Error( + `Skipping DataExtension '${ + metadata[this.definition.nameField] + }': Could not find specified DataExtension Template. Please note that DataExtensions based on SMSMessageTracking and SMSSubscriptionLog cannot be deployed automatically across BUs at this point.` + ); + } + } + + // contenttype + delete metadata.r__folder_ContentType; + + return metadata; + } + + /** + * Saves json content to a html table in the local file system. Will create the parent directory if it does not exist. + * The json's first level of keys must represent the rows and the secend level the columns + * @private + * @param {DataExtensionItem} json single dataextension + * @param {Array} tabled prepped array for output in tabular format + * @returns {string} file content + */ + static _generateDocHtml(json, tabled) { + let output = + ' '; + + output += `

${json.CustomerKey}

`; + if (json.CustomerKey !== json.Name) { + output += `

Error - Name not equal to External Key: ${json.Name}

`; + } + + output += `

Description: ${json.Description || 'n/a'}

`; + output += `

Folder: ${ + json.r__folder_Path + ? json.r__folder_Path + : 'Hidden! Could not find folder with ID ' + json.CategoryID + '' + }

`; + output += `

Fields in table: ${tabled.length - 1}

`; + output += '

Sendable: '; + if (json.IsSendable === 'true') { + output += + 'Yes (' + + json.SendableDataExtensionField.Name + + ' to ' + + json.SendableSubscriberField.Name + + ')

\n\n'; + } else { + output += `No

\n\n`; + } + output += `

Testable: ${json.IsTestable === 'true' ? 'Yes' : 'No'}

\n\n`; + if (json.r__dataExtensionTemplate_Name) { + output += `

Template: ${json.r__dataExtensionTemplate_Name}

`; + } + + output += ''; + tabled[0].forEach((element) => { + output += ''; + }); + output += ''; + for (let i = 1; i < tabled.length; i++) { + output += ''; + tabled[i].forEach((field) => { + output += ``; + }); + output += ''; + } + output += '
' + element + '
${field}
'; + return output; + } + + /** + * Experimental: Only working for DataExtensions: + * Saves json content to a html table in the local file system. Will create the parent directory if it does not exist. + * The json's first level of keys must represent the rows and the secend level the columns + * @private + * @param {DataExtensionItem} json dataextension + * @param {Array} tabled prepped array for output in tabular format + * @returns {string} file content + */ + static _generateDocMd(json, tabled) { + let output = `## ${json.CustomerKey}\n\n`; + if (json.CustomerKey !== json.Name) { + output += `**Name** (not equal to External Key)**:** ${json.Name}\n\n`; + } + + output += + `**Description:** ${json.Description || 'n/a'}\n\n` + + `**Folder:** ${ + json.r__folder_Path + ? json.r__folder_Path + : '_Hidden! Could not find folder with ID ' + json.CategoryID + '_' + }/\n\n` + + `**Fields in table:** ${tabled.length - 1}\n\n`; + output += '**Sendable:** '; + if (json.IsSendable === 'true') { + output += + 'Yes (`' + + json.SendableDataExtensionField.Name + + '` to `' + + json.SendableSubscriberField.Name + + '`)\n\n'; + } else { + output += `No\n\n`; + } + output += `**Testable:** ${json.IsTestable === 'true' ? 'Yes' : 'No'}\n\n`; + if (json.r__dataExtensionTemplate_Name) { + output += `**Template:** ${json.r__dataExtensionTemplate_Name}\n\n`; + } + + let tableSeparator = ''; + tabled[0].forEach((column) => { + output += `| ${column} `; + tableSeparator += '| --- '; + }); + output += `|\n${tableSeparator}|\n`; + for (let i = 1; i < tabled.length; i++) { + tabled[i].forEach((field) => { + output += `| ${field} `; + }); + output += '|\n'; + } + return output; + } + + /** + * Saves json content to a html table in the local file system. Will create the parent directory if it does not exist. + * The json's first level of keys must represent the rows and the secend level the columns + * @private + * @param {string} directory directory the file will be written to + * @param {string} filename name of the file without '.json' ending + * @param {DataExtensionItem} json dataextension.columns + * @param {'html'|'md'} mode html or md + * @param {string[]} [fieldsToKeep] list of keys(columns) to show. This will also specify + * @returns {Promise} Promise of success of saving the file + */ + static async _writeDoc(directory, filename, json, mode, fieldsToKeep) { + if (!File.existsSync(directory)) { + File.mkdirpSync(directory); + } + let fieldsJson = Object.values(json.Fields); + if (fieldsToKeep) { + const newJson = []; + fieldsJson.forEach((element) => { + const newJsonElement = {}; + fieldsToKeep.forEach((field) => { + newJsonElement[field] = element[field]; + }); + newJson.push(newJsonElement); + }); + fieldsJson = newJson; + } + const tabled = jsonToTable(fieldsJson); + let output; + if (mode === 'html') { + output = DataExtension._generateDocHtml(json, tabled); + } else if (mode === 'md') { + output = DataExtension._generateDocMd(json, tabled); + } + try { + // write to disk + await File.writeToFile(directory, filename + '.dataExtension', mode, output); + } catch (ex) { + Util.logger.error(`DataExtension.writeDeToX(${mode}):: error | ` + ex.message); + } + } + /** + * Parses metadata into a readable Markdown/HTML format then saves it + * @param {Util.BuObject} buObject properties for auth + * @param {DataExtensionMap} [metadata] a list of dataExtension definitions + * @param {boolean} [isDeploy] used to skip non-supported message during deploy + * @returns {Promise} - + */ + static async document(buObject, metadata, isDeploy) { + if (!metadata) { + metadata = this.readBUMetadataForType( + File.normalizePath([ + this.properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + ]), + true + ).dataExtension; + } + const docPath = File.normalizePath([ + this.properties.directories.dataExtension, + buObject.credential, + buObject.businessUnit, + ]); + if (!metadata || !Object.keys(metadata).length) { + // as part of retrieve & manual execution we could face an empty folder + return; + } + if (!isDeploy) { + File.removeSync(docPath); + } + const columnsToIterateThrough = ['IsNullable', 'IsPrimaryKey']; + const columnsToPrint = [ + 'Name', + 'FieldType', + 'MaxLength', + 'IsPrimaryKey', + 'IsNullable', + 'DefaultValue', + ]; + for (const customerKey in metadata) { + if (metadata[customerKey].Fields && metadata[customerKey].Fields.length) { + metadata[customerKey].Fields.forEach((field) => { + field.IsNullable = (!(field.IsRequired === 'true')).toString(); + columnsToIterateThrough.forEach((key) => { + if (field[key] === 'true') { + field[key] = '+'; + } else if (field[key] === 'false') { + field[key] = '-'; + } + }); + }); + + if (['html', 'both'].includes(this.properties.options.documentType)) { + this._writeDoc( + docPath + '/', + customerKey, + metadata[customerKey], + 'html', + columnsToPrint + ); + } + if (['md', 'both'].includes(this.properties.options.documentType)) { + this._writeDoc( + docPath + '/', + customerKey, + metadata[customerKey], + 'md', + columnsToPrint + ); + } + } + } + if (['html', 'both'].includes(this.properties.options.documentType)) { + Util.logger.info(`Created ${docPath}/*.dataExtension.html`); + } + if (['md', 'both'].includes(this.properties.options.documentType)) { + Util.logger.info(`Created ${docPath}/*.dataExtension.md`); + } + } + + /** + * Delete a data extension from the specified business unit + * @param {Object} buObject references credentials + * @param {string} customerKey Identifier of data extension + * @returns {Promise} - + */ + static async deleteByKey(buObject, customerKey) { + let client; + try { + client = await Util.getETClient(buObject); + } catch (ex) { + Util.logger.error(ex.message); + return; + } + const config = { + props: { CustomerKey: customerKey }, + }; + + client.dataExtension(config).delete((error, response) => { + if (error && error.results && error.results[0]) { + if (error.results[0].ErrorCode === '310007') { + Util.logger.error( + 'mcdev.deleteDE:: It seems the DataExtension you were trying to delete does not exist on the given BU or its External Key was changed.' + ); + } else { + Util.logger.error('mcdev.deleteDE:: ' + error.results[0].StatusMessage); + } + } else if (error) { + Util.logger.error('mcdev.deleteDE:: ' + JSON.stringify(error)); + } else if ( + response && + response.body && + response.body.Results && + response.body.Results[0] + ) { + Util.logger.info( + `mcdev.deleteDE:: Success: ${response.body.Results[0].StatusMessage} (${customerKey})` + ); + // delete local copy: retrieve/cred/bu/dataExtension/...json + const jsonFile = File.normalizePath([ + this.properties.directories.retrieve, + buObject.credential, + buObject.businessUnit, + this.definition.type, + `${customerKey}.${this.definition.type}-meta.json`, + ]); + if (File.existsSync(jsonFile)) { + File.unlinkSync(jsonFile); + } + // delete local copy: doc/dataExtension/cred/bu/...md + const mdFile = File.normalizePath([ + this.properties.directories.dataExtension, + buObject.credential, + buObject.businessUnit, + `${customerKey}.${this.definition.type}.md`, + ]); + if (File.existsSync(mdFile)) { + File.unlinkSync(mdFile); + } + } else { + Util.logger.info('mcdev.deleteDE:: Success: ' + JSON.stringify(response)); + } + }); + } + + /** + * Retrieves folder metadata into local filesystem. Also creates a uniquePath attribute for each folder. + * @param {Object} buObject properties for auth + * @returns {Promise} Promise + */ + static async retrieveForCache(buObject) { + return this.retrieve(null, ['ObjectID', 'CustomerKey', 'Name'], buObject); + } + /** + * Retrieves dataExtension metadata in template format. + * @param {string} templateDir Directory where retrieved metadata directory will be saved + * @param {string} name name of the metadata item + * @param {Util.TemplateMap} variables variables to be replaced in the metadata + * @returns {Promise<{metadata:DataExtensionMap,type:string}>} Promise of items + */ + static async retrieveAsTemplate(templateDir, name, variables) { + const templateFields = this.getFieldNamesToRetrieve(); + const dataExtensionOptions = { + props: templateFields, + filter: { + leftOperand: 'Name', + operator: 'equals', + rightOperand: name, + }, + }; + + const p = new Promise((resolve) => { + this.client.dataExtension(dataExtensionOptions).get((error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response); + } + }); + }); + const response = await p; + /** @type DataExtensionMap */ + const metadata = super.parseResponseBody(response.body); + const customerKey = Object.keys(metadata)[0]; + await this._retrieveFieldsForSingleDe(metadata, customerKey); + + for (const key in metadata) { + try { + // API returns field unsorted + metadata[key].Fields.sort((a, b) => a.Ordinal - b.Ordinal); + + const metadataCleaned = JSON.parse( + JSON.stringify(await this.postRetrieveTasks(metadata[key], null, !!variables)) + ); + + this.keepTemplateFields(metadataCleaned); + const metadataTemplated = JSON.parse( + Util.replaceByObject(JSON.stringify(metadataCleaned), variables) + ); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + metadataTemplated[this.definition.keyField] + + '.' + + this.definition.type + + '-meta', + metadataTemplated + ); + } catch (ex) { + Util.metadataLogger('error', this.definition.type, 'retrieve', ex, key); + } + } + + Util.logger.info( + `DataExtension.retrieveAsTemplate:: All records written to filesystem (${customerKey})` + ); + return { metadata: metadata, type: 'dataExtension' }; + } + + /** + * parses retrieved Metadata before saving + * @private + * @param {DataExtensionItem} metadata a single dataExtension definition + * @returns {DataExtensionItem} a single dataExtension definition + */ + static _parseMetadata(metadata) { + let error = false; + let verbose = false; + // data extension type (from folder) + try { + metadata.r__folder_ContentType = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'ContentType' + ); + } catch (ex) { + if (/(_Salesforce)(_\d\d?\d?)?$/.test(metadata.Name)) { + verbose = true; + metadata.r__folder_ContentType = 'synchronizeddataextension'; + } else { + error = true; + Util.logger.warn(`Data Extension '${metadata.Name}': ${ex.message}`); + } + } + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'Path' + ); + delete metadata.CategoryID; + } catch (ex) { + if (/(_Salesforce)(_\d\d?\d?)?$/.test(metadata.Name)) { + metadata.r__folder_Path = 'Synchronized Data Extensions'; + delete metadata.CategoryID; + + if (!verbose) { + Util.logger.verbose( + `Synchronized Data Extension of other BU found: '${metadata.Name}'. Setting folder to 'Synchronized Data Extensions'` + ); + } + } else if (!error) { + Util.logger.error(`Data Extension '${metadata.Name}': ${ex.message}`); + } + } + // DataExtensionTemplate + if (metadata.Template && metadata.Template.CustomerKey) { + try { + metadata.r__dataExtensionTemplate_Name = Util.getFromCache( + this.cache, + 'dataExtensionTemplate', + metadata.Template.CustomerKey, + 'CustomerKey', + 'Name' + ); + delete metadata.Template; + } catch (ex) { + Util.logger.debug(ex.message); + // Let's allow retrieving such DEs but warn that they cannot be deployed to another BU. + // Deploying to same BU still works! + // A workaround exists but it's likely not beneficial to explain to users: + // Create a DE based on the not-supported template on the target BU, retrieve it, copy the Template.CustomerKey into the to-be-deployed DE (or use mcdev-templating), done + Util.logger.warn( + `Issue with DataExtension '${ + metadata[this.definition.nameField] + }': Could not find specified DataExtension Template. Please note that DataExtensions based on SMSMessageTracking and SMSSubscriptionLog cannot be deployed automatically across BUs at this point.` + ); + } + } + + return metadata; + } + + /** + * Retrieves dataExtension metadata. Afterwards starts retrieval of dataExtensionColumn metadata retrieval + * @private + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @returns {Promise} keyField => metadata map + */ + static async _retrieveAll(additionalFields) { + const metadata = (await super.retrieveSOAPgeneric(null, null, null, additionalFields)) + .metadata; + for (const key in metadata) { + // some system data extensions do not have CategoryID which throws errors in other places. These do not need to be parsed + if (!metadata[key].CategoryID) { + delete metadata[key]; + } else { + metadata[key].Fields = []; + } + } + return metadata; + } +} + +// Assign definition to static attributes +DataExtension.definition = require('../MetadataTypeDefinitions').dataExtension; +DataExtension.client = undefined; +DataExtension.cache = {}; + +module.exports = DataExtension; diff --git a/lib/metadataTypes/DataExtensionField.js b/lib/metadataTypes/DataExtensionField.js new file mode 100644 index 000000000..42dfdaeb3 --- /dev/null +++ b/lib/metadataTypes/DataExtensionField.js @@ -0,0 +1,104 @@ +'use strict'; + +/** + * @typedef {Object} DataExtensionFieldItem + * @property {string} [ObjectID] id + * @property {string} [CustomerKey] key + * @property {Object} [DataExtension] - + * @property {string} DataExtension.CustomerKey key of DE + * @property {string} Name name + * @property {string} DefaultValue - + * @property {'true'|'false'} IsRequired - + * @property {'true'|'false'} IsPrimaryKey - + * @property {string} Ordinal 1, 2, 3, ... + * @property {'Text'|'Date'|'Number'|'Decimal'|'Email'} FieldType - + * + * @typedef {Object.} DataExtensionFieldMap + */ + +const MetadataType = require('./MetadataType'); + +/** + * DataExtensionField MetadataType + * @augments MetadataType + */ +class DataExtensionField extends MetadataType { + /** + * Retrieves all records and saves it to disk + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Object} buObject properties for auth + * @returns {Promise<{metadata:DataExtensionFieldMap,type:string}>} Promise of items + */ + static async retrieve(retrieveDir, additionalFields, buObject) { + return super.retrieveSOAPgeneric(retrieveDir, buObject, null, additionalFields); + } + /** + * Retrieves all records for caching + * @param {Object} [options] required for the specific request (filter for example) + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @returns {Promise<{metadata:DataExtensionFieldMap,type:string}>} Promise of items + */ + static async retrieveForCache(options, additionalFields) { + return super.retrieveSOAPgeneric(null, null, options, additionalFields); + } + /** + * helper for DataExtension.js that sorts the fields into an array + * @param {DataExtensionFieldMap} fieldsObj customerKey-based list of fields for one dataExtension + * @returns {DataExtensionFieldItem[]} sorted array of field objects + */ + static convertToSortedArray(fieldsObj) { + return ( + Object.keys(fieldsObj) + .map((field) => fieldsObj[field]) + // the API returns the fields not sorted + .sort(this.sortDeFields) + ); + } + + /** + * sorting method to ensure `Ordinal` is respected + * @param {DataExtensionFieldItem} a - + * @param {DataExtensionFieldItem} b - + * @returns {boolean} sorting based on Ordinal + */ + static sortDeFields(a, b) { + return a.Ordinal - b.Ordinal; + } + /** + * manages post retrieve steps + * @param {DataExtensionFieldItem} metadata a single item + * @param {boolean} forDataExtension when used by DataExtension class we remove more fields + * @returns {DataExtensionFieldItem} metadata + */ + static postRetrieveTasks(metadata, forDataExtension) { + return this._parseMetadata(metadata, forDataExtension); + } + + /** + * parses retrieved Metadata before saving + * @private + * @param {DataExtensionFieldItem} metadata a single record + * @param {boolean} forDataExtension when used by DataExtension class we remove more fields + * @returns {DataExtensionFieldItem} parsed metadata definition + */ + static _parseMetadata(metadata, forDataExtension) { + if (forDataExtension) { + // remove fields according to definition + this.keepRetrieveFields(metadata); + + // remove fields that we do not need after association to a DE + delete metadata.CustomerKey; + delete metadata.DataExtension; + delete metadata.Ordinal; + } + return metadata; + } +} + +// Assign definition to static attributes +DataExtensionField.definition = require('../MetadataTypeDefinitions').dataExtensionField; +DataExtensionField.cache = {}; +DataExtensionField.client = undefined; + +module.exports = DataExtensionField; diff --git a/lib/metadataTypes/DataExtensionTemplate.js b/lib/metadataTypes/DataExtensionTemplate.js new file mode 100644 index 000000000..c050324af --- /dev/null +++ b/lib/metadataTypes/DataExtensionTemplate.js @@ -0,0 +1,23 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * DataExtensionTemplate MetadataType + * @augments MetadataType + */ +class DataExtensionTemplate extends MetadataType { + /** + * Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + return super.retrieveSOAPgeneric(retrieveDir); + } +} + +// Assign definition to static attributes +DataExtensionTemplate.definition = require('../MetadataTypeDefinitions').dataExtensionTemplate; + +module.exports = DataExtensionTemplate; diff --git a/lib/metadataTypes/DataExtract.js b/lib/metadataTypes/DataExtract.js new file mode 100644 index 000000000..5e6e7fadd --- /dev/null +++ b/lib/metadataTypes/DataExtract.js @@ -0,0 +1,149 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * DataExtract MetadataType + * @augments MetadataType + */ +class DataExtract extends MetadataType { + /** + * Retrieves Metadata of Data Extract Activity. + * Endpoint /automation/v1/dataextracts/ returns all Data Extracts + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static async retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/dataextracts/', null); + } + /** + * Retrieves Metadata of Data Extract Activity for caching + * @returns {Promise} Promise of metadata + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/dataextracts/', null); + } + + /** + * Retrieve a specific dataExtract Definition by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} variables variables to be replaced in the metadata + * @returns {Promise} Promise of metadata + */ + static async retrieveAsTemplate(templateDir, name, variables) { + const options = { + uri: '/automation/v1/dataextracts/?$filter=name%20eq%20' + name.split(' ').join('%20'), + }; + + const res = await this.client.RestClient.get(options); + if (res.body && Array.isArray(res.body.items) && res.body.items.length) { + // eq-operator returns a similar, not exact match and hence might return more than 1 entry + const [metadata] = res.body.items.filter((item) => item.name === name); + if (!metadata) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + return; + } + + // get full definition + const extended = await this.client.RestClient.get({ + uri: '/automation/v1/dataextracts/' + metadata.id, + }); + const val = JSON.parse( + Util.replaceByObject(JSON.stringify(this.parseMetadata(extended.body)), variables) + ); + + // remove all fields listed in Definition for templating + this.keepTemplateFields(val); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + val.customerKey + '.' + this.definition.type + '-meta', + JSON.parse(Util.replaceByObject(JSON.stringify(val), variables)) + ); + Util.logger.info( + `Dataextracts.retrieveAsTemplate:: Written Metadata to filesystem (${name})` + ); + return { metadata: val, type: this.definition.type }; + } else if (res.body && res.body.items) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + } else { + throw new Error( + `Encountered unknown error when retrieveing ${ + this.definition.typeName + } "${name}": ${JSON.stringify(res.body)}` + ); + } + } + + /** + * manages post retrieve steps + * @param {Object} fileTransfer a single fileTransfer + * @returns {Object[]} metadata + */ + static postRetrieveTasks(fileTransfer) { + return this.parseMetadata(fileTransfer); + } + + /** + * Creates a single Data Extract + * @param {Object} dataExtract a single Data Extract + * @returns {Promise} Promise + */ + static create(dataExtract) { + return super.createREST(dataExtract, '/automation/v1/dataextracts/'); + } + + /** + * Updates a single Data Extract + * @param {Object} dataExtract a single Data Extract + * @returns {Promise} Promise + */ + static update(dataExtract) { + return super.updateREST( + dataExtract, + '/automation/v1/dataextracts/' + dataExtract.dataExtractDefinitionId + ); + } + + /** + * prepares a dataExtract for deployment + * @param {Object} metadata a single dataExtract activity definition + * @returns {Object} metadata object + */ + static preDeployTasks(metadata) { + metadata.dataExtractTypeId = Util.getFromCache( + this.cache, + 'dataExtractType', + metadata.r__dataExtractType_name, + 'name', + 'extractId' + ); + delete metadata.r__dataExtractType_name; + return metadata; + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single dataExtract activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + metadata.r__dataExtractType_name = Util.getFromCache( + this.cache, + 'dataExtractType', + metadata.dataExtractTypeId, + 'extractId', + 'name' + ); + delete metadata.dataExtractTypeId; + return JSON.parse(JSON.stringify(metadata)); + } +} + +// Assign definition to static attributes +DataExtract.definition = require('../MetadataTypeDefinitions').dataExtract; +DataExtract.cache = {}; +DataExtract.client = undefined; + +module.exports = DataExtract; diff --git a/lib/metadataTypes/DataExtractType.js b/lib/metadataTypes/DataExtractType.js new file mode 100644 index 000000000..31a3d0ea7 --- /dev/null +++ b/lib/metadataTypes/DataExtractType.js @@ -0,0 +1,34 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * DataExtractType MetadataType + * Only for Caching No retrieve/upsert is required + * as this is a configuration in the EID + * @augments MetadataType + */ +class DataExtractType extends MetadataType { + /** + * Retrieves Metadata of Data Extract Type. + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/dataextracttypes/', null); + } + /** + * Retrieves Metadata of Data Extract Type for caching. + * @returns {Promise} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/dataextracttypes/', null); + } +} + +// Assign definition to static attributes +DataExtractType.definition = require('../MetadataTypeDefinitions').dataExtractType; +DataExtractType.cache = {}; +DataExtractType.client = undefined; + +module.exports = DataExtractType; diff --git a/lib/metadataTypes/Discovery.js b/lib/metadataTypes/Discovery.js new file mode 100644 index 000000000..7f95baa10 --- /dev/null +++ b/lib/metadataTypes/Discovery.js @@ -0,0 +1,49 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * ImportFile MetadataType + * @augments MetadataType + */ +class Discovery extends MetadataType { + /** + * Retrieves API endpoint + * documentation: https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/routes.htm + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {String[]} [_] not used + * @param {Object} buObject properties for auth + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir, _, buObject) { + if (buObject.eid !== buObject.mid) { + // don't run for BUs other than Parent BU + Util.logger.warn('Skipping Discovery retrieval on non-parent BU'); + return; + } else { + const res = await Promise.all( + Object.keys(this.definition.endPointMapping).map((endpoint) => + this.client.RestClient.get({ + uri: this.definition.endPointMapping[endpoint], + headers: {}, + }) + ) + ); + const metadataStructure = {}; + for (const [i, v] of res.entries()) { + v.body.key = Object.keys(this.definition.endPointMapping)[i]; + metadataStructure[v.body.key] = v.body; + } + await this.saveResults(metadataStructure, retrieveDir, null); + Util.logger.info('Downloaded: ' + this.definition.type); + return { metadata: metadataStructure, type: this.definition.type }; + } + } +} + +// Assign definition to static attributes +Discovery.definition = require('../MetadataTypeDefinitions').discovery; +Discovery.client = undefined; + +module.exports = Discovery; diff --git a/lib/metadataTypes/Email.js b/lib/metadataTypes/Email.js new file mode 100644 index 000000000..245beb5ba --- /dev/null +++ b/lib/metadataTypes/Email.js @@ -0,0 +1,71 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * Email MetadataType + * @augments MetadataType + */ +class Email extends MetadataType { + /** + * Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + Util.logger.warn( + 'Classic E-Mails are deprecated and will be discontinued by SFMC in the near future. Ensure that you migrate any existing E-Mails to Content Builder as soon as possible.' + ); + // !dont activate `await File.initPrettier('html');` as we only want to retrieve for migration and formatting might mess with the outcome + return super.retrieveSOAPgeneric(retrieveDir, null); + } + /** + * manages post retrieve steps + * @param {Object} metadata a single query + * @returns {Object[]} Array with one metadata object and one query string + */ + static postRetrieveTasks(metadata) { + return this.parseMetadata(metadata); + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single query activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'Path' + ); + delete metadata.CategoryID; + } catch (ex) { + Util.logger.warn( + `Classic E-Mail '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } + + // extract code + const codeArr = [ + { + subFolder: null, + fileName: metadata.CustomerKey, + fileExt: 'html', + content: metadata.HTMLBody, + }, + ]; + delete metadata.HTMLBody; + + return { json: metadata, codeArr: codeArr, subFolder: null }; + } +} + +// Assign definition to static attributes +Email.definition = require('../MetadataTypeDefinitions').email; + +module.exports = Email; diff --git a/lib/metadataTypes/EmailSendDefinition.js b/lib/metadataTypes/EmailSendDefinition.js new file mode 100644 index 000000000..09d90738c --- /dev/null +++ b/lib/metadataTypes/EmailSendDefinition.js @@ -0,0 +1,266 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * MessageSendActivity MetadataType + * @augments MetadataType + */ +class EmailSendDefinition extends MetadataType { + /** + * Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {void} _ - + * @param {Object} buObject properties for auth + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir, _, buObject) { + const options = { + filter: { + leftOperand: { + // somehow that parameter controls visible (non deleted?) email send activities + leftOperand: 'IsPlatformObject', + operator: 'equals', + rightOperand: false, + }, + operator: 'AND', + rightOperand: { + leftOperand: 'Description', + operator: 'notEquals', + rightOperand: 'SFSendDefinition', + }, + }, + }; + return super.retrieveSOAPgeneric(retrieveDir, buObject, options); + } + + /** + * Updates a single item + * @param {Object} metadataItem a single item + * @returns {Promise} Promise + */ + static update(metadataItem) { + return super.updateSOAP(metadataItem); + } + + /** + * Creates a single item + * @param {Object} metadataItem a single item + * @returns {Promise} Promise + */ + static create(metadataItem) { + return super.createSOAP(metadataItem); + } + + /** + * prepares a single item for deployment + * @param {Object} metadata a single script activity definition + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + // re-add IsPlatformObject, required for visibility + metadata.IsPlatformObject = false; + // folder + try { + metadata.CategoryID = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + delete metadata.r__folder_Path; + } catch (ex) { + Util.logger.error(`${this.definition.typeName} '${metadata.key}': ${ex.message}`); + } + // email + try { + metadata.Email = {}; + if (metadata.r__email_Name) { + // classic + metadata.Email.ID = Util.getFromCache( + this.cache, + 'email', + metadata.r__email_Name, + 'Name', + 'ID' + ); + delete metadata.r__email_Name; + } else if (metadata.r__assetMessage_Key) { + // content builder + // * this ignores r__assetMessage_Name on purpose as that is only unique per parent folder but useful during PR reviews + metadata.Email.ID = Util.getFromCache( + this.cache, + 'asset', + metadata.r__assetMessage_Key, + 'customerKey', + 'legacyData.legacyId' + ); + delete metadata.r__assetMessage_Key; + delete metadata.r__assetMessage_Name; + } + } catch (ex) { + Util.logger.error(`${this.definition.typeName} '${metadata.key}': ${ex.message}`); + } + // Target Audience DataExtension + // normalize first because this can be an array + if (!metadata.SendDefinitionList) { + metadata.SendDefinitionList = []; + } else if (!Array.isArray(metadata.SendDefinitionList)) { + metadata.SendDefinitionList = [metadata.SendDefinitionList]; + } + // Target Audience source + // - DataSourceTypeID=CustomObject --> DataExtension is source; list is also defined + // - DataSourceTypeID=List --> List is source; DE is not defined + for (const sdl of metadata.SendDefinitionList) { + // get DataExtension (optional) + if (sdl.r__dataExtension_Key) { + if (sdl.DataSourceTypeID !== 'CustomObject') { + throw new Error( + `Skipping ${metadata.Name} (${metadata.CustomerKey}): Expecting DataSourceTypeID to equal 'CustomObject' when r__dataExtension_Key is defined; Found '${sdl.DataSourceTypeID}'` + ); + } + sdl.CustomObjectID = Util.getFromCache( + this.cache, + 'dataExtension', + sdl.r__dataExtension_Key, + 'CustomerKey', + 'ObjectID' + ); + delete sdl.r__dataExtension_Key; + } else if (sdl.DataSourceTypeID === 'CustomObject') { + throw new Error( + `Skipping ${metadata.Name} (${metadata.CustomerKey}): Expecting r__dataExtension_Key to be defined if DataSourceTypeID='CustomObject'` + ); + } + // get List (required) + if (sdl.r__list_PathName) { + sdl.List = { + ID: Util.getListObjectIdFromCache(this.cache, sdl.r__list_PathName, 'ID'), + }; + delete sdl.r__list_PathName; + } else { + throw new Error( + `Field SendDefinitionList.r__list_PathName was not defined. Please try re-retrieving this ESD from your source BU.` + ); + } + } + + return metadata; + } + + /** + * manages post retrieve steps + * @param {Object} metadata a single query + * @returns {Object[]} Array with one metadata object and one query string + */ + static postRetrieveTasks(metadata) { + return this.parseMetadata(metadata); + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single query activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // remove IsPlatformObject, always has to be 'false' + delete metadata.IsPlatformObject; + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'Path' + ); + delete metadata.CategoryID; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } + // email + try { + // classic + const classicEmail = Util.getFromCache( + this.cache, + 'email', + metadata.Email.ID, + 'ID', + 'Name' + ); + metadata.r__email_Name = classicEmail; + delete metadata.Email; + } catch (ex) { + try { + // content builder + const contentBuilderEmailName = Util.getFromCache( + this.cache, + 'asset', + metadata.Email.ID, + 'legacyData.legacyId', + 'name' + ); + metadata.r__assetMessage_Name = contentBuilderEmailName; + const contentBuilderEmailKey = Util.getFromCache( + this.cache, + 'asset', + metadata.Email.ID, + 'legacyData.legacyId', + 'customerKey' + ); + metadata.r__assetMessage_Key = contentBuilderEmailKey; + delete metadata.Email; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.` + ); + } + } + // Target Audience DataExtension + // normalize first because this can be an array + if (!metadata.SendDefinitionList) { + metadata.SendDefinitionList = []; + } else if (!Array.isArray(metadata.SendDefinitionList)) { + metadata.SendDefinitionList = [metadata.SendDefinitionList]; + } + // Target Audience DataExtension + for (const sdl of metadata.SendDefinitionList) { + // get DataExtension keys + if (sdl.CustomObjectID) { + try { + sdl.r__dataExtension_Key = Util.getFromCache( + this.cache, + 'dataExtension', + sdl.CustomObjectID, + 'ObjectID', + 'CustomerKey' + ); + delete sdl.CustomObjectID; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find Target Audience (DataExtension) with ObjectID ${sdl.CustomObjectID}.` + ); + } + } + // List + try { + sdl.r__list_PathName = Util.getListPathNameFromCache(this.cache, sdl.List.ID, 'ID'); + delete sdl.List; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } + } + + return metadata; + } +} + +// Assign definition to static attributes +EmailSendDefinition.definition = require('../MetadataTypeDefinitions').emailSendDefinition; + +module.exports = EmailSendDefinition; diff --git a/lib/metadataTypes/EventDefinition.js b/lib/metadataTypes/EventDefinition.js new file mode 100644 index 000000000..4fb2140ec --- /dev/null +++ b/lib/metadataTypes/EventDefinition.js @@ -0,0 +1,188 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * EventDefinition MetadataType + * @augments MetadataType + */ +class EventDefinition extends MetadataType { + /** + * Retrieves Metadata of Event Definition. + * Endpoint /interaction/v1/EventDefinitions return all Event Definitions with all details. + * Currently it is not needed to loop over Imports with endpoint /interaction/v1/EventDefinitions/{id} + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/interaction/v1/EventDefinitions/', null); + } + + /** + * Retrieves event definition metadata for caching + * @returns {Promise} Promise of metadata + */ + static retrieveForCache() { + return super.retrieveREST(null, '/interaction/v1/EventDefinitions/', null); + } + + /** + * Retrieve a specific Event Definition by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} variables variables to be replaced in the metadata + * @returns {Promise} Promise of metadata + */ + static async retrieveAsTemplate(templateDir, name, variables) { + // todo template based on name + const options = { + uri: '/interaction/v1/EventDefinitions?name=' + encodeURIComponent(name), + }; + const res = await this.client.RestClient.get(options); + const event = res.body.items.filter((item) => item.name === name); + try { + if (!event || event.length === 0) { + throw new Error(`No Event Definitions Found with name "${name}"`); + } else if (event.length > 1) { + throw new Error( + `Multiple Event Definitions with name "${name}"` + + `please rename to be unique to avoid issues` + ); + } else if (event && event.length === 1) { + const eventDef = JSON.parse( + Util.replaceByObject(JSON.stringify(this.parseMetadata(event[0])), variables) + ); + if (!eventDef.dataExtensionId) { + throw new Error( + `EventDefinition.parseMetadata:: ` + + `No Data Extension found for ` + + `event: ${eventDef.name}. ` + + `This cannot be templated` + ); + } + + // remove all fields listed in Definition for templating + this.keepTemplateFields(eventDef); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + eventDef.customerKey + '.' + this.definition.type + '-meta', + JSON.parse(Util.replaceByObject(JSON.stringify(eventDef), variables)) + ); + Util.logger.info( + `EventDefinition.retrieveAsTemplate:: Written Metadata to filesystem (${name})` + ); + return { metadata: eventDef, type: this.definition.type }; + } else { + throw new Error( + `Encountered unknown error when retrieveing ${ + this.definition.typeName + } "${name}": ${JSON.stringify(res.body)}` + ); + } + } catch (ex) { + Util.logger.error('EventDefinition.retrieveAsTemplate:: ' + ex); + return null; + } + } + + /** + * manages post retrieve steps + * @param {Object} eventDef a single importDef + * @returns {Object[]} metadata + */ + static postRetrieveTasks(eventDef) { + const val = this.parseMetadata(eventDef); + this.keepRetrieveFields(val); + return val; + } + + /** + * Creates a single Event Definition + * @param {Object} EventDefinition a single Event Definition + * @returns {Promise} Promise + */ + static create(EventDefinition) { + return super.createREST(EventDefinition, '/interaction/v1/EventDefinitions/'); + } + + /** + * Updates a single Event Definition (using PUT method since PATCH isn't supported) + * @param {Object} EventDefinition a single Event Definition + * @returns {Promise} Promise + */ + static async update(EventDefinition) { + this.removeNotUpdateableFields(EventDefinition); + const options = { + uri: '/interaction/v1/EventDefinitions/' + EventDefinition.id, + json: EventDefinition, + headers: {}, + }; + try { + const response = await this.client.RestClient.put(options); + super.checkForErrors(response); + return response; + } catch (ex) { + Util.metadataLogger( + 'error', + this.definition.type, + 'updateREST', + ex, + EventDefinition.name + ); + return null; + } + } + + /** + * prepares an event definition for deployment + * @param {Object} metadata a single eventDefinition + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + metadata.dataExtensionId = Util.getFromCache( + this.cache, + 'dataExtension', + metadata.dataExtensionName, + 'Name', + 'ObjectID' + ); + metadata.arguments.dataExtensionId = metadata.dataExtensionId; + return metadata; + } + + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single event definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + try { + metadata.dataExtensionId = Util.getFromCache( + this.cache, + 'dataExtension', + metadata.dataExtensionId, + 'ObjectID', + 'CustomerKey' + ); + metadata.arguments.dataExtensionId = metadata.dataExtensionId; + return JSON.parse(JSON.stringify(metadata)); + } catch (ex) { + Util.metadataLogger( + 'verbose', + this.definition.type, + 'parseMetadata', + `No related Data Extension found for Event '${metadata.name}'. Consider deleting the event definition` + ); + return metadata; + } + } +} + +// Assign definition to static attributes +EventDefinition.definition = require('../MetadataTypeDefinitions').eventDefinition; +EventDefinition.cache = {}; +EventDefinition.client = undefined; + +module.exports = EventDefinition; diff --git a/lib/metadataTypes/FileTransfer.js b/lib/metadataTypes/FileTransfer.js new file mode 100644 index 000000000..f60c56031 --- /dev/null +++ b/lib/metadataTypes/FileTransfer.js @@ -0,0 +1,146 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * FileTransfer MetadataType + * @augments MetadataType + */ +class FileTransfer extends MetadataType { + /** + * Retrieves Metadata of FileTransfer Activity. + * Endpoint /automation/v1/filetransfers/ returns all File Transfers + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/filetransfers/', null); + } + /** + * Retrieves Metadata of FileTransfer Activity for caching + * @returns {Promise} Promise + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/filetransfers/', null); + } + + /** + * Retrieve a specific File Transfer Definition by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} variables variables to be replaced in the metadata + * @returns {Promise} Promise + */ + static async retrieveAsTemplate(templateDir, name, variables) { + const options = { + uri: '/automation/v1/filetransfers/?$filter=name%20eq%20' + name.split(' ').join('%20'), + }; + + const res = await this.client.RestClient.get(options); + if (res.body && Array.isArray(res.body.items) && res.body.items.length) { + // eq-operator returns a similar, not exact match and hence might return more than 1 entry + const [metadata] = res.body.items.filter((item) => item.name === name); + if (!metadata) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + return; + } + + // get full definition + const extended = await this.client.RestClient.get({ + uri: '/automation/v1/filetransfers/' + metadata.id, + }); + const val = JSON.parse( + Util.replaceByObject(JSON.stringify(this.parseMetadata(extended.body)), variables) + ); + + // remove all fields listed in Definition for templating + this.keepTemplateFields(val); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + val.customerKey + '.' + this.definition.type + '-meta', + JSON.parse(Util.replaceByObject(JSON.stringify(val), variables)) + ); + Util.logger.info( + `FileTransfer.retrieveAsTemplate:: Written Metadata to filesystem (${name})` + ); + return { metadata: val, type: this.definition.type }; + } else if (res.body && res.body.items) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + } else { + throw new Error( + `Encountered unknown error when retrieveing ${ + this.definition.typeName + } "${name}": ${JSON.stringify(res.body)}` + ); + } + } + + /** + * manages post retrieve steps + * @param {Object} metadata a single fileTransfer activity definition + * @returns {Object[]} metadata + */ + static postRetrieveTasks(metadata) { + const values = this.parseMetadata(metadata); + return values; + } + + /** + * Creates a single File Transfer + * @param {Object} fileTransfer a single File Transfer + * @returns {Promise} Promise + */ + static create(fileTransfer) { + return super.createREST(fileTransfer, '/automation/v1/filetransfers/'); + } + + /** + * Updates a single File Transfer + * @param {Object} fileTransfer a single File Transfer + * @returns {Promise} Promise + */ + static update(fileTransfer) { + return super.updateREST(fileTransfer, '/automation/v1/filetransfers/' + fileTransfer.id); + } + + /** + * prepares a fileTransfer for deployment + * @param {Object} metadata a single fileTransfer activity definition + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + metadata.fileTransferLocationId = Util.getFromCache( + this.cache, + 'ftpLocation', + metadata.r__ftpLocation_name, + 'name', + 'id' + ); + return metadata; + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single fileTransfer activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + metadata.r__ftpLocation_name = Util.getFromCache( + this.cache, + 'ftpLocation', + metadata.fileTransferLocationId, + 'id', + 'name' + ); + delete metadata.fileTransferLocationId; + return metadata; + } +} + +// Assign definition to static attributes +FileTransfer.definition = require('../MetadataTypeDefinitions').fileTransfer; +FileTransfer.cache = {}; +FileTransfer.client = undefined; + +module.exports = FileTransfer; diff --git a/lib/metadataTypes/Filter.js b/lib/metadataTypes/Filter.js new file mode 100644 index 000000000..658fa493d --- /dev/null +++ b/lib/metadataTypes/Filter.js @@ -0,0 +1,26 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * Filter MetadataType + * @augments MetadataType + */ +class Filter extends MetadataType { + /** + * Retrieves Metadata of Filter. + * Endpoint /automation/v1/filters/ returns all Filters, + * but only with some of the fields. So it is needed to loop over + * Filters with the endpoint /automation/v1/filters/{id} + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/filters/', null); + } +} + +// Assign definition to static attributes +Filter.definition = require('../MetadataTypeDefinitions').filter; + +module.exports = Filter; diff --git a/lib/metadataTypes/Folder.js b/lib/metadataTypes/Folder.js new file mode 100644 index 000000000..69b74c3a5 --- /dev/null +++ b/lib/metadataTypes/Folder.js @@ -0,0 +1,564 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const toposort = require('toposort'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * Folder MetadataType + * @augments MetadataType + */ +class Folder extends MetadataType { + /** + * Retrieves metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {String[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Object} buObject properties for auth + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir, overrideFields, buObject) { + const queryAllFolders = await this.retrieveHelper(overrideFields, true); + // if this is the parent, no need to query twice as QueryAllAccounts works. + + if (buObject.eid !== buObject.mid) { + queryAllFolders.push(...(await this.retrieveHelper(overrideFields, false))); + } + const sortPairs = toposort(queryAllFolders.map((a) => [a.ParentFolder.ID, a.ID])); + const idMap = {}; + for (const val of queryAllFolders) { + // Contact Builder Lists create a folder called "Audiences" with the same Customer Key as the + // main data extension folder. We restrict this from deploy so setting customer key to allow retrieve + if (val.CustomerKey === 'dataextension_default' && val.Name === 'Audiences') { + val.CustomerKey = 'dataextension_audiences'; + } + // by default folders do not have an external key, we set this to ID plus EID as this will be unique + else if (!val.CustomerKey) { + val.CustomerKey = [buObject.eid, val.ID].join('-'); + } + + idMap[val.ID] = val; + } + + // create root node for attaching, but will be deleted later + idMap[0] = { + Name: '', + }; + for (const id of sortPairs) { + if (!idMap[id]) { + Util.logger.debug(`Error: id ${id} not found in idMap-obj but in sortPairs-array.`); + if (Util.logger.level === 'debug') { + console.log(`Error: id ${id} not found in idMap-ob but in sortPairs-array.`); + } + } else if ( + idMap[id].ParentFolder && + idMap[id].ParentFolder.ID && + idMap[id].Name !== '' + ) { + // if the parent folder can be found by ID + if (idMap[idMap[id].ParentFolder.ID]) { + // if the parent folder has a Path + if (idMap[idMap[id].ParentFolder.ID].Path) { + const parent = idMap[idMap[id].ParentFolder.ID]; + // we use / here not system separator as it is important to keep metadata consistent + idMap[id].Path = [parent.Path, idMap[id].Name].join( + Util.standardizedSplitChar + ); + idMap[id].ParentFolder.Path = parent.Path; + } else { + idMap[id].Path = idMap[id].Name; + } + } else { + Util.logger.error( + `Skipping - Cannot find parent folder (${idMap[id].ParentFolder.ID}) of: ${idMap[id].Name} (${id}, type: ${idMap[id].ContentType})` + ); + } + } + // All folders except the artificial root have ParentFolder attribute. If they dont something else is wrong + else if (idMap[id].Name !== '') { + idMap[id].Path = ''; + Util.logger.error( + `Skipping - Folder ${idMap[id].Name} does not have a parent folder (type: ${idMap[id].ContentType})` + ); + } + } + + // helper for error warning + const buMapping = {}; + for (const cred in this.properties.credentials) { + const buObj = this.properties.credentials[cred].businessUnits; + for (const bu in buObj) { + buMapping[buObj[bu]] = `${cred}/${bu}`; + } + } + + // build a new map using the customer key instead of id + const metadata = {}; + for (const id in idMap) { + // remove keys which are listed in other BUs and skip root + if ( + idMap[id].Client && + idMap[id].Client.ID && + (buObject.mid == idMap[id].Client.ID || + this.definition.folderTypesFromParent.includes( + idMap[id].ContentType.toLowerCase() + )) + ) { + if (metadata[idMap[id].CustomerKey]) { + // check the shortest path as this is likely the more important folder. + if (metadata[idMap[id].CustomerKey].Path.length > idMap[id].Path.length) { + Util.logger.debug( + `MetadataType[folder-${ + idMap[id].ContentType + }].retrieve:: Duplicate CustomerKey on Folder. Keeping: ${ + idMap[id].Path + }, Overwriting: ${metadata[idMap[id].CustomerKey].Path}` + ); + metadata[idMap[id].CustomerKey] = idMap[id]; + } else { + Util.logger.debug( + `MetadataType[folder-${ + idMap[id].ContentType + }].retrieve:: Duplicate CustomerKey on Folder. Keeping: ${ + metadata[idMap[id].CustomerKey].Path + }, Ignoring: ${idMap[id].Path}` + ); + } + } else { + metadata[idMap[id].CustomerKey] = idMap[id]; + } + } else { + delete idMap[id]; + } + } + if (retrieveDir) { + const savedMetadata = await this.saveResults(metadata, retrieveDir, buObject.mid); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + } + return { metadata: metadata, type: this.definition.type }; + } + + /** + * Retrieves folder metadata for caching + * @param {Object} buObject properties for auth + * @returns {Promise} Promise + */ + static retrieveForCache(buObject) { + return this.retrieve(null, null, buObject); + } + + /** + * Folder upsert (copied from Metadata Upsert), after retrieving from target + * and comparing to check if create or update operation is needed. + * Copied due to having a dependency on itself, meaning the created need to be serial + * @param {Object} metadata metadata mapped by their keyField + * @returns {Promise} Promise of saved metadata + */ + static async upsert(metadata) { + let updateCount = 0; + let createCount = 0; + const upsertResults = {}; + const sortPairs = toposort( + Object.keys(metadata).map((customerKey) => [ + metadata[customerKey].ParentFolder.Path, + metadata[customerKey].Path, + ]) + ); + // loop of loops to find a match between two arrays + for (const folderPath of sortPairs) { + for (const metadataKey in metadata) { + if (metadata[metadataKey].Path === folderPath) { + try { + // preDeployTasks parsing + const deployableMetadata = await this.preDeployTasks(metadata[metadataKey]); + // if preDeploy returns nothing then it cannot be deployed so skip deployment + if (deployableMetadata) { + const normalizedKey = File.reverseFilterIllegalFilenames(metadataKey); + + let existingId; + try { + // perform a secondary check based on path + existingId = Util.getFromCache( + this.cache, + 'folder', + deployableMetadata.Path, + 'Path', + 'ID' + ); + } catch (ex) { + // In case no path matching, then try to use CustomerKey + existingId = this.cache.folder[normalizedKey] + ? this.cache.folder[normalizedKey].ID + : null; + } + + let result; + // since deployableMetadata will be modified for deploy, make a copy for reference + const beforeMetadata = JSON.parse(JSON.stringify(deployableMetadata)); + if (existingId) { + // if an existing folder exists with the same name/path then use that + deployableMetadata.ID = existingId; + result = await this.update(deployableMetadata); + updateCount++; + } else { + result = await this.create(deployableMetadata); + createCount++; + } + if (result && result.body && result.body.Results) { + const parsed = this.parseResponseBody({ + Results: [result.body.Results[0].Object], + }); + if ( + !result.body.Results[0].Object.CustomerKey && + parsed['undefined'] + ) { + // when inserting folders without specifying a CustomerKey, this happens in parseResponseBody() + parsed[normalizedKey] = parsed['undefined']; + delete parsed['undefined']; + } + this.cache.folder[normalizedKey] = Object.assign( + beforeMetadata, + parsed[normalizedKey] + ); + + upsertResults[metadataKey] = beforeMetadata; + } else { + Util.logger.debug(result); + throw new Error( + `'${beforeMetadata[metadataKey].Path}' was not deployed correctly` + ); + } + } + } catch (ex) { + Util.metadataLogger('error', 'folder', 'upsert', ex, metadataKey); + } + } + } + } + // Logging + Util.metadataLogger( + 'info', + this.definition.type, + 'upsert', + `${createCount} created / ${updateCount} updated` + ); + + if (updateCount) { + Util.logger.warn( + `Folders are recognized for updates based on their CustomerKey or, if that is not given, their folder-path.` + ); + } + return upsertResults; + } + + /** + * creates a folder based on metatadata + * @param {Object} metadata metadata of the folder + * @returns {Promise} Promise + */ + static create(metadata) { + const tempCustomerKey = metadata.CustomerKey; + const path = metadata.Path; + this.removeNotCreateableFields(metadata); + return new Promise((resolve) => { + if (metadata.Parent && metadata.Parent.ID && metadata.Parent.ID === 0) { + Util.logger.error( + `${this.definition.type}-${metadata.ContentType}.create:: Cannot create Root Folder: ${metadata.Name}` + ); + resolve({}); + } else { + this.client.SoapClient.create('DataFolder', metadata, null, (ex, response) => { + if (ex && ex.results) { + Util.logger.error( + `${this.definition.type}-${metadata.ContentType}.create:: error creating: ${path}. ${ex.results[0].StatusMessage}` + ); + resolve(); + } else if (ex) { + Util.logger.error( + `${this.definition.type}-${metadata.ContentType}.create:: error creating: ${path}. ${ex.message}` + ); + resolve(); + } else { + response.body.Results[0].Object = metadata; + response.body.Results[0].Object.ID = response.body.Results[0].NewID; + response.body.Results[0].Object.CustomerKey = tempCustomerKey; + delete response.body.Results[0].Object.$; + Util.logger.info(`- created folder: ${path}`); + resolve(response); + } + }); + } + }); + } + + /** + * Updates a single Folder. + * @param {Object} metadata single metadata entry + * @returns {Promise} Promise + */ + static async update(metadata) { + return await new Promise((resolve) => { + const tempCustomerKey = metadata.CustomerKey; + const path = metadata.Path; + this.removeNotUpdateableFields(metadata); + this.client.SoapClient.update('DataFolder', metadata, null, (ex, response) => { + if (ex && ex.results) { + Util.logger.error( + `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.results[0].StatusMessage}` + ); + resolve(); + } else if (ex) { + Util.logger.error( + `${this.definition.type}-${metadata.ContentType}.update:: error updating: '${path}'. ${ex.message}` + ); + resolve(); + } else { + response.body.Results[0].Object = metadata; + response.body.Results[0].Object.CustomerKey = tempCustomerKey; + delete response.body.Results[0].Object.$; + Util.logger.info(`- updated folder: ${path}`); + resolve(response); + } + }); + }); + } + + /** + * prepares a folder for deployment + * @param {Object} metadata a single folder definition + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + if (!this.definition.deployFolderTypes.includes(metadata.ContentType.toLowerCase())) { + Util.logger.warn( + `Folder ${metadata.Name} is of a type which does not support deployment (Type: ${metadata.ContentType}). Please create this manually in the Web-Interface.` + ); + return; + } + // skip certain folders by name + else if (this.definition.deployFolderBlacklist.includes(metadata.Name.toLowerCase())) { + Util.metadataLogger( + 'debug', + 'folder', + 'preDeployTasks', + `Folder ${metadata.Name} is blacklisted for deployment due to it being a reserved name or that folder cannot be deployed` + ); + return; + } + + // skip when metadata isnt editable + else if (!metadata.IsEditable) { + Util.metadataLogger( + 'warn', + 'folder', + 'preDeployTasks', + `Folders with IsEditable (such as ${metadata.Name}) are skipped in deployment to avoid overwriting system folder` + ); + return; + } + // retreive ID based on the matching Path of the parent folder + else if (metadata.ParentFolder && metadata.ParentFolder.Path) { + metadata.ParentFolder.ID = Util.getFromCache( + this.cache, + 'folder', + metadata.ParentFolder.Path, + 'Path', + 'ID' + ); + return metadata; + } else { + Util.metadataLogger( + 'warn', + 'folder', + 'preDeployTasks', + `skipping root folder '${metadata.Name}' (no need to include this in deployment)` + ); + return; + } + } + + /** + * Returns file contents mapped to their filename without '.json' ending + * @param {String} dir directory that contains '.json' files to be read + * @param {boolean} [listBadKeys=false] do not print errors, used for badKeys() + * @returns {Object} fileName => fileContent map + */ + static getJsonFromFS(dir, listBadKeys) { + try { + const fileName2FileContent = {}; + const directories = File.readDirectoriesSync(dir, 10, true); + let newCounter = 0; + for (const subdir of directories) { + // standardise to / and then remove the stem up until folder as this + // should not be in the path for the metadata. In case no split then return empty as this is root + + const standardSubDir = File.reverseFilterIllegalFilenames( + subdir.replace(/\\/g, '/').split(/folder\//)[1] || '' + ); + for (const fileName of File.readdirSync(subdir)) { + try { + if (fileName.endsWith('meta.json')) { + const fileContent = File.readJSONFile(subdir, fileName, true, false); + + const fileNameWithoutEnding = File.reverseFilterIllegalFilenames( + fileName.split(/\.(\w|-)+-meta.json/)[0] + ); + + if (fileContent.Name === fileNameWithoutEnding) { + fileContent.Path = + (standardSubDir ? standardSubDir + '/' : '') + + fileNameWithoutEnding; + fileContent.ParentFolder = { + Path: standardSubDir, + }; + const key = fileContent.CustomerKey + ? fileContent.CustomerKey + : `new-${++newCounter}`; + if (fileName2FileContent[key]) { + Util.logger.error( + `Your have multiple folder-JSONs with the same CustomerKey '${key}' - skipping ${fileName}` + ); + } else { + fileName2FileContent[key] = fileContent; + } + } else if (!listBadKeys) { + Util.metadataLogger( + 'error', + this.definition.type, + 'getJsonFromFS', + 'Name of the Folder and the Filename must match', + JSON.stringify({ + Filename: fileNameWithoutEnding, + MetadataName: fileContent.Name, + }) + ); + } + } + } catch (ex) { + // by catching this in the loop we gracefully handle the issue and move on to the next file + Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex); + } + } + } + return fileName2FileContent; + } catch (ex) { + Util.metadataLogger('error', this.definition.type, 'getJsonFromFS', ex); + throw new Error(ex); + } + } + + /** + * Helper to retrieve the folders as promise + * @param {String[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Boolean} [queryAllAccounts] which queryAllAccounts setting to use + * @returns {Promise} soap object + */ + static async retrieveHelper(overrideFields, queryAllAccounts) { + const options = { queryAllAccounts: !!queryAllAccounts }; + let status = 'MoreDataAvailable'; + const Results = []; + do { + const response = await new Promise((resolve, reject) => { + // filtered out path as we need them to be stored locally, but do not want to try and retrieve + this.client.SoapClient.retrieve( + 'DataFolder', + this.getFieldNamesToRetrieve(overrideFields).filter( + (field) => !field.includes('Path') + ), + options, + (ex, response) => (ex ? reject(ex) : resolve(response)) + ); + }); + // merge results with existing + Results.push(...response.body.Results); + // set status for calling again if required + status = response.body.OverallStatus; + options.continueRequest = response.body.RequestID; + if (status === 'MoreDataAvailable') { + Util.logger.info( + '- more than ' + + Results.length + + ' folders in Business Unit - loading next batch...' + ); + } + } while (status === 'MoreDataAvailable'); + return Results; + } + /** + * Gets executed after retreive of metadata type + * @param {Object} metadata metadata mapped by their keyField + * @returns {Object[]} cloned metadata + */ + static postRetrieveTasks(metadata) { + return JSON.parse(JSON.stringify(metadata)); + } + /** + * Helper for writing Metadata to disk, used for Retrieve and deploy + * @param {Object} results metadata results from deploy + * @param {String} retrieveDir directory where metadata should be stored after deploy/retrieve + * @param {Number} mid current mid for this credential / business unit + * @returns {Promise} Promise of saved metadata + */ + static async saveResults(results, retrieveDir, mid) { + const savedResults = {}; + for (const metadataEntry in results) { + try { + // skip saving shared folders as they technically live in parent. + // ! Warning: our result set does not have Client.ID in it - bad check? + if (results[metadataEntry].Client && mid != results[metadataEntry].Client.ID) { + continue; + } else if ( + results[metadataEntry] && + (this.isFiltered(results[metadataEntry], true) || + this.isFiltered(results[metadataEntry], false)) + ) { + // if current metadata entry is filtered skip writeJSONToFile() + continue; + } + // get subfolder into which we need to save this JSON + // * cannot use split('/') here due to the "A/B Testing" folder + const stem = results[metadataEntry].Path.slice( + 0, + -results[metadataEntry].Name.length + ); + + // we dont store Id on local disk, but we need it for caching logic, + // so its in retrieve but not in save. Here we put into the clone so that the original + // object used for caching doesnt have the Id removed. + const tempHolder = this.postRetrieveTasks(results[metadataEntry]); + this.keepRetrieveFields(tempHolder); + delete tempHolder.ParentFolder; + delete tempHolder.Client; + if (!this.definition.keepId) { + delete tempHolder.ID; + } + savedResults[metadataEntry] = tempHolder; + + // need to {await} writeJSONToFile() here because sometimes SFMC allows the same folder to be created twice and then we run into race conditions, causing bad JSON to be saved + await File.writeJSONToFile( + // manage subtypes + [retrieveDir, 'folder', stem], + tempHolder.Name + '.folder-meta', + tempHolder + ); + } catch (ex) { + Util.metadataLogger( + 'error', + this.definition.type, + 'saveResults', + ex, + metadataEntry + ); + } + } + return savedResults; + } +} + +// Assign definition to static attributes +Folder.definition = require('../MetadataTypeDefinitions').folder; +Folder.client = undefined; + +module.exports = Folder; diff --git a/lib/metadataTypes/FtpLocation.js b/lib/metadataTypes/FtpLocation.js new file mode 100644 index 000000000..a727c2b48 --- /dev/null +++ b/lib/metadataTypes/FtpLocation.js @@ -0,0 +1,34 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * ImportFile MetadataType + * @augments MetadataType + */ +class FtpLocation extends MetadataType { + /** + * Retrieves Metadata of FtpLocation + * Endpoint /automation/v1/ftplocations/ return all FtpLocations + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/ftplocations/', null); + } + + /** + * Retrieves folder metadata into local filesystem. Also creates a uniquePath attribute for each folder. + * @returns {Promise} Promise + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/ftplocations/', null); + } +} + +// Assign definition to static attributes +FtpLocation.definition = require('../MetadataTypeDefinitions').ftpLocation; +FtpLocation.cache = {}; +FtpLocation.client = undefined; + +module.exports = FtpLocation; diff --git a/lib/metadataTypes/ImportFile.js b/lib/metadataTypes/ImportFile.js new file mode 100644 index 000000000..8cfd3fd23 --- /dev/null +++ b/lib/metadataTypes/ImportFile.js @@ -0,0 +1,226 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * ImportFile MetadataType + * @augments MetadataType + */ +class ImportFile extends MetadataType { + /** + * Retrieves Metadata of Import File. + * Endpoint /automation/v1/imports/ return all Import Files with all details. + * Currently it is not needed to loop over Imports with endpoint /automation/v1/imports/{id} + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/imports/', null); + } + + /** + * Retrieves import definition metadata for caching + * @returns {Promise} Promise + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/imports/', null); + } + + /** + * Retrieve a specific Import Definition by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} variables variables to be replaced in the metadata + * @returns {Promise} Promise + */ + static async retrieveAsTemplate(templateDir, name, variables) { + const options = { + uri: '/automation/v1/imports/?$filter=name%20eq%20' + name.split(' ').join('%20'), + }; + + const res = await this.client.RestClient.get(options); + if (res.body && Array.isArray(res.body.items) && res.body.items.length) { + // eq-operator returns a similar, not exact match and hence might return more than 1 entry + const [metadata] = res.body.items.filter((item) => item.name === name); + if (!metadata) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + return; + } + + const val = JSON.parse( + Util.replaceByObject(JSON.stringify(this.parseMetadata(metadata)), variables) + ); + + // remove all fields listed in Definition for templating + this.keepTemplateFields(val); + File.writeJSONToFile( + [templateDir, this.definition.type].join('/'), + val.customerKey + '.' + this.definition.type + '-meta', + JSON.parse(Util.replaceByObject(JSON.stringify(val), variables)) + ); + Util.logger.info( + `ImportFile.retrieveAsTemplate:: Written Metadata to filesystem (${name})` + ); + return { metadata: val, type: this.definition.type }; + } else if (res.body && res.body.items) { + Util.logger.error(`No ${this.definition.typeName} found with name "${name}"`); + } else { + throw new Error( + `Encountered unknown error when retrieveing ${ + this.definition.typeName + } "${name}": ${JSON.stringify(res.body)}` + ); + } + } + + /** + * manages post retrieve steps + * @param {Object} importDef a single importDef + * @returns {Object[]} metadata + */ + static postRetrieveTasks(importDef) { + const val = this.parseMetadata(importDef); + return val; + } + + /** + * Creates a single Import File + * @param {Object} importFile a single Import File + * @returns {Promise} Promise + */ + static create(importFile) { + return super.createREST(importFile, '/automation/v1/imports/'); + } + + /** + * Updates a single Import File + * @param {Object} importFile a single Import File + * @returns {Promise} Promise + */ + static update(importFile) { + return super.updateREST( + importFile, + '/automation/v1/imports/' + importFile.importDefinitionId + ); + } + + /** + * prepares a import definition for deployment + * @param {Object} metadata a single importDef + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata) { + metadata.fileTransferLocationId = Util.getFromCache( + this.cache, + 'ftpLocation', + metadata.r__ftpLocation_name, + 'name', + 'id' + ); + delete metadata.r__ftpLocation_name; + + if (metadata.c__destinationType === 'DataExtension') { + try { + metadata.destinationObjectId = Util.getFromCache( + this.cache, + 'dataExtension', + metadata.r__dataExtension_CustomerKey, + 'CustomerKey', + 'ObjectID' + ); + delete metadata.r__dataExtension_CustomerKey; + } catch (ex) { + throw new Error(`ImportFile ${metadata.customerKey}: ${ex.message}`); + } + } else if (metadata.c__destinationType === 'List') { + try { + metadata.destinationObjectId = Util.getListObjectIdFromCache( + this.cache, + metadata.r__list_PathName, + 'ObjectID' + ); + delete metadata.r__list_PathName; + } catch (ex) { + throw new Error(`ImportFile ${metadata.customerKey}: ${ex.message}`); + } + } + // When the destinationObjectTypeId is 584 is refers to Mobile Connect which is not supported as an Import Type + metadata.destinationObjectTypeId = this.definition.destinationObjectTypeMapping[ + metadata.c__destinationType + ]; + metadata.subscriberImportTypeId = this.definition.subscriberImportTypeMapping[ + metadata.c__subscriberImportType + ]; + metadata.updateTypeId = this.definition.updateTypeMapping[metadata.c__dataAction]; + return metadata; + } + + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single import definition + * @returns {Object} parsed metadata definition + */ + static parseMetadata(metadata) { + metadata.r__ftpLocation_name = Util.getFromCache( + this.cache, + 'ftpLocation', + metadata.fileTransferLocationId, + 'id', + 'name' + ); + delete metadata.fileTransferLocationId; + + // * When the destinationObjectTypeId is 584 it refers to Mobile Connect which is not supported as an Import Type + metadata.c__destinationType = Util.inverseGet( + this.definition.destinationObjectTypeMapping, + metadata.destinationObjectTypeId + ); + delete metadata.destinationObjectTypeId; + if (metadata.c__destinationType === 'DataExtension') { + try { + metadata.r__dataExtension_CustomerKey = Util.getFromCache( + this.cache, + 'dataExtension', + metadata.destinationObjectId, + 'ObjectID', + 'CustomerKey' + ); + delete metadata.destinationObjectId; + } catch (ex) { + Util.logger.error(`ImportFile ${metadata.customerKey}: ${ex.message}`); + } + } else if (metadata.c__destinationType === 'List') { + try { + metadata.r__list_PathName = Util.getListPathNameFromCache( + this.cache, + metadata.destinationObjectId, + 'ObjectID' + ); + delete metadata.destinationObjectId; + } catch (ex) { + Util.logger.error(`ImportFile ${metadata.customerKey}: ${ex.message}`); + } + } + + metadata.c__subscriberImportType = Util.inverseGet( + this.definition.subscriberImportTypeMapping, + metadata.subscriberImportTypeId + ); + delete metadata.subscriberImportTypeId; + metadata.c__dataAction = Util.inverseGet( + this.definition.updateTypeMapping, + metadata.updateTypeId + ); + delete metadata.updateTypeId; + return metadata; + } +} + +// Assign definition to static attributes +ImportFile.definition = require('../MetadataTypeDefinitions').importFile; +ImportFile.cache = {}; +ImportFile.client = undefined; + +module.exports = ImportFile; diff --git a/lib/metadataTypes/Interaction.js b/lib/metadataTypes/Interaction.js new file mode 100644 index 000000000..0c9a86ad2 --- /dev/null +++ b/lib/metadataTypes/Interaction.js @@ -0,0 +1,24 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * Script MetadataType + * @augments MetadataType + */ +class Interaction extends MetadataType { + /** + * Retrieves Metadata of Interaction + * Endpoint /interaction/v1/interactions?extras=all&pageSize=50000 return 50000 Scripts with all details. + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/interaction/v1/interactions?extras=all', null); + } +} + +// Assign definition to static attributes +Interaction.definition = require('../MetadataTypeDefinitions').interaction; + +module.exports = Interaction; diff --git a/lib/metadataTypes/List.js b/lib/metadataTypes/List.js new file mode 100644 index 000000000..7a75d4d40 --- /dev/null +++ b/lib/metadataTypes/List.js @@ -0,0 +1,66 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * List MetadataType + * @augments MetadataType + */ +class List extends MetadataType { + /** + * Retrieves Metadata of Lists + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return super.retrieveSOAPgeneric(retrieveDir); + } + /** + * Gets metadata cache with limited fields and does not store value to disk + * @returns {Promise} Promise of metadata + */ + static async retrieveForCache() { + const results = await this.retrieve(null); + for (const metadataEntry in results.metadata) { + this.parseMetadata(results.metadata[metadataEntry], true); + } + return results; + } + + /** + * manages post retrieve steps + * @param {Object} list a single list + * @returns {Object[]} metadata + */ + static postRetrieveTasks(list) { + return this.parseMetadata(list); + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single list definition + * @param {Boolean} [parseForCache] if set to true, the Category ID is kept + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata, parseForCache) { + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.Category, + 'ID', + 'Path' + ); + if (!parseForCache) { + delete metadata.Category; + } + return metadata; + } catch (ex) { + Util.logger.warn(`List ${metadata.ID}: '${metadata.CustomerKey}': ${ex.message}`); + } + } +} +// Assign definition to static attributes +List.definition = require('../MetadataTypeDefinitions').list; + +module.exports = List; diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js new file mode 100644 index 000000000..d40b81ccd --- /dev/null +++ b/lib/metadataTypes/MetadataType.js @@ -0,0 +1,1342 @@ +'use strict'; + +/* eslint no-unused-vars:off */ +/* + * README no-unused-vars is reduced to WARNING here as this file is a template + * for all metadata types and methods often define params that are not used + * in the generic version of the method + */ + +/** + * @typedef {Object.} MetadataTypeItem + * + * @typedef {Object.} MetadataTypeMap + * + * @typedef {Object.} MultiMetadataTypeMap + */ + +const Util = require('../util/util'); +const File = require('../util/file'); + +// @ts-ignore +const Promise = require('bluebird'); +const Mustache = require('mustache'); + +/** + * MetadataType class that gets extended by their specific metadata type class. + * Provides default functionality that can be overwritten by child metadata type classes + * + */ +class MetadataType { + /** + * Instantiates a metadata constructor to avoid passing variables. + * + * @param {Util.ET_Client} client client for sfmc fuelsdk + * @param {string} businessUnit Name of business unit (corresponding to their keys in 'properties.json' file). Used to access correct directories + * @param {Object} cache metadata cache + * @param {Object} properties mcdev config + * @param {string} [subType] limit retrieve to specific subType + */ + constructor(client, businessUnit, cache, properties, subType) { + this.client = client; + this.businessUnit = businessUnit; + this.cache = cache; + this.properties = properties; + this.subType = subType; + } + + /** + * Returns file contents mapped to their filename without '.json' ending + * @param {string} dir directory that contains '.json' files to be read + * @param {boolean} [listBadKeys=false] do not print errors, used for badKeys() + * @returns {Object} fileName => fileContent map + */ + static getJsonFromFS(dir, listBadKeys) { + const fileName2FileContent = {}; + try { + const files = File.readdirSync(dir); + files.forEach((fileName) => { + try { + if (fileName.endsWith('.json')) { + const fileContent = File.readJSONFile(dir, fileName, true, false); + const fileNameWithoutEnding = File.reverseFilterIllegalFilenames( + fileName.split(/\.(\w|-)+-meta.json/)[0] + ); + // We always store the filename using the External Key (CustomerKey or key) to avoid duplicate names. + // to ensure any changes are done to both the filename and external key do a check here + if ( + fileContent[this.definition.keyField] === fileNameWithoutEnding || + listBadKeys + ) { + fileName2FileContent[fileNameWithoutEnding] = fileContent; + } else { + Util.metadataLogger( + 'error', + this.definition.type, + 'getJsonFromFS', + 'Name of the Metadata and the External Identifier must match', + JSON.stringify({ + Filename: fileNameWithoutEnding, + ExternalIdentifier: fileContent[this.definition.keyField], + }) + ); + } + } + } catch (ex) { + // by catching this in the loop we gracefully handle the issue and move on to the next file + Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex); + } + }); + } catch (ex) { + // this will catch issues with readdirSync + Util.metadataLogger('debug', this.definition.type, 'getJsonFromFS', ex); + throw new Error(ex); + } + return fileName2FileContent; + } + + /** + * Returns fieldnames of Metadata Type. 'this.definition.fields' variable only set in child classes. + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @returns {string[]} Fieldnames + */ + static getFieldNamesToRetrieve(additionalFields) { + const fieldNames = []; + for (const fieldName in this.definition.fields) { + if ( + (additionalFields && additionalFields.includes(fieldName)) || + this.definition.fields[fieldName].retrieving + ) { + fieldNames.push(fieldName); + } + } + if (!fieldNames.includes(this.definition.idField)) { + // Always retrieve the ID because it may be used in references + fieldNames.push(this.definition.idField); + } + return fieldNames; + } + + /** + * Deploys metadata + * @param {MetadataTypeMap} metadata metadata mapped by their keyField + * @param {string} deployDir directory where deploy metadata are saved + * @param {string} retrieveDir directory where metadata after deploy should be saved + * @param {Util.BuObject} buObject properties for auth + * @returns {Promise} Promise of keyField => metadata map + */ + static async deploy(metadata, deployDir, retrieveDir, buObject) { + const upsertResults = await this.upsert(metadata, deployDir, buObject); + await this.postDeployTasks(upsertResults, metadata); + const savedMetadata = await this.saveResults(upsertResults, retrieveDir, null); + if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { + // * do not await here as this might take a while and has no impact on the deploy + this.document(buObject, savedMetadata, true); + } + return upsertResults; + } + + /** + * Gets executed after deployment of metadata type + * @param {MetadataTypeMap} metadata metadata mapped by their keyField + * @param {MetadataTypeMap} originalMetadata metadata to be updated (contains additioanl fields) + * @returns {void} + */ + static postDeployTasks(metadata, originalMetadata) {} + + /** + * Gets executed after retreive of metadata type + * @param {MetadataTypeItem} metadata a single item + * @param {string} targetDir folder where retrieves should be saved + * @param {boolean} [isTemplating] signals that we are retrieving templates + * @returns {MetadataTypeItem} cloned metadata + */ + static postRetrieveTasks(metadata, targetDir, isTemplating) { + return JSON.parse(JSON.stringify(metadata)); + } + /** + * used to synchronize name and external key during retrieveAsTemplate + * @param {MetadataTypeItem} metadata a single item + * @param {string} [warningMsg] optional msg to show the user + * @returns {void} + */ + static overrideKeyWithName(metadata, warningMsg) { + if ( + this.definition.nameField && + this.definition.keyField && + metadata[this.definition.nameField] !== metadata[this.definition.keyField] + ) { + Util.logger.warn( + `Reset external key of ${this.definition.type} ${ + metadata[this.definition.nameField] + } to its name (${metadata[this.definition.keyField]})` + ); + if (warningMsg) { + Util.logger.warn(warningMsg); + } + // do this after printing to cli or we lost the info + metadata[this.definition.keyField] = metadata[this.definition.nameField]; + } + } + /** + * Gets metadata from Marketing Cloud + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {string[]} [overrideFields] Returns specified fields even if their retrieve definition is not set to true + * @param {Util.BuObject} buObject properties for auth + * @param {string} [subType] optionally limit to a single subtype + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata + */ + static retrieve(retrieveDir, overrideFields, buObject, subType) { + Util.metadataLogger('error', this.definition.type, 'retrieve', `Not Supported`); + const metadata = {}; + return { metadata: null, type: this.definition.type }; + } + + /** + * Gets metadata cache with limited fields and does not store value to disk + * @param {Util.BuObject} buObject properties for auth + * @param {string} [subType] optionally limit to a single subtype + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata + */ + static async retrieveForCache(buObject, subType) { + return this.retrieve(null, null, buObject, subType); + } + /** + * Gets metadata cache with limited fields and does not store value to disk + * @param {string} templateDir Directory where retrieved metadata directory will be saved + * @param {string} name name of the metadata file + * @param {Util.TemplateMap} templateVariables variables to be replaced in the metadata + * @param {string} [subType] optionally limit to a single subtype + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} metadata + */ + static retrieveAsTemplate(templateDir, name, templateVariables, subType) { + Util.metadataLogger( + 'error', + this.definition.type, + 'retrieveAsTemplate', + `(${templateDir}, ${name}, ${templateVariables}) no templating function` + ); + return { metadata: null, type: this.definition.type }; + } + + /** + * Gets executed before deploying metadata + * @param {MetadataTypeItem} metadata a single metadata item + * @param {string} deployDir folder where files for deployment are stored + * @returns {Promise} Promise of a single metadata item + */ + static async preDeployTasks(metadata, deployDir) { + return metadata; + } + + /** + * Abstract create method that needs to be implemented in child metadata type + * @param {MetadataTypeItem} metadata single metadata entry + * @param {string} deployDir directory where deploy metadata are saved + * @returns {void} + */ + static create(metadata, deployDir) { + Util.metadataLogger('error', this.definition.type, 'create', 'create not supported'); + return; + } + + /** + * Abstract update method that needs to be implemented in child metadata type + * @param {MetadataTypeItem} metadata single metadata entry + * @param {MetadataTypeItem} [metadataBefore] metadata mapped by their keyField + * @returns {void} + */ + static update(metadata, metadataBefore) { + Util.metadataLogger('error', this.definition.type, 'update', 'update not supported'); + return; + } + + /** + * MetadataType upsert, after retrieving from target and comparing to check if create or update operation is needed. + * @param {MetadataTypeMap} metadata metadata mapped by their keyField + * @param {string} deployDir directory where deploy metadata are saved + * @param {Util.BuObject} [buObject] properties for auth + * @returns {Promise} keyField => metadata map + */ + static async upsert(metadata, deployDir, buObject) { + const metadataToUpdate = []; + const metadataToCreate = []; + for (const metadataKey in metadata) { + try { + // preDeployTasks parsing + const deployableMetadata = await this.preDeployTasks( + metadata[metadataKey], + deployDir + ); + // if preDeploy returns nothing then it cannot be deployed so skip deployment + if (deployableMetadata) { + metadata[metadataKey] = deployableMetadata; + const normalizedKey = File.reverseFilterIllegalFilenames(metadataKey); + // Update if it already exists; Create it if not + if ( + Util.logger.level === 'debug' && + metadata[metadataKey][this.definition.idField] + ) { + // TODO: re-evaluate in future releases if & when we managed to solve folder dependencies once and for all + // only used if resource is excluded from cache and we still want to update it + // needed e.g. to rewire lost folders + Util.logger.warn( + 'Hotfix for non-cachable resource found in deploy folder. Trying update:' + ); + Util.logger.warn(JSON.stringify(metadata[metadataKey])); + metadataToUpdate.push({ + before: {}, + after: metadata[metadataKey], + }); + } else if (this.cache[this.definition.type][normalizedKey]) { + // normal way of processing update files + metadata[metadataKey][this.definition.idField] = this.cache[ + this.definition.type + ][normalizedKey][this.definition.idField]; + metadataToUpdate.push({ + before: this.cache[this.definition.type][normalizedKey], + after: metadata[metadataKey], + }); + } else { + metadataToCreate.push(metadata[metadataKey]); + } + } + } catch (ex) { + Util.metadataLogger('error', this.definition.type, 'upsert', ex, metadataKey); + } + } + + const createResults = ( + await Promise.map( + metadataToCreate, + (metadataEntry) => this.create(metadataEntry, deployDir), + { concurrency: 10 } + ) + ).filter((r) => r !== undefined && r !== null); + const updateResults = ( + await Promise.map( + metadataToUpdate, + (metadataEntry) => this.update(metadataEntry.after, metadataEntry.before), + { concurrency: 10 } + ) + ).filter((r) => r !== undefined && r !== null); + // Logging + Util.metadataLogger( + 'info', + this.definition.type, + 'upsert', + `${createResults.length} of ${metadataToCreate.length} created / ${updateResults.length} of ${metadataToUpdate.length} updated` + ); + + // if Results then parse as SOAP + if (this.definition.bodyIteratorField === 'Results') { + // put in Retrieve Format for parsing + // todo add handling when response does not contain items. + // @ts-ignore + const metadataResults = createResults + .concat(updateResults) + .filter((r) => r !== undefined && r !== null) + .map((r) => r.body.Results) + .flat() + .map((r) => r.Object); + return this.parseResponseBody({ Results: metadataResults }); + } else { + // put in Retrieve Format for parsing + // todo add handling when response does not contain items. + // @ts-ignore + const metadataResults = createResults + .concat(updateResults) + .filter((r) => r !== undefined && r !== null && r.res && r.res.body) + .map((r) => r.res.body); + return this.parseResponseBody({ items: metadataResults }); + } + } + + /** + * Creates a single metadata entry via REST + * @param {MetadataTypeItem} metadataEntry a single metadata Entry + * @param {string} uri rest endpoint for POST + * @returns {Promise} Promise + */ + static async createREST(metadataEntry, uri) { + this.removeNotCreateableFields(metadataEntry); + const options = { + uri: uri, + json: metadataEntry, + headers: {}, + }; + try { + let response; + await Util.retryOnError( + `Retrying ${this.definition.type}: ${metadataEntry[this.definition.nameField]}`, + async () => (response = await this.client.RestClient.post(options)) + ); + this.checkForErrors(response); + Util.logger.info( + `- created ${this.definition.type}: ${metadataEntry[this.definition.keyField]}` + ); + return response; + } catch (ex) { + Util.logger.error( + `- error creating ${this.definition.type}: ${ + metadataEntry[this.definition.keyField] + } (${ex.message})` + ); + return null; + } + } + + /** + * Creates a single metadata entry via fuel-soap (generic lib not wrapper) + * @param {MetadataTypeItem} metadataEntry single metadata entry + * @returns {Promise} Promise + */ + static async createSOAP(metadataEntry) { + try { + const res = await new Promise((resolve, reject) => { + this.removeNotCreateableFields(metadataEntry); + this.client.SoapClient.create( + this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1), + metadataEntry, + null, + (error, response) => (error ? reject(error) : resolve(response)) + ); + }); + Util.logger.info( + `- created ${this.definition.type}: ${metadataEntry[this.definition.keyField]}` + ); + return res; + } catch (ex) { + Util.logger.error( + `- error creating ${this.definition.type}: ${ + metadataEntry[this.definition.keyField] + } (${ex.message})` + ); + + return null; + } + } + + /** + * Updates a single metadata entry via REST + * @param {MetadataTypeItem} metadataEntry a single metadata Entry + * @param {string} uri rest endpoint for PATCH + * @returns {Promise} Promise + */ + static async updateREST(metadataEntry, uri) { + this.removeNotUpdateableFields(metadataEntry); + const options = { + uri: uri, + json: metadataEntry, + headers: {}, + }; + try { + let response; + await Util.retryOnError( + `Retrying ${this.definition.type}: ${metadataEntry[this.definition.nameField]}`, + async () => (response = await this.client.RestClient.patch(options)) + ); + this.checkForErrors(response); + Util.logger.info( + `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}` + ); + return response; + } catch (ex) { + Util.logger.error( + `- error updating ${this.definition.type}: ${ + metadataEntry[this.definition.keyField] + } (${ex.message})` + ); + return null; + } + } + + /** + * Updates a single metadata entry via fuel-soap (generic lib not wrapper) + * @param {MetadataTypeItem} metadataEntry single metadata entry + * @returns {Promise} Promise + */ + static async updateSOAP(metadataEntry) { + try { + const res = await new Promise((resolve, reject) => { + this.removeNotUpdateableFields(metadataEntry); + this.client.SoapClient.update( + this.definition.type.charAt(0).toUpperCase() + this.definition.type.slice(1), + metadataEntry, + null, + (error, response) => (error ? reject(error) : resolve(response)) + ); + }); + Util.logger.info( + `- updated ${this.definition.type}: ${metadataEntry[this.definition.keyField]}` + ); + return res; + } catch (ex) { + Util.logger.error( + `- error updating ${this.definition.type}: ${ + metadataEntry[this.definition.keyField] + } (${ex.message})` + ); + + return null; + } + } + /** + * Retrieves SOAP via generic fuel-soap wrapper based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {Util.BuObject} buObject properties for auth + * @param {Object} [options] required for the specific request (filter for example) + * @param {string[]} [additionalFields] Returns specified fields even if their retrieve definition is not set to true + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} Promise of item map + */ + static async retrieveSOAPgeneric(retrieveDir, buObject, options, additionalFields) { + const fields = this.getFieldNamesToRetrieve(additionalFields); + + const metadata = await this.retrieveSOAPBody(fields, options); + if (retrieveDir) { + const savedMetadata = await this.saveResults(metadata, retrieveDir, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + + if ( + buObject && + this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type) + ) { + await this.document(buObject, savedMetadata); + } + } + return { metadata: metadata, type: this.definition.type }; + } + /** + * helper that handles batched retrieve via SOAP + * @param {string[]} fields list of fields that we want to see retrieved + * @param {Object} [options] required for the specific request (filter for example) + * @param {string} [type] optionally overwrite the API type of the metadata here + * @returns {Promise} keyField => metadata map + */ + static async retrieveSOAPBody(fields, options, type) { + let status; + let batchCounter = 1; + const defaultBatchSize = 2500; // 2500 is the typical batch size + options = options || {}; + let metadata = {}; + do { + const resultsBatch = await new Promise((resolve) => { + this.client.SoapClient.retrieve( + type || this.definition.type, + fields, + options || {}, + (error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response.body); + } + } + ); + }); + status = resultsBatch.OverallStatus; + if (status === 'MoreDataAvailable') { + options.continueRequest = resultsBatch.RequestID; + Util.logger.info( + `- more than ${batchCounter * defaultBatchSize} ${ + this.definition.typeName + }s found in Business Unit - loading next batch...` + ); + batchCounter++; + } + const metadataBatch = this.parseResponseBody(resultsBatch); + + metadata = { ...metadata, ...metadataBatch }; + } while (status === 'MoreDataAvailable'); + + return metadata; + } + + /** + * Retrieves Metadata for Rest Types + * @param {string} retrieveDir Directory where retrieved metadata directory will be saved + * @param {string} uri rest endpoint for GET + * @param {string} [overrideType] force a metadata type (mainly used for Folders) + * @param {Util.TemplateMap} [templateVariables] variables to be replaced in the metadata + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} Promise of item map + */ + static async retrieveREST(retrieveDir, uri, overrideType, templateVariables) { + const options = { + uri: uri, + headers: {}, + }; + let moreResults; + let lastPage = null; + let results = {}; + do { + options.uri = this.paginate(options.uri, lastPage); + const response = await this.client.RestClient.get(options); + const metadata = this.parseResponseBody(response.body); + results = Object.assign(results, metadata); + if ( + this.definition.restPagination && + Object.keys(metadata).length > 0 && + response.body.page * response.body.pageSize < response.body.count + ) { + lastPage = Number(response.body.page); + moreResults = true; + } else { + moreResults = false; + } + } while (moreResults); + // get extended metadata if applicable + if (this.definition.hasExtended) { + const extended = await Promise.all( + Object.keys(results).map((key) => + this.client.RestClient.get({ + uri: uri + results[key][this.definition.idField], + }) + ) + ); + for (const ext of extended) { + const key = ext.body[this.definition.keyField]; + results[key] = Object.assign(results[key], ext.body); + } + } + + if (retrieveDir) { + const savedMetadata = await this.saveResults( + results, + retrieveDir, + overrideType, + templateVariables + ); + Util.logger.info( + `Downloaded: ${overrideType || this.definition.type} (${ + Object.keys(savedMetadata).length + })` + ); + } + + return { metadata: results, type: overrideType || this.definition.type }; + } + + /** + * Builds map of metadata entries mapped to their keyfields + * @param {Object} body json of response body + * @param {string} [bodyIteratorField] overrides bodyIteratorField of definition + * @param {string} [keyField] overrides keyField of definition + * @returns {Promise} keyField => metadata map + */ + static parseResponseBody(body, bodyIteratorField, keyField) { + if (!bodyIteratorField) { + bodyIteratorField = this.definition.bodyIteratorField; + } + if (!keyField) { + keyField = this.definition.keyField; + } + const metadataStructure = {}; + // in some cases data is just an array + if (Array.isArray(bodyIteratorField)) { + for (const item of body) { + const key = item[keyField]; + metadataStructure[key] = item; + } + } else { + for (const item of body[bodyIteratorField]) { + const key = item[keyField]; + metadataStructure[key] = item; + } + } + return metadataStructure; + } + + /** + * Deletes a field in a metadata entry if the selected definition property equals false. + * @example + * Removes field (or nested fields childs) that are not updateable + * deleteFieldByDefinition(metadataEntry, 'CustomerKey', 'isUpdateable'); + * @param {MetadataTypeItem} metadataEntry One entry of a metadataType + * @param {string} fieldPath field path to be checked if it conforms to the definition (dot seperated if nested): 'fuu.bar' + * @param {'isCreateable'|'isUpdateable'|'retrieving'|'templating'} definitionProperty delete field if definitionProperty equals false for specified field. Options: [isCreateable | isUpdateable] + * @param {string} origin string of parent object, required when using arrays as these are parsed slightly differently. + * @returns {void} + */ + static deleteFieldByDefinition(metadataEntry, fieldPath, definitionProperty, origin) { + // Get content of nested property + let fieldContent; + try { + fieldContent = fieldPath.split('.').reduce((field, key) => field[key], metadataEntry); + } catch (e) { + // when we hit fields that have dots in their name (e.g. interarction, metaData['simulation.id']) then this will fail + // decided to skip these cases for now entirely + return; + } + let originHelper; + + // revert back placeholder to dots + if (origin) { + originHelper = origin + '.' + fieldPath; + } else { + originHelper = fieldPath; + } + + if ( + this.definition.fields[originHelper] && + this.definition.fields[originHelper].skipValidation + ) { + // skip if current field should not be validated + return; + } else if ( + Array.isArray(fieldContent) && + this.definition.fields[originHelper] && + this.definition.fields[originHelper][definitionProperty] === true + ) { + for (const subObject of fieldContent) { + // for simple arrays skip, only process object or array arrays further + if (Array.isArray(subObject) || typeof subObject === 'object') { + for (const subField in subObject) { + this.deleteFieldByDefinition( + subObject, + subField, + definitionProperty, + originHelper + '[]' + ); + } + } + } + } else if ( + typeof fieldContent === 'object' && + !Array.isArray(fieldContent) && + (this.definition.fields[originHelper] == null || + this.definition.fields[originHelper][definitionProperty] === true) + ) { + // Recursive call of this method if there are nested fields + for (const subField in fieldContent) { + this.deleteFieldByDefinition( + metadataEntry, + originHelper + '.' + subField, + definitionProperty, + null + ); + } + } else if (!this.definition.fields[originHelper]) { + // Display warining if there is no definition for the current field + Util.logger.verbose( + `MetadataType[${this.definition.type}].deleteFieldByDefinition[${definitionProperty}]:: Field ${originHelper} not in metadata info` + ); + } else if (this.definition.fields[originHelper][definitionProperty] === false) { + // Check if field/nested field should be deleted depending on the definitionProperty + fieldPath.split('.').reduce((field, key, index) => { + if (index === fieldPath.split('.').length - 1) { + delete field[key]; + } else { + return field[key]; + } + }, metadataEntry); + } + } + /** + * Remove fields from metadata entry that are not createable + * @param {MetadataTypeItem} metadataEntry metadata entry + * @returns {void} + */ + static removeNotCreateableFields(metadataEntry) { + for (const field in metadataEntry) { + this.deleteFieldByDefinition(metadataEntry, field, 'isCreateable', null); + } + } + + /** + * Remove fields from metadata entry that are not updateable + * @param {MetadataTypeItem} metadataEntry metadata entry + * @returns {void} + */ + static removeNotUpdateableFields(metadataEntry) { + for (const field in metadataEntry) { + this.deleteFieldByDefinition(metadataEntry, field, 'isUpdateable', null); + } + } + + /** + * Remove fields from metadata entry that are not needed in the template + * @param {MetadataTypeItem} metadataEntry metadata entry + * @returns {void} + */ + static keepTemplateFields(metadataEntry) { + for (const field in metadataEntry) { + this.deleteFieldByDefinition(metadataEntry, field, 'template', null); + } + } + + /** + * Remove fields from metadata entry that are not needed in the stored metadata + * @param {MetadataTypeItem} metadataEntry metadata entry + * @returns {void} + */ + static keepRetrieveFields(metadataEntry) { + for (const field in metadataEntry) { + this.deleteFieldByDefinition(metadataEntry, field, 'retrieving', null); + } + } + + /** + * checks if the current metadata entry should be saved on retrieve or not + * @static + * @param {MetadataTypeItem} metadataEntry metadata entry + * @param {boolean} [include=false] true: use definition.include / options.include; false=exclude: use definition.filter / options.exclude + * @returns {boolean} true: skip saving == filtered; false: continue with saving + * @memberof MetadataType + */ + static isFiltered(metadataEntry, include) { + if (include) { + // check include-only filters (== discard rest) + const includeByDefinition = this._filterOther(this.definition.include, metadataEntry); + const includeByConfig = this._filterOther( + this.properties.options.include[this.definition.type], + metadataEntry + ); + if (includeByDefinition === false || includeByConfig === false) { + Util.logger.debug( + `Filtered ${this.definition.type} '${ + metadataEntry[this.definition.nameField] + }' (${metadataEntry[this.definition.keyField]}): not matching include filter` + ); + + return true; + } + } else { + // check exclude-only filters (== keep rest) + const excludeByDefinition = this._filterOther(this.definition.filter, metadataEntry); + const excludeByConfig = this._filterOther( + this.properties.options.exclude[this.definition.type], + metadataEntry + ); + if (excludeByDefinition || excludeByConfig) { + Util.logger.debug( + `Filtered ${this.definition.type} '${ + metadataEntry[this.definition.nameField] + }' (${metadataEntry[this.definition.keyField]}): matching exclude filter` + ); + return true; + } + } + // this metadata type has no filters defined or no match was found + return false; + } + /** + * optionally filter by what folder something is in + * @static + * @param {Object} metadataEntry metadata entry + * @param {boolean} [include=false] true: use definition.include / options.include; false=exclude: use definition.filter / options.exclude + * @returns {boolean} true: filtered == do NOT save; false: not filtered == do save + * @memberof MetadataType + */ + static isFilteredFolder(metadataEntry, include) { + if (metadataEntry.json && metadataEntry.json.r__folder_Path) { + // r__folder_Path found in sub-object + metadataEntry = metadataEntry.json; + } else if (!metadataEntry.r__folder_Path) { + // no r__folder_Path found at all + return false; + } + // r__folder_Path found + + if (include) { + const errorMsg = `Filtered ${this.definition.type} '${ + metadataEntry[this.definition.nameField] + }' (${ + metadataEntry[this.definition.keyField] + }): not matching include filter for folder`; + // check include-only filters (== discard rest) + const includeByDefinition = this._filterFolder( + this.definition.include, + metadataEntry.r__folder_Path + ); + if (includeByDefinition === false) { + Util.logger.debug(errorMsg + ' (Accenture SFMC DevTools default)'); + return true; + } + + const includeByConfig = this._filterFolder( + this.properties.options.include[this.definition.type], + metadataEntry.r__folder_Path + ); + if (includeByConfig === false) { + Util.logger.debug(errorMsg + ' (project config)'); + return true; + } + } else { + const errorMsg = `Filtered ${this.definition.type} '${ + metadataEntry[this.definition.nameField] + }' (${metadataEntry[this.definition.keyField]}): matching exclude filter for folder`; + // check exclude-only filters (== keep rest) + const excludeByDefinition = this._filterFolder( + this.definition.filter, + metadataEntry.r__folder_Path + ); + if (excludeByDefinition) { + Util.logger.debug(errorMsg + ' (project config)'); + return true; + } + + const excludeByConfig = this._filterFolder( + this.properties.options.exclude[this.definition.type], + metadataEntry.r__folder_Path + ); + if (excludeByConfig) { + Util.logger.debug(errorMsg + ' (Accenture SFMC DevTools default)'); + return true; + } + } + // this metadata type has no filters defined or no match was found + return false; + } + /** + * internal helper + * @private + * @param {Object} myFilter include/exclude filter object + * @param {string} r__folder_Path already determined folder path + * @returns {?boolean} true: filter value found; false: filter value not found; null: no filter defined + */ + static _filterFolder(myFilter, r__folder_Path) { + if (!myFilter || !myFilter.r__folder_Path) { + // no filter defined + return null; + } + // consolidate input: could be String[] or String + const filteredValues = Array.isArray(myFilter.r__folder_Path) + ? myFilter.r__folder_Path + : [myFilter.r__folder_Path]; + + for (const path of filteredValues) { + if (r__folder_Path.startsWith(path)) { + // filter matched + // this filters the given folder and anything below it. + // to only filter subfolders, end with "/", to also filter the given folder, omit the "/" + return true; + } + } + + // no filters matched + return false; + } + /** + * internal helper + * @private + * @param {Object} myFilter include/exclude filter object + * @param {Object} metadataEntry metadata entry + * @returns {?boolean} true: filter value found; false: filter value not found; null: no filter defined + */ + static _filterOther(myFilter, metadataEntry) { + // not possible to check r__folder_Path before parseMetadata was run; handled in `isFilteredFolder()` + if ( + !myFilter || + !Object.keys(myFilter).filter((item) => item !== 'r__folder_Path').length + ) { + // no filter defined + return null; + } + + for (const key in myFilter) { + // consolidate input: could be String[] or String + const filteredValues = Array.isArray(myFilter[key]) ? myFilter[key] : [myFilter[key]]; + + if (filteredValues.includes(metadataEntry[key])) { + // filter matched + return true; + } + } + + // no filters matched + return false; + } + + /** + * Paginates a URL + * @param {string} url url of the request + * @param {number} last Number of the page of the last request + * @returns {string} new url with pagination + */ + static paginate(url, last) { + if (this.definition.restPagination) { + const baseUrl = url.split('?')[0]; + const queryParams = new URLSearchParams(url.split('?')[1]); + // if no page add page + if (!queryParams.has('$page')) { + queryParams.append('$page', (1).toString()); + } + // if there is a page and a last value, then add to it. + else if (queryParams.has('$page') && last) { + queryParams.set('$page', (Number(last) + 1).toString()); + } + return baseUrl + '?' + decodeURIComponent(queryParams.toString()); + } else { + return url; + } + } + /** + * Helper for writing Metadata to disk, used for Retrieve and deploy + * @param {MetadataTypeMap} results metadata results from deploy + * @param {string} retrieveDir directory where metadata should be stored after deploy/retrieve + * @param {string} [overrideType] for use when there is a subtype (such as folder-queries) + * @param {Util.TemplateMap} [templateVariables] variables to be replaced in the metadata + * @returns {Promise} Promise of saved metadata + */ + static async saveResults(results, retrieveDir, overrideType, templateVariables) { + const savedResults = {}; + const subtypeExtension = '.' + (overrideType || this.definition.type) + '-meta'; + let filterCounter = 0; + for (const metadataEntry in results) { + try { + if ( + this.isFiltered(results[metadataEntry], true) || + this.isFiltered(results[metadataEntry], false) + ) { + // if current metadata entry is filtered don't save it + filterCounter++; + continue; + } + // define directory into which the current metdata shall be saved + const baseDir = [retrieveDir, ...(overrideType || this.definition.type).split('-')]; + + results[metadataEntry] = await this.postRetrieveTasks( + results[metadataEntry], + retrieveDir, + templateVariables ? true : false + ); + if (!results[metadataEntry] || results[metadataEntry] === null) { + // we encountered a situation in our postRetrieveTasks that made us want to filter this record + delete results[metadataEntry]; + filterCounter++; + continue; + } + + if ( + this.isFilteredFolder(results[metadataEntry], true) || + this.isFilteredFolder(results[metadataEntry], false) + ) { + // if current metadata entry is filtered don't save it + filterCounter++; + continue; + } + + // for complex types like asset, script, query we need to save the scripts that were extracted from the JSON + if (results[metadataEntry].json && results[metadataEntry].codeArr) { + // replace market values with template variable placeholders (do not do it on .codeArr) + if (templateVariables) { + results[metadataEntry].json = Util.replaceByObject( + results[metadataEntry].json, + templateVariables + ); + results[metadataEntry].subFolder = Util.replaceByObject( + results[metadataEntry].subFolder, + templateVariables + ); + } + + const postRetrieveData = results[metadataEntry]; + if (postRetrieveData.subFolder) { + // very complex types have their own subfolder + baseDir.push(...postRetrieveData.subFolder); + } + // save extracted scripts + for (const script of postRetrieveData.codeArr) { + const dir = [...baseDir]; + if (script.subFolder) { + // some files shall be saved in yet a deeper subfolder + dir.push(...script.subFolder); + } + File.writePrettyToFile( + dir, + script.fileName + subtypeExtension, + script.fileExt, + script.content, + templateVariables + ); + } + // normalize results[metadataEntry] + results[metadataEntry] = postRetrieveData.json; + } else { + // not a complex type, run the the entire JSON through templating + // replace market values with template variable placeholders + if (templateVariables) { + results[metadataEntry] = Util.replaceByObject( + results[metadataEntry], + templateVariables + ); + } + } + + // we dont store Id on local disk, but we need it for caching logic, + // so its in retrieve but not in save. Here we put into the clone so that the original + // object used for caching doesnt have the Id removed. + const saveClone = JSON.parse(JSON.stringify(results[metadataEntry])); + if (!this.definition.keepId) { + delete saveClone[this.definition.idField]; + } + + if (templateVariables) { + this.keepTemplateFields(saveClone); + } else { + this.keepRetrieveFields(saveClone); + } + savedResults[metadataEntry] = saveClone; + File.writeJSONToFile( + // manage subtypes + baseDir, + saveClone[this.definition.keyField] + subtypeExtension, + saveClone + ); + } catch (ex) { + console.log(ex.stack); + Util.metadataLogger( + 'error', + this.definition.type, + 'saveResults', + ex, + metadataEntry + ); + } + } + if (filterCounter) { + if (this.definition.type !== 'asset') { + // interferes with progress bar in assets and is printed 1-by-1 otherwise + Util.logger.info( + `Filtered ${this.definition.type}: ${filterCounter} (downloaded but not saved to disk)` + ); + } + } + return savedResults; + } + /** + * helper for buildDefinition + * handles extracted code if any are found for complex types (e.g script, asset, query) + * @param {string} templateDir Directory where metadata templates are stored + * @param {string} targetDir Directory where built definitions will be saved + * @param {MetadataTypeItem} metadata main JSON file that was read from file system + * @param {Util.TemplateMap} variables variables to be replaced in the metadata + * @param {string} templateName name of the template to be built + * @returns {Promise} Promise + */ + static async buildDefinitionForExtracts( + templateDir, + targetDir, + metadata, + variables, + templateName + ) { + // generic version here does nothing. actual cases handled in type classes + return null; + } + /** + * check template directory for complex types that open subfolders for their subtypes + * @param {string} templateDir Directory where metadata templates are stored + * @param {string} templateName name of the metadata file + * @returns {string} subtype name + */ + static findSubType(templateDir, templateName) { + return null; + } + /** + * optional method used for some types to try a different folder structure + * @param {string} templateDir Directory where metadata templates are stored + * @param {string[]} typeDirArr current subdir for this type + * @param {string} templateName name of the metadata template + * @param {string} fileName name of the metadata template file w/o extension + * @param {Error} ex error from first attempt + * @returns {Object} metadata + */ + static async readSecondaryFolder(templateDir, typeDirArr, templateName, fileName, ex) { + // we just want to push the method into the catch here + throw new Error(ex); + } + /** + * Builds definition based on template + * NOTE: Most metadata files should use this generic method, unless custom + * parsing is required (for example scripts & queries) + * @param {string} templateDir Directory where metadata templates are stored + * @param {String|String[]} targetDir (List of) Directory where built definitions will be saved + * @param {string} templateName name of the metadata file + * @param {Util.TemplateMap} variables variables to be replaced in the metadata + * @returns {Promise<{metadata:MetadataTypeMap,type:string}>} Promise of item map + */ + static async buildDefinition(templateDir, targetDir, templateName, variables) { + // retrieve metadata template + let metadataStr; + let typeDirArr = [this.definition.type]; + const subType = this.findSubType(templateDir, templateName); + if (subType) { + typeDirArr.push(subType); + } + const suffix = subType ? `-${subType}-meta` : '-meta'; + const fileName = templateName + '.' + this.definition.type + suffix; + try { + // ! do not load via readJSONFile to ensure we get a string, not parsed JSON + // templated files might contain illegal json before the conversion back to the file that shall be saved + metadataStr = await File.readFile([templateDir, ...typeDirArr], fileName, 'json'); + } catch (ex) { + try { + metadataStr = await this.readSecondaryFolder( + templateDir, + typeDirArr, + templateName, + fileName, + ex + ); + } catch (ex) { + throw new Error( + `${this.definition.type}:: Could not find ./${File.normalizePath([ + templateDir, + ...typeDirArr, + fileName + '.json', + ])}.` + ); + } + // return; + } + + let metadata; + try { + // update all initial variables & create metadata object + metadata = JSON.parse(Mustache.render(metadataStr, variables)); + typeDirArr = typeDirArr.map((el) => Mustache.render(el, variables)); + } catch (ex) { + throw new Error( + `${this.definition.type}:: Error applying template variables on ${ + templateName + '.' + this.definition.type + }${suffix}.json. Please check if your replacement values will result in valid json.` + ); + } + + // handle extracted code + // run after metadata was templated and converted into JS-object + // templating to extracted content is applied inside of buildDefinitionForExtracts() + await this.buildDefinitionForExtracts( + templateDir, + targetDir, + metadata, + variables, + templateName + ); + + try { + // write to file + let targetDirArr; + if (!Array.isArray(targetDir)) { + targetDirArr = [targetDir]; + } else { + targetDirArr = targetDir; + } + for (const targetDir of targetDirArr) { + File.writeJSONToFile( + [targetDir, ...typeDirArr], + metadata[this.definition.keyField] + '.' + this.definition.type + suffix, + metadata + ); + } + Util.logger.info( + 'MetadataType[' + + this.definition.type + + '].buildDefinition:: Complete - ' + + metadata[this.definition.keyField] + ); + + return { metadata: metadata, type: this.definition.type }; + } catch (ex) { + throw new Error(`${this.definition.type}:: ${ex.message}`); + } + } + /** + * + * @param {Object} response response payload from REST API + * @returns {void} + */ + static checkForErrors(response) { + if (response && response.res.statusCode >= 400 && response.res.statusCode < 600) { + const errors = []; + if (response.body.errors) { + for (const errMsg of response.body.errors) { + errors.push(errMsg.message.split('
').join('')); + } + } else if (response.body.validationErrors) { + for (const errMsg of response.body.validationErrors) { + errors.push(errMsg.message.split('
').join('')); + } + } else if (response.body.message) { + errors.push(response.body.message); + } else { + errors.push(`Undefined Errors: ${JSON.stringify(response.body)}`); + } + throw new Error( + `Errors on upserting metadata at ${response.res.request.path}: ${errors.join( + '
' + )}` + ); + } + } + + /** + * Gets metadata cache with limited fields and does not store value to disk + * @param {Util.BuObject} [buObject] properties for auth + * @param {MetadataTypeMap} [metadata] a list of type definitions + * @param {boolean} [isDeploy] used to skip non-supported message during deploy + * @returns {void} + */ + static document(buObject, metadata, isDeploy) { + if (!isDeploy) { + Util.logger.error(`Documenting type ${this.definition.type} is not supported.`); + } + } + + /** + * Delete a data extension from the specified business unit + * @param {Util.BuObject} buObject references credentials + * @param {string} customerKey Identifier of data extension + * @returns {void} - + */ + static deleteByKey(buObject, customerKey) { + Util.logger.error(`Deleting type ${this.definition.type} is not supported.`); + } + /** + * Returns metadata of a business unit that is saved locally + * @param {string} readDir root directory of metadata. + * @param {boolean} [listBadKeys=false] do not print errors, used for badKeys() + * @param {Object} [buMetadata] Metadata of BU in local directory + * @returns {Object} Metadata of BU in local directory + */ + static readBUMetadataForType(readDir, listBadKeys, buMetadata) { + buMetadata = buMetadata || {}; + readDir = File.normalizePath([readDir, this.definition.type]); + try { + if (File.existsSync(readDir)) { + // check if folder name is a valid metadataType, then check if the user limited to a certain type in the command params + buMetadata[this.definition.type] = this.getJsonFromFS(readDir, listBadKeys); + return buMetadata; + } else { + throw new Error(`Directory '${readDir}' does not exist.`); + } + } catch (ex) { + throw new Error(ex.message); + } + } +} + +MetadataType.definition = { + bodyIteratorField: '', + dependencies: [], + fields: null, + hasExtended: null, + idField: '', + keyField: '', + nameField: '', + type: '', +}; +/** + * @type {Util.ET_Client} + */ +MetadataType.client = undefined; +/** + * @type {MultiMetadataTypeMap} + */ +MetadataType.cache = {}; +MetadataType.properties = null; + +module.exports = MetadataType; diff --git a/lib/metadataTypes/Query.js b/lib/metadataTypes/Query.js new file mode 100644 index 000000000..0b1c3711c --- /dev/null +++ b/lib/metadataTypes/Query.js @@ -0,0 +1,221 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); +const Mustache = require('mustache'); +/** + * Query MetadataType + * @augments MetadataType + */ +class Query extends MetadataType { + /** + * Retrieves Metadata of queries + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static async retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/automation/v1/queries/', null); + } + + /** + * Retrieves query metadata for caching + * @returns {Promise} Promise of metadata + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/queries/', null); + } + + /** + * Retrieve a specific Query by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} templateVariables variables to be replaced in the metadata + * @returns {Promise} Promise of metadata + */ + static async retrieveAsTemplate(templateDir, name, templateVariables) { + return super.retrieveREST( + templateDir, + '/automation/v1/queries/?$filter=Name%20eq%20' + name.split(' ').join('%20'), + null, + templateVariables + ); + } + + /** + * manages post retrieve steps + * @param {Object} metadata a single query + * @param {String} _ unused + * @param {Boolean} isTemplating signals that we are retrieving templates + * @returns {Object[]} Array with one metadata object and one query string + */ + static postRetrieveTasks(metadata, _, isTemplating) { + // if retrieving template, replace the name with customer key if that wasn't already the case + if (isTemplating) { + const warningMsg = + 'Ensure that Automations using this query are updated with the new query-key before deployment.'; + this.overrideKeyWithName(metadata, warningMsg); + } + return this.parseMetadata(metadata); + } + + /** + * Creates a single query + * @param {Object} query a single query + * @returns {Promise} Promise + */ + static create(query) { + const uri = '/automation/v1/queries/'; + return super.createREST(query, uri); + } + + /** + * Updates a single query + * @param {Object} query a single query + * @returns {Promise} Promise + */ + static update(query) { + const uri = '/automation/v1/queries/' + query.queryDefinitionId; + return super.updateREST(query, uri); + } + + /** + * prepares a Query for deployment + * @param {Object} metadata a single query activity definition + * @param {String} deployDir directory of deploy files + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata, deployDir) { + metadata.queryText = await File.readFile( + deployDir + '/' + this.definition.type, + metadata.key + '.' + this.definition.type + '-meta', + 'sql' + ); + try { + metadata.targetKey = Util.getFromCache( + this.cache, + 'dataExtension', + metadata.targetKey, + 'CustomerKey', + 'CustomerKey' + ); + } catch (ex) { + throw new Error(`Query '${metadata.key}': ${ex.message}`); + } + try { + metadata.categoryId = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + delete metadata.r__folder_Path; + } catch (ex) { + throw new Error(`Query '${metadata.key}': ${ex.message}`); + } + metadata.targetUpdateTypeId = this.definition.targetUpdateTypeMapping[ + metadata.targetUpdateTypeName + ]; + return metadata; + } + + /** + * helper for buildDefinition + * handles extracted code if any are found for complex types + * @param {String} templateDir Directory where metadata templates are stored + * @param {String|String[]} targetDir (List of) Directory where built definitions will be saved + * @param {Object} metadata main JSON file that was read from file system + * @param {Object} variables variables to be replaced in the metadata + * @param {String} templateName name of the template to be built + * @returns {Promise} Promise + */ + static async buildDefinitionForExtracts( + templateDir, + targetDir, + metadata, + variables, + templateName + ) { + // get SQL from filesystem + let code = await File.readFile( + [templateDir, this.definition.type], + templateName + '.' + this.definition.type + '-meta', + 'sql' + ); + // fix bad formatting applied by SQL Formatter Plus + code = code + .split(' { { { ') + .join('{{{') + .split('{ { { ') + .join('{{{') + .split(' } } } ') + .join('}}}') + .split(' } } }') + .join('}}}'); + + // replace template variables with their values + try { + code = Mustache.render(code, variables); + } catch (ex) { + throw new Error( + `${this.definition.type}:: Error applying template variables on ${ + metadata[this.definition.keyField] + '.' + this.definition.type + }-meta.sql.` + ); + } + + // write to file + const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir]; + + for (const targetDir of targetDirArr) { + File.writeToFile( + [targetDir, this.definition.type], + metadata[this.definition.keyField] + '.' + this.definition.type + '-meta', + 'sql', + code + ); + } + } + + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single query activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.categoryId, + 'ID', + 'Path' + ); + delete metadata.categoryId; + } catch (ex) { + Util.logger.warn(`Query '${metadata.key}': ${ex.message}`); + } + + // extract SQL + const codeArr = [ + { + subFolder: null, + fileName: metadata.key, + fileExt: 'sql', + content: metadata.queryText, + }, + ]; + delete metadata.queryText; + + return { json: metadata, codeArr: codeArr, subFolder: null }; + } +} + +// Assign definition & cache to static attributes +Query.definition = require('../MetadataTypeDefinitions').query; +Query.cache = {}; +Query.client = undefined; + +module.exports = Query; diff --git a/lib/metadataTypes/Role.js b/lib/metadataTypes/Role.js new file mode 100644 index 000000000..d3d395b8f --- /dev/null +++ b/lib/metadataTypes/Role.js @@ -0,0 +1,273 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); + +/** + * ImportFile MetadataType + * @augments MetadataType + */ +class Role extends MetadataType { + /** + * Gets metadata from Marketing Cloud + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @param {String[]} _ Returns specified fields even if their retrieve definition is not set to true + * @param {Object} buObject properties for auth + * @returns {Promise} Metadata store object + */ + static async retrieve(retrieveDir, _, buObject) { + if (retrieveDir) { + // don't run for BUs other than Parent BU + // this check does not work during caching + if (buObject.eid !== buObject.mid) { + Util.logger.info('Skipping Role retrieval on non-parent BU'); + return; + } + } + + const fields = Object.keys(this.definition.fields).reduce((accumulator, currentValue) => { + if (this.definition.fields[currentValue].retrieving) { + accumulator.push(currentValue); + } + return accumulator; + }, []); + const options = { + // filter individual roles + filter: { + leftOperand: 'IsPrivate', + operator: 'equals', + rightOperand: false, + }, + }; + const results = await new Promise((resolve) => { + this.client.SoapClient.retrieve('Role', fields, options, (error, response) => { + if (error) { + throw new Error(error); + } else { + resolve(response.body); + } + }); + }); + const parsed = this.parseResponseBody(results); + if (retrieveDir) { + const savedMetadata = await this.saveResults(parsed, retrieveDir, null); + Util.logger.info( + `Downloaded: ${this.definition.type} (${Object.keys(savedMetadata).length})` + ); + if (this.properties.metaDataTypes.documentOnRetrieve.includes(this.definition.type)) { + await this.document(buObject, savedMetadata); + } + } + return { metadata: parsed, type: this.definition.type }; + } + /** + * Gets executed before deploying metadata + * @param {Object} metadata a single metadata item + * @returns {Promise} Promise of a single metadata item + */ + static async preDeployTasks(metadata) { + if (this.definition.deployBlacklist.includes(metadata.CustomerKey)) { + throw new Error( + `Skipped ${metadata.Name} (${metadata.CustomerKey}) because its CustomerKey is reserved for a default system role.` + ); + } + return metadata; + } + + /** + * Create a single Role. + * @param {Object} metadata single metadata entry + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createSOAP(metadata); + } + + /** + * Updates a single Role. + * @param {Object} metadata single metadata entry + * @returns {Promise} Promise + */ + static update(metadata) { + return super.updateSOAP(metadata); + } + + /** + * Creates markdown documentation of all roles + * @param {Object} buObject properties for auth + * @param {Object} [metadata] role definitions + * @returns {Promise} - + */ + static async document(buObject, metadata) { + if (buObject.eid !== buObject.mid) { + Util.logger.error( + `Roles can only be retrieved & documented for the ${Util.parentBuName}` + ); + return; + } + if (!metadata) { + metadata = this.readBUMetadataForType( + File.normalizePath([ + this.properties.directories.retrieve, + buObject.credential, + Util.parentBuName, + ]), + true + ).role; + } + const directory = this.properties.directories.roles; + + // initialize permission object + this.allPermissions = {}; + // traverse all permissions recursively and write them into allPermissions object once it has reached the end + for (const role in metadata) { + metadata[role].PermissionSets.PermissionSet.forEach((element) => { + this._traverseRoles(role, element); + }); + } + // Create output markdown + let output = `# Permission Overview - ${buObject.credential}\n\n`; + output += `> **Legend** +> +>
+> +> **[Role Name]** = System Default Role +> +> **Role Name** = Custom Role +> +> **+** = _Allow_: User has access to the application or functionality +> +> **•** = _Not Set_: User permission for this app or functionality is not explicitely granted nor denied, but defaults to Deny +> +> **-** = _Deny_: User does not have access to the app or functionality +> +>
\n\n`; + // Loop through all permissions + for (const permGroup in this.allPermissions) { + output += '## ' + permGroup + '\n\n'; + // create table header + output += '| Permission |'; + let separator = '| --- |'; + for (const role in metadata) { + output += + metadata[role].IsSystemDefined === 'true' + ? ` [${metadata[role].Name}] |` + : ` ${metadata[role].Name} |`; + separator += ' --- |'; + } + output += '\n' + separator + '\n'; + // Write all permissions of a major permission group + // output += '| '; + for (const permission in this.allPermissions[permGroup]) { + output += '| ' + permission + ' |'; + for (const role in this.allPermissions[permGroup][permission]) { + if (this.allPermissions[permGroup][permission][role] === 'true') { + output += ' + |'; + } else if (this.allPermissions[permGroup][permission][role] === 'false') { + output += ' - |'; + } else if ( + 'undefined' === typeof this.allPermissions[permGroup][permission][role] + ) { + output += ' • |'; + } else { + output += ' ' + this.allPermissions[permGroup][permission][role] + ' |'; + } + } + output += '\n'; + } + output += '\n'; + } + try { + const filename = buObject.credential; + // ensure docs/roles folder is existing (depends on setup in .mcdevrc.json) + if (!File.existsSync(directory)) { + File.mkdirpSync(directory); + } + // write to disk + await File.writeToFile(directory, filename + '.roles', 'md', output); + Util.logger.info(`Created ${directory}${filename}.roles.md`); + if (['html', 'both'].includes(this.properties.options.documentType)) { + Util.logger.warn('HTML-based documentation of roles currently not supported.'); + } + } catch (ex) { + Util.logger.error(`Roles.document():: error | `, ex.message); + } + } + + /** + * iterates through permissions to output proper row-names for nested permissionss + * + * @static + * @param {string} role name of the user role + * @param {object} element data of the permission + * @param {string} [permission] name of the permission + * @param {string} [isAllowed] "true" / "false" from the + * @memberof Role + * @returns {void} + */ + static _traverseRoles(role, element, permission, isAllowed) { + let _permission; + if (permission) { + _permission = permission + ' > ' + element.Name; + } else { + _permission = element.Name; + } + // Reached end: write permission into this.allPermissions + if (element.Operation) { + const permSplit = _permission.split(' > '); + const basePermission = permSplit.shift(); + const permissionName = permSplit.join(' > '); + if (!this.allPermissions[basePermission]) { + this.allPermissions[basePermission] = {}; + } + if (!this.allPermissions[basePermission][permissionName]) { + this.allPermissions[basePermission][permissionName] = {}; + } + this.allPermissions[basePermission][permissionName][role] = element.IsAllowed + ? element.IsAllowed + : isAllowed; + // Not at end: Traverse more + } else if (element.PermissionSets) { + if (Array.isArray(element.PermissionSets.PermissionSet)) { + element.PermissionSets.PermissionSet.forEach((nextElement) => { + this._traverseRoles(role, nextElement, _permission); + }); + } else { + this._traverseRoles( + role, + element.PermissionSets.PermissionSet, + _permission, + element.IsAllowed, + isAllowed + ); + } + // Not at end: Traverse more + } else if (element.Permissions) { + if (Array.isArray(element.Permissions.Permission)) { + element.Permissions.Permission.forEach((nextElement) => { + this._traverseRoles( + role, + nextElement, + _permission, + element.IsAllowed ? element.IsAllowed : isAllowed + ); + }); + } else { + this._traverseRoles( + role, + element.Permissions.Permission, + _permission, + element.IsAllowed ? element.IsAllowed : isAllowed + ); + } + } + } +} + +// Assign definition to static attributes +Role.definition = require('../MetadataTypeDefinitions').role; +Role.cache = {}; +Role.client = undefined; + +module.exports = Role; diff --git a/lib/metadataTypes/Script.js b/lib/metadataTypes/Script.js new file mode 100644 index 000000000..f71395539 --- /dev/null +++ b/lib/metadataTypes/Script.js @@ -0,0 +1,243 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); +const File = require('../util/file'); +const Mustache = require('mustache'); + +/** + * Script MetadataType + * @augments MetadataType + */ +class Script extends MetadataType { + /** + * Retrieves Metadata of Script + * Endpoint /automation/v1/scripts/ return all Scripts with all details. + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static async retrieve(retrieveDir) { + await File.initPrettier('ssjs'); + return super.retrieveREST(retrieveDir, '/automation/v1/scripts/', null); + } + /** + * Retrieves script metadata for caching + * @returns {Promise} Promise + */ + static async retrieveForCache() { + return super.retrieveREST(null, '/automation/v1/scripts/', null); + } + + /** + * Retrieve a specific Script by Name + * @param {String} templateDir Directory where retrieved metadata directory will be saved + * @param {String} name name of the metadata file + * @param {Object} templateVariables variables to be replaced in the metadata + * @returns {Promise} Promise + */ + static async retrieveAsTemplate(templateDir, name, templateVariables) { + await File.initPrettier('ssjs'); + return super.retrieveREST( + templateDir, + '/automation/v1/scripts/?$filter=name%20eq%20' + name.split(' ').join('%20'), + null, + templateVariables + ); + } + + /** + * manages post retrieve steps + * @param {Object} metadata a single script + * @param {String} [_] unused + * @param {Boolean} [isTemplating] signals that we are retrieving templates + * @returns {Object[]} Array with one metadata object and one ssjs string + */ + static postRetrieveTasks(metadata, _, isTemplating) { + // if retrieving template, replace the name with customer key if that wasn't already the case + if (isTemplating) { + const warningMsg = + 'Ensure that Automations using this script are updated with the new script-key before deployment.'; + this.overrideKeyWithName(metadata, warningMsg); + } + return this.parseMetadata(metadata); + } + + /** + * Updates a single Script + * @param {Object} script a single Script + * @returns {Promise} Promise + */ + static update(script) { + return super.updateREST(script, '/automation/v1/scripts/' + script.ssjsActivityId); + } + + /** + * Creates a single Script + * @param {Object} script a single Script + * @returns {Promise} Promise + */ + static create(script) { + return super.createREST(script, '/automation/v1/scripts/'); + } + + /** + * helper for this.preDeployTasks() that loads extracted code content back into JSON + * @param {Object} metadata a single asset definition + * @param {String} deployDir directory of deploy files + * @param {String} [templateName] name of the template used to built defintion (prior applying templating) + * @returns {Promise} content for metadata.script + */ + static async _mergeCode(metadata, deployDir, templateName) { + templateName = templateName || metadata.key; + let code; + const codePath = File.normalizePath([ + deployDir, + this.definition.type, + templateName + '.' + this.definition.type + '-meta', + ]); + + if (File.existsSync(codePath + '.ssjs')) { + code = await File.readFile( + [deployDir, this.definition.type], + templateName + '.' + this.definition.type + '-meta', + 'ssjs' + ); + code = ``; + } else if (File.existsSync(codePath + '.html')) { + code = await File.readFile( + [deployDir, this.definition.type], + templateName + '.' + this.definition.type + '-meta', + 'html' + ); + } else { + throw new Error(`Could not find ${codePath}.ssjs (or html)`); + } + return code; + } + /** + * prepares a Script for deployment + * @param {Object} metadata a single script activity definition + * @param {String} dir directory of deploy files + * @returns {Promise} Promise + */ + static async preDeployTasks(metadata, dir) { + // folder + try { + metadata.categoryId = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + delete metadata.r__folder_Path; + } catch (ex) { + Util.logger.error(`Script '${metadata.key}': ${ex.message}`); + } + + // code + metadata.script = await this._mergeCode(metadata, dir); + + return metadata; + } + + /** + * helper for buildDefinition + * handles extracted code if any are found for complex types + * @param {String} templateDir Directory where metadata templates are stored + * @param {String|String[]} targetDir (List of) Directory where built definitions will be saved + * @param {Object} metadata main JSON file that was read from file system + * @param {Object} variables variables to be replaced in the metadata + * @param {String} templateName name of the template to be built + * @returns {Promise} Promise + */ + static async buildDefinitionForExtracts( + templateDir, + targetDir, + metadata, + variables, + templateName + ) { + // get SSJS from filesystem + let code = this._mergeCode(metadata, templateDir, templateName); + + // replace template variables with their values + try { + code = Mustache.render(code, variables); + } catch (ex) { + throw new Error( + `${this.definition.type}:: Error applying template variables on ${ + metadata[this.definition.keyField] + '.' + this.definition.type + }-meta.ssjs.` + ); + } + + // write to file + const targetDirArr = Array.isArray(targetDir) ? targetDir : [targetDir]; + + for (const targetDir of targetDirArr) { + File.writeToFile( + [targetDir, this.definition.type], + metadata[this.definition.keyField] + '.' + this.definition.type + '-meta', + 'ssjs', + code + ); + } + } + + /** + * Splits the script metadata into two parts and parses in a standard manner + * @param {Object} metadata a single script activity definition + * @returns {Object[]} Array with one metadata object and one ssjs string + */ + static parseMetadata(metadata) { + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.categoryId, + 'ID', + 'Path' + ); + delete metadata.categoryId; + } catch (ex) { + Util.logger.warn(`Script '${metadata.key}': ${ex.message}`); + } + + // extract SSJS + const codeArr = []; + // keep between tags + const regex = /<\s*script .*?>(.+?)<\s*\/script>/gms; + let ssjs; + let fileExt; + const regexMatches = regex.exec(metadata.script); + if (regexMatches && regexMatches.length > 1) { + ssjs = regexMatches[1]; + fileExt = 'ssjs'; + } else { + ssjs = metadata.script; + fileExt = 'html'; + Util.logger.warn( + 'Script.parseMetadata:: Could not find script tags, saving whole text in SSJS: ' + + metadata.name + ); + } + delete metadata.script; + codeArr.push({ + subFolder: null, + fileName: metadata.key, + fileExt: fileExt, + content: ssjs, + }); + + return { json: metadata, codeArr: codeArr, subFolder: null }; + } +} + +// Assign definition & cache to static attributes +Script.definition = require('../MetadataTypeDefinitions').script; +Script.cache = {}; +Script.client = undefined; + +module.exports = Script; diff --git a/lib/metadataTypes/SetDefinition.js b/lib/metadataTypes/SetDefinition.js new file mode 100644 index 000000000..9b96022f9 --- /dev/null +++ b/lib/metadataTypes/SetDefinition.js @@ -0,0 +1,32 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); + +/** + * SetDefinition MetadataType + * @augments MetadataType + */ +class SetDefinition extends MetadataType { + /** + * Retrieves Metadata of schema set Definitions. + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise + */ + static retrieve(retrieveDir) { + return super.retrieveREST(retrieveDir, '/hub/v1/contacts/schema/setDefinitions', null); + } + /** + * Retrieves Metadata of schema set definitions for caching. + * @returns {Promise} Promise + */ + static retrieveForCache() { + return super.retrieveREST(null, '/hub/v1/contacts/schema/setDefinitions', null); + } +} + +// Assign definition to static attributes +SetDefinition.definition = require('../MetadataTypeDefinitions').setDefinition; +SetDefinition.cache = {}; +SetDefinition.client = undefined; + +module.exports = SetDefinition; diff --git a/lib/metadataTypes/TriggeredSendDefinition.js b/lib/metadataTypes/TriggeredSendDefinition.js new file mode 100644 index 000000000..bbd3fb995 --- /dev/null +++ b/lib/metadataTypes/TriggeredSendDefinition.js @@ -0,0 +1,240 @@ +'use strict'; + +const MetadataType = require('./MetadataType'); +const Util = require('../util/util'); + +/** + * MessageSendActivity MetadataType + * @augments MetadataType + */ +class TriggeredSendDefinition extends MetadataType { + /** + * Retrieves SOAP based metadata of metadata type into local filesystem. executes callback with retrieved metadata + * @param {String} retrieveDir Directory where retrieved metadata directory will be saved + * @returns {Promise} Promise of metadata + */ + static retrieve(retrieveDir) { + const options = { + filter: { + leftOperand: { + // somehow that parameter controls visible (non deleted?) email send activities + leftOperand: 'IsPlatformObject', + operator: 'equals', + rightOperand: false, + }, + operator: 'AND', + rightOperand: { + leftOperand: 'TriggeredSendStatus', + operator: 'IN', + rightOperand: ['New', 'Active', 'Inactive'], // New, Active=Running, Inactive=Paused, (Deleted) + }, + }, + }; + return super.retrieveSOAPgeneric(retrieveDir, null, options); + } + + /** + * Create a single TSD. + * @param {Object} metadata single metadata entry + * @returns {Promise} Promise + */ + static create(metadata) { + return super.createSOAP(metadata); + } + + /** + * Updates a single TSD. + * @param {Object} metadata single metadata entry + * @returns {Promise} Promise + */ + static update(metadata) { + // * in case of update and active definition, we need to pause first. + // * this should be done manually to not accidentally purge production queues + return super.updateSOAP(metadata); + } + /** + * checks if the current metadata entry should be saved on retrieve or not + * @static + * @param {Object} metadataEntry metadata entry + * @returns {boolean} if false, do not save + * @memberof MetadataType + */ + static isFiltered(metadataEntry) { + try { + // get folder path to be able to filter journey-created TSDs + const folderPath = Util.getFromCache( + this.cache, + 'folder', + metadataEntry.CategoryID, + 'ID', + 'Path' + ); + + if (folderPath && folderPath.startsWith('Journey Builder Sends/')) { + // filter out any triggered sends that were auto-created by journeys + return true; + } + } catch (ex) { + // handle it in parseMetadata() + } + return false; + } + + /** + * manages post retrieve steps + * @param {Object} metadata a single query + * @returns {Object[]} Array with one metadata object and one query string + */ + static postRetrieveTasks(metadata) { + return this.parseMetadata(metadata); + } + /** + * parses retrieved Metadata before saving + * @param {Object} metadata a single query activity definition + * @returns {Array} Array with one metadata object and one sql string + */ + static parseMetadata(metadata) { + // remove IsPlatformObject, always has to be 'false' + delete metadata.IsPlatformObject; + // folder + try { + metadata.r__folder_Path = Util.getFromCache( + this.cache, + 'folder', + metadata.CategoryID, + 'ID', + 'Path' + ); + delete metadata.CategoryID; + } catch (ex) { + Util.logger.warn( + `Triggered Send '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } + // email + try { + // classic + const classicEmail = Util.getFromCache( + this.cache, + 'email', + metadata.Email.ID, + 'ID', + 'Name' + ); + metadata.r__email_Name = classicEmail; + delete metadata.Email; + } catch (ex) { + try { + // content builder + const contentBuilderEmailName = Util.getFromCache( + this.cache, + 'asset', + metadata.Email.ID, + 'legacyData.legacyId', + 'name' + ); + metadata.r__assetMessage_Name = contentBuilderEmailName; + const contentBuilderEmailKey = Util.getFromCache( + this.cache, + 'asset', + metadata.Email.ID, + 'legacyData.legacyId', + 'customerKey' + ); + metadata.r__assetMessage_Key = contentBuilderEmailKey; + delete metadata.Email; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': Could not find email with ID ${metadata.Email.ID} in Classic nor in Content Builder.` + ); + } + } + // List + try { + metadata.r__list_PathName = Util.getListPathNameFromCache( + this.cache, + metadata.List.ID, + 'ID' + ); + delete metadata.List; + } catch (ex) { + Util.logger.warn( + `${this.definition.typeName} '${metadata.Name}'/'${metadata.CustomerKey}': ${ex.message}` + ); + } + + return metadata; + } + /** + * prepares a TSD for deployment + * @param {Object} metadata of a single TSD + * @returns {Object} metadata object + */ + static async preDeployTasks(metadata) { + const cachedVersion = this.cache[this.definition.type][metadata.CustomerKey]; + if ( + cachedVersion && + cachedVersion.TriggeredSendStatus === 'Active' && + cachedVersion.TriggeredSendStatus === metadata.TriggeredSendStatus + ) { + throw new Error( + `Please pause the Triggered Send '${metadata.Name}' before updating it. You may do so via GUI; or via Accenture SFMC DevTools by setting TriggeredSendStatus to 'Inactive'.` + ); + } + // re-add IsPlatformObject, required for visibility + metadata.IsPlatformObject = false; + // folder + metadata.CategoryID = Util.getFromCache( + this.cache, + 'folder', + metadata.r__folder_Path, + 'Path', + 'ID' + ); + delete metadata.r__folder_Path; + // email + if (metadata.r__email_Name) { + // classic + metadata.Email = { + ID: Util.getFromCache(this.cache, 'email', metadata.r__email_Name, 'Name', 'ID'), + }; + delete metadata.r__email_Name; + } else if (metadata.r__assetMessage_Key) { + // content builder + // * this ignores r__assetMessage_Name on purpose as that is only unique per parent folder but useful during PR reviews + metadata.Email = { + ID: Util.getFromCache( + this.cache, + 'asset', + metadata.r__assetMessage_Key, + 'customerKey', + 'legacyData.legacyId' + ), + }; + delete metadata.r__assetMessage_Key; + delete metadata.r__assetMessage_Name; + } else if (metadata.Email && metadata.Email.ID) { + throw new Error( + `r__assetMessage_Key / r__email_Name not defined but instead found Email.ID. Please try re-retrieving this TSD from your BU.` + ); + } + // get List + if (metadata.r__list_PathName) { + metadata.List = { + ID: Util.getListObjectIdFromCache(this.cache, metadata.r__list_PathName, 'ID'), + }; + delete metadata.r__list_PathName; + } else if (metadata.List && metadata.List.ID) { + throw new Error( + `r__list_PathName not defined but instead found List.ID. Please try re-retrieving this TSD from your BU.` + ); + } + + return metadata; + } +} + +// Assign definition to static attributes +TriggeredSendDefinition.definition = require('../MetadataTypeDefinitions').triggeredSendDefinition; + +module.exports = TriggeredSendDefinition; diff --git a/lib/metadataTypes/definitions/Asset.definition.js b/lib/metadataTypes/definitions/Asset.definition.js new file mode 100644 index 000000000..50fac20cf --- /dev/null +++ b/lib/metadataTypes/definitions/Asset.definition.js @@ -0,0 +1,894 @@ +// asset types https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/base-asset-types.htm +module.exports = { + bodyIteratorField: 'items', + dependencies: ['folder'], + folderType: 'asset', + hasExtended: false, + idField: 'id', + keyField: 'customerKey', + nameField: 'name', + restPagination: true, + type: 'asset', + typeDescription: 'Assets from Content Builder grouped into subtypes.', + typeRetrieveByDefault: ['asset', 'code', 'textfile', 'block', 'message', 'template', 'other'], + typeName: 'Asset-[Subtype]', + fields: { + activeDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + allowedBlocks: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + assetType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'assetType.displayName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'assetType.id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'assetType.name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + availableViews: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modelVersion: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + blocks: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + businessUnitAvailability: { + skipValidation: true, + }, + category: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'category.id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: true, + }, + 'category.name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'category.parentId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + channels: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + content: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + contentType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'createdBy.email': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'createdBy.id': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'createdBy.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'createdBy.userId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + customerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + customFields: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'data.campaigns': { + skipValidation: true, + }, + 'data.approvals': { + skipValidation: true, + }, + 'data.email.options': { + skipValidation: true, + }, + 'data.email.legacy': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'data.portfolio': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + design: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + enterpriseId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + file: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'fileProperties.fileName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileProperties.extension': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileProperties.externalUrl': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileProperties.fileSize': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + + 'fileProperties.fileCreatedDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + + 'fileProperties.width': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + + 'fileProperties.height': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileProperties.publishedURL': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + id: { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: false, + }, + legacyData: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + locked: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxBlocks: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + memberId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + meta: { + skipValidation: true, + }, + minBlocks: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'modifiedBy.email': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'modifiedBy.id': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'modifiedBy.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'modifiedBy.userId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + objectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + owner: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'owner.email': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'owner.id': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'owner.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'owner.userId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + sharingProperties: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'sharingProperties.localAssets': { + skipValidation: true, + }, + 'sharingProperties.sharedWith': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'sharingProperties.sharingType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + slots: { + skipValidation: true, + }, + 'status.id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: true, + }, + 'status.name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + superContent: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + tags: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + template: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + thumbnail: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + version: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + views: { + skipValidation: true, + }, + r__folder_Path: { skipValidation: true }, + }, + subTypes: [ + 'archive', + 'asset', + 'audio', + 'block', + 'code', + 'document', + 'image', + 'message', + 'other', + 'rawimage', + 'template', + 'textfile', + 'video', + ], + extendedSubTypes: { + asset: ['webpage', 'webtemplate', 'jsonmessage', 'icemailformblock'], + image: [ + 'ai', + 'psd', + 'pdd', + 'eps', + 'gif', + 'jpe', + 'jpeg', + 'jpg', + 'jp2', + 'jpx', + 'pict', + 'pct', + 'png', + 'tif', + 'tiff', + 'tga', + 'bmp', + 'wmf', + 'vsd', + 'pnm', + 'pgm', + 'pbm', + 'ppm', + 'svg', + ], + rawimage: [ + '3fr', + 'ari', + 'arw', + 'bay', + 'cap', + 'crw', + 'cr2', + 'dcr', + 'dcs', + 'dng', + 'drf', + 'eip', + 'erf', + 'fff', + 'iiq', + 'k25', + 'kdc', + 'mef', + 'mos', + 'mrw', + 'nef', + 'nrw', + 'orf', + 'pef', + 'ptx', + 'pxn', + 'raf', + 'raw', + 'rw2', + 'rwl', + 'rwz', + 'srf', + 'sr2', + 'srw', + 'x3f', + ], + video: [ + '3gp', + '3gpp', + '3g2', + '3gp2', + 'asf', + 'avi', + 'm2ts', + 'mts', + 'dif', + 'dv', + 'mkv', + 'mpg', + 'f4v', + 'flv', + 'mjpg', + 'mjpeg', + 'mxf', + 'mpeg', + 'mp4', + 'm4v', + 'mp4v', + 'mov', + 'swf', + 'wmv', + 'rm', + 'ogv', + ], + document: [ + 'indd', + 'indt', + 'incx', + 'wwcx', + 'doc', + 'docx', + 'dot', + 'dotx', + 'mdb', + 'mpp', + 'ics', + 'xls', + 'xlsx', + 'xlk', + 'xlsm', + 'xlt', + 'xltm', + 'csv', + 'tsv', + 'tab', + 'pps', + 'ppsx', + 'ppt', + 'pptx', + 'pot', + 'thmx', + 'pdf', + 'ps', + 'qxd', + 'rtf', + 'sxc', + 'sxi', + 'sxw', + 'odt', + 'ods', + 'ots', + 'odp', + 'otp', + 'epub', + 'dvi', + 'key', + 'keynote', + 'pez', + ], + audio: [ + 'aac', + 'm4a', + 'au', + 'aif', + 'aiff', + 'aifc', + 'mp3', + 'wav', + 'wma', + 'midi', + 'oga', + 'ogg', + 'ra', + 'vox', + 'voc', + ], + archive: [ + '7z', + 'arj', + 'bz2', + 'cab', + 'gz', + 'gzip', + 'iso', + 'lha', + 'sit', + 'tgz', + 'jar', + 'rar', + 'tar', + 'zip', + 'gpg', + ], + code: [ + 'htm', + 'html', + 'xhtml', + 'xht', + 'css', + 'less', + 'sass', + 'js', + 'json', + 'atom', + 'rss', + 'xml', + 'xsl', + 'xslt', + 'md', + 'markdown', + 'as', + 'fla', + 'eml', + ], + textfile: ['text', 'txt'], + block: [ + 'freeformblock', + 'textblock', + 'htmlblock', + 'textplusimageblock', + 'imageblock', + 'abtestblock', + 'dynamicblock', + 'stylingblock', + 'einsteincontentblock', + 'socialshareblock', + 'socialfollowblock', + 'buttonblock', + 'layoutblock', + ], + message: ['templatebasedemail', 'htmlemail', 'textonlyemail'], + template: ['defaulttemplate'], + other: [ + 'smartcaptureblock', + 'smartcaptureformfieldblock', + 'smartcapturesubmitoptionsblock', + 'slotpropertiesblock', + 'externalcontentblock', + 'codesnippetblock', + 'rssfeedblock', + 'formstylingblock', + 'referenceblock', + 'imagecarouselblock', + 'customblock', + 'liveimageblock', + 'livesettingblock', + 'contentmap', + ], + }, + typeMapping: { + psd: 17, + pdd: 18, + eps: 19, + gif: 20, + jpe: 21, + jpeg: 22, + jpg: 23, + jp2: 24, + jpx: 25, + pict: 26, + pct: 27, + png: 28, + tif: 29, + tiff: 30, + tga: 31, + bmp: 32, + wmf: 33, + vsd: 34, + pnm: 35, + pgm: 36, + pbm: 37, + ppm: 38, + svg: 39, + '3fr': 40, + ari: 41, + arw: 42, + bay: 43, + cap: 44, + crw: 45, + cr2: 46, + dcr: 47, + dcs: 48, + dng: 49, + drf: 50, + eip: 51, + erf: 52, + fff: 53, + iiq: 54, + k25: 55, + kdc: 56, + mef: 57, + mos: 58, + mrw: 59, + nef: 60, + nrw: 61, + orf: 62, + pef: 63, + ptx: 64, + pxn: 65, + raf: 66, + raw: 67, + rw2: 68, + rwl: 69, + rwz: 70, + srf: 71, + sr2: 72, + srw: 73, + x3f: 74, + '3gp': 75, + '3gpp': 76, + '3g2': 77, + '3gp2': 78, + asf: 79, + avi: 80, + m2ts: 81, + mts: 82, + dif: 83, + dv: 84, + mkv: 85, + mpg: 86, + f4v: 87, + flv: 88, + mjpg: 89, + mjpeg: 90, + mxf: 91, + mpeg: 92, + mp4: 93, + m4v: 94, + mp4v: 95, + mov: 96, + swf: 97, + wmv: 98, + rm: 99, + ogv: 100, + indd: 101, + indt: 102, + incx: 103, + wwcx: 104, + doc: 105, + docx: 106, + dot: 107, + dotx: 108, + mdb: 109, + mpp: 110, + ics: 111, + xls: 112, + xlsx: 113, + xlk: 114, + xlsm: 115, + xlt: 116, + xltm: 117, + csv: 118, + tsv: 119, + tab: 120, + pps: 121, + ppsx: 122, + ppt: 123, + pptx: 124, + pot: 125, + thmx: 126, + pdf: 127, + ps: 128, + qxd: 129, + rtf: 130, + sxc: 131, + sxi: 132, + sxw: 133, + odt: 134, + ods: 135, + ots: 136, + odp: 137, + otp: 138, + epub: 139, + dvi: 140, + key: 141, + keynote: 142, + pez: 143, + aac: 144, + m4a: 145, + au: 146, + aif: 147, + aiff: 148, + aifc: 149, + mp3: 150, + wav: 151, + wma: 152, + midi: 153, + oga: 154, + ogg: 155, + ra: 156, + vox: 157, + voc: 158, + '7z': 159, + arj: 160, + bz2: 161, + cab: 162, + gz: 163, + gzip: 164, + iso: 165, + lha: 166, + sit: 167, + tgz: 168, + jar: 169, + rar: 170, + tar: 171, + zip: 172, + gpg: 173, + htm: 174, + html: 175, + xhtml: 176, + xht: 177, + css: 178, + less: 179, + sass: 180, + js: 181, + json: 182, + atom: 183, + rss: 184, + xml: 185, + xsl: 186, + xslt: 187, + md: 188, + markdown: 189, + as: 190, + fla: 191, + eml: 192, + text: 193, + txt: 194, + freeformblock: 195, + textblock: 196, + htmlblock: 197, + textplusimageblock: 198, + imageblock: 199, + abtestblock: 200, + dynamicblock: 201, + stylingblock: 202, + einsteincontentblock: 203, + webpage: 205, + webtemplate: 206, + templatebasedemail: 207, + htmlemail: 208, + textonlyemail: 209, + socialshareblock: 210, + socialfollowblock: 211, + buttonblock: 212, + layoutblock: 213, + defaulttemplate: 214, + smartcaptureblock: 215, + smartcaptureformfieldblock: 216, + smartcapturesubmitoptionsblock: 217, + slotpropertiesblock: 218, + externalcontentblock: 219, + codesnippetblock: 220, + rssfeedblock: 221, + formstylingblock: 222, + referenceblock: 223, + imagecarouselblock: 224, + customblock: 225, + liveimageblock: 226, + livesettingblock: 227, + contentmap: 228, + jsonmessage: 230, + icemailformblock: 232, + }, +}; diff --git a/lib/metadataTypes/definitions/AttributeGroup.definition.js b/lib/metadataTypes/definitions/AttributeGroup.definition.js new file mode 100644 index 000000000..5cfb2df6a --- /dev/null +++ b/lib/metadataTypes/definitions/AttributeGroup.definition.js @@ -0,0 +1,225 @@ +module.exports = { + bodyIteratorField: 'attributeGroupDefinitions', + dependencies: [], // future may have dependency on Data Extensions + hasExtended: false, + idField: 'definitionID', + keyField: 'definitionKey', + nameField: 'definitionName.value', + restPagination: false, + type: 'attributeGroup', + typeDescription: 'BETA: Groupings of Set Definitions (Data Extensions) in Data Designer.', + typeRetrieveByDefault: false, + typeName: 'Data Designer Attribute Groups', + fields: { + applicationID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + applicationKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + attributeCount: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + attributeGroupIconKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + attributeGroupType: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + attributeSetIdentifiers: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'attributeSetIdentifiers[].connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'attributeSetIdentifiers[].definitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'attributeSetIdentifiers[].definitionKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'attributeSetIdentifiers[].definitionName.Contact': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'attributeSetIdentifiers[].namespace': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + canAddProperties: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canAddRelationships: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canChangeProperties: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canModify: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canRemove: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + connectingID: { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + containsSchemaAttributes: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + definitionID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + definitionKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'definitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + description: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + displayOrder: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + fullyQualifiedName: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isHidden: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isOwner: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isPrimary: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isSystemDefined: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + localizedDescription: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'localizedDescription.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + mID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + namespace: { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + objectState: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + requiredRelationships: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/Automation.definition.js b/lib/metadataTypes/definitions/Automation.definition.js new file mode 100644 index 000000000..bf51600b5 --- /dev/null +++ b/lib/metadataTypes/definitions/Automation.definition.js @@ -0,0 +1,564 @@ +module.exports = { + activityTypeMapping: { + dataExtract: 73, + dataFactoryUtility: 425, + emailSendDefinition: 42, + fileTransfer: 53, + filter: 303, + fireEvent: 749, + importFile: 43, + journeyEntry: 952, + journeyEntryOld: 733, + query: 300, + script: 423, + verification: 1000, + wait: 467, + push: 736, + sms: 725, + reportDefinition: 84, + refreshMobileFilteredList: 724, + refreshGroup: 45, + interactions: 1101, + interactionStudioData: 1010, + importMobileContact: 726, + }, + bodyIteratorField: 'items', + dependencies: [ + 'dataExtract', + 'emailSendDefinition', + 'fileTransfer', + 'folder', + 'importFile', + 'query', + 'script', + ], + hasExtended: false, + filter: { + description: ['Event Definition Automation'], + }, + idField: 'id', + keyField: 'key', + nameField: 'name', + restPagination: false, + statusMapping: { + AwaitingTrigger: 7, + Building: 1, + BuildingError: 0, + Error: -1, + InactiveTrigger: 8, + PausedSchedule: 4, + Ready: 2, + Running: 3, + Scheduled: 6, + Stopped: 5, + }, + timeZoneMapping: { + // bugs in SFMC timezones: + // * Yerevan GMT+4 always is changing to 'Caucasus Standard Time' GMT+4, so no id 29 + // * La Paz GMT+4 is changing always to 'SA Western Standard Time' GMT-3, so no id 71 + // * no ids 81 and 85, because two Mexican timezones in UI are showing with the suffix 'Old' + 'Afghanistan Standard Time': 30, + 'Alaskan Standard Time': 89, + 'Arab Standard Time': 20, + 'Arabian Standard Time': 25, + 'Arabic Standard Time': 19, + 'Argentina Standard Time': 65, + 'Atlantic Standard Time': 70, + 'AUS Central Standard Time': 51, + 'AUS Eastern Standard Time': 53, + 'Azerbaijan Standard Time': 26, + 'Azores Standard Time': 61, + 'Canada Central Standard Time': 82, + 'Cape Verde Standard Time': 62, + 'Caucasus Standard Time': 27, + 'Cen. Australia Standard Time': 50, + 'Central America Standard Time': 78, + 'Central Asia Standard Time': 38, + 'Central Brazilian Standard Time': 72, + 'Central Europe Standard Time': 6, + 'Central European Standard Time': 8, + 'Central Pacific Standard Time': 57, + 'Central Standard Time': 79, + 'Central Standard Time (Mexico)': 80, + 'Central Standard Time (no DST)': 1, + 'China Standard Time': 42, + 'Dateline Standard Time': 92, + 'E. Africa Standard Time': 22, + 'E. Australia Standard Time': 52, + 'E. Europe Standard Time': 17, + 'E. South America Standard Time': 64, + 'Eastern Standard Time': 76, + 'Egypt Standard Time': 13, + 'Ekaterinburg Standard Time': 31, + 'Fiji Standard Time': 59, + 'FLE Standard Time': 15, + 'Georgian Standard Time': 23, + 'GMT Standard Time': 3, + 'Greenland Standard Time': 67, + 'Greenwich Standard Time': 4, + 'GTB Standard Time': 11, + 'Hawaiian Standard Time': 90, + 'India Standard Time': 34, + 'Iran Standard Time': 24, + 'Israel Standard Time': 16, + 'Jordan Standard Time': 10, + 'Korea Standard Time': 48, + 'Mauritius Standard Time': 28, + 'Mid-Atlantic Standard Time': 63, + 'Middle East Standard Time': 12, + 'Montevideo Standard Time': 68, + 'Morocco Standard Time': 2, + 'Mountain Standard Time': 86, + 'Mountain Standard Time (Mexico)': 84, + 'Myanmar Standard Time': 39, + 'N. Central Asia Standard Time': 37, + 'Namibia Standard Time': 18, + 'Nepal Standard Time': 36, + 'New Zealand Standard Time': 58, + 'Newfoundland Standard Time': 69, + 'North Asia East Standard Time': 43, + 'North Asia Standard Time': 41, + 'Pacific SA Standard Time': 73, + 'Pacific Standard Time': 87, + 'Pacific Standard Time (Mexico)': 88, + 'Pakistan Standard Time': 32, + 'Romance Standard Time': 7, + 'Russian Standard Time': 21, + 'SA Pacific Standard Time': 75, + 'SA Western Standard Time': 66, + 'Samoa Standard Time': 91, + 'SE Asia Standard Time': 40, + 'Singapore Standard Time': 44, + 'South Africa Standard Time': 14, + 'Sri Lanka Standard Time': 35, + 'Taipei Standard Time': 46, + 'Tasmania Standard Time': 55, + 'Tokyo Standard Time': 47, + 'Tonga Standard Time': 60, + 'US Eastern Standard Time': 77, + 'US Mountain Standard Time': 83, + 'Venezuela Standard Time': 74, + 'Vladivostok Standard Time': 56, + 'W. Australia Standard Time': 45, + 'W. Central Africa Standard Time': 9, + 'W. Europe Standard Time': 5, + 'West Asia Standard Time': 33, + 'West Pacific Standard Time': 54, + 'Yakutsk Standard Time': 49, + }, + timeZoneDifference: { + 1: '-06:00', + 2: '+00:00', + 3: '+00:00', + 4: '+00:00', + 5: '+01:00', + 6: '+01:00', + 7: '+01:00', + 8: '+01:00', + 9: '+01:00', + 10: '+02:00', + 11: '+02:00', + 12: '+02:00', + 13: '+02:00', + 14: '+02:00', + 15: '+02:00', + 16: '+02:00', + 17: '+02:00', + 18: '+02:00', + 19: '+03:00', + 20: '+03:00', + 21: '+03:00', + 22: '+03:00', + 23: '+03:00', + 24: '+03:30', + 25: '+04:00', + 26: '+04:00', + 27: '+04:00', + 28: '+04:00', + 29: '+04:00', + 30: '+04:30', + 31: '+05:00', + 32: '+05:00', + 33: '+05:00', + 34: '+05:30', + 35: '+05:30', + 36: '+05:45', + 37: '+06:00', + 38: '+06:00', + 39: '+06:30', + 40: '+07:00', + 41: '+07:00', + 42: '+08:00', + 43: '+08:00', + 44: '+08:00', + 45: '+08:00', + 46: '+08:00', + 47: '+09:00', + 48: '+09:00', + 49: '+09:00', + 50: '+09:30', + 51: '+09:30', + 52: '+10:00', + 53: '+10:00', + 54: '+10:00', + 55: '+10:00', + 56: '+10:00', + 57: '+11:00', + 58: '+12:00', + 59: '+12:00', + 60: '+13:00', + 61: '-01:00', + 62: '-01:00', + 63: '-02:00', + 64: '-03:00', + 65: '-03:00', + 66: '-03:00', + 67: '-03:00', + 68: '-03:00', + 69: '-03:30', + 70: '-04:00', + 71: '-04:00', + 72: '-04:00', + 73: '-04:00', + 74: '-04:30', + 75: '-05:00', + 76: '-05:00', + 77: '-05:00', + 78: '-06:00', + 79: '-06:00', + 80: '-06:00', + 81: '-06:00', + 82: '-06:00', + 83: '-07:00', + 84: '-07:00', + 85: '-07:00', + 86: '-07:00', + 87: '-08:00', + 88: '-08:00', + 89: '-09:00', + 90: '-10:00', + 91: '-11:00', + 92: '-12:00', + }, + type: 'automation', + typeDescription: + 'Used via Automation Studio directly - or indirectly via Journey Builder & MC Connect.', + typeRetrieveByDefault: true, + typeName: 'Automation', + manualDeployTypes: ['wait'], + fields: { + categoryId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.fileNamePatternTypeId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.fileNamingPattern': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.folderLocationText': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.isPublished': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.queueFiles': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'fileTrigger.triggerActive': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + id: { + isCreateable: false, + isUpdateable: null, + retrieving: true, + template: false, + }, + key: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + lastRunInstanceId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + lastRunTime: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + legacyId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + lastSavedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + lastSavedByName: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdByName: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + updateInProgress: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + startSource: { + skipValidation: true, + }, + 'schedule.endDate': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.icalRecur': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'schedule.occurrences': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'schedule.pattern': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'schedule.rangeTypeId': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'schedule.scheduledTime': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: true, + }, + 'schedule.scheduleStatus': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'schedule.startDate': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.timezoneId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.timezoneName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.typeId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + status: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + statusId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + steps: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].activities': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].activities[].activityObjectId': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'steps[].activities[].displayOrder': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].activities[].id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'steps[].activities[].name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].activities[].objectTypeId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].activities[].targetDataExtensions': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'steps[].activities[].description': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'steps[].activities[].serializedObject': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'steps[].activities[].r__type': { + skipValidation: true, + }, + 'steps[].description': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].annotation': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'steps[].name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].step': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'steps[].stepNumber': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + type: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + typeId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: true, + }, + r__folder_Path: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/Campaign.definition.js b/lib/metadataTypes/definitions/Campaign.definition.js new file mode 100644 index 000000000..e86db884e --- /dev/null +++ b/lib/metadataTypes/definitions/Campaign.definition.js @@ -0,0 +1,14 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: [], + hasExtended: false, + idField: 'id', + keyField: 'name', + nameField: 'name', + restPagination: true, + type: 'campaign', + typeDescription: 'Way of tagging/categorizing emails, journeys and alike.', + typeRetrieveByDefault: true, + typeName: 'Campaign Tag', + fields: {}, +}; diff --git a/lib/metadataTypes/definitions/ContentArea.definition.js b/lib/metadataTypes/definitions/ContentArea.definition.js new file mode 100644 index 000000000..2c5fdbff5 --- /dev/null +++ b/lib/metadataTypes/definitions/ContentArea.definition.js @@ -0,0 +1,180 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder'], + hasExtended: false, + idField: 'ID', + keepId: true, + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: null, + type: 'contentArea', + typeDescription: + 'DEPRECATED: Old way of saving Content Blocks; please migrate these to new Content Blocks (`Asset: ...`).', + typeRetrieveByDefault: false, + typeName: 'Content Area (Classic)', + fields: { + // https://developer.salesforce.com/docs/atlas.en-us.noversion.mc-apis.meta/mc-apis/contentarea.htm + BackgroundColor: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + BorderColor: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + BorderWidth: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CategoryID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Cellpadding: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Cellspacing: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Content: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CorrelationID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + FontFamily: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + HasFontSize: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + IsBlank: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + IsDynamicContent: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + IsLocked: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsSurvey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Key: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Layout: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Owner: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerProperties: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Width: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/DataExtension.definition.js b/lib/metadataTypes/definitions/DataExtension.definition.js new file mode 100644 index 000000000..e7aa390a7 --- /dev/null +++ b/lib/metadataTypes/definitions/DataExtension.definition.js @@ -0,0 +1,271 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder', 'dataExtensionTemplate'], + folderType: 'dataextension', + filter: { + CustomerKey: [ + 'Einstein_MC_Email_Frequency_Oversaturation', + 'Einstein_MC_Email_Frequency_Undersaturation', + 'Einstein_MC_Predictive_Scores', + 'IGO_PRODUCTATTRIBS', + 'IGO_PRODUCTS', + 'IGO_PROFILES', + 'IGO_PURCHASES', + 'IGO_VIEWS', + 'PI_ABANDONED_CART_EVENT', + 'PI_ABANDONED_CART_ITEMS', + 'PI_CONTENT', + 'PI_CONTENTATTRIBS', + 'PI_CONTENTVIEWS', + 'PI_SESSION_ENDS', + 'PI_SESSIONS', + 'PI_TRIGGEREVENT', + 'PI_TRIGGEREVENTDETAIL', + 'PREDICTIVE_SCORES', + ], + Name: [ + '_ChatMessagingSubscription', + '_EnterpriseAttribute', + '_MobileAddress', + '_MobileLineAddress', + '_MobileLineAddressContact', + '_MobileLineProfile', + '_MobileLineProfileAttribute', + '_MobileLineSubscription', + '_MobileSubscription', + '_PushAddress', + '_PushTag', + 'CloudPages_DataExtension', + 'ExpressionBuilderAttributes', + 'MobileLineOrphanContact', + 'SocialPages_DataExtension', + ], + }, + templateFields: { + AudienceBuilderResult: [ + 'SubscriberKey', + 'CustomerKey', + 'AudienceId', + 'TrackingCode', + 'AudienceCode', + 'SegmentCode', + 'SegmentName', + 'Priority', + 'SegmentID', + 'SplitID', + 'SplitName', + 'SplitCode', + 'SendGroupID', + ], + CONTEXTUAL_SUPPRESSION_LISTS: ['Email Address', 'Date Added'], + DomainExclusion: ['Domain'], + 'Event DE Template': ['EventInstanceID', 'ParentEventInstanceID', 'ContactKey'], + PushSendLog: [ + 'PushJobID', + 'PushTriggeredSendRequestID', + 'PushBatchID', + 'SubID', + 'DeviceId', + 'AppId', + 'LogDate', + ], + SendLog: ['JobID', 'ListID', 'BatchID', 'SubID', 'TriggeredSendID', 'ErrorCode'], + 'SmartCapture - Contacts Template Extension': ['Source', 'EmailAddress', 'CreateDate'], + SmsSendLog: ['SmsJobID', 'SmsTriggeredSendID', 'SmsBatchID', 'SubID'], + SMSMessageTracking: null, // CustomerKey cannot be retrieved + SMSSubscriptionLog: null, // CustomerKey cannot be retrieved + TriggeredSendDataExtension: ['SubscriberKey', 'EmailAddress'], + }, + hasExtended: false, + idField: 'ObjectID', + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: false, + type: 'dataExtension', + typeDescription: 'Database table schemas.', + typeRetrieveByDefault: true, + typeName: 'Data Extension', + fields: { + CategoryID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + DataRetentionPeriod: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + DataRetentionPeriodLength: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + DataRetentionPeriodUnitOfMeasure: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + DeleteAtEndOfRetentionPeriod: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + Description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + Fields: { + skipValidation: true, + }, + folderContentType: { + skipValidation: true, + }, + IsPlatformObject: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + IsSendable: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + IsTestable: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + ResetRetentionPeriodOnImport: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + RetainUntil: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + RowBasedRetention: { + isCreateable: true, + isUpdateable: false, + retrieving: true, + template: true, + }, + 'SendableDataExtensionField.Name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'SendableDataExtensionField.ObjectID': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + 'SendableDataExtensionField.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + SendableSubscriberField: { + skipValidation: true, + }, + 'SendableSubscriberField.Name': { + // '_SubscriberKey' needs to be replaced with 'Subscriber Key' + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + Status: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'Template.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'Template.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'Template.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + r__folder_ContentType: { + skipValidation: true, + }, + r__folder_Path: { + skipValidation: true, + }, + r__dataExtensionTemplate_Name: { + skipValidation: true, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/DataExtensionField.definition.js b/lib/metadataTypes/definitions/DataExtensionField.definition.js new file mode 100644 index 000000000..954e44888 --- /dev/null +++ b/lib/metadataTypes/definitions/DataExtensionField.definition.js @@ -0,0 +1,118 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: [], + filter: {}, + hasExtended: false, + idField: 'ObjectID', + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: false, + type: 'dataExtensionField', + typeDescription: 'Internal Type: Fields for type dataExtension.', + typeRetrieveByDefault: false, + typeName: 'Data Extension Field', + fields: { + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'DataExtension.CustomerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + 'DataExtension.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'DataExtension.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + DefaultValue: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + FieldType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + IsPrimaryKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + IsRequired: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + MaxLength: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + ObjectID: { + isCreateable: false, + isUpdateable: null, + retrieving: false, + template: false, + }, + Ordinal: { + isCreateable: true, + isUpdateable: true, // Ignored on update for existing fields but respected for the order of amended new fields. to re-order existing fields, delete DE on target & recreate + retrieving: true, + template: true, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Scale: { + isCreateable: true, + isUpdateable: true, // Doesnt work for decimal field updates + retrieving: true, + template: true, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js b/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js new file mode 100644 index 000000000..711f8454f --- /dev/null +++ b/lib/metadataTypes/definitions/DataExtensionTemplate.definition.js @@ -0,0 +1,114 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: [], + filter: {}, + hasExtended: false, + idField: 'ObjectID', + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: false, + type: 'dataExtensionTemplate', + typeDescription: 'Templates used for special DE use cases like Triggered Send.', + typeRetrieveByDefault: false, + typeName: 'Data Extension Template', + fields: { + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + CustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + Name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + Description: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + IsSendable: { isCreateable: false, isUpdateable: false, retrieving: true, template: false }, + IsTestable: { isCreateable: false, isUpdateable: false, retrieving: true, template: false }, + SendableCustomObjectField: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + SendableSubscriberField: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + DataRetentionPeriodLength: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + DataRetentionPeriodUnitOfMeasure: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + RowBasedRetention: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + ResetRetentionPeriodOnImport: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + DeleteAtEndOfRetentionPeriod: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + RetainUntil: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/DataExtract.definition.js b/lib/metadataTypes/definitions/DataExtract.definition.js new file mode 100644 index 000000000..b0493de98 --- /dev/null +++ b/lib/metadataTypes/definitions/DataExtract.definition.js @@ -0,0 +1,97 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['dataExtension', 'dataExtractType'], + hasExtended: true, + idField: 'dataExtractDefinitionId', + keyField: 'key', + nameField: 'name', + restPagination: true, + type: 'dataExtract', + typeDescription: 'Creates zipped files in your FTP directory or convert XML into CSV.', + typeRetrieveByDefault: true, + typeName: 'Automation: Data Extract Activity', + fields: { + createdBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + dataExtractDefinitionId: { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: false, + }, + dataExtractTypeId: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + dataFields: { + skipValidation: true, + }, + description: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + endDate: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + fileSpec: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + intervalType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + key: { + isCreateable: null, + isUpdateable: false, + retrieving: true, + template: true, + }, + modifiedBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: null, + isUpdateable: false, + retrieving: true, + template: true, + }, + startDate: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + r__dataExtractType_name: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/DataExtractType.definition.js b/lib/metadataTypes/definitions/DataExtractType.definition.js new file mode 100644 index 000000000..86fae7628 --- /dev/null +++ b/lib/metadataTypes/definitions/DataExtractType.definition.js @@ -0,0 +1,28 @@ +module.exports = { + bodyIteratorField: [], + dependencies: [], + hasExtended: false, + idField: 'extractId', + keyField: 'name', + nameField: 'name', + restPagination: false, + type: 'dataExtractType', + typeDescription: + 'Types of Data Extracts enabled for a specific business unit. This normally should not be stored.', + typeRetrieveByDefault: false, + typeName: 'Data Extract Type', + fields: { + name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + extractId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/Discovery.definition.js b/lib/metadataTypes/definitions/Discovery.definition.js new file mode 100644 index 000000000..1e25682f4 --- /dev/null +++ b/lib/metadataTypes/definitions/Discovery.definition.js @@ -0,0 +1,132 @@ +module.exports = { + bodyIteratorField: null, + dependencies: [], + endPointMapping: { + Address: '/address/v1/rest', + Asset: '/asset/v1/rest', + Automation: '/automation/v1/rest', + Contacts: '/contacts/v1/rest', + Data: '/data/v1/rest', + Device: '/device/v1/rest', + Email: '/email/v1/rest', + Guide: '/guide/v1/rest', + Hub: '/hub/v1/rest', + Interaction: '/interaction/v1/rest', + 'Interaction-Experimental': '/interaction-experimental/v1/rest', + Legacy: '/legacy/v1/rest', + Messaging: '/messaging/v1/rest', + 'Messaging-Experimental': '/messaging-experimental/v1/rest', + OTT: '/ott/v1/rest', + 'OTT-Experimental': '/ott/v1/rest/experimental', + Platform: '/platform/v1/rest', + 'Platform-Experimental': '/platform/v1/rest/experimental', + Provisioning: '/provisioning/v1/rest', + Push: '/push/v1/rest', + SMS: '/sms/v1/rest', + }, + hasExtended: false, + idField: '', + keyField: 'key', + nameField: 'name', + restPagination: false, + type: 'discovery', + typeDescription: + 'Description of all API endpoints accessible via REST API; only relevant for developers of Accenture SFMC DevTools.', + typeRetrieveByDefault: false, + typeName: 'API Discovery', + fields: { + basePath: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + baseUrl: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + description: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + discoveryVersion: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + documentationLink: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + id: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + key: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + kind: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + methods: { + skipValidation: true, + }, + name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + protocol: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + resources: { + skipValidation: true, + }, + rootUrl: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + schemas: { + skipValidation: true, + }, + servicePath: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + title: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + version: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/Email.definition.js b/lib/metadataTypes/definitions/Email.definition.js new file mode 100644 index 000000000..eda5da370 --- /dev/null +++ b/lib/metadataTypes/definitions/Email.definition.js @@ -0,0 +1,311 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder'], + hasExtended: false, + idField: 'ID', + keepId: true, + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: null, + type: 'email', + typeDescription: + 'DEPRECATED: Old way of saving E-Mails; please migrate these to new E-Mail (`Asset: message`).', + typeRetrieveByDefault: false, + typeName: 'E-Mail (Classic)', + fields: { + CategoryID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CharacterSet: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'Client.PartnerClientKey': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ClonedFromID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ContentAreas: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + 'ContentAreas.CategoryID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.Content': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.CustomerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.IsBlank': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.IsDynamicContent': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.IsLocked': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.IsSurvey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.Key': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.Name': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].CategoryID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].Content': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].CustomerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].IsBlank': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].IsDynamicContent': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].IsLocked': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].IsSurvey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].Key': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].Name': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'ContentAreas[].PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ContentCheckStatus: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + EmailType: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Folder: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + HasDynamicSubjectLine: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + HTMLBody: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + IsActive: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + IsHTMLPaste: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + PreHeader: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Status: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Subject: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + SyncTextWithHTML: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + TextBody: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/EmailSendDefinition.definition.js b/lib/metadataTypes/definitions/EmailSendDefinition.definition.js new file mode 100644 index 000000000..ea6bb2b9b --- /dev/null +++ b/lib/metadataTypes/definitions/EmailSendDefinition.definition.js @@ -0,0 +1,531 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder', 'email', 'asset-message', 'dataExtension', 'list'], // filter, SendClassification, SenderProfile, DeliveryProfile + folderType: 'userinitiatedsends', + hasExtended: false, + idField: 'ObjectID', + keepId: true, + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: null, + type: 'emailSendDefinition', + typeDescription: 'Mainly used in Automations as "Send Email Activity".', + typeRetrieveByDefault: true, + typeName: 'E-Mail Send Definition', + fields: { + Additional: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + AutoBccEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + BccEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + CategoryID: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + CCEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CorrelationID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CreatedDate: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + DeduplicateByEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'DeliveryProfile.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'DeliveryProfile.DomainType': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.FooterContentArea.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.FooterSalutationSource': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.HeaderContentArea.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.HeaderSalutationSource': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.PrivateDomain': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'DeliveryProfile.PrivateIP': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'DeliveryProfile.SourceAddressType': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + DeliveryScheduledTime: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + DomainType: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + DynamicEmailSubject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'Email.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'Email.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'Email.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + EmailSubject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + ExclusionFilter: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + FooterContentArea: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + FooterSalutationSource: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + FromAddress: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + FromName: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + HeaderContentArea: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + HeaderSalutationSource: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IntegratedTracking: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + InteractionObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsMultipart: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + IsPlatformObject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: true, + }, + IsSeedListSend: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsSendLogging: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + IsWrapped: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + Keyword: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + MessageDeliveryType: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ModifiedDate: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + Name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ObjectState: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Owner: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerProperties: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PreHeader: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PrivateDomain: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PrivateIP: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ReplyToAddress: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ReplyToDisplayName: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SeedListOccurance: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendClassification.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'SendClassification.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendClassification.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendDefinitionList.CustomObjectID': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + templating: false, + }, + SendDefinitionList: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'SendDefinitionList[].ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendDefinitionList[].PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendDefinitionList[].SendDefinitionListType': { + skipValidation: true, // cannot be asked for but will be retrieved + }, + 'SendDefinitionList[].DataSourceTypeID': { + skipValidation: true, // cannot be asked for but will be retrieved + }, + 'SendDefinitionList[].IsTestObject': { + skipValidation: true, // cannot be asked for but will be retrieved + }, + 'SendDefinitionList[].SalesForceObjectID': { + skipValidation: true, // cannot be asked for but will be retrieved + }, + 'SendDefinitionList[].Name': { + skipValidation: true, // cannot be asked for but will be retrieved + }, + 'SendDefinitionList[].r__dataExtension_Key': { + skipValidation: true, + }, + 'SendDefinitionList[].List.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendDefinitionList[].List.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'SenderProfile.FromAddress': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.FromName': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendLimit: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SendWindowClose: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SendWindowDelete: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendWindowOpen: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SourceAddressType: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SuppressTracking: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TestEmailAddr: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TimeZone: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + TrackingUsers: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + r__assetMessage_Name: { skipValidation: true }, + r__assetMessage_Key: { skipValidation: true }, + r__email_Name: { skipValidation: true }, + r__folder_Path: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/EventDefinition.definition.js b/lib/metadataTypes/definitions/EventDefinition.definition.js new file mode 100644 index 000000000..1068d5c81 --- /dev/null +++ b/lib/metadataTypes/definitions/EventDefinition.definition.js @@ -0,0 +1,898 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['dataExtension'], + hasExtended: false, + idField: 'id', + keyField: 'eventDefinitionKey', + nameField: 'name', + restPagination: true, + type: 'eventDefinition', + typeDescription: 'Used in Journeys (Interactions) to define Entry Events.', + typeRetrieveByDefault: false, + typeName: 'Journey: Entry Event Definition', + fields: { + 'arguments.automationId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.contactAttributeGroup': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.contactAttributeId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.contactAttributeName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.criteria': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.dataExtensionId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.dataTargetName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.formName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + + 'arguments.dateOffset': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.dateOffsetUnit': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.dateType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.eid': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.eventDefinitionId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'arguments.eventDefinitionKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.mid': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.resetHighWatermark': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.serializedObjectType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.transactionKeyDataExtension': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.transactionKeyEvent': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'arguments.useHighWatermark': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + automationId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + category: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + configurationArguments: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.applicationExtensionKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.contactKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.contactPersonType': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.dataExtensionId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.evaluationCriteriaSummary': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.eventDataConfig': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.eventDataSummary': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.objectAPIName': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.passThroughArgument': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.primaryObjectFilterCriteria': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.primaryObjectFilterSummary': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.relatedObjectFilterCriteria': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.relatedObjectFilterSummary': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.salesforceTriggerCriteria': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.unconfigured': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.version': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'configurationArguments.whoToInject': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + createdBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + dataExtensionId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + dataExtensionName: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + eventDefinitionKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + filterDefinitionId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + filterDefinitionTemplate: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + iconUrl: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + id: { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: false, + }, + interactionCount: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + isPlatformObject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isVisibleInPicker: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.attributeName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.automationData.automationType': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.categoryId': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.createdBy.email': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.createdBy.id': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.createdBy.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.createdDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.description': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.folderPath': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.guidId': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.id': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: true, + }, + 'metaData.automationData.isPlatformObject': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.key': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.automationData.lastRunInstance': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.lastRunTime': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.lastSaveDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.lastSavedBy.email': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.lastSavedBy.id': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.lastSavedBy.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.memberId': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.modifiedDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.notifications': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.processes': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'metaData.automationData.schedule': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.createdBy': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.createdDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.description': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.iCalRecur': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.id': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.key': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.lastUpdated': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.lastUpdatedBy': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.name': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.scheduleState': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.scheduleStatus': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.startDate': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.timeZone': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduleObject.timeZoneId': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.scheduledTime': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.selectedCategoryId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'metaData.automationData.selectedCategoryId[]': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'metaData.automationData.status': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'metaData.automationData.updateInProgress': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'metaData.criteriaDescription': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.customAttributeName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.formattedDate': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.formattedTime': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.icon': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.original_icon': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.isConfigured': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.runOnceScheduleMode': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.scheduleFlowMode': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.scheduleState': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.timeZone': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.transactionKeys': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.transactionKeys.0': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.transactionKeys.0.from': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'metaData.transactionKeys.0.to': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + mode: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + modifiedBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + publishedInteractionCount: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.endDateTime': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.endType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.frequency': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.interval': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.occurrences': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.recurrencePattern': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.scheduledDay': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.startDateTime': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.timeZone': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schedule.monday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.tuesday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.wednesday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.thursday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.friday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.saturday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schedule.sunday': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'schema.fields': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].dataType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].defaultValue': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].isDevicePreference': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].isNullable': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].isPrimaryKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].maxLength': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.fields[].name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.id': { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: false, + }, + 'schema.name': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.sendableCustomObjectField': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'schema.sendableSubscriberField': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + sourceApplicationExtensionId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + type: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/FileTransfer.definition.js b/lib/metadataTypes/definitions/FileTransfer.definition.js new file mode 100644 index 000000000..39f2d13d9 --- /dev/null +++ b/lib/metadataTypes/definitions/FileTransfer.definition.js @@ -0,0 +1,119 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['ftpLocation'], + hasExtended: true, + idField: 'id', + keyField: 'customerKey', + nameField: 'name', + restPagination: true, + type: 'fileTransfer', + typeDescription: + 'Unzip, decrypt a file or move a file from secure location into FTP directory.', + typeRetrieveByDefault: true, + typeName: 'Automation: File Transfer Activity', + fields: { + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + customerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fileSpec: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fileTransferLocationId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + id: { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: false, + }, + isCompressed: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isEncrypted: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isFileSpecLocalized: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isPgp: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isUpload: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxFileAge: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxFileAgeScheduleOffset: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxImportFrequency: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + publicKeyManagementId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + r__ftpLocation_name: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/Filter.definition.js b/lib/metadataTypes/definitions/Filter.definition.js new file mode 100644 index 000000000..b9503571f --- /dev/null +++ b/lib/metadataTypes/definitions/Filter.definition.js @@ -0,0 +1,93 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: [], + hasExtended: false, + idField: 'id', + keyField: 'customerKey', + nameField: 'name', + restPagination: true, + type: 'filter', + typeDescription: + 'BETA: Part of how filtered Data Extensions are created. Depends on type "FilterDefinitions".', + typeRetrieveByDefault: false, + typeName: 'Automation: Filter Activity', + fields: { + categoryId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + customerKey: { + isCreateable: null, + isUpdateable: false, + retrieving: true, + template: false, + }, + description: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + destinationObjectId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + destinationTypeId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + filterActivityId: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + }, + filterDefinitionId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: null, + isUpdateable: false, + retrieving: true, + template: false, + }, + sourceObjectId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + sourceTypeId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + statusId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/Folder.definition.js b/lib/metadataTypes/definitions/Folder.definition.js new file mode 100644 index 000000000..86e0bbc5a --- /dev/null +++ b/lib/metadataTypes/definitions/Folder.definition.js @@ -0,0 +1,173 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: [], + deployFolderTypes: [ + // lower-case values! + 'asset', + 'dataextension', + 'filteractivity', + 'filterdefinition', + 'list', + 'mysubs', + 'publication', + 'queryactivity', + 'salesforcedataextension', + 'shared_dataextension', + 'shared_salesforcedataextension', + 'ssjsactivity', + 'suppression_list', + 'synchronizeddataextension', + 'triggered_send', + 'triggered_send_journeybuilder', + ], + deployFolderBlacklist: [ + // lower-case values! + 'shared data extensions', + 'shared salesforce data extensions', + ], + folderTypesFromParent: [ + 'shared_data', + 'shared_dataextension', + 'shared_salesforcedataextension', + 'synchronizeddataextension', + 'shared_item', + 'shared_portfolio', + 'shared_template', + 'shared_email', + 'shared_content', + 'shared_suppression_list', + 'shared_publication', + 'asset-shared', + ], + hasExtended: false, + idField: 'ID', + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: false, + type: 'folder', + typeDescription: 'Used to structure all kinds of other metadata.', + typeRetrieveByDefault: false, + typeName: 'Folder', + fields: { + $: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + ID: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + Description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + ContentType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + IsActive: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + IsEditable: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + AllowChildren: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'ParentFolder.CustomerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'ParentFolder.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + 'ParentFolder.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'ParentFolder.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'ParentFolder.Path': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Path: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/FtpLocation.definition.js b/lib/metadataTypes/definitions/FtpLocation.definition.js new file mode 100644 index 000000000..7c1d3962b --- /dev/null +++ b/lib/metadataTypes/definitions/FtpLocation.definition.js @@ -0,0 +1,46 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: [], + hasExtended: false, + idField: 'id', + keyField: 'name', + nameField: 'name', + restPagination: false, + type: 'ftpLocation', + typeDescription: + 'A File Location which can be used for export or import of files to/from Marketing Cloud.', + typeRetrieveByDefault: true, + typeName: 'FTP Location', + fields: { + id: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + locationTypeId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + locationUrl: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + relPath: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/ImportFile.definition.js b/lib/metadataTypes/definitions/ImportFile.definition.js new file mode 100644 index 000000000..7daa17824 --- /dev/null +++ b/lib/metadataTypes/definitions/ImportFile.definition.js @@ -0,0 +1,224 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['ftpLocation', 'dataExtension', 'list'], + destinationObjectTypeMapping: { + DataExtension: 310, + List: 13, + SMS: 584, + Push: 613, + }, + hasExtended: false, + idField: 'importDefinitionId', + keyField: 'customerKey', + nameField: 'name', + restPagination: false, + subscriberImportTypeMapping: { + DataExtension: 255, + Email: 0, + }, + type: 'importFile', + typeDescription: 'Reads files in FTP directory for further processing.', + typeRetrieveByDefault: true, + typeName: 'Automation: Import File Activity', + updateTypeMapping: { + Add: 1, + AddUpdate: 0, + Overwrite: 4, + Update: 2, + }, + fields: { + allowErrors: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + customerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + dateFormatLocale: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + deleteFile: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + destinationId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + destinationObjectId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + destinationObjectTypeId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + encodingName: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fieldMappingType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fieldMappings: { + skipValidation: true, + }, + fileNamingPattern: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fileTransferLocationId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fileTransferLocationTypeId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + fileType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + filter: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + hasColumnHeader: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + importDefinitionId: { + isCreateable: false, + isUpdateable: true, + retrieving: true, + template: true, + }, + isOrderedImport: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isSequential: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxFileAgeHours: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxFileAgeScheduleOffsetHours: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + maxImportFrequencyHours: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + }, + notificationEmailAddress: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + otherDelimiter: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + sendEmailNotification: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + standardQuotedStrings: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + subscriberImportTypeId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + updateTypeId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + c__dataAction: { skipValidation: true }, + c__destinationType: { skipValidation: true }, + c__subscriberImportType: { skipValidation: true }, + r__dataExtension_CustomerKey: { skipValidation: true }, + r__ftpLocation_name: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/Interaction.definition.js b/lib/metadataTypes/definitions/Interaction.definition.js new file mode 100644 index 000000000..8ffed3100 --- /dev/null +++ b/lib/metadataTypes/definitions/Interaction.definition.js @@ -0,0 +1,398 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: [], + hasExtended: false, + idField: 'id', + keyField: 'key', + nameField: 'name', + restPagination: false, + type: 'interaction', + typeDescription: 'Journey from Builder (internally called "Interaction").', + typeRetrieveByDefault: false, + typeName: 'Journey', + fields: { + activities: { + skipValidation: true, + }, + categoryId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + channel: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + 'defaults.email': { + skipValidation: true, + }, + 'defaults.mobileNumber': { + skipValidation: true, + }, + 'defaults.properties.analyticsTracking.enabled': { + isCreateable: null, + isUpdateable: null, + retrieving: null, + template: null, + }, + 'defaults.properties.analyticsTracking.analyticsType': { + isCreateable: null, + isUpdateable: null, + retrieving: null, + template: null, + }, + 'defaults.properties.analyticsTracking.urlDomainsToTrack': { + isCreateable: null, + isUpdateable: null, + retrieving: null, + template: null, + }, + definitionId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + definitionType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + entryMode: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + executionMode: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + exits: { + skipValidation: true, + }, + goals: { + skipValidation: true, + }, + healthStats: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + id: { + isCreateable: false, + isUpdateable: true, + retrieving: false, + template: false, + }, + key: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + lastPublishedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'metaData.templateId': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + scheduledStatus: { + isCreateable: null, + isUpdateable: null, + retrieving: null, + template: null, + }, + stats: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + status: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + triggers: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].id': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].key': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].name': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].description': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].type': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].outcomes': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].arguments': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].arguments.startActivityKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + + 'triggers[].arguments.dequeueReason': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].arguments.lastExecutedActivityKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].arguments.filterResult': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.eventDataConfig': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.primaryObjectFilterCriteria': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.relatedObjectFilterCriteria': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.salesforceTriggerCriteria': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.objectAPIName': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.version': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.contactKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.contactPersonType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.primaryObjectFilterSummary': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.relatedObjectFilterSummary': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + + 'triggers[].configurationArguments.eventDataSummary': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.evaluationCriteriaSummary': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.applicationExtensionKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.passThroughArgument': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.filterDefinitionId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].configurationArguments.criteria': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].configurationArguments.schemaVersionId': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData.sourceInteractionId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].metaData.eventDefinitionId': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'triggers[].metaData.eventDefinitionKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData.chainType': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData.configurationRequired': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData.iconUrl': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + 'triggers[].metaData.title': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + version: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + workflowApiVersion: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/List.definition.js b/lib/metadataTypes/definitions/List.definition.js new file mode 100644 index 000000000..d2126fb96 --- /dev/null +++ b/lib/metadataTypes/definitions/List.definition.js @@ -0,0 +1,113 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder'], + folderType: 'list', + hasExtended: false, + idField: 'ObjectID', + keyField: 'CustomerKey', + nameField: 'ListName', + restPagination: null, + type: 'list', + typeDescription: 'Old way of storing data. Still used for central Email Subscriber DB.', + typeRetrieveByDefault: true, + typeName: 'List', + fields: { + 'AutomatedEmail.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + 'AutomatedEmail.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'AutomatedEmail.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Category: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + 'Client.PartnerClientKey': { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + CustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + Description: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + ID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + ListClassification: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + ListName: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + Type: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: true, + }, + r__folder_Path: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/Query.definition.js b/lib/metadataTypes/definitions/Query.definition.js new file mode 100644 index 000000000..a08f35e1c --- /dev/null +++ b/lib/metadataTypes/definitions/Query.definition.js @@ -0,0 +1,121 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['folder', 'dataExtension'], + folderType: 'queryactivity', + filter: { + description: ['Query created by Query Studio'], + }, + hasExtended: false, + idField: 'queryDefinitionId', + keyField: 'key', + nameField: 'name', + restPagination: true, + targetUpdateTypeMapping: { + Append: 2, + Overwrite: 0, + Update: 1, + }, + type: 'query', + typeDescription: 'Select & transform data using SQL.', + typeRetrieveByDefault: true, + typeName: 'Automation: SQL Query Activity', + fields: { + categoryId: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + isFrozen: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + key: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + queryDefinitionId: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + queryText: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + targetDescription: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + targetId: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + targetKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + targetName: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + targetUpdateTypeId: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + template: false, + }, + targetUpdateTypeName: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: true, + }, + validatedQueryText: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + r__folder_Path: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/Role.definition.js b/lib/metadataTypes/definitions/Role.definition.js new file mode 100644 index 000000000..1f1c0828e --- /dev/null +++ b/lib/metadataTypes/definitions/Role.definition.js @@ -0,0 +1,78 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: [], + deployBlacklist: [ + 'SYS_DEF_ADMIN', + 'SYS_DEF_ANALYST', + 'SYS_DEF_CONTENT', + 'SYS_DEF_DATA', + 'SYS_DEF_DS_USER', + 'SYS_DEF_IMHADMIN', + 'SYS_DEF_CHANNELMANAGER', + 'SYS_DEF_CONTENTEDIT', + 'SYS_DEF_SECURITYADMIN', + 'SYS_DEF_VIEWER', + ], + hasExtended: true, + idField: 'ObjectID', + keyField: 'CustomerKey', + nameField: 'Name', + type: 'role', + typeDescription: + 'User Roles define groups that are used to grant users access to SFMC systems.', + typeRetrieveByDefault: true, + typeName: 'Role', + fields: { + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + Description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + template: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + Name: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + }, + ObjectID: { + isCreateable: false, + isUpdateable: true, + retrieving: false, + template: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + IsSystemDefined: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + PermissionSets: { + retrieving: true, + skipValidation: true, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/Script.definition.js b/lib/metadataTypes/definitions/Script.definition.js new file mode 100644 index 000000000..f79b53ff4 --- /dev/null +++ b/lib/metadataTypes/definitions/Script.definition.js @@ -0,0 +1,95 @@ +module.exports = { + bodyIteratorField: 'items', + dependencies: ['folder'], + folderType: 'ssjsactivity', + hasExtended: false, + idField: 'ssjsActivityId', + keyField: 'key', + nameField: 'name', + restPagination: true, + type: 'script', + typeDescription: 'Execute more complex tasks via SSJS or AMPScript.', + typeRetrieveByDefault: true, + typeName: 'Automation: Script Activity', + fields: { + categoryId: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + createdBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + createdDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + description: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + folderLocationText: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + template: false, + }, + key: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + modifiedBy: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + modifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + template: false, + }, + name: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + script: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: true, + }, + ssjsActivityId: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: false, + }, + status: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: false, + }, + statusId: { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: false, + }, + r__folder_Path: { skipValidation: true }, + }, +}; diff --git a/lib/metadataTypes/definitions/SetDefinition.definition.js b/lib/metadataTypes/definitions/SetDefinition.definition.js new file mode 100644 index 000000000..7acc1efde --- /dev/null +++ b/lib/metadataTypes/definitions/SetDefinition.definition.js @@ -0,0 +1,807 @@ +module.exports = { + bodyIteratorField: 'setDefinition', + dependencies: [], // future may have dependency on Data Extensions + hasExtended: false, + idField: 'definitionID', + keyField: 'definitionKey', + nameField: 'name', + restPagination: false, + type: 'setDefinition', + typeDescription: 'BETA: Data Extensions linked to Attribute Groups in Data Designer.', + typeRetrieveByDefault: false, + typeName: 'Data Designer Set Definitions', + fields: { + applicationID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + applicationKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + attributeCount: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canAddValues: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canChangeValues: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canModify: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + canRemove: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + categoryID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + createDate: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + createdBy: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + customObjectOwnerMID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'dataRetentionProperties.isDeleteAtEndOfRetentionPeriod': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'dataRetentionProperties.isResetRetentionPeriodOnImport': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'dataRetentionProperties.isRowBasedRetention': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'dataRetentionProperties.periodUnitOfMeasure': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'dataRetentionProperties.setDefinitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + definitionID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + definitionKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'definitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + fullyQualifiedName: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isCustomObjectBacked: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isEvent: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isHidden: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isReadOnly: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isRoot: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isSendable: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isShared: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isSystemDefined: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + isTestaable: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftConnectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftItem.cardinality': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftItem.cardinality ': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftItem.connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftItem.identifier': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'leftItem.relationshipType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + localizedDescription: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'localizedDescription.resourceSetKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'localizedDescription.resourceValueKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'localizedDescription.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + name: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'obfuscationProperties.maskType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'obfuscationProperties.maskTypeID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'obfuscationProperties.storageType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'obfuscationProperties.storageTypeID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'obfuscationProperties.valueDefinitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'parentDefinition.connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'parentDefinition.definitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'parentDefinition.definitionKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'parentDefinition.definitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + parentID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + relationshipCount: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + relationships: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].canModify': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].canRemove': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].isGroupToSetRelationship': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].isHidden': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].isSystemDefined': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].leftRelationshipID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].leftRelationshipIDs': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].leftRelationshipIDs[].type': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].leftRelationshipIDs[].value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].leftRelationshipReferenceType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].relationshipAttributes': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].relationshipAttributes[].leftAttributeID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].relationshipAttributes[].rightAttributeID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'relationships[].relationshipID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'rightConnectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'rightItem.cardinality': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'rightItem.connectingID.identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'rightItem.identifier': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'rightItem.relationshipType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + sendAttributeStorageName: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + sendContactKeyStorageName: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + setDefinitionID: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + setDefinitionKey: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'setDefinitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageFieldReferenceID.type': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageFieldReferenceID.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + storageLogicalType: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + storageName: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageObjectFieldInformation.externalIsRowIdentifier': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageObjectFieldInformation.externalObjectFieldAPIName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageObjectFieldInformation.externalObjectFieldDataTypeName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageObjectFieldInformation.externalObjectFieldLength': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + storageObjectIDs: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageObjectInformation.externalObjectAPIName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageReferenceID.type': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'storageReferenceID.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + valueDefinitions: { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].baseType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].customerDataID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].dataSourceID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].dataSourceName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].dataType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].defaultValue': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].definitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].definitionKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].definitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].description': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].displayOrder': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].fullyQualifiedName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].identifierType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isHidden': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isIdentityValue': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isNullable': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isPrimaryKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isReadOnly': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isSystemDefined': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].isUpdateable': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].length': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].localizedDescription.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].name': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].obfuscationProperties.maskType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].obfuscationProperties.maskTypeID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].obfuscationProperties.storageType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].obfuscationProperties.storageTypeID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].obfuscationProperties.valueDefinitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].ordinal': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].parentDefinition': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].parentIdentifier': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].parentType': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].restrictionLookupListID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].scale': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].setDefinitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].setDefinitionKey': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].setDefinitionName.value': { + isCreateable: null, + isUpdateable: null, + retrieving: false, + template: null, + }, + 'valueDefinitions[].storageFieldReferenceID.type': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].storageFieldReferenceID.value': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].storageName': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].valueDefinitionID': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + 'valueDefinitions[].valueDefinitionKey': { + isCreateable: null, + isUpdateable: null, + retrieving: true, + template: null, + }, + }, +}; diff --git a/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js b/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js new file mode 100644 index 000000000..de24f253e --- /dev/null +++ b/lib/metadataTypes/definitions/TriggeredSendDefinition.definition.js @@ -0,0 +1,524 @@ +module.exports = { + bodyIteratorField: 'Results', + dependencies: ['folder', 'email', 'asset-message', 'list'], // SendClassification, SenderProfile, DeliveryProfile + folderType: 'triggered_send', + hasExtended: false, + idField: 'ObjectID', + keepId: true, + keyField: 'CustomerKey', + nameField: 'Name', + restPagination: null, + type: 'triggeredSendDefinition', + typeDescription: 'DEPRECATED: Sends emails via API or DataExtension Event.', + typeRetrieveByDefault: true, + typeName: 'Triggered Send', + fields: { + AllowedSlots: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + AutoAddSubscribers: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + AutoUpdateSubscribers: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + BatchInterval: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + BccEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + CategoryID: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + CCEmail: { + isCreateable: true, + isUpdateable: true, + retrieving: false, + templating: false, + }, + 'Client.ID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'Client.PartnerClientKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CorrelationID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + CreatedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + CustomerKey: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + DataSchemas: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'DeliveryProfile.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'DeliveryProfile.ObjectID': { + isCreateable: true, + isUpdateable: true, + retrieving: false, + templating: false, + }, + Description: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + DisableOnEmailBuildError: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + DomainType: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + DynamicEmailSubject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'Email.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'Email.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'Email.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + EmailSubject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + ExclusionFilter: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ExclusionListCollection: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'FooterContentArea.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + FooterSalutationSource: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + FromAddress: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + FromName: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'HeaderContentArea.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + HeaderSalutationSource: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + InteractionObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsAlwaysOn: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsMultipart: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + IsPlatformObject: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: true, + }, + IsSendLogging: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + IsWrapped: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + KeepExistingEmailSubject: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Keyword: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'List.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'List.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'List.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ModifiedDate: { + isCreateable: false, + isUpdateable: false, + retrieving: true, + templating: false, + }, + Name: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + NewSlotTrigger: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + ObjectID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ObjectState: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + OptionFlags: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + OptionFlagsUpdateMask: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + OptionVersion: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Owner: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PartnerProperties: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + PreHeader: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + Priority: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'PrivateDomain.ObjectID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'PrivateDomain.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'PrivateIP.ID': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'PrivateIP.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + RefreshContent: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ReplyToAddress: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + ReplyToDisplayName: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + RequestExpirationSeconds: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendClassification.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'SendClassification.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SendClassification.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.CustomerKey': { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + 'SenderProfile.ObjectID': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + 'SenderProfile.PartnerKey': { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendLimit: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SendSourceCustomerKey: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendSourceDataExtension: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendWindowClose: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SendWindowDelete: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SendWindowOpen: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + SourceAddressType: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + SuppressTracking: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TestEmailAddr: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TriggeredSendClass: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + TriggeredSendStatus: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TriggeredSendSubClass: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + TriggeredSendType: { + isCreateable: true, + isUpdateable: true, + retrieving: true, + templating: false, + }, + TriggeredSendVersionID: { + isCreateable: false, + isUpdateable: false, + retrieving: false, + templating: false, + }, + r__folder_Path: { skipValidation: true }, + r__assetMessage_Name: { skipValidation: true }, + r__assetMessage_Key: { skipValidation: true }, + }, +}; diff --git a/lib/util/businessUnit.js b/lib/util/businessUnit.js new file mode 100644 index 000000000..9b845071e --- /dev/null +++ b/lib/util/businessUnit.js @@ -0,0 +1,118 @@ +'use strict'; + +const Util = require('./util'); +const File = require('./file'); +const Retriever = require('../Retriever'); + +/** + * Helper that handles retrieval of BU info + */ +const BusinessUnit = { + /** + * Refreshes BU names and ID's from MC instance + * @param {object} properties current properties that have to be refreshed + * @param {string} credentialsName identifying name of the installed package / project + * @returns {Promise} success of refresh + */ + refreshBUProperties: async function (properties, credentialsName) { + const currentCredentials = properties.credentials[credentialsName]; + const buObject = { + clientId: currentCredentials.clientId, + clientSecret: currentCredentials.clientSecret, + tenant: currentCredentials.tenant, + }; + let client; + Util.logger.info(`Testing credentials`); + try { + client = await Util.getETClient(buObject); + } catch (ex) { + Util.logger.error(ex.message); + return false; + } + const retriever = new Retriever(properties, buObject, client); + Util.logger.info(`Loading BUs`); + + const buResult = await new Promise((resolve) => { + retriever.client.SoapClient.retrieve( + 'BusinessUnit', + ['Name', 'ID', 'ParentName', 'ParentID', 'IsActive'], + { queryAllAccounts: true }, + (error, response) => { + if (error) { + Util.logger.error(`Credentials failure. ${error}`); + resolve(null); + // throw new Error(error); + } else { + resolve(response.body.Results); + } + } + ); + }); + if (buResult !== null && !buResult.length) { + Util.logger.error(`Credentials worked but no BUs found. Check access rights!`); + } else if (buResult !== null) { + Util.logger.info(`Found ${buResult.length} BUs:`); + // create shortcut and reset BU list at the same time. we don't want old entries clutter the new list + const myBuList = (currentCredentials.businessUnits = {}); + // sort array by name for better display (wont affect object in settings) + buResult + .sort((a, b) => { + if (a.ParentID === '0') { + return -1; + } + if (b.ParentID === '0') { + return 1; + } + if (a.Name.toLowerCase() < b.Name.toLowerCase()) { + return -1; + } + if (a.Name.toLowerCase() > b.Name.toLowerCase()) { + return 1; + } + return 0; + }) + .forEach((element) => { + if (element.ParentID === '0') { + myBuList[Util.parentBuName] = element.ID; + currentCredentials.eid = element.ID; + Util.logger.info(` - ${Util.parentBuName} (${element.Name})`); + } else { + const equalizedName = element.Name.replace(/[^\w\s]/gi, '') // remove special chars + .replace(/ +/g, '_') // convert spaces to underscore + .replace(/__+/g, '_'); // make sure we never have more than one underscore in a row + myBuList[equalizedName] = element.ID; + Util.logger.info( + ` - ${element.Name} ${ + element.Name !== equalizedName ? `(${equalizedName})` : '' + }` + ); + } + }); + Util.logger.debug(`EID: ${currentCredentials.eid}`); + if (currentCredentials.eid === null) { + Util.logger.warn( + `It seems your 'installed package' was not created on the Parent BU of your instance.` + ); + Util.logger.warn( + `While basic functionality will work, it is strongly recommended that you create a new 'installed package' there to enable support for shared Data Extensions and automatic retrieval of the BU list.` + ); + Util.logger.warn( + `If you cannot create a package on the Parent BU, please open your ./.mcdevrc.json and update the list of BUs and their MIDs manually.` + ); + // allow user to work by setting this to an obviously false value which nonetheless doesn't block execution + currentCredentials.eid = -1; + } + // store BU list for repo + await File.writeJSONToFile( + properties.directories.businessUnits, + File.filterIllegalFilenames(credentialsName + '.businessUnits'), + buResult + ); + // update config + await File.saveConfigFile(properties); + } + return true; + }, +}; + +module.exports = BusinessUnit; diff --git a/lib/util/cli.js b/lib/util/cli.js new file mode 100644 index 000000000..291c29aa5 --- /dev/null +++ b/lib/util/cli.js @@ -0,0 +1,601 @@ +'use strict'; + +const BuHelper = require('./businessUnit'); +const File = require('./file'); +const inquirer = require('inquirer'); +const MetadataDefinitions = require('./../MetadataTypeDefinitions'); +const Util = require('./util'); + +/** + * CLI helper class + */ + +const Cli = { + /** + * used when initially setting up a project. + * loads default config and adds first credential + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.clientId client id of installed package + * @param {String} skipInteraction.clientSecret client id of installed package + * @param {String} skipInteraction.tenant client id of installed package + * @param {String} skipInteraction.credentialsName how you would like the credential to be named + * @returns {Promise} success of init + */ + async initMcdevConfig(skipInteraction) { + Util.logger.info('-- Initialising server connection --'); + Util.logger.info('Please enter a name for your "Installed Package" credentials:'); + const propertiesTemplate = Util.getDefaultProperties(); + delete propertiesTemplate.credentials.default; + + // wait for the interaction to finish or else an outer await will run before this is done + return this._setCredential(propertiesTemplate, null, skipInteraction); + }, + /** + * Extends template file for properties.json + * @param {Object} properties config file's json + * @param {Object} properties.credentials list of existing credentials + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.clientId client id of installed package + * @param {String} skipInteraction.clientSecret client id of installed package + * @param {String} skipInteraction.tenant client id of installed package + * @param {String} skipInteraction.credentialsName how you would like the credential to be named + * @returns {Promise} - + */ + async addExtraCredential(properties, skipInteraction) { + if (!Util.checkProperties(properties)) { + // return null here to avoid seeing 2 error messages for the same issue + return null; + } else { + Util.logger.info('Found the following credentials in your config file:'); + for (const cred in properties.credentials) { + if (Object.prototype.hasOwnProperty.call(properties.credentials, cred)) { + Util.logger.info(` - ${cred}`); + } + } + Util.logger.info('\nPlease enter your new credentials'); + if (skipInteraction && properties.credentials[skipInteraction.credentialsName]) { + Util.logger.error( + `Credential '${skipInteraction.credentialsName}' already existing. If you tried updating please provide run 'mcdev init ${skipInteraction.credentialsName}'` + ); + } + return this._setCredential(properties, null, skipInteraction); + } + }, + /** + * Extends template file for properties.json + * update credentials + * @param {object} properties config file's json + * @param {string} credName name of credential that needs updating + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.clientId client id of installed package + * @param {String} skipInteraction.clientSecret client id of installed package + * @param {String} skipInteraction.tenant client id of installed package + * @param {String} skipInteraction.credentialsName how you would like the credential to be named + * @returns {Promise} success of update + */ + async updateCredential(properties, credName, skipInteraction) { + if (credName) { + if (!skipInteraction) { + Util.logger.info(`Please enter the details for '${credName}'`); + } + return await this._setCredential(properties, credName, skipInteraction); + } + }, + /** + * Returns Object with parameters required for accessing API + * @param {Object} properties object of all configuration including credentials + * @param {String} target code of BU to use + * @param {boolean|string} [isCredentialOnly] true:don't ask for BU | string: name of BU + * @param {boolean} [allowAll] Offer ALL as option in BU selection + * @returns {Promise} credential to be used for Business Unit + */ + async getCredentialObject(properties, target, isCredentialOnly, allowAll) { + try { + if (!Util.checkProperties(properties)) { + // return null here to avoid seeing 2 error messages for the same issue + return null; + } + let [credential, businessUnit] = target ? target.split('/') : [null, null]; + if ( + credential && + properties.credentials[credential] && + !businessUnit && + 'string' === typeof isCredentialOnly + ) { + // correct credential provided and BU pre-selected + businessUnit = isCredentialOnly; + } else if (!credential || !properties.credentials[credential]) { + // no or unknown credential provided; BU either to be selected or pre-selected + if (credential !== null) { + Util.logger.warn(`Credential '${credential}' not found`); + } + const response = await this._selectBU(properties, null, isCredentialOnly, allowAll); + credential = response.credential; + businessUnit = response.businessUnit; + if (!isCredentialOnly) { + Util.logger.info( + `You could directly pass in this info with '${credential}/${businessUnit}'` + ); + } else if (credential && !businessUnit && 'string' === typeof isCredentialOnly) { + // BU pre-selected + businessUnit = isCredentialOnly; + } + } else if ( + !isCredentialOnly && + (!businessUnit || !properties.credentials[credential].businessUnits[businessUnit]) + ) { + // correct credential provided but BU still needed + if (businessUnit && businessUnit !== 'undefined') { + Util.logger.warn( + `Business Unit '${businessUnit}' not found for credential '${credential}'` + ); + } + const response = await this._selectBU(properties, credential, null, allowAll); + businessUnit = response.businessUnit; + Util.logger.info( + `You could directly pass in this info with '${credential}/${businessUnit}'` + ); + } + return { + clientId: properties.credentials[credential].clientId, + clientSecret: properties.credentials[credential].clientSecret, + tenant: properties.credentials[credential].tenant, + eid: properties.credentials[credential].eid, + mid: properties.credentials[credential].businessUnits[businessUnit], + businessUnit: businessUnit, + credential: credential, + }; + } catch (ex) { + Util.logger.error(ex.message); + return null; + } + }, + /** + * helps select the right credential in case of bad initial input + * + * @param {object} properties config file's json + * @param {string} [credential] name of valid credential + * @param {boolean} [isCredentialOnly] don't ask for BU if true + * @param {boolean} [allowAll] Offer ALL as option in BU selection + * @returns {Promise} selected credential/BU combo + */ + async _selectBU(properties, credential, isCredentialOnly, allowAll) { + const credList = []; + const buList = []; + const questions = []; + const allBUsAnswer = '* (All BUs)'; + // no proper credential nor BU was given. ask for credential first + if (!credential) { + for (const cred in properties.credentials) { + if (Object.keys(properties.credentials[cred].businessUnits).length) { + // only add credentials that have BUs + credList.push(cred); + } + } + questions.push({ + type: 'list', + name: 'credential', + message: 'Please select the credential you were looking for:', + choices: credList, + // eslint-disable-next-line require-jsdoc + filter: function (answer) { + if (!isCredentialOnly) { + for (const bu in properties.credentials[answer].businessUnits) { + buList.push(bu); + } + if (!buList.length) { + // unlikely error as we are filtering for this already while creating credList + throw new Error('No Business Unit defined for this credential'); + } else if (allowAll) { + // add ALL option to beginning of list + buList.unshift(allBUsAnswer); + } + } + return answer; + }, + }); + } else if (credential) { + for (const bu in properties.credentials[credential].businessUnits) { + buList.push(bu); + } + if (!buList.length) { + // that could only happen if config is faulty + throw new Error('No Business Unit defined for this credential'); + } else if (allowAll) { + // add ALL option to beginning of list + buList.unshift(allBUsAnswer); + } + } + if ((credential && buList.length) || (!credential && !isCredentialOnly)) { + questions.push({ + type: 'list', + name: 'businessUnit', + message: 'Please select the right BU:', + choices: buList, + }); + } + let responses = null; + if (questions.length) { + try { + responses = await new Promise((resolve) => { + inquirer + .prompt(questions) + .then((answers) => { + resolve(answers); + }) + .catch((err) => { + Util.logger.error(err); + }); + }); + } catch (e) { + Util.logger.info(e); + } + if (responses.businessUnit && responses.businessUnit === allBUsAnswer) { + // remove textual explanation of * + responses.businessUnit = '*'; + } + } else { + throw new Error('credentials / BUs not configured'); + } + return responses; + }, + /** + * helper around _askCredentials + * + * @param {object} properties from config file + * @param {string} [credName] name of credential that needs updating + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.clientId client id of installed package + * @param {String} skipInteraction.clientSecret client id of installed package + * @param {String} skipInteraction.tenant client id of installed package + * @param {String} skipInteraction.credentialsName how you would like the credential to be named + * + * @returns {Promise} success of refresh or credential name + */ + async _setCredential(properties, credName, skipInteraction) { + // Get user input + let credentialsGood = null; + let inputData; + do { + if (skipInteraction) { + if ( + skipInteraction.clientId && + skipInteraction.clientSecret && + skipInteraction.tenant && + skipInteraction.credentialsName + ) { + // assume skipInteraction=={clientId,clientSecret,tenant,credentialsName} + inputData = skipInteraction; + } else { + throw new Error( + '--skipInteraction flag found but missing required input for clientId,clientSecret,tenant,credentialsName' + ); + } + } else { + inputData = await this._askCredentials(properties, credName); + } + // Replace defaults with user input + const buObject = { + clientId: inputData.clientId, + clientSecret: inputData.clientSecret, + tenant: inputData.tenant, + eid: null, + businessUnits: null, + }; + + // test if credentials are valid + try { + await Util.getETClient(buObject); + credentialsGood = true; + + // update central config now that the credentials are verified + properties.credentials[inputData.credentialsName] = buObject; + } catch (ex) { + Util.logger.error(ex.message); + credentialsGood = false; + if (skipInteraction) { + // break the otherwise infinite loop + return; + } + } + } while (!credentialsGood); + // Get all business units and add them to the properties + const status = await BuHelper.refreshBUProperties(properties, inputData.credentialsName); + return status ? inputData.credentialsName : status; + }, + + /** + * helper for addExtraCredential() + * + * @param {Object} properties from config file + * @param {string} [credName] name of credential that needs updating + * @returns {Promise} credential info + */ + async _askCredentials(properties, credName) { + const questions = []; + if (!credName) { + questions.push({ + type: 'input', + name: 'credentialsName', + message: 'Credential name (your choice)', + // eslint-disable-next-line require-jsdoc + validate: function (value) { + if (!value || value.trim().length < 2) { + return 'Please enter at least 2 characters'; + } + if (properties && properties.credentials[value]) { + return `There already is an account with the name '${value}' in your config.`; + } + const converted = encodeURIComponent(value).replace(/[*]/g, '_STAR_'); + if (value != converted) { + return 'Please do not use any special chars'; + } + return true; + }, + }); + } + const tenantRegex = /^https:\/\/([\w-]*).(auth|soap|rest).marketingcloudapis.com[/]?$/iu; + questions.push( + { + type: 'input', + name: 'clientId', + message: 'Client Id', + // eslint-disable-next-line require-jsdoc + validate: function (value) { + if (!value || value.trim().length < 10) { + return 'Please enter valid client id'; + } + return true; + }, + }, + { + type: 'input', + name: 'clientSecret', + message: 'Client Secret', + // eslint-disable-next-line require-jsdoc + validate: function (value) { + if (!value || value.trim().length < 10) { + return 'Please enter valid client secret'; + } + return true; + }, + }, + { + type: 'input', + name: 'tenant', + message: 'Authentication Base URI', + validate: (value) => { + if (!value || value.trim().length < 10) { + return 'Please enter a valid tenant identifier'; + } else if (!tenantRegex.test(value.trim())) { + return `Please copy the URI directly from the installed package's "API Integration" section. It looks like this: https://a1b2b3xy56z.auth.marketingcloudapis.com/`; + } else { + // all good + return true; + } + }, + } + ); + const responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + // remove extra white space + responses.clientId = responses.clientId.trim(); + responses.clientSecret = responses.clientSecret.trim(); + responses.tenant = responses.tenant.trim(); + + // extract the tenant sub-domain from the URI; the format was already validated above + responses.tenant = responses.tenant.match(tenantRegex)[1]; + + if (credName) { + // if credential name was provided as parameter, we didn't ask the user for it + responses.credentialsName = credName; + } + + return responses; + }, + /** + * allows updating the metadata types that shall be retrieved + * @param {object} properties config file's json + * @param {object} properties.metaDataTypes - + * @param {String[]} properties.metaDataTypes.retrieve list of currently retrieved types + * @param {String[]} [setTypesArr] skip user prompt and overwrite with this list if given + * @returns {Promise} - + */ + async selectTypes(properties, setTypesArr) { + let responses; + if (!setTypesArr) { + if (Util.logger.level !== 'debug') { + Util.logger.info( + 'Run mcdev selectTypes --debug if you need to use "disabled" types.' + ); + } else { + Util.logger.warn( + 'Debug mode enabled. Allowing selection of "disabled" types. Please be aware that these might be unstable.' + ); + } + const flattenedDefinitions = []; + for (const el in MetadataDefinitions) { + // if subtypes on metadata (eg. Assets) then add each nested subtype + if (MetadataDefinitions[el].subTypes) { + for (const subtype of MetadataDefinitions[el].subTypes) { + flattenedDefinitions.push({ + typeName: + MetadataDefinitions[el].typeName.replace('-[Subtype]', ': ') + + subtype, + type: MetadataDefinitions[el].type + '-' + subtype, + mainType: MetadataDefinitions[el].type, + typeRetrieveByDefault: MetadataDefinitions[ + el + ].typeRetrieveByDefault.includes(subtype), + }); + } + } + // else just return normal type + else { + flattenedDefinitions.push({ + typeName: MetadataDefinitions[el].typeName, + type: MetadataDefinitions[el].type, + typeRetrieveByDefault: MetadataDefinitions[el].typeRetrieveByDefault, + }); + } + } + // walk through all definitions (sub and main) and select them if already selected + const typeChoices = flattenedDefinitions.map((def) => ({ + name: + def.typeName + + (Util.logger.level === 'debug' && !def.typeRetrieveByDefault + ? ' \x1b[1;30;40m(non-default)\u001b[0m' + : ''), + value: def.type, + disabled: !(Util.logger.level === 'debug' || def.typeRetrieveByDefault) + ? 'disabled' + : false, + // subtypes can be activated through their main type + checked: + properties.metaDataTypes.retrieve.includes(def.type) || + (properties.metaDataTypes.retrieve.includes(def.mainType) && + def.typeRetrieveByDefault) + ? true + : false, + })); + + // sort types by 1) initially selected and 2) alphabetically + typeChoices.sort((a, b) => { + if (a.name && b.name && a.name.toLowerCase() < b.name.toLowerCase()) { + return -1; + } + if (a.name && b.name && a.name.toLowerCase() > b.name.toLowerCase()) { + return 1; + } + if (a.value.toLowerCase() < b.value.toLowerCase()) { + return -1; + } + if (a.value.toLowerCase() > b.value.toLowerCase()) { + return 1; + } + return 0; + }); + // add end-of-list marker + typeChoices.push(new inquirer.Separator(' ==== ')); + + const questions = [ + { + type: 'checkbox', + message: 'Select Metadata types for retrieval', + name: 'selectedTypes', + pageSize: 10, + choices: typeChoices, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } else { + responses = { + selectedTypes: setTypesArr, + }; + } + + if (responses.selectedTypes) { + this._summarizeSubtypes(responses, 'asset'); + // update config + properties.metaDataTypes.retrieve = responses.selectedTypes; + await File.saveConfigFile(properties); + } + }, + /** + * helper for this.selectTypes() that converts subtypes back to main type if all and only defaults were selected + * this keeps the config automatically upgradable when we add new subtypes or change what is selected by default + * @param {Object} responses wrapper object for respones + * @param {String[]} responses.selectedTypes what types the user selected + * @param {String} type metadata type + * @returns {void} + */ + _summarizeSubtypes(responses, type) { + const selectedAssetSubtypes = responses.selectedTypes.filter((str) => + str.includes(type + '-') + ); + if ( + selectedAssetSubtypes.length === MetadataDefinitions[type].typeRetrieveByDefault.length + ) { + const nonDefaultSelectedAssetSubtypes = selectedAssetSubtypes + .map((subtype) => subtype.replace(type + '-', '')) + .filter( + (subtype) => !MetadataDefinitions[type].typeRetrieveByDefault.includes(subtype) + ); + if (!nonDefaultSelectedAssetSubtypes.length) { + // found all defaults and nothing else. replace with main type + responses.selectedTypes = responses.selectedTypes.filter( + (str) => !str.includes(type + '-') + ); + + responses.selectedTypes.push(type); + responses.selectedTypes.sort(); + } + } + }, + + /** + * shows metadata type descriptions + * @returns {void} - + */ + explainTypes() { + // overwrites default layout of console.table + require('console.table'); + + const typeChoices = []; + for (const el in MetadataDefinitions) { + if (MetadataDefinitions[el].subTypes) { + // used for assets to show whats available by default + typeChoices.push({ + Name: MetadataDefinitions[el].typeName, + Default: '┐', + Description: MetadataDefinitions[el].typeDescription, + }); + let lastCountdown = MetadataDefinitions[el].subTypes.length; + for (const subtype of MetadataDefinitions[el].subTypes) { + lastCountdown--; + const subTypeRetrieveByDefault = MetadataDefinitions[ + el + ].typeRetrieveByDefault.includes(subtype); + const definition = + ' ' + MetadataDefinitions[el].extendedSubTypes[subtype].join(', '); + typeChoices.push({ + Name: + MetadataDefinitions[el].typeName.replace('-[Subtype]', ': ') + subtype, + Default: + (lastCountdown > 0 ? '├ ' : '└ ') + + (subTypeRetrieveByDefault ? 'yes' : '-'), + Description: + definition.length > 90 ? definition.substr(0, 90) + '...' : definition, + }); + } + // change leading symbol of last subtype to close the tree visually + } else { + // types without subtypes + typeChoices.push({ + Name: MetadataDefinitions[el].typeName, + Default: MetadataDefinitions[el].typeRetrieveByDefault ? 'yes' : '-', + Description: MetadataDefinitions[el].typeDescription, + }); + } + } + typeChoices.sort((a, b) => { + if (a.Name.toLowerCase() < b.Name.toLowerCase()) { + return -1; + } + if (a.Name.toLowerCase() > b.Name.toLowerCase()) { + return 1; + } + return 0; + }); + console.table(typeChoices); + }, +}; + +module.exports = Cli; diff --git a/lib/util/devops.js b/lib/util/devops.js new file mode 100644 index 000000000..f05bc95a8 --- /dev/null +++ b/lib/util/devops.js @@ -0,0 +1,279 @@ +const File = require('./file'); +const path = require('path'); +const inquirer = require('inquirer'); +const Util = require('./util'); +const git = require('simple-git/promise')(); +const MetadataType = require('../MetadataTypeInfo'); +const jsonToTable = require('json-to-table'); +/** + * DevOps helper class + */ + +const DevOps = { + /** + * Extracts the delta between a commit and the current state for deployment. + * Interactive commit selection if no commits are passed. + * @param {Object} properties central properties object + * @param {String} [range] git commit range + * @param {boolean} [saveToDeployDir] if true, copy metadata changes into deploy directory + * @param {String} [filterPaths] filter file paths that start with any specified path (comma separated) + * @returns {Promise} - + */ + async createDeltaPkg(properties, range, saveToDeployDir, filterPaths) { + const rangeUserInput = range; + Util.logger.info('Create Delta Package ::'); + if (filterPaths) { + filterPaths = filterPaths.split(',').map((filePath) => + path + .normalize(properties.directories.retrieve + filePath) + .split('\\') + .join('/') + ); + } else { + filterPaths = [properties.directories.retrieve]; + } + if (range) { + if (!range.includes('..')) { + // we limit the user here somewhat by always comparing to current branch if no range was given + // this should make it easier in most scenrios though + range = range + '..HEAD'; + } + Util.logger.info( + `Analyzing changes in directories: ${filterPaths} based on commit range: ${range}` + ); + } else { + // get the last 10 commits by default to choose from. Default can be changed in mcdev config. + // Current commit is skipped due to no changes + const commits = await git.log([ + '--skip=1', + `-${properties.options.deployment.commitHistory || 10}`, + ]); + const display = commits.all.map((commit) => ({ + name: commit.date + ' / ' + commit.message + ' / ' + commit.author_name, + value: commit.hash, + })); + display.push(new inquirer.Separator(' ==== ')); + + const questions = [ + { + type: 'list', + message: 'Select base commit for comparison with current commit', + name: 'commit', + pageSize: 10, + choices: display, + }, + ]; + const responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + range = `${responses.commit}..HEAD`; + } + + const metadata = {}; + // TODO: add filter based on if metadata type is deployable or not + const gitActionsCounter = { + delete: 0, + 'add/update': 0, + move: 0, + }; + const delta = (await git.diffSummary([range])).files + // populate additional info for all changed files + .map((file) => { + // If file was moved it's path needs to be parsed + file.moved = file.file.includes('=>'); + if (file.moved) { + const p = file.file; + const paths = { + base: p.substring(0, p.indexOf('{')), + before: p.substring(p.indexOf('{') + 1, p.indexOf('=>') - 1), + after: p.substring(p.indexOf('=>') + 3, p.indexOf('}')), + end: p.substring(p.indexOf('}') + 1), + }; + file.fromPath = path + .normalize(`${paths.base}${paths.before}${paths.end}`) + .split('\\') + .join('/'); + file.file = path + .normalize(`${paths.base}${paths.after}${paths.end}`) + .split('\\') + .join('/'); + } else { + file.fromPath = '-'; + file.file = path.normalize(file.file).split('\\').join('/'); + } + + // get metadata type from file name + file.type = path.basename(file.file).split('.')[1].split('-').shift(); + + return file; + }) + // Filter to only handle files that start with at least one of the passed filterPaths. + // ! Filter happens after initial parse, because if file was moved its new path has to be calculated + .filter((file) => filterPaths.some((path) => file.file.startsWith(path))) + // ensure badly named files on unsupported metadata types are not in our subset + .filter((file) => { + if (!MetadataType[file.type]) { + Util.logger.debug( + `Unknown metadata-type found for (${file.file}): ` + file.type + ); + return false; + } else { + return true; + } + }) + .map((file) => { + // Gets external key based on file name und the assumption that filename = externalKey + if (file.type === 'folder') { + file.externalKey = null; + file.name = path.basename(file.file).split('.').shift(); + } else { + file.externalKey = path.basename(file.file).split('.').shift(); + file.name = null; + } + + // Check if file doesn't exist in reported path, that means it was a git deletion + // TODO: improve git action detection by switching from diffSummary to diff with --summary result parsing + if (!File.existsSync(file.file)) { + file.gitAction = 'delete'; + } else if (file.moved) { + file.gitAction = 'move'; + } else { + file.gitAction = 'add/update'; + } + gitActionsCounter[file.gitAction]++; + file._credential = file.file.split('/')[1]; + file._businessUnit = file.file.split('/')[2]; + + // Parse retrieve directory to also populate the name field (not possible for deleted files) + if (file.gitAction !== 'delete' && file.type !== 'folder') { + // folders are saved with their name as file-name, not with their key, hence this section can be skipped for folders + const buPath = `${properties.directories.retrieve}/${file._credential}/${file._businessUnit}/`; + if (!metadata[file._credential]) { + metadata[file._credential] = {}; + } + if (!metadata[file._credential][file._businessUnit]) { + metadata[file._credential][file._businessUnit] = {}; + } + if (!metadata[file._credential][file._businessUnit][file.type]) { + try { + MetadataType[file.type].readBUMetadataForType( + buPath, + false, + metadata[file._credential][file._businessUnit] + ); + } catch (ex) { + // silently catch directory-not-found errors here + Util.logger.debug(ex.message); + } + } + const fileContent = + metadata[file._credential][file._businessUnit][file.type][file.externalKey]; + if (fileContent) { + file.name = fileContent[MetadataType[file.type].definition.nameField]; + } + } + return file; + }); + if ( + !gitActionsCounter['add/update'] && + !gitActionsCounter.move && + !gitActionsCounter.delete + ) { + Util.logger.warn( + `- ❌ No changes found. Check what branch you are currently on and if the target branch name (${rangeUserInput}${ + range !== rangeUserInput ? ' converted to ' + range : '' + }) was correct` + ); + return []; + } + // Write into delta.json to serve as documentation + File.writeJSONToFile(properties.directories.deltaPackage, 'delta_package', delta); + this.document(properties.directories.deltaPackage, delta); + Util.logger.info( + `- ✔️ Identified changes: Add/Update=${gitActionsCounter['add/update']}, Move=${gitActionsCounter['move']}, Delete=${gitActionsCounter['delete']}` + ); + if (gitActionsCounter.delete > 0) { + Util.logger.warn( + 'Please note that deletions have to be done manually on the SFMC website.' + ); + } + Util.logger.info( + `Saved report in ${path.join(properties.directories.deltaPackage, 'delta_package.md')}` + ); + + // Copy filtered list of files into deploy directory + if (saveToDeployDir) { + const copied = delta.map((file) => + File.copyFile( + file.file, + file.file.replace( + path.normalize(properties.directories.retrieve), + path.normalize(properties.directories.deploy) + ) + ) + ); + const results = await Promise.all(copied); + const failed = results.filter((result) => result.status === 'failed'); + const skipped = results.filter((result) => result.status === 'skipped'); + + Util.logger.info( + `Copied changes to deploy directory (${ + results.length - skipped.length - failed.length + } copied)` + ); + Util.logger.debug( + `Copied changes to deploy directory (${ + results.length - skipped.length - failed.length + } copied, ${skipped.length} skipped, ${failed.length} failed)` + ); + if (skipped.length > 0) { + skipped.forEach((file) => + Util.logger.debug(`Skipped - ${file.statusMessage} - ${file.file}`) + ); + } + if (failed.length > 0) { + failed.forEach((file) => + Util.logger.error(`Failed - ${file.statusMessage} - ${file.file}`) + ); + } + } + return delta; + }, + /** + * create markdown file for deployment listing + * @param {String} directory - + * @param {Object} jsonReport - + * @returns {void} + */ + document(directory, jsonReport) { + const tabled = jsonToTable(jsonReport); + let output = `# Deployment Report\n\n`; + let tableSeparator = ''; + tabled[0].forEach((column) => { + if (column !== '') { + output += `| ${column} `; + tableSeparator += '| --- '; + } + }); + output += `|\n${tableSeparator}|\n`; + for (let i = 1; i < tabled.length; i++) { + tabled[i].forEach((field) => { + if (field !== '') { + field = field === true ? '✓' : field === false ? '✗' : field; + output += `| ${field} `; + } + }); + output += '|\n'; + } + try { + // write to disk (asynchronously) + File.writeToFile(directory, 'delta_package', 'md', output); + } catch (ex) { + Util.logger.error(`DevOps.document():: error | ` + ex.message); + } + }, +}; + +module.exports = DevOps; diff --git a/lib/util/file.js b/lib/util/file.js new file mode 100644 index 000000000..02e688ae3 --- /dev/null +++ b/lib/util/file.js @@ -0,0 +1,565 @@ +/* eslint-disable no-control-regex */ +'use strict'; + +const fs = require('fs-extra'); +const packageJson = require('../../package.json'); +const path = require('path'); +const prettier = require('prettier'); +const Util = require('./util'); +const sql = require('sql-formatter-plus'); +const updateNotifier = require('update-notifier-git'); + +// inform user when there is an update +const notifier = updateNotifier({ + pkg: packageJson, + updateCheckInterval: 1000 * 3600 * 24, // once per day + remoteUrl: packageJson.repository.url.split('git+')[1], +}); +// Notify using the built-in convenience method +notifier.notify(); + +/** + * File extends fs-extra. It adds logger and util methods for file handling + */ +const File = { + /** + * copies a file from one path to another + * @param {String} from - full filepath including name of existing file + * @param {String} to - full filepath including name where file should go + * @returns {Object} - results object + */ + async copyFile(from, to) { + try { + await fs.copy(from, to); + return { status: 'ok', file: from }; + } catch (ex) { + // This can happen in some cases where referencing files deleted in Commit + if (ex.message.startsWith('ENOENT: no such file or directory')) { + return { + status: 'skipped', + statusMessage: 'deleted from repository', + file: from, + }; + } else { + return { status: 'failed', statusMessage: ex.message, file: from }; + } + } + }, + /** + * makes sure Windows accepts path names + * @param {String} path - filename or path + * @returns {String} - corrected string + */ + filterIllegalPathChars(path) { + return ( + encodeURIComponent(path) + .replace(/[*]/g, '_STAR_') + // convert space back + .split('%20') + .join(' ') + // convert forward slash back as it's needed in dirs + .split('%2F') + .join('/') + // convert backward slash back as it's needed in dirs + .split('%5C') + .join('\\') + // convert opening-curly brackets back for templating + .split('%7B') + .join('{') + // convert closing-curly brackets back for templating + .split('%7D') + .join('}') + ); + }, + + /** + * makes sure Windows accepts file names + * @param {String} filename - filename or path + * @returns {String} - corrected string + */ + filterIllegalFilenames(filename) { + return ( + encodeURIComponent(filename) + .replace(/[*]/g, '_STAR_') + // convert space back + .split('%20') + .join(' ') + // convert opening-curly brackets back for templating + .split('%7B') + .join('{') + // convert closing-curly brackets back for templating + .split('%7D') + .join('}') + ); + }, + + /** + * makes sure Windows accepts file names + * @param {String} filename - filename or path + * @returns {String} - corrected string + */ + reverseFilterIllegalFilenames(filename) { + return decodeURIComponent(filename).split('_STAR_').join('*'); + }, + + /** + * Takes various types of path strings and formats into a platform specific path + * @param {string|string[]} denormalizedPath directory the file will be written to + * @returns {String} Path strings + */ + normalizePath: function (denormalizedPath) { + if (Array.isArray(denormalizedPath)) { + // if the value is undefined set to empty string to allow parsing + return path.join(...denormalizedPath.map((val) => val || '')); + } else { + // if directory is empty put . as otherwill will write to c:// + return path.join(denormalizedPath || '.'); + } + }, + /** + * Saves json content to a file in the local file system. Will create the parent directory if it does not exist + * @param {string|string[]} directory directory the file will be written to + * @param {String} filename name of the file without '.json' ending + * @param {Object} content filecontent + * @returns {Promise} Promise + */ + writeJSONToFile: async function (directory, filename, content) { + directory = this.filterIllegalPathChars(this.normalizePath(directory)); + filename = this.filterIllegalFilenames(filename); + if (!fs.existsSync(directory)) { + fs.mkdirpSync(directory); + } + try { + await fs.writeJSON(path.join(directory, filename + '.json'), content, { spaces: 4 }); + } catch (ex) { + Util.logger.error('File.writeJSONToFile:: error | ' + ex.message); + } + }, + /** + * Saves beautified files in the local file system. Will create the parent directory if it does not exist + * ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this + * @param {string|string[]} directory directory the file will be written to + * @param {String} filename name of the file without suffix + * @param {String} filetype filetype ie. JSON or SSJS + * @param {String} content filecontent + * @param {Object} [templateVariables] templating variables to be replaced in the metadata + * @returns {Promise} Promise + */ + writePrettyToFile: async function (directory, filename, filetype, content, templateVariables) { + let formatted; + if (filetype === 'sql') { + formatted = this._beautify_sql(content); + } else { + // we need to ensure formatted is a String, not a Promise + formatted = await this._beautify_prettier(directory, filename, filetype, content); + } + if (templateVariables) { + formatted = Util.replaceByObject(formatted, templateVariables); + } + + return this.writeToFile(directory, filename, filetype, formatted); + }, + /** + * helper for writePrettyToFile, applying sql formatting onto given stringified content + * @param {String} content filecontent + * @returns {String} original string on error; formatted string on success + */ + _beautify_sql: function (content) { + let formatted; + try { + formatted = sql.format(content, { + language: 'sql', // Defaults to "sql" + indent: ' ', // Defaults to two spaces,W + uppercase: true, // Defaults to false + linesBetweenQueries: 1, // Defaults to 1 + }); + // if templating variables were in the code, those now have extra spaces + formatted = formatted.split('{ { { ').join('{{{').split(' } } }').join('}}}'); + } catch (ex) { + Util.logger.debug('SQL Formatter Exception: ' + ex.message); + formatted = content; + } + return formatted; + }, + /** + * helper for writePrettyToFile, applying prettier onto given stringified content + * ! Important: run 'await File.initPrettier()' in your MetadataType.retrieve() once before hitting this + * @param {string|string[]} directory directory the file will be written to + * @param {String} filename name of the file without suffix + * @param {String} filetype filetype ie. JSON or SSJS + * @param {String} content filecontent + * @returns {String} original string on error; formatted string on success + */ + _beautify_prettier: function (directory, filename, filetype, content) { + if (!FileFs.prettierConfig) { + // either no prettier config in project directory or initPrettier was not run before this + return content; + } else if (content.includes('%%[') || content.includes('%%=')) { + // in case we find AMPScript we need to abort beautifying as prettier + // will throw an error falsely assuming bad syntax + return content; + } + let formatted = ''; + try { + // load the right prettier config relative to our file + switch (filetype) { + case 'htm': + case 'html': + FileFs.prettierConfig.parser = 'html'; + break; + case 'ssjs': + case 'js': + FileFs.prettierConfig.parser = 'babel'; + break; + case 'json': + FileFs.prettierConfig.parser = 'json'; + break; + case 'yaml': + case 'yml': + FileFs.prettierConfig.parser = 'yaml'; + break; + case 'ts': + FileFs.prettierConfig.parser = 'babel-ts'; + break; + case 'css': + FileFs.prettierConfig.parser = 'css'; + break; + case 'less': + FileFs.prettierConfig.parser = 'less'; + break; + case 'sass': + case 'scss': + FileFs.prettierConfig.parser = 'scss'; + break; + case 'md': + FileFs.prettierConfig.parser = 'markdown'; + break; + default: + FileFs.prettierConfig.parser = 'babel'; + } + formatted = prettier.format(content, FileFs.prettierConfig); + } catch (ex) { + const warnMsg = `Potential Code issue found in ${this.normalizePath([ + ...directory, + filename + '.' + filetype, + ])}`; + Util.logger.debug(warnMsg); + + // save prettier errror into log file + // Note: we have to filter color codes from prettier's error message before saving it to file + this.writeToFile( + directory, + filename + '.error', + 'log', + `Error Log\nParser: ${FileFs.prettierConfig.parser}\n${ex.message.replace( + /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g, + + '' + )}` + ); + + formatted = content; + } + + return formatted; + }, + /** + * Saves text content to a file in the local file system. Will create the parent directory if it does not exist + * @param {string|string[]} directory directory the file will be written to + * @param {String} filename name of the file without '.json' ending + * @param {String} filetype filetype suffix + * @param {String} content filecontent + * @param {Object} [encoding] added for certain file types (like images) + * @returns {Promise} Promise + */ + writeToFile: async function (directory, filename, filetype, content, encoding) { + directory = this.filterIllegalPathChars(this.normalizePath(directory)); + if (!fs.existsSync(directory)) { + fs.mkdirpSync(directory); + } + // filter characters that are illegal for file names in Windows + filename = this.filterIllegalFilenames(filename); + + try { + await fs.writeFile(path.join(directory, filename + '.' + filetype), content, encoding); + return true; + } catch (ex) { + Util.logger.error('File.writeToFile:: error | ' + ex.message); + return false; + } + }, + + /** + * Saves json content to a file in the local file system. Will create the parent directory if it does not exist + * @param {String|String[]} directory directory where the file is stored + * @param {String} filename name of the file without '.json' ending + * @param {Boolean} sync should execute sync (default is async) + * @param {Boolean} cleanPath should execute sync (default is true) + * @returns {Promise|Object} Promise or JSON object depending on if async or not + */ + readJSONFile: function (directory, filename, sync, cleanPath) { + try { + if (cleanPath == null || cleanPath == true) { + directory = this.filterIllegalPathChars(this.normalizePath(directory)); + filename = this.filterIllegalFilenames(filename); + } else { + directory = this.normalizePath(directory); + } + + if (filename.endsWith('.json')) { + filename = filename.slice(0, -5); + } + let json; + try { + json = sync + ? fs.readJsonSync(path.join(directory, filename + '.json')) + : fs.readJson(path.join(directory, filename + '.json')); + } catch (ex) { + // Note: this only works for sync, not async + Util.logger.debug(ex.stack); + throw new Error(`${ex.code}: ${ex.message}`); + } + return json; + } catch (ex) { + Util.logger.error('File.readJSONFile:: error | ' + ex.message); + } + }, + /** + * reads file from local file system. + * @param {String|String[]} directory directory where the file is stored + * @param {String} filename name of the file without '.json' ending + * @param {String} filetype filetype suffix + * @param {String} [encoding='utf8'] read file with encoding (defaults to utf-8) + * @returns {Promise} file contents + */ + readFile: function (directory, filename, filetype, encoding) { + try { + directory = this.filterIllegalPathChars(this.normalizePath(directory)); + filename = this.filterIllegalFilenames(filename); + return fs.readFile(path.join(directory, filename + '.' + filetype), encoding || 'utf8'); + } catch (ex) { + Util.logger.error('File.readFile:: error | ' + ex.message); + } + }, + /** + * reads directories to a specific depth returning an array + * of file paths to be iterated over + * @example ['deploy/mcdev/bu1'] + * @param {String} directory directory to checkin + * @param {Number} depth how many levels to check (1 base) + * @param {Boolean} [includeStem] include the parent directory in the response + * @param {Number} [_stemLength] set recursively for subfolders. do not set manually! + * @returns {Promise} array of fully defined file paths + */ + readDirectories: async function (directory, depth, includeStem, _stemLength) { + try { + if (!_stemLength) { + // only set this on first iteration + _stemLength = directory.length; + } + const raw = await fs.readdir(directory, { withFileTypes: true }); + let children = []; + for (const dirent of raw) { + const direntPath = path.join(directory, dirent.name); + if ( + fs.existsSync(direntPath) && + fs.lstatSync(direntPath).isDirectory() && + depth > 0 + ) { + const nestedChildren = await this.readDirectories( + direntPath, + depth - 1, + includeStem, + _stemLength + ); + children = children.concat(nestedChildren); + } + } + if (children.length === 0) { + if (includeStem) { + return [directory]; + } else { + // remove base directory and leading slahes and backslashes + return [ + directory.substring(_stemLength).replace(/^\\+/, '').replace(/^\/+/, ''), + ]; + } + } else { + return children; + } + } catch (ex) { + Util.logger.error('File.readDirectories:: error | ' + ex.message); + Util.logger.debug(ex.stack); + } + }, + + /** + * reads directories to a specific depth returning an array + * of file paths to be iterated over using sync api (required in constructors) + * @example ['deploy/mcdev/bu1'] + * @param {String} directory directory to checkin + * @param {Number} [depth] how many levels to check (1 base) + * @param {Boolean} [includeStem] include the parent directory in the response + * @param {Number} [_stemLength] set recursively for subfolders. do not set manually! + * @returns {String[]} array of fully defined file paths + */ + readDirectoriesSync: function (directory, depth, includeStem, _stemLength) { + try { + const children = []; + + if (!_stemLength) { + // only set this on first iteration + _stemLength = directory.length; + } + + // add current directory + if (includeStem) { + children.push(directory); + } else { + // remove base directory and leading slahes and backslashes + const currentPath = directory.substring(_stemLength).replace(path.sep, ''); + children.push(currentPath ? currentPath : '.'); + } + // read all directories + const raw = fs.readdirSync(directory, { withFileTypes: true }); + + // loop through children of current directory (if not then this is skipped) + for (const dirent of raw) { + // if directory found and should get children then recursively call + if (dirent.isDirectory() && depth > 0) { + const nestedChildren = this.readDirectoriesSync( + path.join(directory, dirent.name), + depth - 1, + includeStem, + _stemLength + ); + children.push(...nestedChildren); + } + } + return children; + } catch (ex) { + Util.logger.error('File.readDirectoriesSync:: error | ' + ex.message); + Util.logger.debug(ex.stack); + } + }, + /** + * loads central properties from config file + * @param {Boolean} [silent] omit throwing errors and print messages; assuming not silent if not set + * @returns {Object} central properties object + */ + loadConfigFile(silent) { + let properties; + if (fs.existsSync(Util.configFileName)) { + // properties = JSON.parse(fs.readFileSync(Util.configFileName, 'utf8')); + try { + properties = fs.readJsonSync(Util.configFileName); + } catch (ex) { + Util.logger.error(`${ex.code}: ${ex.message}`); + return; + } + if (fs.existsSync(Util.authFileName)) { + let auth; + try { + auth = fs.readJsonSync(Util.authFileName); + } catch (ex) { + Util.logger.error(`${ex.code}: ${ex.message}`); + return; + } + + if (!auth.credentials) { + const err = `${Util.authFileName} is not set up correctly.`; + Util.logger.error(err); + throw new Error(err); + } + for (const cred in properties.credentials) { + const configset = properties.credentials[cred]; + const authset = auth.credentials[cred]; + if (authset) { + if (configset.eid === authset.eid) { + for (const key in authset) { + configset[key] = authset[key]; + } + } else if (!silent) { + const err = `'${cred}' found in ${Util.configFileName} and ${Util.authFileName} have a Enterprise ID mismatch. Please check.`; + Util.logger.error(err); + throw new Error(err); + } + } else if (!silent) { + Util.logger.warn( + `'${cred}' found in ${Util.configFileName} but not in ${Util.authFileName}. Please run 'mcdev init' to provide the missing credential details.` + ); + } + } + } else if (!silent) { + Util.logger.warn( + `${Util.authFileName} not found. Please run 'mcdev init' to provide the missing credential details.` + ); + } + } + return properties; + }, + /** + * helper that splits the config back into auth & config parts to save them separately + * @param {Object} properties central properties object + * @returns {Promise} - + */ + async saveConfigFile(properties) { + const auth = { credentials: {} }; + const config = properties; + for (const cred in config.credentials) { + auth.credentials[cred] = {}; + // copy id+secret+tenant to auth file + auth.credentials[cred].clientId = config.credentials[cred].clientId; + auth.credentials[cred].clientSecret = config.credentials[cred].clientSecret; + auth.credentials[cred].tenant = config.credentials[cred].tenant; + // copy eid as well to make sure we can test for equality when merging the files during runtime + auth.credentials[cred].eid = config.credentials[cred].eid; + // delete id+secret from config file + delete config.credentials[cred].clientId; + delete config.credentials[cred].clientSecret; + delete config.credentials[cred].tenant; + } + // we want to save to save the full version here to allow us to upgrade configs properly in the future + config.version = packageJson.version; + + await this.writeJSONToFile('', Util.configFileName.split('.json')[0], config); + await this.writeJSONToFile('', Util.authFileName.split('.json')[0], auth); + Util.logger.info(`✔️ ${Util.configFileName} and ${Util.authFileName} saved successfully`); + }, + /** + * Initalises Prettier formatting lib async. + * @param {String} [filetype='html'] filetype ie. JSON or SSJS + * @returns {Promise} success of config load + */ + async initPrettier(filetype) { + if (FileFs.prettierConfig === null) { + filetype = filetype || 'html'; + try { + // pass in project dir with fake index.html to avoid "no parser" error + // by using process.cwd we are limiting ourselves to a config in the project root + // note: overrides will be ignored unless they are for *.html if hand in an html file here. This method includes the overrides corresponding to the file we pass in + FileFs.prettierConfig = await prettier.resolveConfig( + path.join(process.cwd(), 'index.' + filetype) + ); + if (FileFs.prettierConfig === null) { + // set to false to avoid re-running this after an initial failure + throw new Error( + `No .prettierrc found in your project directory. Please run 'mcdev upgrade' to create it` + ); + } + + return true; + } catch (ex) { + FileFs.prettierConfig = false; + Util.logger.error('Cannot apply auto-formatting to your code:' + ex.message); + return false; + } + } else { + return false; + } + }, +}; +const FileFs = { ...fs, ...File }; +FileFs.prettierConfig = null; + +module.exports = FileFs; diff --git a/lib/util/init.config.js b/lib/util/init.config.js new file mode 100644 index 000000000..8bde7ebe0 --- /dev/null +++ b/lib/util/init.config.js @@ -0,0 +1,293 @@ +'use strict'; + +const Cli = require('./cli'); +const File = require('./file'); +const Util = require('./util'); +const inquirer = require('inquirer'); +const path = require('path'); + +/** + * CLI helper class + */ + +const Init = { + /** + * helper method for this.upgradeProject that upgrades project config if needed + * @param {Object} properties config file's json + * @returns {Promise} returns true if worked without errors + */ + async fixMcdevConfig(properties) { + if (!properties) { + // skip if no config exists yet + return true; + } + + let updateConfigNeeded = false; + + const upgradeMsgs = [`Upgrading existing ${Util.configFileName}:`]; + + const missingFields = Util.checkProperties(properties, true); + if (missingFields.length) { + const defaultProps = Util.getDefaultProperties(); + missingFields.forEach((fieldName) => { + switch (fieldName) { + case 'marketList': + if (properties.marketBulk) { + upgradeMsgs.push(`- ✔️ converted 'marketBulk' to '${fieldName}'`); + properties[fieldName] = properties.marketBulk; + delete properties.marketBulk; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'directories.dataExtension': + if (properties.directories.dataextension) { + upgradeMsgs.push( + `- ✔️ converted 'directories.dataextension' to '${fieldName}'` + ); + properties.directories.dataExtension = + properties.directories.dataextension; + delete properties.directories.dataextension; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'metaDataTypes.documentOnRetrieve': + if (!properties.options.documentOnRetrieve) { + properties.metaDataTypes.documentOnRetrieve = []; + } else { + this._updateLeaf(properties, defaultProps, fieldName); + } + delete properties.options.documentOnRetrieve; + upgradeMsgs.push( + `- ✔️ converted 'options.documentOnRetrieve' to '${fieldName}'` + ); + break; + case 'options.deployment.commitHistory': + if (properties.options.commitHistory) { + upgradeMsgs.push( + `- ✔️ converted 'options.commitHistory' to '${fieldName}'` + ); + properties.options.deployment.commitHistory = + properties.options.commitHistory; + delete properties.options.commitHistory; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'options.exclude': + if (properties.options.filter) { + upgradeMsgs.push(`- ✔️ converted 'options.filter' to '${fieldName}'`); + properties.options.exclude = properties.options.filter; + delete properties.options.filter; + } else { + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + this._updateLeaf(properties, defaultProps, fieldName); + } + break; + case 'version': + // do nothing other than ensure we re-save the config (with the new version) + upgradeMsgs.push(`- ✔️ version updated`); + break; + default: + this._updateLeaf(properties, defaultProps, fieldName); + upgradeMsgs.push(`- ✔️ added '${fieldName}'`); + } + }); + updateConfigNeeded = true; + } + + // check if metaDataTypes.retrieve is set to default values and if not, launch selectTypes + const defaultRetrieveArr = Util.getRetrieveTypeChoices(); + let reselectDefaultRetrieve = false; + const toBeRemovedTypes = properties.metaDataTypes.retrieve.filter( + (type) => !defaultRetrieveArr.includes(type) + ); + const toBeAddedTypes = defaultRetrieveArr.filter( + (type) => !properties.metaDataTypes.retrieve.includes(type) + ); + + if (toBeRemovedTypes.length || toBeAddedTypes.length) { + reselectDefaultRetrieve = true; + updateConfigNeeded = true; + } + + // update config + if (updateConfigNeeded) { + for (const msg of upgradeMsgs) { + Util.logger.info(msg); + } + if (reselectDefaultRetrieve) { + // run selectTypes here as it _also_ runs File.saveConfigFile() + Util.logger.warn( + 'Your metaDataTypes.retrieve list is not set to standard values. Resetting config.' + ); + Util.logger.warn(''); + if (toBeAddedTypes.length) { + Util.logger.warn('Adding types:'); + toBeAddedTypes.forEach((type) => { + Util.logger.warn(` - ${type}`); + }); + Util.logger.warn(''); + } + if (toBeRemovedTypes.length) { + Util.logger.warn('Removing types:'); + toBeRemovedTypes.forEach((type) => { + Util.logger.warn(` - ${type}`); + }); + Util.logger.warn(''); + } + await Cli.selectTypes(properties, defaultRetrieveArr); + } else { + // update config if anything else was changed but no re-selection of retrieve-types was triggered + await File.saveConfigFile(properties); + } + } else { + Util.logger.info(`✔️ No problems found in existing ${Util.configFileName}`); + } + + return true; + }, + /** + * handles creation/update of all config file from the boilerplate + * @returns {Promise} status of config file creation + */ + async createIdeConfigFiles() { + Util.logger.info('Checking configuration files (existing files will not be changed):'); + const creationLog = []; + + if (!File.existsSync('deploy/')) { + File.mkdirpSync('deploy/'); + } + + if (!File.existsSync('src/cloudPages')) { + File.mkdirpSync('src/cloudPages'); + } + + // copy in .gitignore (cant be retrieved via npm install directly) + const gitignoreFileName = path.resolve( + __dirname, + Util.boilerplateDirectory, + 'gitignore-template' + ); + if (!File.existsSync(gitignoreFileName)) { + Util.logger.debug(`Dependency file not found in ${gitignoreFileName}`); + return false; + } else { + const fileContent = File.readFileSync(gitignoreFileName, 'utf8'); + creationLog.push( + await this._createIdeConfigFile(['.' + path.sep, '', '.gitignore'], fileContent) + ); + } + + // load file list from boilerplate dir and initiate copy process + const boilerPlateFilesPath = path.resolve(__dirname, Util.boilerplateDirectory, 'files'); + const directories = File.readDirectoriesSync(boilerPlateFilesPath, 10, false); + + for (const subdir of directories) { + // walk thru the root of our boilerplate-files directory and all sub folders + const curDir = path.join(boilerPlateFilesPath, subdir); + for (const file of File.readdirSync(curDir)) { + // read all files in these directories + if (!File.lstatSync(path.join(curDir, file)).isDirectory()) { + // filter entries that are actually folders + const fileArr = file.split('.'); + const ext = '.' + fileArr.pop(); + // awaiting the result here due to interactive optional overwrite + creationLog.push( + await this._createIdeConfigFile([subdir + path.sep, fileArr.join('.'), ext]) + ); + } + } + } + + if (creationLog.includes(false) && creationLog.includes(true)) { + Util.logger.warn('✋ Configuration files creation partially failed.'); + return true; + } else if (creationLog.includes(false)) { + Util.logger.error('❌ Configuration files creation failed.'); + return false; + } else { + Util.logger.info('✔️ Configuration files done.'); + return true; + } + }, + /** + * recursive helper for _fixMcdevConfig that adds missing settings + * @param {Object} propertiersCur current sub-object of project settings + * @param {Object} defaultPropsCur current sub-object of default settings + * @param {String} fieldName dot-concatenated object-path that needs adding + * @returns {void} + */ + _updateLeaf(propertiersCur, defaultPropsCur, fieldName) { + if (fieldName.includes('.')) { + const fieldNameArr = fieldName.split('.'); + const curKey = fieldNameArr[0]; + if (!propertiersCur[curKey]) { + propertiersCur[curKey] = {}; + } + fieldNameArr.splice(0, 1); + this._updateLeaf( + propertiersCur[curKey], + defaultPropsCur[curKey], + fieldNameArr.join('.') + ); + } else { + propertiersCur[fieldName] = defaultPropsCur[fieldName]; + } + }, + /** + * handles creation/update of one config file from the boilerplate at a time + * @param {String[]} fileNameArr 0: path, 1: filename, 2: extension with dot + * @param {String} [fileContent] in case we cannot copy files 1:1 this can be used to pass in content + * @returns {Promise} install successful or error occured + */ + async _createIdeConfigFile(fileNameArr, fileContent) { + const fileName = fileNameArr.join(''); + const boilerplateFileName = path.resolve( + __dirname, + Util.boilerplateDirectory, + 'files', + fileName + ); + if (File.existsSync(fileName)) { + Util.logger.info(`- ✋ ${fileName} already existing`); + const questions = [ + { + type: 'confirm', + name: 'overrideFile', + message: 'Would you like to override it?', + default: false, + }, + ]; + const responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + if (!responses.overrideFile) { + return true; + } + } + fileContent = fileContent || File.readFileSync(boilerplateFileName, 'utf8'); + const saveStatus = await File.writeToFile( + fileNameArr[0], + fileNameArr[1], + fileNameArr[2].substr(1), + fileContent + ); + + if (saveStatus) { + Util.logger.info(`- ✔️ ${fileName} created`); + return true; + } else { + Util.logger.warn(`- ❌ ${fileName} creation failed`); + return false; + } + }, +}; + +module.exports = Init; diff --git a/lib/util/init.git.js b/lib/util/init.git.js new file mode 100644 index 000000000..8015e5a87 --- /dev/null +++ b/lib/util/init.git.js @@ -0,0 +1,279 @@ +'use strict'; + +const File = require('./file'); +const inquirer = require('inquirer'); +const Util = require('./util'); +const commandExists = require('command-exists'); +const git = require('simple-git/promise')(); + +/** + * CLI helper class + */ + +const Init = { + /** + * check if git repo exists and otherwise create one + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.gitRemoteUrl URL of Git remote server + * @returns {Promise<{status:String, repoName:String}>} success flag + */ + async initGitRepo(skipInteraction) { + const result = { status: null, repoName: null }; + // check if git is installed (https://www.npmjs.com/package/command-exists) + if (!commandExists.sync('git')) { + Util.logger.error('Git installation not found.'); + Util.logger.error( + 'Please follow our tutorial on installing Git: https://go.accenture.com/mcdevdocs' + ); + result.status = 'error'; + return result; + } + // 3. test if in git repo + const gitRepoFoundInCWD = File.existsSync('.git'); + let newRepoInitialized = null; + if (gitRepoFoundInCWD) { + Util.logger.info(`✔️ Git repository found`); + newRepoInitialized = false; + } else { + Util.logger.warn('No Git repository found. Initializing git:'); + Util.execSync('git', ['init']); + if (File.existsSync('.git')) { + newRepoInitialized = true; + } else { + Util.logger.error( + 'We detected a problem initializing your Git repository. Please run "git init" manually' + ); + result.status = 'error'; + return result; + } + } + Util.logger.info('Ensuring long file paths are not causing issues with git:'); + try { + Util.execSync('git', ['config', '--local', 'core.longpaths', 'true']); + } catch (ex) { + Util.logger.warn( + `Updating your git config failed. We recommend running the above command manually yourself to avoid issues.` + ); + } + Util.logger.info('Ensuring checkout (git pull) as-is and commit Unix-style line endings:'); + try { + Util.execSync('git', ['config', '--local', 'core.autocrlf', 'input']); + } catch (ex) { + Util.logger.warn( + `Updating your git config failed. We recommend running the above command manually yourself to avoid issues.` + ); + } + + // offer to update local user.name and user.email + await this._updateGitConfigUser(skipInteraction); + if (newRepoInitialized) { + // offer to insert git remote url now + result.repoName = await this._addGitRemote(skipInteraction); + } + + Util.logger.info('✔️ Git initialization done.'); + result.status = newRepoInitialized ? 'init' : 'update'; + return result; + }, + /** + * offer to push the new repo straight to the server + * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @returns {void} + */ + async gitPush(skipInteraction) { + const gitRemotes = (await git.getRemotes(true)).filter((item) => item.name === 'origin'); + if (gitRemotes.length && gitRemotes[0].refs.push) { + // check if remote repo is still empty (otherwise to risky to blindly push) + let remoteBranchesExist; + Util.logger.info('Checking remote Git repository for existing branches...'); + try { + // First, we need to update our local copy of the repo + await git.fetch(); + // Then, we can check how many remote branches 'git fetch' has found + remoteBranchesExist = (await git.branch(['-r'])).all.length > 0; + } catch (ex) { + Util.logger.error('Could not contact remote git server: ' + ex.message); + } + if (remoteBranchesExist === false) { + // offer git push if no remote branches found + Util.logger.info( + `Your remote Git repository is still empty and ready to store your initial backup. Hint: This is the server version of the repo which you share with your team.` + ); + let responses; + if (!skipInteraction) { + const questions = [ + { + type: 'confirm', + name: 'gitPush', + message: `Would you like to 'push' your backup to the remote Git repo?`, + default: true, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + if (skipInteraction || responses.gitPush) { + Util.execSync('git', ['push', '-u', 'origin', 'master']); + } + } else if (remoteBranchesExist === true) { + Util.logger.info( + 'Your remote Git repository already contains data. Please execute a git push manually.' + ); + } + } + }, + /** + * offers to add the git remote origin + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.gitRemoteUrl URL of Git remote server + * @returns {String} repo name (optionally) + */ + async _addGitRemote(skipInteraction) { + // #1 ask if the user wants to do it now + let responses; + if (!skipInteraction) { + const questions = [ + { + type: 'confirm', + name: 'gitOriginKnown', + message: `Do you know the remote/clone URL of your Git repo (starts with ssh:// or http:// and ends on '.git')?`, + default: true, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + if (skipInteraction || responses.gitOriginKnown) { + // #2 if yes, guide the user to input the right url + if (skipInteraction) { + responses = skipInteraction; + } else { + const questions = [ + { + type: 'input', + name: 'gitRemoteUrl', + message: 'Git Remote URL', + validate: (value) => { + value = value.trim(); + if (!value || value.length < 10) { + return 'Please enter a valid remote URL'; + } else if (!value.startsWith('http') && !value.startsWith('ssh')) { + return `Your Git Remote URL should start with 'http' or 'ssh'`; + } else if (!value.endsWith('.git')) { + return `Your Git Remote URL should end with '.git'`; + } else { + // all good + return true; + } + }, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + responses.gitRemoteUrl = responses.gitRemoteUrl.trim(); + Util.execSync('git', ['remote', 'add', 'origin', responses.gitRemoteUrl]); + return responses.gitRemoteUrl.split('/').pop().split('.')[0]; + } + }, + /** + * checks global config and ask to config the user info and then store it locally + * @param {Object|Boolean} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @returns {void} + */ + async _updateGitConfigUser(skipInteraction) { + const gitUser = (await this._getGitConfigUser()) || {}; + Util.logger.info( + `Please confirm your Git user name & email. It should be in the format 'FirstName LastName' and 'your.email@accenture.com'. The current (potentially wrong) values are provided as default. If correct, confirm with ENTER, otherwise please update:` + ); + let responses; + if (skipInteraction) { + responses = { + name: gitUser['user.name'], + email: gitUser['user.email'], + }; + } else { + const questions = [ + { + type: 'input', + name: 'name', + message: 'Git user.name', + default: gitUser['user.name'] || null, + // eslint-disable-next-line require-jsdoc + validate: function (value) { + if ( + !value || + value.trim().length < 4 || + value.includes('"') || + value.includes("'") + ) { + return 'Please enter valid name'; + } + return true; + }, + }, + { + type: 'input', + name: 'email', + message: 'Git user.email', + default: gitUser['user.email'] || null, + // eslint-disable-next-line require-jsdoc + validate: function (value) { + value = value.trim(); + const regex = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + if (!value || !regex.test(String(value).toLowerCase())) { + return 'Please enter valid email'; + } + return true; + }, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + if (responses.name && responses.email) { + // name can contain spaces - wrap it in quotes + const name = `"${responses.name.trim()}"`; + const email = responses.email.trim(); + Util.execSync('git', ['config', '--local', 'user.name', name]); + Util.execSync('git', ['config', '--local', 'user.email', email]); + } + }, + /** + * retrieves the global user.name and user.email values + * @returns {Promise<{'user.name': String, 'user.email': String}>} user.name and user.email + */ + async _getGitConfigUser() { + const gitConfigs = await git.listConfig(); + // remove local config + delete gitConfigs.values['.git/config']; + const result = {}; + + Object.keys(gitConfigs.values).forEach((file) => { + if (gitConfigs.values[file]['user.name']) { + result['user.name'] = gitConfigs.values[file]['user.name']; + } + if (gitConfigs.values[file]['user.email']) { + result['user.email'] = gitConfigs.values[file]['user.email']; + } + }); + if (!result['user.name'] || !result['user.email']) { + return null; + } + return result; + }, +}; + +module.exports = Init; diff --git a/lib/util/init.js b/lib/util/init.js new file mode 100644 index 000000000..4df030193 --- /dev/null +++ b/lib/util/init.js @@ -0,0 +1,282 @@ +'use strict'; + +const Cli = require('./cli'); +const File = require('./file'); +const InitGit = require('./init.git'); +const InitNpm = require('./init.npm'); +const InitConfig = require('./init.config'); +const inquirer = require('inquirer'); +const Util = require('./util'); + +/** + * CLI helper class + */ + +const Init = { + /** + * Creates template file for properties.json + * @param {Object} properties config file's json + * @param {string} credentialsName identifying name of the installed package / project + * @param {Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @param {String} skipInteraction.clientId client id of installed package + * @param {String} skipInteraction.clientSecret client id of installed package + * @param {String} skipInteraction.tenant client id of installed package + * @param {String} skipInteraction.credentialsName how you would like the credential to be named + * @param {String} skipInteraction.gitRemoteUrl URL of Git remote server + * @returns {Promise} - + */ + async initProject(properties, credentialsName, skipInteraction) { + const missingCredentials = this._getMissingCredentials(properties); + if (File.existsSync(Util.configFileName) && properties) { + // config exists + if (credentialsName) { + Util.logger.info(`Updating credential '${credentialsName}'`); + // update-credential mode + if (!properties.credentials[credentialsName]) { + Util.logger.error(`Could not find credential '${credentialsName}'`); + const response = await Cli._selectBU(properties, null, true); + credentialsName = response.credential; + } + let error; + do { + error = false; + try { + const success = await Cli.updateCredential( + properties, + credentialsName, + skipInteraction + ); + if (success) { + Util.logger.info(`✔️ Credential '${credentialsName}' updated.`); + } else { + error = true; + } + } catch (ex) { + error = true; + } + } while (error && !skipInteraction); + Util.logger.debug('reloading config'); + properties = File.loadConfigFile(true); + } else if (missingCredentials.length) { + // forced update-credential mode - user likely cloned repo and is missing mcdev-auth.json + Util.logger.warn( + `We found ${missingCredentials.length} credential${ + missingCredentials.length > 1 ? 's' : '' + } in your ${Util.configFileName} that ${ + missingCredentials.length > 1 ? 'are' : 'is' + } missing details.` + ); + for (const badCredName of missingCredentials) { + let error; + do { + error = false; + try { + const success = await Cli.updateCredential( + properties, + badCredName, + skipInteraction + ); + if (success) { + Util.logger.info(`✔️ Credential '${badCredName}' updated.`); + } else { + error = true; + } + } catch (ex) { + error = true; + } + } while (error); + Util.logger.debug('reloading config'); + properties = File.loadConfigFile(true); + } + Util.logger.info('✔️ All credentials updated.'); + // assume node dependencies are not installed + Util.execSync('npm', ['install']); + Util.logger.info('✔️ Dependencies installed.'); + Util.logger.info('You can now start using Accenture SFMC DevTools.'); + } else if (!missingCredentials.length) { + // add-credential mode + Util.logger.warn(Util.configFileName + ' found in root'); + + let responses; + if (skipInteraction) { + if ( + skipInteraction.clientId && + skipInteraction.clientSecret && + skipInteraction.tenant && + skipInteraction.credentialsName + ) { + // assume automated input; only option here is to add a new credential + // requires skipInteraction=={clientId,clientSecret,tenant,credentialsName} + // will be checked inside of Cli.addExtraCredential() + Util.logger.info('Adding another credential'); + } else { + throw new Error( + '--skipInteraction flag found but missing required input for clientId,clientSecret,tenant,credentialsName' + ); + } + } else { + const questions = [ + { + type: 'confirm', + name: 'isAddCredential', + message: 'Do you want to add another credential instead?', + default: false, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + let credentialName; + if (skipInteraction || responses.isAddCredential) { + credentialName = await Cli.addExtraCredential(properties, skipInteraction); + } + if (credentialName) { + await this._downloadAllBUs(`${credentialName}/*`, 'update', skipInteraction); + } + } + } else { + // config does not exist + // assuming it's the first time this command is run for this project + + // initialize git repo + const initGit = await InitGit.initGitRepo(skipInteraction); + if (initGit.status === 'error') { + return; + } + + // set up IDE files and load npm dependencies + let status = await this.upgradeProject(properties, true, initGit.repoName); + if (!status) { + return; + } + + // ask for credentials and create mcdev config + status = await Cli.initMcdevConfig(skipInteraction); + if (!status) { + return; + } + + // create first commit to backup the project configuration + if (initGit.status === 'init') { + Util.logger.info(`Committing initial setup to Git:`); + Util.execSync('git', ['add', '.']); + Util.execSync('git', ['commit', '-m', '"Initial commit"', '--quiet']); + Util.logger.info(`✔️ Configuration committed`); + } + + // do initial retrieve * + await this._downloadAllBUs('"*"', initGit.status); + + // backup to server + await InitGit.gitPush(skipInteraction); + + // all done + Util.logger.info('You are now ready to work with Accenture SFMC DevTools!'); + Util.logger.warn( + 'If you use VSCode, please restart it now to install recommended extensions.' + ); + } + }, + /** + * helper for this.initProject() + * @param {String} bu cred/bu or cred/* or * + * @param {String} gitStatus signals what state the git repo is in + * @param {Boolean|Object} [skipInteraction] signals what to insert automatically for things usually asked via wizard + * @returns {Promise} - + */ + async _downloadAllBUs(bu, gitStatus, skipInteraction) { + let responses; + if (!skipInteraction) { + const questions = [ + { + type: 'confirm', + name: 'initialRetrieveAll', + message: 'Do you want to start downloading all Business Units (recommended)?', + default: true, + }, + ]; + responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + } + if (skipInteraction || responses.initialRetrieveAll) { + Util.execSync('mcdev', ['retrieve', bu]); + + if (gitStatus === 'init') { + Util.logger.info(`Committing first backup of your SFMC instance:`); + Util.execSync('git', ['add', '.']); + Util.execSync('git', ['commit', '-m', '"First instance backup"', '--quiet']); + Util.logger.info(`✔️ SFMC instance backed up`); + } else if (gitStatus === 'update') { + Util.logger.warn( + 'Please manually commit this backup according to your projects guidelines.' + ); + // TODO create guided commit: + // 1. ask if commit with all changes shall be created + // 2. ask if that should be done to current branch (show which one we are on) or a new branch + // a. if new: ask off of which we shall branch off of (show list) and then auto-create new branch and switch to it + // 3. create commit + } + } + }, + /** + * wrapper around npm dependency & configuration file setup + * @param {Object} properties config file's json + * @param {Boolean} [initial] print message if not part of initial setup + * @param {String} [repoName] if git URL was provided earlier, the repo name was extracted to use it for npm init + * @returns {Promise} success flag + */ + async upgradeProject(properties, initial, repoName) { + let status; + + if (!initial) { + Util.logger.info( + 'Upgrading project with newest configuration, npm dependencies & other project configurations:' + ); + + // ensure an existing config is up to current specs + status = await InitConfig.fixMcdevConfig(properties); + if (!status) { + return false; + } + } + + // create files before installing dependencies to ensure .gitignore is properly set up + status = await InitConfig.createIdeConfigFiles(); + if (!status) { + return false; + } + + // install node dependencies + status = await InitNpm.installDependencies(repoName); + if (!status) { + return false; + } + + return true; + }, + /** + * finds credentials that are set up in config but not in auth file + * @param {object} properties javascript object in .mcdevrc.json + * @returns {String[]} list of credential names + */ + _getMissingCredentials(properties) { + let missingCredentials; + if (properties && properties.credentials) { + missingCredentials = Object.keys(properties.credentials).filter( + (cred) => + !properties.credentials[cred].clientId || + !properties.credentials[cred].clientSecret || + !properties.credentials[cred].tenant + ); + } + return missingCredentials || []; + }, +}; + +module.exports = Init; diff --git a/lib/util/init.npm.js b/lib/util/init.npm.js new file mode 100644 index 000000000..740f203cc --- /dev/null +++ b/lib/util/init.npm.js @@ -0,0 +1,148 @@ +'use strict'; + +const File = require('./file'); +const inquirer = require('inquirer'); +const path = require('path'); +const Util = require('./util'); + +/** + * CLI helper class + */ + +const Init = { + /** + * initiates npm project and then + * takes care of loading the pre-configured dependency list + * from the boilerplate directory to them as dev-dependencies + * @param {String} [repoName] if git URL was provided earlier, the repo name was extracted to use it for npm init + * @returns {Promise} install successful or error occured + */ + async installDependencies(repoName) { + let fileContent; + let projectPackageJson; + if (File.existsSync('package.json')) { + try { + fileContent = File.readFileSync('package.json', 'utf8'); + } catch (ex) { + Util.logger.error( + 'Your package.json was found but seems to be corrupted: ' + ex.message + ); + } + if (fileContent) { + projectPackageJson = JSON.parse(fileContent); + this._getDefaultPackageJson(projectPackageJson); + await File.writeJSON('./package.json', projectPackageJson, { spaces: 2 }); + Util.logger.info('✔️ package.json found'); + } + } else { + Util.logger.warn('No package.json found. Initializing node project:'); + // make sure the npm project name is compliant with npm rules + const currentFolderName = path.basename(path.resolve()); + const standardNpmName = + repoName || + currentFolderName + .toLowerCase() + .replace(/[^a-z0-9 ]/gi, '') + .replace(/ /gi, '-'); + projectPackageJson = { name: standardNpmName }; + this._getDefaultPackageJson(projectPackageJson); + await File.writeToFile('./', 'package', 'json', JSON.stringify(projectPackageJson)); + // execute "no questions asked" npm init + Util.execSync('npm', ['init', '--yes']); + try { + fileContent = File.readFileSync('package.json', 'utf8'); + if (fileContent) { + projectPackageJson = JSON.parse(fileContent); + } + Util.logger.info('✔️ package.json created'); + } catch (ex) { + Util.logger.error('No package.json found. Please run "npm init" manually'); + return false; + } + } + + // ensure npm dependencies are loaded + const dependencyFile = path.resolve( + __dirname, + Util.boilerplateDirectory, + 'npm-dependencies.json' + ); + if (!File.existsSync(dependencyFile)) { + Util.logger.debug(`Dependency file not found in ${dependencyFile}`); + return false; + } + const defaultDependencies = File.readJsonSync(dependencyFile); + + const loadDependencies = defaultDependencies.filter( + (name) => + !projectPackageJson || + !projectPackageJson.devDependencies || + !projectPackageJson.devDependencies[name] + ); + if (loadDependencies.length < defaultDependencies.length) { + Util.logger.info( + `✔️ ${ + !loadDependencies.length ? 'All' : 'Some' + } default dependencies are already installed: ` + defaultDependencies.join(', ') + ); + const questions = [ + { + type: 'confirm', + name: 'runUpdate', + message: 'Would you like to attempt updating them?', + default: true, + }, + ]; + const responses = await new Promise((resolve) => { + inquirer.prompt(questions).then((answers) => { + resolve(answers); + }); + }); + if (responses.runUpdate) { + loadDependencies.length = 0; + loadDependencies.push(...defaultDependencies); + } + } + if (loadDependencies.length) { + Util.logger.info('Installing Dependencies:'); + const args = ['install', '--save-dev'].concat(loadDependencies); + + Util.execSync('npm', args); + Util.logger.info('✔️ Dependencies installed.'); + } + return true; + }, + /** + * ensure we have certain default values in our config + * @param {Object} [currentContent] what was read from existing package.json file + * @returns {Promise<{script: Object, author: String, license: String}>} extended currentContent + */ + async _getDefaultPackageJson(currentContent) { + currentContent = currentContent || {}; + // #1 scripts + const predefinedCommandList = { + build: 'sfmc-build all', + 'build-cp': 'sfmc-build cloudPages', + 'build-email': 'sfmc-build emails', + upgrade: 'npm-check --update', + 'eslint-check': 'eslint', + }; + if (!currentContent.scripts) { + currentContent.scripts = {}; + } + for (const key in predefinedCommandList) { + currentContent.scripts[key] = predefinedCommandList[key]; + } + // #2 Author + if (!currentContent.author || currentContent.author === '') { + currentContent.author = 'Accenture'; + } + // #3 License + if (!currentContent.license || currentContent.license === 'ISC') { + currentContent.license = 'UNLICENSED'; + } + return currentContent; + }, +}; + +module.exports = Init; diff --git a/lib/util/util.js b/lib/util/util.js new file mode 100644 index 000000000..bfc9fce7c --- /dev/null +++ b/lib/util/util.js @@ -0,0 +1,779 @@ +'use strict'; + +/** + * @ignore @typedef {import('sfmc-fuelsdk-node')} ET_Client + * + * @typedef {Object} BuObject + * @property {String} clientId installed package client id + * @property {String} clientSecret installed package client secret + * @property {String} tenant subdomain part of Authentication Base Uri + * @property {String} [eid] Enterprise ID = MID of the parent BU + * @property {String} [mid] MID of the BU to work with + * @property {String} [businessUnit] name of the BU to interact with + * @property {String} [credential] name of the credential to interact with + */ +/** + * @typedef {Object.} TemplateMap + */ + +/** @type ET_Client */ +const ET_Client = require('sfmc-fuelsdk-node'); +const fs = require('fs-extra'); // ! do not switch to util/file.js to avoid circular dependency +const MetadataDefinitions = require('./../MetadataTypeDefinitions'); +const packageJsonMcdev = require('../../package.json'); +const path = require('path'); +const toposort = require('toposort'); +const winston = require('winston'); +const inquirer = require('inquirer'); +const child_process = require('child_process'); +const semver = require('semver'); + +/** + * Util that contains logger and simple util methods + */ +const Util = { + authFileName: '.mcdev-auth.json', + boilerplateDirectory: '../../boilerplate', + configFileName: '.mcdevrc.json', + parentBuName: '_ParentBU_', + standardizedSplitChar: '/', + expectedAuthScope: [ + 'accounts_read', + 'accounts_write', + 'approvals_read', + 'approvals_write', + 'audiences_read', + 'audiences_write', + 'automations_execute', + 'automations_read', + 'automations_write', + 'calendar_read', + 'calendar_write', + 'campaign_read', + 'campaign_write', + 'data_extensions_read', + 'data_extensions_write', + 'documents_and_images_read', + 'documents_and_images_write', + 'email_read', + 'email_send', + 'email_write', + 'event_notification_callback_create', + 'event_notification_callback_delete', + 'event_notification_callback_read', + 'event_notification_callback_update', + 'event_notification_subscription_create', + 'event_notification_subscription_delete', + 'event_notification_subscription_read', + 'event_notification_subscription_update', + 'file_locations_read', + 'file_locations_write', + 'journeys_execute', + 'journeys_read', + 'journeys_write', + 'key_manage_revoke', + 'key_manage_rotate', + 'key_manage_view', + 'list_and_subscribers_read', + 'list_and_subscribers_write', + 'marketing_cloud_connect_read', + 'marketing_cloud_connect_send', + 'marketing_cloud_connect_write', + 'offline', + 'ott_channels_read', + 'ott_channels_write', + 'ott_chat_messaging_read', + 'ott_chat_messaging_send', + 'push_read', + 'push_send', + 'push_write', + 'saved_content_read', + 'saved_content_write', + 'sms_read', + 'sms_send', + 'sms_write', + 'social_post', + 'social_publish', + 'social_read', + 'social_write', + 'tags_read', + 'tags_write', + 'tracking_events_read', + 'tracking_events_write', + 'users_read', + 'users_write', + 'web_publish', + 'web_read', + 'web_write', + 'webhooks_read', + 'webhooks_write', + 'workflows_read', + 'workflows_write', + ], + /** + * defines how the properties.json should look like + * used for creating a template and for checking if variables are set + * + * @returns {object} default properties + */ + getDefaultProperties: function () { + const configFileName = path.resolve(__dirname, this.boilerplateDirectory, 'config.json'); + if (!fs.existsSync(configFileName)) { + this.logger.debug(`Default config file file not found in ${configFileName}`); + return false; + } + const defaultProperties = fs.readJsonSync(configFileName); + // set default name for parent BU + defaultProperties.credentials.default.businessUnits[this.parentBuName] = '000000000'; + // set default retrieve values + defaultProperties.metaDataTypes.retrieve = this.getRetrieveTypeChoices(); + + return defaultProperties; + }, + /** + * helper for getDefaultProperties() + * @returns {string[]} type choices + */ + getRetrieveTypeChoices() { + const typeChoices = []; + for (const el in MetadataDefinitions) { + if ( + Array.isArray(MetadataDefinitions[el].typeRetrieveByDefault) || + MetadataDefinitions[el].typeRetrieveByDefault === true + ) { + // complex types like assets are saved as array but to ease upgradability we + // save the main type only unless the user deviates from our pre-selection. + // Types that dont have subtypes set this field to true or false + typeChoices.push(MetadataDefinitions[el].type); + } + } + + typeChoices.sort((a, b) => { + if (a.toLowerCase() < b.toLowerCase()) { + return -1; + } + if (a.toLowerCase() > b.toLowerCase()) { + return 1; + } + return 0; + }); + + return typeChoices; + }, + /** + * check if the config file is correctly formatted and has values + * + * @param {object} properties javascript object in .mcdevrc.json + * @param {boolean} [silent] set to true for internal use w/o cli output + * @returns {boolean|String[]} file structure ok OR list of fields to be fixed + */ + checkProperties: function (properties, silent) { + if (!fs.existsSync(Util.configFileName) || !properties) { + Util.logger.error(`\nCould not find ${Util.configFileName} in ${process.cwd()}.`); + Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); + return false; + } + if (!fs.existsSync(Util.authFileName) || !properties) { + Util.logger.error(`\nCould not find ${Util.authFileName} in ${process.cwd()}.`); + Util.logger.error(`Run 'mcdev init' to initialize your project.\n`); + return false; + } + + // check if user is running older mcdev version than whats saved to the config + if (semver.gt(properties.version, packageJsonMcdev.version)) { + Util.logger.error( + `Your Catalst version ${packageJsonMcdev.version} is lower than your project's config version ${properties.version}` + ); + const questions = [ + { + type: 'confirm', + name: 'runUpgradeNow', + message: `Do you want to run 'npm update -g mcdev' now? This may take a few minutes.`, + default: true, + }, + ]; + inquirer.prompt(questions).then((responses) => { + if (responses.runUpgradeNow) { + // use _execSync here to avoid a circular dependency + this.execSync('npm', ['update', '-g', 'mcdev']); + } + }); + return false; + } + + // check config properties + const defaultProps = this.getDefaultProperties(); + const errorMsgs = []; + const solutionSet = new Set(); + const missingFields = []; + for (const key in defaultProps) { + if (Object.prototype.hasOwnProperty.call(defaultProps, key)) { + if (!Object.prototype.hasOwnProperty.call(properties, key)) { + errorMsgs.push(`${key}{} missing`); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(key); + } else { + if (!silent && key === 'credentials') { + if (!Object.keys(properties.credentials)) { + errorMsgs.push(`no Credential defined`); + } else { + for (const cred in properties.credentials) { + if (cred.includes('/') || cred.includes('\\')) { + errorMsgs.push( + `Credential names may not includes slashes: ${cred}` + ); + solutionSet.add('Apply manual fix in your config.'); + } + if ( + !properties.credentials[cred].clientId || + properties.credentials[cred].clientId === '--- update me ---' + ) { + errorMsgs.push(`invalid ClientId on ${cred}`); + solutionSet.add(`Run 'mcdev init ${cred}'`); + } + if ( + !properties.credentials[cred].clientSecret || + properties.credentials[cred].clientSecret === + '--- update me ---' + ) { + errorMsgs.push(`invalid ClientSecret on ${cred}`); + solutionSet.add(`Run 'mcdev init ${cred}'`); + } + if ( + !properties.credentials[cred].tenant || + properties.credentials[cred].tenant === '--- update me ---' + ) { + errorMsgs.push(`invalid tenant on ${cred}`); + solutionSet.add(`Run 'mcdev init ${cred}'`); + } + if ( + !properties.credentials[cred].eid || + properties.credentials[cred].eid === '000000000' + ) { + errorMsgs.push(`invalid eid on ${cred}`); + solutionSet.add(`Run 'mcdev init ${cred}'`); + } + let i = 0; + for (const buName in properties.credentials[cred].businessUnits) { + if (buName.includes('/') || buName.includes('\\')) { + errorMsgs.push( + `Business Unit names may not includes slashes: ${cred}: ${buName}` + ); + solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); + } + if ( + Object.prototype.hasOwnProperty.call( + properties.credentials[cred].businessUnits, + buName + ) && + properties.credentials[cred].businessUnits[buName] !== + '000000000' + ) { + i++; + } + } + if (!i) { + errorMsgs.push(`no Business Units defined`); + solutionSet.add(`Run 'mcdev reloadBUs ${cred}'`); + } + } + } + } else if (['directories', 'metaDataTypes', 'options'].includes(key)) { + for (const subkey in defaultProps[key]) { + if ( + Object.prototype.hasOwnProperty.call(defaultProps[key], subkey) && + !Object.prototype.hasOwnProperty.call(properties[key], subkey) + ) { + errorMsgs.push( + `${key}.${subkey} missing. Default value (${ + Array.isArray(defaultProps[key][subkey]) + ? 'Array' + : typeof defaultProps[key][subkey] + }): ${defaultProps[key][subkey]}` + ); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(`${key}.${subkey}`); + } else if (subkey === 'deployment') { + for (const dkey in defaultProps[key][subkey]) { + if ( + Object.prototype.hasOwnProperty.call( + defaultProps[key][subkey], + dkey + ) && + !Object.prototype.hasOwnProperty.call( + properties[key][subkey], + dkey + ) + ) { + errorMsgs.push( + `${key}.${subkey} missing. Default value (${ + Array.isArray(defaultProps[key][subkey][dkey]) + ? 'Array' + : typeof defaultProps[key][subkey][dkey] + }): ${defaultProps[key][subkey][dkey]}` + ); + solutionSet.add( + `Run 'mcdev upgrade' to fix missing or changed configuration options` + ); + missingFields.push(`${key}.${subkey}.${dkey}`); + } + } + } + } + } + } + } + } + // check if project config version is outdated compared to user's mcdev version + if (semver.gt(packageJsonMcdev.version, properties.version)) { + errorMsgs.push( + `Your project's config version ${properties.version} is lower than your Accenture SFMC DevTools version ${packageJsonMcdev.version}` + ); + solutionSet.add(`Run 'mcdev upgrade' to ensure optimal performance`); + missingFields.push('version'); + } + if (silent) { + return missingFields; + } else { + if (errorMsgs.length) { + const errorMsgOutput = [ + `Found problems in your ./${Util.configFileName} that you need to fix first:`, + ]; + for (const msg of errorMsgs) { + errorMsgOutput.push(' - ' + msg); + } + Util.logger.error(errorMsgOutput.join('\n')); + Util.logger.info( + [ + 'Here is what you can do to fix these issues:', + ...Array.from(solutionSet), + ].join('\n- ') + ); + const questions = [ + { + type: 'confirm', + name: 'runUpgradeNow', + message: `Do you want to run 'mcdev upgrade' now?`, + default: true, + }, + ]; + inquirer.prompt(questions).then((responses) => { + if (responses.runUpgradeNow) { + // use _execSync here to avoid a circular dependency + this.execSync('mcdev', ['upgrade']); + } + }); + + return false; + } else { + return true; + } + } + }, + loggerTransports: null, + /** + * Logger that creates timestamped log file in 'logs/' directory + */ + logger: null, + restartLogger: startLogger, + /** + * Logger helper for Metadata functions + * @param {String} level of log (error, info, warn) + * @param {String} type of metadata being referenced + * @param {String} method name which log was called from + * @param {*} payload generic object which details the error + * @param {String} [source] key/id of metadata which relates to error + * @returns {void} + */ + metadataLogger: function (level, type, method, payload, source) { + let prependSource = ''; + if (source) { + prependSource = source + ' - '; + } + if (payload instanceof Error) { + // extract error message + Util.logger[level](`${type}.${method}: ${prependSource}${payload.message}`); + } else if (typeof payload === 'string') { + // print out simple string + Util.logger[level](`${type} ${method}: ${prependSource}${payload}`); + } else { + // Print out JSON String as default. + Util.logger[level](`${type}.${method}: ${prependSource}${JSON.stringify(payload)}`); + } + }, + /** + * replaces values in a JSON object string, based on a series of + * key-value pairs (obj) + * @param {String|Object} str JSON object or its stringified version, which has values to be replaced + * @param {TemplateMap} obj key value object which contains keys to be replaced and values to be replaced with + * @returns {String|Object} replaced version of str + */ + replaceByObject: function (str, obj) { + let convertType = false; + if ('string' !== typeof str) { + convertType = true; + str = JSON.stringify(str); + } + // sort by value length + const sortable = []; + for (const key in obj) { + // only push in value if not null + if (obj[key]) { + sortable.push([key, obj[key]]); + } + } + + sortable.sort(function (a, b) { + return b[1].length - a[1].length; + }); + for (const pair of sortable) { + const escVal = pair[1].replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); + const regString = new RegExp(escVal, 'g'); + str = str.replace(regString, '{{{' + pair[0] + '}}}'); + } + if (convertType) { + str = JSON.parse(str); + } + return str; + }, + /** + * get key of an object based on the first matching value + * @param {Object} objs object of objects to be searched + * @param {String} val value to be searched for + * @returns {String} key + */ + inverseGet: function (objs, val) { + for (const obj in objs) { + if (objs[obj] === val) { + return obj; + } + } + throw new Error(`${val} not found in object`); + }, + + /** + * Returns Order in which metadata needs to be retrieved/deployed + * @param {String[]} metadataTypes which should be retrieved/deployed + * @returns {String[]} retrieve/deploy order as array + */ + getMetadataHierachy(metadataTypes) { + const dependencies = []; + // loop through all metadata types which are being retrieved/deployed + const subTypeDeps = {}; + for (const metadataType of metadataTypes) { + const type = metadataType.split('-')[0]; + // if they have dependencies then add a dependency pair for each type + if (MetadataDefinitions[type].dependencies.length > 0) { + dependencies.push( + ...MetadataDefinitions[type].dependencies.map((dep) => { + if (dep.includes('-')) { + // log subtypes to be able to replace them if main type is also present + subTypeDeps[dep.split('-')[0]] = + subTypeDeps[dep.split('-')[0]] || new Set(); + subTypeDeps[dep.split('-')[0]].add(dep); + } + return [dep, metadataType]; + }) + ); + } + // if they have no dependencies then just add them with undefined. + else { + dependencies.push([undefined, metadataType]); + } + } + // remove subtypes if main type is in the list + Object.keys(subTypeDeps) + // only look at subtype deps that are also supposed to be retrieved fully + .filter((type) => metadataTypes.includes(type)) + // Rewrite the subtype dependecies to main types. + .forEach((type) => { + // convert set into array to walk its elements + [...subTypeDeps[type]].forEach((subType) => { + dependencies.forEach((item) => { + if (item[0] === subType) { + // if subtype recognized, replace with main type + item[0] = type; + } + }); + }); + }); + + // sort list & remove the undefined dependencies + return toposort(dependencies).filter((a) => !!a); + }, + /** + * signs in with SFMC + * + * @param {BuObject} buObject properties for auth + * @returns {Promise} auth object + */ + async getETClient(buObject) { + /** @type ET_Client */ + const myClient = new ET_Client(buObject.clientId, buObject.clientSecret, null, { + authOptions: { + authVersion: 2, + accountId: buObject.mid, + }, + globalReqOptions: {}, + authOrigin: 'https://' + buObject.tenant + '.auth.marketingcloudapis.com', + origin: null, + soapOrigin: null, + }); + try { + // check credentials to allow clear log output and stop execution + const test = await myClient.FuelAuthClient.getAccessToken(); + if (test.error) { + throw new Error(test.error_description); + } else if (test.scope) { + // find missing rights + const currentScope = test.scope.split(' '); + const missingAccess = Util.expectedAuthScope.filter( + (element) => !currentScope.includes(element) + ); + const excessAccess = currentScope.filter( + (element) => !Util.expectedAuthScope.includes(element) + ); + if (excessAccess.length) { + Util.logger.debug('Extra access found:' + excessAccess.join(', ')); + } + if (missingAccess.length) { + Util.logger.warn( + 'Installed package has insufficient access. You might encounter malfunctions!' + ); + Util.logger.warn('Missing scope: ' + missingAccess.join(', ')); + } + } + } catch (ex) { + throw new Error(ex.message); + } + return myClient; + }, + + /** + * standardized method for getting data from cache. + * + * @param {Object} cache data retrieved from sfmc instance + * @param {String} metadataType metadata type ie. query + * @param {String} searchValue unique identifier of metadata being looked for + * @param {String} searchField field name (key in object) which contains the unique identifer + * @param {String} returnField field which should be returned + * @returns {String} unique user definable metadata key (usually external/customer key) + */ + getFromCache(cache, metadataType, searchValue, searchField, returnField) { + for (const key in cache[metadataType]) { + if (Util.resolveObjPath(searchField, cache[metadataType][key]) == searchValue) { + try { + if (Util.resolveObjPath(returnField, cache[metadataType][key])) { + return Util.resolveObjPath(returnField, cache[metadataType][key]); + } else { + throw new Error(); + } + } catch (ex) { + throw new Error( + `${metadataType} with ${searchField} '${searchValue}' does not have field '${returnField}'` + ); + } + } + } + throw new Error( + `Missing one or more dependent metadata. ${metadataType} with ${searchField}='${searchValue}' was not found. Please ensure to create this first or include it in the deployment package.` + ); + }, + /** + * let's you dynamically walk down an object and get a value + * @param {String} path 'fieldA.fieldB.fieldC' + * @param {Object} obj some parent object + * @returns {any} value of obj.path + */ + resolveObjPath(path, obj) { + return path.split('.').reduce(function (prev, curr) { + return prev ? prev[curr] : null; + }, obj); + }, + /** + * standardized method for getting data from cache - adapted for special case of lists + * ! keeping this in util/util.js rather than in metadataTypes/List.js to avoid potential circular dependencies + * + * @param {Object} cache data retrieved from sfmc instance + * @param {String} listPathName folderPath/ListName combo of list + * @param {String} returnField ObjectID or ID + * @returns {String} unique ObjectId of list + */ + getListObjectIdFromCache(cache, listPathName, returnField) { + let folderPath = listPathName.split('/'); + const listName = folderPath.pop(); + folderPath = folderPath.join('/'); + for (const key in cache['list']) { + if ( + cache['list'][key].ListName === listName && + cache['list'][key].r__folder_Path === folderPath + ) { + try { + if (cache['list'][key][returnField]) { + return cache['list'][key][returnField]; + } else { + throw new Error(); + } + } catch (ex) { + throw new Error( + `${'list'} with ListName='${listName}' and r__folder_Path='${folderPath}' does not have field '${returnField}'` + ); + } + } + } + throw new Error( + `Missing one or more dependent metadata. list with ListName='${listName}' and r__folder_Path='${folderPath}' was not found. Please ensure to create this first or include it in the deployment package.` + ); + }, + + /** + * standardized method for getting data from cache - adapted for special case of lists + * ! keeping this in util/util.js rather than in metadataTypes/List.js to avoid potential circular dependencies + * + * @param {Object} cache data retrieved from sfmc instance + * @param {String} searchValue unique identifier of metadata being looked for + * @param {String} searchField ObjectID or ID + * @returns {String} unique folderPath/ListName combo of list + */ + getListPathNameFromCache(cache, searchValue, searchField) { + const returnField1 = 'r__folder_Path'; + const returnField2 = 'ListName'; + for (const key in cache['list']) { + if (cache['list'][key][searchField] === searchValue) { + try { + if (cache['list'][key][returnField1] && cache['list'][key][returnField2]) { + return ( + cache['list'][key][returnField1] + + '/' + + cache['list'][key][returnField2] + ); + } else { + throw new Error(); + } + } catch (ex) { + throw new Error( + `${'list'} with ${searchField}='${searchValue}' does not have the fields ${returnField1} and ${returnField2}` + ); + } + } + } + throw new Error( + `Missing one or more dependent metadata. list with ${searchField}='${searchValue}' was not found. Please ensure to create this first or include it in the deployment package.` + ); + }, + /** + * retry on network issues + * @param {String} errorMsg what to print behind "Connection error. " + * @param {Function} callback what to try executing + * @param {Boolean} [silentError=false] prints retry messages to log only; default=false + * @param {Number} [retries=1] number of retries; default=1 + * @returns {Promise} - + */ + async retryOnError(errorMsg, callback, silentError, retries) { + if ('undefined' === typeof retries || retries === null) { + retries = 1; + } + try { + await callback(); + } catch (ex) { + if ( + retries > 0 && + ex.code && + ['ETIMEDOUT', 'EHOSTUNREACH', 'ENOTFOUND', 'ECONNRESET'].includes(ex.code) + ) { + retries--; + if (silentError) { + Util.logger.debug( + `Connection error. ${errorMsg} - Retries left: ${retries + 1}` + ); + } else { + Util.logger.error(`Connection error. ${errorMsg}`); + } + Util.logger.debug(ex.stack); + await this.retryOnError(errorMsg, callback, retries); + } else if ( + ex.code && + ['ETIMEDOUT', 'EHOSTUNREACH', 'ENOTFOUND', 'ECONNRESET'].includes(ex.code) + ) { + Util.logger.error( + `Failed due to a Connection Error (${ex.code}) - Please check your network connection and try again` + ); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + throw new Error(ex.message); + } else { + Util.logger.error(ex.message); + Util.logger.debug(ex.stack); + if (Util.logger.level === 'debug') { + console.log(ex.stack); + } + throw new Error(ex.message); + } + } + }, + /** + * helper to run other commands as if run manually by user + * @param {string} cmd to be executed command + * @param {string[]} [args] list of arguments + * @returns {undefined} + */ + execSync(cmd, args) { + args = args || []; + Util.logger.info('⚡ ' + cmd + ' ' + args.join(' ')); + + // the following options ensure the user sees the same output and + // interaction options as if the command was manually run + const options = { stdio: [0, 1, 2] }; + return child_process.execSync(cmd + ' ' + args.join(' '), options); + }, +}; +/** + * wrapper around our standard winston logging to console and logfile + * @returns {object} initiated logger for console and file + */ +function createNewLoggerTransport() { + return { + console: new winston.transports.Console({ + // Write logs to Console + level: 'info', + format: winston.format.combine( + winston.format.colorize(), + winston.format.timestamp({ format: 'HH:mm:ss' }), + winston.format.simple(), + winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`) + ), + }), + file: new winston.transports.File({ + // Write logs to logfile + filename: 'logs/' + new Date().toISOString().split(':').join('.') + '.log', + // filename: 'logs/' + Math.floor(Date.now() / 1000) + '.log', + level: 'debug', + format: winston.format.combine( + winston.format.timestamp({ format: 'HH:mm:ss.SSS' }), + winston.format.simple(), + winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`) + ), + }), + }; +} +/** + * initiate winston logger + * @returns {void} + */ +function startLogger() { + Util.loggerTransports = createNewLoggerTransport(); + Util.logger = winston.createLogger({ + levels: winston.config.npm.levels, + transports: [Util.loggerTransports.console, Util.loggerTransports.file], + }); + Util.logger.debug(`:: mcdev ${packageJsonMcdev.version} ::`); +} +startLogger(); + +module.exports = Util; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..00ec46f57 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,15724 @@ +{ + "name": "mcdev", + "version": "3.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "mcdev", + "version": "3.0.0", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bluebird": "3.7.2", + "cli-progress": "3.9.0", + "command-exists": "1.2.9", + "console.table": "0.10.0", + "fs-extra": "9.1.0", + "inquirer": "8.0.0", + "json-to-table": "4.2.1", + "mustache": "4.1.0", + "prettier": "2.2.1", + "semver": "5.7.1", + "sfmc-fuelsdk-node": "2.4.0", + "simple-git": "2.37.0", + "sql-formatter-plus": "1.3.6", + "toposort": "2.0.2", + "update-notifier-git": "5.0.3", + "winston": "3.3.3", + "yargs": "16.2.0" + }, + "bin": { + "mcdev": "lib/index.js" + }, + "devDependencies": { + "chai": "4.3.4", + "eslint": "7.22.0", + "eslint-config-prettier": "8.1.0", + "eslint-config-ssjs": "1.1.11", + "eslint-plugin-mocha": "8.1.0", + "eslint-plugin-prettier": "3.3.1", + "husky": "5.1.3", + "jsdoc-to-markdown": "7.0.0", + "lint-staged": "10.5.4", + "mocha": "8.3.2", + "npm-check": "5.9.2", + "npm-run-all": "4.1.5" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "type": "corporate", + "url": "https://alm.accenture.com/wiki/display/CFSCL/SFMC+Catalyst" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/generator": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz", + "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.9.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + } + }, + "node_modules/@babel/generator/node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "dev": true, + "dependencies": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" + } + }, + "node_modules/@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.8.3" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.8.3" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "node_modules/@babel/highlight": { + "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, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/@babel/highlight/node_modules/@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 + }, + "node_modules/@babel/parser": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", + "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/polyfill": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.1.tgz", + "integrity": "sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g==", + "dependencies": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + } + }, + "node_modules/@babel/polyfill/node_modules/regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + }, + "node_modules/@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + } + }, + "node_modules/@babel/template/node_modules/@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz", + "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.5", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", + "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "dev": true, + "dependencies": { + "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", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/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==", + "dev": true + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "dependencies": { + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "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, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, + "node_modules/@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@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==", + "dependencies": { + "defer-to-connect": "^1.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "node_modules/@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 + }, + "node_modules/acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "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, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/aggregate-error/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dependencies": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dependencies": { + "string-width": "^3.0.0" + } + }, + "node_modules/ansi-colors": { + "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, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escape-sequences": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", + "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/ansi-escape-sequences/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "dependencies": { + "type-fest": "^0.11.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", + "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/array-back": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", + "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "dependencies": { + "array-uniq": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", + "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "node_modules/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==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "node_modules/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=", + "dev": true, + "dependencies": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "node_modules/boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/boxen/node_modules/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==" + }, + "node_modules/boxen/node_modules/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==" + }, + "node_modules/boxen/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/boxen/node_modules/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cache-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", + "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "dev": true, + "dependencies": { + "array-back": "^4.0.1", + "fs-then-native": "^2.0.0", + "mkdirp2": "^1.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dependencies": { + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/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==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacheable-request/node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/callsite-record": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/callsite-record/-/callsite-record-3.2.2.tgz", + "integrity": "sha1-mgOQZC5D/ou4I5ReUUZPafQWQ94=", + "dev": true, + "dependencies": { + "callsite": "^1.0.0", + "chalk": "^1.1.1", + "error-stack-parser": "^1.3.3", + "highlight-es": "^1.0.0", + "lodash": "4.6.1 || ^4.16.1", + "pinkie-promise": "^2.0.0" + } + }, + "node_modules/callsite-record/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsite-record/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsite-record/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsite-record/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/callsite-record/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "dependencies": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/camelcase-keys/node_modules/camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "node_modules/catharsis": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "dependencies": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "node_modules/check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "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" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" + } + }, + "node_modules/ci-info": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", + "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", + "dev": true + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "dependencies": { + "colors": "^1.1.2", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/cli-progress/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/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==" + }, + "node_modules/cli-progress/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-progress/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cli-truncate/node_modules/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 + }, + "node_modules/cli-truncate/node_modules/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 + }, + "node_modules/cli-truncate/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-truncate/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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 + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dependencies": { + "mimic-response": "^1.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collect-all": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.3.tgz", + "integrity": "sha512-0y0rBgoX8IzIjBAUnO73SEtSb4Mhk3IoceWJq5zZSxb9mWORhWH8xLYo4EDSOE1jRBk1LhmfjqWFFt10h/+MEA==", + "dev": true, + "dependencies": { + "stream-connect": "^1.0.2", + "stream-via": "^1.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dependencies": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "node_modules/color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "dependencies": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/command-line-args": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", + "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-args/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/command-line-args/node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-tool": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", + "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", + "dev": true, + "dependencies": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "command-line-args": "^5.0.0", + "command-line-usage": "^4.1.0", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-tool/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/command-line-usage": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "dev": true, + "dependencies": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "table-layout": "^0.4.2", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/common-sequence": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", + "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha1-ZnZjWQUFooO/JqSE1oSJ10xUhdo=", + "dev": true, + "dependencies": { + "walk-back": "^2.0.1" + } + }, + "node_modules/config-master/node_modules/walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha1-VU4qnYdPrEeoywBr9EwvDEmYoKQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dependencies": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha1-CRcCVYiHW+/XDPLv9L7yxuLXXQQ=", + "dependencies": { + "easy-table": "1.1.0" + }, + "engines": { + "node": "> 0.10" + } + }, + "node_modules/core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "hasInstallScript": true + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "node_modules/cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cosmiconfig/node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "dependencies": { + "capture-stack-trace": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", + "dev": true, + "dependencies": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + } + }, + "node_modules/crypto-random-string": { + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "dependencies": { + "array-find-index": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "node_modules/deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "node_modules/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "optional": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "node_modules/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==" + }, + "node_modules/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==", + "dependencies": { + "object-keys": "^1.0.12" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depcheck": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.8.3.tgz", + "integrity": "sha512-xcLTnaovCFFTts5Ge7mUUhMGHSu6eRfftvVvOjN7gXO5EFUhJfX6UQa1b08a0SIwKfzG9eKNn5mzZlXp0mZARA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.3.1", + "@babel/traverse": "^7.2.3", + "builtin-modules": "^3.0.0", + "deprecate": "^1.0.0", + "deps-regex": "^0.1.4", + "js-yaml": "^3.4.2", + "lodash": "^4.17.11", + "minimatch": "^3.0.2", + "node-sass-tilde-importer": "^1.0.2", + "please-upgrade-node": "^3.1.1", + "require-package-name": "^2.0.1", + "resolve": "^1.10.0", + "vue-template-compiler": "^2.6.10", + "walkdir": "^0.3.2", + "yargs": "^13.2.2" + }, + "bin": { + "depcheck": "bin/depcheck.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/depcheck/node_modules/yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "dev": true, + "dependencies": { + "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" + } + }, + "node_modules/deprecate": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.1.1.tgz", + "integrity": "sha512-ZGDXefq1xknT292LnorMY5s8UVU08/WKdzDZCUT6t9JzsiMSP4uzUhgpqugffNVcT5WC6wMBiSQ+LFjlv3v7iQ==", + "dev": true + }, + "node_modules/deps-regex": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz", + "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ=", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dmd": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.0.0.tgz", + "integrity": "sha512-PwWZlqZnJPETwqZZ70haRa+UDZcD5jeBD3ywW1Kf+jYYv0MHu/S7Ri9jsSoeTMwkcMVW9hXOMA1IZUMEufBhOg==", + "dev": true, + "dependencies": { + "array-back": "^5.0.0", + "cache-point": "^2.0.0", + "common-sequence": "^2.0.0", + "file-set": "^4.0.1", + "handlebars": "^4.7.7", + "marked": "^2.0.0", + "object-get": "^2.1.1", + "reduce-flatten": "^3.0.0", + "reduce-unique": "^2.0.1", + "reduce-without": "^1.0.1", + "test-value": "^3.0.0", + "walk-back": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/dmd/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/dmd/node_modules/reduce-flatten": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.0.tgz", + "integrity": "sha512-eczl8wAYBxJ6Egl6I1ECIF+8z6sHu+KE7BzaEDZTpPXKXfy9SUDQlVYwkRcNTjJLC3Iakxbhss50KuT/R6SYfg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dmd/node_modules/walk-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.0.0.tgz", + "integrity": "sha512-ASerU3aOj9ok+uMNiW0A6/SEwNOxhPmiprbHmPFw1faz7Qmoy9wD3sbmL5HYG+IBxT5c/4cX9O2kSpNv8OAM9A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "node_modules/easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha1-hvmrTBAvA3G3KXuSplHVgkvIy3M=", + "dependencies": { + "wcwidth": ">=1.0.1" + }, + "optionalDependencies": { + "wcwidth": ">=1.0.1" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "node_modules/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==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/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, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/entities": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "node_modules/error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "dev": true, + "dependencies": { + "stackframe": "^0.3.1" + } + }, + "node_modules/es-abstract": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz", + "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==", + "dependencies": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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=", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/eslint": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", + "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-config-ssjs": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/eslint-config-ssjs/-/eslint-config-ssjs-1.1.11.tgz", + "integrity": "sha512-8C7J0TgjkwQM4j7vra9/SuqjhZa+rq6CrPzwPnPRqcrVclfOFqI6uoo3LXMnxCI/ovOoOy0KqmD++uzIXsmr5g==", + "dev": true, + "engines": { + "node": "^6.14.0 || ^8.10.0 || >=9.10.0" + } + }, + "node_modules/eslint-plugin-mocha": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.1.0.tgz", + "integrity": "sha512-1EgHvXKRl7W3mq3sntZAi5T24agRMyiTPL4bSXe+B4GksYOjAPEWYx+J3eJg4It1l2NMNZJtk0gQyQ6mfiPhQg==", + "dev": true, + "dependencies": { + "eslint-utils": "^2.1.0", + "ramda": "^0.27.1" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "eslint": ">=5.0.0", + "prettier": ">=1.13.0" + }, + "peerDependenciesMeta": { + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "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, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/node_modules/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 + }, + "node_modules/eslint/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/eslint/node_modules/eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^1.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint/node_modules/eslint-utils/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "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, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint/node_modules/shebang-command": { + "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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "dependencies": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "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, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.2.2.tgz", + "integrity": "sha1-4urUcsLDGq1vc/GslW7vReEjIMs=", + "dev": true, + "dependencies": { + "cross-spawn-async": "^2.1.1", + "npm-run-path": "^1.0.0", + "object-assign": "^4.0.1", + "path-key": "^1.0.0", + "strip-eof": "^1.0.0" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/execa/node_modules/path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "node_modules/external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "engines": [ + "node >=0.6.0" + ] + }, + "node_modules/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=" + }, + "node_modules/fast-diff": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", + "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", + "dev": true + }, + "node_modules/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=" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "node_modules/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==" + }, + "node_modules/fecha": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-set": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.1.tgz", + "integrity": "sha512-tRzX4kGPmxS2HDK2q2L4qcPopTl/gcyahve2/O8l8hHNJgJ7m+r/ZncCJ1MmFWEMp1yHxJGIU9gAcsWu5jPMpg==", + "dev": true, + "dependencies": { + "array-back": "^4.0.1", + "glob": "^7.1.6" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/file-set/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-parent-dir": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", + "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", + "dev": true + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-replace/node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "engines": { + "node": "*" + } + }, + "node_modules/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==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-extra/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/fs-then-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fuel-auth": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fuel-auth/-/fuel-auth-3.3.0.tgz", + "integrity": "sha512-ZJSfLmhcTHkY8h9i7Wmb8hebYjC064zLC+LxUaFj78qUQ/T/5v2rVwJ5dtpqhJ6BLwpVepKa0hDMfHak8+WaYA==", + "dependencies": { + "lodash.merge": "^4.6.1", + "request": "~2.88.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/fuel-rest": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fuel-rest/-/fuel-rest-3.2.2.tgz", + "integrity": "sha512-vqCvpOblssPQPLtn9QKCz4pMleRBneUUY0OXQc8tGyv+7qynwpkNw62OJw346RzKG4TtN9EuHb80Cm26aJCz6Q==", + "dependencies": { + "fuel-auth": "^3.3.0", + "lodash.clone": "~4.5.0", + "lodash.isplainobject": "~4.0.4", + "lodash.merge": "^4.6.1", + "request": "~2.88.0" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/fuel-soap": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fuel-soap/-/fuel-soap-2.2.4.tgz", + "integrity": "sha512-0+glcwMlBuF7NUpNNJ6x0gpUWOJzgT58GYnLIIuOaDrG7eS3i8JnOG+oElBCKdbymrmAqLwUBLsdBDYm/EdbQA==", + "dependencies": { + "fuel-auth": "^3.3.0", + "lodash.clone": "^4.5.0", + "lodash.isempty": "^4.4.0", + "lodash.isplainobject": "^4.0.3", + "lodash.merge": "^4.6.0", + "request": "^2.81.0", + "xml2js": "^0.4.16" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/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", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/git-version-tag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/git-version-tag/-/git-version-tag-1.0.2.tgz", + "integrity": "sha512-342rLtHtBdXcgngQOPhHt+IhxLbrijmd+Pa9rWDnQ9ubZYrWWSOWSQd9MTi8xo599RkrWkYh4lTYrNzPNxTmsQ==", + "dependencies": { + "semver": "^5.6.0", + "simple-git": "^2.17.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/giturl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/giturl/-/giturl-1.0.1.tgz", + "integrity": "sha512-wQourBdI13n8tbjcZTDl6k+ZrCRMU6p9vfp9jknZq+zfWc8xXNztpZFM4XkPHVzHcMSUZxEMYYKZjIGkPlei6Q==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": "*" + } + }, + "node_modules/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, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globals": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", + "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", + "dev": true, + "dependencies": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/globby/node_modules/glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/globby/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "dependencies": { + "create-error-class": "^3.0.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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "dependencies": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-ansi/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/highlight-es": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", + "integrity": "sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==", + "dev": true, + "dependencies": { + "chalk": "^2.4.0", + "is-es2016-keyword": "^1.0.0", + "js-tokens": "^3.0.0" + } + }, + "node_modules/highlight-es/node_modules/js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "node_modules/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==" + }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/husky": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz", + "integrity": "sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/typicode" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/husky" + } + ], + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "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, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "engines": { + "node": ">=4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "dependencies": { + "repeating": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "engines": { + "node": "*" + } + }, + "node_modules/inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "dependencies": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/inquirer/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/inquirer/node_modules/cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/inquirer/node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/node_modules/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==" + }, + "node_modules/inquirer/node_modules/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==" + }, + "node_modules/inquirer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/node_modules/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "node_modules/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, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "dependencies": { + "ci-info": "^1.5.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-es2016-keyword": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-es2016-keyword/-/is-es2016-keyword-1.0.0.tgz", + "integrity": "sha1-9uVOEQxeT40mXmnS7Q6vjPX0dxg=", + "dev": true + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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=", + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dependencies": { + "has": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dependencies": { + "has-symbols": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "node_modules/is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "node_modules/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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==" + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/js2xmlparser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", + "dev": true, + "dependencies": { + "xmlcreate": "^2.0.3" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "node_modules/jsdoc": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.4.tgz", + "integrity": "sha512-3G9d37VHv7MFdheviDCjUfQoIjdv4TC5zTTf5G9VODLtOnVS6La1eoYBDlbWfsRT3/Xo+j2MIqki2EV12BZfwA==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.9.4", + "bluebird": "^3.7.2", + "catharsis": "^0.8.11", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.1", + "klaw": "^3.0.0", + "markdown-it": "^10.0.0", + "markdown-it-anchor": "^5.2.7", + "marked": "^0.8.2", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.10.2" + }, + "bin": { + "jsdoc": "jsdoc.js" + }, + "engines": { + "node": ">=8.15.0" + } + }, + "node_modules/jsdoc-api": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-6.0.0.tgz", + "integrity": "sha512-zvfB63nAc9e+Rv2kKmJfE6tmo4x8KFho5vKr6VfYTlCCgqtrfPv0McCdqT4betUT9rWtw0zGkNUVkVqeQipY6Q==", + "dev": true, + "dependencies": { + "array-back": "^4.0.1", + "cache-point": "^2.0.0", + "collect-all": "^1.0.3", + "file-set": "^4.0.1", + "fs-then-native": "^2.0.0", + "jsdoc": "^3.6.4", + "object-to-spawn-args": "^2.0.0", + "temp-path": "^1.0.0", + "walk-back": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdoc-parse": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.0.0.tgz", + "integrity": "sha512-35DhfCHL1bq5r0TvolhyyGhhoem700IfEvviL8I1t99Qxa3aSmWbBEpnvvouA7TyXlwxcQfSg75ryXW8Ppq7FA==", + "dev": true, + "dependencies": { + "array-back": "^5.0.0", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "reduce-extract": "^1.0.0", + "sort-array": "^4.1.3", + "test-value": "^3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jsdoc-parse/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdoc-to-markdown": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.0.0.tgz", + "integrity": "sha512-pcZluhsRqi+qx/BKcBfdUWBOXPk7G0aRKGkBgxemedqnqM0XfxO+SYFeouExrIsuAGGmsQ/eQk2uqynG6FM2ug==", + "dev": true, + "dependencies": { + "array-back": "^5.0.0", + "command-line-tool": "^0.8.0", + "config-master": "^3.1.0", + "dmd": "^6.0.0", + "jsdoc-api": "^6.0.0", + "jsdoc-parse": "^6.0.0", + "walk-back": "^5.0.0" + }, + "bin": { + "jsdoc2md": "bin/cli.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/jsdoc-to-markdown/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdoc-to-markdown/node_modules/walk-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.0.0.tgz", + "integrity": "sha512-ASerU3aOj9ok+uMNiW0A6/SEwNOxhPmiprbHmPFw1faz7Qmoy9wD3sbmL5HYG+IBxT5c/4cX9O2kSpNv8OAM9A==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/jsdoc/node_modules/@babel/parser": { + "version": "7.10.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.3.tgz", + "integrity": "sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/jsdoc/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsdoc/node_modules/marked": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/jsdoc/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jsdoc/node_modules/strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "node_modules/json-to-table": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/json-to-table/-/json-to-table-4.2.1.tgz", + "integrity": "sha512-4PSWfPZyFssRj5BwjusoMSPSKoqfoULLZdrAgHvDkpXSZVID4gSe32NdemeePANf7oLa+x8znIWVIc8ba4gM/g==", + "dependencies": { + "lodash": "~4.17.20", + "traverse": "~0.6.6" + } + }, + "node_modules/jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "dependencies": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "node_modules/jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "node_modules/keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dependencies": { + "json-buffer": "3.0.0" + } + }, + "node_modules/klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "node_modules/latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dependencies": { + "package-json": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/latest-version/node_modules/get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/latest-version/node_modules/got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dependencies": { + "@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": "^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" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/latest-version/node_modules/package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dependencies": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/latest-version/node_modules/prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "engines": { + "node": ">=4" + } + }, + "node_modules/latest-version/node_modules/registry-auth-token": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", + "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/latest-version/node_modules/registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dependencies": { + "rc": "^1.2.8" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/latest-version/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/latest-version/node_modules/url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dependencies": { + "prepend-http": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "node_modules/linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/lint-staged": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.0" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lint-staged/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/lint-staged/node_modules/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 + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lint-staged/node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/lint-staged/node_modules/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, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/shebang-command": { + "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, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lint-staged/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/listr2": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.3.tgz", + "integrity": "sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.3", + "through": "^2.3.8" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/listr2/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/listr2/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/node_modules/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 + }, + "node_modules/listr2/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/listr2/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-yaml-file": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.1.1.tgz", + "integrity": "sha512-G910TofXH7u0NfslAzqv6c9pHpvBzObNOo2hMG0/KUDpdHeFY0wE/fTBMExt0Gb12gg5bXS7Hj6pb0e+xbBXLA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^2.3.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-yaml-file/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "node_modules/lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "node_modules/lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "node_modules/lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "dev": true + }, + "node_modules/lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, + "node_modules/lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "node_modules/lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", + "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-symbols/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/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 + }, + "node_modules/log-symbols/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/log-update/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/node_modules/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 + }, + "node_modules/log-update/node_modules/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 + }, + "node_modules/log-update/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-update/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "dependencies": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "dependencies": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "pseudomap": "^1.0.2", + "yallist": "^2.1.2" + } + }, + "node_modules/make-dir": { + "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==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it-anchor": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "dev": true + }, + "node_modules/marked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz", + "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==", + "dev": true, + "bin": { + "marked": "bin/marked" + }, + "engines": { + "node": ">= 8.16.2" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "dependencies": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "dependencies": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dependencies": { + "mime-db": "1.40.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "node_modules/mkdirp2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.4.tgz", + "integrity": "sha512-Q2PKB4ZR4UPtjLl76JfzlgSCUZhSV1AXQgAZa1qt5RiaALFjP/CDrGvFBrOz7Ck6McPcwMAxTsJvWOUjOU8XMw==", + "dev": true + }, + "node_modules/mocha": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", + "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "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": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/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 + }, + "node_modules/mocha/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/find-up": { + "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, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "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, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "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, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-json-comments": { + "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, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/mustache": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.1.0.tgz", + "integrity": "sha512-0FsgP/WVq4mKyjolIyX+Z9Bd+3WS8GOwoUTyKXT5cTYMGeauNTi2HPCwERqseC1IHAy0Z7MDZnJBfjabd4O8GQ==", + "bin": { + "mustache": "bin/mustache" + } + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "node_modules/nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/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_modules/node-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dev": true, + "dependencies": { + "lodash.toarray": "^4.4.0" + } + }, + "node_modules/node-sass-tilde-importer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz", + "integrity": "sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==", + "dev": true, + "dependencies": { + "find-parent-dir": "^0.3.0" + } + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm-check": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/npm-check/-/npm-check-5.9.2.tgz", + "integrity": "sha512-YlTZGP1A8+Rad5wldGil9STYxgZpZl18X6GZI03f4Ch6qTI1TLHIYM0ISco19qgg8M3UHfooEqMfYOpOkF3AeA==", + "dev": true, + "dependencies": { + "babel-runtime": "^6.6.1", + "callsite-record": "^3.0.0", + "chalk": "^1.1.3", + "co": "^4.6.0", + "depcheck": "0.8.3", + "execa": "^0.2.2", + "giturl": "^1.0.0", + "global-modules": "^1.0.0", + "globby": "^4.0.0", + "inquirer": "^0.12.0", + "is-ci": "^1.0.8", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "minimatch": "^3.0.2", + "node-emoji": "^1.0.3", + "ora": "^0.2.1", + "package-json": "^4.0.1", + "path-exists": "^2.1.0", + "pkg-dir": "^1.0.0", + "preferred-pm": "^1.0.1", + "semver": "^5.0.1", + "semver-diff": "^2.0.0", + "text-table": "^0.2.0", + "throat": "^2.0.2", + "update-notifier": "^2.1.0", + "xtend": "^4.0.1" + }, + "bin": { + "npm-check": "bin/cli.js" + }, + "engines": { + "node": ">=0.11.0" + } + }, + "node_modules/npm-check/node_modules/ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "dev": true, + "dependencies": { + "string-width": "^2.0.0" + } + }, + "node_modules/npm-check/node_modules/ansi-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/ansi-align/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/ansi-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/ansi-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/boxen": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-1.3.0.tgz", + "integrity": "sha512-TNPjfTr432qx7yOjQyaXm3dSR0MH9vXp7eT1BFSl/C51g+EFnOR9hTg1IreahGBmDNCehscshe45f+C1TBZbLw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/boxen/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "dependencies": { + "restore-cursor": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, + "dependencies": { + "dot-prop": "^4.2.1", + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "lru-cache": "^4.0.1", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "node_modules/npm-check/node_modules/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=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, + "dependencies": { + "is-obj": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "dependencies": { + "ini": "^1.3.4" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "dependencies": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "node_modules/npm-check/node_modules/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=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/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=", + "dev": true, + "dependencies": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "path-is-inside": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "dependencies": { + "package-json": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "path-key": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "dependencies": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "dependencies": { + "once": "^1.3.0" + } + }, + "node_modules/npm-check/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-check/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "execa": "^0.7.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/term-size/node_modules/execa": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", + "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "dependencies": { + "crypto-random-string": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "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" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/update-notifier/node_modules/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, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/update-notifier/node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/update-notifier/node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/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, + "dependencies": { + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/widest-line/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/widest-line/node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/widest-line/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/widest-line/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-check/node_modules/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==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "node_modules/npm-check/node_modules/xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", + "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=", + "dev": true, + "dependencies": { + "path-key": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-get": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", + "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object-to-spawn-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.0.tgz", + "integrity": "sha512-ZMT4owlXg3JGegecLlAgAA/6BsdKHn63R3ayXcAa3zFkF7oUBHcSb0oxszeutYe0FO2c1lT5pwCuidLkC4Gx3g==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dependencies": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "dependencies": { + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "dependencies": { + "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" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "dependencies": { + "restore-cursor": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "dependencies": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ora/node_modules/supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/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=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/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, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "dependencies": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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 + }, + "node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "node_modules/picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "dependencies": { + "semver-compare": "^1.0.0" + } + }, + "node_modules/preferred-pm": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-1.0.1.tgz", + "integrity": "sha512-9Uxgin5Xnsl67DBvlNFsmDIlBuG9/XKK2cVBTj//7/7wW6ZY+IC9/GlLqxyHABpoasAsJ1MARFOdYPxMUtndxA==", + "dev": true, + "dependencies": { + "path-exists": "^3.0.0", + "which-pm": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/process-nextick-args": { + "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==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "node_modules/psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dependencies": { + "escape-goat": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "dependencies": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "dependencies": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "dependencies": { + "error-ex": "^1.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "dependencies": { + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "dependencies": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read-pkg-up/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + } + }, + "node_modules/readline2/node_modules/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=", + "dev": true, + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/readline2/node_modules/mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + }, + "node_modules/redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "dependencies": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", + "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "dev": true, + "dependencies": { + "test-value": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-extract/node_modules/array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "dependencies": { + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/reduce-extract/node_modules/test-value": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", + "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "dev": true, + "dependencies": { + "array-back": "^1.0.2", + "typical": "^2.4.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-unique": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", + "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/reduce-without": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "dev": true, + "dependencies": { + "test-value": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reduce-without/node_modules/array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "dependencies": { + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/reduce-without/node_modules/test-value": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "dev": true, + "dependencies": { + "array-back": "^1.0.3", + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "node_modules/regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "dependencies": { + "rc": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "dependencies": { + "is-finite": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "node_modules/require-package-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", + "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=", + "dev": true + }, + "node_modules/requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "dependencies": { + "path-parse": "^1.0.6" + } + }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dependencies": { + "lowercase-keys": "^1.0.0" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "node_modules/rxjs": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", + "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", + "dependencies": { + "tslib": "^1.9.0" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "node_modules/semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "dependencies": { + "semver": "^5.0.3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "node_modules/sfmc-fuelsdk-node": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/sfmc-fuelsdk-node/-/sfmc-fuelsdk-node-2.4.0.tgz", + "integrity": "sha512-Orl/Mjc9KlFBjXuJvlzMaXkK1Kl4ns9Y+NS3WwczcmPj4PnNEsdJC7x+KLarxGbKzgR6iGA+PVG8Fhs3tTb1Ww==", + "dependencies": { + "fuel-auth": "^3.3.0", + "fuel-rest": "^3.2.2", + "fuel-soap": "^2.2.3", + "lodash.merge": "^4.6.1", + "request": "^2.88.0", + "uuid": "^3.3.2" + } + }, + "node_modules/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, + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "node_modules/signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "node_modules/simple-git": { + "version": "2.37.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.37.0.tgz", + "integrity": "sha512-ZK6qRnP+Xa2v23UEZDNHUfzswsuNCDHOQpWZRkpqNaXn7V5wVBBx3zRJLji3pROJGzrzA7mXwY7preL5EKuAaQ==", + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.2" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/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, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/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 + }, + "node_modules/slice-ansi/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/sort-array": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz", + "integrity": "sha512-GVFN6Y1sHKrWaSYOJTk9093ZnrBMc9sP3nuhANU44S4xg3rE6W5Z5WyamuT8VpMBbssnetx5faKCua0LEmUnSw==", + "dev": true, + "dependencies": { + "array-back": "^5.0.0", + "typical": "^6.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sort-array/node_modules/array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/sort-array/node_modules/typical": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", + "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/sql-formatter-plus": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sql-formatter-plus/-/sql-formatter-plus-1.3.6.tgz", + "integrity": "sha512-3pelcpLve9GgsXshWL/59zyDkhICkXDURKjXyUN+PJqVGAt+ZmNPB2JY+hgnaQG5X9TQI7Q+WiyLEprHQAWuaQ==", + "dependencies": { + "@babel/polyfill": "^7.6.0", + "lodash": "^4.17.15" + } + }, + "node_modules/sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "engines": { + "node": "*" + } + }, + "node_modules/stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=", + "dev": true + }, + "node_modules/stream-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "dev": true, + "dependencies": { + "array-back": "^1.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stream-connect/node_modules/array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "dependencies": { + "typical": "^2.6.0" + }, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/stream-via": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, + "node_modules/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==", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "dependencies": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "dependencies": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "dependencies": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/stringify-object/node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "dependencies": { + "get-stdin": "^4.0.1" + }, + "bin": { + "strip-indent": "cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-indent/node_modules/get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "dev": true, + "dependencies": { + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "dev": true, + "dependencies": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz", + "integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/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 + }, + "node_modules/table/node_modules/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==", + "dev": true + }, + "node_modules/table/node_modules/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/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, + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "node_modules/temp-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "dev": true + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/test-value": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", + "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", + "dev": true, + "dependencies": { + "array-back": "^2.0.0", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/test-value/node_modules/array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "dependencies": { + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "node_modules/throat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-2.0.2.tgz", + "integrity": "sha1-qfzoCLaeEzpjJZB4DzQsMKYkmwI=", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "node_modules/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, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/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==", + "engines": { + "node": ">=6" + } + }, + "node_modules/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==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, + "node_modules/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==", + "dependencies": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tough-cookie/node_modules/punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "node_modules/traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "node_modules/trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "node_modules/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, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dependencies": { + "is-typedarray": "^1.0.0" + } + }, + "node_modules/typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "dev": true + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", + "dev": true, + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/update-notifier-git": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/update-notifier-git/-/update-notifier-git-5.0.3.tgz", + "integrity": "sha512-Ikg++hC5EUKURxT+aCsnA58ZYq67Hiof2jK0rlFCJuF/lQsm3g2AKx1BrQZ9cO2x0mksUhabzWvAAJH45nXSJw==", + "dependencies": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "git-version-tag": "^1.0.2", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "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" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier-git/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier-git/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier-git/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/update-notifier-git/node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/update-notifier-git/node_modules/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==" + }, + "node_modules/update-notifier-git/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier-git/node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/update-notifier-git/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/update-notifier-git/node_modules/semver-diff": { + "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==", + "dependencies": { + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-notifier-git/node_modules/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==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/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==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/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=", + "dev": true, + "dependencies": { + "prepend-http": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dependencies": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node_modules/uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "bin": { + "uuid": "bin/uuid" + } + }, + "node_modules/v8-compile-cache": { + "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 + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "dev": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "node_modules/walk-back": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-4.0.0.tgz", + "integrity": "sha512-kudCA8PXVQfrqv2mFTG72vDBRi8BKWxGgFLwPpzHcpZnSwZk93WMwUDVcLHWNsnm+Y0AC4Vb6MUNRgaHfyV2DQ==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/walkdir": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.3.2.tgz", + "integrity": "sha512-0Twghia4Z5wDGDYWURlhZmI47GvERMCsXIu0QZWVVZyW9ZjpbbZvD9Zy9M6cWiQQRRbAcYajIyKNavaZZDt1Uw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "optional": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/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 + }, + "node_modules/which-pm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-1.1.0.tgz", + "integrity": "sha512-7GHHJQpALk7BWMD8I+xSILSbHyngvBlfSXlwGpdRFY2voFwVCx+eJAybXTzTnUYmt7zio6B9SEdI81T0fBjxNA==", + "dev": true, + "dependencies": { + "load-yaml-file": "^0.1.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dependencies": { + "string-width": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/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==" + }, + "node_modules/widest-line/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/widest-line/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "dependencies": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "node_modules/winston-transport/node_modules/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==", + "dependencies": { + "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" + } + }, + "node_modules/winston-transport/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/winston-transport/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/winston/node_modules/is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "engines": { + "node": ">=8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "node_modules/wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "dependencies": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "node_modules/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, + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "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==", + "dependencies": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "node_modules/xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "engines": { + "node": ">=8" + } + }, + "node_modules/xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "dependencies": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlcreate": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", + "dev": true + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/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==", + "dev": true + }, + "node_modules/yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "node_modules/yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/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, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/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, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/camelcase": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", + "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser/node_modules/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, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/cliui": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.3.tgz", + "integrity": "sha512-Gj3QHTkVMPKqwP3f7B4KPkBZRMR9r4rfi5bXFpg1a+Svvj8l7q5CnkBkVQzfxT5DFSsGk2+PascOgL0JYkL2kw==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/yargs/node_modules/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==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/yargs/node_modules/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==" + }, + "node_modules/yargs/node_modules/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==" + }, + "node_modules/yargs/node_modules/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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/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==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dependencies": { + "ansi-regex": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==", + "engines": { + "node": ">=10" + } + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/generator": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.9.5.tgz", + "integrity": "sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==", + "dev": true, + "requires": { + "@babel/types": "^7.9.5", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz", + "integrity": "sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.8.3", + "@babel/template": "^7.8.3", + "@babel/types": "^7.9.5" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.8.3.tgz", + "integrity": "sha512-FVDR+Gd9iLjUMY1fzE2SR0IuaJToR4RkCDARVfsBBPSP53GEqSFjD8gNyxg246VUyc/ALRxFaAK8rVG7UT7xRA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz", + "integrity": "sha512-3x3yOeyBhW851hroze7ElzdkeRXQYQbFIb7gLK1WQYsw2GWDay5gAJNw1sWJ0VFP6z5J1whqeXH/WCdCjZv6dA==", + "dev": true, + "requires": { + "@babel/types": "^7.8.3" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", + "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==", + "dev": true + }, + "@babel/highlight": { + "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", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "@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/parser": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.7.3.tgz", + "integrity": "sha512-bqv+iCo9i+uLVbI0ILzKkvMorqxouI+GbV13ivcARXn9NNEabi2IEz912IgNpT/60BNXac5dgcfjb94NjsF33A==", + "dev": true + }, + "@babel/polyfill": { + "version": "7.10.1", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.10.1.tgz", + "integrity": "sha512-TviueJ4PBW5p48ra8IMtLXVkDucrlOZAIZ+EXqS3Ot4eukHbWiqcn7DcqpA1k5PcKtmJ4Xl9xwdv6yQvvcA+3g==", + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", + "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + } + } + }, + "@babel/template": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.8.6.tgz", + "integrity": "sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/parser": "^7.8.6", + "@babel/types": "^7.8.6" + }, + "dependencies": { + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true + } + } + }, + "@babel/traverse": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.9.5.tgz", + "integrity": "sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/generator": "^7.9.5", + "@babel/helper-function-name": "^7.9.5", + "@babel/helper-split-export-declaration": "^7.8.3", + "@babel/parser": "^7.9.0", + "@babel/types": "^7.9.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "@babel/parser": { + "version": "7.9.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.9.4.tgz", + "integrity": "sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==", + "dev": true + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.9.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.9.5.tgz", + "integrity": "sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.9.5", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@dabh/diagnostics": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz", + "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==", + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@eslint/eslintrc": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", + "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "dev": true, + "requires": { + "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", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "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==", + "dev": true + }, + "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" + } + }, + "strip-json-comments": { + "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 + } + } + }, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@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==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@types/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", + "dev": true + }, + "@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 + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "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, + "requires": {} + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "dependencies": { + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true + } + } + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "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-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "requires": { + "string-width": "^3.0.0" + } + }, + "ansi-colors": { + "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-escape-sequences": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-escape-sequences/-/ansi-escape-sequences-4.1.0.tgz", + "integrity": "sha512-dzW9kHxH011uBsidTXd14JXgzye/YLb2LzeKZ4bsgl/Knwx8AtbSFkkGxagdNOoh0DlqHCmfiEjWKBaqjOanVw==", + "dev": true, + "requires": { + "array-back": "^3.0.1" + }, + "dependencies": { + "array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true + } + } + }, + "ansi-escapes": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", + "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "requires": { + "type-fest": "^0.11.0" + }, + "dependencies": { + "type-fest": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", + "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==" + } + } + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==" + }, + "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" + } + }, + "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" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-back": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.1.tgz", + "integrity": "sha512-Z/JnaVEXv+A9xabHzN43FiiiWEE7gPCRXMrVmRm00tWbjZRul1iHm7ECzlyNq1p4a4ATXz+G9FJ3GqGOkOV3fg==", + "dev": true + }, + "array-find-index": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", + "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=", + "dev": true + }, + "array-union": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", + "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", + "dev": true, + "requires": { + "array-uniq": "^1.0.1" + } + }, + "array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", + "dev": true + }, + "arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "async": { + "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==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "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=", + "dev": true, + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "boxen": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", + "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", + "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==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "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==", + "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==" + }, + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==" + }, + "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==", + "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==", + "requires": { + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "cache-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/cache-point/-/cache-point-2.0.0.tgz", + "integrity": "sha512-4gkeHlFpSKgm3vm2gJN5sPqfmijYRFYCQ6tv5cLw0xVmT6r1z1vd4FNnpuOREco3cBs1G709sZ72LdgddKvL5w==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "fs-then-native": "^2.0.0", + "mkdirp2": "^1.0.4" + } + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "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==", + "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==" + } + } + }, + "callsite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz", + "integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA=", + "dev": true + }, + "callsite-record": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/callsite-record/-/callsite-record-3.2.2.tgz", + "integrity": "sha1-mgOQZC5D/ou4I5ReUUZPafQWQ94=", + "dev": true, + "requires": { + "callsite": "^1.0.0", + "chalk": "^1.1.1", + "error-stack-parser": "^1.3.3", + "highlight-es": "^1.0.0", + "lodash": "4.6.1 || ^4.16.1", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "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" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==" + }, + "camelcase-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz", + "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=", + "dev": true, + "requires": { + "camelcase": "^2.0.0", + "map-obj": "^1.0.0" + }, + "dependencies": { + "camelcase": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz", + "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=", + "dev": true + } + } + }, + "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==", + "dev": true + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "catharsis": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.11.tgz", + "integrity": "sha512-a+xUyMV7hD1BrDQA/3iPV7oc+6W26BgVJO05PGEoatMyIuPScQKsde6i3YorWX1qs+AZjnJ18NqdKoCtKiNh1g==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "chai": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA==", + "dev": true, + "requires": { + "assertion-error": "^1.1.0", + "check-error": "^1.0.2", + "deep-eql": "^3.0.1", + "get-func-name": "^2.0.0", + "pathval": "^1.1.1", + "type-detect": "^4.0.5" + } + }, + "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" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==" + }, + "check-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", + "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "dev": true + }, + "chokidar": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", + "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "dev": true, + "requires": { + "anymatch": "~3.1.1", + "braces": "~3.0.2", + "fsevents": "~2.3.1", + "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==", + "dev": true + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": 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==" + }, + "cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-progress": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.9.0.tgz", + "integrity": "sha512-g7rLWfhAo/7pF+a/STFH/xPyosaL1zgADhI0OM83hl3c7S43iGvJWEAV2QuDOnQ8i6EMBj/u4+NTd0d5L+4JfA==", + "requires": { + "colors": "^1.1.2", + "string-width": "^4.2.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==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "cli-spinners": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-0.1.2.tgz", + "integrity": "sha1-u3ZNiOGF+54eaiofGXcjGPYF4xw=", + "dev": true + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.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 + }, + "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" + } + }, + "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 + }, + "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 + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "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" + } + } + } + }, + "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": "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" + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", + "optional": true + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "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=", + "dev": true + }, + "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=", + "dev": true + }, + "collect-all": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-all/-/collect-all-1.0.3.tgz", + "integrity": "sha512-0y0rBgoX8IzIjBAUnO73SEtSb4Mhk3IoceWJq5zZSxb9mWORhWH8xLYo4EDSOE1jRBk1LhmfjqWFFt10h/+MEA==", + "dev": true, + "requires": { + "stream-connect": "^1.0.2", + "stream-via": "^1.0.4" + } + }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "color-string": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.4.tgz", + "integrity": "sha512-57yF5yt8Xa3czSEW1jfQDE79Idk0+AkN/4KWad6tbdxUmAs3MvjxlWSWD4deYytcRfoZ9nhKyFl1kj5tBvidbw==", + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" + }, + "colorspace": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.2.tgz", + "integrity": "sha512-vt+OoIP2d76xLhjwbBaucYlNSpPsrJWPlBTtwCpQKIu6/CSMutyzX93O/Do0qzpH3YoHEes8YEFXyZ797rEhzQ==", + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "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" + } + }, + "command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "command-line-args": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.1.1.tgz", + "integrity": "sha512-hL/eG8lrll1Qy1ezvkant+trihbGnaKaeEjj6Scyr3DN+RC7iQ5Rz84IeLERfAWDGo0HBSNAakczwgCilDXnWg==", + "dev": true, + "requires": { + "array-back": "^3.0.1", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "dependencies": { + "array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true + }, + "typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true + } + } + }, + "command-line-tool": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/command-line-tool/-/command-line-tool-0.8.0.tgz", + "integrity": "sha512-Xw18HVx/QzQV3Sc5k1vy3kgtOeGmsKIqwtFFoyjI4bbcpSgnw2CWVULvtakyw4s6fhyAdI6soQQhXc2OzJy62g==", + "dev": true, + "requires": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "command-line-args": "^5.0.0", + "command-line-usage": "^4.1.0", + "typical": "^2.6.1" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "^2.6.1" + } + } + } + }, + "command-line-usage": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-4.1.0.tgz", + "integrity": "sha512-MxS8Ad995KpdAC0Jopo/ovGIroV/m0KHwzKfXxKag6FHOkGsH8/lv5yjgablcRxCJJC0oJeUMuO/gmaq+Wq46g==", + "dev": true, + "requires": { + "ansi-escape-sequences": "^4.0.0", + "array-back": "^2.0.0", + "table-layout": "^0.4.2", + "typical": "^2.6.1" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "^2.6.1" + } + } + } + }, + "common-sequence": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/common-sequence/-/common-sequence-2.0.2.tgz", + "integrity": "sha512-jAg09gkdkrDO9EWTdXfv80WWH3yeZl5oT69fGfedBNS9pXUKYInVJ1bJ+/ht2+Moeei48TmSbQDYMc8EOx9G0g==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "config-master": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/config-master/-/config-master-3.1.0.tgz", + "integrity": "sha1-ZnZjWQUFooO/JqSE1oSJ10xUhdo=", + "dev": true, + "requires": { + "walk-back": "^2.0.1" + }, + "dependencies": { + "walk-back": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-2.0.1.tgz", + "integrity": "sha1-VU4qnYdPrEeoywBr9EwvDEmYoKQ=", + "dev": true + } + } + }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + } + }, + "console.table": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", + "integrity": "sha1-CRcCVYiHW+/XDPLv9L7yxuLXXQQ=", + "requires": { + "easy-table": "1.1.0" + } + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cosmiconfig": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", + "integrity": "sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "dependencies": { + "parse-json": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.1.0.tgz", + "integrity": "sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, + "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==", + "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" + } + }, + "cross-spawn-async": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz", + "integrity": "sha1-hF/wwINKPe2dFg2sptOQkGuyiMw=", + "dev": true, + "requires": { + "lru-cache": "^4.0.0", + "which": "^1.2.8" + } + }, + "crypto-random-string": { + "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==" + }, + "currently-unhandled": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz", + "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=", + "dev": true, + "requires": { + "array-find-index": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha1-sgOOhG3DO6pXlhKNCAS0VbjB4h0=", + "dev": true + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=", + "dev": true + }, + "deep-eql": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", + "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "dev": true, + "requires": { + "type-detect": "^4.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "optional": true, + "requires": { + "clone": "^1.0.2" + } + }, + "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==" + }, + "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==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depcheck": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/depcheck/-/depcheck-0.8.3.tgz", + "integrity": "sha512-xcLTnaovCFFTts5Ge7mUUhMGHSu6eRfftvVvOjN7gXO5EFUhJfX6UQa1b08a0SIwKfzG9eKNn5mzZlXp0mZARA==", + "dev": true, + "requires": { + "@babel/parser": "^7.3.1", + "@babel/traverse": "^7.2.3", + "builtin-modules": "^3.0.0", + "deprecate": "^1.0.0", + "deps-regex": "^0.1.4", + "js-yaml": "^3.4.2", + "lodash": "^4.17.11", + "minimatch": "^3.0.2", + "node-sass-tilde-importer": "^1.0.2", + "please-upgrade-node": "^3.1.1", + "require-package-name": "^2.0.1", + "resolve": "^1.10.0", + "vue-template-compiler": "^2.6.10", + "walkdir": "^0.3.2", + "yargs": "^13.2.2" + }, + "dependencies": { + "yargs": { + "version": "13.3.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", + "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "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.2" + } + } + } + }, + "deprecate": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deprecate/-/deprecate-1.1.1.tgz", + "integrity": "sha512-ZGDXefq1xknT292LnorMY5s8UVU08/WKdzDZCUT6t9JzsiMSP4uzUhgpqugffNVcT5WC6wMBiSQ+LFjlv3v7iQ==", + "dev": true + }, + "deps-regex": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deps-regex/-/deps-regex-0.1.4.tgz", + "integrity": "sha1-UYZnt2kUYKXn4KNBvnbrfOgJAYQ=", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dmd": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/dmd/-/dmd-6.0.0.tgz", + "integrity": "sha512-PwWZlqZnJPETwqZZ70haRa+UDZcD5jeBD3ywW1Kf+jYYv0MHu/S7Ri9jsSoeTMwkcMVW9hXOMA1IZUMEufBhOg==", + "dev": true, + "requires": { + "array-back": "^5.0.0", + "cache-point": "^2.0.0", + "common-sequence": "^2.0.0", + "file-set": "^4.0.1", + "handlebars": "^4.7.7", + "marked": "^2.0.0", + "object-get": "^2.1.1", + "reduce-flatten": "^3.0.0", + "reduce-unique": "^2.0.1", + "reduce-without": "^1.0.1", + "test-value": "^3.0.0", + "walk-back": "^5.0.0" + }, + "dependencies": { + "array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true + }, + "reduce-flatten": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-3.0.0.tgz", + "integrity": "sha512-eczl8wAYBxJ6Egl6I1ECIF+8z6sHu+KE7BzaEDZTpPXKXfy9SUDQlVYwkRcNTjJLC3Iakxbhss50KuT/R6SYfg==", + "dev": true + }, + "walk-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.0.0.tgz", + "integrity": "sha512-ASerU3aOj9ok+uMNiW0A6/SEwNOxhPmiprbHmPFw1faz7Qmoy9wD3sbmL5HYG+IBxT5c/4cX9O2kSpNv8OAM9A==", + "dev": true + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "requires": { + "is-obj": "^2.0.0" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "easy-table": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.0.tgz", + "integrity": "sha1-hvmrTBAvA3G3KXuSplHVgkvIy3M=", + "requires": { + "wcwidth": ">=1.0.1" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==" + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" + }, + "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==", + "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": "2.0.3", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz", + "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + } + } + }, + "error-stack-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-1.3.6.tgz", + "integrity": "sha1-4Oc7k+QXE40c18C3RrGkoUhUwpI=", + "dev": true, + "requires": { + "stackframe": "^0.3.1" + } + }, + "es-abstract": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.15.0.tgz", + "integrity": "sha512-bhkEqWJ2t2lMeaJDuk7okMkJWI/yqgH/EoGwpcvv0XW9RWQsRspI4wt6xuyuvMvvQE3gg/D9HXppgk21w78GyQ==", + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.0", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-inspect": "^1.6.0", + "object-keys": "^1.1.1", + "string.prototype.trimleft": "^2.1.0", + "string.prototype.trimright": "^2.1.0" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "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==" + }, + "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=" + }, + "eslint": { + "version": "7.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.22.0.tgz", + "integrity": "sha512-3VawOtjSJUQiiqac8MQc+w457iGLfuNGLFn8JmF051tTKbh5/x/0vlcEj8OgDCaw7Ysa2Jn8paGshV7x2abKXg==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.21", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.4", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "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 + }, + "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 + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "eslint-utils": { + "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": "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 + }, + "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 + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "shebang-command": { + "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": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "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-json-comments": { + "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 + }, + "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" + } + }, + "which": { + "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" + } + } + } + }, + "eslint-config-prettier": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.1.0.tgz", + "integrity": "sha512-oKMhGv3ihGbCIimCAjqkdzx2Q+jthoqnXSP+d86M9tptwugycmTFdVR4IpLgq2c4SHifbwO90z2fQ8/Aio73yw==", + "dev": true, + "requires": {} + }, + "eslint-config-ssjs": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/eslint-config-ssjs/-/eslint-config-ssjs-1.1.11.tgz", + "integrity": "sha512-8C7J0TgjkwQM4j7vra9/SuqjhZa+rq6CrPzwPnPRqcrVclfOFqI6uoo3LXMnxCI/ovOoOy0KqmD++uzIXsmr5g==", + "dev": true + }, + "eslint-plugin-mocha": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-8.1.0.tgz", + "integrity": "sha512-1EgHvXKRl7W3mq3sntZAi5T24agRMyiTPL4bSXe+B4GksYOjAPEWYx+J3eJg4It1l2NMNZJtk0gQyQ6mfiPhQg==", + "dev": true, + "requires": { + "eslint-utils": "^2.1.0", + "ramda": "^0.27.1" + } + }, + "eslint-plugin-prettier": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz", + "integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==", + "dev": true, + "requires": { + "prettier-linter-helpers": "^1.0.0" + } + }, + "eslint-scope": { + "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.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "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" + } + }, + "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 + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "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.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": "^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.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "execa": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/execa/-/execa-0.2.2.tgz", + "integrity": "sha1-4urUcsLDGq1vc/GslW7vReEjIMs=", + "dev": true, + "requires": { + "cross-spawn-async": "^2.1.1", + "npm-run-path": "^1.0.0", + "object-assign": "^4.0.1", + "path-key": "^1.0.0", + "strip-eof": "^1.0.0" + }, + "dependencies": { + "path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=", + "dev": true + } + } + }, + "exit-hook": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", + "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", + "dev": true + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "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==", + "dev": true + }, + "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=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "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==" + }, + "fecha": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz", + "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg==" + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "file-set": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/file-set/-/file-set-4.0.1.tgz", + "integrity": "sha512-tRzX4kGPmxS2HDK2q2L4qcPopTl/gcyahve2/O8l8hHNJgJ7m+r/ZncCJ1MmFWEMp1yHxJGIU9gAcsWu5jPMpg==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "glob": "^7.1.6" + }, + "dependencies": { + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": 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" + } + } + } + }, + "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" + } + }, + "find-parent-dir": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.0.tgz", + "integrity": "sha1-M8RLQpqysvBkYpnF+fcY83b/jVQ=", + "dev": true + }, + "find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "requires": { + "array-back": "^3.0.1" + }, + "dependencies": { + "array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true + } + } + }, + "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": { + "locate-path": "^3.0.0" + } + }, + "flat": { + "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": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", + "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "dev": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "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==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "fs-then-native": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fs-then-native/-/fs-then-native-2.0.0.tgz", + "integrity": "sha1-GaEk2U2QwiyOBF8ujdbr6jbUjGc=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "fuel-auth": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/fuel-auth/-/fuel-auth-3.3.0.tgz", + "integrity": "sha512-ZJSfLmhcTHkY8h9i7Wmb8hebYjC064zLC+LxUaFj78qUQ/T/5v2rVwJ5dtpqhJ6BLwpVepKa0hDMfHak8+WaYA==", + "requires": { + "lodash.merge": "^4.6.1", + "request": "~2.88.0" + } + }, + "fuel-rest": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/fuel-rest/-/fuel-rest-3.2.2.tgz", + "integrity": "sha512-vqCvpOblssPQPLtn9QKCz4pMleRBneUUY0OXQc8tGyv+7qynwpkNw62OJw346RzKG4TtN9EuHb80Cm26aJCz6Q==", + "requires": { + "fuel-auth": "^3.3.0", + "lodash.clone": "~4.5.0", + "lodash.isplainobject": "~4.0.4", + "lodash.merge": "^4.6.1", + "request": "~2.88.0" + } + }, + "fuel-soap": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/fuel-soap/-/fuel-soap-2.2.4.tgz", + "integrity": "sha512-0+glcwMlBuF7NUpNNJ6x0gpUWOJzgT58GYnLIIuOaDrG7eS3i8JnOG+oElBCKdbymrmAqLwUBLsdBDYm/EdbQA==", + "requires": { + "fuel-auth": "^3.3.0", + "lodash.clone": "^4.5.0", + "lodash.isempty": "^4.4.0", + "lodash.isplainobject": "^4.0.3", + "lodash.merge": "^4.6.0", + "request": "^2.81.0", + "xml2js": "^0.4.16" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==" + }, + "get-func-name": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", + "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", + "dev": true + }, + "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", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true + }, + "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 + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-version-tag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/git-version-tag/-/git-version-tag-1.0.2.tgz", + "integrity": "sha512-342rLtHtBdXcgngQOPhHt+IhxLbrijmd+Pa9rWDnQ9ubZYrWWSOWSQd9MTi8xo599RkrWkYh4lTYrNzPNxTmsQ==", + "requires": { + "semver": "^5.6.0", + "simple-git": "^2.17.0" + } + }, + "giturl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/giturl/-/giturl-1.0.1.tgz", + "integrity": "sha512-wQourBdI13n8tbjcZTDl6k+ZrCRMU6p9vfp9jknZq+zfWc8xXNztpZFM4XkPHVzHcMSUZxEMYYKZjIGkPlei6Q==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": 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" + } + }, + "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" + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "13.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.6.0.tgz", + "integrity": "sha512-YFKCX0SiPg7l5oKYCJ2zZGxcXprVXHcSnVuvzrT3oSENQonVLqM5pf9fN5dLGZGyCjhw8TN8Btwe/jKnZ0pjvQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "globby": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-4.1.0.tgz", + "integrity": "sha1-CA9UVJ7BuCpsYOYx/ILhIR2+lfg=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^6.0.1", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "dev": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "got": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz", + "integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=", + "dev": true, + "requires": { + "create-error-class": "^3.0.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" + } + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "handlebars": { + "version": "4.7.7", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.7.tgz", + "integrity": "sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==", + "dev": true, + "requires": { + "minimist": "^1.2.5", + "neo-async": "^2.6.0", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4", + "wordwrap": "^1.0.0" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "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==", + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "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=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + } + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==" + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "highlight-es": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/highlight-es/-/highlight-es-1.0.3.tgz", + "integrity": "sha512-s/SIX6yp/5S1p8aC/NRDC1fwEb+myGIfp8/TzZz0rtAv8fzsdX7vGl3Q1TrXCsczFq8DI3CBFBCySPClfBSdbg==", + "dev": true, + "requires": { + "chalk": "^2.4.0", + "is-es2016-keyword": "^1.0.0", + "js-tokens": "^3.0.0" + }, + "dependencies": { + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", + "dev": true + } + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "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==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-5.1.3.tgz", + "integrity": "sha512-fbNJ+Gz5wx2LIBtMweJNY1D7Uc8p1XERi5KNRMccwfQA+rXlxWNSdUxswo0gT8XqxywTIw7Ywm/F4v/O35RdMg==", + "dev": true + }, + "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" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "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", + "resolve-from": "^4.0.0" + } + }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=" + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" + }, + "indent-string": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz", + "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=", + "dev": true, + "requires": { + "repeating": "^2.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==" + }, + "inquirer": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.0.0.tgz", + "integrity": "sha512-ON8pEJPPCdyjxj+cxsYRe6XfCJepTxANdNnTebsTuQgXpRyZRRT9t4dJwjRubgmvn20CLSEnozRUayXyM9VTXA==", + "requires": { + "ansi-escapes": "^4.2.1", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-width": "^3.0.0", + "external-editor": "^3.0.3", + "figures": "^3.0.0", + "lodash": "^4.17.21", + "mute-stream": "0.0.8", + "run-async": "^2.4.0", + "rxjs": "^6.6.6", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.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==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "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==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-width": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz", + "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==" + }, + "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==", + "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==" + }, + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==" + }, + "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==", + "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==", + "requires": { + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, + "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-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==" + }, + "is-ci": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", + "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", + "dev": true, + "requires": { + "ci-info": "^1.5.0" + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-es2016-keyword": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-es2016-keyword/-/is-es2016-keyword-1.0.0.tgz", + "integrity": "sha1-9uVOEQxeT40mXmnS7Q6vjPX0dxg=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "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==", + "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=" + }, + "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-npm": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", + "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==" + }, + "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 + }, + "is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==" + }, + "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-redirect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-redirect/-/is-redirect-1.0.0.tgz", + "integrity": "sha1-HQPd7VO9jbDzDCbk+V02/HyH3CQ=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "requires": { + "has": "^1.0.1" + } + }, + "is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha1-/S2INUXEa6xaYz57mgnof6LLUGk=", + "dev": true + }, + "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-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "is-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=", + "dev": true + }, + "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==", + "dev": true + }, + "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==" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", + "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "js2xmlparser": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.1.tgz", + "integrity": "sha512-KrPTolcw6RocpYjdC7pL7v62e55q7qOMHvLX1UCLc5AAS8qeJ6nukarEJAF2KL2PZxlbGueEbINqZR2bDe/gUw==", + "dev": true, + "requires": { + "xmlcreate": "^2.0.3" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsdoc": { + "version": "3.6.4", + "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.6.4.tgz", + "integrity": "sha512-3G9d37VHv7MFdheviDCjUfQoIjdv4TC5zTTf5G9VODLtOnVS6La1eoYBDlbWfsRT3/Xo+j2MIqki2EV12BZfwA==", + "dev": true, + "requires": { + "@babel/parser": "^7.9.4", + "bluebird": "^3.7.2", + "catharsis": "^0.8.11", + "escape-string-regexp": "^2.0.0", + "js2xmlparser": "^4.0.1", + "klaw": "^3.0.0", + "markdown-it": "^10.0.0", + "markdown-it-anchor": "^5.2.7", + "marked": "^0.8.2", + "mkdirp": "^1.0.4", + "requizzle": "^0.2.3", + "strip-json-comments": "^3.1.0", + "taffydb": "2.6.2", + "underscore": "~1.10.2" + }, + "dependencies": { + "@babel/parser": { + "version": "7.10.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.3.tgz", + "integrity": "sha512-oJtNJCMFdIMwXGmx+KxuaD7i3b8uS7TTFYW/FNG2BT8m+fmGHoiPYoH0Pe3gya07WuFmM5FCDIr1x0irkD/hyA==", + "dev": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "marked": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.8.2.tgz", + "integrity": "sha512-EGwzEeCcLniFX51DhTpmTom+dSA/MG/OBUDjnWtHbEnjAH180VzUeAw+oE4+Zv+CoYBWyRlYOTR0N8SO9R1PVw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.0.tgz", + "integrity": "sha512-e6/d0eBu7gHtdCqFt0xJr642LdToM5/cN4Qb9DbHjVx1CP5RyeM+zH7pbecEmDv/lBqb0QH+6Uqq75rxFPkM0w==", + "dev": true + } + } + }, + "jsdoc-api": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-api/-/jsdoc-api-6.0.0.tgz", + "integrity": "sha512-zvfB63nAc9e+Rv2kKmJfE6tmo4x8KFho5vKr6VfYTlCCgqtrfPv0McCdqT4betUT9rWtw0zGkNUVkVqeQipY6Q==", + "dev": true, + "requires": { + "array-back": "^4.0.1", + "cache-point": "^2.0.0", + "collect-all": "^1.0.3", + "file-set": "^4.0.1", + "fs-then-native": "^2.0.0", + "jsdoc": "^3.6.4", + "object-to-spawn-args": "^2.0.0", + "temp-path": "^1.0.0", + "walk-back": "^4.0.0" + } + }, + "jsdoc-parse": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-parse/-/jsdoc-parse-6.0.0.tgz", + "integrity": "sha512-35DhfCHL1bq5r0TvolhyyGhhoem700IfEvviL8I1t99Qxa3aSmWbBEpnvvouA7TyXlwxcQfSg75ryXW8Ppq7FA==", + "dev": true, + "requires": { + "array-back": "^5.0.0", + "lodash.omit": "^4.5.0", + "lodash.pick": "^4.4.0", + "reduce-extract": "^1.0.0", + "sort-array": "^4.1.3", + "test-value": "^3.0.0" + }, + "dependencies": { + "array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true + } + } + }, + "jsdoc-to-markdown": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/jsdoc-to-markdown/-/jsdoc-to-markdown-7.0.0.tgz", + "integrity": "sha512-pcZluhsRqi+qx/BKcBfdUWBOXPk7G0aRKGkBgxemedqnqM0XfxO+SYFeouExrIsuAGGmsQ/eQk2uqynG6FM2ug==", + "dev": true, + "requires": { + "array-back": "^5.0.0", + "command-line-tool": "^0.8.0", + "config-master": "^3.1.0", + "dmd": "^6.0.0", + "jsdoc-api": "^6.0.0", + "jsdoc-parse": "^6.0.0", + "walk-back": "^5.0.0" + }, + "dependencies": { + "array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true + }, + "walk-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-5.0.0.tgz", + "integrity": "sha512-ASerU3aOj9ok+uMNiW0A6/SEwNOxhPmiprbHmPFw1faz7Qmoy9wD3sbmL5HYG+IBxT5c/4cX9O2kSpNv8OAM9A==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json-to-table": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/json-to-table/-/json-to-table-4.2.1.tgz", + "integrity": "sha512-4PSWfPZyFssRj5BwjusoMSPSKoqfoULLZdrAgHvDkpXSZVID4gSe32NdemeePANf7oLa+x8znIWVIc8ba4gM/g==", + "requires": { + "lodash": "~4.17.20", + "traverse": "~0.6.6" + } + }, + "jsonfile": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", + "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^1.0.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "klaw": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-3.0.0.tgz", + "integrity": "sha512-0Fo5oir+O9jnXu5EefYbVK+mHMBeEVEy2cmctR1O1NECcCkPRreJKrS6Qt/j3KC2C148Dfo9i3pCmCMsdqGr0g==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==" + }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "requires": { + "package-json": "^6.3.0" + }, + "dependencies": { + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@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": "^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" + } + }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + } + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "registry-auth-token": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.0.tgz", + "integrity": "sha512-P+lWzPrsgfN+UEpDS3U8AQKg/UjZX6mQSJueZj3EK+vNESoqBSpBUD3gmu4sF9lOsjXWjF11dQKUqemf3veq1w==", + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "requires": { + "rc": "^1.2.8" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + } + } + }, + "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" + } + }, + "lines-and-columns": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", + "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "dev": true + }, + "linkify-it": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz", + "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, + "lint-staged": { + "version": "10.5.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-10.5.4.tgz", + "integrity": "sha512-EechC3DdFic/TdOPgj/RB3FicqE6932LTHCUm0Y2fsD9KGlLB+RwJl2q1IYBIvEsKzDOgn0D4gll+YxG5RsrKg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "commander": "^6.2.0", + "cosmiconfig": "^7.0.0", + "debug": "^4.2.0", + "dedent": "^0.7.0", + "enquirer": "^2.3.6", + "execa": "^4.1.0", + "listr2": "^3.2.2", + "log-symbols": "^4.0.0", + "micromatch": "^4.0.2", + "normalize-path": "^3.0.0", + "please-upgrade-node": "^3.2.0", + "string-argv": "0.3.1", + "stringify-object": "^3.3.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 + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "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" + } + }, + "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-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "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": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "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" + } + }, + "which": { + "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" + } + } + } + }, + "listr2": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.2.3.tgz", + "integrity": "sha512-vUb80S2dSUi8YxXahO8/I/s29GqnOL8ozgHVLjfWQXa03BNEeS1TpBLjh2ruaqq5ufx46BRGvfymdBSuoXET5w==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "cli-truncate": "^2.1.0", + "figures": "^3.2.0", + "indent-string": "^4.0.0", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rxjs": "^6.6.3", + "through": "^2.3.8" + }, + "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 + }, + "indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "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" + } + } + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "load-yaml-file": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.1.1.tgz", + "integrity": "sha512-G910TofXH7u0NfslAzqv6c9pHpvBzObNOo2hMG0/KUDpdHeFY0wE/fTBMExt0Gb12gg5bXS7Hj6pb0e+xbBXLA==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^2.3.0", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + } + } + }, + "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": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", + "dev": true + }, + "lodash.clone": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz", + "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=" + }, + "lodash.isempty": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", + "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.omit": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz", + "integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA=", + "dev": true + }, + "lodash.padend": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/lodash.padend/-/lodash.padend-4.6.1.tgz", + "integrity": "sha1-U8y6BH0G4VjTEfRdpiX05J5vFm4=", + "dev": true + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=", + "dev": true + }, + "lodash.toarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.toarray/-/lodash.toarray-4.4.0.tgz", + "integrity": "sha1-JMS/zWsvuji/0FlNsRedjptlZWE=", + "dev": true + }, + "log-symbols": { + "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": "^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" + } + } + } + }, + "log-update": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz", + "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==", + "dev": true, + "requires": { + "ansi-escapes": "^4.3.0", + "cli-cursor": "^3.1.0", + "slice-ansi": "^4.0.0", + "wrap-ansi": "^6.2.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 + }, + "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" + } + }, + "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 + }, + "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" + } + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "logform": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.2.0.tgz", + "integrity": "sha512-N0qPlqfypFx7UHNn4B3lzS/b0uLqt2hmuoa+PpuXNYgozdJYAyauF5Ky0BWVjrxDlMWiT3qN4zPq3vVAfZy7Yg==", + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + } + }, + "loud-rejection": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz", + "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=", + "dev": true, + "requires": { + "currently-unhandled": "^0.4.1", + "signal-exit": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "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" + } + }, + "make-dir": { + "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==", + "requires": { + "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==" + } + } + }, + "map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=", + "dev": true + }, + "markdown-it": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz", + "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~2.0.0", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "markdown-it-anchor": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz", + "integrity": "sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA==", + "dev": true + }, + "marked": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-2.0.1.tgz", + "integrity": "sha512-5+/fKgMv2hARmMW7DOpykr2iLhl0NgjyELk5yn92iE7z8Se1IS9n3UsFm86hFXIkvMBmVxki8+ckcpjBeyo/hw==", + "dev": true + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=", + "dev": true + }, + "meow": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz", + "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=", + "dev": true, + "requires": { + "camelcase-keys": "^2.0.0", + "decamelize": "^1.1.2", + "loud-rejection": "^1.0.0", + "map-obj": "^1.0.1", + "minimist": "^1.1.3", + "normalize-package-data": "^2.3.4", + "object-assign": "^4.0.1", + "read-pkg-up": "^1.0.1", + "redent": "^1.0.0", + "trim-newlines": "^1.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "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" + } + }, + "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==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp2": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp2/-/mkdirp2-1.0.4.tgz", + "integrity": "sha512-Q2PKB4ZR4UPtjLl76JfzlgSCUZhSV1AXQgAZa1qt5RiaALFjP/CDrGvFBrOz7Ck6McPcwMAxTsJvWOUjOU8XMw==", + "dev": true + }, + "mocha": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.2.tgz", + "integrity": "sha512-UdmISwr/5w+uXLPKspgoV7/RXZwKRTiTjJ2/AC5ZiEztIoOYdfKb19+9jNmEInzx5pBsCyJQzarAxqIGBNYJhg==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.1", + "debug": "4.3.1", + "diff": "5.0.0", + "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": "4.0.0", + "log-symbols": "4.0.0", + "minimatch": "3.0.4", + "ms": "2.1.3", + "nanoid": "3.1.20", + "serialize-javascript": "5.0.1", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "wide-align": "1.1.3", + "workerpool": "6.1.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "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 + } + } + }, + "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 + }, + "find-up": { + "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": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "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 + }, + "js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "locate-path": { + "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": "^5.0.0" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "p-limit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz", + "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "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": "^3.0.2" + } + }, + "path-exists": { + "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 + }, + "strip-json-comments": { + "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 + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "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" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "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==" + }, + "mustache": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.1.0.tgz", + "integrity": "sha512-0FsgP/WVq4mKyjolIyX+Z9Bd+3WS8GOwoUTyKXT5cTYMGeauNTi2HPCwERqseC1IHAy0Z7MDZnJBfjabd4O8GQ==" + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==" + }, + "nanoid": { + "version": "3.1.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", + "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "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-emoji": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.10.0.tgz", + "integrity": "sha512-Yt3384If5H6BYGVHiHwTL+99OzJKHhgp82S8/dktEK73T26BazdgZ4JZh92xSVtGNJvz9UbXdNAc5hcrXV42vw==", + "dev": true, + "requires": { + "lodash.toarray": "^4.4.0" + } + }, + "node-sass-tilde-importer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/node-sass-tilde-importer/-/node-sass-tilde-importer-1.0.2.tgz", + "integrity": "sha512-Swcmr38Y7uB78itQeBm3mThjxBy9/Ah/ykPIaURY/L6Nec9AyRoL/jJ7ECfMR+oZeCTVQNxVMu/aHU+TLRVbdg==", + "dev": true, + "requires": { + "find-parent-dir": "^0.3.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "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 + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "npm-check": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/npm-check/-/npm-check-5.9.2.tgz", + "integrity": "sha512-YlTZGP1A8+Rad5wldGil9STYxgZpZl18X6GZI03f4Ch6qTI1TLHIYM0ISco19qgg8M3UHfooEqMfYOpOkF3AeA==", + "dev": true, + "requires": { + "babel-runtime": "^6.6.1", + "callsite-record": "^3.0.0", + "chalk": "^1.1.3", + "co": "^4.6.0", + "depcheck": "0.8.3", + "execa": "^0.2.2", + "giturl": "^1.0.0", + "global-modules": "^1.0.0", + "globby": "^4.0.0", + "inquirer": "^0.12.0", + "is-ci": "^1.0.8", + "lodash": "^4.17.15", + "meow": "^3.7.0", + "minimatch": "^3.0.2", + "node-emoji": "^1.0.3", + "ora": "^0.2.1", + "package-json": "^4.0.1", + "path-exists": "^2.1.0", + "pkg-dir": "^1.0.0", + "preferred-pm": "^1.0.1", + "semver": "^5.0.1", + "semver-diff": "^2.0.0", + "text-table": "^0.2.0", + "throat": "^2.0.2", + "update-notifier": "^2.1.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "ansi-align": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-2.0.0.tgz", + "integrity": "sha1-w2rsy6VjuJzrVW82kPCx2eNUf38=", + "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" + } + } + } + }, + "ansi-escapes": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", + "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", + "dev": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "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=", + "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" + } + }, + "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" + } + }, + "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" + } + }, + "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" + } + } + } + }, + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "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" + } + }, + "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": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "configstore": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-3.1.5.tgz", + "integrity": "sha512-nlOhI4+fdzoK5xmJ+NY+1gZK56bwEaWZr8fYuXohZ9Vkc1o3a4T/R3M+yE/w7x/ZVJ1zF8c+oaOvF0dztdUgmA==", + "dev": true, + "requires": { + "dot-prop": "^4.2.1", + "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" + } + }, + "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" + } + }, + "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=", + "dev": true + }, + "dot-prop": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.1.tgz", + "integrity": "sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==", + "dev": true, + "requires": { + "is-obj": "^1.0.0" + } + }, + "figures": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", + "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5", + "object-assign": "^4.1.0" + } + }, + "global-dirs": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz", + "integrity": "sha1-sxnA3UYH81PzvpzKTHL8FIxJ9EU=", + "dev": true, + "requires": { + "ini": "^1.3.4" + } + }, + "inquirer": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", + "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", + "dev": true, + "requires": { + "ansi-escapes": "^1.1.0", + "ansi-regex": "^2.0.0", + "chalk": "^1.0.0", + "cli-cursor": "^1.0.1", + "cli-width": "^2.0.0", + "figures": "^1.3.5", + "lodash": "^4.3.0", + "readline2": "^1.0.1", + "run-async": "^0.1.0", + "rx-lite": "^3.1.2", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.0", + "through": "^2.3.6" + } + }, + "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=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "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=", + "dev": true, + "requires": { + "global-dirs": "^0.1.0", + "is-path-inside": "^1.0.0" + } + }, + "is-npm": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-1.0.0.tgz", + "integrity": "sha1-8vtjpl5JBbQGyGBydloaTceTufQ=", + "dev": true + }, + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "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" + } + }, + "latest-version": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-3.1.0.tgz", + "integrity": "sha1-ogU4P+oyKzO1rjsYq+4NwvNW7hU=", + "dev": true, + "requires": { + "package-json": "^4.0.0" + } + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "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, + "requires": { + "path-key": "^2.0.0" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "run-async": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", + "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", + "dev": true, + "requires": { + "once": "^1.3.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "dev": true + }, + "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" + }, + "dependencies": { + "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" + } + } + } + }, + "unique-string": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz", + "integrity": "sha1-nhBXzKhRq7kzmPizOuGHuZyuwRo=", + "dev": true, + "requires": { + "crypto-random-string": "^1.0.0" + } + }, + "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" + }, + "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" + } + } + } + }, + "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": { + "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" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "xdg-basedir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", + "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=", + "dev": true + } + } + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + } + }, + "npm-run-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-1.0.0.tgz", + "integrity": "sha1-9cMr9ZX+ga6Sfa7FLoL4sACsPI8=", + "dev": true, + "requires": { + "path-key": "^1.0.0" + }, + "dependencies": { + "path-key": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-1.0.0.tgz", + "integrity": "sha1-XVPVeAGWRsDWiADbThRua9wqx68=", + "dev": true + } + } + }, + "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=", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true + }, + "object-get": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/object-get/-/object-get-2.1.1.tgz", + "integrity": "sha512-7n4IpLMzGGcLEMiQKsNR7vCe+N5E9LORFrtNUVy4sO3dj9a3HedZCxEL2T7QuLhcHN1NBuBsMOKaOsAYI9IIvg==", + "dev": true + }, + "object-inspect": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.6.0.tgz", + "integrity": "sha512-GJzfBZ6DgDAmnuaM3104jR4s1Myxr3Y3zfIyN4z3UdqN69oSRacNK8UhnobDdC+7J2AHCjGwxQubNJfE70SXXQ==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + }, + "object-to-spawn-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-to-spawn-args/-/object-to-spawn-args-2.0.0.tgz", + "integrity": "sha512-ZMT4owlXg3JGegecLlAgAA/6BsdKHn63R3ayXcAa3zFkF7oUBHcSb0oxszeutYe0FO2c1lT5pwCuidLkC4Gx3g==", + "dev": true + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "requires": { + "fn.name": "1.x.x" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "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.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "ora": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/ora/-/ora-0.2.3.tgz", + "integrity": "sha1-N1J9Igrc1Tw5tzVx11QVbV22V6Q=", + "dev": true, + "requires": { + "chalk": "^1.1.1", + "cli-cursor": "^1.0.2", + "cli-spinners": "^0.1.2", + "object-assign": "^4.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "dev": true + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "dev": true, + "requires": { + "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" + } + }, + "cli-cursor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", + "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", + "dev": true, + "requires": { + "restore-cursor": "^1.0.1" + } + }, + "onetime": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", + "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", + "dev": true + }, + "restore-cursor": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", + "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", + "dev": true, + "requires": { + "exit-hook": "^1.0.0", + "onetime": "^1.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", + "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=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "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": { + "p-try": "^2.0.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==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "package-json": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-4.0.1.tgz", + "integrity": "sha1-iGmgQBJTZhxMTKPabCEh7VVfXu0=", + "dev": true, + "requires": { + "got": "^6.7.1", + "registry-auth-token": "^3.0.1", + "registry-url": "^3.0.3", + "semver": "^5.1.0" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "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=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pidtree": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz", + "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==", + "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 + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", + "dev": true + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "dev": true, + "requires": { + "pinkie": "^2.0.0" + } + }, + "pkg-dir": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", + "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", + "dev": true, + "requires": { + "find-up": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + } + } + }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "preferred-pm": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-1.0.1.tgz", + "integrity": "sha512-9Uxgin5Xnsl67DBvlNFsmDIlBuG9/XKK2cVBTj//7/7wW6ZY+IC9/GlLqxyHABpoasAsJ1MARFOdYPxMUtndxA==", + "dev": true, + "requires": { + "path-exists": "^3.0.0", + "which-pm": "^1.0.1" + } + }, + "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 + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==" + }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, + "process-nextick-args": { + "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", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "pseudomap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", + "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=", + "dev": true + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "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==", + "requires": { + "escape-goat": "^2.0.0" + } + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "ramda": { + "version": "0.27.1", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.1.tgz", + "integrity": "sha512-PgIdVpn5y5Yns8vqb8FzBUEYn98V3xcPgawAkkgj0YJ0qDsnHCiNmZYfOGMgOvoB0eWFLpYbhxUR3mxfDIMvpw==", + "dev": true + }, + "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" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", + "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=", + "dev": true, + "requires": { + "find-up": "^1.0.0", + "read-pkg": "^1.0.0" + }, + "dependencies": { + "find-up": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", + "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", + "dev": true, + "requires": { + "path-exists": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "load-json-file": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", + "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0", + "strip-bom": "^2.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "path-exists": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", + "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", + "dev": true, + "requires": { + "pinkie-promise": "^2.0.0" + } + }, + "path-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", + "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "read-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", + "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=", + "dev": true, + "requires": { + "load-json-file": "^1.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^1.0.0" + } + }, + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "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" + } + }, + "readdirp": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", + "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "readline2": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", + "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "mute-stream": "0.0.5" + }, + "dependencies": { + "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=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "mute-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", + "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", + "dev": true + } + } + }, + "redent": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz", + "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=", + "dev": true, + "requires": { + "indent-string": "^2.1.0", + "strip-indent": "^1.0.1" + } + }, + "reduce-extract": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/reduce-extract/-/reduce-extract-1.0.0.tgz", + "integrity": "sha1-Z/I4W+2mUGG19fQxJmLosIDKFSU=", + "dev": true, + "requires": { + "test-value": "^1.0.1" + }, + "dependencies": { + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "^2.6.0" + } + }, + "test-value": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-1.1.0.tgz", + "integrity": "sha1-oJE29y7AQ9J8iTcHwrFZv6196T8=", + "dev": true, + "requires": { + "array-back": "^1.0.2", + "typical": "^2.4.2" + } + } + } + }, + "reduce-flatten": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", + "integrity": "sha1-JYx479FT3fk8tWEjf2EYTzaW4yc=", + "dev": true + }, + "reduce-unique": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/reduce-unique/-/reduce-unique-2.0.1.tgz", + "integrity": "sha512-x4jH/8L1eyZGR785WY+ePtyMNhycl1N2XOLxhCbzZFaqF4AXjLzqSxa2UHgJ2ZVR/HHyPOvl1L7xRnW8ye5MdA==", + "dev": true + }, + "reduce-without": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/reduce-without/-/reduce-without-1.0.1.tgz", + "integrity": "sha1-aK0OrRGFXJo31OglbBW7+Hly/Iw=", + "dev": true, + "requires": { + "test-value": "^2.0.0" + }, + "dependencies": { + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "^2.6.0" + } + }, + "test-value": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-2.1.0.tgz", + "integrity": "sha1-Edpv9nDzRxpztiXKTz/c97t0gpE=", + "dev": true, + "requires": { + "array-back": "^1.0.3", + "typical": "^2.6.0" + } + } + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", + "dev": true + }, + "regexpp": { + "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==", + "dev": true, + "requires": { + "rc": "^1.1.6", + "safe-buffer": "^5.0.1" + } + }, + "registry-url": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-3.1.0.tgz", + "integrity": "sha1-PU74cPc93h138M+aOBQyRE4XSUI=", + "dev": true, + "requires": { + "rc": "^1.0.1" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "dev": true, + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "require-package-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/require-package-name/-/require-package-name-2.0.1.tgz", + "integrity": "sha1-wR6XJ2tluOKSP3Xav1+y7ww4Qbk=", + "dev": true + }, + "requizzle": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.3.tgz", + "integrity": "sha512-YanoyJjykPxGHii0fZP0uUPEXpvqfBDxWV7s6GKAiiOsiqhX6vHNyW3Qzdmqp/iq/ExbhaGbVrjB4ruEVSM4GQ==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==" + }, + "rx-lite": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", + "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", + "dev": true + }, + "rxjs": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz", + "integrity": "sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg==", + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-diff": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-2.1.0.tgz", + "integrity": "sha1-S7uEN8jTfksM8aaP1ybsbWRdbTY=", + "dev": true, + "requires": { + "semver": "^5.0.3" + } + }, + "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" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "sfmc-fuelsdk-node": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/sfmc-fuelsdk-node/-/sfmc-fuelsdk-node-2.4.0.tgz", + "integrity": "sha512-Orl/Mjc9KlFBjXuJvlzMaXkK1Kl4ns9Y+NS3WwczcmPj4PnNEsdJC7x+KLarxGbKzgR6iGA+PVG8Fhs3tTb1Ww==", + "requires": { + "fuel-auth": "^3.3.0", + "fuel-rest": "^3.2.2", + "fuel-soap": "^2.2.3", + "lodash.merge": "^4.6.1", + "request": "^2.88.0", + "uuid": "^3.3.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 + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + }, + "simple-git": { + "version": "2.37.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-2.37.0.tgz", + "integrity": "sha512-ZK6qRnP+Xa2v23UEZDNHUfzswsuNCDHOQpWZRkpqNaXn7V5wVBBx3zRJLji3pROJGzrzA7mXwY7preL5EKuAaQ==", + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.2" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "requires": { + "is-arrayish": "^0.3.1" + } + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.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" + } + }, + "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 + }, + "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 + } + } + }, + "sort-array": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/sort-array/-/sort-array-4.1.4.tgz", + "integrity": "sha512-GVFN6Y1sHKrWaSYOJTk9093ZnrBMc9sP3nuhANU44S4xg3rE6W5Z5WyamuT8VpMBbssnetx5faKCua0LEmUnSw==", + "dev": true, + "requires": { + "array-back": "^5.0.0", + "typical": "^6.0.1" + }, + "dependencies": { + "array-back": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-5.0.0.tgz", + "integrity": "sha512-kgVWwJReZWmVuWOQKEOohXKJX+nD02JAZ54D1RRWlv8L0NebauKAaFxACKzB74RTclt1+WNz5KHaLRDAPZbDEw==", + "dev": true + }, + "typical": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-6.0.1.tgz", + "integrity": "sha512-+g3NEp7fJLe9DPa1TArHm9QAA7YciZmWnfAqEaFrBihQ7epOv9i99rjtgb6Iz0wh3WuQDjsCTDfgRoGnmHN81A==", + "dev": true + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "sql-formatter-plus": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sql-formatter-plus/-/sql-formatter-plus-1.3.6.tgz", + "integrity": "sha512-3pelcpLve9GgsXshWL/59zyDkhICkXDURKjXyUN+PJqVGAt+ZmNPB2JY+hgnaQG5X9TQI7Q+WiyLEprHQAWuaQ==", + "requires": { + "@babel/polyfill": "^7.6.0", + "lodash": "^4.17.15" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" + }, + "stackframe": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-0.3.1.tgz", + "integrity": "sha1-M6qE8Rd6VUjIk1Uzy/6zQgl19aQ=", + "dev": true + }, + "stream-connect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-connect/-/stream-connect-1.0.2.tgz", + "integrity": "sha1-GLyB8u2zW4tdmoAJIAqYUxRCipc=", + "dev": true, + "requires": { + "array-back": "^1.0.2" + }, + "dependencies": { + "array-back": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-1.0.4.tgz", + "integrity": "sha1-ZEun8JX3/898Q7Xw3DnTwfA8Bjs=", + "dev": true, + "requires": { + "typical": "^2.6.0" + } + } + } + }, + "stream-via": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/stream-via/-/stream-via-1.0.4.tgz", + "integrity": "sha512-DBp0lSvX5G9KGRDTkR/R+a29H+Wk2xItOF+MpZLLNDWbEV9tGPnqLPxHEYjmiz8xGtJHRIqmI+hCjmNzqoA4nQ==", + "dev": true + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "string-argv": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.1.tgz", + "integrity": "sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==", + "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==", + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "string.prototype.padend": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz", + "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.4.3", + "function-bind": "^1.0.2" + } + }, + "string.prototype.trimleft": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.0.tgz", + "integrity": "sha512-FJ6b7EgdKxxbDxc79cOlok6Afd++TTs5szo+zJTUyow3ycrRfJVE2pq3vcN53XexvKZu/DJMDfeI/qMiZTrjTw==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimright": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.0.tgz", + "integrity": "sha512-fXZTSV55dNBwv16uw+hh5jkghxSnc5oHq+5K/gXgizHwAvMetdAJlHqqoFC1FSDVPYWLkAKl2cxpUT41sV7nSg==", + "requires": { + "define-properties": "^1.1.3", + "function-bind": "^1.1.1" + } + }, + "stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "requires": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "dependencies": { + "is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", + "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==", + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "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-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz", + "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=", + "dev": true, + "requires": { + "get-stdin": "^4.0.1" + }, + "dependencies": { + "get-stdin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz", + "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=", + "dev": true + } + } + }, + "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=" + }, + "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" + } + }, + "table": { + "version": "6.0.7", + "resolved": "https://registry.npmjs.org/table/-/table-6.0.7.tgz", + "integrity": "sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g==", + "dev": true, + "requires": { + "ajv": "^7.0.2", + "lodash": "^4.17.20", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0" + }, + "dependencies": { + "ajv": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-7.2.1.tgz", + "integrity": "sha512-+nu0HDv7kNSOua9apAVc979qd932rrZeb3WOvoiD31A/p1mIE5/9bN2027pE2rOPYEdS3UHzsvof4hY+lM9/WQ==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "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 + }, + "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==", + "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 + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "string-width": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "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" + } + } + } + }, + "table-layout": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-0.4.5.tgz", + "integrity": "sha512-zTvf0mcggrGeTe/2jJ6ECkJHAQPIYEwDoqsiqBjI24mvRmQbInK5jq33fyypaCBxX08hMkfmdOqj6haT33EqWw==", + "dev": true, + "requires": { + "array-back": "^2.0.0", + "deep-extend": "~0.6.0", + "lodash.padend": "^4.6.1", + "typical": "^2.6.1", + "wordwrapjs": "^3.0.0" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "^2.6.1" + } + } + } + }, + "taffydb": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", + "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=", + "dev": true + }, + "temp-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/temp-path/-/temp-path-1.0.0.tgz", + "integrity": "sha1-JLFUOXOrRCiW2a02fdnL2/r+kYs=", + "dev": true + }, + "term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==" + }, + "test-value": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/test-value/-/test-value-3.0.0.tgz", + "integrity": "sha512-sVACdAWcZkSU9x7AOmJo5TqE+GyNJknHaHsMrR6ZnhjVlVN9Yx6FjHrsKZ3BjIpPCT68zYesPWkakrNupwfOTQ==", + "dev": true, + "requires": { + "array-back": "^2.0.0", + "typical": "^2.6.1" + }, + "dependencies": { + "array-back": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-2.0.0.tgz", + "integrity": "sha512-eJv4pLLufP3g5kcZry0j6WXpIbzYw9GUB4mVJZno9wfwiBxbizTnHCw3VJb07cBihbFX48Y7oSrW9y+gt4glyw==", + "dev": true, + "requires": { + "typical": "^2.6.1" + } + } + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==" + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "throat": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/throat/-/throat-2.0.2.tgz", + "integrity": "sha1-qfzoCLaeEzpjJZB4DzQsMKYkmwI=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "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==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "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==" + }, + "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==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, + "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==", + "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=" + } + } + }, + "traverse": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz", + "integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc=" + }, + "trim-newlines": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz", + "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=", + "dev": true + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + }, + "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" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==" + }, + "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==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "typical": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/typical/-/typical-2.6.1.tgz", + "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", + "dev": true + }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "uglify-js": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.13.1.tgz", + "integrity": "sha512-EWhx3fHy3M9JbaeTnO+rEqzCe1wtyQClv6q3YWq0voOj4E+bMZBErVS1GAHPDiRGONYq34M1/d8KuQMgvi6Gjw==", + "dev": true, + "optional": true + }, + "underscore": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz", + "integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==", + "dev": true + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "requires": { + "crypto-random-string": "^2.0.0" + } + }, + "universalify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", + "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" + }, + "unzip-response": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unzip-response/-/unzip-response-2.0.1.tgz", + "integrity": "sha1-0vD3N9FrBhXnKmk17QQhRXLVb5c=", + "dev": true + }, + "update-notifier-git": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/update-notifier-git/-/update-notifier-git-5.0.3.tgz", + "integrity": "sha512-Ikg++hC5EUKURxT+aCsnA58ZYq67Hiof2jK0rlFCJuF/lQsm3g2AKx1BrQZ9cO2x0mksUhabzWvAAJH45nXSJw==", + "requires": { + "boxen": "^4.2.0", + "chalk": "^3.0.0", + "configstore": "^5.0.1", + "git-version-tag": "^1.0.2", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "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": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "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==", + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "requires": { + "ci-info": "^2.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + }, + "semver-diff": { + "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==", + "requires": { + "semver": "^6.3.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==", + "requires": { + "has-flag": "^4.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==", + "requires": { + "punycode": "^2.1.0" + } + }, + "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=", + "dev": true, + "requires": { + "prepend-http": "^1.0.1" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + }, + "v8-compile-cache": { + "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 + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vue-template-compiler": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.6.11.tgz", + "integrity": "sha512-KIq15bvQDrcCjpGjrAhx4mUlyyHfdmTaoNfeoATHLAiWB+MU3cx4lOzMwrnUh9cCxy0Lt1T11hAFY6TQgroUAA==", + "dev": true, + "requires": { + "de-indent": "^1.0.2", + "he": "^1.1.0" + } + }, + "walk-back": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/walk-back/-/walk-back-4.0.0.tgz", + "integrity": "sha512-kudCA8PXVQfrqv2mFTG72vDBRi8BKWxGgFLwPpzHcpZnSwZk93WMwUDVcLHWNsnm+Y0AC4Vb6MUNRgaHfyV2DQ==", + "dev": true + }, + "walkdir": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/walkdir/-/walkdir-0.3.2.tgz", + "integrity": "sha512-0Twghia4Z5wDGDYWURlhZmI47GvERMCsXIu0QZWVVZyW9ZjpbbZvD9Zy9M6cWiQQRRbAcYajIyKNavaZZDt1Uw==", + "dev": true + }, + "wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "optional": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "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-pm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-1.1.0.tgz", + "integrity": "sha512-7GHHJQpALk7BWMD8I+xSILSbHyngvBlfSXlwGpdRFY2voFwVCx+eJAybXTzTnUYmt7zio6B9SEdI81T0fBjxNA==", + "dev": true, + "requires": { + "load-yaml-file": "^0.1.0", + "path-exists": "^3.0.0" + } + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + }, + "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 + }, + "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" + } + } + } + }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "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==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==", + "requires": { + "ansi-regex": "^5.0.0" + } + } + } + }, + "winston": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.3.3.tgz", + "integrity": "sha512-oEXTISQnC8VlSAKf1KYSSd7J6IWuRPQqDdo8eoRNaYKLvwSb5+79Z3Yi1lrl6KDpU6/VWaxpakDAtb1oQ4n9aw==", + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.1.0", + "is-stream": "^2.0.0", + "logform": "^2.2.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.4.0" + }, + "dependencies": { + "is-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", + "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" + } + } + }, + "winston-transport": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.4.0.tgz", + "integrity": "sha512-Lc7/p3GtqtqPBYYtS6KCN3c77/2QCev51DvcJKbkFPQNoj1sinkGwLGFDxkXY9J6p9+EPnYs+D90uwbnaiURTw==", + "requires": { + "readable-stream": "^2.3.7", + "triple-beam": "^1.2.0" + }, + "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" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wordwrapjs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-3.0.0.tgz", + "integrity": "sha512-mO8XtqyPvykVCsrwj5MlOVWvSnCdT+C+QVbm6blradR7JExAhbkZ7hZ9A+9NUtwzSqrlUo9a67ws0EiILrvRpw==", + "dev": true, + "requires": { + "reduce-flatten": "^1.0.1", + "typical": "^2.6.1" + } + }, + "workerpool": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", + "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "dev": true + }, + "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" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "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==", + "requires": { + "imurmurhash": "^0.1.4", + "is-typedarray": "^1.0.0", + "signal-exit": "^3.0.2", + "typedarray-to-buffer": "^3.1.5" + } + }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==" + }, + "xml2js": { + "version": "0.4.22", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.22.tgz", + "integrity": "sha512-MWTbxAQqclRSTnehWWe5nMKzI3VmJ8ltiJEco8akcC6j3miOhjjfzKum5sId+CWhfxdOs/1xauYr8/ZDBtQiRw==", + "requires": { + "sax": ">=0.6.0", + "util.promisify": "~1.0.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==" + }, + "xmlcreate": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-2.0.3.tgz", + "integrity": "sha512-HgS+X6zAztGa9zIK3Y3LXuJes33Lz9x+YyTxgrkIdabu2vqcGOWwdfCpf1hWLRrd553wd4QCDf6BBO6FfdsRiQ==", + "dev": true + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true + }, + "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==", + "dev": true + }, + "yallist": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", + "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=", + "dev": true + }, + "yaml": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz", + "integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "cliui": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.3.tgz", + "integrity": "sha512-Gj3QHTkVMPKqwP3f7B4KPkBZRMR9r4rfi5bXFpg1a+Svvj8l7q5CnkBkVQzfxT5DFSsGk2+PascOgL0JYkL2kw==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.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==", + "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==" + }, + "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==" + }, + "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==" + }, + "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==", + "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==", + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", + "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==" + }, + "yargs-parser": { + "version": "20.2.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.3.tgz", + "integrity": "sha512-emOFRT9WVHw03QSvN5qor9QQT9+sw5vwxfYweivSMHTcAXPefwVae2FjO7JJjj8hCE4CzPOPeFM83VwT29HCww==" + } + } + }, + "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 + }, + "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 + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..1117a0f11 --- /dev/null +++ b/package.json @@ -0,0 +1,76 @@ +{ + "name": "mcdev", + "version": "3.0.0", + "description": "Accenture Salesforce Marketing Cloud DevTools", + "author": "joern.berkefeld, douglas.midgley, robert.zimmermann, maciej.barnas", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/Accenture/sfmc-devtools.git" + }, + "homepage": "https://github.com/Accenture/sfmc-devtools", + "bugs": { + "url": "https://github.com/Accenture/sfmc-devtools/issues", + "email": "joern.berkefeld@accenture.com" + }, + "funding": { + "type": "corporate", + "url": "https://github.com/Accenture/sfmc-devtools" + }, + "main": "./lib/index.js", + "bin": { + "mcdev": "./lib/index.js" + }, + "engines": { + "node": ">=14.16" + }, + "scripts": { + "start": "node lib/index.js", + "mcdev": "node lib/index.js", + "postinstall": "node postinstall.js", + "build": "run-s lint docs", + "debug": "node --nolazy --inspect-brk=9229 lib/index.js", + "docs": "jsdoc2md --files lib/**/*.js > docs/dist/documentation.md", + "lint": "eslint --fix lib/**/*.js && eslint --fix test/**/*.js && prettier --write lib/**/*.js", + "test": "mocha", + "upgrade": "npm-check --update", + "prepare": "husky install" + }, + "dependencies": { + "bluebird": "3.7.2", + "cli-progress": "3.9.0", + "command-exists": "1.2.9", + "console.table": "0.10.0", + "fs-extra": "9.1.0", + "inquirer": "8.0.0", + "json-to-table": "4.2.1", + "mustache": "4.1.0", + "prettier": "2.2.1", + "semver": "5.7.1", + "sfmc-fuelsdk-node": "2.4.0", + "simple-git": "2.37.0", + "sql-formatter-plus": "1.3.6", + "toposort": "2.0.2", + "update-notifier-git": "5.0.3", + "winston": "3.3.3", + "yargs": "16.2.0" + }, + "devDependencies": { + "chai": "4.3.4", + "eslint": "7.22.0", + "eslint-config-prettier": "8.1.0", + "eslint-config-ssjs": "1.1.11", + "eslint-plugin-mocha": "8.1.0", + "eslint-plugin-prettier": "3.3.1", + "husky": "5.1.3", + "jsdoc-to-markdown": "7.0.0", + "lint-staged": "10.5.4", + "mocha": "8.3.2", + "npm-check": "5.9.2", + "npm-run-all": "4.1.5" + }, + "lint-staged": { + "*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"], + "*.{md}": ["prettier --write"] + } +} diff --git a/postinstall.js b/postinstall.js new file mode 100644 index 000000000..cc8bdb991 --- /dev/null +++ b/postinstall.js @@ -0,0 +1,41 @@ +#!/usr/bin/env node +'use strict'; +const greenOnBlack = '\x1b[32m\x1b[40m'; +const highlight = '\x1b[7m'; +const reset = '\x1b[0m'; +console.log(greenOnBlack); // green on black +console.log('Congratulations: You installed Accenture Salesforce Marketing Cloud DevTools!'); +console.log( + `If you have existing Accenture SFMC DevTools projects please run ${highlight}mcdev upgrade${reset}${greenOnBlack} in each of your project directories.` +); + +console.log(reset); // reset cli colors + +/* +Color reference +Reset = "\x1b[0m" +Bright = "\x1b[1m" +Dim = "\x1b[2m" +Underscore = "\x1b[4m" +Blink = "\x1b[5m" +Reverse = "\x1b[7m" +Hidden = "\x1b[8m" + +FgBlack = "\x1b[30m" +FgRed = "\x1b[31m" +FgGreen = "\x1b[32m" +FgYellow = "\x1b[33m" +FgBlue = "\x1b[34m" +FgMagenta = "\x1b[35m" +FgCyan = "\x1b[36m" +FgWhite = "\x1b[37m" + +BgBlack = "\x1b[40m" +BgRed = "\x1b[41m" +BgGreen = "\x1b[42m" +BgYellow = "\x1b[43m" +BgBlue = "\x1b[44m" +BgMagenta = "\x1b[45m" +BgCyan = "\x1b[46m" +BgWhite = "\x1b[47m" +*/ diff --git a/test/deployer.js b/test/deployer.js new file mode 100644 index 000000000..a058b27cd --- /dev/null +++ b/test/deployer.js @@ -0,0 +1,16 @@ +const assert = require('chai').assert; +const Deployer = require('../lib/Deployer'); +const testDataDir = 'test/data/deployer/'; + +describe('Deployer', () => { + describe('#readBUMetadata', () => { + it('should read metadata from local filesystem', async () => { + const metadata = Deployer.readBUMetadata(testDataDir); + // Check that metadata was successfully read + assert.exists(metadata.dataExtension); + assert.exists(metadata.dataExtension.Test); + assert.exists(metadata.dataExtension.Test.columns.idField); + assert.exists(metadata.folder); + }); + }); +}); diff --git a/test/util.js b/test/util.js new file mode 100644 index 000000000..79845eee5 --- /dev/null +++ b/test/util.js @@ -0,0 +1,26 @@ +const assert = require('chai').assert; +const fs = require('fs-extra'); +const Util = require('../lib/util'); +const dataDir = 'test_tmp/util/'; + +describe('Util', () => { + after(() => { + fs.removeSync(dataDir); + }); + describe('#writeJSONToFile', () => { + it('should create json file and directory', async () => { + assert.strictEqual(fs.existsSync(dataDir + 'test1.json'), false); + const jsonContent = { + string: 'abc', + boolean: true, + number: 5, + array: ['asd', 4, 'asdf'], + obj: { + name: 'object', + }, + }; + await Util.writeJSONToFile(dataDir, 'test1', jsonContent); + assert.deepEqual(fs.readJSONSync(dataDir + 'test1.json'), jsonContent); + }); + }); +});